revisi semhas

This commit is contained in:
ninavirgiana 2026-05-04 12:18:09 +07:00
parent dc36c6fa51
commit c297a81116
20 changed files with 736 additions and 129 deletions

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\MutasiAyam; use App\Models\MutasiAyam;
use App\Models\JenisMutasi;
use App\Models\Kandang; use App\Models\Kandang;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -13,7 +14,8 @@ class AyamController extends Controller
{ {
public function index(Request $request) public function index(Request $request)
{ {
$query = MutasiAyam::with('kandang'); $query = MutasiAyam::with(['kandang', 'jenisMutasi']);
$jenisMutasi = JenisMutasi::all();
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = Auth::user(); $user = Auth::user();
@ -45,32 +47,33 @@ public function index(Request $request)
->select('id', 'nama_kandang') ->select('id', 'nama_kandang')
->selectSub(function ($q) { ->selectSub(function ($q) {
$q->from('mutasi_ayam') $q->from('mutasi_ayam')
->join('jenis_mutasi', 'mutasi_ayam.jenis_mutasi_id', '=', 'jenis_mutasi.id')
->whereColumn('mutasi_ayam.kandang_id', 'kandang.id') ->whereColumn('mutasi_ayam.kandang_id', 'kandang.id')
->selectRaw(" ->selectRaw("
COALESCE(SUM( COALESCE(SUM(
CASE CASE
WHEN jenis_mutasi = 'masuk' THEN jumlah WHEN jenis_mutasi.tipe = 'tambah' THEN jumlah
WHEN jenis_mutasi IN ('mati','afkir','pindah') THEN -jumlah WHEN jenis_mutasi.tipe = 'kurang' THEN -jumlah
WHEN jenis_mutasi.tipe = 'transfer' THEN -jumlah
ELSE 0 ELSE 0
END END
), 0) ),0)
"); ");
}, 'jumlah_ayam_terakhir') }, 'jumlah_ayam_terakhir')
->get(); ->get();
return view('inventori-ayam', compact('data', 'kandang', 'jenisMutasi'));
return view('inventori-ayam', compact('data', 'kandang')); }
}
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([ $request->validate([
'jenis_mutasi' => 'required|in:masuk,mati,afkir,pindah', 'jenis_mutasi_id' => 'required|exists:jenis_mutasi,id',
'jumlah' => 'required|integer|min:1|max:9999', 'jumlah' => 'required|integer|min:1|max:9999',
'tanggal' => 'required|date|before_or_equal:today', 'tanggal' => 'required|date|before_or_equal:today',
'keterangan' => 'required_if:jenis_mutasi,masuk,mati,afkir|string|max:255', 'keterangan' => 'nullable|string|max:255',
], [ ], [
'jenis_mutasi.required' => 'Jenis mutasi wajib dipilih.', 'jenis_mutasi_id.required' => 'Jenis mutasi wajib dipilih.',
'jenis_mutasi.in' => 'Jenis mutasi tidak valid.', 'jenis_mutasi_id.exists' => 'Jenis mutasi tidak valid.',
'jumlah.required' => 'Jumlah ayam wajib diisi.', 'jumlah.required' => 'Jumlah ayam wajib diisi.',
'jumlah.integer' => 'Jumlah ayam harus berupa angka.', 'jumlah.integer' => 'Jumlah ayam harus berupa angka.',
@ -87,10 +90,14 @@ public function store(Request $request)
DB::transaction(function () use ($request) { DB::transaction(function () use ($request) {
// PINDAH // ✅ ambil jenis mutasi dari tabel master
if ($request->jenis_mutasi === 'pindah') { $jenisPindah = JenisMutasi::where('nama', 'pindah')->first();
$jenisMasuk = JenisMutasi::where('nama', 'masuk')->first();
$request->validate([ // PINDAH
$jenis = JenisMutasi::findOrFail($request->jenis_mutasi_id);
if ($jenis->nama === 'pindah'){ $request->validate([
'kandang_asal_id' => 'required|exists:kandang,id|different:kandang_tujuan_id', 'kandang_asal_id' => 'required|exists:kandang,id|different:kandang_tujuan_id',
'kandang_tujuan_id' => 'required|exists:kandang,id', 'kandang_tujuan_id' => 'required|exists:kandang,id',
], [ ], [
@ -103,14 +110,17 @@ public function store(Request $request)
$kandangTujuan = Kandang::findOrFail($request->kandang_tujuan_id); $kandangTujuan = Kandang::findOrFail($request->kandang_tujuan_id);
$stokAsal = $kandangAsal->mutasiAyam() $stokAsal = $kandangAsal->mutasiAyam()
->join('jenis_mutasi', 'mutasi_ayam.jenis_mutasi_id', '=', 'jenis_mutasi.id')
->selectRaw(" ->selectRaw("
COALESCE(SUM( COALESCE(SUM(
CASE CASE
WHEN jenis_mutasi='masuk' THEN jumlah WHEN jenis_mutasi.tipe = 'tambah' THEN jumlah
WHEN jenis_mutasi IN ('mati','afkir','pindah') THEN -jumlah WHEN jenis_mutasi.tipe IN ('kurang','transfer') THEN -jumlah
ELSE 0 END ELSE 0
),0) as stok END
")->value('stok'); ),0) as stok
")
->value('stok');
if ($stokAsal <= 0) { if ($stokAsal <= 0) {
throw \Illuminate\Validation\ValidationException::withMessages([ throw \Illuminate\Validation\ValidationException::withMessages([
@ -127,8 +137,8 @@ public function store(Request $request)
MutasiAyam::create([ MutasiAyam::create([
'kandang_id' => $request->kandang_asal_id, 'kandang_id' => $request->kandang_asal_id,
'user_id' => Auth::id(), 'user_id' => Auth::id(),
'jenis_mutasi' => 'pindah', 'jenis_mutasi_id' => $jenisPindah->id,
'jumlah' => $request->jumlah, 'jumlah' => $request->jumlah,
'tanggal' => $request->tanggal, 'tanggal' => $request->tanggal,
'keterangan' => 'Pindah ke ' . $kandangTujuan->nama_kandang, 'keterangan' => 'Pindah ke ' . $kandangTujuan->nama_kandang,
]); ]);
@ -136,8 +146,8 @@ public function store(Request $request)
MutasiAyam::create([ MutasiAyam::create([
'kandang_id' => $request->kandang_tujuan_id, 'kandang_id' => $request->kandang_tujuan_id,
'user_id' => Auth::id(), 'user_id' => Auth::id(),
'jenis_mutasi' => 'masuk', 'jenis_mutasi_id' => $jenisMasuk->id,
'jumlah' => $request->jumlah, 'jumlah' => $request->jumlah,
'tanggal' => $request->tanggal, 'tanggal' => $request->tanggal,
'keterangan' => 'Pindahan dari ' . $kandangAsal->nama_kandang, 'keterangan' => 'Pindahan dari ' . $kandangAsal->nama_kandang,
]); ]);
@ -152,22 +162,24 @@ public function store(Request $request)
'kandang_id.required' => 'Nama kandang wajib dipilih.', 'kandang_id.required' => 'Nama kandang wajib dipilih.',
'kandang_id.exists' => 'Kandang tidak ditemukan.', 'kandang_id.exists' => 'Kandang tidak ditemukan.',
]); ]);
$kandang = Kandang::findOrFail($request->kandang_id); $kandang = Kandang::findOrFail($request->kandang_id);
$stok = $kandang->mutasiAyam()->selectRaw(" $stok = $kandang->mutasiAyam()
COALESCE(SUM( ->join('jenis_mutasi', 'mutasi_ayam.jenis_mutasi_id', '=', 'jenis_mutasi.id')
CASE ->selectRaw("
WHEN jenis_mutasi='masuk' THEN jumlah COALESCE(SUM(
WHEN jenis_mutasi IN ('mati','afkir','pindah') THEN -jumlah CASE
ELSE 0 END WHEN jenis_mutasi.tipe = 'tambah' THEN jumlah
),0) as stok WHEN jenis_mutasi.tipe IN ('kurang','transfer') THEN -jumlah
")->value('stok'); ELSE 0
END
),0) as stok
")
->value('stok'); // ← WAJIB
$jenis = JenisMutasi::findOrFail($request->jenis_mutasi_id);
// Kalau mutasi keluar/mati/afkir → cek stok // Kalau mutasi keluar/mati/afkir → cek stok
if (in_array($request->jenis_mutasi, ['mati', 'afkir'])) { if (in_array($jenis->nama, ['mati', 'afkir'])) {
if ($stok <= 0) {
if ($stok <= 0) {
throw \Illuminate\Validation\ValidationException::withMessages([ throw \Illuminate\Validation\ValidationException::withMessages([
'jumlah' => 'Stok ayam di kandang ini sudah habis.' 'jumlah' => 'Stok ayam di kandang ini sudah habis.'
]); ]);
@ -183,8 +195,8 @@ public function store(Request $request)
MutasiAyam::create([ MutasiAyam::create([
'kandang_id' => $request->kandang_id, 'kandang_id' => $request->kandang_id,
'user_id' => Auth::id(), 'user_id' => Auth::id(),
'jenis_mutasi' => $request->jenis_mutasi, 'jenis_mutasi_id' => $request->jenis_mutasi_id,
'jumlah' => $request->jumlah, 'jumlah' => $request->jumlah,
'tanggal' => $request->tanggal, 'tanggal' => $request->tanggal,
'keterangan' => $request->keterangan, 'keterangan' => $request->keterangan,
]); ]);
@ -200,8 +212,8 @@ public function update(Request $request, $id)
$mutasi = MutasiAyam::findOrFail($id); $mutasi = MutasiAyam::findOrFail($id);
// CEGAH EDIT DATA PINDAH // CEGAH EDIT DATA PINDAH
if ( if (
$mutasi->jenis_mutasi === 'pindah' || $mutasi->jenisMutasi->nama === 'pindah' ||
str_contains($mutasi->keterangan, 'Pindahan dari') str_contains($mutasi->keterangan, 'Pindahan dari')
) { ) {
abort(403, 'Data hasil pindahan tidak boleh diedit.'); abort(403, 'Data hasil pindahan tidak boleh diedit.');
} }
@ -214,16 +226,16 @@ public function update(Request $request, $id)
// VALIDASI MANUAL // VALIDASI MANUAL
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
'kandang_id' => 'required|exists:kandang,id', 'kandang_id' => 'required|exists:kandang,id',
'jenis_mutasi' => 'required|in:masuk,mati,afkir', 'jenis_mutasi_id' => 'required|exists:jenis_mutasi,id',
'jumlah' => 'required|integer|min:1|max:9999', 'jumlah' => 'required|integer|min:1|max:9999',
'tanggal' => 'required|date|before_or_equal:today', 'tanggal' => 'required|date|before_or_equal:today',
'keterangan' => 'required_if:jenis_mutasi,masuk,mati,afkir|string|max:255', 'keterangan' => 'nullable|string|max:255',
], [ ], [
'kandang_id.required' => 'Nama kandang wajib dipilih.', 'kandang_id.required' => 'Nama kandang wajib dipilih.',
'kandang_id.exists' => 'Kandang tidak ditemukan.', 'kandang_id.exists' => 'Kandang tidak ditemukan.',
'jenis_mutasi.required' => 'Jenis mutasi wajib dipilih.', 'jenis_mutasi_id.required' => 'Jenis mutasi wajib dipilih.',
'jenis_mutasi.in' => 'Jenis mutasi tidak valid.', 'jenis_mutasi_id.exists' => 'Jenis mutasi tidak valid.',
'jumlah.required' => 'Jumlah ayam wajib diisi.', 'jumlah.required' => 'Jumlah ayam wajib diisi.',
'jumlah.integer' => 'Jumlah ayam harus berupa angka.', 'jumlah.integer' => 'Jumlah ayam harus berupa angka.',
'jumlah.min' => 'Jumlah ayam minimal 1 ekor.', 'jumlah.min' => 'Jumlah ayam minimal 1 ekor.',
@ -241,29 +253,30 @@ public function update(Request $request, $id)
->withInput() ->withInput()
->with('edit_id', $mutasi->id); ->with('edit_id', $mutasi->id);
} }
$jenis = JenisMutasi::findOrFail($request->jenis_mutasi_id);
// HITUNG STOK // HITUNG STOK
$kandang = Kandang::findOrFail($request->kandang_id); $kandang = Kandang::findOrFail($request->kandang_id);
$stok = $kandang->mutasiAyam() $stok = $kandang->mutasiAyam()
->where('id', '!=', $mutasi->id) ->join('jenis_mutasi', 'mutasi_ayam.jenis_mutasi_id', '=', 'jenis_mutasi.id')
->selectRaw(" ->selectRaw("
COALESCE(SUM( COALESCE(SUM(
CASE CASE
WHEN jenis_mutasi='masuk' THEN jumlah WHEN jenis_mutasi.tipe = 'tambah' THEN jumlah
WHEN jenis_mutasi IN ('mati','afkir','pindah') THEN -jumlah WHEN jenis_mutasi.tipe IN ('kurang','transfer') THEN -jumlah
ELSE 0 END ELSE 0
),0) as stok END
")->value('stok'); ),0) as stok
")
->value('stok'); // ✅ WAJIB
// kembalikan stok lama jika mutasi lama adalah pengurang // kembalikan stok lama jika mutasi lama adalah pengurang
if (in_array($mutasi->jenis_mutasi, ['mati', 'afkir', 'pindah'])) { if (in_array($mutasi->jenisMutasi->nama, ['mati', 'afkir', 'pindah'])) {
$stok += $mutasi->jumlah; $stok += $mutasi->jumlah;
} }
// CEK STOK // CEK STOK
if (in_array($request->jenis_mutasi, ['mati', 'afkir'])) { if (in_array($jenis->nama, ['mati', 'afkir'])) {
if ($stok <= 0) {
if ($stok <= 0) {
throw \Illuminate\Validation\ValidationException::withMessages([ throw \Illuminate\Validation\ValidationException::withMessages([
'jumlah' => 'Stok ayam di kandang ini sudah habis.' 'jumlah' => 'Stok ayam di kandang ini sudah habis.'
]); ]);
@ -279,8 +292,8 @@ public function update(Request $request, $id)
// UPDATE DATA // UPDATE DATA
$mutasi->update([ $mutasi->update([
'kandang_id' => $request->kandang_id, 'kandang_id' => $request->kandang_id,
'jenis_mutasi' => $request->jenis_mutasi, 'jenis_mutasi_id' => $request->jenis_mutasi_id,
'jumlah' => $request->jumlah, 'jumlah' => $request->jumlah,
'tanggal' => $request->tanggal, 'tanggal' => $request->tanggal,
'keterangan' => $request->keterangan, 'keterangan' => $request->keterangan,
]); ]);
@ -300,8 +313,8 @@ public function destroy($id)
$mutasi = MutasiAyam::findOrFail($id); $mutasi = MutasiAyam::findOrFail($id);
if ( if (
$mutasi->jenis_mutasi === 'pindah' || $mutasi->jenisMutasi->nama === 'pindah'||
str_contains($mutasi->keterangan, 'Pindahan dari') str_contains($mutasi->keterangan, 'Pindahan dari')
) { ) {
abort(403, 'Data hasil pindahan tidak boleh dihapus.'); abort(403, 'Data hasil pindahan tidak boleh dihapus.');
} }

View File

@ -23,9 +23,17 @@ public function index()
/* STAT UMUM */ /* STAT UMUM */
$jumlahMasuk = MutasiAyam::where('jenis_mutasi', 'masuk')->sum('jumlah'); $jumlahAyam = MutasiAyam::join('jenis_mutasi', 'mutasi_ayam.jenis_mutasi_id', '=', 'jenis_mutasi.id')
$jumlahKeluar = MutasiAyam::whereIn('jenis_mutasi', ['keluar', 'mati', 'afkir', 'pindah'])->sum('jumlah'); ->selectRaw("
$jumlahAyam = $jumlahMasuk - $jumlahKeluar; COALESCE(SUM(
CASE
WHEN jenis_mutasi.tipe = 'tambah' THEN jumlah
WHEN jenis_mutasi.tipe IN ('kurang','transfer') THEN -jumlah
ELSE 0
END
),0) as total
")
->value('total');
$produksiHariIni = ProduksiTelur::whereDate('tanggal_produksi', $today) $produksiHariIni = ProduksiTelur::whereDate('tanggal_produksi', $today)
->sum(DB::raw('berat_telur_layak + berat_telur_rusak')); ->sum(DB::raw('berat_telur_layak + berat_telur_rusak'));

View File

@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Models\JenisMutasi;
use Illuminate\Http\Request;
class JenisMutasiController extends Controller
{
public function index()
{
$data = JenisMutasi::orderBy('id', 'desc')->get();
return view('jenis-mutasi', compact('data'));
}
public function store(Request $request)
{
$request->validate([
'nama' => 'required|string|max:255',
'tipe' => 'required|in:tambah,kurang,transfer',
]);
JenisMutasi::create([
'nama' => $request->nama,
'tipe' => $request->tipe,
]);
return redirect()->route('jenis-mutasi')
->with('success', 'Jenis mutasi berhasil ditambahkan.');
}
public function update(Request $request, $id)
{
$data = JenisMutasi::findOrFail($id);
$request->validate([
'nama' => 'required|string|max:255',
'tipe' => 'required|in:tambah,kurang,transfer',
]);
$data->update([
'nama' => $request->nama,
'tipe' => $request->tipe,
]);
return redirect()->route('jenis-mutasi')
->with('success', 'Jenis mutasi berhasil diperbarui.');
}
public function destroy($id)
{
$data = JenisMutasi::findOrFail($id);
$data->delete();
return redirect()->route('jenis-mutasi')
->with('success', 'Jenis mutasi berhasil dihapus.');
}
}

View File

@ -28,7 +28,7 @@ public function index(Request $request)
} }
$perPage = in_array($request->perPage, [10, 25, 50]) ? $request->perPage : 10; $perPage = in_array($request->perPage, [10, 25, 50]) ? $request->perPage : 10;
$data = $query $data = $query
->orderBy('created_at', 'desc') ->orderBy('created_at', 'desc')
->paginate($perPage) ->paginate($perPage)
@ -41,8 +41,8 @@ public function store(Request $request)
{ {
$request->validate( $request->validate(
[ [
'nama_karyawan' => 'required|string|min:3|max:100', 'nama_karyawan' => ['required', 'string', 'min:3', 'max:100', 'regex:/^[A-Za-z\s]+$/'],
'no_hp' => ['required', 'regex:/^08[0-9]{8,11}$/'], 'no_hp' => ['required', 'regex:/^08[0-9]{8,11}$/'],
'alamat' => 'required|string|max:255', 'alamat' => 'required|string|max:255',
'status_karyawan' => 'required|in:aktif,nonaktif', 'status_karyawan' => 'required|in:aktif,nonaktif',
], ],
@ -51,6 +51,7 @@ public function store(Request $request)
'nama_karyawan.string' => 'Nama karyawan harus berupa teks', 'nama_karyawan.string' => 'Nama karyawan harus berupa teks',
'nama_karyawan.min' => 'Nama karyawan minimal 3 karakter', 'nama_karyawan.min' => 'Nama karyawan minimal 3 karakter',
'nama_karyawan.max' => 'Nama karyawan maksimal 100 karakter', 'nama_karyawan.max' => 'Nama karyawan maksimal 100 karakter',
'nama_karyawan.regex' => 'Nama hanya boleh berisi huruf dan spasi',
'no_hp.required' => 'Nomor HP wajib diisi', 'no_hp.required' => 'Nomor HP wajib diisi',
'no_hp.regex' => 'Nomor HP harus 1013 digit, diawali 08, dan hanya angka.', 'no_hp.regex' => 'Nomor HP harus 1013 digit, diawali 08, dan hanya angka.',
@ -112,8 +113,8 @@ public function update(Request $request, $id)
{ {
$request->validate( $request->validate(
[ [
'nama_karyawan' => 'required|string|min:3|max:100', 'nama_karyawan' => ['required', 'string', 'min:3', 'max:100', 'regex:/^[A-Za-z\s]+$/'],
'no_hp' => ['required', 'regex:/^08[0-9]{8,11}$/'], 'no_hp' => ['required', 'regex:/^08[0-9]{8,11}$/'],
'alamat' => 'required|string|max:255', 'alamat' => 'required|string|max:255',
], ],
[ [
@ -121,6 +122,7 @@ public function update(Request $request, $id)
'nama_karyawan.string' => 'Nama karyawan harus berupa teks', 'nama_karyawan.string' => 'Nama karyawan harus berupa teks',
'nama_karyawan.min' => 'Nama karyawan minimal 3 karakter', 'nama_karyawan.min' => 'Nama karyawan minimal 3 karakter',
'nama_karyawan.max' => 'Nama karyawan maksimal 100 karakter', 'nama_karyawan.max' => 'Nama karyawan maksimal 100 karakter',
'nama_karyawan.regex' => 'Nama hanya boleh berisi huruf dan spasi',
'no_hp.required' => 'Nomor HP wajib diisi', 'no_hp.required' => 'Nomor HP wajib diisi',
'no_hp.regex' => 'Nomor HP harus 1013 digit, diawali 08, dan hanya angka.', 'no_hp.regex' => 'Nomor HP harus 1013 digit, diawali 08, dan hanya angka.',

View File

@ -79,8 +79,13 @@ public function index(Request $request)
$min = $batasMinimum[$item->nama_barang] ?? 5; $min = $batasMinimum[$item->nama_barang] ?? 5;
return $item->stok <= $min; return $item->stok <= $min;
}); });
$stokAman = $stok->filter(function ($item) use ($batasMinimum) {
$min = $batasMinimum[$item->nama_barang] ?? 5;
return $item->stok > $min;
});
return view('inventori-pakan', compact('data', 'stokMenipis')); // return view('inventori-pakan', compact('data', 'stokMenipis'));
return view('inventori-pakan', compact('data', 'stokMenipis', 'stokAman'));
} }
public function store(Request $request) public function store(Request $request)

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class JenisMutasi extends Model
{
protected $table = 'jenis_mutasi';
protected $fillable = [
'nama',
'tipe',
];
public function mutasiAyam()
{
return $this->hasMany(MutasiAyam::class, 'jenis_mutasi_id');
}
}

View File

@ -14,7 +14,7 @@ class MutasiAyam extends Model
protected $fillable = [ protected $fillable = [
'kandang_id', 'kandang_id',
'user_id', 'user_id',
'jenis_mutasi', 'jenis_mutasi_id', // ✅ ganti ini
'jumlah', 'jumlah',
'tanggal', 'tanggal',
'keterangan', 'keterangan',
@ -33,4 +33,10 @@ public function user()
{ {
return $this->belongsTo(User::class, 'user_id'); return $this->belongsTo(User::class, 'user_id');
} }
// ✅ RELASI BARU
public function jenisMutasi()
{
return $this->belongsTo(JenisMutasi::class, 'jenis_mutasi_id');
}
} }

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jenis_mutasi', function (Blueprint $table) {
$table->id();
$table->string('nama')->unique();
$table->enum('tipe', ['tambah', 'kurang', 'transfer']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jenis_mutasi');
}
};

View File

@ -0,0 +1,64 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Tambah kolom baru
Schema::table('mutasi_ayam', function (Blueprint $table) {
$table->unsignedBigInteger('jenis_mutasi_id')->nullable()->after('user_id');
});
// 2. Mapping data lama (enum → id)
$data = DB::table('mutasi_ayam')->get();
foreach ($data as $item) {
$jenis = DB::table('jenis_mutasi')
->where('nama', $item->jenis_mutasi)
->first();
if ($jenis) {
DB::table('mutasi_ayam')
->where('id', $item->id)
->update([
'jenis_mutasi_id' => $jenis->id
]);
}
}
// 3. Tambahkan foreign key
Schema::table('mutasi_ayam', function (Blueprint $table) {
$table->foreign('jenis_mutasi_id')
->references('id')
->on('jenis_mutasi')
->cascadeOnDelete();
});
// 4. (JANGAN DIHAPUS DULU)
// Nanti kalau semua sudah aman baru aktifkan ini
/*
Schema::table('mutasi_ayam', function (Blueprint $table) {
$table->dropColumn('jenis_mutasi');
});
*/
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('mutasi_ayam', function (Blueprint $table) {
$table->dropForeign(['jenis_mutasi_id']);
$table->dropColumn('jenis_mutasi_id');
});
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('mutasi_ayam', function (Blueprint $table) {
if (Schema::hasColumn('mutasi_ayam', 'jenis_mutasi')) {
$table->dropColumn('jenis_mutasi');
}
});
}
public function down(): void
{
Schema::table('mutasi_ayam', function (Blueprint $table) {
if (!Schema::hasColumn('mutasi_ayam', 'jenis_mutasi')) {
$table->enum('jenis_mutasi', ['masuk', 'keluar', 'mati', 'afkir', 'pindah'])
->after('jenis_mutasi_id')
->nullable();
}
});
}
};

View File

@ -11,6 +11,7 @@ public function run(): void
$this->call([ $this->call([
UserSeeder::class, UserSeeder::class,
KandangSeeder::class, KandangSeeder::class,
JenisMutasiSeeder::class,
KaryawanSeeder::class, KaryawanSeeder::class,
GajiKaryawanSeeder::class, GajiKaryawanSeeder::class,
InventoriKandangSeeder::class, InventoriKandangSeeder::class,

View File

@ -0,0 +1,39 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class JenisMutasiSeeder extends Seeder
{
public function run(): void
{
DB::table('jenis_mutasi')->insert([
[
'nama' => 'masuk',
'tipe' => 'tambah',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'mati',
'tipe' => 'kurang',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'afkir',
'tipe' => 'kurang',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'pindah',
'tipe' => 'transfer',
'created_at' => now(),
'updated_at' => now(),
],
]);
}
}

View File

@ -12,7 +12,7 @@ public function run(): void
MutasiAyam::create([ MutasiAyam::create([
'kandang_id' => 1, 'kandang_id' => 1,
'user_id' => 1, 'user_id' => 1,
'jenis_mutasi' => 'masuk', 'jenis_mutasi_id' => 1,
'jumlah' => 7000, 'jumlah' => 7000,
'tanggal' => now(), 'tanggal' => now(),
'keterangan' => 'DOC baru' 'keterangan' => 'DOC baru'

View File

@ -505,4 +505,21 @@ @media (max-width: 768px) {
letter-spacing: .5px; letter-spacing: .5px;
background: #f8f9fb; background: #f8f9fb;
} }
.info-card {
background: #fff;
border: 1px solid #e5e7eb;
border-left: 4px solid #ccc;
border-radius: 10px;
padding: 12px 14px;
height: 100%;
}
.info-card.warning {
border-left-color: #f59e0b;
/* soft orange */
}
.info-card.success {
border-left-color: #10b981;
/* soft green */
}

View File

@ -141,7 +141,7 @@ class="form-control form-control-sm">
<td>{{ number_format($item->jumlah, 0, ',', '.') }}</td> <td>{{ number_format($item->jumlah, 0, ',', '.') }}</td>
<td> <td>
{{ ucfirst($item->jenis_mutasi ?? '-') }} {{ $item->jenisMutasi->nama ?? '-' }}
</td> </td>
<td>{{ $item->keterangan ?? '-' }}</td> <td>{{ $item->keterangan ?? '-' }}</td>
@ -154,8 +154,8 @@ class="form-control form-control-sm">
@php @php
$isPindahan = $isPindahan =
$item->jenis_mutasi === 'pindah' || optional($item->jenisMutasi)->nama === 'pindah' ||
str_contains($item->keterangan, 'Pindahan dari'); str_contains($item->keterangan ?? '', 'Pindahan dari');
@endphp @endphp
{{-- TOMBOL EDIT --}} {{-- TOMBOL EDIT --}}
@ -165,7 +165,7 @@ class="btn btn-warning btn-sm {{ $isPindahan ? 'disabled opacity-50' : '' }}"
data-target="#modalEdit" @endif data-target="#modalEdit" @endif
data-id="{{ $item->id }}" data-kandang="{{ $item->kandang_id }}" data-id="{{ $item->id }}" data-kandang="{{ $item->kandang_id }}"
data-jumlah="{{ $item->jumlah }}" data-jumlah="{{ $item->jumlah }}"
data-jenis="{{ $item->jenis_mutasi }}" data-jenis="{{ $item->jenis_mutasi_id }}"
data-keterangan="{{ $item->keterangan }}" data-keterangan="{{ $item->keterangan }}"
data-tanggal="{{ $item->tanggal }}" data-tanggal="{{ $item->tanggal }}"
style="{{ $isPindahan ? 'pointer-events: none;' : '' }}" style="{{ $isPindahan ? 'pointer-events: none;' : '' }}"
@ -249,19 +249,17 @@ class="btn btn-danger btn-sm {{ $isPindahan ? 'disabled opacity-50' : '' }}"
{{-- ================= JENIS MUTASI ================= --}} {{-- ================= JENIS MUTASI ================= --}}
<div class="p-3 mb-3 border rounded bg-light"> <div class="p-3 mb-3 border rounded bg-light">
<label class="fw-bold">1. Pilih Jenis Mutasi</label> <label class="fw-bold">1. Pilih Jenis Mutasi</label>
<select name="jenis_mutasi" id="jenisMutasi" <select name="jenis_mutasi_id" id="jenisMutasi"
class="form-control mt-2 @error('jenis_mutasi') is-invalid @enderror"> class="form-control mt-2 @error('jenis_mutasi_id') is-invalid @enderror">
<option value="">-- Pilih Jenis Mutasi --</option> @foreach ($jenisMutasi as $jm)
<option value="masuk" {{ old('jenis_mutasi') == 'masuk' ? 'selected' : '' }}>Masuk (DOC) <option value="{{ $jm->id }}" data-nama="{{ $jm->nama }}"
</option> {{ old('jenis_mutasi_id') == $jm->id ? 'selected' : '' }}>
<option value="mati" {{ old('jenis_mutasi') == 'mati' ? 'selected' : '' }}>Mati</option> {{ ucfirst($jm->nama) }}
<option value="afkir" {{ old('jenis_mutasi') == 'afkir' ? 'selected' : '' }}>Afkir </option>
</option> @endforeach
<option value="pindah" {{ old('jenis_mutasi') == 'pindah' ? 'selected' : '' }}>Pindah
Kandang</option>
</select> </select>
@error('jenis_mutasi') @error('jenis_mutasi_id')
<small class="text-danger">{{ $message }}</small> <small class="text-danger">{{ $message }}</small>
@enderror @enderror
</div> </div>
@ -425,16 +423,16 @@ class="form-control @error('tanggal') is-invalid @enderror"
<div class="form-group"> <div class="form-group">
<label>Jenis Mutasi</label> <label>Jenis Mutasi</label>
<select name="jenis_mutasi" class="form-control @error('jenis_mutasi') is-invalid @enderror"> <select name="jenis_mutasi_id" class="form-control">
@foreach ($jenisMutasi as $jm)
<option value="masuk" {{ old('jenis_mutasi') == 'masuk' ? 'selected' : '' }}>Masuk <option value="{{ $jm->id }}"
</option> {{ old('jenis_mutasi_id') == $jm->id ? 'selected' : '' }}>
<option value="mati" {{ old('jenis_mutasi') == 'mati' ? 'selected' : '' }}>Mati</option> {{ ucfirst($jm->nama) }}
<option value="afkir" {{ old('jenis_mutasi') == 'afkir' ? 'selected' : '' }}>Afkir </option>
</option> @endforeach
</select> </select>
@error('jenis_mutasi') @error('jenis_mutasi_id')
<small class="text-danger">{{ $message }}</small> <small class="text-danger">{{ $message }}</small>
@enderror @enderror
</div> </div>
@ -506,6 +504,8 @@ class="form-control @error('tanggal') is-invalid @enderror"
<script> <script>
const baseUrl = "{{ route('ayam') }}"; const baseUrl = "{{ route('ayam') }}";
$(document).ready(function() { $(document).ready(function() {
$('#jenisMutasi').trigger('change');
/* ============================= /* =============================
HANDLE MODAL EDIT NORMAL HANDLE MODAL EDIT NORMAL
============================= */ ============================= */
@ -523,7 +523,7 @@ class="form-control @error('tanggal') is-invalid @enderror"
$('#formEdit [name=kandang_id]').val(btn.data('kandang')); $('#formEdit [name=kandang_id]').val(btn.data('kandang'));
$('#formEdit [name=jumlah]').val(btn.data('jumlah')); $('#formEdit [name=jumlah]').val(btn.data('jumlah'));
$('#formEdit [name=jenis_mutasi]').val(btn.data('jenis')); $('#formEdit [name=jenis_mutasi_id]').val(btn.data('jenis'));
$('#formEdit [name=tanggal]').val(btn.data('tanggal')); $('#formEdit [name=tanggal]').val(btn.data('tanggal'));
$('#formEdit [name=keterangan]').val(btn.data('keterangan')); $('#formEdit [name=keterangan]').val(btn.data('keterangan'));
}); });
@ -537,7 +537,7 @@ class="form-control @error('tanggal') is-invalid @enderror"
$('#formEdit [name=kandang_id]').val("{{ old('kandang_id') }}"); $('#formEdit [name=kandang_id]').val("{{ old('kandang_id') }}");
$('#formEdit [name=jumlah]').val("{{ old('jumlah') }}"); $('#formEdit [name=jumlah]').val("{{ old('jumlah') }}");
$('#formEdit [name=jenis_mutasi]').val("{{ old('jenis_mutasi') }}"); $('#formEdit [name=jenis_mutasi_id]').val("{{ old('jenis_mutasi_id') }}");
$('#formEdit [name=tanggal]').val("{{ old('tanggal') }}"); $('#formEdit [name=tanggal]').val("{{ old('tanggal') }}");
$('#formEdit [name=keterangan]').val(@json(old('keterangan'))); $('#formEdit [name=keterangan]').val(@json(old('keterangan')));
@ -577,19 +577,19 @@ class="form-control @error('tanggal') is-invalid @enderror"
/* ============================= /* =============================
TOGGLE PINDAH TOGGLE PINDAH
============================== */ ============================== */
if ($('#jenisMutasi').length) { $('#jenisMutasi').on('change', function() {
$('#jenisMutasi').on('change', function() {
if ($(this).val() === 'pindah') { let nama = $(this).find(':selected').data('nama');
$('#groupKandang').slideUp(200);
$('#groupPindah').slideDown(200);
} else {
$('#groupPindah').slideUp(200);
$('#groupKandang').slideDown(200);
}
}); if (nama === 'pindah') {
} $('#groupKandang').slideUp(200);
$('#groupPindah').slideDown(200);
} else {
$('#groupPindah').slideUp(200);
$('#groupKandang').slideDown(200);
}
});
}); });
</script> </script>

View File

@ -8,6 +8,47 @@
<div class="mb-4"> <div class="mb-4">
<h4 class="mb-1 fw-bold">Manajemen Pakan</h4> <h4 class="mb-1 fw-bold">Manajemen Pakan</h4>
</div> </div>
@if (auth()->user()->isAdmin() &&
((isset($stokMenipis) && $stokMenipis->count()) || (isset($stokAman) && $stokAman->count())))
<div class="mb-3 row">
{{-- STOK MENIPIS --}}
@if (isset($stokMenipis) && $stokMenipis->count())
<div class="col-md-6">
<div class="info-card warning">
<div class="d-flex align-items-start">
<div class="me-2"></div>
<div>
<div class="fw-semibold">Stok Menipis</div>
<small class="text-muted">
{{ $stokMenipis->map(fn($s) => $s->nama_barang . ' (' . number_format($s->stok, 0, ',', '.') . ')')->join(', ') }}
</small>
</div>
</div>
</div>
</div>
@endif
{{-- STOK AMAN --}}
@if (isset($stokAman) && $stokAman->count())
<div class="col-md-6">
<div class="info-card success">
<div class="d-flex align-items-start">
<div class="me-2"></div>
<div>
<div class="fw-semibold">Stok Aman</div>
<small class="text-muted">
{{ $stokAman->map(fn($s) => $s->nama_barang . ' (' . number_format($s->stok, 0, ',', '.') . ')')->join(', ') }}
</small>
</div>
</div>
</div>
</div>
@endif
</div>
@endif
<div class="shadow-sm card table-card operasional-page"> <div class="shadow-sm card table-card operasional-page">
<!-- FILTER BAR --> <!-- FILTER BAR -->
<form method="GET" id="formFilter"> <form method="GET" id="formFilter">
@ -88,9 +129,10 @@ class="form-control form-control-sm">
</div> </div>
</form> </form>
@if (auth()->user()->isAdmin() && isset($stokMenipis) && $stokMenipis->count())
<div class="alert alert-warning"> {{-- @if (auth()->user()->isAdmin() && isset($stokMenipis) && $stokMenipis->count())
<strong> Stok Menipis</strong> <div class="alert-custom warning">
<strong> Stok Menipis</strong>
<ul class="mb-0"> <ul class="mb-0">
@foreach ($stokMenipis as $s) @foreach ($stokMenipis as $s)
<li> <li>
@ -101,7 +143,13 @@ class="form-control form-control-sm">
</ul> </ul>
</div> </div>
@endif @endif
@if (auth()->user()->isAdmin() && isset($stokAman) && $stokAman->count())
<div class="alert-custom success">
<strong> Stok Aman:</strong>
{{ $stokAman->map(fn($s) => $s->nama_barang . ' (' . number_format($s->stok, 0, ',', '.') . ')')->join(', ') }}
karung
</div>
@endif --}}
@if ($errors->has('delete')) @if ($errors->has('delete'))
<div class="alert alert-danger"> <div class="alert alert-danger">
@ -662,7 +710,7 @@ function hitungEdit() {
let harga = parseInt(btn.data('harga')) || 0; let harga = parseInt(btn.data('harga')) || 0;
$('#hargaEdit').val(harga ? formatRupiah(harga.toString()) : ''); $('#hargaEdit').val(harga ? formatRupiah(harga.toString()) : '');
// tanggal // tanggal
$('#tanggalEdit').val(btn.data('tanggal')); $('#tanggalEdit').val(btn.data('tanggal'));

View File

@ -0,0 +1,251 @@
@extends('template')
@section('title', 'Jenis Mutasi')
@section('content')
<div class="main-panel">
<div class="content">
<div class="container-fluid">
<h4 class="mb-3">Kelola Jenis Mutasi</h4>
<!-- TAMBAH -->
{{-- <button class="mb-3 btn btn-primary btn-sm" data-toggle="modal" data-target="#modalTambah">
+ Tambah Jenis
</button> --}}
<!-- TABLE -->
<div class="shadow-sm card table-card">
<div class="card-body border-bottom">
<div class="d-flex justify-content-between align-items-center">
{{-- <h5 class="mb-0 fw-bold">
Jenis Mutasi
</h5> --}}
<button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#modalTambah">
<i class="la la-plus"></i> Tambah
</button>
</div>
</div>
<div class="p-0 card-body">
<div class="table-responsive">
<table class="table mb-0 table-standard">
<thead>
<tr>
<th width="60" class="text-center">No</th>
<th>Nama</th>
<th>Tipe</th>
<th width="120" class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($data as $i => $item)
<tr>
<td class="text-center">{{ $i + 1 }}</td>
<td>{{ $item->nama }}</td>
<td>
<span class="badge badge-secondary">
{{ ucfirst($item->tipe) }}
</span>
</td>
<td class="text-center">
{{-- EDIT --}}
<button class="btn btn-warning btn-sm"
data-toggle="modal"
data-target="#modalEdit"
data-id="{{ $item->id }}"
data-nama="{{ $item->nama }}"
data-tipe="{{ $item->tipe }}"
title="Edit">
<i class="la la-edit"></i>
</button>
{{-- DELETE --}}
<button class="btn btn-danger btn-sm"
data-toggle="modal"
data-target="#modalHapus"
data-id="{{ $item->id }}"
title="Hapus">
<i class="la la-trash"></i>
</button>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="py-4 text-center text-muted">
Belum ada data jenis mutasi
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- MODAL TAMBAH --}}
<div class="modal fade" id="modalTambah" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<form method="POST" action="{{ route('jenis-mutasi.store') }}">
@csrf
<div class="modal-header modal-header-clean">
<h6 class="modal-title">
<i class="mr-1 la la-plus-circle text-muted"></i>
Tambah Jenis Mutasi
</h6>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Nama Jenis</label>
<input type="text" name="nama" class="form-control" placeholder="Contoh: Panen / Mati">
</div>
<div class="form-group">
<label>Tipe</label>
<select name="tipe" class="form-control">
<option value="tambah">Tambah</option>
<option value="kurang">Kurang</option>
<option value="transfer">Transfer</option>
</select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button class="btn btn-primary">
<i class="la la-save"></i> Simpan
</button>
</div>
</form>
</div>
</div>
</div>
{{-- MODAL EDIT --}}
<div class="modal fade" id="modalEdit" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<form method="POST" id="formEdit">
@csrf
@method('PUT')
<div class="modal-header modal-header-clean">
<h6 class="modal-title">
<i class="la la-edit"></i> Edit Jenis Mutasi
</h6>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Nama Jenis</label>
<input type="text" name="nama" id="editNama" class="form-control">
</div>
<div class="form-group">
<label>Tipe</label>
<select name="tipe" id="editTipe" class="form-control">
<option value="tambah">Tambah</option>
<option value="kurang">Kurang</option>
<option value="transfer">Transfer</option>
</select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button class="btn btn-primary">
<i class="la la-save"></i> Update
</button>
</div>
</form>
</div>
</div>
</div>
{{-- MODAL HAPUS --}}
<div class="modal fade" id="modalHapus" tabindex="-1">
<div class="modal-dialog modal-sm modal-dialog-centered">
<div class="modal-content">
<form method="POST" id="formHapus">
@csrf
@method('DELETE')
<div class="modal-header modal-header-clean">
<h6 class="modal-title">
<i class="la la-trash"></i> Hapus Data
</h6>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="text-center modal-body">
<p class="mb-0">Yakin ingin menghapus data ini?</p>
</div>
<div class="modal-footer justify-content-center">
<button class="btn btn-secondary btn-sm" data-dismiss="modal">Batal</button>
<button class="btn btn-danger btn-sm">
<i class="la la-trash"></i> Hapus
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
$('#modalEdit').on('show.bs.modal', function(e){
let btn = $(e.relatedTarget);
$('#formEdit').attr('action', '/jenis-mutasi/' + btn.data('id'));
$('#editNama').val(btn.data('nama'));
$('#editTipe').val(btn.data('tipe'));
});
$('#modalHapus').on('show.bs.modal', function(e){
let btn = $(e.relatedTarget);
$('#formHapus').attr('action', '/jenis-mutasi/' + btn.data('id'));
});
</script>
@endsection

View File

@ -181,16 +181,18 @@ class="d-inline">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label>Nama Lengkap</label> <label>Nama Lengkap</label>
<input type="text" name="nama_karyawan" value="{{ old('nama_karyawan') }}" <input type="text" name="nama_karyawan"
class="form-control @error('nama_karyawan') is-invalid @enderror"> value="{{ old('nama_karyawan') }}"
oninput="this.value = this.value.replace(/[^A-Za-z\s]/g,'')"
class="form-control @error('nama_karyawan') is-invalid @enderror">
@error('nama_karyawan') @error('nama_karyawan')
<small class="text-danger">{{ $message }}</small> <small class="text-danger">{{ $message }}</small>
@enderror @enderror
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">

View File

@ -134,6 +134,13 @@ class="d-none d-sm-inline font-weight-bold text-dark">{{ auth()->user()->name }}
<span class="sub-item">Populasi Ayam</span> <span class="sub-item">Populasi Ayam</span>
</a> </a>
</li> </li>
@if (auth()->user()->role === 'admin')
<li class="{{ Route::is('jenis-mutasi') ? 'active' : '' }}">
<a href="{{ route('jenis-mutasi') }}">
<span class="sub-item">Kelola Jenis Mutasi Ayam</span>
</a>
</li>
@endif
</ul> </ul>
</div> </div>
</li> </li>

View File

@ -12,7 +12,9 @@
KeuanganController, KeuanganController,
LaporanController, LaporanController,
ProfilController, ProfilController,
LaporanExportController LaporanExportController,
JenisMutasiController
}; };
use App\Http\Middleware\CheckRole; use App\Http\Middleware\CheckRole;
@ -97,6 +99,14 @@
Route::get('/laporan', [LaporanController::class, 'index'])->name('laporan'); Route::get('/laporan', [LaporanController::class, 'index'])->name('laporan');
Route::get('/laporan/export', [LaporanController::class, 'export'])->name('laporan.export'); Route::get('/laporan/export', [LaporanController::class, 'export'])->name('laporan.export');
// JENIS MUTASI (MASTER DATA)
Route::prefix('jenis-mutasi')->group(function () {
Route::get('/', [JenisMutasiController::class, 'index'])->name('jenis-mutasi');
Route::post('/', [JenisMutasiController::class, 'store'])->name('jenis-mutasi.store');
Route::put('/{id}', [JenisMutasiController::class, 'update'])->name('jenis-mutasi.update');
Route::delete('/{id}', [JenisMutasiController::class, 'destroy'])->name('jenis-mutasi.delete');
});
}); });
}); });
Route::fallback(function () { Route::fallback(function () {