using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NCC.Extend.Entitys.Enum; using NCC.Extend.Entitys.lq_attendance_group; using NCC.Extend.Entitys.lq_attendance_record; using NCC.Extend.Entitys.lq_attendance_summary; using NCC.Extend.Entitys.lq_mdxx; using NCC.System.Entitys.Permission; using SqlSugar; namespace NCC.Extend { /// /// 月度考勤报表 / 考勤汇总共用的「当月在职」人员行(与花名册、Skill historical-on-job-inference 一致)。 /// public sealed class AttendanceMonthEligibleUserLine { public string UserId { get; set; } public string EmployeeName { get; set; } public string StoreId { get; set; } public string StoreName { get; set; } public string AttendanceGroupId { get; set; } public string GroupName { get; set; } } /// /// 按自然月查询「当月在职口径」人员(与 LqEmployeeRosterService、月度考勤矩阵相同 JOIN 条件)。 /// /// /// 规则:该月存在有效考勤汇总且 F_EmployeeStatus = 1 则入选;无汇总且非未来月时按主档 /// 入职不晚于月末、离职为空或≥月初;未来月仅认汇总(仅第一分支成立)。 /// public static class AttendanceMonthOnJobUserQuery { /// /// 查询指定年月的在职口径人员列表。 /// /// 数据库客户端 /// 年 /// 月 /// 该月 1 日 00:00:00 /// 该月最后一天 23:59:59 /// 是否为未来月(相对当前系统日期) /// 可选,姓名/账号/门店/分组模糊 /// 可选考勤分组 public static async Task> QueryEligibleUsersAsync( ISqlSugarClient db, int year, int month, DateTime monthStart, DateTime monthLast, bool isFutureMonth, string keyword, string attendanceGroupId) { var kw = keyword?.Trim(); var lines = await db.Queryable( (u, s, g, summ) => new JoinQueryInfos( JoinType.Left, s.Id == u.Mdid, JoinType.Left, g.Id == s.AttendanceGroupId, JoinType.Left, summ.UserId == u.Id && summ.Year == year && summ.Month == month && summ.IsEffective == StatusEnum.有效.GetHashCode())) .Where((u, s, g, summ) => u.DeleteMark == null) .Where((u, s, g, summ) => (!SqlFunc.IsNullOrEmpty(summ.Id) && summ.EmployeeStatus == 1) || (!isFutureMonth && SqlFunc.IsNullOrEmpty(summ.Id) && u.EntryDate != null && u.EntryDate <= monthLast && (u.LeaveDate == null || u.LeaveDate >= monthStart))) .WhereIF(!string.IsNullOrWhiteSpace(attendanceGroupId), (u, s, g, summ) => s.AttendanceGroupId == attendanceGroupId) .WhereIF(!string.IsNullOrWhiteSpace(kw), (u, s, g, summ) => SqlFunc.Contains(SqlFunc.IsNull(u.RealName, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(u.Account, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(s.Dm, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(g.GroupName, ""), kw)) .Select((u, s, g, summ) => new AttendanceMonthEligibleUserLine { UserId = u.Id, EmployeeName = u.RealName, StoreId = u.Mdid, StoreName = s.Dm, AttendanceGroupId = s.AttendanceGroupId, GroupName = g.GroupName }) .ToListAsync(); return lines .OrderBy(x => x.StoreName ?? "") .ThenBy(x => x.GroupName ?? "") .ThenBy(x => x.EmployeeName ?? "") .ToList(); } /// /// 月度矩阵展示用:在「在职口径」名单之外,把该月已在 lq_attendance_record 中有有效行, /// 但未被 选中的员工补进列表(常见:主档入职日为未填、与花名册口径不一致等), /// 避免定时补 INSERT 后管理端仍显示无数据。 /// public static async Task> MergeWithRecordOnlyHoldersAsync( ISqlSugarClient db, DateTime monthStart, DateTime monthEnd, IReadOnlyCollection baseEligible, string keyword, string attendanceGroupId) { var baseIds = baseEligible .Select(x => x.UserId) .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct() .ToList(); var orphanIds = await db.Queryable() .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) .Where(x => x.AttendanceDate >= monthStart && x.AttendanceDate < monthEnd) .Where(x => !string.IsNullOrWhiteSpace(x.UserId)) .Where(baseIds.Count > 0 ? (x => !baseIds.Contains(x.UserId)) : (x => true)) .Select(x => x.UserId) .Distinct() .ToListAsync(); if (orphanIds == null || orphanIds.Count == 0) { return baseEligible.ToList(); } var kw = keyword?.Trim(); var extra = await db.Queryable( (u, s, g) => new JoinQueryInfos( JoinType.Left, s.Id == u.Mdid, JoinType.Left, g.Id == s.AttendanceGroupId)) .Where((u, s, g) => u.DeleteMark == null) .Where((u, s, g) => orphanIds.Contains(u.Id)) .WhereIF(!string.IsNullOrWhiteSpace(attendanceGroupId), (u, s, g) => s.AttendanceGroupId == attendanceGroupId) .WhereIF(!string.IsNullOrWhiteSpace(kw), (u, s, g) => SqlFunc.Contains(SqlFunc.IsNull(u.RealName, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(u.Account, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(s.Dm, ""), kw) || SqlFunc.Contains(SqlFunc.IsNull(g.GroupName, ""), kw)) .Select((u, s, g) => new AttendanceMonthEligibleUserLine { UserId = u.Id, EmployeeName = u.RealName, StoreId = u.Mdid, StoreName = s.Dm, AttendanceGroupId = s.AttendanceGroupId, GroupName = g.GroupName }) .ToListAsync(); return baseEligible .Concat(extra) .OrderBy(x => x.StoreName ?? "") .ThenBy(x => x.GroupName ?? "") .ThenBy(x => x.EmployeeName ?? "") .ToList(); } } }