You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
16 KiB

2 years ago
  1. using Entity.Sugar;
  2. using SqlSugar;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace EasyBL
  9. {
  10. public class AttendanceMethods
  11. {
  12. /// <summary>
  13. /// 彈性工時計算
  14. /// </summary>
  15. /// <param name="attendanceRule"></param>
  16. /// <param name="SignIn"></param>
  17. /// <returns>依序:工作起、工作迄、是否遲到</returns>
  18. public Tuple<string, string, bool> GetFlexTime(AttendanceRule attendanceRule, string SignIn)
  19. {
  20. var UseStartTime = @""; //實際使用上班時間。
  21. var UseEndTime = @""; //實際使用上班時間。
  22. var AnyBuffer = true;
  23. var WorkDelay = false;
  24. if (string.IsNullOrWhiteSpace(attendanceRule.WorkStartBuffer))
  25. {
  26. AnyBuffer = false;
  27. }
  28. //檢查有無buffer
  29. if (AnyBuffer)
  30. {
  31. var SignInTime = Convert.ToDateTime(SignIn);
  32. var StandardWorkStart = Convert.ToDateTime(attendanceRule.WorkStart);
  33. var StandardWorkEnd = Convert.ToDateTime(attendanceRule.WorkEnd);
  34. var BufferedWorkStart = Convert.ToDateTime(attendanceRule.WorkStartBuffer + ":59");
  35. //-1: t1 早於 t2。 0: t1 與 t2 相同。 1: t1 晚於 t2。
  36. var CheckStandardThreadhold = DateTime.Compare(SignInTime, StandardWorkStart) > 0;
  37. if (CheckStandardThreadhold)
  38. {
  39. var CheckBufferThreadhold = DateTime.Compare(SignInTime, BufferedWorkStart);
  40. switch (CheckBufferThreadhold)
  41. {
  42. case 1:
  43. WorkDelay = true;
  44. var TotalBufferHour = ExecDateDiff(StandardWorkStart, BufferedWorkStart);
  45. StandardWorkEnd = StandardWorkEnd.AddHours(TotalBufferHour);
  46. if (attendanceRule.DelayBuffer > 0)
  47. {
  48. BufferedWorkStart = BufferedWorkStart.AddMinutes(-1 * attendanceRule.DelayBuffer);
  49. StandardWorkEnd = StandardWorkEnd.AddMinutes(-1 * attendanceRule.DelayBuffer);
  50. }
  51. UseEndTime = StandardWorkEnd.ToString("HH:mm");
  52. UseStartTime = BufferedWorkStart.ToString("HH:mm");
  53. break;
  54. case 0:
  55. case -1:
  56. var WorkStartBuffer = ExecDateDiff(StandardWorkStart, SignInTime);
  57. WorkDelay = false;
  58. UseEndTime = StandardWorkEnd.AddHours(WorkStartBuffer).ToString("HH:mm");
  59. UseStartTime = StandardWorkStart.AddHours(WorkStartBuffer).ToString("HH:mm");
  60. break;
  61. default:
  62. break;
  63. }
  64. }
  65. else
  66. {
  67. WorkDelay = false;
  68. UseEndTime = attendanceRule.WorkEnd;
  69. UseStartTime = attendanceRule.WorkStart;
  70. }
  71. }
  72. //無緩衝
  73. else
  74. {
  75. WorkDelay = DateTime.Compare(Convert.ToDateTime(SignIn), Convert.ToDateTime(attendanceRule.WorkStart)) > 0;
  76. UseStartTime = attendanceRule.WorkStart;
  77. UseEndTime = attendanceRule.WorkEnd;
  78. }
  79. return new Tuple<string, string,bool>(UseStartTime, UseEndTime, WorkDelay);
  80. }
  81. /// <summary>
  82. /// 計算工作時間。
  83. /// 必須SignOut、SignIn有時間,SignOut晚於SignIn、TimeA(工作時間起)和TimeP(工作時間迄)
  84. /// </summary>
  85. /// <param name="sSignTime"></param>
  86. /// <param name="info"></param>
  87. /// <returns></returns>
  88. public double GetWorkingHour(string sSignTime, OTB_EIP_Attendance info)
  89. {
  90. var WorkingHour = 0.0;
  91. var CheckAvaliableTime = !string.IsNullOrWhiteSpace(info.SignOut) && !string.IsNullOrWhiteSpace(info.SignIn);
  92. var HasDiffTime = ExecDateDiff(Convert.ToDateTime(info.SignIn), Convert.ToDateTime(info.SignOut)) > 0;
  93. var HasWorkTime = !string.IsNullOrWhiteSpace(info.TimeA) && !string.IsNullOrWhiteSpace(info.TimeP);
  94. if (CheckAvaliableTime && HasDiffTime && HasWorkTime)
  95. {
  96. var StandardWorkStart = Convert.ToDateTime(info.TimeA);
  97. var StandardWorkEnd = Convert.ToDateTime(info.TimeP);
  98. var SignInTime = Convert.ToDateTime(info.SignIn);
  99. if (SignInTime.Second > 0) //取最小量
  100. SignInTime = SignInTime.AddSeconds(-1 * SignInTime.Second);
  101. var SignOutTime = Convert.ToDateTime(sSignTime);
  102. if (SignOutTime.Second > 0) //取最大量
  103. SignOutTime = SignOutTime.AddSeconds(-1 * SignOutTime.Second);
  104. var ActualStartTime = new DateTime();
  105. var ActualEndTime = new DateTime();
  106. //實際認列開始工作時間。過早一律認列上班時間
  107. var WorkStartThreadhold = DateTime.Compare(SignInTime, StandardWorkStart);
  108. //實際認列結束工作時間。
  109. var WorkEndThreadhold = DateTime.Compare(SignOutTime, StandardWorkEnd);
  110. switch (WorkStartThreadhold)
  111. {
  112. //刷進時間 晚於 變動上班時間
  113. case 1:
  114. ActualStartTime = SignInTime;
  115. break;
  116. //刷進時間 早於(等於) 變動上班時間
  117. case 0:
  118. case -1:
  119. ActualStartTime = StandardWorkStart;
  120. break;
  121. default:
  122. break;
  123. }
  124. switch (WorkEndThreadhold)
  125. {
  126. case 1:
  127. case 0:
  128. ActualEndTime = StandardWorkEnd;
  129. break;
  130. case -1:
  131. ActualEndTime = SignOutTime;
  132. break;
  133. default:
  134. break;
  135. }
  136. //實際工作起 早於 實際工作迄(前面條件會變更)
  137. var TotalWorkTimeThreadhold = DateTime.Compare(ActualStartTime, ActualEndTime);
  138. switch (TotalWorkTimeThreadhold)
  139. {
  140. case 1:
  141. case 0:
  142. WorkingHour = 0;
  143. break;
  144. case -1:
  145. WorkingHour = Math.Round(ExecDateDiff(ActualStartTime, ActualEndTime), 2, MidpointRounding.AwayFromZero);
  146. break;
  147. default:
  148. break;
  149. }
  150. }
  151. return WorkingHour;
  152. }
  153. /// <summary>
  154. /// 計算考勤
  155. /// </summary>
  156. /// <param name="AllLineDatas">卡中資料</param>
  157. /// <param name="AttendanceRules">出勤規則</param>
  158. /// <param name="CardDate">打卡日期</param>
  159. /// <returns></returns>
  160. public List<OTB_EIP_Attendance> CalculateAttendance(string[] AllLineDatas, List<AttendanceRule> AttendanceRules, DateTime CardDate)
  161. {
  162. var saCardAttendanceInfo = new List<OTB_EIP_Attendance>();
  163. //讀取打卡信息
  164. foreach (string str in AllLineDatas)
  165. {
  166. //打卡日期,卡號 , 刷卡時間,姓名 ,組織
  167. //20190701,0410164401,14:03:39,陳OO ,TE
  168. var strInfo = str.Split(',');
  169. var sOrgID = strInfo[4].ToString().Trim();
  170. if (string.IsNullOrEmpty(sOrgID))
  171. {
  172. continue;
  173. }
  174. var sCardDate = strInfo[0].ToString().Trim();
  175. var sCardId = strInfo[1].ToString().Trim();
  176. var sSignTime = strInfo[2].ToString().Trim();
  177. var sCardUserName = strInfo[3].ToString().Trim();
  178. var sTimeA = @"09:00"; //標準上班時間
  179. var sTimeP = @"17:30"; //標準下班時間
  180. var sTimeAE = ""; //允許最彈性下班時間
  181. var UseStartTime = @""; //實際使用上班時間。
  182. var UseEndTime = @""; //實際使用上班時間。
  183. var AnyBuffer = true;
  184. var ApplyRule = AttendanceRules.Where(ar => ar.OrgID == sOrgID).FirstOrDefault();
  185. if (ApplyRule != null)
  186. {
  187. sTimeA = ApplyRule.WorkStart;
  188. sTimeP = ApplyRule.WorkEnd;
  189. sTimeAE = ApplyRule.WorkStartBuffer;
  190. }
  191. if (string.IsNullOrWhiteSpace(sTimeAE))
  192. {
  193. AnyBuffer = false;
  194. sTimeAE = sTimeP;
  195. }
  196. if (saCardAttendanceInfo.Any(x => x.CardId == sCardId))
  197. {
  198. //如果存在則更新打卡信息
  199. foreach (OTB_EIP_Attendance info in saCardAttendanceInfo)
  200. {
  201. if (info.CardId == sCardId)
  202. {
  203. info.SignOut = sSignTime;
  204. var TotalWorkingHours = GetWorkingHour(sSignTime, info);
  205. var bStatusP = DateTime.Compare(Convert.ToDateTime(info.TimeP), Convert.ToDateTime(sSignTime)) > 0;
  206. info.Hours = TotalWorkingHours.ToString();
  207. info.StatusP = bStatusP;
  208. break;
  209. }
  210. }
  211. }
  212. //不存在則新增
  213. else
  214. {
  215. var FlexTime = GetFlexTime(ApplyRule, sSignTime);
  216. var oAttendanceInfo = new OTB_EIP_Attendance
  217. {
  218. OrgID = sOrgID,
  219. CardDate = CardDate,
  220. CardId = sCardId,
  221. TimeA = FlexTime.Item1,
  222. TimeP = FlexTime.Item2,
  223. CardUserName = sCardUserName,
  224. Hours = "0",
  225. SignIn = sSignTime,
  226. SignOut = ""
  227. };
  228. oAttendanceInfo.StatusA = FlexTime.Item3;
  229. oAttendanceInfo.StatusP = false;
  230. oAttendanceInfo.Memo = "";
  231. oAttendanceInfo.CreateDate = DateTime.Now;
  232. saCardAttendanceInfo.Add(oAttendanceInfo);
  233. }
  234. }
  235. return saCardAttendanceInfo;
  236. }
  237. /// <summary>
  238. /// 取得考勤規則
  239. /// </summary>
  240. /// <param name="db"></param>
  241. /// <param name="ConfigSettings">至少要4個</param>
  242. /// <returns></returns>
  243. public List<AttendanceRule> GetAttendanceRule(SqlSugarClient db, List<string> ConfigSettings)
  244. {
  245. var AttendanceRule = new List<AttendanceRule>();
  246. try
  247. {
  248. do
  249. {
  250. //WebConfig依序:上班開始時間、上班結束時間、最晚上班時間(工作緩衝時間)、遲到緩衝時間(分)
  251. //var ConfigSettings = new List<string>() { Common.ConfigGetValue("", "WorkTimePMKey"), Common.ConfigGetValue("", "WorkTimeAMKey")
  252. // ,Common.ConfigGetValue("", "LatestShiftTimeKey"), Common.ConfigGetValue("", "DelayBufferTimeKey") };
  253. var AllOrgIDs = db.Queryable<OTB_SYS_Organization>().Select(it => it.OrgID).ToList().Distinct();
  254. if(!ConfigSettings.Any())
  255. {
  256. ConfigSettings = new List<string> { "WorkTimePM", "WorkTimeAM", "LatestShiftTime", "DelayBufferTime" };
  257. }
  258. var spWorkTimePMKey = new SugarParameter("@WorkTimePM", ConfigSettings[0]);
  259. var spDWorkTimeAMKey = new SugarParameter("@WorkTimeAM", ConfigSettings[1]);
  260. var spLatestShiftTimeKey = new SugarParameter("@LatestShiftTime", ConfigSettings[2]);
  261. var spDelayBufferKey = new SugarParameter("@DelayBuffer", ConfigSettings[3]);
  262. var AllOfSystemSettings = db.Ado.SqlQuery<OTB_SYS_SystemSetting>(@"select *from OTB_SYS_SystemSetting where Effective ='Y'
  263. and SettingItem in (@WorkTimePM, @WorkTimeAM, @LatestShiftTime, @DelayBuffer )", spWorkTimePMKey, spDWorkTimeAMKey, spLatestShiftTimeKey, spDelayBufferKey).ToArray();
  264. //
  265. foreach (var OrgID in AllOrgIDs)
  266. {
  267. var attendanceRule = new AttendanceRule() { OrgID = OrgID };
  268. foreach (var Key in ConfigSettings)
  269. {
  270. var MatchedRule = AllOfSystemSettings.Where(s => s.OrgID == OrgID && s.SettingItem == Key).FirstOrDefault();
  271. if (MatchedRule != null && !string.IsNullOrWhiteSpace(MatchedRule.SettingValue))
  272. {
  273. var SplittedTime = MatchedRule.SettingValue.Split(new char[] { '~' }, StringSplitOptions.RemoveEmptyEntries);
  274. switch (Key)
  275. {
  276. case "WorkTimePM":
  277. attendanceRule.WorkStart = SplittedTime[0].PadLeft(5, '0');//開始時間
  278. attendanceRule.WorkTimePMDeadLine = MatchedRule.SettingValue;
  279. break;
  280. case "WorkTimeAM":
  281. attendanceRule.WorkEnd = SplittedTime[1].PadLeft(5, '0');//結束時間
  282. attendanceRule.WorkTimeAMDeadLine = MatchedRule.SettingValue;
  283. break;
  284. case "LatestShiftTime":
  285. attendanceRule.WorkStartBuffer = SplittedTime[0].PadLeft(5, '0');
  286. break;
  287. case "DelayBufferTime":
  288. var DelayBuffer = 0;
  289. var CheckIntValue = int.TryParse(MatchedRule.SettingValue, out DelayBuffer);
  290. attendanceRule.DelayBuffer = DelayBuffer;
  291. break;
  292. default:
  293. break;
  294. }
  295. }
  296. }
  297. AttendanceRule.Add(attendanceRule);
  298. }
  299. } while (false);
  300. }
  301. catch (Exception error)
  302. {
  303. throw error;
  304. }
  305. return AttendanceRule;
  306. }
  307. /// <summary>
  308. /// 程序执行时间测试
  309. /// </summary>
  310. /// <param name="dateBegin">开始时间</param>
  311. /// <param name="dateEnd">结束时间</param>
  312. /// <returns>返回(秒)单位,比如: 0.00239秒</returns>
  313. private double ExecDateDiff(DateTime dateBegin, DateTime dateEnd)
  314. {
  315. var ts1 = new TimeSpan(dateBegin.Ticks);
  316. var ts2 = new TimeSpan(dateEnd.Ticks);
  317. var ts3 = ts1.Subtract(ts2).Duration();
  318. //你想转的格式
  319. return ts3.TotalHours;
  320. }
  321. }
  322. }