From f3d7dac174ebdacf929481c1f8c534729fde951c Mon Sep 17 00:00:00 2001 From: Mischa The Evil Date: Fri, 13 Aug 2021 21:59:24 +0200 Subject: [PATCH] Add a start date to versions. If a version does not have a start date set explicitly, it falls back on the behavior of considering the earliest start date of the issues targeted against it as its start date. --- app/models/version.rb | 10 ++++-- .../projects/settings/_versions.html.erb | 4 ++- app/views/versions/_form.html.erb | 5 +++ app/views/versions/_overview.html.erb | 13 ++++--- app/views/versions/index.api.rsb | 1 + app/views/versions/show.api.rsb | 1 + ...210806100000_add_start_date_to_versions.rb | 5 +++ test/functional/versions_controller_test.rb | 8 +++-- test/unit/version_test.rb | 34 +++++++++++++++++++ 9 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20210806100000_add_start_date_to_versions.rb diff --git a/app/models/version.rb b/app/models/version.rb index 2e5430746..8e5ac9def 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -130,7 +130,7 @@ class Version < ActiveRecord::Base validates_uniqueness_of :name, :scope => [:project_id], :case_sensitive => true validates_length_of :name, :maximum => 60 validates_length_of :description, :wiki_page_title, :maximum => 255 - validates :effective_date, :date => true + validates :start_date, :effective_date, :date => true validates_inclusion_of :status, :in => VERSION_STATUSES validates_inclusion_of :sharing, :in => VERSION_SHARINGS @@ -154,6 +154,7 @@ class Version < ActiveRecord::Base safe_attributes 'name', 'description', + 'start_date', 'effective_date', 'due_date', 'wiki_page_title', @@ -218,8 +219,13 @@ class Version < ActiveRecord::Base base_reload(*args) end + # Returns the start_date of a given version from the database + def self.persistent_start_date(version) + Version.visible.where(id: version.id).pluck(:start_date) + end + def start_date - @start_date ||= fixed_issues.minimum('start_date') + @start_date = (read_attribute('start_date') || fixed_issues.minimum('start_date')) end def due_date diff --git a/app/views/projects/settings/_versions.html.erb b/app/views/projects/settings/_versions.html.erb index 80d67cc24..66c25476a 100644 --- a/app/views/projects/settings/_versions.html.erb +++ b/app/views/projects/settings/_versions.html.erb @@ -24,6 +24,7 @@ <%= l(:label_version) %> <%= l(:field_default_version) %> + <%= l(:field_start_date) %> <%= l(:field_effective_date) %> <%= l(:field_description) %> <%= l(:field_status) %> @@ -36,7 +37,8 @@ <%= link_to_version version %> <%= checked_image(version.id == @project.default_version_id) %> - <%= format_date(version.effective_date) %> + <%= format_date(version.start_date) %> + <%= format_date(version.effective_date) %> <%= version.description %> <%= l("version_status_#{version.status}") %> <%=h format_version_sharing(version.sharing) %> diff --git a/app/views/versions/_form.html.erb b/app/views/versions/_form.html.erb index 62119769b..2e323a64c 100644 --- a/app/views/versions/_form.html.erb +++ b/app/views/versions/_form.html.erb @@ -8,6 +8,11 @@

<%= f.select :status, Version::VERSION_STATUSES.collect {|s| [l("version_status_#{s}"), s]} %>

<% end %>

<%= f.text_field :wiki_page_title, :label => :label_wiki_page, :size => 60, :disabled => @project.wiki.nil? %>

+

+ <%= f.date_field :start_date, :size => 10, + :value => Version.persistent_start_date(@version) %> + <%= calendar_for('version_start_date') %> +

<%= f.date_field :effective_date, :size => 10 %><%= calendar_for('version_effective_date') %>

<%= f.select :sharing, @version.allowed_sharings.collect {|v| [format_version_sharing(v), v]} %>

<% if @version.new_record? %> diff --git a/app/views/versions/_overview.html.erb b/app/views/versions/_overview.html.erb index 48c6b5972..7afd4eace 100644 --- a/app/views/versions/_overview.html.erb +++ b/app/views/versions/_overview.html.erb @@ -1,9 +1,14 @@
-<% if version.completed? %> -

<%= format_date(version.effective_date) %>

-<% elsif version.effective_date %> -

<%= due_date_distance_in_words(version.effective_date) %> (<%= format_date(version.effective_date) %>)

+

