145 lines
4.4 KiB
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.'.',
|
|
]);
|
|
}
|
|
}
|
|
}
|