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