Skip to content

Commit

Permalink
feat: adds upsertWorkWeeks mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
fermion committed Sep 22, 2024
1 parent ecae692 commit 8a1f9c5
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 0 deletions.
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

0 comments on commit 8a1f9c5

Please sign in to comment.