MIF_E31231430/app/Services/InventoryStockService.php

145 lines
4.4 KiB
PHP

<?php
namespace App\Services;
use App\Models\Kamera;
use App\Models\Lensa;
use App\Models\Penyewaan;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class InventoryStockService
{
public static function reserveKamera(Kamera $kamera, int $qty): void
{
$lockedKamera = Kamera::query()->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.'.',
]);
}
}
}