add status spatial plannings

This commit is contained in:
arifal
2025-08-19 18:15:58 +07:00
parent 71ca8dc553
commit 1b084ed485
20 changed files with 892 additions and 250 deletions

View File

@@ -13,6 +13,11 @@ enum PbgTaskFilterData : string
case issuance_realization_pbg = 'issuance-realization-pbg';
case process_in_technical_office = 'process-in-technical-office';
case waiting_click_dpmptsp = 'waiting-click-dpmptsp';
case non_business_rab = 'non-business-rab';
case non_business_krk = 'non-business-krk';
case business_rab = 'business-rab';
case business_krk = 'business-krk';
case business_dlh = 'business-dlh';
public static function getAllOptions() : array {
return [
@@ -25,6 +30,11 @@ enum PbgTaskFilterData : string
self::issuance_realization_pbg->value => 'Realisasi PBG',
self::process_in_technical_office->value => 'Proses Di Dinas Teknis',
self::waiting_click_dpmptsp->value => 'Menunggu Klik DPMPTSP',
self::non_business_rab->value => 'Non Usaha - RAB',
self::non_business_krk->value => 'Non Usaha - KRK',
self::business_rab->value => 'Usaha - RAB',
self::business_krk->value => 'Usaha - KRK',
self::business_dlh->value => 'Usaha - DLH',
];
}
}

View File

@@ -8,6 +8,7 @@ use App\Http\Controllers\Controller;
use App\Http\Resources\BigdataResumeResource;
use App\Models\BigdataResume;
use App\Models\DataSetting;
use App\Models\SpatialPlanning;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Illuminate\Http\Request;
@@ -26,13 +27,12 @@ class BigDataResumeController extends Controller
$type = $request->get("type");
if (!$filterDate || $filterDate === "latest") {
$big_data_resume = BigdataResume::where('resume_type', $type)->latest()->first();
$big_data_resume = BigdataResume::latest()->first();
if (!$big_data_resume) {
return $this->response_empty_resume();
}
} else {
$big_data_resume = BigdataResume::where('resume_type', $type)
->whereDate('created_at', $filterDate)
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate)
->orderBy('id', 'desc')
->first();
@@ -54,7 +54,11 @@ class BigDataResumeController extends Controller
$proses_dinas_teknis_sum = $big_data_resume->process_in_technical_office_sum;
$proses_dinas_teknis_count = $big_data_resume->process_in_technical_office_count;
$tata_ruang = $big_data_resume->spatial_sum;
// Get real-time spatial planning data using new calculation formula
$spatialData = $this->getSpatialPlanningData();
$tata_ruang = $spatialData['sum'];
$tata_ruang_count = $spatialData['count'];
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
// percentage kekurangan potensi
@@ -115,8 +119,8 @@ class BigDataResumeController extends Controller
'percentage' => 100,
],
'tata_ruang' => [
'sum' => $big_data_resume->spatial_sum,
'count' => $big_data_resume->spatial_count,
'sum' => $tata_ruang,
'count' => $tata_ruang_count,
'percentage' => $tata_ruang_percentage,
],
'kekurangan_potensi' => [
@@ -399,4 +403,58 @@ class BigDataResumeController extends Controller
return response()->json($result);
}
/**
* Get spatial planning data using new calculation formula
*/
private function getSpatialPlanningData(): array
{
try {
// Get spatial plannings that are not yet issued (is_terbit = false) and have valid data
$spatialPlannings = SpatialPlanning::where('land_area', '>', 0)
->where('site_bcr', '>', 0)
->where('is_terbit', false)
->get();
$totalSum = 0;
$businessCount = 0;
$nonBusinessCount = 0;
foreach ($spatialPlannings as $spatialPlanning) {
// Use new calculation formula: LUAS LAHAN × BCR × HARGA SATUAN
$calculatedAmount = $spatialPlanning->calculated_retribution;
$totalSum += $calculatedAmount;
// Count business types
if ($spatialPlanning->is_business_type) {
$businessCount++;
} else {
$nonBusinessCount++;
}
}
Log::info("Real-time Spatial Planning Data (is_terbit = false only)", [
'total_records' => $spatialPlannings->count(),
'business_count' => $businessCount,
'non_business_count' => $nonBusinessCount,
'total_sum' => $totalSum,
'filtered_by' => 'is_terbit = false'
]);
return [
'count' => $spatialPlannings->count(),
'sum' => (float) $totalSum,
'business_count' => $businessCount,
'non_business_count' => $nonBusinessCount,
];
} catch (\Exception $e) {
Log::error("Error getting spatial planning data", ['error' => $e->getMessage()]);
return [
'count' => 0,
'sum' => 0.0,
'business_count' => 0,
'non_business_count' => 0,
];
}
}
}

