From a355a1d1d84f591e6ae7dc9d8a4a8f0a25eda9eb Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Mon, 8 Jun 2026 11:23:33 +0700 Subject: [PATCH] Panen and include all support --- .../Api/PanenTanamanController.php | 143 ++++++++++++++++++ .../Api/Produktivitas/PanenController.php | 61 ++++++-- .../Api/Produktivitas/PanenController01.php | 136 +++++++++++++++++ app/Models/Map/Panen.php | 26 +++- app/Models/Map/PanenTanaman.php | 25 +++ .../2026_04_27_092223_create_panen_table.php | 6 +- ...6_06_134032_create_panen_tanamen_table.php | 38 +++++ 7 files changed, 411 insertions(+), 24 deletions(-) create mode 100644 app/Http/Controllers/Api/PanenTanamanController.php create mode 100644 app/Http/Controllers/Api/Produktivitas/PanenController01.php create mode 100644 app/Models/Map/PanenTanaman.php create mode 100644 database/migrations/2026_06_06_134032_create_panen_tanamen_table.php diff --git a/app/Http/Controllers/Api/PanenTanamanController.php b/app/Http/Controllers/Api/PanenTanamanController.php new file mode 100644 index 0000000..731f662 --- /dev/null +++ b/app/Http/Controllers/Api/PanenTanamanController.php @@ -0,0 +1,143 @@ +get(); + + return response()->json([ + 'success' => true, + 'message' => 'Daftar data panen tanaman berhasil diambil.', + 'data' => $panenTanaman + ], 200); + } catch (Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Terjadi kesalahan saat mengambil data.', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + $validator = Validator::make($request->all(), [ + 'panen_id' => 'required|integer', + 'tanaman_id' => 'required|integer', + 'commodity_name' => 'required|string|max:255', + 'sync_status' => 'nullable|string|max:50', + 'weight_kg' => 'required|numeric|min:0', + ]); + + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Validasi gagal.', + 'errors' => $validator->errors() + ], 422); + } + + try { + $panenTanaman = PanenTanaman::create($request->all()); + + return response()->json([ + 'success' => true, + 'message' => 'Data panen tanaman berhasil disimpan.', + 'data' => $panenTanaman + ], 201); + } catch (Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Gagal menyimpan data.', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * Display the specified resource. + */ + public function show(PanenTanaman $panenTanaman) + { + return response()->json([ + 'success' => true, + 'message' => 'Detail data panen tanaman ditemukan.', + 'data' => $panenTanaman + ], 200); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, PanenTanaman $panenTanaman) + { + $validator = Validator::make($request->all(), [ + 'panen_id' => 'sometimes|required|integer', + 'tanaman_id' => 'sometimes|required|integer', + 'commodity_name' => 'sometimes|required|string|max:255', + 'sync_status' => 'nullable|string|max:50', + 'weight_kg' => 'sometimes|required|numeric|min:0', + ]); + + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Validasi gagal.', + 'errors' => $validator->errors() + ], 422); + } + + try { + $panenTanaman->update($request->all()); + + return response()->json([ + 'success' => true, + 'message' => 'Data panen tanaman berhasil diperbarui.', + 'data' => $panenTanaman + ], 200); + } catch (Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Gagal memperbarui data.', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(PanenTanaman $panenTanaman) + { + try { + $panenTanaman->delete(); // Karena pakai SoftDeletes, data tidak terhapus permanen di DB + + return response()->json([ + 'success' => true, + 'message' => 'Data panen tanaman berhasil dihapus (soft delete).' + ], 200); + } catch (Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Gagal menghapus data.', + 'error' => $e->getMessage() + ], 500); + } + } +} diff --git a/app/Http/Controllers/Api/Produktivitas/PanenController.php b/app/Http/Controllers/Api/Produktivitas/PanenController.php index c537b58..277f45b 100644 --- a/app/Http/Controllers/Api/Produktivitas/PanenController.php +++ b/app/Http/Controllers/Api/Produktivitas/PanenController.php @@ -1,12 +1,13 @@ validate([ + 'uid' => ['required', 'string'], + 'title' => ['required', 'string'], 'land_id' => ['required', 'integer'], - 'plant_id' => ['required', 'integer'], - 'commodity_name' => ['required', 'string', 'max:255'], - 'weight_kg' => ['required', 'numeric'], + 'farmer_id' => ['required', 'integer'], + 'land_description' => ['nullable', 'string'], 'harvest_date' => ['required', 'date'], + 'commodities' => ['required', 'array', 'min:1'], + 'commodities.*.plant_id' => ['nullable', 'integer'], + 'commodities.*.commodity_name' => ['required', 'string'], + 'commodities.*.weight_kg' => ['required', 'numeric'], 'notes' => ['nullable', 'string'], ]); - $panen = Panen::create([ - 'land_id' => $validated['land_id'], - 'plant_id' => $validated['plant_id'], - 'farmer_id' => Auth::user()->profile->id, - 'commodity_name' => $validated['commodity_name'], - 'weight_kg' => $validated['weight_kg'], - 'harvest_date' => $validated['harvest_date'], - 'notes' => $validated['notes'] ?? null, - 'sync_status' => 'synced' - ]); + try { + $panen = DB::transaction(function () use ($validated) { + + $panenData = collect($validated)->except('commodities')->toArray(); + $panen = Panen::create($panenData); - return response()->json($panen, 201); + foreach ($validated['commodities'] as $commodity) { + \App\Models\Map\PanenTanaman::create([ + 'panen_id' => $panen->id, + 'tanaman_id' => $commodity['plant_id'] ?? null, + 'commodity_name' => $commodity['commodity_name'], + 'weight_kg' => $commodity['weight_kg'], + 'sync_status' => 'synced', + ]); + } + + return $panen; + }); + $panen->load('panenTanaman'); + + return response()->json([ + 'success' => true, + 'message' => 'Data panen dan detail tanaman berhasil disimpan.', + 'data' => $panen + ], 201); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Gagal menyimpan data ke database.', + 'error' => $e->getMessage() + ], 500); + } } + + public function show(string $id): JsonResponse { if (!Gate::any(['petani', 'fasilitator','super_admin'])) { diff --git a/app/Http/Controllers/Api/Produktivitas/PanenController01.php b/app/Http/Controllers/Api/Produktivitas/PanenController01.php new file mode 100644 index 0000000..edb49a3 --- /dev/null +++ b/app/Http/Controllers/Api/Produktivitas/PanenController01.php @@ -0,0 +1,136 @@ +profile; + + $profileId = $user->role === 'petani' ? $profile->id : $request->id; + $listPanen = Panen::when($profileId, fn($q,$v) => $q->where('farmer_id',$user->id))->get(); + + return response()->json($listPanen); + } + + public function store(Request $request): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + + $validated = $request->validate([ + 'land_id' => ['required', 'integer'], + 'plant_id' => ['required', 'integer'], + 'commodity_name' => ['required', 'string', 'max:255'], + 'weight_kg' => ['required', 'numeric'], + 'harvest_date' => ['required', 'date'], + 'notes' => ['nullable', 'string'], + ]); + + $panen = Panen::create([ + 'land_id' => $validated['land_id'], + 'plant_id' => $validated['plant_id'], + 'farmer_id' => Auth::user()->profile->id, + 'commodity_name' => $validated['commodity_name'], + 'weight_kg' => $validated['weight_kg'], + 'harvest_date' => $validated['harvest_date'], + 'notes' => $validated['notes'] ?? null, + 'sync_status' => 'synced' + ]); + + return response()->json($panen, 201); + } + + public function show(string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + $panen = Panen::findOrFail($id); + return response()->json($panen); + } + + public function update(Request $request, string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + $panen = Panen::findOrFail($id); + + $validated = $request->validate([ + 'commodity_name' => ['string', 'max:255'], + 'weight_kg' => ['numeric'], + 'harvest_date' => ['date'], + 'notes' => ['nullable', 'string'], + ]); + + $panen->update($validated); + return response()->json($panen); + } + + public function destroy(string $id): JsonResponse + { + Gate::authorize(['petani', 'fasilitator']); + Panen::findOrFail($id)->delete(); + return response()->json(null, 204); + } + + /** + * Digunakan untuk sinkronisasi massal dari data lokal (offline) + */ + public function batchUpsert(Request $request): JsonResponse + { + if (Gate::authorize(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + $profileId = Auth::user()->profile->id; + + $validated = $request->validate([ + 'panens' => ['required', 'array'], + 'panens.*.id' => ['nullable', 'integer'], + 'panens.*.land_id' => ['required', 'integer'], + 'panens.*.plant_id' => ['required', 'integer'], + 'panens.*.commodity_name' => ['required', 'string'], + 'panens.*.weight_kg' => ['required', 'numeric'], + 'panens.*.harvest_date' => ['required', 'date'], + ]); + + $dataUpsert = []; + foreach ($validated['panens'] as $item) { + $dataUpsert[] = [ + 'id' => $item['id'] ?? null, + 'land_id' => $item['land_id'], + 'plant_id' => $item['plant_id'], + 'farmer_id' => $profileId, + 'commodity_name' => $item['commodity_name'], + 'weight_kg' => $item['weight_kg'], + 'harvest_date' => $item['harvest_date'], + 'sync_status' => 'synced', + 'created_at' => now(), + 'updated_at' => now(), + ]; + } + + // Melakukan upsert berdasarkan ID + Panen::upsert($dataUpsert, ['id'], [ + 'weight_kg', 'commodity_name', 'harvest_date', 'sync_status', 'updated_at' + ]); + + return response()->json(['status' => 'success'], 200); + } +} \ No newline at end of file diff --git a/app/Models/Map/Panen.php b/app/Models/Map/Panen.php index df007a2..47386c3 100644 --- a/app/Models/Map/Panen.php +++ b/app/Models/Map/Panen.php @@ -5,6 +5,8 @@ namespace App\Models\Map; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class Panen extends Model @@ -18,11 +20,12 @@ class Panen extends Model * Atribut yang dapat diisi (mass assignable). */ protected $fillable = [ + 'uid', // PN-1234567890-1234567890-1234567890 (lahan-tanaman-panen) + // 'plant_id', + 'title', 'land_id', - 'plant_id', 'farmer_id', - 'commodity_name', - 'weight_kg', + 'land_description', 'harvest_date', 'notes', 'sync_status', @@ -41,12 +44,25 @@ class Panen extends Model { return $this->belongsTo(Lahan::class, 'land_id'); } - public function tanaman(): BelongsTo + public function detailKomoditas(): HasMany { - return $this->belongsTo(Tanaman::class, 'plant_id'); + return $this->hasMany(PanenKomoditas::class, 'panen_id'); + } + + public function tanamans(): BelongsToMany + { + return $this->belongsToMany(Tanaman::class, 'panen_komoditas', 'panen_id', 'plant_id') + ->using(PanenKomoditas::class) + ->withPivot(['uid', 'commodity_name', 'weight_kg', 'sync_status']) + ->withTimestamps(); } public function petani(): BelongsTo { return $this->belongsTo(\App\Models\Profile::class, 'farmer_id'); } + + public function panenTanaman(): HasMany + { + return $this->hasMany(PanenTanaman::class, 'panen_id', 'id'); + } } \ No newline at end of file diff --git a/app/Models/Map/PanenTanaman.php b/app/Models/Map/PanenTanaman.php new file mode 100644 index 0000000..b735a70 --- /dev/null +++ b/app/Models/Map/PanenTanaman.php @@ -0,0 +1,25 @@ +belongsTo(\App\Models\Map\Panen::class, 'panen_id', 'id'); + } +} diff --git a/database/migrations/2026_04_27_092223_create_panen_table.php b/database/migrations/2026_04_27_092223_create_panen_table.php index e03ded3..70b346c 100644 --- a/database/migrations/2026_04_27_092223_create_panen_table.php +++ b/database/migrations/2026_04_27_092223_create_panen_table.php @@ -8,11 +8,11 @@ return new class extends Migration { public function up(): void { Schema::create('map_panen', function (Blueprint $table) { $table->id(); + $table->string('uid')->unique()->nullable(); // Bagus untuk sinkronisasi Dexie offline + $table->string('title'); // Judul aktivitas panen $table->unsignedBigInteger('land_id')->nullable(); - $table->unsignedBigInteger('plant_id')->nullable(); $table->unsignedBigInteger('farmer_id')->nullable(); - $table->string('commodity_name'); - $table->double('weight_kg')->default(0); + $table->text('land_description')->nullable(); // Deskripsi kondisi lahan saat panen $table->date('harvest_date'); $table->text('notes')->nullable(); $table->string('sync_status')->default('pending'); diff --git a/database/migrations/2026_06_06_134032_create_panen_tanamen_table.php b/database/migrations/2026_06_06_134032_create_panen_tanamen_table.php new file mode 100644 index 0000000..9ab2c9a --- /dev/null +++ b/database/migrations/2026_06_06_134032_create_panen_tanamen_table.php @@ -0,0 +1,38 @@ +id(); + + $bluePrint->foreignId('panen_id') + ->constrained('panen') + ->onDelete('cascade'); + + $bluePrint->unsignedBigInteger('tanaman_id'); + $bluePrint->string('commodity_name'); + $bluePrint->string('sync_status', 50)->nullable()->default('synced'); + $bluePrint->decimal('weight_kg', 10, 2); // Menggunakan decimal agar mendukung angka pecahan (misal: 10.5 kg) + + $bluePrint->timestamps(); + $bluePrint->softDeletes(); // Menambahkan kolom deleted_at untuk fitur SoftDeletes + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('panen_tanamen'); + } +};