diff --git a/modules/resource_management/app/models/resource_allocation.rb b/modules/resource_management/app/models/resource_allocation.rb index f5174116280..12ff081a309 100644 --- a/modules/resource_management/app/models/resource_allocation.rb +++ b/modules/resource_management/app/models/resource_allocation.rb @@ -29,6 +29,8 @@ #++ class ResourceAllocation < ApplicationRecord + ALLOWED_ENTITY_TYPES = %w[WorkPackage].freeze + belongs_to :entity, polymorphic: true, optional: false belongs_to :principal, class_name: "User", optional: true, inverse_of: :resource_allocations @@ -46,6 +48,10 @@ class ResourceAllocation < ApplicationRecord presence: true, numericality: { only_integer: true, greater_than: 0 } + validates :entity_type, + inclusion: { in: ALLOWED_ENTITY_TYPES }, + allow_blank: true + validate :end_date_after_start_date # Resource allocations are scoped to whatever project their (polymorphic) diff --git a/modules/resource_management/spec/contracts/resource_allocations/create_contract_spec.rb b/modules/resource_management/spec/contracts/resource_allocations/create_contract_spec.rb index 72427bd9319..435a8cdb288 100644 --- a/modules/resource_management/spec/contracts/resource_allocations/create_contract_spec.rb +++ b/modules/resource_management/spec/contracts/resource_allocations/create_contract_spec.rb @@ -43,8 +43,8 @@ RSpec.describe ResourceAllocations::CreateContract do let(:current_user) do create(:user, member_with_permissions: { project => %i[view_resource_planners allocate_user_resources] }) end - let(:planner) { create(:resource_planner, project:, principal: current_user) } - let(:resource_allocation) { build_stubbed(:resource_allocation, entity: planner, principal: current_user) } + let(:work_package) { create(:work_package, project:) } + let(:resource_allocation) { build_stubbed(:resource_allocation, entity: work_package, principal: current_user) } let(:contract) { described_class.new(resource_allocation, current_user) } it "allows entity to be set" do diff --git a/modules/resource_management/spec/contracts/resource_allocations/delete_contract_spec.rb b/modules/resource_management/spec/contracts/resource_allocations/delete_contract_spec.rb index a73a6ea38cd..319cc59bd94 100644 --- a/modules/resource_management/spec/contracts/resource_allocations/delete_contract_spec.rb +++ b/modules/resource_management/spec/contracts/resource_allocations/delete_contract_spec.rb @@ -36,9 +36,9 @@ RSpec.describe ResourceAllocations::DeleteContract do shared_let(:project) { create(:project, enabled_module_names: %w[resource_management]) } shared_let(:owner) { create(:user) } - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } - let(:resource_allocation) { build_stubbed(:resource_allocation, entity: planner, principal: owner) } + let(:resource_allocation) { build_stubbed(:resource_allocation, entity: work_package, principal: owner) } let(:contract) { described_class.new(resource_allocation, current_user) } context "when user has allocate_user_resources" do diff --git a/modules/resource_management/spec/contracts/resource_allocations/shared_contract_examples.rb b/modules/resource_management/spec/contracts/resource_allocations/shared_contract_examples.rb index 6dc00ed586e..107c7c1440c 100644 --- a/modules/resource_management/spec/contracts/resource_allocations/shared_contract_examples.rb +++ b/modules/resource_management/spec/contracts/resource_allocations/shared_contract_examples.rb @@ -34,10 +34,10 @@ require "contracts/shared/model_contract_shared_context" RSpec.shared_examples_for "resource allocation contract" do shared_let(:project) { create(:project, enabled_module_names: %w[resource_management]) } shared_let(:owner) { create(:user) } - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } let(:resource_allocation) do - build_stubbed(:resource_allocation, entity: planner, principal: owner) + build_stubbed(:resource_allocation, entity: work_package, principal: owner) end context "when user has the allocate_user_resources permission" do diff --git a/modules/resource_management/spec/contracts/resource_allocations/update_contract_spec.rb b/modules/resource_management/spec/contracts/resource_allocations/update_contract_spec.rb index e21158a8e33..ebbcae25b16 100644 --- a/modules/resource_management/spec/contracts/resource_allocations/update_contract_spec.rb +++ b/modules/resource_management/spec/contracts/resource_allocations/update_contract_spec.rb @@ -43,8 +43,8 @@ RSpec.describe ResourceAllocations::UpdateContract do let(:current_user) do create(:user, member_with_permissions: { project => %i[view_resource_planners allocate_user_resources] }) end - let(:planner) { create(:resource_planner, project:, principal: current_user) } - let(:resource_allocation) { build_stubbed(:resource_allocation, entity: planner, principal: current_user) } + let(:work_package) { create(:work_package, project:) } + let(:resource_allocation) { build_stubbed(:resource_allocation, entity: work_package, principal: current_user) } let(:contract) { described_class.new(resource_allocation, current_user) } it "does not allow entity to be set" do diff --git a/modules/resource_management/spec/factories/resource_allocation_factory.rb b/modules/resource_management/spec/factories/resource_allocation_factory.rb index f277279473e..cbec2ab1045 100644 --- a/modules/resource_management/spec/factories/resource_allocation_factory.rb +++ b/modules/resource_management/spec/factories/resource_allocation_factory.rb @@ -30,7 +30,7 @@ FactoryBot.define do factory :resource_allocation, class: "ResourceAllocation" do - entity factory: :resource_planner + entity factory: :work_package principal factory: :user state { "requested" } start_date { Date.new(2026, 1, 5) } diff --git a/modules/resource_management/spec/models/resource_allocation_spec.rb b/modules/resource_management/spec/models/resource_allocation_spec.rb index 233770a069e..1129790d56b 100644 --- a/modules/resource_management/spec/models/resource_allocation_spec.rb +++ b/modules/resource_management/spec/models/resource_allocation_spec.rb @@ -70,9 +70,9 @@ RSpec.describe ResourceAllocation do describe "validations" do shared_let(:project) { create(:project, enabled_module_names: %w[resource_management]) } shared_let(:owner) { create(:user, member_with_permissions: { project => %i[view_resource_planners] }) } - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } - let(:allocation) { build(:resource_allocation, entity: planner, principal: owner) } + let(:allocation) { build(:resource_allocation, entity: work_package, principal: owner) } it "is valid with the factory defaults" do expect(allocation).to be_valid @@ -115,6 +115,23 @@ RSpec.describe ResourceAllocation do end end + describe "entity type" do + it "lists the supported entity types" do + expect(described_class::ALLOWED_ENTITY_TYPES).to eq(%w[WorkPackage]) + end + + it "is valid when the entity type is in the allowed list" do + allocation.entity = work_package + expect(allocation).to be_valid + end + + it "is invalid when the entity type is outside the allowed list" do + allocation.entity = create(:resource_planner, project:, principal: owner) + expect(allocation).not_to be_valid + expect(allocation.errors.symbols_for(:entity_type)).to include(:inclusion) + end + end + describe "allocated_time numericality" do it "is invalid when zero" do allocation.allocated_time = 0 @@ -175,7 +192,7 @@ RSpec.describe ResourceAllocation do describe "user_filter serialization" do shared_let(:project) { create(:project, enabled_module_names: %w[resource_management]) } shared_let(:owner) { create(:user, member_with_permissions: { project => %i[view_resource_planners] }) } - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } it "serializes filters using the same coder as UserQuery" do coder = described_class.type_for_attribute(:user_filter).coder @@ -191,7 +208,7 @@ RSpec.describe ResourceAllocation do filter.operator = "~" filter.values = ["alice"] - allocation = create(:resource_allocation, entity: planner, principal: owner, user_filter: [filter]) + allocation = create(:resource_allocation, entity: work_package, principal: owner, user_filter: [filter]) reloaded = described_class.find(allocation.id) expect(reloaded.user_filter.size).to eq(1) @@ -201,7 +218,7 @@ RSpec.describe ResourceAllocation do end it "defaults to an empty array" do - allocation = create(:resource_allocation, entity: planner, principal: owner) + allocation = create(:resource_allocation, entity: work_package, principal: owner) expect(allocation.reload.user_filter).to eq([]) end end diff --git a/modules/resource_management/spec/services/resource_allocations/create_service_spec.rb b/modules/resource_management/spec/services/resource_allocations/create_service_spec.rb index 213d86df16d..9e1ca208f6a 100644 --- a/modules/resource_management/spec/services/resource_allocations/create_service_spec.rb +++ b/modules/resource_management/spec/services/resource_allocations/create_service_spec.rb @@ -35,12 +35,12 @@ RSpec.describe ResourceAllocations::CreateService, type: :model do shared_let(:owner) do create(:user, member_with_permissions: { project => %i[view_resource_planners allocate_user_resources] }) end - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } let(:assignee) { create(:user, member_with_permissions: { project => %i[view_resource_planners] }) } let(:params) do { - entity: planner, + entity: work_package, principal: assignee, start_date: Date.new(2026, 1, 1), end_date: Date.new(2026, 1, 31), @@ -53,7 +53,7 @@ RSpec.describe ResourceAllocations::CreateService, type: :model do it "creates a resource allocation" do result = service_call expect(result).to be_success, "expected success but got: #{result.errors.full_messages}" - expect(result.result.entity).to eq(planner) + expect(result.result.entity).to eq(work_package) expect(result.result.principal).to eq(assignee) expect(result.result.allocated_time).to eq(8) end diff --git a/modules/resource_management/spec/services/resource_allocations/delete_service_spec.rb b/modules/resource_management/spec/services/resource_allocations/delete_service_spec.rb index 2cd83d64c93..a25f95da2da 100644 --- a/modules/resource_management/spec/services/resource_allocations/delete_service_spec.rb +++ b/modules/resource_management/spec/services/resource_allocations/delete_service_spec.rb @@ -35,9 +35,9 @@ RSpec.describe ResourceAllocations::DeleteService, type: :model do shared_let(:owner) do create(:user, member_with_permissions: { project => %i[view_resource_planners allocate_user_resources] }) end - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } - let!(:resource_allocation) { create(:resource_allocation, entity: planner, principal: owner) } + let!(:resource_allocation) { create(:resource_allocation, entity: work_package, principal: owner) } subject(:service_call) do described_class.new(user:, model: resource_allocation).call diff --git a/modules/resource_management/spec/services/resource_allocations/update_service_spec.rb b/modules/resource_management/spec/services/resource_allocations/update_service_spec.rb index 81ae0bd5e57..8eff1cfe6dc 100644 --- a/modules/resource_management/spec/services/resource_allocations/update_service_spec.rb +++ b/modules/resource_management/spec/services/resource_allocations/update_service_spec.rb @@ -35,10 +35,10 @@ RSpec.describe ResourceAllocations::UpdateService, type: :model do shared_let(:owner) do create(:user, member_with_permissions: { project => %i[view_resource_planners allocate_user_resources] }) end - shared_let(:planner) { create(:resource_planner, project:, principal: owner) } + shared_let(:work_package) { create(:work_package, project:) } let!(:resource_allocation) do - create(:resource_allocation, entity: planner, principal: owner, state: "requested", allocated_time: 8) + create(:resource_allocation, entity: work_package, principal: owner, state: "requested", allocated_time: 8) end subject(:service_call) do @@ -52,13 +52,13 @@ RSpec.describe ResourceAllocations::UpdateService, type: :model do end context "when attempting to change the entity" do - let(:other_planner) { create(:resource_planner, project:, principal: owner) } + let(:other_work_package) { create(:work_package, project:) } it "fails because entity is not writable" do - result = described_class.new(user: owner, model: resource_allocation).call(entity: other_planner) + result = described_class.new(user: owner, model: resource_allocation).call(entity: other_work_package) expect(result).not_to be_success expect(result.errors.symbols_for(:entity_id)).to include(:error_readonly) - expect(resource_allocation.reload.entity).to eq(planner) + expect(resource_allocation.reload.entity).to eq(work_package) end end