|
|
|
@@ -16,9 +16,9 @@ using System.Threading.Tasks;
|
|
|
|
|
using WorkFlowCheck.BL.DocumentGenerator;
|
|
|
|
|
using WorkFlowCheck.BL.Services.Interfaces;
|
|
|
|
|
using WorkFlowCheck.Common.DataTables;
|
|
|
|
|
using WorkFlowCheck.Common.DTO;
|
|
|
|
|
using WorkFlowCheck.Common.Enums;
|
|
|
|
|
using WorkFlowCheck.Common.Helper;
|
|
|
|
|
using WorkFlowCheck.Common.DTO;
|
|
|
|
|
using WorkFlowCheck.Common.Enums;
|
|
|
|
|
using WorkFlowCheck.Common.Helper;
|
|
|
|
|
using WorkFlowCheck.DL;
|
|
|
|
|
using WorkFlowCheck.DL.Entities;
|
|
|
|
|
|
|
|
|
@@ -35,177 +35,177 @@ namespace WorkFlowCheck.BL.Services
|
|
|
|
|
INumberGeneratorService numberGeneratorService,
|
|
|
|
|
IMessageService messageService,
|
|
|
|
|
IUserService userService) : base(dbContext, mapper)
|
|
|
|
|
{
|
|
|
|
|
_numberGeneratorService = numberGeneratorService;
|
|
|
|
|
_messageService = messageService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<DashboardDTO> GetDashboardInfoAsync()
|
|
|
|
|
{
|
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
|
var currentMonthStart = new DateTime(now.Year, now.Month, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
|
var nextMonthStart = currentMonthStart.AddMonths(1);
|
|
|
|
|
var previousMonthStart = currentMonthStart.AddMonths(-1);
|
|
|
|
|
var sixMonthStart = currentMonthStart.AddMonths(-5);
|
|
|
|
|
var fortyEightHoursAgo = now.AddHours(-48);
|
|
|
|
|
|
|
|
|
|
var headers = _dbContext.CheckListHeaders.AsNoTracking();
|
|
|
|
|
|
|
|
|
|
var statusCounts = await headers
|
|
|
|
|
.GroupBy(x => x.CheckStatus)
|
|
|
|
|
.Select(x => new { Status = x.Key, Count = x.Count() })
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var monthlyRaw = await headers
|
|
|
|
|
.Where(x => x.CreatedAt >= sixMonthStart && x.CreatedAt < nextMonthStart)
|
|
|
|
|
.GroupBy(x => new { x.CreatedAt.Year, x.CreatedAt.Month })
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
x.Key.Year,
|
|
|
|
|
x.Key.Month,
|
|
|
|
|
Total = x.Count(),
|
|
|
|
|
Approved = x.Count(y => y.CheckStatus == CheckStatus.Signed || y.CheckStatus == CheckStatus.Closed)
|
|
|
|
|
})
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var topUsers = await headers
|
|
|
|
|
.Where(x => x.CreatedAt >= currentMonthStart && x.CreatedAt < nextMonthStart)
|
|
|
|
|
.GroupBy(x => new { x.UserId, x.User.LastName, x.User.FirstName })
|
|
|
|
|
.Select(x => new DashboardUserItemDTO
|
|
|
|
|
{
|
|
|
|
|
Name = (x.Key.LastName + " " + x.Key.FirstName).Trim(),
|
|
|
|
|
Count = x.Count()
|
|
|
|
|
})
|
|
|
|
|
.OrderByDescending(x => x.Count)
|
|
|
|
|
.ThenBy(x => x.Name)
|
|
|
|
|
.Take(5)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var recentItems = await headers
|
|
|
|
|
.OrderByDescending(x => x.LastModAt)
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
x.Id,
|
|
|
|
|
x.DocumentNumber,
|
|
|
|
|
x.ShortName,
|
|
|
|
|
x.User.LastName,
|
|
|
|
|
x.User.FirstName,
|
|
|
|
|
x.LastModAt,
|
|
|
|
|
x.CheckStatus
|
|
|
|
|
})
|
|
|
|
|
.Take(8)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var delayedApprovalCount = await headers.CountAsync(x =>
|
|
|
|
|
x.CheckStatus == CheckStatus.Sent && x.LastModAt < fortyEightHoursAgo);
|
|
|
|
|
|
|
|
|
|
var blockedCount = statusCounts.FirstOrDefault(x => x.Status == CheckStatus.Blocked)?.Count ?? 0;
|
|
|
|
|
var currentMonthCount = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == currentMonthStart.Year && x.Month == currentMonthStart.Month)?.Total ?? 0;
|
|
|
|
|
var previousMonthCount = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == previousMonthStart.Year && x.Month == previousMonthStart.Month)?.Total ?? 0;
|
|
|
|
|
var approvedThisMonth = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == currentMonthStart.Year && x.Month == currentMonthStart.Month)?.Approved ?? 0;
|
|
|
|
|
|
|
|
|
|
var culture = System.Globalization.CultureInfo.GetCultureInfo("hu-HU");
|
|
|
|
|
var result = new DashboardDTO
|
|
|
|
|
{
|
|
|
|
|
GeneratedAt = now,
|
|
|
|
|
TotalCheckLists = statusCounts.Sum(x => x.Count),
|
|
|
|
|
CheckListsThisMonth = currentMonthCount,
|
|
|
|
|
MonthlyChangePercent = previousMonthCount == 0
|
|
|
|
|
? (currentMonthCount > 0 ? 100 : 0)
|
|
|
|
|
: Math.Round((currentMonthCount - previousMonthCount) * 100m / previousMonthCount, 1),
|
|
|
|
|
OpenAndInProgressCount = statusCounts
|
|
|
|
|
.Where(x => x.Status == CheckStatus.Open || x.Status == CheckStatus.InProgress)
|
|
|
|
|
.Sum(x => x.Count),
|
|
|
|
|
AwaitingApprovalCount = statusCounts.FirstOrDefault(x => x.Status == CheckStatus.Sent)?.Count ?? 0,
|
|
|
|
|
BlockedCount = blockedCount,
|
|
|
|
|
ApprovedThisMonthCount = approvedThisMonth,
|
|
|
|
|
ApprovalRate = currentMonthCount == 0 ? 0 : Math.Round(approvedThisMonth * 100m / currentMonthCount, 1),
|
|
|
|
|
ActiveUserCount = await _dbContext.Users.AsNoTracking().CountAsync(x => x.Active),
|
|
|
|
|
TemplateCount = await _dbContext.CheckListTemplateHeaders.AsNoTracking().CountAsync(),
|
|
|
|
|
TopUsers = topUsers,
|
|
|
|
|
AttentionItems = new List<DashboardAttentionItemDTO>(),
|
|
|
|
|
RecentCheckLists = recentItems.Select(x => new DashboardRecentCheckListDTO
|
|
|
|
|
{
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
DocumentNumber = x.DocumentNumber,
|
|
|
|
|
ShortName = x.ShortName,
|
|
|
|
|
Responsible = (x.LastName + " " + x.FirstName).Trim(),
|
|
|
|
|
ModifiedAt = x.LastModAt,
|
|
|
|
|
Status = x.CheckStatus,
|
|
|
|
|
StatusName = DisplayNameHelper.GetEnumDisplayName(x.CheckStatus)
|
|
|
|
|
}).ToList()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < 6; i++)
|
|
|
|
|
{
|
|
|
|
|
var month = sixMonthStart.AddMonths(i);
|
|
|
|
|
var value = monthlyRaw.FirstOrDefault(x => x.Year == month.Year && x.Month == month.Month);
|
|
|
|
|
result.MonthlyItems.Add(new DashboardMonthlyItemDTO
|
|
|
|
|
{
|
|
|
|
|
Month = month.ToString("MMM", culture).TrimEnd('.'),
|
|
|
|
|
Total = value?.Total ?? 0,
|
|
|
|
|
Approved = value?.Approved ?? 0
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var status in Enum.GetValues<CheckStatus>())
|
|
|
|
|
{
|
|
|
|
|
result.StatusItems.Add(new DashboardStatusItemDTO
|
|
|
|
|
{
|
|
|
|
|
Status = status,
|
|
|
|
|
Name = DisplayNameHelper.GetEnumDisplayName(status),
|
|
|
|
|
Count = statusCounts.FirstOrDefault(x => x.Status == status)?.Count ?? 0,
|
|
|
|
|
Color = GetDashboardStatusColor(status)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (delayedApprovalCount > 0)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{delayedApprovalCount} munkalap több mint 48 órája jóváhagyásra vár",
|
|
|
|
|
Description = "Érdemes ellenőrizni a beküldött munkalapokat.",
|
|
|
|
|
IsCritical = true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (blockedCount > 0)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{blockedCount} blokkolt munkalap",
|
|
|
|
|
Description = "A folytatásuk felhasználói beavatkozást igényel.",
|
|
|
|
|
IsCritical = true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (result.AwaitingApprovalCount > delayedApprovalCount)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{result.AwaitingApprovalCount - delayedApprovalCount} friss beküldött munkalap",
|
|
|
|
|
Description = "Jóváhagyásra váró, 48 óránál frissebb tételek.",
|
|
|
|
|
IsCritical = false
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetDashboardStatusColor(CheckStatus status) => status switch
|
|
|
|
|
{
|
|
|
|
|
CheckStatus.Open => "#64748b",
|
|
|
|
|
CheckStatus.InProgress => "#e28a17",
|
|
|
|
|
CheckStatus.Blocked => "#dc2626",
|
|
|
|
|
CheckStatus.Storno => "#7c3aed",
|
|
|
|
|
CheckStatus.Sent => "#38bdf8",
|
|
|
|
|
CheckStatus.Signed => "#16875b",
|
|
|
|
|
CheckStatus.Closed => "#123c69",
|
|
|
|
|
_ => "#94a3b8"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public async Task<CheckListHeaderDTO> GetCheckListHeaderAsync(int Id)
|
|
|
|
|
{
|
|
|
|
|
_numberGeneratorService = numberGeneratorService;
|
|
|
|
|
_messageService = messageService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<DashboardDTO> GetDashboardInfoAsync()
|
|
|
|
|
{
|
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
|
var currentMonthStart = new DateTime(now.Year, now.Month, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
|
var nextMonthStart = currentMonthStart.AddMonths(1);
|
|
|
|
|
var previousMonthStart = currentMonthStart.AddMonths(-1);
|
|
|
|
|
var sixMonthStart = currentMonthStart.AddMonths(-5);
|
|
|
|
|
var fortyEightHoursAgo = now.AddHours(-48);
|
|
|
|
|
|
|
|
|
|
var headers = _dbContext.CheckListHeaders.AsNoTracking();
|
|
|
|
|
|
|
|
|
|
var statusCounts = await headers
|
|
|
|
|
.GroupBy(x => x.CheckStatus)
|
|
|
|
|
.Select(x => new { Status = x.Key, Count = x.Count() })
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var monthlyRaw = await headers
|
|
|
|
|
.Where(x => x.CreatedAt >= sixMonthStart && x.CreatedAt < nextMonthStart)
|
|
|
|
|
.GroupBy(x => new { x.CreatedAt.Year, x.CreatedAt.Month })
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
x.Key.Year,
|
|
|
|
|
x.Key.Month,
|
|
|
|
|
Total = x.Count(),
|
|
|
|
|
Approved = x.Count(y => y.CheckStatus == CheckStatus.Signed || y.CheckStatus == CheckStatus.Closed)
|
|
|
|
|
})
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var topUsers = await headers
|
|
|
|
|
.Where(x => x.CreatedAt >= currentMonthStart && x.CreatedAt < nextMonthStart)
|
|
|
|
|
.GroupBy(x => new { x.UserId, x.User.LastName, x.User.FirstName })
|
|
|
|
|
.Select(x => new DashboardUserItemDTO
|
|
|
|
|
{
|
|
|
|
|
Name = (x.Key.LastName + " " + x.Key.FirstName).Trim(),
|
|
|
|
|
Count = x.Count()
|
|
|
|
|
})
|
|
|
|
|
.OrderByDescending(x => x.Count)
|
|
|
|
|
.ThenBy(x => x.Name)
|
|
|
|
|
.Take(5)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var recentItems = await headers
|
|
|
|
|
.OrderByDescending(x => x.LastModAt)
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
x.Id,
|
|
|
|
|
x.DocumentNumber,
|
|
|
|
|
x.ShortName,
|
|
|
|
|
x.User.LastName,
|
|
|
|
|
x.User.FirstName,
|
|
|
|
|
x.LastModAt,
|
|
|
|
|
x.CheckStatus
|
|
|
|
|
})
|
|
|
|
|
.Take(8)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var delayedApprovalCount = await headers.CountAsync(x =>
|
|
|
|
|
x.CheckStatus == CheckStatus.Sent && x.LastModAt < fortyEightHoursAgo);
|
|
|
|
|
|
|
|
|
|
var blockedCount = statusCounts.FirstOrDefault(x => x.Status == CheckStatus.Blocked)?.Count ?? 0;
|
|
|
|
|
var currentMonthCount = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == currentMonthStart.Year && x.Month == currentMonthStart.Month)?.Total ?? 0;
|
|
|
|
|
var previousMonthCount = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == previousMonthStart.Year && x.Month == previousMonthStart.Month)?.Total ?? 0;
|
|
|
|
|
var approvedThisMonth = monthlyRaw
|
|
|
|
|
.FirstOrDefault(x => x.Year == currentMonthStart.Year && x.Month == currentMonthStart.Month)?.Approved ?? 0;
|
|
|
|
|
|
|
|
|
|
var culture = System.Globalization.CultureInfo.GetCultureInfo("hu-HU");
|
|
|
|
|
var result = new DashboardDTO
|
|
|
|
|
{
|
|
|
|
|
GeneratedAt = now,
|
|
|
|
|
TotalCheckLists = statusCounts.Sum(x => x.Count),
|
|
|
|
|
CheckListsThisMonth = currentMonthCount,
|
|
|
|
|
MonthlyChangePercent = previousMonthCount == 0
|
|
|
|
|
? (currentMonthCount > 0 ? 100 : 0)
|
|
|
|
|
: Math.Round((currentMonthCount - previousMonthCount) * 100m / previousMonthCount, 1),
|
|
|
|
|
OpenAndInProgressCount = statusCounts
|
|
|
|
|
.Where(x => x.Status == CheckStatus.Open || x.Status == CheckStatus.InProgress)
|
|
|
|
|
.Sum(x => x.Count),
|
|
|
|
|
AwaitingApprovalCount = statusCounts.FirstOrDefault(x => x.Status == CheckStatus.Sent)?.Count ?? 0,
|
|
|
|
|
BlockedCount = blockedCount,
|
|
|
|
|
ApprovedThisMonthCount = approvedThisMonth,
|
|
|
|
|
ApprovalRate = currentMonthCount == 0 ? 0 : Math.Round(approvedThisMonth * 100m / currentMonthCount, 1),
|
|
|
|
|
ActiveUserCount = await _dbContext.Users.AsNoTracking().CountAsync(x => x.Active),
|
|
|
|
|
TemplateCount = await _dbContext.CheckListTemplateHeaders.AsNoTracking().CountAsync(),
|
|
|
|
|
TopUsers = topUsers,
|
|
|
|
|
AttentionItems = new List<DashboardAttentionItemDTO>(),
|
|
|
|
|
RecentCheckLists = recentItems.Select(x => new DashboardRecentCheckListDTO
|
|
|
|
|
{
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
DocumentNumber = x.DocumentNumber,
|
|
|
|
|
ShortName = x.ShortName,
|
|
|
|
|
Responsible = (x.LastName + " " + x.FirstName).Trim(),
|
|
|
|
|
ModifiedAt = x.LastModAt,
|
|
|
|
|
Status = x.CheckStatus,
|
|
|
|
|
StatusName = DisplayNameHelper.GetEnumDisplayName(x.CheckStatus)
|
|
|
|
|
}).ToList()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < 6; i++)
|
|
|
|
|
{
|
|
|
|
|
var month = sixMonthStart.AddMonths(i);
|
|
|
|
|
var value = monthlyRaw.FirstOrDefault(x => x.Year == month.Year && x.Month == month.Month);
|
|
|
|
|
result.MonthlyItems.Add(new DashboardMonthlyItemDTO
|
|
|
|
|
{
|
|
|
|
|
Month = month.ToString("MMM", culture).TrimEnd('.'),
|
|
|
|
|
Total = value?.Total ?? 0,
|
|
|
|
|
Approved = value?.Approved ?? 0
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var status in Enum.GetValues<CheckStatus>())
|
|
|
|
|
{
|
|
|
|
|
result.StatusItems.Add(new DashboardStatusItemDTO
|
|
|
|
|
{
|
|
|
|
|
Status = status,
|
|
|
|
|
Name = DisplayNameHelper.GetEnumDisplayName(status),
|
|
|
|
|
Count = statusCounts.FirstOrDefault(x => x.Status == status)?.Count ?? 0,
|
|
|
|
|
Color = GetDashboardStatusColor(status)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (delayedApprovalCount > 0)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{delayedApprovalCount} munkalap több mint 48 órája jóváhagyásra vár",
|
|
|
|
|
Description = "Érdemes ellenőrizni a beküldött munkalapokat.",
|
|
|
|
|
IsCritical = true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (blockedCount > 0)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{blockedCount} blokkolt munkalap",
|
|
|
|
|
Description = "A folytatásuk felhasználói beavatkozást igényel.",
|
|
|
|
|
IsCritical = true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (result.AwaitingApprovalCount > delayedApprovalCount)
|
|
|
|
|
{
|
|
|
|
|
result.AttentionItems.Add(new DashboardAttentionItemDTO
|
|
|
|
|
{
|
|
|
|
|
Title = $"{result.AwaitingApprovalCount - delayedApprovalCount} friss beküldött munkalap",
|
|
|
|
|
Description = "Jóváhagyásra váró, 48 óránál frissebb tételek.",
|
|
|
|
|
IsCritical = false
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetDashboardStatusColor(CheckStatus status) => status switch
|
|
|
|
|
{
|
|
|
|
|
CheckStatus.Open => "#64748b",
|
|
|
|
|
CheckStatus.InProgress => "#e28a17",
|
|
|
|
|
CheckStatus.Blocked => "#dc2626",
|
|
|
|
|
CheckStatus.Storno => "#7c3aed",
|
|
|
|
|
CheckStatus.Sent => "#38bdf8",
|
|
|
|
|
CheckStatus.Signed => "#16875b",
|
|
|
|
|
CheckStatus.Closed => "#123c69",
|
|
|
|
|
_ => "#94a3b8"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public async Task<CheckListHeaderDTO> GetCheckListHeaderAsync(int Id)
|
|
|
|
|
{
|
|
|
|
|
var retVal = new CheckListHeaderDTO()
|
|
|
|
|
{
|
|
|
|
|