Taking emailids and email body from database rather than local or hardcoded as well logging every mail to database

This commit is contained in:
ashutosh.nehete 2025-05-30 17:46:51 +05:30
parent 235ca073ce
commit e73d3e0744
14 changed files with 3207 additions and 232 deletions

View File

@ -6,6 +6,7 @@ using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Forum; using Marco.Pms.Model.Forum;
using Marco.Pms.Model.Mail;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Roles; using Marco.Pms.Model.Roles;
@ -28,53 +29,30 @@ namespace Marco.Pms.DataAccess.Data
} }
public DbSet<RefreshToken> RefreshTokens { get; set; } public DbSet<RefreshToken> RefreshTokens { get; set; }
public DbSet<Tenant> Tenants { get; set; } public DbSet<Tenant> Tenants { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; } public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<ActivityMaster> ActivityMasters { get; set; } public DbSet<ActivityMaster> ActivityMasters { get; set; }
public DbSet<Project> Projects { get; set; } public DbSet<Project> Projects { get; set; }
public DbSet<ProjectAllocation> ProjectAllocations { get; set; } public DbSet<ProjectAllocation> ProjectAllocations { get; set; }
public DbSet<StatusMaster> StatusMasters { get; set; } public DbSet<StatusMaster> StatusMasters { get; set; }
public DbSet<Building> Buildings { get; set; } public DbSet<Building> Buildings { get; set; }
public DbSet<Floor> Floor { get; set; } public DbSet<Floor> Floor { get; set; }
public DbSet<WorkArea> WorkAreas { get; set; } public DbSet<WorkArea> WorkAreas { get; set; }
public DbSet<WorkItem> WorkItems { get; set; } public DbSet<WorkItem> WorkItems { get; set; }
//public DbSet<WorkItemMapping> WorkItemMapping { get; set; }
public DbSet<WorkShift> WorkShifts { get; set; } public DbSet<WorkShift> WorkShifts { get; set; }
public DbSet<TaskAllocation> TaskAllocations { get; set; } public DbSet<TaskAllocation> TaskAllocations { get; set; }
public DbSet<TaskComment> TaskComments { get; set; } public DbSet<TaskComment> TaskComments { get; set; }
public DbSet<TaskMembers> TaskMembers { get; set; } public DbSet<TaskMembers> TaskMembers { get; set; }
// public DbSet<Attendance> Attendances { get; set; }
public DbSet<Attendance> Attendes { get; set; } public DbSet<Attendance> Attendes { get; set; }
public DbSet<AttendanceLog> AttendanceLogs { get; set; } public DbSet<AttendanceLog> AttendanceLogs { get; set; }
// public DbSet<AttendLog> AttendLogs { get; set; }
public DbSet<Employee> Employees { get; set; } public DbSet<Employee> Employees { get; set; }
public DbSet<EmployeeRoleMapping> EmployeeRoleMappings { get; set; } public DbSet<EmployeeRoleMapping> EmployeeRoleMappings { get; set; }
public DbSet<Module> Modules { get; set; } public DbSet<Module> Modules { get; set; }
public DbSet<Feature> Features { get; set; } public DbSet<Feature> Features { get; set; }
public DbSet<FeaturePermission> FeaturePermissions { get; set; } public DbSet<FeaturePermission> FeaturePermissions { get; set; }
public DbSet<ApplicationRole> ApplicationRoles { get; set; } public DbSet<ApplicationRole> ApplicationRoles { get; set; }
public DbSet<JobRole> JobRoles { get; set; } public DbSet<JobRole> JobRoles { get; set; }
public DbSet<RolePermissionMappings> RolePermissionMappings { get; set; } public DbSet<RolePermissionMappings> RolePermissionMappings { get; set; }
public DbSet<Industry> Industries { get; set; } public DbSet<Industry> Industries { get; set; }
public DbSet<ActivityCheckList> ActivityCheckLists { get; set; } public DbSet<ActivityCheckList> ActivityCheckLists { get; set; }
public DbSet<CheckListMappings> CheckListMappings { get; set; } public DbSet<CheckListMappings> CheckListMappings { get; set; }
@ -89,6 +67,9 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<Document> Documents { get; set; } public DbSet<Document> Documents { get; set; }
public DbSet<TicketTag> TicketTags { get; set; } public DbSet<TicketTag> TicketTags { get; set; }
public DbSet<WorkCategoryMaster> WorkCategoryMasters { get; set; } public DbSet<WorkCategoryMaster> WorkCategoryMasters { get; set; }
public DbSet<MailingList> MailingList { get; set; }
public DbSet<MailDetails> MailDetails { get; set; }
public DbSet<MailLog> MailLogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Mail_Related_Tables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MailingList",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Title = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Body = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Keywords = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_MailingList", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "MailLogs",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Body = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
EmailId = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TimeStamp = table.Column<DateTime>(type: "datetime(6)", nullable: false),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
EmployeeId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_MailLogs", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "MailDetails",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Recipient = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Subject = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Schedule = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
MailListId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_MailDetails", x => x.Id);
table.ForeignKey(
name: "FK_MailDetails_MailingList_MailListId",
column: x => x.MailListId,
principalTable: "MailingList",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_MailDetails_MailListId",
table: "MailDetails",
column: "MailListId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MailDetails");
migrationBuilder.DropTable(
name: "MailLogs");
migrationBuilder.DropTable(
name: "MailingList");
}
}
}

