| 23 | 
  23 | 
  
      before_destroy :nullify_projects_default_version 
   | 
  | 24 | 
  24 | 
  
    
   | 
  | 25 | 
  25 | 
  
      belongs_to :project 
   | 
  | 26 | 
   | 
  
      has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify 
   | 
   | 
  26 | 
  
      has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify do 
   | 
   | 
  27 | 
  
        # Returns the total estimated time for this version 
   | 
   | 
  28 | 
  
        # (sum of leaves estimated_hours) 
   | 
   | 
  29 | 
  
        def estimated_hours 
   | 
   | 
  30 | 
  
          @estimated_hours ||= sum(:estimated_hours).to_f 
   | 
   | 
  31 | 
  
        end 
   | 
   | 
  32 | 
  
        # 
   | 
   | 
  33 | 
  
        # Returns the total amount of open issues for this version. 
   | 
   | 
  34 | 
  
        def open_count 
   | 
   | 
  35 | 
  
          load_counts 
   | 
   | 
  36 | 
  
          @open_count 
   | 
   | 
  37 | 
  
        end 
   | 
   | 
  38 | 
  
    
   | 
   | 
  39 | 
  
        # Returns the total amount of closed issues for this version. 
   | 
   | 
  40 | 
  
        def closed_count 
   | 
   | 
  41 | 
  
          load_counts 
   | 
   | 
  42 | 
  
          @closed_count 
   | 
   | 
  43 | 
  
        end 
   | 
   | 
  44 | 
  
    
   | 
   | 
  45 | 
  
        # Returns the completion percentage of this version based on the amount of open/closed issues 
   | 
   | 
  46 | 
  
        # and the time spent on the open issues. 
   | 
   | 
  47 | 
  
        def completed_percent 
   | 
   | 
  48 | 
  
          if count == 0 
   | 
   | 
  49 | 
  
            0 
   | 
   | 
  50 | 
  
          elsif open_count == 0 
   | 
   | 
  51 | 
  
            100 
   | 
   | 
  52 | 
  
          else 
   | 
   | 
  53 | 
  
            issues_progress(false) + issues_progress(true) 
   | 
   | 
  54 | 
  
          end 
   | 
   | 
  55 | 
  
        end 
   | 
   | 
  56 | 
  
    
   | 
   | 
  57 | 
  
        # Returns the percentage of issues that have been marked as 'closed'. 
   | 
   | 
  58 | 
  
        def closed_percent 
   | 
   | 
  59 | 
  
          if count == 0 
   | 
   | 
  60 | 
  
            0 
   | 
   | 
  61 | 
  
          else 
   | 
   | 
  62 | 
  
            issues_progress(false) 
   | 
   | 
  63 | 
  
          end 
   | 
   | 
  64 | 
  
        end 
   | 
   | 
  65 | 
  
    
   | 
   | 
  66 | 
  
        private 
   | 
   | 
  67 | 
  
    
   | 
   | 
  68 | 
  
        def load_counts 
   | 
   | 
  69 | 
  
          unless @open_count 
   | 
   | 
  70 | 
  
            @open_count = 0 
   | 
   | 
  71 | 
  
            @closed_count = 0 
   | 
   | 
  72 | 
  
            self.group(:status).count.each do |status, count| 
   | 
   | 
  73 | 
  
              if status.is_closed? 
   | 
   | 
  74 | 
  
                @closed_count += count 
   | 
   | 
  75 | 
  
              else 
   | 
   | 
  76 | 
  
                @open_count += count 
   | 
   | 
  77 | 
  
              end 
   | 
   | 
  78 | 
  
            end 
   | 
   | 
  79 | 
  
          end 
   | 
   | 
  80 | 
  
        end 
   | 
   | 
  81 | 
  
    
   | 
   | 
  82 | 
  
        # Returns the average estimated time of assigned issues 
   | 
   | 
  83 | 
  
        # or 1 if no issue has an estimated time 
   | 
   | 
  84 | 
  
        # Used to weight unestimated issues in progress calculation 
   | 
   | 
  85 | 
  
        def estimated_average 
   | 
   | 
  86 | 
  
          if @estimated_average.nil? 
   | 
   | 
  87 | 
  
            average = average(:estimated_hours).to_f 
   | 
   | 
  88 | 
  
            if average == 0 
   | 
   | 
  89 | 
  
              average = 1 
   | 
   | 
  90 | 
  
            end 
   | 
   | 
  91 | 
  
            @estimated_average = average 
   | 
   | 
  92 | 
  
          end 
   | 
   | 
  93 | 
  
          @estimated_average 
   | 
   | 
  94 | 
  
        end 
   | 
   | 
  95 | 
  
    
   | 
   | 
  96 | 
  
        # Returns the total progress of open or closed issues.  The returned percentage takes into account 
   | 
   | 
  97 | 
  
        # the amount of estimated time set for this version. 
   | 
   | 
  98 | 
  
        # 
   | 
   | 
  99 | 
  
        # Examples: 
   | 
   | 
  100 | 
  
        # issues_progress(true)   => returns the progress percentage for open issues. 
   | 
   | 
  101 | 
  
        # issues_progress(false)  => returns the progress percentage for closed issues. 
   | 
   | 
  102 | 
  
        def issues_progress(open) 
   | 
   | 
  103 | 
  
          @issues_progress ||= {}
   | 
   | 
  104 | 
  
          @issues_progress[open] ||= begin 
   | 
   | 
  105 | 
  
            progress = 0 
   | 
   | 
  106 | 
  
            if count > 0 
   | 
   | 
  107 | 
  
              ratio = open ? 'done_ratio' : 100 
   | 
   | 
  108 | 
  
    
   | 
   | 
  109 | 
  
              done = open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f
   | 
   | 
  110 | 
  
              progress = done / (estimated_average * count) 
   | 
   | 
  111 | 
  
            end 
   | 
   | 
  112 | 
  
            progress 
   | 
   | 
  113 | 
  
          end 
   | 
   | 
  114 | 
  
        end 
   | 
   | 
  115 | 
  
      end 
   | 
   | 
  116 | 
  
    
   | 
  | 27 | 
  117 | 
  
      acts_as_customizable 
   | 
  | 28 | 
  118 | 
  
      acts_as_attachable :view_permission => :view_files, 
   | 
  | 29 | 
  119 | 
  
                         :edit_permission => :manage_files, 
   | 
  | ... | ... |  | 
  | 104 | 
  194 | 
  
      # Returns the total estimated time for this version 
   | 
  | 105 | 
  195 | 
  
      # (sum of leaves estimated_hours) 
   | 
  | 106 | 
  196 | 
  
      def estimated_hours 
   | 
  | 107 | 
   | 
  
        @estimated_hours ||= fixed_issues.sum(:estimated_hours).to_f 
   | 
   | 
  197 | 
  
        fixed_issues.estimated_hours 
   | 
  | 108 | 
  198 | 
  
      end 
   | 
  | 109 | 
  199 | 
  
    
   | 
  | 110 | 
  200 | 
  
      # Returns the total reported time for this version 
   | 
  | ... | ... |  | 
  | 139 | 
  229 | 
  
      # Returns the completion percentage of this version based on the amount of open/closed issues 
   | 
  | 140 | 
  230 | 
  
      # and the time spent on the open issues. 
   | 
  | 141 | 
  231 | 
  
      def completed_percent 
   | 
  | 142 | 
   | 
  
        if issues_count == 0 
   | 
  | 143 | 
   | 
  
          0 
   | 
  | 144 | 
   | 
  
        elsif open_issues_count == 0 
   | 
  | 145 | 
   | 
  
          100 
   | 
  | 146 | 
   | 
  
        else 
   | 
  | 147 | 
   | 
  
          issues_progress(false) + issues_progress(true) 
   | 
  | 148 | 
   | 
  
        end 
   | 
   | 
  232 | 
  
        fixed_issues.completed_percent 
   | 
  | 149 | 
  233 | 
  
      end 
   | 
  | 150 | 
  234 | 
  
    
   | 
  | 151 | 
  235 | 
  
      # Returns the percentage of issues that have been marked as 'closed'. 
   | 
  | 152 | 
  236 | 
  
      def closed_percent 
   | 
  | 153 | 
   | 
  
        if issues_count == 0 
   | 
  | 154 | 
   | 
  
          0 
   | 
  | 155 | 
   | 
  
        else 
   | 
  | 156 | 
   | 
  
          issues_progress(false) 
   | 
  | 157 | 
   | 
  
        end 
   | 
   | 
  237 | 
  
        fixed_issues.closed_percent 
   | 
  | 158 | 
  238 | 
  
      end 
   | 
  | 159 | 
  239 | 
  
    
   | 
  | 160 | 
  240 | 
  
      # Returns true if the version is overdue: due date reached and some open issues 
   | 
  | ... | ... |  | 
  | 164 | 
  244 | 
  
    
   | 
  | 165 | 
  245 | 
  
      # Returns assigned issues count 
   | 
  | 166 | 
  246 | 
  
      def issues_count 
   | 
  | 167 | 
   | 
  
        load_issue_counts 
   | 
  | 168 | 
   | 
  
        @issue_count 
   | 
   | 
  247 | 
  
        fixed_issues.count 
   | 
  | 169 | 
  248 | 
  
      end 
   | 
  | 170 | 
  249 | 
  
    
   | 
  | 171 | 
  250 | 
  
      # Returns the total amount of open issues for this version. 
   | 
  | 172 | 
  251 | 
  
      def open_issues_count 
   | 
  | 173 | 
   | 
  
        load_issue_counts 
   | 
  | 174 | 
   | 
  
        @open_issues_count 
   | 
   | 
  252 | 
  
        fixed_issues.open_count 
   | 
  | 175 | 
  253 | 
  
      end 
   | 
  | 176 | 
  254 | 
  
    
   | 
  | 177 | 
  255 | 
  
      # Returns the total amount of closed issues for this version. 
   | 
  | 178 | 
  256 | 
  
      def closed_issues_count 
   | 
  | 179 | 
   | 
  
        load_issue_counts 
   | 
  | 180 | 
   | 
  
        @closed_issues_count 
   | 
   | 
  257 | 
  
        fixed_issues.closed_count 
   | 
  | 181 | 
  258 | 
  
      end 
   | 
  | 182 | 
  259 | 
  
    
   | 
  | 183 | 
  260 | 
  
      def wiki_page 
   | 
  | ... | ... |  | 
  | 284 | 
  361 | 
  
    
   | 
  | 285 | 
  362 | 
  
      private 
   | 
  | 286 | 
  363 | 
  
    
   | 
  | 287 | 
   | 
  
      def load_issue_counts 
   | 
  | 288 | 
   | 
  
        unless @issue_count 
   | 
  | 289 | 
   | 
  
          @open_issues_count = 0 
   | 
  | 290 | 
   | 
  
          @closed_issues_count = 0 
   | 
  | 291 | 
   | 
  
          fixed_issues.group(:status).count.each do |status, count| 
   | 
  | 292 | 
   | 
  
            if status.is_closed? 
   | 
  | 293 | 
   | 
  
              @closed_issues_count += count 
   | 
  | 294 | 
   | 
  
            else 
   | 
  | 295 | 
   | 
  
              @open_issues_count += count 
   | 
  | 296 | 
   | 
  
            end 
   | 
  | 297 | 
   | 
  
          end 
   | 
  | 298 | 
   | 
  
          @issue_count = @open_issues_count + @closed_issues_count 
   | 
  | 299 | 
   | 
  
        end 
   | 
  | 300 | 
   | 
  
      end 
   | 
  | 301 | 
   | 
  
    
   | 
  | 302 | 
  364 | 
  
      # Update the issue's fixed versions. Used if a version's sharing changes. 
   | 
  | 303 | 
  365 | 
  
      def update_issues_from_sharing_change 
   | 
  | 304 | 
  366 | 
  
        if saved_change_to_sharing? 
   | 
  | ... | ... |  | 
  | 316 | 
  378 | 
  
        end 
   | 
  | 317 | 
  379 | 
  
      end 
   | 
  | 318 | 
  380 | 
  
    
   | 
  | 319 | 
   | 
  
      # Returns the average estimated time of assigned issues 
   | 
  | 320 | 
   | 
  
      # or 1 if no issue has an estimated time 
   | 
  | 321 | 
   | 
  
      # Used to weight unestimated issues in progress calculation 
   | 
  | 322 | 
   | 
  
      def estimated_average 
   | 
  | 323 | 
   | 
  
        if @estimated_average.nil? 
   | 
  | 324 | 
   | 
  
          average = fixed_issues.average(:estimated_hours).to_f 
   | 
  | 325 | 
   | 
  
          if average == 0 
   | 
  | 326 | 
   | 
  
            average = 1 
   | 
  | 327 | 
   | 
  
          end 
   | 
  | 328 | 
   | 
  
          @estimated_average = average 
   | 
  | 329 | 
   | 
  
        end 
   | 
  | 330 | 
   | 
  
        @estimated_average 
   | 
  | 331 | 
   | 
  
      end 
   | 
  | 332 | 
   | 
  
    
   | 
  | 333 | 
   | 
  
      # Returns the total progress of open or closed issues.  The returned percentage takes into account 
   | 
  | 334 | 
   | 
  
      # the amount of estimated time set for this version. 
   | 
  | 335 | 
   | 
  
      # 
   | 
  | 336 | 
   | 
  
      # Examples: 
   | 
  | 337 | 
   | 
  
      # issues_progress(true)   => returns the progress percentage for open issues. 
   | 
  | 338 | 
   | 
  
      # issues_progress(false)  => returns the progress percentage for closed issues. 
   | 
  | 339 | 
   | 
  
      def issues_progress(open) 
   | 
  | 340 | 
   | 
  
        @issues_progress ||= {}
   | 
  | 341 | 
   | 
  
        @issues_progress[open] ||= begin 
   | 
  | 342 | 
   | 
  
          progress = 0 
   | 
  | 343 | 
   | 
  
          if issues_count > 0 
   | 
  | 344 | 
   | 
  
            ratio = open ? 'done_ratio' : 100 
   | 
  | 345 | 
   | 
  
    
   | 
  | 346 | 
   | 
  
            done = fixed_issues.open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f
   | 
  | 347 | 
   | 
  
            progress = done / (estimated_average * issues_count) 
   | 
  | 348 | 
   | 
  
          end 
   | 
  | 349 | 
   | 
  
          progress 
   | 
  | 350 | 
   | 
  
        end 
   | 
  | 351 | 
   | 
  
      end 
   | 
  | 352 | 
   | 
  
    
   | 
  | 353 | 
  381 | 
  
      def referenced_by_a_custom_field? 
   | 
  | 354 | 
  382 | 
  
        CustomValue.joins(:custom_field). 
   | 
  | 355 | 
  383 | 
  
          where(:value => id.to_s, :custom_fields => {:field_format => 'version'}).any?
   |