patch34609-1.diff

Holger Mößinger, 2021-10-21 18:00

Download (17.8 KB)

View differences:

app/helpers/settings_helper.rb
185 185
  def parent_issue_dates_options
186 186
    options = [
187 187
      [:label_parent_task_attributes_derived, 'derived'],
188
      [:label_parent_task_attributes_partially_derived, 'partially_derived'],
188 189
      [:label_parent_task_attributes_independent, 'independent']
189 190
    ]
190 191

  
app/models/issue.rb
745 745
      errors.add :due_date, :greater_than_start_date
746 746
    end
747 747

  
748
    if dates_partially_derived? && start_date && start_date_changed? && start_date > children.minimum(:start_date)
749
      errors.add :start_date, :later_than_minimum_start_date, :date => format_date(children.minimum(:start_date))
750
    end
751

  
752
    if dates_partially_derived? && due_date && due_date_changed? && due_date < children.maximum(:due_date)
753
      errors.add :due_date, :earlier_than_maximum_due_date, :date => format_date(children.maximum(:due_date))
754
    end
755

  
748 756
    if start_date && start_date_changed? && soonest_start && start_date < soonest_start
749 757
      errors.add :start_date, :earlier_than_minimum_start_date, :date => format_date(soonest_start)
750 758
    end
......
1412 1420
    !leaf? && Setting.parent_issue_dates == 'derived'
1413 1421
  end
1414 1422

  
1423
  def dates_partially_derived?
1424
    !leaf? && Setting.parent_issue_dates == 'partially_derived'
1425
  end
1426

  
1415 1427
  def priority_derived?
1416 1428
    !leaf? && Setting.parent_issue_priority == 'derived'
1417 1429
  end
......
1829 1841
        end
1830 1842
      end
1831 1843

  
1844
      if p.dates_partially_derived?
1845
        # start/due dates = lowest/highest dates of children or own date
1846
        p.start_date = [p.children.minimum(:start_date), p.start_date].compact.min
1847
        p.due_date = [p.children.maximum(:due_date),p.due_date].compact.max
1848
        if p.start_date && p.due_date && p.due_date < p.start_date
1849
          p.start_date, p.due_date = p.due_date, p.start_date
1850
        end
1851
      end
1852

  
1832 1853
      if p.done_ratio_derived?
1833 1854
        # done ratio = average ratio of children weighted with their total estimated hours
1834 1855
        unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
config/locales/de.yml
146 146
        circular_dependency: "Diese Beziehung würde eine zyklische Abhängigkeit erzeugen"
147 147
        cant_link_an_issue_with_a_descendant: "Ein Ticket kann nicht mit einem seiner untergeordneten Tickets verlinkt werden"
148 148
        earlier_than_minimum_start_date: "kann wegen eines Vorgängertickets nicht vor %{date} liegen"
149
        later_than_minimum_start_date: "kann wegen eines Untertickets nicht nach %{date} liegen"
150
        earlier_than_maximum_due_date: "kann wegen eines Untertickets nicht vor %{date} liegen"
149 151
        not_a_regexp: "ist kein gültiger regulärer Ausdruck"
150 152
        open_issue_with_closed_parent: "Ein offenes Ticket kann nicht an ein geschlossenes übergeordnetes Ticket angehängt werden"
151 153
        must_contain_uppercase: "muss Großbuchstaben (A-Z) enthalten"
......
1173 1175
  field_remote_ip: IP-Adresse
1174 1176
  label_parent_task_attributes: Eigenschaften übergeordneter Tickets
1175 1177
  label_parent_task_attributes_derived: Abgeleitet von untergeordneten Tickets
1178
  label_parent_task_attributes_partially_derived: Teilweise abgeleitet von untergeordneten Tickets
1176 1179
  label_parent_task_attributes_independent: Unabhängig von untergeordneten Tickets
1177 1180
  label_time_entries_visibility_all: Alle Zeitaufwände
1178 1181
  label_time_entries_visibility_own: Nur eigene Aufwände
config/locales/en-GB.yml
134 134
        circular_dependency: "This relation would create a circular dependency"
135 135
        cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
136 136
        earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
137
        later_than_minimum_start_date: "cannot be later than %{date} because of child issues"
138
        earlier_than_maximum_due_date: "cannot be earlier than %{date} because of child issues"
137 139
        not_a_regexp: "is not a valid regular expression"
138 140
        open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task"
139 141
        must_contain_uppercase: "must contain uppercase letters (A-Z)"
......
1132 1134
  field_time_entries_visibility: Time logs visibility
1133 1135
  setting_password_max_age: Require password change after
1134 1136
  label_parent_task_attributes: Parent tasks attributes
1137
  label_parent_task_attributes_partially_derived: Partially calculated from subtasks
1135 1138
  label_parent_task_attributes_derived: Calculated from subtasks
1136 1139
  label_parent_task_attributes_independent: Independent of subtasks
