Rails 确定工资期的范围

Rails Scoping a payroll period

我构建了一个 Rails 应用程序,用于记录员工时间 (clockin/clockout) 并计算总时数,允许导出到 CSV/PDF,根据日期搜索考勤卡等。

我真正想做的是通过某种方法的范围来实现工资周期。

薪资期从周日开始,到 14 天后的周六结束。编写这样的范围的最佳方法是什么?也可以将薪资期间的一周分成两周吗?

我写了这些作用域,但它们有缺陷:

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)
}

如果您当前处于发薪期,这些方法会起作用,但是一旦过了周末,范围就不再起作用,因为我的时间安排是 Time.zone.now

有没有办法真正做到这一点?即使我必须设置某种静态范围或值,表示 4 月 10 日至 23 日是工资单周期 1,等等。我真的不确定如何解决这个问题以及什么可能有效。到目前为止,我所写的内容在当前支付期间有效,但随着时间的推移,范围会发生变化。

如有任何帮助,我们将不胜感激。

我想你想要的是创建一个范围,它可以接收一个 start_day 作为参数:

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

然后,将来您可以在支付期的第一天调用您的范围:

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

更新:

根据您的评论,您似乎正在从体系结构的角度寻找更多信息。在不了解您的数据库架构的情况下很难为您提供帮助,所以我将从高层次出发。

假设您有一个 Employee 模型和一个带有 clock_inclock_out 字段的 Shift 模型。您可能还需要一个名为 PayPeriod 的模型,其中包含字段 start_dateend_date.

每个 Employee has_many :shifts 和每个 PayPeriod has_many :shifts

您可以在 PayPeriod 上添加几个 class 方法,这样您就可以找到 and/or 为任何给定的日期时间创建 PayPeriod

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

create_for逻辑比较复杂,举个例子可以帮助你理解:

假设我今天打卡May 17th, 2016,今天的yday138,如果你用integer division(ruby的默认值)来划分乘以 14(你的工资周期的长度),你会得到 9。再次乘以 14,你会得到 126,这是最近的 yday 可被 14 整除的值。如果你加上这个数字距离今年年初还有几天,您将获得 PayPeriod 的开始。 PayPeriod 的结束时间是 start_date 之后的 14 天,但不会延续到下一年。

然后我会做的是向 Shift 添加一个 before_save 回调以查找或创建相应的 PayPeriod

before_save :associate_pay_period

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

那么每个PayPeriod都会有一堆Shift,每个Shift都会属于一个PayPeriod.

例如,如果您想要获取特定员工在特定 PayPeriod 期间的所有班次(也许是对 PayPeriod 的工作时间求和),请将范围添加到 Shift:

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

并调用 pay_period.shifts.for(user)

更新#2:

另一个(更简单)我认为(如果你不想创建一个实际的 PayPeriod 模型),就是向具有 clock_in 的模型添加一个方法(我将其称为 Shift):

def pay_period
  clock_in.to_date.mjd / 14
end

这基本上只是将任何 clock_in 时间归结为代表 14 天周期的整数。然后你可以调用

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

如果您需要每个 pay_period 包含在一个日历年内,您可以这样做:

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