fastreport把多条记录放在一个单元格_服气!10分钟核对8000考勤记录,还能一眼看出谁迟到、早退!...

本文介绍了如何利用FastReport将多条考勤记录整合到一个单元格,快速核对8000条记录,轻松识别迟到和早退情况,提升工作效率。
摘要由CSDN通过智能技术生成

86889a15f09463d4387bcca2f0276a0c.png

作者:Coco

来源:薪人薪事企小薪(ID:xrxs_qixiaoxin)

不知不觉又到了月底,HR 们想必超级抓狂…… 因为八月份的考勤记录已经出来啦! 据以往经验,每到月底,就会有好多同学发来求助信息:
每个月的考勤数据导出后成千上万条,有些人一天打 N 次卡,我要怎么统计出所有员工的出勤情况啊? 我们公司的班次还特别复杂,早班、夜班、正常班、周末班,我真是被逼疯了!
有什么方法,能一次性处理好所有员工的考勤信息么?

01

做考勤的 HR 都知道,每个考勤机都能导出【打卡原始记录】。 在这个原始记录里,谁在什么时候打的卡,打了多少次卡,全都被记录下来了。 而【报表】因为要设置迟到、早退、旷工以及加班的规则,所以要设置有效打卡的时间段。 由于公司班次复杂,总会有人超出有效时间打卡,所以报表出来的时候,显示有人没打卡而旷工。 我们可以看到,考勤机的【打卡原始记录】长这样: cdb3790cacaf4a1e44111382c93e7b0a.png 我们先来分析一下,打卡机给出的报表,比原始打卡记录多出来的信息有:迟到、早退、加班、以及没打卡的记录。 这里的没打卡的记录,有的可能是因为员工请假了,有的可能是出差外出了,还有的可能是忘打卡了,最严重的是旷工了。 然后我们捋一下思路: 【迟到】——我们可以通过找到员工一天中第一次打卡时间,对比上班时间,判断员工是否迟到; 【早退】——同理,通过找到该员工当天最后一次打卡时间,对比下班时间,判断员工是否早退; 【加班】——通过计算最后一次打卡和第一次打卡之间的时间差,对比一天的应上班时长,判断员工的加班时间; 【没打卡】——则对照请假单、出差申请单等,自行判断员工到底是旷工了还是忘打卡了。 以上思路捋清之后,我们就可以在 Excel 表里开始操作了。 这里主要用到了以下几个函数: if 函数:根据判断条件的真伪,返回相应的值; countifs 函数:多条件汇总,汇总满足多个条件的值; index 函数:给出特定范围,根据相应的坐标,找到正确的值。

02

下面正式开始~ 我们可以看到,原始记录中日期时间合并显示在一列里,这对后面的计算不利。 所以首先要将日期、时间分两列。 e72b3860e5ea15b69415cfa77820f3df.png 然后把这两列分别重命名为「日期」和「时间」,这样我们可以从表中清楚地看见某个人在某一天共打了多少次卡。 有了这样一张表之后,我们就可以放飞自我啦!哦,不是,是 可以计算每天的上下班打卡时间啦!

 上班时间 

