From 46af27e537b5dfffba98f08db68df580b9731102 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 8 Aug 2026 14:31:58 +0700 Subject: [PATCH] refactor: rename create/delete methods to store/destroy across multiple services; add transaction handling for cash and stock adjustments --- .../Admin/Finance/EmployeeAdvanceService.php | 4 +- app/Services/Admin/Finance/ExpenseService.php | 4 +- .../Finance/PayrollAdjustmentService.php | 4 +- .../Admin/Finance/PayrollPeriodService.php | 10 +++ app/Services/Admin/HR/AttendanceService.php | 36 ++++++++++- app/Services/Admin/HR/EmployeeService.php | 4 +- app/Services/Admin/HR/LeaveRequestService.php | 9 ++- app/Services/Admin/Manage/CuttingService.php | 20 +----- app/Services/Admin/Manage/PurchaseService.php | 6 -- app/Services/Admin/Master/CategoryService.php | 4 +- app/Services/Admin/Master/CustomerService.php | 4 +- .../Master/RawMaterial/RawMaterialService.php | 24 +++++++- app/Services/Admin/Master/SupplierService.php | 4 +- app/Services/Admin/Settings/RoleService.php | 33 ++++++---- .../Concerns/HandlesCashTransactions.php | 61 ++++++++++--------- app/Services/Concerns/HasStockAdjustment.php | 14 ++--- app/Services/DashboardService.php | 6 +- app/Services/NotificationService.php | 18 ++++-- app/Services/StockMutationService.php | 48 ++++++++------- 19 files changed, 188 insertions(+), 125 deletions(-) diff --git a/app/Services/Admin/Finance/EmployeeAdvanceService.php b/app/Services/Admin/Finance/EmployeeAdvanceService.php index 29db41c..fb9773e 100644 --- a/app/Services/Admin/Finance/EmployeeAdvanceService.php +++ b/app/Services/Admin/Finance/EmployeeAdvanceService.php @@ -43,7 +43,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): EmployeeAdvance + public function store(array $data): EmployeeAdvance { $employee = auth()->user()->employee; @@ -123,7 +123,7 @@ public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeA return $employeeAdvance; } - public function delete(EmployeeAdvance $employeeAdvance): bool + public function destroy(EmployeeAdvance $employeeAdvance): bool { return DB::transaction(function () use ($employeeAdvance) { if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) { diff --git a/app/Services/Admin/Finance/ExpenseService.php b/app/Services/Admin/Finance/ExpenseService.php index a9f45fc..f563337 100644 --- a/app/Services/Admin/Finance/ExpenseService.php +++ b/app/Services/Admin/Finance/ExpenseService.php @@ -75,7 +75,7 @@ private function formatExpense(Expense $expense): array ]; } - public function create(array $data): Expense + public function store(array $data): Expense { $expense = DB::transaction(function () use ($data) { $cashTransaction = $this->debitCash( @@ -173,7 +173,7 @@ public function update(Expense $expense, array $data): Expense return $expense; } - public function delete(Expense $expense): bool + public function destroy(Expense $expense): bool { return DB::transaction(function () use ($expense) { $this->creditCash( diff --git a/app/Services/Admin/Finance/PayrollAdjustmentService.php b/app/Services/Admin/Finance/PayrollAdjustmentService.php index 386bffb..eec9226 100644 --- a/app/Services/Admin/Finance/PayrollAdjustmentService.php +++ b/app/Services/Admin/Finance/PayrollAdjustmentService.php @@ -11,7 +11,7 @@ class PayrollAdjustmentService { - public function create(Payroll $payroll, array $data): PayrollAdjustment + public function store(Payroll $payroll, array $data): PayrollAdjustment { if ($payroll->status !== PayrollStatus::UNPAID) { throw ValidationException::withMessages([ @@ -35,7 +35,7 @@ public function create(Payroll $payroll, array $data): PayrollAdjustment }); } - public function delete(PayrollAdjustment $adjustment): bool + public function destroy(PayrollAdjustment $adjustment): bool { $payroll = $adjustment->payroll; diff --git a/app/Services/Admin/Finance/PayrollPeriodService.php b/app/Services/Admin/Finance/PayrollPeriodService.php index a229a57..a582bec 100644 --- a/app/Services/Admin/Finance/PayrollPeriodService.php +++ b/app/Services/Admin/Finance/PayrollPeriodService.php @@ -23,6 +23,16 @@ private function canViewAll(): bool return auth()->user()->hasAnyRole(['developer', 'owner', 'direktur', 'admin-toko']); } + public function getCurrentOrCreate(): PayrollPeriod + { + $now = now(); + + return PayrollPeriod::firstOrCreate( + ['year' => $now->year, 'month' => $now->month], + ['status' => PayrollPeriodStatus::OPEN] + ); + } + public function getAll(array $filters = []): Collection { return PayrollPeriod::select(['id', 'year', 'month', 'status', 'closed_at', 'created_at']) diff --git a/app/Services/Admin/HR/AttendanceService.php b/app/Services/Admin/HR/AttendanceService.php index 9f12e7e..2a5b372 100644 --- a/app/Services/Admin/HR/AttendanceService.php +++ b/app/Services/Admin/HR/AttendanceService.php @@ -9,9 +9,11 @@ use App\Services\Concerns\RegistersMedia; use App\Services\NotificationService; use App\Services\S3PresignedService; +use App\Settings\HRSettings; use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; +use Illuminate\Validation\ValidationException; class AttendanceService { @@ -21,6 +23,32 @@ public function __construct( private S3PresignedService $s3Service, ) {} + public function getIndexData(int $year, int $month): array + { + $user = auth()->user(); + $isAdmin = $user->hasAnyRole(['developer', 'owner']); + $hrSettings = app(HRSettings::class); + + $employeeId = $isAdmin ? null : $user->employee?->id; + + return [ + 'attendances' => $this->getByMonth($year, $month, $employeeId), + 'leaves' => $this->getLeavesByMonth($year, $month, $employeeId), + 'employees' => $isAdmin ? $this->getAllEmployees() : [], + 'todayAttendance' => $isAdmin ? null : $this->getToday(), + 'currentYear' => $year, + 'currentMonth' => $month, + 'monthStats' => $this->getMonthStats($year, $month, $employeeId), + 'hrSettings' => [ + 'scheduled_check_in_time' => $hrSettings->scheduled_check_in_time, + 'scheduled_check_out_time' => $hrSettings->scheduled_check_out_time, + ], + 'isOnLeave' => $isAdmin ? false : $this->isOnLeave($user), + 'canCheckIn' => $isAdmin ? false : $user->employee !== null, + 'isAdmin' => $isAdmin, + ]; + } + public function getAll(): Collection { return Attendance::with(['employee.user.userProfile', 'media']) @@ -217,7 +245,9 @@ public function checkIn(array $data): Attendance $employee = auth()->user()->employee; if (! $employee) { - throw new \Exception('Anda tidak terdaftar sebagai karyawan.'); + throw ValidationException::withMessages([ + 'employee' => 'Anda tidak terdaftar sebagai karyawan.', + ]); } $today = now()->toDateString(); @@ -227,7 +257,9 @@ public function checkIn(array $data): Attendance ->first(); if ($existing) { - throw new \Exception('Anda sudah melakukan presensi hari ini.'); + throw ValidationException::withMessages([ + 'attendance' => 'Anda sudah melakukan presensi hari ini.', + ]); } $attendance = Attendance::create([ diff --git a/app/Services/Admin/HR/EmployeeService.php b/app/Services/Admin/HR/EmployeeService.php index e2d5a2a..2e9d859 100644 --- a/app/Services/Admin/HR/EmployeeService.php +++ b/app/Services/Admin/HR/EmployeeService.php @@ -66,7 +66,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): User + public function store(array $data): User { return DB::transaction(function () use ($data) { $user = User::create([ @@ -132,7 +132,7 @@ public function update(User $user, array $data): User return $user->fresh(['userProfile', 'employee']); } - public function delete(User $user): bool + public function destroy(User $user): bool { return DB::transaction(function () use ($user) { $user->employee()->delete(); diff --git a/app/Services/Admin/HR/LeaveRequestService.php b/app/Services/Admin/HR/LeaveRequestService.php index d17fcd0..af157ae 100644 --- a/app/Services/Admin/HR/LeaveRequestService.php +++ b/app/Services/Admin/HR/LeaveRequestService.php @@ -9,6 +9,7 @@ use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use Illuminate\Validation\ValidationException; class LeaveRequestService { @@ -43,13 +44,15 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): LeaveRequest + public function store(array $data): LeaveRequest { $leaveRequest = DB::transaction(function () use ($data) { $employee = auth()->user()->employee; if (! $employee) { - throw new \Exception('Anda tidak terdaftar sebagai karyawan.'); + throw ValidationException::withMessages([ + 'employee' => 'Anda tidak terdaftar sebagai karyawan.', + ]); } $startDate = new Carbon($data['start_date']); @@ -95,7 +98,7 @@ public function update(LeaveRequest $leaveRequest, array $data): LeaveRequest }); } - public function delete(LeaveRequest $leaveRequest): bool + public function destroy(LeaveRequest $leaveRequest): bool { return $leaveRequest->delete(); } diff --git a/app/Services/Admin/Manage/CuttingService.php b/app/Services/Admin/Manage/CuttingService.php index db1ba90..5f4c56b 100644 --- a/app/Services/Admin/Manage/CuttingService.php +++ b/app/Services/Admin/Manage/CuttingService.php @@ -6,8 +6,8 @@ use App\Models\CuttingMaterial; use App\Models\CuttingMaterialCombination; use App\Models\CuttingResult; -use App\Models\RawMaterial; use App\Models\RawMaterialPrice; +use App\Services\Admin\Master\RawMaterial\RawMaterialService; use App\Services\Concerns\RegistersMedia; use App\Services\S3PresignedService; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -20,6 +20,7 @@ class CuttingService public function __construct( private S3PresignedService $s3Service, + private RawMaterialService $rawMaterialService, ) {} public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc'): LengthAwarePaginator @@ -64,22 +65,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = public function getForCreate(): array { return [ - 'rawMaterials' => RawMaterial::query() - ->select(['id', 'name', 'unit', 'is_active']) - ->with([ - 'rawMaterialPrices:id,raw_material_id,variant,price,stock', - ]) - ->active() - ->orderBy('name') - ->get() - ->each(function (RawMaterial $rawMaterial) { - $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $media = $price->getFirstMedia('photos'); - $price->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) - : null; - }); - }), + 'rawMaterials' => $this->rawMaterialService->getVariantsForCutting(), ]; } diff --git a/app/Services/Admin/Manage/PurchaseService.php b/app/Services/Admin/Manage/PurchaseService.php index 3f2c9ea..4676f0a 100644 --- a/app/Services/Admin/Manage/PurchaseService.php +++ b/app/Services/Admin/Manage/PurchaseService.php @@ -334,12 +334,6 @@ public function update(Purchase $purchase, array $data): Purchase } }); - $oldMaterial = $oldItems - ->map(fn (PurchaseItem $item) => $item->rawMaterialPrice?->rawMaterial) - ->filter() - ->unique(fn (RawMaterial $material) => $material->id) - ->first(); - $oldMaterial = $oldItems ->map(fn (PurchaseItem $item) => $item->rawMaterialPrice?->rawMaterial) ->filter() diff --git a/app/Services/Admin/Master/CategoryService.php b/app/Services/Admin/Master/CategoryService.php index bb49e34..aaf70a7 100644 --- a/app/Services/Admin/Master/CategoryService.php +++ b/app/Services/Admin/Master/CategoryService.php @@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): Category + public function store(array $data): Category { return Category::create($data); } @@ -34,7 +34,7 @@ public function update(Category $category, array $data): Category return $category; } - public function delete(Category $category): bool + public function destroy(Category $category): bool { return $category->delete(); } diff --git a/app/Services/Admin/Master/CustomerService.php b/app/Services/Admin/Master/CustomerService.php index 75c0ca2..8e6db70 100644 --- a/app/Services/Admin/Master/CustomerService.php +++ b/app/Services/Admin/Master/CustomerService.php @@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): Customer + public function store(array $data): Customer { return Customer::create($data); } @@ -34,7 +34,7 @@ public function update(Customer $customer, array $data): Customer return $customer; } - public function delete(Customer $customer): bool + public function destroy(Customer $customer): bool { return $customer->delete(); } diff --git a/app/Services/Admin/Master/RawMaterial/RawMaterialService.php b/app/Services/Admin/Master/RawMaterial/RawMaterialService.php index 9c97b86..a30bbae 100644 --- a/app/Services/Admin/Master/RawMaterial/RawMaterialService.php +++ b/app/Services/Admin/Master/RawMaterial/RawMaterialService.php @@ -71,7 +71,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = return $paginator; } - public function create(array $data): RawMaterial + public function store(array $data): RawMaterial { return DB::transaction(function () use ($data) { $rawMaterial = RawMaterial::create([ @@ -220,7 +220,7 @@ public function update(RawMaterial $rawMaterial, array $data): RawMaterial }); } - public function delete(RawMaterial $rawMaterial): bool + public function destroy(RawMaterial $rawMaterial): bool { return DB::transaction(function () use ($rawMaterial) { $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { @@ -239,6 +239,26 @@ public function toggleStatus(RawMaterial $rawMaterial): void ]); } + public function getVariantsForCutting(): array + { + return RawMaterial::query() + ->select(['id', 'name', 'unit', 'is_active']) + ->with([ + 'rawMaterialPrices:id,raw_material_id,variant,price,stock', + ]) + ->active() + ->orderBy('name') + ->get() + ->each(function (RawMaterial $rawMaterial) { + $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { + $media = $price->getFirstMedia('photos'); + $price->photo_url = $media + ? $this->s3Service->getTemporaryUrl($media->file_name) + : null; + }); + }); + } + private function registerPhoto(RawMaterialPrice $price, string $s3Key): void { $this->registerMedia( diff --git a/app/Services/Admin/Master/SupplierService.php b/app/Services/Admin/Master/SupplierService.php index bbfbf86..ae4d23d 100644 --- a/app/Services/Admin/Master/SupplierService.php +++ b/app/Services/Admin/Master/SupplierService.php @@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); } - public function create(array $data): Supplier + public function store(array $data): Supplier { return Supplier::create($data); } @@ -34,7 +34,7 @@ public function update(Supplier $supplier, array $data): Supplier return $supplier; } - public function delete(Supplier $supplier): bool + public function destroy(Supplier $supplier): bool { return $supplier->delete(); } diff --git a/app/Services/Admin/Settings/RoleService.php b/app/Services/Admin/Settings/RoleService.php index c87f133..e09526b 100644 --- a/app/Services/Admin/Settings/RoleService.php +++ b/app/Services/Admin/Settings/RoleService.php @@ -10,26 +10,17 @@ class RoleService { - public function getAll(): Collection - { - return Role::withCount('permissions')->get(); - } - public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator { return Role::query() + ->select(['id', 'name']) ->withCount('permissions') ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%")) ->orderBy($sort, $direction) ->paginate($perPage); } - public function getById(int $id): Role - { - return Role::with('permissions')->findOrFail($id); - } - - public function create(array $data): Role + public function store(array $data): Role { return DB::transaction(function () use ($data) { $role = Role::create(['name' => $data['name']]); @@ -49,7 +40,7 @@ public function update(Role $role, array $data): Role return $role->fresh('permissions'); } - public function delete(Role $role): bool + public function destroy(Role $role): bool { return $role->delete(); } @@ -61,4 +52,22 @@ public function getPermissionsByModule(): array ->map(fn ($group) => $group->pluck('name')->map(fn ($name) => explode('.', $name, 2)[1])->values()->toArray()) ->toArray(); } + + public function getForEmployee(): Collection + { + $query = Role::where('name', '!=', 'Developer'); + + $user = auth()->user(); + + if ($user->hasAnyRole(['admin-toko', 'direktur'])) { + $query->where('name', '!=', 'admin-bahan-baku'); + } + + if (! $user->hasAnyRole(['developer', 'owner', 'direktur', 'admin-toko'])) { + $userRoles = $user->roles->pluck('name'); + $query->whereIn('name', $userRoles); + } + + return $query->get(['id', 'name']); + } } diff --git a/app/Services/Concerns/HandlesCashTransactions.php b/app/Services/Concerns/HandlesCashTransactions.php index a499f30..a4bfe91 100644 --- a/app/Services/Concerns/HandlesCashTransactions.php +++ b/app/Services/Concerns/HandlesCashTransactions.php @@ -5,6 +5,7 @@ use App\Enums\CashTransactionType; use App\Models\CashAccount; use App\Models\CashTransaction; +use Illuminate\Support\Facades\DB; use Illuminate\Validation\ValidationException; trait HandlesCashTransactions @@ -16,41 +17,45 @@ private function getCashAccount(): CashAccount private function creditCash(int $amount, string $description, CashTransactionType $type = CashTransactionType::DEPOSIT): CashTransaction { - $cashAccount = $this->getCashAccount(); - $newBalance = $cashAccount->balance + $amount; + return DB::transaction(function () use ($amount, $description, $type) { + $cashAccount = $this->getCashAccount(); + $newBalance = $cashAccount->balance + $amount; - $cashAccount->update(['balance' => $newBalance]); + $cashAccount->update(['balance' => $newBalance]); - return CashTransaction::create([ - 'cash_account_id' => $cashAccount->id, - 'created_by_id' => auth()->id(), - 'amount' => $amount, - 'balance_after' => $newBalance, - 'type' => $type, - 'description' => $description, - ]); + return CashTransaction::create([ + 'cash_account_id' => $cashAccount->id, + 'created_by_id' => auth()->id(), + 'amount' => $amount, + 'balance_after' => $newBalance, + 'type' => $type, + 'description' => $description, + ]); + }); } private function debitCash(int $amount, string $description, CashTransactionType $type = CashTransactionType::EXPENSE): CashTransaction { - $cashAccount = $this->getCashAccount(); + return DB::transaction(function () use ($amount, $description, $type) { + $cashAccount = $this->getCashAccount(); - if ($cashAccount->balance < $amount) { - throw ValidationException::withMessages([ - 'amount' => 'Saldo tidak mencukupi.', + if ($cashAccount->balance < $amount) { + throw ValidationException::withMessages([ + 'amount' => 'Saldo tidak mencukupi.', + ]); + } + + $newBalance = $cashAccount->balance - $amount; + $cashAccount->update(['balance' => $newBalance]); + + return CashTransaction::create([ + 'cash_account_id' => $cashAccount->id, + 'created_by_id' => auth()->id(), + 'amount' => $amount, + 'balance_after' => $newBalance, + 'type' => $type, + 'description' => $description, ]); - } - - $newBalance = $cashAccount->balance - $amount; - $cashAccount->update(['balance' => $newBalance]); - - return CashTransaction::create([ - 'cash_account_id' => $cashAccount->id, - 'created_by_id' => auth()->id(), - 'amount' => $amount, - 'balance_after' => $newBalance, - 'type' => $type, - 'description' => $description, - ]); + }); } } diff --git a/app/Services/Concerns/HasStockAdjustment.php b/app/Services/Concerns/HasStockAdjustment.php index 868ad69..67126ed 100644 --- a/app/Services/Concerns/HasStockAdjustment.php +++ b/app/Services/Concerns/HasStockAdjustment.php @@ -5,6 +5,7 @@ use App\Enums\ProductStockQuality; use App\Models\ProductVariant; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; use Illuminate\Validation\ValidationException; trait HasStockAdjustment @@ -48,13 +49,10 @@ private function adjustVariantStock(int $variantId, int $quantity, int $sign, st private function applyStock(array $items, string $stockType, int $sign): void { - foreach ($items as $item) { - $this->adjustVariantStock($item['product_variant_id'], $item['quantity'], $sign, $stockType); - } - } - - private function reverseStock(array $items, string $stockType, int $sign): void - { - $this->applyStock($items, $stockType, -$sign); + DB::transaction(function () use ($items, $stockType, $sign) { + foreach ($items as $item) { + $this->adjustVariantStock($item['product_variant_id'], $item['quantity'], $sign, $stockType); + } + }); } } diff --git a/app/Services/DashboardService.php b/app/Services/DashboardService.php index 2f19ffb..2cea3b2 100644 --- a/app/Services/DashboardService.php +++ b/app/Services/DashboardService.php @@ -49,15 +49,13 @@ public function getRevenueSummary(): array ->selectRaw('COUNT(*) as total_orders') ->selectRaw('COALESCE(SUM(total_amount), 0) as total_revenue') ->selectRaw('COALESCE(SUM(discount), 0) as total_discount') - ->selectRaw('COALESCE(SUM(total_amount), 0) - COALESCE(SUM(total_amount), 0) as total_marketplace_fees') - ->selectRaw('COALESCE(SUM(discount), 0) as total_deduction') ->first(); return [ 'total_revenue' => (int) $stats->total_revenue, 'total_discount' => (int) $stats->total_discount, - 'total_marketplace_fees' => (int) $stats->total_marketplace_fees, - 'total_deduction' => (int) $stats->total_deduction, + 'total_marketplace_fees' => 0, + 'total_deduction' => (int) $stats->total_discount, 'total_orders' => (int) $stats->total_orders, 'avg_order' => $stats->total_orders > 0 ? (int) ($stats->total_revenue / $stats->total_orders) : 0, ]; diff --git a/app/Services/NotificationService.php b/app/Services/NotificationService.php index 929c892..7a18f6c 100644 --- a/app/Services/NotificationService.php +++ b/app/Services/NotificationService.php @@ -4,12 +4,14 @@ use App\Models\User; use App\Notifications\WebPushNotification; +use Illuminate\Support\Facades\Log; class NotificationService { public static function notify(array $roles, string $title, string $body, string $url, ?User $additionalUser = null): void { $users = User::query() + ->select(['id']) ->where('is_active', true) ->whereHas('roles', fn ($q) => $q->whereIn('name', $roles)) ->get(); @@ -19,13 +21,17 @@ public static function notify(array $roles, string $title, string $body, string } $users->each(function (User $user) use ($title, $body, $url) { - $user->notifications()->create([ - 'title' => $title, - 'body' => $body, - 'url' => $url, - ]); + try { + $user->notifications()->create([ + 'title' => $title, + 'body' => $body, + 'url' => $url, + ]); - $user->notify(new WebPushNotification($title, $body)); + $user->notify(new WebPushNotification($title, $body)); + } catch (\Exception $e) { + Log::error("Gagal mengirim notifikasi ke user {$user->id}: {$e->getMessage()}"); + } }); } } diff --git a/app/Services/StockMutationService.php b/app/Services/StockMutationService.php index fd5c030..2eca6c5 100644 --- a/app/Services/StockMutationService.php +++ b/app/Services/StockMutationService.php @@ -144,31 +144,33 @@ public function recordTransfer( int $toBefore, string $description = 'Transfer stok', ): void { - $userId = auth()->id(); + DB::transaction(function () use ($model, $quantity, $fromQuality, $toQuality, $fromBefore, $toBefore, $description) { + $userId = auth()->id(); - StockMutation::create([ - 'user_id' => $userId, - 'stockable_type' => get_class($model), - 'stockable_id' => $model->id, - 'type' => 'out', - 'quantity' => -$quantity, - 'stock_before' => $fromBefore, - 'stock_after' => $fromBefore - $quantity, - 'stock_quality' => $fromQuality, - 'description' => $description, - ]); + StockMutation::create([ + 'user_id' => $userId, + 'stockable_type' => get_class($model), + 'stockable_id' => $model->id, + 'type' => 'out', + 'quantity' => -$quantity, + 'stock_before' => $fromBefore, + 'stock_after' => $fromBefore - $quantity, + 'stock_quality' => $fromQuality, + 'description' => $description, + ]); - StockMutation::create([ - 'user_id' => $userId, - 'stockable_type' => get_class($model), - 'stockable_id' => $model->id, - 'type' => 'in', - 'quantity' => $quantity, - 'stock_before' => $toBefore, - 'stock_after' => $toBefore + $quantity, - 'stock_quality' => $toQuality, - 'description' => $description, - ]); + StockMutation::create([ + 'user_id' => $userId, + 'stockable_type' => get_class($model), + 'stockable_id' => $model->id, + 'type' => 'in', + 'quantity' => $quantity, + 'stock_before' => $toBefore, + 'stock_after' => $toBefore + $quantity, + 'stock_quality' => $toQuality, + 'description' => $description, + ]); + }); } public function paginated(Model $model, int $perPage = 20): LengthAwarePaginator