3 votes

Rails Déterminer la portée d'une période de paie

J'ai développé une application Rails qui enregistre les heures de travail des employés (pointage) et calcule le nombre total d'heures, permet d'exporter au format CSV/PDF, de rechercher les fiches de temps en fonction des dates, etc.

Ce que je veux vraiment faire, c'est mettre en œuvre les périodes de paie par le biais d'un champ d'application d'une sorte de méthode.

La période de paie commence un dimanche et se termine un samedi 14 jours plus tard. Quelle est la meilleure façon d'écrire un champ d'application comme celui-ci ? Est-ce qu'il est possible de diviser les semaines en deux pour la période de paie ?

J'ai écrit ces lunettes, mais elles sont imparfaites :

scope :payroll_week_1, -> {
  start = Time.zone.now.beginning_of_week - 1.day
  ending = Time.zone.now.end_of_week - 1.day
  where(clock_in: start..ending)
}
scope :payroll_week_2, -> {
  start = Time.zone.now.end_of_week.beginning_of_day
  ending = Time.zone.now.end_of_week.end_of_day + 6.days
  where(clock_in: start..ending)
}

Cela fonctionne si vous êtes actuellement dans une période de paie, mais une fois que vous avez dépassé la fin de la semaine, les champs d'application ne fonctionnent plus parce que je me base sur le calendrier de Time.zone.now

Existe-t-il un moyen de le faire ? Même si je dois définir une sorte de périmètre ou de valeur statique qui indique que la période de paie 1 s'étend du 10 au 23 avril, etc etc. Je ne sais vraiment pas comment aborder ce problème et ce qui pourrait fonctionner. Jusqu'à présent, ce que j'ai écrit fonctionne pour la période de paie en cours, mais au fur et à mesure que le temps passe, le champ d'application dérive.

Toute aide serait grandement appréciée.

2voto

BananaNeil Points 1351

Je pense que ce que vous voulez, c'est créer un champ d'application, qui peut recevoir un start_day comme paramètre :

scope :payroll_week_starting, -> (start_day) {
  where(clock_in: start_day..(start_day + 1.week))
}

Ainsi, à l'avenir, vous pourrez appeler votre champ d'application le premier jour de votre période de paie :

ModelName.payroll_week_starting(Date.parse('31/12/2015'))

UPDATE :

Selon votre commentaire, il semble que vous recherchiez un peu plus d'informations d'un point de vue architectural. Il est difficile de vous aider sans comprendre l'architecture de votre base de données, je vais donc partir d'un niveau élevé.

Supposons que vous ayez un Employee et un modèle de Shift avec le modèle clock_in y clock_out champs. Vous pouvez également vouloir un modèle appelé PayPeriod avec les champs start_date y end_date .

Chaque Employee has_many :shifts et chaque PayPeriod has_many :shifts

Vous pouvez ajouter quelques méthodes de classe sur PayPeriod afin que vous puissiez trouver et/ou créer le PayPeriod pour une date donnée.

def self.for(time)
  find_by("start_date < ? AND end_date > ?", time, time)
end

def self.create_for(time)
  # yday is the number of days into the current year
  period_start_yday = 14 * (time.yday / 14)
  start_date = Date.new(time.year) + period_start_yday.days
  next_year = Date.new(time.year) + 1.year
  create(
    start_date: start_date,
    end_date: [start_date + 14.days, last_day_of_year].min,
  )
end

def self.find_or_create_for(time)
  for(time) || create_for(time)
end

Le create_for La logique est assez compliquée, mais un exemple vous aidera à comprendre :

Disons que j'ai pointé aujourd'hui May 17th, 2016 , le yday pour aujourd'hui est 138 Si vous utilisez division entière (par défaut pour ruby) pour diviser par 14 (la durée de vos périodes de paie), vous obtiendrez 9. En multipliant à nouveau ce chiffre par 14, vous obtiendrez 126, ce qui correspond à la période de paie la plus récente. yday divisible par 14. Si vous ajoutez ce nombre de jours au début de cette année, vous obtiendrez le début de la PayPeriod . La fin de la PayPeriod est de 14 jours après le start_date mais pas de report sur l'année suivante.

Ce que je ferais alors, c'est ajouter un before_save à l'adresse Shift pour trouver ou créer le PayPeriod

before_save :associate_pay_period

def associate_pay_period
  self.pay_period_id = PayPeriod.find_or_create_for(clock_in)
end

Ensuite, chaque PayPeriod aura un tas de Shift et chaque Shift appartiendra à un PayPeriod .

Si, par exemple, vous souhaitez obtenir toutes les affectations d'un employé spécifique au cours d'une période de temps donnée, vous pouvez utiliser l'outil de gestion des affectations. PayPeriod (pour éventuellement additionner les heures travaillées pour ce PayPeriod ) ajoute un champ d'application à Shift :

scope :for, -> (user) { where(user: user) }

Et appeler pay_period.shifts.for(user)

MISE À JOUR N° 2 :

Une autre idée (beaucoup plus simple) m'est venue à l'esprit (si vous ne voulez pas créer une véritable PayPeriod ), il suffirait d'ajouter une méthode au modèle qui a la fonction clock_in (Je vais l'appeler Shift ):

def pay_period
  clock_in.to_date.mjd / 14
end

Ce qui revient à réduire tout temps clock_in à un entier représentant une période de 14 jours. Vous pouvez alors appeler

Shift.all.group_by { |shift| shift.pay_period }

Si vous avez besoin de chaque pay_period à l'intérieur d'une même année civile, vous pouvez le faire :

def pay_period
  [clock_in.year, clock_in.to_date.yday / 14]
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X