From eb2047a3e23ab51bc9f76450b9ef70ec07b782e2 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Fri, 12 Jun 2026 22:48:23 +0700 Subject: [PATCH] Implement FlashesEntityMessage trait across multiple controllers for consistent flash messaging. Update flash messages in various methods to utilize the new trait, enhancing code maintainability and readability. Additionally, add attribute methods in request classes for improved validation feedback. --- .../Admin/Account/AppearanceController.php | 5 +- .../Admin/Account/PasswordController.php | 7 +- .../Admin/Account/ProfileController.php | 7 +- .../Admin/Finance/CashController.php | 8 +- .../Finance/EmployeeAdvanceController.php | 27 ++-- .../Admin/Finance/ExpenseController.php | 8 +- .../Admin/Finance/PayrollController.php | 8 +- .../Admin/Finance/PayrollPeriodController.php | 4 +- .../Admin/Hr/AttendanceController.php | 15 ++- .../Admin/Hr/EmployeeController.php | 12 +- .../Admin/Hr/LeaveRequestController.php | 23 ++-- .../Admin/Manage/CuttingController.php | 23 ++-- .../Admin/Manage/OrderController.php | 12 +- .../Admin/Manage/PurchaseController.php | 8 +- .../Admin/Master/CategoryController.php | 8 +- .../Admin/Master/CustomerController.php | 8 +- .../Admin/Master/ProductController.php | 10 +- .../Admin/Master/RawMaterialController.php | 10 +- .../Admin/Master/SupplierController.php | 8 +- .../Admin/System/SettingController.php | 9 +- app/Http/Controllers/Auth/LoginController.php | 9 +- .../Controllers/Auth/LogoutController.php | 9 +- .../Concerns/FlashesEntityMessage.php | 38 ++++++ .../Admin/Account/UpdateAppearanceRequest.php | 13 ++ .../Admin/Account/UpdatePasswordRequest.php | 15 +++ .../Admin/Account/UpdateProfileRequest.php | 21 +++- .../Admin/Finance/DepositCashRequest.php | 17 ++- .../Admin/Finance/EmployeeAdvanceRequest.php | 15 ++- .../Requests/Admin/Finance/ExpenseRequest.php | 14 ++- .../Finance/PayrollAdjustmentRequest.php | 14 ++- .../Finance/RejectEmployeeAdvanceRequest.php | 10 ++ .../Finance/UpdateCashTransactionRequest.php | 17 ++- .../Admin/Hr/AttendanceCheckInRequest.php | 19 ++- .../Admin/Hr/AttendanceCheckOutRequest.php | 19 ++- .../Requests/Admin/Hr/EmployeeRequest.php | 22 +++- .../Admin/Hr/RejectLeaveRequestRequest.php | 10 ++ .../Requests/Admin/Hr/SubmitLeaveRequest.php | 11 ++ .../Requests/Admin/Manage/CuttingRequest.php | 29 ++++- .../Manage/CuttingStatusTransitionRequest.php | 13 +- .../Requests/Admin/Manage/OrderRequest.php | 25 +++- .../Manage/OrderStatusTransitionRequest.php | 12 +- .../Requests/Admin/Manage/PurchaseRequest.php | 19 ++- .../Requests/Admin/Master/CategoryRequest.php | 10 ++ .../Requests/Admin/Master/CustomerRequest.php | 16 ++- .../Requests/Admin/Master/ProductRequest.php | 24 +++- .../Admin/Master/RawMaterialRequest.php | 21 +++- .../Requests/Admin/Master/SupplierRequest.php | 16 ++- .../System/Setting/MarketplaceRequest.php | 27 ++++ .../System/Setting/SocialMediaRequest.php | 15 +++ .../Admin/System/Setting/SystemRequest.php | 19 +++ app/Http/Requests/Auth/LoginRequest.php | 11 ++ .../Concerns/ValidatesMediaUploads.php | 26 ++++ app/Models/Attendance.php | 70 ++++++----- app/Models/CashAccount.php | 12 +- app/Models/CashTransaction.php | 94 +++++++------- app/Models/Category.php | 2 + app/Models/Customer.php | 2 + app/Models/Cutting.php | 30 ++--- app/Models/CuttingMaterial.php | 24 ++-- app/Models/CuttingResult.php | 4 + app/Models/Employee.php | 105 ++++++++-------- app/Models/EmployeeAdvance.php | 110 +++++++++-------- app/Models/Expense.php | 30 ++--- app/Models/LeaveRequest.php | 72 +++++------ app/Models/Order.php | 68 +++++----- app/Models/OrderItem.php | 32 ++--- app/Models/Payroll.php | 116 +++++++++--------- app/Models/PayrollAdjustment.php | 30 ++--- app/Models/PayrollPeriod.php | 36 +++--- app/Models/Product.php | 2 + app/Models/ProductPrice.php | 12 +- app/Models/ProductVariant.php | 56 +++++---- app/Models/Purchase.php | 88 ++++++------- app/Models/PurchaseItem.php | 30 ++--- app/Models/RawMaterial.php | 14 ++- app/Models/RawMaterialPrice.php | 44 +++---- app/Models/Rejection.php | 2 + app/Models/Supplier.php | 2 + app/Models/SystemConfiguration.php | 2 + app/Models/User.php | 67 +++++----- app/Models/UserProfile.php | 18 +-- app/Services/Account/ProfileService.php | 8 +- .../Concerns/ResolvesAuthEmployee.php | 23 ++++ .../Finance/EmployeeAdvanceService.php | 38 +++--- app/Services/Hr/AttendanceService.php | 25 ++-- app/Services/Hr/LeaveRequestService.php | 34 +++-- app/Services/Manage/CuttingService.php | 20 ++- app/Services/Manage/PurchaseService.php | 4 +- app/Services/Master/CategoryService.php | 3 +- app/Services/Master/CustomerService.php | 5 +- app/Services/Master/SupplierService.php | 5 +- database/factories/AttendanceFactory.php | 47 +++++++ database/factories/CashAccountFactory.php | 22 ++++ database/factories/CashTransactionFactory.php | 37 ++++++ database/factories/CategoryFactory.php | 23 ++++ database/factories/CustomerFactory.php | 21 ++++ database/factories/CuttingFactory.php | 37 ++++++ database/factories/CuttingMaterialFactory.php | 27 ++++ database/factories/CuttingResultFactory.php | 28 +++++ database/factories/EmployeeAdvanceFactory.php | 50 ++++++++ database/factories/EmployeeFactory.php | 32 +++++ database/factories/ExpenseFactory.php | 41 +++++++ database/factories/LeaveRequestFactory.php | 50 ++++++++ database/factories/OrderFactory.php | 47 +++++++ database/factories/OrderItemFactory.php | 28 +++++ .../factories/PayrollAdjustmentFactory.php | 41 +++++++ database/factories/PayrollFactory.php | 43 +++++++ database/factories/PayrollPeriodFactory.php | 34 +++++ database/factories/ProductFactory.php | 32 +++++ database/factories/ProductPriceFactory.php | 23 ++++ database/factories/ProductVariantFactory.php | 22 ++++ database/factories/PurchaseFactory.php | 29 +++++ database/factories/PurchaseItemFactory.php | 28 +++++ database/factories/RawMaterialFactory.php | 29 +++++ .../factories/RawMaterialPriceFactory.php | 23 ++++ database/factories/RejectionFactory.php | 35 ++++++ database/factories/SupplierFactory.php | 21 ++++ .../factories/SystemConfigurationFactory.php | 17 +++ database/factories/UserFactory.php | 25 +--- database/factories/UserProfileFactory.php | 24 ++++ .../factories/UserProfileSeederFactory.php | 26 ++++ ...6_06_09_185347_create_categories_table.php | 5 +- ...026_06_10_100001_create_products_table.php | 5 +- ...100002_create_product_categories_table.php | 2 - ...0_100003_create_product_variants_table.php | 10 +- ..._10_100004_create_product_prices_table.php | 8 +- ..._120002_create_cash_transactions_table.php | 2 +- ..._140001_change_cash_amounts_to_integer.php | 32 ----- ...026_06_10_150001_create_expenses_table.php | 2 +- ..._160001_create_employee_advances_table.php | 2 +- ...6_06_10_170001_create_rejections_table.php | 14 ++- ...11_100001_create_payroll_periods_table.php | 13 +- ...026_06_11_100002_create_payrolls_table.php | 17 +-- ...00003_create_payroll_adjustments_table.php | 11 +- ..._11_120000_create_leave_requests_table.php | 14 ++- ...ove_photo_paths_from_attendances_table.php | 23 ---- ...06_12_035838_create_activity_log_table.php | 3 +- .../2026_06_12_100001_create_orders_table.php | 25 ++-- ..._06_12_100002_create_order_items_table.php | 5 +- ...026_06_12_110001_create_cuttings_table.php | 12 +- ..._110002_create_cutting_materials_table.php | 9 +- ...12_110003_create_cutting_results_table.php | 9 +- ...50_create_system_configurations_table.php} | 0 database/seeders/AttendanceSeeder.php | 29 +++++ database/seeders/CashAccountSeeder.php | 8 +- database/seeders/CashTransactionSeeder.php | 39 ++++++ database/seeders/CategorySeeder.php | 5 +- database/seeders/CustomerSeeder.php | 11 +- database/seeders/CuttingMaterialSeeder.php | 30 +++++ database/seeders/CuttingResultSeeder.php | 30 +++++ database/seeders/CuttingSeeder.php | 21 ++++ database/seeders/DatabaseSeeder.php | 23 +++- database/seeders/EmployeeAdvanceSeeder.php | 29 +++++ database/seeders/ExpenseSeeder.php | 21 ++++ database/seeders/LeaveRequestSeeder.php | 29 +++++ database/seeders/OrderItemSeeder.php | 30 +++++ database/seeders/OrderSeeder.php | 24 ++++ database/seeders/PayrollAdjustmentSeeder.php | 34 +++++ database/seeders/PayrollPeriodSeeder.php | 23 ++++ database/seeders/PayrollSeeder.php | 29 +++++ database/seeders/ProductPriceSeeder.php | 28 +++++ database/seeders/ProductSeeder.php | 7 +- database/seeders/ProductVariantSeeder.php | 24 ++++ database/seeders/PurchaseItemSeeder.php | 30 +++++ database/seeders/PurchaseSeeder.php | 24 ++++ database/seeders/RawMaterialPriceSeeder.php | 23 ++++ database/seeders/RawMaterialSeeder.php | 7 +- database/seeders/RejectionSeeder.php | 44 +++++++ database/seeders/SupplierSeeder.php | 7 +- .../seeders/SystemConfigurationSeeder.php | 28 +++++ database/seeders/UserSeeder.php | 8 +- .../finance/cash/CashTransactionFormModal.vue | 35 ++++-- .../EmployeeAdvanceFormModal.vue | 34 +++-- .../RejectEmployeeAdvanceModal.vue | 3 +- .../finance/expenses/ExpenseFormModal.vue | 34 +++-- .../payroll/PayrollAdjustmentModal.vue | 4 +- .../js/components/admin/hr/EmployeeForm.vue | 9 +- .../RejectLeaveRequestModal.vue | 3 +- .../admin/manage/cuttings/CuttingPosForm.vue | 18 +-- .../manage/cuttings/data-table-actions.vue | 55 +++------ .../manage/purchases/PurchasePosForm.vue | 35 ++++-- .../master/categories/CategoryFormModal.vue | 3 +- .../master/customers/CustomerFormModal.vue | 7 +- .../admin/master/products/ProductForm.vue | 7 +- .../master/raw-materials/RawMaterialForm.vue | 7 +- .../master/suppliers/SupplierFormModal.vue | 7 +- .../phone-number-input/PhoneNumberInput.vue | 14 +-- resources/js/lib/field-limits.ts | 13 ++ resources/js/pages/admin/account/Profile.vue | 8 +- resources/js/types/cash.ts | 5 +- resources/js/types/employee-advance.ts | 5 +- resources/js/types/expense.ts | 5 +- resources/js/types/media.ts | 14 +++ resources/js/types/purchase.ts | 2 + 194 files changed, 3174 insertions(+), 1078 deletions(-) create mode 100644 app/Http/Controllers/Concerns/FlashesEntityMessage.php create mode 100644 app/Services/Concerns/ResolvesAuthEmployee.php create mode 100644 database/factories/AttendanceFactory.php create mode 100644 database/factories/CashAccountFactory.php create mode 100644 database/factories/CashTransactionFactory.php create mode 100644 database/factories/CategoryFactory.php create mode 100644 database/factories/CustomerFactory.php create mode 100644 database/factories/CuttingFactory.php create mode 100644 database/factories/CuttingMaterialFactory.php create mode 100644 database/factories/CuttingResultFactory.php create mode 100644 database/factories/EmployeeAdvanceFactory.php create mode 100644 database/factories/EmployeeFactory.php create mode 100644 database/factories/ExpenseFactory.php create mode 100644 database/factories/LeaveRequestFactory.php create mode 100644 database/factories/OrderFactory.php create mode 100644 database/factories/OrderItemFactory.php create mode 100644 database/factories/PayrollAdjustmentFactory.php create mode 100644 database/factories/PayrollFactory.php create mode 100644 database/factories/PayrollPeriodFactory.php create mode 100644 database/factories/ProductFactory.php create mode 100644 database/factories/ProductPriceFactory.php create mode 100644 database/factories/ProductVariantFactory.php create mode 100644 database/factories/PurchaseFactory.php create mode 100644 database/factories/PurchaseItemFactory.php create mode 100644 database/factories/RawMaterialFactory.php create mode 100644 database/factories/RawMaterialPriceFactory.php create mode 100644 database/factories/RejectionFactory.php create mode 100644 database/factories/SupplierFactory.php create mode 100644 database/factories/SystemConfigurationFactory.php create mode 100644 database/factories/UserProfileFactory.php create mode 100644 database/factories/UserProfileSeederFactory.php delete mode 100644 database/migrations/2026_06_10_140001_change_cash_amounts_to_integer.php delete mode 100644 database/migrations/2026_06_11_120501_remove_photo_paths_from_attendances_table.php rename database/migrations/{2026_06_11_120500_create_system_configurations_table.php => 2026_06_12_111450_create_system_configurations_table.php} (100%) create mode 100644 database/seeders/AttendanceSeeder.php create mode 100644 database/seeders/CashTransactionSeeder.php create mode 100644 database/seeders/CuttingMaterialSeeder.php create mode 100644 database/seeders/CuttingResultSeeder.php create mode 100644 database/seeders/CuttingSeeder.php create mode 100644 database/seeders/EmployeeAdvanceSeeder.php create mode 100644 database/seeders/ExpenseSeeder.php create mode 100644 database/seeders/LeaveRequestSeeder.php create mode 100644 database/seeders/OrderItemSeeder.php create mode 100644 database/seeders/OrderSeeder.php create mode 100644 database/seeders/PayrollAdjustmentSeeder.php create mode 100644 database/seeders/PayrollPeriodSeeder.php create mode 100644 database/seeders/PayrollSeeder.php create mode 100644 database/seeders/ProductPriceSeeder.php create mode 100644 database/seeders/ProductVariantSeeder.php create mode 100644 database/seeders/PurchaseItemSeeder.php create mode 100644 database/seeders/PurchaseSeeder.php create mode 100644 database/seeders/RawMaterialPriceSeeder.php create mode 100644 database/seeders/RejectionSeeder.php create mode 100644 database/seeders/SystemConfigurationSeeder.php create mode 100644 resources/js/lib/field-limits.ts 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