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; } }