Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/Email-Staff-Hours'
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/dotnet/HQ.Server/Program.cs
#	src/dotnet/HQ.Server/Services/EmailMessageService.cs
  • Loading branch information
rmaffitsancsoft committed Jan 13, 2025
2 parents fae6731 + 2eb37d0 commit 6a5906b
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 5 deletions.
62 changes: 62 additions & 0 deletions src/dotnet/HQ.Abstractions/Emails/EmployeeHoursEmail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

using HQ.Abstractions.Staff;
using HQ.Abstractions.Times;

namespace HQ.Abstractions.Emails
{
public class EmployeeHoursEmail : NotificationEmail
{
//This will contain staff name and hours
public List<StaffHoursModel> Staff { get; set; } = null!;
public DateOnly Date { get; set; }

public DateOnly PeriodBegin { get; set; }
public DateOnly PeriodEnd { get; set; }

public static new EmployeeHoursEmail Sample = new()
{
Heading = "Staff Hours",
Message = "Staff with red signify they have less working hours than expected",
ButtonLabel = "Open HQ",
ButtonUrl = new Uri("http://hq.localhost:4200/dashboard"),
Date = new DateOnly(2024, 7, 17),
PeriodBegin = new DateOnly(2024, 7, 17),
PeriodEnd = new DateOnly(2024, 7, 23),

Staff = new List<StaffHoursModel>{
new StaffHoursModel{
StaffName = "Amr",
HoursLastWeek= 0,
HoursLastMonth = 0,
HoursThisMonth = 4,
MissingHours = true
},
new StaffHoursModel{
StaffName = "Ryan",
HoursLastWeek= 5,
HoursLastMonth = 6,
HoursThisMonth = 7,
LessThanExpectedHours = true
},
new StaffHoursModel{
StaffName = "Pri",
HoursLastWeek= 8,
HoursLastMonth = 9,
HoursThisMonth = 10
}
}

};
public class StaffHoursModel
{
public decimal HoursLastWeek { get; set; }
public decimal HoursLastMonth { get; set; }
public decimal HoursThisMonth { get; set; }
public string? StaffName { get; set; }
public bool MissingHours { get; set; }
public bool LessThanExpectedHours { get; set; }
public int WorkHours { get; set; }

}
}
}
3 changes: 2 additions & 1 deletion src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ public enum EmailMessage
{
Notification = 1,
RejectTimeEntry = 2,
UpdatedPlanningPoints = 3
UpdatedPlanningPoints = 3,
EmployeeHours = 4
}
37 changes: 37 additions & 0 deletions src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@model HQ.Abstractions.Emails.EmployeeHoursEmail
@{
Layout = "_LayoutEmailNotificationHTML";
}
<mj-text>
<div><strong>Dates:</strong> @Model.PeriodBegin - @Model.PeriodEnd</div>
</mj-text>
<mj-table>
<tr style="white;padding:15px 0;color:white;text-align:center;padding:15px 5px;">
<td style="padding: 0 15px 0 0;font-weight:bold;border-bottom:1px solid white;">Staff</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Last Week</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">This Month</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Last Month</td>
</tr>
@foreach (var staff in Model.Staff)
{
@if (staff.LessThanExpectedHours || staff.MissingHours)
{
<tr style="color:red; text-align:center;">
<td style="padding: 0 15px 0 0;">@staff.StaffName</td>
<td style="padding: 0 15px;">@staff.HoursLastWeek</td>
<td style="padding: 0 15px;">@staff.HoursThisMonth</td>
<td style="padding: 0 15px;">@staff.HoursLastMonth</td>
</tr>
}
else
{
<tr style="color:white; text-align:center;">
<td style="padding: 0 15px 0 0;">@staff.StaffName</td>
<td style="padding: 0 15px;">@staff.HoursLastWeek</td>
<td style="padding: 0 15px;">@staff.HoursThisMonth</td>
<td style="padding: 0 15px;">@staff.HoursLastMonth</td>
</tr>
}
}
</mj-table>
<mj-text></mj-text>
53 changes: 53 additions & 0 deletions src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@model HQ.Abstractions.Emails.EmployeeHoursEmail
@if (!String.IsNullOrEmpty(Model.Heading))
{
<text>@Model.Heading</text>
<text>---</text>
<text></text>
}

@if (!String.IsNullOrEmpty(Model.Message))
{
<text>@Model.Message</text>
<text></text>
}

Dates: <text>@Model.PeriodBegin - @Model.PeriodEnd</text>

<table>
<tr style="white;padding:15px 0;color:white;text-align:center;padding:15px 5px;">
<td style="padding: 0 15px 0 0;font-weight:bold;border-bottom:1px solid white;">Staff</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Last Week</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">This Month</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Last Month</td>
</tr>
@foreach (var staff in @Model.Staff)
{
@if (staff.LessThanExpectedHours || staff.MissingHours)
{

<tr style="color:red; text-align:center;">
<td style="padding: 0 15px 0 0;">@staff.StaffName</td>
<td style="padding: 0 15px;">@staff.HoursLastWeek</td>
<td style="padding: 0 15px;">@staff.HoursThisMonth</td>
<td style="padding: 0 15px;">@staff.HoursLastMonth</td>
</tr>

}
else
{
<tr style="color:white; text-align:center;">
<td style="padding: 0 15px 0 0;">@staff.StaffName</td>
<td style="padding: 0 15px;">@staff.HoursLastWeek</td>
<td style="padding: 0 15px;">@staff.HoursThisMonth</td>
<td style="padding: 0 15px;">@staff.HoursLastMonth</td>
</tr>
}

}

