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_in
和 clock_out
字段的 Shift
模型。您可能还需要一个名为 PayPeriod
的模型,其中包含字段 start_date
和 end_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
,今天的yday
是138
,如果你用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
我构建了一个 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_in
和 clock_out
字段的 Shift
模型。您可能还需要一个名为 PayPeriod
的模型,其中包含字段 start_date
和 end_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
,今天的yday
是138
,如果你用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