View File

@@ -25,7 +25,7 @@ class RequestAssignmentController extends Controller
// Build base query for counting (without relationships to avoid duplicates)
$baseQuery = PbgTask::query();
// Filter only valid data (is_valid = true)
// Always filter only valid data (is_valid = true)
$baseQuery->where('is_valid', true);
// Apply year filter if provided (to match BigdataResume behavior)
@@ -35,100 +35,17 @@ class RequestAssignmentController extends Controller
Log::info('RequestAssignmentController year filter applied', ['year' => $year]);
}
// Apply filters to base query (matching BigdataResume logic exactly)
if ($request->has('filter') && !empty($request->get('filter'))) {
$filter = strtolower(trim($request->get('filter')));
// Log filter for debugging
Log::info('RequestAssignmentController filter applied', ['filter' => $filter, 'original' => $request->get('filter')]);
switch ($filter) {
case 'non-business':
// Match BigdataResume non-business logic exactly
$baseQuery->where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
});
break;
case 'business':
// Match BigdataResume business logic exactly
$baseQuery->where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
});
break;
case 'verified':
// Match BigdataResume verified logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getVerified());
break;
case 'non-verified':
// Match BigdataResume non-verified logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getNonVerified());
break;
case 'potention':
// Match BigdataResume potention logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getPotention());
break;
case 'issuance-realization-pbg':
// Match BigdataResume issuance realization logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getIssuanceRealizationPbg());
break;
case 'process-in-technical-office':
// Match BigdataResume process in technical office logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getProcessInTechnicalOffice());
break;
case 'waiting-click-dpmptsp':
// Match BigdataResume waiting click DPMPTSP logic exactly
$baseQuery->whereIn("status", PbgTaskStatus::getWaitingClickDpmptsp());
break;
default:
// Log unrecognized filter for debugging
Log::warning('Unrecognized filter value', ['filter' => $filter, 'original' => $request->get('filter')]);
break;
}
}
// Apply search to base query
if ($request->has('search') && !empty($request->get("search"))) {
$search = $request->get('search');
// Search in pbg_task columns
$baseQuery->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%")
->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('owner_name', 'LIKE', "%$search%")
->orWhere('address', 'LIKE', "%$search%");
});
// If search term exists, also find UUIDs from name_building search
$namesBuildingUuids = DB::table('pbg_task_details')
->where('name_building', 'LIKE', "%$search%")
->pluck('pbg_task_uid')
->toArray();
// If we found matching name_building records, include them in the search
if (!empty($namesBuildingUuids)) {
$baseQuery->orWhereIn('uuid', $namesBuildingUuids);
}
}
// Get filter value, default to 'all' if not provided or empty
$filter = $request->has('filter') && !empty($request->get('filter'))
? strtolower(trim($request->get('filter')))
: 'all';
// Log filter for debugging
Log::info('RequestAssignmentController filter applied', ['filter' => $filter, 'original' => $request->get('filter')]);
// Apply filters to base query using single consolidated method
$this->applyFilter($baseQuery, $filter);
// Get accurate count from base query (without relationships)
$accurateCount = $baseQuery->count();
@@ -144,7 +61,7 @@ class RequestAssignmentController extends Controller
// Log final query count for debugging
Log::info('RequestAssignmentController final result', [
'filter' => $request->get('filter'),
'filter' => $filter,
'search' => $request->get('search'),
'year' => $request->get('year'),
'accurate_count' => $accurateCount,
@@ -153,13 +70,17 @@ class RequestAssignmentController extends Controller
]);
// Cross-validation with BigdataResume logic (for debugging consistency)
if ($request->has('filter') && $request->has('year') &&
!empty($request->get('filter')) && !empty($request->get('year'))) {
$this->validateConsistencyWithBigdataResume($request->get('filter'), $request->get('year'), $accurateCount);
if ($filter !== 'all' && $request->has('year') && !empty($request->get('year'))) {
$this->validateConsistencyWithBigdataResume($filter, $request->get('year'), $accurateCount);
}
// Apply search to data query
if ($request->has('search') && !empty($request->get("search"))) {
$this->applySearch($dataQuery, $request->get('search'));
}
// Additional logging for potention filter
if ($request->get('filter') === 'potention') {
if ($filter === 'potention') {
$rejectedCount = PbgTask::whereIn('status', PbgTaskStatus::getRejected())->count();
Log::info('Potention filter details', [
'potention_count' => $accurateCount,
@@ -170,8 +91,8 @@ class RequestAssignmentController extends Controller
}
// Also log to console for immediate debugging
if ($request->has('filter') && !empty($request->get('filter'))) {
error_log('RequestAssignment Filter Debug: ' . $request->get('filter') . ' -> Count: ' . $accurateCount);
if ($filter !== 'all') {
error_log('RequestAssignment Filter Debug: ' . $filter . ' -> Count: ' . $accurateCount);
}
// Get paginated results with relationships
@@ -183,6 +104,198 @@ class RequestAssignmentController extends Controller
return RequestAssignmentResouce::collection($paginatedResults);
}
/**
* Apply filter logic to the query
*/
private function applyFilter($query, string $filter)
{
switch ($filter) {
case 'all':
// No additional filters, just return all valid records
break;
case 'non-business':
// Match BigdataResume non-business logic exactly
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
});
break;
case 'business':
// Match BigdataResume business logic exactly
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
});
break;
case 'verified':
// Match BigdataResume verified logic exactly
$query->whereIn("status", PbgTaskStatus::getVerified());
break;
case 'non-verified':
// Match BigdataResume non-verified logic exactly
$query->whereIn("status", PbgTaskStatus::getNonVerified());
break;
case 'potention':
// Match BigdataResume potention logic exactly
$query->whereIn("status", PbgTaskStatus::getPotention());
break;
case 'issuance-realization-pbg':
// Match BigdataResume issuance realization logic exactly
$query->whereIn("status", PbgTaskStatus::getIssuanceRealizationPbg());
break;
case 'process-in-technical-office':
// Match BigdataResume process in technical office logic exactly
$query->whereIn("status", PbgTaskStatus::getProcessInTechnicalOffice());
break;
case 'waiting-click-dpmptsp':
// Match BigdataResume waiting click DPMPTSP logic exactly
$query->whereIn("status", PbgTaskStatus::getWaitingClickDpmptsp());
break;
case 'non-business-rab':
// Non-business tasks that have RAB documents (data_type = 3) with at least one status != 1
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 3)
->where('pbg_task_detail_data_lists.status', '!=', 1);
});
break;
case 'non-business-krk':
// Non-business tasks that have KRK documents (data_type = 2) with at least one status != 1
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 2)
->where('pbg_task_detail_data_lists.status', '!=', 1);
});
break;
case 'business-rab':
// Business tasks that have RAB documents (data_type = 3) with at least one status != 1
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 3)
->where('pbg_task_detail_data_lists.status', '!=', 1);
});
break;
case 'business-krk':
// Business tasks that have KRK documents (data_type = 2) with at least one status != 1
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 2)
->where('pbg_task_detail_data_lists.status', '!=', 1);
});
break;
case 'business-dlh':
// Business tasks that have DLH documents (data_type = 5) with at least one status != 1
$query->where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 5)
->where('pbg_task_detail_data_lists.status', '!=', 1);
});
break;
default:
// Log unrecognized filter for debugging
Log::warning('Unrecognized filter value', ['filter' => $filter]);
break;
}
}
/**
* Apply search logic to the query
*/
private function applySearch($query, string $search)
{
// Search in pbg_task columns
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%")
->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('owner_name', 'LIKE', "%$search%")
->orWhere('address', 'LIKE', "%$search%");
});
// If search term exists, also find UUIDs from name_building search
$namesBuildingUuids = DB::table('pbg_task_details')
->where('name_building', 'LIKE', "%$search%")
->pluck('pbg_task_uid')
->toArray();
// If we found matching name_building records, include them in the search
if (!empty($namesBuildingUuids)) {
$query->orWhereIn('uuid', $namesBuildingUuids);
}
}
public function report_payment_recaps(Request $request)
{
try {
@@ -369,6 +482,112 @@ class RequestAssignmentController extends Controller
->whereYear('task_created_at', $year)
->count();
break;
case 'non-business-rab':
$bigdataResumeCount = PbgTask::where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->where('is_valid', true)
->whereYear('task_created_at', $year)
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 3)
->where('pbg_task_detail_data_lists.status', '!=', 1);
})
->count();
break;
case 'non-business-krk':
$bigdataResumeCount = PbgTask::where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->where('is_valid', true)
->whereYear('task_created_at', $year)
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 2)
->where('pbg_task_detail_data_lists.status', '!=', 1);
})
->count();
break;
case 'business-rab':
$bigdataResumeCount = PbgTask::where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->where('is_valid', true)
->whereYear('task_created_at', $year)
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 3)
->where('pbg_task_detail_data_lists.status', '!=', 1);
})
->count();
break;
case 'business-krk':
$bigdataResumeCount = PbgTask::where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->where('is_valid', true)
->whereYear('task_created_at', $year)
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 2)
->where('pbg_task_detail_data_lists.status', '!=', 1);
})
->count();
break;
case 'business-dlh':
$bigdataResumeCount = PbgTask::where(function ($q) {
$q->where(function ($q2) {
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})
->where('is_valid', true)
->whereYear('task_created_at', $year)
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pbg_task_detail_data_lists')
->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid')
->where('pbg_task_detail_data_lists.data_type', 5)
->where('pbg_task_detail_data_lists.status', '!=', 1);
})
->count();
break;
default:
Log::info('Unknown filter for consistency validation', [

View File

@@ -26,6 +26,10 @@ class SpatialPlanningController extends Controller
$search = $request->input('search', '');
$query = SpatialPlanning::query();
// Only include spatial plannings that are not yet issued (is_terbit = false)
$query->where('is_terbit', false);
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%$search%")
@@ -42,9 +46,11 @@ class SpatialPlanningController extends Controller
// Menambhakan nomor urut (No)
$start = ($spatialPlannings->currentPage()-1) * $perPage + 1;
// Tambahkan nomor urut ke dalam data
// Tambahkan nomor urut ke dalam data (calculated_retribution sudah auto-append)
$data = $spatialPlannings->map(function ($item, $index) use ($start) {
return array_merge($item->toArray(), ['no' => $start + $index]);
$itemArray = $item->toArray();
$itemArray['no'] = $start + $index;
return $itemArray;
});
info($data);
@@ -104,9 +110,10 @@ class SpatialPlanningController extends Controller
/**
* Display the specified resource.
*/
public function show(SpatialPlanning $spatialPlanning): SpatialPlanning
public function show(SpatialPlanning $spatialPlanning): array
{
return $spatialPlanning;
// calculated_retribution and formatted_retribution are already appended via $appends
return $spatialPlanning->toArray();
}
/**

View File

@@ -82,9 +82,15 @@ class SpatialPlanningController extends Controller
"kbli"=> "KBLI",
"activities"=> "Kegiatan",
"area"=> "Luas (m2)",
"land_area"=> "Luas Lahan (m2)",
"location"=> "Lokasi",
"number"=> "Nomor",
"date"=> "Tanggal",
"site_bcr"=> "BCR",
"building_function"=> "Fungsi Bangunan",
"business_type_info"=> "Jenis Usaha",
"is_terbit"=> "Status Terbit",
"calculated_retribution"=> "Retribusi",
];
}
@@ -95,9 +101,15 @@ class SpatialPlanningController extends Controller
"kbli"=> "text",
"activities"=> "text",
"area"=> "text",
"land_area"=> "text",
"location"=> "text",
"number"=> "text",
"date"=> "date",
"site_bcr"=> "text",
"building_function"=> "text",
"business_type_info"=> "readonly",
"is_terbit"=> "select",
"calculated_retribution"=> "readonly",
];
}
}

View File

@@ -22,13 +22,14 @@ class SpatialPlanningRequest extends FormRequest
public function rules(): array
{
return [
'name' => 'string',
'kbli' => 'string',
'activities' => 'string',
'area' => 'string',
'location' => 'string',
'number' => 'string',
'date' => 'date_format:Y-m-d',
'name' => 'nullable|string',
'kbli' => 'nullable|string',
'activities' => 'nullable|string',
'area' => 'nullable|string',
'location' => 'nullable|string',
'number' => 'nullable|string',
'date' => 'nullable|date_format:Y-m-d',
'is_terbit' => 'nullable|boolean',
];
}

View File

@@ -1,35 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class SpatialPlanningsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required','string','max:255'],
'kbli' => ['required','string','max:255'],
'kegiatan' => ['required','string'],
'luas' => ['required','numeric','regex:/^\d{1,16}(\.\d{1,2})?$/'],
'lokasi' => ['required','string'],
'nomor' => ['required','string','max:255',Rule::unique('spatial_plannings')->ignore($this->id)],
'sp_date' => ['required','date'],
];
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class SpatialPlanningsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -166,16 +166,13 @@ class BigdataResume extends Model
')
->value('total_count') ?? 0;
// Non-Business DLH count - for each non-business task with data_type=5:
// Business DLH count - for each business task with data_type=5:
// if any status != 1 then return 1, if all status = 1 then return 0, then sum all
$business_dlh_count = DB::table('pbg_task')
->where(function ($q) {
$q->where(function ($q2) {
$q2->where(function ($q3) {
$q3->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->orWhereNull('function_type');
$q2->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%'])
->orWhereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']);
})
->whereIn("status", PbgTaskStatus::getNonVerified());
})

View File

@@ -34,14 +34,24 @@ class SpatialPlanning extends Model
*
* @var array<int, string>
*/
protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date', 'no_tapak', 'no_skkl', 'no_ukl', 'building_function', 'sub_building_function', 'number_of_floors', 'land_area', 'site_bcr'];
protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date', 'no_tapak', 'no_skkl', 'no_ukl', 'building_function', 'sub_building_function', 'number_of_floors', 'land_area', 'site_bcr', 'is_terbit'];
protected $casts = [
'area' => 'decimal:6',
'land_area' => 'decimal:6',
'site_bcr' => 'decimal:6',
'number_of_floors' => 'integer',
'date' => 'date'
'date' => 'date',
'is_terbit' => 'boolean'
];
protected $appends = [
'calculated_retribution',
'formatted_retribution',
'is_business_type',
'calculation_details',
'old_calculation_amount',
'calculation_source'
];
@@ -62,7 +72,171 @@ class SpatialPlanning extends Model
return (float) ($this->area ?? $this->land_area ?? 0);
}
/**
* Get calculated retribution amount
* Priority: Manual calculation (new formula) > Active calculation (old system)
*/
public function getCalculatedRetributionAttribute(): float
{
try {
// PRIORITY 1: Use new manual formula (LUAS LAHAN × BCR × HARGA SATUAN)
$manualCalculation = $this->calculateRetributionManually();
// If manual calculation is valid (> 0), use it
if ($manualCalculation > 0) {
return $manualCalculation;
}
// PRIORITY 2: Fallback to active retribution calculation if exists
$activeCalculation = $this->activeRetributionCalculation;
if ($activeCalculation && $activeCalculation->retributionCalculation) {
return (float) $activeCalculation->retributionCalculation->retribution_amount;
}
// PRIORITY 3: Return 0 if nothing works
return 0.0;
} catch (\Exception $e) {
\Log::warning('Failed to calculate retribution for SpatialPlanning ID: ' . $this->id, [
'error' => $e->getMessage(),
'spatial_planning' => $this->toArray()
]);
return 0.0;
}
}
/**
* Manual calculation based on area and building function
* Formula: LUAS LAHAN × BCR × HARGA SATUAN
* NON USAHA: 16,000 per m2
* USAHA: 44,300 per m2
*/
private function calculateRetributionManually(): float
{
// Get land area (luas lahan)
$landArea = (float) ($this->land_area ?? 0);
// Get BCR (Building Coverage Ratio) - convert from percentage to decimal
$bcrPercentage = (float) ($this->site_bcr ?? 0);
$bcr = $bcrPercentage / 100; // Convert percentage to decimal (24.49% -> 0.2449)
if ($landArea <= 0 || $bcr <= 0) {
return 0.0;
}
// Determine if this is business (USAHA) or non-business (NON USAHA)
$isBusiness = $this->isBusinessType();
// Set unit price based on business type
$unitPrice = $isBusiness ? 44300 : 16000;
// Calculate: LUAS LAHAN × BCR (as decimal) × HARGA SATUAN
$calculatedAmount = $landArea * $bcr * $unitPrice;
return $calculatedAmount;
}
/**
* Determine if this spatial planning is for business purposes
*/
private function isBusinessType(): bool
{
$buildingFunction = strtolower($this->building_function ?? $this->activities ?? '');
// Business keywords
$businessKeywords = [
'usaha', 'dagang', 'perdagangan', 'komersial', 'commercial', 'bisnis', 'business',
'toko', 'warung', 'pasar', 'kios', 'mall', 'plaza', 'supermarket', 'department',
'hotel', 'resort', 'restoran', 'restaurant', 'cafe', 'kantor', 'perkantoran', 'office',
'industri', 'pabrik', 'gudang', 'warehouse', 'manufacturing', 'produksi',
'bengkel', 'workshop', 'showroom', 'dealer', 'apotek', 'pharmacy', 'klinik swasta',
'rumah sakit swasta', 'bank', 'atm', 'money changer', 'asuransi', 'leasing',
'rental', 'sewa', 'jasa', 'service', 'salon', 'spa', 'fitness', 'gym',
'tempat usaha', 'fungsi usaha', 'kegiatan usaha', 'bangunan usaha'
];
// Check if any business keyword is found
foreach ($businessKeywords as $keyword) {
if (str_contains($buildingFunction, $keyword)) {
return true;
}
}
// Non-business (default)
return false;
}
/**
* Get formatted retribution amount for display
*/
public function getFormattedRetributionAttribute(): string
{
$amount = $this->calculated_retribution;
return number_format($amount, 0, ',', '.');
}
/**
* Check if this is business type
*/
public function getIsBusinessTypeAttribute(): bool
{
return $this->isBusinessType();
}
/**
* Get calculation details for transparency
*/
public function getCalculationDetailsAttribute(): array
{
$landArea = (float) ($this->land_area ?? 0);
$bcrPercentage = (float) ($this->site_bcr ?? 0);
$bcr = $bcrPercentage / 100; // Convert to decimal
$isBusiness = $this->isBusinessType();
$unitPrice = $isBusiness ? 44300 : 16000;
$calculatedAmount = $landArea * $bcr * $unitPrice;
return [
'formula' => 'LUAS LAHAN × BCR (decimal) × HARGA SATUAN',
'land_area' => $landArea,
'bcr_percentage' => $bcrPercentage,
'bcr_decimal' => $bcr,
'business_type' => $isBusiness ? 'USAHA' : 'NON USAHA',
'unit_price' => $unitPrice,
'calculation' => "{$landArea} × {$bcr} × {$unitPrice}",
'result' => $calculatedAmount,
'building_function' => $this->building_function ?? $this->activities ?? 'N/A'
];
}
/**
* Get old calculation amount from database
*/
public function getOldCalculationAmountAttribute(): float
{
$activeCalculation = $this->activeRetributionCalculation;
if ($activeCalculation && $activeCalculation->retributionCalculation) {
return (float) $activeCalculation->retributionCalculation->retribution_amount;
}
return 0.0;
}
/**
* Get calculation source info
*/
public function getCalculationSourceAttribute(): string
{
$manualCalculation = $this->calculateRetributionManually();
$hasActiveCalculation = $this->hasActiveRetributionCalculation();
if ($manualCalculation > 0) {
return $hasActiveCalculation ? 'NEW_FORMULA' : 'NEW_FORMULA_ONLY';
} elseif ($hasActiveCalculation) {
return 'OLD_DATABASE';
}
return 'NONE';
}
}