View File

@ -882,6 +882,97 @@ namespace Marco.Pms.DataAccess.Migrations
}); });
}); });
modelBuilder.Entity("Marco.Pms.Model.Mail.MailDetails", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("MailListId")
.HasColumnType("char(36)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<string>("Recipient")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Schedule")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("MailListId");
b.ToTable("MailDetails");
});
modelBuilder.Entity("Marco.Pms.Model.Mail.MailLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("EmailId")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid?>("EmployeeId")
.HasColumnType("char(36)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime>("TimeStamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("MailLogs");
});
modelBuilder.Entity("Marco.Pms.Model.Mail.MailingList", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Keywords")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("MailingList");
});
modelBuilder.Entity("Marco.Pms.Model.Master.ActivityMaster", b => modelBuilder.Entity("Marco.Pms.Model.Master.ActivityMaster", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -2218,6 +2309,17 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Ticket"); b.Navigation("Ticket");
}); });
modelBuilder.Entity("Marco.Pms.Model.Mail.MailDetails", b =>
{
b.HasOne("Marco.Pms.Model.Mail.MailingList", "MailBody")
.WithMany()
.HasForeignKey("MailListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("MailBody");
});
modelBuilder.Entity("Marco.Pms.Model.Master.ActivityMaster", b => modelBuilder.Entity("Marco.Pms.Model.Master.ActivityMaster", b =>
{ {
b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant") b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant")

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.Dtos.Mail
{
public class MailDetailsDto
{
public Guid ProjectId { get; set; }
public string Recipient { get; set; } = string.Empty; // Eamil Address of recipient
public string Subject { get; set; } = string.Empty;
public string Schedule { get; set; } = string.Empty; // json object which includes when to send mail and at what interval
public Guid MailListId { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Mail
{
public class MailTemeplateDto
{
public string Title { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
public string Keywords { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace Marco.Pms.Model.Mail
{
public class MailDetails
{
public Guid Id { get; set; }
public Guid ProjectId { get; set; }
public string Recipient { get; set; } = string.Empty; // Eamil Address of recipient
public string Subject { get; set; } = string.Empty; // Eamil Address of recipient
public string Schedule { get; set; } = string.Empty; // json object which includes when to send mail and at what interval
public Guid MailListId { get; set; }
[ValidateNever]
[ForeignKey(nameof(MailListId))]
public MailingList? MailBody { get; set; }
public Guid TenantId { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Marco.Pms.Model.Mail
{
public class MailLog
{
public Guid Id { get; set; }
public Guid ProjectId { get; set; }
public string Body { get; set; } = string.Empty;
public string EmailId { get; set; } = string.Empty;
public DateTime TimeStamp { get; set; }
public Guid TenantId { get; set; }
public Guid? EmployeeId { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.Mail
{
public class MailingList
{
public Guid Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
public string Keywords { get; set; } = string.Empty; // Comma seprated list of variables in mail body
public Guid TenantId { get; set; }
}
}

View File

@ -1,8 +1,10 @@
using System.Globalization; using System.Data;
using System.Globalization;
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Dtos.Mail;
using Marco.Pms.Model.Roles; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Mail;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Report; using Marco.Pms.Model.ViewModels.Report;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
@ -21,198 +23,308 @@ namespace Marco.Pms.Services.Controllers
{ {
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
private readonly IEmailSender _emailSender; private readonly IEmailSender _emailSender;
private readonly IConfiguration _configuration;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
public ReportController(ApplicationDbContext context, IEmailSender emailSender, IConfiguration configuration, ILoggingService logger, UserHelper userHelper) private readonly IWebHostEnvironment _env;
public ReportController(ApplicationDbContext context, IEmailSender emailSender, ILoggingService logger, UserHelper userHelper, IWebHostEnvironment env)
{ {
_context = context; _context = context;
_emailSender = emailSender; _emailSender = emailSender;
_configuration = configuration;
_logger = logger; _logger = logger;
_userHelper = userHelper; _userHelper = userHelper;
_env = env;
} }
[HttpGet("project-statistics/{id}")] [HttpPost("set-mail")]
public async Task<IActionResult> GetProjectStatistics(Guid id, [FromQuery] string? date) public async Task<IActionResult> AddMailDetails([FromBody] MailDetailsDto mailDetailsDto)
{ {
DateTime reportDate = DateTime.UtcNow; Guid tenantId = _userHelper.GetTenantId();
if (date != null && DateTime.TryParse(date, out reportDate) == false) MailDetails mailDetails = new MailDetails
{ {
_logger.LogError("User sent Invalid from Date while featching project report"); ProjectId = mailDetailsDto.ProjectId,
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); Recipient = mailDetailsDto.Recipient,
Schedule = mailDetailsDto.Schedule,
MailListId = mailDetailsDto.MailListId,
Subject = mailDetailsDto.Subject,
TenantId = tenantId
};
_context.MailDetails.Add(mailDetails);
await _context.SaveChangesAsync();
return Ok("Success");
} }
if (id == Guid.Empty)
[HttpPost("mail-template")]
public async Task<IActionResult> AddMailTemplate([FromBody] MailTemeplateDto mailTemeplateDto)
{ {
_logger.LogError("Provided empty project ID while fetching project report"); Guid tenantId = _userHelper.GetTenantId();
return BadRequest(ApiResponse<object>.ErrorResponse("Provided empty ProjectID", "Provided empty ProjectID", 400)); MailingList mailingList = new MailingList
{
Title = mailTemeplateDto.Title,
Body = mailTemeplateDto.Body,
Keywords = mailTemeplateDto.Keywords,
TenantId = tenantId
};
_context.MailingList.Add(mailingList);
await _context.SaveChangesAsync();
return Ok("Success");
} }
Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == id);
[HttpGet("project-statistics")]
public async Task<IActionResult> SendProjectReport()
{
Guid tenantId = _userHelper.GetTenantId();
// Use AsNoTracking() for read-only queries to improve performance
List<MailDetails> mailDetails = await _context.MailDetails
.AsNoTracking()
.Include(m => m.MailBody)
.Where(m => m.TenantId == tenantId)
.ToListAsync();
int successCount = 0;
int notFoundCount = 0;
int invalidIdCount = 0;
var groupedMails = mailDetails
.GroupBy(m => new { m.ProjectId, m.MailListId })
.Select(g => new
{
ProjectId = g.Key.ProjectId,
MailListId = g.Key.MailListId,
Recipients = g.Select(m => m.Recipient).Distinct().ToList(),
MailBody = g.FirstOrDefault()?.MailBody?.Body ?? ""
})
.ToList();
var semaphore = new SemaphoreSlim(1);
// Using Task.WhenAll to send reports concurrently for better performance
var sendTasks = groupedMails.Select(async mailDetail =>
{
await semaphore.WaitAsync();
try
{
var response = await GetProjectStatistics(mailDetail.ProjectId, mailDetail.Recipients, mailDetail.MailBody, tenantId);
if (response.StatusCode == 200)
Interlocked.Increment(ref successCount);
else if (response.StatusCode == 404)
Interlocked.Increment(ref notFoundCount);
else if (response.StatusCode == 400)
Interlocked.Increment(ref invalidIdCount);
}
finally
{
semaphore.Release();
}
}).ToList();
await Task.WhenAll(sendTasks);
//var response = await GetProjectStatistics(Guid.Parse("2618eb89-2823-11f0-9d9e-bc241163f504"), "ashutosh.nehete@marcoaiot.com", tenantId);
_logger.LogInfo(
"Emails of project reports sent for tenant {TenantId}. Successfully sent: {SuccessCount}, Projects not found: {NotFoundCount}, Invalid IDs: {InvalidIdsCount}",
tenantId, successCount, notFoundCount, invalidIdCount);
return Ok(ApiResponse<object>.SuccessResponse(
new { },
$"Reports sent successfully: {successCount}. Projects not found: {notFoundCount}. Invalid IDs: {invalidIdCount}.",
200));
}
/// <summary>
/// Retrieves project statistics for a given project ID and sends an email report.
/// </summary>
/// <param name="projectId">The ID of the project.</param>
/// <param name="recipientEmail">The email address of the recipient.</param>
/// <returns>An ApiResponse indicating the success or failure of retrieving statistics and sending the email.</returns>
public async Task<ApiResponse<object>> GetProjectStatistics(Guid projectId, List<string> recipientEmails, string body, Guid tenantId)
{
DateTime reportDate = DateTime.UtcNow.AddDays(-1).Date;
if (projectId == Guid.Empty)
{
_logger.LogError("Provided empty project ID while fetching project report.");
return ApiResponse<object>.ErrorResponse("Provided empty Project ID.", "Provided empty Project ID.", 400);
}
var project = await _context.Projects
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == projectId);
if (project == null) if (project == null)
{ {
_logger.LogWarning("User attempted to fetch project progress of project ID {ProjectId} but not found in database", id); _logger.LogWarning("User attempted to fetch project progress for project ID {ProjectId} but not found.", projectId);
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404)); return ApiResponse<object>.ErrorResponse("Project not found.", "Project not found.", 404);
} }
ProjectStatisticReport statisticReport = new ProjectStatisticReport
var statisticReport = new ProjectStatisticReport
{ {
Date = reportDate, Date = reportDate,
ProjectName = project.Name ?? "", ProjectName = project.Name ?? "",
TimeStamp = DateTime.Now.ToString("dd-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture) 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();
// Preload relevant data
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).ToHashSet();
var attendances = await _context.Attendes
.AsNoTracking()
.Where(a => a.ProjectID == project.Id && a.InTime != null && a.InTime.Value.Date == reportDate)
.ToListAsync();
var checkedInEmployeeIds = attendances.Select(a => a.EmployeeID).Distinct().ToHashSet();
var checkoutPendingIds = attendances.Where(a => a.OutTime == null).Select(a => a.EmployeeID).Distinct().ToHashSet();
var regularizationIds = attendances
.Where(a => a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
.Select(a => a.EmployeeID).Distinct().ToHashSet();
// Preload buildings, floors, areas
var buildings = await _context.Buildings.Where(b => b.ProjectId == project.Id).ToListAsync(); var buildings = await _context.Buildings.Where(b => b.ProjectId == project.Id).ToListAsync();
var buildingIds = buildings.Select(b => b.Id).Distinct().ToList(); var buildingIds = buildings.Select(b => b.Id).ToList();
var floors = await _context.Floor.Where(f => buildingIds.Contains(f.BuildingId)).ToListAsync(); var floors = await _context.Floor.Where(f => buildingIds.Contains(f.BuildingId)).ToListAsync();
var floorIds = floors.Select(f => f.Id).Distinct().ToList(); var floorIds = floors.Select(f => f.Id).ToList();
var areas = await _context.WorkAreas.Where(a => floorIds.Contains(a.FloorId)).ToListAsync(); var areas = await _context.WorkAreas.Where(a => floorIds.Contains(a.FloorId)).ToListAsync();
var areaIds = areas.Select(a => a.Id).Distinct().ToList(); var areaIds = areas.Select(a => a.Id).ToList();
var workItems = await _context.WorkItems.Include(i => i.ActivityMaster).Where(i => areaIds.Contains(i.WorkAreaId)).ToListAsync(); var workItems = await _context.WorkItems
var itemIds = workItems.Select(i => i.Id).Distinct().ToList(); .Include(w => w.ActivityMaster)
.Where(w => areaIds.Contains(w.WorkAreaId))
.ToListAsync();
var tasks = await _context.TaskAllocations.Where(t => itemIds.Contains(t.WorkItemId)).ToListAsync(); var itemIds = workItems.Select(i => i.Id).ToList();
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(); var tasks = await _context.TaskAllocations
.Where(t => itemIds.Contains(t.WorkItemId))
.ToListAsync();
double totalPlannedWork = 0; var taskIds = tasks.Select(t => t.Id).ToList();
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 taskMembers = await _context.TaskMembers
.Include(m => m.Employee)
.Where(m => taskIds.Contains(m.TaskAllocationId))
.ToListAsync();
// Aggregate data
double totalPlannedWork = workItems.Sum(w => w.PlannedWork);
double totalCompletedWork = workItems.Sum(w => w.CompletedWork);
var todayAssignedTasks = tasks.Where(t => t.AssignmentDate.Date == reportDate).ToList();
var reportPending = tasks.Where(t => t.ReportedDate == null).ToList(); var reportPending = tasks.Where(t => t.ReportedDate == null).ToList();
double totalPlannedTask = 0; double totalPlannedTask = todayAssignedTasks.Sum(t => t.PlannedTask);
double totalCompletedTask = 0; double totalCompletedTask = todayAssignedTasks.Sum(t => t.CompletedTask);
foreach (var items in todayAssigned)
{
totalPlannedTask += items.PlannedTask;
totalCompletedTask += items.CompletedTask;
}
var jobRoles = await _context.JobRoles.Where(r => r.TenantId == project.TenantId).ToListAsync(); var jobRoles = await _context.JobRoles
List<TeamOnSite> teamOnSite = new List<TeamOnSite>(); .Where(j => j.TenantId == project.TenantId)
foreach (var role in jobRoles) .ToListAsync();
{
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<PerformedTask> performedTasks = new List<PerformedTask>();
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(); // Team on site
string areaName = workArea.AreaName ?? ""; var teamOnSite = jobRoles
.Select(role =>
{
var count = projectAllocations.Count(p => p.JobRoleId == role.Id && checkedInEmployeeIds.Contains(p.EmployeeId));
return new TeamOnSite { RoleName = role.Name, NumberofEmployees = count };
})
.OrderByDescending(t => t.NumberofEmployees)
.ToList();
Floor floor = floors.FirstOrDefault(f => f.Id == workArea.FloorId) ?? new Floor(); // Task details
string floorName = floor.FloorName ?? ""; var performedTasks = todayAssignedTasks.Select(task =>
{
var workItem = workItems.FirstOrDefault(w => w.Id == task.WorkItemId);
var area = areas.FirstOrDefault(a => a.Id == workItem?.WorkAreaId);
var floor = floors.FirstOrDefault(f => f.Id == area?.FloorId);
var building = buildings.FirstOrDefault(b => b.Id == floor?.BuildingId);
Building building = buildings.FirstOrDefault(b => b.Id == floor.BuildingId) ?? new Building(); string activityName = workItem?.ActivityMaster?.ActivityName ?? "";
string buildingName = building.Name ?? ""; string location = $"{building?.Name} > {floor?.FloorName}</span><br/><span style=\"color: gray; font-size: small; padding-left: 10px;\"> {floor?.FloorName}-{area?.AreaName}";
double pending = (workItem?.PlannedWork ?? 0) - (workItem?.CompletedWork ?? 0);
string location = $"{buildingName} > {floorName} </span><br/><span style=\"color: gray; font-size: small; padding-left: 10px;\"> {floorName}-{areaName}"; var taskTeam = taskMembers
double pending = workItem.PlannedWork - workItem.CompletedWork; .Where(m => m.TaskAllocationId == task.Id)
PerformedTask performedTask = new PerformedTask .Select(m =>
{
string name = $"{m.Employee?.FirstName ?? ""} {m.Employee?.LastName ?? ""}";
var role = jobRoles.FirstOrDefault(r => r.Id == m.Employee?.JobRoleId);
return new TaskTeam { Name = name, RoleName = role?.Name ?? "" };
})
.ToList();
return new PerformedTask
{ {
Activity = activityName, Activity = activityName,
Location = location, Location = location,
AssignedToday = task.PlannedTask, AssignedToday = task.PlannedTask,
Pending = pending,
CompletedToday = task.CompletedTask, CompletedToday = task.CompletedTask,
Comment = task.Description Pending = pending,
Comment = task.Description,
Team = taskTeam
}; };
}).ToList();
var taskTeams = taskMembers.Where(m => m.TaskAllocationId == task.Id).ToList(); // Attendance details
List<TaskTeam> Team = new List<TaskTeam>(); var performedAttendance = attendances.Select(att =>
foreach (var team in taskTeams)
{ {
string firstName = (team.Employee != null ? team.Employee.FirstName : "") ?? ""; var alloc = projectAllocations.FirstOrDefault(p => p.EmployeeId == att.EmployeeID);
string lastName = (team.Employee != null ? team.Employee.LastName : "") ?? ""; var role = jobRoles.FirstOrDefault(r => r.Id == alloc?.JobRoleId);
string name = $"{firstName} {lastName}"; string name = $"{alloc?.Employee?.FirstName ?? ""} {alloc?.Employee?.LastName ?? ""}";
JobRole role = jobRoles.FirstOrDefault(r => r.Id == (team.Employee != null ? team.Employee.JobRoleId : Guid.Empty)) ?? new JobRole(); return new PerformedAttendance
string roleName = role.Name ?? "";
TaskTeam taskTeam = new TaskTeam
{ {
Name = name, Name = name,
RoleName = roleName, RoleName = role?.Name ?? "",
InTime = att.InTime ?? DateTime.UtcNow,
OutTime = att.OutTime,
Comment = att.Comment
}; };
Team.Add(taskTeam); }).ToList();
}
performedTask.Team = Team;
performedTasks.Add(performedTask);
}
List<PerformedAttendance> performedAttendances = new List<PerformedAttendance>();
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);
}
// Fill report
statisticReport.TodaysAttendances = checkedInEmployeeIds.Count; statisticReport.TodaysAttendances = checkedInEmployeeIds.Count;
statisticReport.TotalEmployees = assignedEmployeeIds.Count; statisticReport.TotalEmployees = assignedEmployeeIds.Count;
statisticReport.RegularizationPending = regularizationEmployeeIds.Count; statisticReport.RegularizationPending = regularizationIds.Count;
statisticReport.CheckoutPending = checkedOutPendingEmployeeIds.Count; statisticReport.CheckoutPending = checkoutPendingIds.Count;
statisticReport.TotalPlannedWork = totalPlannedWork; statisticReport.TotalPlannedWork = totalPlannedWork;
statisticReport.TotalCompletedWork = totalCompletedWork; statisticReport.TotalCompletedWork = totalCompletedWork;
statisticReport.TotalPlannedTask = totalPlannedTask; statisticReport.TotalPlannedTask = totalPlannedTask;
statisticReport.TotalCompletedTask = totalCompletedTask; statisticReport.TotalCompletedTask = totalCompletedTask;
statisticReport.CompletionStatus = totalCompletedWork / totalPlannedWork; statisticReport.CompletionStatus = totalPlannedWork > 0 ? totalCompletedWork / totalPlannedWork : 0;
statisticReport.TodaysAssignTasks = todayAssigned.Count; statisticReport.TodaysAssignTasks = todayAssignedTasks.Count;
statisticReport.ReportPending = reportPending.Count; statisticReport.ReportPending = reportPending.Count;
statisticReport.TeamOnSite = teamOnSite.OrderByDescending(c => c.NumberofEmployees).ToList(); statisticReport.TeamOnSite = teamOnSite;
statisticReport.PerformedTasks = performedTasks; statisticReport.PerformedTasks = performedTasks;
statisticReport.PerformedAttendance = performedAttendances; statisticReport.PerformedAttendance = performedAttendance;
string emails = _configuration["MailingList:ProjectStatisticsReceivers"] ?? ""; // Send Email
List<string> result = emails var emailBody = await _emailSender.SendProjectStatisticsEmail(recipientEmails, body, statisticReport);
.Split(';', StringSplitOptions.RemoveEmptyEntries) var employee = await _context.Employees.FirstOrDefaultAsync(e => e.Email != null && recipientEmails.Contains(e.Email)) ?? new Employee();
.Select(item => item.Trim())
.ToList(); List<MailLog> mailLogs = new List<MailLog>();
await _emailSender.SendProjectStatisticsEmail(result, statisticReport); foreach (var recipientEmail in recipientEmails)
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Email sent successfully", 200)); {
mailLogs.Add(
new MailLog
{
ProjectId = projectId,
EmailId = recipientEmail,
Body = emailBody,
EmployeeId = employee.Id,
TimeStamp = DateTime.UtcNow,
TenantId = tenantId
});
} }
_context.MailLogs.AddRange(mailLogs);
await _context.SaveChangesAsync();
return ApiResponse<object>.SuccessResponse(statisticReport, "Email sent successfully", 200);
}
} }
} }

View File

@ -442,7 +442,7 @@
Regularization Pending Regularization Pending
</div> </div>
<div style="font-size: 25px; color: #bc3803; margin: 20px 20px 0px !important; font-weight: bold;"> <div style="font-size: 25px; color: #bc3803; margin: 20px 20px 0px !important; font-weight: bold;">
{{REGULRIZATION_PENDING}} {{REGULARIZATION_PENDING}}
</div> </div>
</div> </div>
</td> </td>
@ -453,7 +453,7 @@
Checkout Pending Checkout Pending
</div> </div>
<div style="font-size: 25px; color: #bc3803; margin: 20px 20px 0px !important; font-weight: bold;"> <div style="font-size: 25px; color: #bc3803; margin: 20px 20px 0px !important; font-weight: bold;">
{{CHECKOUT_PRNDING}} {{CHECKOUT_PENDING}}
</div> </div>
</div> </div>
</td> </td>

View File

@ -112,11 +112,8 @@ namespace MarcoBMS.Services.Service
} }
public async Task SendProjectStatisticsEmail(List<string> toEmails, ProjectStatisticReport report) public async Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, ProjectStatisticReport report)
{ {
//List<string> toEmails = [
// "ashutosh.nehete@marcoaiot.com"
//];
var date = report.Date.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture); var date = report.Date.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture);
var replacements = new Dictionary<string, string> var replacements = new Dictionary<string, string>
{ {
@ -127,75 +124,115 @@ namespace MarcoBMS.Services.Service
{"TOTAL_EMPLOYEES", report.TotalEmployees.ToString("N0")}, {"TOTAL_EMPLOYEES", report.TotalEmployees.ToString("N0")},
{"TODAYS_PLANNED", report.TotalPlannedTask.ToString("N0")}, {"TODAYS_PLANNED", report.TotalPlannedTask.ToString("N0")},
{"TODAYS_COMPLETED", report.TotalCompletedTask.ToString("N0")}, {"TODAYS_COMPLETED", report.TotalCompletedTask.ToString("N0")},
{"REGULRIZATION_PENDING", report.RegularizationPending.ToString("N0") }, {"REGULARIZATION_PENDING", report.RegularizationPending.ToString("N0")},
{"CHECKOUT_PRNDING",report.CheckoutPending.ToString("N0") }, {"CHECKOUT_PENDING", report.CheckoutPending.ToString("N0")},
{"TOTAL_PLANNED", report.TotalPlannedWork.ToString("N0")}, {"TOTAL_PLANNED", report.TotalPlannedWork.ToString("N0")},
{"TOTAL_COMPLETED",report.TotalCompletedWork.ToString() }, {"TOTAL_COMPLETED", report.TotalCompletedWork.ToString("N0")},
{"PROJECT_STATUS", report.CompletionStatus.ToString("P")}, {"PROJECT_STATUS", report.CompletionStatus.ToString("P")},
{"TODAYS_ASSIGNED", report.TodaysAssignTasks.ToString("N0")}, {"TODAYS_ASSIGNED", report.TodaysAssignTasks.ToString("N0")},
{"REPORT_PENDING", report.ReportPending.ToString("N0")} {"REPORT_PENDING", report.ReportPending.ToString("N0")}
}; };
string emailBody = await GetEmailTemplate("project-report", replacements); foreach (var item in replacements)
//string emailBody = await GetEmailTemplate("test-project", replacements);
var teamHtml = new StringBuilder();
teamHtml.Append("<tr style=\"vertical-align:middle\">");
int flag = 0;
foreach (var item in report.TeamOnSite)
{ {
if (flag == 6) emailBody = emailBody.Replace($"{{{{{item.Key}}}}}", item.Value);
{
teamHtml.Append("</tr>");
teamHtml.Append("<tr style=\"vertical-align:middle\">");
flag = 0;
}
teamHtml.AppendFormat("<td class=\"team\" style=\"text-align:center\"><div style=\"border: 1px solid #d5d5d5; border-radius: 10px; margin: 10px 10px; padding: 10px;\"><div style=\"font-size: 15px; color: #525b75 \">{0}</div> <div style=\"font-size: 20px; color: #003cc7; margin: 20px !important; font-weight: bold; \">{1}</div></div></td>", item.RoleName, item.NumberofEmployees);
flag += 1;
}
teamHtml.Append("</tr>");
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("<tr>\r\n<td style=\"text-align:left;\">\r\n<span style=\"padding-left: 10px; text-align: left;\">\r\n {0} \r\n</span><br />\r\n<span style=\"color: gray; font-size: small; padding-left: 10px;\"> {1} </span>\r\n</td>\r\n<td style=\"text-align:center\">\r\n {2} / {3} \r\n</td>\r\n<td style=\"text-align:center\">\r\n {4} \r\n</td>\r\n<td style=\"text-align:center\"> {5} </td>\r\n <td style=\"padding-left: 10px; text-align: left;\">\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} &nbsp; \r\n<span style=\"color: gray; font-size: small; padding-left: 10px;\">({1})</span>\r\n<br />",
member.Name, member.RoleName);
}
taskHtml.AppendFormat("</td>\r\n<td style=\"padding-left: 10px; max-width: 150px; text-align: left;\"> {0} </td>\r\n</tr>", task.Comment);
}
}
else
{
taskHtml.Append("<tr><td style=\"padding: 10px 0px 5px;\" colspan=\"6\"><span style=\"line-height: 25.2px; color: #666666;margin:10px\">No Activities (Tasks) Performed Today</span></td></tr>");
} }
emailBody = emailBody.Replace("{{PERFORMED_TASK}}", taskHtml.ToString()); emailBody = emailBody.Replace("{{TEAM_ON_SITE}}", BuildTeamOnSiteHtml(report.TeamOnSite));
emailBody = emailBody.Replace("{{PERFORMED_TASK}}", BuildPerformedTaskHtml(report.PerformedTasks, report.Date));
emailBody = emailBody.Replace("{{PERFORMED_ATTENDANCE}}", BuildPerformedAttendanceHtml(report.PerformedAttendance));
var attendanceHtml = new StringBuilder();
if (report.PerformedAttendance.Count > 0)
{
foreach (var attendance in report.PerformedAttendance)
{
attendanceHtml.AppendFormat("<tr>\r\n<td style=\"text-align:left\">\r\n<span style=\"padding-left:10px;\"> {0} </span>\r\n</td>\r\n<td style=\"text-align:center\"> {1} </td>\r\n<td style=\"text-align:center\"> {2} </td>\r\n<td style=\"text-align:center\"> {3} </td>\r\n<td style=\"padding-left:10px; max-width:150px\"> {4} </td>\r\n</tr>",
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("<tr><td style=\"padding: 10px 0px 5px;\" colspan=\"5\"><span style=\"line-height: 25.2px; color: #666666;margin:10px\">No Attendance Performed Today</span></td></tr>");
}
emailBody = emailBody.Replace("{{PERFORMED_ATTENDANCE}}", attendanceHtml.ToString());
string subject = $"DPR - {date} - {report.ProjectName}"; string subject = $"DPR - {date} - {report.ProjectName}";
await SendEmailAsync(toEmails, subject, emailBody); await SendEmailAsync(toEmails, subject, emailBody);
return emailBody;
}
private string BuildTeamOnSiteHtml(List<TeamOnSite> team)
{
if (team == null || !team.Any()) return "";
var sb = new StringBuilder();
sb.Append("<tr style=\"vertical-align:middle\">");
int count = 0;
foreach (var member in team)
{
if (count == 6)
{
sb.Append("</tr><tr style=\"vertical-align:middle\">");
count = 0;
}
sb.AppendFormat(
"<td class=\"team\" style=\"text-align:center\">" +
"<div style=\"border: 1px solid #d5d5d5; border-radius: 10px; margin: 10px; padding: 10px;\">" +
"<div style=\"font-size: 15px; color: #525b75\">{0}</div>" +
"<div style=\"font-size: 20px; color: #003cc7; margin: 20px !important; font-weight: bold;\">{1}</div>" +
"</div></td>", member.RoleName, member.NumberofEmployees);
count++;
}
sb.Append("</tr>");
return sb.ToString();
}
private string BuildPerformedTaskHtml(List<PerformedTask> tasks, DateTime reportDate)
{
if (tasks == null || !tasks.Any())
{
return "<tr><td style=\"padding: 10px 0px 5px;\" colspan=\"6\">" +
"<span style=\"line-height: 25.2px; color: #666666;margin:10px\">" +
"No Activities (Tasks) Performed Today</span></td></tr>";
}
var sb = new StringBuilder();
foreach (var task in tasks)
{
sb.AppendFormat("<tr>" +
"<td style=\"text-align:left;\"><span style=\"padding-left: 10px;\">{0}</span><br />" +
"<span style=\"color: gray; font-size: small; padding-left: 10px;\">{1}</span></td>" +
"<td style=\"text-align:center\">{2} / {3}</td>" +
"<td style=\"text-align:center\">{4}</td>" +
"<td style=\"text-align:center\">{5}</td>" +
"<td style=\"padding-left: 10px; text-align: left;\">",
task.Activity, task.Location, task.AssignedToday, task.Pending, task.CompletedToday, reportDate.ToString("dd-MMM-yyyy"));
foreach (var member in task.Team)
{
sb.AppendFormat("{0} <span style=\"color: gray; font-size: small; padding-left: 10px;\">({1})</span><br />",
member.Name, member.RoleName);
}
sb.AppendFormat("</td><td style=\"padding-left: 10px; max-width: 150px; text-align: left;\">{0}</td></tr>", task.Comment);
}
return sb.ToString();
}
private string BuildPerformedAttendanceHtml(List<PerformedAttendance> attendances)
{
if (attendances == null || !attendances.Any())
{
return "<tr><td style=\"padding: 10px 0px 5px;\" colspan=\"5\">" +
"<span style=\"line-height: 25.2px; color: #666666;margin:10px\">No Attendance Performed Today</span></td></tr>";
}
var sb = new StringBuilder();
foreach (var a in attendances)
{
sb.AppendFormat("<tr>" +
"<td style=\"text-align:left\"><span style=\"padding-left:10px;\">{0}</span></td>" +
"<td style=\"text-align:center\">{1}</td>" +
"<td style=\"text-align:center\">{2}</td>" +
"<td style=\"text-align:center\">{3}</td>" +
"<td style=\"padding-left:10px; max-width:150px\">{4}</td>" +
"</tr>",
a.Name,
a.RoleName,
a.InTime.ToString("dd-MMM-yyyy h:mm tt", CultureInfo.InvariantCulture),
a.OutTime?.ToString("dd-MMM-yyyy h:mm tt", CultureInfo.InvariantCulture) ?? "",
a.Comment);
}
return sb.ToString();
} }
public async Task SendEmailAsync(List<string> toEmails, string subject, string body) public async Task SendEmailAsync(List<string> toEmails, string subject, string body)

View File

@ -10,6 +10,6 @@ namespace MarcoBMS.Services.Service
Task SendResetPasswordSuccessEmail(string toEmail, string userName); Task SendResetPasswordSuccessEmail(string toEmail, string userName);
Task SendRequestDemoEmail(List<string> toEmails, InquiryEmailObject demoEmailObject); Task SendRequestDemoEmail(List<string> toEmails, InquiryEmailObject demoEmailObject);
Task SendEmailAsync(List<string> toEmails, string subject, string body); Task SendEmailAsync(List<string> toEmails, string subject, string body);
Task SendProjectStatisticsEmail(List<string> toEmails, ProjectStatisticReport report); Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, ProjectStatisticReport report);
} }
} }

View File

@ -6,8 +6,7 @@
}, },
"ConnectionStrings": { "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=MarcoBMS10"
"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSGuid"
}, },
"SmtpSettings": { "SmtpSettings": {
"SmtpServer": "smtp.gmail.com", "SmtpServer": "smtp.gmail.com",
@ -36,7 +35,7 @@
}, },
"MailingList": { "MailingList": {
"RequestDemoReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com", "RequestDemoReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com",
"ProjectStatisticsReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com" //"ProjectStatisticsReceivers": "ashutosh.nehete@marcoaiot.com;vikas@marcoaiot.com;umesh@marcoait.com"
}, },
"AWS": { "AWS": {
"AccessKey": "AKIARZDBH3VDMSUUY2FX", "AccessKey": "AKIARZDBH3VDMSUUY2FX",