null, 'master_kabupaten_kota' => 'provinsi_id', 'master_kecamatan' => 'kabupaten_kota_id', 'master_desa_kelurahan' => 'kecamatan_id', ] as $name => $parent) { Schema::create($name, function (Blueprint $table) use ($parent) { $table->id(); $table->string('nama'); $table->text('path')->nullable(); if ($parent) { $table->integer($parent); } $table->softDeletes(); }); DB::table($name)->insert(['id' => 1, 'nama' => 'Wilayah'] + ($parent ? [$parent => 1] : [])); } Schema::create('master_komoditas', function (Blueprint $table) { $table->id(); $table->string('nama'); }); foreach (['petani', 'profiles'] as $name) { Schema::create($name, function (Blueprint $table) { $table->id(); $table->string('nama'); $table->softDeletes(); }); } foreach (['map_lahan', 'map_tanaman'] as $name) { Schema::create($name, function (Blueprint $table) use ($name) { $table->id(); $table->integer('komoditas_id'); $table->integer('umur_tanaman')->nullable(); foreach (['serangan_hama', 'penyakit_tanaman', 'jenis_hama', 'jenis_penyakit'] as $column) { $table->text($column)->nullable(); } if ($name === 'map_lahan') { $table->integer('petani_id')->nullable(); $table->integer('profile_id')->nullable(); $table->integer('desa_kelurahan_id'); $table->text('path'); $table->float('luas_lahan'); $table->integer('jumlah_tanaman'); } else { $table->integer('lahan_id'); } $table->timestamps(); $table->softDeletes(); }); } Schema::create('praktik_budidaya', function (Blueprint $table) { $table->id(); $table->integer('petani_id'); $table->integer('frekuensi_pemangkasan_utama'); $table->integer('pemberian_pupuk'); $table->text('kebiasaan_konservasi_tanah'); $table->timestamps(); $table->softDeletes(); }); DB::table('master_komoditas')->insert(['id' => 1, 'nama' => 'Kakao']); DB::table('petani')->insert(['id' => 1, 'nama' => 'Petani A']); DB::table('profiles')->insert(['id' => 1, 'nama' => 'Pemilik B']); $land = [ 'komoditas_id' => 1, 'desa_kelurahan_id' => 1, 'luas_lahan' => 1.5, 'jumlah_tanaman' => 50, 'umur_tanaman' => 8, 'path' => '[[[-8.1,121.1],[-8.2,121.1],[-8.1,121.2]]]', 'jenis_hama' => '["Penggerek buah"]', ]; DB::table('map_lahan')->insert($land + ['id' => 1, 'petani_id' => 1]); DB::table('map_lahan')->insert($land + ['id' => 2, 'profile_id' => 1]); DB::table('map_lahan')->insert($land + ['id' => 3, 'petani_id' => 1, 'deleted_at' => '2026-01-01']); DB::table('map_tanaman')->insert([ 'lahan_id' => 1, 'komoditas_id' => 1, 'umur_tanaman' => 4, 'jenis_penyakit' => '["Busuk buah"]', ]); foreach ([ ['2026-01-01', 1, null], ['2026-02-01', 4, null], ['2026-03-01', 5, '2026-03-02'], ] as [$updatedAt, $value, $deletedAt]) { DB::table('praktik_budidaya')->insert([ 'petani_id' => 1, 'frekuensi_pemangkasan_utama' => $value, 'pemberian_pupuk' => $value, 'kebiasaan_konservasi_tanah' => '["Menumpuk daun kering/sisa tanaman di sekitar pohon"]', 'updated_at' => $updatedAt, 'deleted_at' => $deletedAt, ]); } $controller = app(SummaryController::class); $method = new ReflectionMethod($controller, 'productionMapLevelData'); $this->mapLevels = fn (array $filters = []) => $method->invoke($controller, new Request($filters)); }); test('land map combines plants and OPT with the latest active cultivation practice', function () { $lands = collect(($this->mapLevels)()['lahan'])->keyBy('id'); expect($lands->keys()->all())->toBe([1, 2]); expect($lands[1])->toMatchArray([ 'nama_petani' => 'Petani A', 'luas' => 1.5, 'komoditas' => [['nama' => 'Kakao', 'jumlah' => 1, 'umur' => 4.0]], 'opt' => ['Busuk Buah', 'Penggerek Buah'], 'pemangkasan' => FrekuensiPemangkasanUtama::DUA_KALI_TAHUN->label(), 'pemupukan' => PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT->label(), 'praktik_baik' => ['Menumpuk daun kering/sisa tanaman di sekitar pohon'], ]); expect($lands[2])->toMatchArray([ 'nama_petani' => 'Pemilik B', 'komoditas' => [['nama' => 'Kakao', 'jumlah' => 50, 'umur' => 8.0]], 'pemangkasan' => 'Belum ada data', 'pemupukan' => 'Belum ada data', 'praktik_baik' => [], ]); }); test('combined map respects region and commodity filters', function () { expect(($this->mapLevels)(['desaKelurahan_id' => 1, 'komoditas_id' => 1])['lahan'])->toHaveCount(2); expect(($this->mapLevels)(['desaKelurahan_id' => 99])['lahan'])->toBe([]); expect(($this->mapLevels)(['komoditas_id' => 99])['lahan'])->toBe([]); });