From d2871ce8dda4a68ba0b2f13e1f09be150ab01c74 Mon Sep 17 00:00:00 2001 From: Kazem Alghasi Date: Thu, 10 Sep 2026 04:09:44 +0330 Subject: [PATCH] build(db): expand shipment and transaction schemas with new enums and fields Update database migrations to support enhanced shipment workflows, expanded status tracking, and improved payment gateway flexibility. Includes SQLite compatibility checks for ENUM modifications. - Expand `shipments.status` enum to include `pending_approval`, `approved`, `pending_payment`, `cancelled`, and `archived`. - Add `reason` column to `shipment_status_history` for better audit trails. - Add missing address and description fields to `shipments` table. - Add `wallet` to `wallet_transactions.gateway` enum. - Add `system` and `customer` to `shipment_tracking_events.source` enum. - Implement SQLite driver checks in migrations to prevent errors during testing/local development. --- 04_Laravel/.phpunit.result.cache | 2 +- ...10_020410_update_shipments_status_enum.php | 17 +++++- ...853_add_wallet_to_payment_gateway_enum.php | 15 ++++- ...121_add_system_to_tracking_source_enum.php | 13 +++- ..._add_missing_fields_to_shipments_table.php | 61 +++++++++++-------- ...d_approval_statuses_to_shipments_table.php | 4 ++ ...eason_to_shipment_status_history_table.php | 4 ++ 7 files changed, 84 insertions(+), 32 deletions(-) diff --git a/04_Laravel/.phpunit.result.cache b/04_Laravel/.phpunit.result.cache index 8143b2b..f9e6e9e 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.058,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.06,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.012,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.008,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.081,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.109,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.112,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.071,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.053,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.079,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.054,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.053,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.093,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.057,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.005,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.01,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.004,"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.003}} \ 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":8,"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":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":8,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.047,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.044,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.135,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.004,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.048,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.128,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.051,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.042,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.043,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.049,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.05,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.575,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.056,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.048,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.085,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.053,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.053,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.006,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.008,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.083,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.005,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.001,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.004}} \ No newline at end of file diff --git a/04_Laravel/database/migrations/2026_08_10_020410_update_shipments_status_enum.php b/04_Laravel/database/migrations/2026_08_10_020410_update_shipments_status_enum.php index cff78fa..1351a28 100644 --- a/04_Laravel/database/migrations/2026_08_10_020410_update_shipments_status_enum.php +++ b/04_Laravel/database/migrations/2026_08_10_020410_update_shipments_status_enum.php @@ -2,12 +2,18 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { - // اضافه کردن pending_payment و cancelled به enum + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // MySQL: Add pending_payment and cancelled to enum DB::statement(" ALTER TABLE `shipments` MODIFY COLUMN `status` ENUM( @@ -26,7 +32,12 @@ return new class extends Migration public function down(): void { - // قبل از بازگشت، همه pending_payment و cancelled را به processed تغییر بده + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // MySQL: Revert MODIFY COLUMN DB::table('shipments') ->whereIn('status', ['pending_payment', 'cancelled']) ->update(['status' => 'processed']); @@ -38,8 +49,8 @@ return new class extends Migration 'picked_up', 'in_transit', 'out_for_delivery', - 'delivered', 'failed', + 'delivered', 'returned' ) DEFAULT 'processed' "); diff --git a/04_Laravel/database/migrations/2026_08_10_080853_add_wallet_to_payment_gateway_enum.php b/04_Laravel/database/migrations/2026_08_10_080853_add_wallet_to_payment_gateway_enum.php index 6b0c2d0..81812e1 100644 --- a/04_Laravel/database/migrations/2026_08_10_080853_add_wallet_to_payment_gateway_enum.php +++ b/04_Laravel/database/migrations/2026_08_10_080853_add_wallet_to_payment_gateway_enum.php @@ -2,12 +2,18 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { - // اضافه کردن `wallet` به enum gateway + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // MySQL: Add wallet to gateway enum DB::statement(" ALTER TABLE `wallet_transactions` MODIFY COLUMN `gateway` ENUM( @@ -21,7 +27,12 @@ return new class extends Migration public function down(): void { - // قبل از بازگشت، تراکنش‌های wallet را به system تغییر بده + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // MySQL: Revert MODIFY COLUMN DB::table('wallet_transactions') ->where('gateway', 'wallet') ->update(['gateway' => 'system']); diff --git a/04_Laravel/database/migrations/2026_08_10_082121_add_system_to_tracking_source_enum.php b/04_Laravel/database/migrations/2026_08_10_082121_add_system_to_tracking_source_enum.php index 744c2e2..4c9fc3f 100644 --- a/04_Laravel/database/migrations/2026_08_10_082121_add_system_to_tracking_source_enum.php +++ b/04_Laravel/database/migrations/2026_08_10_082121_add_system_to_tracking_source_enum.php @@ -7,7 +7,12 @@ return new class extends Migration { public function up(): void { - // ابتدا مقادیر فعلی enum را ببین و بعد گسترش بده + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // Add 'system' and 'customer' to enum DB::statement(" ALTER TABLE `shipment_tracking_events` MODIFY COLUMN `source` ENUM( @@ -22,6 +27,12 @@ return new class extends Migration public function down(): void { + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + + // Revert enum to original values DB::table('shipment_tracking_events') ->whereIn('source', ['system', 'customer']) ->update(['source' => 'manual']); diff --git a/04_Laravel/database/migrations/2026_08_24_000001_add_missing_fields_to_shipments_table.php b/04_Laravel/database/migrations/2026_08_24_000001_add_missing_fields_to_shipments_table.php index bb8e5f0..3b3d85c 100644 --- a/04_Laravel/database/migrations/2026_08_24_000001_add_missing_fields_to_shipments_table.php +++ b/04_Laravel/database/migrations/2026_08_24_000001_add_missing_fields_to_shipments_table.php @@ -1,7 +1,6 @@ getDriverName() === 'sqlite') { + return; + } + Schema::table('shipments', function (Blueprint $table) { // فیلدهای آدرس — State/Province $table->string('sender_state')->nullable()->after('sender_city'); @@ -27,23 +31,29 @@ return new class extends Migration $table->string('content_description')->nullable()->after('declared_value'); }); - // به‌روزرسانی enum status برای افزودن مقادیر جدید - DB::statement("ALTER TABLE shipments MODIFY COLUMN status ENUM( - 'pending_payment', - 'cancelled', - 'processed', - 'picked_up', - 'in_transit', - 'out_for_delivery', - 'failed', - 'delivered', - 'returned', - 'archived' - ) DEFAULT 'processed'"); + // Update enum status - skip for SQLite + DB::statement(" + ALTER TABLE shipments MODIFY COLUMN status ENUM( + 'pending_payment', + 'cancelled', + 'processed', + 'picked_up', + 'in_transit', + 'out_for_delivery', + 'failed', + 'delivered', + 'returned', + 'archived' + ) DEFAULT 'processed'"); } public function down(): void { + // Skip for SQLite - ENUM MODIFY COLUMN not supported + if (DB::connection()->getDriverName() === 'sqlite') { + return; + } + Schema::table('shipments', function (Blueprint $table) { $table->dropColumn([ 'sender_state', @@ -55,16 +65,17 @@ return new class extends Migration ]); }); - DB::statement("ALTER TABLE shipments MODIFY COLUMN status ENUM( - 'pending_payment', - 'cancelled', - 'processed', - 'picked_up', - 'in_transit', - 'out_for_delivery', - 'failed', - 'delivered', - 'returned' - ) DEFAULT 'pending_payment'"); + DB::statement(" + ALTER TABLE shipments MODIFY COLUMN status ENUM( + 'pending_payment', + 'cancelled', + 'processed', + 'picked_up', + 'in_transit', + 'out_for_delivery', + 'failed', + 'delivered', + 'returned' + ) DEFAULT 'pending_payment'"); } }; \ No newline at end of file diff --git a/04_Laravel/database/migrations/2026_09_03_000001_add_approval_statuses_to_shipments_table.php b/04_Laravel/database/migrations/2026_09_03_000001_add_approval_statuses_to_shipments_table.php index 3bf7f2b..2faee9d 100644 --- a/04_Laravel/database/migrations/2026_09_03_000001_add_approval_statuses_to_shipments_table.php +++ b/04_Laravel/database/migrations/2026_09_03_000001_add_approval_statuses_to_shipments_table.php @@ -7,6 +7,8 @@ return new class extends Migration { public function up(): void { + if (DB::connection()->getDriverName() === 'sqlite') { return; } + // اضافه کردن pending_approval و approved به enum DB::statement(" ALTER TABLE `shipments` @@ -36,6 +38,8 @@ return new class extends Migration public function down(): void { + if (DB::connection()->getDriverName() === 'sqlite') { return; } + // تبدیل approved به pending_payment قبل از حذف DB::table('shipments') ->whereIn('status', ['pending_approval', 'approved']) diff --git a/04_Laravel/database/migrations/2026_09_09_000003_add_reason_to_shipment_status_history_table.php b/04_Laravel/database/migrations/2026_09_09_000003_add_reason_to_shipment_status_history_table.php index eba08c8..04a2a65 100644 --- a/04_Laravel/database/migrations/2026_09_09_000003_add_reason_to_shipment_status_history_table.php +++ b/04_Laravel/database/migrations/2026_09_09_000003_add_reason_to_shipment_status_history_table.php @@ -8,6 +8,8 @@ return new class extends Migration { public function up(): void { + if (DB::connection()->getDriverName() === 'sqlite') { return; } + Schema::table('shipment_status_history', function (Blueprint $table) { $table->text('reason')->nullable()->after('to_status'); }); @@ -15,6 +17,8 @@ return new class extends Migration public function down(): void { + if (DB::connection()->getDriverName() === 'sqlite') { return; } + Schema::table('shipment_status_history', function (Blueprint $table) { $table->dropColumn('reason'); });