[英]Rails Scoping a payroll period
I've built a Rails app that logs employee time (clockin/clockout) and calculates total hours, allows exports to CSV/PDF, search timecards based on dates, etc. 我已经构建了一个Rails应用程序,记录员工时间(clockin / clockout)并计算总小时数,允许导出为CSV / PDF,根据日期搜索时间卡等。
What I'm really wanting to do is to implement payroll periods via a scope of some sort of a method. 我真正想要做的是通过某种方法的范围来实现工资核算期。
The payroll period begins on a Sunday and ends on a Saturday 14 days later. 工资核算期从星期日开始,到14天后的星期六结束。 What would be the best way to write a scope like this?
写这样一个范围的最佳方法是什么? Also is it possible to split the weeks into two for the payroll period?
还有可能将工资周期的周数分成两周吗?
I wrote these scopes but they are flawed: 我写了这些范围,但它们有缺陷:
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)
}
These works if you are currently in a payroll period, but once you pass the end of the week, the scopes no longer work because I'm basing my timing off of Time.zone.now
如果您目前处于工资核算期,这些工作正常,但是一旦您通过本周结束,范围将不再有效,因为我的时间基于
Time.zone.now
Is there any way to actually do this? 有没有办法真正做到这一点? Even if I have to set some sort of static scope or value which says April 10 - 23 is payroll period 1, etc etc. I'm really not sure how to approach this problem and what might work.
即使我必须设置某种静态范围或值,即4月10日 - 23日是工资单期间1等等,我真的不确定如何处理这个问题以及可能有什么用处。 So far what I've written works in the current pay period but as time advances the scope drifts.
到目前为止,我所撰写的内容在当前的支付期内有效,但随着时间的推移,范围也会发生变化。
Any help would be greatly appreciated. 任何帮助将不胜感激。
I think what you want is to create a scope, which can receive a start_day
as a parameter: 我想你想要的是创建一个范围,它可以接收
start_day
作为参数:
scope :payroll_week_starting, -> (start_day) {
where(clock_in: start_day..(start_day + 1.week))
}
Then, in the future you will be able to call your scope with the first day of your pay period: 然后,将来您可以在支付期的第一天调用您的范围:
ModelName.payroll_week_starting(Date.parse('31/12/2015'))
As per your comment, it seems that you're looking for a bit more information from an architectural perspective. 根据您的评论,您似乎从架构角度寻找更多信息。 It's pretty tough to help you without understanding your database architecture, so I'm just going to go from a high level.
在不了解您的数据库架构的情况下帮助您是非常困难的,所以我只是从高层次开始。
Let's assume you have an Employee
model and a Shift
model with the clock_in
and clock_out
fields. 假设您有一个
Employee
模型和一个带有clock_in
和clock_out
字段的Shift
模型。 You may also want a model called PayPeriod
with the fields start_date
and end_date
. 您可能还需要一个名为
PayPeriod
的模型,其字段为start_date
和end_date
。
Each Employee
has_many :shifts
and each PayPeriod
has_many :shifts
每个
Employee
has_many :shifts
,每个PayPeriod
has_many :shifts
You might add a couple of class methods on PayPeriod
, so you can find and/or create the PayPeriod
for any given datetime. 您可以在
PayPeriod
上添加几个类方法,这样您就可以在任何给定的日期时间内找到和/或创建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
The create_for
logic is pretty complicated, but an example will help you understand: create_for
逻辑非常复杂,但一个例子可以帮助您理解:
Say I clocked in today May 17th, 2016
, the yday
for today is 138
, if you use integer division (default for ruby) to divide by 14 (the length of your pay periods), you'll get 9. By multiplying that by 14 again, you'll get 126, which is the most recent yday
divisible by 14. If you add that number of days to the beginning of this year, you'll get the begining of the PayPeriod
. 假设我今天在
May 17th, 2016
,今天的yday
是138
,如果你使用整数除法 (默认为ruby)除以14(你的支付期的长度),你将得到9.乘以14再次,你将获得126,这是最近的yday
可被14整除。如果你将这个天数添加到今年年初,你将获得PayPeriod
的开始。 The end of the PayPeriod
is 14 days after the start_date
, but not rolling over to the next year. PayPeriod
是start_date
之后的14天,但没有滚动到下一年。
What I would then do, is add a before_save
callback to Shift
to find or create the corresponding PayPeriod
然后我会做的是将一个
before_save
回调添加到Shift
以查找或创建相应的PayPeriod
before_save :associate_pay_period
def associate_pay_period
self.pay_period_id = PayPeriod.find_or_create_for(clock_in)
end
Then every PayPeriod
will have a bunch of Shift
s, and every Shift
will belong to a PayPeriod
. 然后每个
PayPeriod
都会有一堆Shift
,每个Shift
都属于PayPeriod
。
If, for example, you wanted to get all of the shifts for a specific employee during a specific PayPeriod
(to perhaps sum the hours worked for that PayPeriod
) add a scope to Shift
: 例如,如果您希望在特定
PayPeriod
期间获得特定员工的所有班次(可能总结该PayPeriod
工作小时PayPeriod
),请向Shift
添加一个范围:
scope :for, -> (user) { where(user: user) }
And call pay_period.shifts.for(user)
并致电
pay_period.shifts.for(user)
One other (much simpler) thought I had (if you don't want to create an actual PayPeriod
model), would be to just add a method to the model that has clock_in
(I'm going to refer to it as Shift
): 另一个(更简单)我认为(如果你不想创建一个实际的
PayPeriod
模型),只是添加一个方法到具有clock_in
的模型(我将把它称为Shift
):
def pay_period
clock_in.to_date.mjd / 14
end
Which will basically just boil down any clock_in time to an integer that represents a 14 day period. 这将基本上将任何clock_in时间归结为表示14天周期的整数。 Then you can call
然后你可以打电话
Shift.all.group_by { |shift| shift.pay_period }
If you need each pay_period
to be contained within a single calendar year, you can do: 如果您需要在一个日历年内包含每个
pay_period
,您可以执行以下操作:
def pay_period
[clock_in.year, clock_in.to_date.yday / 14]
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.