*/ use HasFactory, HasUuid; protected $fillable = [ 'scenario_id', 'name', 'type', 'priority', 'sort_order', 'allocation_type', 'allocation_value', 'buffer_multiplier', 'starting_amount', ]; protected $casts = [ 'type' => BucketTypeEnum::class, 'priority' => 'integer', 'sort_order' => 'integer', 'allocation_value' => 'decimal:2', 'buffer_multiplier' => '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; } /** * Get the effective capacity including buffer. * Formula: allocation_value * (1 + buffer_multiplier) */ public function getEffectiveCapacity(): float { if ($this->allocation_type !== BucketAllocationTypeEnum::FIXED_LIMIT) { return PHP_FLOAT_MAX; } $base = (float) ($this->allocation_value ?? 0); return round($base * (1 + (float) $this->buffer_multiplier), 2); } /** * 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->getEffectiveCapacity(); } /** * 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->getEffectiveCapacity() - $this->getCurrentBalance()); } /** * Get display label for allocation type. */ public function getAllocationTypeLabel(): string { return $this->allocation_type->getLabel(); } /** * Get allocation value validation rules based on type. */ public static function allocationValueRules(BucketAllocationTypeEnum $allocationType): array { return $allocationType->getAllocationValueRules(); } }