feat(laravel): implement user synchronization and shipment schema
Add new core functionality and database structures to support WordPress integration and shipment tracking. - Add `SyncWordPressUsers` Artisan command to synchronize users between WordPress and Laravel. - Create `shipment_items` table migration. - Fix `discount_codes` enum type by changing `percent` to `percentage`. - Add `SampleDataSeeder` for testing shipments, transactions, and discount codes. - Update project status documentation.
This commit is contained in:
parent
0d04c89e91
commit
a699c2dbee
@ -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ها با موفقیت تست شدند
|
||||
- پاکسازی فایلهای تست و اضافی از ریشه پروژه
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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}}
|
||||
{"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}}
|
||||
141
04_Laravel/app/Console/Commands/SyncWordPressUsers.php
Normal file
141
04_Laravel/app/Console/Commands/SyncWordPressUsers.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Enums\UserRole;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SyncWordPressUsers extends Command
|
||||
{
|
||||
protected $signature = 'ifnex:sync-wp-users
|
||||
{--email=info@ifnex.ir : ایمیل کاربر وردپرس برای sincron}
|
||||
{--create-if-missing : ایجاد کاربر در لاراول اگر وجود نداشته باشد}';
|
||||
|
||||
protected $description = 'یکسازی کاربران وردپرس با لاراول و ساخت توکن Sanctum';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$wpEmail = $this->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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('shipment_items', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('discount_codes', function (Blueprint $table) {
|
||||
$table->enum('type', ['fixed', 'percentage'])->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('discount_codes', function (Blueprint $table) {
|
||||
$table->enum('type', ['fixed', 'percent'])->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
262
04_Laravel/database/seeders/SampleDataSeeder.php
Normal file
262
04_Laravel/database/seeders/SampleDataSeeder.php
Normal file
@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\DiscountCode;
|
||||
use App\Models\Shipment;
|
||||
use App\Models\ShipmentCarrierMapping;
|
||||
use App\Models\ShipmentItem;
|
||||
use App\Models\ShipmentTrackingEvent;
|
||||
use App\Models\ShippingRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\WalletTransaction;
|
||||
use App\Enums\ShipmentDirection;
|
||||
use App\Enums\ShipmentStatus;
|
||||
use App\Enums\ShipmentType;
|
||||
use App\Enums\TransactionStatus;
|
||||
use App\Enums\TransactionType;
|
||||
use App\Enums\PaymentGateway;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SampleDataSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
DB::transaction(function () {
|
||||
// Get or create a customer user
|
||||
$customer = User::firstOrCreate(
|
||||
['email' => '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) . ' آیتم گمرکی');
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user