fix:가망고객 모드에서 계약상품 선택 표시 및 저장 지원
- scenario-step.blade.php: 가망고객 모드에서도 상품 선택 UI 표시 - product-selection.blade.php: 가망고객/테넌트 모드 공통 지원 - management_id 기반으로 상품 조회/저장 - isProspect 플래그로 모드 구분 - SalesContractController: prospect_id 지원 추가 - tenant_id 또는 prospect_id 중 하나로 상품 저장 가능 - 카테고리별 상품 삭제 후 저장으로 변경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,15 @@ class SalesContractController extends Controller
|
||||
{
|
||||
/**
|
||||
* 계약 상품 저장 (전체 교체 방식)
|
||||
* - tenant_id 또는 prospect_id 중 하나 필수
|
||||
*/
|
||||
public function saveProducts(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'tenant_id' => 'required|exists:tenants,id',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
'prospect_id' => 'nullable|exists:tenant_prospects,id',
|
||||
'management_id' => 'nullable|exists:sales_tenant_managements,id',
|
||||
'category_id' => 'required|exists:sales_product_categories,id',
|
||||
'products' => 'required|array',
|
||||
'products.*.product_id' => 'required|exists:sales_products,id',
|
||||
'products.*.category_id' => 'required|exists:sales_product_categories,id',
|
||||
@@ -28,15 +32,34 @@ public function saveProducts(Request $request): JsonResponse
|
||||
'products.*.subscription_fee' => 'required|numeric|min:0',
|
||||
]);
|
||||
|
||||
// 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 {
|
||||
DB::transaction(function () use ($validated) {
|
||||
$tenantId = $validated['tenant_id'];
|
||||
$managementId = null;
|
||||
|
||||
DB::transaction(function () use ($validated, &$managementId) {
|
||||
$tenantId = $validated['tenant_id'] ?? null;
|
||||
$prospectId = $validated['prospect_id'] ?? null;
|
||||
$categoryId = $validated['category_id'];
|
||||
|
||||
// 영업관리 레코드 조회 (없으면 생성)
|
||||
$management = SalesTenantManagement::findOrCreateByTenant($tenantId);
|
||||
if ($tenantId) {
|
||||
$management = SalesTenantManagement::findOrCreateByTenant($tenantId);
|
||||
} else {
|
||||
$management = SalesTenantManagement::findOrCreateByProspect($prospectId);
|
||||
}
|
||||
$managementId = $management->id;
|
||||
|
||||
// 기존 상품 삭제
|
||||
SalesContractProduct::where('tenant_id', $tenantId)->delete();
|
||||
// 해당 카테고리의 기존 상품 삭제
|
||||
SalesContractProduct::where('management_id', $management->id)
|
||||
->where('category_id', $categoryId)
|
||||
->delete();
|
||||
|
||||
// 새 상품 저장
|
||||
foreach ($validated['products'] as $product) {
|
||||
@@ -51,16 +74,22 @@ public function saveProducts(Request $request): JsonResponse
|
||||
'created_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
// 총 가입비 업데이트
|
||||
$totalRegistrationFee = SalesContractProduct::where('management_id', $management->id)
|
||||
->sum('registration_fee');
|
||||
$management->update(['total_registration_fee' => $totalRegistrationFee]);
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '계약 상품이 저장되었습니다.',
|
||||
'management_id' => $managementId,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '저장 중 오류가 발생했습니다.',
|
||||
'message' => '저장 중 오류가 발생했습니다: ' . $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,36 @@
|
||||
@php
|
||||
use App\Models\Sales\SalesProductCategory;
|
||||
use App\Models\Sales\SalesContractProduct;
|
||||
use App\Models\Sales\SalesTenantManagement;
|
||||
|
||||
// 가망고객/테넌트 모드 확인
|
||||
$isProspect = $isProspect ?? false;
|
||||
$entity = $entity ?? $tenant ?? null;
|
||||
|
||||
$categories = SalesProductCategory::active()
|
||||
->ordered()
|
||||
->with(['products' => fn($q) => $q->active()->ordered()])
|
||||
->get();
|
||||
|
||||
// 이미 선택된 상품들 조회
|
||||
$selectedProducts = SalesContractProduct::where('tenant_id', $tenant->id)
|
||||
->pluck('product_id')
|
||||
->toArray();
|
||||
// Management ID 조회 (가망고객/테넌트 공통)
|
||||
if ($isProspect) {
|
||||
$management = SalesTenantManagement::where('tenant_prospect_id', $entity->id)->first();
|
||||
} else {
|
||||
$management = SalesTenantManagement::where('tenant_id', $entity->id)->first();
|
||||
}
|
||||
$managementId = $management?->id;
|
||||
|
||||
// 기존 계약 상품 정보 (가격 커스터마이징 포함)
|
||||
$contractProducts = SalesContractProduct::where('tenant_id', $tenant->id)
|
||||
->get()
|
||||
->keyBy('product_id');
|
||||
// 이미 선택된 상품들 조회 (management_id 기반)
|
||||
$selectedProducts = [];
|
||||
$contractProducts = collect();
|
||||
if ($managementId) {
|
||||
$selectedProducts = SalesContractProduct::where('management_id', $managementId)
|
||||
->pluck('product_id')
|
||||
->toArray();
|
||||
$contractProducts = SalesContractProduct::where('management_id', $managementId)
|
||||
->get()
|
||||
->keyBy('product_id');
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div x-data="productSelection()" class="mt-6 bg-gradient-to-br from-indigo-50 to-blue-50 rounded-xl p-5 border border-indigo-100">
|
||||
@@ -153,6 +168,9 @@ function productSelection() {
|
||||
totalSubFee: 0,
|
||||
selectedCount: 0,
|
||||
saving: false,
|
||||
isProspect: {{ $isProspect ? 'true' : 'false' }},
|
||||
entityId: {{ $entity->id }},
|
||||
managementId: {{ $managementId ?? 'null' }},
|
||||
|
||||
init() {
|
||||
// 카테고리 코드 → ID 매핑
|
||||
@@ -229,6 +247,20 @@ function productSelection() {
|
||||
subscription_fee: this.productData[id].subFee,
|
||||
}));
|
||||
|
||||
// 요청 데이터 구성 (가망고객/테넌트 모드에 따라)
|
||||
const requestData = {
|
||||
category_id: currentCategoryId,
|
||||
products: products,
|
||||
};
|
||||
|
||||
if (this.isProspect) {
|
||||
requestData.prospect_id = this.entityId;
|
||||
requestData.management_id = this.managementId;
|
||||
} else {
|
||||
requestData.tenant_id = this.entityId;
|
||||
requestData.management_id = this.managementId;
|
||||
}
|
||||
|
||||
const response = await fetch('/sales/contracts/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -236,16 +268,16 @@ function productSelection() {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tenant_id: {{ $tenant->id }},
|
||||
category_id: currentCategoryId,
|
||||
products: products,
|
||||
}),
|
||||
body: JSON.stringify(requestData),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
alert('상품 선택이 저장되었습니다.');
|
||||
// management_id 업데이트 (새로 생성된 경우)
|
||||
if (result.management_id) {
|
||||
this.managementId = result.management_id;
|
||||
}
|
||||
} else {
|
||||
alert(result.message || '저장에 실패했습니다.');
|
||||
}
|
||||
|
||||
@@ -172,9 +172,12 @@ class="border-t border-gray-100">
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- 계약 체결 단계 (Step 6)에서만 상품 선택 컴포넌트 표시 (테넌트 전용) --}}
|
||||
@if($step['id'] === 6 && $scenarioType === 'sales' && !$isProspectMode)
|
||||
@include('sales.modals.partials.product-selection', ['tenant' => $entity])
|
||||
{{-- 계약 체결 단계 (Step 6)에서만 상품 선택 컴포넌트 표시 --}}
|
||||
@if($step['id'] === 6 && $scenarioType === 'sales')
|
||||
@include('sales.modals.partials.product-selection', [
|
||||
'entity' => $entity,
|
||||
'isProspect' => $isProspectMode,
|
||||
])
|
||||
@endif
|
||||
|
||||
{{-- 단계 이동 버튼 --}}
|
||||
|
||||
Reference in New Issue
Block a user