first
This commit is contained in:
139
app/Services/Admin/Management/WheelchairManagementService.php
Normal file
139
app/Services/Admin/Management/WheelchairManagementService.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Admin\Management;
|
||||
|
||||
use App\Models\Election;
|
||||
use App\Models\PusatMengundi;
|
||||
use App\Models\User;
|
||||
use App\Models\WheelchairAllocation;
|
||||
use App\Models\WheelchairTransaction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class WheelchairManagementService
|
||||
{
|
||||
/**
|
||||
* @param array{election_id: int, pusat_mengundi_id: int, allocated_quantity: int, notes?: string|null} $data
|
||||
*/
|
||||
public function upsertAllocation(User $actor, array $data): WheelchairAllocation
|
||||
{
|
||||
return DB::transaction(function () use ($actor, $data): WheelchairAllocation {
|
||||
/** @var Election $election */
|
||||
$election = Election::query()->findOrFail($data['election_id']);
|
||||
/** @var PusatMengundi $pusat */
|
||||
$pusat = PusatMengundi::query()->findOrFail($data['pusat_mengundi_id']);
|
||||
|
||||
if ((int) $pusat->election_id !== (int) $election->id) {
|
||||
throw ValidationException::withMessages([
|
||||
'pusat_mengundi_id' => 'Pusat Mengundi tidak berada dalam pilihanraya yang sama.',
|
||||
]);
|
||||
}
|
||||
|
||||
$allocation = WheelchairAllocation::query()->firstOrNew([
|
||||
'election_id' => $election->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
]);
|
||||
|
||||
$outstanding = $allocation->exists ? $this->outstandingQuantity($allocation) : 0;
|
||||
|
||||
if ($data['allocated_quantity'] < $outstanding) {
|
||||
throw ValidationException::withMessages([
|
||||
'allocated_quantity' => 'Peruntukan tidak boleh kurang daripada jumlah kerusi roda yang masih belum dipulang.',
|
||||
]);
|
||||
}
|
||||
|
||||
$before = $allocation->exists ? $allocation->only(['allocated_quantity', 'notes']) : null;
|
||||
|
||||
$allocation->forceFill([
|
||||
'allocated_quantity' => $data['allocated_quantity'],
|
||||
'notes' => $data['notes'] ?? null,
|
||||
])->save();
|
||||
|
||||
activity('wheelchairs')
|
||||
->performedOn($allocation)
|
||||
->causedBy($actor)
|
||||
->withProperties([
|
||||
'before' => $before,
|
||||
'after' => $allocation->only(['allocated_quantity', 'notes']),
|
||||
])
|
||||
->log('wheelchair_allocation_upserted');
|
||||
|
||||
return $allocation;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{transaction_type: string, quantity: int, taken_at?: string|null, taken_by_name?: string|null, returned_at?: string|null, return_condition?: string|null, notes?: string|null} $data
|
||||
*/
|
||||
public function recordTransaction(WheelchairAllocation $allocation, User $actor, array $data): WheelchairTransaction
|
||||
{
|
||||
return DB::transaction(function () use ($allocation, $actor, $data): WheelchairTransaction {
|
||||
$allocation->refresh();
|
||||
$type = $data['transaction_type'];
|
||||
$quantity = $data['quantity'];
|
||||
|
||||
if ($type === 'taken' && $quantity > $this->availableQuantity($allocation)) {
|
||||
throw ValidationException::withMessages([
|
||||
'quantity' => 'Jumlah diambil melebihi baki peruntukan kerusi roda.',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($type === 'returned' && $quantity > $this->outstandingQuantity($allocation)) {
|
||||
throw ValidationException::withMessages([
|
||||
'quantity' => 'Jumlah dipulang melebihi jumlah kerusi roda yang masih belum dipulang.',
|
||||
]);
|
||||
}
|
||||
|
||||
$transaction = WheelchairTransaction::query()->create([
|
||||
'wheelchair_allocation_id' => $allocation->id,
|
||||
'transaction_type' => $type,
|
||||
'quantity' => $quantity,
|
||||
'taken_at' => $type === 'taken' ? ($data['taken_at'] ?? now()) : null,
|
||||
'taken_by_name' => $type === 'taken' ? ($data['taken_by_name'] ?? null) : null,
|
||||
'returned_at' => $type === 'returned' ? ($data['returned_at'] ?? now()) : null,
|
||||
'return_condition' => $type === 'returned' ? ($data['return_condition'] ?? null) : null,
|
||||
'returned_by_name' => $type === 'returned' ? ($data['returned_by_name'] ?? null) : null,
|
||||
'recorded_by_user_id' => $actor->id,
|
||||
'notes' => $data['notes'] ?? null,
|
||||
]);
|
||||
|
||||
activity('wheelchairs')
|
||||
->performedOn($transaction)
|
||||
->causedBy($actor)
|
||||
->withProperties([
|
||||
'allocation_id' => $allocation->id,
|
||||
'transaction_type' => $type,
|
||||
'quantity' => $quantity,
|
||||
'available_after' => $this->availableQuantity($allocation->fresh()),
|
||||
'outstanding_after' => $this->outstandingQuantity($allocation->fresh()),
|
||||
])
|
||||
->log('wheelchair_transaction_recorded');
|
||||
|
||||
return $transaction;
|
||||
});
|
||||
}
|
||||
|
||||
public function takenQuantity(WheelchairAllocation $allocation): int
|
||||
{
|
||||
return (int) $allocation->transactions()
|
||||
->where('transaction_type', 'taken')
|
||||
->sum('quantity');
|
||||
}
|
||||
|
||||
public function returnedQuantity(WheelchairAllocation $allocation): int
|
||||
{
|
||||
return (int) $allocation->transactions()
|
||||
->where('transaction_type', 'returned')
|
||||
->sum('quantity');
|
||||
}
|
||||
|
||||
public function outstandingQuantity(WheelchairAllocation $allocation): int
|
||||
{
|
||||
return max(0, $this->takenQuantity($allocation) - $this->returnedQuantity($allocation));
|
||||
}
|
||||
|
||||
public function availableQuantity(WheelchairAllocation $allocation): int
|
||||
{
|
||||
return max(0, (int) $allocation->allocated_quantity - $this->outstandingQuantity($allocation));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user