@if (!String.IsNullOrEmpty(Model.ButtonLabel) && Model.ButtonUrl != null)
{
<text>@Model.ButtonLabel: @Model.ButtonUrl</text>
}
</table>
20 changes: 20 additions & 0 deletions src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ public Task<ActionResult> UpdatedPlanningPointsSendEmail([FromForm] string to, C
_emailService.SendEmail(EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, to, "Updated Points Test", System.Net.Mail.MailPriority.Low, null, ct)
.ToActionResult(new HQResultEndpointProfile());

[HttpGet(nameof(StaffHoursText))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/plain")]
public Task<ActionResult> StaffHoursText(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.Text, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct);

[HttpGet(nameof(StaffHoursMJML))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/plain")]
public Task<ActionResult> StaffHoursMJML(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.MJML, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct);

[HttpGet(nameof(StaffHoursHTML))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/html")]
public Task<ActionResult> StaffHoursHTML(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.HTML, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct);

[HttpPost(nameof(StaffHoursSendEmail))]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public Task<ActionResult> StaffHoursSendEmail([FromForm] string to, CancellationToken ct = default) =>
_emailService.SendEmail(EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, to, "Staff Hours Test", System.Net.Mail.MailPriority.Low, null, ct)
.ToActionResult(new HQResultEndpointProfile());
private async Task<ActionResult> GetEmailTemplate<T>(EmailMessageOutput output, EmailMessage emailMessage, T model, CancellationToken ct = default) where T : BaseEmail
{
var request = new GetEmailTemplateV1.Request<T>()
Expand Down
14 changes: 10 additions & 4 deletions src/dotnet/HQ.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,12 @@
Cron.Weekly(DayOfWeek.Monday, 12),
recurringJobOptions);

recurringJobManager.AddOrUpdate<EmailMessageService>(
nameof(EmailMessageService.SendEmployeeHoursEmail),
(t) => t.SendEmployeeHoursEmail(CancellationToken.None),
"15 12 * * 1",
recurringJobOptions);

// Friday morning
recurringJobManager.AddOrUpdate<HolidayServiceV1>(
nameof(HolidayServiceV1.BackgroundAutoGenerateHolidayTimeEntryV1),
Expand All @@ -387,10 +393,10 @@
recurringJobOptions);

recurringJobManager.AddOrUpdate<PointServiceV1>(
nameof(PointServiceV1.BackgroundAutoGenerateVacationPlanningPointsV1),
(t) => t.BackgroundAutoGenerateVacationPlanningPointsV1(CancellationToken.None),
Cron.Weekly(DayOfWeek.Friday, 8),
recurringJobOptions);
nameof(PointServiceV1.BackgroundAutoGenerateVacationPlanningPointsV1),
(t) => t.BackgroundAutoGenerateVacationPlanningPointsV1(CancellationToken.None),
Cron.Weekly(DayOfWeek.Friday, 8),
recurringJobOptions);

recurringJobManager.AddOrUpdate<TimeEntryServiceV1>(
nameof(TimeEntryServiceV1.BackgroundSendTimeEntryReminderEmail),
Expand Down
48 changes: 48 additions & 0 deletions src/dotnet/HQ.Server/Services/EmailMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using HQ.Abstractions.Enumerations;
using HQ.Abstractions.Services;
using HQ.API;
using HQ.Abstractions.Times;
using HQ.Server.Data;
using HQ.Server.Services;

Expand Down Expand Up @@ -353,5 +354,52 @@ public async Task SendUpdatedPlanningPointsEmail(Guid staffId, Guid modifiedBySt
await SendEmail(EmailMessage.UpdatedPlanningPoints, model, staff.Email, "[HQ] Planning Points Updated", MailPriority.High, null, ct);
}
}
public async Task SendEmployeeHoursEmail(CancellationToken ct)
{
var date = DateOnly.FromDateTime(DateTime.UtcNow);

var staff = await _context.Staff
.AsNoTracking()
.Where(t => t.EndDate == null)
.Select(t => new EmployeeHoursEmail.StaffHoursModel()
{
HoursLastWeek = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.LastWeek) && x.Date <= date.GetPeriodEndDate(Period.LastWeek)).Sum(x => x.Hours),
HoursLastMonth = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.LastMonth) && x.Date <= date.GetPeriodEndDate(Period.LastMonth)).Sum(x => x.Hours),
HoursThisMonth = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.Month) && x.Date <= date.GetPeriodEndDate(Period.Month)).Sum(x => x.Hours),
StaffName = t.Name,
WorkHours = t.WorkHours
})
.OrderBy(t => t.HoursLastWeek)
.ToListAsync(ct);
foreach (var employee in staff)
{
employee.MissingHours = employee.HoursLastWeek == 0;
employee.LessThanExpectedHours = employee.HoursLastWeek < employee.WorkHours;
}

var managersToNotify = await _context.Projects
.AsNoTracking()
.Where(t => t.EndDate == null && t.ProjectManager!.Email != null)
.Select(t => t.ProjectManager!.Email)
.Distinct()
.ToListAsync(ct);

var model = new EmployeeHoursEmail()
{
Date = date,
PeriodBegin = date.GetPeriodStartDate(Period.LastWeek),
PeriodEnd = date.GetPeriodEndDate(Period.LastWeek),
ButtonLabel = "Open HQ",
ButtonUrl = _options.CurrentValue.WebUrl,
Staff = staff
};
foreach (var manager in managersToNotify)
{
if (manager != null)
{
await SendEmail(EmailMessage.EmployeeHours, model, manager, "[HQ] Staff Hours", MailPriority.High, null, ct);
}
}
}
}
}

0 comments on commit 6a5906b

Please sign in to comment.