From 7e70297e7c8650df207792b512dff5527a805d10 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Fri, 23 May 2025 10:37:58 +0530 Subject: [PATCH 1/2] Added an Email service and API to send an Email of daily project progress report --- .../ViewModels/Report/PerformedAttendance.cs | 12 + .../ViewModels/Report/PerformedTask.cs | 13 + .../Report/ProjectStatisticReport.cs | 26 + Marco.Pms.Model/ViewModels/Report/TaskTeam.cs | 8 + .../ViewModels/Report/TeamOnSite.cs | 8 + .../Controllers/MarketController.cs | 2 +- .../Controllers/ReportController.cs | 218 +++ .../EmailTemplates/project-report.html | 1194 ++++++++--------- Marco.Pms.Services/Service/EmailSender.cs | 91 +- Marco.Pms.Services/Service/IEmailSender.cs | 2 + .../appsettings.Development.json | 16 +- .../appsettings.Production.json | 7 +- 12 files changed, 947 insertions(+), 650 deletions(-) create mode 100644 Marco.Pms.Model/ViewModels/Report/PerformedAttendance.cs create mode 100644 Marco.Pms.Model/ViewModels/Report/PerformedTask.cs create mode 100644 Marco.Pms.Model/ViewModels/Report/ProjectStatisticReport.cs create mode 100644 Marco.Pms.Model/ViewModels/Report/TaskTeam.cs create mode 100644 Marco.Pms.Model/ViewModels/Report/TeamOnSite.cs create mode 100644 Marco.Pms.Services/Controllers/ReportController.cs diff --git a/Marco.Pms.Model/ViewModels/Report/PerformedAttendance.cs b/Marco.Pms.Model/ViewModels/Report/PerformedAttendance.cs new file mode 100644 index 0000000..8d6344b --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Report/PerformedAttendance.cs @@ -0,0 +1,12 @@ +namespace Marco.Pms.Model.ViewModels.Report +{ + public class PerformedAttendance + { + public string? Name { get; set; } + public string? RoleName { get; set; } + public DateTime InTime { get; set; } + public DateTime? OutTime { get; set; } + public string? Comment { get; set; } + + } +} diff --git a/Marco.Pms.Model/ViewModels/Report/PerformedTask.cs b/Marco.Pms.Model/ViewModels/Report/PerformedTask.cs new file mode 100644 index 0000000..fa47722 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Report/PerformedTask.cs @@ -0,0 +1,13 @@ +namespace Marco.Pms.Model.ViewModels.Report +{ + public class PerformedTask + { + public required string Activity { get; set; } + public required string Location { get; set; } + public double AssignedToday { get; set; } + public double Pending { get; set; } + public double CompletedToday { get; set; } + public List Team { get; set; } = new List(); + public string? Comment { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/Report/ProjectStatisticReport.cs b/Marco.Pms.Model/ViewModels/Report/ProjectStatisticReport.cs new file mode 100644 index 0000000..c69d27f --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Report/ProjectStatisticReport.cs @@ -0,0 +1,26 @@ +namespace Marco.Pms.Model.ViewModels.Report +{ + public class ProjectStatisticReport + { + public DateTime Date { get; set; } + public required string ProjectName { get; set; } + public required string TimeStamp { get; set; } + public int TodaysAttendances { get; set; } + public int TotalEmployees { get; set; } + public int RegularizationPending { get; set; } + public int CheckoutPending { get; set; } + public double TotalPlannedWork { get; set; } + public double TotalCompletedWork { get; set; } + public double TotalPlannedTask { get; set; } + public double TotalCompletedTask { get; set; } + public double CompletionStatus { get; set; } + public int ReportPending { get; set; } + public int TodaysAssignTasks { get; set; } + public List TeamOnSite { get; set; } = new List(); + public List PerformedTasks { get; set; } = new List(); + public List PerformedAttendance { get; set; } = new List(); + + + + } +} diff --git a/Marco.Pms.Model/ViewModels/Report/TaskTeam.cs b/Marco.Pms.Model/ViewModels/Report/TaskTeam.cs new file mode 100644 index 0000000..fccbce0 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Report/TaskTeam.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.ViewModels.Report +{ + public class TaskTeam + { + public string? Name { get; set; } + public string? RoleName { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/Report/TeamOnSite.cs b/Marco.Pms.Model/ViewModels/Report/TeamOnSite.cs new file mode 100644 index 0000000..72da13c --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Report/TeamOnSite.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.ViewModels.Report +{ + public class TeamOnSite + { + public string? RoleName { get; set; } + public int NumberofEmployees { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/MarketController.cs b/Marco.Pms.Services/Controllers/MarketController.cs index 670176c..df89a03 100644 --- a/Marco.Pms.Services/Controllers/MarketController.cs +++ b/Marco.Pms.Services/Controllers/MarketController.cs @@ -48,7 +48,7 @@ namespace Marco.Pms.Services.Controllers if (industry != null && industry.Name != null) { InquiryEmailObject inquiryEmailObject = inquiryDto.ToInquiryEmailObjectFromInquiriesDto(industry.Name); - string emails = _configuration["MaliingList:RequestDemoRecivers"] ?? ""; + string emails = _configuration["MailingList:RequestDemoReceivers"] ?? ""; List result = emails .Split(';', StringSplitOptions.RemoveEmptyEntries) .Select(item => item.Trim()) diff --git a/Marco.Pms.Services/Controllers/ReportController.cs b/Marco.Pms.Services/Controllers/ReportController.cs new file mode 100644 index 0000000..b6238ee --- /dev/null +++ b/Marco.Pms.Services/Controllers/ReportController.cs @@ -0,0 +1,218 @@ +using System.Globalization; +using Marco.Pms.DataAccess.Data; +using Marco.Pms.Model.Dtos.Attendance; +using Marco.Pms.Model.Projects; +using Marco.Pms.Model.Roles; +using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Report; +using MarcoBMS.Services.Helpers; +using MarcoBMS.Services.Service; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using MongoDB.Driver; + +namespace Marco.Pms.Services.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class ReportController : ControllerBase + { + private readonly ApplicationDbContext _context; + private readonly IEmailSender _emailSender; + private readonly IConfiguration _configuration; + private readonly ILoggingService _logger; + private readonly UserHelper _userHelper; + public ReportController(ApplicationDbContext context, IEmailSender emailSender, IConfiguration configuration, ILoggingService logger, UserHelper userHelper) + { + _context = context; + _emailSender = emailSender; + _configuration = configuration; + _logger = logger; + _userHelper = userHelper; + } + + [HttpGet("project-statistics/{id}")] + public async Task GetProjectStatistics(Guid id, [FromQuery] string? date) + { + DateTime reportDate = DateTime.UtcNow; + if (date != null && DateTime.TryParse(date, out reportDate) == false) + { + _logger.LogError("User sent Invalid from Date while featching project report"); + return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); + } + if (id == Guid.Empty) + { + _logger.LogError("Provided empty project ID while fetching project report"); + return BadRequest(ApiResponse.ErrorResponse("Provided empty ProjectID", "Provided empty ProjectID", 400)); + } + Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == id); + if (project == null) + { + _logger.LogWarning("User attempted to fetch project progress of project ID {ProjectId} but not found in database", id); + return NotFound(ApiResponse.ErrorResponse("Project not found", "Project not found", 404)); + } + ProjectStatisticReport statisticReport = new ProjectStatisticReport + { + Date = reportDate, + ProjectName = project.Name ?? "", + TimeStamp = DateTime.Now.ToString("dd-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture) + }; + var projectAllocations = await _context.ProjectAllocations.Include(p => p.Employee).Where(p => p.ProjectId == project.Id && p.IsActive).ToListAsync(); + var assignedEmployeeIds = projectAllocations.Select(p => p.EmployeeId).ToList(); + var attendances = await _context.Attendes.AsNoTracking().Where(a => a.ProjectID == project.Id && a.InTime != null && a.InTime.Value.Date == reportDate.Date).ToListAsync(); + var checkedInEmployeeIds = attendances.Select(p => p.EmployeeID).Distinct().ToList(); + var checkedOutPendingEmployeeIds = attendances.Where(a => a.ProjectID == project.Id && a.InTime != null && a.InTime.Value.Date == reportDate.Date && a.OutTime == null).Select(p => p.EmployeeID).Distinct().ToList(); + var regularizationEmployeeIds = attendances.Where(a => a.ProjectID == project.Id && a.InTime != null && a.InTime.Value.Date == reportDate.Date && a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE).Select(p => p.EmployeeID).Distinct().ToList(); + + var buildings = await _context.Buildings.Where(b => b.ProjectId == project.Id).ToListAsync(); + var buildingIds = buildings.Select(b => b.Id).Distinct().ToList(); + + var floors = await _context.Floor.Where(f => buildingIds.Contains(f.BuildingId)).ToListAsync(); + var floorIds = floors.Select(f => f.Id).Distinct().ToList(); + + var areas = await _context.WorkAreas.Where(a => floorIds.Contains(a.FloorId)).ToListAsync(); + var areaIds = areas.Select(a => a.Id).Distinct().ToList(); + + var workItems = await _context.WorkItems.Include(i => i.ActivityMaster).Where(i => areaIds.Contains(i.WorkAreaId)).ToListAsync(); + var itemIds = workItems.Select(i => i.Id).Distinct().ToList(); + + var tasks = await _context.TaskAllocations.Where(t => itemIds.Contains(t.WorkItemId)).ToListAsync(); + var taskIds = tasks.Select(t => t.Id).Distinct().ToList(); + + var taskMembers = await _context.TaskMembers.Include(m => m.Employee).Where(m => taskIds.Contains(m.TaskAllocationId)).ToListAsync(); + + double totalPlannedWork = 0; + double totalCompletedWork = 0; + foreach (var items in workItems) + { + totalPlannedWork += items.PlannedWork; + totalCompletedWork += items.CompletedWork; + } + + var todayAssigned = tasks.Where(t => t.AssignmentDate.Date == reportDate.Date).ToList(); + var reportPending = tasks.Where(t => t.ReportedDate == null).ToList(); + + double totalPlannedTask = 0; + double totalCompletedTask = 0; + foreach (var items in todayAssigned) + { + totalPlannedTask += items.PlannedTask; + totalCompletedTask += items.CompletedTask; + } + + var jobRoles = await _context.JobRoles.Where(r => r.TenantId == project.TenantId).ToListAsync(); + List teamOnSite = new List(); + foreach (var role in jobRoles) + { + int numberOfEmployees = 0; + var roleassigned = projectAllocations.Where(p => p.JobRoleId == role.Id && checkedInEmployeeIds.Contains(p.EmployeeId)).ToList(); + if (roleassigned.Count > 0) + { + numberOfEmployees = roleassigned.Count; + } + TeamOnSite team = new TeamOnSite + { + RoleName = role.Name, + NumberofEmployees = numberOfEmployees, + }; + teamOnSite.Add(team); + } + List performedTasks = new List(); + var todaysTask = tasks.Where(t => t.AssignmentDate.Date == reportDate.Date).ToList(); + foreach (var task in todaysTask) + { + WorkItem workItem = workItems.FirstOrDefault(i => i.Id == task.WorkItemId) ?? new WorkItem(); + string activityName = (workItem.ActivityMaster != null ? workItem.ActivityMaster.ActivityName : "") ?? ""; + + WorkArea workArea = areas.FirstOrDefault(a => a.Id == workItem.WorkAreaId) ?? new WorkArea(); + string areaName = workArea.AreaName ?? ""; + + Floor floor = floors.FirstOrDefault(f => f.Id == workArea.FloorId) ?? new Floor(); + string floorName = floor.FloorName ?? ""; + + Building building = buildings.FirstOrDefault(b => b.Id == floor.BuildingId) ?? new Building(); + string buildingName = building.Name ?? ""; + + string location = $"{buildingName} > {floorName}
{floorName}-{areaName}"; + double pending = workItem.PlannedWork - workItem.CompletedWork; + PerformedTask performedTask = new PerformedTask + { + Activity = activityName, + Location = location, + AssignedToday = task.PlannedTask, + Pending = pending, + CompletedToday = task.CompletedTask, + Comment = task.Description + }; + + var taskTeams = taskMembers.Where(m => m.TaskAllocationId == task.Id).ToList(); + List Team = new List(); + foreach (var team in taskTeams) + { + string firstName = (team.Employee != null ? team.Employee.FirstName : "") ?? ""; + string lastName = (team.Employee != null ? team.Employee.LastName : "") ?? ""; + string name = $"{firstName} {lastName}"; + + JobRole role = jobRoles.FirstOrDefault(r => r.Id == (team.Employee != null ? team.Employee.JobRoleId : Guid.Empty)) ?? new JobRole(); + string roleName = role.Name ?? ""; + + TaskTeam taskTeam = new TaskTeam + { + Name = name, + RoleName = roleName, + }; + Team.Add(taskTeam); + } + performedTask.Team = Team; + performedTasks.Add(performedTask); + } + + List performedAttendances = new List(); + foreach (var attendance in attendances) + { + ProjectAllocation projectAllocation = projectAllocations.FirstOrDefault(p => p.EmployeeId == attendance.EmployeeID) ?? new ProjectAllocation(); + JobRole role = jobRoles.FirstOrDefault(r => r.Id == projectAllocation.JobRoleId) ?? new JobRole(); + + string firstName = (projectAllocation.Employee != null ? projectAllocation.Employee.FirstName : "") ?? ""; + string lastName = (projectAllocation.Employee != null ? projectAllocation.Employee.LastName : "") ?? ""; + string name = $"{firstName} {lastName}"; + PerformedAttendance performedAttendance = new PerformedAttendance + { + Name = name, + RoleName = role.Name ?? "", + InTime = attendance.InTime ?? DateTime.UtcNow, + OutTime = attendance.OutTime, + Comment = attendance.Comment + }; + performedAttendances.Add(performedAttendance); + } + + statisticReport.TodaysAttendances = checkedInEmployeeIds.Count; + statisticReport.TotalEmployees = assignedEmployeeIds.Count; + statisticReport.RegularizationPending = regularizationEmployeeIds.Count; + statisticReport.CheckoutPending = checkedOutPendingEmployeeIds.Count; + statisticReport.TotalPlannedWork = totalPlannedWork; + statisticReport.TotalCompletedWork = totalCompletedWork; + statisticReport.TotalPlannedTask = totalPlannedTask; + statisticReport.TotalCompletedTask = totalCompletedTask; + statisticReport.CompletionStatus = totalCompletedWork / totalPlannedWork; + statisticReport.TodaysAssignTasks = todayAssigned.Count; + statisticReport.ReportPending = reportPending.Count; + statisticReport.TeamOnSite = teamOnSite.OrderByDescending(c => c.NumberofEmployees).ToList(); + statisticReport.PerformedTasks = performedTasks; + statisticReport.PerformedAttendance = performedAttendances; + + string emails = _configuration["MailingList:ProjectStatisticsReceivers"] ?? ""; + List result = emails + .Split(';', StringSplitOptions.RemoveEmptyEntries) + .Select(item => item.Trim()) + .ToList(); + await _emailSender.SendProjectStatisticsEmail(result, statisticReport); + return Ok(ApiResponse.SuccessResponse(new { }, "Email sent successfully", 200)); + } + + + } +} diff --git a/Marco.Pms.Services/EmailTemplates/project-report.html b/Marco.Pms.Services/EmailTemplates/project-report.html index c60a174..4414b53 100644 --- a/Marco.Pms.Services/EmailTemplates/project-report.html +++ b/Marco.Pms.Services/EmailTemplates/project-report.html @@ -1,7 +1,8 @@ - + + - - - +
"); + int flag = 0; + foreach (var item in report.TeamOnSite) + { + if (flag == 6) + { + teamHtml.Append(""); + teamHtml.Append(""); + flag = 0; + } + teamHtml.AppendFormat("", item.RoleName, item.NumberofEmployees); + flag += 1; + } + teamHtml.Append(""); + emailBody = emailBody.Replace("{{TEAM_ON_SITE}}", teamHtml.ToString()); + + var taskHtml = new StringBuilder(); + if (report.PerformedTasks.Count > 0) + { + foreach (var task in report.PerformedTasks) + { + taskHtml.AppendFormat("\r\n\r\n\r\n\r\n\r\n \r\n\r\n", task.Comment); + } + } + else + { + taskHtml.Append(""); + } + + emailBody = emailBody.Replace("{{PERFORMED_TASK}}", taskHtml.ToString()); + + var attendanceHtml = new StringBuilder(); + if (report.PerformedAttendance.Count > 0) + { + foreach (var attendance in report.PerformedAttendance) + { + attendanceHtml.AppendFormat("\r\n\r\n\r\n\r\n\r\n\r\n", + attendance.Name, attendance.RoleName, attendance.InTime.ToString("dd-MMM-yyyy h:mm tt", CultureInfo.InvariantCulture), (attendance.OutTime != null ? attendance.OutTime.Value.ToString("dd-MMM-yyyy h:mm tt", CultureInfo.InvariantCulture) : ""), attendance.Comment); + } + } + else + { + attendanceHtml.Append(""); + } + + emailBody = emailBody.Replace("{{PERFORMED_ATTENDANCE}}", attendanceHtml.ToString()); + string subject = $"DPR - {date} - {report.ProjectName}"; + await SendEmailAsync(toEmails, subject, emailBody); + + } + public async Task SendEmailAsync(List toEmails, string subject, string body) { var email = new MimeMessage(); diff --git a/Marco.Pms.Services/Service/IEmailSender.cs b/Marco.Pms.Services/Service/IEmailSender.cs index e2e41be..db4088e 100644 --- a/Marco.Pms.Services/Service/IEmailSender.cs +++ b/Marco.Pms.Services/Service/IEmailSender.cs @@ -1,4 +1,5 @@ using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Report; namespace MarcoBMS.Services.Service { @@ -9,5 +10,6 @@ namespace MarcoBMS.Services.Service Task SendResetPasswordSuccessEmail(string toEmail, string userName); Task SendRequestDemoEmail(List toEmails, InquiryEmailObject demoEmailObject); Task SendEmailAsync(List toEmails, string subject, string body); + Task SendProjectStatisticsEmail(List toEmails, ProjectStatisticReport report); } } diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index 836b23c..98e8098 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -7,15 +7,22 @@ "ConnectionStrings": { //"DefaultConnectionString": "Server=localhost;port=3306;User ID=root;Password=root;Database=MarcoBMS2" - "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSDev" + "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSProd" }, "SmtpSettings": { "SmtpServer": "smtp.gmail.com", "Port": 587, - "SenderName": "MarcoIoIT", + "SenderName": "MarcoAIOT", "SenderEmail": "marcoioitsoft@gmail.com", "Password": "qrtq wfuj hwpp fhqr" }, + //"SmtpSettings": { + // "SmtpServer": "mail.marcoaiot.com", + // "Port": 587, + // "SenderName": "MarcoAIOT", + // "SenderEmail": "ashutosh.nehete@marcoaiot.com", + // "Password": "Reset@123" + //}, "AppSettings": { "WebFrontendUrl": "http://localhost:5173", "ImagesBaseUrl": "http://localhost:5173" @@ -27,8 +34,9 @@ "ExpiresInMinutes": 60, "RefreshTokenExpiresInDays": 7 }, - "MaliingList": { - "RequestDemoRecivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com" + "MailingList": { + "RequestDemoReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com", + "ProjectStatisticsReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com" }, "AWS": { "AccessKey": "AKIARZDBH3VDMSUUY2FX", diff --git a/Marco.Pms.Services/appsettings.Production.json b/Marco.Pms.Services/appsettings.Production.json index b2a0391..4921761 100644 --- a/Marco.Pms.Services/appsettings.Production.json +++ b/Marco.Pms.Services/appsettings.Production.json @@ -10,7 +10,7 @@ "SmtpSettings": { "SmtpServer": "smtp.gmail.com", "Port": 587, - "SenderName": "MarcoIoIT", + "SenderName": "MarcoAIOT", "SenderEmail": "marcoioitsoft@gmail.com", "Password": "qrtq wfuj hwpp fhqr" }, @@ -25,8 +25,9 @@ "ExpiresInMinutes": 60, "RefreshTokenExpiresInDays": 7 }, - "MaliingList": { - "RequestDemoRecivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com" + "MailingList": { + "RequestDemoReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com", + "ProjectStatisticsReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com" }, "AWS": { "AccessKey": "AKIARZDBH3VDMSUUY2FX", From 276b253e2df58082793f0beb7e7f3ac7ba5ecb98 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Mon, 26 May 2025 11:25:13 +0530 Subject: [PATCH 2/2] Added fixed height to table elements --- .../EmailTemplates/project-report.html | 105 +++++++++--------- Marco.Pms.Services/Service/EmailSender.cs | 22 ++-- .../appsettings.Development.json | 2 +- 3 files changed, 66 insertions(+), 63 deletions(-) diff --git a/Marco.Pms.Services/EmailTemplates/project-report.html b/Marco.Pms.Services/EmailTemplates/project-report.html index 4414b53..10a6da8 100644 --- a/Marco.Pms.Services/EmailTemplates/project-report.html +++ b/Marco.Pms.Services/EmailTemplates/project-report.html @@ -389,82 +389,86 @@ Project Status Reported - Generated at {{TIMESTAMP}}
@@ -125,21 +174,28 @@
-
+
-
+
-
+ +
- +
- + +
+ - +
- - +
+
+
+ + +
+
+ +
+ + +
@@ -169,25 +225,36 @@
-
+
-
+
-
+ +
- +
- - - -
+ - +
- @@ -213,25 +280,36 @@
-
+
-
+
-
+ +
-
+ - Image + Image
+
- - - - -
+ - +
- @@ -242,16 +320,28 @@
+ - Image + Image
- +
- @@ -274,11 +364,13 @@
-
+
-
+
@@ -286,300 +378,165 @@ -
+
-

Daily Progress Report - 22-May-2025

+

+ + Daily + Progress Report - {{DATE}} + +

-

Raja Bahadur International Ltd.

+

+ + Raja + {{PROJECT_NAME}} + +

+
- - - - - - - - + - + + + - @@ -713,243 +553,315 @@
- -
- - -
-
+
- Project Status Reported - Generated at < timestamp > - -
- - - - - - - - - - - - -
-
-
Todays Attendane
-
20 / 25
-
-
Regularization Pending
20
Checkout Pending
20
Tasks Completed
280/300
Project Completion Status
50%
Activity Report Pending
1/8
-
-
-
- Team Available On Site Today + + Project Status Reported - Generated at {{TIMESTAMP}} + +
- - - - - - + + + - - - - - + + + +
Welder
20
Fitter
11
Helper
10
Helper
6
Helper
6
+
+
+ Todays Attendane +
+
+ {{TODAYS_ATTENDANCES}} / + {{TOTAL_EMPLOYEES}} +
+ + * Checked-in / Assigned + +
+
+
+
+ Daily Tasks Completed +
+
+ {{TODAYS_COMPLETED}} / + {{TODAYS_PLANNED}} +
+ + * Today's Completed Task / Today's Planned Task + +
+
+
+
+ Project Completion Status +
+
+ {{TOTAL_COMPLETED}} / + {{TOTAL_PLANNED}} ({{PROJECT_STATUS}}) +
+ + * Total Completed Task / Total Planned Task + +
+
Painter
40
Site Engineer
1
Superwiser
2
Helper
50
Helper
6
+
+
+ Regularization Pending +
+
+ {{REGULRIZATION_PENDING}} +
+
+
+
+
+ Checkout Pending +
+
+ {{CHECKOUT_PRNDING}} +
+
+
+
+
+ Activity Report Pending +
+
+ {{REPORT_PENDING}} / + {{TODAYS_ASSIGNED}} +
+
+
+ +
+ + Team + Available On Site Today + + + {{TEAM_ON_SITE}} +
+
+
- Activities (Tasks) Performed Today
+ + Activities + (Tasks) Performed Today +
- - - - - - - - - - - - - - -
Activity/
Location
Assigned Today/
Pending
Completed TodayDate Team MembersComment
- - Branch Pipe Instalation -
- RB101 > First Floor > First Floor- South Area -
- 1 /18 - - 0 - 2025-05-17 - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) + + + + + + + + + + + {{PERFORMED_TASK}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Activity/
+ Location +
+ Assigned + Today/
Pending +
+ Completed Today + Date Team MembersComment
Task cannot be completed because access permissions have not been granted.
- - Hose Box Installlation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-17 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - Hose Box Installation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - 02025-05-16 - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - Hose Box Installlation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-15 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - Hose Box Installlation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-15 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - Hose > Box Installlation -
- RB101 First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-15 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - Hose Box Installlation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-15 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
- - - Hose Box Installlation -
- RB101 > First Floor > First Floor- South Area -
- 1 /1 - - 0 - - 2025-05-15 - - Ramesh Wagh  (Site Engineer) -
Parag Kale  (Fitter) - - - -
Task cannot be completed because access permissions have not been granted.
+
+
- Attendance Performed Today
+ + Attendance + Performed Today +
- +
@@ -587,125 +544,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + {{PERFORMED_ATTENDANCE}}
Name Job RoleCheck Out Comment
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- - Ram Rane - - - Site Engineer - - 2025-May-15 10:30 AM - 2025-May-15 7:30 PM - Completed all assigned tasks efficiently and ahead of schedule. -
- -
- - - - - - - - - - - - -
-
-
- - -
-
-
- - - - - - - - -
- -
- - -

Contact Us: info@marcoaiot.com

- - -
- -
- - -
+ +
+
+
+ + +
- - - -
-
-
- - - - - - - - -
- -
-
- - - - - - - - -
- - Facebook - -
- - - - - - - - -
- - Twitter - -
- - - - - - - - -
- - Instagram - -
- - - - - - - - -
- - LinkedIn - -
- - -
-
- -
- - - -
-
-
- - - - - + - -
-
-
- - -
-
-
- +
+
+
+ + +
+
+ +
+ - - - - + + +
-

Marco AIoT Technologies Pvt. Ltd. ©  All Rights Reserved

- - + +

+ + Contact + Us: info@marcoaiot.com + +

+ + + + +
+ + +
+
+
+ + +
+
+ +
+ + + + + + + + +
+ +
+
+ + + + + + + + +
+ + Facebook + +
+ + + + + + + + +
+ + Twitter + +
+ + + + + + + + +
+ + Instagram + +
+ + + + + + + + +
+ + LinkedIn + +
+ + +
+
+ +
+ + - -
- - -
+ +
+
+
+ + +
+
-
- - -
- - -
-
-
- - -
-
-
- - - - - - - -
-
- You're receiving this email because you have a MarcoPMS account. This email is not a marketing or promotional email. That is why this email does not contain an unsubscribe link. You will receive this email even if you have unsubscribed from MarcoPMS's marketing emails -
+ -
+
+
+
+ + +
+
+ +
+ - -
+ + + + + + +
+

+ + + + Marco + AIoT + Technologies Pvt. Ltd. ©  All + Rights + Reserved + + + +

+ + + +
+ + +
+
+
+ + +
+
-
- - -
-
-
- -
+ + + + + +
+ +
+ + You're receiving this + email because you + have a MarcoPMS account. This email is not a + marketing or + promotional email. That is why this email does not + contain an + unsubscribe link. You will receive this email even + if you have + unsubscribed from MarcoPMS's marketing + emails + + +
+ +
+ + + + + + + + + + + +
- + \ No newline at end of file diff --git a/Marco.Pms.Services/Service/EmailSender.cs b/Marco.Pms.Services/Service/EmailSender.cs index 1ca68ba..61757b1 100644 --- a/Marco.Pms.Services/Service/EmailSender.cs +++ b/Marco.Pms.Services/Service/EmailSender.cs @@ -1,5 +1,8 @@ -using MailKit.Net.Smtp; +using System.Globalization; +using System.Text; +using MailKit.Net.Smtp; using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Report; using Microsoft.Extensions.Options; using MimeKit; @@ -109,6 +112,92 @@ namespace MarcoBMS.Services.Service } + public async Task SendProjectStatisticsEmail(List toEmails, ProjectStatisticReport report) + { + //List toEmails = [ + // "ashutosh.nehete@marcoaiot.com" + //]; + var date = report.Date.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture); + var replacements = new Dictionary + { + {"DATE",date }, + {"PROJECT_NAME",report.ProjectName }, + {"TIMESTAMP",report.TimeStamp }, + {"TODAYS_ATTENDANCES", report.TodaysAttendances.ToString()}, + {"TOTAL_EMPLOYEES",report.TotalEmployees.ToString() }, + {"TODAYS_PLANNED",report.TotalPlannedTask.ToString() }, + {"TODAYS_COMPLETED",report.TotalCompletedTask.ToString() }, + {"REGULRIZATION_PENDING", report.RegularizationPending.ToString() }, + {"CHECKOUT_PRNDING",report.CheckoutPending.ToString() }, + {"TOTAL_PLANNED",report.TotalPlannedWork.ToString() }, + {"TOTAL_COMPLETED",report.TotalCompletedWork.ToString() }, + {"PROJECT_STATUS",report.CompletionStatus.ToString("P") }, + {"TODAYS_ASSIGNED",report.TodaysAssignTasks.ToString() }, + {"REPORT_PENDING",report.ReportPending.ToString() } + }; + + string emailBody = await GetEmailTemplate("project-report", replacements); + //string emailBody = await GetEmailTemplate("test-project", replacements); + + var teamHtml = new StringBuilder(); + teamHtml.Append("
{0}
{1}
\r\n\r\n {0} \r\n
\r\n {1} \r\n
\r\n {2} / {3} \r\n\r\n {4} \r\n {5} \r\n", + task.Activity, task.Location, task.AssignedToday, task.Pending, task.CompletedToday, report.Date.ToString("dd-MMM-yyy")); + foreach (var member in task.Team) + { + taskHtml.AppendFormat(" {0}   \r\n({1})\r\n
", + member.Name, member.RoleName); + } + taskHtml.AppendFormat("
{0}
No Activities (Tasks) Performed Today
\r\n {0} \r\n {1} {2} {3} {4}
No Attendance Performed Today
+ border="0" style="margin-top: 15px"> @@ -499,28 +503,27 @@ (Tasks) Performed Today
-
-
+
Todays Attendane
-
+
{{TODAYS_ATTENDANCES}} / {{TOTAL_EMPLOYEES}}
- * Checked-in / Assigned + Checked-in / Assigned
-
+
Daily Tasks Completed
-
+
{{TODAYS_COMPLETED}} / {{TODAYS_PLANNED}}
- * Today's Completed Task / Today's Planned Task + Today's Completed Task / Today's Planned Task
-
+
Project Completion Status
-
+
{{TOTAL_COMPLETED}} / - {{TOTAL_PLANNED}} ({{PROJECT_STATUS}}) + {{TOTAL_PLANNED}} +
({{PROJECT_STATUS}})
- * Total Completed Task / Total Planned Task + Total Completed Task / Total Planned Task
-
+
Regularization Pending
-
+
{{REGULRIZATION_PENDING}}
-
+
Checkout Pending
-
+
{{CHECKOUT_PRNDING}}
-
+
Activity Report Pending
-
+
{{REPORT_PENDING}} / {{TODAYS_ASSIGNED}}
+ + Total Pending Report / Today's' Assigned Task +
- - - - - - - - - - {{PERFORMED_TASK}} - -
- Activity/
- Location -
- Assigned - Today/
Pending -
- Completed Today - Date Team MembersComment
+ + + + + + + + + + + {{PERFORMED_TASK}} +
+ Activity/
+ Location +
+ Assigned + Today/
Pending +
+ Completed Today + Date Team MembersComment
@@ -535,18 +538,18 @@ Performed Today
- - - - - - - - - - {{PERFORMED_ATTENDANCE}} -
NameJob RoleCheck InCheck Out Comment
+ + + + + + + + + + {{PERFORMED_ATTENDANCE}} +
NameJob RoleCheck InCheck Out Comment
diff --git a/Marco.Pms.Services/Service/EmailSender.cs b/Marco.Pms.Services/Service/EmailSender.cs index 61757b1..5c3b278 100644 --- a/Marco.Pms.Services/Service/EmailSender.cs +++ b/Marco.Pms.Services/Service/EmailSender.cs @@ -123,17 +123,17 @@ namespace MarcoBMS.Services.Service {"DATE",date }, {"PROJECT_NAME",report.ProjectName }, {"TIMESTAMP",report.TimeStamp }, - {"TODAYS_ATTENDANCES", report.TodaysAttendances.ToString()}, - {"TOTAL_EMPLOYEES",report.TotalEmployees.ToString() }, - {"TODAYS_PLANNED",report.TotalPlannedTask.ToString() }, - {"TODAYS_COMPLETED",report.TotalCompletedTask.ToString() }, - {"REGULRIZATION_PENDING", report.RegularizationPending.ToString() }, - {"CHECKOUT_PRNDING",report.CheckoutPending.ToString() }, - {"TOTAL_PLANNED",report.TotalPlannedWork.ToString() }, + {"TODAYS_ATTENDANCES", report.TodaysAttendances.ToString("N0")}, + {"TOTAL_EMPLOYEES",report.TotalEmployees.ToString("N0") }, + {"TODAYS_PLANNED",report.TotalPlannedTask.ToString("N0") }, + {"TODAYS_COMPLETED",report.TotalCompletedTask.ToString("N0") }, + {"REGULRIZATION_PENDING", report.RegularizationPending.ToString("N0") }, + {"CHECKOUT_PRNDING",report.CheckoutPending.ToString("N0") }, + {"TOTAL_PLANNED",report.TotalPlannedWork.ToString("N0") }, {"TOTAL_COMPLETED",report.TotalCompletedWork.ToString() }, {"PROJECT_STATUS",report.CompletionStatus.ToString("P") }, - {"TODAYS_ASSIGNED",report.TodaysAssignTasks.ToString() }, - {"REPORT_PENDING",report.ReportPending.ToString() } + {"TODAYS_ASSIGNED",report.TodaysAssignTasks.ToString("N0") }, + {"REPORT_PENDING",report.ReportPending.ToString("N0") } }; string emailBody = await GetEmailTemplate("project-report", replacements); @@ -173,7 +173,7 @@ namespace MarcoBMS.Services.Service } else { - taskHtml.Append("No Activities (Tasks) Performed Today"); + taskHtml.Append("No Activities (Tasks) Performed Today"); } emailBody = emailBody.Replace("{{PERFORMED_TASK}}", taskHtml.ToString()); @@ -189,7 +189,7 @@ namespace MarcoBMS.Services.Service } else { - attendanceHtml.Append("No Attendance Performed Today"); + attendanceHtml.Append("No Attendance Performed Today"); } emailBody = emailBody.Replace("{{PERFORMED_ATTENDANCE}}", attendanceHtml.ToString()); diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index 98e8098..a4d0431 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -7,7 +7,7 @@ "ConnectionStrings": { //"DefaultConnectionString": "Server=localhost;port=3306;User ID=root;Password=root;Database=MarcoBMS2" - "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSProd" + "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSGuid" }, "SmtpSettings": { "SmtpServer": "smtp.gmail.com",