proposalService = $proposalService; } /** * Display a listing of retribution proposals */ public function index(Request $request): JsonResponse { $query = RetributionProposal::with(['spatialPlanning', 'buildingFunction', 'retributionFormula']); // Filtering if ($request->has('building_function_id')) { $query->where('building_function_id', $request->building_function_id); } if ($request->has('spatial_planning_id')) { $query->where('spatial_planning_id', $request->spatial_planning_id); } if ($request->has('floor_number')) { $query->where('floor_number', $request->floor_number); } if ($request->has('has_spatial_planning')) { if ($request->boolean('has_spatial_planning')) { $query->whereNotNull('spatial_planning_id'); } else { $query->whereNull('spatial_planning_id'); } } // Search if ($request->has('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('proposal_number', 'like', "%{$search}%") ->orWhere('notes', 'like', "%{$search}%") ->orWhereHas('spatialPlanning', function ($sq) use ($search) { $sq->where('name', 'like', "%{$search}%"); }) ->orWhereHas('buildingFunction', function ($bq) use ($search) { $bq->where('name', 'like', "%{$search}%"); }); }); } // Sorting $sortBy = $request->get('sort_by', 'created_at'); $sortOrder = $request->get('sort_order', 'desc'); $query->orderBy($sortBy, $sortOrder); // Pagination $perPage = $request->get('per_page', 15); $proposals = $query->paginate($perPage); return response()->json([ 'success' => true, 'data' => $proposals, 'meta' => [ 'total_amount' => RetributionProposal::sum('total_retribution_amount'), 'total_proposals' => RetributionProposal::count(), 'formatted_total_amount' => 'Rp ' . number_format(RetributionProposal::sum('total_retribution_amount'), 0, ',', '.') ] ]); } /** * Store a new retribution proposal */ public function store(Request $request): JsonResponse { $request->validate([ 'spatial_planning_id' => 'nullable|exists:spatial_plannings,id', 'building_function_id' => 'required|exists:building_functions,id', 'floor_number' => 'required|integer|min:1|max:10', 'floor_area' => 'required|numeric|min:0.01', 'total_building_area' => 'nullable|numeric|min:0.01', 'notes' => 'nullable|string|max:1000' ]); try { if ($request->spatial_planning_id) { $spatialPlanning = SpatialPlanning::findOrFail($request->spatial_planning_id); $proposal = $this->proposalService->createProposalForSpatialPlanning( $spatialPlanning, $request->building_function_id, $request->floor_number, $request->floor_area, $request->total_building_area, $request->notes ); } else { $proposal = $this->proposalService->createStandaloneProposal( $request->building_function_id, $request->floor_number, $request->floor_area, $request->total_building_area, $request->notes ); } $proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']); return response()->json([ 'success' => true, 'message' => 'Retribution proposal created successfully', 'data' => $proposal ], 201); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to create retribution proposal', 'error' => $e->getMessage() ], 400); } } /** * Display the specified retribution proposal */ public function show(int $id): JsonResponse { $proposal = RetributionProposal::with(['spatialPlanning', 'buildingFunction', 'retributionFormula']) ->findOrFail($id); return response()->json([ 'success' => true, 'data' => $proposal ]); } /** * Update the specified retribution proposal */ public function update(Request $request, int $id): JsonResponse { $proposal = RetributionProposal::findOrFail($id); $request->validate([ 'notes' => 'nullable|string|max:1000' ]); $proposal->update($request->only(['notes'])); $proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']); return response()->json([ 'success' => true, 'message' => 'Retribution proposal updated successfully', 'data' => $proposal ]); } /** * Remove the specified retribution proposal */ public function destroy(int $id): JsonResponse { $proposal = RetributionProposal::findOrFail($id); $proposal->delete(); return response()->json([ 'success' => true, 'message' => 'Retribution proposal deleted successfully' ]); } /** * Get retribution proposal statistics */ public function statistics(): JsonResponse { $stats = $this->proposalService->getStatistics(); return response()->json([ 'success' => true, 'data' => $stats ]); } /** * Get total sum of all retribution proposals */ public function totalSum(): JsonResponse { $totalAmount = RetributionProposal::sum('total_retribution_amount'); return response()->json([ 'success' => true, 'data' => [ 'total_amount' => $totalAmount, 'formatted_total_amount' => 'Rp ' . number_format($totalAmount, 0, ',', '.'), 'total_proposals' => RetributionProposal::count() ] ]); } /** * Get building functions list */ public function buildingFunctions(): JsonResponse { $buildingFunctions = BuildingFunction::whereNotNull('parent_id') ->with(['parameter']) ->orderBy('name') ->get(); return response()->json([ 'success' => true, 'data' => $buildingFunctions ]); } /** * Auto-detect building function and create proposal */ public function autoCreateProposal(Request $request): JsonResponse { $request->validate([ 'spatial_planning_id' => 'nullable|exists:spatial_plannings,id', 'building_function_text' => 'required|string', 'floor_number' => 'required|integer|min:1|max:10', 'floor_area' => 'required|numeric|min:0.01', 'total_building_area' => 'nullable|numeric|min:0.01', 'notes' => 'nullable|string|max:1000' ]); try { // Auto-detect building function $buildingFunction = $this->proposalService->detectBuildingFunction($request->building_function_text); if (!$buildingFunction) { return response()->json([ 'success' => false, 'message' => 'Could not detect building function from text', 'suggestion' => 'Please specify building function manually' ], 400); } if ($request->spatial_planning_id) { $spatialPlanning = SpatialPlanning::findOrFail($request->spatial_planning_id); $proposal = $this->proposalService->createProposalForSpatialPlanning( $spatialPlanning, $buildingFunction->id, $request->floor_number, $request->floor_area, $request->total_building_area, $request->notes ); } else { $proposal = $this->proposalService->createStandaloneProposal( $buildingFunction->id, $request->floor_number, $request->floor_area, $request->total_building_area, $request->notes ); } $proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']); return response()->json([ 'success' => true, 'message' => 'Retribution proposal created successfully with auto-detected building function', 'data' => $proposal, 'detected_building_function' => $buildingFunction->name ], 201); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to create retribution proposal', 'error' => $e->getMessage() ], 400); } } }