class Target < ApplicationRecord include NutrientVector has_many :target_allocations, inverse_of: :target, dependent: :destroy after_initialize :set_defaults, if: :new_record? validate :allocations_sum_to_100 # Recompute when allocations change; keep it simple (memoized per instance) def recompute_nutrients! @nutrient_values = nil nutrient_values end private def nutrient_values @nutrient_values ||= get_nutrient_values end def get_nutrient_values sums = Hash.new(0.0) allocs = target_allocations.includes(:nutrient_profile) allocs.each do |alloc| weight = alloc.percentage.to_f / 100.0 profile = alloc.nutrient_profile NutrientVector::NUTRIENT_KEYS.each do |k| sums[k] += profile.public_send(k).to_f * weight end end # Ensure all keys exist, even when there are no allocations NutrientVector::NUTRIENT_KEYS.each { |k| sums[k] ||= 0.0 } sums.freeze end def set_defaults self.name ||= "Cible #{Date.today + 1.month}" if target_allocations.empty? nutrient_profiles = NutrientProfile.limit(3) nutrient_profiles.each do |profile| target_allocations.build(nutrient_profile: profile, percentage: 0) end end end def allocations_sum_to_100 sum = target_allocations.reject(&:marked_for_destruction?).sum { |a| a.percentage.to_f } errors.add(:base, "La somme des pourcentages doit ĂȘtre 100%") unless (sum - 100).abs < 0.01 end end