add spatial plannings retribution calculations
This commit is contained in:
@@ -13,15 +13,17 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('building_functions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('code')->unique();
|
||||
$table->string('name');
|
||||
$table->string('description')->nullable();
|
||||
$table->unsignedBigInteger('parent_id')->nullable();
|
||||
$table->string('code')->unique()->comment('Kode unik fungsi bangunan');
|
||||
$table->string('name', 255)->comment('Nama fungsi bangunan');
|
||||
$table->text('description')->nullable()->comment('Deskripsi detail fungsi bangunan');
|
||||
$table->unsignedBigInteger('parent_id')->nullable()->comment('ID parent untuk hierarki');
|
||||
$table->foreign('parent_id')->references('id')->on('building_functions')->onDelete('cascade');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->integer('level')->default(0);
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->index(['parent_id', 'is_active']);
|
||||
$table->integer('level')->default(0)->comment('Level hierarki (0=root, 1=child, dst)');
|
||||
$table->integer('sort_order')->default(0)->comment('Urutan tampilan');
|
||||
$table->decimal('base_tariff', 15, 2)->nullable()->comment('Tarif dasar per m2');
|
||||
|
||||
// Indexes untuk performa
|
||||
$table->index(['parent_id', 'level']);
|
||||
$table->index(['level', 'sort_order']);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('floor_height_indices', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('floor_number')->comment('Nomor lantai');
|
||||
$table->decimal('ip_ketinggian', 10, 6)->comment('Indeks ketinggian per lantai');
|
||||
$table->text('description')->nullable()->comment('Deskripsi indeks ketinggian');
|
||||
$table->timestamps();
|
||||
|
||||
// Unique constraint untuk floor_number
|
||||
$table->unique('floor_number');
|
||||
$table->index('floor_number');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('floor_height_indices');
|
||||
}
|
||||
};
|
||||
@@ -14,13 +14,12 @@ return new class extends Migration
|
||||
Schema::create('building_function_parameters', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
|
||||
$table->decimal('fungsi_bangunan', 10, 6)->comment('Parameter fungsi bangunan');
|
||||
$table->decimal('ip_permanen', 10, 6)->comment('Parameter IP permanen');
|
||||
$table->decimal('ip_kompleksitas', 10, 6)->comment('Parameter IP kompleksitas');
|
||||
$table->decimal('ip_ketinggian', 10, 6)->comment('Parameter IP ketinggian');
|
||||
$table->decimal('indeks_lokalitas', 10, 6)->comment('Parameter indeks lokalitas');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->text('notes')->nullable();
|
||||
$table->decimal('fungsi_bangunan', 10, 6)->nullable()->comment('Parameter fungsi bangunan');
|
||||
$table->decimal('ip_permanen', 10, 6)->nullable()->comment('Parameter IP permanen');
|
||||
$table->decimal('ip_kompleksitas', 10, 6)->nullable()->comment('Parameter IP kompleksitas');
|
||||
$table->decimal('indeks_lokalitas', 10, 6)->nullable()->comment('Parameter indeks lokalitas');
|
||||
$table->decimal('asumsi_prasarana', 8, 6)->nullable()->comment('Parameter asumsi prasarana untuk perhitungan retribusi');
|
||||
$table->decimal('koefisien_dasar', 15, 6)->nullable()->comment('Koefisien dasar perhitungan');
|
||||
$table->timestamps();
|
||||
|
||||
// Unique constraint untuk 1:1 relationship
|
||||
|
||||
@@ -14,14 +14,13 @@ return new class extends Migration
|
||||
Schema::create('retribution_formulas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
|
||||
$table->string('name')->comment('Nama formula');
|
||||
$table->string('name', 255)->comment('Nama formula');
|
||||
$table->integer('floor_number')->comment('Nomor lantai (1, 2, 3, dst, 0=semua lantai)');
|
||||
$table->text('formula_expression')->comment('Rumus matematika untuk perhitungan');
|
||||
$table->text('description')->nullable()->comment('Deskripsi formula');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
|
||||
// Unique constraint untuk 1:1 relationship
|
||||
$table->unique('building_function_id');
|
||||
// Indexes untuk performa
|
||||
$table->index(['building_function_id', 'floor_number'], 'idx_building_floor');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('retribution_calculations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('spatial_planning_id')->constrained('spatial_plannings')->onDelete('cascade');
|
||||
$table->foreignId('retribution_formula_id')->nullable()->constrained('retribution_formulas')->onDelete('set null');
|
||||
$table->foreignId('detected_building_function_id')->nullable()->constrained('building_functions')->onDelete('set null');
|
||||
$table->decimal('luas_bangunan', 15, 6)->comment('Luas bangunan yang digunakan dalam perhitungan');
|
||||
$table->json('used_parameters')->nullable()->comment('Parameter yang digunakan dalam perhitungan');
|
||||
$table->string('used_formula')->nullable()->comment('Formula yang digunakan dalam perhitungan');
|
||||
$table->decimal('calculation_result', 15, 6)->nullable()->comment('Hasil perhitungan retribusi');
|
||||
$table->datetime('calculation_date')->nullable()->comment('Tanggal perhitungan');
|
||||
$table->foreignId('calculated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
// Unique constraint untuk 1:1 relationship dengan spatial_plannings
|
||||
$table->unique('spatial_planning_id');
|
||||
|
||||
// Index untuk performa query dengan nama yang lebih pendek
|
||||
$table->index(['retribution_formula_id', 'calculation_date'], 'idx_formula_calc_date');
|
||||
$table->index(['detected_building_function_id', 'calculation_date'], 'idx_building_func_calc_date');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('retribution_calculations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('retribution_proposals', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('spatial_planning_id')->nullable()->constrained('spatial_plannings')->onDelete('cascade');
|
||||
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
|
||||
$table->foreignId('retribution_formula_id')->constrained('retribution_formulas')->onDelete('cascade');
|
||||
$table->string('proposal_number')->unique()->comment('Nomor usulan retribusi');
|
||||
$table->integer('floor_number')->comment('Nomor lantai (1, 2, 3, dst)');
|
||||
$table->decimal('floor_area', 15, 6)->comment('Luas lantai ini (m2)');
|
||||
$table->decimal('total_building_area', 15, 6)->comment('Total luas bangunan (m2)');
|
||||
$table->decimal('ip_ketinggian', 10, 6)->comment('IP ketinggian untuk lantai ini');
|
||||
$table->decimal('floor_retribution_amount', 15, 2)->comment('Jumlah retribusi untuk lantai ini');
|
||||
$table->decimal('total_retribution_amount', 15, 2)->comment('Total retribusi keseluruhan');
|
||||
$table->json('calculation_parameters')->nullable()->comment('Parameter yang digunakan dalam perhitungan');
|
||||
$table->json('calculation_breakdown')->nullable()->comment('Breakdown detail perhitungan');
|
||||
$table->text('notes')->nullable()->comment('Catatan tambahan');
|
||||
$table->datetime('calculated_at')->comment('Waktu perhitungan dilakukan');
|
||||
$table->timestamps();
|
||||
|
||||
// Indexes untuk performa
|
||||
$table->index(['spatial_planning_id', 'floor_number'], 'idx_spatial_floor');
|
||||
$table->index(['building_function_id'], 'idx_function');
|
||||
$table->index('calculated_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('retribution_proposals');
|
||||
}
|
||||
};
|
||||
148
database/seeders/BuildingFunctionParameterSeeder.php
Normal file
148
database/seeders/BuildingFunctionParameterSeeder.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\BuildingFunction;
|
||||
use App\Models\BuildingFunctionParameter;
|
||||
|
||||
class BuildingFunctionParameterSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$parameters = [
|
||||
// Fungsi Keagamaan
|
||||
[
|
||||
'building_function_code' => 'AGAMA',
|
||||
'fungsi_bangunan' => 0,
|
||||
'ip_permanen' => 0,
|
||||
'ip_kompleksitas' => 0,
|
||||
'indeks_lokalitas' => 0,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk fungsi keagamaan'
|
||||
],
|
||||
|
||||
// Fungsi Sosial Budaya
|
||||
[
|
||||
'building_function_code' => 'SOSIAL_BUDAYA',
|
||||
'fungsi_bangunan' => 0.3,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.3,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk fungsi sosial budaya'
|
||||
],
|
||||
|
||||
// Fungsi Campuran Kecil
|
||||
[
|
||||
'building_function_code' => 'CAMPURAN_KECIL',
|
||||
'fungsi_bangunan' => 0.6,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.5,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk fungsi campuran kecil'
|
||||
],
|
||||
|
||||
// Fungsi Campuran Besar
|
||||
[
|
||||
'building_function_code' => 'CAMPURAN_BESAR',
|
||||
'fungsi_bangunan' => 0.8,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.5,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk fungsi campuran besar'
|
||||
],
|
||||
|
||||
// UMKM (Usaha Kecil)
|
||||
[
|
||||
'building_function_code' => 'USAHA_KECIL',
|
||||
'fungsi_bangunan' => 0.5,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.4,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk UMKM'
|
||||
],
|
||||
|
||||
// Usaha Besar (Non-Mikro)
|
||||
[
|
||||
'building_function_code' => 'USAHA_BESAR',
|
||||
'fungsi_bangunan' => 0.7,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.5,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk usaha besar (non-mikro)'
|
||||
],
|
||||
|
||||
// Hunian Sederhana < 100 m2
|
||||
[
|
||||
'building_function_code' => 'HUNIAN_KECIL',
|
||||
'fungsi_bangunan' => 0.15,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.3,
|
||||
'indeks_lokalitas' => 0.4,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk hunian sederhana < 100 m2'
|
||||
],
|
||||
|
||||
// Hunian Sederhana > 100 m2
|
||||
[
|
||||
'building_function_code' => 'HUNIAN_BESAR',
|
||||
'fungsi_bangunan' => 0.17,
|
||||
'ip_permanen' => 0.4,
|
||||
'ip_kompleksitas' => 0.6,
|
||||
'indeks_lokalitas' => 0.4,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk hunian sederhana > 100 m2'
|
||||
],
|
||||
|
||||
// MBR (Masyarakat Berpenghasilan Rendah)
|
||||
[
|
||||
'building_function_code' => 'HUNIAN_MBR',
|
||||
'fungsi_bangunan' => 0,
|
||||
'ip_permanen' => 0,
|
||||
'ip_kompleksitas' => 0,
|
||||
'indeks_lokalitas' => 0,
|
||||
'asumsi_prasarana' => 0.5,
|
||||
'is_active' => true,
|
||||
'notes' => 'Parameter untuk MBR (tarif lebih rendah)'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($parameters as $parameterData) {
|
||||
$buildingFunction = BuildingFunction::where('code', $parameterData['building_function_code'])->first();
|
||||
|
||||
if ($buildingFunction) {
|
||||
// Remove building_function_code from parameter data
|
||||
$parameterData['building_function_id'] = $buildingFunction->id;
|
||||
unset($parameterData['building_function_code']);
|
||||
|
||||
BuildingFunctionParameter::updateOrCreate(
|
||||
['building_function_id' => $buildingFunction->id],
|
||||
$parameterData
|
||||
);
|
||||
|
||||
$this->command->info("Created/Updated parameter for: {$buildingFunction->name}");
|
||||
} else {
|
||||
$this->command->warn("Building function not found: {$parameterData['building_function_code']}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info('Building Function Parameters seeding completed!');
|
||||
}
|
||||
}
|
||||
@@ -5,141 +5,152 @@ namespace Database\Seeders;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\BuildingFunction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class BuildingFunctionSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$now = Carbon::now();
|
||||
|
||||
// 1. Building Functions Data - Fixed structure with proper parent-child relationships
|
||||
$buildingFunctions = [
|
||||
[
|
||||
'code' => 'AGAMA',
|
||||
'name' => 'Fungsi Keagamaan',
|
||||
'description' => 'Fungsi Keagamaan',
|
||||
'parent_id' => null,
|
||||
'is_active' => true,
|
||||
'level' => 0,
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'SOSIAL_BUDAYA',
|
||||
'name' => 'Fungsi Sosial Budaya',
|
||||
'description' => 'Fungsi Sosial Budaya',
|
||||
'parent_id' => null,
|
||||
'is_active' => true,
|
||||
'level' => 0,
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'code' => 'CAMPURAN',
|
||||
'name' => 'Fungsi Campuran',
|
||||
'description' => 'Fungsi Campuran',
|
||||
'parent_id' => null,
|
||||
'is_active' => true,
|
||||
'level' => 0,
|
||||
'sort_order' => 3,
|
||||
'children' => [
|
||||
[
|
||||
'code' => 'CAMPURAN_KECIL',
|
||||
'name' => 'Fungsi Campuran Kecil',
|
||||
'description' => 'Fungsi Campuran Kecil',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'CAMPURAN_BESAR',
|
||||
'name' => 'Fungsi Campuran Besar',
|
||||
'description' => 'Fungsi Campuran Besar',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 2,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'code' => 'USAHA',
|
||||
'name' => 'Fungsi Usaha',
|
||||
'description' => 'Fungsi Usaha',
|
||||
'parent_id' => null,
|
||||
'is_active' => true,
|
||||
'level' => 0,
|
||||
'sort_order' => 4,
|
||||
'children' => [
|
||||
[
|
||||
'code' => 'USAHA_KECIL',
|
||||
'name' => 'UMKM',
|
||||
'description' => 'Fungsi Usaha Kecil',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'USAHA_BESAR',
|
||||
'name' => 'Usaha Besar (Non-Mikro)',
|
||||
'description' => 'Fungsi Usaha Besar',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 2,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'code' => 'HUNIAN',
|
||||
'name' => 'Fungsi Hunian',
|
||||
'description' => 'Fungsi Hunian',
|
||||
'parent_id' => null,
|
||||
'is_active' => true,
|
||||
'level' => 0,
|
||||
'sort_order' => 5,
|
||||
'children' => [
|
||||
[
|
||||
'code' => 'HUNIAN_KECIL',
|
||||
'name' => 'Sederhana < 100 m2',
|
||||
'description' => 'Sederhana < 100 m2',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'HUNIAN_BESAR',
|
||||
'name' => 'Sederhana > 100 m2',
|
||||
'description' => 'Sederhana > 100 m2',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'code' => 'HUNIAN_MBR',
|
||||
'name' => 'MBR',
|
||||
'description' => 'Rumah Tinggal Deret (MBR) dan Rumah Tinggal Tunggal (MBR)',
|
||||
'is_active' => true,
|
||||
'level' => 1,
|
||||
'sort_order' => 3,
|
||||
]
|
||||
]
|
||||
]
|
||||
// Parent Categories
|
||||
['id' => 1, 'code' => 'AGAMA', 'name' => 'Bangunan Keagamaan', 'parent_id' => null, 'level' => 0, 'sort_order' => 1, 'base_tariff' => 0.00],
|
||||
['id' => 2, 'code' => 'SOSIAL_BUDAYA', 'name' => 'Bangunan Sosial Budaya', 'parent_id' => null, 'level' => 0, 'sort_order' => 2, 'base_tariff' => 30000.00],
|
||||
['id' => 3, 'code' => 'CAMPURAN', 'name' => 'Bangunan Campuran', 'parent_id' => null, 'level' => 0, 'sort_order' => 3, 'base_tariff' => 70000.00],
|
||||
['id' => 4, 'code' => 'USAHA', 'name' => 'Bangunan Usaha', 'parent_id' => null, 'level' => 0, 'sort_order' => 4, 'base_tariff' => 60000.00],
|
||||
['id' => 5, 'code' => 'HUNIAN', 'name' => 'Bangunan Hunian', 'parent_id' => null, 'level' => 0, 'sort_order' => 5, 'base_tariff' => 40000.00],
|
||||
|
||||
// Sub Categories - Campuran
|
||||
['id' => 6, 'code' => 'CAMPURAN_KECIL', 'name' => 'Bangunan Campuran Kecil', 'parent_id' => 3, 'level' => 1, 'sort_order' => 1, 'base_tariff' => 60000.00],
|
||||
['id' => 7, 'code' => 'CAMPURAN_BESAR', 'name' => 'Bangunan Campuran Besar', 'parent_id' => 3, 'level' => 1, 'sort_order' => 2, 'base_tariff' => 80000.00],
|
||||
|
||||
// Sub Categories - Usaha
|
||||
['id' => 8, 'code' => 'USAHA_KECIL', 'name' => 'Bangunan Usaha Kecil (UMKM)', 'parent_id' => 4, 'level' => 1, 'sort_order' => 1, 'base_tariff' => 50000.00],
|
||||
['id' => 9, 'code' => 'USAHA_BESAR', 'name' => 'Bangunan Usaha Besar', 'parent_id' => 4, 'level' => 1, 'sort_order' => 2, 'base_tariff' => 70000.00],
|
||||
|
||||
// Sub Categories - Hunian
|
||||
['id' => 10, 'code' => 'HUNIAN_SEDERHANA', 'name' => 'Hunian Sederhana', 'parent_id' => 5, 'level' => 1, 'sort_order' => 1, 'base_tariff' => 35000.00],
|
||||
['id' => 11, 'code' => 'HUNIAN_TIDAK_SEDERHANA', 'name' => 'Hunian Tidak Sederhana', 'parent_id' => 5, 'level' => 1, 'sort_order' => 2, 'base_tariff' => 45000.00],
|
||||
['id' => 12, 'code' => 'MBR', 'name' => 'Hunian MBR (Masyarakat Berpenghasilan Rendah)', 'parent_id' => 5, 'level' => 1, 'sort_order' => 3, 'base_tariff' => 0.00],
|
||||
];
|
||||
|
||||
foreach ($buildingFunctions as $function) {
|
||||
$this->insertOrUpdateByCode($function);
|
||||
DB::table('building_functions')->updateOrInsert(
|
||||
['id' => $function['id']],
|
||||
array_merge($function, [
|
||||
'description' => 'Deskripsi untuk ' . $function['name'],
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function insertOrUpdateByCode(array $data, ?int $parentId = null): void
|
||||
{
|
||||
$children = $data['children'] ?? [];
|
||||
unset($data['children']);
|
||||
// 2. Building Function Parameters
|
||||
$parameters = [
|
||||
// AGAMA - No charge
|
||||
['building_function_id' => 1, 'fungsi_bangunan' => 0, 'ip_permanen' => 0, 'ip_kompleksitas' => 0, 'indeks_lokalitas' => 0, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// SOSIAL_BUDAYA
|
||||
['building_function_id' => 2, 'fungsi_bangunan' => 0.3, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.3, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// CAMPURAN_KECIL
|
||||
['building_function_id' => 6, 'fungsi_bangunan' => 0.6, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.5, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// CAMPURAN_BESAR
|
||||
['building_function_id' => 7, 'fungsi_bangunan' => 0.8, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.5, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// USAHA_KECIL
|
||||
['building_function_id' => 8, 'fungsi_bangunan' => 0.5, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.4, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// USAHA_BESAR
|
||||
['building_function_id' => 9, 'fungsi_bangunan' => 0.7, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.5, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// HUNIAN_SEDERHANA
|
||||
['building_function_id' => 10, 'fungsi_bangunan' => 0.15, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.3, 'indeks_lokalitas' => 0.4, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// HUNIAN_TIDAK_SEDERHANA
|
||||
['building_function_id' => 11, 'fungsi_bangunan' => 0.17, 'ip_permanen' => 0.4, 'ip_kompleksitas' => 0.6, 'indeks_lokalitas' => 0.4, 'asumsi_prasarana' => 0.5],
|
||||
|
||||
// MBR - No charge
|
||||
['building_function_id' => 12, 'fungsi_bangunan' => 0, 'ip_permanen' => 0, 'ip_kompleksitas' => 0, 'indeks_lokalitas' => 0, 'asumsi_prasarana' => 0.5],
|
||||
];
|
||||
|
||||
$data['parent_id'] = $parentId;
|
||||
|
||||
// Insert or update by code
|
||||
$record = BuildingFunction::updateOrCreate(
|
||||
['code' => $data['code']],
|
||||
$data
|
||||
);
|
||||
|
||||
foreach ($children as $child) {
|
||||
$this->insertOrUpdateByCode($child, $record->id);
|
||||
foreach ($parameters as $param) {
|
||||
DB::table('building_function_parameters')->updateOrInsert(
|
||||
['building_function_id' => $param['building_function_id']],
|
||||
array_merge($param, [
|
||||
'koefisien_dasar' => 1.0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Floor Height Indices (IP Ketinggian per lantai)
|
||||
$floorHeightIndices = [
|
||||
['floor_number' => 1, 'ip_ketinggian' => 1.000000, 'description' => 'IP ketinggian untuk lantai 1'],
|
||||
['floor_number' => 2, 'ip_ketinggian' => 1.090000, 'description' => 'IP ketinggian untuk lantai 2'],
|
||||
['floor_number' => 3, 'ip_ketinggian' => 1.120000, 'description' => 'IP ketinggian untuk lantai 3'],
|
||||
['floor_number' => 4, 'ip_ketinggian' => 1.350000, 'description' => 'IP ketinggian untuk lantai 4'],
|
||||
['floor_number' => 5, 'ip_ketinggian' => 1.620000, 'description' => 'IP ketinggian untuk lantai 5'],
|
||||
['floor_number' => 6, 'ip_ketinggian' => 1.197000, 'description' => 'IP ketinggian untuk lantai 6'],
|
||||
];
|
||||
|
||||
foreach ($floorHeightIndices as $heightIndex) {
|
||||
DB::table('floor_height_indices')->updateOrInsert(
|
||||
['floor_number' => $heightIndex['floor_number']],
|
||||
array_merge($heightIndex, [
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// Formula Templates removed - not used in current system
|
||||
|
||||
// 5. Retribution Formulas per Floor (for child building functions only)
|
||||
$childBuildingFunctionIds = [1, 2, 6, 7, 8, 9, 10, 11, 12]; // All leaf nodes
|
||||
$formulaExpression = '(1 * luas_bangunan * (indeks_lokalitas * 70350 * ((fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian)))) * 1)) + (0.5 * (1 * luas_bangunan * (indeks_lokalitas * 70350 * ((fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian)))) * 1)))';
|
||||
|
||||
foreach ($childBuildingFunctionIds as $buildingFunctionId) {
|
||||
$buildingFunction = DB::table('building_functions')->where('id', $buildingFunctionId)->first();
|
||||
|
||||
if ($buildingFunction) {
|
||||
// Create formulas for floors 1-5
|
||||
for ($floorNumber = 1; $floorNumber <= 5; $floorNumber++) {
|
||||
DB::table('retribution_formulas')->updateOrInsert(
|
||||
[
|
||||
'building_function_id' => $buildingFunctionId,
|
||||
'floor_number' => $floorNumber
|
||||
],
|
||||
[
|
||||
'name' => "{$buildingFunction->name} - Lantai {$floorNumber}",
|
||||
'formula_expression' => $formulaExpression,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info('Building Function data seeded successfully!');
|
||||
$this->command->info('Building Function Parameters seeded successfully!');
|
||||
$this->command->info('Floor Height Indices seeded successfully!');
|
||||
$this->command->info('Retribution Formulas with IP Ketinggian seeded successfully!');
|
||||
|
||||
// Display summary
|
||||
$this->command->info('=== SUMMARY ===');
|
||||
$this->command->info('Parent Functions: ' . DB::table('building_functions')->whereNull('parent_id')->count());
|
||||
$this->command->info('Child Functions: ' . DB::table('building_functions')->whereNotNull('parent_id')->count());
|
||||
$this->command->info('Parameters: ' . DB::table('building_function_parameters')->count());
|
||||
$this->command->info('Floor Height Indices: ' . DB::table('floor_height_indices')->count());
|
||||
$this->command->info('Retribution Formulas: ' . DB::table('retribution_formulas')->count());
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,10 @@ class DatabaseSeeder extends Seeder
|
||||
UsersRoleMenuSeeder::class,
|
||||
GlobalSettingSeeder::class,
|
||||
BuildingFunctionSeeder::class,
|
||||
BuildingFunctionParameterSeeder::class,
|
||||
FloorHeightIndexSeeder::class,
|
||||
RetributionFormulaSeeder::class,
|
||||
RetributionProposalSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
62
database/seeders/FloorHeightIndexSeeder.php
Normal file
62
database/seeders/FloorHeightIndexSeeder.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\FloorHeightIndex;
|
||||
|
||||
class FloorHeightIndexSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$indices = [
|
||||
[
|
||||
'floor_number' => 1,
|
||||
'ip_ketinggian' => 1.000000,
|
||||
'description' => 'IP ketinggian untuk lantai 1'
|
||||
],
|
||||
[
|
||||
'floor_number' => 2,
|
||||
'ip_ketinggian' => 1.090000,
|
||||
'description' => 'IP ketinggian untuk lantai 2'
|
||||
],
|
||||
[
|
||||
'floor_number' => 3,
|
||||
'ip_ketinggian' => 1.120000,
|
||||
'description' => 'IP ketinggian untuk lantai 3'
|
||||
],
|
||||
[
|
||||
'floor_number' => 4,
|
||||
'ip_ketinggian' => 1.350000,
|
||||
'description' => 'IP ketinggian untuk lantai 4'
|
||||
],
|
||||
[
|
||||
'floor_number' => 5,
|
||||
'ip_ketinggian' => 1.620000,
|
||||
'description' => 'IP ketinggian untuk lantai 5'
|
||||
],
|
||||
[
|
||||
'floor_number' => 6,
|
||||
'ip_ketinggian' => 1.197000,
|
||||
'description' => 'IP ketinggian untuk lantai 6'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($indices as $index) {
|
||||
FloorHeightIndex::updateOrCreate(
|
||||
['floor_number' => $index['floor_number']],
|
||||
$index
|
||||
);
|
||||
}
|
||||
|
||||
$this->command->info('Floor Height Index seeding completed!');
|
||||
$this->command->info('IP Ketinggian values:');
|
||||
foreach ($indices as $index) {
|
||||
$this->command->info("Lantai {$index['floor_number']}: {$index['ip_ketinggian']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
74
database/seeders/RetributionFormulaSeeder.php
Normal file
74
database/seeders/RetributionFormulaSeeder.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\BuildingFunction;
|
||||
use App\Models\RetributionFormula;
|
||||
|
||||
class RetributionFormulaSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Get building function codes from the actual seeded data (child functions only)
|
||||
$buildingFunctionCodes = [
|
||||
'AGAMA',
|
||||
'SOSIAL_BUDAYA',
|
||||
'CAMPURAN_KECIL',
|
||||
'CAMPURAN_BESAR',
|
||||
'USAHA_KECIL',
|
||||
'USAHA_BESAR',
|
||||
'HUNIAN_SEDERHANA',
|
||||
'HUNIAN_TIDAK_SEDERHANA',
|
||||
'MBR'
|
||||
];
|
||||
|
||||
// Formula lengkap retribusi dengan perhitungan per lantai
|
||||
// Bagian 1: Perhitungan per lantai
|
||||
$floorCalculationFormula = "(fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian)))";
|
||||
|
||||
// Bagian 2: Formula retribusi lengkap
|
||||
$mainFormula = "(1 * luas_bangunan * (indeks_lokalitas * 70350 * ({$floorCalculationFormula}) * 1))";
|
||||
$additionalFormula = "(0.5 * (1 * luas_bangunan * (indeks_lokalitas * 70350 * ({$floorCalculationFormula}) * 1)))";
|
||||
$fullFormulaExpression = "{$mainFormula} + {$additionalFormula}";
|
||||
|
||||
// Buat formula untuk 5 lantai pertama saja
|
||||
for ($floorNumber = 1; $floorNumber <= 5; $floorNumber++) {
|
||||
foreach ($buildingFunctionCodes as $code) {
|
||||
$buildingFunction = BuildingFunction::where('code', $code)->first();
|
||||
|
||||
if ($buildingFunction) {
|
||||
$formulaName = "{$buildingFunction->name} - Lantai {$floorNumber}";
|
||||
|
||||
RetributionFormula::updateOrCreate(
|
||||
[
|
||||
'building_function_id' => $buildingFunction->id,
|
||||
'floor_number' => $floorNumber
|
||||
],
|
||||
[
|
||||
'name' => $formulaName,
|
||||
'formula_expression' => $fullFormulaExpression,
|
||||
'description' => "Formula retribusi untuk {$buildingFunction->name} dengan {$floorNumber} lantai. Formula: {$fullFormulaExpression}",
|
||||
'floor_number' => $floorNumber,
|
||||
'luas_bangunan_rate' => $buildingFunction->base_tariff ?? 50000,
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
$this->command->info("Created formula for: {$formulaName}");
|
||||
} else {
|
||||
$this->command->warn("Building function not found: {$code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info('Retribution Formulas seeding completed for 5 floors!');
|
||||
$this->command->info("Building functions processed: " . implode(', ', $buildingFunctionCodes));
|
||||
$this->command->info("Floor calculation formula: {$floorCalculationFormula}");
|
||||
$this->command->info("Full retribution formula: {$fullFormulaExpression}");
|
||||
}
|
||||
}
|
||||
159
database/seeders/RetributionProposalSeeder.php
Normal file
159
database/seeders/RetributionProposalSeeder.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\RetributionProposal;
|
||||
use App\Models\SpatialPlanning;
|
||||
use App\Models\BuildingFunction;
|
||||
use App\Models\RetributionFormula;
|
||||
use App\Models\FloorHeightIndex;
|
||||
use App\Models\BuildingFunctionParameter;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class RetributionProposalSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Get some sample data
|
||||
$spatialPlannings = SpatialPlanning::take(5)->get();
|
||||
$buildingFunctions = BuildingFunction::whereNotNull('parent_id')->get(); // Only child functions
|
||||
|
||||
if ($spatialPlannings->isEmpty() || $buildingFunctions->isEmpty()) {
|
||||
$this->command->warn('No spatial plannings or building functions found. Please seed them first.');
|
||||
return;
|
||||
}
|
||||
|
||||
$sampleProposals = [
|
||||
[
|
||||
'spatial_planning_id' => $spatialPlannings->first()?->id,
|
||||
'building_function_code' => 'HUNIAN_SEDERHANA',
|
||||
'floor_number' => 2,
|
||||
'floor_area' => 45666,
|
||||
'total_building_area' => 91332, // 2 floors
|
||||
'notes' => 'Sample calculation for Hunian Sederhana'
|
||||
],
|
||||
[
|
||||
'spatial_planning_id' => $spatialPlannings->skip(1)->first()?->id,
|
||||
'building_function_code' => 'USAHA_KECIL',
|
||||
'floor_number' => 1,
|
||||
'floor_area' => 150,
|
||||
'total_building_area' => 150,
|
||||
'notes' => 'Sample calculation for UMKM'
|
||||
],
|
||||
[
|
||||
'spatial_planning_id' => null, // Testing nullable spatial_planning_id
|
||||
'building_function_code' => 'CAMPURAN_KECIL',
|
||||
'floor_number' => 3,
|
||||
'floor_area' => 200,
|
||||
'total_building_area' => 600,
|
||||
'notes' => 'Sample calculation without spatial planning link'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($sampleProposals as $proposalData) {
|
||||
$buildingFunction = BuildingFunction::where('code', $proposalData['building_function_code'])->first();
|
||||
|
||||
if (!$buildingFunction) {
|
||||
$this->command->warn("Building function not found: {$proposalData['building_function_code']}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get parameters
|
||||
$parameters = BuildingFunctionParameter::where('building_function_id', $buildingFunction->id)->first();
|
||||
$floorHeightIndex = FloorHeightIndex::where('floor_number', $proposalData['floor_number'])->first();
|
||||
$retributionFormula = RetributionFormula::where('building_function_id', $buildingFunction->id)
|
||||
->where('floor_number', $proposalData['floor_number'])
|
||||
->first();
|
||||
|
||||
if (!$parameters || !$floorHeightIndex || !$retributionFormula) {
|
||||
$this->command->warn("Missing data for building function: {$buildingFunction->name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate retribution using the Excel formula
|
||||
$floorArea = $proposalData['floor_area'];
|
||||
$fungsi_bangunan = $parameters->fungsi_bangunan;
|
||||
$ip_permanen = $parameters->ip_permanen;
|
||||
$ip_kompleksitas = $parameters->ip_kompleksitas;
|
||||
$ip_ketinggian = $floorHeightIndex->ip_ketinggian;
|
||||
$indeks_lokalitas = $parameters->indeks_lokalitas;
|
||||
$base_value = 70350;
|
||||
$additional_factor = 0.5;
|
||||
|
||||
// Step 1: Calculate H13 (floor coefficient)
|
||||
$h13 = $fungsi_bangunan * ($ip_permanen + $ip_kompleksitas + (0.5 * $ip_ketinggian));
|
||||
|
||||
// Step 2: Main calculation
|
||||
$main_calculation = 1 * $floorArea * ($indeks_lokalitas * $base_value * $h13 * 1);
|
||||
|
||||
// Step 3: Additional (50%)
|
||||
$additional_calculation = $additional_factor * $main_calculation;
|
||||
|
||||
// Step 4: Total
|
||||
$total_retribution = $main_calculation + $additional_calculation;
|
||||
|
||||
// Prepare calculation parameters and breakdown
|
||||
$calculationParameters = [
|
||||
'fungsi_bangunan' => $fungsi_bangunan,
|
||||
'ip_permanen' => $ip_permanen,
|
||||
'ip_kompleksitas' => $ip_kompleksitas,
|
||||
'ip_ketinggian' => $ip_ketinggian,
|
||||
'indeks_lokalitas' => $indeks_lokalitas,
|
||||
'base_value' => $base_value,
|
||||
'additional_factor' => $additional_factor,
|
||||
'floor_area' => $floorArea
|
||||
];
|
||||
|
||||
$calculationBreakdown = [
|
||||
'h13_calculation' => [
|
||||
'formula' => 'fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian))',
|
||||
'calculation' => "{$fungsi_bangunan} * ({$ip_permanen} + {$ip_kompleksitas} + (0.5 * {$ip_ketinggian}))",
|
||||
'result' => $h13
|
||||
],
|
||||
'main_calculation' => [
|
||||
'formula' => '1 * floor_area * (indeks_lokalitas * base_value * h13 * 1)',
|
||||
'calculation' => "1 * {$floorArea} * ({$indeks_lokalitas} * {$base_value} * {$h13} * 1)",
|
||||
'result' => $main_calculation
|
||||
],
|
||||
'additional_calculation' => [
|
||||
'formula' => 'additional_factor * main_calculation',
|
||||
'calculation' => "{$additional_factor} * {$main_calculation}",
|
||||
'result' => $additional_calculation
|
||||
],
|
||||
'total_calculation' => [
|
||||
'formula' => 'main_calculation + additional_calculation',
|
||||
'calculation' => "{$main_calculation} + {$additional_calculation}",
|
||||
'result' => $total_retribution
|
||||
]
|
||||
];
|
||||
|
||||
// Create the proposal
|
||||
RetributionProposal::create([
|
||||
'spatial_planning_id' => $proposalData['spatial_planning_id'],
|
||||
'building_function_id' => $buildingFunction->id,
|
||||
'retribution_formula_id' => $retributionFormula->id,
|
||||
'floor_number' => $proposalData['floor_number'],
|
||||
'floor_area' => $proposalData['floor_area'],
|
||||
'total_building_area' => $proposalData['total_building_area'],
|
||||
'ip_ketinggian' => $ip_ketinggian,
|
||||
'floor_retribution_amount' => $total_retribution,
|
||||
'total_retribution_amount' => $total_retribution, // For single floor, same as floor amount
|
||||
'calculation_parameters' => $calculationParameters,
|
||||
'calculation_breakdown' => $calculationBreakdown,
|
||||
'notes' => $proposalData['notes'],
|
||||
'calculated_at' => Carbon::now()
|
||||
]);
|
||||
|
||||
$this->command->info("Created retribution proposal for: {$buildingFunction->name} - Floor {$proposalData['floor_number']}");
|
||||
$this->command->info(" Amount: Rp " . number_format($total_retribution, 0, ',', '.'));
|
||||
}
|
||||
|
||||
$this->command->info('Retribution Proposal seeding completed!');
|
||||
$this->command->info('Total proposals created: ' . RetributionProposal::count());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user