280 lines
8.6 KiB
PHP
280 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Sales;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Sales\SalesProduct;
|
|
use App\Models\Sales\SalesProductCategory;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\View\View;
|
|
|
|
/**
|
|
* 영업 상품관리 컨트롤러 (HQ 전용)
|
|
*/
|
|
class SalesProductController extends Controller
|
|
{
|
|
/**
|
|
* 상품관리 메인 화면
|
|
*/
|
|
public function index(Request $request): View|Response
|
|
{
|
|
// HTMX 요청인 경우 전체 페이지 리로드 (Alpine.js 스크립트 실행 필요)
|
|
if ($request->header('HX-Request')) {
|
|
return response('', 200)->header('HX-Redirect', route('sales.products.index'));
|
|
}
|
|
|
|
$categories = SalesProductCategory::active()
|
|
->ordered()
|
|
->with(['products' => fn ($q) => $q->ordered()])
|
|
->get();
|
|
|
|
$currentCategoryCode = $request->input('category', $categories->first()?->code);
|
|
$currentCategory = $categories->firstWhere('code', $currentCategoryCode) ?? $categories->first();
|
|
|
|
return view('sales.products.index', compact('categories', 'currentCategory'));
|
|
}
|
|
|
|
/**
|
|
* 상품 목록 (HTMX용)
|
|
*/
|
|
public function productList(Request $request): View
|
|
{
|
|
$categoryCode = $request->input('category');
|
|
$category = SalesProductCategory::where('code', $categoryCode)
|
|
->with(['products' => fn ($q) => $q->ordered()])
|
|
->first();
|
|
|
|
return view('sales.products.partials.product-list', compact('category'));
|
|
}
|
|
|
|
/**
|
|
* 상품 저장
|
|
*/
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'category_id' => 'required|exists:sales_product_categories,id',
|
|
'code' => 'required|string|max:50',
|
|
'name' => 'required|string|max:100',
|
|
'description' => 'nullable|string',
|
|
'development_fee' => 'required|numeric|min:0',
|
|
'registration_fee' => 'required|numeric|min:0',
|
|
'subscription_fee' => 'required|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',
|
|
]);
|
|
|
|
// 코드 중복 체크
|
|
$exists = SalesProduct::where('category_id', $validated['category_id'])
|
|
->where('code', $validated['code'])
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => '이미 존재하는 상품 코드입니다.',
|
|
], 422);
|
|
}
|
|
|
|
// 순서 설정 (마지막)
|
|
$maxOrder = SalesProduct::where('category_id', $validated['category_id'])->max('display_order') ?? 0;
|
|
$validated['display_order'] = $maxOrder + 1;
|
|
$validated['partner_commission_rate'] = $validated['partner_commission_rate'] ?? 20.00;
|
|
$validated['manager_commission_rate'] = $validated['manager_commission_rate'] ?? 5.00;
|
|
|
|
$product = SalesProduct::create($validated);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '상품이 등록되었습니다.',
|
|
'product' => $product,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 상품 수정
|
|
*/
|
|
public function update(Request $request, int $id): JsonResponse
|
|
{
|
|
$product = SalesProduct::findOrFail($id);
|
|
|
|
$validated = $request->validate([
|
|
'name' => 'sometimes|string|max:100',
|
|
'description' => 'nullable|string',
|
|
'development_fee' => 'sometimes|numeric|min:0',
|
|
'registration_fee' => 'sometimes|numeric|min:0',
|
|
'subscription_fee' => 'sometimes|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',
|
|
'is_active' => 'boolean',
|
|
]);
|
|
|
|
$product->update($validated);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '상품이 수정되었습니다.',
|
|
'product' => $product->fresh(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 상품 삭제 (soft delete)
|
|
*/
|
|
public function destroy(int $id): JsonResponse
|
|
{
|
|
$product = SalesProduct::findOrFail($id);
|
|
$product->delete();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '상품이 삭제되었습니다.',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 활성화 토글
|
|
*/
|
|
public function toggleActive(int $id): JsonResponse
|
|
{
|
|
$product = SalesProduct::findOrFail($id);
|
|
$product->update(['is_active' => ! $product->is_active]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $product->is_active ? '상품이 활성화되었습니다.' : '상품이 비활성화되었습니다.',
|
|
'is_active' => $product->is_active,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 순서 변경
|
|
*/
|
|
public function reorder(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'orders' => 'required|array',
|
|
'orders.*.id' => 'required|exists:sales_products,id',
|
|
'orders.*.order' => 'required|integer|min:0',
|
|
]);
|
|
|
|
foreach ($validated['orders'] as $item) {
|
|
SalesProduct::where('id', $item['id'])->update(['display_order' => $item['order']]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '순서가 변경되었습니다.',
|
|
]);
|
|
}
|
|
|
|
// ==================== 카테고리 관리 ====================
|
|
|
|
/**
|
|
* 카테고리 목록
|
|
*/
|
|
public function categories(): JsonResponse
|
|
{
|
|
$categories = SalesProductCategory::ordered()->get();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $categories,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 카테고리 생성
|
|
*/
|
|
public function storeCategory(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'code' => 'required|string|max:50|unique:sales_product_categories,code',
|
|
'name' => 'required|string|max:100',
|
|
'description' => 'nullable|string',
|
|
'base_storage' => 'nullable|string|max:20',
|
|
]);
|
|
|
|
$maxOrder = SalesProductCategory::max('display_order') ?? 0;
|
|
$validated['display_order'] = $maxOrder + 1;
|
|
|
|
$category = SalesProductCategory::create($validated);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '카테고리가 생성되었습니다.',
|
|
'category' => $category,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 카테고리 수정
|
|
*/
|
|
public function updateCategory(Request $request, int $id): JsonResponse
|
|
{
|
|
$category = SalesProductCategory::findOrFail($id);
|
|
|
|
$validated = $request->validate([
|
|
'name' => 'sometimes|string|max:100',
|
|
'description' => 'nullable|string',
|
|
'base_storage' => 'nullable|string|max:20',
|
|
'is_active' => 'boolean',
|
|
]);
|
|
|
|
$category->update($validated);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '카테고리가 수정되었습니다.',
|
|
'category' => $category->fresh(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 카테고리 삭제
|
|
*/
|
|
public function deleteCategory(int $id): JsonResponse
|
|
{
|
|
$category = SalesProductCategory::findOrFail($id);
|
|
|
|
// 상품이 있으면 삭제 불가
|
|
if ($category->products()->exists()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => '상품이 있는 카테고리는 삭제할 수 없습니다.',
|
|
], 422);
|
|
}
|
|
|
|
$category->delete();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '카테고리가 삭제되었습니다.',
|
|
]);
|
|
}
|
|
|
|
// ==================== API (영업 시나리오용) ====================
|
|
|
|
/**
|
|
* 상품 목록 API (카테고리 포함)
|
|
*/
|
|
public function getProductsApi(): JsonResponse
|
|
{
|
|
$categories = SalesProductCategory::active()
|
|
->ordered()
|
|
->with(['products' => fn ($q) => $q->active()->ordered()])
|
|
->get();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $categories,
|
|
]);
|
|
}
|
|
}
|