Update Koperasi

This commit is contained in:
2026-11-06 22:38:45 +07:00
parent 6232b28e11
commit cb36682d9e
79 changed files with 4639 additions and 1378 deletions
@@ -0,0 +1,86 @@
<?php
namespace App\Services\Koperasi;
use App\Models\Koperasi\PengurusKoperasi;
use BackedEnum;
use Illuminate\Support\Collection;
abstract class KoperasiSurveyService
{
abstract public function dependencies(): array;
public function context(?PengurusKoperasi $model): array
{
return [];
}
public function requiredFields(): array
{
return [];
}
protected function arrayFields(): array
{
return [];
}
public function values(mixed $value): array
{
if ($value instanceof Collection) {
$value = $value->all();
} elseif (is_string($value)) {
$decoded = json_decode($value, true);
$value = is_array($decoded) ? $decoded : [$value];
}
$values = array_map(fn ($item) => $item instanceof BackedEnum ? $item->value : $item, is_array($value) ? $value : [$value]);
return array_values(array_filter($values, fn ($item) => is_string($item) && $item !== ''));
}
protected function matches(array $condition, array $data, array $context): bool
{
if (isset($condition['any'])) {
foreach ($condition['any'] as $option) {
if ($this->matches($option, $data, $context)) {
return true;
}
}
return false;
}
$parent = $condition['field'];
if ($condition['source'] === 'profile') {
$selected = $this->values($context[$parent] ?? null);
} else {
if (! $this->active($parent, $data, $context)) {
return false;
}
$selected = $this->values($data[$parent] ?? null);
}
return (bool) array_intersect($selected, $condition['values']);
}
public function active(string $field, array $data, array $context): bool
{
foreach ($this->dependencies()[$field] ?? [] as $condition) {
if (! $this->matches($condition, $data, $context)) {
return false;
}
}
return true;
}
public function normalize(array $data, array $context): array
{
foreach (array_keys($this->dependencies()) as $field) {
if (! $this->active($field, $data, $context)) {
$data[$field] = in_array($field, $this->arrayFields(), true) ? [] : null;
}
}
return $data;
}
}
@@ -0,0 +1,123 @@
<?php
namespace App\Services\Koperasi;
use App\Enums\Koperasi\BagianOperasionalKoperasi;
use App\Enums\Koperasi\FaktorManajemenKoperasi;
use App\Enums\Koperasi\FasilitasKoperasi;
use App\Enums\Koperasi\FormatPembukuanKoperasi;
use App\Enums\Koperasi\FrekuensiPertemuanKoperasi;
use App\Enums\Koperasi\JawabanYaTidakKoperasi;
use App\Enums\Koperasi\JenisKoperasi;
use App\Enums\Koperasi\KetersediaanKoperasi;
use App\Enums\Koperasi\KomoditasAgregasiKoperasi;
use App\Enums\Koperasi\LayananKoperasi;
use App\Enums\Koperasi\PeranRantaiNilaiKoperasi;
use App\Enums\Koperasi\PraktikFermentasiKoperasi;
use App\Enums\Koperasi\PraktikPengeringanKoperasi;
use App\Enums\Koperasi\PraktikSortasiKoperasi;
use App\Enums\Koperasi\StatusAdArtKoperasi;
use App\Enums\Koperasi\StatusPembukuanKoperasi;
use App\Models\Koperasi\PengurusKoperasi;
class OperasionalKoperasiService extends KoperasiSurveyService
{
public function context(?PengurusKoperasi $model): array
{
return ['jenis_koperasi' => $model?->jenis_koperasi];
}
public function dependencies(): array
{
$when = fn (string $field, array $values, string $source = 'form') => [
'field' => $field, 'values' => $values, 'source' => $source,
];
$simpanPinjam = $when('jenis_koperasi', [JenisKoperasi::SIMPAN_PINJAM->value], 'profile');
$komoditas = array_map(fn ($case) => $case->value, array_filter(
KomoditasAgregasiKoperasi::cases(), fn ($case) => $case !== KomoditasAgregasiKoperasi::TIDAK_TERLIBAT,
));
$agregasi = $when('agregasi_komoditas', $komoditas);
return [
'tahun_ad_art' => [$when('ad_art', [StatusAdArtKoperasi::LENGKAP->value])],
'layanan_lainnya' => [$when('layanan_koperasi', [LayananKoperasi::LAINNYA->value])],
'format_pembukuan' => [$when('status_pembukuan', [StatusPembukuanKoperasi::SUDAH->value])],
'pertemuan_lainnya' => [$when('pertemuan_rutin', [FrekuensiPertemuanKoperasi::LAINNYA->value])],
'kasus_kredit_macet' => [$simpanPinjam],
'sanksi_kredit_macet' => [$simpanPinjam],
'efektivitas_sanksi' => [$simpanPinjam, $when('sanksi_kredit_macet', [KetersediaanKoperasi::ADA->value])],
'faktor_manajemen_lainnya' => [$when('faktor_manajemen', [FaktorManajemenKoperasi::LAINNYA->value])],
'agregasi_komoditas' => [$when('keterlibatan_rantai_nilai', [PeranRantaiNilaiKoperasi::AGREGASI->value])],
'agregasi_komoditas_lainnya' => [$when('agregasi_komoditas', [KomoditasAgregasiKoperasi::LAINNYA->value])],
'fasilitas_koperasi' => [$agregasi],
'fasilitas_lainnya' => [$when('fasilitas_koperasi', [FasilitasKoperasi::LAINNYA->value])],
'pengolahan_lanjutan' => [$agregasi],
'detail_pengolahan_lanjutan' => [$when('pengolahan_lanjutan', [JawabanYaTidakKoperasi::YA->value])],
'cara_penentuan_harga' => [$agregasi],
'kontrak_offtaker' => [$agregasi],
'kontrak_komoditas' => [$when('kontrak_offtaker', [KetersediaanKoperasi::ADA->value])],
'standar_sortasi' => [$when('praktik_sortasi', [PraktikSortasiKoperasi::RUTIN->value, PraktikSortasiKoperasi::TIDAK_KONSISTEN->value])],
'cara_pengeringan' => [$when('praktik_pengeringan', [PraktikPengeringanKoperasi::RUTIN->value, PraktikPengeringanKoperasi::KADANG->value])],
'model_fermentasi' => [$when('praktik_fermentasi', [PraktikFermentasiKoperasi::RUTIN->value, PraktikFermentasiKoperasi::PERNAH->value])],
'proses_fermentasi' => [$when('praktik_fermentasi', [PraktikFermentasiKoperasi::RUTIN->value, PraktikFermentasiKoperasi::PERNAH->value])],
'harga_premium_rupiah' => [$this->postHarvestCondition()],
'harga_premium_persen' => [$this->postHarvestCondition()],
];
}
public function requiredFields(): array
{
return [
'tahun_ad_art', 'layanan_lainnya', 'format_pembukuan', 'pertemuan_lainnya',
'efektivitas_sanksi', 'faktor_manajemen_lainnya', 'agregasi_komoditas',
'agregasi_komoditas_lainnya', 'fasilitas_lainnya', 'detail_pengolahan_lanjutan', 'kontrak_komoditas',
'standar_sortasi', 'cara_pengeringan', 'model_fermentasi', 'proses_fermentasi',
];
}
protected function arrayFields(): array
{
return ['agregasi_komoditas', 'fasilitas_koperasi', 'model_fermentasi'];
}
public function sectionFields(BagianOperasionalKoperasi $section): array
{
return match ($section) {
BagianOperasionalKoperasi::PENGELOLAAN => [
'ad_art', 'tahun_ad_art', 'layanan_koperasi', 'layanan_lainnya',
'pelatihan_produksi', 'pelatihan_pascapanen', 'pelatihan_pengolahan',
'pelatihan_keuangan', 'pelatihan_pemasaran', 'pelatihan_lainnya',
'status_pembukuan', 'format_pembukuan', 'pertemuan_rutin', 'pertemuan_lainnya',
'kasus_kredit_macet', 'sanksi_kredit_macet', 'efektivitas_sanksi',
'faktor_manajemen', 'faktor_manajemen_lainnya',
],
BagianOperasionalKoperasi::RANTAI_NILAI => [
'keterlibatan_rantai_nilai', 'agregasi_komoditas', 'agregasi_komoditas_lainnya',
'fasilitas_koperasi', 'fasilitas_lainnya', 'pengolahan_lanjutan', 'detail_pengolahan_lanjutan',
'cara_penentuan_harga', 'kontrak_offtaker', 'kontrak_komoditas',
'praktik_sortasi', 'standar_sortasi', 'praktik_pengeringan', 'cara_pengeringan',
'praktik_fermentasi', 'model_fermentasi', 'proses_fermentasi', 'harga_premium_rupiah', 'harga_premium_persen',
],
};
}
private function postHarvestCondition(): array
{
return ['any' => [
['field' => 'pengolahan_lanjutan', 'source' => 'form', 'values' => [JawabanYaTidakKoperasi::YA->value]],
['field' => 'praktik_sortasi', 'source' => 'form', 'values' => [PraktikSortasiKoperasi::RUTIN->value, PraktikSortasiKoperasi::TIDAK_KONSISTEN->value]],
['field' => 'praktik_pengeringan', 'source' => 'form', 'values' => [PraktikPengeringanKoperasi::RUTIN->value, PraktikPengeringanKoperasi::KADANG->value]],
['field' => 'praktik_fermentasi', 'source' => 'form', 'values' => [PraktikFermentasiKoperasi::RUTIN->value, PraktikFermentasiKoperasi::PERNAH->value]],
]];
}
public function normalize(array $data, array $context): array
{
$data = parent::normalize($data, $context);
if (($data['status_pembukuan'] ?? null) === StatusPembukuanKoperasi::BELUM->value) {
$data['format_pembukuan'] = FormatPembukuanKoperasi::TIDAK_ADA->value;
}
return $data;
}
}
@@ -0,0 +1,198 @@
<?php
namespace App\Services\Koperasi;
use App\Enums\Koperasi\FokusKomoditasKoperasi;
use App\Enums\Koperasi\JenisKoperasi;
use App\Enums\Koperasi\LayananKoperasi;
use App\Enums\Koperasi\StatusAdArtKoperasi;
use App\Enums\Koperasi\UrutanKoperasi;
use App\Enums\Role;
use App\Models\Koperasi\PengurusKoperasi;
use Illuminate\Support\Collection;
class PengurusKoperasiService
{
public function __construct(private WilayahKoperasiService $wilayah) {}
public const RUPIAH_FIELDS = [
'shu_2025', 'shu_2024', 'shu_2023', 'simpanan_terbesar', 'harga_premium_rupiah',
'harga_segar_sekarang', 'harga_segar_1_tahun', 'harga_segar_2_tahun',
'harga_basah_sekarang', 'harga_basah_1_tahun', 'harga_basah_2_tahun',
'harga_kering_sekarang', 'harga_kering_1_tahun', 'harga_kering_2_tahun',
'harga_fermentasi_sekarang', 'harga_fermentasi_1_tahun', 'harga_fermentasi_2_tahun',
];
public function indexData(array $validated): array
{
$filters = [
'search' => $validated['search'] ?? '',
'kabupaten_kota_id' => $validated['kabupaten_kota_id'] ?? ($this->wilayah->available() ? $this->wilayah->defaultKabupaten()?->id : ''),
'kecamatan_id' => $validated['kecamatan_id'] ?? '', 'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? '',
'jenis_koperasi' => $validated['jenis_koperasi'] ?? '',
'layanan' => $validated['layanan'] ?? '', 'ad_art' => $validated['ad_art'] ?? '',
'sort_by' => $validated['sort_by'] ?? UrutanKoperasi::TERBARU->value,
];
$query = PengurusKoperasi::query()->with('creator:id,name')
->when(filled($filters['jenis_koperasi']), function ($query) use ($filters) {
if ($filters['jenis_koperasi'] === JenisKoperasi::LAINNYA->value) {
$standard = array_map(fn ($jenis) => $jenis->value, array_filter(JenisKoperasi::cases(), fn ($jenis) => $jenis !== JenisKoperasi::LAINNYA));
return $query->whereNotIn('jenis_koperasi', $standard);
}
return $query->where('jenis_koperasi', $filters['jenis_koperasi']);
})
->when(filled($filters['layanan']), fn ($query) => $query->whereJsonContains('layanan_koperasi', $filters['layanan']))
->when(filled($filters['ad_art']), fn ($query) => $query->where('ad_art', $filters['ad_art']))
->when(filled($filters['search']), function ($query) use ($filters) {
$query->where(function ($query) use ($filters) {
foreach (['nama_koperasi', 'alamat_koperasi', 'jenis_koperasi', 'nama_lengkap'] as $field) {
$query->orWhereLike($field, '%'.$filters['search'].'%');
}
});
});
$this->wilayah->applyFilters($query, $filters);
$order = UrutanKoperasi::from($filters['sort_by']);
match ($order) {
UrutanKoperasi::NAMA_ASC, UrutanKoperasi::NAMA_DESC => $query->orderByRaw('LOWER(nama_koperasi) '.($order === UrutanKoperasi::NAMA_ASC ? 'asc' : 'desc')),
UrutanKoperasi::ANGGOTA_ASC, UrutanKoperasi::ANGGOTA_DESC => $query
->orderByRaw('CASE WHEN jumlah_anggota_dewasa IS NULL OR jumlah_anggota_anak IS NULL THEN 1 ELSE 0 END')
->orderByRaw('(jumlah_anggota_dewasa + jumlah_anggota_anak) '.($order === UrutanKoperasi::ANGGOTA_ASC ? 'asc' : 'desc')),
UrutanKoperasi::TERBARU => $query->latest(),
};
$koperasi = $query->orderByDesc('id')->paginate((int) ($validated['per_page'] ?? 10))->withQueryString();
$ringkasanKoperasi = $koperasi->getCollection()->mapWithKeys(fn (PengurusKoperasi $item) => [$item->id => $this->profileSummary($item)]);
$jenisTerdata = PengurusKoperasi::whereNotNull('jenis_koperasi')->where('jenis_koperasi', '!=', '')
->distinct()->orderBy('jenis_koperasi')->pluck('jenis_koperasi');
$jenisKoperasiOptions = collect(JenisKoperasi::cases())->map(fn ($jenis) => $jenis->value)
->merge($jenisTerdata)->unique()->values();
return [
'koperasi' => $koperasi,
'ringkasanKoperasi' => $ringkasanKoperasi,
'filters' => $filters,
'jenisKoperasiOptions' => $jenisKoperasiOptions,
...$this->wilayah->filterOptions($filters),
'showEnumerator' => in_array(auth()->user()?->role, [Role::SUPER_ADMIN, Role::ADMIN], true),
'totalKoperasi' => PengurusKoperasi::count(),
'totalJenisKoperasi' => $jenisTerdata->count(),
'totalDokumenLengkap' => PengurusKoperasi::where('ad_art', StatusAdArtKoperasi::LENGKAP->value)->count(),
];
}
public function profileViewData(?PengurusKoperasi $pengurus = null): array
{
return [
'pengurus' => $pengurus ?? new PengurusKoperasi,
'ringkasanProfil' => $this->profileSummary($pengurus),
...$this->wilayah->profileOptions($pengurus, session()->getOldInput()),
];
}
public function profileSummary(?PengurusKoperasi $pengurus = null): array
{
$empty = 'Belum terisi';
$dewasa = $pengurus?->jumlah_anggota_dewasa;
$anak = $pengurus?->jumlah_anggota_anak;
$count = fn ($value) => filled($value) ? number_format((int) $value, 0, ',', '.') : $empty;
$layanan = collect($pengurus?->layanan_koperasi ?? [])->map(function (LayananKoperasi $item) use ($pengurus) {
return $item === LayananKoperasi::LAINNYA && filled($pengurus->layanan_lainnya)
? $item->label().': '.$pengurus->layanan_lainnya : $item->label();
})->unique()->values()->all();
return [
'judul' => filled($pengurus?->nama_koperasi) ? $pengurus->nama_koperasi : 'Nama Koperasi',
'jenis' => filled($pengurus?->jenis_koperasi) ? $pengurus->jenis_koperasi : $empty,
'alamat' => filled($pengurus?->alamat_koperasi) ? $pengurus->alamat_koperasi : $empty,
'tahun_berdiri' => $pengurus?->tahun_berdiri ?? $empty,
'anggota' => filled($dewasa) && filled($anak) ? $count((int) $dewasa + (int) $anak) : $empty,
'anggota_rincian' => filled($dewasa) || filled($anak) ? 'Dewasa: '.$count($dewasa).' · Anak: '.$count($anak) : null,
'pengurus' => $count($pengurus?->jumlah_pengurus),
'layanan' => $layanan,
'responden' => filled($pengurus?->nama_lengkap) ? $pengurus->nama_lengkap : $empty,
'jabatan' => $pengurus?->posisi_dalam_koperasi?->value ?? $empty,
'kontak' => filled($pengurus?->no_hp) ? $pengurus->no_hp : $empty,
'diperbarui' => $pengurus?->updated_at ?? $pengurus?->created_at,
'tersimpan' => $pengurus?->exists ?? false,
];
}
public static function normalizeRupiah(mixed $value): mixed
{
if (is_string($value) && preg_match('/^-?\d{1,3}(?:\.\d{3})+$/D', $value)) {
return str_replace('.', '', $value);
}
return $value;
}
public static function formatRupiah(mixed $value): string
{
$value = (string) self::normalizeRupiah($value);
return preg_match('/^-?\d+$/D', $value)
? preg_replace('/\B(?=(\d{3})+(?!\d))/', '.', $value)
: $value;
}
public static function hasSelection(mixed $value, string $selection): bool
{
if ($value instanceof Collection) {
$value = $value->all();
}
if ($value instanceof \BackedEnum) {
$value = $value->value;
}
if (is_array($value)) {
return in_array($selection, array_map(fn ($item) => $item instanceof \BackedEnum ? $item->value : $item, $value), true);
}
$decoded = json_decode((string) $value, true);
return is_array($decoded)
? in_array($selection, $decoded, true)
: str_contains((string) $value, $selection);
}
public function profileData(array $data): array
{
unset($data['provinsi_id'], $data['kabupaten_kota_id'], $data['kecamatan_id'], $data['created_by']);
if (! $this->wilayah->available()) {
unset($data['desa_kelurahan_id']);
}
if ($data['jenis_koperasi'] === JenisKoperasi::LAINNYA->value && ! empty($data['jenis_koperasi_lainnya'])) {
$data['jenis_koperasi'] = $data['jenis_koperasi_lainnya'];
}
unset($data['jenis_koperasi_lainnya']);
if (array_key_exists('fokus_komoditas', $data) && $data['fokus_komoditas'] !== FokusKomoditasKoperasi::YA->value) {
$data['detail_fokus_komoditas'] = null;
}
return $data;
}
public function createProfile(array $data, int $creatorId): PengurusKoperasi
{
$data = $this->profileData($data);
if ($this->wilayah->available()) {
$data['created_by'] = $creatorId;
}
return PengurusKoperasi::create($data);
}
public function tabData(array $data): array
{
$model = new PengurusKoperasi;
foreach ($data as $field => $value) {
if (is_array($value) && ! $model->hasCast($field)) {
$data[$field] = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}
}
return $data;
}
}
@@ -0,0 +1,71 @@
<?php
namespace App\Services\Koperasi;
use App\Enums\Koperasi\BagianRantaiNilaiKoperasi;
use App\Enums\Koperasi\BentukProdukKoperasi;
use App\Enums\Koperasi\HubunganPemasokKoperasi;
use App\Enums\Koperasi\PenetapanHargaKoperasi;
use App\Enums\Koperasi\SumberPasokanKoperasi;
use App\Enums\Koperasi\TujuanPenjualanKoperasi;
class RantaiNilaiKoperasiService extends KoperasiSurveyService
{
public const PERIODS = ['sekarang' => 'Panen Terakhir', '1_tahun' => 'Tahun Lalu', '2_tahun' => 'Dua Tahun Lalu'];
public function dependencies(): array
{
$when = fn (string $field, array $values) => ['field' => $field, 'values' => $values, 'source' => 'form'];
$buyers = array_values(array_map(fn ($case) => $case->value, array_filter(
TujuanPenjualanKoperasi::cases(), fn ($case) => $case !== TujuanPenjualanKoperasi::BELUM_MENJUAL,
)));
$rules = [
'sumber_pasokan_lainnya' => [$when('sumber_pasokan', [SumberPasokanKoperasi::LAINNYA->value])],
'tujuan_penjualan_lainnya' => [$when('tujuan_penjualan', [TujuanPenjualanKoperasi::LAINNYA->value])],
'sifat_hubungan_lainnya' => [$when('sifat_hubungan_bisnis', [HubunganPemasokKoperasi::LAINNYA->value])],
'cara_menetapkan_harga_lainnya' => [$when('cara_menetapkan_harga_pasar', [PenetapanHargaKoperasi::LAINNYA->value])],
'bentuk_penjualan' => [$when('tujuan_penjualan', $buyers)],
];
foreach (array_keys(self::PERIODS) as $period) {
$rules['volume_jual_'.$period] = [$when('tujuan_penjualan', $buyers)];
}
return $rules;
}
public function requiredFields(): array
{
return ['sumber_pasokan_lainnya', 'tujuan_penjualan_lainnya', 'sifat_hubungan_lainnya', 'cara_menetapkan_harga_lainnya'];
}
protected function arrayFields(): array
{
return ['bentuk_penjualan'];
}
public function sectionFields(BagianRantaiNilaiKoperasi $section): array
{
if ($section === BagianRantaiNilaiKoperasi::HARGA) {
$fields = ['cara_menetapkan_harga_pasar', 'cara_menetapkan_harga_lainnya'];
foreach (BentukProdukKoperasi::cases() as $product) {
foreach (array_keys(self::PERIODS) as $period) {
$fields[] = 'harga_'.$product->pricePrefix().'_'.$period;
}
}
return $fields;
}
$fields = [
'sumber_pasokan', 'sumber_pasokan_lainnya', 'bentuk_pembelian',
'tujuan_penjualan', 'tujuan_penjualan_lainnya', 'bentuk_penjualan',
'sifat_hubungan_bisnis', 'sifat_hubungan_lainnya', 'mekanisme_pembayaran', 'status_perjanjian_tertulis',
];
foreach (array_keys(self::PERIODS) as $period) {
$fields[] = 'volume_beli_'.$period;
$fields[] = 'volume_jual_'.$period;
}
return $fields;
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Services\Koperasi;
use App\Enums\Koperasi\AlasanPinjamanKoperasi;
use App\Enums\Koperasi\BentukPembayaranKoperasi;
use App\Enums\Koperasi\JawabanYaTidakKoperasi;
use App\Enums\Koperasi\JenisKoperasi;
use App\Enums\Koperasi\KetersediaanKoperasi;
use App\Models\Koperasi\PengurusKoperasi;
class SosialEkonomiKoperasiService extends KoperasiSurveyService
{
public function context(?PengurusKoperasi $model): array
{
return ['jenis_koperasi' => $model?->jenis_koperasi];
}
public function dependencies(): array
{
$when = fn (string $field, array $values, string $source = 'form') => [
'field' => $field, 'values' => $values, 'source' => $source,
];
$simpanPinjam = $when('jenis_koperasi', [JenisKoperasi::SIMPAN_PINJAM->value], 'profile');
return [
'alasan_pinjaman' => [$simpanPinjam],
'alasan_pinjaman_lainnya' => [$when('alasan_pinjaman', [AlasanPinjamanKoperasi::LAINNYA->value])],
'pembayaran_selain_uang' => [$simpanPinjam],
'bentuk_pembayaran_barang' => [$when('pembayaran_selain_uang', [KetersediaanKoperasi::ADA->value])],
'bentuk_pembayaran_lainnya' => [$when('bentuk_pembayaran_barang', [BentukPembayaranKoperasi::LAINNYA->value])],
'perhitungan_harga_pasar' => [$when('bentuk_pembayaran_barang', [BentukPembayaranKoperasi::HASIL_BUMI->value])],
'alasan_bukan_harga_pasar' => [$when('perhitungan_harga_pasar', [JawabanYaTidakKoperasi::TIDAK->value])],
];
}
public function requiredFields(): array
{
return ['alasan_pinjaman_lainnya', 'bentuk_pembayaran_barang', 'bentuk_pembayaran_lainnya', 'perhitungan_harga_pasar', 'alasan_bukan_harga_pasar'];
}
protected function arrayFields(): array
{
return ['alasan_pinjaman', 'bentuk_pembayaran_barang'];
}
}
@@ -0,0 +1,96 @@
<?php
namespace App\Services\Koperasi;
use App\Models\Koperasi\PengurusKoperasi;
use App\Models\Wilayah\DesaKelurahan;
use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan;
use App\Models\Wilayah\Provinsi;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Schema;
class WilayahKoperasiService
{
private ?bool $available = null;
public function available(): bool
{
return $this->available ??= Schema::hasColumn('pengurus_koperasi', 'desa_kelurahan_id')
&& Schema::hasColumn('pengurus_koperasi', 'created_by');
}
public function applyFilters(Builder $query, array $filters): void
{
if (! $this->available()) {
if (filled($filters['kabupaten_kota_id']) || filled($filters['kecamatan_id']) || filled($filters['desa_kelurahan_id'])) {
$query->whereRaw('1 = 0');
}
return;
}
// Seperti BUMDes, lokasi koperasi memakai seluruh master wilayah, termasuk di luar program aktif.
// Profil yang belum memiliki desa tetap dapat ditemukan dan dilengkapi pada daftar kabupaten.
$query->when($filters['kabupaten_kota_id'], fn ($query, $id) => $query->where(fn ($wilayah) => $wilayah
->whereHas('desaKelurahan', fn ($desa) => $desa
->withoutGlobalScope('program')->whereHas('kecamatan', fn ($kecamatan) => $kecamatan
->withoutGlobalScope('program')->where('kabupaten_kota_id', $id)))
->orWhereNull('desa_kelurahan_id')))
->when($filters['kecamatan_id'], fn ($query, $id) => $query->whereHas('desaKelurahan', fn ($desa) => $desa
->withoutGlobalScope('program')->where('kecamatan_id', $id)))
->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id));
}
public function filterOptions(array $filters): array
{
$ready = $this->available();
return [
'wilayahTersedia' => $ready,
'selectedKabupaten' => filled($filters['kabupaten_kota_id'])
? KabupatenKota::withoutGlobalScope('program')->find($filters['kabupaten_kota_id'], ['id', 'nama']) : $this->defaultKabupaten(),
'kecamatanOptions' => $ready && filled($filters['kabupaten_kota_id'])
? Kecamatan::withoutGlobalScope('program')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get(['id', 'nama']) : collect(),
'desaOptions' => $ready && filled($filters['kecamatan_id'])
? DesaKelurahan::withoutGlobalScope('program')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get(['id', 'nama']) : collect(),
];
}
public function defaultKabupaten(): ?KabupatenKota
{
return KabupatenKota::withoutGlobalScope('program')->whereLike('nama', '%ngada%')->orderBy('nama')->first(['id', 'nama']);
}
public function profileOptions(?PengurusKoperasi $model, array $old): array
{
if (! $this->available()) {
return ['wilayahTersedia' => false];
}
$model?->loadMissing([
'desaKelurahan' => fn ($query) => $query->withoutGlobalScope('program'),
'desaKelurahan.kecamatan' => fn ($query) => $query->withoutGlobalScope('program'),
'desaKelurahan.kecamatan.kabupatenKota' => fn ($query) => $query->withoutGlobalScope('program'),
]);
$desa = $model?->desaKelurahan;
$value = function ($key, $fallback) use ($old) {
$selected = array_key_exists($key, $old) ? $old[$key] : $fallback;
return is_int($selected) || (is_string($selected) && ctype_digit($selected)) ? $selected : null;
};
$selected = [
'provinsi_id' => $value('provinsi_id', $desa?->kecamatan?->kabupatenKota?->provinsi_id),
'kabupaten_kota_id' => $value('kabupaten_kota_id', $desa?->kecamatan?->kabupaten_kota_id),
'kecamatan_id' => $value('kecamatan_id', $desa?->kecamatan_id),
'desa_kelurahan_id' => $value('desa_kelurahan_id', $model?->desa_kelurahan_id),
];
return [
...$this->filterOptions($selected),
'selectedWilayah' => $selected,
'provinsiOptions' => Provinsi::withoutGlobalScope('program')->orderBy('nama')->get(['id', 'nama']),
'kabupatenOptions' => filled($selected['provinsi_id'])
? KabupatenKota::withoutGlobalScope('program')->where('provinsi_id', $selected['provinsi_id'])->orderBy('nama')->get(['id', 'nama']) : collect(),
];
}
}