Feature #34609 » patch34609-1.diff
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 |