buckets/app/Actions/CreateBucketAction.php

105 lines
4 KiB
PHP
Raw Permalink Normal View History

2025-12-29 23:32:05 +01:00
<?php
namespace App\Actions;
use App\Models\Bucket;
use App\Models\Scenario;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
class CreateBucketAction
{
public function execute(
Scenario $scenario,
string $name,
string $allocationType,
?float $allocationValue = null,
?int $priority = null
): Bucket {
// Validate allocation type
$validTypes = [Bucket::TYPE_FIXED_LIMIT, Bucket::TYPE_PERCENTAGE, Bucket::TYPE_UNLIMITED];
if (!in_array($allocationType, $validTypes)) {
throw new InvalidArgumentException("Invalid allocation type: {$allocationType}");
}
// Validate allocation value based on type
$this->validateAllocationValue($allocationType, $allocationValue);
// Set allocation_value to null for unlimited buckets
if ($allocationType === Bucket::TYPE_UNLIMITED) {
$allocationValue = null;
}
return DB::transaction(function () use ($scenario, $name, $allocationType, $allocationValue, $priority) {
// Determine priority (append to end if not specified)
if ($priority === null) {
$maxPriority = $scenario->buckets()->max('priority') ?? 0;
$priority = $maxPriority + 1;
} else {
// Validate priority is positive
if ($priority < 1) {
throw new InvalidArgumentException("Priority must be at least 1");
}
// Check if priority already exists and shift others if needed
$existingBucket = $scenario->buckets()->where('priority', $priority)->first();
if ($existingBucket) {
// Shift priorities to make room
$scenario->buckets()
->where('priority', '>=', $priority)
->increment('priority');
}
}
// Create the bucket
return $scenario->buckets()->create([
'name' => $name,
'priority' => $priority,
'sort_order' => $priority, // Start with sort_order matching priority
'allocation_type' => $allocationType,
'allocation_value' => $allocationValue,
]);
});
}
/**
* Create default buckets for a new scenario.
*/
public function createDefaultBuckets(Scenario $scenario): void
{
$this->execute($scenario, 'Monthly Expenses', Bucket::TYPE_FIXED_LIMIT, 0, 1);
$this->execute($scenario, 'Emergency Fund', Bucket::TYPE_FIXED_LIMIT, 0, 2);
$this->execute($scenario, 'Investments', Bucket::TYPE_UNLIMITED, null, 3);
}
/**
* Validate allocation value based on allocation type.
*/
private function validateAllocationValue(string $allocationType, ?float $allocationValue): void
{
switch ($allocationType) {
case Bucket::TYPE_FIXED_LIMIT:
if ($allocationValue === null) {
throw new InvalidArgumentException('Fixed limit buckets require an allocation value');
}
if ($allocationValue < 0) {
throw new InvalidArgumentException('Fixed limit allocation value must be non-negative');
}
break;
case Bucket::TYPE_PERCENTAGE:
if ($allocationValue === null) {
throw new InvalidArgumentException('Percentage buckets require an allocation value');
}
if ($allocationValue < 0.01 || $allocationValue > 100) {
throw new InvalidArgumentException('Percentage allocation value must be between 0.01 and 100');
}
break;
case Bucket::TYPE_UNLIMITED:
// Unlimited buckets should not have an allocation value
// We'll set it to null in the main method regardless
break;
}
}
}