diff --git a/app/Http/Controllers/Admin/Account/AppearanceController.php b/app/Http/Controllers/Admin/Account/AppearanceController.php index 4af6862..0be19ea 100644 --- a/app/Http/Controllers/Admin/Account/AppearanceController.php +++ b/app/Http/Controllers/Admin/Account/AppearanceController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Account; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Account\UpdateAppearanceRequest; use Illuminate\Http\RedirectResponse; @@ -11,6 +12,8 @@ class AppearanceController extends Controller { + use FlashesEntityMessage; + public function edit(): Response { return Inertia::render('admin/account/Appearance', [ @@ -22,7 +25,7 @@ public function update(UpdateAppearanceRequest $request): RedirectResponse { $appearance = $request->string('appearance')->toString(); - Inertia::flash('success', 'Tampilan berhasil diperbarui.'); + $this->flashUpdated('Tampilan'); return redirect() ->route('admin.account.appearance') diff --git a/app/Http/Controllers/Admin/Account/PasswordController.php b/app/Http/Controllers/Admin/Account/PasswordController.php index 3cabce1..c525598 100644 --- a/app/Http/Controllers/Admin/Account/PasswordController.php +++ b/app/Http/Controllers/Admin/Account/PasswordController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Account; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Account\UpdatePasswordRequest; use Illuminate\Http\RedirectResponse; @@ -11,6 +12,8 @@ class PasswordController extends Controller { + use FlashesEntityMessage; + public function edit(): Response { return Inertia::render('admin/account/Password'); @@ -19,10 +22,10 @@ public function edit(): Response public function update(UpdatePasswordRequest $request): RedirectResponse { $user = $request->user(); - $user->password = Hash::make($request->string('password')->toString()); + $user->password = Hash::make($request->validated('password')); $user->save(); - Inertia::flash('success', 'Kata sandi berhasil diperbarui.'); + $this->flashUpdated('Kata sandi'); return redirect()->route('admin.account.password'); } diff --git a/app/Http/Controllers/Admin/Account/ProfileController.php b/app/Http/Controllers/Admin/Account/ProfileController.php index 554f1c0..9bec07d 100644 --- a/app/Http/Controllers/Admin/Account/ProfileController.php +++ b/app/Http/Controllers/Admin/Account/ProfileController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Account; use App\Enums\Gender; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Account\UpdateProfileRequest; use App\Services\Account\ProfileService; @@ -12,6 +13,8 @@ class ProfileController extends Controller { + use FlashesEntityMessage; + public function __construct( private readonly ProfileService $profileService, ) {} @@ -28,9 +31,9 @@ public function edit(): Response public function update(UpdateProfileRequest $request): RedirectResponse { - $this->profileService->update($request->validated()); + $this->profileService->update($request->validated(), $request->user()); - Inertia::flash('success', 'Profil berhasil diperbarui.'); + $this->flashUpdated('Profil'); return redirect()->route('admin.account.profile'); } diff --git a/app/Http/Controllers/Admin/Finance/CashController.php b/app/Http/Controllers/Admin/Finance/CashController.php index 0cd52e3..b8b064b 100644 --- a/app/Http/Controllers/Admin/Finance/CashController.php +++ b/app/Http/Controllers/Admin/Finance/CashController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Finance; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Finance\DepositCashRequest; @@ -15,6 +16,7 @@ class CashController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -39,7 +41,7 @@ public function deposit(DepositCashRequest $request): RedirectResponse $this->cashService->deposit($cashAccount, $request->validated(), $request->user()); - Inertia::flash('success', 'Setor kas berhasil dicatat.'); + $this->flashSuccess('Setor kas berhasil dicatat.'); return redirect()->route('admin.finance.cash.index'); } @@ -48,7 +50,7 @@ public function update(UpdateCashTransactionRequest $request, CashTransaction $c { $this->cashService->updateDeposit($cashTransaction, $request->validated()); - Inertia::flash('success', 'Setor kas berhasil diperbarui.'); + $this->flashSuccess('Setor kas berhasil diperbarui.'); return redirect()->route('admin.finance.cash.index'); } @@ -57,7 +59,7 @@ public function destroy(CashTransaction $cashTransaction): RedirectResponse { $this->cashService->deleteTransaction($cashTransaction); - Inertia::flash('success', 'Setor kas berhasil dihapus.'); + $this->flashSuccess('Setor kas berhasil dihapus.'); return redirect()->route('admin.finance.cash.index'); } diff --git a/app/Http/Controllers/Admin/Finance/EmployeeAdvanceController.php b/app/Http/Controllers/Admin/Finance/EmployeeAdvanceController.php index 88cffe6..b104e8c 100644 --- a/app/Http/Controllers/Admin/Finance/EmployeeAdvanceController.php +++ b/app/Http/Controllers/Admin/Finance/EmployeeAdvanceController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Finance; use App\Enums\Permission; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Finance\EmployeeAdvanceRequest; @@ -16,6 +17,7 @@ class EmployeeAdvanceController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -25,7 +27,7 @@ public function __construct( public function index(Request $request): Response { $tableQuery = $this->parseDataTableQuery($request); - $user = auth()->user(); + $user = $request->user(); return Inertia::render('admin/finance/employee-advances/Index', [ 'employeeAdvances' => $this->employeeAdvanceService->paginateForIndex($tableQuery), @@ -40,18 +42,18 @@ public function index(Request $request): Response public function store(EmployeeAdvanceRequest $request): RedirectResponse { - $this->employeeAdvanceService->create($request->validated()); + $this->employeeAdvanceService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Kasbon berhasil diajukan.'); + $this->flashSuccess('Kasbon berhasil diajukan.'); return redirect()->route('admin.finance.employee-advances.index'); } public function update(EmployeeAdvanceRequest $request, EmployeeAdvance $employeeAdvance): RedirectResponse { - $this->employeeAdvanceService->update($employeeAdvance, $request->validated()); + $this->employeeAdvanceService->update($employeeAdvance, $request->validated(), $request->user()); - Inertia::flash('success', 'Kasbon berhasil diperbarui.'); + $this->flashUpdated('Kasbon'); return redirect()->route('admin.finance.employee-advances.index'); } @@ -60,18 +62,18 @@ public function destroy(EmployeeAdvance $employeeAdvance): RedirectResponse { abort_unless(! auth()->user()?->can(Permission::EMPLOYEE_ADVANCES_VERIFY->value), 403); - $this->employeeAdvanceService->delete($employeeAdvance); + $this->employeeAdvanceService->delete($employeeAdvance, auth()->user()); - Inertia::flash('success', 'Kasbon berhasil dihapus.'); + $this->flashDeleted('Kasbon'); return redirect()->route('admin.finance.employee-advances.index'); } public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse { - $this->employeeAdvanceService->approve($employeeAdvance); + $this->employeeAdvanceService->approve($employeeAdvance, auth()->user()); - Inertia::flash('success', 'Kasbon disetujui dan dicairkan dari kas.'); + $this->flashSuccess('Kasbon berhasil disetujui dan dicairkan dari kas.'); return redirect()->route('admin.finance.employee-advances.index'); } @@ -81,18 +83,19 @@ public function reject(RejectEmployeeAdvanceRequest $request, EmployeeAdvance $e $this->employeeAdvanceService->reject( $employeeAdvance, $request->validated('reason'), + auth()->user(), ); - Inertia::flash('success', 'Kasbon berhasil ditolak.'); + $this->flashSuccess('Kasbon berhasil ditolak.'); return redirect()->route('admin.finance.employee-advances.index'); } public function pay(EmployeeAdvance $employeeAdvance): RedirectResponse { - $this->employeeAdvanceService->pay($employeeAdvance); + $this->employeeAdvanceService->pay($employeeAdvance, auth()->user()); - Inertia::flash('success', 'Kasbon berhasil dilunasi.'); + $this->flashSuccess('Kasbon berhasil dilunasi.'); return redirect()->route('admin.finance.employee-advances.index'); } diff --git a/app/Http/Controllers/Admin/Finance/ExpenseController.php b/app/Http/Controllers/Admin/Finance/ExpenseController.php index 2cdb774..c29103f 100644 --- a/app/Http/Controllers/Admin/Finance/ExpenseController.php +++ b/app/Http/Controllers/Admin/Finance/ExpenseController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Finance; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Finance\ExpenseRequest; @@ -14,6 +15,7 @@ class ExpenseController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -34,7 +36,7 @@ public function store(ExpenseRequest $request): RedirectResponse { $this->expenseService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Pengeluaran berhasil ditambahkan.'); + $this->flashCreated('Pengeluaran'); return redirect()->route('admin.finance.expenses.index'); } @@ -43,7 +45,7 @@ public function update(ExpenseRequest $request, Expense $expense): RedirectRespo { $this->expenseService->update($expense, $request->validated()); - Inertia::flash('success', 'Pengeluaran berhasil diperbarui.'); + $this->flashUpdated('Pengeluaran'); return redirect()->route('admin.finance.expenses.index'); } @@ -52,7 +54,7 @@ public function destroy(Expense $expense): RedirectResponse { $this->expenseService->delete($expense); - Inertia::flash('success', 'Pengeluaran berhasil dihapus.'); + $this->flashDeleted('Pengeluaran'); return redirect()->route('admin.finance.expenses.index'); } diff --git a/app/Http/Controllers/Admin/Finance/PayrollController.php b/app/Http/Controllers/Admin/Finance/PayrollController.php index de6f32e..1aa93a9 100644 --- a/app/Http/Controllers/Admin/Finance/PayrollController.php +++ b/app/Http/Controllers/Admin/Finance/PayrollController.php @@ -2,15 +2,17 @@ namespace App\Http\Controllers\Admin\Finance; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Finance\PayrollAdjustmentRequest; use App\Models\Payroll; use App\Services\Finance\PayrollService; use Illuminate\Http\RedirectResponse; -use Inertia\Inertia; class PayrollController extends Controller { + use FlashesEntityMessage; + public function __construct( private readonly PayrollService $payrollService, ) {} @@ -19,7 +21,7 @@ public function pay(Payroll $payroll): RedirectResponse { $this->payrollService->pay($payroll, auth()->user()); - Inertia::flash('success', 'Gaji berhasil dibayar.'); + $this->flashSuccess('Gaji berhasil dibayar.'); return redirect()->route('admin.finance.payroll.index', [ 'period_id' => $payroll->payroll_period_id, @@ -34,7 +36,7 @@ public function storeAdjustment(PayrollAdjustmentRequest $request, Payroll $payr auth()->user(), ); - Inertia::flash('success', 'Penyesuaian gaji berhasil ditambahkan.'); + $this->flashSuccess('Penyesuaian gaji berhasil ditambahkan.'); return redirect()->route('admin.finance.payroll.index', [ 'period_id' => $payroll->payroll_period_id, diff --git a/app/Http/Controllers/Admin/Finance/PayrollPeriodController.php b/app/Http/Controllers/Admin/Finance/PayrollPeriodController.php index 9615961..c2b22ae 100644 --- a/app/Http/Controllers/Admin/Finance/PayrollPeriodController.php +++ b/app/Http/Controllers/Admin/Finance/PayrollPeriodController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Finance; use App\Enums\PayrollAdjustmentType; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Models\PayrollPeriod; @@ -14,6 +15,7 @@ class PayrollPeriodController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -47,7 +49,7 @@ public function close(PayrollPeriod $payrollPeriod): RedirectResponse { $this->payrollService->closePeriod($payrollPeriod, auth()->user()); - Inertia::flash('success', 'Periode gaji berhasil ditutup.'); + $this->flashSuccess('Periode gaji berhasil ditutup.'); return redirect()->route('admin.finance.payroll.index', [ 'period_id' => $payrollPeriod->id, diff --git a/app/Http/Controllers/Admin/Hr/AttendanceController.php b/app/Http/Controllers/Admin/Hr/AttendanceController.php index 58889be..a61a965 100644 --- a/app/Http/Controllers/Admin/Hr/AttendanceController.php +++ b/app/Http/Controllers/Admin/Hr/AttendanceController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Hr; use App\Enums\Permission; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Hr\AttendanceCheckInRequest; use App\Http\Requests\Admin\Hr\AttendanceCheckOutRequest; @@ -15,13 +16,15 @@ class AttendanceController extends Controller { + use FlashesEntityMessage; + public function __construct( private readonly AttendanceService $attendanceService, ) {} public function index(Request $request): Response { - $user = auth()->user(); + $user = $request->user(); $employee = $user?->employee; $canManageAll = $user?->can(Permission::ATTENDANCES_MANAGE->value) ?? false; @@ -53,18 +56,18 @@ public function index(Request $request): Response public function checkIn(AttendanceCheckInRequest $request): RedirectResponse { - $this->attendanceService->checkIn($request->validated()); + $this->attendanceService->checkIn($request->validated(), $request->user()); - Inertia::flash('success', 'Presensi masuk berhasil dicatat.'); + $this->flashSuccess('Presensi masuk berhasil dicatat.'); return redirect()->route('admin.hr.attendances.index'); } public function checkOut(AttendanceCheckOutRequest $request): RedirectResponse { - $this->attendanceService->checkOut($request->validated()); + $this->attendanceService->checkOut($request->validated(), $request->user()); - Inertia::flash('success', 'Presensi pulang berhasil dicatat.'); + $this->flashSuccess('Presensi pulang berhasil dicatat.'); return redirect()->route('admin.hr.attendances.index'); } @@ -73,7 +76,7 @@ public function destroy(Attendance $attendance): RedirectResponse { $this->attendanceService->delete($attendance); - Inertia::flash('success', 'Data presensi berhasil dihapus.'); + $this->flashDeleted('Data presensi'); return redirect()->route('admin.hr.attendances.index'); } diff --git a/app/Http/Controllers/Admin/Hr/EmployeeController.php b/app/Http/Controllers/Admin/Hr/EmployeeController.php index 8072ab9..1771b10 100644 --- a/app/Http/Controllers/Admin/Hr/EmployeeController.php +++ b/app/Http/Controllers/Admin/Hr/EmployeeController.php @@ -5,6 +5,7 @@ use App\Enums\EmploymentStatus; use App\Enums\Gender; use App\Enums\Role; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Hr\EmployeeRequest; @@ -17,6 +18,7 @@ class EmployeeController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -50,7 +52,7 @@ public function store(EmployeeRequest $request): RedirectResponse { $this->employeeService->create($request->validated()); - Inertia::flash('success', 'Pegawai berhasil ditambahkan.'); + $this->flashCreated('Pegawai'); return redirect()->route('admin.hr.employees.index'); } @@ -71,7 +73,7 @@ public function update(EmployeeRequest $request, User $user): RedirectResponse { $this->employeeService->update($user, $request->validated()); - Inertia::flash('success', 'Data pegawai berhasil diperbarui.'); + $this->flashSuccess('Data pegawai berhasil diperbarui.'); return redirect()->route('admin.hr.employees.index'); } @@ -84,7 +86,7 @@ public function toggleStatus(Request $request, User $user): RedirectResponse $this->employeeService->toggleStatus($user, $validated['is_active']); - Inertia::flash('success', 'Status pegawai berhasil diperbarui.'); + $this->flashStatusUpdated('pegawai'); return back(); } @@ -93,7 +95,7 @@ public function resetPassword(User $user): RedirectResponse { $this->employeeService->resetPassword($user); - Inertia::flash('success', 'Kata sandi berhasil direset. Pengguna telah logout dari semua sesi.'); + $this->flashSuccess('Kata sandi berhasil direset. Pengguna telah logout dari semua sesi.'); return back(); } @@ -102,7 +104,7 @@ public function destroy(User $user): RedirectResponse { $this->employeeService->delete($user); - Inertia::flash('success', 'Pegawai berhasil dihapus.'); + $this->flashDeleted('Pegawai'); return redirect()->route('admin.hr.employees.index'); } diff --git a/app/Http/Controllers/Admin/Hr/LeaveRequestController.php b/app/Http/Controllers/Admin/Hr/LeaveRequestController.php index 004f2b7..5ebd1a7 100644 --- a/app/Http/Controllers/Admin/Hr/LeaveRequestController.php +++ b/app/Http/Controllers/Admin/Hr/LeaveRequestController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Hr; use App\Enums\Permission; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Hr\RejectLeaveRequestRequest; @@ -16,6 +17,7 @@ class LeaveRequestController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -25,7 +27,7 @@ public function __construct( public function index(Request $request): Response { $tableQuery = $this->parseDataTableQuery($request); - $user = auth()->user(); + $user = $request->user(); return Inertia::render('admin/hr/leave-requests/Index', [ 'leaveRequests' => $this->leaveRequestService->paginateForIndex($tableQuery), @@ -39,18 +41,18 @@ public function index(Request $request): Response public function store(SubmitLeaveRequest $request): RedirectResponse { - $this->leaveRequestService->create($request->validated()); + $this->leaveRequestService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Pengajuan cuti berhasil diajukan.'); + $this->flashSuccess('Pengajuan cuti berhasil diajukan.'); return redirect()->route('admin.hr.leave-requests.index'); } public function update(SubmitLeaveRequest $request, LeaveRequest $leaveRequest): RedirectResponse { - $this->leaveRequestService->update($leaveRequest, $request->validated()); + $this->leaveRequestService->update($leaveRequest, $request->validated(), $request->user()); - Inertia::flash('success', 'Pengajuan cuti berhasil diperbarui.'); + $this->flashUpdated('Pengajuan cuti'); return redirect()->route('admin.hr.leave-requests.index'); } @@ -59,18 +61,18 @@ public function destroy(LeaveRequest $leaveRequest): RedirectResponse { abort_unless(! auth()->user()?->can(Permission::LEAVE_REQUESTS_VERIFY->value), 403); - $this->leaveRequestService->delete($leaveRequest); + $this->leaveRequestService->delete($leaveRequest, auth()->user()); - Inertia::flash('success', 'Pengajuan cuti berhasil dihapus.'); + $this->flashDeleted('Pengajuan cuti'); return redirect()->route('admin.hr.leave-requests.index'); } public function approve(LeaveRequest $leaveRequest): RedirectResponse { - $this->leaveRequestService->approve($leaveRequest); + $this->leaveRequestService->approve($leaveRequest, auth()->user()); - Inertia::flash('success', 'Pengajuan cuti disetujui.'); + $this->flashSuccess('Pengajuan cuti berhasil disetujui.'); return redirect()->route('admin.hr.leave-requests.index'); } @@ -80,9 +82,10 @@ public function reject(RejectLeaveRequestRequest $request, LeaveRequest $leaveRe $this->leaveRequestService->reject( $leaveRequest, $request->validated('reason'), + auth()->user(), ); - Inertia::flash('success', 'Pengajuan cuti berhasil ditolak.'); + $this->flashSuccess('Pengajuan cuti berhasil ditolak.'); return redirect()->route('admin.hr.leave-requests.index'); } diff --git a/app/Http/Controllers/Admin/Manage/CuttingController.php b/app/Http/Controllers/Admin/Manage/CuttingController.php index 26162f1..887cef6 100644 --- a/app/Http/Controllers/Admin/Manage/CuttingController.php +++ b/app/Http/Controllers/Admin/Manage/CuttingController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Manage; use App\Enums\CuttingStatus; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Manage\CuttingRequest; @@ -16,6 +17,7 @@ class CuttingController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -44,7 +46,7 @@ public function store(CuttingRequest $request): RedirectResponse { $this->cuttingService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Cutting berhasil ditambahkan.'); + $this->flashCreated('Proses potong'); return redirect()->route('admin.manage.cuttings.index'); } @@ -52,7 +54,7 @@ public function store(CuttingRequest $request): RedirectResponse public function edit(Cutting $cutting): Response|RedirectResponse { if (! $cutting->status->isEditable()) { - Inertia::flash('error', 'Cutting tidak dapat diubah.'); + $this->flashError('Proses potong tidak dapat diubah.'); return redirect()->route('admin.manage.cuttings.index'); } @@ -68,7 +70,7 @@ public function update(CuttingRequest $request, Cutting $cutting): RedirectRespo { $this->cuttingService->update($cutting, $request->validated()); - Inertia::flash('success', 'Cutting berhasil diperbarui.'); + $this->flashUpdated('Proses potong'); return redirect()->route('admin.manage.cuttings.index'); } @@ -77,7 +79,7 @@ public function destroy(Cutting $cutting): RedirectResponse { $this->cuttingService->delete($cutting); - Inertia::flash('success', 'Cutting berhasil dihapus.'); + $this->flashDeleted('Proses potong'); return redirect()->route('admin.manage.cuttings.index'); } @@ -89,18 +91,19 @@ public function transitionStatus(CuttingStatusTransitionRequest $request, Cuttin $this->cuttingService->transitionStatus( $cutting, $status, + $request->user(), $request->validated('reason'), ); $message = match ($status) { - CuttingStatus::COMPLETED => 'Cutting berhasil diselesaikan. Menunggu verifikasi admin toko.', - CuttingStatus::VERIFIED => 'Cutting berhasil diverifikasi. Stok produk telah diperbarui.', - CuttingStatus::REJECTED => 'Cutting ditolak.', - CuttingStatus::IN_PROGRESS => 'Cutting dikembalikan ke proses.', - default => 'Status cutting berhasil diperbarui.', + CuttingStatus::COMPLETED => 'Proses potong berhasil diselesaikan. Menunggu verifikasi admin toko.', + CuttingStatus::VERIFIED => 'Proses potong berhasil diverifikasi. Stok produk telah diperbarui.', + CuttingStatus::REJECTED => 'Proses potong berhasil ditolak.', + CuttingStatus::IN_PROGRESS => 'Proses potong dikembalikan ke proses.', + default => 'Status proses potong berhasil diperbarui.', }; - Inertia::flash('success', $message); + $this->flashSuccess($message); return redirect()->route('admin.manage.cuttings.index'); } diff --git a/app/Http/Controllers/Admin/Manage/OrderController.php b/app/Http/Controllers/Admin/Manage/OrderController.php index 06c271e..b777a10 100644 --- a/app/Http/Controllers/Admin/Manage/OrderController.php +++ b/app/Http/Controllers/Admin/Manage/OrderController.php @@ -4,6 +4,7 @@ use App\Enums\OrderChannel; use App\Enums\OrderStatus; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Manage\OrderRequest; @@ -17,6 +18,7 @@ class OrderController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -47,7 +49,7 @@ public function store(OrderRequest $request): RedirectResponse { $this->orderService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Pesanan berhasil ditambahkan.'); + $this->flashCreated('Pesanan'); return redirect()->route('admin.manage.orders.index'); } @@ -55,7 +57,7 @@ public function store(OrderRequest $request): RedirectResponse public function edit(Order $order): Response|RedirectResponse { if (! $order->status->isEditable()) { - Inertia::flash('error', 'Pesanan tidak dapat diubah.'); + $this->flashError('Pesanan tidak dapat diubah.'); return redirect()->route('admin.manage.orders.index'); } @@ -73,7 +75,7 @@ public function update(OrderRequest $request, Order $order): RedirectResponse { $this->orderService->update($order, $request->validated()); - Inertia::flash('success', 'Pesanan berhasil diperbarui.'); + $this->flashUpdated('Pesanan'); return redirect()->route('admin.manage.orders.index'); } @@ -82,7 +84,7 @@ public function destroy(Order $order): RedirectResponse { $this->orderService->delete($order); - Inertia::flash('success', 'Pesanan berhasil dihapus.'); + $this->flashDeleted('Pesanan'); return redirect()->route('admin.manage.orders.index'); } @@ -100,7 +102,7 @@ public function transitionStatus(OrderStatusTransitionRequest $request, Order $o default => 'Status pesanan berhasil diperbarui.', }; - Inertia::flash('success', $message); + $this->flashSuccess($message); return redirect()->route('admin.manage.orders.index'); } diff --git a/app/Http/Controllers/Admin/Manage/PurchaseController.php b/app/Http/Controllers/Admin/Manage/PurchaseController.php index 9e790d6..8b8fa76 100644 --- a/app/Http/Controllers/Admin/Manage/PurchaseController.php +++ b/app/Http/Controllers/Admin/Manage/PurchaseController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Manage; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Manage\PurchaseRequest; @@ -14,6 +15,7 @@ class PurchaseController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -42,7 +44,7 @@ public function store(PurchaseRequest $request): RedirectResponse { $this->purchaseService->create($request->validated(), $request->user()); - Inertia::flash('success', 'Belanja berhasil ditambahkan.'); + $this->flashCreated('Belanja'); return redirect()->route('admin.manage.purchases.index'); } @@ -60,7 +62,7 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe { $this->purchaseService->update($purchase, $request->validated()); - Inertia::flash('success', 'Belanja berhasil diperbarui.'); + $this->flashUpdated('Belanja'); return redirect()->route('admin.manage.purchases.index'); } @@ -69,7 +71,7 @@ public function destroy(Purchase $purchase): RedirectResponse { $this->purchaseService->delete($purchase); - Inertia::flash('success', 'Belanja berhasil dihapus.'); + $this->flashDeleted('Belanja'); return redirect()->route('admin.manage.purchases.index'); } diff --git a/app/Http/Controllers/Admin/Master/CategoryController.php b/app/Http/Controllers/Admin/Master/CategoryController.php index 57cdb65..743eaf9 100644 --- a/app/Http/Controllers/Admin/Master/CategoryController.php +++ b/app/Http/Controllers/Admin/Master/CategoryController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Master; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\CategoryRequest; @@ -14,6 +15,7 @@ class CategoryController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -34,7 +36,7 @@ public function store(CategoryRequest $request): RedirectResponse { $this->categoryService->create($request->validated()); - Inertia::flash('success', 'Kategori berhasil ditambahkan.'); + $this->flashCreated('Kategori'); return redirect()->route('admin.master.categories.index'); } @@ -43,7 +45,7 @@ public function update(CategoryRequest $request, Category $category): RedirectRe { $this->categoryService->update($category, $request->validated()); - Inertia::flash('success', 'Kategori berhasil diperbarui.'); + $this->flashUpdated('Kategori'); return redirect()->route('admin.master.categories.index'); } @@ -52,7 +54,7 @@ public function destroy(Category $category): RedirectResponse { $this->categoryService->delete($category); - Inertia::flash('success', 'Kategori berhasil dihapus.'); + $this->flashDeleted('Kategori'); return redirect()->route('admin.master.categories.index'); } diff --git a/app/Http/Controllers/Admin/Master/CustomerController.php b/app/Http/Controllers/Admin/Master/CustomerController.php index 20d934d..4c8affc 100644 --- a/app/Http/Controllers/Admin/Master/CustomerController.php +++ b/app/Http/Controllers/Admin/Master/CustomerController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Master; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\CustomerRequest; @@ -14,6 +15,7 @@ class CustomerController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -34,7 +36,7 @@ public function store(CustomerRequest $request): RedirectResponse { $this->customerService->create($request->validated()); - Inertia::flash('success', 'Customer berhasil ditambahkan.'); + $this->flashCreated('Pelanggan'); return redirect()->route('admin.master.customers.index'); } @@ -43,7 +45,7 @@ public function update(CustomerRequest $request, Customer $customer): RedirectRe { $this->customerService->update($customer, $request->validated()); - Inertia::flash('success', 'Customer berhasil diperbarui.'); + $this->flashUpdated('Pelanggan'); return redirect()->route('admin.master.customers.index'); } @@ -52,7 +54,7 @@ public function destroy(Customer $customer): RedirectResponse { $this->customerService->delete($customer); - Inertia::flash('success', 'Customer berhasil dihapus.'); + $this->flashDeleted('Pelanggan'); return redirect()->route('admin.master.customers.index'); } diff --git a/app/Http/Controllers/Admin/Master/ProductController.php b/app/Http/Controllers/Admin/Master/ProductController.php index 06f50df..0dbb005 100644 --- a/app/Http/Controllers/Admin/Master/ProductController.php +++ b/app/Http/Controllers/Admin/Master/ProductController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Master; use App\Enums\PriceType; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\ProductRequest; @@ -17,6 +18,7 @@ class ProductController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -48,7 +50,7 @@ public function store(ProductRequest $request): RedirectResponse { $this->productService->create($request->validated()); - Inertia::flash('success', 'Produk berhasil ditambahkan.'); + $this->flashCreated('Produk'); return redirect()->route('admin.master.products.index'); } @@ -77,7 +79,7 @@ public function update(ProductRequest $request, Product $product): RedirectRespo { $this->productService->update($product, $request->validated()); - Inertia::flash('success', 'Produk berhasil diperbarui.'); + $this->flashUpdated('Produk'); return redirect()->route('admin.master.products.index'); } @@ -90,7 +92,7 @@ public function toggleStatus(Request $request, Product $product): RedirectRespon $this->productService->toggleStatus($product, $validated['is_active']); - Inertia::flash('success', 'Status produk berhasil diperbarui.'); + $this->flashStatusUpdated('produk'); return back(); } @@ -99,7 +101,7 @@ public function destroy(Product $product): RedirectResponse { $this->productService->delete($product); - Inertia::flash('success', 'Produk berhasil dihapus.'); + $this->flashDeleted('Produk'); return redirect()->route('admin.master.products.index'); } diff --git a/app/Http/Controllers/Admin/Master/RawMaterialController.php b/app/Http/Controllers/Admin/Master/RawMaterialController.php index ca7b9d8..81ea21e 100644 --- a/app/Http/Controllers/Admin/Master/RawMaterialController.php +++ b/app/Http/Controllers/Admin/Master/RawMaterialController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Master; use App\Enums\RawMaterialUnit; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\RawMaterialRequest; @@ -17,6 +18,7 @@ class RawMaterialController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -47,7 +49,7 @@ public function store(RawMaterialRequest $request): RedirectResponse { $this->rawMaterialService->create($request->validated()); - Inertia::flash('success', 'Bahan baku berhasil ditambahkan.'); + $this->flashCreated('Bahan baku'); return redirect()->route('admin.master.raw-materials.index'); } @@ -72,7 +74,7 @@ public function update(RawMaterialRequest $request, RawMaterial $rawMaterial): R { $this->rawMaterialService->update($rawMaterial, $request->validated()); - Inertia::flash('success', 'Bahan baku berhasil diperbarui.'); + $this->flashUpdated('Bahan baku'); return redirect()->route('admin.master.raw-materials.index'); } @@ -85,7 +87,7 @@ public function toggleStatus(Request $request, RawMaterial $rawMaterial): Redire $this->rawMaterialService->toggleStatus($rawMaterial, $validated['is_active']); - Inertia::flash('success', 'Status bahan baku berhasil diperbarui.'); + $this->flashStatusUpdated('bahan baku'); return back(); } @@ -94,7 +96,7 @@ public function destroy(RawMaterial $rawMaterial): RedirectResponse { $this->rawMaterialService->delete($rawMaterial); - Inertia::flash('success', 'Bahan baku berhasil dihapus.'); + $this->flashDeleted('Bahan baku'); return redirect()->route('admin.master.raw-materials.index'); } diff --git a/app/Http/Controllers/Admin/Master/SupplierController.php b/app/Http/Controllers/Admin/Master/SupplierController.php index e37307d..bf2f329 100644 --- a/app/Http/Controllers/Admin/Master/SupplierController.php +++ b/app/Http/Controllers/Admin/Master/SupplierController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Master; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\SupplierRequest; @@ -14,6 +15,7 @@ class SupplierController extends Controller { + use FlashesEntityMessage; use ParsesDataTableQuery; public function __construct( @@ -34,7 +36,7 @@ public function store(SupplierRequest $request): RedirectResponse { $this->supplierService->create($request->validated()); - Inertia::flash('success', 'Supplier berhasil ditambahkan.'); + $this->flashCreated('Pemasok'); return redirect()->route('admin.master.suppliers.index'); } @@ -43,7 +45,7 @@ public function update(SupplierRequest $request, Supplier $supplier): RedirectRe { $this->supplierService->update($supplier, $request->validated()); - Inertia::flash('success', 'Supplier berhasil diperbarui.'); + $this->flashUpdated('Pemasok'); return redirect()->route('admin.master.suppliers.index'); } @@ -52,7 +54,7 @@ public function destroy(Supplier $supplier): RedirectResponse { $this->supplierService->delete($supplier); - Inertia::flash('success', 'Supplier berhasil dihapus.'); + $this->flashDeleted('Pemasok'); return redirect()->route('admin.master.suppliers.index'); } diff --git a/app/Http/Controllers/Admin/System/SettingController.php b/app/Http/Controllers/Admin/System/SettingController.php index 3dd6d02..7133034 100644 --- a/app/Http/Controllers/Admin/System/SettingController.php +++ b/app/Http/Controllers/Admin/System/SettingController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\System; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\System\Setting\MarketplaceRequest; use App\Http\Requests\Admin\System\Setting\SocialMediaRequest; @@ -15,6 +16,8 @@ class SettingController extends Controller { + use FlashesEntityMessage; + public function __construct( private readonly SystemService $systemService, private readonly SocialMediaService $socialMediaService, @@ -34,7 +37,7 @@ public function updateSystem(SystemRequest $request): RedirectResponse { $this->systemService->updateSystem($request->validated()); - Inertia::flash('success', 'Pengaturan sistem berhasil disimpan.'); + $this->flashSuccess('Pengaturan sistem berhasil disimpan.'); return redirect()->route('admin.system.setting.index'); } @@ -43,7 +46,7 @@ public function updateSocialMedia(SocialMediaRequest $request): RedirectResponse { $this->socialMediaService->updateSocialMedia($request->validated()); - Inertia::flash('success', 'Pengaturan media sosial berhasil disimpan.'); + $this->flashSuccess('Pengaturan media sosial berhasil disimpan.'); return redirect()->route('admin.system.setting.index'); } @@ -52,7 +55,7 @@ public function updateMarketplace(MarketplaceRequest $request): RedirectResponse { $this->marketplaceService->updateMarketplace($request->validated()); - Inertia::flash('success', 'Pengaturan marketplace berhasil disimpan.'); + $this->flashSuccess('Pengaturan marketplace berhasil disimpan.'); return redirect()->route('admin.system.setting.index'); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 9c4eec0..a8f0c17 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Auth; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use App\Http\Requests\Auth\LoginRequest; use Illuminate\Http\RedirectResponse; @@ -10,6 +11,8 @@ class LoginController extends Controller { + use FlashesEntityMessage; + public function index(): Response { return Inertia::render('auth/Login'); @@ -30,8 +33,8 @@ public function store(LoginRequest $request): RedirectResponse $user?->update(['last_login_at' => now()]); - return redirect() - ->intended(route('admin.dashboard')) - ->with('success', 'Berhasil masuk. Selamat datang kembali!'); + $this->flashSuccess('Berhasil masuk. Selamat datang kembali!'); + + return redirect()->intended(route('admin.dashboard')); } } diff --git a/app/Http/Controllers/Auth/LogoutController.php b/app/Http/Controllers/Auth/LogoutController.php index 4422c71..9f0f8b9 100644 --- a/app/Http/Controllers/Auth/LogoutController.php +++ b/app/Http/Controllers/Auth/LogoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Auth; +use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -9,6 +10,8 @@ class LogoutController extends Controller { + use FlashesEntityMessage; + public function store(Request $request): RedirectResponse { if ($user = $request->user()) { @@ -23,8 +26,8 @@ public function store(Request $request): RedirectResponse $request->session()->invalidate(); $request->session()->regenerateToken(); - return redirect() - ->route('login') - ->with('success', 'Anda berhasil keluar.'); + $this->flashSuccess('Anda berhasil keluar.'); + + return redirect()->route('login'); } } diff --git a/app/Http/Controllers/Concerns/FlashesEntityMessage.php b/app/Http/Controllers/Concerns/FlashesEntityMessage.php new file mode 100644 index 0000000..f4003e3 --- /dev/null +++ b/app/Http/Controllers/Concerns/FlashesEntityMessage.php @@ -0,0 +1,38 @@ +check(); } + /** + * @return array + */ public function rules(): array { return [ 'appearance' => ['required', Rule::in(['light', 'dark', 'system'])], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'appearance' => 'tampilan', + ]; + } } diff --git a/app/Http/Requests/Admin/Account/UpdatePasswordRequest.php b/app/Http/Requests/Admin/Account/UpdatePasswordRequest.php index 6d2611f..e579c39 100644 --- a/app/Http/Requests/Admin/Account/UpdatePasswordRequest.php +++ b/app/Http/Requests/Admin/Account/UpdatePasswordRequest.php @@ -12,6 +12,9 @@ public function authorize(): bool return auth()->check(); } + /** + * @return array + */ public function rules(): array { return [ @@ -19,4 +22,16 @@ public function rules(): array 'password' => ['required', 'confirmed', Password::defaults()], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'current_password' => 'kata sandi saat ini', + 'password' => 'kata sandi baru', + 'password_confirmation' => 'konfirmasi kata sandi baru', + ]; + } } diff --git a/app/Http/Requests/Admin/Account/UpdateProfileRequest.php b/app/Http/Requests/Admin/Account/UpdateProfileRequest.php index 943ff52..09a6701 100644 --- a/app/Http/Requests/Admin/Account/UpdateProfileRequest.php +++ b/app/Http/Requests/Admin/Account/UpdateProfileRequest.php @@ -13,16 +13,35 @@ public function authorize(): bool return auth()->check(); } + /** + * @return array + */ public function rules(): array { return [ 'email' => ['required', 'email', 'max:100', Rule::unique('users', 'email')->ignore($this->user()?->id)], 'username' => ['required', 'string', 'max:20', 'alpha_dash', Rule::unique('users', 'username')->ignore($this->user()?->id)], 'full_name' => ['required', 'string', 'max:200'], - 'phone_number' => ['nullable', 'string', 'regex:/^08\d{8,11}$/'], + 'phone_number' => ['nullable', 'string', 'max:20', 'regex:/^08\d{8,11}$/'], 'gender' => ['nullable', Rule::enum(Gender::class)], 'birth_date' => ['nullable', 'date', 'before:today'], 'address' => ['nullable', 'string'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'email' => 'email', + 'username' => 'username', + 'full_name' => 'nama lengkap', + 'phone_number' => 'nomor telepon', + 'gender' => 'jenis kelamin', + 'birth_date' => 'tanggal lahir', + 'address' => 'alamat', + ]; + } } diff --git a/app/Http/Requests/Admin/Finance/DepositCashRequest.php b/app/Http/Requests/Admin/Finance/DepositCashRequest.php index f70a09c..096909d 100644 --- a/app/Http/Requests/Admin/Finance/DepositCashRequest.php +++ b/app/Http/Requests/Admin/Finance/DepositCashRequest.php @@ -15,12 +15,27 @@ public function authorize(): bool return $this->user()?->can(Permission::CASH_DEPOSIT->value) ?? false; } + /** + * @return array + */ public function rules(): array { return [ 'amount' => ['required', 'integer', 'min:1'], - 'description' => ['required', 'string', 'max:200'], + 'description' => ['required', 'string', 'max:100'], ...$this->photoRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'amount' => 'jumlah', + 'description' => 'keterangan', + ...$this->photoUploadAttributes('foto bukti'), + ]; + } } diff --git a/app/Http/Requests/Admin/Finance/EmployeeAdvanceRequest.php b/app/Http/Requests/Admin/Finance/EmployeeAdvanceRequest.php index 3bcbc97..db5925e 100644 --- a/app/Http/Requests/Admin/Finance/EmployeeAdvanceRequest.php +++ b/app/Http/Requests/Admin/Finance/EmployeeAdvanceRequest.php @@ -43,9 +43,22 @@ public function rules(): array { return [ 'amount' => ['required', 'integer', 'min:1'], - 'description' => ['required', 'string', 'max:500'], + 'description' => ['required', 'string', 'max:100'], 'due_date' => ['required', 'date'], ...$this->photoRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'amount' => 'jumlah', + 'description' => 'keterangan', + 'due_date' => 'jatuh tempo', + ...$this->photoUploadAttributes('foto bukti'), + ]; + } } diff --git a/app/Http/Requests/Admin/Finance/ExpenseRequest.php b/app/Http/Requests/Admin/Finance/ExpenseRequest.php index 02c4bf3..b194fad 100644 --- a/app/Http/Requests/Admin/Finance/ExpenseRequest.php +++ b/app/Http/Requests/Admin/Finance/ExpenseRequest.php @@ -26,8 +26,20 @@ public function rules(): array { return [ 'amount' => ['required', 'integer', 'min:1'], - 'description' => ['required', 'string', 'max:500'], + 'description' => ['required', 'string', 'max:100'], ...$this->photoRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'amount' => 'jumlah', + 'description' => 'keterangan', + ...$this->photoUploadAttributes('foto bukti'), + ]; + } } diff --git a/app/Http/Requests/Admin/Finance/PayrollAdjustmentRequest.php b/app/Http/Requests/Admin/Finance/PayrollAdjustmentRequest.php index 34b737a..1ee878f 100644 --- a/app/Http/Requests/Admin/Finance/PayrollAdjustmentRequest.php +++ b/app/Http/Requests/Admin/Finance/PayrollAdjustmentRequest.php @@ -22,7 +22,19 @@ public function rules(): array return [ 'type' => ['required', Rule::enum(PayrollAdjustmentType::class)], 'amount' => ['required', 'integer', 'min:1'], - 'description' => ['required', 'string', 'max:500'], + 'description' => ['required', 'string', 'max:100'], + ]; + } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'type' => 'jenis', + 'amount' => 'jumlah', + 'description' => 'keterangan', ]; } } diff --git a/app/Http/Requests/Admin/Finance/RejectEmployeeAdvanceRequest.php b/app/Http/Requests/Admin/Finance/RejectEmployeeAdvanceRequest.php index b67e1b1..b740c76 100644 --- a/app/Http/Requests/Admin/Finance/RejectEmployeeAdvanceRequest.php +++ b/app/Http/Requests/Admin/Finance/RejectEmployeeAdvanceRequest.php @@ -21,4 +21,14 @@ public function rules(): array 'reason' => ['required', 'string', 'max:500'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'reason' => 'alasan penolakan', + ]; + } } diff --git a/app/Http/Requests/Admin/Finance/UpdateCashTransactionRequest.php b/app/Http/Requests/Admin/Finance/UpdateCashTransactionRequest.php index 1432350..5708b3d 100644 --- a/app/Http/Requests/Admin/Finance/UpdateCashTransactionRequest.php +++ b/app/Http/Requests/Admin/Finance/UpdateCashTransactionRequest.php @@ -18,12 +18,27 @@ public function authorize(): bool && $transaction?->reference_type === null; } + /** + * @return array + */ public function rules(): array { return [ 'amount' => ['required', 'integer', 'min:1'], - 'description' => ['required', 'string', 'max:200'], + 'description' => ['required', 'string', 'max:100'], ...$this->photoRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'amount' => 'jumlah', + 'description' => 'keterangan', + ...$this->photoUploadAttributes('foto bukti'), + ]; + } } diff --git a/app/Http/Requests/Admin/Hr/AttendanceCheckInRequest.php b/app/Http/Requests/Admin/Hr/AttendanceCheckInRequest.php index 0a551f4..808324b 100644 --- a/app/Http/Requests/Admin/Hr/AttendanceCheckInRequest.php +++ b/app/Http/Requests/Admin/Hr/AttendanceCheckInRequest.php @@ -18,10 +18,23 @@ public function authorize(): bool public function rules(): array { return [ - 'photo' => ['required', 'string'], - 'latitude' => ['required', 'numeric', 'between:-90,90'], - 'longitude' => ['required', 'numeric', 'between:-180,180'], + 'photo' => ['required', 'string', 'max:255'], + 'latitude' => ['required', 'numeric', 'decimal:0,7', 'between:-90,90'], + 'longitude' => ['required', 'numeric', 'decimal:0,7', 'between:-180,180'], 'location_tag' => ['required', 'string', 'max:255'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'photo' => 'foto', + 'latitude' => 'latitude', + 'longitude' => 'longitude', + 'location_tag' => 'lokasi', + ]; + } } diff --git a/app/Http/Requests/Admin/Hr/AttendanceCheckOutRequest.php b/app/Http/Requests/Admin/Hr/AttendanceCheckOutRequest.php index c647893..5932f08 100644 --- a/app/Http/Requests/Admin/Hr/AttendanceCheckOutRequest.php +++ b/app/Http/Requests/Admin/Hr/AttendanceCheckOutRequest.php @@ -18,10 +18,23 @@ public function authorize(): bool public function rules(): array { return [ - 'photo' => ['required', 'string'], - 'latitude' => ['required', 'numeric', 'between:-90,90'], - 'longitude' => ['required', 'numeric', 'between:-180,180'], + 'photo' => ['required', 'string', 'max:255'], + 'latitude' => ['required', 'numeric', 'decimal:0,7', 'between:-90,90'], + 'longitude' => ['required', 'numeric', 'decimal:0,7', 'between:-180,180'], 'location_tag' => ['required', 'string', 'max:255'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'photo' => 'foto', + 'latitude' => 'latitude', + 'longitude' => 'longitude', + 'location_tag' => 'lokasi', + ]; + } } diff --git a/app/Http/Requests/Admin/Hr/EmployeeRequest.php b/app/Http/Requests/Admin/Hr/EmployeeRequest.php index 98af213..85461e7 100644 --- a/app/Http/Requests/Admin/Hr/EmployeeRequest.php +++ b/app/Http/Requests/Admin/Hr/EmployeeRequest.php @@ -29,7 +29,7 @@ public function rules(): array 'email' => ['required', 'email', 'max:100', Rule::unique('users', 'email')->ignore($this->route('user')?->id)], 'username' => ['required', 'string', 'max:20', 'alpha_dash', Rule::unique('users', 'username')->ignore($this->route('user')?->id)], 'full_name' => ['required', 'string', 'max:200'], - 'phone_number' => ['nullable', 'string', 'regex:/^08\d{8,11}$/'], + 'phone_number' => ['nullable', 'string', 'max:20', 'regex:/^08\d{8,11}$/'], 'gender' => ['nullable', Rule::enum(Gender::class)], 'birth_date' => ['nullable', 'date', 'before:today'], 'address' => ['nullable', 'string'], @@ -39,4 +39,24 @@ public function rules(): array 'role' => ['required', Rule::in(Role::assignableValues())], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'email' => 'email', + 'username' => 'username', + 'full_name' => 'nama lengkap', + 'phone_number' => 'nomor telepon', + 'gender' => 'jenis kelamin', + 'birth_date' => 'tanggal lahir', + 'address' => 'alamat', + 'join_date' => 'tanggal bergabung', + 'employment_status' => 'status kepegawaian', + 'base_salary' => 'gaji pokok', + 'role' => 'role', + ]; + } } diff --git a/app/Http/Requests/Admin/Hr/RejectLeaveRequestRequest.php b/app/Http/Requests/Admin/Hr/RejectLeaveRequestRequest.php index d5aa78b..baed633 100644 --- a/app/Http/Requests/Admin/Hr/RejectLeaveRequestRequest.php +++ b/app/Http/Requests/Admin/Hr/RejectLeaveRequestRequest.php @@ -21,4 +21,14 @@ public function rules(): array 'reason' => ['required', 'string', 'max:500'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'reason' => 'alasan penolakan', + ]; + } } diff --git a/app/Http/Requests/Admin/Hr/SubmitLeaveRequest.php b/app/Http/Requests/Admin/Hr/SubmitLeaveRequest.php index ea62828..70dc754 100644 --- a/app/Http/Requests/Admin/Hr/SubmitLeaveRequest.php +++ b/app/Http/Requests/Admin/Hr/SubmitLeaveRequest.php @@ -43,4 +43,15 @@ public function rules(): array 'end_date' => ['required', 'date', 'after_or_equal:start_date'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'start_date' => 'tanggal mulai', + 'end_date' => 'tanggal selesai', + ]; + } } diff --git a/app/Http/Requests/Admin/Manage/CuttingRequest.php b/app/Http/Requests/Admin/Manage/CuttingRequest.php index 6e3e481..61e75ed 100644 --- a/app/Http/Requests/Admin/Manage/CuttingRequest.php +++ b/app/Http/Requests/Admin/Manage/CuttingRequest.php @@ -23,7 +23,8 @@ public function authorize(): bool public function rules(): array { return [ - 'description' => ['nullable', 'string', 'max:500'], + 'description' => ['nullable', 'string', 'max:100'], + 'materials' => ['required', 'array', 'min:1'], 'materials.*.raw_material_price_id' => [ 'required', @@ -31,8 +32,9 @@ public function rules(): array 'distinct', Rule::exists('raw_material_prices', 'id')->whereNull('deleted_at'), ], - 'materials.*.material_usage' => ['required', 'numeric', 'gt:0'], - 'materials.*.remaining_material' => ['required', 'numeric', 'gte:0'], + 'materials.*.material_usage' => ['required', 'numeric', 'decimal:0,4', 'gt:0'], + 'materials.*.remaining_material' => ['required', 'numeric', 'decimal:0,4', 'gte:0'], + 'results' => ['required', 'array', 'min:1'], 'results.*.product_variant_id' => [ 'required', @@ -42,7 +44,26 @@ public function rules(): array ], 'results.*.cutting_result' => ['required', 'integer', 'min:1'], 'results.*.warehouse_stock' => ['required', 'integer', 'min:0'], - 'results.*.cutting_reject' => ['nullable', 'integer', 'min:0'], + 'results.*.cutting_reject' => ['required', 'integer', 'min:0'], + ]; + } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'description' => 'keterangan', + 'materials' => 'bahan baku', + 'materials.*.raw_material_price_id' => 'bahan baku', + 'materials.*.material_usage' => 'pemakaian', + 'materials.*.remaining_material' => 'sisa', + 'results' => 'hasil produk', + 'results.*.product_variant_id' => 'varian produk', + 'results.*.cutting_result' => 'hasil', + 'results.*.warehouse_stock' => 'gudang', + 'results.*.cutting_reject' => 'reject', ]; } } diff --git a/app/Http/Requests/Admin/Manage/CuttingStatusTransitionRequest.php b/app/Http/Requests/Admin/Manage/CuttingStatusTransitionRequest.php index 03ef008..d014a35 100644 --- a/app/Http/Requests/Admin/Manage/CuttingStatusTransitionRequest.php +++ b/app/Http/Requests/Admin/Manage/CuttingStatusTransitionRequest.php @@ -27,11 +27,22 @@ public function authorize(): bool public function rules(): array { return [ - 'status' => ['required', 'string', Rule::in(array_column(CuttingStatus::cases(), 'value'))], + 'status' => ['required', Rule::enum(CuttingStatus::class)], 'reason' => ['nullable', 'string', 'max:500'], ]; } + /** + * @return array + */ + public function attributes(): array + { + return [ + 'status' => 'status', + 'reason' => 'alasan penolakan', + ]; + } + public function withValidator(Validator $validator): void { $validator->after(function (Validator $validator): void { diff --git a/app/Http/Requests/Admin/Manage/OrderRequest.php b/app/Http/Requests/Admin/Manage/OrderRequest.php index d88ec04..f7e0f81 100644 --- a/app/Http/Requests/Admin/Manage/OrderRequest.php +++ b/app/Http/Requests/Admin/Manage/OrderRequest.php @@ -28,11 +28,12 @@ public function rules(): array { $rules = [ 'customer_id' => ['nullable', 'integer', Rule::exists('customers', 'id')->whereNull('deleted_at')], - 'channel' => ['required', 'string', Rule::in(array_column(OrderChannel::cases(), 'value'))], - 'price_type' => ['required', 'string', Rule::in(array_column(PriceType::cases(), 'value'))], + 'channel' => ['required', Rule::enum(OrderChannel::class)], + 'price_type' => ['required', Rule::enum(PriceType::class)], 'discount' => ['nullable', 'integer', 'min:0'], 'marketplace_fee' => ['nullable', 'integer', 'min:0'], - 'notes' => ['nullable', 'string', 'max:100'], + 'notes' => ['nullable', 'string'], + 'items' => ['required', 'array', 'min:1'], 'items.*.product_variant_id' => [ 'required', @@ -45,6 +46,24 @@ public function rules(): array return $rules; } + /** + * @return array + */ + public function attributes(): array + { + return [ + 'customer_id' => 'pelanggan', + 'channel' => 'channel', + 'price_type' => 'tipe harga', + 'discount' => 'diskon', + 'marketplace_fee' => 'biaya marketplace', + 'notes' => 'keterangan', + 'items' => 'produk', + 'items.*.product_variant_id' => 'varian produk', + 'items.*.quantity' => 'jumlah', + ]; + } + public function withValidator(Validator $validator): void { if (! $this->isMethod('PUT') && ! $this->isMethod('PATCH')) { diff --git a/app/Http/Requests/Admin/Manage/OrderStatusTransitionRequest.php b/app/Http/Requests/Admin/Manage/OrderStatusTransitionRequest.php index 1257d21..21291c8 100644 --- a/app/Http/Requests/Admin/Manage/OrderStatusTransitionRequest.php +++ b/app/Http/Requests/Admin/Manage/OrderStatusTransitionRequest.php @@ -27,7 +27,17 @@ public function authorize(): bool public function rules(): array { return [ - 'status' => ['required', 'string', Rule::in(array_column(OrderStatus::cases(), 'value'))], + 'status' => ['required', Rule::enum(OrderStatus::class)], + ]; + } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'status' => 'status', ]; } diff --git a/app/Http/Requests/Admin/Manage/PurchaseRequest.php b/app/Http/Requests/Admin/Manage/PurchaseRequest.php index 429d720..beac7bb 100644 --- a/app/Http/Requests/Admin/Manage/PurchaseRequest.php +++ b/app/Http/Requests/Admin/Manage/PurchaseRequest.php @@ -29,14 +29,31 @@ public function rules(): array 'supplier_id' => ['required', 'integer', Rule::exists('suppliers', 'id')->whereNull('deleted_at')], 'discount' => ['nullable', 'integer', 'min:0'], 'notes' => ['nullable', 'string', 'max:100'], + 'items' => ['required', 'array', 'min:1'], 'items.*.raw_material_price_id' => [ 'required', 'integer', Rule::exists('raw_material_prices', 'id')->whereNull('deleted_at'), ], - 'items.*.quantity' => ['required', 'numeric', 'gt:0'], + 'items.*.quantity' => ['required', 'numeric', 'decimal:0,4', 'gt:0'], ...$this->photoRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'supplier_id' => 'supplier', + 'discount' => 'diskon', + 'notes' => 'keterangan', + 'items' => 'bahan baku', + 'items.*.raw_material_price_id' => 'bahan baku', + 'items.*.quantity' => 'jumlah', + ...$this->photoUploadAttributes('bukti transaksi'), + ]; + } } diff --git a/app/Http/Requests/Admin/Master/CategoryRequest.php b/app/Http/Requests/Admin/Master/CategoryRequest.php index e8ea83a..8f76cd9 100644 --- a/app/Http/Requests/Admin/Master/CategoryRequest.php +++ b/app/Http/Requests/Admin/Master/CategoryRequest.php @@ -25,4 +25,14 @@ public function rules(): array 'name' => ['required', 'string', 'max:50'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'name' => 'nama', + ]; + } } diff --git a/app/Http/Requests/Admin/Master/CustomerRequest.php b/app/Http/Requests/Admin/Master/CustomerRequest.php index e8e2aae..d9df170 100644 --- a/app/Http/Requests/Admin/Master/CustomerRequest.php +++ b/app/Http/Requests/Admin/Master/CustomerRequest.php @@ -23,8 +23,20 @@ public function rules(): array { return [ 'name' => ['required', 'string', 'max:200'], - 'address' => ['required', 'string'], - 'phone_number' => ['required', 'string', 'max:20'], + 'phone_number' => ['nullable', 'string', 'max:20'], + 'address' => ['nullable', 'string'], + ]; + } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'name' => 'nama', + 'phone_number' => 'nomor telepon', + 'address' => 'alamat', ]; } } diff --git a/app/Http/Requests/Admin/Master/ProductRequest.php b/app/Http/Requests/Admin/Master/ProductRequest.php index a5c923c..164fbad 100644 --- a/app/Http/Requests/Admin/Master/ProductRequest.php +++ b/app/Http/Requests/Admin/Master/ProductRequest.php @@ -29,8 +29,10 @@ public function rules(): array return [ 'name' => ['required', 'string', 'max:200'], 'description' => ['nullable', 'string'], + 'category_ids' => ['required', 'array', 'min:1'], 'category_ids.*' => ['integer', Rule::exists('categories', 'id')->whereNull('deleted_at')], + 'variants' => ['required', 'array', 'min:1'], 'variants.*.id' => [ 'nullable', @@ -43,8 +45,28 @@ public function rules(): array 'variants.*.stock' => ['required', 'integer', 'min:0'], 'variants.*.prices' => ['required', 'array', 'min:1'], 'variants.*.prices.*.type' => ['required', Rule::enum(PriceType::class)], - 'variants.*.prices.*.price' => ['required', 'numeric', 'gt:0'], + 'variants.*.prices.*.price' => ['required', 'numeric', 'decimal:0,2', 'gt:0'], ...$this->variantImageRules(), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'name' => 'nama produk', + 'description' => 'deskripsi', + 'category_ids' => 'kategori', + 'category_ids.*' => 'kategori', + 'variants' => 'varian', + 'variants.*.name' => 'nama varian', + 'variants.*.stock' => 'stok', + 'variants.*.prices' => 'harga', + 'variants.*.prices.*.type' => 'tipe harga', + 'variants.*.prices.*.price' => 'harga', + ...$this->variantImageAttributes('variants', 'foto varian'), + ]; + } } diff --git a/app/Http/Requests/Admin/Master/RawMaterialRequest.php b/app/Http/Requests/Admin/Master/RawMaterialRequest.php index 2c1bc10..4af1adf 100644 --- a/app/Http/Requests/Admin/Master/RawMaterialRequest.php +++ b/app/Http/Requests/Admin/Master/RawMaterialRequest.php @@ -29,6 +29,7 @@ public function rules(): array return [ 'name' => ['required', 'string', 'max:200'], 'unit' => ['required', Rule::enum(RawMaterialUnit::class)], + 'prices' => ['required', 'array', 'min:1'], 'prices.*.id' => [ 'nullable', @@ -38,9 +39,25 @@ public function rules(): array ->whereNull('deleted_at'), ], 'prices.*.variant' => ['required', 'string', 'max:200'], - 'prices.*.price' => ['required', 'numeric', 'gt:0'], - 'prices.*.stock' => ['required', 'numeric', 'min:0'], + 'prices.*.price' => ['required', 'numeric', 'decimal:0,2', 'gt:0'], + 'prices.*.stock' => ['required', 'numeric', 'decimal:0,4', 'min:0'], ...$this->variantImageRules('prices'), ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'name' => 'nama bahan baku', + 'unit' => 'satuan', + 'prices' => 'varian', + 'prices.*.variant' => 'nama varian', + 'prices.*.price' => 'harga', + 'prices.*.stock' => 'stok', + ...$this->variantImageAttributes('prices', 'foto varian'), + ]; + } } diff --git a/app/Http/Requests/Admin/Master/SupplierRequest.php b/app/Http/Requests/Admin/Master/SupplierRequest.php index 010cac0..11de518 100644 --- a/app/Http/Requests/Admin/Master/SupplierRequest.php +++ b/app/Http/Requests/Admin/Master/SupplierRequest.php @@ -23,8 +23,20 @@ public function rules(): array { return [ 'name' => ['required', 'string', 'max:200'], - 'address' => ['required', 'string'], - 'phone_number' => ['required', 'string', 'max:20'], + 'phone_number' => ['nullable', 'string', 'max:20'], + 'address' => ['nullable', 'string'], + ]; + } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'name' => 'nama', + 'phone_number' => 'nomor telepon', + 'address' => 'alamat', ]; } } diff --git a/app/Http/Requests/Admin/System/Setting/MarketplaceRequest.php b/app/Http/Requests/Admin/System/Setting/MarketplaceRequest.php index 0e11c40..0e543b9 100644 --- a/app/Http/Requests/Admin/System/Setting/MarketplaceRequest.php +++ b/app/Http/Requests/Admin/System/Setting/MarketplaceRequest.php @@ -12,6 +12,9 @@ public function authorize(): bool return $this->user()?->can(Permission::SETTINGS_UPDATE->value) ?? false; } + /** + * @return array + */ public function rules(): array { return [ @@ -33,4 +36,28 @@ public function rules(): array 'shopee_voucher_fee' => ['required', 'numeric', 'min:0', 'max:100'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'tiktok_shop_enabled' => 'aktif', + 'tiktok_shop_admin_fee' => 'biaya admin TikTok Shop', + 'tiktok_shop_transaction_fee' => 'biaya transaksi TikTok Shop', + 'tiktok_shop_payment_fee' => 'biaya pembayaran TikTok Shop', + 'tiktok_shop_affiliate_commission' => 'komisi afiliasi TikTok Shop', + 'tiktok_shop_shipping_subsidy' => 'subsidi ongkir TikTok Shop', + 'tiktok_shop_vat_rate' => 'PPN / pajak TikTok Shop', + 'shopee_enabled' => 'aktif', + 'shopee_commission_fee' => 'komisi Shopee', + 'shopee_transaction_fee' => 'biaya transaksi Shopee', + 'shopee_service_fee' => 'biaya layanan Shopee', + 'shopee_payment_fee' => 'biaya pembayaran Shopee', + 'shopee_affiliate_commission' => 'komisi afiliasi Shopee', + 'shopee_shipping_subsidy' => 'subsidi ongkir Shopee', + 'shopee_voucher_fee' => 'biaya voucher / diskon Shopee', + ]; + } } diff --git a/app/Http/Requests/Admin/System/Setting/SocialMediaRequest.php b/app/Http/Requests/Admin/System/Setting/SocialMediaRequest.php index 7d86cce..dd17d06 100644 --- a/app/Http/Requests/Admin/System/Setting/SocialMediaRequest.php +++ b/app/Http/Requests/Admin/System/Setting/SocialMediaRequest.php @@ -12,6 +12,9 @@ public function authorize(): bool return $this->user()?->can(Permission::SETTINGS_UPDATE->value) ?? false; } + /** + * @return array + */ public function rules(): array { return [ @@ -20,4 +23,16 @@ public function rules(): array 'tiktok_url' => ['nullable', 'url', 'max:100'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'instagram_url' => 'Instagram', + 'facebook_url' => 'Facebook', + 'tiktok_url' => 'TikTok', + ]; + } } diff --git a/app/Http/Requests/Admin/System/Setting/SystemRequest.php b/app/Http/Requests/Admin/System/Setting/SystemRequest.php index 23dc6be..21bd19c 100644 --- a/app/Http/Requests/Admin/System/Setting/SystemRequest.php +++ b/app/Http/Requests/Admin/System/Setting/SystemRequest.php @@ -12,6 +12,9 @@ public function authorize(): bool return $this->user()?->can(Permission::SETTINGS_UPDATE->value) ?? false; } + /** + * @return array + */ public function rules(): array { return [ @@ -24,4 +27,20 @@ public function rules(): array 'login_cover' => ['required', 'image', 'max:5120'], ]; } + + /** + * @return array + */ + public function attributes(): array + { + return [ + 'app_name' => 'nama aplikasi', + 'about_app' => 'tentang aplikasi', + 'email' => 'email', + 'phone' => 'nomor telepon', + 'address' => 'alamat', + 'logo' => 'logo', + 'login_cover' => 'cover login', + ]; + } } diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php index 261496c..956ee63 100644 --- a/app/Http/Requests/Auth/LoginRequest.php +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -27,6 +27,17 @@ public function rules(): array ]; } + /** + * @return array + */ + public function attributes(): array + { + return [ + 'login' => 'email/username', + 'password' => 'kata sandi', + ]; + } + /** * @throws ValidationException */ diff --git a/app/Http/Requests/Concerns/ValidatesMediaUploads.php b/app/Http/Requests/Concerns/ValidatesMediaUploads.php index 5de428d..3ad2eb3 100644 --- a/app/Http/Requests/Concerns/ValidatesMediaUploads.php +++ b/app/Http/Requests/Concerns/ValidatesMediaUploads.php @@ -29,4 +29,30 @@ protected function variantImageRules(string $variantsKey = 'variants', int $max "{$variantsKey}.*.remove_media_ids.*" => ['integer'], ]; } + + /** + * @return array + */ + protected function photoUploadAttributes(string $label): array + { + return [ + 'photos' => $label, + 'photos.*' => $label, + 'remove_media_ids' => 'media yang dihapus', + 'remove_media_ids.*' => 'media yang dihapus', + ]; + } + + /** + * @return array + */ + protected function variantImageAttributes(string $variantsKey, string $label): array + { + return [ + "{$variantsKey}.*.images" => $label, + "{$variantsKey}.*.images.*" => $label, + "{$variantsKey}.*.remove_media_ids" => 'media yang dihapus', + "{$variantsKey}.*.remove_media_ids.*" => 'media yang dihapus', + ]; + } } diff --git a/app/Models/Attendance.php b/app/Models/Attendance.php index 1f0e58d..0eb0e11 100644 --- a/app/Models/Attendance.php +++ b/app/Models/Attendance.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Spatie\MediaLibrary\HasMedia; @@ -25,20 +26,10 @@ ])] class Attendance extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; - public static function mediaModuleName(): string - { - return 'attendance'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('checkin')->singleFile(); - $this->addMediaCollection('checkout')->singleFile(); - } - protected function casts(): array { return [ @@ -58,13 +49,6 @@ public function employee(): BelongsTo return $this->belongsTo(Employee::class); } - public function employeeName(): Attribute - { - return Attribute::make( - get: fn () => $this->employee?->user?->profile?->full_name, - ); - } - public function attendanceDateFormatted(): Attribute { return Attribute::make( @@ -79,6 +63,17 @@ public function checkInAtFormatted(): Attribute ); } + public function checkInPhotoUrl(): Attribute + { + return Attribute::make( + get: function () { + $photo = MediaPresenter::first($this, 'checkin'); + + return $photo['url'] ?? null; + }, + ); + } + public function checkOutAtFormatted(): Attribute { return Attribute::make( @@ -86,6 +81,24 @@ public function checkOutAtFormatted(): Attribute ); } + public function checkOutPhotoUrl(): Attribute + { + return Attribute::make( + get: function () { + $photo = MediaPresenter::first($this, 'checkout'); + + return $photo['url'] ?? null; + }, + ); + } + + public function employeeName(): Attribute + { + return Attribute::make( + get: fn () => $this->employee?->user?->profile?->full_name, + ); + } + public function workDurationFormatted(): Attribute { return Attribute::make( @@ -106,25 +119,14 @@ public function workDurationFormatted(): Attribute ); } - public function checkInPhotoUrl(): Attribute + public static function mediaModuleName(): string { - return Attribute::make( - get: function () { - $photo = MediaPresenter::first($this, 'checkin'); - - return $photo['url'] ?? null; - }, - ); + return 'attendance'; } - public function checkOutPhotoUrl(): Attribute + public function registerMediaCollections(): void { - return Attribute::make( - get: function () { - $photo = MediaPresenter::first($this, 'checkout'); - - return $photo['url'] ?? null; - }, - ); + $this->addMediaCollection('checkin')->singleFile(); + $this->addMediaCollection('checkout')->singleFile(); } } diff --git a/app/Models/CashAccount.php b/app/Models/CashAccount.php index 6b4e3b8..47e332c 100644 --- a/app/Models/CashAccount.php +++ b/app/Models/CashAccount.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -13,6 +14,7 @@ #[Appends(['balance_formatted'])] class CashAccount extends Model { + use HasFactory; use InteractsWithActivityLog; protected function casts(): array @@ -22,15 +24,15 @@ protected function casts(): array ]; } + public function transactions(): HasMany + { + return $this->hasMany(CashTransaction::class); + } + public function balanceFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->balance, 0, ',', '.'), ); } - - public function transactions(): HasMany - { - return $this->hasMany(CashTransaction::class); - } } diff --git a/app/Models/CashTransaction.php b/app/Models/CashTransaction.php index ceacfcc..023f3d4 100644 --- a/app/Models/CashTransaction.php +++ b/app/Models/CashTransaction.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -24,20 +25,11 @@ ])] class CashTransaction extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; use SoftDeletes; - public static function mediaModuleName(): string - { - return 'cash'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('photos'); - } - protected function casts(): array { return [ @@ -46,6 +38,21 @@ protected function casts(): array ]; } + public function cashAccount(): BelongsTo + { + return $this->belongsTo(CashAccount::class); + } + + public function createdBy(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + + public function reference(): MorphTo + { + return $this->morphTo(); + } + public function amountFormatted(): Attribute { return Attribute::make( @@ -60,6 +67,33 @@ public function balanceAfterFormatted(): Attribute ); } + public function createdAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), + ); + } + + public function createdByName(): Attribute + { + return Attribute::make( + get: fn () => $this->createdBy?->profile?->full_name ?? $this->createdBy?->username, + ); + } + + public function isIncoming(): Attribute + { + return Attribute::make( + get: function () { + if ($this->reference_type === EmployeeAdvance::class && $this->reference instanceof EmployeeAdvance) { + return $this->reference->repayment_cash_transaction_id === $this->id; + } + + return self::isIncomingTransaction($this); + }, + ); + } + public function referenceLabel(): Attribute { return Attribute::make( @@ -75,31 +109,14 @@ public function referenceLabel(): Attribute ); } - public function isIncoming(): Attribute + public static function mediaModuleName(): string { - return Attribute::make( - get: function () { - if ($this->reference_type === EmployeeAdvance::class && $this->reference instanceof EmployeeAdvance) { - return $this->reference->repayment_cash_transaction_id === $this->id; - } - - return self::isIncomingTransaction($this); - }, - ); + return 'cash'; } - public function createdAtFormatted(): Attribute + public function registerMediaCollections(): void { - return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), - ); - } - - public function createdByName(): Attribute - { - return Attribute::make( - get: fn () => $this->createdBy?->profile?->full_name ?? $this->createdBy?->username, - ); + $this->addMediaCollection('photos'); } public static function labelForReferenceType(?string $referenceType): string @@ -135,19 +152,4 @@ public static function isIncomingTransaction(self $transaction): bool return self::isIncomingReference($transaction->reference_type); } - - public function cashAccount(): BelongsTo - { - return $this->belongsTo(CashAccount::class); - } - - public function createdBy(): BelongsTo - { - return $this->belongsTo(User::class, 'created_by_id'); - } - - public function reference(): MorphTo - { - return $this->morphTo(); - } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 2414764..e20298a 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -4,6 +4,7 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -13,6 +14,7 @@ #[Sluggable(from: 'name', to: 'slug')] class Category extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; diff --git a/app/Models/Customer.php b/app/Models/Customer.php index d94df35..0d54c95 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -4,6 +4,7 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -11,6 +12,7 @@ #[Guarded(['id'])] class Customer extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; diff --git a/app/Models/Cutting.php b/app/Models/Cutting.php index ce353d1..bec35f5 100644 --- a/app/Models/Cutting.php +++ b/app/Models/Cutting.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -20,6 +21,7 @@ ])] class Cutting extends Model { + use HasFactory; use HasRejection; use InteractsWithActivityLog; use SoftDeletes; @@ -31,20 +33,6 @@ protected function casts(): array ]; } - public function statusLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->status->label(), - ); - } - - public function createdAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), - ); - } - public function createdBy(): BelongsTo { return $this->belongsTo(User::class, 'created_by_id'); @@ -59,4 +47,18 @@ public function results(): HasMany { return $this->hasMany(CuttingResult::class); } + + public function createdAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), + ); + } + + public function statusLabel(): Attribute + { + return Attribute::make( + get: fn () => $this->status->label(), + ); + } } diff --git a/app/Models/CuttingMaterial.php b/app/Models/CuttingMaterial.php index 02f78fb..9bcc9a5 100644 --- a/app/Models/CuttingMaterial.php +++ b/app/Models/CuttingMaterial.php @@ -6,8 +6,10 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; #[Guarded(['id'])] #[Appends([ @@ -19,7 +21,9 @@ ])] class CuttingMaterial extends Model { + use HasFactory; use InteractsWithActivityLog; + use SoftDeletes; protected function casts(): array { @@ -29,6 +33,16 @@ protected function casts(): array ]; } + public function cutting(): BelongsTo + { + return $this->belongsTo(Cutting::class); + } + + public function rawMaterialPrice(): BelongsTo + { + return $this->belongsTo(RawMaterialPrice::class); + } + public function materialUsageFormatted(): Attribute { return Attribute::make( @@ -64,16 +78,6 @@ public function unitAbbreviation(): Attribute ); } - public function cutting(): BelongsTo - { - return $this->belongsTo(Cutting::class); - } - - public function rawMaterialPrice(): BelongsTo - { - return $this->belongsTo(RawMaterialPrice::class); - } - private function formatQuantity(float|string|null $value): string { $formatted = rtrim(rtrim(number_format((float) $value, 4, ',', '.'), '0'), ','); diff --git a/app/Models/CuttingResult.php b/app/Models/CuttingResult.php index 5405f92..c58e5b4 100644 --- a/app/Models/CuttingResult.php +++ b/app/Models/CuttingResult.php @@ -4,13 +4,17 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; #[Guarded(['id'])] class CuttingResult extends Model { + use HasFactory; use InteractsWithActivityLog; + use SoftDeletes; protected function casts(): array { diff --git a/app/Models/Employee.php b/app/Models/Employee.php index 8bc11ae..f113445 100644 --- a/app/Models/Employee.php +++ b/app/Models/Employee.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -19,6 +20,7 @@ #[Appends(['base_salary_formatted', 'join_date_formatted', 'resign_date_formatted', 'join_date_input', 'employment_status_label'])] class Employee extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; @@ -32,6 +34,13 @@ protected function casts(): array ]; } + #[Scope] + public function contract(Builder $query): void + { + $query->where('employment_status', EmploymentStatus::CONTRACT->value); + } + + #[Scope] public function fullTime(Builder $query): void { $query->where('employment_status', EmploymentStatus::FULL_TIME->value); @@ -43,68 +52,17 @@ public function partTime(Builder $query): void $query->where('employment_status', EmploymentStatus::PART_TIME->value); } - #[Scope] - public function contract(Builder $query): void - { - $query->where('employment_status', EmploymentStatus::CONTRACT->value); - } - #[Scope] public function temporary(Builder $query): void { $query->where('employment_status', EmploymentStatus::TEMPORARY->value); } - public function baseSalaryFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->base_salary, 0, ',', '.'), - ); - } - - public function joinDateFormatted(): Attribute - { - return Attribute::make( - get: fn () => Carbon::parse($this->join_date)->format('l, d F Y'), - ); - } - - public function resignDateFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->resign_date ? Carbon::parse($this->resign_date)->format('l, d F Y') : null, - ); - } - - public function joinDateInput(): Attribute - { - return Attribute::make( - get: fn () => $this->join_date?->format('Y-m-d'), - ); - } - - public function employmentStatusLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->employment_status?->label(), - ); - } - - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } - public function advances(): HasMany { return $this->hasMany(EmployeeAdvance::class); } - public function payrolls(): HasMany - { - return $this->hasMany(Payroll::class); - } - public function attendances(): HasMany { return $this->hasMany(Attendance::class); @@ -114,4 +72,49 @@ public function leaveRequests(): HasMany { return $this->hasMany(LeaveRequest::class); } + + public function payrolls(): HasMany + { + return $this->hasMany(Payroll::class); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function baseSalaryFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->base_salary, 0, ',', '.'), + ); + } + + public function employmentStatusLabel(): Attribute + { + return Attribute::make( + get: fn () => $this->employment_status?->label(), + ); + } + + public function joinDateFormatted(): Attribute + { + return Attribute::make( + get: fn () => Carbon::parse($this->join_date)->format('l, d F Y'), + ); + } + + public function joinDateInput(): Attribute + { + return Attribute::make( + get: fn () => $this->join_date?->format('Y-m-d'), + ); + } + + public function resignDateFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->resign_date ? Carbon::parse($this->resign_date)->format('l, d F Y') : null, + ); + } } diff --git a/app/Models/EmployeeAdvance.php b/app/Models/EmployeeAdvance.php index 34ceeac..1f6bf19 100644 --- a/app/Models/EmployeeAdvance.php +++ b/app/Models/EmployeeAdvance.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Spatie\MediaLibrary\HasMedia; @@ -28,20 +29,11 @@ ])] class EmployeeAdvance extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use HasRejection; use InteractsWithActivityLog; - public static function mediaModuleName(): string - { - return 'employee-advance'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('photos'); - } - protected function casts(): array { return [ @@ -53,6 +45,31 @@ protected function casts(): array ]; } + public function cashTransaction(): BelongsTo + { + return $this->belongsTo(CashTransaction::class); + } + + public function employee(): BelongsTo + { + return $this->belongsTo(Employee::class); + } + + public function paidBy(): BelongsTo + { + return $this->belongsTo(User::class, 'paid_by_id'); + } + + public function repaymentCashTransaction(): BelongsTo + { + return $this->belongsTo(CashTransaction::class, 'repayment_cash_transaction_id'); + } + + public function verifiedBy(): BelongsTo + { + return $this->belongsTo(User::class, 'verified_by_id'); + } + public function amountFormatted(): Attribute { return Attribute::make( @@ -60,6 +77,27 @@ public function amountFormatted(): Attribute ); } + public function canPay(): Attribute + { + return Attribute::make( + get: fn () => $this->status === EmployeeAdvanceStatus::APPROVED, + ); + } + + public function canVerify(): Attribute + { + return Attribute::make( + get: fn () => $this->status === EmployeeAdvanceStatus::PENDING, + ); + } + + public function createdAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), + ); + } + public function dueDateFormatted(): Attribute { return Attribute::make( @@ -74,13 +112,6 @@ public function dueDateInput(): Attribute ); } - public function statusLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->status?->label(), - ); - } - public function employeeName(): Attribute { return Attribute::make( @@ -89,20 +120,6 @@ public function employeeName(): Attribute ); } - public function rejectionReason(): Attribute - { - return Attribute::make( - get: fn () => $this->rejection?->reason, - ); - } - - public function createdAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), - ); - } - public function isEditable(): Attribute { return Attribute::make( @@ -110,42 +127,27 @@ public function isEditable(): Attribute ); } - public function canVerify(): Attribute + public function rejectionReason(): Attribute { return Attribute::make( - get: fn () => $this->status === EmployeeAdvanceStatus::PENDING, + get: fn () => $this->rejection?->reason, ); } - public function canPay(): Attribute + public function statusLabel(): Attribute { return Attribute::make( - get: fn () => $this->status === EmployeeAdvanceStatus::APPROVED, + get: fn () => $this->status?->label(), ); } - public function employee(): BelongsTo + public static function mediaModuleName(): string { - return $this->belongsTo(Employee::class); + return 'employee-advance'; } - public function cashTransaction(): BelongsTo + public function registerMediaCollections(): void { - return $this->belongsTo(CashTransaction::class); - } - - public function repaymentCashTransaction(): BelongsTo - { - return $this->belongsTo(CashTransaction::class, 'repayment_cash_transaction_id'); - } - - public function verifiedBy(): BelongsTo - { - return $this->belongsTo(User::class, 'verified_by_id'); - } - - public function paidBy(): BelongsTo - { - return $this->belongsTo(User::class, 'paid_by_id'); + $this->addMediaCollection('photos'); } } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 2d397b7..55d2fa8 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -16,20 +17,11 @@ #[Appends(['amount_formatted', 'created_at_formatted', 'created_by_name'])] class Expense extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; use SoftDeletes; - public static function mediaModuleName(): string - { - return 'expense'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('photos'); - } - protected function casts(): array { return [ @@ -37,6 +29,16 @@ protected function casts(): array ]; } + public function cashTransaction(): BelongsTo + { + return $this->belongsTo(CashTransaction::class); + } + + public function createdBy(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + public function amountFormatted(): Attribute { return Attribute::make( @@ -58,13 +60,13 @@ public function createdByName(): Attribute ); } - public function cashTransaction(): BelongsTo + public static function mediaModuleName(): string { - return $this->belongsTo(CashTransaction::class); + return 'expense'; } - public function createdBy(): BelongsTo + public function registerMediaCollections(): void { - return $this->belongsTo(User::class, 'created_by_id'); + $this->addMediaCollection('photos'); } } diff --git a/app/Models/LeaveRequest.php b/app/Models/LeaveRequest.php index b599862..9b88083 100644 --- a/app/Models/LeaveRequest.php +++ b/app/Models/LeaveRequest.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -26,6 +27,7 @@ ])] class LeaveRequest extends Model { + use HasFactory; use HasRejection; use InteractsWithActivityLog; @@ -40,38 +42,27 @@ protected function casts(): array ]; } - public function startDateFormatted(): Attribute + public function employee(): BelongsTo + { + return $this->belongsTo(Employee::class); + } + + public function verifiedBy(): BelongsTo + { + return $this->belongsTo(User::class, 'verified_by_id'); + } + + public function canVerify(): Attribute { return Attribute::make( - get: fn () => $this->start_date?->translatedFormat('l, d F Y'), + get: fn () => $this->status === LeaveRequestStatus::PENDING, ); } - public function endDateFormatted(): Attribute + public function createdAtFormatted(): Attribute { return Attribute::make( - get: fn () => $this->end_date?->translatedFormat('l, d F Y'), - ); - } - - public function startDateInput(): Attribute - { - return Attribute::make( - get: fn () => $this->start_date?->format('Y-m-d'), - ); - } - - public function endDateInput(): Attribute - { - return Attribute::make( - get: fn () => $this->end_date?->format('Y-m-d'), - ); - } - - public function statusLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->status?->label(), + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), ); } @@ -83,17 +74,17 @@ public function employeeName(): Attribute ); } - public function rejectionReason(): Attribute + public function endDateFormatted(): Attribute { return Attribute::make( - get: fn () => $this->rejection?->reason, + get: fn () => $this->end_date?->translatedFormat('l, d F Y'), ); } - public function createdAtFormatted(): Attribute + public function endDateInput(): Attribute { return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), + get: fn () => $this->end_date?->format('Y-m-d'), ); } @@ -104,20 +95,31 @@ public function isEditable(): Attribute ); } - public function canVerify(): Attribute + public function rejectionReason(): Attribute { return Attribute::make( - get: fn () => $this->status === LeaveRequestStatus::PENDING, + get: fn () => $this->rejection?->reason, ); } - public function employee(): BelongsTo + public function startDateFormatted(): Attribute { - return $this->belongsTo(Employee::class); + return Attribute::make( + get: fn () => $this->start_date?->translatedFormat('l, d F Y'), + ); } - public function verifiedBy(): BelongsTo + public function startDateInput(): Attribute { - return $this->belongsTo(User::class, 'verified_by_id'); + return Attribute::make( + get: fn () => $this->start_date?->format('Y-m-d'), + ); + } + + public function statusLabel(): Attribute + { + return Attribute::make( + get: fn () => $this->status?->label(), + ); } } diff --git a/app/Models/Order.php b/app/Models/Order.php index d3a4ebd..4209ff1 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -27,6 +28,7 @@ ])] class Order extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; @@ -43,10 +45,37 @@ protected function casts(): array ]; } - public function subtotalFormatted(): Attribute + public function cashTransaction(): BelongsTo + { + return $this->belongsTo(CashTransaction::class); + } + + public function createdBy(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class); + } + + public function items(): HasMany + { + return $this->hasMany(OrderItem::class); + } + + public function channelLabel(): Attribute { return Attribute::make( - get: fn () => 'Rp '.number_format($this->subtotal, 0, ',', '.'), + get: fn () => $this->channel->label(), + ); + } + + public function createdAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), ); } @@ -71,20 +100,6 @@ public function netAmountFormatted(): Attribute ); } - public function createdAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), - ); - } - - public function channelLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->channel->label(), - ); - } - public function priceTypeLabel(): Attribute { return Attribute::make( @@ -99,23 +114,10 @@ public function statusLabel(): Attribute ); } - public function customer(): BelongsTo + public function subtotalFormatted(): Attribute { - return $this->belongsTo(Customer::class); - } - - public function items(): HasMany - { - return $this->hasMany(OrderItem::class); - } - - public function cashTransaction(): BelongsTo - { - return $this->belongsTo(CashTransaction::class); - } - - public function createdBy(): BelongsTo - { - return $this->belongsTo(User::class, 'created_by_id'); + return Attribute::make( + get: fn () => 'Rp '.number_format($this->subtotal, 0, ',', '.'), + ); } } diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php index ac1bd48..9a362ec 100644 --- a/app/Models/OrderItem.php +++ b/app/Models/OrderItem.php @@ -6,8 +6,10 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; #[Guarded(['id'])] #[Appends([ @@ -18,7 +20,9 @@ ])] class OrderItem extends Model { + use HasFactory; use InteractsWithActivityLog; + use SoftDeletes; protected function casts(): array { @@ -29,6 +33,16 @@ protected function casts(): array ]; } + public function order(): BelongsTo + { + return $this->belongsTo(Order::class); + } + + public function productVariant(): BelongsTo + { + return $this->belongsTo(ProductVariant::class); + } + public function quantityFormatted(): Attribute { return Attribute::make( @@ -43,13 +57,6 @@ public function quantityInput(): Attribute ); } - public function unitPriceFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->unit_price, 0, ',', '.'), - ); - } - public function subtotalFormatted(): Attribute { return Attribute::make( @@ -57,13 +64,10 @@ public function subtotalFormatted(): Attribute ); } - public function order(): BelongsTo + public function unitPriceFormatted(): Attribute { - return $this->belongsTo(Order::class); - } - - public function productVariant(): BelongsTo - { - return $this->belongsTo(ProductVariant::class); + return Attribute::make( + get: fn () => 'Rp '.number_format($this->unit_price, 0, ',', '.'), + ); } } diff --git a/app/Models/Payroll.php b/app/Models/Payroll.php index d48b1ac..35cb491 100644 --- a/app/Models/Payroll.php +++ b/app/Models/Payroll.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -27,6 +28,7 @@ ])] class Payroll extends Model { + use HasFactory; use InteractsWithActivityLog; protected function casts(): array @@ -41,6 +43,31 @@ protected function casts(): array ]; } + public function adjustments(): HasMany + { + return $this->hasMany(PayrollAdjustment::class); + } + + public function cashTransaction(): BelongsTo + { + return $this->belongsTo(CashTransaction::class); + } + + public function employee(): BelongsTo + { + return $this->belongsTo(Employee::class); + } + + public function paidBy(): BelongsTo + { + return $this->belongsTo(User::class, 'paid_by_id'); + } + + public function payrollPeriod(): BelongsTo + { + return $this->belongsTo(PayrollPeriod::class); + } + public function baseSalaryFormatted(): Attribute { return Attribute::make( @@ -55,39 +82,12 @@ public function bonusAmountFormatted(): Attribute ); } - public function deductionAmountFormatted(): Attribute + public function canAdjust(): Attribute { return Attribute::make( - get: fn () => 'Rp '.number_format($this->deduction_amount, 0, ',', '.'), - ); - } - - public function netAmountFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->net_amount, 0, ',', '.'), - ); - } - - public function statusLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->status?->label(), - ); - } - - public function employeeName(): Attribute - { - return Attribute::make( - get: fn () => $this->employee?->user?->profile?->full_name - ?? $this->employee?->user?->username, - ); - } - - public function paidAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->paid_at?->translatedFormat('l, d F Y H:i'), + get: fn () => $this->status === PayrollStatus::UNPAID + && $this->relationLoaded('payrollPeriod') + && $this->payrollPeriod?->isOpen(), ); } @@ -100,38 +100,52 @@ public function canPay(): Attribute ); } - public function canAdjust(): Attribute + public function deductionAmountFormatted(): Attribute { return Attribute::make( - get: fn () => $this->status === PayrollStatus::UNPAID - && $this->relationLoaded('payrollPeriod') - && $this->payrollPeriod?->isOpen(), + get: fn () => 'Rp '.number_format($this->deduction_amount, 0, ',', '.'), ); } - public function payrollPeriod(): BelongsTo + public function employeeName(): Attribute { - return $this->belongsTo(PayrollPeriod::class); + return Attribute::make( + get: fn () => $this->employee?->user?->profile?->full_name + ?? $this->employee?->user?->username, + ); } - public function employee(): BelongsTo + public function netAmountFormatted(): Attribute { - return $this->belongsTo(Employee::class); + return Attribute::make( + get: fn () => 'Rp '.number_format($this->net_amount, 0, ',', '.'), + ); } - public function paidBy(): BelongsTo + public function paidAtFormatted(): Attribute { - return $this->belongsTo(User::class, 'paid_by_id'); + return Attribute::make( + get: fn () => $this->paid_at?->translatedFormat('l, d F Y H:i'), + ); } - public function cashTransaction(): BelongsTo + public function statusLabel(): Attribute { - return $this->belongsTo(CashTransaction::class); + return Attribute::make( + get: fn () => $this->status?->label(), + ); } - public function adjustments(): HasMany + public function calculateKasbonDeduction(): int { - return $this->hasMany(PayrollAdjustment::class); + $outstanding = (int) EmployeeAdvance::query() + ->where('employee_id', $this->employee_id) + ->where('status', EmployeeAdvanceStatus::APPROVED) + ->sum('amount'); + + return min($outstanding, $this->base_salary + (int) $this->adjustments() + ->where('type', PayrollAdjustmentType::BONUS) + ->sum('amount')); } public function recalculateAmounts(): void @@ -150,16 +164,4 @@ public function recalculateAmounts(): void $this->deduction_amount = $kasbonDeduction + $manualDeduction; $this->net_amount = max(0, $this->base_salary + $bonusAmount - $this->deduction_amount); } - - public function calculateKasbonDeduction(): int - { - $outstanding = (int) EmployeeAdvance::query() - ->where('employee_id', $this->employee_id) - ->where('status', EmployeeAdvanceStatus::APPROVED) - ->sum('amount'); - - return min($outstanding, $this->base_salary + (int) $this->adjustments() - ->where('type', PayrollAdjustmentType::BONUS) - ->sum('amount')); - } } diff --git a/app/Models/PayrollAdjustment.php b/app/Models/PayrollAdjustment.php index a7f9b3e..e725f27 100644 --- a/app/Models/PayrollAdjustment.php +++ b/app/Models/PayrollAdjustment.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -14,6 +15,7 @@ #[Appends(['amount_formatted', 'type_label', 'created_at_formatted', 'created_by_name'])] class PayrollAdjustment extends Model { + use HasFactory; use InteractsWithActivityLog; public $timestamps = false; @@ -27,6 +29,16 @@ protected function casts(): array ]; } + public function createdBy(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + + public function payroll(): BelongsTo + { + return $this->belongsTo(Payroll::class); + } + public function amountFormatted(): Attribute { return Attribute::make( @@ -34,13 +46,6 @@ public function amountFormatted(): Attribute ); } - public function typeLabel(): Attribute - { - return Attribute::make( - get: fn () => $this->type?->label(), - ); - } - public function createdAtFormatted(): Attribute { return Attribute::make( @@ -55,13 +60,10 @@ public function createdByName(): Attribute ); } - public function payroll(): BelongsTo + public function typeLabel(): Attribute { - return $this->belongsTo(Payroll::class); - } - - public function createdBy(): BelongsTo - { - return $this->belongsTo(User::class, 'created_by_id'); + return Attribute::make( + get: fn () => $this->type?->label(), + ); } } diff --git a/app/Models/PayrollPeriod.php b/app/Models/PayrollPeriod.php index 21af811..f8da60e 100644 --- a/app/Models/PayrollPeriod.php +++ b/app/Models/PayrollPeriod.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -16,6 +17,7 @@ #[Appends(['period_label', 'status_label', 'closed_at_formatted'])] class PayrollPeriod extends Model { + use HasFactory; use InteractsWithActivityLog; protected function casts(): array @@ -26,6 +28,23 @@ protected function casts(): array ]; } + public function closedBy(): BelongsTo + { + return $this->belongsTo(User::class, 'closed_by_id'); + } + + public function payrolls(): HasMany + { + return $this->hasMany(Payroll::class); + } + + public function closedAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->closed_at?->translatedFormat('l, d F Y H:i'), + ); + } + public function periodLabel(): Attribute { return Attribute::make( @@ -40,23 +59,6 @@ public function statusLabel(): Attribute ); } - public function closedAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->closed_at?->translatedFormat('l, d F Y H:i'), - ); - } - - public function payrolls(): HasMany - { - return $this->hasMany(Payroll::class); - } - - public function closedBy(): BelongsTo - { - return $this->belongsTo(User::class, 'closed_by_id'); - } - public function isOpen(): bool { return $this->status === PayrollPeriodStatus::OPEN; diff --git a/app/Models/Product.php b/app/Models/Product.php index 9ae9209..b1f172d 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -4,6 +4,7 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -14,6 +15,7 @@ #[Sluggable(from: 'name', to: 'slug')] class Product extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; diff --git a/app/Models/ProductPrice.php b/app/Models/ProductPrice.php index 3541650..a64c2f0 100644 --- a/app/Models/ProductPrice.php +++ b/app/Models/ProductPrice.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -14,6 +15,7 @@ #[Appends(['price_formatted', 'price_input', 'type_label'])] class ProductPrice extends Model { + use HasFactory; use InteractsWithActivityLog; protected function casts(): array @@ -24,6 +26,11 @@ protected function casts(): array ]; } + public function variant(): BelongsTo + { + return $this->belongsTo(ProductVariant::class, 'variant_id'); + } + public function priceFormatted(): Attribute { return Attribute::make( @@ -44,9 +51,4 @@ public function typeLabel(): Attribute get: fn () => $this->type->label(), ); } - - public function variant(): BelongsTo - { - return $this->belongsTo(ProductVariant::class, 'variant_id'); - } } diff --git a/app/Models/ProductVariant.php b/app/Models/ProductVariant.php index dd53001..52173ad 100644 --- a/app/Models/ProductVariant.php +++ b/app/Models/ProductVariant.php @@ -5,6 +5,7 @@ use App\Models\Concerns\HasModuleMedia; use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -14,10 +15,38 @@ #[Guarded(['id'])] class ProductVariant extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; use SoftDeletes; + protected function casts(): array + { + return [ + 'stock' => 'integer', + ]; + } + + public function cuttingResults(): HasMany + { + return $this->hasMany(CuttingResult::class); + } + + public function orderItems(): HasMany + { + return $this->hasMany(OrderItem::class); + } + + public function prices(): HasMany + { + return $this->hasMany(ProductPrice::class, 'variant_id'); + } + + public function product(): BelongsTo + { + return $this->belongsTo(Product::class); + } + public static function mediaModuleName(): string { return 'product'; @@ -27,31 +56,4 @@ public function registerMediaCollections(): void { $this->addMediaCollection('images'); } - - protected function casts(): array - { - return [ - 'stock' => 'integer', - ]; - } - - public function product(): BelongsTo - { - return $this->belongsTo(Product::class); - } - - public function prices(): HasMany - { - return $this->hasMany(ProductPrice::class, 'variant_id'); - } - - public function orderItems(): HasMany - { - return $this->hasMany(OrderItem::class); - } - - public function cuttingResults(): HasMany - { - return $this->hasMany(CuttingResult::class); - } } diff --git a/app/Models/Purchase.php b/app/Models/Purchase.php index d1acb0c..545a543 100644 --- a/app/Models/Purchase.php +++ b/app/Models/Purchase.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -22,20 +23,11 @@ ])] class Purchase extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; use SoftDeletes; - public static function mediaModuleName(): string - { - return 'purchase'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('photos'); - } - protected function casts(): array { return [ @@ -45,39 +37,6 @@ protected function casts(): array ]; } - public function subtotalFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->subtotal, 0, ',', '.'), - ); - } - - public function discountFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->discount, 0, ',', '.'), - ); - } - - public function totalFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->total, 0, ',', '.'), - ); - } - - public function createdAtFormatted(): Attribute - { - return Attribute::make( - get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), - ); - } - - public function supplier(): BelongsTo - { - return $this->belongsTo(Supplier::class); - } - public function createdBy(): BelongsTo { return $this->belongsTo(User::class, 'created_by_id'); @@ -87,4 +46,47 @@ public function items(): HasMany { return $this->hasMany(PurchaseItem::class); } + + public function supplier(): BelongsTo + { + return $this->belongsTo(Supplier::class); + } + + public function createdAtFormatted(): Attribute + { + return Attribute::make( + get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), + ); + } + + public function discountFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->discount, 0, ',', '.'), + ); + } + + public function subtotalFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->subtotal, 0, ',', '.'), + ); + } + + public function totalFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->total, 0, ',', '.'), + ); + } + + public static function mediaModuleName(): string + { + return 'purchase'; + } + + public function registerMediaCollections(): void + { + $this->addMediaCollection('photos'); + } } diff --git a/app/Models/PurchaseItem.php b/app/Models/PurchaseItem.php index fba1825..cc21683 100644 --- a/app/Models/PurchaseItem.php +++ b/app/Models/PurchaseItem.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -20,6 +21,7 @@ ])] class PurchaseItem extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; @@ -32,6 +34,16 @@ protected function casts(): array ]; } + public function purchase(): BelongsTo + { + return $this->belongsTo(Purchase::class); + } + + public function rawMaterialPrice(): BelongsTo + { + return $this->belongsTo(RawMaterialPrice::class); + } + public function quantityFormatted(): Attribute { return Attribute::make( @@ -50,13 +62,6 @@ public function quantityInput(): Attribute ); } - public function unitPriceFormatted(): Attribute - { - return Attribute::make( - get: fn () => 'Rp '.number_format($this->unit_price, 0, ',', '.'), - ); - } - public function subtotalFormatted(): Attribute { return Attribute::make( @@ -71,13 +76,10 @@ public function unitAbbreviation(): Attribute ); } - public function purchase(): BelongsTo + public function unitPriceFormatted(): Attribute { - return $this->belongsTo(Purchase::class); - } - - public function rawMaterialPrice(): BelongsTo - { - return $this->belongsTo(RawMaterialPrice::class); + return Attribute::make( + get: fn () => 'Rp '.number_format($this->unit_price, 0, ',', '.'), + ); } } diff --git a/app/Models/RawMaterial.php b/app/Models/RawMaterial.php index c053ce7..caff4f1 100644 --- a/app/Models/RawMaterial.php +++ b/app/Models/RawMaterial.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -17,6 +18,7 @@ #[Appends(['unit_label', 'unit_abbreviation'])] class RawMaterial extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; @@ -40,11 +42,9 @@ public function inactive(Builder $query): void $query->where('is_active', false); } - public function unitLabel(): Attribute + public function prices(): HasMany { - return Attribute::make( - get: fn () => $this->unit->label(), - ); + return $this->hasMany(RawMaterialPrice::class); } public function unitAbbreviation(): Attribute @@ -54,8 +54,10 @@ public function unitAbbreviation(): Attribute ); } - public function prices(): HasMany + public function unitLabel(): Attribute { - return $this->hasMany(RawMaterialPrice::class); + return Attribute::make( + get: fn () => $this->unit->label(), + ); } } diff --git a/app/Models/RawMaterialPrice.php b/app/Models/RawMaterialPrice.php index 00e97f8..62c035b 100644 --- a/app/Models/RawMaterialPrice.php +++ b/app/Models/RawMaterialPrice.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -17,20 +18,11 @@ #[Appends(['price_formatted', 'stock_formatted', 'price_input', 'stock_input'])] class RawMaterialPrice extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; use SoftDeletes; - public static function mediaModuleName(): string - { - return 'raw-material'; - } - - public function registerMediaCollections(): void - { - $this->addMediaCollection('images'); - } - protected function casts(): array { return [ @@ -39,6 +31,16 @@ protected function casts(): array ]; } + public function cuttingMaterials(): HasMany + { + return $this->hasMany(CuttingMaterial::class); + } + + public function rawMaterial(): BelongsTo + { + return $this->belongsTo(RawMaterial::class); + } + public function priceFormatted(): Attribute { return Attribute::make( @@ -46,6 +48,13 @@ public function priceFormatted(): Attribute ); } + public function priceInput(): Attribute + { + return Attribute::make( + get: fn () => (string) (int) $this->price, + ); + } + public function stockFormatted(): Attribute { return Attribute::make( @@ -57,13 +66,6 @@ public function stockFormatted(): Attribute ); } - public function priceInput(): Attribute - { - return Attribute::make( - get: fn () => (string) (int) $this->price, - ); - } - public function stockInput(): Attribute { return Attribute::make( @@ -71,13 +73,13 @@ public function stockInput(): Attribute ); } - public function rawMaterial(): BelongsTo + public static function mediaModuleName(): string { - return $this->belongsTo(RawMaterial::class); + return 'raw-material'; } - public function cuttingMaterials(): HasMany + public function registerMediaCollections(): void { - return $this->hasMany(CuttingMaterial::class); + $this->addMediaCollection('images'); } } diff --git a/app/Models/Rejection.php b/app/Models/Rejection.php index 30904d4..10a9175 100644 --- a/app/Models/Rejection.php +++ b/app/Models/Rejection.php @@ -4,6 +4,7 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -11,6 +12,7 @@ #[Guarded(['id'])] class Rejection extends Model { + use HasFactory; use InteractsWithActivityLog; public function rejectable(): MorphTo diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index 570c4ea..a7e50ac 100644 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -4,6 +4,7 @@ use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -11,6 +12,7 @@ #[Guarded(['id'])] class Supplier extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; diff --git a/app/Models/SystemConfiguration.php b/app/Models/SystemConfiguration.php index a90ed6d..5212dc0 100644 --- a/app/Models/SystemConfiguration.php +++ b/app/Models/SystemConfiguration.php @@ -5,12 +5,14 @@ use App\Models\Concerns\HasModuleMedia; use App\Models\Concerns\InteractsWithActivityLog; use Illuminate\Database\Eloquent\Attributes\Guarded; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Spatie\MediaLibrary\HasMedia; #[Guarded(['id'])] class SystemConfiguration extends Model implements HasMedia { + use HasFactory; use HasModuleMedia; use InteractsWithActivityLog; diff --git a/app/Models/User.php b/app/Models/User.php index 0477acb..c85c0a6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,7 +24,12 @@ #[Appends(['role_label'])] class User extends Authenticatable { - use CausesActivity, HasFactory, HasRoles, InteractsWithActivityLog, Notifiable, SoftDeletes; + use CausesActivity; + use HasFactory; + use HasRoles; + use InteractsWithActivityLog; + use Notifiable; + use SoftDeletes; protected function casts(): array { @@ -47,6 +52,36 @@ public function inactive(Builder $query): void $query->where('is_active', false); } + public function cashTransactions(): HasMany + { + return $this->hasMany(CashTransaction::class, 'created_by_id'); + } + + public function employee(): HasOne + { + return $this->hasOne(Employee::class); + } + + public function expenses(): HasMany + { + return $this->hasMany(Expense::class, 'created_by_id'); + } + + public function orders(): HasMany + { + return $this->hasMany(Order::class, 'created_by_id'); + } + + public function profile(): HasOne + { + return $this->hasOne(UserProfile::class); + } + + public function purchases(): HasMany + { + return $this->hasMany(Purchase::class, 'created_by_id'); + } + public function roleLabel(): Attribute { return Attribute::make( @@ -57,34 +92,4 @@ public function roleLabel(): Attribute }, ); } - - public function profile(): HasOne - { - return $this->hasOne(UserProfile::class); - } - - public function employee(): HasOne - { - return $this->hasOne(Employee::class); - } - - public function cashTransactions(): HasMany - { - return $this->hasMany(CashTransaction::class, 'created_by_id'); - } - - public function expenses(): HasMany - { - return $this->hasMany(Expense::class, 'created_by_id'); - } - - public function purchases(): HasMany - { - return $this->hasMany(Purchase::class, 'created_by_id'); - } - - public function orders(): HasMany - { - return $this->hasMany(Order::class, 'created_by_id'); - } } diff --git a/app/Models/UserProfile.php b/app/Models/UserProfile.php index a5f8ef9..fcc2d6f 100644 --- a/app/Models/UserProfile.php +++ b/app/Models/UserProfile.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Attributes\Appends; use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -16,6 +17,7 @@ #[Appends(['birth_date_formatted', 'birth_date_input', 'gender_label'])] class UserProfile extends Model { + use HasFactory; use InteractsWithActivityLog; use SoftDeletes; @@ -27,29 +29,29 @@ protected function casts(): array ]; } - protected function birthDateFormatted(): Attribute + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function birthDateFormatted(): Attribute { return Attribute::make( get: fn () => Carbon::parse($this->birth_date)->format('l, d F Y'), ); } - protected function birthDateInput(): Attribute + public function birthDateInput(): Attribute { return Attribute::make( get: fn () => $this->birth_date?->format('Y-m-d'), ); } - protected function genderLabel(): Attribute + public function genderLabel(): Attribute { return Attribute::make( get: fn () => $this->gender?->label(), ); } - - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } } diff --git a/app/Services/Account/ProfileService.php b/app/Services/Account/ProfileService.php index 50f2da6..b48f48e 100644 --- a/app/Services/Account/ProfileService.php +++ b/app/Services/Account/ProfileService.php @@ -2,14 +2,16 @@ namespace App\Services\Account; +use App\Models\User; use Illuminate\Support\Facades\DB; class ProfileService { - public function update(array $validated): void + /** + * @param array $validated + */ + public function update(array $validated, User $user): void { - $user = auth()->user(); - DB::transaction(function () use ($user, $validated): void { $user->email = $validated['email']; $user->username = $validated['username']; diff --git a/app/Services/Concerns/ResolvesAuthEmployee.php b/app/Services/Concerns/ResolvesAuthEmployee.php new file mode 100644 index 0000000..a1a979b --- /dev/null +++ b/app/Services/Concerns/ResolvesAuthEmployee.php @@ -0,0 +1,23 @@ +user())?->employee; + + if ($employee === null) { + throw ValidationException::withMessages([ + 'employee' => 'Akun Anda tidak terhubung ke data pegawai.', + ]); + } + + return $employee; + } +} diff --git a/app/Services/Finance/EmployeeAdvanceService.php b/app/Services/Finance/EmployeeAdvanceService.php index a88716b..fe75d2c 100644 --- a/app/Services/Finance/EmployeeAdvanceService.php +++ b/app/Services/Finance/EmployeeAdvanceService.php @@ -4,6 +4,8 @@ use App\Enums\EmployeeAdvanceStatus; use App\Models\EmployeeAdvance; +use App\Models\User; +use App\Services\Concerns\ResolvesAuthEmployee; use App\Services\Media\MediaService; use App\Support\Media\MediaPresenter; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -13,6 +15,8 @@ class EmployeeAdvanceService { + use ResolvesAuthEmployee; + private const MAX_PHOTOS = 1; public function __construct( @@ -75,15 +79,9 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator /** * @param array{amount: int, description: string, due_date: string} $validated */ - public function create(array $validated): void + public function create(array $validated, User $user): void { - $employee = auth()->user()?->employee; - - if ($employee === null) { - throw ValidationException::withMessages([ - 'employee' => 'Akun Anda tidak terhubung ke data pegawai.', - ]); - } + $employee = $this->resolveAuthEmployee($user); DB::transaction(function () use ($validated, $employee): void { $employeeAdvance = EmployeeAdvance::create([ @@ -101,9 +99,9 @@ public function create(array $validated): void /** * @param array{amount: int, description: string, due_date: string} $validated */ - public function update(EmployeeAdvance $employeeAdvance, array $validated): void + public function update(EmployeeAdvance $employeeAdvance, array $validated, User $user): void { - $this->ensureOwnedBySubmitter($employeeAdvance); + $this->ensureOwnedBySubmitter($employeeAdvance, $user); $this->ensurePending($employeeAdvance, 'Kasbon hanya dapat diubah saat status menunggu.'); DB::transaction(function () use ($employeeAdvance, $validated): void { @@ -116,21 +114,19 @@ public function update(EmployeeAdvance $employeeAdvance, array $validated): void }); } - public function delete(EmployeeAdvance $employeeAdvance): void + public function delete(EmployeeAdvance $employeeAdvance, User $user): void { - $this->ensureOwnedBySubmitter($employeeAdvance); + $this->ensureOwnedBySubmitter($employeeAdvance, $user); $this->ensurePending($employeeAdvance, 'Kasbon hanya dapat dihapus saat status menunggu.'); $employeeAdvance->clearMediaCollection('photos'); $employeeAdvance->delete(); } - public function approve(EmployeeAdvance $employeeAdvance): void + public function approve(EmployeeAdvance $employeeAdvance, User $user): void { $this->ensurePending($employeeAdvance, 'Kasbon ini sudah diverifikasi.'); - $user = auth()->user(); - DB::transaction(function () use ($employeeAdvance, $user): void { $employeeAdvance->loadMissing('employee.user.profile'); @@ -154,12 +150,10 @@ public function approve(EmployeeAdvance $employeeAdvance): void }); } - public function reject(EmployeeAdvance $employeeAdvance, string $reason): void + public function reject(EmployeeAdvance $employeeAdvance, string $reason, User $user): void { $this->ensurePending($employeeAdvance, 'Kasbon ini sudah diverifikasi.'); - $user = auth()->user(); - DB::transaction(function () use ($employeeAdvance, $user, $reason): void { $employeeAdvance->status = EmployeeAdvanceStatus::REJECTED; $employeeAdvance->verified_at = now(); @@ -173,7 +167,7 @@ public function reject(EmployeeAdvance $employeeAdvance, string $reason): void }); } - public function pay(EmployeeAdvance $employeeAdvance): void + public function pay(EmployeeAdvance $employeeAdvance, User $user): void { if ($employeeAdvance->status !== EmployeeAdvanceStatus::APPROVED) { throw ValidationException::withMessages([ @@ -181,8 +175,6 @@ public function pay(EmployeeAdvance $employeeAdvance): void ]); } - $user = auth()->user(); - DB::transaction(function () use ($employeeAdvance, $user): void { $employeeAdvance->loadMissing('employee.user.profile'); @@ -222,9 +214,9 @@ private function syncPhotos(EmployeeAdvance $employeeAdvance, array $validated): ); } - private function ensureOwnedBySubmitter(EmployeeAdvance $employeeAdvance): void + private function ensureOwnedBySubmitter(EmployeeAdvance $employeeAdvance, User $user): void { - if (auth()->user()?->employee?->id !== $employeeAdvance->employee_id) { + if ($user->employee?->id !== $employeeAdvance->employee_id) { throw ValidationException::withMessages([ 'employee_advance' => 'Anda tidak memiliki akses untuk mengubah kasbon ini.', ]); diff --git a/app/Services/Hr/AttendanceService.php b/app/Services/Hr/AttendanceService.php index cbbcf65..2a06f85 100644 --- a/app/Services/Hr/AttendanceService.php +++ b/app/Services/Hr/AttendanceService.php @@ -4,6 +4,8 @@ use App\Models\Attendance; use App\Models\Employee; +use App\Models\User; +use App\Services\Concerns\ResolvesAuthEmployee; use App\Services\Media\MediaService; use Carbon\CarbonInterface; use Illuminate\Database\Eloquent\Builder; @@ -13,6 +15,8 @@ class AttendanceService { + use ResolvesAuthEmployee; + public function __construct( private readonly MediaService $mediaService, ) {} @@ -55,9 +59,9 @@ public function todayAttendanceForEmployee(Employee $employee): ?array /** * @param array{photo: string, latitude: float, longitude: float, location_tag: string} $validated */ - public function checkIn(array $validated): void + public function checkIn(array $validated, User $user): void { - $employee = $this->resolveAuthEmployee(); + $employee = $this->resolveAuthEmployee($user); $existing = Attendance::query() ->where('employee_id', $employee->id) @@ -96,9 +100,9 @@ public function checkIn(array $validated): void /** * @param array{photo: string, latitude: float, longitude: float, location_tag: string} $validated */ - public function checkOut(array $validated): void + public function checkOut(array $validated, User $user): void { - $employee = $this->resolveAuthEmployee(); + $employee = $this->resolveAuthEmployee($user); $attendance = Attendance::query() ->where('employee_id', $employee->id) @@ -148,19 +152,6 @@ public function delete(Attendance $attendance): void $attendance->delete(); } - private function resolveAuthEmployee(): Employee - { - $employee = auth()->user()?->employee; - - if ($employee === null) { - throw ValidationException::withMessages([ - 'employee' => 'Akun Anda tidak terhubung ke data pegawai.', - ]); - } - - return $employee; - } - private function resolveLocationTag(string $clientTag, float $latitude, float $longitude): string { if ($clientTag !== '') { diff --git a/app/Services/Hr/LeaveRequestService.php b/app/Services/Hr/LeaveRequestService.php index 8213e1d..3aecccc 100644 --- a/app/Services/Hr/LeaveRequestService.php +++ b/app/Services/Hr/LeaveRequestService.php @@ -4,6 +4,8 @@ use App\Enums\LeaveRequestStatus; use App\Models\LeaveRequest; +use App\Models\User; +use App\Services\Concerns\ResolvesAuthEmployee; use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; @@ -12,6 +14,8 @@ class LeaveRequestService { + use ResolvesAuthEmployee; + /** * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery */ @@ -38,15 +42,9 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator /** * @param array{start_date: string, end_date: string} $validated */ - public function create(array $validated): void + public function create(array $validated, User $user): void { - $employee = auth()->user()?->employee; - - if ($employee === null) { - throw ValidationException::withMessages([ - 'employee' => 'Akun Anda tidak terhubung ke data pegawai.', - ]); - } + $employee = $this->resolveAuthEmployee($user); $startDate = Carbon::parse($validated['start_date'])->startOfDay(); $endDate = Carbon::parse($validated['end_date'])->startOfDay(); @@ -66,9 +64,9 @@ public function create(array $validated): void /** * @param array{start_date: string, end_date: string} $validated */ - public function update(LeaveRequest $leaveRequest, array $validated): void + public function update(LeaveRequest $leaveRequest, array $validated, User $user): void { - $this->ensureOwnedBySubmitter($leaveRequest); + $this->ensureOwnedBySubmitter($leaveRequest, $user); $this->ensurePending($leaveRequest, 'Pengajuan cuti hanya dapat diubah saat status menunggu.'); $startDate = Carbon::parse($validated['start_date'])->startOfDay(); @@ -83,20 +81,18 @@ public function update(LeaveRequest $leaveRequest, array $validated): void $leaveRequest->save(); } - public function delete(LeaveRequest $leaveRequest): void + public function delete(LeaveRequest $leaveRequest, User $user): void { - $this->ensureOwnedBySubmitter($leaveRequest); + $this->ensureOwnedBySubmitter($leaveRequest, $user); $this->ensurePending($leaveRequest, 'Pengajuan cuti hanya dapat dihapus saat status menunggu.'); $leaveRequest->delete(); } - public function approve(LeaveRequest $leaveRequest): void + public function approve(LeaveRequest $leaveRequest, User $user): void { $this->ensurePending($leaveRequest, 'Pengajuan cuti ini sudah diverifikasi.'); - $user = auth()->user(); - DB::transaction(function () use ($leaveRequest, $user): void { $leaveRequest->status = LeaveRequestStatus::APPROVED; $leaveRequest->verified_at = now(); @@ -105,12 +101,10 @@ public function approve(LeaveRequest $leaveRequest): void }); } - public function reject(LeaveRequest $leaveRequest, string $reason): void + public function reject(LeaveRequest $leaveRequest, string $reason, User $user): void { $this->ensurePending($leaveRequest, 'Pengajuan cuti ini sudah diverifikasi.'); - $user = auth()->user(); - DB::transaction(function () use ($leaveRequest, $user, $reason): void { $leaveRequest->status = LeaveRequestStatus::REJECTED; $leaveRequest->verified_at = now(); @@ -149,9 +143,9 @@ private function ensureValidDateRange(Carbon $startDate, Carbon $endDate): void } } - private function ensureOwnedBySubmitter(LeaveRequest $leaveRequest): void + private function ensureOwnedBySubmitter(LeaveRequest $leaveRequest, User $user): void { - if (auth()->user()?->employee?->id !== $leaveRequest->employee_id) { + if ($user->employee?->id !== $leaveRequest->employee_id) { throw ValidationException::withMessages([ 'leave_request' => 'Anda tidak memiliki akses untuk mengubah pengajuan cuti ini.', ]); diff --git a/app/Services/Manage/CuttingService.php b/app/Services/Manage/CuttingService.php index 78f598d..52d852a 100644 --- a/app/Services/Manage/CuttingService.php +++ b/app/Services/Manage/CuttingService.php @@ -209,7 +209,7 @@ public function update(Cutting $cutting, array $validated): void { if (! $cutting->status->isEditable()) { throw ValidationException::withMessages([ - 'status' => 'Cutting tidak dapat diubah.', + 'status' => 'Proses potong tidak dapat diubah.', ]); } @@ -241,7 +241,7 @@ public function delete(Cutting $cutting): void { if ($cutting->status !== CuttingStatus::IN_PROGRESS) { throw ValidationException::withMessages([ - 'status' => 'Cutting hanya dapat dihapus saat masih proses.', + 'status' => 'Proses potong hanya dapat dihapus saat masih proses.', ]); } @@ -252,15 +252,15 @@ public function delete(Cutting $cutting): void }); } - public function transitionStatus(Cutting $cutting, CuttingStatus $status, ?string $reason = null): void + public function transitionStatus(Cutting $cutting, CuttingStatus $status, User $user, ?string $reason = null): void { if (! $cutting->status->canTransitionTo($status)) { throw ValidationException::withMessages([ - 'status' => 'Status cutting tidak dapat diubah.', + 'status' => 'Status proses potong tidak dapat diubah.', ]); } - DB::transaction(function () use ($cutting, $status, $reason): void { + DB::transaction(function () use ($cutting, $status, $reason, $user): void { $cutting->load(['materials', 'results']); if ($status === CuttingStatus::COMPLETED) { @@ -273,7 +273,7 @@ public function transitionStatus(Cutting $cutting, CuttingStatus $status, ?strin if ($status === CuttingStatus::REJECTED) { $this->reverseMaterialStock($cutting); - $this->storeRejection($cutting, $reason); + $this->storeRejection($cutting, $reason, $user); } if ($status === CuttingStatus::IN_PROGRESS && $cutting->status === CuttingStatus::REJECTED) { @@ -347,7 +347,7 @@ private function buildResults(array $results): array if ($cuttingResult < 1) { throw ValidationException::withMessages([ - "results.{$index}.cutting_result" => 'Hasil cutting minimal 1 pcs.', + "results.{$index}.cutting_result" => 'Hasil potong minimal 1 pcs.', ]); } @@ -365,7 +365,7 @@ private function buildResults(array $results): array if (($warehouseStock + $cuttingReject) !== $cuttingResult) { throw ValidationException::withMessages([ - "results.{$index}.cutting_result" => 'Hasil cutting harus sama dengan stok gudang ditambah reject.', + "results.{$index}.cutting_result" => 'Hasil potong harus sama dengan stok gudang ditambah reject.', ]); } @@ -427,7 +427,7 @@ private function applyProductStockOnVerify(Cutting $cutting): void } } - private function storeRejection(Cutting $cutting, ?string $reason): void + private function storeRejection(Cutting $cutting, ?string $reason, User $user): void { if ($reason === null || trim($reason) === '') { throw ValidationException::withMessages([ @@ -435,8 +435,6 @@ private function storeRejection(Cutting $cutting, ?string $reason): void ]); } - $user = auth()->user(); - $cutting->rejection()?->delete(); $cutting->rejection()->create([ diff --git a/app/Services/Manage/PurchaseService.php b/app/Services/Manage/PurchaseService.php index 09c4e56..bcb6b93 100644 --- a/app/Services/Manage/PurchaseService.php +++ b/app/Services/Manage/PurchaseService.php @@ -18,7 +18,7 @@ class PurchaseService { - private const MAX_PHOTOS_FILES = 1; + private const MAX_PHOTOS = 1; public function __construct( private readonly MediaService $mediaService, @@ -262,7 +262,7 @@ private function syncPhotos(Purchase $purchase, array $validated): void 'photos', $validated['photos'] ?? null, $validated['remove_media_ids'] ?? null, - self::MAX_PHOTOS_FILES, + self::MAX_PHOTOS, required: false, errorKey: 'photos', ); diff --git a/app/Services/Master/CategoryService.php b/app/Services/Master/CategoryService.php index 9184ea7..fddd639 100644 --- a/app/Services/Master/CategoryService.php +++ b/app/Services/Master/CategoryService.php @@ -41,8 +41,7 @@ public function create(array $validated): void */ public function update(Category $category, array $validated): void { - $category->name = $validated['name']; - $category->save(); + $category->fill($validated)->save(); } public function delete(Category $category): void diff --git a/app/Services/Master/CustomerService.php b/app/Services/Master/CustomerService.php index 5c1f3ae..5fb7a4e 100644 --- a/app/Services/Master/CustomerService.php +++ b/app/Services/Master/CustomerService.php @@ -43,10 +43,7 @@ public function create(array $validated): void */ public function update(Customer $customer, array $validated): void { - $customer->name = $validated['name']; - $customer->phone_number = $validated['phone_number']; - $customer->address = $validated['address']; - $customer->save(); + $customer->fill($validated)->save(); } public function delete(Customer $customer): void diff --git a/app/Services/Master/SupplierService.php b/app/Services/Master/SupplierService.php index 81769db..b7b3bef 100644 --- a/app/Services/Master/SupplierService.php +++ b/app/Services/Master/SupplierService.php @@ -43,10 +43,7 @@ public function create(array $validated): void */ public function update(Supplier $supplier, array $validated): void { - $supplier->name = $validated['name']; - $supplier->phone_number = $validated['phone_number']; - $supplier->address = $validated['address']; - $supplier->save(); + $supplier->fill($validated)->save(); } public function delete(Supplier $supplier): void diff --git a/database/factories/AttendanceFactory.php b/database/factories/AttendanceFactory.php new file mode 100644 index 0000000..06838a7 --- /dev/null +++ b/database/factories/AttendanceFactory.php @@ -0,0 +1,47 @@ + + */ +class AttendanceFactory extends Factory +{ + public function definition(): array + { + $checkInAt = fake()->dateTimeBetween('-30 days', 'now'); + $checkOutAt = (clone $checkInAt)->modify('+'.fake()->numberBetween(4, 9).' hours'); + + return [ + 'employee_id' => Employee::factory(), + 'attendance_date' => $checkInAt->format('Y-m-d'), + 'check_in_at' => $checkInAt, + 'check_out_at' => $checkOutAt, + 'check_in_photo_path' => 'attendance/checkin/'.fake()->uuid().'.jpg', + 'check_out_photo_path' => 'attendance/checkout/'.fake()->uuid().'.jpg', + 'check_in_latitude' => fake()->latitude(-7, -6), + 'check_in_longitude' => fake()->longitude(106, 107), + 'check_out_latitude' => fake()->latitude(-7, -6), + 'check_out_longitude' => fake()->longitude(106, 107), + 'check_in_location_tag' => fake()->optional()->city(), + 'check_out_location_tag' => fake()->optional()->city(), + 'work_duration_minutes' => fake()->numberBetween(240, 540), + ]; + } + + public function checkedInOnly(): static + { + return $this->state(fn (array $attributes) => [ + 'check_out_at' => null, + 'check_out_photo_path' => null, + 'check_out_latitude' => null, + 'check_out_longitude' => null, + 'check_out_location_tag' => null, + 'work_duration_minutes' => null, + ]); + } +} diff --git a/database/factories/CashAccountFactory.php b/database/factories/CashAccountFactory.php new file mode 100644 index 0000000..2ece2be --- /dev/null +++ b/database/factories/CashAccountFactory.php @@ -0,0 +1,22 @@ + + */ +class CashAccountFactory extends Factory +{ + public function definition(): array + { + return [ + 'created_by_id' => User::factory(), + 'name' => fake()->randomElement(['Kas Toko', 'Kas Operasional', 'Kas Utama']), + 'balance' => 0, + ]; + } +} diff --git a/database/factories/CashTransactionFactory.php b/database/factories/CashTransactionFactory.php new file mode 100644 index 0000000..4c799b1 --- /dev/null +++ b/database/factories/CashTransactionFactory.php @@ -0,0 +1,37 @@ + + */ +class CashTransactionFactory extends Factory +{ + public function definition(): array + { + $amount = fake()->numberBetween(10_000, 5_000_000); + + return [ + 'cash_account_id' => CashAccount::factory(), + 'created_by_id' => User::factory(), + 'reference_type' => null, + 'reference_id' => null, + 'amount' => $amount, + 'balance_after' => $amount, + 'description' => fake()->sentence(3), + ]; + } + + public function deposit(): static + { + return $this->state(fn (array $attributes) => [ + 'reference_type' => null, + 'reference_id' => null, + ]); + } +} diff --git a/database/factories/CategoryFactory.php b/database/factories/CategoryFactory.php new file mode 100644 index 0000000..51e3e7e --- /dev/null +++ b/database/factories/CategoryFactory.php @@ -0,0 +1,23 @@ + + */ +class CategoryFactory extends Factory +{ + public function definition(): array + { + $name = fake()->unique()->words(2, true); + + return [ + 'name' => Str::limit($name, 50, ''), + 'slug' => Str::slug($name), + ]; + } +} diff --git a/database/factories/CustomerFactory.php b/database/factories/CustomerFactory.php new file mode 100644 index 0000000..26844d1 --- /dev/null +++ b/database/factories/CustomerFactory.php @@ -0,0 +1,21 @@ + + */ +class CustomerFactory extends Factory +{ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'phone_number' => fake()->numerify('08##########'), + 'address' => fake()->optional()->address(), + ]; + } +} diff --git a/database/factories/CuttingFactory.php b/database/factories/CuttingFactory.php new file mode 100644 index 0000000..e0c4b80 --- /dev/null +++ b/database/factories/CuttingFactory.php @@ -0,0 +1,37 @@ + + */ +class CuttingFactory extends Factory +{ + public function definition(): array + { + return [ + 'status' => CuttingStatus::IN_PROGRESS->value, + 'description' => fake()->optional()->sentence(3), + 'created_by_id' => User::factory(), + ]; + } + + public function completed(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => CuttingStatus::COMPLETED->value, + ]); + } + + public function verified(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => CuttingStatus::VERIFIED->value, + ]); + } +} diff --git a/database/factories/CuttingMaterialFactory.php b/database/factories/CuttingMaterialFactory.php new file mode 100644 index 0000000..dd0c2eb --- /dev/null +++ b/database/factories/CuttingMaterialFactory.php @@ -0,0 +1,27 @@ + + */ +class CuttingMaterialFactory extends Factory +{ + public function definition(): array + { + $materialUsage = fake()->randomFloat(4, 1, 25); + $remainingMaterial = fake()->randomFloat(4, 0, 10); + + return [ + 'cutting_id' => Cutting::factory(), + 'raw_material_price_id' => RawMaterialPrice::factory(), + 'material_usage' => $materialUsage, + 'remaining_material' => $remainingMaterial, + ]; + } +} diff --git a/database/factories/CuttingResultFactory.php b/database/factories/CuttingResultFactory.php new file mode 100644 index 0000000..1cb861e --- /dev/null +++ b/database/factories/CuttingResultFactory.php @@ -0,0 +1,28 @@ + + */ +class CuttingResultFactory extends Factory +{ + public function definition(): array + { + $cuttingResult = fake()->numberBetween(10, 200); + $cuttingReject = fake()->numberBetween(0, (int) ($cuttingResult * 0.1)); + + return [ + 'cutting_id' => Cutting::factory(), + 'product_variant_id' => ProductVariant::factory(), + 'cutting_result' => $cuttingResult, + 'warehouse_stock' => $cuttingResult - $cuttingReject, + 'cutting_reject' => $cuttingReject, + ]; + } +} diff --git a/database/factories/EmployeeAdvanceFactory.php b/database/factories/EmployeeAdvanceFactory.php new file mode 100644 index 0000000..f4390bc --- /dev/null +++ b/database/factories/EmployeeAdvanceFactory.php @@ -0,0 +1,50 @@ + + */ +class EmployeeAdvanceFactory extends Factory +{ + public function definition(): array + { + return [ + 'employee_id' => Employee::factory(), + 'amount' => fake()->numberBetween(100_000, 3_000_000), + 'description' => fake()->sentence(3), + 'due_date' => fake()->dateTimeBetween('now', '+3 months'), + 'status' => EmployeeAdvanceStatus::PENDING->value, + 'cash_transaction_id' => null, + 'repayment_cash_transaction_id' => null, + 'verified_at' => null, + 'verified_by_id' => null, + 'paid_at' => null, + 'paid_by_id' => null, + ]; + } + + public function approved(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => EmployeeAdvanceStatus::APPROVED->value, + 'verified_at' => now(), + 'verified_by_id' => User::factory(), + ]); + } + + public function rejected(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => EmployeeAdvanceStatus::REJECTED->value, + 'verified_at' => now(), + 'verified_by_id' => User::factory(), + ]); + } +} diff --git a/database/factories/EmployeeFactory.php b/database/factories/EmployeeFactory.php new file mode 100644 index 0000000..e2a3512 --- /dev/null +++ b/database/factories/EmployeeFactory.php @@ -0,0 +1,32 @@ + + */ +class EmployeeFactory extends Factory +{ + public function definition(): array + { + return [ + 'user_id' => User::factory(), + 'join_date' => fake()->dateTimeBetween('-5 years', 'now'), + 'resign_date' => null, + 'employment_status' => fake()->randomElement(EmploymentStatus::cases())->value, + 'base_salary' => fake()->numberBetween(2_000_000, 8_000_000), + ]; + } + + public function resigned(): static + { + return $this->state(fn (array $attributes) => [ + 'resign_date' => fake()->dateTimeBetween($attributes['join_date'] ?? '-1 year', 'now'), + ]); + } +} diff --git a/database/factories/ExpenseFactory.php b/database/factories/ExpenseFactory.php new file mode 100644 index 0000000..d256479 --- /dev/null +++ b/database/factories/ExpenseFactory.php @@ -0,0 +1,41 @@ + + */ +class ExpenseFactory extends Factory +{ + public function definition(): array + { + $amount = fake()->numberBetween(10_000, 2_000_000); + + return [ + 'cash_transaction_id' => null, + 'created_by_id' => User::factory(), + 'amount' => $amount, + 'description' => fake()->sentence(3), + ]; + } + + public function withCashTransaction(): static + { + return $this->state(function (array $attributes) { + $amount = $attributes['amount'] ?? fake()->numberBetween(10_000, 2_000_000); + + return [ + 'cash_transaction_id' => CashTransaction::factory()->state([ + 'amount' => $amount, + 'balance_after' => fake()->numberBetween($amount, $amount + 10_000_000), + 'created_by_id' => $attributes['created_by_id'] ?? User::factory(), + ]), + ]; + }); + } +} diff --git a/database/factories/LeaveRequestFactory.php b/database/factories/LeaveRequestFactory.php new file mode 100644 index 0000000..5d8b17b --- /dev/null +++ b/database/factories/LeaveRequestFactory.php @@ -0,0 +1,50 @@ + + */ +class LeaveRequestFactory extends Factory +{ + public function definition(): array + { + $startDate = fake()->dateTimeBetween('now', '+2 months'); + $totalDays = fake()->numberBetween(1, 5); + $endDate = (clone $startDate)->modify('+'.($totalDays - 1).' days'); + + return [ + 'employee_id' => Employee::factory(), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'total_days' => $totalDays, + 'status' => LeaveRequestStatus::PENDING->value, + 'verified_at' => null, + 'verified_by_id' => null, + ]; + } + + public function approved(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => LeaveRequestStatus::APPROVED->value, + 'verified_at' => now(), + 'verified_by_id' => User::factory(), + ]); + } + + public function rejected(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => LeaveRequestStatus::REJECTED->value, + 'verified_at' => now(), + 'verified_by_id' => User::factory(), + ]); + } +} diff --git a/database/factories/OrderFactory.php b/database/factories/OrderFactory.php new file mode 100644 index 0000000..e044d39 --- /dev/null +++ b/database/factories/OrderFactory.php @@ -0,0 +1,47 @@ + + */ +class OrderFactory extends Factory +{ + public function definition(): array + { + $channel = fake()->randomElement(OrderChannel::cases()); + $priceType = $channel->defaultPriceType() ?? fake()->randomElement(PriceType::cases()); + $subtotal = fake()->numberBetween(100_000, 5_000_000); + $discount = fake()->numberBetween(0, (int) ($subtotal * 0.15)); + + return [ + 'customer_id' => Customer::factory(), + 'order_number' => 'ORD-'.now()->format('Ymd').'-'.Str::upper(Str::random(6)), + 'channel' => $channel->value, + 'price_type' => $priceType->value, + 'status' => OrderStatus::PENDING->value, + 'subtotal' => $subtotal, + 'discount' => $discount, + 'net_amount' => $subtotal - $discount, + 'notes' => fake()->optional()->sentence(), + 'cash_transaction_id' => null, + 'created_by_id' => User::factory(), + ]; + } + + public function completed(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => OrderStatus::COMPLETED->value, + ]); + } +} diff --git a/database/factories/OrderItemFactory.php b/database/factories/OrderItemFactory.php new file mode 100644 index 0000000..1a6a723 --- /dev/null +++ b/database/factories/OrderItemFactory.php @@ -0,0 +1,28 @@ + + */ +class OrderItemFactory extends Factory +{ + public function definition(): array + { + $quantity = fake()->numberBetween(1, 20); + $unitPrice = fake()->numberBetween(25_000, 500_000); + + return [ + 'order_id' => Order::factory(), + 'product_variant_id' => ProductVariant::factory(), + 'quantity' => $quantity, + 'unit_price' => $unitPrice, + 'subtotal' => $quantity * $unitPrice, + ]; + } +} diff --git a/database/factories/PayrollAdjustmentFactory.php b/database/factories/PayrollAdjustmentFactory.php new file mode 100644 index 0000000..dedaf78 --- /dev/null +++ b/database/factories/PayrollAdjustmentFactory.php @@ -0,0 +1,41 @@ + + */ +class PayrollAdjustmentFactory extends Factory +{ + public function definition(): array + { + return [ + 'payroll_id' => Payroll::factory(), + 'type' => fake()->randomElement(PayrollAdjustmentType::cases())->value, + 'amount' => fake()->numberBetween(50_000, 1_000_000), + 'description' => fake()->sentence(3), + 'created_by_id' => User::factory(), + 'created_at' => now(), + ]; + } + + public function bonus(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => PayrollAdjustmentType::BONUS->value, + ]); + } + + public function deduction(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => PayrollAdjustmentType::DEDUCTION->value, + ]); + } +} diff --git a/database/factories/PayrollFactory.php b/database/factories/PayrollFactory.php new file mode 100644 index 0000000..8854ccd --- /dev/null +++ b/database/factories/PayrollFactory.php @@ -0,0 +1,43 @@ + + */ +class PayrollFactory extends Factory +{ + public function definition(): array + { + $baseSalary = fake()->numberBetween(2_000_000, 8_000_000); + $bonusAmount = fake()->numberBetween(0, 1_000_000); + $deductionAmount = fake()->numberBetween(0, 500_000); + + return [ + 'payroll_period_id' => PayrollPeriod::factory(), + 'employee_id' => Employee::factory(), + 'cash_transaction_id' => null, + 'base_salary' => $baseSalary, + 'bonus_amount' => $bonusAmount, + 'deduction_amount' => $deductionAmount, + 'net_amount' => max(0, $baseSalary + $bonusAmount - $deductionAmount), + 'status' => PayrollStatus::UNPAID->value, + 'paid_at' => null, + 'paid_by_id' => null, + ]; + } + + public function paid(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => PayrollStatus::PAID->value, + 'paid_at' => now(), + ]); + } +} diff --git a/database/factories/PayrollPeriodFactory.php b/database/factories/PayrollPeriodFactory.php new file mode 100644 index 0000000..99dc266 --- /dev/null +++ b/database/factories/PayrollPeriodFactory.php @@ -0,0 +1,34 @@ + + */ +class PayrollPeriodFactory extends Factory +{ + public function definition(): array + { + $date = fake()->dateTimeBetween('-12 months', 'now'); + + return [ + 'year' => (int) $date->format('Y'), + 'month' => (int) $date->format('n'), + 'status' => PayrollPeriodStatus::OPEN->value, + 'closed_at' => null, + 'closed_by_id' => null, + ]; + } + + public function closed(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => PayrollPeriodStatus::CLOSED->value, + 'closed_at' => now(), + ]); + } +} diff --git a/database/factories/ProductFactory.php b/database/factories/ProductFactory.php new file mode 100644 index 0000000..9bf337e --- /dev/null +++ b/database/factories/ProductFactory.php @@ -0,0 +1,32 @@ + + */ +class ProductFactory extends Factory +{ + public function definition(): array + { + $name = fake()->unique()->words(3, true); + + return [ + 'name' => Str::limit($name, 200, ''), + 'slug' => Str::slug($name), + 'description' => fake()->optional()->paragraph(), + 'is_active' => true, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } +} diff --git a/database/factories/ProductPriceFactory.php b/database/factories/ProductPriceFactory.php new file mode 100644 index 0000000..9a0b209 --- /dev/null +++ b/database/factories/ProductPriceFactory.php @@ -0,0 +1,23 @@ + + */ +class ProductPriceFactory extends Factory +{ + public function definition(): array + { + return [ + 'variant_id' => ProductVariant::factory(), + 'type' => fake()->randomElement(PriceType::cases())->value, + 'price' => fake()->numberBetween(25_000, 500_000), + ]; + } +} diff --git a/database/factories/ProductVariantFactory.php b/database/factories/ProductVariantFactory.php new file mode 100644 index 0000000..8578d63 --- /dev/null +++ b/database/factories/ProductVariantFactory.php @@ -0,0 +1,22 @@ + + */ +class ProductVariantFactory extends Factory +{ + public function definition(): array + { + return [ + 'product_id' => Product::factory(), + 'name' => fake()->randomElement(['S', 'M', 'L', 'XL', 'All Size']), + 'stock' => fake()->numberBetween(0, 100), + ]; + } +} diff --git a/database/factories/PurchaseFactory.php b/database/factories/PurchaseFactory.php new file mode 100644 index 0000000..ad3a074 --- /dev/null +++ b/database/factories/PurchaseFactory.php @@ -0,0 +1,29 @@ + + */ +class PurchaseFactory extends Factory +{ + public function definition(): array + { + $subtotal = fake()->numberBetween(100_000, 10_000_000); + $discount = fake()->numberBetween(0, (int) ($subtotal * 0.1)); + + return [ + 'supplier_id' => Supplier::factory(), + 'created_by_id' => User::factory(), + 'subtotal' => $subtotal, + 'discount' => $discount, + 'total' => $subtotal - $discount, + 'notes' => fake()->optional()->sentence(3), + ]; + } +} diff --git a/database/factories/PurchaseItemFactory.php b/database/factories/PurchaseItemFactory.php new file mode 100644 index 0000000..223c977 --- /dev/null +++ b/database/factories/PurchaseItemFactory.php @@ -0,0 +1,28 @@ + + */ +class PurchaseItemFactory extends Factory +{ + public function definition(): array + { + $quantity = fake()->randomFloat(4, 1, 50); + $unitPrice = fake()->numberBetween(10_000, 200_000); + + return [ + 'purchase_id' => Purchase::factory(), + 'raw_material_price_id' => RawMaterialPrice::factory(), + 'quantity' => $quantity, + 'unit_price' => $unitPrice, + 'subtotal' => (int) round($quantity * $unitPrice), + ]; + } +} diff --git a/database/factories/RawMaterialFactory.php b/database/factories/RawMaterialFactory.php new file mode 100644 index 0000000..a7a3e8a --- /dev/null +++ b/database/factories/RawMaterialFactory.php @@ -0,0 +1,29 @@ + + */ +class RawMaterialFactory extends Factory +{ + public function definition(): array + { + return [ + 'name' => fake()->unique()->words(3, true), + 'unit' => fake()->randomElement(RawMaterialUnit::cases())->value, + 'is_active' => true, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } +} diff --git a/database/factories/RawMaterialPriceFactory.php b/database/factories/RawMaterialPriceFactory.php new file mode 100644 index 0000000..bc5e132 --- /dev/null +++ b/database/factories/RawMaterialPriceFactory.php @@ -0,0 +1,23 @@ + + */ +class RawMaterialPriceFactory extends Factory +{ + public function definition(): array + { + return [ + 'raw_material_id' => RawMaterial::factory(), + 'variant' => fake()->colorName(), + 'price' => fake()->numberBetween(10_000, 200_000), + 'stock' => fake()->randomFloat(4, 1, 250), + ]; + } +} diff --git a/database/factories/RejectionFactory.php b/database/factories/RejectionFactory.php new file mode 100644 index 0000000..bde1e1d --- /dev/null +++ b/database/factories/RejectionFactory.php @@ -0,0 +1,35 @@ + + */ +class RejectionFactory extends Factory +{ + public function definition(): array + { + return [ + 'rejectable_type' => EmployeeAdvance::class, + 'rejectable_id' => EmployeeAdvance::factory(), + 'reason' => fake()->sentence(), + 'rejected_by_id' => User::factory(), + ]; + } + + public function forLeaveRequest(): static + { + return $this->state(fn (array $attributes) => [ + 'rejectable_type' => LeaveRequest::class, + 'rejectable_id' => LeaveRequest::factory()->state([ + 'status' => 'rejected', + ]), + ]); + } +} diff --git a/database/factories/SupplierFactory.php b/database/factories/SupplierFactory.php new file mode 100644 index 0000000..070e280 --- /dev/null +++ b/database/factories/SupplierFactory.php @@ -0,0 +1,21 @@ + + */ +class SupplierFactory extends Factory +{ + public function definition(): array + { + return [ + 'name' => fake()->company(), + 'phone_number' => fake()->numerify('08##########'), + 'address' => fake()->optional()->address(), + ]; + } +} diff --git a/database/factories/SystemConfigurationFactory.php b/database/factories/SystemConfigurationFactory.php new file mode 100644 index 0000000..03feaf4 --- /dev/null +++ b/database/factories/SystemConfigurationFactory.php @@ -0,0 +1,17 @@ + + */ +class SystemConfigurationFactory extends Factory +{ + public function definition(): array + { + return []; + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index c4ceb07..1c82393 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -4,42 +4,29 @@ use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; -use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Str; /** * @extends Factory */ class UserFactory extends Factory { - /** - * The current password being used by the factory. - */ protected static ?string $password; - /** - * Define the model's default state. - * - * @return array - */ public function definition(): array { return [ - 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), + 'username' => fake()->unique()->regexify('[a-z][a-z0-9]{4,14}'), + 'password' => static::$password ??= 'password', + 'is_active' => true, + 'last_login_at' => null, ]; } - /** - * Indicate that the model's email address should be unverified. - */ - public function unverified(): static + public function inactive(): static { return $this->state(fn (array $attributes) => [ - 'email_verified_at' => null, + 'is_active' => false, ]); } } diff --git a/database/factories/UserProfileFactory.php b/database/factories/UserProfileFactory.php new file mode 100644 index 0000000..aae1da8 --- /dev/null +++ b/database/factories/UserProfileFactory.php @@ -0,0 +1,24 @@ + + */ +class UserProfileFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/UserProfileSeederFactory.php b/database/factories/UserProfileSeederFactory.php new file mode 100644 index 0000000..0dcd04a --- /dev/null +++ b/database/factories/UserProfileSeederFactory.php @@ -0,0 +1,26 @@ + + */ +class UserProfileFactory extends Factory +{ + public function definition(): array + { + return [ + 'user_id' => User::factory(), + 'full_name' => fake()->name(), + 'phone_number' => fake()->numerify('08##########'), + 'gender' => fake()->randomElement(Gender::cases())->value, + 'birth_date' => fake()->dateTimeBetween('-50 years', '-18 years'), + 'address' => fake()->address(), + ]; + } +} diff --git a/database/migrations/2026_06_09_185347_create_categories_table.php b/database/migrations/2026_06_09_185347_create_categories_table.php index 456166a..96d2092 100644 --- a/database/migrations/2026_06_09_185347_create_categories_table.php +++ b/database/migrations/2026_06_09_185347_create_categories_table.php @@ -10,9 +10,12 @@ public function up(): void { Schema::create('categories', function (Blueprint $table) { $table->id(); + $table->string('name', 50); $table->string('slug', 50)->unique(); - $table->timestamps(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_10_100001_create_products_table.php b/database/migrations/2026_06_10_100001_create_products_table.php index f6b427f..20f2fec 100644 --- a/database/migrations/2026_06_10_100001_create_products_table.php +++ b/database/migrations/2026_06_10_100001_create_products_table.php @@ -10,11 +10,14 @@ public function up(): void { Schema::create('products', function (Blueprint $table) { $table->id(); + $table->string('name', 200); $table->string('slug', 200)->unique(); $table->text('description')->nullable(); $table->boolean('is_active')->default(true); - $table->timestamps(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_10_100002_create_product_categories_table.php b/database/migrations/2026_06_10_100002_create_product_categories_table.php index 9b6d42d..cedab59 100644 --- a/database/migrations/2026_06_10_100002_create_product_categories_table.php +++ b/database/migrations/2026_06_10_100002_create_product_categories_table.php @@ -11,8 +11,6 @@ public function up(): void Schema::create('product_categories', function (Blueprint $table) { $table->foreignId('product_id')->constrained()->cascadeOnDelete(); $table->foreignId('category_id')->constrained()->cascadeOnDelete(); - - $table->primary(['product_id', 'category_id']); }); } diff --git a/database/migrations/2026_06_10_100003_create_product_variants_table.php b/database/migrations/2026_06_10_100003_create_product_variants_table.php index f8476de..fe7eeab 100644 --- a/database/migrations/2026_06_10_100003_create_product_variants_table.php +++ b/database/migrations/2026_06_10_100003_create_product_variants_table.php @@ -10,13 +10,15 @@ public function up(): void { Schema::create('product_variants', function (Blueprint $table) { $table->id(); - $table->foreignId('product_id')->constrained()->cascadeOnDelete(); + + $table->foreignId('product_id')->constrained('products')->cascadeOnDelete(); + $table->string('name', 200); $table->unsignedInteger('stock')->default(0); - $table->timestamps(); - $table->softDeletes(); - $table->index('product_id'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_10_100004_create_product_prices_table.php b/database/migrations/2026_06_10_100004_create_product_prices_table.php index 4f69f5b..fea2fad 100644 --- a/database/migrations/2026_06_10_100004_create_product_prices_table.php +++ b/database/migrations/2026_06_10_100004_create_product_prices_table.php @@ -10,13 +10,15 @@ public function up(): void { Schema::create('product_prices', function (Blueprint $table) { $table->id(); + $table->foreignId('variant_id')->constrained('product_variants')->cascadeOnDelete(); + $table->string('type', 20); $table->decimal('price', 18, 2); - $table->timestamps(); - $table->unique(['variant_id', 'type']); - $table->index('variant_id'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_10_120002_create_cash_transactions_table.php b/database/migrations/2026_06_10_120002_create_cash_transactions_table.php index b586203..89d0207 100644 --- a/database/migrations/2026_06_10_120002_create_cash_transactions_table.php +++ b/database/migrations/2026_06_10_120002_create_cash_transactions_table.php @@ -17,7 +17,7 @@ public function up(): void $table->nullableMorphs('reference'); $table->unsignedBigInteger('amount'); $table->unsignedBigInteger('balance_after'); - $table->string('description', 200); + $table->string('description', 100); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); diff --git a/database/migrations/2026_06_10_140001_change_cash_amounts_to_integer.php b/database/migrations/2026_06_10_140001_change_cash_amounts_to_integer.php deleted file mode 100644 index 9a6c66c..0000000 --- a/database/migrations/2026_06_10_140001_change_cash_amounts_to_integer.php +++ /dev/null @@ -1,32 +0,0 @@ -unsignedBigInteger('balance')->default(0)->change(); - }); - - Schema::table('cash_transactions', function (Blueprint $table) { - $table->unsignedBigInteger('amount')->change(); - $table->unsignedBigInteger('balance_after')->change(); - }); - } - - public function down(): void - { - Schema::table('cash_accounts', function (Blueprint $table) { - $table->decimal('balance', 18, 2)->default(0)->change(); - }); - - Schema::table('cash_transactions', function (Blueprint $table) { - $table->decimal('amount', 18, 2)->change(); - $table->decimal('balance_after', 18, 2)->change(); - }); - } -}; diff --git a/database/migrations/2026_06_10_150001_create_expenses_table.php b/database/migrations/2026_06_10_150001_create_expenses_table.php index 2847adb..4dc5715 100644 --- a/database/migrations/2026_06_10_150001_create_expenses_table.php +++ b/database/migrations/2026_06_10_150001_create_expenses_table.php @@ -15,7 +15,7 @@ public function up(): void $table->foreignId('created_by_id')->constrained('users')->restrictOnDelete(); $table->unsignedBigInteger('amount'); - $table->string('description', 200); + $table->string('description', 100); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); diff --git a/database/migrations/2026_06_10_160001_create_employee_advances_table.php b/database/migrations/2026_06_10_160001_create_employee_advances_table.php index 80a43b5..193224a 100644 --- a/database/migrations/2026_06_10_160001_create_employee_advances_table.php +++ b/database/migrations/2026_06_10_160001_create_employee_advances_table.php @@ -13,7 +13,7 @@ public function up(): void $table->id(); $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); $table->unsignedBigInteger('amount'); - $table->string('description', 500); + $table->string('description', 100); $table->date('due_date'); $table->enum('status', array_column(EmployeeAdvanceStatus::cases(), 'value')) ->default(EmployeeAdvanceStatus::PENDING->value); diff --git a/database/migrations/2026_06_10_170001_create_rejections_table.php b/database/migrations/2026_06_10_170001_create_rejections_table.php index e3588e4..7447c7d 100644 --- a/database/migrations/2026_06_10_170001_create_rejections_table.php +++ b/database/migrations/2026_06_10_170001_create_rejections_table.php @@ -10,12 +10,16 @@ public function up(): void { Schema::create('rejections', function (Blueprint $table) { $table->id(); - $table->morphs('rejectable'); - $table->string('reason', 500); - $table->foreignId('rejected_by_id')->constrained('users')->restrictOnDelete(); - $table->timestamps(); - $table->unique(['rejectable_type', 'rejectable_id']); + $table->nullableMorphs('rejectable'); + + $table->string('reason', 500); + + $table->foreignId('rejected_by_id')->constrained('users')->restrictOnDelete(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_11_100001_create_payroll_periods_table.php b/database/migrations/2026_06_11_100001_create_payroll_periods_table.php index 85c8497..db5eaf5 100644 --- a/database/migrations/2026_06_11_100001_create_payroll_periods_table.php +++ b/database/migrations/2026_06_11_100001_create_payroll_periods_table.php @@ -11,16 +11,17 @@ public function up(): void { Schema::create('payroll_periods', function (Blueprint $table) { $table->id(); + $table->unsignedSmallInteger('year'); $table->unsignedTinyInteger('month'); - $table->enum('status', array_column(PayrollPeriodStatus::cases(), 'value')) - ->default(PayrollPeriodStatus::OPEN->value); + $table->enum('status', array_column(PayrollPeriodStatus::cases(), 'value'))->default(PayrollPeriodStatus::OPEN->value); $table->timestamp('closed_at')->nullable(); - $table->foreignId('closed_by_id')->nullable()->constrained('users')->nullOnDelete(); - $table->timestamps(); - $table->unique(['year', 'month']); - $table->index('status'); + $table->foreignId('closed_by_id')->nullable()->constrained('users')->nullOnDelete(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_11_100002_create_payrolls_table.php b/database/migrations/2026_06_11_100002_create_payrolls_table.php index 0e807aa..cce3dab 100644 --- a/database/migrations/2026_06_11_100002_create_payrolls_table.php +++ b/database/migrations/2026_06_11_100002_create_payrolls_table.php @@ -11,22 +11,23 @@ public function up(): void { Schema::create('payrolls', function (Blueprint $table) { $table->id(); + $table->foreignId('payroll_period_id')->constrained()->cascadeOnDelete(); $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); + $table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete(); + $table->unsignedInteger('base_salary'); $table->unsignedBigInteger('bonus_amount')->default(0); $table->unsignedBigInteger('deduction_amount')->default(0); $table->unsignedBigInteger('net_amount'); - $table->enum('status', array_column(PayrollStatus::cases(), 'value')) - ->default(PayrollStatus::UNPAID->value); - $table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete(); + $table->enum('status', array_column(PayrollStatus::cases(), 'value'))->default(PayrollStatus::UNPAID->value); $table->timestamp('paid_at')->nullable(); - $table->foreignId('paid_by_id')->nullable()->constrained('users')->nullOnDelete(); - $table->timestamps(); - $table->unique(['payroll_period_id', 'employee_id']); - $table->index(['payroll_period_id', 'status']); - $table->index('employee_id'); + $table->foreignId('paid_by_id')->nullable()->constrained('users')->nullOnDelete(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_11_100003_create_payroll_adjustments_table.php b/database/migrations/2026_06_11_100003_create_payroll_adjustments_table.php index 7e5b6f7..144bd44 100644 --- a/database/migrations/2026_06_11_100003_create_payroll_adjustments_table.php +++ b/database/migrations/2026_06_11_100003_create_payroll_adjustments_table.php @@ -12,13 +12,16 @@ public function up(): void Schema::create('payroll_adjustments', function (Blueprint $table) { $table->id(); $table->foreignId('payroll_id')->constrained()->cascadeOnDelete(); + $table->enum('type', array_column(PayrollAdjustmentType::cases(), 'value')); $table->unsignedBigInteger('amount'); - $table->string('description', 500); - $table->foreignId('created_by_id')->constrained('users')->restrictOnDelete(); - $table->timestamp('created_at')->useCurrent(); + $table->string('description', 100); - $table->index(['payroll_id', 'created_at']); + $table->foreignId('created_by_id')->constrained('users')->restrictOnDelete(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_11_120000_create_leave_requests_table.php b/database/migrations/2026_06_11_120000_create_leave_requests_table.php index 6029638..b150110 100644 --- a/database/migrations/2026_06_11_120000_create_leave_requests_table.php +++ b/database/migrations/2026_06_11_120000_create_leave_requests_table.php @@ -11,18 +11,20 @@ public function up(): void { Schema::create('leave_requests', function (Blueprint $table) { $table->id(); + $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); + $table->date('start_date'); $table->date('end_date'); $table->unsignedInteger('total_days'); - $table->enum('status', array_column(LeaveRequestStatus::cases(), 'value')) - ->default(LeaveRequestStatus::PENDING->value); + $table->enum('status', array_column(LeaveRequestStatus::cases(), 'value'))->default(LeaveRequestStatus::PENDING->value); $table->timestamp('verified_at')->nullable(); - $table->foreignId('verified_by_id')->nullable()->constrained('users')->restrictOnDelete(); - $table->timestamps(); - $table->index(['employee_id', 'status']); - $table->index(['start_date', 'status']); + $table->foreignId('verified_by_id')->nullable()->constrained('users')->restrictOnDelete(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_11_120501_remove_photo_paths_from_attendances_table.php b/database/migrations/2026_06_11_120501_remove_photo_paths_from_attendances_table.php deleted file mode 100644 index 78868e4..0000000 --- a/database/migrations/2026_06_11_120501_remove_photo_paths_from_attendances_table.php +++ /dev/null @@ -1,23 +0,0 @@ -dropColumn(['check_in_photo_path', 'check_out_photo_path']); - }); - } - - public function down(): void - { - Schema::table('attendances', function (Blueprint $table) { - $table->string('check_in_photo_path', 255)->after('check_out_at'); - $table->string('check_out_photo_path', 255)->nullable()->after('check_in_photo_path'); - }); - } -}; diff --git a/database/migrations/2026_06_12_035838_create_activity_log_table.php b/database/migrations/2026_06_12_035838_create_activity_log_table.php index 5c17c24..75e0c86 100644 --- a/database/migrations/2026_06_12_035838_create_activity_log_table.php +++ b/database/migrations/2026_06_12_035838_create_activity_log_table.php @@ -17,7 +17,8 @@ public function up(): void $table->nullableMorphs('causer', 'causer'); $table->json('attribute_changes')->nullable(); $table->json('properties')->nullable(); - $table->timestamps(); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); }); } }; diff --git a/database/migrations/2026_06_12_100001_create_orders_table.php b/database/migrations/2026_06_12_100001_create_orders_table.php index 28aace8..ca80f72 100644 --- a/database/migrations/2026_06_12_100001_create_orders_table.php +++ b/database/migrations/2026_06_12_100001_create_orders_table.php @@ -1,5 +1,8 @@ id(); - $table->string('order_number', 30)->unique(); $table->foreignId('customer_id')->nullable()->constrained()->nullOnDelete(); - $table->string('channel', 20); - $table->string('price_type', 20); - $table->string('status', 20)->default('pending'); + $table->string('order_number', 30)->unique(); + $table->enum('channel', array_column(OrderChannel::cases(), 'value')); + $table->enum('price_type', array_column(PriceType::cases(), 'value')); + $table->enum('status', array_column(OrderStatus::cases(), 'value'))->default(OrderStatus::PENDING->value); $table->unsignedBigInteger('subtotal'); $table->unsignedBigInteger('discount')->default(0); - $table->unsignedBigInteger('marketplace_fee')->default(0); $table->unsignedBigInteger('net_amount'); - $table->text('notes')->nullable(); - $table->foreignId('cash_transaction_id') - ->nullable() - ->unique() - ->constrained() - ->restrictOnDelete(); + $table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete(); $table->foreignId('created_by_id')->constrained('users')->restrictOnDelete(); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); $table->softDeletes(); - - $table->index('status'); - $table->index('customer_id'); - $table->index('channel'); - $table->index('price_type'); - $table->index('created_at'); }); } diff --git a/database/migrations/2026_06_12_100002_create_order_items_table.php b/database/migrations/2026_06_12_100002_create_order_items_table.php index 2cb47ed..17e9a4a 100644 --- a/database/migrations/2026_06_12_100002_create_order_items_table.php +++ b/database/migrations/2026_06_12_100002_create_order_items_table.php @@ -20,10 +20,7 @@ public function up(): void $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); - - $table->unique(['order_id', 'product_variant_id']); - $table->index('order_id'); - $table->index('product_variant_id'); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_12_110001_create_cuttings_table.php b/database/migrations/2026_06_12_110001_create_cuttings_table.php index 6a0b52a..b834cc7 100644 --- a/database/migrations/2026_06_12_110001_create_cuttings_table.php +++ b/database/migrations/2026_06_12_110001_create_cuttings_table.php @@ -1,5 +1,6 @@ id(); - $table->string('status')->default('in_progress'); - $table->text('description')->nullable(); + + $table->enum('status', array_column(CuttingStatus::cases(), 'value'))->default(CuttingStatus::IN_PROGRESS->value); + $table->string('description', 100)->nullable(); + $table->foreignId('created_by_id')->constrained('users')->restrictOnDelete(); - $table->timestamps(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); $table->softDeletes(); - $table->index('status'); }); } diff --git a/database/migrations/2026_06_12_110002_create_cutting_materials_table.php b/database/migrations/2026_06_12_110002_create_cutting_materials_table.php index aa5419e..8580716 100644 --- a/database/migrations/2026_06_12_110002_create_cutting_materials_table.php +++ b/database/migrations/2026_06_12_110002_create_cutting_materials_table.php @@ -10,15 +10,16 @@ public function up(): void { Schema::create('cutting_materials', function (Blueprint $table) { $table->id(); + $table->foreignId('cutting_id')->constrained()->cascadeOnDelete(); $table->foreignId('raw_material_price_id')->constrained()->restrictOnDelete(); + $table->decimal('material_usage', 18, 4); $table->decimal('remaining_material', 18, 4); - $table->timestamps(); - $table->unique(['cutting_id', 'raw_material_price_id']); - $table->index('cutting_id'); - $table->index('raw_material_price_id'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/database/migrations/2026_06_12_110003_create_cutting_results_table.php b/database/migrations/2026_06_12_110003_create_cutting_results_table.php index 495cd54..2ad2b2e 100644 --- a/database/migrations/2026_06_12_110003_create_cutting_results_table.php +++ b/database/migrations/2026_06_12_110003_create_cutting_results_table.php @@ -10,16 +10,17 @@ public function up(): void { Schema::create('cutting_results', function (Blueprint $table) { $table->id(); + $table->foreignId('cutting_id')->constrained()->cascadeOnDelete(); $table->foreignId('product_variant_id')->constrained()->restrictOnDelete(); + $table->unsignedInteger('cutting_result'); $table->unsignedInteger('warehouse_stock'); $table->unsignedInteger('cutting_reject')->default(0); - $table->timestamps(); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); - $table->unique(['cutting_id', 'product_variant_id']); - $table->index('cutting_id'); - $table->index('product_variant_id'); }); } diff --git a/database/migrations/2026_06_11_120500_create_system_configurations_table.php b/database/migrations/2026_06_12_111450_create_system_configurations_table.php similarity index 100% rename from database/migrations/2026_06_11_120500_create_system_configurations_table.php rename to database/migrations/2026_06_12_111450_create_system_configurations_table.php diff --git a/database/seeders/AttendanceSeeder.php b/database/seeders/AttendanceSeeder.php new file mode 100644 index 0000000..91d6120 --- /dev/null +++ b/database/seeders/AttendanceSeeder.php @@ -0,0 +1,29 @@ +limit(3)->get(); + + if ($employees->isEmpty()) { + Attendance::factory()->count(5)->create(); + + return; + } + + foreach ($employees as $employee) { + Attendance::factory() + ->count(3) + ->create([ + 'employee_id' => $employee->id, + ]); + } + } +} diff --git a/database/seeders/CashAccountSeeder.php b/database/seeders/CashAccountSeeder.php index d84e738..924aed2 100644 --- a/database/seeders/CashAccountSeeder.php +++ b/database/seeders/CashAccountSeeder.php @@ -10,10 +10,14 @@ class CashAccountSeeder extends Seeder { public function run(): void { + $user = User::query()->first(); + CashAccount::firstOrCreate( - ['created_by_id' => User::first()->id], ['name' => 'Kas Toko'], - ['balance' => 0], + [ + 'created_by_id' => $user->id, + 'balance' => 0, + ], ); } } diff --git a/database/seeders/CashTransactionSeeder.php b/database/seeders/CashTransactionSeeder.php new file mode 100644 index 0000000..d9c48f1 --- /dev/null +++ b/database/seeders/CashTransactionSeeder.php @@ -0,0 +1,39 @@ +first(); + $user = User::query()->first(); + + if ($cashAccount === null || $user === null) { + CashTransaction::factory()->count(3)->deposit()->create(); + + return; + } + + $balance = (int) $cashAccount->balance; + + foreach (range(1, 3) as $index) { + $amount = fake()->numberBetween(50_000, 500_000); + $balance += $amount; + + CashTransaction::factory()->deposit()->create([ + 'cash_account_id' => $cashAccount->id, + 'created_by_id' => $user->id, + 'amount' => $amount, + 'balance_after' => $balance, + ]); + } + + $cashAccount->update(['balance' => $balance]); + } +} diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php index 06b0498..9e3c72e 100644 --- a/database/seeders/CategorySeeder.php +++ b/database/seeders/CategorySeeder.php @@ -7,13 +7,10 @@ class CategorySeeder extends Seeder { - /** - * Run the database seeds. - */ public function run(): void { foreach (['Daster', 'Setelan Celana', 'Atasan', 'Bawahan', 'Busui'] as $category) { - Category::create([ + Category::factory()->create([ 'name' => $category, 'slug' => str()->slug($category), ]); diff --git a/database/seeders/CustomerSeeder.php b/database/seeders/CustomerSeeder.php index c675858..97e42bb 100644 --- a/database/seeders/CustomerSeeder.php +++ b/database/seeders/CustomerSeeder.php @@ -7,17 +7,14 @@ class CustomerSeeder extends Seeder { - /** - * Run the database seeds. - */ public function run(): void { - foreach (['Udin', 'Budi', 'Cici', 'Asep', 'Jon', 'Ucup', 'Adit', 'Denis', 'Eko', 'Fajar', 'Gita', 'Hari', 'Iwan', 'Joko', 'Kiki', 'Lala', 'Mama', 'Nana', 'Oki', 'Papa', 'Qiqi', 'Rara', 'Sari', 'Tata', 'Uci', 'Viki', 'Wawan', 'Xiao', 'Yuda', 'Zara'] as $name) { - Customer::create([ + foreach (['Udin', 'Budi', 'Cici', 'Asep', 'Jon', 'Ucup', 'Adit', 'Denis', 'Eko', 'Fajar'] as $name) { + Customer::factory()->create([ 'name' => $name, - 'phone_number' => fake()->phoneNumber(), - 'address' => fake()->address(), ]); } + + Customer::factory()->count(10)->create(); } } diff --git a/database/seeders/CuttingMaterialSeeder.php b/database/seeders/CuttingMaterialSeeder.php new file mode 100644 index 0000000..df4683c --- /dev/null +++ b/database/seeders/CuttingMaterialSeeder.php @@ -0,0 +1,30 @@ +first(); + $rawMaterialPrice = RawMaterialPrice::query()->first(); + + if ($cutting === null || $rawMaterialPrice === null) { + CuttingMaterial::factory()->count(3)->create(); + + return; + } + + CuttingMaterial::factory() + ->count(2) + ->create([ + 'cutting_id' => $cutting->id, + 'raw_material_price_id' => $rawMaterialPrice->id, + ]); + } +} diff --git a/database/seeders/CuttingResultSeeder.php b/database/seeders/CuttingResultSeeder.php new file mode 100644 index 0000000..3be0a8d --- /dev/null +++ b/database/seeders/CuttingResultSeeder.php @@ -0,0 +1,30 @@ +first(); + $variant = ProductVariant::query()->first(); + + if ($cutting === null || $variant === null) { + CuttingResult::factory()->count(3)->create(); + + return; + } + + CuttingResult::factory() + ->count(2) + ->create([ + 'cutting_id' => $cutting->id, + 'product_variant_id' => $variant->id, + ]); + } +} diff --git a/database/seeders/CuttingSeeder.php b/database/seeders/CuttingSeeder.php new file mode 100644 index 0000000..8037985 --- /dev/null +++ b/database/seeders/CuttingSeeder.php @@ -0,0 +1,21 @@ +first(); + + Cutting::factory() + ->count(3) + ->create([ + 'created_by_id' => $user?->id, + ]); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c62867c..60ed8f9 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -9,20 +9,37 @@ class DatabaseSeeder extends Seeder { use WithoutModelEvents; - /** - * Seed the application's database. - */ public function run(): void { $this->call([ RolePermissionSeeder::class, UserSeeder::class, + SystemConfigurationSeeder::class, CategorySeeder::class, ProductSeeder::class, + ProductVariantSeeder::class, + ProductPriceSeeder::class, RawMaterialSeeder::class, + RawMaterialPriceSeeder::class, SupplierSeeder::class, CustomerSeeder::class, CashAccountSeeder::class, + CashTransactionSeeder::class, + ExpenseSeeder::class, + PayrollPeriodSeeder::class, + PayrollSeeder::class, + PayrollAdjustmentSeeder::class, + AttendanceSeeder::class, + LeaveRequestSeeder::class, + EmployeeAdvanceSeeder::class, + RejectionSeeder::class, + PurchaseSeeder::class, + PurchaseItemSeeder::class, + OrderSeeder::class, + OrderItemSeeder::class, + CuttingSeeder::class, + CuttingMaterialSeeder::class, + CuttingResultSeeder::class, ]); } } diff --git a/database/seeders/EmployeeAdvanceSeeder.php b/database/seeders/EmployeeAdvanceSeeder.php new file mode 100644 index 0000000..cf4ece8 --- /dev/null +++ b/database/seeders/EmployeeAdvanceSeeder.php @@ -0,0 +1,29 @@ +first(); + + if ($employee === null) { + EmployeeAdvance::factory()->count(3)->create(); + + return; + } + + EmployeeAdvance::factory()->count(2)->create([ + 'employee_id' => $employee->id, + ]); + + EmployeeAdvance::factory()->approved()->create([ + 'employee_id' => $employee->id, + ]); + } +} diff --git a/database/seeders/ExpenseSeeder.php b/database/seeders/ExpenseSeeder.php new file mode 100644 index 0000000..c57d605 --- /dev/null +++ b/database/seeders/ExpenseSeeder.php @@ -0,0 +1,21 @@ +first(); + + Expense::factory() + ->count(5) + ->create([ + 'created_by_id' => $user?->id, + ]); + } +} diff --git a/database/seeders/LeaveRequestSeeder.php b/database/seeders/LeaveRequestSeeder.php new file mode 100644 index 0000000..15edc98 --- /dev/null +++ b/database/seeders/LeaveRequestSeeder.php @@ -0,0 +1,29 @@ +first(); + + if ($employee === null) { + LeaveRequest::factory()->count(3)->create(); + + return; + } + + LeaveRequest::factory()->count(2)->create([ + 'employee_id' => $employee->id, + ]); + + LeaveRequest::factory()->approved()->create([ + 'employee_id' => $employee->id, + ]); + } +} diff --git a/database/seeders/OrderItemSeeder.php b/database/seeders/OrderItemSeeder.php new file mode 100644 index 0000000..a975386 --- /dev/null +++ b/database/seeders/OrderItemSeeder.php @@ -0,0 +1,30 @@ +first(); + $variant = ProductVariant::query()->first(); + + if ($order === null || $variant === null) { + OrderItem::factory()->count(3)->create(); + + return; + } + + OrderItem::factory() + ->count(2) + ->create([ + 'order_id' => $order->id, + 'product_variant_id' => $variant->id, + ]); + } +} diff --git a/database/seeders/OrderSeeder.php b/database/seeders/OrderSeeder.php new file mode 100644 index 0000000..d575c8b --- /dev/null +++ b/database/seeders/OrderSeeder.php @@ -0,0 +1,24 @@ +first(); + $user = User::query()->first(); + + Order::factory() + ->count(5) + ->create([ + 'customer_id' => $customer?->id, + 'created_by_id' => $user?->id, + ]); + } +} diff --git a/database/seeders/PayrollAdjustmentSeeder.php b/database/seeders/PayrollAdjustmentSeeder.php new file mode 100644 index 0000000..44de89f --- /dev/null +++ b/database/seeders/PayrollAdjustmentSeeder.php @@ -0,0 +1,34 @@ +first(); + $user = User::query()->first(); + + if ($payroll === null || $user === null) { + return; + } + + PayrollAdjustment::factory()->bonus()->create([ + 'payroll_id' => $payroll->id, + 'created_by_id' => $user->id, + 'type' => PayrollAdjustmentType::BONUS->value, + ]); + + PayrollAdjustment::factory()->deduction()->create([ + 'payroll_id' => $payroll->id, + 'created_by_id' => $user->id, + 'type' => PayrollAdjustmentType::DEDUCTION->value, + ]); + } +} diff --git a/database/seeders/PayrollPeriodSeeder.php b/database/seeders/PayrollPeriodSeeder.php new file mode 100644 index 0000000..d88efe0 --- /dev/null +++ b/database/seeders/PayrollPeriodSeeder.php @@ -0,0 +1,23 @@ +copy()->addMonths($monthOffset); + + PayrollPeriod::factory()->create([ + 'year' => $date->year, + 'month' => $date->month, + ]); + } + } +} diff --git a/database/seeders/PayrollSeeder.php b/database/seeders/PayrollSeeder.php new file mode 100644 index 0000000..171a21b --- /dev/null +++ b/database/seeders/PayrollSeeder.php @@ -0,0 +1,29 @@ +latest('id')->first(); + + if ($period === null) { + return; + } + + Employee::query()->each(function (Employee $employee) use ($period): void { + Payroll::factory()->create([ + 'payroll_period_id' => $period->id, + 'employee_id' => $employee->id, + 'base_salary' => $employee->base_salary, + 'net_amount' => $employee->base_salary, + ]); + }); + } +} diff --git a/database/seeders/ProductPriceSeeder.php b/database/seeders/ProductPriceSeeder.php new file mode 100644 index 0000000..28b1d40 --- /dev/null +++ b/database/seeders/ProductPriceSeeder.php @@ -0,0 +1,28 @@ +whereDoesntHave('prices') + ->each(function (ProductVariant $variant): void { + $basePrice = fake()->numberBetween(50_000, 250_000); + + foreach (PriceType::cases() as $type) { + ProductPrice::factory()->create([ + 'variant_id' => $variant->id, + 'type' => $type->value, + 'price' => $basePrice + fake()->numberBetween(0, 50_000), + ]); + } + }); + } +} diff --git a/database/seeders/ProductSeeder.php b/database/seeders/ProductSeeder.php index 2a39b8f..064184f 100644 --- a/database/seeders/ProductSeeder.php +++ b/database/seeders/ProductSeeder.php @@ -74,7 +74,7 @@ public function run(): void DB::transaction(function () use ($products, $categories): void { foreach ($products as $productData) { - $product = Product::create([ + $product = Product::factory()->create([ 'name' => $productData['name'], 'slug' => str()->slug($productData['name']), 'description' => $productData['description'], @@ -88,7 +88,8 @@ public function run(): void ); foreach ($productData['variants'] as $variantData) { - $variant = $product->variants()->create([ + $variant = ProductVariant::factory()->create([ + 'product_id' => $product->id, 'name' => $variantData['name'], 'stock' => $variantData['stock'], ]); @@ -112,7 +113,7 @@ private function seedVariantPrices(ProductVariant $variant, int $basePrice): voi ]; foreach ($multipliers as $type => $multiplier) { - ProductPrice::create([ + ProductPrice::factory()->create([ 'variant_id' => $variant->id, 'type' => $type, 'price' => (int) round($basePrice * $multiplier), diff --git a/database/seeders/ProductVariantSeeder.php b/database/seeders/ProductVariantSeeder.php new file mode 100644 index 0000000..eac3788 --- /dev/null +++ b/database/seeders/ProductVariantSeeder.php @@ -0,0 +1,24 @@ +whereDoesntHave('variants') + ->each(function (Product $product): void { + foreach (['S', 'M', 'L'] as $size) { + ProductVariant::factory()->create([ + 'product_id' => $product->id, + 'name' => $size, + ]); + } + }); + } +} diff --git a/database/seeders/PurchaseItemSeeder.php b/database/seeders/PurchaseItemSeeder.php new file mode 100644 index 0000000..93a328b --- /dev/null +++ b/database/seeders/PurchaseItemSeeder.php @@ -0,0 +1,30 @@ +first(); + $rawMaterialPrice = RawMaterialPrice::query()->first(); + + if ($purchase === null || $rawMaterialPrice === null) { + PurchaseItem::factory()->count(3)->create(); + + return; + } + + PurchaseItem::factory() + ->count(3) + ->create([ + 'purchase_id' => $purchase->id, + 'raw_material_price_id' => $rawMaterialPrice->id, + ]); + } +} diff --git a/database/seeders/PurchaseSeeder.php b/database/seeders/PurchaseSeeder.php new file mode 100644 index 0000000..1a039f5 --- /dev/null +++ b/database/seeders/PurchaseSeeder.php @@ -0,0 +1,24 @@ +first(); + $user = User::query()->first(); + + Purchase::factory() + ->count(3) + ->create([ + 'supplier_id' => $supplier?->id, + 'created_by_id' => $user?->id, + ]); + } +} diff --git a/database/seeders/RawMaterialPriceSeeder.php b/database/seeders/RawMaterialPriceSeeder.php new file mode 100644 index 0000000..8a9f09a --- /dev/null +++ b/database/seeders/RawMaterialPriceSeeder.php @@ -0,0 +1,23 @@ +whereDoesntHave('prices') + ->each(function (RawMaterial $rawMaterial): void { + RawMaterialPrice::factory() + ->count(2) + ->create([ + 'raw_material_id' => $rawMaterial->id, + ]); + }); + } +} diff --git a/database/seeders/RawMaterialSeeder.php b/database/seeders/RawMaterialSeeder.php index a21335d..f2d2159 100644 --- a/database/seeders/RawMaterialSeeder.php +++ b/database/seeders/RawMaterialSeeder.php @@ -10,9 +10,6 @@ class RawMaterialSeeder extends Seeder { - /** - * Run the database seeds. - */ public function run(): void { $rawMaterials = [ @@ -47,14 +44,14 @@ public function run(): void DB::transaction(function () use ($rawMaterials): void { foreach ($rawMaterials as $rawMaterialData) { - $rawMaterial = RawMaterial::create([ + $rawMaterial = RawMaterial::factory()->create([ 'name' => $rawMaterialData['name'], 'unit' => $rawMaterialData['unit'], 'is_active' => true, ]); foreach ($rawMaterialData['prices'] as $priceData) { - RawMaterialPrice::create([ + RawMaterialPrice::factory()->create([ 'raw_material_id' => $rawMaterial->id, 'variant' => $priceData['variant'], 'price' => $priceData['price'], diff --git a/database/seeders/RejectionSeeder.php b/database/seeders/RejectionSeeder.php new file mode 100644 index 0000000..98b7342 --- /dev/null +++ b/database/seeders/RejectionSeeder.php @@ -0,0 +1,44 @@ +first(); + + if ($user === null) { + return; + } + + $advance = EmployeeAdvance::factory()->create([ + 'status' => EmployeeAdvanceStatus::REJECTED->value, + 'verified_at' => now(), + 'verified_by_id' => $user->id, + ]); + + Rejection::factory()->create([ + 'rejectable_type' => EmployeeAdvance::class, + 'rejectable_id' => $advance->id, + 'rejected_by_id' => $user->id, + ]); + + $leaveRequest = LeaveRequest::factory()->rejected()->create([ + 'verified_by_id' => $user->id, + ]); + + Rejection::factory()->create([ + 'rejectable_type' => LeaveRequest::class, + 'rejectable_id' => $leaveRequest->id, + 'rejected_by_id' => $user->id, + ]); + } +} diff --git a/database/seeders/SupplierSeeder.php b/database/seeders/SupplierSeeder.php index a663afc..3f55cec 100644 --- a/database/seeders/SupplierSeeder.php +++ b/database/seeders/SupplierSeeder.php @@ -7,16 +7,11 @@ class SupplierSeeder extends Seeder { - /** - * Run the database seeds. - */ public function run(): void { foreach (['PT. ABC', 'PT. XYZ', 'PT. LMN'] as $name) { - Supplier::create([ + Supplier::factory()->create([ 'name' => $name, - 'phone_number' => fake()->phoneNumber(), - 'address' => fake()->address(), ]); } } diff --git a/database/seeders/SystemConfigurationSeeder.php b/database/seeders/SystemConfigurationSeeder.php new file mode 100644 index 0000000..ad89968 --- /dev/null +++ b/database/seeders/SystemConfigurationSeeder.php @@ -0,0 +1,28 @@ +app_name)) { + return; + } + + $settings->app_name = 'DST Collection'; + $settings->about_app = 'Sistem manajemen toko dan produksi DST Collection.'; + $settings->email = 'info@dstcollection.test'; + $settings->phone = '081234567890'; + $settings->address = 'Jl. Contoh No. 1, Jakarta'; + $settings->save(); + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 79d6128..9f8bc6e 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -8,21 +8,19 @@ use App\Models\UserProfile; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Hash; class UserSeeder extends Seeder { public function run(): void { DB::transaction(function () { - - $developer = User::create([ + $developer = User::factory()->create([ 'email' => 'project.pangestuyoga@gmail.com', 'username' => 'pangestu', - 'password' => Hash::make(config('auth.password_default')), + 'password' => config('auth.password_default'), ]); - UserProfile::create([ + UserProfile::factory()->create([ 'user_id' => $developer->id, 'full_name' => 'Yoga Pangestu', 'phone_number' => '082121495806', diff --git a/resources/js/components/admin/finance/cash/CashTransactionFormModal.vue b/resources/js/components/admin/finance/cash/CashTransactionFormModal.vue index 9a593fc..b14fb56 100644 --- a/resources/js/components/admin/finance/cash/CashTransactionFormModal.vue +++ b/resources/js/components/admin/finance/cash/CashTransactionFormModal.vue @@ -22,9 +22,11 @@ import ImageUploadField from '@/components/ui/image-upload-field/ImageUploadFiel import { RupiahInput } from '@/components/ui/rupiah-input'; import { Textarea } from '@/components/ui/textarea'; import { useFormDialog } from '@/composables/useFormDialog'; +import { FIELD_LIMITS } from '@/lib/field-limits'; import { formErrors } from '@/lib/form'; import { parseRupiah } from '@/lib/rupiah'; -import type { CashTransactionListItem } from '@/types/cash'; +import type { CashTransactionFormData, CashTransactionListItem } from '@/types/cash'; +import { appendPhotosToFormData } from '@/types/media'; const open = defineModel('open', { default: false }); @@ -38,16 +40,24 @@ const existingPhotoId = ref(null); const currentPhotoUrl = computed(() => props.transaction?.photos?.[0]?.url ?? null); - -const form = useForm({ +const form = useForm({ amount: '', description: '', - photo: null as File | null, + photos: [], + remove_media_ids: [], +}); + +const photoFile = computed({ + get: () => form.photos[0] ?? null, + set: (file) => { + form.photos = file ? [file] : []; + }, }); function resetForm() { form.reset(); - form.photo = null; + form.photos = []; + form.remove_media_ids = []; existingPhotoId.value = null; form.clearErrors(); } @@ -81,14 +91,14 @@ function buildFormData(forUpdate: boolean): FormData { formData.append('amount', parseRupiah(form.amount)); formData.append('description', form.description); - if (form.photo) { - formData.append('photos[]', form.photo); + const removeMediaIds = [...form.remove_media_ids]; - if (existingPhotoId.value) { - formData.append('remove_media_ids[]', String(existingPhotoId.value)); - } + if (form.photos.length > 0 && existingPhotoId.value !== null) { + removeMediaIds.push(existingPhotoId.value); } + appendPhotosToFormData(formData, form.photos, removeMediaIds); + return formData; } @@ -136,10 +146,11 @@ function submit() { Keterangan