PayrollComputation.cs 97 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777
  1. using Google.Protobuf.WellKnownTypes;
  2. using NPOI.HPSF;
  3. using NPOI.OpenXmlFormats.Spreadsheet;
  4. using NPOI.SS.Formula.Functions;
  5. using OASystem.API.OAMethodLib.QiYeWeChatAPI;
  6. using OASystem.Domain;
  7. using OASystem.Domain.Entities.PersonnelModule;
  8. using OASystem.Domain.ViewModels.PersonnelModule;
  9. using OASystem.Domain.ViewModels.QiYeWeChat;
  10. using OASystem.Infrastructure.Repositories.Groups;
  11. using System;
  12. using System.Diagnostics.Eventing.Reader;
  13. using System.Linq.Expressions;
  14. using TencentCloud.Ocr.V20181119.Models;
  15. namespace OASystem.API.OAMethodLib
  16. {
  17. /// <summary>
  18. /// 工资计算
  19. /// </summary>
  20. public static class PayrollComputation
  21. {
  22. private static Result _result = new Result();
  23. private static readonly IQiYeWeChatApiService _qiYeWeChatApiService = AutofacIocManager.Instance.GetService<IQiYeWeChatApiService>();
  24. private static readonly UsersRepository _usersRep = AutofacIocManager.Instance.GetService<UsersRepository>();
  25. private static readonly IMapper _mapper = AutofacIocManager.Instance.GetService<IMapper>();
  26. private static readonly decimal _chengDuMinimumWage = 2100.00M;
  27. /// <summary>
  28. /// 计算工资
  29. /// </summary>
  30. /// <param name="pm_WageSheetDattaSources"></param>
  31. /// <param name="userNames"></param>
  32. /// <param name="thisYearMonth"></param>
  33. /// <param name="startDt"></param>
  34. /// <param name="endDt"></param>
  35. /// <returns></returns>
  36. public static async Task<Result> SalaryCalculatorAsync1(
  37. List<Pm_WageSheet> pm_WageSheetDattaSources, List<UserNameView> userNames, int userId, string thisYearMonth, DateTime startDt, DateTime endDt)
  38. {
  39. if (pm_WageSheetDattaSources.Count <= 0)
  40. {
  41. _result.Msg = "计算工资传入数据为空!";
  42. return _result;
  43. }
  44. if (userNames.Count <= 0)
  45. {
  46. var nameData = await _usersRep.GetUserNameList(1);
  47. userNames = nameData.Data;
  48. }
  49. //计算时间段内工作日
  50. int work_days = await GetWorkDays(thisYearMonth);
  51. //获取所有打卡数据
  52. CheckInView checkIn = await _qiYeWeChatApiService.GetCheckin_MonthDataAsync(startDt, endDt); //时间段内所有 打卡数据
  53. if (checkIn.errcode != 0)
  54. {
  55. _result.Msg = "【企业微信】【打卡】【获取所有月打卡数据】【Msg】" + checkIn.errmsg;
  56. return _result;
  57. }
  58. List<Data> checkInDatas = checkIn.datas;
  59. //获取 请假类型 Sp_Detail.template_id
  60. string leave_template_id = "C4NzTJCh1onCUK915rRkvy7Fh5Vqz4YbiEV9jrBY1";
  61. List<VacationLeaveTypeView> vacationLeaveTypes = await GetVacationLeaveTypes(leave_template_id);
  62. if (vacationLeaveTypes.Count <= 0)
  63. {
  64. _result.Msg = startDt + " - " + endDt + "请假 类型数据 获取失败!";
  65. return _result;
  66. }
  67. _result.Msg = string.Empty;
  68. try
  69. {
  70. foreach (var pm_wsInfo in pm_WageSheetDattaSources)
  71. {
  72. string itemName = userNames.Where(it => it.Id == pm_wsInfo.UserId).FirstOrDefault().CnName;
  73. //补贴 金额
  74. decimal meal_subsidy = 0.00M; // 午餐(午餐10元/天) 补贴 * 计算方式:单日上午请假时长(小时)大于或者等于三小时 没有餐补
  75. //事假 病假 总金额
  76. decimal personalLeaveTotal = 0.00M, // 事假 日薪 *计算方式:日平均工资 = 月工资/当月应出勤天数。
  77. sickLeaveTotal = 0.00M; // 病假 日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内
  78. //扣款金额
  79. decimal beLate_deduction = 0.00M, // 迟到 扣款金额 *计算方式:
  80. // 一个自然月内,不足 10 分钟的迟到/早退,不超过 2 次的部分,不做处罚;3 次及以上,按50元 / 次处罚;
  81. // 超过 10 分钟(含 10 分钟),不足 60 分钟的迟到/早退,按 50 元/次处罚;
  82. // 超过 60 分钟(含 60 分钟),不足 3 小时的迟到/早退,且无请假者,按旷工半日处理;超过3 小时的迟到 / 早退,且无请假者,按旷工一日处理。
  83. early_deduction = 0.00M, // 早退 扣款金额
  84. absenteeism_deduction = 0.00M, // 旷工 扣款金额 *计算方式:旷工扣发当日工资
  85. unprinted_deduction = 0.00M, // 未打卡 扣款金额 *计算方式:
  86. // 试用期员工每月有 2 次 补卡机会,超过 2 次不足 5 次的部分,按 10 元 / 次处罚,5 次及以上的漏卡,按 50 元 / 次处罚;
  87. // 正式员工每月 3 次以内的补卡,按 10 元 / 次处罚,3 次及以上的漏卡,按 50 元 / 次处罚。
  88. sickLeave_deduction = 0.00M, // 病假
  89. other_deduction = 0.00M; // 其他 扣款金额
  90. decimal meal_deduction = 0.00M; // 餐补 扣款金额
  91. decimal reissuecard_deduction = 0.00M; // 补卡 扣款金额
  92. //打卡数据
  93. Data? checkInData = checkInDatas.Where(it => it.base_info.name == itemName).FirstOrDefault();
  94. if (checkInData == null) { continue; }
  95. string acctid = checkInData.base_info.acctid; //用户Id
  96. //当月总计数据
  97. Summary_Info? summary_Info = checkInData.summary_info;
  98. if (summary_Info == null) { continue; }
  99. int dk_work_days = summary_Info.work_days - 1; //应出勤天数
  100. //int regular_days = summary_Info.regular_days - 1; //正常出勤天数
  101. meal_subsidy = dk_work_days * 10; //应发放餐补
  102. #region 计算日工资 正常日薪 事假日薪 病假日薪
  103. //月 - 应发工资
  104. decimal amountPayable = pm_wsInfo.Basic + pm_wsInfo.Floats + pm_wsInfo.PostAllowance + pm_wsInfo.InformationSecurityFee + pm_wsInfo.OtherSubsidies;
  105. // 日薪 = *计算方式:日平均工资 = 月工资/当月应出勤天数。
  106. decimal dailyWage = ConvertToDecimal(amountPayable / work_days);
  107. // 病假日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内
  108. decimal sickLeave_dailywage = ConvertToDecimal(_chengDuMinimumWage / work_days);
  109. //病假 一天扣款
  110. sickLeave_deduction = dailyWage - sickLeave_dailywage;
  111. List<Ex_Items> ex_Items = new List<Ex_Items>();//假勤 And 打卡备注集合
  112. #endregion
  113. if (!itemName.Equals("张海麟")) //不算考勤
  114. {
  115. int annualLeaveNum = 0, //年假
  116. personalLeaveNum = 0, //事假
  117. sickLeaveNum = 0, //病假
  118. lieuLeaveNum = 0, //调休假
  119. marriageLeaveNum = 0, //婚嫁
  120. maternityLeaveNum = 0, //产假
  121. paternityLeaveNum = 0, //陪产假
  122. funeralLeaveNum = 0, //丧假
  123. reissueCardNum = 0, //补卡 次数
  124. evectionNum = 0, //出差 次数
  125. outIngNum = 0, //外出 次数
  126. outWorkNum = 0, //外勤 次数
  127. otherLeaveNum = 0; //其他
  128. #region 假勤 处理 1-请假;2-补卡;3-出差;4-外出;100-外勤;
  129. List<Sp_Item>? sp_items = checkInData.sp_items.Where(it => it.count != 0).ToList();
  130. if (sp_items != null && sp_items.Count > 0)
  131. {
  132. annualLeaveNum = Fallibilitydispose(sp_items, 1, "年假");
  133. personalLeaveNum = Fallibilitydispose(sp_items, 1, "事假");
  134. sickLeaveNum = Fallibilitydispose(sp_items, 1, "病假");
  135. lieuLeaveNum = Fallibilitydispose(sp_items, 1, "调休假");
  136. marriageLeaveNum = Fallibilitydispose(sp_items, 1, "婚嫁");
  137. maternityLeaveNum = Fallibilitydispose(sp_items, 1, "产假");
  138. paternityLeaveNum = Fallibilitydispose(sp_items, 1, "陪产假");
  139. funeralLeaveNum = Fallibilitydispose(sp_items, 1, "丧假");
  140. otherLeaveNum = Fallibilitydispose(sp_items, 1, "其他");
  141. reissueCardNum = Fallibilitydispose(sp_items, 2, "补卡次数");
  142. evectionNum = Fallibilitydispose(sp_items, 3, "出差");
  143. outIngNum = Fallibilitydispose(sp_items, 4, "外出");
  144. outWorkNum = Fallibilitydispose(sp_items, 3, "外勤");
  145. }
  146. #region 请假类型金额/餐补 处理
  147. List<Sp_Detail> sp_leave_details = new List<Sp_Detail>();
  148. sp_leave_details = await _qiYeWeChatApiService.GetApprovalDetailsAsync(startDt, endDt, acctid, 2, 1); //时间段内所有 已同意的 请假 审批数据
  149. if (sp_leave_details.Count <= 0)
  150. {
  151. _result.Msg += startDt + " - " + endDt + " " + itemName + " 请假 审批数据获取未获取到!\r\n";
  152. //continue;
  153. }
  154. Ex_Items ex_Items_jq = new Ex_Items() { Type = "假勤" }; //假勤
  155. List<Ex_Item> ex_ItemInfos = new List<Ex_Item>();
  156. foreach (Sp_Detail sp_item in sp_leave_details)
  157. {
  158. Apply_data? apply_data = sp_item.apply_data;
  159. if (apply_data != null)
  160. {
  161. List<ContentsItem> contents = apply_data.contents;
  162. ContentsItem content_Vacation = contents.Where(it => it.control == "Vacation").FirstOrDefault(); //请假类型
  163. ContentsItem content_Textarea = contents.Where(it => it.control == "Textarea").FirstOrDefault(); //多行文本
  164. if (content_Vacation != null)
  165. {
  166. Vacation vacation = content_Vacation.value.vacation;
  167. Attendance attendance = vacation.attendance; //假勤组件
  168. Selector selector = vacation.selector; //请假类型
  169. List<OptionsItem> optionsItems = selector.options; //key 请假类型 id
  170. List<TitleItem> value = optionsItems[0].value; // value 文本描述值
  171. int leaveType = int.Parse(optionsItems[0].key); //key 请假子类型 id
  172. Date_range date_Range = attendance.date_range;
  173. //筛选 不在工作日内的假勤申请
  174. if (startDt >= date_Range.new_begin_dt && date_Range.new_end_dt <= endDt)
  175. {
  176. continue;
  177. }
  178. decimal thisTypeDeduction = 0.00M;//当前类型扣款
  179. string leave_starttime = date_Range.new_begin_dt.ToString("HH:mm");
  180. string leave_endtime = date_Range.new_end_dt.ToString("HH:mm");
  181. string typeName = string.Empty;
  182. string unit = string.Empty;
  183. int leaveTypeId = leaveType;
  184. var leaveTypeData = vacationLeaveTypes.Where(it => it.id == leaveTypeId).FirstOrDefault();
  185. if (leaveTypeData != null) { typeName = leaveTypeData.name; }
  186. string startTime = string.Empty;
  187. string endTime = string.Empty;
  188. //计算请假类型扣款金额
  189. decimal new_duration = 0.00M;
  190. if (date_Range.type == "halfday")
  191. {
  192. new_duration = Convert.ToDecimal(date_Range.new_duration) / 86400M;
  193. unit = "天";
  194. startTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd") + " 09:00";
  195. endTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd") + " 18:00";
  196. }
  197. else if (date_Range.type == "hour")
  198. {
  199. new_duration = Convert.ToDecimal(date_Range.new_duration) / 3600M;
  200. unit = "小时";
  201. startTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd HH:mm:ss");
  202. endTime = date_Range.new_end_dt.ToString("yyyy-MM-dd HH:mm:ss");
  203. }
  204. decimal leave_meals = 0.00M;
  205. //计算餐补 假勤类型扣款
  206. CalculateTypeFee(leaveType, date_Range.type, leave_starttime, leave_endtime, amountPayable, work_days, new_duration,
  207. out leave_meals, out thisTypeDeduction);
  208. #region 累计类型扣款
  209. //1年假;2事假;3病假;4调休假;5婚假;6产假;7陪产假;8其他;9丧假
  210. if (leaveType == 2) //事假
  211. {
  212. personalLeaveTotal += thisTypeDeduction;
  213. }
  214. else if (leaveType == 3) //病假
  215. {
  216. sickLeaveTotal = thisTypeDeduction;
  217. }
  218. #endregion
  219. meal_deduction += leave_meals;
  220. Ex_Item ex_Item = new Ex_Item()
  221. {
  222. SubTypeId = leaveType,
  223. SubType = typeName,
  224. StartTimeDt = Convert.ToDateTime(startTime),
  225. EndTimeDt = Convert.ToDateTime(endTime),
  226. Duration = new_duration,
  227. Unit = unit,
  228. Deduction = thisTypeDeduction,
  229. //Reason = apply_data.reason,
  230. Apply_time_dt = Convert.ToDateTime(sp_item.apply_time_dt.ToString("yyyy-MM-dd HH:mm:ss")),
  231. //Approval_name = sp_item.approval_name,
  232. };
  233. ex_ItemInfos.Add(ex_Item);
  234. }
  235. }
  236. }
  237. if (ex_ItemInfos.Count > 0)
  238. {
  239. ex_Items_jq.Ex_ItemInfo = ex_ItemInfos.OrderBy(it => it.SubTypeId).ThenBy(it => it.Apply_time_dt).ToList();
  240. ex_Items.Add(ex_Items_jq);
  241. }
  242. #endregion
  243. #endregion
  244. Ex_Items ex_Items_dk = new Ex_Items() { Type = "打卡" }; //打卡
  245. List<Ex_Item> ex_reissuecard_Items = new List<Ex_Item>();
  246. #region 打卡补卡 补卡次数 处理
  247. if (reissueCardNum > 0)
  248. {
  249. List<Sp_Detail> sp_buka_details = new List<Sp_Detail>();
  250. sp_leave_details = await _qiYeWeChatApiService.GetApprovalDetailsAsync(startDt, endDt, acctid, 2, 2); //时间段内所有 已同意的 请假 审批数据
  251. int bukaNum = 1;
  252. foreach (var item in sp_leave_details)
  253. {
  254. decimal bukaPrice = 0.00M;
  255. if (bukaNum <= 2)
  256. {
  257. bukaPrice = 10.00M;
  258. }
  259. else
  260. {
  261. bukaPrice = 50.00M;
  262. }
  263. var app_data = item.apply_data;
  264. var punch_correction1 = app_data.contents[0].value.punch_correction; //未打卡时间
  265. var punch_correction2 = app_data.contents[1].value;
  266. Ex_Item ex_reissueCard = new Ex_Item()
  267. {
  268. SubTypeId = 7,
  269. SubType = "打卡补卡",
  270. StartTimeDt = Convert.ToDateTime(punch_correction1.time_dt.ToString("yyyy-MM-dd HH:mm:ss")), //未打卡时间
  271. Deduction = bukaPrice,
  272. Reason = punch_correction2.text,
  273. Unit = string.Empty
  274. };
  275. unprinted_deduction += bukaPrice;
  276. ex_reissuecard_Items.Add(ex_reissueCard);
  277. bukaNum++;
  278. }
  279. }
  280. #endregion
  281. List<Sp_Detail> sp_reissuecard_details = new List<Sp_Detail>();
  282. #region 迟到早退处理
  283. //统计迟到次数
  284. int beLateNum = 0, // 1-迟到;
  285. leaveEarlyNum = 0, // 2-早退;
  286. dummyDeckNum = 0, // 3-缺卡;
  287. minerNum = 0, // 4-旷工;
  288. locationAnomalyNum = 0, // 5-地点异常;
  289. unitExNum = 0; // 6-设备异常;
  290. if (summary_Info.except_days > 0)
  291. {
  292. List<Exception_Info>? ex_infos = checkInData.exception_infos;
  293. if (ex_infos != null && ex_infos.Count >= 0)
  294. {
  295. beLateNum = ExceptionStatistics(ex_infos, 1);
  296. leaveEarlyNum = ExceptionStatistics(ex_infos, 2);
  297. dummyDeckNum = ExceptionStatistics(ex_infos, 3);
  298. minerNum = ExceptionStatistics(ex_infos, 4);
  299. locationAnomalyNum = ExceptionStatistics(ex_infos, 5);
  300. unitExNum = ExceptionStatistics(ex_infos, 6);
  301. }
  302. }
  303. //打卡记录
  304. CheckInDataView checkInDataView = new CheckInDataView();
  305. checkInDataView = await _qiYeWeChatApiService.GetCheckinDataAsync(new List<string>() { acctid }, 3, startDt, endDt);
  306. if (checkInDataView.errcode != 0)
  307. {
  308. _result.Msg += startDt + " - " + endDt + " " + itemName + " 打卡记录 " + checkInDataView.errmsg + " \r\n";
  309. }
  310. //筛选时间异常的打卡记录
  311. List<CheckInDataInfo> checkInDataInfos = checkInDataView.checkindata.Where(it => !string.IsNullOrEmpty(it.exception_type)).ToList();
  312. //处理 未打卡的记录是否已经通过假勤审批
  313. List<CheckInDataInfo> leave_checkInDataInfos = new List<CheckInDataInfo>();
  314. if (checkInDataInfos.Count > 0)
  315. {
  316. foreach (var leaveItem in ex_ItemInfos)
  317. {
  318. if (leaveItem.StartTimeDt.ToString("yyyy-MM-dd").Equals(leaveItem.EndTimeDt.ToString("yyyy-MM-dd"))) //单天
  319. {
  320. leave_checkInDataInfos.AddRange(checkInDataInfos.Where(it => it.checkin_time_dt.ToString("yyyy-MM-dd") == leaveItem.StartTimeDt.ToString("yyyy-MM-dd")).ToList());
  321. }
  322. else //多天
  323. {
  324. leave_checkInDataInfos.AddRange(checkInDataInfos.Where(it => it.checkin_time_dt >= leaveItem.StartTimeDt && it.checkin_time_dt <= leaveItem.EndTimeDt).ToList());
  325. }
  326. }
  327. }
  328. //处理 时间异常的记录是否已经通过补卡审批
  329. List<CheckInDataInfo> pullcard_checkInDataInfos = new List<CheckInDataInfo>();
  330. if (leave_checkInDataInfos.Count > 0)
  331. {
  332. //获取异常打卡数据 并且 未通过假勤审批
  333. pullcard_checkInDataInfos = checkInDataInfos.Except(leave_checkInDataInfos).ToList();
  334. if (pullcard_checkInDataInfos.Count > 0)
  335. {
  336. CheckInDayDataView checkInDayDataView = await _qiYeWeChatApiService.GetCheckInDayDataAsync(new List<string>() { acctid }, startDt, endDt);
  337. if (checkInDayDataView.errcode != 0)
  338. {
  339. _result.Msg += startDt + " - " + endDt + " " + itemName + " 异常信息数据获取未获取到!\r\n";
  340. }
  341. List<Root> roots_words = checkInDayDataView.datas.Where(it => it.base_info.day_type == 0).ToList(); //获取工作日日报信息
  342. List<Root> roots_exs = checkInDayDataView.datas.Where(it => it.exception_infos.Count > 0).ToList();
  343. int user_cd_zt_num = 0;
  344. foreach (var roots_ex in roots_exs)
  345. {
  346. foreach (var exception_info in roots_ex.exception_infos)
  347. {
  348. decimal timelength = ConvertToDecimal((Convert.ToDecimal(exception_info.duration) / 3600.00M) * 60.00M); //时长 分钟
  349. if (timelength == 9) timelength = 7.50M;
  350. int exception = exception_info.exception; //异常类型
  351. decimal day_miner_unit = ConvertToDecimal(dailyWage / 15); //以0.5小时为单位
  352. //1:一个自然月内,不足 10 分钟的迟到/早退,不超过 2 次的部分,不做处罚;3 次及以上,按50 元 / 次处罚;
  353. //2:超过 10 分钟(含 10 分钟),不足 60 分钟的迟到 / 早退,按 50 元 / 次处罚;
  354. //3:超过 60 分钟(含 60 分钟),不足 3 小时的迟到 / 早退,且无请假者,按旷工半日处理;超过 3 小时的迟到 / 早退,且无请假者,按旷工一日处理。
  355. long date = roots_ex.base_info.date; //当日工作日期
  356. long earliest_time = roots_ex.summary_info.earliest_time; //最早打卡时间
  357. long lastest_time = roots_ex.summary_info.lastest_time; //最晚打卡时间
  358. long this_date = date + earliest_time;
  359. DateTime thisDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks(this_date * 10000000);
  360. string thisDtStr = thisDt.ToString("yyyy-MM-dd");
  361. Ex_Item beLate_belate_ex = new Ex_Item()
  362. {
  363. SubTypeId = 4,
  364. SubType = "旷工",
  365. Duration = timelength,
  366. StartTimeDt = Convert.ToDateTime(roots_ex.base_info.dateDt.ToString("yyyy-MM-dd HH:mm:ss")),
  367. Unit = "分钟",
  368. };
  369. decimal day_deduction = 0.00M;
  370. //1 - 迟到;2 - 早退;3 - 缺卡;4 - 旷工;5 - 地点异常;6 - 设备异常
  371. if (exception == 1) //迟到
  372. {
  373. if (timelength < 10)
  374. {
  375. user_cd_zt_num++;
  376. beLate_belate_ex.SubTypeId = 1;
  377. beLate_belate_ex.SubType = "迟到";
  378. string thisStartDt = (TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + earliest_time) * 10000000))
  379. .ToString("yyyy-MM-dd HH:mm:ss");
  380. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisStartDt);
  381. if (reissueCardNum >= 3)
  382. {
  383. day_deduction = 50.00M;
  384. }
  385. else
  386. {
  387. day_deduction = 0.00M;
  388. }
  389. beLate_deduction += day_deduction; //迟到扣款 总额
  390. }
  391. else if (timelength >= 10 && timelength <= 60)
  392. {
  393. string thisStartDt = (TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + earliest_time) * 10000000))
  394. .ToString("yyyy-MM-dd HH:mm:ss");
  395. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisStartDt);
  396. day_deduction = 50.00M;
  397. beLate_deduction += day_deduction; //迟到扣款 总额
  398. beLate_belate_ex.SubTypeId = 1;
  399. beLate_belate_ex.SubType = "迟到";
  400. }
  401. else if (timelength > 60 && timelength <= 180)
  402. {
  403. day_deduction = day_miner_unit * 6; //3小时
  404. meal_deduction += 10.00M; //餐补扣款
  405. absenteeism_deduction += day_deduction; //矿工半日
  406. }
  407. else
  408. {
  409. day_deduction = dailyWage;
  410. absenteeism_deduction += day_deduction; //矿工一日
  411. meal_deduction += 10.00M;
  412. }
  413. beLate_belate_ex.Deduction = day_deduction;
  414. ex_reissuecard_Items.Add(beLate_belate_ex);
  415. }
  416. else if (exception == 2) //早退
  417. {
  418. if (timelength < 10)
  419. {
  420. user_cd_zt_num++;
  421. beLate_belate_ex.SubTypeId = 2;
  422. beLate_belate_ex.SubType = "早退";
  423. string thisEndDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + lastest_time) * 10000000)
  424. .ToString("yyyy-MM-dd HH:mm:ss");
  425. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisEndDt);
  426. if (reissueCardNum >= 3)
  427. {
  428. day_deduction = 50.00M;
  429. }
  430. else
  431. {
  432. day_deduction = 0.00M;
  433. }
  434. early_deduction += day_deduction; //早退扣款 总计
  435. }
  436. else if (timelength >= 10 && timelength <= 60)
  437. {
  438. day_deduction = 50.00M;
  439. early_deduction += day_deduction; //早退扣款 总计
  440. beLate_belate_ex.SubTypeId = 2;
  441. beLate_belate_ex.SubType = "早退";
  442. string thisEndDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + lastest_time) * 10000000)
  443. .ToString("yyyy-MM-dd HH:mm:ss");
  444. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisEndDt);
  445. }
  446. else if (timelength > 60 && timelength <= 180)
  447. {
  448. day_deduction = day_miner_unit * 6; //3小时
  449. absenteeism_deduction += day_deduction; //矿工半日
  450. }
  451. else
  452. {
  453. day_deduction = dailyWage;
  454. meal_deduction += 10.00M;
  455. absenteeism_deduction += day_deduction; //矿工一日
  456. }
  457. beLate_belate_ex.Deduction = day_deduction;
  458. ex_reissuecard_Items.Add(beLate_belate_ex);
  459. }
  460. else if (exception == 3) //缺卡
  461. {
  462. if (roots_ex.exception_infos.Count == 2)
  463. {
  464. day_deduction = dailyWage;
  465. meal_deduction += 10.00M;
  466. absenteeism_deduction += day_deduction;
  467. beLate_belate_ex.Reason = "上午-下午 缺卡/未打卡为旷工 一天";
  468. beLate_belate_ex.Deduction = day_deduction;
  469. ex_reissuecard_Items.Add(beLate_belate_ex);
  470. }
  471. else if (roots_ex.exception_infos.Count == 1)
  472. {
  473. if (earliest_time == lastest_time)
  474. {
  475. DateTime thisDt1 = Convert.ToDateTime(thisDtStr + " 12:00");
  476. //ex_ItemInfos
  477. Ex_Item zt_jq = new Ex_Item();
  478. zt_jq = ex_ItemInfos.Where(it => it.StartTimeDt.ToString("yyyy-MM-dd") == thisDtStr).FirstOrDefault();
  479. if (thisDt <= thisDt1) //旷工 下午
  480. {
  481. if (zt_jq != null) if (zt_jq.EndTimeDt == Convert.ToDateTime(thisDtStr + " 18:00")) continue; //排除已有假期的缺卡
  482. day_deduction = day_miner_unit * 9; //4.5小时
  483. meal_deduction += 10.00M;
  484. absenteeism_deduction += day_deduction; //矿工半日
  485. beLate_belate_ex.Reason = thisDtStr + " 下午(18:00)缺卡/未打卡视为下午旷工(4.5小时)";
  486. beLate_belate_ex.Deduction = day_deduction;
  487. ex_reissuecard_Items.Add(beLate_belate_ex);
  488. }
  489. else if (thisDt >= thisDt1) //旷工下午
  490. {
  491. if (zt_jq != null) if (zt_jq.EndTimeDt == Convert.ToDateTime(thisDtStr + " 09:00")) continue; //排除已有假期的缺卡
  492. day_deduction = day_miner_unit * 6; //3小时
  493. absenteeism_deduction += day_deduction; //矿工半日
  494. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)缺卡/未打卡视为上午旷工(3小时)";
  495. beLate_belate_ex.Deduction = day_deduction;
  496. ex_reissuecard_Items.Add(beLate_belate_ex);
  497. }
  498. else //矿工一日
  499. {
  500. day_deduction = dailyWage;
  501. meal_deduction += 10.00M;
  502. absenteeism_deduction += day_deduction;
  503. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)-下午(18:00) 缺卡/未打视为旷工一天(7.5小时)";
  504. beLate_belate_ex.Deduction = day_deduction;
  505. ex_reissuecard_Items.Add(beLate_belate_ex);
  506. }
  507. }
  508. }
  509. }
  510. else if (exception == 4) //旷工
  511. {
  512. if (timelength > 60 && timelength <= 180)
  513. {
  514. day_deduction = day_miner_unit * 6; //3小时
  515. beLate_belate_ex.Reason = thisDtStr + " 缺卡/未打视为旷工上午(3小时)";
  516. meal_deduction += 10.00M;
  517. absenteeism_deduction += day_deduction; //矿工半日
  518. }
  519. else
  520. {
  521. day_deduction = dailyWage;
  522. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)-下午(18:00) 缺卡/未打视为旷工一天(7.5小时)";
  523. meal_deduction += 10.00M;
  524. absenteeism_deduction += day_deduction; //矿工一日
  525. }
  526. beLate_belate_ex.Deduction = day_deduction;
  527. ex_reissuecard_Items.Add(beLate_belate_ex);
  528. }
  529. }
  530. }
  531. }
  532. }
  533. if (ex_reissuecard_Items.Count > 0)
  534. {
  535. ex_Items_dk.Ex_ItemInfo = ex_reissuecard_Items;
  536. ex_Items_dk.Ex_ItemInfo = ex_reissuecard_Items.OrderBy(it => it.SubTypeId).ThenBy(it => it.StartTimeDt).ToList();
  537. ex_Items.Add(ex_Items_dk);
  538. }
  539. #endregion
  540. }
  541. else
  542. {
  543. meal_subsidy = work_days * 10.00M;
  544. }
  545. #region 应发合计 实发合计 扣款合计(假勤扣款,其他扣款,社保扣款,公积金代扣,个税扣款)
  546. decimal mealTotal = meal_subsidy - meal_deduction; //餐补
  547. decimal salaryTotal = 0.00M;
  548. if (dk_work_days >= work_days)
  549. {
  550. dk_work_days = work_days;
  551. salaryTotal = amountPayable + mealTotal; //应发合计
  552. }
  553. else
  554. {
  555. if (itemName.Equals("张海麟"))
  556. {
  557. salaryTotal = amountPayable + mealTotal;
  558. }
  559. else
  560. {
  561. salaryTotal = (dk_work_days * dailyWage) + mealTotal; //应发合计
  562. }
  563. }
  564. //扣款合计 不含个税
  565. decimal eductionTotal = sickLeaveTotal + personalLeaveTotal + beLate_deduction + early_deduction + absenteeism_deduction + unprinted_deduction + other_deduction +
  566. pm_wsInfo.WithholdingInsurance + pm_wsInfo.ReservedFunds + pm_wsInfo.OtherDeductions;
  567. decimal actualReleaseTotal = salaryTotal - eductionTotal; //实发合计 * 不含个税
  568. #endregion
  569. #region 处理当月工资数据
  570. pm_wsInfo.YearMonth = thisYearMonth;
  571. pm_wsInfo.StartDate = startDt.ToString("yyyy-MM-dd");
  572. pm_wsInfo.EndDate = endDt.AddDays(-1).ToString("yyyy-MM-dd");
  573. pm_wsInfo.WorkDays = work_days; //当月应出勤天数
  574. pm_wsInfo.RegularDays = dk_work_days; //当月正常出勤天数
  575. pm_wsInfo.SickLeave = sickLeaveTotal; //病假
  576. pm_wsInfo.SomethingFalse = personalLeaveTotal; //事假
  577. pm_wsInfo.LateTo = beLate_deduction; //迟到
  578. pm_wsInfo.LeaveEarly = early_deduction; //早退
  579. pm_wsInfo.Absenteeism = absenteeism_deduction; //旷工
  580. pm_wsInfo.NotPunch = unprinted_deduction; //未打卡
  581. pm_wsInfo.OtherDeductions = other_deduction; //其他
  582. pm_wsInfo.Ex_ItemsRemark = JsonConvert.SerializeObject(ex_Items); //
  583. pm_wsInfo.Mealsupplement = mealTotal; //餐补
  584. pm_wsInfo.Should = salaryTotal; //应发合计
  585. pm_wsInfo.TotalDeductions = eductionTotal; //扣款合计
  586. pm_wsInfo.TotalRealHair = actualReleaseTotal; //实发合计
  587. pm_wsInfo.AfterTax = actualReleaseTotal - pm_wsInfo.WithholdingTax; //税后工资
  588. pm_wsInfo.LastUpdateUserId = userId;
  589. pm_wsInfo.LastUpdateDt = DateTime.Now;
  590. pm_wsInfo.CreateUserId = userId;
  591. pm_wsInfo.CreateTime = DateTime.Now;
  592. pm_wsInfo.DeleteUserId = null;
  593. pm_wsInfo.DeleteTime = null;
  594. #endregion
  595. }
  596. }
  597. catch (Exception ex)
  598. {
  599. _result.Msg = ex.Message;
  600. return _result;
  601. }
  602. _result.Code = 0;
  603. _result.Data = pm_WageSheetDattaSources;
  604. return _result;
  605. }
  606. /// <summary>
  607. /// 计算工资
  608. /// </summary>
  609. /// <param name="pm_WageSheetDattaSources"></param>
  610. /// <param name="userNames"></param>
  611. /// <param name="thisYearMonth"></param>
  612. /// <param name="startDt"></param>
  613. /// <param name="endDt"></param>
  614. /// <returns></returns>
  615. public static async Task<Result> SalaryCalculatorAsync(
  616. List<Pm_WageSheet> pm_WageSheetDattaSources, List<UserNameView> userNames, int userId, string thisYearMonth, DateTime startDt, DateTime endDt)
  617. {
  618. if (pm_WageSheetDattaSources.Count <= 0)
  619. {
  620. _result.Msg = "计算工资传入数据为空!";
  621. return _result;
  622. }
  623. if (userNames.Count <= 0)
  624. {
  625. var nameData = await _usersRep.GetUserNameList(1);
  626. userNames = nameData.Data;
  627. }
  628. //计算时间段内工作日
  629. int work_days = await GetWorkDays(thisYearMonth);
  630. if (work_days <=0)
  631. {
  632. _result.Msg = thisYearMonth+" 工作日未设置,请前往《工作日管理页面》设置!";
  633. return _result;
  634. }
  635. UserIdListView userIdListView = await _qiYeWeChatApiService.GetUserIdListAsync();
  636. if (userIdListView.errcode != 0)
  637. {
  638. _result.Msg = "【企业微信】【打卡】【获取员工ID】【Msg】" + userIdListView.errmsg;
  639. return _result;
  640. }
  641. List<string> qyWhchatIdList = new List<string>();
  642. qyWhchatIdList = userIdListView.dept_user.Select(it => it.userid).ToList();
  643. CheckInDayDataView checkInDayDataView = await _qiYeWeChatApiService.GetCheckInDayDataAsync(qyWhchatIdList, startDt, endDt);
  644. if (checkInDayDataView.errcode != 0)
  645. {
  646. _result.Msg = "【企业微信】【打卡】【获取时间段内所有日打卡】【Msg】" + checkInDayDataView.errmsg;
  647. return _result;
  648. }
  649. //筛选出工作日日报
  650. List<Root> workday_userRoots = checkInDayDataView.datas.Where(it => it.base_info.day_type == 0 && it.base_info.record_type == 1).ToList(); //工作日日报
  651. workday_userRoots = workday_userRoots.OrderBy(it => it.base_info.date).ToList();
  652. //获取 请假类型 Sp_Detail.template_id
  653. string leave_template_id = "C4NzTJCh1onCUK915rRkvy7Fh5Vqz4YbiEV9jrBY1";
  654. List<VacationLeaveTypeView> vacationLeaveTypes = await GetVacationLeaveTypes(leave_template_id);
  655. if (vacationLeaveTypes.Count <= 0)
  656. {
  657. _result.Msg = "【企业微信】【审批】【获取审批类型】【Msg】" + startDt + " - " + endDt + "请假 类型数据 获取失败!";
  658. return _result;
  659. }
  660. try
  661. {
  662. foreach (var pm_wsInfo in pm_WageSheetDattaSources)
  663. {
  664. string itemName = userNames.Where(it => it.Id == pm_wsInfo.UserId).FirstOrDefault().CnName;
  665. //补贴 金额
  666. decimal meal_subsidy = 0.00M; // 午餐(午餐10元/天) 补贴 * 计算方式:单日上午请假时长(小时)大于或者等于三小时 没有餐补
  667. //事假 病假 总金额
  668. decimal personalLeaveTotal = 0.00M, // 事假 日薪 *计算方式:日平均工资 = 月工资/当月应出勤天数。
  669. sickLeaveTotal = 0.00M; // 病假 日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内
  670. //扣款金额
  671. decimal beLate_deduction = 0.00M, // 迟到 扣款金额 *计算方式:
  672. // 一个自然月内,不足 10 分钟的迟到/早退,不超过 2 次的部分,不做处罚;3 次及以上,按50元 / 次处罚;
  673. // 超过 10 分钟(含 10 分钟),不足 60 分钟的迟到/早退,按 50 元/次处罚;
  674. // 超过 60 分钟(含 60 分钟),不足 3 小时的迟到/早退,且无请假者,按旷工半日处理;超过3 小时的迟到 / 早退,且无请假者,按旷工一日处理。
  675. early_deduction = 0.00M, // 早退 扣款金额
  676. absenteeism_deduction = 0.00M, // 旷工 扣款金额 *计算方式:旷工扣发当日工资
  677. unprinted_deduction = 0.00M, // 未打卡 扣款金额 *计算方式:
  678. // 试用期员工每月有 2 次 补卡机会,超过 2 次不足 5 次的部分,按 10 元 / 次处罚,5 次及以上的漏卡,按 50 元 / 次处罚;
  679. // 正式员工每月 3 次以内的补卡,按 10 元 / 次处罚,3 次及以上的漏卡,按 50 元 / 次处罚。
  680. sickLeave_deduction = 0.00M, // 病假
  681. other_deduction = 0.00M; // 其他 扣款金额
  682. decimal meal_deduction = 0.00M; // 餐补 扣款金额
  683. decimal reissuecard_deduction = 0.00M; // 补卡 扣款金额
  684. #region 计算日工资 正常日薪 事假日薪 病假日薪
  685. //月 - 应发工资
  686. decimal amountPayable = pm_wsInfo.Basic + pm_wsInfo.Floats + pm_wsInfo.PostAllowance + pm_wsInfo.InformationSecurityFee + pm_wsInfo.OtherSubsidies;
  687. // 日薪 = *计算方式:日平均工资 = 月工资/当月应出勤天数。
  688. decimal dailyWage = amountPayable / work_days;
  689. // 病假日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内
  690. decimal sickLeave_dailywage = _chengDuMinimumWage / work_days;
  691. //病假 一天扣款
  692. sickLeave_deduction = dailyWage - sickLeave_dailywage;
  693. List<Ex_Items> ex_Items = new List<Ex_Items>();//假勤 And 打卡备注集合
  694. Ex_Items ex_Items_dk = new Ex_Items() { Type = "打卡" }; //打卡
  695. Ex_Items ex_Items_jq = new Ex_Items() { Type = "假勤" }; //假勤
  696. #endregion
  697. List<Root> userRoots = new List<Root>();
  698. if (itemName == "蔡雯")
  699. {
  700. userRoots = workday_userRoots.Where(it => it.base_info.name == "蔡蔡" || it.base_info.name == "蔡雯").ToList(); //工作日日报 1-固定上下班;
  701. }
  702. else
  703. {
  704. userRoots = workday_userRoots.Where(it => it.base_info.name == itemName).ToList(); //工作日日报 1-固定上下班;
  705. }
  706. //userRoots = userRoots.Distinct().ToList();
  707. userRoots = userRoots.OrderBy(it => it.base_info.date).ToList();
  708. int dk_work_days = userRoots.Count; //应出勤天数
  709. if (dk_work_days > work_days)
  710. {
  711. dk_work_days = work_days;
  712. }
  713. meal_subsidy = dk_work_days * 10; //应发放餐补
  714. if (!itemName.Equals("张海麟"))
  715. {
  716. if (userRoots.Count <= 0)
  717. {
  718. _result.Msg = "【企业微信】【打卡】【获取打卡数据】【Msg】" + startDt + " - " + endDt + "打卡日数据 获取失败!";
  719. continue;
  720. }
  721. string acctid = userRoots[0].base_info.acctid;
  722. List<Ex_Item> ex_reissuecard_Items = new List<Ex_Item>(); //打卡类型 数据
  723. List<Sp_items> acc_sp_items = new List<Sp_items>(); //审批数据
  724. int user_probationary_bk_num = 0;
  725. decimal user_probationary_bk_decimal = pm_wsInfo.Floats; //绩效工资为0 则为试用员工
  726. #region 迟到 早退 旷工
  727. int user_cd_zt_num = 0; //早退/迟到 次数 10分钟内 2次以内不记处罚 三次及以上50一次
  728. foreach (var root in userRoots)
  729. {
  730. List<Holiday_infos> holiday_Infos = root.holiday_infos; //当天假勤信息
  731. List<Exception_infos> exception_infos = root.exception_infos; //当天校准状态信息
  732. List<Sp_items> sp_Items = root.sp_items;//当天假勤统计信息
  733. if (sp_Items.Count > 0)
  734. {
  735. sp_Items = sp_Items.Where(it => it.count > 0).ToList();
  736. acc_sp_items.AddRange(sp_Items);
  737. }
  738. foreach (var exception_info in exception_infos)
  739. {
  740. decimal timelength = ConvertToDecimal((Convert.ToDecimal(exception_info.duration) / 3600.00M) * 60.00M); //时长 分钟
  741. if (timelength == 9) timelength = 7.50M;
  742. int exception = exception_info.exception; //异常类型
  743. decimal day_miner_unit = dailyWage / 15; //以0.5小时为单位
  744. //1:一个自然月内,不足 10 分钟的迟到/早退,不超过 2 次的部分,不做处罚;3 次及以上,按50 元 / 次处罚;
  745. //2:超过 10 分钟(含 10 分钟),不足 60 分钟的迟到 / 早退,按 50 元 / 次处罚;
  746. //3:超过 60 分钟(含 60 分钟),不足 3 小时的迟到 / 早退,且无请假者,按旷工半日处理;超过 3 小时的迟到 / 早退,且无请假者,按旷工一日处理。
  747. long date = root.base_info.date; //当日工作日期
  748. long earliest_time = root.summary_info.earliest_time; //最早打卡时间
  749. long lastest_time = root.summary_info.lastest_time; //最晚打卡时间
  750. long this_date = date + earliest_time;
  751. DateTime thisDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks(this_date * 10000000);
  752. string thisDtStr = thisDt.ToString("yyyy-MM-dd");
  753. Ex_Item beLate_belate_ex = new Ex_Item()
  754. {
  755. SubTypeId = 4,
  756. SubType = "旷工",
  757. Duration = timelength,
  758. StartTimeDt = Convert.ToDateTime(root.base_info.dateDt.ToString("yyyy-MM-dd HH:mm:ss")),
  759. Unit = "分钟",
  760. };
  761. decimal day_deduction = 0.00M;
  762. //1 - 迟到;2 - 早退;3 - 缺卡;4 - 旷工;5 - 地点异常;6 - 设备异常
  763. if (exception == 1) //迟到
  764. {
  765. if (timelength < 10)
  766. {
  767. user_cd_zt_num++;
  768. beLate_belate_ex.SubTypeId = 1;
  769. beLate_belate_ex.SubType = "迟到";
  770. string thisStartDt = (TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + earliest_time) * 10000000))
  771. .ToString("yyyy-MM-dd HH:mm:ss");
  772. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisStartDt);
  773. if (user_cd_zt_num >= 3)
  774. {
  775. day_deduction = 50.00M;
  776. }
  777. else
  778. {
  779. day_deduction = 0.00M;
  780. }
  781. beLate_deduction += day_deduction; //迟到扣款 总额
  782. }
  783. else if (timelength >= 10 && timelength <= 60)
  784. {
  785. string thisStartDt = (TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + earliest_time) * 10000000))
  786. .ToString("yyyy-MM-dd HH:mm:ss");
  787. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisStartDt);
  788. day_deduction = 50.00M;
  789. beLate_deduction += day_deduction; //迟到扣款 总额
  790. beLate_belate_ex.SubTypeId = 1;
  791. beLate_belate_ex.SubType = "迟到";
  792. }
  793. else if (timelength > 60 && timelength <= 180)
  794. {
  795. day_deduction = ConvertToDecimal(day_miner_unit * 6); //3小时
  796. meal_deduction += 10.00M; //餐补扣款
  797. absenteeism_deduction += day_deduction; //矿工半日
  798. }
  799. else
  800. {
  801. day_deduction = ConvertToDecimal(dailyWage);
  802. absenteeism_deduction += day_deduction; //矿工一日
  803. meal_deduction += 10.00M;
  804. }
  805. beLate_belate_ex.Deduction = day_deduction;
  806. ex_reissuecard_Items.Add(beLate_belate_ex);
  807. }
  808. else if (exception == 2) //早退
  809. {
  810. if (timelength < 10)
  811. {
  812. user_cd_zt_num++;
  813. beLate_belate_ex.SubTypeId = 2;
  814. beLate_belate_ex.SubType = "早退";
  815. string thisEndDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + lastest_time) * 10000000)
  816. .ToString("yyyy-MM-dd HH:mm:ss");
  817. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisEndDt);
  818. if (user_cd_zt_num >= 3)
  819. {
  820. day_deduction = 50.00M;
  821. }
  822. else
  823. {
  824. day_deduction = 0.00M;
  825. }
  826. early_deduction += day_deduction; //早退扣款 总计
  827. }
  828. else if (timelength >= 10 && timelength <= 60)
  829. {
  830. day_deduction = 50.00M;
  831. early_deduction += day_deduction; //早退扣款 总计
  832. beLate_belate_ex.SubTypeId = 2;
  833. beLate_belate_ex.SubType = "早退";
  834. string thisEndDt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddTicks((date + lastest_time) * 10000000)
  835. .ToString("yyyy-MM-dd HH:mm:ss");
  836. beLate_belate_ex.StartTimeDt = Convert.ToDateTime(thisEndDt);
  837. }
  838. else if (timelength > 60 && timelength <= 180)
  839. {
  840. day_deduction = ConvertToDecimal(day_miner_unit * 6); //3小时
  841. absenteeism_deduction += day_deduction; //矿工半日
  842. }
  843. else
  844. {
  845. day_deduction = ConvertToDecimal(dailyWage);
  846. meal_deduction += 10.00M;
  847. absenteeism_deduction += day_deduction; //矿工一日
  848. }
  849. beLate_belate_ex.Deduction = day_deduction;
  850. ex_reissuecard_Items.Add(beLate_belate_ex);
  851. }
  852. else if (exception == 3) //缺卡
  853. {
  854. if (root.exception_infos.Count == 2)
  855. {
  856. day_deduction = dailyWage;
  857. meal_deduction += 10.00M;
  858. absenteeism_deduction += day_deduction;
  859. beLate_belate_ex.Reason = "上午-下午 缺卡/未打卡为旷工 一天";
  860. beLate_belate_ex.Deduction = day_deduction;
  861. ex_reissuecard_Items.Add(beLate_belate_ex);
  862. }
  863. else if (root.exception_infos.Count == 1)
  864. {
  865. if (earliest_time == lastest_time)
  866. {
  867. DateTime thisDt1 = Convert.ToDateTime(thisDtStr + " 12:00");
  868. if (thisDt <= thisDt1) //旷工 下午
  869. {
  870. day_deduction = day_miner_unit * 9; //4.5小时
  871. meal_deduction += 10.00M;
  872. absenteeism_deduction += day_deduction; //矿工半日
  873. beLate_belate_ex.Reason = thisDtStr + " 下午(18:00)缺卡/未打卡视为下午旷工(4.5小时)";
  874. beLate_belate_ex.Deduction = day_deduction;
  875. ex_reissuecard_Items.Add(beLate_belate_ex);
  876. }
  877. else if (thisDt >= thisDt1) //旷工下午
  878. {
  879. day_deduction = day_miner_unit * 6; //3小时
  880. absenteeism_deduction += day_deduction; //矿工半日
  881. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)缺卡/未打卡视为上午旷工(3小时)";
  882. beLate_belate_ex.Deduction = day_deduction;
  883. ex_reissuecard_Items.Add(beLate_belate_ex);
  884. }
  885. else //矿工一日
  886. {
  887. day_deduction = dailyWage;
  888. meal_deduction += 10.00M;
  889. absenteeism_deduction += day_deduction;
  890. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)-下午(18:00) 缺卡/未打视为旷工一天(7.5小时)";
  891. beLate_belate_ex.Deduction = day_deduction;
  892. ex_reissuecard_Items.Add(beLate_belate_ex);
  893. }
  894. }
  895. }
  896. }
  897. else if (exception == 4) //旷工
  898. {
  899. if (timelength > 60 && timelength <= 180)
  900. {
  901. day_deduction = ConvertToDecimal(day_miner_unit * 6); //3小时
  902. beLate_belate_ex.Reason = thisDtStr + " 缺卡/未打视为旷工上午(3小时)";
  903. meal_deduction += 10.00M;
  904. absenteeism_deduction += day_deduction; //矿工半日
  905. }
  906. else
  907. {
  908. day_deduction = ConvertToDecimal(dailyWage);
  909. beLate_belate_ex.Reason = thisDtStr + " 上午(09:00)-下午(18:00) 缺卡/未打视为旷工一天(7.5小时)";
  910. meal_deduction += 10.00M;
  911. absenteeism_deduction += day_deduction; //矿工一日
  912. }
  913. beLate_belate_ex.Deduction = day_deduction;
  914. ex_reissuecard_Items.Add(beLate_belate_ex);
  915. }
  916. }
  917. }
  918. #endregion
  919. #region 假勤/补卡次数 审批
  920. int leaveNum = 0; //请假次数
  921. int reissuecardNum = 0; //补卡次数\
  922. //类型:1 - 请假;2 - 补卡;3 - 出差;4 - 外出;100 - 外勤
  923. leaveNum = acc_sp_items.Where(it => it.type == 1).ToList().Count();
  924. reissuecardNum = acc_sp_items.Where(it => it.type == 2).ToList().Count();
  925. //请假审批
  926. if (leaveNum > 0)
  927. {
  928. List<Sp_Detail> sp_leave_details = new List<Sp_Detail>();
  929. sp_leave_details = await _qiYeWeChatApiService.GetApprovalDetailsAsync(startDt, endDt, acctid, 2, 1); //时间段内所有 已同意的 请假 审批数据
  930. if (sp_leave_details.Count <= 0)
  931. {
  932. _result.Msg += startDt + " - " + endDt + " " + itemName + " 请假 审批数据获取未获取到!\r\n";
  933. //continue;
  934. }
  935. List<Ex_Item> ex_ItemInfos = new List<Ex_Item>();
  936. foreach (Sp_Detail sp_item in sp_leave_details)
  937. {
  938. Apply_data? apply_data = sp_item.apply_data;
  939. if (apply_data != null)
  940. {
  941. List<ContentsItem> contents = apply_data.contents;
  942. ContentsItem content_Vacation = contents.Where(it => it.control == "Vacation").FirstOrDefault(); //请假类型
  943. ContentsItem content_Textarea = contents.Where(it => it.control == "Textarea").FirstOrDefault(); //多行文本
  944. if (content_Vacation != null)
  945. {
  946. Vacation vacation = content_Vacation.value.vacation;
  947. Attendance attendance = vacation.attendance; //假勤组件
  948. Selector selector = vacation.selector; //请假类型
  949. List<OptionsItem> optionsItems = selector.options; //key 请假类型 id
  950. List<TitleItem> value = optionsItems[0].value; // value 文本描述值
  951. int leaveType = int.Parse(optionsItems[0].key); //key 请假子类型 id
  952. Date_range date_Range = attendance.date_range;
  953. //筛选 不在工作日内的假勤申请
  954. if (startDt >= date_Range.new_begin_dt || Convert.ToDateTime( date_Range.new_end_dt.ToString("yyyy-MM-dd")) > endDt)
  955. {
  956. continue;
  957. }
  958. decimal thisTypeDeduction = 0.00M;//当前类型扣款
  959. string leave_starttime = date_Range.new_begin_dt.ToString("HH:mm");
  960. string leave_endtime = date_Range.new_end_dt.ToString("HH:mm");
  961. string typeName = string.Empty;
  962. string unit = string.Empty;
  963. int leaveTypeId = leaveType;
  964. var leaveTypeData = vacationLeaveTypes.Where(it => it.id == leaveTypeId).FirstOrDefault();
  965. if (leaveTypeData != null) { typeName = leaveTypeData.name; }
  966. string startTime = string.Empty;
  967. string endTime = string.Empty;
  968. //计算请假类型扣款金额
  969. decimal new_duration = 0.00M;
  970. if (date_Range.type == "halfday")
  971. {
  972. new_duration = Convert.ToDecimal(date_Range.new_duration) / 86400M;
  973. unit = "天";
  974. startTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd") + " 09:00";
  975. endTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd") + " 18:00";
  976. }
  977. else if (date_Range.type == "hour")
  978. {
  979. new_duration = Convert.ToDecimal(date_Range.new_duration) / 3600M;
  980. unit = "小时";
  981. startTime = date_Range.new_begin_dt.ToString("yyyy-MM-dd HH:mm:ss");
  982. endTime = date_Range.new_end_dt.ToString("yyyy-MM-dd HH:mm:ss");
  983. }
  984. decimal leave_meals = 0.00M;
  985. //计算餐补 假勤类型扣款
  986. CalculateTypeFee(leaveType, date_Range.type, leave_starttime, leave_endtime, amountPayable, work_days, new_duration,
  987. out leave_meals, out thisTypeDeduction);
  988. #region 累计类型扣款
  989. //1年假;2事假;3病假;4调休假;5婚假;6产假;7陪产假;8其他;9丧假
  990. if (leaveType == 2) //事假
  991. {
  992. personalLeaveTotal += thisTypeDeduction;
  993. }
  994. else if (leaveType == 3) //病假
  995. {
  996. sickLeaveTotal = thisTypeDeduction;
  997. }
  998. #endregion
  999. meal_deduction += leave_meals;
  1000. Ex_Item ex_Item = new Ex_Item()
  1001. {
  1002. SubTypeId = leaveType,
  1003. SubType = typeName,
  1004. StartTimeDt = Convert.ToDateTime(startTime),
  1005. EndTimeDt = Convert.ToDateTime(endTime),
  1006. Duration = new_duration,
  1007. Unit = unit,
  1008. Deduction = thisTypeDeduction,
  1009. //Reason = apply_data.reason,
  1010. Apply_time_dt = Convert.ToDateTime(sp_item.apply_time_dt.ToString("yyyy-MM-dd HH:mm:ss")),
  1011. //Approval_name = sp_item.approval_name,
  1012. };
  1013. ex_ItemInfos.Add(ex_Item);
  1014. }
  1015. }
  1016. }
  1017. if (ex_ItemInfos.Count > 0)
  1018. {
  1019. ex_Items_jq.Ex_ItemInfo = ex_ItemInfos.OrderBy(it => it.StartTimeDt).ThenBy(it => it.Apply_time_dt).ToList();
  1020. ex_Items.Add(ex_Items_jq);
  1021. }
  1022. }
  1023. //打卡补卡
  1024. //补卡:员工发现自己漏打卡时,需及时提起补卡申请,并说明情况。
  1025. //试用期员工每月有 2 次补卡机会,超过 2 次不足 5 次的部分,按 10 元/次处罚,5 次及以上的漏卡,按 50 元/次处罚;
  1026. //正式员工每月 3 次以内的补卡,按 10 元/次处罚,3 次及以上的漏卡,按 50 元/次处罚。
  1027. if (reissuecardNum > 0)
  1028. {
  1029. List<Sp_Detail> sp_buka_details = new List<Sp_Detail>();
  1030. sp_buka_details = await _qiYeWeChatApiService.GetApprovalDetailsAsync(startDt, endDt, acctid, 2, 2); //时间段内所有 已同意的 请假 审批数据
  1031. int bukaNum = 1;
  1032. foreach (var item in sp_buka_details)
  1033. {
  1034. Apply_data? apply_data = item.apply_data;
  1035. if (apply_data != null)
  1036. {
  1037. List<ContentsItem> contents = apply_data.contents;
  1038. ContentsItem content_Vacation = contents.Where(it => it.control == "PunchCorrection").FirstOrDefault(); //请假类型
  1039. ContentsItem content_Textarea = contents.Where(it => it.control == "Textarea").FirstOrDefault(); //多行文本
  1040. if (content_Vacation != null)
  1041. {
  1042. var punch_correction = content_Vacation.value.punch_correction;
  1043. DateTime bukaDt = punch_correction.time_dt;
  1044. DateTime bukaDtJudge = Convert.ToDateTime(bukaDt.ToString("yyyy-MM-dd"));
  1045. //筛选 不在工作日内的假勤申请
  1046. if (startDt >= bukaDtJudge || bukaDtJudge > endDt)
  1047. {
  1048. continue;
  1049. }
  1050. decimal bukaPrice = 0.00M;
  1051. if (user_probationary_bk_decimal == 0) //计算试用员工补卡次数
  1052. {
  1053. if (bukaNum <= 2) bukaPrice = 0.00M;
  1054. else if (bukaNum <= 4 && bukaNum > 2) bukaPrice = 10.00M;
  1055. else bukaPrice = 50.00M;
  1056. }
  1057. else //计算正式员工补卡次数
  1058. {
  1059. if (bukaNum <= 2) bukaPrice = 10.00M;
  1060. else bukaPrice = 50.00M;
  1061. }
  1062. var app_data = item.apply_data;
  1063. var punch_correction1 = app_data.contents[0].value.punch_correction; //未打卡时间
  1064. var punch_correction2 = app_data.contents[1].value;
  1065. Ex_Item ex_reissueCard = new Ex_Item()
  1066. {
  1067. SubTypeId = 7,
  1068. SubType = "打卡补卡",
  1069. StartTimeDt = Convert.ToDateTime(punch_correction1.time_dt.ToString("yyyy-MM-dd HH:mm:ss")), //未打卡时间
  1070. Deduction = bukaPrice,
  1071. Reason = punch_correction2.text,
  1072. Unit = string.Empty
  1073. };
  1074. unprinted_deduction += bukaPrice;
  1075. ex_reissuecard_Items.Add(ex_reissueCard);
  1076. bukaNum++;
  1077. }
  1078. }
  1079. }
  1080. }
  1081. if (ex_reissuecard_Items.Count > 0)
  1082. {
  1083. ex_Items_dk.Ex_ItemInfo = ex_reissuecard_Items;
  1084. //ex_Items_dk.Ex_ItemInfo = ex_reissuecard_Items.OrderBy(it => it.SubTypeId).ThenBy(it => it.StartTimeDt).ToList();
  1085. ex_Items.Add(ex_Items_dk);
  1086. }
  1087. #endregion
  1088. }
  1089. else
  1090. {
  1091. meal_subsidy = work_days * 10.00M;
  1092. }
  1093. #region 应发合计 实发合计 扣款合计(假勤扣款,其他扣款,社保扣款,公积金代扣,个税扣款)
  1094. decimal mealTotal = meal_subsidy - meal_deduction; //餐补
  1095. decimal salaryTotal = 0.00M;
  1096. if (dk_work_days >= work_days)
  1097. {
  1098. dk_work_days = work_days;
  1099. salaryTotal = amountPayable + mealTotal; //应发合计
  1100. }
  1101. else
  1102. {
  1103. if (itemName.Equals("张海麟"))
  1104. {
  1105. salaryTotal = amountPayable + mealTotal; //应发合计
  1106. }
  1107. else
  1108. {
  1109. salaryTotal = (dk_work_days * dailyWage) + mealTotal; //应发合计
  1110. }
  1111. }
  1112. //扣款合计 不含个税
  1113. decimal eductionTotal = sickLeaveTotal + personalLeaveTotal + beLate_deduction + early_deduction + absenteeism_deduction + unprinted_deduction + other_deduction +
  1114. pm_wsInfo.WithholdingInsurance + pm_wsInfo.ReservedFunds + pm_wsInfo.OtherDeductions;
  1115. decimal actualReleaseTotal = salaryTotal - eductionTotal; //实发合计 * 不含个税
  1116. #endregion
  1117. #region 处理当月工资数据
  1118. pm_wsInfo.YearMonth = thisYearMonth;
  1119. pm_wsInfo.StartDate = startDt.ToString("yyyy-MM-dd");
  1120. pm_wsInfo.EndDate = endDt.ToString("yyyy-MM-dd");
  1121. pm_wsInfo.WorkDays = work_days; //当月应出勤天数
  1122. pm_wsInfo.RegularDays = dk_work_days; //当月正常出勤天数
  1123. pm_wsInfo.SickLeave = sickLeaveTotal; //病假
  1124. pm_wsInfo.SomethingFalse = personalLeaveTotal; //事假
  1125. pm_wsInfo.LateTo = beLate_deduction; //迟到
  1126. pm_wsInfo.LeaveEarly = early_deduction; //早退
  1127. pm_wsInfo.Absenteeism = absenteeism_deduction; //旷工
  1128. pm_wsInfo.NotPunch = unprinted_deduction; //未打卡
  1129. pm_wsInfo.OtherDeductions = other_deduction; //其他
  1130. pm_wsInfo.Ex_ItemsRemark = JsonConvert.SerializeObject(ex_Items); //
  1131. pm_wsInfo.Mealsupplement = mealTotal; //餐补
  1132. pm_wsInfo.Should = salaryTotal; //应发合计
  1133. pm_wsInfo.TotalDeductions = eductionTotal; //扣款合计
  1134. pm_wsInfo.TotalRealHair = actualReleaseTotal - pm_wsInfo.WithholdingTax; //实发合计
  1135. pm_wsInfo.AfterTax = actualReleaseTotal - pm_wsInfo.WithholdingTax; //税后工资
  1136. pm_wsInfo.LastUpdateUserId = userId;
  1137. pm_wsInfo.LastUpdateDt = DateTime.Now;
  1138. pm_wsInfo.CreateUserId = userId;
  1139. pm_wsInfo.CreateTime = DateTime.Now;
  1140. pm_wsInfo.DeleteUserId = null;
  1141. pm_wsInfo.DeleteTime = null;
  1142. #endregion
  1143. }
  1144. }
  1145. catch (Exception ex)
  1146. {
  1147. _result.Msg = ex.Message;
  1148. return _result;
  1149. }
  1150. _result.Code = 0;
  1151. _result.Data = pm_WageSheetDattaSources;
  1152. return _result;
  1153. }
  1154. /// <summary>
  1155. /// decimal 保留两位小数 不四舍五入
  1156. /// </summary>
  1157. /// <param name="number"></param>
  1158. /// <returns></returns>
  1159. public static decimal ConvertToDecimal(decimal myDecimal)
  1160. {
  1161. var subDecimal = Math.Floor(myDecimal * 100) / 100;//保留两位小数,直接截取
  1162. return subDecimal;
  1163. }
  1164. /// <summary>
  1165. /// 获取请假类型
  1166. /// </summary>
  1167. /// <param name="template_id"></param>
  1168. /// <returns></returns>
  1169. public static async Task<List<VacationLeaveTypeView>> GetVacationLeaveTypes(string template_id)
  1170. {
  1171. List<VacationLeaveTypeView> vacationLeaveTypes = new List<VacationLeaveTypeView>();
  1172. TemplateDetailView templateDetailView = new TemplateDetailView();
  1173. templateDetailView = await _qiYeWeChatApiService.GetTemplateDetailAsync(template_id);
  1174. if (templateDetailView.errcode != 0)
  1175. {
  1176. Serilog.Log.Error("【企业微信】【审批】【获取假勤类型的审批】【Msg】"+ templateDetailView.errmsg);
  1177. return vacationLeaveTypes;
  1178. }
  1179. List<VacationItemInfo> VacationItemInfos = templateDetailView.vacation_list.item;
  1180. foreach (var item in VacationItemInfos)
  1181. {
  1182. ValueItem valueInfo = item.name.Where(it => it.lang == "zh_CN").FirstOrDefault();
  1183. if (valueInfo != null)
  1184. {
  1185. vacationLeaveTypes.Add(
  1186. new VacationLeaveTypeView()
  1187. {
  1188. id = item.id,
  1189. name = valueInfo.text
  1190. });
  1191. }
  1192. }
  1193. return vacationLeaveTypes;
  1194. }
  1195. /// <summary>
  1196. /// 计算类型费用
  1197. /// </summary>
  1198. /// <param name="leaveType">
  1199. /// 1年假;2事假;3病假;4调休假;5婚假;6产假;7陪产假;8其他;9丧假
  1200. /// </param>
  1201. /// <param name="date_Range_type">
  1202. /// halfday 全天
  1203. /// hour 小时
  1204. /// </param>
  1205. /// <param name="startTime"></param>
  1206. /// <param name="endTime"></param>
  1207. /// <param name="duration"></param>
  1208. /// <param name="mealDeduction"></param>
  1209. /// <param name="typeDeduction"></param>
  1210. public static void CalculateTypeFee(int leaveType, string date_Range_type, string startTime, string endTime,decimal amountPayable,int work_days,
  1211. decimal duration, out decimal mealDeduction, out decimal typeDeduction)
  1212. {
  1213. typeDeduction = 0;
  1214. mealDeduction = 0;
  1215. string am_starttime = "08:59";
  1216. string am_endtime = "13:01";
  1217. decimal personalkLeave_dailywage_day = amountPayable / work_days; //日薪 = 事假日薪 *计算方式:日平均工资 = 当月应发工资 /当月应出勤天数。
  1218. //半小时单位
  1219. decimal halfHour = Convert.ToDecimal(7.5) / Convert.ToDecimal(0.5);
  1220. switch (leaveType)
  1221. {
  1222. case 1: //年假
  1223. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1224. break;
  1225. case 2: //2事假
  1226. // 事假日薪 *计算方式:日平均工资 = 当月应发工资 /当月应出勤天数。
  1227. decimal personalkLeave_dailywage_halfhour = personalkLeave_dailywage_day / halfHour; //事假单位 0.5小时
  1228. if (date_Range_type == "halfday")
  1229. {
  1230. mealDeduction = 10; //餐补扣款
  1231. typeDeduction = ConvertToDecimal(personalkLeave_dailywage_day);
  1232. }
  1233. else if (date_Range_type == "hour")
  1234. {
  1235. decimal leave_halfHour = Convert.ToDecimal(duration) / Convert.ToDecimal(0.5);
  1236. typeDeduction = ConvertToDecimal( personalkLeave_dailywage_halfhour * leave_halfHour);
  1237. //duration = 11M;
  1238. if (duration >= 3 && duration < 7.5M) //单天请假三小时
  1239. {
  1240. mealDeduction = 10; //餐补扣款
  1241. }
  1242. else if (duration >= 7.5M) //多天计算
  1243. {
  1244. decimal leave_halfHour1 = Convert.ToDecimal(duration) / Convert.ToDecimal(0.5);
  1245. typeDeduction = ConvertToDecimal(personalkLeave_dailywage_day);
  1246. decimal leaveDays = duration / 7.5M;
  1247. if (leaveDays % 1 == 0)
  1248. {
  1249. mealDeduction = 10 * leaveDays; //餐补扣款
  1250. }
  1251. else
  1252. {
  1253. typeDeduction = personalkLeave_dailywage_day * Convert.ToInt32(leaveDays);
  1254. decimal sy_shijiaunit = leave_halfHour1 - Convert.ToDecimal (15.00M * Convert.ToInt32(leaveDays));
  1255. if (sy_shijiaunit > 0)
  1256. {
  1257. typeDeduction += ConvertToDecimal(personalkLeave_dailywage_halfhour * sy_shijiaunit);
  1258. }
  1259. mealDeduction = 10 * Convert.ToInt32(leaveDays);
  1260. //得到最后一天的请假时间 是否有餐补
  1261. int lastHours = (Convert.ToDateTime(endTime) - Convert.ToDateTime("09:00")).Hours;
  1262. if (lastHours >= 3)
  1263. {
  1264. mealDeduction += 10; //餐补扣款
  1265. }
  1266. }
  1267. }
  1268. }
  1269. break;
  1270. case 3: //3病假
  1271. // 病假日薪 *计算方式:日平均工资 = 成都市最低工资标准的80% /当月应出勤天数。 短期病假=当月15天内
  1272. decimal chengDuMinimumWage_halrHour = _chengDuMinimumWage / work_days / halfHour;
  1273. decimal sickLeave_dailywage_halfhour_deduction = personalkLeave_dailywage_day - chengDuMinimumWage_halrHour; //病假单位 0.5小时 扣款金额
  1274. if (date_Range_type == "halfday")
  1275. {
  1276. mealDeduction = 10; //餐补扣款
  1277. typeDeduction = ConvertToDecimal( sickLeave_dailywage_halfhour_deduction * halfHour);
  1278. }
  1279. else if (date_Range_type == "hour")
  1280. {
  1281. decimal sickLeave_halfHour = duration / 0.5M;
  1282. typeDeduction = ConvertToDecimal(personalkLeave_dailywage_day); ;
  1283. if (duration >= 3 && duration < 7.5M) //单天请假三小时 && 请假时间在上午 则没有餐补
  1284. {
  1285. mealDeduction = 10; //餐补扣款
  1286. }
  1287. else if (duration >= 7.5M) //多天计算
  1288. {
  1289. decimal sickLeave_halfHour1 = duration / 0.5M;
  1290. decimal leaveDays = Convert.ToDecimal(duration / 7.5M);
  1291. typeDeduction = ConvertToDecimal(sickLeave_dailywage_halfhour_deduction * sickLeave_halfHour1);
  1292. if (leaveDays % 1 == 0)
  1293. {
  1294. mealDeduction = 10 * leaveDays; //餐补扣款
  1295. }
  1296. else
  1297. {
  1298. mealDeduction = 10 * Convert.ToInt32(leaveDays);
  1299. typeDeduction = ConvertToDecimal(sickLeave_dailywage_halfhour_deduction * Convert.ToInt32(leaveDays));
  1300. decimal sy_bingjiaunit = sickLeave_halfHour1 - Convert.ToDecimal(15.00M * Convert.ToInt32(leaveDays));
  1301. if (sy_bingjiaunit > 0)
  1302. {
  1303. typeDeduction += ConvertToDecimal(sickLeave_dailywage_halfhour_deduction * sy_bingjiaunit);
  1304. }
  1305. //得到最后一天的请假时间 是否有餐补
  1306. int lastHours = (Convert.ToDateTime(endTime) - Convert.ToDateTime("09:00")).Hours;
  1307. if (lastHours >= 3)
  1308. {
  1309. mealDeduction += 10; //餐补扣款
  1310. }
  1311. }
  1312. }
  1313. }
  1314. break;
  1315. case 4: //4调休假
  1316. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1317. break;
  1318. case 5: //5婚假
  1319. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1320. break;
  1321. case 6: //6产假
  1322. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1323. break;
  1324. case 7: //7陪产假
  1325. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1326. break;
  1327. case 8: //8其他
  1328. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1329. break;
  1330. case 9: //9丧假
  1331. CalculateTypeFeeSub(date_Range_type, startTime, endTime, duration, out mealDeduction);
  1332. break;
  1333. }
  1334. }
  1335. /// <summary>
  1336. /// 计算类型费用
  1337. /// </summary>
  1338. /// <param name="date_Range_type">
  1339. /// halfday 全天
  1340. /// hour 小时
  1341. /// </param>
  1342. /// <param name="startTime"></param>
  1343. /// <param name="endTime"></param>
  1344. /// <param name="duration"></param>
  1345. /// <param name="mealDeduction"></param>
  1346. public static void CalculateTypeFeeSub(string date_Range_type, string startTime, string endTime, decimal duration, out decimal mealDeduction)
  1347. {
  1348. mealDeduction = 0;
  1349. string am_starttime = "09:00";
  1350. string am_endtime = "11:59";
  1351. if (date_Range_type == "halfday")
  1352. {
  1353. mealDeduction = 10; //餐补扣款
  1354. }
  1355. else if (date_Range_type == "hour")
  1356. {
  1357. if (duration >= 3 && duration < 7) //单天请假三小时 && 请假时间在上午 则没有餐补
  1358. {
  1359. mealDeduction = 10; //餐补扣款
  1360. #region 上午请假超三小时 扣款
  1361. //TimeSpan start_ts = TimeSpan.Parse(startTime); // 10 PM
  1362. //TimeSpan end_ts = TimeSpan.Parse(endTime); // 2 AM
  1363. //TimeSpan am_start_ts = TimeSpan.Parse(am_starttime); // 10 PM
  1364. //TimeSpan am_end_ts = TimeSpan.Parse(am_endtime); // 2 AM
  1365. ////处理开始时间
  1366. //if (start_ts >= am_start_ts && start_ts <= am_end_ts)
  1367. //{
  1368. // //处理结束时间
  1369. // if (end_ts > am_start_ts && end_ts >= am_end_ts)
  1370. // {
  1371. // mealDeduction = 10; //餐补扣款
  1372. // }
  1373. //}
  1374. #endregion
  1375. }
  1376. else if (duration >= 7 && duration <= 7.50M )
  1377. {
  1378. mealDeduction = 10; //餐补扣款
  1379. }
  1380. else if (duration >= 7.50M) //多天计算
  1381. {
  1382. decimal leaveDays = Convert.ToDecimal(duration / 7.50M);
  1383. if (leaveDays % 1 == 0)
  1384. {
  1385. mealDeduction = 10 * leaveDays; //餐补扣款
  1386. }
  1387. else
  1388. {
  1389. mealDeduction = 10 * Convert.ToInt32(leaveDays);
  1390. //得到最后一天的请假时间 是否有餐补
  1391. int lastHours = (Convert.ToDateTime(endTime) - Convert.ToDateTime("09:00")).Hours;
  1392. if (lastHours >= 3)
  1393. {
  1394. mealDeduction += 10; //餐补扣款
  1395. ////处理结束时间
  1396. //if (endTime.CompareTo(am_starttime) > 0 && endTime.CompareTo(am_endtime) < 0)
  1397. //{
  1398. // mealDeduction += 10; //餐补扣款
  1399. //}
  1400. }
  1401. }
  1402. }
  1403. }
  1404. }
  1405. /// <summary>
  1406. /// 获取打卡补卡类型
  1407. /// </summary>
  1408. /// <param name="template_id"></param>
  1409. /// <returns></returns>
  1410. public static async Task<List<VacationLeaveTypeView>> GetVacationReissueCardTypes(string template_id)
  1411. {
  1412. List<VacationLeaveTypeView> vacationLeaveTypes = new List<VacationLeaveTypeView>();
  1413. TemplateDetailView templateDetailView = new TemplateDetailView();
  1414. templateDetailView = await _qiYeWeChatApiService.GetTemplateDetailAsync(template_id);
  1415. if (templateDetailView.errcode != 0)
  1416. {
  1417. return vacationLeaveTypes;
  1418. }
  1419. List<VacationItemInfo> VacationItemInfos = templateDetailView.vacation_list.item;
  1420. foreach (var item in VacationItemInfos)
  1421. {
  1422. ValueItem valueInfo = item.name.Where(it => it.lang == "zh_CN").FirstOrDefault();
  1423. if (valueInfo != null)
  1424. {
  1425. vacationLeaveTypes.Add(
  1426. new VacationLeaveTypeView()
  1427. {
  1428. id = item.id,
  1429. name = valueInfo.text
  1430. });
  1431. }
  1432. }
  1433. return vacationLeaveTypes;
  1434. }
  1435. /// <summary>
  1436. /// 打卡数据
  1437. /// 假勤数据 统计
  1438. /// </summary>
  1439. /// <param name="datas">数据源</param>
  1440. /// <param name="type">
  1441. /// 1-请假;2-补卡;3-出差;4-外出;100-外勤;
  1442. /// </param>
  1443. /// <param name="subTypeName">
  1444. /// 年假 事假 病假 调休假 婚嫁 产假 陪产假 丧假 补卡次数 出差 外出数 外勤 其他
  1445. /// </param>
  1446. /// <returns></returns>
  1447. private static int Fallibilitydispose(List<Sp_Item> datas, int type, string? subTypeName)
  1448. {
  1449. int num = 0;
  1450. Sp_Item _Info = datas.Where(it => it.type == type && it.name == subTypeName).FirstOrDefault();
  1451. if (_Info != null) { num = _Info.count; }
  1452. return num;
  1453. }
  1454. /// <summary>
  1455. /// 打卡数据
  1456. /// 异常数据 统计
  1457. /// </summary>
  1458. /// <returns></returns>
  1459. private static int ExceptionStatistics(List<Exception_Info> datas, int type)
  1460. {
  1461. int num = 0;
  1462. Exception_Info _Info = datas.Where(it => it.exception == type).FirstOrDefault();
  1463. if (_Info != null) { num = _Info.count; }
  1464. return num;
  1465. }
  1466. /// <summary>
  1467. /// 获取时间段内除周末 节假日外的 工作日
  1468. /// </summary>
  1469. /// <param name="startDt"></param>
  1470. /// <param name="endDt"></param>
  1471. /// <returns></returns>
  1472. public static async Task<int> GetWorkDays(string yearMonth)
  1473. {
  1474. int workdays = 0;
  1475. string sql = string.Format(@"Select * From Pm_WageIssueWorkingDay
  1476. Where Isdel = 0 And YearMonth = '{0}' ", yearMonth);
  1477. var data = await _usersRep._sqlSugar.SqlQueryable<WageYearMonthView>(sql).FirstAsync();
  1478. if (data != null)
  1479. {
  1480. workdays = data.Workdays;
  1481. }
  1482. return workdays;
  1483. }
  1484. }
  1485. }