1137 1140
  label_time_entries_visibility_all: All time entries
config/locales/en.yml
130 130
        circular_dependency: "This relation would create a circular dependency"
131 131
        cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
132 132
        earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
133
        earlier_than_maximum_due_date: "cannot be earlier than %{date} because of child issues"
134
        later_than_minimum_start_date: "cannot be later than %{date} because of child issues"
133 135
        not_a_regexp: "is not a valid regular expression"
134 136
        open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task"
135 137
        must_contain_uppercase: "must contain uppercase letters (A-Z)"
......
1064 1066
  label_blank_value: blank
1065 1067
  label_parent_task_attributes: Parent tasks attributes
1066 1068
  label_parent_task_attributes_derived: Calculated from subtasks
1069
  label_parent_task_attributes_partially_derived: Partially calculated from subtasks
1067 1070
  label_parent_task_attributes_independent: Independent of subtasks
1068 1071
  label_time_entries_visibility_all: All time entries
1069 1072
  label_time_entries_visibility_own: Time entries created by the user
1070
- 
app/helpers/settings_helper.rb
195 195
  def parent_issue_priority_options
196 196
    options = [
197 197
      [:label_parent_task_attributes_derived, 'derived'],
198
      [:label_parent_task_attributes_partially_derived, 'partially_derived'],
198 199
      [:label_parent_task_attributes_independent, 'independent']
199 200
    ]
200 201

  
......
204 205
  def parent_issue_done_ratio_options
205 206
    options = [
206 207
      [:label_parent_task_attributes_derived, 'derived'],
208
      [:label_parent_task_attributes_partially_derived, 'partially_derived'],
207 209
      [:label_parent_task_attributes_independent, 'independent']
208 210
    ]
209 211

  
app/models/issue.rb
765 765
      end
766 766
    end
767 767

  
768
    if priority_partially_derived? && priority_id && priority_id_changed?
769
      #Get derived priority from children and reject if own priority is lower
770
      children_priority_position = children.open.joins(:priority).maximum("#{IssuePriority.table_name}.position")
771
      own_priority_position = IssuePriority.find_by_id(priority_id).position
772
      if children_priority_position &&  children_priority_position > own_priority_position
773
        own_priority_name = IssuePriority.find_by_position(own_priority_position).name
774
	children_priority_name = IssuePriority.find_by_position(children_priority_position)
775
        errors.add :priority, :priority_lower_than_children, :own_priority => own_priority_name, :priority => children_priority_name
776
      end
777
    end
778

  
779
    if done_ratio_partially_derived? && done_ratio && done_ratio_changed?
780
      #get derived done_ratio from children and reject if own done_ratio is higher
781
      children_done_ratio = 100 #set comparison to max in case we can't determine a child done ratio below.
782
      chld=children.to_a
783
      if chld.any?
784
        chld_with_total_estimated_hours = chld.select {|c| c.total_estimated_hours.to_f > 0.0}
785
        if chld_with_total_estimated_hours.any?
786
          average= chld_with_total_estimated_hours.sum(&:total_estimated_hours).to_d / chld_with_total_estimated_hours.count
787
	else
788
	  average = 1.0.to_d
789
        end
790
        done = children.sum do |c|
791
          estimated = (c.total_estimated_hours || 0.0).to_d
792
	  estimated = average unless estimated > 0.0
793
	  ratio = c.closed? ? 100 : (c.done_ratio || 0)
794
	  estimated * ratio
795
        end
796
        progress = done / (average * chld.count)
797
        children_done_ratio = progress.floor
798
      end
799

  
800
      own_done_ratio = done_ratio
801
      if children_done_ratio && children_done_ratio < own_done_ratio
802
        errors.add :done_ratio, :done_ratio_higher_than_children, :done_ratio => children_done_ratio
803
      end
804
    end
805

  
768 806
    # Checks that the issue can not be added/moved to a disabled tracker
769 807
    if project && (tracker_id_changed? || project_id_changed?)
770 808
      if tracker && !project.trackers.include?(tracker)
......
1428 1466
    !leaf? && Setting.parent_issue_priority == 'derived'
1429 1467
  end
1430 1468

  
1469
  def priority_partially_derived?
1470
    !leaf? && Setting.parent_issue_priority == 'partially_derived'
1471
  end
1472

  
1431 1473
  def done_ratio_derived?
1432 1474
    !leaf? && Setting.parent_issue_done_ratio == 'derived'
1433 1475
  end
1434 1476

  
1477
  def done_ratio_partially_derived?
1478
    !leaf? && Setting.parent_issue_done_ratio == 'partially_derived'
1479
  end
1480

  
1435 1481
  def <=>(issue)
1436 1482
    if issue.nil?
1437 1483
      -1
......
1821 1867

  
1822 1868
  def recalculate_attributes_for(issue_id)
1823 1869
    if issue_id && p = Issue.find_by_id(issue_id)
