buckets/app/Actions/CreateBucketAction.php

127 lines
4.3 KiB
PHP

<?php
namespace App\Actions;
use App\Enums\BucketAllocationTypeEnum;
use App\Models\Bucket;
use App\Models\Scenario;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
class CreateBucketAction
{
public function execute(
Scenario $scenario,
string $name,
BucketAllocationTypeEnum $allocationType,
?float $allocationValue = null,
?int $priority = null
): Bucket {
// Validate allocation value based on type
$this->validateAllocationValue($allocationType, $allocationValue);
// Set allocation_value to null for unlimited buckets
if ($allocationType === BucketAllocationTypeEnum::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,
]);
});
}
/**
* Validate allocation value based on allocation type.
*/
private function validateAllocationValue(BucketAllocationTypeEnum $allocationType, ?float $allocationValue): void
{
switch ($allocationType) {
case BucketAllocationTypeEnum::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 BucketAllocationTypeEnum::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 BucketAllocationTypeEnum::UNLIMITED:
// Unlimited buckets should not have an allocation value
// We'll set it to null in the main method regardless
break;
}
}
/**
* Create default buckets for a scenario.
*/
public function createDefaultBuckets(Scenario $scenario): array
{
$buckets = [];
// Monthly Expenses - Fixed limit, priority 1
$buckets[] = $this->execute(
$scenario,
'Monthly Expenses',
BucketAllocationTypeEnum::FIXED_LIMIT,
0,
1
);
// Emergency Fund - Fixed limit, priority 2
$buckets[] = $this->execute(
$scenario,
'Emergency Fund',
BucketAllocationTypeEnum::FIXED_LIMIT,
0,
2
);
// Investments - Unlimited, priority 3
$buckets[] = $this->execute(
$scenario,
'Investments',
BucketAllocationTypeEnum::UNLIMITED,
null,
3
);
return $buckets;
}
}