diff --git a/app/Http/Controllers/Sales/SalesProductController.php b/app/Http/Controllers/Sales/SalesProductController.php index 46e77dbd..88e33960 100644 --- a/app/Http/Controllers/Sales/SalesProductController.php +++ b/app/Http/Controllers/Sales/SalesProductController.php @@ -62,19 +62,14 @@ public function store(Request $request): JsonResponse 'development_fee' => 'required|numeric|min:0', 'registration_fee' => 'required|numeric|min:0', 'subscription_fee' => 'required|numeric|min:0', + 'min_development_fee' => 'nullable|numeric|min:0', + 'min_subscription_fee' => 'nullable|numeric|min:0', 'partner_commission_rate' => 'nullable|numeric|min:0|max:100', 'manager_commission_rate' => 'nullable|numeric|min:0|max:100', 'allow_flexible_pricing' => 'boolean', 'is_required' => 'boolean', ]); - // 최저가 검증 - $category = SalesProductCategory::findOrFail($validated['category_id']); - $minFeeErrors = $this->validateMinFees($category, $validated); - if ($minFeeErrors) { - return response()->json(['success' => false, 'message' => $minFeeErrors], 422); - } - // 코드 중복 체크 $exists = SalesProduct::where('category_id', $validated['category_id']) ->where('code', $validated['code']) @@ -115,6 +110,8 @@ public function update(Request $request, int $id): JsonResponse 'development_fee' => 'sometimes|numeric|min:0', 'registration_fee' => 'sometimes|numeric|min:0', 'subscription_fee' => 'sometimes|numeric|min:0', + 'min_development_fee' => 'nullable|numeric|min:0', + 'min_subscription_fee' => 'nullable|numeric|min:0', 'partner_commission_rate' => 'nullable|numeric|min:0|max:100', 'manager_commission_rate' => 'nullable|numeric|min:0|max:100', 'allow_flexible_pricing' => 'boolean', @@ -122,14 +119,6 @@ public function update(Request $request, int $id): JsonResponse 'is_active' => 'boolean', ]); - // 최저가 검증 - $category = $product->category; - $checkData = array_merge($product->toArray(), $validated); - $minFeeErrors = $this->validateMinFees($category, $checkData); - if ($minFeeErrors) { - return response()->json(['success' => false, 'message' => $minFeeErrors], 422); - } - $product->update($validated); return response()->json([ @@ -276,30 +265,6 @@ public function deleteCategory(int $id): JsonResponse ]); } - // ==================== 내부 헬퍼 ==================== - - /** - * 최저가 검증 - */ - private function validateMinFees(SalesProductCategory $category, array $data): ?string - { - $errors = []; - - if ($category->min_development_fee > 0 && isset($data['registration_fee'])) { - if ($data['registration_fee'] < $category->min_development_fee) { - $errors[] = '개발비(할인가)는 최저 개발비 ₩'.number_format($category->min_development_fee).' 이상이어야 합니다.'; - } - } - - if ($category->min_subscription_fee > 0 && isset($data['subscription_fee'])) { - if ($data['subscription_fee'] < $category->min_subscription_fee) { - $errors[] = '월 구독료는 최저 구독료 ₩'.number_format($category->min_subscription_fee).' 이상이어야 합니다.'; - } - } - - return $errors ? implode(' ', $errors) : null; - } - // ==================== API (영업 시나리오용) ==================== /** diff --git a/app/Models/Sales/SalesProduct.php b/app/Models/Sales/SalesProduct.php index f89e7072..d7ba663d 100644 --- a/app/Models/Sales/SalesProduct.php +++ b/app/Models/Sales/SalesProduct.php @@ -17,6 +17,8 @@ * @property float $development_fee * @property float $registration_fee * @property float $subscription_fee + * @property float $min_development_fee + * @property float $min_subscription_fee * @property float $partner_commission_rate * @property float $manager_commission_rate * @property bool $allow_flexible_pricing @@ -29,6 +31,7 @@ class SalesProduct extends Model use SoftDeletes; protected $connection = 'codebridge'; + protected $table = 'sales_products'; protected $fillable = [ @@ -39,6 +42,8 @@ class SalesProduct extends Model 'development_fee', 'registration_fee', 'subscription_fee', + 'min_development_fee', + 'min_subscription_fee', 'partner_commission_rate', 'manager_commission_rate', 'allow_flexible_pricing', @@ -52,6 +57,8 @@ class SalesProduct extends Model 'development_fee' => 'decimal:2', 'registration_fee' => 'decimal:2', 'subscription_fee' => 'decimal:2', + 'min_development_fee' => 'decimal:2', + 'min_subscription_fee' => 'decimal:2', 'partner_commission_rate' => 'decimal:2', 'manager_commission_rate' => 'decimal:2', 'allow_flexible_pricing' => 'boolean', diff --git a/resources/views/sales/products/index.blade.php b/resources/views/sales/products/index.blade.php index 2993df89..d772b297 100644 --- a/resources/views/sales/products/index.blade.php +++ b/resources/views/sales/products/index.blade.php @@ -67,28 +67,6 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white b - {{-- 최저가 안내 --}} - - {{-- 상품 카드 그리드 --}}
@include('sales.products.partials.product-list', ['category' => $currentCategory]) @@ -169,13 +147,38 @@ class="w-full px-3 py-2 rounded-lg focus:ring-2 focus:ring-indigo-500 text-right -
+ + {{-- 최저가 설정 --}} +
+
+ + + + 최저가 설정 (절대 이 금액 이하로 내릴 수 없음) +
+
+
+ + +
+
+ + +
+
+
+
@@ -218,72 +221,6 @@ class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:b
- {{-- 최저가 설정 모달 --}} -
-
-
-
- -

