Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds upsertWorkWeeks mutation #315

Merged
merged 1 commit into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions app/graphql/mutations/upsert_work_weeks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Mutations
class UpsertWorkWeeks < BaseMutation
description "Create or update a work week record for a StaffPlan user."

# arguments passed to the `resolve` method
argument :assignment_id, ID, required: true, description: "The ID of the assignment this work week is being created for."
argument :work_weeks, [Types::StaffPlan::WorkWeeksInputObject], required: true, description: "Attributes for creating or updating a work week record for a StaffPlan user."

# return type from the mutation
type Types::StaffPlan::AssignmentType

def resolve(assignment_id:, work_weeks:)
current_company = context[:current_company]

# try and find the assignment
assignment = current_company.assignments.find(assignment_id)

unless assignment.user.memberships.active.exists?(company: current_company)
# if the assignment isn't for an active user, raise an error
raise GraphQL::ExecutionError, "User is not an active member of the company"
end

work_weeks.each do |ww|
work_week = assignment.work_weeks.find_or_initialize_by(cweek: ww.cweek, year: ww.year)
work_week.update!(ww.to_h.slice(:estimated_hours, :actual_hours))
end

assignment
end
end
end
40 changes: 40 additions & 0 deletions app/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,21 @@ type Mutation {
"""
year: Int!
): WorkWeek!

"""
Create or update a work week record for a StaffPlan user.
"""
upsertWorkWeeks(
"""
The ID of the assignment this work week is being created for.
"""
assignmentId: ID!

"""
Attributes for creating or updating a work week record for a StaffPlan user.
"""
workWeeks: [WorkWeeksInputObject!]!
): Assignment!
}

type Project {
Expand Down Expand Up @@ -470,3 +485,28 @@ type WorkWeek {
user: User!
year: Int!
}

"""
Attributes for creating or updating a work week record for a StaffPlan user.
"""
input WorkWeeksInputObject {
"""
The actual hours for this work week.
"""
actualHours: Int

"""
The calendar week number of the work week.
"""
cweek: Int!

"""
The estimated hours for this work week.
"""
estimatedHours: Int

"""
The calendar year of the work week.
"""
year: Int!
}
1 change: 1 addition & 0 deletions app/graphql/types/mutation_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Types
class MutationType < Types::BaseObject
field :set_current_company, mutation: Mutations::SetCurrentCompany
field :upsert_work_week, mutation: Mutations::UpsertWorkWeek
field :upsert_work_weeks, mutation: Mutations::UpsertWorkWeeks
field :upsert_assignment, mutation: Mutations::UpsertAssignment
field :upsert_project, mutation: Mutations::UpsertProject
field :upsert_client, mutation: Mutations::UpsertClient
Expand Down
8 changes: 8 additions & 0 deletions app/graphql/types/staff_plan/work_weeks_input_object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

class Types::StaffPlan::WorkWeeksInputObject < Types::BaseInputObject
description "Attributes for creating or updating a work week record for a StaffPlan user."
argument :cweek, Int, required: true, description: "The calendar week number of the work week."
argument :year, Int, required: true, description: "The calendar year of the work week."
argument :estimated_hours, Int, required: false, description: "The estimated hours for this work week."
argument :actual_hours, Int, required: false, description: "The actual hours for this work week."
end
24 changes: 24 additions & 0 deletions app/models/work_week.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,28 @@ class WorkWeek < ApplicationRecord
validates :year, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 2000, less_than_or_equal_to: 2200 }
validates :estimated_hours, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 168 }
validates :actual_hours, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 168 }
validate :no_future_actual_hours

private

def no_future_actual_hours
return if actual_hours_allowed?

assign_attributes(actual_hours: 0) if actual_hours > 0
end

def year_zero?
year.blank? || year == 0
end

def cweek_zero?
cweek.blank? || cweek == 0
end

def actual_hours_allowed?
return false if year_zero? || cweek_zero?

today = Date.today
today.year < year || (today.year == year && today.cweek >= cweek)
end
end
179 changes: 179 additions & 0 deletions spec/graphql/mutations/upsert_work_weeks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Mutations::UpsertWorkWeeks do

context "when updating work weeks" do
it "updates the work weeks with valid params" do
query_string = <<-GRAPHQL
mutation($assignmentId: ID!, $workWeeks: [WorkWeeksInputObject!]!) {
upsertWorkWeeks(assignmentId: $assignmentId, workWeeks: $workWeeks) {
workWeeks {
cweek
year
actualHours
estimatedHours
}
}
}
GRAPHQL

user = create(:user)
assignment = assignment_for_user(user:)
work_weeks = 5.times.map do |i|
date = Date.today - i.weeks
create(:work_week, :blank, assignment:, cweek: date.cweek, year: date.year)
end

updated_work_weeks = work_weeks.map.with_index do |week, i|
{
cweek: week.cweek,
year: week.year,
actualHours: i * 5,
estimatedHours: i * 6
}
end

result = StaffplanReduxSchema.execute(
query_string,
context: {
current_user: user,
current_company: assignment.company
},
variables: {
assignmentId: assignment.id,
workWeeks: updated_work_weeks
}
)

post_result = result["data"]["upsertWorkWeeks"]["workWeeks"]

post_result.each do |result|
work_week = updated_work_weeks.detect do |uww|
uww[:cweek] == result["cweek"] && uww[:year] == result["year"]
end

expect(work_week).to be_present
expect(work_week[:actualHours]).to eq(result["actualHours"])
expect(work_week[:estimatedHours]).to eq(result["estimatedHours"])
end
end

it "fails if the assignment is not found" do
query_string = <<-GRAPHQL
mutation($assignmentId: ID!, $workWeeks: [WorkWeeksInputObject!]!) {
upsertWorkWeeks(assignmentId: $assignmentId, workWeeks: $workWeeks) {
workWeeks {
cweek
year
actualHours
estimatedHours
}
}
}
GRAPHQL

user = create(:user)
work_weeks = Array(create(:work_week, :blank)).map do |ww|
{
cweek: ww.cweek,
year: ww.year,
actualHours: 5,
estimatedHours: 6
}
end

result = StaffplanReduxSchema.execute(
query_string,
context: {
current_user: user,
current_company: user.current_company
},
variables: {
assignmentId: -1,
workWeeks: work_weeks
}
)

post_result = result["errors"]
expect(post_result.length).to eq(1)
expect(post_result.first["message"]).to eq("Assignment not found")
end

it "fails if the current_user is not a member of the company that the assignment belongs to" do
query_string = <<-GRAPHQL
mutation($assignmentId: ID!, $workWeeks: [WorkWeeksInputObject!]!) {
upsertWorkWeeks(assignmentId: $assignmentId, workWeeks: $workWeeks) {
workWeeks {
actualHours
estimatedHours
}
}
}
GRAPHQL

user = create(:user)
assignment = assignment_for_user(user:)
random_user = create(:user)

result = StaffplanReduxSchema.execute(
query_string,
context: {
current_user: random_user,
current_company: random_user.current_company
},
variables: {
assignmentId: assignment.id,
workWeeks: [{
cweek: 15,
year: 2024,
actualHours: 5,
estimatedHours: 5
}]
}
)

post_result = result["errors"]
expect(post_result.length).to eq(1)
expect(post_result.first["message"]).to eq("Assignment not found")
end

it "fails if the user is not an active member of the company" do
query_string = <<-GRAPHQL
mutation($assignmentId: ID!, $workWeeks: [WorkWeeksInputObject!]!) {
upsertWorkWeeks(assignmentId: $assignmentId, workWeeks: $workWeeks) {
workWeeks {
actualHours
estimatedHours
}
}
}
GRAPHQL

work_week = create(:work_week, :blank)
work_week.user.memberships.update_all(status: "inactive")

result = StaffplanReduxSchema.execute(
query_string,
context: {
current_user: work_week.user,
current_company: work_week.company
},
variables: {
assignmentId: work_week.assignment_id,
workWeeks: [{
cweek: 14,
year: 2023,
actualHours: 5,
estimatedHours: 12
}]
}
)

post_result = result["errors"]
expect(post_result.length).to eq(1)
expect(post_result.first["message"]).to eq("User is not an active member of the company")
end
end
end
8 changes: 8 additions & 0 deletions spec/models/work_week_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
it { should validate_numericality_of(:estimated_hours).only_integer.is_greater_than_or_equal_to(0).is_less_than_or_equal_to(168) }
it { should validate_presence_of(:actual_hours) }
it { should validate_numericality_of(:actual_hours).only_integer.is_greater_than_or_equal_to(0).is_less_than_or_equal_to(168) }

it 'resets actual_hours values greater than 0 for future assignments' do
next_month = Date.today.next_month
work_week = create(:work_week, actual_hours: 1, year: next_month.year, cweek: next_month.cweek, assignment: create(:assignment))

expect(work_week).to be_valid
expect(work_week.reload.actual_hours).to eql(0)
end
end

context "associations" do
Expand Down
Loading