第一步,找到每个人每天的第一次打卡时间。 ❶ 先判断「时间」列的打卡时间,分别是某个员工当天第几次打卡。 在 E 列「时间」列的右边新建一列(即 F 列),命名为【第几次打卡】,并在 F2 单元格中输入如下公式: =COUNTIFS($B$2:B2,B2,$D$2:D2,D2) 输好以后按回车,则 F2 单元格中显示了数字 1,然后向下填充。 则 F 列每个单元格就被1、2、3、4……这样的数字填满了,表示了所对应的 E 列的时间,分别是员工在当天的第几次打卡: a960a4b1d72430f187604d679a0b18a6.gif 那这是怎么实现的呢?我们来剖析一下 Countifs 这个函数。 Countif 函数,是用来统计指定(单个)区域符合特定(单个)条件单元格计数。 Countifs 函数则是统计指定(多个)区域符合(多个)条件的单元格个数。 COUNTIFS($B$2:B2,B2,$D$2:D2,D2)这个公式里面有两个区域——[$B$2:B2]和[$D$2:D2],还有两个特定条件——[B2]和[D2]。 我们在 F2 单元格里输入这个公式的时候,用通俗的语言表达就是: 在 $B$2:B2 这个区域里,符合 B2(小薪)这个值,同时在 $D$2:D2 这个区域里,符合 D2(2017/6/1)这个值的有多少个。 我们可以直接看出,这个区域符合这两个条件的只有 1 行。 当我们把 F2 单元格向下填充的时候,公式里面的区域以及条件值也会跟着变化。 但是带有 $ 符号的属于绝对引用,不会跟随目标单元格的变化而变化。 所以我们可以看到 F3 单元格里的公式变成了: =COUNTIFS($B$2:B3,B3,$D$2:D3,D3) 4065481a7cdd9d43fabe602773a6428f.png说明在 $B$2:B3 这个区域里,符合 B3(小薪)这个值,同时在 $D$2:D3 这个区域里,符合 D3(2017/6/1)这个值的有 2 个。(我们可以看到,当我们的目标单元格从 F2 变成 F3 时,公式里面带 $ 符号的引用没有跟随变化,但是不带 $ 符号的引用都跟随变化了。)同理,F4 单元格的公式变成了: =COUNTIFS($B$2:B4,B4,$D$2:D4,D4)表示符合在 $B$2:B4 和 $D$2:D4 这两个区域里,同时符合【B4(小薪)——D4(2017/6/1)】有 3 个。 daeaf89464b859784ad387d2c401829e.png通过这种方式,我们最终得出了 E 列的每一个打卡时间分别是某一天的第几次打卡。 ❷ 新建一列,记录员工「上班打卡时间」。不管员工一天打多少次卡, 这里我们把每天第一次打卡定义为上班打卡。 有了上一步的基础,找出上班打卡时间就容易得多了。上一步中,我们通过 Countifs 函数知道了 E 列的每一个打卡时间分别是当天的第几次打卡。通过分析我们知道: 只要 F 列的数值为 1,则对应的 E 列中的时间就是上班打卡时间。为了后面统计方便,我们在 F 列右边新建一列 G 列,命名为「上班打卡时间」。在 G2 单元格输入如下公式: =if(F2=1,E2,””)意思是,如果 F2=1,则在 G2 中返回 E2 的值,否则返回空值: f3d7f6496a4e379f6cea873a2ddfcbff.png然后向下填充,通过这个操作,我们就把 E 列中每个人每天的第一次打卡时间填充到了 G 列,就像这样: 7efeb015938a21f5229ee83248ab234b.gif

 下班时间 

找到每个人每天最后一次打卡的时间。 我们转换一下思路,最后一次打卡时间,可以先算当天一共打了多次卡。比如某个人一天一共打了 4 次卡,那第 4 次打卡,就是最后一次打卡了。❶ 计算每人每天一共打多少次卡。这里我们依然用 Countifs 函数,在最右边新建一列 H 列,命名「共计打卡次数/天」。在 H2 输入如下公式: =countifs(B:B,B2,D:D,D2) 5ff61c60d0dc93fa7e7281db757a85a3.gif细心的同学可能发现了,H 列和 F 列用的是同一个函数,只是参数中引用的方式、范围不一样,导致了输出结果不一样。通俗地来讲,在 F 列中 Countifs 函数每次都计算的从 B2/D2 单元格到当前位置满足条件的单元格数量,从而算出的就是第几次打卡;而在 H 列中,Countifs 函数每次都是计算整个 B 列和 D 列中,满足相应条件的一共有多少个单元格,从而输出每天每人的打卡次数。❷ 输出显示每人每天最后一次打卡时间。新建一列,让这一列只显示某一天下班打卡时间。这里我们用 if 函数嵌套 index 函数来实现。在 I2 中输入如下公式: =IF(G2<>"",INDEX(E2:$E$466,H2),"")跟前面一样,if 函数有三个参数:第一个是判断条件,【G2<>""】判断「G2 单元格不是空值」这个命题是真命题还是假命题,其中“<>”是「不等于」的意思;如果是真命题,则返回第二个参数;如果是假命题则返回第三个参数,即空值。这里的第二个参数,也就是当命题为真时,返回 index 函数的输出值——在 E2:$E$466 这列中第「H2」个单元格里的值: 9f56fc581e350634cda53d02a4628ab5.gif其实这里我们用 if 函数,是对下班打卡时间显示的位置进行了固定——只能显示在有上班打卡时间的单元格所对应的 H 列中。这样就保证一个人在一天中,上下班打卡时间显示在同一行,更加直观。当然我们也可以不用 if 来固定,直接用 index 函数,但是这样会让 H 列中每个单元格都被填充上时间,而且会有重复值出现。

 最后修改 

