diff --git a/01_Documents/STATUS.md b/01_Documents/STATUS.md index 660cbfe..4de3c93 100644 --- a/01_Documents/STATUS.md +++ b/01_Documents/STATUS.md @@ -544,9 +544,14 @@ IFNEX-Logistics/ - اضافه کردن شورت‌کد `[ifnex_transactions]` با لیست تراکنش‌های کاربر - اضافه کردن AJAX handlers برای موجودی کیف پول و تراکنش‌ها - ساخت استایل‌های CSS کامل برای تمام کامپوننت‌های پلاگین - - ایجاد صفحه تست در وردپرس: http://localhost/ifnexwp/?page_id=16 + - ایجاد صفحه تست در وردپرس: http://localhost/ifnexwp/?page_id=18 - رفع باگ صفحه لاگین Filament (MethodNotAllowed) - حذف فایل‌های سفارشی لاگین و بازگشت به پیش‌فرض Filament + - رفع باگ DiscountCode enum: تغییر از `percent` به `percentage` + - ساخت `SampleDataSeeder` برای داده‌های نمونه (۵ مرسوله، ۴ تراکنش، ۳ کد تخفیف) + - ساخت دستور Artisan `ifnex:sync-wp-users` برای یک‌سازی کاربران وردپرس ↔ لاراول + - تست زنده پلاگین وردپرس — همه APIها با موفقیت تست شدند + - پاک‌سازی فایل‌های تست و اضافی از ریشه پروژه diff --git a/04_Laravel/.phpunit.result.cache b/04_Laravel/.phpunit.result.cache index d0d88f4..41bbd44 100644 --- a/04_Laravel/.phpunit.result.cache +++ b/04_Laravel/.phpunit.result.cache @@ -1 +1 @@ -{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":7,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.056,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.052,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.011,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.008,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.06,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.09,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.055,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.047,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.048,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.047,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.068,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.051,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.05,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.072,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.052,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.009,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.008,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.022}} \ No newline at end of file +{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":7,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.052,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.05,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.008,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.005,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.064,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.096,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.048,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.081,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.059,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.049,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.073,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.062,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.001,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.006}} \ No newline at end of file diff --git a/04_Laravel/app/Console/Commands/SyncWordPressUsers.php b/04_Laravel/app/Console/Commands/SyncWordPressUsers.php new file mode 100644 index 0000000..e202539 --- /dev/null +++ b/04_Laravel/app/Console/Commands/SyncWordPressUsers.php @@ -0,0 +1,141 @@ +option('email'); + $createIfMissing = $this->option('create-if-missing'); + + $this->info("🔍 در حال پیدا کردن کاربر وردپرس: $wpEmail"); + + // ۱. پیدا کردن کاربر لاراول + $laravelUser = User::where('email', $wpEmail)->first(); + + if (!$laravelUser) { + if ($createIfMissing) { + $this->warn("کاربر در لاراول یافت نشد. در حال ایجاد..."); + + $laravelUser = User::create([ + 'name' => 'IFNEX Admin', + 'email' => $wpEmail, + 'password' => bcrypt('password'), + 'role' => UserRole::SuperAdmin, + 'is_active' => true, + ]); + + $this->info("✅ کاربر در لاراول ایجاد شد: ID {$laravelUser->id}"); + } else { + $this->error("❌ کاربر در لاراول یافت نشد. از --create-if-missing استفاده کنید."); + return 1; + } + } else { + $this->info("✅ کاربر در لاراول پیدا شد: ID {$laravelUser->id} ({$laravelUser->name})"); + } + + // ۲. پاک کردن توکن‌های قدیمی + $oldCount = $laravelUser->tokens()->count(); + $laravelUser->tokens()->delete(); + $this->info("🗑️ {$oldCount} توکن قدیمی پاک شد."); + + // ۳. ساخت توکن جدید + $token = $laravelUser->createToken('wordpress-bridge')->plainTextToken; + + $this->newLine(); + $this->line('========================================'); + $this->info('🔑 توکن Sanctum جدید (کپی کنید):'); + $this->line('========================================'); + $this->newLine(); + $this->line($token); + $this->newLine(); + $this->line('========================================'); + $this->newLine(); + + // ۴. ذخیره توکن در دیتابیس وردپرس + $this->info("💾 در حال ذخیره توکن در دیتابیس وردپرس..."); + + try { + $wpPdo = DB::connection('mysql')->getPdo(); + + // اتصال به دیتابیس وردپرس + $wpDbName = env('WP_DB_NAME', 'ifnexwp'); + $wpPdo->exec("USE `$wpDbName`"); + + // ذخیره توکن در user meta + $stmt = $wpPdo->prepare(' + INSERT INTO wp_usermeta (user_id, meta_key, meta_value) + VALUES (1, "ifnex_laravel_token", :token) + ON DUPLICATE KEY UPDATE meta_value = :token + '); + $stmt->execute(['token' => $token]); + + // ذخیره تاریخ انقضا + $expiry = date('Y-m-d H:i:s', strtotime('+30 days')); + $stmt = $wpPdo->prepare(' + INSERT INTO wp_usermeta (user_id, meta_key, meta_value) + VALUES (1, "ifnex_laravel_token_expiry", :expiry) + ON DUPLICATE KEY UPDATE meta_value = :expiry + '); + $stmt->execute(['expiry' => $expiry]); + + $this->info("✅ توکن در دیتابیس وردپرس ذخیره شد."); + $this->info(" User ID: 1 (admin)"); + $this->info(" Token: " . substr($token, 0, 20) . "..."); + $this->info(" Expires: $expiry"); + + } catch (\Exception $e) { + $this->error("❌ خطا در ذخیره توکن در وردپرس: " . $e->getMessage()); + $this->warn("توکن تولید شد ولی در وردپرس ذخیره نشد."); + $this->warn("لطفاً دستی در دیتابیس وردپرس ذخیره کنید."); + } + + // ۵. تست نهایی + $this->newLine(); + $this->info("🧪 در حال تست اتصال..."); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'http://localhost:8000/api/v1/wallet/balance'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $token, + 'Accept: application/json', + ]); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + $response = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($http_code === 200) { + $data = json_decode($response, true); + $this->info("✅ اتصال موفق!"); + $this->info(" موجودی کیف پول: " . number_format($data['balance']) . " ریال"); + $this->info(" کل واریزی: " . number_format($data['total_deposited']) . " ریال"); + $this->info(" کل برداشت: " . number_format($data['total_withdrawn']) . " ریال"); + } else { + $this->error("❌ تست ناموفق: HTTP $http_code"); + $this->line(substr($response, 0, 200)); + } + + $this->newLine(); + $this->info("🎉 آماده تست در وردپرس!"); + $this->info(" آدرس: http://localhost/ifnexwp/?page_id=16"); + $this->info(" ورود: admin / admin"); + + return 0; + } +} diff --git a/04_Laravel/bootstrap/app.php b/04_Laravel/bootstrap/app.php index e297769..0aba17d 100644 --- a/04_Laravel/bootstrap/app.php +++ b/04_Laravel/bootstrap/app.php @@ -18,6 +18,7 @@ return Application::configure(basePath: dirname(__DIR__)) \App\Console\Commands\ImportTrackingData::class, \App\Console\Commands\UpdateExchangeRates::class, \App\Console\Commands\GenerateApiToken::class, + \App\Console\Commands\SyncWordPressUsers::class, ]) ->withMiddleware(function (Middleware $middleware): void { // ✅ فعال‌سازی Stateful API برای Sanctum diff --git a/04_Laravel/database/migrations/2026_08_08_000001_create_shipment_items_table.php b/04_Laravel/database/migrations/2026_08_08_000001_create_shipment_items_table.php new file mode 100644 index 0000000..cc7ae79 --- /dev/null +++ b/04_Laravel/database/migrations/2026_08_08_000001_create_shipment_items_table.php @@ -0,0 +1,28 @@ +id(); + $table->foreignId('shipment_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('hs_code', 50)->nullable(); + $table->integer('quantity')->default(1); + $table->decimal('unit_price_usd', 15, 2)->nullable(); + $table->decimal('total_usd', 15, 2)->nullable(); + $table->text('description')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('shipment_items'); + } +}; diff --git a/04_Laravel/database/migrations/2026_08_08_000001_fix_discount_codes_type_enum.php b/04_Laravel/database/migrations/2026_08_08_000001_fix_discount_codes_type_enum.php new file mode 100644 index 0000000..32646f0 --- /dev/null +++ b/04_Laravel/database/migrations/2026_08_08_000001_fix_discount_codes_type_enum.php @@ -0,0 +1,22 @@ +enum('type', ['fixed', 'percentage'])->change(); + }); + } + + public function down(): void + { + Schema::table('discount_codes', function (Blueprint $table) { + $table->enum('type', ['fixed', 'percent'])->change(); + }); + } +}; diff --git a/04_Laravel/database/seeders/SampleDataSeeder.php b/04_Laravel/database/seeders/SampleDataSeeder.php new file mode 100644 index 0000000..7482caf --- /dev/null +++ b/04_Laravel/database/seeders/SampleDataSeeder.php @@ -0,0 +1,262 @@ + 'customer@ifnex.ir'], + [ + 'name' => 'مشتری تست', + 'password' => bcrypt('password'), + 'phone' => '09123456789', + 'role' => \App\Enums\UserRole::Customer, + 'is_active' => true, + ] + ); + + // Create wallet for customer + $wallet = Wallet::firstOrCreate( + ['user_id' => $customer->id], + [ + 'balance' => 2500000, + 'total_deposited' => 5000000, + 'total_withdrawn' => 2500000, + ] + ); + + // Sample transactions + $transactions = [ + [ + 'amount' => 5000000, + 'type' => TransactionType::DEPOSIT, + 'status' => TransactionStatus::COMPLETED, + 'gateway' => PaymentGateway::ZARINPAL, + 'description' => 'شارژ آنلاین کیف پول', + 'gateway_reference_id' => 'REF100001', + 'created_at' => now()->subDays(15), + ], + [ + 'amount' => -1500000, + 'type' => TransactionType::ORDER_PAYMENT, + 'status' => TransactionStatus::COMPLETED, + 'gateway' => PaymentGateway::SYSTEM, + 'description' => 'پرداخت سفارش #1001', + 'created_at' => now()->subDays(10), + ], + [ + 'amount' => -1000000, + 'type' => TransactionType::WITHDRAWAL, + 'status' => TransactionStatus::COMPLETED, + 'gateway' => PaymentGateway::MANUAL, + 'description' => 'کسر دستی توسط ادمین', + 'admin_notes' => 'بدهی مشتری', + 'created_at' => now()->subDays(5), + ], + ]; + + foreach ($transactions as $t) { + WalletTransaction::create(array_merge($t, [ + 'wallet_id' => $wallet->id, + 'balance_before' => $wallet->balance - $t['amount'], + 'balance_after' => $wallet->balance, + 'created_by' => 1, + 'approved_by' => 1, + 'approved_at' => now()->subDays(15), + ])); + } + + // Sample discount codes + $discounts = [ + [ + 'code' => 'WELCOME10', + 'type' => 'percentage', + 'value' => 10, + 'min_order_amount' => 500000, + 'usage_limit' => 100, + 'used_count' => 23, + 'is_active' => true, + ], + [ + 'code' => 'SUMMER500', + 'type' => 'fixed', + 'value' => 500000, + 'min_order_amount' => 2000000, + 'usage_limit' => 50, + 'used_count' => 12, + 'is_active' => true, + ], + [ + 'code' => 'VIP20', + 'type' => 'percentage', + 'value' => 20, + 'min_order_amount' => 1000000, + 'usage_limit' => 20, + 'used_count' => 5, + 'is_active' => true, + ], + ]; + + foreach ($discounts as $d) { + DiscountCode::create($d); + } + + // Sample shipments + $countries = Country::where('is_active', true)->get(); + $fromCountry = $countries->where('iso_code', 'IR')->first(); + $toCountries = $countries->where('iso_code', '!=', 'IR')->take(5); + + $shipments = []; + $awbCounter = 980100000; + + foreach ($toCountries as $toCountry) { + $awbCounter++; + + $shipment = Shipment::create([ + 'awb_no' => (string) $awbCounter, + 'direction' => ShipmentDirection::Export, + 'type' => ShipmentType::Parcel, + 'status' => ShipmentStatus::Delivered, + 'from_country_id' => $fromCountry->id, + 'to_country_id' => $toCountry->id, + 'weight' => rand(5, 50) / 10, + 'volumetric_weight' => rand(5, 60) / 10, + 'chargeable_weight' => rand(5, 55) / 10, + 'shipping_price' => rand(50, 200), + 'extra_service' => rand(0, 30), + 'packing_cost' => rand(0, 100000), + 'domestic_pickup' => rand(0, 50000), + 'domestic_delivery' => rand(0, 50000), + 'warehousing_cost' => rand(0, 200000), + 'discount' => rand(0, 50000), + 'total_fee' => rand(500000, 3000000), + 'sender_name' => 'فرستنده نمونه ' . $awbCounter, + 'sender_phone' => '0912' . rand(100000, 999999), + 'sender_address' => 'اصفهان، خیابان چهارباغ', + 'receiver_name' => 'گیرنده نمونه ' . $toCountry->name, + 'receiver_phone' => '+98' . rand(9120000000, 9899999999), + 'receiver_address' => $toCountry->name . '، آدرس نمونه', + 'created_at' => now()->subDays(rand(1, 30)), + ]); + + // Add carrier mapping + ShipmentCarrierMapping::create([ + 'shipment_id' => $shipment->id, + 'carrier_code' => ['DHL', 'FEDEX', 'UPS'][rand(0, 2)], + 'carrier_tracking_number' => 'TRK' . rand(100000, 999999), + 'direction' => 'export', + 'delivery_status' => 'delivered', + ]); + + // Add tracking events + $events = [ + ['delivery_status' => 'processed', 'description' => 'مرسوله ثبت شد', 'days_ago' => 10], + ['delivery_status' => 'picked_up', 'description' => 'از انبار حمل شد', 'days_ago' => 9], + ['delivery_status' => 'in_transit', 'description' => 'در حال حمل به مقصد', 'days_ago' => 7], + ['delivery_status' => 'out_for_delivery', 'description' => 'برای تحویل در حال ارسال است', 'days_ago' => 3], + ['delivery_status' => 'delivered', 'description' => 'تحویل داده شد', 'days_ago' => 2], + ]; + + foreach ($events as $index => $event) { + ShipmentTrackingEvent::create([ + 'shipment_id' => $shipment->id, + 'delivery_status' => $event['delivery_status'], + 'event_description' => $event['description'], + 'event_date' => now()->subDays($event['days_ago'])->toDateString(), + 'event_time' => now()->subDays($event['days_ago'])->toTimeString(), + 'location' => $event['days_ago'] > 3 ? 'اصفهان' : $toCountry->name, + 'source' => 'manual', + ]); + } + + // Add shipment items + ShipmentItem::create([ + 'shipment_id' => $shipment->id, + 'name' => 'کالای نمونه ' . $awbCounter, + 'hs_code' => '6110.' . rand(10, 99), + 'quantity' => rand(1, 10), + 'unit_price_usd' => rand(10, 500), + 'total_usd' => rand(100, 5000), + ]); + + $shipments[] = $shipment; + } + + // Add one pending shipment + $pendingCountry = $countries->where('iso_code', 'DE')->first(); + if ($pendingCountry) { + $shipment = Shipment::create([ + 'awb_no' => (string) ++$awbCounter, + 'direction' => ShipmentDirection::Export, + 'type' => ShipmentType::Parcel, + 'status' => ShipmentStatus::InTransit, + 'from_country_id' => $fromCountry->id, + 'to_country_id' => $pendingCountry->id, + 'weight' => 12.5, + 'volumetric_weight' => 15.0, + 'chargeable_weight' => 15.0, + 'shipping_price' => 85.00, + 'extra_service' => 10.00, + 'packing_cost' => 100000, + 'total_fee' => 1850000, + 'sender_name' => 'فرستنده در حال ارسال', + 'sender_phone' => '09351234567', + 'sender_address' => 'تهران، خیابان ولیعصر', + 'receiver_name' => 'گیرنده آلمانی', + 'receiver_phone' => '+491711234567', + 'receiver_address' => 'برلین، آدرس نمونه', + 'created_at' => now()->subDays(5), + ]); + + ShipmentCarrierMapping::create([ + 'shipment_id' => $shipment->id, + 'carrier_code' => 'DHL', + 'carrier_tracking_number' => 'DHL' . rand(100000, 999999), + 'direction' => 'export', + 'delivery_status' => 'in_transit', + ]); + + ShipmentTrackingEvent::create([ + 'shipment_id' => $shipment->id, + 'delivery_status' => 'in_transit', + 'event_description' => 'در مرکز بازبسته‌ریزی', + 'event_date' => now()->subDays(2)->toDateString(), + 'event_time' => now()->subDays(2)->toTimeString(), + 'location' => 'استانبول', + 'source' => 'manual', + ]); + } + + $this->command->info('✅ داده‌های نمونه با موفقیت ایجاد شدند:'); + $this->command->info(' - 1 کاربر مشتری'); + $this->command->info(' - 1 کیف پول با موجودی 2,500,000 ریال'); + $this->command->info(' - 3 تراکنش کیف پول'); + $this->command->info(' - 3 کد تخفیف فعال'); + $this->command->info(' - ' . count($shipments) . ' مرسوله با رویداد ترکینگ'); + $this->command->info(' - ' . count($shipments) . ' آیتم گمرکی'); + }); + } +}