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_trial' => 'nullable|boolean', '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, ], ]); } }