From 9c4d4ebc466d6f8767f4b943b11eb604cf6d3715 Mon Sep 17 00:00:00 2001 From: Markus Kahl Date: Mon, 4 May 2020 13:27:19 +0100 Subject: [PATCH] added :schedule_manually attribute to work package --- app/contracts/work_packages/base_contract.rb | 2 + app/models/work_package/scheduling_rules.rb | 4 ++ config/locales/en.yml | 1 + ..._add_schedule_manually_to_work_packages.rb | 9 +++++ docs/api/apiv3/endpoints/work-packages.apib | 3 +- .../schema/work_package_schema_representer.rb | 5 +++ .../work_packages/work_package_representer.rb | 8 ++++ .../work_package_schema_representer_spec.rb | 11 +++++ .../work_package_payload_representer_spec.rb | 22 ++++++++++ .../work_package_representer_spec.rb | 26 ++++++++++++ .../api/v3/work_package_resource_spec.rb | 40 +++++++++++++++++++ 11 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20200504085933_add_schedule_manually_to_work_packages.rb diff --git a/app/contracts/work_packages/base_contract.rb b/app/contracts/work_packages/base_contract.rb index e8cc20ae93..deea6d3e55 100644 --- a/app/contracts/work_packages/base_contract.rb +++ b/app/contracts/work_packages/base_contract.rb @@ -80,6 +80,8 @@ module WorkPackages model.project.possible_responsible_members end + attribute :schedule_manually + attribute :start_date, writeable: ->(*) { model.leaf? diff --git a/app/models/work_package/scheduling_rules.rb b/app/models/work_package/scheduling_rules.rb index d58977679e..b840adee09 100644 --- a/app/models/work_package/scheduling_rules.rb +++ b/app/models/work_package/scheduling_rules.rb @@ -38,6 +38,10 @@ module WorkPackage::SchedulingRules .call(date) end + def schedule_automatically? + !schedule_manually? + end + # Calculates the minimum date that # will not violate the precedes relations (max(finish date, start date) + delay) # of this work package or its ancestors diff --git a/config/locales/en.yml b/config/locales/en.yml index 2702094a2c..0dd08f08ab 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -535,6 +535,7 @@ en: parent_work_package: "Parent" priority: "Priority" progress: "Progress (%)" + schedule_manually: "Schedule manually" spent_hours: "Spent time" spent_time: "Spent time" subproject: "Subproject" diff --git a/db/migrate/20200504085933_add_schedule_manually_to_work_packages.rb b/db/migrate/20200504085933_add_schedule_manually_to_work_packages.rb new file mode 100644 index 0000000000..351e7845d2 --- /dev/null +++ b/db/migrate/20200504085933_add_schedule_manually_to_work_packages.rb @@ -0,0 +1,9 @@ +class AddScheduleManuallyToWorkPackages < ActiveRecord::Migration[6.0] + def change + add_column :work_packages, :schedule_manually, :boolean, default: false + + # We add a partial index here because 90% of the values will be false. + # So we only index the true values. This way the index is actually useful. + add_index :work_packages, :schedule_manually, where: :schedule_manually + end +end diff --git a/docs/api/apiv3/endpoints/work-packages.apib b/docs/api/apiv3/endpoints/work-packages.apib index 3261b65c45..523ae0af22 100644 --- a/docs/api/apiv3/endpoints/work-packages.apib +++ b/docs/api/apiv3/endpoints/work-packages.apib @@ -50,6 +50,7 @@ | subject | Work package subject | String | not null; 1 <= length <= 255 | READ / WRITE | | | type | Name of the work package's type | String | not null | READ | | | description | The work package description | Formattable | | READ / WRITE | | +| scheduleManually | If false (default) schedule automatically. | Boolean | | READ / WRITE | | | startDate | Scheduled beginning of a work package | Date | Cannot be set for parent work packages; must be equal or greater than the earliest possible start date; Exists only on work packages of a non milestone type | READ / WRITE | | | dueDate | Scheduled end of a work package | Date | Cannot be set for parent work packages; must be greater than or equal to the start date; Exists only on work packages of a non milestone type | READ / WRITE | | | date | Date on which a milestone is achieved | Date | Exists only on work packages of a milestone type @@ -257,6 +258,7 @@ and [update](#work-packages-work-package-patch). The attachments the work packag "raw": "Develop super cool OpenProject API.", "html": "

Develop super cool OpenProject API.

" }, + "scheduleManually": false, "startDate": null, "dueDate": null, "estimatedTime": "PT2H", @@ -2312,4 +2314,3 @@ Gets a list of users that can be assigned as the responsible of a work package i "errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound", "message": "The specified project does not exist." } - diff --git a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb index 882c3fe66b..41c922e9b3 100644 --- a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb +++ b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb @@ -119,6 +119,11 @@ module API type: 'Formattable', required: false + schema :schedule_manually, + type: 'Boolean', + required: false, + has_default: true + schema :start_date, type: 'Date', required: false, diff --git a/lib/api/v3/work_packages/work_package_representer.rb b/lib/api/v3/work_packages/work_package_representer.rb index caeda92423..e782d7d29e 100644 --- a/lib/api/v3/work_packages/work_package_representer.rb +++ b/lib/api/v3/work_packages/work_package_representer.rb @@ -329,6 +329,10 @@ module API formattable_property :description + property :schedule_manually, + exec_context: :decorator, + getter: ->(*) { represented.schedule_manually? } + date_property :start_date, skip_render: ->(represented:, **) { represented.milestone? @@ -544,6 +548,10 @@ module API @visible_children ||= represented.children.select(&:visible?) end + def schedule_manually=(value) + represented.schedule_manually = value + end + def estimated_time=(value) represented.estimated_hours = datetime_formatter.parse_duration_to_hours(value, 'estimatedTime', diff --git a/spec/lib/api/v3/work_packages/schema/work_package_schema_representer_spec.rb b/spec/lib/api/v3/work_packages/schema/work_package_schema_representer_spec.rb index c1f0466d82..728f2c63a9 100644 --- a/spec/lib/api/v3/work_packages/schema/work_package_schema_representer_spec.rb +++ b/spec/lib/api/v3/work_packages/schema/work_package_schema_representer_spec.rb @@ -289,6 +289,17 @@ describe ::API::V3::WorkPackages::Schema::WorkPackageSchemaRepresenter do end end + describe 'scheduleManually' do + it_behaves_like 'has basic schema properties' do + let(:path) { 'scheduleManually' } + let(:type) { 'Boolean' } + let(:name) { I18n.t('activerecord.attributes.work_package.schedule_manually') } + let(:required) { false } + let(:has_default) { true } + let(:writable) { true } + end + end + describe 'date' do before do allow(schema) diff --git a/spec/lib/api/v3/work_packages/work_package_payload_representer_spec.rb b/spec/lib/api/v3/work_packages/work_package_payload_representer_spec.rb index d129c11f4e..08e89a748f 100644 --- a/spec/lib/api/v3/work_packages/work_package_payload_representer_spec.rb +++ b/spec/lib/api/v3/work_packages/work_package_payload_representer_spec.rb @@ -485,6 +485,28 @@ describe ::API::V3::WorkPackages::WorkPackagePayloadRepresenter do end end + describe 'scheduleManually' do + let(:value) { raise "define value" } + + let(:attributes) { { scheduleManually: value } } + + context 'with true' do + let(:value) { true } + + it 'reads true' do + expect(subject.schedule_manually).to eq true + end + end + + context 'with false' do + let(:value) { false } + + it 'reads false' do + expect(subject.schedule_manually).to eq false + end + end + end + describe 'startDate' do it_behaves_like 'settable ISO 8601 date only' do let(:property) { :startDate } diff --git a/spec/lib/api/v3/work_packages/work_package_representer_spec.rb b/spec/lib/api/v3/work_packages/work_package_representer_spec.rb index 6571b91094..cab4abd7f0 100644 --- a/spec/lib/api/v3/work_packages/work_package_representer_spec.rb +++ b/spec/lib/api/v3/work_packages/work_package_representer_spec.rb @@ -41,6 +41,7 @@ describe ::API::V3::WorkPackages::WorkPackageRepresenter do let(:priority) { FactoryBot.build_stubbed(:priority, updated_at: Time.now) } let(:assignee) { nil } let(:responsible) { nil } + let(:schedule_manually) { nil } let(:start_date) { Date.today.to_datetime } let(:due_date) { Date.today.to_datetime } let(:type_milestone) { false } @@ -49,6 +50,7 @@ describe ::API::V3::WorkPackages::WorkPackageRepresenter do let(:spent_hours) { 0 } let(:work_package) do FactoryBot.build_stubbed(:stubbed_work_package, + schedule_manually: schedule_manually, start_date: start_date, due_date: due_date, done_ratio: 50, @@ -122,6 +124,30 @@ describe ::API::V3::WorkPackages::WorkPackageRepresenter do let(:html) { '

' + work_package.description + '

' } end + describe 'scheduleManually' do + context 'no value' do + it 'renders as false (default value)' do + is_expected.to be_json_eql(false.to_json).at_path('scheduleManually') + end + end + + context 'false' do + let(:schedule_manually) { false } + + it 'renders as false' do + is_expected.to be_json_eql(false.to_json).at_path('scheduleManually') + end + end + + context 'true' do + let(:schedule_manually) { true } + + it 'renders as true' do + is_expected.to be_json_eql(true.to_json).at_path('scheduleManually') + end + end + end + describe 'startDate' do it_behaves_like 'has ISO 8601 date only' do let(:date) { start_date } diff --git a/spec/requests/api/v3/work_package_resource_spec.rb b/spec/requests/api/v3/work_package_resource_spec.rb index 2fa73d4e53..4dc5d475a6 100644 --- a/spec/requests/api/v3/work_package_resource_spec.rb +++ b/spec/requests/api/v3/work_package_resource_spec.rb @@ -474,6 +474,19 @@ describe 'API v3 Work package resource', end end + context 'schedule manually' do + let(:schedule_manually) { true } + let(:params) { valid_params.merge(scheduleManually: schedule_manually) } + + include_context 'patch request' + + it { expect(response.status).to eq(200) } + + it 'should update the scheduling mode' do + expect(subject.body).to be_json_eql(schedule_manually.to_json).at_path('scheduleManually') + end + end + context 'start date' do let(:dateString) { Date.today.to_date.iso8601 } let(:params) { valid_params.merge(startDate: dateString) } @@ -1260,6 +1273,33 @@ describe 'API v3 Work package resource', end end + context 'schedule manually' do + let(:work_package) { WorkPackage.first } + + context 'with true' do + # mind the () for the super call, those are required in rspec's super + let(:parameters) { super().merge(scheduleManually: true) } + + it 'should set the scheduling mode to true' do + expect(work_package.schedule_manually).to eq true + end + end + + context 'with false' do + let(:parameters) { super().merge(scheduleManually: false) } + + it 'should set the scheduling mode to false' do + expect(work_package.schedule_manually).to eq false + end + end + + context 'with scheduleManually absent' do + it 'should set the scheduling mode to false (default)' do + expect(work_package.schedule_manually).to eq false + end + end + end + context 'invalid value' do let(:parameters) do {