diff --git a/app/Models/Ecommerce/Product/Order/Concerns/HasShipping.php b/app/Models/Ecommerce/Product/Order/Concerns/HasShipping.php index ad34cb4..891a234 100644 --- a/app/Models/Ecommerce/Product/Order/Concerns/HasShipping.php +++ b/app/Models/Ecommerce/Product/Order/Concerns/HasShipping.php @@ -11,6 +11,10 @@ trait HasShipping */ public function shippingPrice(): int { + if ($this->shippingMethod) { + return intval($this->shipping_price) ?: $this->shippingMethod->price; + } + return intval($this->shipping_price) ?: OrderShipping::priceFor($this->shipping_method); } @@ -19,6 +23,10 @@ trait HasShipping */ public function formattedShippingMethod(): string { + if ($this->shippingMethod) { + return $this->shippingMethod->name; + } + return OrderShipping::formattedShippingMethod($this->shipping_method); } diff --git a/app/Models/Ecommerce/Product/Order/Order.php b/app/Models/Ecommerce/Product/Order/Order.php index 06ff0cd..d6174b2 100644 --- a/app/Models/Ecommerce/Product/Order/Order.php +++ b/app/Models/Ecommerce/Product/Order/Order.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use App\Models\Ecommerce\Product\Order\Shipping\OrderShippingMethod; use Illuminate\Database\Eloquent\SoftDeletes; class Order extends Model @@ -31,6 +32,7 @@ class Order extends Model 'number', 'status', 'shipping_method', + 'shipping_method_id', 'shipping_price', 'payment_type_id', 'notes', @@ -85,4 +87,12 @@ class Order extends Model { return $this->belongsTo(PaymentType::class, 'payment_type_id'); } + + /** + * Shipping method + */ + public function shippingMethod(): BelongsTo + { + return $this->belongsTo(OrderShippingMethod::class, 'shipping_method_id'); + } } diff --git a/app/Models/Ecommerce/Product/Order/Shipping/OrderShippingMethod.php b/app/Models/Ecommerce/Product/Order/Shipping/OrderShippingMethod.php new file mode 100644 index 0000000..034c1a1 --- /dev/null +++ b/app/Models/Ecommerce/Product/Order/Shipping/OrderShippingMethod.php @@ -0,0 +1,65 @@ + + */ + protected $fillable = [ + 'name', + 'slug', + 'description', + 'price', + 'is_active', + ]; + + /** + * Translatable fields + * + * @var array + */ + public $translatable = [ + 'name', + 'description', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'is_active' => 'boolean', + ]; + + /** + * Get the options for generating the slug. + */ + public function getSlugOptions(): SlugOptions + { + return SlugOptions::create() + ->generateSlugsFrom('name') + ->saveSlugsTo('slug'); + } +} diff --git a/app/Nova/Resources/Ecommerce/Product/Order/Actions/ExportOrderInvoiceAction.php b/app/Nova/Resources/Ecommerce/Product/Order/Actions/ExportOrderInvoiceAction.php index 0946336..38c7eed 100644 --- a/app/Nova/Resources/Ecommerce/Product/Order/Actions/ExportOrderInvoiceAction.php +++ b/app/Nova/Resources/Ecommerce/Product/Order/Actions/ExportOrderInvoiceAction.php @@ -73,7 +73,7 @@ class ExportOrderInvoiceAction extends Action 'created_at' => $order->created_at->format('H:i, d.m.Y'), 'order_shipping_method' => $order->formattedShippingMethod(), - 'shipping_price' => $order->shipping_method === OrderShipping::SELF_PICKUP + 'shipping_price' => ($order->shippingMethod && $order->shippingMethod->slug === 'self_pickup') || $order->shipping_method === OrderShipping::SELF_PICKUP ? 'Özüm baryp aljak' : (string) $order->shippingPrice().' TMT', diff --git a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForCreate.php b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForCreate.php index c88ea5e..7dcc98c 100644 --- a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForCreate.php +++ b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForCreate.php @@ -8,8 +8,11 @@ use App\Models\Ecommerce\Product\Order\Status\OrderStatus; use App\Models\System\Settings\Location\Province; use App\Models\System\Settings\Location\Region; use App\Models\System\Settings\OS; +use App\Nova\Resources\Ecommerce\Product\Order\OrderShippingMethod; +use App\Models\Ecommerce\Product\Order\Shipping\OrderShippingMethod as OrderShippingMethodModel; use App\Repositories\Ecommerce\Order\NovaOrderRepository; use Illuminate\Support\Str; +use Laravel\Nova\Fields\BelongsTo; use Laravel\Nova\Fields\Date; use Laravel\Nova\Fields\Hidden; use Laravel\Nova\Fields\ID; @@ -62,18 +65,16 @@ class OrderFieldsForCreate ->options(OrderPayment::values()) ->default(OrderPayment::default()), - Select::make(__('Shipping method'), 'shipping_method') - ->displayUsingLabels() - ->searchable() - ->options(OrderShipping::values()) - ->default(OrderShipping::default()) - ->sortable(), + BelongsTo::make(__('Shipping method'), 'shippingMethod', OrderShippingMethod::class), Text::make(__('Shipping price'), 'shipping_price') ->rules('required', 'numeric') - ->dependsOn('shipping_method', function ($field, $request, $formData) { - if ($formData->shipping_method) { - $field->setValue(OrderShipping::priceFor($formData->shipping_method)); + ->dependsOn('shippingMethod', function ($field, $request, $formData) { + if ($formData->shippingMethod) { + $method = OrderShippingMethodModel::query()->find($formData->shippingMethod); + if ($method) { + $field->setValue($method->price); + } } }), diff --git a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForIndex.php b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForIndex.php index 8dc578e..eff162b 100644 --- a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForIndex.php +++ b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForIndex.php @@ -7,7 +7,9 @@ use App\Models\Ecommerce\Product\Order\Status\OrderStatus; use App\Models\System\Settings\Location\Region; use App\Models\System\Settings\OS; use App\Nova\Fields\FieldHelpers; +use App\Nova\Resources\Ecommerce\Product\Order\OrderShippingMethod; use Laravel\Nova\Fields\Badge; +use Laravel\Nova\Fields\BelongsTo; use Laravel\Nova\Fields\Date; use Laravel\Nova\Fields\DateTime; use Laravel\Nova\Fields\ID; @@ -49,10 +51,7 @@ class OrderFieldsForIndex ->displayUsing(FieldHelpers::tmDate()) ->sortable(), - Select::make(__('Shipping method'), 'shipping_method') - ->displayUsingLabels() - ->options(OrderShipping::values()) - ->default(OrderShipping::default()) + BelongsTo::make(__('Shipping method'), 'shippingMethod', OrderShippingMethod::class) ->sortable(), Select::make(__('Source'), 'source_app') diff --git a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForUpdate.php b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForUpdate.php index f35a313..3c8b770 100644 --- a/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForUpdate.php +++ b/app/Nova/Resources/Ecommerce/Product/Order/Concerns/OrderFieldsForUpdate.php @@ -7,7 +7,10 @@ use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping; use App\Models\Ecommerce\Product\Order\Status\OrderStatus; use App\Models\System\Settings\Location\Province; use App\Models\System\Settings\Location\Region; +use App\Nova\Resources\Ecommerce\Product\Order\OrderShippingMethod; +use App\Models\Ecommerce\Product\Order\Shipping\OrderShippingMethod as OrderShippingMethodModel; use App\Repositories\Ecommerce\Order\NovaOrderRepository; +use Laravel\Nova\Fields\BelongsTo; use Laravel\Nova\Fields\Date; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Select; @@ -54,22 +57,16 @@ class OrderFieldsForUpdate ->options(OrderPayment::values()) ->default(OrderPayment::default()), - Select::make(__('Shipping method'), 'shipping_method') - ->displayUsingLabels() - ->searchable() - ->options(OrderShipping::values()) - ->default(OrderShipping::default()) - ->sortable(), + BelongsTo::make(__('Shipping method'), 'shippingMethod', OrderShippingMethod::class), Text::make(__('Shipping price'), 'shipping_price') ->rules('required', 'numeric') - ->dependsOn('shipping_method', function ($field, $request, $formData) use ($resource) { - if ($formData->shipping_price) { - return; - } - - if ($formData->shipping_method) { - $field->setValue(OrderShipping::orderShippingPrice($resource)); + ->dependsOn('shippingMethod', function ($field, $request, $formData) { + if ($formData->shippingMethod) { + $method = OrderShippingMethodModel::query()->find($formData->shippingMethod); + if ($method) { + $field->setValue($method->price); + } } }), diff --git a/app/Nova/Resources/Ecommerce/Product/Order/Order.php b/app/Nova/Resources/Ecommerce/Product/Order/Order.php index 871370d..9ec9dca 100644 --- a/app/Nova/Resources/Ecommerce/Product/Order/Order.php +++ b/app/Nova/Resources/Ecommerce/Product/Order/Order.php @@ -69,7 +69,7 @@ class Order extends Resource * * @var array */ - public static $with = ['items']; + public static $with = ['items', 'shippingMethod']; /** * Indicates whether the resource should automatically poll for new resources. diff --git a/app/Nova/Resources/Ecommerce/Product/Order/OrderShippingMethod.php b/app/Nova/Resources/Ecommerce/Product/Order/OrderShippingMethod.php new file mode 100644 index 0000000..784155b --- /dev/null +++ b/app/Nova/Resources/Ecommerce/Product/Order/OrderShippingMethod.php @@ -0,0 +1,84 @@ + + */ + public static $model = OrderShippingMethodModel::class; + + /** + * The single value that should be used to represent the resource when being displayed. + * + * @var string + */ + public static $title = 'name'; + + /** + * The columns that should be searched. + * + * @var array + */ + public static $search = [ + 'id', 'name', 'slug', + ]; + + /** + * Get the displayable label of the resource. + */ + public static function label(): string + { + return __('Shipping methods'); + } + + /** + * Get the displayable singular label of the resource. + */ + public static function singularLabel(): string + { + return __('Shipping method'); + } + + /** + * Get the fields displayed by the resource. + */ + public function fields(NovaRequest $request): array + { + return [ + ID::make()->sortable(), + + Text::make(__('Name'), 'name') + ->translatable() + ->rules('required', 'max:255'), + + Text::make(__('Slug'), 'slug') + ->hideWhenCreating() + ->hideWhenUpdating() + ->rules('nullable', 'string', 'max:255'), + + Textarea::make(__('Description'), 'description') + ->translatable() + ->rules('nullable', 'max:1000'), + + Number::make(__('Price'), 'price') + ->step(0.01) + ->rules('required', 'numeric', 'min:0'), + + NovaSwitcher::make(__('Is active'), 'is_active') + ->default(true), + ]; + } +} diff --git a/app/Providers/NovaServiceProvider.php b/app/Providers/NovaServiceProvider.php index 6eefc50..c03f939 100644 --- a/app/Providers/NovaServiceProvider.php +++ b/app/Providers/NovaServiceProvider.php @@ -21,6 +21,7 @@ use App\Nova\Resources\Ecommerce\Product\Inventory\Inventory; use App\Nova\Resources\Ecommerce\Product\Inventory\InventoryHistoryRemovedResource; use App\Nova\Resources\Ecommerce\Product\Inventory\InventoryHistoryResource; use App\Nova\Resources\Ecommerce\Product\Order\Order; +use App\Nova\Resources\Ecommerce\Product\Order\OrderShippingMethod; use App\Nova\Resources\Ecommerce\Product\Product\Product; use App\Nova\Resources\Ecommerce\Product\Review\Review; use App\Nova\Resources\Legal\LegalPage; @@ -211,6 +212,10 @@ class NovaServiceProvider extends NovaApplicationServiceProvider MenuItem::resource(PaymentType::class), ])->collapsedByDefault(), + MenuGroup::make(__('Shipping methods'), [ + MenuItem::resource(OrderShippingMethod::class), + ])->collapsedByDefault(), + MenuGroup::make(__('App'), [ MenuItem::resource(AppVersion::class), ])->collapsedByDefault(), diff --git a/database/migrations/2026_04_29_170636_create_order_shipping_methods_table.php b/database/migrations/2026_04_29_170636_create_order_shipping_methods_table.php new file mode 100644 index 0000000..9857a7f --- /dev/null +++ b/database/migrations/2026_04_29_170636_create_order_shipping_methods_table.php @@ -0,0 +1,32 @@ +id(); + $table->json('name'); + $table->string('slug')->unique(); + $table->json('description')->nullable(); + $table->string('price')->default(0); + $table->boolean('is_active')->default(true); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('order_shipping_methods'); + } +}; diff --git a/database/migrations/2026_04_29_193140_change_shipping_method_to_id_in_orders_table.php b/database/migrations/2026_04_29_193140_change_shipping_method_to_id_in_orders_table.php new file mode 100644 index 0000000..8091cf4 --- /dev/null +++ b/database/migrations/2026_04_29_193140_change_shipping_method_to_id_in_orders_table.php @@ -0,0 +1,29 @@ +foreignId('shipping_method_id')->nullable()->constrained('order_shipping_methods')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('orders', function (Blueprint $table) { + $table->dropForeign(['shipping_method_id']); + $table->dropColumn('shipping_method_id'); + }); + } +}; diff --git a/database/seeders/BrandTableSeeder.php b/database/seeders/BrandTableSeeder.php index 199703e..230ca11 100644 --- a/database/seeders/BrandTableSeeder.php +++ b/database/seeders/BrandTableSeeder.php @@ -2,9 +2,9 @@ namespace Database\Seeders; -use Illuminate\Support\Facades\File; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\File; class BrandTableSeeder extends Seeder { diff --git a/database/seeders/ChannelTableSeeder.php b/database/seeders/ChannelTableSeeder.php index 8fd58aa..7140be0 100644 --- a/database/seeders/ChannelTableSeeder.php +++ b/database/seeders/ChannelTableSeeder.php @@ -3,9 +3,9 @@ namespace Database\Seeders; use App\Models\Ecommerce\Channel\Channel; -use Illuminate\Support\Facades\File; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\File; class ChannelTableSeeder extends Seeder { diff --git a/database/seeders/LegalPageTableSeeder.php b/database/seeders/LegalPageTableSeeder.php index 6729d81..60cd6b3 100644 --- a/database/seeders/LegalPageTableSeeder.php +++ b/database/seeders/LegalPageTableSeeder.php @@ -4,9 +4,9 @@ namespace Database\Seeders; use App\Models\Legal\LegalPage; use Exception; -use Illuminate\Support\Facades\File; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\File; class LegalPageTableSeeder extends Seeder { diff --git a/database/seeders/OrderShippingMethodSeeder.php b/database/seeders/OrderShippingMethodSeeder.php new file mode 100644 index 0000000..d5143a0 --- /dev/null +++ b/database/seeders/OrderShippingMethodSeeder.php @@ -0,0 +1,49 @@ + 'standart', + 'name' => ['en' => 'Standart', 'tk' => 'Standart', 'ru' => 'Стандарт'], + 'price' => 20, + 'is_active' => true, + ], + [ + 'slug' => 'express', + 'name' => ['en' => 'Express', 'tk' => 'Express', 'ru' => 'Экспресс'], + 'price' => 30, + 'is_active' => true, + ], + [ + 'slug' => 'self_pickup', + 'name' => ['en' => 'Self Pickup', 'tk' => 'Öz-özüňi almak', 'ru' => 'Самовывоз'], + 'price' => 0, + 'is_active' => true, + ], + [ + 'slug' => 'region', + 'name' => ['en' => 'Region', 'tk' => 'Welaýat', 'ru' => 'Регион'], + 'price' => 40, + 'is_active' => true, + ], + ]; + + foreach ($methods as $method) { + OrderShippingMethod::firstOrCreate( + ['slug' => $method['slug']], + $method + ); + } + } +} diff --git a/database/seeders/ProvinceTableSeeder.php b/database/seeders/ProvinceTableSeeder.php index 6f98fb0..d69c5b8 100644 --- a/database/seeders/ProvinceTableSeeder.php +++ b/database/seeders/ProvinceTableSeeder.php @@ -3,9 +3,9 @@ namespace Database\Seeders; use Exception; -use Illuminate\Support\Facades\File; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\File; class ProvinceTableSeeder extends Seeder { diff --git a/database/seeders/UserTableSeeder.php b/database/seeders/UserTableSeeder.php index cdc636f..cc523ba 100644 --- a/database/seeders/UserTableSeeder.php +++ b/database/seeders/UserTableSeeder.php @@ -36,7 +36,7 @@ class UserTableSeeder extends Seeder 'last_name' => 'Admin', 'email' => 'admin@smartelektronika.com', 'password' => bcrypt('PuteraSeroja'), - ] + ], ])->each(function ($data) { $user = User::create($data); diff --git a/resources/views/vendor/nova/resources/order/create-fields/shipping.blade.php b/resources/views/vendor/nova/resources/order/create-fields/shipping.blade.php index 7ad3bf5..2f9a778 100644 --- a/resources/views/vendor/nova/resources/order/create-fields/shipping.blade.php +++ b/resources/views/vendor/nova/resources/order/create-fields/shipping.blade.php @@ -1,4 +1,4 @@ - {{ $model->shipping_price ?: App\Models\Ecommerce\Product\Order\Shipping\OrderShipping::priceFor($model->shipping_method) }} TMT, - {{ App\Models\Ecommerce\Product\Order\Shipping\OrderShipping::formattedShippingMethod($model->shipping_method) }} + {{ $model->shippingPrice() }} TMT, + {{ $model->formattedShippingMethod() }}