From 47289d158efa21f0086c11a94514086482b42f8f Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 2 Jul 2026 10:44:47 +0700 Subject: [PATCH] feat: update ProductRequest and related components to conditionally handle price fields based on user roles; enhance form behavior for better variant price management --- .../Requests/Admin/Master/ProductRequest.php | 28 +++-- app/Services/Master/ProductService.php | 12 ++- .../master/products/form/ProductForm.vue | 6 +- .../products/form/ProductVariantSection.vue | 100 ++++++------------ 4 files changed, 64 insertions(+), 82 deletions(-) diff --git a/app/Http/Requests/Admin/Master/ProductRequest.php b/app/Http/Requests/Admin/Master/ProductRequest.php index 8abedc8..e4fd5fe 100644 --- a/app/Http/Requests/Admin/Master/ProductRequest.php +++ b/app/Http/Requests/Admin/Master/ProductRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests\Admin\Master; use App\Enums\Permission; +use App\Enums\Role; use App\Http\Requests\Concerns\ValidatesMediaUploads; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -25,7 +26,9 @@ public function authorize(): bool */ public function rules(): array { - return [ + $isAdminBahanBaku = $this->user()?->hasRole(Role::ADMIN_BAHAN_BAKU->value) ?? false; + + $rules = [ 'name' => ['required', 'string', 'max:200'], 'description' => ['nullable', 'string'], @@ -43,17 +46,22 @@ public function rules(): array 'variants.*.name' => ['required', 'string', 'max:200'], 'variants.*.stock' => ['required', 'integer', 'min:0'], 'variants.*.retail_stock' => ['required', 'integer', 'min:0'], - 'variants.*.prices' => ['required', 'array'], - 'variants.*.prices.distributor' => ['required', 'integer', 'min:0'], - 'variants.*.prices.agent' => ['required', 'integer', 'min:0'], - 'variants.*.prices.sub_agent' => ['required', 'integer', 'min:0'], - 'variants.*.prices.grosir' => ['required', 'integer', 'min:0'], - 'variants.*.prices.retail' => ['required', 'integer', 'min:0'], - 'variants.*.prices.tiktok' => ['required', 'integer', 'min:0'], - 'variants.*.prices.shopee' => ['required', 'integer', 'min:0'], - 'variants.*.prices.harga_modal' => ['required', 'integer', 'min:0'], ...$this->variantImageRules(), ]; + + if (! $isAdminBahanBaku) { + $rules['variants.*.prices'] = ['required', 'array']; + $rules['variants.*.prices.distributor'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.agent'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.sub_agent'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.grosir'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.retail'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.tiktok'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.shopee'] = ['required', 'integer', 'min:0']; + $rules['variants.*.prices.harga_modal'] = ['required', 'integer', 'min:0']; + } + + return $rules; } /** diff --git a/app/Services/Master/ProductService.php b/app/Services/Master/ProductService.php index 986f55b..34306b1 100644 --- a/app/Services/Master/ProductService.php +++ b/app/Services/Master/ProductService.php @@ -138,11 +138,13 @@ public function create(array $validated, User $user): void $this->syncVariantImages($variant, $variantData, $index); - foreach ($variantData['prices'] as $type => $priceValue) { - $variant->prices()->create([ - 'type' => $type, - 'price' => $priceValue, - ]); + if (! empty($variantData['prices'])) { + foreach ($variantData['prices'] as $type => $priceValue) { + $variant->prices()->create([ + 'type' => $type, + 'price' => $priceValue, + ]); + } } } diff --git a/resources/js/pages/admin/master/products/form/ProductForm.vue b/resources/js/pages/admin/master/products/form/ProductForm.vue index af2bc6b..50e3b91 100644 --- a/resources/js/pages/admin/master/products/form/ProductForm.vue +++ b/resources/js/pages/admin/master/products/form/ProductForm.vue @@ -5,12 +5,16 @@ import { computed, ref } from 'vue'; import { toast } from 'vue-sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { useCan } from '@/composables/useCan'; import { useVariantList } from '@/composables/useVariantList'; import { appendMediaToFormData, createMediaUploadState } from '@/types/media'; import type { CategoryOption, ProductFormInitialData, ProductVariantFormItem } from '@/types/product'; import ProductInfoSection from './ProductInfoSection.vue'; import ProductVariantSection from './ProductVariantSection.vue'; +const { hasRole } = useCan(); +const showPrices = !hasRole('admin-bahan-baku'); + const props = withDefaults( defineProps<{ categories: CategoryOption[]; @@ -156,7 +160,7 @@ function buildFormData(): FormData { formData.append(`variants[${index}][name]`, variant.name.trim()); formData.append(`variants[${index}][stock]`, String(Number.parseInt(String(variant.stock), 10) || 0)); formData.append(`variants[${index}][retail_stock]`, String(Number.parseInt(String(variant.retail_stock), 10) || 0)); - if (variant.prices) { + if (variant.prices && showPrices) { Object.entries(variant.prices as Record).forEach(([type, value]) => { formData.append(`variants[${index}][prices][${type}]`, String(Number.parseInt(value, 10) || 0)); }); diff --git a/resources/js/pages/admin/master/products/form/ProductVariantSection.vue b/resources/js/pages/admin/master/products/form/ProductVariantSection.vue index 8410c80..f5b8d92 100644 --- a/resources/js/pages/admin/master/products/form/ProductVariantSection.vue +++ b/resources/js/pages/admin/master/products/form/ProductVariantSection.vue @@ -1,6 +1,6 @@