调整表格,去「糟粕」留精华。到这里,我们前期的处理步骤已经完成了一大半!(撒花花~)但是为了美观好看,方便后面计算加班迟到,还要继续调整。现在的表格中,有很多列是为了输出上下班打卡时间而建立的,现在它们的使命已经完成,就该功成身退了~比如 F 列、H 列。但是不能直接暴力删除,因为 G 列 I 列对它们有引用,直接删除会引起报错。 正确的处理方法是:先将 G 列 I 列保存为数值格式——选中 G 列 I 列复制,然后粘贴为值。 7aa45a2228fe638fec7bd341660702aa.png操作完这一步,就可以删除原来的 F 列和 H 列,仅保留上下班打卡时间啦: 3a9ec162a84ceb2c663446be3cbcbe10.png删除了两列后,表格清爽了很多,但是还有一个问题,现在的 F 列和 G 列中那么些空格也挺遭人嫌弃的。这里用筛选命令,选出空白单元格删除就可以。选中 F 列,点击表格右上角「筛选」,这时「上班打卡」单元格右下角会显示一个小三角;我们点击小三角,选择「空白」: f7c86b26a54f51f8c6beb2a65ac28e59.png然后我们的表格变成了这样: 9ce8139a916d61a9a2d025668f521a81.png这时我们把显示的这些行全部删除: 79d06691f033bd79b9ca3d5a6361a023.png 18fe4f8e0f79511f948ddf36430c1e01.png不要哭,数据没有消失~刚才对上班打卡这一列进行了筛选,现在我们让其他非空单元格显示出来就好了,点击【上班打卡】旁边的小箭头,选择【全选】——【确定】: de84467e90de0ae75ce30a07f8425fee.png然后,我们的表格就变成了这个样子: 53c1d88adf3efc9b14b7160df1874c95.png这里可以把 E 列「时间」也删掉,得到这样的结构,表格会更清爽: a922f0ebfdbe10f10d7fd63eecc95695.png部门—姓名—日期—上班打卡时间—下班打卡时间,一行全部搞定啦!

 查找迟到人员 

8:30 上班,只要 E 列上班打卡时间大于 8:30:00,则对应的员工在当天视为迟到(这里假设员工没有请假没有外出)。选中 E 列,在菜单栏中依次点击【条件格式】-【突出显示单元格规则】-【大于】: 940782761e27782fccf691dc4be25c74.png然后在弹出的对话框里这样设置: c10b1c120428803e05663d7a20d532cd.png这时我们已经看到,E 列中打卡时间晚于 8:30 的被标红突出显示。我们可以很清楚的看到某个员工哪天迟到了。emm 小薪这个月迟到次数有点多,一会我得找她谈谈。 除了迟到,早退、是否加班,我们也可以通过这个表判断~

03

以上就是如何手动处理打卡记录的方法啦!如果操作熟练,也可以用上面提到的函数进行嵌套实现~ 668af3e36f2205c9e1de122461f32eb0.png

最后,给坚持看到这里的同学点个赞!2a3eed3dc2f0223ecfdfa962cada82a2.png

a064cc1bcd72fc5f3abb80d1ef221a0c.png 2a1b728e462f5136b16668831f3ec275.png

f4491cb6c632ddeee449c8a6f440ba4b.png

491a3f0e1621cefc2a583eea3cfe7fc0.png

f3f20c8cab04aa05628b0657443a1be6.png
:首先是要分组,因为如果不分组,想合并的列与其它列会自动打印。这样,只能使用FR自带的隐藏重复数据的功能,但很多类似报表都是要求合并行居中的,这点FR就不能自动实现了。 2:要全并的列放在分组脚中,其它放在分组数据BAND中,这样,打印完分组数据BAND后,动态改变分组脚中要合并列的TOP和HEIGHT就可以了。而且这样打印,因为要合并的行实际上只打印了一遍,因此应该效率更高。所以这就需要在打印分组数据BAND后有一个记录此分组已打印高度的功能。 3:代码解释 procedure MCOnAfterData(Sender: TfrxComponent); begin if =1 then MC.Tag:=Int(MC.Height) else MC.Tag:=MC.Tag+Int(MC.Height); end; 因为FR的在每次分组后重新计数,使用MC.Tag保存现在数据BAND上的所有不合并行的总高度。当然也可以使用一个变量,但放着这么多TAG不用,多浪费呀。况且使用TAG的语法也比使用变量简单,使用变量使程序看上去很难懂。 if >=1 then 这样的写法看起来多痛苦呀。 procedure MBBOnAfterData(Sender: TfrxComponent); begin MBB.Tag:=GetStandHeight(MBB.CalcHeight); //这一句是为了保持行高的一致性,如果不需要直接 MBB.Tag:=MBB.CalcHeight; //这样可能计算的行高不是标准行高的整数倍。 MBB.Visible:=False; end; procedure MD1OnBeforePrint(Sender: TfrxComponent); begin if =1 then MBB.Visible:=True; end; 上二句只是为了不多次统计CalcHeight,但不能直接设可视性为假,否则不会触发MBBOnAfterData事件。 计算此例中可能会很高的列的计算高度,这是在宽度已经确定的情况下计算的。 因为此例中的第二列单行高度可能大于其它列的总高度。如果没有此例中的特殊情况,可以省略。 4:如果此例中没有第二列单行高度可能大于其它列的总高度的话,MBB是不需要的。 5:如果不是要求空行为多行多列显示的话,GFOnBeforePrint也是不需要的,只需要在GF上放二个MEMOVIEW,宽高和左边距分别与上方的二个相同,调用我例子中注释掉的代码改变它们的TOP和HEIGHT即可。 6:TAG是所有Tcomponent的属性呀,这是我最常使用的一个属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值