View File

@@ -539,14 +539,16 @@ class ServiceGoogleSheet
}
/**
* Get count of spatial plannings that have active retribution calculations
* Get count of spatial plannings that can be calculated with new formula
*/
public function getSpatialPlanningWithCalculationCount(): int
{
try {
return SpatialPlanning::whereHas('retributionCalculations', function ($query) {
$query->where('is_active', true);
})->count();
// Count spatial plannings that have valid data and are not yet issued (is_terbit = false)
return SpatialPlanning::where('land_area', '>', 0)
->where('site_bcr', '>', 0)
->where('is_terbit', false)
->count();
} catch (\Exception $e) {
Log::error("Error getting spatial planning with calculation count", ['error' => $e->getMessage()]);
return 0;
@@ -554,25 +556,29 @@ class ServiceGoogleSheet
}
/**
* Get total sum of retribution amounts for spatial plannings with active calculations
* Get total sum of retribution amounts using new calculation formula
*/
public function getSpatialPlanningCalculationSum(): float
{
try {
// Get all spatial plannings that have active calculations
$spatialPlannings = SpatialPlanning::whereHas('retributionCalculations', function ($query) {
$query->where('is_active', true);
})->with(['retributionCalculations.retributionCalculation'])
->get();
// Get spatial plannings that are not yet issued (is_terbit = false) and have valid data
$spatialPlannings = SpatialPlanning::where('land_area', '>', 0)
->where('site_bcr', '>', 0)
->where('is_terbit', false)
->get();
$totalSum = 0;
foreach ($spatialPlannings as $spatialPlanning) {
$activeCalculation = $spatialPlanning->activeRetributionCalculation;
if ($activeCalculation && $activeCalculation->retributionCalculation) {
$totalSum += $activeCalculation->retributionCalculation->retribution_amount;
}
// Use new calculation formula: LUAS LAHAN × BCR × HARGA SATUAN
$totalSum += $spatialPlanning->calculated_retribution;
}
Log::info("Spatial Planning Calculation Sum (is_terbit = false only)", [
'total_records' => $spatialPlannings->count(),
'total_sum' => $totalSum,
'filtered_by' => 'is_terbit = false'
]);
return (float) $totalSum;
} catch (\Exception $e) {
Log::error("Error getting spatial planning calculation sum", ['error' => $e->getMessage()]);