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 === 'fasilitator') { $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' )); } public function create() { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } $provinsi = $this->provinsiOptions(); $jabatan = JabatanBumdes::options(); $pendidikan = Pendidikan::options(); return view('bumdes.profil.add', compact('provinsi', 'jabatan', 'pendidikan')); } public function store(Request $request) { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } $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.'); } public function show(string $id) { return redirect()->route('bumdes.edit', $id); } public function edit(string $id) { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { 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')); } public function update(Request $request, string $id) { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } $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.'); } public function destroy(string $id) { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } $pengurus = $this->pengurusQuery()->findOrFail($id); $pengurus->update(['deleted_by' => auth()->id()]); $pengurus->delete(); return response()->json(['message' => 'Data pengurus BUMDes berhasil dihapus.']); } public function kelembagaanAktor(string $id) { if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } $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', 'fasilitator'])) { 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 === 'fasilitator') { $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', ]; } }