Skip to content

Commit

Permalink
fix: fixed hour value logic
Browse files Browse the repository at this point in the history
  • Loading branch information
celsoMartins committed Dec 5, 2023
1 parent 91c87d8 commit aca5395
Show file tree
Hide file tree
Showing 17 changed files with 430 additions and 326 deletions.
1 change: 0 additions & 1 deletion app/graphql/mutations/save_membership_mutation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def resolve(membership_id:, member_role:, hours_per_month:, effort_percentage:,
membership = Membership.find_by(id: membership_id)

if membership.present?
History::MembershipAvailableHoursHistory.create(membership_id: membership_id, available_hours: hours_per_month)
membership.update(member_role: member_role, hours_per_month: hours_per_month, effort_percentage: effort_percentage, start_date: start_date, end_date: end_date)
{ status_message: 'SUCCESS', membership: membership, message: 'Membership updated.' }
else
Expand Down
3 changes: 2 additions & 1 deletion app/graphql/types/team_member_consolidation_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module Types
class TeamMemberConsolidationType < BaseObject
field :consolidation_date, GraphQL::Types::ISO8601Date, null: false
field :value_per_hour_performed, Float, null: false
field :hour_value_expected, Float, null: false
field :hour_value_realized, Float, null: false
end
end
2 changes: 1 addition & 1 deletion app/graphql/types/team_members_hourly_rate_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
module Types
class TeamMembersHourlyRateType < BaseObject
field :period_date, GraphQL::Types::ISO8601Date, null: true
field :value_per_hour_performed, Float, null: true
field :hour_value_realized, Float, null: true
end
end
2 changes: 1 addition & 1 deletion app/graphql/types/teams/membership_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def team_members_hourly_rate_list
private

def build_hour_rate(date)
{ 'value_per_hour_performed' => compute_hours_per_month(object.monthly_payment, object.effort_in_period(Time.zone.today.ago(date.month).beginning_of_month, Time.zone.today.ago(date.month).end_of_month)), 'period_date' => Time.zone.today.ago(date.month).end_of_month }
{ 'hour_value_realized' => compute_hours_per_month(object.monthly_payment, object.effort_in_period(Time.zone.today.ago(date.month).beginning_of_month, Time.zone.today.ago(date.month).end_of_month)), 'period_date' => Time.zone.today.ago(date.month).end_of_month }
end

def compute_hours_per_month(monthly_payment, effort_in_period)
Expand Down
2 changes: 1 addition & 1 deletion app/graphql/types/teams/team_member_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def project_hours_data

# TODO: Fix Logic
def build_member_value_per_hour(month, membership)
{ 'consolidation_date' => month.month.ago.beginning_of_month, 'value_per_hour_performed' => compute_hours_per_month(membership.monthly_payment, membership.demand_efforts.to_dates(month.month.ago.beginning_of_month, month.month.ago.end_of_month).sum(&:effort_value).to_f) }
{ 'consolidation_date' => month.month.ago.beginning_of_month, 'hour_value_realized' => compute_hours_per_month(membership.monthly_payment, membership.demand_efforts.to_dates(month.month.ago.beginning_of_month, month.month.ago.end_of_month).sum(&:effort_value).to_f) }
end

def operations_dashboards
Expand Down
33 changes: 17 additions & 16 deletions app/models/demand_effort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@
#
# Table name: demand_efforts
#
# id :bigint not null, primary key
# automatic_update :boolean default(TRUE), not null
# effort_value :decimal(, ) default(0.0), not null
# finish_time_to_computation :datetime not null
# lock_version :integer
# main_effort_in_transition :boolean default(FALSE), not null
# management_percentage :decimal(, ) default(0.0), not null
# pairing_percentage :decimal(, ) default(0.0), not null
# stage_percentage :decimal(, ) default(0.0), not null
# start_time_to_computation :datetime not null
# total_blocked :decimal(, ) default(0.0), not null
# created_at :datetime not null
# updated_at :datetime not null
# demand_id :integer not null
# demand_transition_id :integer not null
# item_assignment_id :integer not null
# id :bigint not null, primary key
# automatic_update :boolean default(TRUE), not null
# effort_value :decimal(, ) default(0.0), not null
# finish_time_to_computation :datetime not null
# lock_version :integer
# main_effort_in_transition :boolean default(FALSE), not null
# management_percentage :decimal(, ) default(0.0), not null
# membership_effort_percentage :decimal(, )
# pairing_percentage :decimal(, ) default(0.0), not null
# stage_percentage :decimal(, ) default(0.0), not null
# start_time_to_computation :datetime not null
# total_blocked :decimal(, ) default(0.0), not null
# created_at :datetime not null
# updated_at :datetime not null
# demand_id :integer not null
# demand_transition_id :integer not null
# item_assignment_id :integer not null
#
# Indexes
#
Expand Down
10 changes: 4 additions & 6 deletions app/models/history/membership_available_hours_history.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# Table name: membership_available_hours_histories
#
# id :bigint not null, primary key
# available_hours :integer
# change_date :date
# available_hours :integer not null
# change_date :datetime not null
# created_at :datetime not null
# updated_at :datetime not null
# membership_id :integer not null
Expand All @@ -23,10 +23,8 @@ module History
class MembershipAvailableHoursHistory < ApplicationRecord
belongs_to :membership

before_save :update_avaliable_hours
validates :available_hours, :change_date, presence: true

def update_avaliable_hours
self.change_date = Time.zone.now
end
scope :until_date, ->(date) { where('change_date <= :limit_date', limit_date: date) }
end
end
53 changes: 36 additions & 17 deletions app/models/membership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
#
# Table name: memberships
#
# id :bigint not null, primary key
# end_date :date
# hours_per_month :integer
# member_role :integer default("developer"), not null
# start_date :date not null
# created_at :datetime not null
# updated_at :datetime not null
# team_id :integer not null
# team_member_id :integer not null
# id :bigint not null, primary key
# effort_percentage :decimal(, )
# end_date :date
# hours_per_month :integer
# member_role :integer default("developer"), not null
# start_date :date not null
# created_at :datetime not null
# updated_at :datetime not null
# team_id :integer not null
# team_member_id :integer not null
#
# Indexes
#
Expand All @@ -36,7 +37,6 @@ class Membership < ApplicationRecord
has_many :membership_available_hours_histories, class_name: 'History::MembershipAvailableHoursHistory', dependent: :destroy

validates :start_date, :member_role, presence: true
validate :active_team_member_unique

scope :active, -> { where('memberships.end_date' => nil) }
scope :inactive, -> { where.not('memberships.end_date' => nil) }
Expand All @@ -48,6 +48,9 @@ class Membership < ApplicationRecord
delegate :company, to: :team
delegate :projects, to: :team_member

before_create :active_team_member_unique
before_update :save_hours_history

def to_hash
{ member_name: team_member_name, jira_account_id: team_member.jira_account_id }
end
Expand Down Expand Up @@ -98,17 +101,21 @@ def stages_to_work_on
stages_to_work_on
end

def expected_hour_value
return 0 if hours_per_month.zero?
def expected_hour_value(date = Time.zone.now)
current_hours_per_month = current_hours_per_month(date)

return 0 if current_hours_per_month.zero?

monthly_payment / hours_per_month
monthly_payment(date) / current_hours_per_month
end

def monthly_payment
def monthly_payment(date = Time.zone.now)
return 0 if team_member.monthly_payment.blank?

membership_share = if hours_per_month.present? && team_member.hours_per_month.present? && hours_per_month < team_member.hours_per_month
hours_per_month.to_f / team_member.hours_per_month
current_hours_per_month = current_hours_per_month(date)

membership_share = if current_hours_per_month.present? && team_member.hours_per_month.present? && current_hours_per_month < team_member.hours_per_month
current_hours_per_month.to_f / team_member.hours_per_month
else
1
end
Expand Down Expand Up @@ -138,6 +145,10 @@ def cards_count(start_date, end_date)
demand_efforts.to_dates(start_date, end_date).map(&:demand).uniq.count
end

def current_hours_per_month(date = Time.zone.now)
membership_available_hours_histories.until_date(date).order(:change_date).last&.available_hours || hours_per_month
end

private

def pairing_members_in_demand(demand)
Expand All @@ -157,9 +168,17 @@ def pairing_members_in_demand(demand)
end

def active_team_member_unique
return if end_date.present?

existent_memberships = Membership.where(team: team, team_member: team_member, end_date: nil)
return if existent_memberships == [self] || end_date.present?
return if existent_memberships == [self]

errors.add(:team_member, I18n.t('activerecord.errors.models.membership.team_member.already_existent_active')) if existent_memberships.present?
end

def save_hours_history
return if hours_per_month_was == hours_per_month

History::MembershipAvailableHoursHistory.create(membership_id: id, available_hours: hours_per_month, change_date: Time.zone.now)
end
end
2 changes: 1 addition & 1 deletion app/models/service_delivery_review_action_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Table name: service_delivery_review_action_items
#
# id :bigint not null, primary key
# action_type :integer default("cadences_change"), not null
# action_type :integer default("technical_change"), not null
# deadline :date not null
# description :string not null
# done_date :date
Expand Down
2 changes: 1 addition & 1 deletion app/services/slack/slack_notification_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def notify_team_efficiency(slack_notifier, team, start_date, end_date, title, no
effort_text = title

members_efforts.each_with_index do |member, index|
effort_text += "• #{medal_of_honor(index)} #{member[:membership].team_member.name} | Demandas: #{member[:cards_count]} | Horas: #{number_with_precision(member[:effort_in_month])} | Capacidade: #{member[:membership][:hours_per_month]} #{notification_period == 'month' ? "| Vl Hr: #{number_with_precision(member[:value_per_hour_performed])}" : ''}\n"
effort_text += "• #{medal_of_honor(index)} #{member[:membership].team_member.name} | Demandas: #{member[:cards_count]} | Horas: #{number_with_precision(member[:effort_in_month])} | Capacidade: #{member[:membership][:hours_per_month]} #{notification_period == 'month' ? "| Vl Hr: #{number_with_precision(member[:hour_value_realized])}" : ''}\n"
end

effort_info_block = { type: 'section', text: { type: 'mrkdwn', text: effort_text } }
Expand Down
17 changes: 10 additions & 7 deletions app/services/team_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,24 @@ def compute_memberships_realized_hours(team, start_date, end_date)
memberships = team.memberships.active.billable_member

efficiency_data = memberships.map do |membership|
{ membership: membership, effort_in_month: membership.effort_in_period(start_date, end_date),
avg_hours_per_demand: membership.avg_hours_per_demand(start_date, end_date),
{ membership: membership, effort_in_month: membership.effort_in_period(start_date, end_date).to_f,
avg_hours_per_demand: membership.avg_hours_per_demand(start_date, end_date).to_f,
cards_count: membership.cards_count(start_date, end_date),
realized_money_in_month: membership.realized_money_in_period(start_date, end_date), member_capacity_value: membership.hours_per_month || 0,
value_per_hour_performed: calculate_hours_per_month(membership.monthly_payment, membership.effort_in_period(start_date, end_date)) }
realized_money_in_month: membership.realized_money_in_period(start_date, end_date).to_f, member_capacity_value: membership.current_hours_per_month(end_date) || 0,
hour_value_realized: compute_hour_value(membership.monthly_payment(end_date), membership.effort_in_period(start_date, end_date)).to_f,
hour_value_expected: membership.expected_hour_value(end_date).to_f }
end
efficiency_data = efficiency_data.sort_by { |member_ef| member_ef[:effort_in_month] }.reverse
efficiency_data = efficiency_data.sort_by { |member_efficiency| member_efficiency[:effort_in_month] }.reverse

build_members_efficiency(efficiency_data)
end

private

def calculate_hours_per_month(sallary, month_hours)
month_hours.zero? ? 0.0 : sallary / month_hours
def compute_hour_value(monthly_payment, hours_per_month)
return 0 if hours_per_month.zero?

monthly_payment / hours_per_month
end

def build_members_efficiency(efficiency_data)
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20231205130509_change_membership_hours_history_null.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class ChangeMembershipHoursHistoryNull < ActiveRecord::Migration[7.1]
def change
change_table :membership_available_hours_histories do |t|
t.change_null :available_hours, false
t.change_null :change_date, false
end
end
end
Loading

0 comments on commit aca5395

Please sign in to comment.