295 lines
9.8 KiB
PHP
295 lines
9.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\RetributionProposal;
|
|
use App\Models\SpatialPlanning;
|
|
use App\Models\BuildingFunction;
|
|
use App\Services\RetributionProposalService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
class RetributionProposalController extends Controller
|
|
{
|
|
protected RetributionProposalService $proposalService;
|
|
|
|
public function __construct(RetributionProposalService $proposalService)
|
|
{
|
|
$this->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);
|
|
}
|
|
}
|
|
}
|