1824
      if p.priority_derived?
1825
        # priority = highest priority of open children
1870
      if p.priority_derived? || p.priority_partially_derived?
1871
        # priority = highest priority of open children (or higher if partially derived)
1826 1872
        # priority is left unchanged if all children are closed and there's no default priority defined
1827 1873
        if priority_position =
1828 1874
             p.children.open.joins(:priority).maximum("#{IssuePriority.table_name}.position")
1829
          p.priority = IssuePriority.find_by_position(priority_position)
1875
             if p.priority_partially_derived?
1876
               #like priority_derived, but priority can be higher than highest childs priority
1877
               #TODO: use priority derived or set whichever is higher
1878
	       p.priority = IssuePriority.find_by_position(priority_position)
1879
             else             
1880
	       p.priority = IssuePriority.find_by_position(priority_position)
1881
             end
1830 1882
        elsif default_priority = IssuePriority.default
1831 1883
          p.priority = default_priority
1832 1884
        end
......
1850 1902
        end
1851 1903
      end
1852 1904

  
1853
      if p.done_ratio_derived?
1854
        # done ratio = average ratio of children weighted with their total estimated hours
1905
      if p.done_ratio_derived? || p.done_ratio_partially_derived?
1906
        # done ratio = average ratio of children weighted with their total estimated hours (or lower if partially derived)
1855 1907
        unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
1856 1908
          children = p.children.to_a
1857 1909
          if children.any?
......
1870 1922
              estimated * ratio
1871 1923
            end
1872 1924
            progress = done / (average * children.count)
1873
            p.done_ratio = progress.floor
1925
	    if done_ratio_partially_derived?
1926
              #done ratio like done_ratio_derived, but parent done ratio can be lower than calculated done ratio
1927
	      #TODO: use done_ratio derived or set, whichever is lower
1928
              p.done_ratio = [progress.floor, p.done_ratio].compact.min
1929
	    else
1930
              p.done_ratio = progress.floor
1931
            end
1874 1932
          end
1875 1933
        end
1876 1934
      end
config/locales/de.yml
148 148
        earlier_than_minimum_start_date: "kann wegen eines Vorgängertickets nicht vor %{date} liegen"
149 149
        later_than_minimum_start_date: "kann wegen eines Untertickets nicht nach %{date} liegen"
150 150
        earlier_than_maximum_due_date: "kann wegen eines Untertickets nicht vor %{date} liegen"
151
        priority_lower_than_children: "(%{own_priority}) kann nicht niedriger sein als die höchste Priorität eines Untertickets (%{priority})"
152
        done_ratio_higher_than_children: "kann nicht höher sein, als der Ticketfortschritt der Untertickets (%{done_ratio})"
151 153
        not_a_regexp: "ist kein gültiger regulärer Ausdruck"
152 154
        open_issue_with_closed_parent: "Ein offenes Ticket kann nicht an ein geschlossenes übergeordnetes Ticket angehängt werden"
153 155
        must_contain_uppercase: "muss Großbuchstaben (A-Z) enthalten"
config/locales/en-GB.yml
136 136
        earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
137 137
        later_than_minimum_start_date: "cannot be later than %{date} because of child issues"
138 138
        earlier_than_maximum_due_date: "cannot be earlier than %{date} because of child issues"
139
        priority_lower_than_children: "(%{own_priority}) cannot be lower than maximum priority derived from children (%{priority})"
140
        done_ratio_higher_than_children: "cannot be higher than done ratio derived from children (%{done_ratio})"
139 141
        not_a_regexp: "is not a valid regular expression"
140 142
        open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task"
141 143
        must_contain_uppercase: "must contain uppercase letters (A-Z)"
config/locales/en.yml
132 132
        earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
133 133
        earlier_than_maximum_due_date: "cannot be earlier than %{date} because of child issues"
134 134
        later_than_minimum_start_date: "cannot be later than %{date} because of child issues"
135
        priority_lower_than_children: "(%{own_priority}) cannot be lower than maximum priority derived from children (%{priority})"
136
        done_ratio_higher_than_children: "cannot be higher than done ratio derived from children (%{done_ratio})"
135 137
        not_a_regexp: "is not a valid regular expression"
136 138
        open_issue_with_closed_parent: "An open issue cannot be attached to a closed parent task"
137 139
        must_contain_uppercase: "must contain uppercase letters (A-Z)"
138
- 
app/models/issue.rb
1875 1875
             if p.priority_partially_derived?
1876 1876
               #like priority_derived, but priority can be higher than highest childs priority
1877 1877
               #TODO: use priority derived or set whichever is higher
1878
	       p.priority = IssuePriority.find_by_position(priority_position)
1878
	       p.priority = IssuePriority.find_by_position([p.priority.position, priority_position].compact.max)
1879 1879
             else             
1880 1880
	       p.priority = IssuePriority.find_by_position(priority_position)
1881 1881
             end
1882
-