Files
sam-manage/app/Http/Controllers/Sales/SalesContractController.php
김보곤 23aa38baef feat: [sales] 영업 시나리오 상품선택에 프로모션 할인 기능 추가
- 개발비 할인 (비율/금액/전액면제), 구독료 할인, 무료기간, 메모
- 상품 변경 시 프로모션 최대값 자동 조절 (clampPromoValues)
- 프로모션 데이터 management options에 저장/로드
- 합계 영역에 프로모션 적용 금액, 절감액 표시
2026-03-14 16:53:13 +09:00

138 lines
5.5 KiB
PHP

<?php
namespace App\Http\Controllers\Sales;
use App\Http\Controllers\Controller;
use App\Models\Sales\SalesContractProduct;
use App\Models\Sales\SalesTenantManagement;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/**
* 영업 계약관리 컨트롤러
*/
class SalesContractController extends Controller
{
/**
* 계약 상품 저장 (전체 교체 방식)
* - tenant_id 또는 prospect_id 중 하나 필수
*/
public function saveProducts(Request $request): JsonResponse
{
$validated = $request->validate([
'tenant_id' => 'nullable|exists:codebridge.tenants,id',
'prospect_id' => 'nullable|exists:codebridge.tenant_prospects,id',
'management_id' => 'nullable|exists:codebridge.sales_tenant_managements,id',
'category_id' => 'required|exists:codebridge.sales_product_categories,id',
'products' => 'required|array',
'products.*.product_id' => 'required|exists:codebridge.sales_products,id',
'products.*.category_id' => 'required|exists:codebridge.sales_product_categories,id',
'products.*.registration_fee' => 'required|numeric|min:0',
'products.*.subscription_fee' => 'required|numeric|min:0',
'promotion' => 'nullable|array',
'promotion.dev_discount_type' => 'nullable|string|in:percent,amount',
'promotion.dev_discount_amount' => 'nullable|numeric|min:0',
'promotion.dev_waive' => 'nullable|boolean',
'promotion.sub_discount_percent' => 'nullable|numeric|min:0|max:50',
'promotion.free_months' => 'nullable|integer|in:0,1,2,3,6',
'promotion.note' => 'nullable|string|max:200',
]);
// tenant_id 또는 prospect_id 중 하나는 필수
if (empty($validated['tenant_id']) && empty($validated['prospect_id'])) {
return response()->json([
'success' => false,
'message' => 'tenant_id 또는 prospect_id가 필요합니다.',
], 422);
}
try {
$managementId = null;
DB::transaction(function () use ($validated, &$managementId) {
$tenantId = $validated['tenant_id'] ?? null;
$prospectId = $validated['prospect_id'] ?? null;
$categoryId = $validated['category_id'];
// 영업관리 레코드 조회 (없으면 생성)
if ($tenantId) {
$management = SalesTenantManagement::findOrCreateByTenant($tenantId);
} else {
$management = SalesTenantManagement::findOrCreateByProspect($prospectId);
}
$managementId = $management->id;
// 해당 카테고리의 기존 상품 삭제
SalesContractProduct::where('management_id', $management->id)
->where('category_id', $categoryId)
->delete();
// 새 상품 저장
foreach ($validated['products'] as $product) {
SalesContractProduct::create([
'tenant_id' => $tenantId,
'management_id' => $management->id,
'category_id' => $product['category_id'],
'product_id' => $product['product_id'],
'registration_fee' => $product['registration_fee'],
'subscription_fee' => $product['subscription_fee'],
'discount_rate' => 0,
'created_by' => auth()->id(),
]);
}
// 총 개발비 업데이트
$totalRegistrationFee = SalesContractProduct::where('management_id', $management->id)
->sum('registration_fee');
$management->update(['total_registration_fee' => $totalRegistrationFee]);
// 프로모션 저장
if (! empty($validated['promotion'])) {
$opts = $management->options ?? [];
$opts['promotion'] = array_merge($validated['promotion'], [
'applied_at' => now()->toDateTimeString(),
'applied_by_user_id' => auth()->id(),
]);
$management->update(['options' => $opts]);
}
});
return response()->json([
'success' => true,
'message' => '계약 상품이 저장되었습니다.',
'management_id' => $managementId,
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => '저장 중 오류가 발생했습니다: '.$e->getMessage(),
], 500);
}
}
/**
* 계약 상품 조회
*/
public function getProducts(int $tenantId): JsonResponse
{
$products = SalesContractProduct::where('tenant_id', $tenantId)
->with(['product', 'category'])
->get();
$totals = [
'development_fee' => $products->sum('development_fee'),
'subscription_fee' => $products->sum('subscription_fee'),
'count' => $products->count(),
];
return response()->json([
'success' => true,
'data' => [
'products' => $products,
'totals' => $totals,
],
]);
}
}