如何精細化處理考勤
新單位對考勤的核算要求比較高,員工在平時工作日和週末的加班要分開覈算,普員和中乾的加班覈算方式不同,中乾的平時加班是不算加班費的,加班小時數要按季度算工作,員工可以在季末申請剩餘的小時數結轉到下個季度,加班要逐次累計,有加班數纔可以調休,調休數不可以大於可用的加班數。員工要能實時看見自己的可用加班數。如果沒有或不夠,不讓建流程。要方便考勤人員查看每個人的數據確定考勤,因爲必須得一個一個看。
怎麼樣,是不是有點暈啊。
開始吧。
設計
首先聲明,公司的OA系統是基於泛微Ecology8. 總體需要考慮幾個大的部分:
1、員工考勤數據的可視化和流程控制,強調實時性
這要包含具體的流程表單的控制,也要有統計報表。
2、方便考勤人員覈算
主要是通過流程節點控制和優化,當然,完全的自動化考勤是不可能的。
技術實現
第一部分:考勤數據可視化
後臺建立兩個表:
考勤數據的明細表:包含加班時數、調休時數、請假時數,公休日和工作日用一個flag區分,如HD是公休日,WD是工作日。
考勤數據餘額表:這個表是記錄季度餘額的表,其實也可以不設這個表,但是因爲考勤是按季度覈算的,有些人的考勤餘額要轉到下個季度,爲了數據清晰和系統性能,還是設計這個表(我不想一下子把1年或幾年的數據都查出來),這樣最後出來的最多的是餘額+本季的數據。
效果
加班流程表單:
生成的明細表:
季末形成的餘額表,這個通過泛微的計劃任務接口做:
調休時可根據明細表和餘額表計算得出可用的調休時數,並顯示出來。
剩餘可用時數的顯示,泛微有現成的doFieldMath()函數,也可以使用ajax,這裏不再贅述。
流程生成報表可以泛微的流程節點附加操作掛在自定義接口來實現。在這裏加班和調休使用相同的接口,根據標誌的不同而進行區分,加班的接口放在歸檔處(因爲需要人事專員確認實際的加班時數),調休的接口放在人事專員之前。
上接口代碼:
package develop.workflow;
import weaver.interfaces.workflow.action.Action;
import weaver.general.BaseBean;
import weaver.soa.workflow.request.RequestInfo;
import weaver.soa.workflow.request.Property;
import weaver.conn.RecordSet;
import weaver.general.Util;
import java.text.SimpleDateFormat;
import java.util.*;
public class overTime extends BaseBean implements Action {
public String execute(RequestInfo requestinfo) {
String requestid = requestinfo.getRequestid();
Property[] properties = requestinfo.getMainTableInfo().getProperty();// 獲取表單主字段信息
String ymd = Util.null2String(properties[26].getValue());
String userid = Util.null2String(properties[16].getValue());
String avaliable = Util.null2String(properties[30].getValue());
String realhour = Util.null2String(properties[4].getValue());
String series = Util.null2String(properties[34].getValue());
String thistime = Util.null2String(properties[22].getValue());
String qt = Util.null2String(properties[11].getValue());
String sal = Util.null2String(properties[24].getValue());
String delayhour = Util.null2String(properties[25].getValue());
String leave = Util.null2String(properties[10].getValue());
String type = Util.null2String(properties[9].getValue());
String ifconvert = Util.null2String(properties[3].getValue());
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//
String datetime = df.format(new Date());// new Date()
Calendar calendar=Calendar.getInstance();
int mth = calendar.get(Calendar.MONTH) + 1;
int yr = calendar.get(Calendar.YEAR);
String year = String.valueOf(yr);
String month = String.valueOf(mth);
String[] fa = {"1","2","3"};
String[] sa = {"4","5","6"};
String[] ta = {"7","8","9"};
String[] la = {"10","11","12"};
String quarter = "";
String note = "";
String sql = "";
if (Arrays.asList(fa).contains(month)) {
quarter = "1";
}else if(Arrays.asList(sa).contains(month)){
quarter = "2";
}else if(Arrays.asList(ta).contains(month)){
quarter = "3";
}else if(Arrays.asList(la).contains(month)){
quarter = "4";
}
RecordSet rs = new RecordSet();
//計算加班時數,工作日和週末分開,WD工作日。HD週末,法定節假日乘2
if (realhour != "") {
int type1 = Integer.parseInt(type);
if (type1 == 2) {
note = "HD";
double r = Double.valueOf(realhour);
r = r * 2;
String r1 = String.valueOf(r);
sql = "insert into formtable_main_902(requestid,userid,overtime,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+r1+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}else {
if (type1 == 0) {
note = "WD";
} else if (type1 == 1) {
note = "HD";
}
sql = "insert into formtable_main_902(requestid,userid,overtime,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+realhour+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
}
//計算無調休的請假,全部請假
note = "LV";
int cv = Integer.parseInt(ifconvert);
if (ifconvert != "") {
if (cv == 0) {
sql = "insert into formtable_main_902(requestid,userid,on_leave,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+leave+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
else if (cv == 1) {
// 部分請假
float pt1 = Float.parseFloat(leave);
float pt2 = Float.parseFloat(thistime);
float pt3 = pt1 - pt2;
if (pt3 > 0) {
//請假的部分
String pt = String.valueOf(pt3);
sql = "insert into formtable_main_902(requestid,userid,on_leave,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+pt+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
}
}
//計算調休
if (thistime != "" && cv != 0) {
note = "OF";
sql = "insert into formtable_main_902(requestid,userid,overoff,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+thistime+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
//季末延遲調休
if (qt != "") {
//發薪部分
if (sal != "") {
note = "Q_SAL";
sql = "insert into formtable_main_905(requestid,userid,sal,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+sal+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
sql = "insert into formtable_main_902(requestid,userid,sal,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+sal+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
//平衡本季餘額
note = "Q_END";
sql = "insert into formtable_main_902(requestid,userid,overoff,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+avaliable+"','"+series+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
//將結轉部分轉到下月期初
float s1 = 0;
float s2 = 0;
float off = 0;
float q_bal_hd = 0;
float q_bal_wd = 0;
float q_lv = 0;
String quarter1 ="";
String year1 ="";
String month1 = "";
//本季總的平時加班數
note = "WD";
sql = "select sum(overtime) as sum from formtable_main_902 where note = '"+note+"' and quarter = '"+quarter+"' and year = '"+year+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
s1 = rs.getFloat("sum");
}
//週末加班數
note = "HD";
sql = "select sum(overtime) as sum from formtable_main_902 where note = '"+note+"' and year = '"+year+"' and quarter = '"+quarter+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
s2 = rs.getFloat("sum");
}
//調休數
note = "OF";
sql ="select sum(overoff) as sum from formtable_main_902 where note = '"+note+"' and quarter = '"+quarter+"' and year = '"+year+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
off = rs.getFloat("sum");
if(off < 0)
off = 0;
}
//上季度加班餘額
note = "Q_BAL_WD";
sql = "select sum(overtime) as overtime from formtable_main_905 where note = '"+note+"' and quarter = '"+quarter+"' and year = '"+year+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
q_bal_wd = rs.getFloat("overtime");
if (q_bal_wd < 0)
q_bal_wd = 0;
}
s1 = s1 + q_bal_wd;
note = "Q_BAL_HD";
sql = "select sum(overtime) as overtime from formtable_main_905 where note = '"+note+"' and quarter = '"+quarter+"' and year = '"+year+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
q_bal_hd = rs.getFloat("overtime");
if (q_bal_hd < 0)
q_bal_hd = 0;
}
s2 = s2 + q_bal_hd;
float s3 = s1 - off;
float hd_bal = 0;
float wd_bal = 0;
if (s3 < 0) {
hd_bal = s2 + s3;
wd_bal = 0;
} else {
wd_bal = s3;
hd_bal = s2;
}
float d1 = Float.parseFloat(sal);
float s4 = wd_bal - d1;
if (s4 < 0) {
hd_bal = hd_bal + s4;
wd_bal = 0;
} else {
// hd_bal = hd_bal;
wd_bal = s4;
}
//結轉到下季
if (quarter == "1" || quarter == "2" || quarter == "3") {
int q = Integer.parseInt(quarter);
q = q + 1;
quarter1 = String.valueOf(q);
year1 = year;
int mth1 = mth + 1;
month1 = String.valueOf(mth1);
} else if (quarter == "4") {
quarter1 = "1";
int y = Integer.parseInt(year);
y = y + 1;
year1 = String.valueOf(y);
month1 = "1";
}
if (hd_bal > 0) {
note = "Q_BAL_HD";
sql = "insert into formtable_main_905(requestid,userid,overtime,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+hd_bal+"','"+series+"','"+quarter1+"','"+datetime+"','"+year1+"', '"+month1+"','"+note+"')";
rs.executeSql(sql);
}
if (wd_bal > 0) {
note = "Q_BAL_WD";
sql = "insert into formtable_main_905(requestid,userid,overtime,series,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+wd_bal+"','"+series+"','"+quarter1+"','"+datetime+"','"+year1+"', '"+month1+"','"+note+"')";
rs.executeSql(sql);
}
sql = "select sum(on_leave) as sum from formtable_main_902 where year = '"+year+"' and quarter = '"+quarter+"' and userid = '"+userid+"'";
rs.executeSql(sql);
if (rs.next()) {
q_lv = rs.getFloat("sum");
if (q_lv > 0) {
note = "Q_LV";
sql = "insert into formtable_main_905(requestid,userid,on_leave,quarter,datetime,year,month,note) values('"+requestid+"','"+userid+"','"+q_lv+"','"+quarter+"','"+datetime+"','"+year+"', '"+month+"','"+note+"')";
rs.executeSql(sql);
}
}
}
return SUCCESS ;
}
}
到這裏第一部分基本完成了,但是還需要再加一點工作,就是流程的校驗,比如可調休時數爲0,不允許提交流程,不允許調休超過可調休時數的流程。就用泛微提供的函數:checkCustomize,該函數在流程提交時進行校驗,如果不通過就返回 false,流程就不能提交了。
至此第一部分就完成了。
第二部分 方便考勤人員
由於考勤人員需要對每個人覈對考勤,還要換算保留兩位小數,可以看到考勤人員的工作量還是比較大的。
思路是整合考勤系統,在具體的審批單的考勤節點設置兩個按鈕“查考勤”和“填考勤”,查考勤功能通過ajax獲取申請人那一天的考勤數據,形成一個頁面,然後返回給主單據頁面,祝單據頁面在適當位置建立一個iframe,src指向這個頁面就OK了。當然這個頁面除了顯示考勤數據外,還要有計算加班時數的功能,要能區分工作日和公休日,考勤數據沒有形成的時候也不至於報錯。
效果如下:
點擊“查考勤"按鈕:
點擊”填考勤“按鈕,會將實際考勤時數填寫。
怎麼樣,是不是極大的提高了效率。
轉載請註明出處。