lockForUpdate()->findOrFail($kamera->id); self::ensureEnoughStock($lockedKamera->stok, $qty, 'kamera'); $lockedKamera->decrement('stok', $qty); } public static function reserveLensa(Lensa $lensa, int $qty): void { $lockedLensa = Lensa::query()->lockForUpdate()->findOrFail($lensa->id); self::ensureEnoughStock($lockedLensa->stok, $qty, 'lensa'); $lockedLensa->decrement('stok', $qty); } public static function reservePaket(Kamera $kamera, Lensa $lensa, int $qty): void { $lockedKamera = Kamera::query()->lockForUpdate()->findOrFail($kamera->id); $lockedLensa = Lensa::query()->lockForUpdate()->findOrFail($lensa->id); self::ensureEnoughStock($lockedKamera->stok, $qty, 'kamera'); self::ensureEnoughStock($lockedLensa->stok, $qty, 'lensa'); $lockedKamera->decrement('stok', $qty); $lockedLensa->decrement('stok', $qty); } public static function syncPenyewaanStock(Penyewaan $penyewaan, int $newQty, string $newStatus): void { $oldQty = (int) $penyewaan->qty; $oldStatus = (string) $penyewaan->status; $oldReserved = self::statusUsesStock($oldStatus); $newReserved = self::statusUsesStock($newStatus); $kamera = $penyewaan->kamera_id ? Kamera::query()->lockForUpdate()->find($penyewaan->kamera_id) : null; $lensa = $penyewaan->lensa_id ? Lensa::query()->lockForUpdate()->find($penyewaan->lensa_id) : null; if (! $oldReserved && ! $newReserved) { return; } if ($oldReserved && ! $newReserved) { self::restoreItems($kamera, $lensa, $oldQty); return; } if (! $oldReserved && $newReserved) { self::reserveItems($kamera, $lensa, $newQty); return; } $difference = $newQty - $oldQty; if ($difference > 0) { self::reserveItems($kamera, $lensa, $difference); return; } if ($difference < 0) { self::restoreItems($kamera, $lensa, abs($difference)); } } public static function restoreForDeletion(Penyewaan $penyewaan): void { if (! self::statusUsesStock((string) $penyewaan->status)) { return; } $kamera = $penyewaan->kamera_id ? Kamera::query()->lockForUpdate()->find($penyewaan->kamera_id) : null; $lensa = $penyewaan->lensa_id ? Lensa::query()->lockForUpdate()->find($penyewaan->lensa_id) : null; self::restoreItems($kamera, $lensa, (int) $penyewaan->qty); } public static function statusUsesStock(string $status): bool { return in_array($status, ['menunggu', 'disetujui'], true); } public static function transaction(callable $callback): mixed { return DB::transaction($callback); } private static function reserveItems(?Kamera $kamera, ?Lensa $lensa, int $qty): void { if ($kamera) { self::ensureEnoughStock((int) $kamera->stok, $qty, 'kamera'); } if ($lensa) { self::ensureEnoughStock((int) $lensa->stok, $qty, 'lensa'); } if ($kamera) { $kamera->decrement('stok', $qty, []); } if ($lensa) { $lensa->decrement('stok', $qty, []); } } private static function restoreItems(?Kamera $kamera, ?Lensa $lensa, int $qty): void { if ($kamera) { $kamera->update([ 'stok' => min((int) $kamera->stok + $qty, Kamera::DEFAULT_STOCK), ]); } if ($lensa) { $lensa->update([ 'stok' => min((int) $lensa->stok + $qty, Lensa::DEFAULT_STOCK), ]); } } private static function ensureEnoughStock(int $availableStock, int $requestedQty, string $label): void { if ($requestedQty > $availableStock) { throw ValidationException::withMessages([ 'qty' => 'Stok '.$label.' tidak mencukupi. Sisa stok yang tersedia saat ini: '.$availableStock.'.', ]); } } }