简体   繁体   English

Rails确定工资核算期限

[英]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'))

UPDATE: 更新:

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_inclock_out字段的Shift模型。 You may also want a model called PayPeriod with the fields start_date and end_date . 您可能还需要一个名为PayPeriod的模型,其字段为start_dateend_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 ,今天的yday138 ,如果你使用整数除法 (默认为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. PayPeriodstart_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)

UPDATE #2: 更新#2:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM