refactor: rename create/delete methods to store/destroy across multiple services; add transaction handling for cash and stock adjustments

This commit is contained in:
Yoga Pangestu 2026-08-08 14:31:58 +07:00
parent 1c19e6c690
commit 46af27e537
19 changed files with 188 additions and 125 deletions

View File

@ -43,7 +43,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): EmployeeAdvance public function store(array $data): EmployeeAdvance
{ {
$employee = auth()->user()->employee; $employee = auth()->user()->employee;
@ -123,7 +123,7 @@ public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeA
return $employeeAdvance; return $employeeAdvance;
} }
public function delete(EmployeeAdvance $employeeAdvance): bool public function destroy(EmployeeAdvance $employeeAdvance): bool
{ {
return DB::transaction(function () use ($employeeAdvance) { return DB::transaction(function () use ($employeeAdvance) {
if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) { if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) {

View File

@ -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) { $expense = DB::transaction(function () use ($data) {
$cashTransaction = $this->debitCash( $cashTransaction = $this->debitCash(
@ -173,7 +173,7 @@ public function update(Expense $expense, array $data): Expense
return $expense; return $expense;
} }
public function delete(Expense $expense): bool public function destroy(Expense $expense): bool
{ {
return DB::transaction(function () use ($expense) { return DB::transaction(function () use ($expense) {
$this->creditCash( $this->creditCash(

View File

@ -11,7 +11,7 @@
class PayrollAdjustmentService class PayrollAdjustmentService
{ {
public function create(Payroll $payroll, array $data): PayrollAdjustment public function store(Payroll $payroll, array $data): PayrollAdjustment
{ {
if ($payroll->status !== PayrollStatus::UNPAID) { if ($payroll->status !== PayrollStatus::UNPAID) {
throw ValidationException::withMessages([ 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; $payroll = $adjustment->payroll;

View File

@ -23,6 +23,16 @@ private function canViewAll(): bool
return auth()->user()->hasAnyRole(['developer', 'owner', 'direktur', 'admin-toko']); 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 public function getAll(array $filters = []): Collection
{ {
return PayrollPeriod::select(['id', 'year', 'month', 'status', 'closed_at', 'created_at']) return PayrollPeriod::select(['id', 'year', 'month', 'status', 'closed_at', 'created_at'])

View File

@ -9,9 +9,11 @@
use App\Services\Concerns\RegistersMedia; use App\Services\Concerns\RegistersMedia;
use App\Services\NotificationService; use App\Services\NotificationService;
use App\Services\S3PresignedService; use App\Services\S3PresignedService;
use App\Settings\HRSettings;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\ValidationException;
class AttendanceService class AttendanceService
{ {
@ -21,6 +23,32 @@ public function __construct(
private S3PresignedService $s3Service, 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 public function getAll(): Collection
{ {
return Attendance::with(['employee.user.userProfile', 'media']) return Attendance::with(['employee.user.userProfile', 'media'])
@ -217,7 +245,9 @@ public function checkIn(array $data): Attendance
$employee = auth()->user()->employee; $employee = auth()->user()->employee;
if (! $employee) { if (! $employee) {
throw new \Exception('Anda tidak terdaftar sebagai karyawan.'); throw ValidationException::withMessages([
'employee' => 'Anda tidak terdaftar sebagai karyawan.',
]);
} }
$today = now()->toDateString(); $today = now()->toDateString();
@ -227,7 +257,9 @@ public function checkIn(array $data): Attendance
->first(); ->first();
if ($existing) { if ($existing) {
throw new \Exception('Anda sudah melakukan presensi hari ini.'); throw ValidationException::withMessages([
'attendance' => 'Anda sudah melakukan presensi hari ini.',
]);
} }
$attendance = Attendance::create([ $attendance = Attendance::create([

View File

@ -66,7 +66,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): User public function store(array $data): User
{ {
return DB::transaction(function () use ($data) { return DB::transaction(function () use ($data) {
$user = User::create([ $user = User::create([
@ -132,7 +132,7 @@ public function update(User $user, array $data): User
return $user->fresh(['userProfile', 'employee']); return $user->fresh(['userProfile', 'employee']);
} }
public function delete(User $user): bool public function destroy(User $user): bool
{ {
return DB::transaction(function () use ($user) { return DB::transaction(function () use ($user) {
$user->employee()->delete(); $user->employee()->delete();

View File

@ -9,6 +9,7 @@
use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class LeaveRequestService class LeaveRequestService
{ {
@ -43,13 +44,15 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): LeaveRequest public function store(array $data): LeaveRequest
{ {
$leaveRequest = DB::transaction(function () use ($data) { $leaveRequest = DB::transaction(function () use ($data) {
$employee = auth()->user()->employee; $employee = auth()->user()->employee;
if (! $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']); $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(); return $leaveRequest->delete();
} }

View File

@ -6,8 +6,8 @@
use App\Models\CuttingMaterial; use App\Models\CuttingMaterial;
use App\Models\CuttingMaterialCombination; use App\Models\CuttingMaterialCombination;
use App\Models\CuttingResult; use App\Models\CuttingResult;
use App\Models\RawMaterial;
use App\Models\RawMaterialPrice; use App\Models\RawMaterialPrice;
use App\Services\Admin\Master\RawMaterial\RawMaterialService;
use App\Services\Concerns\RegistersMedia; use App\Services\Concerns\RegistersMedia;
use App\Services\S3PresignedService; use App\Services\S3PresignedService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@ -20,6 +20,7 @@ class CuttingService
public function __construct( public function __construct(
private S3PresignedService $s3Service, private S3PresignedService $s3Service,
private RawMaterialService $rawMaterialService,
) {} ) {}
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc'): LengthAwarePaginator 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 public function getForCreate(): array
{ {
return [ return [
'rawMaterials' => RawMaterial::query() 'rawMaterials' => $this->rawMaterialService->getVariantsForCutting(),
->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;
});
}),
]; ];
} }

View File

@ -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 $oldMaterial = $oldItems
->map(fn (PurchaseItem $item) => $item->rawMaterialPrice?->rawMaterial) ->map(fn (PurchaseItem $item) => $item->rawMaterialPrice?->rawMaterial)
->filter() ->filter()

View File

@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): Category public function store(array $data): Category
{ {
return Category::create($data); return Category::create($data);
} }
@ -34,7 +34,7 @@ public function update(Category $category, array $data): Category
return $category; return $category;
} }
public function delete(Category $category): bool public function destroy(Category $category): bool
{ {
return $category->delete(); return $category->delete();
} }

View File

@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): Customer public function store(array $data): Customer
{ {
return Customer::create($data); return Customer::create($data);
} }
@ -34,7 +34,7 @@ public function update(Customer $customer, array $data): Customer
return $customer; return $customer;
} }
public function delete(Customer $customer): bool public function destroy(Customer $customer): bool
{ {
return $customer->delete(); return $customer->delete();
} }

View File

@ -71,7 +71,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
return $paginator; return $paginator;
} }
public function create(array $data): RawMaterial public function store(array $data): RawMaterial
{ {
return DB::transaction(function () use ($data) { return DB::transaction(function () use ($data) {
$rawMaterial = RawMaterial::create([ $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) { return DB::transaction(function () use ($rawMaterial) {
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { $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 private function registerPhoto(RawMaterialPrice $price, string $s3Key): void
{ {
$this->registerMedia( $this->registerMedia(

View File

@ -22,7 +22,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
->paginate($perPage); ->paginate($perPage);
} }
public function create(array $data): Supplier public function store(array $data): Supplier
{ {
return Supplier::create($data); return Supplier::create($data);
} }
@ -34,7 +34,7 @@ public function update(Supplier $supplier, array $data): Supplier
return $supplier; return $supplier;
} }
public function delete(Supplier $supplier): bool public function destroy(Supplier $supplier): bool
{ {
return $supplier->delete(); return $supplier->delete();
} }

View File

@ -10,26 +10,17 @@
class RoleService 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 public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
{ {
return Role::query() return Role::query()
->select(['id', 'name'])
->withCount('permissions') ->withCount('permissions')
->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%")) ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
->orderBy($sort, $direction) ->orderBy($sort, $direction)
->paginate($perPage); ->paginate($perPage);
} }
public function getById(int $id): Role public function store(array $data): Role
{
return Role::with('permissions')->findOrFail($id);
}
public function create(array $data): Role
{ {
return DB::transaction(function () use ($data) { return DB::transaction(function () use ($data) {
$role = Role::create(['name' => $data['name']]); $role = Role::create(['name' => $data['name']]);
@ -49,7 +40,7 @@ public function update(Role $role, array $data): Role
return $role->fresh('permissions'); return $role->fresh('permissions');
} }
public function delete(Role $role): bool public function destroy(Role $role): bool
{ {
return $role->delete(); 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()) ->map(fn ($group) => $group->pluck('name')->map(fn ($name) => explode('.', $name, 2)[1])->values()->toArray())
->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']);
}
} }

View File

@ -5,6 +5,7 @@
use App\Enums\CashTransactionType; use App\Enums\CashTransactionType;
use App\Models\CashAccount; use App\Models\CashAccount;
use App\Models\CashTransaction; use App\Models\CashTransaction;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
trait HandlesCashTransactions trait HandlesCashTransactions
@ -16,41 +17,45 @@ private function getCashAccount(): CashAccount
private function creditCash(int $amount, string $description, CashTransactionType $type = CashTransactionType::DEPOSIT): CashTransaction private function creditCash(int $amount, string $description, CashTransactionType $type = CashTransactionType::DEPOSIT): CashTransaction
{ {
$cashAccount = $this->getCashAccount(); return DB::transaction(function () use ($amount, $description, $type) {
$newBalance = $cashAccount->balance + $amount; $cashAccount = $this->getCashAccount();
$newBalance = $cashAccount->balance + $amount;
$cashAccount->update(['balance' => $newBalance]); $cashAccount->update(['balance' => $newBalance]);
return CashTransaction::create([ return CashTransaction::create([
'cash_account_id' => $cashAccount->id, 'cash_account_id' => $cashAccount->id,
'created_by_id' => auth()->id(), 'created_by_id' => auth()->id(),
'amount' => $amount, 'amount' => $amount,
'balance_after' => $newBalance, 'balance_after' => $newBalance,
'type' => $type, 'type' => $type,
'description' => $description, 'description' => $description,
]); ]);
});
} }
private function debitCash(int $amount, string $description, CashTransactionType $type = CashTransactionType::EXPENSE): CashTransaction 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) { if ($cashAccount->balance < $amount) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi.', '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,
]);
} }
} }

View File

@ -5,6 +5,7 @@
use App\Enums\ProductStockQuality; use App\Enums\ProductStockQuality;
use App\Models\ProductVariant; use App\Models\ProductVariant;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
trait HasStockAdjustment 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 private function applyStock(array $items, string $stockType, int $sign): void
{ {
foreach ($items as $item) { DB::transaction(function () use ($items, $stockType, $sign) {
$this->adjustVariantStock($item['product_variant_id'], $item['quantity'], $sign, $stockType); 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);
} }
} }

View File

@ -49,15 +49,13 @@ public function getRevenueSummary(): array
->selectRaw('COUNT(*) as total_orders') ->selectRaw('COUNT(*) as total_orders')
->selectRaw('COALESCE(SUM(total_amount), 0) as total_revenue') ->selectRaw('COALESCE(SUM(total_amount), 0) as total_revenue')
->selectRaw('COALESCE(SUM(discount), 0) as total_discount') ->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(); ->first();
return [ return [
'total_revenue' => (int) $stats->total_revenue, 'total_revenue' => (int) $stats->total_revenue,
'total_discount' => (int) $stats->total_discount, 'total_discount' => (int) $stats->total_discount,
'total_marketplace_fees' => (int) $stats->total_marketplace_fees, 'total_marketplace_fees' => 0,
'total_deduction' => (int) $stats->total_deduction, 'total_deduction' => (int) $stats->total_discount,
'total_orders' => (int) $stats->total_orders, 'total_orders' => (int) $stats->total_orders,
'avg_order' => $stats->total_orders > 0 ? (int) ($stats->total_revenue / $stats->total_orders) : 0, 'avg_order' => $stats->total_orders > 0 ? (int) ($stats->total_revenue / $stats->total_orders) : 0,
]; ];

View File

@ -4,12 +4,14 @@
use App\Models\User; use App\Models\User;
use App\Notifications\WebPushNotification; use App\Notifications\WebPushNotification;
use Illuminate\Support\Facades\Log;
class NotificationService class NotificationService
{ {
public static function notify(array $roles, string $title, string $body, string $url, ?User $additionalUser = null): void public static function notify(array $roles, string $title, string $body, string $url, ?User $additionalUser = null): void
{ {
$users = User::query() $users = User::query()
->select(['id'])
->where('is_active', true) ->where('is_active', true)
->whereHas('roles', fn ($q) => $q->whereIn('name', $roles)) ->whereHas('roles', fn ($q) => $q->whereIn('name', $roles))
->get(); ->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) { $users->each(function (User $user) use ($title, $body, $url) {
$user->notifications()->create([ try {
'title' => $title, $user->notifications()->create([
'body' => $body, 'title' => $title,
'url' => $url, '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()}");
}
}); });
} }
} }

View File

@ -144,31 +144,33 @@ public function recordTransfer(
int $toBefore, int $toBefore,
string $description = 'Transfer stok', string $description = 'Transfer stok',
): void { ): void {
$userId = auth()->id(); DB::transaction(function () use ($model, $quantity, $fromQuality, $toQuality, $fromBefore, $toBefore, $description) {
$userId = auth()->id();
StockMutation::create([ StockMutation::create([
'user_id' => $userId, 'user_id' => $userId,
'stockable_type' => get_class($model), 'stockable_type' => get_class($model),
'stockable_id' => $model->id, 'stockable_id' => $model->id,
'type' => 'out', 'type' => 'out',
'quantity' => -$quantity, 'quantity' => -$quantity,
'stock_before' => $fromBefore, 'stock_before' => $fromBefore,
'stock_after' => $fromBefore - $quantity, 'stock_after' => $fromBefore - $quantity,
'stock_quality' => $fromQuality, 'stock_quality' => $fromQuality,
'description' => $description, 'description' => $description,
]); ]);
StockMutation::create([ StockMutation::create([
'user_id' => $userId, 'user_id' => $userId,
'stockable_type' => get_class($model), 'stockable_type' => get_class($model),
'stockable_id' => $model->id, 'stockable_id' => $model->id,
'type' => 'in', 'type' => 'in',
'quantity' => $quantity, 'quantity' => $quantity,
'stock_before' => $toBefore, 'stock_before' => $toBefore,
'stock_after' => $toBefore + $quantity, 'stock_after' => $toBefore + $quantity,
'stock_quality' => $toQuality, 'stock_quality' => $toQuality,
'description' => $description, 'description' => $description,
]); ]);
});
} }
public function paginated(Model $model, int $perPage = 20): LengthAwarePaginator public function paginated(Model $model, int $perPage = 20): LengthAwarePaginator