From b3790f2b2fb2c897aca7791da18c14b7b0983d48 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 28 Jun 2026 16:23:57 +0700 Subject: [PATCH] feat: enhance service and controller logic for Cutting and Order management, implement best practices for method handling, and improve pagination defaults --- BEST_PRACTICE.md | 43 ++++ app/Enums/CuttingStatus.php | 11 + app/Enums/OrderStatus.php | 10 + .../Manage/Cutting/CuttingController.php | 4 +- .../Admin/Manage/Order/OrderController.php | 4 +- app/Models/Cutting.php | 13 +- app/Models/Order.php | 10 + app/Services/Account/ProfileService.php | 8 - app/Services/Finance/CashService.php | 54 ++--- .../Finance/EmployeeAdvanceService.php | 18 +- app/Services/Finance/ExpenseService.php | 18 +- app/Services/Finance/PayrollService.php | 22 +- app/Services/Hr/AttendanceService.php | 12 +- app/Services/Hr/EmployeeService.php | 20 +- app/Services/Hr/LeaveRequestService.php | 14 +- .../Manage/CuttingResultPriceResolver.php | 13 +- app/Services/Manage/CuttingService.php | 141 +++---------- app/Services/Manage/OrderService.php | 190 +++++------------- .../Manage/OwnerVerificationService.php | 30 +-- app/Services/Manage/PurchaseService.php | 80 ++------ app/Services/Manage/StockRetailService.php | 12 +- app/Services/Manage/StockService.php | 35 +--- app/Services/Manage/StokOpnameService.php | 44 +--- app/Services/Master/CategoryService.php | 2 +- app/Services/Master/CustomerService.php | 2 +- app/Services/Master/ProductService.php | 56 ++---- app/Services/Master/RawMaterialService.php | 60 ++---- app/Services/Master/SupplierService.php | 2 +- app/Services/Media/MediaService.php | 9 +- app/Services/System/ActivityLogService.php | 2 +- app/Services/System/DashboardService.php | 4 +- .../System/PushNotificationService.php | 14 +- .../System/PushSubscriptionService.php | 10 +- app/Services/System/RoleService.php | 2 +- .../System/Setting/MarketplaceService.php | 32 +-- 35 files changed, 317 insertions(+), 684 deletions(-) diff --git a/BEST_PRACTICE.md b/BEST_PRACTICE.md index 686865a..5453cfe 100644 --- a/BEST_PRACTICE.md +++ b/BEST_PRACTICE.md @@ -284,6 +284,49 @@ ### Controller } ``` +### Service + +- **Tugas Service:** + - Service menampung **semua logika bisnis** (query kompleks, transformasi data, transaksi, authorization bisnis). + - Controller hanya memanggil service dan mengembalikan response. + +- **Urutan Method:** + 1. `paginateForIndex` / method index/list + 2. `findForEdit` / `findForShow` (jika ada) + 3. `create` / `store` + 4. `update` + 5. `delete` / `destroy` + 6. Method tambahan (custom action) + 7. Method `private` di paling bawah + +- **Docblock & Comment:** + - **Jangan** gunakan PHPDoc/docblock pada service. + - **Jangan** gunakan inline comment tipe `@var Model $variable`. + +- **Pagination:** + - Default pagination untuk data table: **25** (`->paginate(25)`). + +- **Update Model:** + - Gunakan `$model->update([...])`, **bukan** assign property lalu `$model->save()`. + - **Hindari:** + ```php + $account->balance = $newBalance; + $account->save(); + ``` + - **Gunakan:** + ```php + $account->update(['balance' => $newBalance]); + ``` + +- **Logic di Model/Enum, Bukan Wrapper di Service:** + - Method yang hanya meneruskan ke model/enum **wajib** ditempatkan di model/enum, bukan di service. + - **Contoh:** `isEditable()`, `transitionStatusMessage()`, `ensureEditable()` → enum/model. + - Service memanggil langsung: `$order->status->isEditable()`, `$status->transitionStatusMessage()`, `$order->ensureEditable()`. + +- **Penamaan:** + - Variable, method, file **Bahasa Inggris**. + - Pesan error/flash ke user **Bahasa Indonesia**. + ## Database & Migrasi ### Struktur File Migrasi diff --git a/app/Enums/CuttingStatus.php b/app/Enums/CuttingStatus.php index af9c7c9..fd739a1 100644 --- a/app/Enums/CuttingStatus.php +++ b/app/Enums/CuttingStatus.php @@ -31,6 +31,17 @@ public function isEditable(): bool return in_array($this, [self::IN_PROGRESS, self::REJECTED], true); } + public function transitionStatusMessage(): string + { + return match ($this) { + self::COMPLETED => 'Proses cutting berhasil diselesaikan. Menunggu verifikasi admin toko.', + self::VERIFIED => 'Proses cutting berhasil diverifikasi. Stok produk telah diperbarui.', + self::REJECTED => 'Proses cutting berhasil ditolak.', + self::IN_PROGRESS => 'Proses cutting dikembalikan ke proses.', + default => 'Status proses cutting berhasil diperbarui.', + }; + } + public function canTransitionTo(self $status): bool { return match ($this) { diff --git a/app/Enums/OrderStatus.php b/app/Enums/OrderStatus.php index 4b19b82..cb17940 100644 --- a/app/Enums/OrderStatus.php +++ b/app/Enums/OrderStatus.php @@ -29,6 +29,16 @@ public function isEditable(): bool return in_array($this, [self::PENDING, self::PROCESSING], true); } + public function transitionStatusMessage(): string + { + return match ($this) { + self::PROCESSING => 'Pesanan berhasil dikirim.', + self::COMPLETED => 'Pesanan berhasil diselesaikan.', + self::CANCELLED => 'Pesanan berhasil dibatalkan.', + default => 'Status pesanan berhasil diperbarui.', + }; + } + public function canTransitionTo(self $status): bool { return match ($this) { diff --git a/app/Http/Controllers/Admin/Manage/Cutting/CuttingController.php b/app/Http/Controllers/Admin/Manage/Cutting/CuttingController.php index 5cb753a..b1f294c 100644 --- a/app/Http/Controllers/Admin/Manage/Cutting/CuttingController.php +++ b/app/Http/Controllers/Admin/Manage/Cutting/CuttingController.php @@ -59,7 +59,7 @@ public function store(CuttingRequest $request): RedirectResponse public function edit(Cutting $cutting): Response|RedirectResponse { try { - $this->cuttingService->ensureEditable($cutting); + $cutting->ensureEditable(); } catch (ValidationException $exception) { $this->flashError($exception->validator->errors()->first('status')); @@ -105,7 +105,7 @@ public function transitionStatus(CuttingStatusTransitionRequest $request, Cuttin $request->validated('result_prices'), ); - $this->flashSuccess($this->cuttingService->transitionStatusMessage($status)); + $this->flashSuccess($status->transitionStatusMessage()); return redirect()->route('admin.manage.cuttings.index'); } diff --git a/app/Http/Controllers/Admin/Manage/Order/OrderController.php b/app/Http/Controllers/Admin/Manage/Order/OrderController.php index fbb6965..2eb202d 100644 --- a/app/Http/Controllers/Admin/Manage/Order/OrderController.php +++ b/app/Http/Controllers/Admin/Manage/Order/OrderController.php @@ -77,7 +77,7 @@ public function show(Order $order): Response public function edit(Order $order): Response|RedirectResponse { try { - $this->orderService->ensureEditable($order); + $order->ensureEditable(); } catch (ValidationException $exception) { $this->flashError($exception->validator->errors()->first('status')); @@ -119,7 +119,7 @@ public function transitionStatus(OrderStatusTransitionRequest $request, Order $o $this->orderService->transitionStatus($order, $status); - $this->flashSuccess($this->orderService->transitionStatusMessage($status)); + $this->flashSuccess($status->transitionStatusMessage()); return redirect()->route('admin.manage.orders.index'); } diff --git a/app/Models/Cutting.php b/app/Models/Cutting.php index 0a7a823..4fc4adb 100644 --- a/app/Models/Cutting.php +++ b/app/Models/Cutting.php @@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Validation\ValidationException; #[Guarded(['id'])] #[Appends([ @@ -79,7 +80,17 @@ public function statusLabel(): Attribute ); } - // 5. Relation + // 5. Other Methods + public function ensureEditable(): void + { + if (! $this->status->isEditable()) { + throw ValidationException::withMessages([ + 'status' => 'Proses cutting tidak dapat diubah.', + ]); + } + } + + // 6. Relation public function createdBy(): BelongsTo { return $this->belongsTo(User::class, 'created_by_id'); diff --git a/app/Models/Order.php b/app/Models/Order.php index ddcd11a..dbde917 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Validation\ValidationException; use Spatie\MediaLibrary\HasMedia; #[Guarded(['id'])] @@ -271,6 +272,15 @@ public static function mediaModuleName(): string return 'order'; } + public function ensureEditable(): void + { + if (! $this->status->isEditable()) { + throw ValidationException::withMessages([ + 'status' => 'Pesanan tidak dapat diubah.', + ]); + } + } + public function registerMediaCollections(): void { $this->addMediaCollection('photos'); diff --git a/app/Services/Account/ProfileService.php b/app/Services/Account/ProfileService.php index ac171f5..8b02b90 100644 --- a/app/Services/Account/ProfileService.php +++ b/app/Services/Account/ProfileService.php @@ -3,7 +3,6 @@ namespace App\Services\Account; use App\Models\User; -use App\Models\UserProfile; use App\Services\Media\MediaService; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -16,9 +15,6 @@ public function __construct( private readonly MediaService $mediaService, ) {} - /** - * @param array $validated - */ public function update(array $validated, User $user): void { try { @@ -28,7 +24,6 @@ public function update(array $validated, User $user): void 'username' => $validated['username'], ]); - /** @var UserProfile $profile */ $profile = $user->profile()->updateOrCreate( ['user_id' => $user->id], [ @@ -58,9 +53,6 @@ public function update(array $validated, User $user): void } } - /** - * Update user password. - */ public function updatePassword(User $user, string $password): void { $user->update([ diff --git a/app/Services/Finance/CashService.php b/app/Services/Finance/CashService.php index 4d54525..2147d5b 100644 --- a/app/Services/Finance/CashService.php +++ b/app/Services/Finance/CashService.php @@ -30,9 +30,7 @@ public function getDefaultAccount(): CashAccount return CashAccount::query()->firstOrFail(); } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(CashAccount $cashAccount, array $tableQuery, string $referenceType = ''): LengthAwarePaginator { $query = CashTransaction::query() @@ -55,7 +53,7 @@ public function paginateForIndex(CashAccount $cashAccount, array $tableQuery, st $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (CashTransaction $transaction) { $transaction->setAttribute( @@ -67,9 +65,7 @@ public function paginateForIndex(CashAccount $cashAccount, array $tableQuery, st }); } - /** - * @param array{amount: int, description: string} $validated - */ + public function deposit(CashAccount $cashAccount, array $validated, User $user): CashTransaction { try { @@ -78,8 +74,7 @@ public function deposit(CashAccount $cashAccount, array $validated, User $user): $amount = (int) $validated['amount']; $newBalance = $account->balance + $amount; - $account->balance = $newBalance; - $account->save(); + $account->update(['balance' => $newBalance]); $transaction = CashTransaction::create([ 'cash_account_id' => $account->id, @@ -116,9 +111,7 @@ public function deposit(CashAccount $cashAccount, array $validated, User $user): return $transaction; } - /** - * @param array{amount: int, description: string} $validated - */ + public function withdraw(CashAccount $cashAccount, array $validated, User $user): CashTransaction { try { @@ -134,8 +127,7 @@ public function withdraw(CashAccount $cashAccount, array $validated, User $user) $newBalance = $account->balance - $amount; - $account->balance = $newBalance; - $account->save(); + $account->update(['balance' => $newBalance]); $transaction = CashTransaction::create([ 'cash_account_id' => $account->id, @@ -193,8 +185,7 @@ public function recordOutgoing( $newBalance = $account->balance - $amount; - $account->balance = $newBalance; - $account->save(); + $account->update(['balance' => $newBalance]); $transaction = new CashTransaction([ 'cash_account_id' => $account->id, @@ -238,8 +229,7 @@ public function recordIncoming( $newBalance = $account->balance + $amount; - $account->balance = $newBalance; - $account->save(); + $account->update(['balance' => $newBalance]); $transaction = new CashTransaction([ 'cash_account_id' => $account->id, @@ -268,9 +258,7 @@ public function recordIncoming( } } - /** - * @param array{amount: int, description: string} $validated - */ + public function updateDeposit(CashTransaction $transaction, array $validated): void { $this->ensureEditable($transaction); @@ -279,9 +267,10 @@ public function updateDeposit(CashTransaction $transaction, array $validated): v DB::transaction(function () use ($transaction, $validated): void { CashAccount::query()->lockForUpdate()->findOrFail($transaction->cash_account_id); - $transaction->amount = (int) $validated['amount']; - $transaction->description = $validated['description']; - $transaction->save(); + $transaction->update([ + 'amount' => (int) $validated['amount'], + 'description' => $validated['description'], + ]); $this->syncPhotos($transaction, $validated); @@ -365,9 +354,10 @@ public function updateReferencedTransaction( DB::transaction(function () use ($transaction, $amount, $description): void { CashAccount::query()->lockForUpdate()->findOrFail($transaction->cash_account_id); - $transaction->amount = $amount; - $transaction->description = $description; - $transaction->save(); + $transaction->update([ + 'amount' => $amount, + 'description' => $description, + ]); $this->recalculateBalances($transaction->cashAccount); @@ -422,9 +412,7 @@ public function deleteReferencedTransaction(CashTransaction $transaction): void } } - /** - * @param array $validated - */ + private function syncPhotos(CashTransaction $transaction, array $validated): void { $this->mediaService->syncCollection( @@ -466,12 +454,10 @@ private function recalculateBalances(CashAccount $cashAccount): void $runningBalance -= $transaction->amount; } - $transaction->balance_after = $runningBalance; - $transaction->saveQuietly(); + $transaction->updateQuietly(['balance_after' => $runningBalance]); } - $account->balance = $runningBalance; - $account->save(); + $account->update(['balance' => $runningBalance]); } private function applySorting(Builder $query, string $sort, string $direction): void diff --git a/app/Services/Finance/EmployeeAdvanceService.php b/app/Services/Finance/EmployeeAdvanceService.php index e24ee9f..d0c76f4 100644 --- a/app/Services/Finance/EmployeeAdvanceService.php +++ b/app/Services/Finance/EmployeeAdvanceService.php @@ -25,9 +25,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @return array{outstanding_amount: int, outstanding_amount_formatted: string, outstanding_count: int} - */ + public function outstandingSummary(?User $user = null): array { $query = EmployeeAdvance::query() @@ -45,9 +43,7 @@ public function outstandingSummary(?User $user = null): array ]; } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery, User $user, string $status = ''): LengthAwarePaginator { $query = EmployeeAdvance::query() @@ -70,13 +66,11 @@ public function paginateForIndex(array $tableQuery, User $user, string $status = $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } - /** - * @param array{amount: int, description: string, due_date: string} $validated - */ + public function create(array $validated, User $user): void { $employee = $this->resolveAuthenticatedEmployee($user); @@ -111,9 +105,7 @@ public function create(array $validated, User $user): void ); } - /** - * @param array{amount: int, description: string, due_date: string} $validated - */ + public function update(EmployeeAdvance $employeeAdvance, array $validated, User $user): void { try { diff --git a/app/Services/Finance/ExpenseService.php b/app/Services/Finance/ExpenseService.php index 924453c..00022e6 100644 --- a/app/Services/Finance/ExpenseService.php +++ b/app/Services/Finance/ExpenseService.php @@ -23,9 +23,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery): LengthAwarePaginator { $query = Expense::query() @@ -40,7 +38,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (Expense $expense) { $expense->setAttribute( @@ -52,9 +50,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator }); } - /** - * @param array{amount: int, description: string} $validated - */ + public function create(array $validated, User $user): void { try { @@ -103,9 +99,7 @@ public function create(array $validated, User $user): void ); } - /** - * @param array{amount: int, description: string} $validated - */ + public function update(Expense $expense, array $validated): void { try { @@ -179,9 +173,7 @@ public function delete(Expense $expense): void ); } - /** - * @param array $validated - */ + private function syncPhotos(Expense $expense, array $validated): void { $this->mediaService->syncCollection( diff --git a/app/Services/Finance/PayrollService.php b/app/Services/Finance/PayrollService.php index ed27925..2688f96 100644 --- a/app/Services/Finance/PayrollService.php +++ b/app/Services/Finance/PayrollService.php @@ -29,9 +29,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @return Collection - */ + public function listPeriods(): Collection { return PayrollPeriod::query() @@ -57,9 +55,7 @@ public function resolvePeriod(?int $periodId): ?PayrollPeriod ->first(); } - /** - * @return array{total_amount: int, total_amount_formatted: string, total_count: int, status: string, status_label: string} - */ + public function periodSummary(PayrollPeriod $period, User $user): array { $query = Payroll::query() @@ -83,9 +79,7 @@ public function periodSummary(PayrollPeriod $period, User $user): array ]; } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForPeriod(PayrollPeriod $period, array $tableQuery, User $user): LengthAwarePaginator { $query = Payroll::query() @@ -106,7 +100,7 @@ public function paginateForPeriod(PayrollPeriod $period, array $tableQuery, User $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } @@ -212,9 +206,7 @@ public function generatePayrollsForPeriod(PayrollPeriod $period): void } } - /** - * @param array{type: string, amount: int, description: string} $validated - */ + public function addAdjustment(Payroll $payroll, array $validated, User $user): void { @@ -404,9 +396,7 @@ public function pay(Payroll $payroll, User $user): void } } - /** - * @return Collection - */ + private function payrollEligibleEmployees(PayrollPeriod $period): Collection { $periodStart = Carbon::create($period->year, $period->month, 1)->startOfMonth(); diff --git a/app/Services/Hr/AttendanceService.php b/app/Services/Hr/AttendanceService.php index f391a5c..379525a 100644 --- a/app/Services/Hr/AttendanceService.php +++ b/app/Services/Hr/AttendanceService.php @@ -46,9 +46,7 @@ public function listForCalendar( ->get(); } - /** - * @return array|null - */ + public function todayAttendanceForEmployee(Employee $employee): ?array { $attendance = Attendance::query() @@ -70,9 +68,7 @@ public function isOnLeaveToday(Employee $employee): bool ->exists(); } - /** - * @param array{photo: string, latitude: float, longitude: float} $validated - */ + public function checkIn(array $validated, User $user): void { $employee = $this->resolveAuthenticatedEmployee($user); @@ -111,9 +107,7 @@ public function checkIn(array $validated, User $user): void ); } - /** - * @param array{photo: string, latitude: float, longitude: float} $validated - */ + public function checkOut(array $validated, User $user): void { $employee = $this->resolveAuthenticatedEmployee($user); diff --git a/app/Services/Hr/EmployeeService.php b/app/Services/Hr/EmployeeService.php index 3706d47..5bd694e 100644 --- a/app/Services/Hr/EmployeeService.php +++ b/app/Services/Hr/EmployeeService.php @@ -21,9 +21,7 @@ public function __construct( private readonly MediaService $mediaService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex( array $tableQuery, string $role = '', @@ -55,7 +53,7 @@ public function paginateForIndex( $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } @@ -76,9 +74,7 @@ public function findForEdit(User $user): array ]; } - /** - * @param array $validated - */ + public function create(array $validated): void { try { @@ -124,9 +120,7 @@ public function create(array $validated): void } } - /** - * @param array $validated - */ + public function update(User $user, array $validated): void { $employee = $user->employee; @@ -138,7 +132,7 @@ public function update(User $user, array $validated): void 'username' => $validated['username'], ]); - /** @var UserProfile $profile */ + $profile = $user->profile()->updateOrCreate( ['user_id' => $user->id], [ @@ -231,9 +225,7 @@ public function delete(User $user): void } } - /** - * @param array $validated - */ + private function syncProfilePhoto(UserProfile $profile, array $validated): void { $newFiles = ! empty($validated['profile_photo']) ? [$validated['profile_photo']] : []; diff --git a/app/Services/Hr/LeaveRequestService.php b/app/Services/Hr/LeaveRequestService.php index 16f2ded..d49a962 100644 --- a/app/Services/Hr/LeaveRequestService.php +++ b/app/Services/Hr/LeaveRequestService.php @@ -23,9 +23,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator { $query = LeaveRequest::query() @@ -46,13 +44,11 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } - /** - * @param array{start_date: string, end_date: string} $validated - */ + public function create(array $validated, User $user): void { $employee = $this->resolveAuthenticatedEmployee($user); @@ -89,9 +85,7 @@ public function create(array $validated, User $user): void ); } - /** - * @param array{start_date: string, end_date: string} $validated - */ + public function update(LeaveRequest $leaveRequest, array $validated, User $user): void { $startDate = Carbon::parse($validated['start_date'])->startOfDay(); diff --git a/app/Services/Manage/CuttingResultPriceResolver.php b/app/Services/Manage/CuttingResultPriceResolver.php index 3abf190..a90e41b 100644 --- a/app/Services/Manage/CuttingResultPriceResolver.php +++ b/app/Services/Manage/CuttingResultPriceResolver.php @@ -20,9 +20,7 @@ public function resolve(int $productVariantId, PriceType $priceType): ?CuttingRe ->first(); } - /** - * @return list - */ + public function latestPricesForVariant(int $productVariantId): array { $prices = []; @@ -38,14 +36,7 @@ public function latestPricesForVariant(int $productVariantId): array return $prices; } - /** - * Batch-fetch latest prices for multiple variant IDs in a single query. - * Returns a Collection keyed by product_variant_id, where each value is - * a Collection of CuttingResultPrice (one per price type, the latest). - * - * @param list $variantIds - * @return Collection> - */ + public function latestPricesForVariants(array $variantIds): Collection { if (empty($variantIds)) { diff --git a/app/Services/Manage/CuttingService.php b/app/Services/Manage/CuttingService.php index cd71e0d..4b42c7c 100644 --- a/app/Services/Manage/CuttingService.php +++ b/app/Services/Manage/CuttingService.php @@ -3,7 +3,6 @@ namespace App\Services\Manage; use App\Enums\CuttingStatus; -use App\Enums\Permission; use App\Models\Cutting; use App\Models\CuttingMaterial; use App\Models\CuttingResult; @@ -29,52 +28,6 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - public function isEditable(CuttingStatus $status): bool - { - return $status->isEditable(); - } - - public function ensureEditable(Cutting $cutting): void - { - if (! $this->isEditable($cutting->status)) { - throw ValidationException::withMessages([ - 'status' => 'Proses cutting tidak dapat diubah.', - ]); - } - } - - public function transitionStatusMessage(CuttingStatus $status): string - { - return match ($status) { - CuttingStatus::COMPLETED => 'Proses cutting berhasil diselesaikan. Menunggu verifikasi admin toko.', - CuttingStatus::VERIFIED => 'Proses cutting berhasil diverifikasi. Stok produk telah diperbarui.', - CuttingStatus::REJECTED => 'Proses cutting berhasil ditolak.', - CuttingStatus::IN_PROGRESS => 'Proses cutting dikembalikan ke proses.', - default => 'Status proses cutting berhasil diperbarui.', - }; - } - - public function canTransitionTo(CuttingStatus $from, CuttingStatus $to): bool - { - return $from->canTransitionTo($to); - } - - public function transitionPermission(CuttingStatus $status): Permission - { - return $status->transitionPermission(); - } - - /** - * @return list - */ - public function availableActions(CuttingStatus $status): array - { - return $status->availableActions(); - } - - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator { $query = Cutting::query() @@ -103,25 +56,23 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (Cutting $cutting) use ($user) { - $actions = collect($this->availableActions($cutting->status)) + $actions = collect($cutting->status->availableActions()) ->filter(fn (array $action) => $user->can($action['permission'])) ->values() ->all(); $cutting->setAttribute('available_actions', $actions); - $cutting->setAttribute('is_editable', $this->isEditable($cutting->status)); + $cutting->setAttribute('is_editable', $cutting->status->isEditable()); $this->appendCostPreview($cutting); return $cutting; }); } - /** - * @return Collection - */ + public function getInProgressCuttings(User $user): Collection { return Cutting::query() @@ -136,20 +87,18 @@ public function getInProgressCuttings(User $user): Collection ->latest() ->get() ->each(function (Cutting $cutting) use ($user): void { - $actions = collect($this->availableActions($cutting->status)) + $actions = collect($cutting->status->availableActions()) ->filter(fn (array $action) => $user->can($action['permission'])) ->values() ->all(); $cutting->setAttribute('available_actions', $actions); - $cutting->setAttribute('is_editable', $this->isEditable($cutting->status)); + $cutting->setAttribute('is_editable', $cutting->status->isEditable()); $this->appendCostPreview($cutting); }); } - /** - * @return Collection - */ + public function getCompletedCuttings(User $user): Collection { return Cutting::query() @@ -164,20 +113,18 @@ public function getCompletedCuttings(User $user): Collection ->latest() ->get() ->each(function (Cutting $cutting) use ($user): void { - $actions = collect($this->availableActions($cutting->status)) + $actions = collect($cutting->status->availableActions()) ->filter(fn (array $action) => $user->can($action['permission'])) ->values() ->all(); $cutting->setAttribute('available_actions', $actions); - $cutting->setAttribute('is_editable', $this->isEditable($cutting->status)); + $cutting->setAttribute('is_editable', $cutting->status->isEditable()); $this->appendCostPreview($cutting); }); } - /** - * @return Collection - */ + public function rawMaterialCatalog(?Cutting $cutting = null, ?User $user = null): Collection { $selectedPriceIds = $cutting @@ -212,9 +159,7 @@ public function rawMaterialCatalog(?Cutting $cutting = null, ?User $user = null) }); } - /** - * @return Collection - */ + public function productCatalog(?Cutting $cutting = null, ?User $user = null): Collection { $selectedVariantIds = $cutting @@ -284,9 +229,7 @@ public function findForEdit(Cutting $cutting): Cutting return $cutting; } - /** - * @return list> - */ + public function draftMaterialsForUser(User $user): array { return $this->draftMaterialsQuery($user) @@ -300,9 +243,7 @@ public function draftMaterialsForUser(User $user): array ->all(); } - /** - * @return list> - */ + public function draftResultsForUser(User $user): array { return $this->draftResultsQuery($user) @@ -316,10 +257,7 @@ public function draftResultsForUser(User $user): array ->all(); } - /** - * @param array $validated - * @return array - */ + public function syncDraftMaterial(array $validated, User $user): array { $price = RawMaterialPrice::query() @@ -353,10 +291,7 @@ public function syncDraftMaterial(array $validated, User $user): array return $this->presentDraftMaterial($item); } - /** - * @param array $validated - * @return array - */ + public function syncDraftResult(array $validated, User $user): array { $variant = ProductVariant::query() @@ -430,20 +365,18 @@ public function removeDraftResult(User $user, ProductVariant $productVariant): v ->delete(); } - /** - * @param array $validated - */ + public function create(array $validated, User $user): Cutting { try { $cutting = DB::transaction(function () use ($validated, $user): Cutting { - /** @var EloquentCollection $draftMaterials */ + $draftMaterials = $this->draftMaterialsQuery($user) ->with('rawMaterialPrice.rawMaterial') ->lockForUpdate() ->get(); - /** @var EloquentCollection $draftResults */ + $draftResults = $this->draftResultsQuery($user) ->lockForUpdate() ->get(); @@ -507,12 +440,10 @@ public function create(array $validated, User $user): Cutting return $cutting; } - /** - * @param array $validated - */ + public function update(Cutting $cutting, array $validated): void { - if (! $this->isEditable($cutting->status)) { + if (! $cutting->status->isEditable()) { throw ValidationException::withMessages([ 'status' => 'Proses cutting tidak dapat diubah.', ]); @@ -630,7 +561,7 @@ public function transitionStatus( ?array $results = null, ?array $resultPrices = null, ): void { - if (! $this->canTransitionTo($cutting->status, $status)) { + if (! $cutting->status->canTransitionTo($status)) { throw ValidationException::withMessages([ 'status' => 'Status proses cutting tidak dapat diubah.', ]); @@ -720,10 +651,7 @@ public function transitionStatus( ); } - /** - * @param list $materials - * @return list - */ + private function buildMaterials(array $materials): array { return collect($materials) @@ -754,10 +682,7 @@ private function buildMaterials(array $materials): array ->all(); } - /** - * @param list $results - * @return list - */ + private function buildResults(array $results): array { return collect($results) @@ -909,9 +834,7 @@ private function applySorting(Builder $query, string $sort, string $direction): $query->latest(); } - /** - * @return Builder - */ + private function draftMaterialsQuery(User $user): Builder { return CuttingMaterial::query() @@ -919,9 +842,7 @@ private function draftMaterialsQuery(User $user): Builder ->where('user_id', $user->id); } - /** - * @return Builder - */ + private function draftResultsQuery(User $user): Builder { return CuttingResult::query() @@ -929,9 +850,7 @@ private function draftResultsQuery(User $user): Builder ->where('user_id', $user->id); } - /** - * @return array - */ + private function presentDraftMaterial(CuttingMaterial $item): array { $price = $item->rawMaterialPrice; @@ -949,9 +868,7 @@ private function presentDraftMaterial(CuttingMaterial $item): array ]; } - /** - * @return array - */ + private function presentDraftResult(CuttingResult $item): array { $variant = $item->productVariant; @@ -1019,9 +936,7 @@ public function calculateCostPerUnit(Cutting $cutting): int return (int) round($this->calculateTotalProductionCost($cutting) / $totalPieces); } - /** - * @param list}> $resultPrices - */ + private function storeResultPrices(Cutting $cutting, array $resultPrices): void { $costPerUnit = (int) ($cutting->cost_per_unit ?? $this->calculateCostPerUnit($cutting)); diff --git a/app/Services/Manage/OrderService.php b/app/Services/Manage/OrderService.php index 614b0c2..38af14f 100644 --- a/app/Services/Manage/OrderService.php +++ b/app/Services/Manage/OrderService.php @@ -5,7 +5,6 @@ use App\Enums\OrderChannel; use App\Enums\OrderStatus; use App\Enums\PaymentType; -use App\Enums\Permission; use App\Enums\PriceType; use App\Enums\ProductStockQuality; use App\Models\Customer; @@ -39,48 +38,6 @@ public function __construct( private readonly MediaService $mediaService, ) {} - public function isEditable(OrderStatus $status): bool - { - return $status->isEditable(); - } - - public function ensureEditable(Order $order): void - { - if (! $this->isEditable($order->status)) { - throw ValidationException::withMessages([ - 'status' => 'Pesanan tidak dapat diubah.', - ]); - } - } - - public function transitionStatusMessage(OrderStatus $status): string - { - return match ($status) { - OrderStatus::PROCESSING => 'Pesanan berhasil dikirim.', - OrderStatus::COMPLETED => 'Pesanan berhasil diselesaikan.', - OrderStatus::CANCELLED => 'Pesanan berhasil dibatalkan.', - default => 'Status pesanan berhasil diperbarui.', - }; - } - - public function canTransitionTo(OrderStatus $from, OrderStatus $to): bool - { - return $from->canTransitionTo($to); - } - - public function transitionPermission(OrderStatus $status): Permission - { - return $status->transitionPermission(); - } - - /** - * @return list - */ - public function availableActions(OrderStatus $status): array - { - return $status->availableActions(); - } - public function defaultPriceType(OrderChannel $channel): ?PriceType { return match ($channel) { @@ -99,9 +56,7 @@ public function stockColumn(ProductStockQuality $quality): string }; } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator { $query = Order::query() @@ -131,24 +86,22 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (Order $order) use ($user) { - $actions = collect($this->availableActions($order->status)) + $actions = collect($order->status->availableActions()) ->filter(fn (array $action) => $user->can($action['permission'])) ->values() ->all(); $order->setAttribute('available_actions', $actions); - $order->setAttribute('is_editable', $this->isEditable($order->status)); + $order->setAttribute('is_editable', $order->status->isEditable()); return $order; }); } - /** - * @return list - */ + public function customerOptions(): array { return Customer::query() @@ -161,9 +114,7 @@ public function customerOptions(): array ->all(); } - /** - * @return list - */ + public function marketingOptions(): array { return User::query() @@ -179,9 +130,7 @@ public function marketingOptions(): array ->all(); } - /** - * @return list - */ + public function storePriceTypeOptions(): array { return collect(PriceType::cases()) @@ -194,9 +143,7 @@ public function storePriceTypeOptions(): array ->all(); } - /** - * @return Collection - */ + public function catalogItems(?Order $order = null, ?User $user = null): Collection { $orderVariantIds = $order @@ -318,19 +265,17 @@ public function findForShow(Order $order): Order } }); - $availableActions = collect($this->availableActions($order->status)) + $availableActions = collect($order->status->availableActions()) ->values() ->all(); $order->setAttribute('available_actions', $availableActions); - $order->setAttribute('is_editable', $this->isEditable($order->status)); + $order->setAttribute('is_editable', $order->status->isEditable()); return $order; } - /** - * @return list> - */ + public function draftItemsForUser(User $user): array { return $this->draftItemsQuery($user) @@ -344,10 +289,7 @@ public function draftItemsForUser(User $user): array ->all(); } - /** - * @param array $validated - * @return array - */ + public function syncDraftItem(array $validated, User $user): array { // Force retail price type and stock quality for cashier role @@ -405,9 +347,7 @@ public function removeDraftItem(User $user, ProductVariant $productVariant, Prod ->delete(); } - /** - * @return list> - */ + public function resyncDraftPrices(User $user, string $priceTypeValue): array { // Force retail price type for cashier role @@ -440,9 +380,7 @@ public function resyncDraftPrices(User $user, string $priceTypeValue): array return $this->draftItemsForUser($user); } - /** - * @param array $validated - */ + public function create(array $validated, User $user): Order { try { @@ -457,7 +395,7 @@ public function create(array $validated, User $user): Order $order = DB::transaction(function () use ($validated, $user): Order { $priceType = $this->resolvePriceType($validated['channel'], $validated['price_type']); - /** @var EloquentCollection $draftItems */ + $draftItems = $this->draftItemsQuery($user) ->lockForUpdate() ->get(); @@ -563,16 +501,10 @@ public function create(array $validated, User $user): Order return $order; } - /** - * @param array $validated - */ + public function update(Order $order, array $validated): void { - if (! $this->isEditable($order->status)) { - throw ValidationException::withMessages([ - 'status' => 'Pesanan tidak dapat diubah.', - ]); - } + $order->ensureEditable(); try { DB::transaction(function () use ($order, $validated): void { @@ -591,36 +523,38 @@ public function update(Order $order, array $validated): void $negoPrice = isset($validated['nego_price']) && $validated['nego_price'] !== '' ? (int) $validated['nego_price'] : null; $totalAmount = $negoPrice !== null ? max($negoPrice, 0) : max($subtotal - $discount, 0); $channel = OrderChannel::from($validated['channel']); - - $order->customer_id = $validated['customer_id'] ?? null; - $order->marketing_id = $validated['marketing_id'] ?? null; - $order->channel = $channel; - $order->price_type = $priceType; - $order->payment_type = PaymentType::from($validated['payment_type']); - $order->is_affiliate = $validated['is_affiliate'] ?? false; - $order->tiktok_order_id = $validated['tiktok_order_id'] ?? null; - $order->shopee_order_id = $validated['shopee_order_id'] ?? null; - $order->subtotal = $subtotal; - $order->discount = $discount; - $order->nego_price = $negoPrice; - $order->marketplace_settings_snapshot = $this->marketplaceService->buildOrderSnapshot( - $channel, - $totalAmount, - $this->lineItemsForSnapshot($lineItems), - $validated['is_affiliate'] ?? false, - ); - $order->total_amount = $totalAmount; - $order->notes = $validated['notes'] ?? null; + $status = $order->status; if (isset($validated['status'])) { $newStatus = OrderStatus::from($validated['status']); if ($order->status->canTransitionTo($newStatus) || $newStatus === $order->status) { - $order->status = $newStatus; + $status = $newStatus; } } - $order->save(); + $order->update([ + 'customer_id' => $validated['customer_id'] ?? null, + 'marketing_id' => $validated['marketing_id'] ?? null, + 'channel' => $channel, + 'price_type' => $priceType, + 'payment_type' => PaymentType::from($validated['payment_type']), + 'is_affiliate' => $validated['is_affiliate'] ?? false, + 'tiktok_order_id' => $validated['tiktok_order_id'] ?? null, + 'shopee_order_id' => $validated['shopee_order_id'] ?? null, + 'subtotal' => $subtotal, + 'discount' => $discount, + 'nego_price' => $negoPrice, + 'marketplace_settings_snapshot' => $this->marketplaceService->buildOrderSnapshot( + $channel, + $totalAmount, + $this->lineItemsForSnapshot($lineItems), + $validated['is_affiliate'] ?? false, + ), + 'total_amount' => $totalAmount, + 'notes' => $validated['notes'] ?? null, + 'status' => $status, + ]); $this->syncPhotos($order, $validated); @@ -673,7 +607,7 @@ public function delete(Order $order): void DB::transaction(function () use ($order): void { $order->load('items'); - if ($this->isEditable($order->status)) { + if ($order->status->isEditable()) { foreach ($order->items as $item) { $this->incrementStock($item); } @@ -708,7 +642,7 @@ public function delete(Order $order): void public function transitionStatus(Order $order, OrderStatus $status): void { - if (! $this->canTransitionTo($order->status, $status)) { + if (! $order->status->canTransitionTo($status)) { throw ValidationException::withMessages([ 'status' => 'Status pesanan tidak dapat diubah.', ]); @@ -752,10 +686,7 @@ public function transitionStatus(Order $order, OrderStatus $status): void ); } - /** - * @param list $items - * @return list - */ + private function buildLineItems(array $items, PriceType $priceType): array { return collect($items) @@ -814,9 +745,7 @@ private function resolvePriceType(string $channel, string $priceType): PriceType return $priceTypeEnum; } - /** - * @return Builder - */ + private function draftItemsQuery(User $user): Builder { return OrderItem::query() @@ -824,9 +753,7 @@ private function draftItemsQuery(User $user): Builder ->where('user_id', $user->id); } - /** - * @return array - */ + private function presentDraftItem(OrderItem $item): array { $variant = $item->productVariant; @@ -843,9 +770,7 @@ private function presentDraftItem(OrderItem $item): array ]; } - /** - * @param EloquentCollection $items - */ + private function applyDraftPrices(EloquentCollection $items, PriceType $priceType): void { foreach ($items as $index => $item) { @@ -884,9 +809,7 @@ private function availableStock(ProductVariant $variant, ProductStockQuality $st return (int) $variant->{$this->stockColumn($stockQuality)}; } - /** - * @param array $validated - */ + private function syncPhotos(Order $order, array $validated): void { $paymentType = $validated['payment_type'] ?? null; @@ -903,10 +826,7 @@ private function syncPhotos(Order $order, array $validated): void ); } - /** - * @param EloquentCollection|list $items - * @return list - */ + private function lineItemsForSnapshot(EloquentCollection|array $items): array { return collect($items) @@ -942,17 +862,7 @@ private function resolveUnitPrice(int $variantId, PriceType $priceType, ?string return (int) $price->price; } - /** - * @return list - */ + private function presentVariantPrices(int $variantId): array { return collect($this->cuttingResultPriceResolver->latestPricesForVariant($variantId)) diff --git a/app/Services/Manage/OwnerVerificationService.php b/app/Services/Manage/OwnerVerificationService.php index 8f99563..3d60f38 100644 --- a/app/Services/Manage/OwnerVerificationService.php +++ b/app/Services/Manage/OwnerVerificationService.php @@ -47,9 +47,7 @@ public function hasPendingMarketplaceVerification(): bool ->exists(); } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex( User $user, array $tableQuery, @@ -57,7 +55,7 @@ public function paginateForIndex( string $subjectType = '', string $action = '', ): LengthAwarePaginator { - $perPage = 10; + $perPage = 25; $page = Paginator::resolveCurrentPage(); $includeCuttings = $this->shouldIncludeCuttings($user, $status, $subjectType, $action); $cuttingRows = $includeCuttings @@ -103,9 +101,7 @@ public function paginateForIndex( ->through(fn (OwnerVerificationRequest $request) => $this->presentRequestRow($request)); } - /** - * @return Collection> - */ + private function pendingCuttingRows(User $user, array $tableQuery): Collection { $rows = $this->stockService @@ -147,9 +143,7 @@ public function pendingCountForUser(User $user): int return $requestCount + Cutting::query()->pendingVerification()->count(); } - /** - * @return list - */ + public function subjectTypeOptions(User $user): array { $options = OwnerVerificationRequest::query() @@ -323,9 +317,7 @@ private function clearVerificationRequestMedia(OwnerVerificationRequest $request }; } - /** - * @return array - */ + private function presentRequestRow(OwnerVerificationRequest $request): array { $payload = is_array($request->payload) ? $request->payload : []; @@ -355,9 +347,7 @@ private function presentRequestRow(OwnerVerificationRequest $request): array ]; } - /** - * @return array - */ + private function presentCuttingRow(Cutting $cutting): array { $totalPieces = $cutting->results->sum('cutting_result'); @@ -389,9 +379,7 @@ private function presentCuttingRow(Cutting $cutting): array ]; } - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + private function buildVerificationRequestQuery( User $user, array $tableQuery, @@ -466,9 +454,7 @@ private function shouldIncludeCuttings( return Cutting::query()->pendingVerification()->exists(); } - /** - * @param Collection> $items - */ + private function makePaginator( Collection $items, int $total, diff --git a/app/Services/Manage/PurchaseService.php b/app/Services/Manage/PurchaseService.php index 8d58562..f224f49 100644 --- a/app/Services/Manage/PurchaseService.php +++ b/app/Services/Manage/PurchaseService.php @@ -31,9 +31,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery): LengthAwarePaginator { $query = Purchase::query() @@ -59,7 +57,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (Purchase $purchase) { $purchase->setAttribute( @@ -78,9 +76,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator }); } - /** - * @return list - */ + public function supplierOptions(): array { return Supplier::query() @@ -93,9 +89,7 @@ public function supplierOptions(): array ->all(); } - /** - * @return Collection - */ + public function catalogItems(?Purchase $purchase = null, ?User $user = null): Collection { $purchasePriceIds = $purchase @@ -156,9 +150,7 @@ public function findForEdit(Purchase $purchase): Purchase return $purchase; } - /** - * @return list> - */ + public function draftItemsForUser(User $user): array { return $this->draftItemsQuery($user) @@ -172,10 +164,7 @@ public function draftItemsForUser(User $user): array ->all(); } - /** - * @param array $validated - * @return array - */ + public function syncDraftItem(array $validated, User $user): array { $price = RawMaterialPrice::query() @@ -216,14 +205,12 @@ public function removeDraftItem(User $user, RawMaterialPrice $rawMaterialPrice): ->delete(); } - /** - * @param array $validated - */ + public function create(array $validated, User $user): Purchase { try { $purchase = DB::transaction(function () use ($validated, $user): Purchase { - /** @var EloquentCollection $draftItems */ + $draftItems = $this->draftItemsQuery($user) ->lockForUpdate() ->get(); @@ -295,9 +282,7 @@ public function create(array $validated, User $user): Purchase return $purchase; } - /** - * @param array $validated - */ + public function update(Purchase $purchase, array $validated, User $user): void { $purchase->load(['supplier', 'items.rawMaterialPrice.rawMaterial:id,name']); @@ -449,10 +434,7 @@ public function applyDelete(OwnerVerificationRequest $verificationRequest): void $this->executeDelete($purchase); } - /** - * @param list $items - * @return list - */ + private function buildLineItems(array $items): array { return collect($items) @@ -479,9 +461,7 @@ private function buildLineItems(array $items): array ->all(); } - /** - * @param array $validated - */ + private function syncPhotos(Purchase $purchase, array $validated): void { $this->mediaService->syncCollection( @@ -495,9 +475,7 @@ private function syncPhotos(Purchase $purchase, array $validated): void ); } - /** - * @return Builder - */ + private function draftItemsQuery(User $user): Builder { return PurchaseItem::query() @@ -505,9 +483,7 @@ private function draftItemsQuery(User $user): Builder ->where('user_id', $user->id); } - /** - * @return array - */ + private function presentDraftItem(PurchaseItem $item): array { $price = $item->rawMaterialPrice; @@ -585,9 +561,7 @@ private function executeDelete(Purchase $purchase): void }); } - /** - * @param array $payload - */ + private function applyPayloadToPurchase( Purchase $purchase, array $payload, @@ -627,9 +601,7 @@ private function applyPayloadToPurchase( }); } - /** - * @return array - */ + private function snapshotPurchase(Purchase $purchase): array { $purchase->load([ @@ -659,10 +631,7 @@ private function snapshotPurchase(Purchase $purchase): array ]; } - /** - * @param array $validated - * @return array - */ + private function buildPayloadFromValidated(array $validated): array { $lineItems = $this->enrichLineItems($this->buildLineItems($validated['items'])); @@ -685,10 +654,7 @@ private function buildPayloadFromValidated(array $validated): array ]; } - /** - * @param list $lineItems - * @return list> - */ + private function enrichLineItems(array $lineItems): array { $prices = RawMaterialPrice::query() @@ -709,9 +675,7 @@ private function enrichLineItems(array $lineItems): array ->all(); } - /** - * @param array $validated - */ + private function syncRequestPhotos(OwnerVerificationRequest $verificationRequest, array $validated): void { $this->mediaService->syncCollection( @@ -725,9 +689,7 @@ private function syncRequestPhotos(OwnerVerificationRequest $verificationRequest ); } - /** - * @param array $payload - */ + private function applyRequestPhotos( OwnerVerificationRequest $verificationRequest, Purchase $purchase, @@ -748,9 +710,7 @@ private function applyRequestPhotos( } } - /** - * @return array - */ + private function payloadNew(OwnerVerificationRequest $verificationRequest): array { $payload = $verificationRequest->payload ?? []; diff --git a/app/Services/Manage/StockRetailService.php b/app/Services/Manage/StockRetailService.php index eb0b16c..231b6fa 100644 --- a/app/Services/Manage/StockRetailService.php +++ b/app/Services/Manage/StockRetailService.php @@ -19,9 +19,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * Submit a stock retail transfer request for owner verification. - */ + public function transfer(int $variantId, int $quantity, User $user, ?string $notes = null): OwnerVerificationRequest { if ($quantity <= 0) { @@ -102,9 +100,7 @@ public function transfer(int $variantId, int $quantity, User $user, ?string $not } } - /** - * Apply the stock retail transfer when owner approves. - */ + public function applyStockRetailTransfer(OwnerVerificationRequest $request): void { $payload = $request->payload ?? []; @@ -141,9 +137,7 @@ public function applyStockRetailTransfer(OwnerVerificationRequest $request): voi }); } - /** - * Reject the stock retail transfer (no-op since nothing changed). - */ + public function rejectStockRetailTransfer(OwnerVerificationRequest $request): void { // No-op: nothing was changed yet, so nothing to rollback. diff --git a/app/Services/Manage/StockService.php b/app/Services/Manage/StockService.php index a935d22..4b084ef 100644 --- a/app/Services/Manage/StockService.php +++ b/app/Services/Manage/StockService.php @@ -20,11 +20,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * Get all completed cuttings pending verification. - * - * @return Collection - */ + public function getPendingVerificationCuttings(User $user): Collection { return Cutting::query() @@ -43,11 +39,7 @@ public function getPendingVerificationCuttings(User $user): Collection }); } - /** - * Get all cuttings pending owner approval. - * - * @return Collection - */ + public function getPendingApprovalCuttings(User $user): Collection { return Cutting::query() @@ -68,12 +60,7 @@ public function getPendingApprovalCuttings(User $user): Collection }); } - /** - * Submit verification for a completed cutting - saves data but does NOT add stock yet. - * - * @param list|null $results - * @param list}>|null $resultPrices - */ + public function submitVerification( Cutting $cutting, User $user, @@ -138,9 +125,7 @@ public function submitVerification( ); } - /** - * Owner approves verification - adds stock to store. - */ + public function approveVerification( Cutting $cutting, User $user, @@ -191,9 +176,7 @@ public function approveVerification( ); } - /** - * Owner rejects verification - sends back to completed. - */ + public function rejectVerification( Cutting $cutting, User $user, @@ -262,9 +245,7 @@ private function applyProductStockOnVerify(Cutting $cutting): void } } - /** - * @param list}> $resultPrices - */ + private function storeResultPrices(Cutting $cutting, array $resultPrices): void { // Extract harga_modal from the first variant's prices and set as cost_per_unit @@ -304,9 +285,7 @@ private function storeResultPrices(Cutting $cutting, array $resultPrices): void } } - /** - * Update product_prices from cutting result prices (called on owner approval). - */ + private function applyResultPricesToProducts(Cutting $cutting): void { $resultPrices = $cutting->resultPrices()->with('productVariant')->get(); diff --git a/app/Services/Manage/StokOpnameService.php b/app/Services/Manage/StokOpnameService.php index 5c80ae4..120d289 100644 --- a/app/Services/Manage/StokOpnameService.php +++ b/app/Services/Manage/StokOpnameService.php @@ -19,9 +19,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery): LengthAwarePaginator { $query = StokOpname::query() @@ -42,13 +40,11 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } - /** - * Get product variants with current stock for the opname form. - */ + public function catalogItems(): array { return Product::query() @@ -72,9 +68,7 @@ public function catalogItems(): array ->toArray(); } - /** - * Get stok opname with items for editing. - */ + public function findForEdit(StokOpname $stokOpname): array { $stokOpname->load(['items.productVariant.product', 'createdBy.profile']); @@ -101,9 +95,7 @@ public function findForEdit(StokOpname $stokOpname): array ]; } - /** - * @param array{opname_date: string, notes: string|null, items: list} $validated - */ + public function create(array $validated, User $user): StokOpname { try { @@ -132,9 +124,7 @@ public function create(array $validated, User $user): StokOpname } } - /** - * @param array{opname_date: string, notes: string|null, items: list} $validated - */ + public function update(StokOpname $stokOpname, array $validated): void { if ($stokOpname->status !== StokOpnameStatus::DRAFT && $stokOpname->status !== StokOpnameStatus::REJECTED) { @@ -192,9 +182,7 @@ public function delete(StokOpname $stokOpname): void } } - /** - * Submit stok opname for verification. - */ + public function submit(StokOpname $stokOpname, User $user): void { if ($stokOpname->status !== StokOpnameStatus::DRAFT && $stokOpname->status !== StokOpnameStatus::REJECTED) { @@ -235,9 +223,7 @@ public function submit(StokOpname $stokOpname, User $user): void ); } - /** - * Verify stok opname - apply stock adjustments. - */ + public function verify(StokOpname $stokOpname, User $user, ?string $verificationNotes = null): void { if ($stokOpname->status !== StokOpnameStatus::PENDING) { @@ -282,9 +268,7 @@ public function verify(StokOpname $stokOpname, User $user, ?string $verification ); } - /** - * Reject stok opname. - */ + public function reject(StokOpname $stokOpname, User $user, string $reason): void { if ($stokOpname->status !== StokOpnameStatus::PENDING) { @@ -321,11 +305,7 @@ public function reject(StokOpname $stokOpname, User $user, string $reason): void ); } - /** - * Auto-save: create or update a draft silently. - * - * @param array{stok_opname_id?: int|null, opname_date: string, notes: string|null, items: list|null} $validated - */ + public function autoSave(array $validated, User $user): StokOpname { try { @@ -364,9 +344,7 @@ public function autoSave(array $validated, User $user): StokOpname } } - /** - * @param list $items - */ + private function syncItems(StokOpname $stokOpname, array $items): void { $stokOpname->items()->delete(); diff --git a/app/Services/Master/CategoryService.php b/app/Services/Master/CategoryService.php index 2b473da..e558c2d 100644 --- a/app/Services/Master/CategoryService.php +++ b/app/Services/Master/CategoryService.php @@ -21,7 +21,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } diff --git a/app/Services/Master/CustomerService.php b/app/Services/Master/CustomerService.php index b869123..39441a3 100644 --- a/app/Services/Master/CustomerService.php +++ b/app/Services/Master/CustomerService.php @@ -23,7 +23,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } diff --git a/app/Services/Master/ProductService.php b/app/Services/Master/ProductService.php index 76bc43f..82e9215 100644 --- a/app/Services/Master/ProductService.php +++ b/app/Services/Master/ProductService.php @@ -27,9 +27,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery, string $isActive, string $categoryId = '', string $stockStatus = ''): LengthAwarePaginator { $query = Product::query() @@ -64,7 +62,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $ca $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (Product $product) { $product->variants->each(function (ProductVariant $variant): void { @@ -116,9 +114,7 @@ public function findForEdit(Product $product): Product return $product; } - /** - * @param array $validated - */ + public function create(array $validated, User $user): void { try { @@ -173,9 +169,7 @@ public function create(array $validated, User $user): void ); } - /** - * @param array $validated - */ + public function update(Product $product, array $validated, User $user): void { try { @@ -249,9 +243,7 @@ public function delete(Product $product, User $user): void ); } - /** - * @param array $validated - */ + public function toggleStatus(Product $product, array $validated, User $user): void { try { @@ -356,9 +348,7 @@ public function applyUpdate(OwnerVerificationRequest $verificationRequest): void $this->applyPayloadToProduct($product, $this->payloadNew($verificationRequest), $verificationRequest); } - /** - * @param array $payload - */ + private function applyPayloadToProduct( Product $product, array $payload, @@ -510,9 +500,7 @@ private function rollbackUpdate(OwnerVerificationRequest $verificationRequest): $this->applyPayloadToProduct($product, $oldPayload); } - /** - * @return array - */ + private function payloadOld(OwnerVerificationRequest $verificationRequest): array { $payload = $verificationRequest->payload ?? []; @@ -524,9 +512,7 @@ private function payloadOld(OwnerVerificationRequest $verificationRequest): arra return []; } - /** - * @return array - */ + private function payloadNew(OwnerVerificationRequest $verificationRequest): array { $payload = $verificationRequest->payload ?? []; @@ -538,9 +524,7 @@ private function payloadNew(OwnerVerificationRequest $verificationRequest): arra return $payload; } - /** - * @return array - */ + private function snapshotProduct(Product $product): array { $product->load(['categories', 'variants']); @@ -561,10 +545,7 @@ private function snapshotProduct(Product $product): array ]); } - /** - * @param array $data - * @return array - */ + private function enrichPayload(array $data): array { $categoryIds = $data['category_ids'] ?? []; @@ -576,10 +557,7 @@ private function enrichPayload(array $data): array return $data; } - /** - * @param array $validated - * @return array - */ + private function buildPayloadFromValidated(array $validated): array { return [ @@ -598,9 +576,7 @@ private function buildPayloadFromValidated(array $validated): array ]; } - /** - * @param array $variantData - */ + private function syncVariantImages( ProductVariant $variant, array $variantData, @@ -617,9 +593,7 @@ private function syncVariantImages( ); } - /** - * @param array $variantData - */ + private function syncRequestVariantImages( OwnerVerificationRequest $verificationRequest, array $variantData, @@ -639,9 +613,7 @@ private function syncRequestVariantImages( ); } - /** - * @param array $variantData - */ + private function applyVariantImageChanges( OwnerVerificationRequest $verificationRequest, ProductVariant $variant, diff --git a/app/Services/Master/RawMaterialService.php b/app/Services/Master/RawMaterialService.php index 2fd7b37..3d54598 100644 --- a/app/Services/Master/RawMaterialService.php +++ b/app/Services/Master/RawMaterialService.php @@ -27,9 +27,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery - */ + public function paginateForIndex(array $tableQuery, string $isActive, string $stockStatus = ''): LengthAwarePaginator { $query = RawMaterial::query() @@ -72,7 +70,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $st $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(function (RawMaterial $rawMaterial) { $rawMaterial->prices->each(function (RawMaterialPrice $price): void { @@ -110,9 +108,7 @@ public function findForEdit(RawMaterial $rawMaterial): RawMaterial return $rawMaterial; } - /** - * @param array $validated - */ + public function create(array $validated, User $user): void { try { @@ -159,9 +155,7 @@ public function create(array $validated, User $user): void ); } - /** - * @param array $validated - */ + public function update(RawMaterial $rawMaterial, array $validated, User $user): void { try { @@ -235,9 +229,7 @@ public function delete(RawMaterial $rawMaterial, User $user): void ); } - /** - * @param array $validated - */ + public function toggleStatus(RawMaterial $rawMaterial, array $validated, User $user): void { try { @@ -395,9 +387,7 @@ public function applyToggleStatus(OwnerVerificationRequest $verificationRequest) ]); } - /** - * @param array $payload - */ + private function applyPayloadToRawMaterial( RawMaterial $rawMaterial, array $payload, @@ -492,9 +482,7 @@ private function rollbackUpdate(OwnerVerificationRequest $verificationRequest): $this->applyPayloadToRawMaterial($rawMaterial, $oldPayload); } - /** - * @return array - */ + private function payloadOld(OwnerVerificationRequest $verificationRequest): array { $payload = $verificationRequest->payload ?? []; @@ -506,9 +494,7 @@ private function payloadOld(OwnerVerificationRequest $verificationRequest): arra return []; } - /** - * @return array - */ + private function payloadNew(OwnerVerificationRequest $verificationRequest): array { $payload = $verificationRequest->payload ?? []; @@ -520,9 +506,7 @@ private function payloadNew(OwnerVerificationRequest $verificationRequest): arra return $payload; } - /** - * @return array - */ + private function snapshotRawMaterial(RawMaterial $rawMaterial): array { $rawMaterial->load(['prices']); @@ -542,10 +526,7 @@ private function snapshotRawMaterial(RawMaterial $rawMaterial): array ]); } - /** - * @param array $data - * @return array - */ + private function enrichPayload(array $data): array { if (isset($data['unit'])) { @@ -560,10 +541,7 @@ private function enrichPayload(array $data): array return $data; } - /** - * @param array $validated - * @return array - */ + private function buildPayloadFromValidated(array $validated): array { return [ @@ -592,9 +570,7 @@ private function applySorting(Builder $query, string $sort, string $direction): $query->latest(); } - /** - * @param array $priceData - */ + private function createPrice(RawMaterial $rawMaterial, array $priceData, int $index): RawMaterialPrice { $price = $rawMaterial->prices()->create([ @@ -608,9 +584,7 @@ private function createPrice(RawMaterial $rawMaterial, array $priceData, int $in return $price; } - /** - * @param array $priceData - */ + private function syncPriceImages(RawMaterialPrice $price, array $priceData, int $index): void { $this->mediaService->syncCollection( @@ -624,9 +598,7 @@ private function syncPriceImages(RawMaterialPrice $price, array $priceData, int ); } - /** - * @param array $priceData - */ + private function syncRequestPriceImages( OwnerVerificationRequest $verificationRequest, array $priceData, @@ -646,9 +618,7 @@ private function syncRequestPriceImages( ); } - /** - * @param array $priceData - */ + private function applyPriceImageChanges( OwnerVerificationRequest $verificationRequest, RawMaterialPrice $price, diff --git a/app/Services/Master/SupplierService.php b/app/Services/Master/SupplierService.php index ffbc0a1..3a83aea 100644 --- a/app/Services/Master/SupplierService.php +++ b/app/Services/Master/SupplierService.php @@ -23,7 +23,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } diff --git a/app/Services/Media/MediaService.php b/app/Services/Media/MediaService.php index 5364c04..0983b4a 100644 --- a/app/Services/Media/MediaService.php +++ b/app/Services/Media/MediaService.php @@ -12,10 +12,7 @@ class MediaService { - /** - * @param list|null $newFiles - * @param list|null $removeIds - */ + public function syncCollection( HasMedia $model, string $collection, @@ -118,9 +115,7 @@ public function replaceSingleFile( return $this->addUploadedFile($model, $file, $collection, $type); } - /** - * @return array{module: string, type?: string} - */ + private function customProperties(HasMedia $model, ?string $type): array { $properties = [ diff --git a/app/Services/System/ActivityLogService.php b/app/Services/System/ActivityLogService.php index b2999d5..c7be4a1 100644 --- a/app/Services/System/ActivityLogService.php +++ b/app/Services/System/ActivityLogService.php @@ -36,7 +36,7 @@ public function paginateForIndex(array $tableQuery, string $event = '', string $ $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString() ->through(fn (Activity $activity) => [ 'id' => $activity->id, diff --git a/app/Services/System/DashboardService.php b/app/Services/System/DashboardService.php index 0b4f946..851c09c 100644 --- a/app/Services/System/DashboardService.php +++ b/app/Services/System/DashboardService.php @@ -19,9 +19,7 @@ class DashboardService { - /** - * @return array|null - */ + public function getTodayAttendanceForUser(User $user): ?array { $employee = $user->employee; diff --git a/app/Services/System/PushNotificationService.php b/app/Services/System/PushNotificationService.php index 4188228..f19fed8 100644 --- a/app/Services/System/PushNotificationService.php +++ b/app/Services/System/PushNotificationService.php @@ -6,27 +6,19 @@ class PushNotificationService { - /** - * Send notification to users with specific roles. - * - * @param list $roles - */ + public function sendToRoles(string $title, string $body, array $roles, string $url = '/admin/dashboard'): void { SendPushNotificationJob::dispatch($title, $body, $url, $roles); } - /** - * Send notification to a specific user. - */ + public function sendToUser(string $title, string $body, int $userId, string $url = '/admin/dashboard'): void { SendPushNotificationJob::dispatch($title, $body, $url, [], $userId); } - /** - * Send notification to all users. - */ + public function sendToAll(string $title, string $body, string $url = '/admin/dashboard'): void { SendPushNotificationJob::dispatch($title, $body, $url, []); diff --git a/app/Services/System/PushSubscriptionService.php b/app/Services/System/PushSubscriptionService.php index 731267e..01e1334 100644 --- a/app/Services/System/PushSubscriptionService.php +++ b/app/Services/System/PushSubscriptionService.php @@ -6,11 +6,7 @@ class PushSubscriptionService { - /** - * Store or update a push subscription. - * - * @param array $validated - */ + public function updateOrCreateSubscription(array $validated, int $userId): void { PushSubscription::updateOrCreate( @@ -25,9 +21,7 @@ public function updateOrCreateSubscription(array $validated, int $userId): void ); } - /** - * Delete a push subscription. - */ + public function deleteSubscription(string $endpoint, int $userId): void { PushSubscription::where('user_id', $userId) diff --git a/app/Services/System/RoleService.php b/app/Services/System/RoleService.php index c610cb0..117dade 100644 --- a/app/Services/System/RoleService.php +++ b/app/Services/System/RoleService.php @@ -28,7 +28,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query - ->paginate(10) + ->paginate(25) ->withQueryString(); } diff --git a/app/Services/System/Setting/MarketplaceService.php b/app/Services/System/Setting/MarketplaceService.php index f9f6361..f73284a 100644 --- a/app/Services/System/Setting/MarketplaceService.php +++ b/app/Services/System/Setting/MarketplaceService.php @@ -20,9 +20,7 @@ public function __construct( private readonly PushNotificationService $pushNotificationService, ) {} - /** - * @return list - */ + private function tiktokFeeKeys(): array { return [ @@ -35,9 +33,7 @@ private function tiktokFeeKeys(): array ]; } - /** - * @return list - */ + private function shopeeFeeKeys(): array { return [ @@ -157,10 +153,7 @@ public function applyVerificationRequest(OwnerVerificationRequest $request): voi $this->saveSettings($newPayload); } - /** - * @param list $lineItems - * @return array|null - */ + public function buildOrderSnapshot(OrderChannel $channel, int $totalAmount, array $lineItems, bool $isAffiliate = false): ?array { $feeSnapshot = $this->feeRulesForChannel($channel); @@ -190,10 +183,7 @@ public function buildOrderSnapshot(OrderChannel $channel, int $totalAmount, arra ]; } - /** - * @param array $fees - * @return array - */ + private function excludeAffiliateFee(array $fees, OrderChannel $channel): array { $affiliateKey = match ($channel) { @@ -213,9 +203,7 @@ private function excludeAffiliateFee(array $fees, OrderChannel $channel): array return $fees; } - /** - * @return array{platform: string, fees: array}|null - */ + private function feeRulesForChannel(OrderChannel $channel): ?array { $settings = app(MarketplaceSettings::class); @@ -250,19 +238,13 @@ private function feeRulesForChannel(OrderChannel $channel): ?array }; } - /** - * @param array{scope: string, value_type: string, value: float|int|string} $data - * @return array{scope: string, value_type: string, value: float} - */ + private function presentFeeRule(array $data): array { return MarketplaceFeeRule::fromArray($data)->toArray(); } - /** - * @param array{scope: string, value_type: string, value: float|int|string} $data - * @return array{scope: string, value_type: string, value: float} - */ + private function normalizeFeeRule(array $data): array { return MarketplaceFeeRule::fromArray($data)->toArray();