최저가 설정

-

- -
-
- - - -

설정된 최저가 이하로는 절대 상품 가격을 내릴 수 없습니다. 영업 할인 협상 시에도 이 금액이 하한선이 됩니다.

-
-
- -
-
- - -

0 입력 시 제한 없음

-
-
- - -

0 입력 시 제한 없음

-
-
- -
- - -
-
-
-
- {{-- 카테고리 관리 모달 --}}
카테고리 관리
@foreach($categories as $category) -
-
-
-
{{ $category->name }}
-
{{ $category->code }} / {{ $category->base_storage }}
-
-
- {{ $category->products->count() }}개 상품 - -
+
+
+
{{ $category->name }}
+
{{ $category->code }} / {{ $category->base_storage }}
+
+
+ {{ $category->products->count() }}개 상품
- @if($category->min_development_fee > 0 || $category->min_subscription_fee > 0) -
- @if($category->min_development_fee > 0) - 최저 개발비: ₩{{ number_format($category->min_development_fee) }} - @endif - @if($category->min_subscription_fee > 0) - 최저 구독료: ₩{{ number_format($category->min_subscription_fee) }} - @endif -
- @endif
@endforeach
@@ -362,22 +280,12 @@ function productManager() { currentCategory: '{{ $currentCategory?->code ?? '' }}', categoryName: '{{ $currentCategory?->name ?? '' }}', baseStorage: '{{ $currentCategory?->base_storage ?? '100GB' }}', - currentMinDevFee: {{ $currentCategory?->min_development_fee ?? 0 }}, - currentMinSubFee: {{ $currentCategory?->min_subscription_fee ?? 0 }}, categories: @json($categories), showProductModal: false, showCategoryModal: false, - showMinFeeModal: false, editingProduct: null, - minFeeForm: { - categoryId: null, - categoryName: '', - min_development_fee: 0, - min_subscription_fee: 0, - }, - productForm: { code: '', name: '', @@ -385,6 +293,8 @@ function productManager() { development_fee: 0, registration_fee: 0, subscription_fee: 0, + min_development_fee: 0, + min_subscription_fee: 0, partner_commission_rate: 20, manager_commission_rate: 5, is_required: false, @@ -402,8 +312,6 @@ function productManager() { if (cat) { this.categoryName = cat.name; this.baseStorage = cat.base_storage; - this.currentMinDevFee = Number(cat.min_development_fee) || 0; - this.currentMinSubFee = Number(cat.min_subscription_fee) || 0; } htmx.ajax('GET', '{{ route("sales.products.list") }}?category=' + code, { target: '#product-list', @@ -541,54 +449,6 @@ function productManager() { this.productForm.development_fee = fee; // 개발비 자동 계산 (개발비의 25%) this.productForm.registration_fee = Math.floor(fee * 0.25); - }, - - openMinFeeModal(categoryId, categoryName, minDevFee, minSubFee) { - this.minFeeForm = { - categoryId: categoryId, - categoryName: categoryName, - min_development_fee: Number(minDevFee) || 0, - min_subscription_fee: Number(minSubFee) || 0, - }; - this.showMinFeeModal = true; - }, - - async saveMinFees() { - try { - const response = await fetch('{{ url("sales/products/categories") }}/' + this.minFeeForm.categoryId, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content, - }, - body: JSON.stringify({ - min_development_fee: this.minFeeForm.min_development_fee, - min_subscription_fee: this.minFeeForm.min_subscription_fee, - }), - }); - const result = await response.json(); - if (result.success) { - this.showMinFeeModal = false; - // 카테고리 데이터 갱신 - const cat = this.categories.find(c => c.id === this.minFeeForm.categoryId); - if (cat) { - cat.min_development_fee = this.minFeeForm.min_development_fee; - cat.min_subscription_fee = this.minFeeForm.min_subscription_fee; - } - // 현재 선택된 카테고리면 상단 안내 갱신 - if (cat && cat.code === this.currentCategory) { - this.currentMinDevFee = this.minFeeForm.min_development_fee; - this.currentMinSubFee = this.minFeeForm.min_subscription_fee; - } - location.reload(); - } else { - alert(result.message || '저장에 실패했습니다.'); - } - } catch (error) { - console.error(error); - alert('저장 중 오류가 발생했습니다.'); - } } }; } diff --git a/resources/views/sales/products/partials/product-list.blade.php b/resources/views/sales/products/partials/product-list.blade.php index 02000528..1e06cb9b 100644 --- a/resources/views/sales/products/partials/product-list.blade.php +++ b/resources/views/sales/products/partials/product-list.blade.php @@ -50,6 +50,25 @@ class="p-1 text-gray-400 hover:text-indigo-600 transition-colors"> 매니저 {{ number_format($product->manager_commission_rate, 0) }}%
+ @if($product->min_development_fee > 0 || $product->min_subscription_fee > 0) +
+ + + + + 최저가: + @if($product->min_development_fee > 0) + 개발 ₩{{ number_format($product->min_development_fee) }} + @endif + @if($product->min_development_fee > 0 && $product->min_subscription_fee > 0) + / + @endif + @if($product->min_subscription_fee > 0) + 구독 ₩{{ number_format($product->min_subscription_fee) }} + @endif + +
+ @endif
{{-- 하단 태그 --}}