+<%= "#{l(:field_start_date)}: #{format_date(version.start_date)}" if version.start_date %> +<% if version.start_date && version.due_date %> +– <% end %> +<%= "#{l(:field_due_date)}: #{format_date(version.due_date)}" if version.due_date %> +<% if version.effective_date && !version.completed? %> +<%= "(#{due_date_distance_in_words(version.effective_date)})" %> +<% end %> +

<%=h version.description %>

<% if version.custom_field_values.any? %> diff --git a/app/views/versions/index.api.rsb b/app/views/versions/index.api.rsb index 65354554f..39dfc60ad 100644 --- a/app/views/versions/index.api.rsb +++ b/app/views/versions/index.api.rsb @@ -7,6 +7,7 @@ api.array :versions, api_meta(:total_count => @versions.size) do api.name version.name api.description version.description api.status version.status + api.start_date version.start_date api.due_date version.effective_date api.sharing version.sharing api.wiki_page_title version.wiki_page_title diff --git a/app/views/versions/show.api.rsb b/app/views/versions/show.api.rsb index 2fa0f1290..a48831f53 100644 --- a/app/views/versions/show.api.rsb +++ b/app/views/versions/show.api.rsb @@ -5,6 +5,7 @@ api.version do api.name @version.name api.description @version.description api.status @version.status + api.start_date @version.start_date api.due_date @version.effective_date api.sharing @version.sharing api.wiki_page_title @version.wiki_page_title diff --git a/db/migrate/20210806100000_add_start_date_to_versions.rb b/db/migrate/20210806100000_add_start_date_to_versions.rb new file mode 100644 index 000000000..458c01437 --- /dev/null +++ b/db/migrate/20210806100000_add_start_date_to_versions.rb @@ -0,0 +1,5 @@ +class AddStartDateToVersions < ActiveRecord::Migration[5.2] + def change + add_column :versions, :start_date, :date, :after => :description + end +end \ No newline at end of file diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index 3691f71f5..0b5b33ecb 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -289,18 +289,22 @@ class VersionsControllerTest < Redmine::ControllerTest def test_post_update @request.session[:user_id] = 2 + + today = Date.today put :update, :params => { :id => 2, :version => { :name => 'New version name', - :effective_date => Date.today.strftime("%Y-%m-%d") + :start_date => today.yesterday.strftime("%Y-%m-%d"), + :effective_date => today.strftime("%Y-%m-%d") } } assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' version = Version.find(2) assert_equal 'New version name', version.name - assert_equal Date.today, version.effective_date + assert_equal today.yesterday, version.start_date + assert_equal today, version.effective_date end def test_post_update_with_validation_failure diff --git a/test/unit/version_test.rb b/test/unit/version_test.rb index 057a97e6e..41d46810d 100644 --- a/test/unit/version_test.rb +++ b/test/unit/version_test.rb @@ -53,6 +53,22 @@ class VersionTest < ActiveSupport::TestCase assert_nil project.reload.default_version end + def test_invalid_start_date_validation + v = Version.new(:project => Project.find(1), :name => '1.1', + :start_date => '99999-01-01') + assert !v.valid? + v.start_date = '2012-11-33' + assert !v.valid? + v.start_date = '2012-31-11' + assert !v.valid? + v.start_date = '-2012-31-11' + assert !v.valid? + v.start_date = 'ABC' + assert !v.valid? + assert_include I18n.translate('activerecord.errors.messages.not_a_date'), + v.errors[:start_date] + end + def test_invalid_effective_date_validation v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '99999-01-01') @@ -69,6 +85,24 @@ class VersionTest < ActiveSupport::TestCase v.errors[:effective_date] end + def test_start_date_with_no_value_saved_should_be_the_date_of_the_earliest_issue + project = Project.find(1) + Issue.generate!(:project => project, :subject => 'not assigned', :start_date => '2021-01-01') + v = Version.create!(:project => project, :name => 'Progress') + add_issue(v, :estimated_hours => 10, :start_date => '2021-03-01') + add_issue(v, :estimated_hours => 10, :start_date => '2021-02-01') + + assert_equal '2021-02-01', v.start_date.to_s + end + + def test_start_date_with_a_value_saved_should_be_the_value + project = Project.find(1) + v = Version.create!(:project => project, :name => 'Progress', :start_date => '2021-01-05') + add_issue(v, :estimated_hours => 10, :start_date => '2020-03-01') + + assert_equal '2021-01-05', v.start_date.to_s + end + def test_progress_should_be_0_with_no_assigned_issues project = Project.find(1) v = Version.create!(:project => project, :name => 'Progress') -- 2.26.0.windows.1