This commit is contained in:
2026-11-06 14:21:50 +07:00
88 changed files with 7510 additions and 1957 deletions
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Web\Bumdes;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bumdes\BumdesRequest;
use App\Http\Requests\Bumdes\StoreKelembagaanAktorBumdesRequest;
use App\Models\Bumdes\PengurusBumdes;
use App\Services\Bumdes\PengurusBumdesService;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class KelembagaanAktorBumdesController extends Controller
{
public function __construct(private readonly PengurusBumdesService $service) {}
public function index(BumdesRequest $request, PengurusBumdes $pengurusBumdes): View
{
return view('bumdes.kelembagaan-aktor.index', $this->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.');
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Web\Bumdes;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bumdes\BumdesRequest;
use App\Http\Requests\Bumdes\StoreOperasionalBumdesRequest;
use App\Models\Bumdes\PengurusBumdes;
use App\Services\Bumdes\PengurusBumdesService;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class OperasionalBumdesController extends Controller
{
public function __construct(private readonly PengurusBumdesService $service) {}
public function index(BumdesRequest $request, PengurusBumdes $pengurusBumdes): View
{
return view('bumdes.operasional.index', $this->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.');
}
}
@@ -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.');
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Web\Bumdes;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bumdes\BumdesRequest;
use App\Http\Requests\Bumdes\StoreRantaiNilaiBumdesRequest;
use App\Models\Bumdes\PengurusBumdes;
use App\Services\Bumdes\PengurusBumdesService;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class RantaiNilaiBumdesController extends Controller
{
public function __construct(private readonly PengurusBumdesService $service) {}
public function index(BumdesRequest $request, PengurusBumdes $pengurusBumdes): View
{
return view('bumdes.rantai-nilai.index', $this->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.');
}
}
@@ -3,6 +3,9 @@
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use App\Http\Requests\Produktivitas\IndexKelompokTaniRequest;
use App\Services\Produktivitas\KelompokTaniService;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
@@ -32,94 +35,10 @@ class SurveyKelompokTaniController extends Controller
]);
}
public function index(Request $request)
public function index(IndexKelompokTaniRequest $request): View
{
$validated = $request->validate([
'search' => ['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'],
'komoditas_id' => ['nullable', 'integer', 'exists:master_komoditas,id'],
'jenis_kelompok' => ['nullable', 'string', 'max:50'],
'sort_by' => ['nullable', 'string'],
'per_page' => ['nullable', 'integer', 'in:10,25'],
]);
$defaultKabupaten = KabupatenKota::select('id', 'nama')
->whereRaw('lower(nama) like ?', ['%ngada%'])->orderBy('nama')->first();
$allowedSorts = ['nama_asc', 'nama_desc', 'anggota_desc', 'anggota_asc'];
$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,
'komoditas_id' => $validated['komoditas_id'] ?? null,
'jenis_kelompok' => $validated['jenis_kelompok'] ?? null,
'sort_by' => in_array($validated['sort_by'] ?? null, $allowedSorts, true)
? $validated['sort_by'] : null,
];
$query = KelompokTani::query()
->when(filled($filters['search']), function ($query) use ($filters) {
$search = '%' . Str::lower($filters['search']) . '%';
$query->where(function ($query) use ($search) {
$query->whereRaw('lower(nama_kelompok) like ?', [$search])
->orWhereRaw('lower(nama_responden) like ?', [$search]);
});
})
->when($filters['kabupaten_kota_id'], function ($query, $id) {
$query->whereHas('desaKelurahan.kecamatan', fn ($query) => $query->where('kabupaten_kota_id', $id));
})
->when($filters['kecamatan_id'], function ($query, $id) {
$query->whereHas('desaKelurahan', fn ($query) => $query->where('kecamatan_id', $id));
})
->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id))
->when($filters['komoditas_id'], function ($query, $id) {
$query->where(function ($query) use ($id) {
$query->whereJsonContains('komoditas_id', (string) $id)
->orWhereJsonContains('komoditas_id', (int) $id);
});
})
->when($filters['jenis_kelompok'], fn ($query, $jenis) => $query->where('jenis_kelompok', $jenis));
$summary = (clone $query)->selectRaw('
COUNT(*) AS total_kelompok,
COUNT(DISTINCT jenis_kelompok) AS total_jenis,
COALESCE(SUM(jumlah_pengurus), 0) AS total_pengurus,
COALESCE(SUM(jumlah_pengurus_aktif), 0) AS pengurus_aktif,
COALESCE(SUM(jumlah_anggota_terdaftar), 0) AS total_anggota,
COALESCE(SUM(jumlah_anggota_aktif), 0) AS anggota_aktif
')->first();
match ($filters['sort_by']) {
'nama_asc' => $query->orderBy('nama_kelompok'),
'nama_desc' => $query->orderByDesc('nama_kelompok'),
'anggota_desc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) DESC'),
'anggota_asc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) ASC'),
default => $query->latest(),
};
$perPage = (int) ($validated['per_page'] ?? 10);
$kelompokTani = $query->with('desaKelurahan.kecamatan')->orderByDesc('id')
->paginate($perPage)->appends([...$filters, 'per_page' => $perPage]);
$selectedKabupaten = $filters['kabupaten_kota_id']
? KabupatenKota::select('id', 'nama')->find($filters['kabupaten_kota_id']) : null;
$kecamatanOptions = $filters['kabupaten_kota_id']
? Kecamatan::select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get()
: collect();
$desaOptions = $filters['kecamatan_id']
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get()
: collect();
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
$jenisKelompokOptions = KelompokTani::query()->whereNotNull('jenis_kelompok')
->where('jenis_kelompok', '!=', '')->distinct()->orderBy('jenis_kelompok')->pluck('jenis_kelompok');
return view('produktivitas.survey-kelompok-tani.index', compact(
'kelompokTani', 'summary', 'filters', 'selectedKabupaten',
'kecamatanOptions', 'desaOptions', 'komoditasOptions', 'jenisKelompokOptions'
));
return view('produktivitas.survey-kelompok-tani.index', app(KelompokTaniService::class)->indexData($request->validated()));
}
public function create()
{
$this->authorizeAccess();
@@ -129,6 +48,7 @@ class SurveyKelompokTaniController extends Controller
return view('produktivitas.survey-kelompok-tani.add', [
...compact('provinsi', 'komoditas'),
'ringkasanProfil' => app(KelompokTaniService::class)->profileSummary(),
...$this->profileLocationOptions(),
]);
}
@@ -178,15 +98,18 @@ class SurveyKelompokTaniController extends Controller
public function edit(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$ringkasanProfil = app(KelompokTaniService::class)->profileSummary($kelompokTani);
return match ($request->query('section', 'identitas')) {
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani')),
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani', 'ringkasanProfil')),
'aktivitas-fungsi' => view('produktivitas.survey-kelompok-tani.aktivitas-fungsi', [
'kelompokTani' => $kelompokTani->loadMissing(['aset', 'bantuan']),
'ringkasanProfil' => $ringkasanProfil,
]),
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani')),
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani', 'ringkasanProfil')),
default => view('produktivitas.survey-kelompok-tani.edit', [
'kelompokTani' => $kelompokTani->loadMissing('pengurus'),
'ringkasanProfil' => $ringkasanProfil,
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
'komoditas' => Komoditas::where('is_utama', true)->get(),
...$this->profileLocationOptions($kelompokTani),
+174
View File
@@ -0,0 +1,174 @@
<?php
namespace App\Http\Requests\Bumdes;
use App\Enums\Bumdes\AsetProduktifBumdes;
use App\Enums\Bumdes\InfrastrukturPascapanenBumdes;
use App\Enums\Bumdes\JabatanPengurusIntiBumdes;
use App\Enums\Bumdes\KondisiInfrastrukturBumdes;
use App\Enums\Bumdes\StatusAdArtBumdes;
use App\Enums\Bumdes\StatusBadanHukumBumdes;
use App\Enums\Bumdes\StatusKeaktifanBumdes;
use App\Enums\Bumdes\StatusKepemilikanAsetBumdes;
use App\Enums\Bumdes\StatusUnitKomoditasBumdes;
use App\Enums\Bumdes\UnitUsahaBumdes;
use App\Enums\Role;
use App\Models\Bumdes\KelembagaanAktorBumdes;
use App\Models\Bumdes\PengurusBumdes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class BumdesRequest extends FormRequest
{
public function authorize(): bool
{
if (! Gate::any(['super_admin', 'admin', 'enumerator'])) {
return false;
}
$pengurus = $this->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',
];
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Requests\Bumdes;
use App\Enums\Bumdes\StatusKeaktifanBumdes;
use Illuminate\Validation\Rule;
class IndexPengurusBumdesRequest extends BumdesRequest
{
public function rules(): array
{
return [
'search' => ['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.'];
}
}
@@ -0,0 +1,11 @@
<?php
namespace App\Http\Requests\Bumdes;
class StoreKelembagaanAktorBumdesRequest extends BumdesRequest
{
public function rules(): array
{
return $this->sectionRules('kelembagaan');
}
}
@@ -0,0 +1,252 @@
<?php
namespace App\Http\Requests\Bumdes;
use App\Enums\Bumdes\CakupanLayananBumdes;
use App\Enums\Bumdes\DukunganDesaBumdes;
use App\Enums\Bumdes\FormatPembukuanBumdes;
use App\Enums\Bumdes\HambatanUsahaBumdes;
use App\Enums\Bumdes\KemitraanEksternalBumdes;
use App\Enums\Bumdes\KeterlibatanKoordinasiBumdes;
use App\Enums\Bumdes\KondisiAsetProduktifBumdes;
use App\Enums\Bumdes\KondisiInfrastrukturBumdes;
use App\Enums\Bumdes\KondisiKeuanganBumdes;
use App\Enums\Bumdes\KontribusiPadesBumdes;
use App\Enums\Bumdes\LayananPetaniBumdes;
use App\Enums\Bumdes\PencatatanPetaniBumdes;
use App\Enums\Bumdes\PengalamanOfftakerBumdes;
use App\Enums\Bumdes\PeranRantaiNilaiBumdes;
use App\Enums\Bumdes\PertanggungjawabanMusdesBumdes;
use App\Enums\Bumdes\SifatAuditBumdes;
use App\Enums\Bumdes\StatusKepemilikanAsetBumdes;
use App\Enums\Bumdes\StatusPembukuanBumdes;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class StoreOperasionalBumdesRequest extends BumdesRequest
{
public function rules(): array
{
$rules = [...$this->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',
];
}
}
@@ -0,0 +1,66 @@
<?php
namespace App\Http\Requests\Bumdes;
use App\Enums\Bumdes\JabatanBumdes;
use App\Enums\Pendidikan;
use Illuminate\Validation\Rule;
class StorePengurusBumdesRequest extends BumdesRequest
{
public function rules(): array
{
return [
'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'],
'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.',
];
}
}
@@ -0,0 +1,172 @@
<?php
namespace App\Http\Requests\Bumdes;
use App\Enums\Bumdes\BentukProdukBumdes;
use App\Enums\Bumdes\HubunganPemasokBumdes;
use App\Enums\Bumdes\MekanismePembayaranBumdes;
use App\Enums\Bumdes\ModelFermentasiBumdes;
use App\Enums\Bumdes\PenetapanHargaBeliBumdes;
use App\Enums\Bumdes\PeriodePanenBumdes;
use App\Enums\Bumdes\PerjanjianPetaniBumdes;
use App\Enums\Bumdes\SaluranPenjualanBumdes;
use App\Enums\Bumdes\StatusFermentasiBumdes;
use App\Enums\Bumdes\StatusPengeringanBumdes;
use App\Enums\Bumdes\StatusSortasiBumdes;
use App\Enums\Bumdes\SumberPasokanBumdes;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class StoreRantaiNilaiBumdesRequest extends BumdesRequest
{
public function rules(): array
{
$trading = $this->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',
];
}
}
@@ -0,0 +1,5 @@
<?php
namespace App\Http\Requests\Bumdes;
class UpdatePengurusBumdesRequest extends StorePengurusBumdesRequest {}
@@ -0,0 +1,55 @@
<?php
namespace App\Http\Requests\Produktivitas;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class IndexKelompokTaniRequest extends FormRequest
{
public function authorize(): bool
{
return Gate::any(['super_admin', 'admin', 'enumerator']);
}
public function rules(): array
{
return [
'search' => ['nullable', 'string', 'max:255'],
'kabupaten_kota_id' => ['bail', 'nullable', 'integer', 'exists:master_kabupaten_kota,id'],
'kecamatan_id' => ['bail', 'nullable', 'integer', 'exists:master_kecamatan,id'],
'desa_kelurahan_id' => ['bail', 'nullable', 'integer', 'exists:master_desa_kelurahan,id'],
'komoditas_id' => ['bail', 'nullable', 'integer', 'exists:master_komoditas,id'],
'jenis_kelompok' => ['nullable', 'string', 'max:50'],
'status_legalitas' => ['nullable', 'string', 'max:50'],
'sort_by' => ['nullable', 'string', 'max:50'],
'per_page' => ['nullable', 'integer', 'in:10,25'],
];
}
public function messages(): array
{
return [
'integer' => ':attribute harus berupa angka bulat.',
'exists' => ':attribute tidak ditemukan.',
'string' => ':attribute harus berupa teks.',
'max' => ':attribute terlalu panjang.',
'in' => 'Pilihan :attribute tidak valid.',
];
}
public function attributes(): array
{
return [
'search' => 'Pencarian',
'kabupaten_kota_id' => 'Kabupaten/Kota',
'kecamatan_id' => 'Kecamatan',
'desa_kelurahan_id' => 'Desa/Kelurahan',
'komoditas_id' => 'Komoditas',
'jenis_kelompok' => 'Jenis kelompok',
'status_legalitas' => 'Status SK/Dokumen Pendaftaran',
'sort_by' => 'Urutan data',
'per_page' => 'Jumlah data per halaman',
];
}
}