diff --git a/app/Enums/Bumdes/AsetProduktifBumdes.php b/app/Enums/Bumdes/AsetProduktifBumdes.php new file mode 100644 index 0000000..020fe59 --- /dev/null +++ b/app/Enums/Bumdes/AsetProduktifBumdes.php @@ -0,0 +1,30 @@ + 'Bangunan / kantor', + self::GUDANG_PENYIMPANAN => 'Gudang penyimpanan', + self::KENDARAAN_OPERASIONAL_ANGKUT => 'Kendaraan operasional/angkut', + self::LAHAN_USAHA => 'Lahan usaha', + self::ALAT_PENGOLAHAN_KOMODITAS => 'Alat pengolahan komoditas', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/BentukProdukBumdes.php b/app/Enums/Bumdes/BentukProdukBumdes.php new file mode 100644 index 0000000..186a55e --- /dev/null +++ b/app/Enums/Bumdes/BentukProdukBumdes.php @@ -0,0 +1,26 @@ + 'Buah segar hasil petik', + self::BIJI_BASAH => 'Biji basah', + self::BIJI_KERING => 'Biji kering/biji kupas', + self::TERFERMENTASI => 'Terfermentasi', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/CakupanLayananBumdes.php b/app/Enums/Bumdes/CakupanLayananBumdes.php new file mode 100644 index 0000000..9b635d8 --- /dev/null +++ b/app/Enums/Bumdes/CakupanLayananBumdes.php @@ -0,0 +1,22 @@ + 'Hanya melayani desa sendiri', + self::DESA_LAIN => 'Melayani desa sendiri dan desa lain', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/DukunganDesaBumdes.php b/app/Enums/Bumdes/DukunganDesaBumdes.php new file mode 100644 index 0000000..30feec9 --- /dev/null +++ b/app/Enums/Bumdes/DukunganDesaBumdes.php @@ -0,0 +1,28 @@ + 'Pendampingan teknis', + self::REGULASI_PERDES => 'Regulasi/Perdes pendukung', + self::FASILITASI_JEJARING => 'Fasilitasi jejaring/kemitraan', + self::TIDAK_ADA => 'Tidak ada dukungan khusus', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/FormatPembukuanBumdes.php b/app/Enums/Bumdes/FormatPembukuanBumdes.php new file mode 100644 index 0000000..d2686cc --- /dev/null +++ b/app/Enums/Bumdes/FormatPembukuanBumdes.php @@ -0,0 +1,26 @@ + 'Manual/buku kas', + self::DIGITAL_EXCEL => 'Digital/file Excel', + self::APLIKASI => 'Aplikasi keuangan khusus', + self::TIDAK_ADA => 'Tidak ada', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/HambatanUsahaBumdes.php b/app/Enums/Bumdes/HambatanUsahaBumdes.php new file mode 100644 index 0000000..2fa7e64 --- /dev/null +++ b/app/Enums/Bumdes/HambatanUsahaBumdes.php @@ -0,0 +1,32 @@ + 'Keterbatasan modal', + self::SDM => 'Keterbatasan SDM/kapasitas pengurus', + self::INFRASTRUKTUR => 'Keterbatasan infrastruktur/alat', + self::PASOKAN => 'Kesulitan mendapat pasokan konsisten dari produsen', + self::PERSAINGAN => 'Persaingan dengan tengkulak/pedagang', + self::AKSES_PASAR => 'Belum ada akses ke offtaker/pasar premium', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/HubunganPemasokBumdes.php b/app/Enums/Bumdes/HubunganPemasokBumdes.php new file mode 100644 index 0000000..e130ea9 --- /dev/null +++ b/app/Enums/Bumdes/HubunganPemasokBumdes.php @@ -0,0 +1,32 @@ + 'Bebas/tidak terikat: petani bebas menjual ke BUMDes atau pihak lain kapan saja, tanpa ikatan apa pun', + self::LANGGANAN => 'Langganan tetap: petani rutin menjual ke BUMDes tanpa perjanjian formal', + self::PINJAMAN => 'Berbasis hutang/pinjaman: petani menjual ke BUMDes karena memiliki pinjaman kepada BUMDes', + self::KONSINYASI => 'Titip jual (konsinyasi): petani dibayar setelah hasil panen terjual', + self::KOMITMEN_VOLUME => 'Ada komitmen volume: kesepakatan volume tertentu yang dipenuhi secara berkala', + self::SEMI_KONTRAK => 'Semi-kontrak: kesepakatan tertulis/lisan mengenai harga, volume, dan waktu penyerahan', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/InfrastrukturPascapanenBumdes.php b/app/Enums/Bumdes/InfrastrukturPascapanenBumdes.php new file mode 100644 index 0000000..6db3b15 --- /dev/null +++ b/app/Enums/Bumdes/InfrastrukturPascapanenBumdes.php @@ -0,0 +1,30 @@ + 'Gudang penyimpanan hasil panen', + self::ALAT_MESIN_PENGERING => 'Alat/mesin pengering', + self::MESIN_PULPER_HULLER => 'Mesin pulper/huller', + self::TIMBANGAN => 'Timbangan', + self::ALAT_SORTASI => 'Alat sortasi', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/JabatanPengurusIntiBumdes.php b/app/Enums/Bumdes/JabatanPengurusIntiBumdes.php new file mode 100644 index 0000000..e2b2c09 --- /dev/null +++ b/app/Enums/Bumdes/JabatanPengurusIntiBumdes.php @@ -0,0 +1,32 @@ + 'Penasihat (Kepala Desa)', + self::KETUA_PENGAWAS => 'Ketua Pengawas', + self::DIREKTUR_KETUA_BUMDES => 'Direktur/Ketua BUMDes', + self::SEKRETARIS => 'Sekretaris', + self::BENDAHARA => 'Bendahara', + self::KEPALA_UNIT_USAHA_KOMODITAS => 'Kepala Unit Usaha Komoditas', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/KemitraanEksternalBumdes.php b/app/Enums/Bumdes/KemitraanEksternalBumdes.php new file mode 100644 index 0000000..e6ca83d --- /dev/null +++ b/app/Enums/Bumdes/KemitraanEksternalBumdes.php @@ -0,0 +1,30 @@ + 'NGO/lembaga pemberdayaan', + self::PEMERINTAH_KABUPATEN => 'Dinas/pemerintah kabupaten', + self::SWASTA_OFFTAKER => 'Perusahaan swasta/offtaker', + self::PROGRAM_PEMERINTAH_LAIN => 'Program pemerintah lain', + self::BELUM_ADA => 'Belum ada kemitraan eksternal', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/KeterlibatanKoordinasiBumdes.php b/app/Enums/Bumdes/KeterlibatanKoordinasiBumdes.php new file mode 100644 index 0000000..ecc760e --- /dev/null +++ b/app/Enums/Bumdes/KeterlibatanKoordinasiBumdes.php @@ -0,0 +1,24 @@ + 'Selalu dilibatkan', + self::KADANG => 'Kadang dilibatkan', + self::TIDAK_PERNAH => 'Tidak pernah dilibatkan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/KondisiAsetProduktifBumdes.php b/app/Enums/Bumdes/KondisiAsetProduktifBumdes.php new file mode 100644 index 0000000..a15b76a --- /dev/null +++ b/app/Enums/Bumdes/KondisiAsetProduktifBumdes.php @@ -0,0 +1,43 @@ + 'Berfungsi', + self::PERLU_PERBAIKAN => 'Perlu perbaikan/evaluasi', + self::RUSAK => 'Rusak/tidak bisa dipakai', + self::MANGKRAK => 'Mangkrak', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } + + public static function normalizeLegacy(mixed $value): ?string + { + if (! is_scalar($value) || trim((string) $value) === '') { + return null; + } + + $value = trim((string) $value); + + return match (mb_strtolower($value)) { + 'baik', 'berfungsi', 'baik dan berfungsi', 'baik_berfungsi' => self::BERFUNGSI->value, + 'rusak ringan', 'perlu perbaikan', 'perlu perbaikan/evaluasi' => self::PERLU_PERBAIKAN->value, + 'rusak', 'rusak/tidak terpakai', 'rusak_tidak_terpakai', 'rusak/tidak bisa dipakai' => self::RUSAK->value, + 'mangkrak' => self::MANGKRAK->value, + default => $value, + }; + } +} diff --git a/app/Enums/Bumdes/KondisiInfrastrukturBumdes.php b/app/Enums/Bumdes/KondisiInfrastrukturBumdes.php new file mode 100644 index 0000000..75e0330 --- /dev/null +++ b/app/Enums/Bumdes/KondisiInfrastrukturBumdes.php @@ -0,0 +1,22 @@ + 'Baik dan berfungsi', + self::RUSAK_TIDAK_TERPAKAI => 'Rusak/tidak terpakai', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/KondisiKeuanganBumdes.php b/app/Enums/Bumdes/KondisiKeuanganBumdes.php new file mode 100644 index 0000000..ab0dc9c --- /dev/null +++ b/app/Enums/Bumdes/KondisiKeuanganBumdes.php @@ -0,0 +1,26 @@ + 'Untung/surplus', + self::IMPAS => 'Impas', + self::RUGI_DEFISIT => 'Rugi/defisit', + self::TIDAK_MENJAWAB => 'Tidak tahu/tidak mau menjawab', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/KontribusiPadesBumdes.php b/app/Enums/Bumdes/KontribusiPadesBumdes.php new file mode 100644 index 0000000..79f7140 --- /dev/null +++ b/app/Enums/Bumdes/KontribusiPadesBumdes.php @@ -0,0 +1,24 @@ + 'Ya, rutin', + self::TIDAK_RUTIN => 'Ya, tapi tidak rutin', + self::BELUM_PERNAH => 'Belum pernah', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/LayananPetaniBumdes.php b/app/Enums/Bumdes/LayananPetaniBumdes.php new file mode 100644 index 0000000..bb6c496 --- /dev/null +++ b/app/Enums/Bumdes/LayananPetaniBumdes.php @@ -0,0 +1,34 @@ + 'Simpan pinjam', + self::PENGADAAN_INPUT => 'Pengadaan input (pupuk, bibit, dll)', + self::PEMASARAN_PANEN => 'Pembelian/pemasaran hasil panen', + self::PENYEWAAN_ALAT => 'Penyewaan alat pengolahan', + self::JASA_PENGOLAHAN => 'Jasa pengolahan/fermentasi', + self::PELATIHAN => 'Pelatihan/pendampingan', + self::BELUM_ADA => 'Belum ada layanan terkait komoditas ini', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/MekanismePembayaranBumdes.php b/app/Enums/Bumdes/MekanismePembayaranBumdes.php new file mode 100644 index 0000000..efc5250 --- /dev/null +++ b/app/Enums/Bumdes/MekanismePembayaranBumdes.php @@ -0,0 +1,28 @@ + 'Tunai langsung', + self::TEMPO => 'Sistem tempo/dibayar kemudian', + self::CAMPURAN => 'Campuran, tergantung situasi', + self::IJON => 'BUMDes membayar di muka sebelum panen, sebagai bagian dari ikatan pinjaman kepada petani (ijon)', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/ModelFermentasiBumdes.php b/app/Enums/Bumdes/ModelFermentasiBumdes.php new file mode 100644 index 0000000..ade0438 --- /dev/null +++ b/app/Enums/Bumdes/ModelFermentasiBumdes.php @@ -0,0 +1,26 @@ + 'Individual (masing-masing petani)', + self::KOLEKTIF_KELOMPOK => 'Kolektif kelompok tani', + self::UNIT_BUMDES => 'Unit pengolah BUMDes/desa', + self::CAMPURAN => 'Campuran', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PencatatanPetaniBumdes.php b/app/Enums/Bumdes/PencatatanPetaniBumdes.php new file mode 100644 index 0000000..c912f46 --- /dev/null +++ b/app/Enums/Bumdes/PencatatanPetaniBumdes.php @@ -0,0 +1,24 @@ + 'Ada, tercatat rapi', + self::TIDAK_RAPI => 'Ada tapi tidak rapi', + self::TIDAK_ADA => 'Tidak ada pencatatan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PenetapanHargaBeliBumdes.php b/app/Enums/Bumdes/PenetapanHargaBeliBumdes.php new file mode 100644 index 0000000..1099a2e --- /dev/null +++ b/app/Enums/Bumdes/PenetapanHargaBeliBumdes.php @@ -0,0 +1,26 @@ + 'Mengikuti harga pasar/tengkulak setempat', + self::KALKULASI_BIAYA => 'Ditetapkan sendiri berdasarkan kalkulasi biaya', + self::HARGA_MITRA => 'Mengikuti harga dari offtaker/mitra', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PengalamanOfftakerBumdes.php b/app/Enums/Bumdes/PengalamanOfftakerBumdes.php new file mode 100644 index 0000000..6ad7439 --- /dev/null +++ b/app/Enums/Bumdes/PengalamanOfftakerBumdes.php @@ -0,0 +1,24 @@ + 'Sedang berjalan', + self::BERHENTI => 'Pernah, tapi sudah berhenti', + self::BELUM_PERNAH => 'Belum pernah', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PeranRantaiNilaiBumdes.php b/app/Enums/Bumdes/PeranRantaiNilaiBumdes.php new file mode 100644 index 0000000..b1373cb --- /dev/null +++ b/app/Enums/Bumdes/PeranRantaiNilaiBumdes.php @@ -0,0 +1,26 @@ + 'Membeli langsung dari petani', + self::PERANTARA => 'Menjadi perantara/mengumpulkan untuk dijual ke pihak lain', + self::MENGOLAH_SENDIRI => 'Mengolah sendiri (sortasi/fermentasi/dll) sebelum dijual', + self::BELUM_BERPERAN => 'Belum berperan dalam rantai nilai komoditas ini', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PeriodePanenBumdes.php b/app/Enums/Bumdes/PeriodePanenBumdes.php new file mode 100644 index 0000000..9f1c67c --- /dev/null +++ b/app/Enums/Bumdes/PeriodePanenBumdes.php @@ -0,0 +1,24 @@ + 'Panen Terakhir', + self::TAHUN_LALU => 'Tahun Lalu', + self::DUA_TAHUN_LALU => 'Dua Tahun Lalu', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PerjanjianPetaniBumdes.php b/app/Enums/Bumdes/PerjanjianPetaniBumdes.php new file mode 100644 index 0000000..853f501 --- /dev/null +++ b/app/Enums/Bumdes/PerjanjianPetaniBumdes.php @@ -0,0 +1,24 @@ + 'Ada, tertulis', + self::LISAN => 'Ada, kesepakatan lisan saja', + self::TIDAK_ADA => 'Tidak ada perjanjian', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/PertanggungjawabanMusdesBumdes.php b/app/Enums/Bumdes/PertanggungjawabanMusdesBumdes.php new file mode 100644 index 0000000..4102fdf --- /dev/null +++ b/app/Enums/Bumdes/PertanggungjawabanMusdesBumdes.php @@ -0,0 +1,24 @@ + 'Ya, setiap tahun', + self::TIDAK_RUTIN => 'Tidak rutin', + self::BELUM_PERNAH => 'Belum pernah', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/SaluranPenjualanBumdes.php b/app/Enums/Bumdes/SaluranPenjualanBumdes.php new file mode 100644 index 0000000..4c254e0 --- /dev/null +++ b/app/Enums/Bumdes/SaluranPenjualanBumdes.php @@ -0,0 +1,30 @@ + 'Offtaker/eksportir langsung', + self::PEDAGANG_KABUPATEN => 'Pedagang besar/pengepul kabupaten', + self::KOPERASI_BUMDES => 'Koperasi atau BUMDes lain', + self::PASAR_LOKAL => 'Dijual eceran/pasar lokal', + self::BELUM_MENJUAL => 'Belum pernah menjual (masih disimpan/diproses)', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/SifatAuditBumdes.php b/app/Enums/Bumdes/SifatAuditBumdes.php new file mode 100644 index 0000000..7483d74 --- /dev/null +++ b/app/Enums/Bumdes/SifatAuditBumdes.php @@ -0,0 +1,22 @@ + 'Internal (pengurus/pengawas BUMDes sendiri)', + self::EKSTERNAL => 'Eksternal (Inspektorat, Dinas PMD, akuntan publik/auditor independen)', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusAdArtBumdes.php b/app/Enums/Bumdes/StatusAdArtBumdes.php new file mode 100644 index 0000000..a3816a1 --- /dev/null +++ b/app/Enums/Bumdes/StatusAdArtBumdes.php @@ -0,0 +1,26 @@ + 'Ada, lengkap', + self::ADA_TIDAK_LENGKAP => 'Ada tapi tidak lengkap', + self::TIDAK_ADA => 'Tidak ada', + self::TIDAK_TAHU => 'Tidak tahu', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusBadanHukumBumdes.php b/app/Enums/Bumdes/StatusBadanHukumBumdes.php new file mode 100644 index 0000000..083f091 --- /dev/null +++ b/app/Enums/Bumdes/StatusBadanHukumBumdes.php @@ -0,0 +1,26 @@ + 'Sudah berbadan hukum (terdaftar Kemenkumham)', + self::PROSES_PENDAFTARAN => 'Proses pendaftaran', + self::BELUM_BERBADAN_HUKUM => 'Belum berbadan hukum', + self::TIDAK_TAHU => 'Tidak tahu', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusFermentasiBumdes.php b/app/Enums/Bumdes/StatusFermentasiBumdes.php new file mode 100644 index 0000000..33a6691 --- /dev/null +++ b/app/Enums/Bumdes/StatusFermentasiBumdes.php @@ -0,0 +1,26 @@ + 'Ya, rutin', + self::PERNAH_MENCOBA => 'Pernah mencoba', + self::DIRENCANAKAN => 'Belum pernah, direncanakan', + self::TIDAK_DIRENCANAKAN => 'Belum pernah, tidak direncanakan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusKeaktifanBumdes.php b/app/Enums/Bumdes/StatusKeaktifanBumdes.php new file mode 100644 index 0000000..04add4e --- /dev/null +++ b/app/Enums/Bumdes/StatusKeaktifanBumdes.php @@ -0,0 +1,26 @@ + 'Aktif beroperasi penuh', + self::AKTIF_TERBATAS => 'Aktif tapi terbatas (sebagian unit usaha berjalan)', + self::TIDAK_AKTIF_VAKUM => 'Tidak aktif/vakum', + self::BERDIRI_BELUM_BEROPERASI => 'Sudah berdiri tapi belum pernah beroperasi', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusKepemilikanAsetBumdes.php b/app/Enums/Bumdes/StatusKepemilikanAsetBumdes.php new file mode 100644 index 0000000..31f85ab --- /dev/null +++ b/app/Enums/Bumdes/StatusKepemilikanAsetBumdes.php @@ -0,0 +1,24 @@ + 'Milik BUMDes sendiri', + self::MILIK_DESA_DIPINJAMKAN_DIKELOLA => 'Milik desa (dipinjamkan/dikelola)', + self::SEWA_PINJAM_PIHAK_LAIN => 'Sewa/pinjam pihak lain', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusPembukuanBumdes.php b/app/Enums/Bumdes/StatusPembukuanBumdes.php new file mode 100644 index 0000000..8155f6e --- /dev/null +++ b/app/Enums/Bumdes/StatusPembukuanBumdes.php @@ -0,0 +1,24 @@ + 'Ya, rutin dan tertib', + self::TIDAK_RUTIN => 'Ada tapi tidak rutin/tidak rapi', + self::TIDAK_ADA => 'Tidak ada pembukuan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusPengeringanBumdes.php b/app/Enums/Bumdes/StatusPengeringanBumdes.php new file mode 100644 index 0000000..d867756 --- /dev/null +++ b/app/Enums/Bumdes/StatusPengeringanBumdes.php @@ -0,0 +1,24 @@ + 'Ya, rutin', + self::KADANG => 'Kadang-kadang', + self::TIDAK_PERNAH => 'Tidak pernah, produk dijual dalam kondisi diterima', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusSortasiBumdes.php b/app/Enums/Bumdes/StatusSortasiBumdes.php new file mode 100644 index 0000000..c8dbab2 --- /dev/null +++ b/app/Enums/Bumdes/StatusSortasiBumdes.php @@ -0,0 +1,24 @@ + 'Ya, rutin dengan standar tertentu', + self::TIDAK_KONSISTEN => 'Ya, tapi tidak konsisten', + self::BELUM_PERNAH => 'Belum pernah dilakukan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/StatusUnitKomoditasBumdes.php b/app/Enums/Bumdes/StatusUnitKomoditasBumdes.php new file mode 100644 index 0000000..a08a3c9 --- /dev/null +++ b/app/Enums/Bumdes/StatusUnitKomoditasBumdes.php @@ -0,0 +1,24 @@ + 'Ya, ada unit khusus', + self::DITANGANI_PENGURUS_INTI => 'Tidak ada unit khusus, ditangani langsung pengurus inti', + self::DIRENCANAKAN => 'Belum ada, masih direncanakan', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/SumberPasokanBumdes.php b/app/Enums/Bumdes/SumberPasokanBumdes.php new file mode 100644 index 0000000..a125fe4 --- /dev/null +++ b/app/Enums/Bumdes/SumberPasokanBumdes.php @@ -0,0 +1,28 @@ + 'Anggota kelompok tani langsung', + self::PETANI_NON_ANGGOTA => 'Petani non-anggota', + self::PENGUMPUL_DESA => 'Pengumpul/sub-agen desa', + self::CAMPURAN => 'Campuran', + self::LAINNYA => 'Lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Enums/Bumdes/UnitUsahaBumdes.php b/app/Enums/Bumdes/UnitUsahaBumdes.php new file mode 100644 index 0000000..854b624 --- /dev/null +++ b/app/Enums/Bumdes/UnitUsahaBumdes.php @@ -0,0 +1,32 @@ + 'Simpan pinjam', + self::PERDAGANGAN_TOKO_DESA => 'Perdagangan/toko desa', + self::PENGELOLAAN_AIR_BERSIH => 'Pengelolaan air bersih', + self::WISATA_DESA_AGROWISATA => 'Wisata desa/agrowisata', + self::PENGELOLAAN_HASIL_PERTANIAN_KOMODITAS => 'Pengelolaan hasil pertanian/komoditas', + self::PENYEWAAN_ALAT_ASET => 'Penyewaan alat/aset', + self::JASA_LAINNYA => 'Jasa lainnya', + }; + } + + public static function options(): array + { + return collect(self::cases())->mapWithKeys(fn (self $value) => [$value->value => $value->label()])->all(); + } +} diff --git a/app/Http/Controllers/Web/Bumdes/KelembagaanAktorBumdesController.php b/app/Http/Controllers/Web/Bumdes/KelembagaanAktorBumdesController.php new file mode 100644 index 0000000..64d7e31 --- /dev/null +++ b/app/Http/Controllers/Web/Bumdes/KelembagaanAktorBumdesController.php @@ -0,0 +1,29 @@ +service->sectionData($pengurusBumdes)); + } + + public function store(StoreKelembagaanAktorBumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse + { + $this->service->saveSection($pengurusBumdes, $request->validated(), 'kelembagaan'); + + return redirect()->route('bumdes.kelembagaan-aktor.index', $pengurusBumdes) + ->with('success', 'Data Kelembagaan berhasil disimpan.'); + } +} diff --git a/app/Http/Controllers/Web/Bumdes/OperasionalBumdesController.php b/app/Http/Controllers/Web/Bumdes/OperasionalBumdesController.php new file mode 100644 index 0000000..ad1f0f6 --- /dev/null +++ b/app/Http/Controllers/Web/Bumdes/OperasionalBumdesController.php @@ -0,0 +1,29 @@ +service->sectionData($pengurusBumdes)); + } + + public function store(StoreOperasionalBumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse + { + $this->service->saveSection($pengurusBumdes, $request->validated(), 'operasional'); + + return redirect()->route('bumdes.operasional.index', $pengurusBumdes) + ->with('success', 'Data Operasional berhasil disimpan.'); + } +} diff --git a/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php b/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php index 2146c49..dfdf7ab 100644 --- a/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php +++ b/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php @@ -2,579 +2,66 @@ namespace App\Http\Controllers\Web\Bumdes; -use App\Enums\Bumdes\JabatanBumdes; -use App\Enums\Pendidikan; use App\Http\Controllers\Controller; -use App\Models\Bumdes\KelembagaanAktorBumdes; +use App\Http\Requests\Bumdes\BumdesRequest; +use App\Http\Requests\Bumdes\IndexPengurusBumdesRequest; +use App\Http\Requests\Bumdes\StorePengurusBumdesRequest; +use App\Http\Requests\Bumdes\UpdatePengurusBumdesRequest; use App\Models\Bumdes\PengurusBumdes; -use App\Models\Wilayah\Provinsi; -use Carbon\Carbon; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\Gate; -use Illuminate\Support\Str; -use Illuminate\Validation\Rule; +use App\Services\Bumdes\PengurusBumdesService; +use Illuminate\Http\RedirectResponse; +use Illuminate\Support\Facades\DB; +use Illuminate\View\View; class PengurusBumdesController extends Controller { - public function index(Request $request) + public function __construct(private readonly PengurusBumdesService $service) {} + + public function index(IndexPengurusBumdesRequest $request): View { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } - - $query = PengurusBumdes::with([ - ...$this->wilayahRelationsWithoutProgramScope(), - 'creator:id,name', - ]) - ->orderBy('created_at', 'desc'); - - if ($request->search) { - $query->where(function ($query) use ($request) { - $query->where('nama', 'like', '%' . $request->search . '%') - ->orWhere('jabatan', 'like', '%' . $request->search . '%') - ->orWhere('jabatan_lainnya', 'like', '%' . $request->search . '%'); - }); - } - - if (auth()->user()->role->value === 'enumerator') { - $query->where('created_by', auth()->id()); - } - - $summaryQuery = clone $query; - $totalPengurus = (clone $summaryQuery)->count(); - $totalLakiLaki = (clone $summaryQuery)->where('jenis_kelamin', true)->count(); - $totalPerempuan = (clone $summaryQuery)->where('jenis_kelamin', false)->count(); - $totalDesaKelurahan = (clone $summaryQuery)->distinct('desa_kelurahan_id')->count('desa_kelurahan_id'); - $totalJabatan = (clone $summaryQuery)->distinct('jabatan')->count('jabatan'); - - $pengurus = $query->paginate($request->per_page ?? 10); - $userRole = auth()->user()->role->value; - $showEnumerator = in_array($userRole, ['super_admin', 'admin']); - - return view('bumdes.profil.index', compact( - 'pengurus', - 'totalPengurus', - 'totalLakiLaki', - 'totalPerempuan', - 'totalDesaKelurahan', - 'totalJabatan', - 'showEnumerator' - )); + return view('bumdes.profil.index', $this->service->indexData($request->validated())); } - public function create() + public function create(BumdesRequest $request): View { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } - - $provinsi = $this->provinsiOptions(); - $jabatan = JabatanBumdes::options(); - $pendidikan = Pendidikan::options(); - - return view('bumdes.profil.add', compact('provinsi', 'jabatan', 'pendidikan')); + return view('bumdes.profil.create', $this->service->profileData()); } - public function store(Request $request) + public function store(StorePengurusBumdesRequest $request): RedirectResponse { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } + $pengurus = $this->service->storeProfile($request->validated()); - $validatedData = $this->validatedData($request); - - $pengurus = PengurusBumdes::create([ - ...$this->formatData($validatedData), - 'uid' => 'BMD-' . strtoupper(Str::random(10)), - 'created_by' => auth()->id(), - 'updated_by' => auth()->id(), - ]); - - return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil ditambahkan.'); + return $this->profileSavedResponse($request, $pengurus); } - public function show(string $id) + public function show(BumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse { - return redirect()->route('bumdes.edit', $id); + return redirect()->route('bumdes.edit', $pengurusBumdes); } - public function edit(string $id) + public function edit(BumdesRequest $request, PengurusBumdes $pengurusBumdes): View { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } - - $pengurus = $this->pengurusQuery() - ->with($this->wilayahRelationsWithoutProgramScope()) - ->findOrFail($id); - $provinsi = $this->provinsiOptions(); - $jabatan = JabatanBumdes::options(); - $pendidikan = Pendidikan::options(); - - return view('bumdes.profil.edit', compact('pengurus', 'provinsi', 'jabatan', 'pendidikan')); + return view('bumdes.profil.edit', $this->service->profileData($pengurusBumdes)); } - public function update(Request $request, string $id) + public function update(UpdatePengurusBumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } + $this->service->updateProfile($pengurusBumdes, $request->validated()); - $pengurus = $this->pengurusQuery()->findOrFail($id); - $validatedData = $this->validatedData($request); - - $pengurus->update([ - ...$this->formatData($validatedData), - 'updated_by' => auth()->id(), - ]); - - return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil diupdate.'); + return $this->profileSavedResponse($request, $pengurusBumdes); } - public function destroy(string $id) + public function destroy(BumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } + DB::transaction(fn () => $pengurusBumdes->delete()); - $pengurus = $this->pengurusQuery()->findOrFail($id); - $pengurus->update(['deleted_by' => auth()->id()]); - $pengurus->delete(); - - return response()->json(['message' => 'Data pengurus BUMDes berhasil dihapus.']); + return redirect()->route('bumdes.index')->with('success', 'Data profil berhasil dihapus.'); } - public function kelembagaanAktor(string $id) + private function profileSavedResponse(BumdesRequest $request, PengurusBumdes $pengurus): RedirectResponse { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } + $fragment = $request->input('redirect_fragment') === 'section-kepengurusan' ? '#section-kepengurusan' : ''; - $pengurus = $this->pengurusQuery() - ->with([ - ...$this->wilayahRelationsWithoutProgramScope(), - 'kelembagaanAktor', - ]) - ->findOrFail($id); - $kelembagaanAktor = $pengurus->kelembagaanAktor; - - return view('bumdes.kelembagaan-aktor.index', array_merge( - compact('pengurus', 'kelembagaanAktor'), - $this->kelembagaanAktorOptions() - )); - } - - public function storeKelembagaanAktor(Request $request, string $id) - { - if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { - abort(403); - } - - $pengurus = $this->pengurusQuery()->findOrFail($id); - $validatedData = $this->validatedKelembagaanAktorData($request); - - KelembagaanAktorBumdes::updateOrCreate( - ['pengurus_bumdes_id' => $pengurus->id], - [ - ...$this->formatKelembagaanAktorData($validatedData), - 'created_by' => $pengurus->kelembagaanAktor?->created_by ?? auth()->id(), - 'updated_by' => auth()->id(), - ] - ); - - return redirect() - ->route('bumdes.kelembagaan-aktor.index', $pengurus->id) - ->with('success', 'Data kelembagaan dan aktor BUMDes berhasil disimpan.'); - } - - private function pengurusQuery() - { - $query = PengurusBumdes::query(); - - if (auth()->user()->role->value === 'enumerator') { - $query->where('created_by', auth()->id()); - } - - return $query; - } - - private function provinsiOptions() - { - return Provinsi::withoutGlobalScope('program') - ->select('id', 'kode', 'nama') - ->orderBy('kode', 'asc') - ->get(); - } - - private function wilayahRelationsWithoutProgramScope(): array - { - return [ - 'desaKelurahan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kecamatan_id'), - 'desaKelurahan.kecamatan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kabupaten_kota_id'), - 'desaKelurahan.kecamatan.kabupatenKota' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'provinsi_id'), - 'desaKelurahan.kecamatan.kabupatenKota.provinsi' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama'), - ]; - } - - private function validatedData(Request $request): array - { - return $request->validate([ - 'nama' => ['required', 'string', 'max:255'], - 'jenis_kelamin' => ['required', 'boolean'], - 'tempat_lahir' => ['required', 'string', 'max:255'], - 'tanggal_lahir' => ['required', 'date_format:d-m-Y'], - 'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)], - 'alamat' => ['required', 'string', 'max:500'], - 'desa_kelurahan_id' => ['required', 'exists:master_desa_kelurahan,id'], - 'nomor_telepon' => ['nullable', 'digits_between:11,13'], - 'jabatan' => ['required', Rule::enum(JabatanBumdes::class)], - 'jabatan_lainnya' => ['nullable', 'required_if:jabatan,' . JabatanBumdes::LAINNYA->value, 'string', 'max:255'], - 'lama_menjabat_tahun' => ['required', 'integer', 'min:0', 'max:80'], - ], [ - 'nama.required' => 'Nama harus diisi.', - 'jenis_kelamin.required' => 'Jenis kelamin harus dipilih.', - 'tempat_lahir.required' => 'Tempat lahir harus diisi.', - 'tanggal_lahir.required' => 'Tanggal lahir harus diisi.', - 'tanggal_lahir.date_format' => 'Tanggal lahir harus menggunakan format tanggal yang valid.', - 'pendidikan_terakhir.required' => 'Pendidikan terakhir harus dipilih.', - 'alamat.required' => 'Alamat harus diisi.', - 'desa_kelurahan_id.required' => 'Desa kelurahan harus dipilih.', - 'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.', - 'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.', - 'jabatan.required' => 'Jabatan/peran harus dipilih.', - 'jabatan_lainnya.required_if' => 'Jabatan lainnya harus diisi.', - 'lama_menjabat_tahun.required' => 'Lama menjabat harus diisi.', - 'lama_menjabat_tahun.integer' => 'Lama menjabat harus berupa angka tahun.', - ]); - } - - private function formatData(array $data): array - { - $data['tanggal_lahir'] = Carbon::createFromFormat('d-m-Y', $data['tanggal_lahir'])->format('Y-m-d'); - $data['tanggal_mulai_menjabat'] = Carbon::now() - ->subYears((int) $data['lama_menjabat_tahun']) - ->format('Y-m-d'); - - if (($data['jabatan'] ?? null) !== JabatanBumdes::LAINNYA->value) { - $data['jabatan_lainnya'] = null; - } - - return $data; - } - - private function validatedKelembagaanAktorData(Request $request): array - { - $this->normalizeKelembagaanAktorInput($request); - $maxYear = now()->year + 1; - - return $request->validate([ - 'status_badan_hukum' => ['nullable', Rule::in(array_keys($this->statusBadanHukumOptions()))], - 'nomor_dasar_hukum' => ['nullable', 'string', 'max:255'], - 'tahun_dasar_hukum' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear], - 'status_keaktifan' => ['nullable', Rule::in(array_keys($this->statusKeaktifanOptions()))], - 'alasan_tidak_aktif' => ['nullable', 'string'], - 'unit_usaha' => ['nullable', 'array'], - 'unit_usaha.*' => ['string', Rule::in(array_keys($this->unitUsahaOptions()))], - 'unit_usaha_jasa_lainnya' => ['nullable', 'string', 'max:255'], - 'status_unit_komoditas' => ['nullable', Rule::in(array_keys($this->statusUnitKomoditasOptions()))], - 'nama_unit_komoditas' => ['nullable', 'string', 'max:255'], - 'status_ad_art' => ['nullable', Rule::in(array_keys($this->statusAdArtOptions()))], - 'tahun_ad_art' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear], - 'pengurus_inti' => ['nullable', 'array'], - 'pengurus_inti.*.jabatan' => ['nullable', 'string', 'max:255'], - 'pengurus_inti.*.nama' => ['nullable', 'string', 'max:255'], - 'pengurus_inti.*.aktif' => ['nullable', 'boolean'], - 'memiliki_staf' => ['nullable', 'boolean'], - 'jumlah_staf' => ['nullable', 'integer', 'min:0'], - 'unit_usaha_staf' => ['nullable', 'string'], - 'modal_awal' => ['nullable', 'integer', 'min:0'], - 'tahun_modal_awal' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear], - 'pernah_penambahan_modal' => ['nullable', 'boolean'], - 'penambahan_modal' => ['nullable', 'array'], - 'penambahan_modal.*.tahun' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear], - 'penambahan_modal.*.jumlah' => ['nullable', 'integer', 'min:0'], - 'aset_produktif' => ['nullable', 'array'], - 'aset_produktif.*.aset' => ['nullable', 'string', 'max:255'], - 'aset_produktif.*.dipilih' => ['nullable', 'boolean'], - 'aset_produktif.*.jumlah' => ['nullable', 'string', 'max:255'], - 'aset_produktif.*.kondisi' => ['nullable', 'string', 'max:255'], - 'aset_produktif_lainnya' => ['nullable', 'string', 'max:255'], - 'status_kepemilikan_aset' => ['nullable', Rule::in(array_keys($this->statusKepemilikanAsetOptions()))], - 'infrastruktur_pascapanen' => ['nullable', 'array'], - 'infrastruktur_pascapanen.*.infrastruktur' => ['nullable', 'string', 'max:255'], - 'infrastruktur_pascapanen.*.dipilih' => ['nullable', 'boolean'], - 'infrastruktur_pascapanen.*.jumlah' => ['nullable', 'string', 'max:255'], - 'infrastruktur_pascapanen.*.kondisi' => ['nullable', Rule::in(array_keys($this->kondisiInfrastrukturOptions()))], - 'infrastruktur_pascapanen_lainnya' => ['nullable', 'string', 'max:255'], - ], $this->kelembagaanAktorValidationMessages(), $this->kelembagaanAktorValidationAttributes()); - } - - private function normalizeKelembagaanAktorInput(Request $request): void - { - foreach (['modal_awal'] as $field) { - if ($request->has($field)) { - $request->merge([$field => preg_replace('/\D/', '', (string) $request->input($field))]); - } - } - - $penambahanModal = collect($request->input('penambahan_modal', [])) - ->map(function ($row) { - if (isset($row['jumlah'])) { - $row['jumlah'] = preg_replace('/\D/', '', (string) $row['jumlah']); - } - - return $row; - }) - ->all(); - - if ($request->has('penambahan_modal')) { - $request->merge(['penambahan_modal' => $penambahanModal]); - } - } - - private function formatKelembagaanAktorData(array $data): array - { - $payload = array_merge([ - 'status_badan_hukum' => null, - 'nomor_dasar_hukum' => null, - 'tahun_dasar_hukum' => null, - 'status_keaktifan' => null, - 'alasan_tidak_aktif' => null, - 'unit_usaha' => [], - 'unit_usaha_jasa_lainnya' => null, - 'status_unit_komoditas' => null, - 'nama_unit_komoditas' => null, - 'status_ad_art' => null, - 'tahun_ad_art' => null, - 'pengurus_inti' => [], - 'memiliki_staf' => null, - 'jumlah_staf' => null, - 'unit_usaha_staf' => null, - 'modal_awal' => null, - 'tahun_modal_awal' => null, - 'pernah_penambahan_modal' => null, - 'penambahan_modal' => [], - 'aset_produktif' => [], - 'aset_produktif_lainnya' => null, - 'status_kepemilikan_aset' => null, - 'infrastruktur_pascapanen' => [], - 'infrastruktur_pascapanen_lainnya' => null, - ], $data); - - $payload['unit_usaha'] = array_values($payload['unit_usaha'] ?? []); - $payload['pengurus_inti'] = $this->filterRows($payload['pengurus_inti'] ?? [], ['nama', 'aktif']); - $payload['penambahan_modal'] = $this->filterRows($payload['penambahan_modal'] ?? [], ['tahun', 'jumlah']); - $payload['aset_produktif'] = $this->filterRows($payload['aset_produktif'] ?? [], ['dipilih', 'jumlah', 'kondisi']); - $payload['infrastruktur_pascapanen'] = $this->filterRows($payload['infrastruktur_pascapanen'] ?? [], ['dipilih', 'jumlah', 'kondisi']); - - if (($payload['status_keaktifan'] ?? null) !== 'tidak_aktif_vakum') { - $payload['alasan_tidak_aktif'] = null; - } - - if (! in_array('jasa_lainnya', $payload['unit_usaha'], true)) { - $payload['unit_usaha_jasa_lainnya'] = null; - } - - if (($payload['status_unit_komoditas'] ?? null) !== 'ya') { - $payload['nama_unit_komoditas'] = null; - } - - if (($payload['status_ad_art'] ?? null) !== 'ada_lengkap') { - $payload['tahun_ad_art'] = null; - } - - if (! (bool) ($payload['memiliki_staf'] ?? false)) { - $payload['jumlah_staf'] = null; - $payload['unit_usaha_staf'] = null; - } - - if (! (bool) ($payload['pernah_penambahan_modal'] ?? false)) { - $payload['penambahan_modal'] = null; - } - - if (! collect($payload['aset_produktif'] ?? [])->contains(fn ($row) => ($row['aset'] ?? null) === 'lainnya')) { - $payload['aset_produktif_lainnya'] = null; - } - - if (! collect($payload['infrastruktur_pascapanen'] ?? [])->contains(fn ($row) => ($row['infrastruktur'] ?? null) === 'lainnya')) { - $payload['infrastruktur_pascapanen_lainnya'] = null; - } - - return $payload; - } - - private function filterRows(array $rows, array $valueKeys): array - { - return collect($rows) - ->map(function ($row) { - return collect($row) - ->map(fn ($value) => $value === '' ? null : $value) - ->all(); - }) - ->filter(function ($row) use ($valueKeys) { - foreach ($valueKeys as $key) { - if (filled($row[$key] ?? null)) { - return true; - } - } - - return false; - }) - ->values() - ->all(); - } - - private function kelembagaanAktorOptions(): array - { - return [ - 'statusBadanHukumOptions' => $this->statusBadanHukumOptions(), - 'statusKeaktifanOptions' => $this->statusKeaktifanOptions(), - 'unitUsahaOptions' => $this->unitUsahaOptions(), - 'statusUnitKomoditasOptions' => $this->statusUnitKomoditasOptions(), - 'statusAdArtOptions' => $this->statusAdArtOptions(), - 'pengurusIntiRows' => $this->pengurusIntiRows(), - 'asetProduktifRows' => $this->asetProduktifRows(), - 'statusKepemilikanAsetOptions' => $this->statusKepemilikanAsetOptions(), - 'infrastrukturPascapanenRows' => $this->infrastrukturPascapanenRows(), - 'kondisiInfrastrukturOptions' => $this->kondisiInfrastrukturOptions(), - ]; - } - - private function statusBadanHukumOptions(): array - { - return [ - 'sudah_berbadan_hukum' => 'Sudah berbadan hukum (terdaftar Kemenkumham)', - 'proses_pendaftaran' => 'Proses pendaftaran', - 'belum_berbadan_hukum' => 'Belum berbadan hukum', - 'tidak_tahu' => 'Tidak tahu', - ]; - } - - private function statusKeaktifanOptions(): array - { - return [ - 'aktif_penuh' => 'Aktif beroperasi penuh', - 'aktif_terbatas' => 'Aktif tapi terbatas (sebagian unit usaha berjalan)', - 'tidak_aktif_vakum' => 'Tidak aktif/vakum', - 'berdiri_belum_beroperasi' => 'Sudah berdiri tapi belum pernah beroperasi', - ]; - } - - private function unitUsahaOptions(): array - { - return [ - 'simpan_pinjam' => 'Simpan pinjam', - 'perdagangan_toko_desa' => 'Perdagangan/toko desa', - 'pengelolaan_air_bersih' => 'Pengelolaan air bersih', - 'wisata_desa_agrowisata' => 'Wisata desa/agrowisata', - 'pengelolaan_hasil_pertanian_komoditas' => 'Pengelolaan hasil pertanian/komoditas', - 'penyewaan_alat_aset' => 'Penyewaan alat/aset', - 'jasa_lainnya' => 'Jasa lainnya', - ]; - } - - private function statusUnitKomoditasOptions(): array - { - return [ - 'ya' => 'Ya, ada unit khusus', - 'ditangani_pengurus_inti' => 'Tidak ada unit khusus, ditangani langsung pengurus inti', - 'direncanakan' => 'Belum ada, masih direncanakan', - ]; - } - - private function statusAdArtOptions(): array - { - return [ - 'ada_lengkap' => 'Ada, lengkap', - 'ada_tidak_lengkap' => 'Ada tapi tidak lengkap', - 'tidak_ada' => 'Tidak ada', - 'tidak_tahu' => 'Tidak tahu', - ]; - } - - private function pengurusIntiRows(): array - { - return [ - 'penasihat_kepala_desa' => 'Penasihat (Kepala Desa)', - 'ketua_pengawas' => 'Ketua Pengawas', - 'direktur_ketua_bumdes' => 'Direktur/Ketua BUMDes', - 'sekretaris' => 'Sekretaris', - 'bendahara' => 'Bendahara', - 'kepala_unit_usaha_komoditas' => 'Kepala Unit Usaha Komoditas', - 'lainnya' => 'Lainnya', - ]; - } - - private function asetProduktifRows(): array - { - return [ - 'bangunan_kantor' => 'Bangunan / kantor', - 'gudang_penyimpanan' => 'Gudang penyimpanan', - 'kendaraan_operasional_angkut' => 'Kendaraan operasional/angkut', - 'lahan_usaha' => 'Lahan usaha', - 'alat_pengolahan_komoditas' => 'Alat pengolahan komoditas', - 'lainnya' => 'Lainnya', - ]; - } - - private function statusKepemilikanAsetOptions(): array - { - return [ - 'milik_bumdes_sendiri' => 'Milik BUMDes sendiri', - 'milik_desa_dipinjamkan_dikelola' => 'Milik desa (dipinjamkan/dikelola)', - 'sewa_pinjam_pihak_lain' => 'Sewa/pinjam pihak lain', - ]; - } - - private function infrastrukturPascapanenRows(): array - { - return [ - 'gudang_penyimpanan_hasil_panen' => 'Gudang penyimpanan hasil panen', - 'alat_mesin_pengering' => 'Alat/mesin pengering', - 'mesin_pulper_huller' => 'Mesin pulper/huller', - 'timbangan' => 'Timbangan', - 'alat_sortasi' => 'Alat sortasi', - 'lainnya' => 'Lainnya', - ]; - } - - private function kondisiInfrastrukturOptions(): array - { - return [ - 'baik_berfungsi' => 'Baik dan berfungsi', - 'rusak_tidak_terpakai' => 'Rusak/tidak terpakai', - ]; - } - - private function kelembagaanAktorValidationMessages(): array - { - return [ - 'integer' => ':attribute harus berupa angka.', - 'digits' => ':attribute harus terdiri dari :digits digit.', - 'min' => ':attribute tidak boleh kurang dari :min.', - 'max' => ':attribute tidak valid.', - 'in' => ':attribute tidak valid.', - 'array' => ':attribute harus berupa daftar data.', - 'boolean' => ':attribute harus dipilih.', - ]; - } - - private function kelembagaanAktorValidationAttributes(): array - { - return [ - 'status_badan_hukum' => 'Status badan hukum', - 'nomor_dasar_hukum' => 'Nomor dasar hukum', - 'tahun_dasar_hukum' => 'Tahun dasar hukum', - 'status_keaktifan' => 'Status keaktifan', - 'unit_usaha' => 'Unit usaha', - 'status_unit_komoditas' => 'Unit usaha komoditas', - 'status_ad_art' => 'Dokumen AD/ART', - 'memiliki_staf' => 'Status staf/karyawan', - 'jumlah_staf' => 'Jumlah staf', - 'modal_awal' => 'Modal awal', - 'tahun_modal_awal' => 'Tahun modal awal', - 'pernah_penambahan_modal' => 'Status penambahan modal', - 'status_kepemilikan_aset' => 'Status kepemilikan aset', - ]; + return redirect(route('bumdes.edit', $pengurus).$fragment)->with('success', 'Data Profil berhasil disimpan.'); } } diff --git a/app/Http/Controllers/Web/Bumdes/RantaiNilaiBumdesController.php b/app/Http/Controllers/Web/Bumdes/RantaiNilaiBumdesController.php new file mode 100644 index 0000000..16b7473 --- /dev/null +++ b/app/Http/Controllers/Web/Bumdes/RantaiNilaiBumdesController.php @@ -0,0 +1,34 @@ +service->sectionData($pengurusBumdes)); + } + + public function store(StoreRantaiNilaiBumdesRequest $request, PengurusBumdes $pengurusBumdes): RedirectResponse + { + $this->service->saveSection($pengurusBumdes, $request->validated(), 'rantai-nilai'); + + $nextSection = $request->validated('redirect_fragment'); + $postharvestSection = in_array($nextSection, ['section-rantai-pengeringan', 'section-rantai-fermentasi', 'section-rantai-premium-harga'], true); + $canContinue = $request->boolean($postharvestSection ? 'melakukan_pengolahan_pascapanen' : 'melakukan_jual_beli'); + $nextSection = $canContinue ? $nextSection : null; + + return redirect()->to(route('bumdes.rantai-nilai.index', $pengurusBumdes).($nextSection ? '#'.$nextSection : '')) + ->with('success', 'Data Rantai Nilai & Akses Pasar berhasil disimpan.'); + } +} diff --git a/app/Http/Requests/Bumdes/BumdesRequest.php b/app/Http/Requests/Bumdes/BumdesRequest.php new file mode 100644 index 0000000..715702a --- /dev/null +++ b/app/Http/Requests/Bumdes/BumdesRequest.php @@ -0,0 +1,174 @@ +route('pengurusBumdes'); + if ($pengurus instanceof PengurusBumdes && $this->user()->role === Role::ENUMERATOR) { + abort_unless((int) $pengurus->created_by === (int) $this->user()->id, 404); + } + + if ($pengurus instanceof PengurusBumdes && $this->routeIs('bumdes.operasional.*', 'bumdes.kelembagaan-aktor.*', 'bumdes.rantai-nilai.*')) { + abort_unless($pengurus->profilLengkap(), 403); + } + + return true; + } + + public function rules(): array + { + return []; + } + + protected function sectionRules(string $section): array + { + $maxYear = now()->year + 1; + + $rules = [ + 'status_badan_hukum' => ['nullable', Rule::enum(StatusBadanHukumBumdes::class)], + 'nomor_dasar_hukum' => [Rule::excludeIf($this->input('status_badan_hukum') !== 'sudah_berbadan_hukum'), 'required_if:status_badan_hukum,sudah_berbadan_hukum', 'string', 'max:255'], + 'tahun_dasar_hukum' => [Rule::excludeIf($this->input('status_badan_hukum') !== 'sudah_berbadan_hukum'), 'required_if:status_badan_hukum,sudah_berbadan_hukum', 'integer', 'digits:4', 'min:1900', 'max:'.$maxYear], + 'status_keaktifan' => ['nullable', Rule::enum(StatusKeaktifanBumdes::class)], + 'alasan_tidak_aktif' => ['nullable', 'string'], + 'unit_usaha' => ['nullable', 'array'], + 'unit_usaha.*' => ['string', Rule::enum(UnitUsahaBumdes::class)], + 'unit_usaha_jasa_lainnya' => ['nullable', 'string', 'max:255'], + 'status_unit_komoditas' => ['nullable', Rule::enum(StatusUnitKomoditasBumdes::class)], + 'nama_unit_komoditas' => [Rule::excludeIf($this->input('status_unit_komoditas') !== 'ya'), 'required_if:status_unit_komoditas,ya', 'string', 'max:255'], + 'status_ad_art' => ['nullable', Rule::enum(StatusAdArtBumdes::class)], + 'tahun_ad_art' => [Rule::excludeIf($this->input('status_ad_art') !== 'ada_lengkap'), 'required_if:status_ad_art,ada_lengkap', 'integer', 'digits:4', 'min:1900', 'max:'.$maxYear], + 'pengurus_inti' => ['nullable', 'array'], + 'pengurus_inti.*' => ['array'], + 'pengurus_inti.*.jabatan' => ['nullable', Rule::enum(JabatanPengurusIntiBumdes::class)], + 'pengurus_inti.*.nama' => ['nullable', 'string', 'max:255'], + 'pengurus_inti.*.aktif' => ['nullable', 'boolean'], + 'memiliki_staf' => ['nullable', 'boolean'], + 'jumlah_staf' => ['nullable', 'integer', 'min:0'], + 'unit_usaha_staf' => ['nullable', 'string'], + 'modal_awal' => ['nullable', 'integer', 'min:0'], + 'tahun_modal_awal' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:'.$maxYear], + 'pernah_penambahan_modal' => ['nullable', 'boolean'], + 'penambahan_modal' => [Rule::excludeIf(! $this->boolean('pernah_penambahan_modal')), 'nullable', 'array'], + 'penambahan_modal.*' => [Rule::excludeIf(! $this->boolean('pernah_penambahan_modal')), 'array'], + 'penambahan_modal.*.tahun' => [Rule::excludeIf(! $this->boolean('pernah_penambahan_modal')), 'nullable', 'integer', 'digits:4', 'min:1900', 'max:'.$maxYear], + 'penambahan_modal.*.jumlah' => [Rule::excludeIf(! $this->boolean('pernah_penambahan_modal')), 'nullable', 'integer', 'min:0'], + 'aset_produktif' => ['nullable', 'array'], + 'aset_produktif.*' => ['array'], + 'aset_produktif.*.aset' => ['nullable', Rule::enum(AsetProduktifBumdes::class)], + 'aset_produktif.*.dipilih' => ['nullable', 'boolean'], + 'aset_produktif.*.jumlah' => ['nullable', 'integer', 'min:0'], + 'aset_produktif.*.kondisi' => ['nullable', 'string', 'max:255'], + 'aset_produktif_lainnya' => ['nullable', 'string', 'max:255'], + 'status_kepemilikan_aset' => ['nullable', Rule::enum(StatusKepemilikanAsetBumdes::class)], + 'infrastruktur_pascapanen' => ['nullable', 'array'], + 'infrastruktur_pascapanen.*' => ['array'], + 'infrastruktur_pascapanen.*.infrastruktur' => ['nullable', Rule::enum(InfrastrukturPascapanenBumdes::class)], + 'infrastruktur_pascapanen.*.dipilih' => ['nullable', 'boolean'], + 'infrastruktur_pascapanen.*.jumlah' => ['nullable', 'integer', 'min:0'], + 'infrastruktur_pascapanen.*.kondisi' => ['nullable', Rule::enum(KondisiInfrastrukturBumdes::class)], + 'infrastruktur_pascapanen_lainnya' => ['nullable', 'string', 'max:255'], + ]; + $fields = KelembagaanAktorBumdes::sectionFields($section); + $rules = array_filter($rules, fn ($field) => in_array(Str::before($field, '.'), $fields, true), ARRAY_FILTER_USE_KEY); + + return $rules; + } + + public function messages(): array + { + return [ + 'nomor_dasar_hukum.required_if' => 'Nomor Peraturan Desa/SK wajib diisi.', + 'tahun_dasar_hukum.required_if' => 'Tahun Ditetapkan wajib diisi.', + 'nama_unit_komoditas.required_if' => 'Nama Unit Usaha Komoditas wajib diisi.', + 'tahun_ad_art.required_if' => 'Tahun Terakhir Diperbarui wajib diisi.', + 'string' => ':attribute harus berupa teks.', + 'required' => ':attribute wajib diisi.', + 'exists' => ':attribute tidak ditemukan.', + 'integer' => ':attribute harus berupa angka.', + 'digits' => ':attribute harus terdiri dari :digits digit.', + 'min' => ':attribute tidak boleh kurang dari :min.', + 'max' => ':attribute tidak valid.', + 'enum' => 'Pilihan :attribute tidak valid.', + 'in' => ':attribute tidak valid.', + 'array' => ':attribute harus berupa daftar data.', + 'boolean' => ':attribute harus dipilih.', + ]; + } + + public function attributes(): array + { + return [ + 'search' => 'Pencarian', + 'kabupaten_kota_id' => 'Kabupaten/Kota', 'kabupaten_id' => 'Kabupaten/Kota', + 'provinsi_id' => 'Provinsi', 'kecamatan_id' => 'Kecamatan', 'desa_kelurahan_id' => 'Desa/Kelurahan', + 'jenis_kelamin' => 'Jenis kelamin', 'per_page' => 'Jumlah data per halaman', + 'nama' => 'Nama', 'tempat_lahir' => 'Tempat lahir', 'tanggal_lahir' => 'Tanggal lahir', + 'pendidikan_terakhir' => 'Pendidikan terakhir', 'alamat' => 'Alamat', 'nomor_telepon' => 'Nomor telepon', + 'jabatan' => 'Jabatan/peran', 'jabatan_lainnya' => 'Jabatan lainnya', 'lama_menjabat_tahun' => 'Lama menjabat', + 'pengurus_inti' => 'Daftar Pengurus', 'pengurus_inti.*.jabatan' => 'Jabatan pengurus', + 'pengurus_inti.*.nama' => 'Nama pengurus', 'pengurus_inti.*.aktif' => 'Keaktifan pengurus', + 'status_badan_hukum' => 'Status badan hukum', + 'nomor_dasar_hukum' => 'Nomor Peraturan Desa/SK', + 'tahun_dasar_hukum' => 'Tahun Ditetapkan', + 'nama_unit_komoditas' => 'Nama Unit Usaha Komoditas', + 'tahun_ad_art' => 'Tahun Terakhir Diperbarui', + 'status_keaktifan' => 'Status keaktifan', + 'unit_usaha' => 'Unit usaha', + 'status_unit_komoditas' => 'Unit usaha komoditas', + 'status_ad_art' => 'Dokumen AD/ART', + 'memiliki_staf' => 'Status staf/karyawan', + 'jumlah_staf' => 'Jumlah staf', + 'modal_awal' => 'Modal awal', + 'tahun_modal_awal' => 'Tahun modal awal', + 'pernah_penambahan_modal' => 'Status penambahan modal', + 'penambahan_modal' => 'Rincian penambahan modal', + 'penambahan_modal.*.tahun' => 'Tahun penambahan modal pada baris :position', + 'penambahan_modal.*.jumlah' => 'Jumlah penambahan modal pada baris :position', + 'status_kepemilikan_aset' => 'Status kepemilikan aset', + 'alasan_tidak_aktif' => 'Alasan tidak aktif', + 'unit_usaha.*' => 'Pilihan unit usaha', + 'unit_usaha_jasa_lainnya' => 'Jasa lainnya', + 'unit_usaha_staf' => 'Unit usaha staf', + 'pengurus_inti.*' => 'Data pengurus pada baris :position', + 'penambahan_modal.*' => 'Penambahan modal pada baris :position', + 'aset_produktif' => 'Aset produktif', + 'aset_produktif.*' => 'Data aset produktif pada baris :position', + 'aset_produktif.*.aset' => 'Jenis aset produktif', + 'aset_produktif.*.dipilih' => 'Pilihan aset produktif', + 'aset_produktif.*.jumlah' => 'Jumlah aset produktif', + 'aset_produktif.*.kondisi' => 'Kondisi aset produktif', + 'aset_produktif_lainnya' => 'Aset produktif lainnya', + 'infrastruktur_pascapanen' => 'Infrastruktur pascapanen', + 'infrastruktur_pascapanen.*' => 'Data infrastruktur pada baris :position', + 'infrastruktur_pascapanen.*.infrastruktur' => 'Jenis infrastruktur', + 'infrastruktur_pascapanen.*.dipilih' => 'Pilihan infrastruktur', + 'infrastruktur_pascapanen.*.jumlah' => 'Jumlah infrastruktur', + 'infrastruktur_pascapanen.*.kondisi' => 'Kondisi infrastruktur', + 'infrastruktur_pascapanen_lainnya' => 'Infrastruktur lainnya', + ]; + } +} diff --git a/app/Http/Requests/Bumdes/IndexPengurusBumdesRequest.php b/app/Http/Requests/Bumdes/IndexPengurusBumdesRequest.php new file mode 100644 index 0000000..ce0ca10 --- /dev/null +++ b/app/Http/Requests/Bumdes/IndexPengurusBumdesRequest.php @@ -0,0 +1,27 @@ + ['nullable', 'string', 'max:255'], + 'kabupaten_kota_id' => ['nullable', 'integer', 'exists:master_kabupaten_kota,id'], + 'kecamatan_id' => ['nullable', 'integer', 'exists:master_kecamatan,id'], + 'desa_kelurahan_id' => ['nullable', 'integer', 'exists:master_desa_kelurahan,id'], + 'status_keaktifan' => ['nullable', Rule::enum(StatusKeaktifanBumdes::class)], + 'sort_by' => ['nullable', 'string'], + 'per_page' => ['nullable', 'integer', 'in:10,25'], + ]; + } + + public function messages(): array + { + return [...parent::messages(), 'exists' => ':attribute tidak ditemukan.', 'string' => ':attribute harus berupa teks.', 'max' => ':attribute terlalu panjang.', 'in' => 'Pilihan :attribute tidak valid.']; + } +} diff --git a/app/Http/Requests/Bumdes/StoreKelembagaanAktorBumdesRequest.php b/app/Http/Requests/Bumdes/StoreKelembagaanAktorBumdesRequest.php new file mode 100644 index 0000000..03120d4 --- /dev/null +++ b/app/Http/Requests/Bumdes/StoreKelembagaanAktorBumdesRequest.php @@ -0,0 +1,11 @@ +sectionRules('kelembagaan'); + } +} diff --git a/app/Http/Requests/Bumdes/StoreOperasionalBumdesRequest.php b/app/Http/Requests/Bumdes/StoreOperasionalBumdesRequest.php new file mode 100644 index 0000000..7b0c649 --- /dev/null +++ b/app/Http/Requests/Bumdes/StoreOperasionalBumdesRequest.php @@ -0,0 +1,252 @@ +sectionRules('operasional'), ...$this->pengelolaanRules()]; + $dukungan = $this->input('dukungan_desa', []); + $kemitraan = $this->input('kemitraan_eksternal', []); + $dukunganLainnya = is_array($dukungan) && in_array(DukunganDesaBumdes::LAINNYA->value, $dukungan, true); + $kemitraanLainnya = is_array($kemitraan) && in_array(KemitraanEksternalBumdes::LAINNYA->value, $kemitraan, true); + + $rules = [...$rules, + 'ada_alokasi_desa' => ['nullable', 'boolean'], + 'jumlah_alokasi_desa' => [Rule::excludeIf(! $this->boolean('ada_alokasi_desa')), 'required_if:ada_alokasi_desa,1', 'integer', 'min:0', 'max:9999999999999999'], + 'dukungan_desa' => ['nullable', 'array'], + 'dukungan_desa.*' => ['string', 'distinct', Rule::enum(DukunganDesaBumdes::class)], + 'dukungan_desa_lainnya' => [Rule::excludeIf(! $dukunganLainnya), Rule::requiredIf($dukunganLainnya), 'string', 'max:255'], + 'keterlibatan_koordinasi_desa' => ['nullable', Rule::enum(KeterlibatanKoordinasiBumdes::class)], + 'kemitraan_eksternal' => ['nullable', 'array'], + 'kemitraan_eksternal.*' => ['string', 'distinct', Rule::enum(KemitraanEksternalBumdes::class)], + 'kemitraan_eksternal_lainnya' => [Rule::excludeIf(! $kemitraanLainnya), Rule::requiredIf($kemitraanLainnya), 'string', 'max:255'], + ]; + $saved = collect($this->route('pengurusBumdes')?->kelembagaanAktor?->aset_produktif ?? [])->keyBy('aset'); + $rows = $this->input('aset_produktif', []); + $hasOtherAsset = false; + + foreach (is_array($rows) ? $rows : [] as $index => $row) { + if (! is_array($row)) { + continue; + } + $exists = in_array($row['dipilih'] ?? null, [true, 1, '1'], true); + $assetKey = is_string($row['aset'] ?? null) ? $row['aset'] : ''; + $hasOtherAsset = $hasOtherAsset || ($exists && ($row['aset'] ?? null) === 'lainnya'); + $conditions = array_keys(KondisiAsetProduktifBumdes::options()); + // Kondisi teks lama tetap dapat ditampilkan dan disimpan untuk aset yang sama. + $legacy = KondisiAsetProduktifBumdes::normalizeLegacy(data_get($saved->get($assetKey), 'kondisi')); + if ($legacy !== null) { + $conditions[] = $legacy; + } + $prefix = 'aset_produktif.'.$index.'.'; + $rules[$prefix.'status_kepemilikan_aset'] = [Rule::excludeIf(! $exists), Rule::requiredIf($exists), Rule::enum(StatusKepemilikanAsetBumdes::class)]; + $rules[$prefix.'kondisi'] = [Rule::excludeIf(! $exists), Rule::requiredIf($exists), Rule::in($conditions)]; + $rules[$prefix.'jumlah'] = [Rule::excludeIf(! $exists), 'nullable', 'integer', 'min:0']; + } + $rules['aset_produktif_lainnya'] = [Rule::excludeIf(! $hasOtherAsset), Rule::requiredIf($hasOtherAsset), 'string', 'max:255']; + + $rows = $this->input('infrastruktur_pascapanen', []); + $hasOtherInfrastructure = false; + foreach (is_array($rows) ? $rows : [] as $index => $row) { + if (! is_array($row)) { + continue; + } + $exists = in_array($row['dipilih'] ?? null, [true, 1, '1'], true); + $hasOtherInfrastructure = $hasOtherInfrastructure || ($exists && ($row['infrastruktur'] ?? null) === 'lainnya'); + $prefix = 'infrastruktur_pascapanen.'.$index.'.'; + $rules[$prefix.'kondisi'] = [Rule::excludeIf(! $exists), Rule::requiredIf($exists), Rule::enum(KondisiInfrastrukturBumdes::class)]; + $rules[$prefix.'jumlah'] = [Rule::excludeIf(! $exists), 'nullable', 'integer', 'min:0']; + } + $rules['infrastruktur_pascapanen_lainnya'] = [Rule::excludeIf(! $hasOtherInfrastructure), Rule::requiredIf($hasOtherInfrastructure), 'string', 'max:255']; + + return $rules; + } + + private function pengelolaanRules(): array + { + $hasAudit = $this->boolean('pernah_audit'); + $annualReport = $this->input('pertanggungjawaban_musdes') === PertanggungjawabanMusdesBumdes::TAHUNAN->value; + $hasPades = in_array($this->input('kontribusi_pades'), [KontribusiPadesBumdes::RUTIN->value, KontribusiPadesBumdes::TIDAK_RUTIN->value], true); + $otherVillage = $this->input('cakupan_layanan') === CakupanLayananBumdes::DESA_LAIN->value; + $activePartnership = $this->input('pengalaman_offtaker') === PengalamanOfftakerBumdes::BERJALAN->value; + $stoppedPartnership = $this->input('pengalaman_offtaker') === PengalamanOfftakerBumdes::BERHENTI->value; + $hasOther = fn (string $field, string $value) => is_array($selected = $this->input($field)) && in_array($value, $selected, true); + $otherService = $hasOther('layanan_petani', LayananPetaniBumdes::LAINNYA->value); + $otherObstacle = $hasOther('hambatan_usaha', HambatanUsahaBumdes::LAINNYA->value); + $year = ['integer', 'digits:4', 'min:1900', 'max:'.now()->year]; + + return [ + 'status_pembukuan' => ['nullable', Rule::enum(StatusPembukuanBumdes::class)], + 'format_pembukuan' => ['nullable', Rule::enum(FormatPembukuanBumdes::class)], + 'pernah_audit' => ['nullable', 'boolean'], + 'sifat_audit' => [Rule::excludeIf(! $hasAudit), Rule::requiredIf($hasAudit), 'array', 'min:1'], + 'sifat_audit.*' => [Rule::excludeIf(! $hasAudit), 'string', 'distinct', Rule::enum(SifatAuditBumdes::class)], + 'pelaksana_audit' => [Rule::excludeIf(! $hasAudit), Rule::requiredIf($hasAudit), 'string', 'max:255'], + 'tahun_audit' => [Rule::excludeIf(! $hasAudit), Rule::requiredIf($hasAudit), ...$year], + 'pertanggungjawaban_musdes' => ['nullable', Rule::enum(PertanggungjawabanMusdesBumdes::class)], + 'tahun_laporan_musdes' => [Rule::excludeIf(! $annualReport), Rule::requiredIf($annualReport), ...$year], + 'kondisi_keuangan' => ['nullable', Rule::enum(KondisiKeuanganBumdes::class)], + 'kontribusi_pades' => ['nullable', Rule::enum(KontribusiPadesBumdes::class)], + 'nominal_kontribusi_pades' => [Rule::excludeIf(! $hasPades), Rule::requiredIf($hasPades), 'integer', 'min:0', 'max:9999999999999999'], + 'cakupan_layanan' => ['nullable', Rule::enum(CakupanLayananBumdes::class)], + 'desa_lain_dilayani' => [Rule::excludeIf(! $otherVillage), Rule::requiredIf($otherVillage), 'string', 'max:2000'], + 'jumlah_petani_desa_sendiri' => ['nullable', 'integer', 'min:0', 'max:2147483647'], + 'jumlah_petani_desa_lain' => [Rule::excludeIf(! $otherVillage), Rule::requiredIf($otherVillage), 'integer', 'min:0', 'max:2147483647'], + 'pencatatan_petani' => ['nullable', Rule::enum(PencatatanPetaniBumdes::class)], + 'layanan_petani' => ['nullable', 'array'], + 'layanan_petani.*' => ['string', 'distinct', Rule::enum(LayananPetaniBumdes::class)], + 'layanan_petani_lainnya' => [Rule::excludeIf(! $otherService), Rule::requiredIf($otherService), 'string', 'max:255'], + 'peran_rantai_nilai' => ['nullable', Rule::enum(PeranRantaiNilaiBumdes::class)], + 'pengalaman_offtaker' => ['nullable', Rule::enum(PengalamanOfftakerBumdes::class)], + 'nama_offtaker' => [Rule::excludeIf(! $activePartnership), Rule::requiredIf($activePartnership), 'string', 'max:255'], + 'tahun_mulai_offtaker' => [Rule::excludeIf(! $activePartnership), Rule::requiredIf($activePartnership), ...$year], + 'alasan_berhenti_offtaker' => [Rule::excludeIf(! $stoppedPartnership), Rule::requiredIf($stoppedPartnership), 'string', 'max:2000'], + 'hambatan_usaha' => ['nullable', 'array'], + 'hambatan_usaha.*' => ['string', 'distinct', Rule::enum(HambatanUsahaBumdes::class)], + 'hambatan_usaha_lainnya' => [Rule::excludeIf(! $otherObstacle), Rule::requiredIf($otherObstacle), 'string', 'max:255'], + ]; + } + + protected function prepareForValidation(): void + { + foreach (['jumlah_alokasi_desa', 'nominal_kontribusi_pades'] as $field) { + $amount = $this->input($field); + if (is_string($amount) && preg_match('/^\d{1,3}(\.\d{3})+$/D', $amount)) { + $this->merge([$field => str_replace('.', '', $amount)]); + } + } + $normalize = static fn ($value) => is_scalar($value) ? preg_replace('/\D/', '', (string) $value) : $value; + $data = []; + if ($this->has('modal_awal')) { + $data['modal_awal'] = $normalize($this->input('modal_awal')); + } + if (is_array($this->input('penambahan_modal'))) { + $data['penambahan_modal'] = array_map(function ($row) use ($normalize) { + if (is_array($row) && array_key_exists('jumlah', $row)) { + $row['jumlah'] = $normalize($row['jumlah']); + } + + return $row; + }, $this->input('penambahan_modal')); + } + if (is_array($this->input('aset_produktif'))) { + $legacyOwnership = $this->input('status_kepemilikan_aset') + ?? $this->route('pengurusBumdes')?->kelembagaanAktor?->status_kepemilikan_aset?->value; + $data['aset_produktif'] = array_map(function ($row) use ($legacyOwnership) { + if (! is_array($row)) { + return $row; + } + if (! array_key_exists('status_kepemilikan_aset', $row)) { + $row['status_kepemilikan_aset'] = $legacyOwnership; + } + if (is_scalar($row['kondisi'] ?? null)) { + $row['kondisi'] = KondisiAsetProduktifBumdes::normalizeLegacy($row['kondisi']); + } + + return $row; + }, $this->input('aset_produktif')); + } + $this->merge($data); + } + + public function after(): array + { + return [function (Validator $validator) { + foreach (['dukungan_desa' => DukunganDesaBumdes::TIDAK_ADA->value, 'kemitraan_eksternal' => KemitraanEksternalBumdes::BELUM_ADA->value, 'layanan_petani' => LayananPetaniBumdes::BELUM_ADA->value] as $field => $none) { + $selected = $this->input($field, []); + if (is_array($selected) && in_array($none, $selected, true) && count($selected) > 1) { + $validator->errors()->add($field, match ($field) { + 'dukungan_desa' => 'Tidak ada dukungan khusus tidak boleh dipilih bersama bentuk dukungan lainnya.', + 'kemitraan_eksternal' => 'Belum ada kemitraan eksternal tidak boleh dipilih bersama jenis kemitraan lainnya.', + 'layanan_petani' => 'Belum ada layanan tidak boleh dipilih bersama jenis layanan lainnya.', + }); + } + } + }]; + } + + public function messages(): array + { + return [...parent::messages(), + 'jumlah_alokasi_desa.required_if' => 'Besaran alokasi APBDes/Dana Desa wajib diisi jika ada alokasi tahun berjalan.', + 'jumlah_alokasi_desa.max' => 'Besaran alokasi APBDes/Dana Desa tidak boleh melebihi Rp 9.999.999.999.999.999.', + 'dukungan_desa_lainnya.required' => 'Bentuk dukungan lainnya wajib diisi jika memilih Lainnya.', + 'kemitraan_eksternal_lainnya.required' => 'Jenis kemitraan lainnya wajib diisi jika memilih Lainnya.', + 'nominal_kontribusi_pades.max' => 'Nominal kontribusi PADes tidak boleh melebihi Rp 9.999.999.999.999.999.', + 'sifat_audit.min' => 'Pilih minimal satu sifat audit/pemeriksaan.', + 'distinct' => ':attribute tidak boleh dipilih lebih dari satu kali.', + 'aset_produktif.*.status_kepemilikan_aset.required' => 'Status Kepemilikan wajib diisi jika aset dipilih Ada.', + 'aset_produktif.*.kondisi.required' => 'Kondisi wajib diisi jika aset dipilih Ada.', + 'aset_produktif_lainnya.required' => 'Nama aset lainnya wajib diisi jika dipilih Ada.', + 'infrastruktur_pascapanen.*.kondisi.required' => 'Kondisi wajib diisi jika infrastruktur dipilih Ada.', + 'infrastruktur_pascapanen_lainnya.required' => 'Nama infrastruktur lainnya wajib diisi jika dipilih Ada.', + ]; + } + + public function attributes(): array + { + return [...parent::attributes(), + 'status_pembukuan' => 'Sistem pembukuan dan laporan keuangan', + 'format_pembukuan' => 'Format pembukuan', + 'pernah_audit' => 'Status audit/pemeriksaan', + 'sifat_audit' => 'Sifat audit/pemeriksaan', + 'pelaksana_audit' => 'Pelaksana audit/pemeriksaan', + 'tahun_audit' => 'Tahun terakhir audit/pemeriksaan', + 'pertanggungjawaban_musdes' => 'Pertanggungjawaban ke musyawarah desa', + 'tahun_laporan_musdes' => 'Tahun terakhir laporan ke musyawarah desa', + 'kondisi_keuangan' => 'Kondisi keuangan', + 'kontribusi_pades' => 'Kontribusi ke PADes', + 'nominal_kontribusi_pades' => 'Nominal kontribusi ke PADes', + 'cakupan_layanan' => 'Cakupan wilayah layanan', + 'desa_lain_dilayani' => 'Desa lain yang dilayani', + 'jumlah_petani_desa_sendiri' => 'Jumlah petani dari desa sendiri', + 'jumlah_petani_desa_lain' => 'Jumlah petani dari desa lain', + 'pencatatan_petani' => 'Pencatatan petani yang dilayani', + 'layanan_petani' => 'Layanan bagi petani', + 'layanan_petani_lainnya' => 'Layanan lainnya', + 'peran_rantai_nilai' => 'Peran dalam rantai nilai', + 'pengalaman_offtaker' => 'Pengalaman kemitraan dengan offtaker', + 'nama_offtaker' => 'Nama offtaker', + 'tahun_mulai_offtaker' => 'Tahun mulai kemitraan', + 'alasan_berhenti_offtaker' => 'Alasan kemitraan berhenti', + 'hambatan_usaha' => 'Hambatan usaha', + 'hambatan_usaha_lainnya' => 'Hambatan lainnya', + 'sifat_audit.*' => 'Pilihan sifat audit/pemeriksaan', + 'layanan_petani.*' => 'Pilihan layanan bagi petani', + 'hambatan_usaha.*' => 'Pilihan hambatan usaha', + 'ada_alokasi_desa' => 'Status alokasi APBDes/Dana Desa', + 'jumlah_alokasi_desa' => 'Besaran alokasi APBDes/Dana Desa', + 'dukungan_desa' => 'Bentuk dukungan pemerintah desa', + 'dukungan_desa.*' => 'Pilihan dukungan pemerintah desa', + 'dukungan_desa_lainnya' => 'Bentuk dukungan lainnya', + 'keterlibatan_koordinasi_desa' => 'Keterlibatan dalam rapat/koordinasi desa', + 'kemitraan_eksternal' => 'Kemitraan eksternal', + 'kemitraan_eksternal.*' => 'Pilihan kemitraan eksternal', + 'kemitraan_eksternal_lainnya' => 'Jenis kemitraan lainnya', + 'aset_produktif.*.status_kepemilikan_aset' => 'Status Kepemilikan aset', + ]; + } +} diff --git a/app/Http/Requests/Bumdes/StorePengurusBumdesRequest.php b/app/Http/Requests/Bumdes/StorePengurusBumdesRequest.php new file mode 100644 index 0000000..1ab8b96 --- /dev/null +++ b/app/Http/Requests/Bumdes/StorePengurusBumdesRequest.php @@ -0,0 +1,66 @@ + ['required', 'string', 'max:255'], + 'jenis_kelamin' => ['required', 'boolean'], + 'tempat_lahir' => ['required', 'string', 'max:255'], + 'tanggal_lahir' => ['required', 'date_format:d-m-Y'], + 'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)], + 'alamat' => ['required', 'string', 'max:500'], + 'provinsi_id' => ['bail', 'required', 'integer', 'exists:master_provinsi,id'], + 'kabupaten_id' => ['bail', 'required', 'integer', 'exists:master_kabupaten_kota,id'], + 'kecamatan_id' => ['bail', 'required', 'integer', 'exists:master_kecamatan,id'], + 'desa_kelurahan_id' => ['bail', 'required', 'integer', 'exists:master_desa_kelurahan,id'], + 'nomor_telepon' => ['nullable', 'digits_between:11,13'], + 'jabatan' => ['required', Rule::enum(JabatanBumdes::class)], + 'jabatan_lainnya' => ['nullable', 'required_if:jabatan,'.JabatanBumdes::LAINNYA->value, 'string', 'max:255'], + 'lama_menjabat_tahun' => ['required', 'integer', 'min:0', 'max:80'], + ...$this->sectionRules('profil'), + ]; + } + + public function messages(): array + { + return [ + ...parent::messages(), + 'nama.required' => 'Nama harus diisi.', + 'jenis_kelamin.required' => 'Jenis kelamin harus dipilih.', + 'tempat_lahir.required' => 'Tempat lahir harus diisi.', + 'tanggal_lahir.required' => 'Tanggal lahir harus diisi.', + 'tanggal_lahir.date_format' => 'Tanggal lahir harus menggunakan format tanggal yang valid.', + 'pendidikan_terakhir.required' => 'Pendidikan terakhir harus dipilih.', + 'alamat.required' => 'Alamat harus diisi.', + 'provinsi_id.required' => 'Provinsi wajib dipilih.', + 'kabupaten_id.required' => 'Kabupaten/Kota wajib dipilih.', + 'kecamatan_id.required' => 'Kecamatan wajib dipilih.', + 'provinsi_id.integer' => 'Pilihan Provinsi tidak valid.', + 'kabupaten_id.integer' => 'Pilihan Kabupaten/Kota tidak valid.', + 'kecamatan_id.integer' => 'Pilihan Kecamatan tidak valid.', + 'desa_kelurahan_id.integer' => 'Pilihan Desa/Kelurahan tidak valid.', + 'provinsi_id.exists' => 'Provinsi tidak ditemukan.', + 'kabupaten_id.exists' => 'Kabupaten/Kota tidak ditemukan.', + 'kecamatan_id.exists' => 'Kecamatan tidak ditemukan.', + 'desa_kelurahan_id.required' => 'Desa kelurahan harus dipilih.', + 'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.', + 'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.', + 'jabatan.required' => 'Jabatan/peran harus dipilih.', + 'jabatan_lainnya.required_if' => 'Jabatan lainnya harus diisi.', + 'lama_menjabat_tahun.required' => 'Lama menjabat harus diisi.', + 'lama_menjabat_tahun.integer' => 'Lama menjabat harus berupa angka tahun.', + 'string' => ':attribute harus berupa teks.', + 'max' => ':attribute tidak valid.', + 'min' => ':attribute tidak boleh kurang dari :min.', + 'enum' => 'Pilihan :attribute tidak valid.', + ]; + } +} diff --git a/app/Http/Requests/Bumdes/StoreRantaiNilaiBumdesRequest.php b/app/Http/Requests/Bumdes/StoreRantaiNilaiBumdesRequest.php new file mode 100644 index 0000000..5994dd6 --- /dev/null +++ b/app/Http/Requests/Bumdes/StoreRantaiNilaiBumdesRequest.php @@ -0,0 +1,172 @@ +boolean('melakukan_jual_beli'); + $hasChoice = fn (string $field, string $value) => is_array($selected = $this->input($field)) && in_array($value, $selected, true); + $notSold = $hasChoice('saluran_penjualan', SaluranPenjualanBumdes::BELUM_MENJUAL->value); + $otherSource = $trading && $hasChoice('sumber_pasokan', SumberPasokanBumdes::LAINNYA->value); + $otherChannel = $trading && $hasChoice('saluran_penjualan', SaluranPenjualanBumdes::LAINNYA->value); + $otherRelationship = $trading && $hasChoice('hubungan_petani_pemasok', HubunganPemasokBumdes::LAINNYA->value); + $otherPayment = $trading && $this->input('mekanisme_pembayaran_petani') === MekanismePembayaranBumdes::LAINNYA->value; + $otherPrice = $trading && $this->input('penetapan_harga_beli') === PenetapanHargaBeliBumdes::LAINNYA->value; + $periods = implode(',', array_keys(PeriodePanenBumdes::options())); + $products = implode(',', array_keys(BentukProdukBumdes::options())); + $rules = [...$this->pascapanenRules(), + 'melakukan_jual_beli' => ['required', 'boolean'], + 'redirect_fragment' => ['nullable', Rule::in(['section-rantai-penjualan', 'section-rantai-hubungan-pemasok', 'section-rantai-harga-beli', 'section-rantai-sortasi', 'section-rantai-pengeringan', 'section-rantai-fermentasi', 'section-rantai-premium-harga'])], + 'sumber_pasokan' => [Rule::excludeIf(! $trading), 'nullable', 'array'], + 'sumber_pasokan.*' => [Rule::excludeIf(! $trading), 'string', 'distinct', Rule::enum(SumberPasokanBumdes::class)], + 'sumber_pasokan_lainnya' => [Rule::excludeIf(! $otherSource), Rule::requiredIf($otherSource), 'string', 'max:255'], + 'bentuk_produk_dibeli' => [Rule::excludeIf(! $trading), 'nullable', 'array'], + 'bentuk_produk_dibeli.*' => [Rule::excludeIf(! $trading), 'string', 'distinct', Rule::enum(BentukProdukBumdes::class)], + 'volume_pembelian' => [Rule::excludeIf(! $trading), 'nullable', 'array:'.$periods], + 'volume_pembelian.*' => [Rule::excludeIf(! $trading), 'nullable', 'numeric', 'decimal:0,3', 'min:0', 'max:999999999999.999'], + 'saluran_penjualan' => [Rule::excludeIf(! $trading), 'nullable', 'array'], + 'saluran_penjualan.*' => [Rule::excludeIf(! $trading), 'string', 'distinct', Rule::enum(SaluranPenjualanBumdes::class)], + 'saluran_penjualan_lainnya' => [Rule::excludeIf(! $otherChannel), Rule::requiredIf($otherChannel), 'string', 'max:255'], + 'bentuk_produk_dijual' => [Rule::excludeIf(! $trading || $notSold), 'nullable', 'array'], + 'bentuk_produk_dijual.*' => [Rule::excludeIf(! $trading || $notSold), 'string', 'distinct', Rule::enum(BentukProdukBumdes::class)], + 'volume_penjualan' => [Rule::excludeIf(! $trading || $notSold), 'nullable', 'array:'.$periods], + 'volume_penjualan.*' => [Rule::excludeIf(! $trading || $notSold), 'nullable', 'numeric', 'decimal:0,3', 'min:0', 'max:999999999999.999'], + 'hubungan_petani_pemasok' => [Rule::excludeIf(! $trading), 'nullable', 'array'], + 'hubungan_petani_pemasok.*' => [Rule::excludeIf(! $trading), 'string', 'distinct', Rule::enum(HubunganPemasokBumdes::class)], + 'hubungan_petani_pemasok_lainnya' => [Rule::excludeIf(! $otherRelationship), Rule::requiredIf($otherRelationship), 'string', 'max:255'], + 'mekanisme_pembayaran_petani' => [Rule::excludeIf(! $trading), 'nullable', Rule::enum(MekanismePembayaranBumdes::class)], + 'mekanisme_pembayaran_lainnya' => [Rule::excludeIf(! $otherPayment), Rule::requiredIf($otherPayment), 'string', 'max:255'], + 'perjanjian_petani' => [Rule::excludeIf(! $trading), 'nullable', Rule::enum(PerjanjianPetaniBumdes::class)], + 'harga_beli_petani' => [Rule::excludeIf(! $trading), 'nullable', 'array:'.$products], + 'penetapan_harga_beli' => [Rule::excludeIf(! $trading), 'nullable', Rule::enum(PenetapanHargaBeliBumdes::class)], + 'penetapan_harga_beli_lainnya' => [Rule::excludeIf(! $otherPrice), Rule::requiredIf($otherPrice), 'string', 'max:255'], + ]; + foreach (BentukProdukBumdes::cases() as $product) { + $enabled = $trading && $hasChoice('bentuk_produk_dibeli', $product->value); + $prefix = 'harga_beli_petani.'.$product->value; + $rules[$prefix] = [Rule::excludeIf(! $enabled), 'nullable', 'array:'.$periods]; + $rules[$prefix.'.*'] = [Rule::excludeIf(! $enabled), 'nullable', 'integer', 'min:0', 'max:9999999999999999']; + } + + return $rules; + } + + private function pascapanenRules(): array + { + $processing = $this->boolean('melakukan_pengolahan_pascapanen'); + $sorting = $processing && in_array($this->input('status_sortasi'), [StatusSortasiBumdes::RUTIN_STANDAR->value, StatusSortasiBumdes::TIDAK_KONSISTEN->value], true); + $drying = $processing && in_array($this->input('status_pengeringan'), [StatusPengeringanBumdes::RUTIN->value, StatusPengeringanBumdes::KADANG->value], true); + $fermenting = $processing && in_array($this->input('status_fermentasi'), [StatusFermentasiBumdes::RUTIN->value, StatusFermentasiBumdes::PERNAH_MENCOBA->value], true); + + return [ + 'melakukan_pengolahan_pascapanen' => ['required', 'boolean'], + 'status_sortasi' => [Rule::excludeIf(! $processing), 'nullable', Rule::enum(StatusSortasiBumdes::class)], + 'kriteria_sortasi' => [Rule::excludeIf(! $sorting), Rule::requiredIf($sorting), 'string', 'max:2000'], + 'status_pengeringan' => [Rule::excludeIf(! $processing), 'nullable', Rule::enum(StatusPengeringanBumdes::class)], + 'cara_tempat_pengeringan' => [Rule::excludeIf(! $drying), Rule::requiredIf($drying), 'string', 'max:2000'], + 'status_fermentasi' => [Rule::excludeIf(! $processing), 'nullable', Rule::enum(StatusFermentasiBumdes::class)], + 'model_fermentasi' => [Rule::excludeIf(! $fermenting), Rule::requiredIf($fermenting), 'array', 'min:1'], + 'model_fermentasi.*' => [Rule::excludeIf(! $fermenting), 'string', 'distinct', Rule::enum(ModelFermentasiBumdes::class)], + 'proses_fermentasi' => [Rule::excludeIf(! $fermenting), Rule::requiredIf($fermenting), 'string', 'max:2000'], + 'premium_harga_pascapanen' => [Rule::excludeIf(! $processing), 'nullable', 'integer', 'min:0', 'max:9999999999999999'], + ]; + } + + protected function prepareForValidation(): void + { + $premium = $this->input('premium_harga_pascapanen'); + if (is_string($premium) && preg_match('/^\d{1,3}(\.\d{3})+$/D', $premium)) { + $this->merge(['premium_harga_pascapanen' => str_replace('.', '', $premium)]); + } + $prices = $this->input('harga_beli_petani'); + if (! is_array($prices)) { + return; + } + foreach ($prices as &$row) { + if (! is_array($row)) { + continue; + } + foreach ($row as &$value) { + if (is_string($value) && preg_match('/^\d{1,3}(\.\d{3})+$/D', $value)) { + $value = str_replace('.', '', $value); + } + } + unset($value); + } + unset($row); + $this->merge(['harga_beli_petani' => $prices]); + } + + public function after(): array + { + return [function (Validator $validator) { + $channels = $this->input('saluran_penjualan'); + if ($this->boolean('melakukan_jual_beli') && is_array($channels) && count($channels) > 1 && in_array(SaluranPenjualanBumdes::BELUM_MENJUAL->value, $channels, true)) { + $validator->errors()->add('saluran_penjualan', 'Belum pernah menjual tidak boleh dipilih bersama saluran penjualan lainnya.'); + } + }]; + } + + public function messages(): array + { + return [...parent::messages(), + 'melakukan_pengolahan_pascapanen.required' => 'Kegiatan pengolahan pascapanen wajib dipilih.', + 'melakukan_jual_beli.required' => 'Kegiatan jual beli komoditas wajib dipilih.', + 'model_fermentasi.min' => 'Pilih minimal satu model fermentasi.', + 'premium_harga_pascapanen.max' => 'Premium harga tidak boleh melebihi Rp 9.999.999.999.999.999 per kg.', + 'numeric' => ':attribute harus berupa angka.', + 'decimal' => ':attribute boleh memiliki maksimal 3 angka di belakang koma.', + 'distinct' => ':attribute tidak boleh dipilih lebih dari satu kali.', + 'harga_beli_petani.*.*.max' => 'Harga beli tidak boleh melebihi Rp 9.999.999.999.999.999 per kg.', + ]; + } + + public function attributes(): array + { + return [...parent::attributes(), + 'melakukan_pengolahan_pascapanen' => 'Kegiatan pengolahan pascapanen', + 'status_sortasi' => 'Praktik sortasi', + 'kriteria_sortasi' => 'Standar/kriteria sortasi', + 'status_pengeringan' => 'Praktik pengeringan', + 'cara_tempat_pengeringan' => 'Cara dan tempat pengeringan', + 'status_fermentasi' => 'Praktik fermentasi/pengepresan', + 'model_fermentasi' => 'Model fermentasi', + 'proses_fermentasi' => 'Proses fermentasi', + 'premium_harga_pascapanen' => 'Premium harga per kg', + 'model_fermentasi.*' => 'Pilihan model fermentasi', + 'melakukan_jual_beli' => 'Kegiatan jual beli komoditas', + 'redirect_fragment' => 'Tujuan setelah penyimpanan', + 'sumber_pasokan' => 'Sumber pasokan', 'sumber_pasokan.*' => 'Pilihan sumber pasokan', + 'sumber_pasokan_lainnya' => 'Sumber pasokan lainnya', + 'bentuk_produk_dibeli' => 'Bentuk produk yang dibeli', 'bentuk_produk_dibeli.*' => 'Pilihan bentuk produk yang dibeli', + 'volume_pembelian' => 'Volume pembelian', 'volume_pembelian.*' => 'Volume pembelian pada musim panen terkait', + 'saluran_penjualan' => 'Saluran penjualan', 'saluran_penjualan.*' => 'Pilihan saluran penjualan', + 'saluran_penjualan_lainnya' => 'Saluran penjualan lainnya', + 'bentuk_produk_dijual' => 'Bentuk produk yang dijual', 'bentuk_produk_dijual.*' => 'Pilihan bentuk produk yang dijual', + 'volume_penjualan' => 'Volume penjualan', 'volume_penjualan.*' => 'Volume penjualan pada musim panen terkait', + 'hubungan_petani_pemasok' => 'Hubungan dengan petani pemasok', 'hubungan_petani_pemasok.*' => 'Pilihan hubungan dengan petani pemasok', + 'hubungan_petani_pemasok_lainnya' => 'Hubungan dengan petani pemasok lainnya', + 'mekanisme_pembayaran_petani' => 'Mekanisme pembayaran kepada petani', 'mekanisme_pembayaran_lainnya' => 'Mekanisme pembayaran lainnya', + 'perjanjian_petani' => 'Perjanjian dengan petani', + 'harga_beli_petani' => 'Harga beli dari petani', 'harga_beli_petani.*' => 'Harga beli untuk bentuk produk terkait', 'harga_beli_petani.*.*' => 'Harga beli pada musim panen terkait', + 'penetapan_harga_beli' => 'Penetapan harga beli', 'penetapan_harga_beli_lainnya' => 'Penetapan harga beli lainnya', + ]; + } +} diff --git a/app/Http/Requests/Bumdes/UpdatePengurusBumdesRequest.php b/app/Http/Requests/Bumdes/UpdatePengurusBumdesRequest.php new file mode 100644 index 0000000..bcbd605 --- /dev/null +++ b/app/Http/Requests/Bumdes/UpdatePengurusBumdesRequest.php @@ -0,0 +1,5 @@ + 'integer', - 'tahun_ad_art' => 'integer', - 'unit_usaha' => 'array', - 'pengurus_inti' => 'array', - 'memiliki_staf' => 'boolean', - 'jumlah_staf' => 'integer', - 'modal_awal' => 'decimal:2', - 'tahun_modal_awal' => 'integer', - 'pernah_penambahan_modal' => 'boolean', - 'penambahan_modal' => 'array', - 'aset_produktif' => 'array', - 'infrastruktur_pascapanen' => 'array', - ]; + protected function casts(): array + { + return [ + 'melakukan_pengolahan_pascapanen' => 'boolean', + 'status_sortasi' => StatusSortasiBumdes::class, + 'status_pengeringan' => StatusPengeringanBumdes::class, + 'status_fermentasi' => StatusFermentasiBumdes::class, + 'model_fermentasi' => AsEnumCollection::of(ModelFermentasiBumdes::class), + 'premium_harga_pascapanen' => 'decimal:2', + 'melakukan_jual_beli' => 'boolean', + 'sumber_pasokan' => AsEnumCollection::of(SumberPasokanBumdes::class), + 'bentuk_produk_dibeli' => AsEnumCollection::of(BentukProdukBumdes::class), + 'volume_pembelian' => 'array', + 'saluran_penjualan' => AsEnumCollection::of(SaluranPenjualanBumdes::class), + 'bentuk_produk_dijual' => AsEnumCollection::of(BentukProdukBumdes::class), + 'volume_penjualan' => 'array', + 'hubungan_petani_pemasok' => AsEnumCollection::of(HubunganPemasokBumdes::class), + 'mekanisme_pembayaran_petani' => MekanismePembayaranBumdes::class, + 'perjanjian_petani' => PerjanjianPetaniBumdes::class, + 'harga_beli_petani' => 'array', + 'penetapan_harga_beli' => PenetapanHargaBeliBumdes::class, + 'status_pembukuan' => StatusPembukuanBumdes::class, + 'format_pembukuan' => FormatPembukuanBumdes::class, + 'pernah_audit' => 'boolean', + 'sifat_audit' => AsEnumCollection::of(SifatAuditBumdes::class), + 'tahun_audit' => 'integer', + 'pertanggungjawaban_musdes' => PertanggungjawabanMusdesBumdes::class, + 'tahun_laporan_musdes' => 'integer', + 'kondisi_keuangan' => KondisiKeuanganBumdes::class, + 'kontribusi_pades' => KontribusiPadesBumdes::class, + 'nominal_kontribusi_pades' => 'decimal:2', + 'cakupan_layanan' => CakupanLayananBumdes::class, + 'jumlah_petani_desa_sendiri' => 'integer', + 'jumlah_petani_desa_lain' => 'integer', + 'pencatatan_petani' => PencatatanPetaniBumdes::class, + 'layanan_petani' => AsEnumCollection::of(LayananPetaniBumdes::class), + 'peran_rantai_nilai' => PeranRantaiNilaiBumdes::class, + 'pengalaman_offtaker' => PengalamanOfftakerBumdes::class, + 'tahun_mulai_offtaker' => 'integer', + 'hambatan_usaha' => AsEnumCollection::of(HambatanUsahaBumdes::class), + 'ada_alokasi_desa' => 'boolean', + 'jumlah_alokasi_desa' => 'decimal:2', + 'dukungan_desa' => AsEnumCollection::of(DukunganDesaBumdes::class), + 'keterlibatan_koordinasi_desa' => KeterlibatanKoordinasiBumdes::class, + 'kemitraan_eksternal' => AsEnumCollection::of(KemitraanEksternalBumdes::class), + 'status_badan_hukum' => StatusBadanHukumBumdes::class, + 'status_keaktifan' => StatusKeaktifanBumdes::class, + 'status_unit_komoditas' => StatusUnitKomoditasBumdes::class, + 'status_ad_art' => StatusAdArtBumdes::class, + 'status_kepemilikan_aset' => StatusKepemilikanAsetBumdes::class, + 'tahun_dasar_hukum' => 'integer', + 'tahun_ad_art' => 'integer', + 'unit_usaha' => AsEnumCollection::of(UnitUsahaBumdes::class), + 'pengurus_inti' => 'array', + 'memiliki_staf' => 'boolean', + 'jumlah_staf' => 'integer', + 'modal_awal' => 'decimal:2', + 'tahun_modal_awal' => 'integer', + 'pernah_penambahan_modal' => 'boolean', + 'penambahan_modal' => 'array', + 'aset_produktif' => 'array', + 'infrastruktur_pascapanen' => 'array', + ]; + } + + public static function sectionFields(string $section): array + { + if ($section === 'rantai-nilai') { + return [ + 'melakukan_pengolahan_pascapanen', + 'status_sortasi', + 'kriteria_sortasi', + 'status_pengeringan', + 'cara_tempat_pengeringan', + 'status_fermentasi', + 'model_fermentasi', + 'proses_fermentasi', + 'premium_harga_pascapanen', + 'melakukan_jual_beli', + 'sumber_pasokan', + 'sumber_pasokan_lainnya', + 'bentuk_produk_dibeli', + 'volume_pembelian', + 'saluran_penjualan', + 'saluran_penjualan_lainnya', + 'bentuk_produk_dijual', + 'volume_penjualan', + 'hubungan_petani_pemasok', + 'hubungan_petani_pemasok_lainnya', + 'mekanisme_pembayaran_petani', + 'mekanisme_pembayaran_lainnya', + 'perjanjian_petani', + 'harga_beli_petani', + 'penetapan_harga_beli', + 'penetapan_harga_beli_lainnya', + ]; + } + + if ($section === 'profil') { + return [ + 'status_keaktifan', 'alasan_tidak_aktif', 'pengurus_inti', 'memiliki_staf', + 'jumlah_staf', 'unit_usaha_staf', + ]; + } + + return $section === 'operasional' ? [ + 'modal_awal', 'tahun_modal_awal', + 'pernah_penambahan_modal', 'penambahan_modal', 'aset_produktif', + 'aset_produktif_lainnya', 'status_kepemilikan_aset', 'infrastruktur_pascapanen', + 'infrastruktur_pascapanen_lainnya', + 'ada_alokasi_desa', 'jumlah_alokasi_desa', 'dukungan_desa', + 'dukungan_desa_lainnya', 'keterlibatan_koordinasi_desa', + 'kemitraan_eksternal', 'kemitraan_eksternal_lainnya', + 'status_pembukuan', + 'format_pembukuan', + 'pernah_audit', + 'sifat_audit', + 'pelaksana_audit', + 'tahun_audit', + 'pertanggungjawaban_musdes', + 'tahun_laporan_musdes', + 'kondisi_keuangan', + 'kontribusi_pades', + 'nominal_kontribusi_pades', + 'cakupan_layanan', + 'desa_lain_dilayani', + 'jumlah_petani_desa_sendiri', + 'jumlah_petani_desa_lain', + 'pencatatan_petani', + 'layanan_petani', + 'layanan_petani_lainnya', + 'peran_rantai_nilai', + 'pengalaman_offtaker', + 'nama_offtaker', + 'tahun_mulai_offtaker', + 'alasan_berhenti_offtaker', + 'hambatan_usaha', + 'hambatan_usaha_lainnya', + ] : [ + 'status_badan_hukum', 'nomor_dasar_hukum', 'tahun_dasar_hukum', 'unit_usaha', + 'unit_usaha_jasa_lainnya', 'status_unit_komoditas', 'nama_unit_komoditas', + 'status_ad_art', 'tahun_ad_art', + ]; + } public function pengurusBumdes(): BelongsTo { diff --git a/app/Models/Bumdes/PengurusBumdes.php b/app/Models/Bumdes/PengurusBumdes.php index 890b6de..1565dad 100644 --- a/app/Models/Bumdes/PengurusBumdes.php +++ b/app/Models/Bumdes/PengurusBumdes.php @@ -2,9 +2,10 @@ namespace App\Models\Bumdes; -use App\Models\Concerns\CatatAktivitas; use App\Enums\Bumdes\JabatanBumdes; use App\Enums\Pendidikan; +use App\Models\Concerns\CatatAktivitas; +use App\Models\Concerns\HasDeletedBy; use App\Models\User; use App\Models\Wilayah\DesaKelurahan; use Carbon\Carbon; @@ -15,7 +16,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; class PengurusBumdes extends Model { - use SoftDeletes, CatatAktivitas; + use CatatAktivitas, HasDeletedBy, SoftDeletes; protected $table = 'bumdes_pengurus'; @@ -38,13 +39,33 @@ class PengurusBumdes extends Model 'deleted_by', ]; - protected $casts = [ - 'jenis_kelamin' => 'boolean', - 'tanggal_lahir' => 'date', - 'pendidikan_terakhir' => Pendidikan::class, - 'tanggal_mulai_menjabat' => 'date', - 'jabatan' => JabatanBumdes::class, - ]; + protected function casts(): array + { + return [ + 'jenis_kelamin' => 'boolean', + 'tanggal_lahir' => 'date', + 'pendidikan_terakhir' => Pendidikan::class, + 'tanggal_mulai_menjabat' => 'date', + 'jabatan' => JabatanBumdes::class, + ]; + } + + protected static function booted(): void + { + static::deleting(function (self $pengurus) { + if (! $pengurus->isForceDeleting()) { + $pengurus->kelembagaanAktor()->each(fn ($data) => $data->delete()); + } + }); + } + + public function profilLengkap(): bool + { + return $this->exists && collect([ + 'nama', 'tempat_lahir', 'tanggal_lahir', 'pendidikan_terakhir', 'alamat', + 'desa_kelurahan_id', 'jabatan', 'lama_menjabat_tahun', + ])->every(fn ($field) => filled($this->{$field})) && $this->jenis_kelamin !== null; + } public function getUsiaAttribute(): ?int { @@ -58,7 +79,7 @@ class PengurusBumdes extends Model public function getLamaMenjabatAttribute(): string { if (! is_null($this->lama_menjabat_tahun)) { - return $this->lama_menjabat_tahun . ' tahun'; + return $this->lama_menjabat_tahun.' tahun'; } if (! $this->tanggal_mulai_menjabat) { @@ -75,15 +96,15 @@ class PengurusBumdes extends Model $bagian = []; if ($selisih->y > 0) { - $bagian[] = $selisih->y . ' tahun'; + $bagian[] = $selisih->y.' tahun'; } if ($selisih->m > 0) { - $bagian[] = $selisih->m . ' bulan'; + $bagian[] = $selisih->m.' bulan'; } if (empty($bagian)) { - return $selisih->d . ' hari'; + return $selisih->d.' hari'; } return implode(' ', $bagian); diff --git a/app/Services/Bumdes/PengurusBumdesService.php b/app/Services/Bumdes/PengurusBumdesService.php new file mode 100644 index 0000000..9844bd8 --- /dev/null +++ b/app/Services/Bumdes/PengurusBumdesService.php @@ -0,0 +1,633 @@ +select('id', 'nama') + ->whereRaw('lower(nama) like ?', ['%ngada%'])->orderBy('nama')->first(); + $filters = [ + 'search' => $validated['search'] ?? null, + 'kabupaten_kota_id' => $validated['kabupaten_kota_id'] ?? $defaultKabupaten?->id, + 'kecamatan_id' => $validated['kecamatan_id'] ?? null, + 'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? null, + 'status_keaktifan' => $validated['status_keaktifan'] ?? null, + 'sort_by' => in_array($validated['sort_by'] ?? null, ['nama_asc', 'nama_desc'], true) + ? $validated['sort_by'] : null, + ]; + + $query = $this->pengurusQuery()->with([ + ...$this->wilayahRelationsWithoutProgramScope(), + 'creator:id,name', + 'kelembagaanAktor', + ]); + + if (filled($filters['search'])) { + $search = '%'.Str::lower($filters['search']).'%'; + $query->where(function ($query) use ($search) { + $query->whereRaw('lower(nama) like ?', [$search]) + ->orWhereHas('desaKelurahan', function ($query) use ($search) { + $query->withoutGlobalScope('program')->where(function ($query) use ($search) { + $query->whereRaw('lower(nama) like ?', [$search]) + ->orWhereHas('kecamatan', fn ($query) => $query->withoutGlobalScope('program')->whereRaw('lower(nama) like ?', [$search])); + }); + }); + }); + } + + $query->when($filters['kabupaten_kota_id'], function ($query, $id) { + $query->whereHas('desaKelurahan', function ($query) use ($id) { + $query->withoutGlobalScope('program')->whereHas('kecamatan', function ($query) use ($id) { + $query->withoutGlobalScope('program')->where('kabupaten_kota_id', $id); + }); + }); + })->when($filters['kecamatan_id'], function ($query, $id) { + $query->whereHas('desaKelurahan', fn ($query) => $query->withoutGlobalScope('program')->where('kecamatan_id', $id)); + })->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id)); + + $query->when($filters['status_keaktifan'], fn ($query, $value) => $query + ->whereHas('kelembagaanAktor', fn ($query) => $query->where('status_keaktifan', $value))); + + $summaryQuery = clone $query; + $totalBumdes = (clone $summaryQuery)->count(); + $totalBumdesAktif = (clone $summaryQuery)->whereHas('kelembagaanAktor', fn ($query) => $query + ->whereIn('status_keaktifan', [StatusKeaktifanBumdes::AKTIF_PENUH->value, StatusKeaktifanBumdes::AKTIF_TERBATAS->value]))->count(); + $totalBerbadanHukum = (clone $summaryQuery)->whereHas('kelembagaanAktor', fn ($query) => $query + ->where('status_badan_hukum', StatusBadanHukumBumdes::SUDAH_BERBADAN_HUKUM->value))->count(); + $totalDesaKelurahan = (clone $summaryQuery)->distinct('desa_kelurahan_id')->count('desa_kelurahan_id'); + + if ($filters['sort_by']) { + $namaDesa = DesaKelurahan::withoutGlobalScope('program')->select('nama') + ->whereColumn('master_desa_kelurahan.id', 'bumdes_pengurus.desa_kelurahan_id')->limit(1); + $query->orderBy($namaDesa, $filters['sort_by'] === 'nama_asc' ? 'asc' : 'desc'); + } else { + $query->latest(); + } + $perPage = (int) ($validated['per_page'] ?? 10); + $pengurus = $query->orderByDesc('id')->paginate($perPage)->appends([...$filters, 'per_page' => $perPage]); + $ringkasanBumdes = $pengurus->getCollection() + ->mapWithKeys(fn (PengurusBumdes $item) => [$item->id => $this->profileSummary($item)]); + $selectedKabupaten = $filters['kabupaten_kota_id'] + ? KabupatenKota::withoutGlobalScope('program')->select('id', 'nama')->find($filters['kabupaten_kota_id']) : null; + $kecamatanOptions = $filters['kabupaten_kota_id'] + ? Kecamatan::withoutGlobalScope('program')->select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get() + : collect(); + $desaOptions = $filters['kecamatan_id'] + ? DesaKelurahan::withoutGlobalScope('program')->select('id', 'nama')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get() + : collect(); + $statusKeaktifanOptions = StatusKeaktifanBumdes::options(); + $userRole = auth()->user()->role->value; + $showEnumerator = in_array($userRole, ['super_admin', 'admin']); + + return compact( + 'pengurus', + 'ringkasanBumdes', + 'totalBumdes', + 'totalBumdesAktif', + 'totalBerbadanHukum', + 'totalDesaKelurahan', + 'showEnumerator', 'filters', 'selectedKabupaten', 'kecamatanOptions', 'desaOptions', + 'statusKeaktifanOptions' + ); + } + + public function profileData(?PengurusBumdes $pengurus = null): array + { + $pengurus?->loadMissing([...$this->wilayahRelationsWithoutProgramScope(), 'kelembagaanAktor']); + + return [ + 'pengurus' => $pengurus, + 'ringkasanProfil' => $this->profileSummary($pengurus), + 'provinsi' => $this->provinsiOptions(), + 'jabatan' => JabatanBumdes::options(), + 'pendidikan' => Pendidikan::options(), + ...$this->profileLocationOptions($pengurus), + ...$this->profileBumdesOptions($pengurus), + ]; + } + + public function sectionData(PengurusBumdes $pengurus): array + { + $pengurus->loadMissing([...$this->wilayahRelationsWithoutProgramScope(), 'kelembagaanAktor']); + + return [ + 'pengurus' => $pengurus, + 'ringkasanProfil' => $this->profileSummary($pengurus), + 'kelembagaanAktor' => $pengurus->kelembagaanAktor?->attributesToArray(), + ...$this->kelembagaanAktorOptions(), + ]; + } + + private function profileSummary(?PengurusBumdes $pengurus): array + { + $data = $pengurus?->kelembagaanAktor; + $desa = $pengurus?->desaKelurahan; + $keaktifan = $data?->status_keaktifan; + $hukum = $data?->status_badan_hukum; + $unitUsaha = collect($data?->unit_usaha ?? [])->map(function (UnitUsahaBumdes $unit) use ($data) { + return $unit === UnitUsahaBumdes::JASA_LAINNYA && filled($data->unit_usaha_jasa_lainnya) + ? $unit->label().': '.$data->unit_usaha_jasa_lainnya : $unit->label(); + })->all(); + $updatedAt = collect([ + $pengurus?->updated_at ?? $pengurus?->created_at, + $data?->updated_at ?? $data?->created_at, + ])->filter()->sortByDesc(fn ($date) => $date->getTimestamp())->first(); + + return [ + 'judul' => $pengurus + ? ($desa?->nama ? 'BUMDes · '.$desa->nama : 'Profil BUMDes') : 'Profil BUMDes Baru', + 'lokasi' => collect([ + $desa?->kecamatan?->nama, $desa?->kecamatan?->kabupatenKota?->nama, + $desa?->kecamatan?->kabupatenKota?->provinsi?->nama, + ])->filter(fn ($value) => filled($value))->implode(' · ') ?: 'Lokasi belum terisi', + 'alamat' => $pengurus?->alamat ?: 'Belum terisi', + 'keaktifan' => [ + 'label' => $keaktifan?->label() ?? 'Belum terisi', + 'warna' => match ($keaktifan) { + StatusKeaktifanBumdes::AKTIF_PENUH => 'success', + StatusKeaktifanBumdes::AKTIF_TERBATAS => 'warning', + StatusKeaktifanBumdes::TIDAK_AKTIF_VAKUM => 'danger', + default => 'secondary', + }, + ], + 'hukum' => [ + 'label' => $hukum?->label() ?? 'Belum terisi', + 'warna' => match ($hukum) { + StatusBadanHukumBumdes::SUDAH_BERBADAN_HUKUM => 'success', + StatusBadanHukumBumdes::PROSES_PENDAFTARAN => 'warning', + default => 'secondary', + }, + ], + 'unit_usaha' => $unitUsaha, + 'responden' => $pengurus?->nama ?: 'Belum terisi', + 'jabatan' => $pengurus ? $pengurus->nama_jabatan : 'Belum terisi', + 'nomor_telepon' => $pengurus?->nomor_telepon ?: 'Belum terisi', + 'diperbarui' => $updatedAt, + ]; + } + + public function storeProfile(array $data): PengurusBumdes + { + return DB::transaction(function () use ($data) { + $pengurus = PengurusBumdes::create([ + ...$this->respondentData($data), + 'uid' => 'BMD-'.strtoupper(Str::random(10)), + 'created_by' => auth()->id(), 'updated_by' => auth()->id(), + ]); + $this->saveSection($pengurus, $data, 'profil'); + + return $pengurus; + }); + } + + public function updateProfile(PengurusBumdes $pengurus, array $data): void + { + DB::transaction(function () use ($pengurus, $data) { + $pengurus->update([...$this->respondentData($data), 'updated_by' => auth()->id()]); + $this->saveSection($pengurus, $data, 'profil'); + }); + } + + private function respondentData(array $data): array + { + $data = array_intersect_key($data, array_flip([ + 'nama', 'jenis_kelamin', 'tempat_lahir', 'tanggal_lahir', 'pendidikan_terakhir', + 'alamat', 'desa_kelurahan_id', 'nomor_telepon', 'jabatan', 'jabatan_lainnya', 'lama_menjabat_tahun', + ])); + + return $this->formatData($data); + } + + public function saveSection(PengurusBumdes $pengurus, array $validatedData, string $section): void + { + $data = array_intersect_key( + $section === 'rantai-nilai' ? $this->formatRantaiNilaiData($validatedData) : $this->formatKelembagaanAktorData($validatedData), + array_flip(KelembagaanAktorBumdes::sectionFields($section)) + ); + + // Kolom kepemilikan lama tetap dipertahankan untuk data sebelum kepemilikan dipisah per aset. + if ($section === 'operasional' && ! array_key_exists('status_kepemilikan_aset', $validatedData)) { + unset($data['status_kepemilikan_aset']); + } + + KelembagaanAktorBumdes::updateOrCreate( + ['pengurus_bumdes_id' => $pengurus->id], + [ + ...$data, + 'created_by' => $pengurus->kelembagaanAktor?->created_by ?? auth()->id(), + 'updated_by' => auth()->id(), + ] + ); + } + + private function profileBumdesOptions(?PengurusBumdes $pengurus = null): array + { + return [ + 'kelembagaanAktor' => $pengurus?->kelembagaanAktor?->attributesToArray(), + 'statusKeaktifanOptions' => StatusKeaktifanBumdes::options(), + 'pengurusIntiRows' => JabatanPengurusIntiBumdes::options(), + ]; + } + + private function pengurusQuery() + { + $query = PengurusBumdes::query(); + + if (auth()->user()->role === Role::ENUMERATOR) { + $query->where('created_by', auth()->id()); + } + + return $query; + } + + // Master wilayah BUMDes tidak dibatasi lokasi program, sama seperti halaman profilnya. + private function provinsiOptions() + { + return Provinsi::withoutGlobalScope('program') + ->select('id', 'kode', 'nama') + ->orderBy('kode', 'asc') + ->get(); + } + + private function wilayahRelationsWithoutProgramScope(): array + { + return [ + 'desaKelurahan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kecamatan_id'), + 'desaKelurahan.kecamatan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kabupaten_kota_id'), + 'desaKelurahan.kecamatan.kabupatenKota' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'provinsi_id'), + 'desaKelurahan.kecamatan.kabupatenKota.provinsi' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama'), + ]; + } + + private function profileLocationOptions(?PengurusBumdes $pengurus = null): array + { + $locationId = static fn ($value) => is_scalar($value) && filter_var($value, FILTER_VALIDATE_INT) !== false ? (int) $value : null; + $provinsiId = $locationId(old('provinsi_id', $pengurus?->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi_id)); + $kabupatenId = $locationId(old('kabupaten_id', $pengurus?->desaKelurahan?->kecamatan?->kabupaten_kota_id)); + $kecamatanId = $locationId(old('kecamatan_id', $pengurus?->desaKelurahan?->kecamatan_id)); + + return [ + 'kabupatenOptions' => $provinsiId ? KabupatenKota::withoutGlobalScope('program')->where('provinsi_id', $provinsiId)->orderBy('nama')->get() : collect(), + 'kecamatanOptions' => $kabupatenId ? Kecamatan::withoutGlobalScope('program')->where('kabupaten_kota_id', $kabupatenId)->orderBy('nama')->get() : collect(), + 'desaOptions' => $kecamatanId ? DesaKelurahan::withoutGlobalScope('program')->where('kecamatan_id', $kecamatanId)->orderBy('nama')->get() : collect(), + ]; + } + + private function formatData(array $data): array + { + $data['tanggal_lahir'] = Carbon::createFromFormat('d-m-Y', $data['tanggal_lahir'])->format('Y-m-d'); + $data['tanggal_mulai_menjabat'] = Carbon::now() + ->subYears((int) $data['lama_menjabat_tahun']) + ->format('Y-m-d'); + + if (($data['jabatan'] ?? null) !== JabatanBumdes::LAINNYA->value) { + $data['jabatan_lainnya'] = null; + } + + return $data; + } + + private function formatRantaiNilaiData(array $data): array + { + $defaults = array_fill_keys(KelembagaanAktorBumdes::sectionFields('rantai-nilai'), null); + foreach (['sumber_pasokan', 'bentuk_produk_dibeli', 'volume_pembelian', 'saluran_penjualan', 'bentuk_produk_dijual', 'volume_penjualan', 'hubungan_petani_pemasok', 'harga_beli_petani'] as $field) { + $defaults[$field] = []; + } + $postharvest = $this->formatPascapanenData($data); + $defaults['melakukan_jual_beli'] = in_array($data['melakukan_jual_beli'] ?? null, [true, 1, '1'], true); + if (! $defaults['melakukan_jual_beli']) { + return array_replace($defaults, $postharvest); + } + $payload = array_replace($defaults, $data); + foreach (['sumber_pasokan', 'bentuk_produk_dibeli', 'saluran_penjualan', 'bentuk_produk_dijual', 'hubungan_petani_pemasok'] as $field) { + $payload[$field] = array_values($payload[$field] ?? []); + } + foreach (['sumber_pasokan' => 'sumber_pasokan_lainnya', 'saluran_penjualan' => 'saluran_penjualan_lainnya', 'hubungan_petani_pemasok' => 'hubungan_petani_pemasok_lainnya'] as $field => $other) { + if (! in_array('lainnya', $payload[$field], true)) { + $payload[$other] = null; + } + } + if ($payload['mekanisme_pembayaran_petani'] !== MekanismePembayaranBumdes::LAINNYA->value) { + $payload['mekanisme_pembayaran_lainnya'] = null; + } + if ($payload['penetapan_harga_beli'] !== PenetapanHargaBeliBumdes::LAINNYA->value) { + $payload['penetapan_harga_beli_lainnya'] = null; + } + $periods = array_keys(PeriodePanenBumdes::options()); + foreach (['volume_pembelian', 'volume_penjualan'] as $field) { + $payload[$field] = collect($payload[$field] ?? [])->only($periods)->filter(fn ($value) => $value !== null && $value !== '')->all(); + } + if (in_array(SaluranPenjualanBumdes::BELUM_MENJUAL->value, $payload['saluran_penjualan'], true)) { + $payload['bentuk_produk_dijual'] = []; + $payload['volume_penjualan'] = []; + } + $payload['harga_beli_petani'] = collect($payload['harga_beli_petani'] ?? [])->only($payload['bentuk_produk_dibeli']) + ->map(fn ($row) => collect($row)->only($periods)->filter(fn ($value) => $value !== null && $value !== '')->all()) + ->filter(fn ($row) => count($row) > 0)->all(); + + return array_replace($payload, $postharvest); + } + + private function formatPascapanenData(array $data): array + { + $processing = in_array($data['melakukan_pengolahan_pascapanen'] ?? null, [true, 1, '1'], true); + $payload = [ + 'melakukan_pengolahan_pascapanen' => ($data['melakukan_pengolahan_pascapanen'] ?? null) === null ? null : $processing, + 'status_sortasi' => null, 'kriteria_sortasi' => null, + 'status_pengeringan' => null, 'cara_tempat_pengeringan' => null, + 'status_fermentasi' => null, 'model_fermentasi' => [], + 'proses_fermentasi' => null, 'premium_harga_pascapanen' => null, + ]; + if (! $processing) { + return $payload; + } + $payload = array_replace($payload, array_intersect_key($data, $payload)); + if (! in_array($payload['status_sortasi'], [StatusSortasiBumdes::RUTIN_STANDAR->value, StatusSortasiBumdes::TIDAK_KONSISTEN->value], true)) { + $payload['kriteria_sortasi'] = null; + } + if (! in_array($payload['status_pengeringan'], [StatusPengeringanBumdes::RUTIN->value, StatusPengeringanBumdes::KADANG->value], true)) { + $payload['cara_tempat_pengeringan'] = null; + } + if (! in_array($payload['status_fermentasi'], [StatusFermentasiBumdes::RUTIN->value, StatusFermentasiBumdes::PERNAH_MENCOBA->value], true)) { + $payload['model_fermentasi'] = []; + $payload['proses_fermentasi'] = null; + } + $payload['model_fermentasi'] = array_values($payload['model_fermentasi'] ?? []); + + return $payload; + } + + private function formatKelembagaanAktorData(array $data): array + { + $payload = array_merge([ + 'status_badan_hukum' => null, + 'nomor_dasar_hukum' => null, + 'tahun_dasar_hukum' => null, + 'status_keaktifan' => null, + 'alasan_tidak_aktif' => null, + 'unit_usaha' => [], + 'unit_usaha_jasa_lainnya' => null, + 'status_unit_komoditas' => null, + 'nama_unit_komoditas' => null, + 'status_ad_art' => null, + 'tahun_ad_art' => null, + 'pengurus_inti' => [], + 'memiliki_staf' => null, + 'jumlah_staf' => null, + 'unit_usaha_staf' => null, + 'modal_awal' => null, + 'tahun_modal_awal' => null, + 'pernah_penambahan_modal' => null, + 'penambahan_modal' => [], + 'aset_produktif' => [], + 'aset_produktif_lainnya' => null, + 'status_kepemilikan_aset' => null, + 'infrastruktur_pascapanen' => [], + 'infrastruktur_pascapanen_lainnya' => null, + 'status_pembukuan' => null, + 'format_pembukuan' => null, + 'pernah_audit' => null, + 'sifat_audit' => [], + 'pelaksana_audit' => null, + 'tahun_audit' => null, + 'pertanggungjawaban_musdes' => null, + 'tahun_laporan_musdes' => null, + 'kondisi_keuangan' => null, + 'kontribusi_pades' => null, + 'nominal_kontribusi_pades' => null, + 'cakupan_layanan' => null, + 'desa_lain_dilayani' => null, + 'jumlah_petani_desa_sendiri' => null, + 'jumlah_petani_desa_lain' => null, + 'pencatatan_petani' => null, + 'layanan_petani' => [], + 'layanan_petani_lainnya' => null, + 'peran_rantai_nilai' => null, + 'pengalaman_offtaker' => null, + 'nama_offtaker' => null, + 'tahun_mulai_offtaker' => null, + 'alasan_berhenti_offtaker' => null, + 'hambatan_usaha' => [], + 'hambatan_usaha_lainnya' => null, + 'ada_alokasi_desa' => null, + 'jumlah_alokasi_desa' => null, + 'dukungan_desa' => [], + 'dukungan_desa_lainnya' => null, + 'keterlibatan_koordinasi_desa' => null, + 'kemitraan_eksternal' => [], + 'kemitraan_eksternal_lainnya' => null, + ], $data); + + foreach (['sifat_audit', 'layanan_petani', 'hambatan_usaha'] as $field) { + $payload[$field] = array_values($payload[$field] ?? []); + } + if (! (bool) $payload['pernah_audit']) { + $payload['sifat_audit'] = []; + $payload['pelaksana_audit'] = null; + $payload['tahun_audit'] = null; + } + if ($payload['pertanggungjawaban_musdes'] !== PertanggungjawabanMusdesBumdes::TAHUNAN->value) { + $payload['tahun_laporan_musdes'] = null; + } + if (! in_array($payload['kontribusi_pades'], [KontribusiPadesBumdes::RUTIN->value, KontribusiPadesBumdes::TIDAK_RUTIN->value], true)) { + $payload['nominal_kontribusi_pades'] = null; + } + if ($payload['cakupan_layanan'] !== CakupanLayananBumdes::DESA_LAIN->value) { + $payload['desa_lain_dilayani'] = null; + $payload['jumlah_petani_desa_lain'] = null; + } + if (! in_array(LayananPetaniBumdes::LAINNYA->value, $payload['layanan_petani'], true)) { + $payload['layanan_petani_lainnya'] = null; + } + if (! in_array(HambatanUsahaBumdes::LAINNYA->value, $payload['hambatan_usaha'], true)) { + $payload['hambatan_usaha_lainnya'] = null; + } + if ($payload['pengalaman_offtaker'] !== PengalamanOfftakerBumdes::BERJALAN->value) { + $payload['nama_offtaker'] = null; + $payload['tahun_mulai_offtaker'] = null; + } + if ($payload['pengalaman_offtaker'] !== PengalamanOfftakerBumdes::BERHENTI->value) { + $payload['alasan_berhenti_offtaker'] = null; + } + + $payload['dukungan_desa'] = array_values($payload['dukungan_desa'] ?? []); + $payload['kemitraan_eksternal'] = array_values($payload['kemitraan_eksternal'] ?? []); + if (! (bool) $payload['ada_alokasi_desa']) { + $payload['jumlah_alokasi_desa'] = null; + } + if (! in_array(DukunganDesaBumdes::LAINNYA->value, $payload['dukungan_desa'], true)) { + $payload['dukungan_desa_lainnya'] = null; + } + if (! in_array(KemitraanEksternalBumdes::LAINNYA->value, $payload['kemitraan_eksternal'], true)) { + $payload['kemitraan_eksternal_lainnya'] = null; + } + + $payload['unit_usaha'] = array_values($payload['unit_usaha'] ?? []); + $payload['pengurus_inti'] = $this->filterRows($payload['pengurus_inti'] ?? [], ['nama']); + $payload['penambahan_modal'] = $this->filterRows($payload['penambahan_modal'] ?? [], ['tahun', 'jumlah']); + foreach (['aset_produktif', 'infrastruktur_pascapanen'] as $field) { + $payload[$field] = collect($payload[$field] ?? []) + ->filter(fn ($row) => in_array($row['dipilih'] ?? null, [true, 1, '1'], true)) + ->values()->all(); + } + + if (($payload['status_badan_hukum'] ?? null) !== 'sudah_berbadan_hukum') { + $payload['nomor_dasar_hukum'] = null; + $payload['tahun_dasar_hukum'] = null; + } + + if (($payload['status_keaktifan'] ?? null) !== 'tidak_aktif_vakum') { + $payload['alasan_tidak_aktif'] = null; + } + + if (! in_array('jasa_lainnya', $payload['unit_usaha'], true)) { + $payload['unit_usaha_jasa_lainnya'] = null; + } + + if (($payload['status_unit_komoditas'] ?? null) !== 'ya') { + $payload['nama_unit_komoditas'] = null; + } + + if (($payload['status_ad_art'] ?? null) !== 'ada_lengkap') { + $payload['tahun_ad_art'] = null; + } + + if (! (bool) ($payload['memiliki_staf'] ?? false)) { + $payload['jumlah_staf'] = null; + $payload['unit_usaha_staf'] = null; + } + + if (! (bool) ($payload['pernah_penambahan_modal'] ?? false)) { + $payload['penambahan_modal'] = null; + } + + if (! collect($payload['aset_produktif'] ?? [])->contains(fn ($row) => ($row['aset'] ?? null) === 'lainnya')) { + $payload['aset_produktif_lainnya'] = null; + } + + if (! collect($payload['infrastruktur_pascapanen'] ?? [])->contains(fn ($row) => ($row['infrastruktur'] ?? null) === 'lainnya')) { + $payload['infrastruktur_pascapanen_lainnya'] = null; + } + + return $payload; + } + + private function filterRows(array $rows, array $valueKeys): array + { + return collect($rows) + ->map(function ($row) { + return collect($row) + ->map(fn ($value) => $value === '' ? null : $value) + ->all(); + }) + ->filter(function ($row) use ($valueKeys) { + foreach ($valueKeys as $key) { + if (filled($row[$key] ?? null)) { + return true; + } + } + + return false; + }) + ->values() + ->all(); + } + + private function kelembagaanAktorOptions(): array + { + return [ + 'statusSortasiOptions' => StatusSortasiBumdes::options(), + 'statusPengeringanOptions' => StatusPengeringanBumdes::options(), + 'statusFermentasiOptions' => StatusFermentasiBumdes::options(), + 'modelFermentasiOptions' => ModelFermentasiBumdes::options(), + 'sumberPasokanOptions' => SumberPasokanBumdes::options(), + 'bentukProdukOptions' => BentukProdukBumdes::options(), + 'periodePanenOptions' => PeriodePanenBumdes::options(), + 'saluranPenjualanOptions' => SaluranPenjualanBumdes::options(), + 'hubunganPemasokOptions' => HubunganPemasokBumdes::options(), + 'mekanismePembayaranOptions' => MekanismePembayaranBumdes::options(), + 'perjanjianPetaniOptions' => PerjanjianPetaniBumdes::options(), + 'penetapanHargaBeliOptions' => PenetapanHargaBeliBumdes::options(), + 'statusBadanHukumOptions' => StatusBadanHukumBumdes::options(), + 'statusKeaktifanOptions' => StatusKeaktifanBumdes::options(), + 'unitUsahaOptions' => UnitUsahaBumdes::options(), + 'statusUnitKomoditasOptions' => StatusUnitKomoditasBumdes::options(), + 'statusAdArtOptions' => StatusAdArtBumdes::options(), + 'pengurusIntiRows' => JabatanPengurusIntiBumdes::options(), + 'asetProduktifRows' => AsetProduktifBumdes::options(), + 'kondisiAsetProduktifOptions' => KondisiAsetProduktifBumdes::options(), + 'statusKepemilikanAsetOptions' => StatusKepemilikanAsetBumdes::options(), + 'infrastrukturPascapanenRows' => InfrastrukturPascapanenBumdes::options(), + 'statusPembukuanOptions' => StatusPembukuanBumdes::options(), + 'formatPembukuanOptions' => FormatPembukuanBumdes::options(), + 'sifatAuditOptions' => SifatAuditBumdes::options(), + 'pertanggungjawabanMusdesOptions' => PertanggungjawabanMusdesBumdes::options(), + 'kondisiKeuanganOptions' => KondisiKeuanganBumdes::options(), + 'kontribusiPadesOptions' => KontribusiPadesBumdes::options(), + 'cakupanLayananOptions' => CakupanLayananBumdes::options(), + 'pencatatanPetaniOptions' => PencatatanPetaniBumdes::options(), + 'layananPetaniOptions' => LayananPetaniBumdes::options(), + 'peranRantaiNilaiOptions' => PeranRantaiNilaiBumdes::options(), + 'pengalamanOfftakerOptions' => PengalamanOfftakerBumdes::options(), + 'hambatanUsahaOptions' => HambatanUsahaBumdes::options(), + 'dukunganDesaOptions' => DukunganDesaBumdes::options(), + 'keterlibatanKoordinasiOptions' => KeterlibatanKoordinasiBumdes::options(), + 'kemitraanEksternalOptions' => KemitraanEksternalBumdes::options(), + 'kondisiInfrastrukturOptions' => KondisiInfrastrukturBumdes::options(), + ]; + } +} diff --git a/database/migrations/2026_11_04_000001_add_dukungan_ekosistem_to_bumdes_kelembagaan_aktor_table.php b/database/migrations/2026_11_04_000001_add_dukungan_ekosistem_to_bumdes_kelembagaan_aktor_table.php new file mode 100644 index 0000000..9cf8700 --- /dev/null +++ b/database/migrations/2026_11_04_000001_add_dukungan_ekosistem_to_bumdes_kelembagaan_aktor_table.php @@ -0,0 +1,32 @@ +boolean('ada_alokasi_desa')->nullable(); + $table->decimal('jumlah_alokasi_desa', 18, 2)->nullable(); + $table->json('dukungan_desa')->nullable(); + $table->string('dukungan_desa_lainnya')->nullable(); + $table->string('keterlibatan_koordinasi_desa')->nullable(); + $table->json('kemitraan_eksternal')->nullable(); + $table->string('kemitraan_eksternal_lainnya')->nullable(); + }); + } + + public function down(): void + { + Schema::table('bumdes_kelembagaan_aktor', function (Blueprint $table) { + $table->dropColumn([ + 'ada_alokasi_desa', 'jumlah_alokasi_desa', 'dukungan_desa', + 'dukungan_desa_lainnya', 'keterlibatan_koordinasi_desa', + 'kemitraan_eksternal', 'kemitraan_eksternal_lainnya', + ]); + }); + } +}; diff --git a/database/migrations/2026_11_04_000002_add_pengelolaan_layanan_to_bumdes_kelembagaan_aktor_table.php b/database/migrations/2026_11_04_000002_add_pengelolaan_layanan_to_bumdes_kelembagaan_aktor_table.php new file mode 100644 index 0000000..9ee22f5 --- /dev/null +++ b/database/migrations/2026_11_04_000002_add_pengelolaan_layanan_to_bumdes_kelembagaan_aktor_table.php @@ -0,0 +1,72 @@ +string('status_pembukuan')->nullable(); + $table->string('format_pembukuan')->nullable(); + $table->boolean('pernah_audit')->nullable(); + $table->json('sifat_audit')->nullable(); + $table->string('pelaksana_audit')->nullable(); + $table->unsignedSmallInteger('tahun_audit')->nullable(); + $table->string('pertanggungjawaban_musdes')->nullable(); + $table->unsignedSmallInteger('tahun_laporan_musdes')->nullable(); + $table->string('kondisi_keuangan')->nullable(); + $table->string('kontribusi_pades')->nullable(); + $table->decimal('nominal_kontribusi_pades', 18, 2)->nullable(); + $table->string('cakupan_layanan')->nullable(); + $table->text('desa_lain_dilayani')->nullable(); + $table->unsignedInteger('jumlah_petani_desa_sendiri')->nullable(); + $table->unsignedInteger('jumlah_petani_desa_lain')->nullable(); + $table->string('pencatatan_petani')->nullable(); + $table->json('layanan_petani')->nullable(); + $table->string('layanan_petani_lainnya')->nullable(); + $table->string('peran_rantai_nilai')->nullable(); + $table->string('pengalaman_offtaker')->nullable(); + $table->string('nama_offtaker')->nullable(); + $table->unsignedSmallInteger('tahun_mulai_offtaker')->nullable(); + $table->text('alasan_berhenti_offtaker')->nullable(); + $table->json('hambatan_usaha')->nullable(); + $table->string('hambatan_usaha_lainnya')->nullable(); + }); + } + + public function down(): void + { + Schema::table('bumdes_kelembagaan_aktor', function (Blueprint $table) { + $table->dropColumn([ + 'status_pembukuan', + 'format_pembukuan', + 'pernah_audit', + 'sifat_audit', + 'pelaksana_audit', + 'tahun_audit', + 'pertanggungjawaban_musdes', + 'tahun_laporan_musdes', + 'kondisi_keuangan', + 'kontribusi_pades', + 'nominal_kontribusi_pades', + 'cakupan_layanan', + 'desa_lain_dilayani', + 'jumlah_petani_desa_sendiri', + 'jumlah_petani_desa_lain', + 'pencatatan_petani', + 'layanan_petani', + 'layanan_petani_lainnya', + 'peran_rantai_nilai', + 'pengalaman_offtaker', + 'nama_offtaker', + 'tahun_mulai_offtaker', + 'alasan_berhenti_offtaker', + 'hambatan_usaha', + 'hambatan_usaha_lainnya', + ]); + }); + } +}; diff --git a/database/migrations/2026_11_04_000003_add_rantai_nilai_to_bumdes_kelembagaan_aktor_table.php b/database/migrations/2026_11_04_000003_add_rantai_nilai_to_bumdes_kelembagaan_aktor_table.php new file mode 100644 index 0000000..4b2777f --- /dev/null +++ b/database/migrations/2026_11_04_000003_add_rantai_nilai_to_bumdes_kelembagaan_aktor_table.php @@ -0,0 +1,56 @@ +boolean('melakukan_jual_beli')->nullable(); + $table->json('sumber_pasokan')->nullable(); + $table->string('sumber_pasokan_lainnya')->nullable(); + $table->json('bentuk_produk_dibeli')->nullable(); + $table->json('volume_pembelian')->nullable(); + $table->json('saluran_penjualan')->nullable(); + $table->string('saluran_penjualan_lainnya')->nullable(); + $table->json('bentuk_produk_dijual')->nullable(); + $table->json('volume_penjualan')->nullable(); + $table->json('hubungan_petani_pemasok')->nullable(); + $table->string('hubungan_petani_pemasok_lainnya')->nullable(); + $table->string('mekanisme_pembayaran_petani')->nullable(); + $table->string('mekanisme_pembayaran_lainnya')->nullable(); + $table->string('perjanjian_petani')->nullable(); + $table->json('harga_beli_petani')->nullable(); + $table->string('penetapan_harga_beli')->nullable(); + $table->string('penetapan_harga_beli_lainnya')->nullable(); + }); + } + + public function down(): void + { + Schema::table('bumdes_kelembagaan_aktor', function (Blueprint $table) { + $table->dropColumn([ + 'melakukan_jual_beli', + 'sumber_pasokan', + 'sumber_pasokan_lainnya', + 'bentuk_produk_dibeli', + 'volume_pembelian', + 'saluran_penjualan', + 'saluran_penjualan_lainnya', + 'bentuk_produk_dijual', + 'volume_penjualan', + 'hubungan_petani_pemasok', + 'hubungan_petani_pemasok_lainnya', + 'mekanisme_pembayaran_petani', + 'mekanisme_pembayaran_lainnya', + 'perjanjian_petani', + 'harga_beli_petani', + 'penetapan_harga_beli', + 'penetapan_harga_beli_lainnya', + ]); + }); + } +}; diff --git a/database/migrations/2026_11_05_000001_add_peluang_nilai_tambah_to_bumdes_kelembagaan_aktor_table.php b/database/migrations/2026_11_05_000001_add_peluang_nilai_tambah_to_bumdes_kelembagaan_aktor_table.php new file mode 100644 index 0000000..ff0c13e --- /dev/null +++ b/database/migrations/2026_11_05_000001_add_peluang_nilai_tambah_to_bumdes_kelembagaan_aktor_table.php @@ -0,0 +1,40 @@ +boolean('melakukan_pengolahan_pascapanen')->nullable(); + $table->string('status_sortasi')->nullable(); + $table->text('kriteria_sortasi')->nullable(); + $table->string('status_pengeringan')->nullable(); + $table->text('cara_tempat_pengeringan')->nullable(); + $table->string('status_fermentasi')->nullable(); + $table->json('model_fermentasi')->nullable(); + $table->text('proses_fermentasi')->nullable(); + $table->decimal('premium_harga_pascapanen', 18, 2)->nullable(); + }); + } + + public function down(): void + { + Schema::table('bumdes_kelembagaan_aktor', function (Blueprint $table) { + $table->dropColumn([ + 'melakukan_pengolahan_pascapanen', + 'status_sortasi', + 'kriteria_sortasi', + 'status_pengeringan', + 'cara_tempat_pengeringan', + 'status_fermentasi', + 'model_fermentasi', + 'proses_fermentasi', + 'premium_harga_pascapanen', + ]); + }); + } +}; diff --git a/public/assets/css/demo.css b/public/assets/css/demo.css index 1d0ceb8..245295f 100644 --- a/public/assets/css/demo.css +++ b/public/assets/css/demo.css @@ -186,18 +186,62 @@ } #layout-menu .menu-sub .sidebar-icon > .ti { width: 19px; height: 19px; font-size: 19px; } -/* Profil Kelompok Tani */ -.kelompok-tani-page { +/* Select2 sizes the floating dropdown itself; its body wrapper must not span the viewport. */ +body > .select2-container--open { + inline-size: auto !important; +} + +/* Tipografi profil Kelompok Tani dan BUMDes */ +.kelompok-tani-page, +.bumdes-page { font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); } -.kelompok-tani-page :is(.form-control, .form-select, .form-check-label, .select2-container) { +.kelompok-tani-page :is(.form-control, .form-select, .form-check-label, .select2-container), +.bumdes-page :is(.form-control, .form-select, .form-check-label, .select2-container) { font-family: var(--bs-body-font-family); } +.bumdes-page form :is(.form-control, .form-select, .form-check-label, .select2-container) { + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); +} + +#bumdes-notifications, +.bumdes-page #section-kepengurusan { + scroll-margin-top: 7rem; +} + +.bumdes-page .pengurus-status-switch-row { + display: flex; + align-items: center; + gap: 12px; + white-space: nowrap; +} + +.bumdes-page .pengurus-status-switch-row .switch { + width: 52px; + min-width: 52px; + max-width: 52px; + padding-left: 0; + margin: 0; + position: relative; + display: inline-block; + flex: 0 0 52px; +} + +.bumdes-page .pengurus-status-switch-row .switch-toggle-slider { + left: 0; +} + +.bumdes-page select.is-invalid + .select2-container .select2-selection { + border-color: var(--bs-form-invalid-border-color); +} + .kelompok-tani-page select.is-invalid + .select2-container .select2-selection { border-color: var(--bs-form-invalid-border-color); } @@ -254,3 +298,127 @@ .kelompok-tani-page .select2-container--disabled .select2-selection::after { color: var(--bs-secondary-color); } + +/* Daftar dan navigasi profil BUMDes. */ + + .bumdes-row { cursor: pointer; } + .bumdes-filter-row { padding-block: 1.5rem; } + .bumdes-filter-line { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; } + .bumdes-filter-label { display: flex; align-items: center; font-weight: 500; margin-right: .5rem; white-space: nowrap; } + .bumdes-filter-line .form-select, + .bumdes-filter-line .btn { min-height: 31px; } + .bumdes-filter-line .form-select { + background-color: var(--bs-paper-bg); + border-color: var(--bs-border-color); + border-radius: var(--bs-border-radius); + color: var(--bs-body-color); + } + .bumdes-filter-line .form-select:focus { + border-color: rgba(40, 167, 69, .55); + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .12); + } + .bumdes-filter-line .btn { white-space: nowrap; } + .bumdes-filter-static { + display: flex; align-items: center; min-height: 31px; + padding: .25rem .75rem; font-size: .8125rem; + background-color: var(--bs-paper-bg); + border: 1px solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + color: var(--bs-body-color); + } + .bumdes-filter-line .input-group-sm { width: 240px; } + .bumdes-data-filters > div:not(.bumdes-filter-label) { + flex: 1 1 11rem; + min-inline-size: 0; + } + .bumdes-data-filters .form-select { text-overflow: ellipsis; } + .bumdes-data-filters .input-group-sm { width: 100%; } + @media (max-width: 767.98px) { + .bumdes-filter-line { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; } + .bumdes-filter-label { margin-right: 0; } + .bumdes-filter-line .btn { justify-content: center; width: 100%; } + .bumdes-filter-line .input-group-sm { width: 100%; } + .bumdes-filter-line .input-group .btn { width: auto; } + } + + + @media (min-width: 768px) { + .bumdes-page .nav-pills-custom-spacing { + display: flex; + width: 100%; + gap: 0.5rem; + flex-wrap: nowrap !important; + overflow-x: auto; + } + + .bumdes-page .nav-pills-custom-spacing::-webkit-scrollbar { + display: none; + } + + .bumdes-page .nav-pills-custom-spacing .nav-item { + margin-right: 0 !important; + flex: 1; + } + + .bumdes-page .nav-pills-custom-spacing .nav-link { + display: flex; + justify-content: center; + align-items: center; + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + width: 100%; + white-space: nowrap; + font-size: var(--bs-body-font-size) !important; + } + + .bumdes-page .nav-pills-custom-spacing .nav-link i { + margin-right: 0.4rem !important; + } + } + +/* Tabel dan ringkasan profil BUMDes. */ +.bumdes-list-table .bumdes-list-identity { + min-inline-size: 12rem; + max-inline-size: 16rem; +} + +.bumdes-list-table .bumdes-list-status { + min-inline-size: 10rem; + max-inline-size: 16rem; +} + +.bumdes-list-table .bumdes-list-units { + min-inline-size: 12rem; + max-inline-size: 18rem; +} + +.bumdes-list-table .badge { + max-inline-size: 100%; + line-height: 1.45; + white-space: normal; + text-align: start; + overflow-wrap: anywhere; +} + +.bumdes-profile-summary .bumdes-summary-content { + min-inline-size: 0; +} + +.bumdes-profile-summary .bumdes-summary-label { + color: var(--bs-secondary-color); + font-size: 0.8125rem; + font-weight: 500; +} + +.bumdes-profile-summary .badge { + max-inline-size: 100%; + line-height: 1.45; + white-space: normal; + text-align: start; + overflow-wrap: anywhere; +} + +.bumdes-profile-summary .bumdes-summary-more { + min-block-size: 1.5rem; + font-size: 0.8125rem; +} diff --git a/public/assets/js/bumdes.js b/public/assets/js/bumdes.js new file mode 100644 index 0000000..4ba8fc2 --- /dev/null +++ b/public/assets/js/bumdes.js @@ -0,0 +1,375 @@ +/* Profil, kelembagaan, dan operasional BUMDes. */ +'use strict'; +$(document).ready(function () { + const $page = $('.bumdes-page'); + if (!$page.length) return; + const profileForm = document.querySelector('[data-bumdes-profile]'); + const ajaxHeaders = { + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, + 'Accept': 'application/json' + }; + if (profileForm) { + const prefix = '#' + profileForm.dataset.prefix; + const lainnyaValue = profileForm.dataset.jabatanLainnya; + + $(`${prefix}-jenis-kelamin, ${prefix}-pendidikan-terakhir, ${prefix}-jabatan`).select2(); + $(`${prefix}-tanggal-lahir`).flatpickr({ + dateFormat: 'd-m-Y' + }); + + function toggleJabatanLainnya() { + const isLainnya = $(`${prefix}-jabatan`).val() === lainnyaValue; + $(`${prefix}-jabatan-lainnya-wrapper`).toggleClass('d-none', !isLainnya); + $(`${prefix}-jabatan-lainnya`).prop('disabled', !isLainnya).prop('required', isLainnya); + + if (!isLainnya) { + $(`${prefix}-jabatan-lainnya`).val(''); + } + } + + $(`${prefix}-jabatan`).on('change', toggleJabatanLainnya); + toggleJabatanLainnya(); + + $(`${prefix}-provinsi`).select2().change(function(e) { + e.preventDefault(); + const provinsiId = $(this).val(); + $(`${prefix}-kabupaten`).prop('disabled', !provinsiId).val('').trigger('change'); + $(`${prefix}-kecamatan`).prop('disabled', true).val('').trigger('change'); + $(`${prefix}-desa`).prop('disabled', true).val('').trigger('change'); + + if (provinsiId) { + $.ajax({ + url: profileForm.dataset.kabupatenUrl.replace(':id', provinsiId) + '?all_wilayah=1', + method: 'GET', + headers: ajaxHeaders, + success: function(data) { + $(`${prefix}-kabupaten`).empty().append(''); + data.forEach(function(kabupaten) { + $(`${prefix}-kabupaten`).append(new Option(kabupaten.nama, kabupaten.id)); + }); + } + }); + } + }); + + $(`${prefix}-kabupaten`).select2().change(function(e) { + e.preventDefault(); + const kabupatenId = $(this).val(); + $(`${prefix}-kecamatan`).prop('disabled', !kabupatenId).val('').trigger('change'); + $(`${prefix}-desa`).prop('disabled', true).val('').trigger('change'); + + if (kabupatenId) { + $.ajax({ + url: profileForm.dataset.kecamatanUrl.replace(':id', kabupatenId) + '?all_wilayah=1', + method: 'GET', + headers: ajaxHeaders, + success: function(data) { + $(`${prefix}-kecamatan`).empty().append(''); + data.forEach(function(kecamatan) { + $(`${prefix}-kecamatan`).append(new Option(kecamatan.nama, kecamatan.id)); + }); + } + }); + } + }); + + $(`${prefix}-kecamatan`).select2().change(function(e) { + e.preventDefault(); + const kecamatanId = $(this).val(); + $(`${prefix}-desa`).prop('disabled', !kecamatanId).val('').trigger('change'); + + if (kecamatanId) { + $.ajax({ + url: profileForm.dataset.desaUrl.replace(':id', kecamatanId) + '?all_wilayah=1', + method: 'GET', + headers: ajaxHeaders, + success: function(data) { + $(`${prefix}-desa`).empty().append(''); + data.forEach(function(desa) { + $(`${prefix}-desa`).append(new Option(desa.nama, desa.id)); + }); + } + }); + } + }); + + $(`${prefix}-desa`).select2(); + } + + if (profileForm) { + function syncProfileDetails() { + const inactive = $('input[name="status_keaktifan"]:checked').val() === 'tidak_aktif_vakum'; + $('#alasan-tidak-aktif').prop('disabled', !inactive); + const hasStaff = $('input[name="memiliki_staf"]:checked').val() === '1'; + $('#staf-detail-wrapper').find('input').prop('disabled', !hasStaff); + } + $('input[name="status_keaktifan"], input[name="memiliki_staf"]').on('change', syncProfileDetails); + syncProfileDetails(); + } + + if (document.getElementById('kelembagaanAktorForm')) { + $('#status-badan-hukum, #status-unit-komoditas, #status-ad-art').select2({ width: '100%' }); + + function setRequired(selector, enabled) { + $(selector).prop('disabled', !enabled).prop('required', enabled).each(function () { + $(`[data-required-for="${this.id}"]`).toggleClass('d-none', !enabled); + }); + } + + function syncDetails() { + const jasaLainnya = $('#unit-usaha-jasa_lainnya').is(':checked'); + $('#unit-usaha-jasa-lainnya-wrapper').toggleClass('d-none', !jasaLainnya).find('input').prop('disabled', !jasaLainnya); + setRequired('#nomor-dasar-hukum, #tahun-dasar-hukum', $('#status-badan-hukum').val() === 'sudah_berbadan_hukum'); + setRequired('#nama-unit-komoditas', $('#status-unit-komoditas').val() === 'ya'); + setRequired('#tahun-ad-art', $('#status-ad-art').val() === 'ada_lengkap'); + } + + $('input[name="unit_usaha[]"], #status-badan-hukum, #status-unit-komoditas, #status-ad-art').on('change', syncDetails); + syncDetails(); + } + + if (document.getElementById('operasionalBumdesForm')) { + function setFieldsEnabled($wrapper, enabled) { + $wrapper.find('input, textarea, select').prop('disabled', !enabled); + + if (!enabled) { + $wrapper.find('input:not([type="radio"]):not([type="checkbox"]), textarea').val(''); + $wrapper.find('input[type="radio"], input[type="checkbox"]').prop('checked', false); + } + } + + function syncPenambahanModal() { + const enabled = $('input[name="pernah_penambahan_modal"]:checked').val() === '1'; + const $wrapper = $('#penambahan-modal-wrapper'); + setFieldsEnabled($wrapper, enabled); + } + + function syncInventarisRow(row) { + const $row = $(row); + const exists = $row.find('.bumdes-inventaris-status-radio[value="1"]').is(':checked'); + $row.find('.bumdes-inventaris-detail').prop('disabled', !exists); + $row.find('.bumdes-inventaris-required').prop('required', exists); + if (!exists) { + $row.find('.bumdes-inventaris-detail').val('').removeClass('is-invalid').removeAttr('aria-invalid aria-describedby'); + } + } + + $('input[name="pernah_penambahan_modal"]').on('change', syncPenambahanModal); + $('.bumdes-inventaris-status-radio').on('change', function () { + syncInventarisRow($(this).closest('tr')); + }); + + syncPenambahanModal(); + $('[data-bumdes-aset], [data-bumdes-infrastruktur]').each(function () { syncInventarisRow(this); }); + } + + const $moneyForms = $('#operasionalBumdesForm, #rantaiNilaiBumdesForm'); + if ($moneyForms.length) { + function onlyDigits(value) { + return String(value || '').replace(/\D/g, ''); + } + + function formatRupiah(value) { + return onlyDigits(value).replace(/\B(?=(\d{3})+(?!\d))/g, '.'); + } + + $moneyForms.on('input', '.rupiah-input', function () { + const cursorAtEnd = this.selectionStart === this.value.length; + this.value = formatRupiah(this.value); + + if (cursorAtEnd) { + this.setSelectionRange(this.value.length, this.value.length); + } + }); + + $moneyForms.find('.rupiah-input').each(function () { + this.value = formatRupiah(this.value); + }); + + $moneyForms.on('submit', function () { + $(this).find('.rupiah-input').each(function () { + this.value = onlyDigits(this.value); + }); + }); + } + + const $choiceForms = $('#operasionalBumdesForm, #rantaiNilaiBumdesForm'); + if ($choiceForms.length) { + function syncChoiceField($input, enabled) { + $input.prop('disabled', !enabled).prop('required', enabled); + $choiceForms.find(`[data-required-for="${$input.attr('id')}"]`).toggleClass('d-none', !enabled); + if (!enabled) $input.val('').removeClass('is-invalid'); + } + + function syncChoiceDetails() { + syncChoiceField($('#jumlah-alokasi-desa'), $choiceForms.find('input[name="ada_alokasi_desa"]:checked').val() === '1'); + $choiceForms.find('[data-bumdes-choices]').each(function () { + const $group = $(this); + const $other = $group.find('[data-ekosistem-other-value]'); + const enabled = $group.find('input[type="checkbox"]').toArray().some(input => input.checked && input.value === $other.data('ekosistem-other-value')); + syncChoiceField($other, enabled); + }); + } + + $choiceForms.on('change', 'input[name="ada_alokasi_desa"]', syncChoiceDetails); + $choiceForms.on('change', '[data-bumdes-choices] input[type="checkbox"]', function () { + const $group = $(this).closest('[data-bumdes-choices]'); + const exclusive = $group.data('exclusive-value'); + if (this.checked) { + $group.find('input[type="checkbox"]').not(this).each((_, input) => { + if (this.value === exclusive || input.value === exclusive) input.checked = false; + }); + } + syncChoiceDetails(); + }); + syncChoiceDetails(); + + const $rantaiForm = $('#rantaiNilaiBumdesForm'); + if ($rantaiForm.length) { + function syncRantaiState() { + const trading = $rantaiForm.find('[name="melakukan_jual_beli"]:checked').val() === '1'; + const $tradeSections = $rantaiForm.find('[data-rantai-section]').prop('disabled', !trading).toggleClass('d-none', !trading); + $tradeSections.closest('.card').not('#section-rantai-pembelian').toggleClass('d-none', !trading); + const notSold = $rantaiForm.find('[name="saluran_penjualan[]"]:checked').toArray().some(input => input.value === $rantaiForm.data('belum-menjual-value')); + const $sales = $('#rantai-penjualan-details').prop('disabled', !trading || notSold); + if (notSold) { + $sales.find('input[type="checkbox"]').prop('checked', false); + $sales.find('input:not([type="checkbox"])').val(''); + } + $rantaiForm.find('[data-rantai-other-field]').each(function () { + const $group = $(this); + const value = $rantaiForm.find(`[name="${$group.data('rantai-other-field')}"]:checked`).val(); + syncChoiceField($group.find('input[type="text"]'), trading && value === $group.data('rantai-other-value')); + }); + const purchased = $rantaiForm.find('[name="bentuk_produk_dibeli[]"]:checked').toArray().map(input => input.value); + $rantaiForm.find('[data-rantai-price-product]').each(function () { + const enabled = trading && purchased.includes($(this).data('rantai-price-product')); + const $inputs = $(this).find('input').prop('disabled', !enabled); + if (!enabled) $inputs.val('').removeClass('is-invalid'); + }); + } + function syncPostharvestState() { + const processing = $rantaiForm.find('[name="melakukan_pengolahan_pascapanen"]:checked').val() === '1'; + const $processingSections = $rantaiForm.find('[data-postharvest-section]').prop('disabled', !processing).toggleClass('d-none', !processing); + $processingSections.closest('.card').not('#section-rantai-sortasi').toggleClass('d-none', !processing); + $rantaiForm.find('[data-postharvest-detail]').each(function () { + const $group = $(this); + const value = $rantaiForm.find(`[name="${$group.data('condition-field')}"]:checked`).val(); + const enabled = processing && $group.data('condition-values').includes(value); + const $inputs = $group.find('input, textarea'); + const $checkboxes = $inputs.filter('[type="checkbox"]'); + $inputs.prop('disabled', !enabled); + $inputs.not('[type="checkbox"]').prop('required', enabled); + $checkboxes.prop('required', false); + if (enabled && !$checkboxes.is(':checked')) $checkboxes.first().prop('required', true); + $group.find('[data-postharvest-required]').toggleClass('d-none', !enabled); + if (!enabled) { + $checkboxes.prop('checked', false); + $inputs.not('[type="checkbox"]').val(''); + $inputs.removeClass('is-invalid'); + } + }); + } + $rantaiForm.on('change', 'input', function () { + if (this.name === 'melakukan_jual_beli' && this.value === '0') { + const $fields = $rantaiForm.find('[data-rantai-section] input'); + $fields.filter('[type="radio"], [type="checkbox"]').prop('checked', false); + $fields.not('[type="radio"], [type="checkbox"]').val(''); + } + if (this.name === 'melakukan_pengolahan_pascapanen' && this.value === '0') { + const $fields = $rantaiForm.find('[data-postharvest-section] input, [data-postharvest-section] textarea'); + $fields.filter('[type="radio"], [type="checkbox"]').prop('checked', false); + $fields.not('[type="radio"], [type="checkbox"]').val(''); + } + syncChoiceDetails(); + syncRantaiState(); + syncPostharvestState(); + }); + syncRantaiState(); + syncPostharvestState(); + } + } + + const $operationalForm = $('#operasionalBumdesForm'); + if ($operationalForm.length) { + function syncOperationalConditions() { + $operationalForm.find('[data-operasional-condition]').each(function () { + const $group = $(this); + const field = $group.data('condition-field'); + const selected = $operationalForm.find(`[name="${field}"]:checked`).val(); + const enabled = $group.data('condition-values').includes(selected); + const $inputs = $group.find('input, textarea, select'); + const $checkboxes = $inputs.filter('[type="checkbox"]'); + $inputs.prop('disabled', !enabled); + $inputs.not('[type="checkbox"]').prop('required', enabled); + $checkboxes.prop('required', false); + if (enabled && !$checkboxes.is(':checked')) $checkboxes.first().prop('required', true); + $group.find('[data-operasional-required]').toggleClass('d-none', !enabled); + if (!enabled) { + $inputs.not('[type="checkbox"]').val(''); + $checkboxes.prop('checked', false); + $inputs.removeClass('is-invalid'); + } + }); + } + $operationalForm.on('change', 'input, select', syncOperationalConditions); + syncOperationalConditions(); + } + + const filterForm = document.getElementById('bumdes-filter-form'); + if (filterForm) { + const $desa = $('#filter-desa'); + let desaRequest; + $('#filter-kecamatan').on('change', function () { + if (desaRequest) desaRequest.abort(); + $desa.empty().append(new Option('Semua Desa', '')).prop('disabled', true); + if (!this.value) return; + desaRequest = $.ajax({ + url: filterForm.dataset.desaUrl.replace(':id', this.value), + data: {all_wilayah: 1}, headers: ajaxHeaders, dataType: 'json' + }).done(function (data) { + data.forEach(desa => $desa.append(new Option(desa.nama, desa.id))); + $desa.prop('disabled', false); + }).fail(function (xhr, status) { + if (status !== 'abort') $desa.empty().append(new Option('Gagal memuat desa', '')); + }); + }); + } + $page.on('click', '.bumdes-row', function (event) { + if (!$(event.target).closest('a, button, input, select, textarea, label').length) { + window.location.href = this.querySelector('.edit-record').href; + } + }); + $page.on('submit', '.bumdes-delete-form', function (event) { + event.preventDefault(); + const form = this; + Swal.fire({ + title: 'Apakah Anda yakin?', text: `Profil "${form.dataset.nama}" akan dihapus.`, + icon: 'warning', showCancelButton: true, confirmButtonText: 'Ya, Hapus!', cancelButtonText: 'Batal', + customClass: {confirmButton: 'btn btn-primary me-3', cancelButton: 'btn btn-label-secondary'}, + buttonsStyling: false + }).then(result => { if (result.isConfirmed) form.submit(); }); + }); + const scrollAfterSave = function () { + const notice = document.getElementById('bumdes-notifications'); + if (notice && notice.dataset.hasErrors !== '1') { + const target = document.getElementById(window.location.hash.slice(1)); + const profileSection = target?.id === 'section-kepengurusan'; + const trading = document.querySelector('#rantaiNilaiBumdesForm [name="melakukan_jual_beli"]:checked')?.value === '1'; + const processing = document.querySelector('#rantaiNilaiBumdesForm [name="melakukan_pengolahan_pascapanen"]:checked')?.value === '1'; + const postharvestTarget = target?.hasAttribute('data-postharvest-card'); + const eligibleForTarget = postharvestTarget ? (processing || (trading && target.id === 'section-rantai-sortasi')) : trading; + const valueChainSection = eligibleForTarget && target?.matches('#rantaiNilaiBumdesForm > .card'); + if ((profileSection || valueChainSection) && target.closest('.bumdes-page') && target.getClientRects().length) { + target.scrollIntoView({block: 'start'}); + return; + } + } + notice?.scrollIntoView({block: 'start'}); + }; + if (document.readyState === 'complete') { + scrollAfterSave(); + } else { + window.addEventListener('load', scrollAfterSave, {once: true}); + } +}); diff --git a/resources/views/bumdes/kelembagaan-aktor/index.blade.php b/resources/views/bumdes/kelembagaan-aktor/index.blade.php index ac8525b..b668667 100644 --- a/resources/views/bumdes/kelembagaan-aktor/index.blade.php +++ b/resources/views/bumdes/kelembagaan-aktor/index.blade.php @@ -1,530 +1,107 @@ @extends('layout.main') @php - $fieldValue = function (string $field, $default = null) use ($kelembagaanAktor) { - return old($field, data_get($kelembagaanAktor, $field, $default)); - }; - - $arrayValue = function (string $field) use ($fieldValue) { - return (array) $fieldValue($field, []); - }; - - $rowValue = function (string $field, string $keyName, string $key) use ($fieldValue) { - return collect((array) $fieldValue($field, []))->firstWhere($keyName, $key) ?? []; - }; - - $rupiahValue = function ($value) { - if (! filled($value)) { - return ''; - } - - if (is_numeric($value)) { - return number_format((float) $value, 0, ',', '.'); - } - - return preg_replace('/\D/', '', (string) $value); - }; + $fieldValue = fn (string $field, $default = null) => old($field, data_get($kelembagaanAktor, $field, $default)); + $unitUsahaSelected = (array) $fieldValue('unit_usaha', []); + $hasBadanHukum = $fieldValue('status_badan_hukum') === 'sudah_berbadan_hukum'; + $hasUnitKomoditas = $fieldValue('status_unit_komoditas') === 'ya'; + $hasAdArtLengkap = $fieldValue('status_ad_art') === 'ada_lengkap'; @endphp @section('content') -