*/ use HasFactory; protected $fillable = [ 'scenario_id', 'name', 'priority', 'sort_order', 'allocation_type', 'allocation_value', 'starting_amount', ]; protected $casts = [ 'priority' => 'integer', 'sort_order' => 'integer', 'allocation_value' => 'decimal:2', 'starting_amount' => 'integer', 'allocation_type' => BucketAllocationTypeEnum::class, ]; public function scenario(): BelongsTo { return $this->belongsTo(Scenario::class); } /** * Get the draws for the bucket. */ public function draws(): HasMany { return $this->hasMany(Draw::class); } /** * Get the outflows for the bucket. */ public function outflows(): HasMany { return $this->hasMany(Outflow::class); } /** * Scope to get buckets ordered by priority. */ public function scopeOrderedByPriority($query) { return $query->orderBy('priority'); } /** * Scope to get buckets ordered by sort order for UI display. */ public function scopeOrderedBySortOrder($query) { return $query->orderBy('sort_order')->orderBy('priority'); } /** * Get the current balance of the bucket. * Calculates starting amount plus total draws (money allocated to bucket) minus total outflows (money spent from bucket). */ public function getCurrentBalance(): int { $totalDraws = $this->draws()->sum('amount'); $totalOutflows = $this->outflows()->sum('amount'); return $this->starting_amount + $totalDraws - $totalOutflows; } /** * Check if the bucket can accept more money (for fixed_limit buckets). */ public function hasAvailableSpace(): bool { if ($this->allocation_type !== BucketAllocationTypeEnum::FIXED_LIMIT) { return true; } return $this->getCurrentBalance() < $this->allocation_value; } /** * Get available space for fixed_limit buckets. */ public function getAvailableSpace(): float { if ($this->allocation_type !== BucketAllocationTypeEnum::FIXED_LIMIT) { return PHP_FLOAT_MAX; } return max(0, $this->allocation_value - $this->getCurrentBalance()); } /** * Get display label for allocation type. */ public function getAllocationTypeLabel(): string { return $this->allocation_type->getLabel(); } /** * Get formatted allocation value for display. */ public function getFormattedAllocationValue(): string { return $this->allocation_type->formatValue($this->allocation_value); } /** * Validation rules for bucket creation/update. */ public static function validationRules($scenarioId = null): array { $rules = [ 'name' => 'required|string|max:255', 'allocation_type' => 'required|in:'.implode(',', BucketAllocationTypeEnum::values()), 'priority' => 'required|integer|min:1', ]; // Add scenario-specific priority uniqueness if scenario ID provided if ($scenarioId) { $rules['priority'] .= '|unique:buckets,priority,NULL,id,scenario_id,'.$scenarioId; } return $rules; } /** * Get allocation value validation rules based on type. */ public static function allocationValueRules(BucketAllocationTypeEnum $allocationType): array { return $allocationType->getAllocationValueRules(); } }