Files
sam-manage/app/Http/Controllers/Sales/SalesProductController.php
2026-02-25 11:45:01 +09:00

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,
]);
}
}