diff --git a/Marco.Pms.Model/Dtos/Organization/CreateOrganizationDto.cs b/Marco.Pms.Model/Dtos/Organization/CreateOrganizationDto.cs index 8007e63..4844410 100644 --- a/Marco.Pms.Model/Dtos/Organization/CreateOrganizationDto.cs +++ b/Marco.Pms.Model/Dtos/Organization/CreateOrganizationDto.cs @@ -8,6 +8,6 @@ public required string Address { get; set; } public required string ContactNumber { get; set; } public string? logoImage { get; set; } - public required List ServiceIds { get; set; } + public List? ServiceIds { get; set; } } } diff --git a/Marco.Pms.Model/Dtos/Organization/UpdateOrganizationDto.cs b/Marco.Pms.Model/Dtos/Organization/UpdateOrganizationDto.cs index 61f37b1..039db72 100644 --- a/Marco.Pms.Model/Dtos/Organization/UpdateOrganizationDto.cs +++ b/Marco.Pms.Model/Dtos/Organization/UpdateOrganizationDto.cs @@ -7,6 +7,6 @@ public required string ContactPerson { get; set; } public required string Address { get; set; } public required string ContactNumber { get; set; } - public required List ServiceIds { get; set; } + public List? ServiceIds { get; set; } } } diff --git a/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs b/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs index 6c808e6..c4ef246 100644 --- a/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs +++ b/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs @@ -17,6 +17,6 @@ public required string OrganizationSize { get; set; } public required Guid IndustryId { get; set; } public required string Reference { get; set; } - public required List ServiceIds { get; set; } + public List? ServiceIds { get; set; } } } diff --git a/Marco.Pms.Model/ViewModels/Organization/OrganizationDetailsVM.cs b/Marco.Pms.Model/ViewModels/Organization/OrganizationDetailsVM.cs new file mode 100644 index 0000000..ca12c1c --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Organization/OrganizationDetailsVM.cs @@ -0,0 +1,24 @@ +using Marco.Pms.Model.ViewModels.Activities; + +namespace Marco.Pms.Model.ViewModels.Organization +{ + public class OrganizationDetailsVM + { + public Guid Id { get; set; } + public string? Name { get; set; } + public string? Email { get; set; } + public string? ContactPerson { get; set; } + public string? Address { get; set; } + public string? ContactNumber { get; set; } + public double SPRID { get; set; } + public int ActiveEmployeeCount { get; set; } + public int ActiveApplicationUserCount { get; set; } + public DateTime CreatedAt { get; set; } + public BasicEmployeeVM? CreatedBy { get; set; } + public BasicEmployeeVM? UpdatedBy { get; set; } + public DateTime? UpdatedAt { get; set; } + public bool IsActive { get; set; } + public List? Projects { get; set; } + public string? logoImage { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/Organization/ProjectServiceMappingVM.cs b/Marco.Pms.Model/ViewModels/Organization/ProjectServiceMappingVM.cs new file mode 100644 index 0000000..ed2ee03 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Organization/ProjectServiceMappingVM.cs @@ -0,0 +1,16 @@ +using Marco.Pms.Model.ViewModels.Master; +using Marco.Pms.Model.ViewModels.Projects; + +namespace Marco.Pms.Model.ViewModels.Organization +{ + public class ProjectServiceMappingVM + { + public BasicProjectVM? Project { get; set; } + public ServiceMasterVM? Service { get; set; } + public DateTime PlannedStartDate { get; set; } + public DateTime PlannedEndDate { get; set; } + public DateTime ActualStartDate { get; set; } + public DateTime? ActualEndDate { get; set; } + public bool IsActive { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/OrganizationController.cs b/Marco.Pms.Services/Controllers/OrganizationController.cs index 0af263e..1629490 100644 --- a/Marco.Pms.Services/Controllers/OrganizationController.cs +++ b/Marco.Pms.Services/Controllers/OrganizationController.cs @@ -55,66 +55,200 @@ namespace Marco.Pms.Services.Controllers public async Task GetOrganizarionListAsync([FromQuery] string? searchString, [FromQuery] double? sprid, [FromQuery] bool active = true, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20) { + _logger.LogDebug("Fetching organization list. SearchString: {SearchString}, SPRID: {SPRID}, Active: {Active}, Page: {PageNumber}, Size: {PageSize}", + searchString ?? "", sprid ?? 0, active, pageNumber, pageSize); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); - using var scope = _serviceScope.CreateScope(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var organizationQuery = _context.Organizations - .Where(o => o.IsActive == active); + // Base query filtering by active status + IQueryable organizationQuery = _context.Organizations.Where(o => o.IsActive == active); if (sprid.HasValue) { + // Filter by SPRID if provided organizationQuery = organizationQuery.Where(o => o.SPRID == sprid.Value); + _logger.LogDebug("Filtering organizations by SPRID: {SPRID}", sprid.Value); } else { - var organizationIds = await _context.TenantOrgMappings.Where(to => to.TenantId == tenantId && to.IsActive).Select(to => to.OrganizationId).ToListAsync(); + // Get organization IDs mapped to current tenant that are active + var organizationIds = await _context.TenantOrgMappings + .Where(to => to.TenantId == tenantId && to.IsActive) + .Select(to => to.OrganizationId) + .ToListAsync(); + organizationQuery = organizationQuery.Where(o => organizationIds.Contains(o.Id)); + _logger.LogDebug("Filtering organizations by tenant's mapped IDs count: {Count}", organizationIds.Count); if (!string.IsNullOrWhiteSpace(searchString)) { - organizationQuery = organizationQuery.Where(sp => - sp.Name.Contains(searchString) - //|| sp.ContactPerson.Contains(searchString) - //|| sp.Address.Contains(searchString) - //|| sp.Email.Contains(searchString) - //|| sp.ContactNumber.Contains(searchString) - ); + // Filter by search string on organization name -- extend here if needed + organizationQuery = organizationQuery.Where(o => o.Name.Contains(searchString)); + _logger.LogDebug("Filtering organizations by search string: {SearchString}", searchString); } } + // Get total count for pagination var totalCount = await organizationQuery.CountAsync(); var totalPages = (int)Math.Ceiling((double)totalCount / pageSize); + // Fetch page of organizations sorted by name var organizations = await organizationQuery - .OrderBy(e => e.Name) + .OrderBy(o => o.Name) .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); - var createdByIds = organizations.Where(o => o.CreatedById != null).Select(o => o.CreatedById).ToList(); - var updatedByIds = organizations.Where(o => o.UpdatedById != null).Select(o => o.UpdatedById).ToList(); + // Collect creator and updater employee IDs + var createdByIds = organizations.Where(o => o.CreatedById != null).Select(o => o.CreatedById!.Value).Distinct().ToList(); + var updatedByIds = organizations.Where(o => o.UpdatedById != null).Select(o => o.UpdatedById!.Value).Distinct().ToList(); - var employees = await _context.Employees.Where(e => createdByIds.Contains(e.Id) || updatedByIds.Contains(e.Id)).ToListAsync(); + // Fetch corresponding employee details in one query + var employeeIds = createdByIds.Union(updatedByIds).ToList(); + var employees = await _context.Employees.Where(e => employeeIds.Contains(e.Id)).ToListAsync(); + // Map data to view models including created and updated by employees var vm = organizations.Select(o => { - var result = _mapper.Map(o); - result.CreatedBy = employees.Where(e => e.Id == o.CreatedById).Select(e => _mapper.Map(e)).FirstOrDefault(); - result.UpdatedBy = employees.Where(e => e.Id == o.UpdatedById).Select(e => _mapper.Map(e)).FirstOrDefault(); - return result; - }); + var orgVm = _mapper.Map(o); + orgVm.CreatedBy = employees.Where(e => e.Id == o.CreatedById).Select(e => _mapper.Map(e)).FirstOrDefault(); + orgVm.UpdatedBy = employees.Where(e => e.Id == o.UpdatedById).Select(e => _mapper.Map(e)).FirstOrDefault(); + return orgVm; + }).ToList(); var response = new { CurrentPage = pageNumber, TotalPages = totalPages, - TotalEntites = totalCount, + TotalEntities = totalCount, Data = vm, }; - return Ok(ApiResponse.SuccessResponse(response, "Successfully fetched the Service Provider list", 200)); + _logger.LogInfo("Fetched {Count} organizations (Page {PageNumber} of {TotalPages})", vm.Count, pageNumber, totalPages); + + return Ok(ApiResponse.SuccessResponse(response, "Successfully fetched the organization list", 200)); + } + + [HttpGet("details/{id}")] + public async Task GetOrganizationDetailsAsync(Guid id) + { + _logger.LogDebug("Started fetching details for OrganizationId: {OrganizationId}", id); + + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + try + { + // Get the logged-in employee (for filter/permission checks) + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + + // Fetch the organization entity by Id + var organization = await _context.Organizations.FirstOrDefaultAsync(o => o.Id == id); + if (organization == null) + { + _logger.LogWarning("Organization not found for OrganizationId: {OrganizationId}", id); + return NotFound(ApiResponse.ErrorResponse("Organization not found", "Organization not found", 404)); + } + + // Fetch CreatedBy employee (with JobRole) + var createdByTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Employees + .Include(e => e.JobRole) + .FirstOrDefaultAsync(e => e.Id == organization.CreatedById); + }); + + // Fetch UpdatedBy employee (with JobRole) + var updatedByTask = Task.Run(async () => + { + if (organization.UpdatedById.HasValue) + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Employees + .Include(e => e.JobRole) + .FirstOrDefaultAsync(e => e.Id == organization.UpdatedById); + } + return null; + }); + + // Fetch the organization's service mappings and corresponding services + var orgServiceMappingTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.OrgServiceMappings + .Include(os => os.Service) + .Where(os => os.OrganizationId == id).ToListAsync(); + }); + + // Fetch active employees in the organization + var employeeListTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Employees + .Where(e => e.OrganizationId == id && e.IsActive).ToListAsync(); + }); + + await Task.WhenAll(createdByTask, updatedByTask, orgServiceMappingTask, employeeListTask); + + var createdByEmployee = createdByTask.Result; + var updatedByEmployee = updatedByTask.Result; + var orgServiceMappings = orgServiceMappingTask.Result; + var employeeList = employeeListTask.Result; + + var activeEmployeeCount = employeeList.Count; + var activeApplicationUserCount = employeeList.Count(e => e.HasApplicationAccess); + + // Start query for projects mapped to this organization (including project and service info) + var baseProjectOrgMappingQuery = _context.ProjectOrgMappings + .Include(po => po.ProjectService) + .ThenInclude(ps => ps!.Service) + .Include(po => po.ProjectService) + .ThenInclude(ps => ps!.Project) + .Where(po => po.OrganizationId == id && po.ProjectService != null); + + // If logged-in employee is not from the requested organization, restrict projects to those also mapped to their org + List projectOrgMappings; + if (loggedInEmployee.OrganizationId != id) + { + var projectIds = await _context.ProjectOrgMappings + .Include(po => po.ProjectService) + .Where(po => po.OrganizationId == loggedInEmployee.OrganizationId && po.ProjectService != null) + .Select(po => po.ProjectService!.ProjectId) + .ToListAsync(); + + projectOrgMappings = await baseProjectOrgMappingQuery + .Where(po => projectIds.Contains(po.ProjectService!.ProjectId)) + .ToListAsync(); + } + else + { + projectOrgMappings = await baseProjectOrgMappingQuery.ToListAsync(); + } + + // Map results to output view model + var response = _mapper.Map(organization); + response.ActiveApplicationUserCount = activeApplicationUserCount; + response.ActiveEmployeeCount = activeEmployeeCount; + response.CreatedBy = _mapper.Map(createdByEmployee); + response.UpdatedBy = _mapper.Map(updatedByEmployee); + response.Projects = _mapper.Map>(projectOrgMappings.Select(po => po.ProjectService).ToList()); + + _logger.LogInfo("Fetched organization details for OrganizationId: {OrganizationId}, Employee count: {EmployeeCount}, App user count: {AppUserCount}, Project count: {ProjectCount}", + id, activeEmployeeCount, activeApplicationUserCount, response.Projects.Count); + + return Ok(ApiResponse.SuccessResponse(response, "Successfully fetched the organization details", 200)); + } + catch (DbUpdateException dbEx) + { + _logger.LogError(dbEx, "Database exception while fetching details for OrganizationId: {OrganizationId}", id); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "A database exception occurred", 500)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Unhandled exception while fetching details for OrganizationId: {OrganizationId}", id); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "An internal exception occurred", 500)); + } } #endregion @@ -207,7 +341,7 @@ namespace Marco.Pms.Services.Controllers ?? await _context.JobRoles.FirstOrDefaultAsync(jr => jr.TenantId == tenantId); // Parse full name safely (consider improving split logic for multi-part names) - var fullName = model.ContactPerson?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); + var fullName = model.ContactPerson.Split(' ', StringSplitOptions.RemoveEmptyEntries); Employee newEmployee = new Employee { @@ -223,19 +357,22 @@ namespace Marco.Pms.Services.Controllers IsSystem = false, IsPrimary = true, OrganizationId = organization.Id, - HasApplicationAccess = user.Id != null + HasApplicationAccess = true }; _context.Employees.Add(newEmployee); // Map organization services - var serviceOrgMappings = model.ServiceIds.Select(s => new OrgServiceMapping + if (model.ServiceIds?.Any() ?? false) { - ServiceId = s, - OrganizationId = organization.Id - }).ToList(); + var serviceOrgMappings = model.ServiceIds.Select(s => new OrgServiceMapping + { + ServiceId = s, + OrganizationId = organization.Id + }).ToList(); - _context.OrgServiceMappings.AddRange(serviceOrgMappings); + _context.OrgServiceMappings.AddRange(serviceOrgMappings); + } // Persist all changes await _context.SaveChangesAsync(); @@ -593,66 +730,115 @@ namespace Marco.Pms.Services.Controllers #region =================================================================== Put Functions =================================================================== - //[HttpPut("edit/{id}")] - //public async Task UpdateServiceProviderAsync(Guid id, [FromBody] UpdateOrganizationDto model) - //{ - // await using var _context = await _dbContextFactory.CreateDbContextAsync(); - // using var scope = _serviceScope.CreateScope(); + [HttpPut("edit/{id}")] + public async Task UpdateOrganiationAsync(Guid id, [FromBody] UpdateOrganizationDto model) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); - // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + try + { + // Get the current logged-in employee + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - // var clientTask = Task.Run(async () => - // { - // await using var context = await _dbContextFactory.CreateDbContextAsync(); - // return await context.Clients.FirstOrDefaultAsync(c => c.PrimaryEmployeeId == loggedInEmployee.Id && c.TenantId == tenantId); - // }); - // var employeeTask = Task.Run(async () => - // { - // await using var context = await _dbContextFactory.CreateDbContextAsync(); - // return await context.Employees.AsNoTracking().FirstOrDefaultAsync(e => e.OrganizationId == id && e.IsPrimary); - // }); + _logger.LogDebug("Started updating service provider OrganizationId: {OrganizationId} by EmployeeId: {EmployeeId}", + id, loggedInEmployee.Id); - // await Task.WhenAll(clientTask, employeeTask); + // Check if the user is a tenant-level employee and restrict editing to their own org + var isTenantEmployee = await _context.Tenants.AnyAsync(t => t.Id == tenantId && t.OrganizationId == loggedInEmployee.OrganizationId); + if (!isTenantEmployee && loggedInEmployee.OrganizationId != id) + { + _logger.LogWarning("Access denied. Tenant-level employee {EmployeeId} attempted to update another organization (OrganizationId: {OrganizationId})", + loggedInEmployee.Id, id); + return StatusCode(403, ApiResponse.ErrorResponse("Access Denied", "User does not have permission to update the organization", 403)); + } - // var client = clientTask.Result; - // var employee = employeeTask.Result; + // Fetch the active organization entity + var organization = await _context.Organizations.FirstOrDefaultAsync(o => o.Id == id && o.IsActive); + if (organization == null) + { + _logger.LogWarning("Organization with Id {OrganizationId} not found or inactive.", id); + return NotFound(ApiResponse.ErrorResponse("Organization not found", "Organization not found", 404)); + } - // if (employee == null) - // { - // return NotFound(ApiResponse.ErrorResponse("Primary employee for service provider not found", "Primary employee for service provider not found in database", 400)); - // } + // Update basic organization fields + organization.Name = model.Name; + organization.ContactPerson = model.ContactPerson; + organization.Address = model.Address; + organization.ContactNumber = model.ContactNumber; + organization.UpdatedById = loggedInEmployee.Id; + organization.UpdatedAt = DateTime.UtcNow; - // if (!(loggedInEmployee.ApplicationUser?.IsRootUser ?? false) && !loggedInEmployee.IsPrimary && client == null && employee.Id == loggedInEmployee.Id) - // { - // return StatusCode(403, ApiResponse.ErrorResponse("Access Denied", "You do not have permission to create new service provider.", 403)); - // } - // if (model.Id == id) - // { - // return BadRequest(ApiResponse.ErrorResponse("Invalid Input", "Id from path parameters do not matches with id from model", 400)); - // } - // var serviceProvider = await _context.ServiceProviders.FirstOrDefaultAsync(sp => sp.Id == model.Id); - // if (serviceProvider == null) - // { - // return NotFound(ApiResponse.ErrorResponse("Service Provider not found", "Service Provider not found in database", 404)); - // } - // _mapper.Map(model, serviceProvider); + // Fetch the primary active employee of the organization + var employee = await _context.Employees.FirstOrDefaultAsync(e => e.OrganizationId == id && e.IsPrimary && e.IsActive); + if (employee == null) + { + _logger.LogWarning("Primary employee not found for OrganizationId: {OrganizationId}", id); + return NotFound(ApiResponse.ErrorResponse("Primary employee not found", "Primary employee not found", 404)); + } - // _context.ServiceProviders.Update(serviceProvider); + // Split contact person's name into first and last names + var fullName = (model.ContactPerson ?? string.Empty).Split(' ', StringSplitOptions.RemoveEmptyEntries); + employee.FirstName = fullName.Length > 0 ? fullName[0] : string.Empty; + employee.LastName = fullName.Length > 1 ? fullName[^1] : string.Empty; + employee.CurrentAddress = model.Address; + employee.PermanentAddress = model.Address; + employee.PhoneNumber = model.ContactNumber; - // var fullName = model.ContactPerson.Split(" "); + // Update organization's service mappings if service IDs are provided + if (model.ServiceIds?.Any() ?? false) + { + // Fetch existing service mappings (as no tracking for diff logic) + var orgServiceMappings = await _context.OrgServiceMappings + .AsNoTracking() + .Where(os => os.OrganizationId == id) + .ToListAsync(); - // employee.FirstName = fullName[0]; - // employee.LastName = fullName[fullName.Length - 1]; - // employee.CurrentAddress = model.Address; - // employee.PermanentAddress = model.Address; - // employee.PhoneNumber = model.PhoneNumber; + var existedServiceIds = orgServiceMappings.Select(os => os.ServiceId).ToList(); - // _context.Employees.Update(employee); - // await _context.SaveChangesAsync(); + // Determine new service mappings to add + var newServiceIds = model.ServiceIds.Except(existedServiceIds).ToList(); + var orgServicesToDelete = orgServiceMappings + .Where(s => !model.ServiceIds.Contains(s.ServiceId)) + .ToList(); - // var response = _mapper.Map(serviceProvider); - // return Ok(ApiResponse.SuccessResponse(response, "Successfully updated the service provider", 200)); - //} + // Add new service mappings + if (newServiceIds.Any()) + { + var newMappings = newServiceIds.Select(sid => new OrgServiceMapping + { + OrganizationId = id, + ServiceId = sid + }); + await _context.OrgServiceMappings.AddRangeAsync(newMappings); + } + + // Remove deleted service mappings + if (orgServicesToDelete.Any()) + { + _context.OrgServiceMappings.RemoveRange(orgServicesToDelete); + } + } + + await _context.SaveChangesAsync(); + + var response = _mapper.Map(organization); + + _logger.LogInfo("Successfully updated service provider OrganizationId: {OrganizationId} by EmployeeId: {EmployeeId}", + id, loggedInEmployee.Id); + + return Ok(ApiResponse.SuccessResponse(response, "Successfully updated the service provider", 200)); + } + catch (DbUpdateException dbEx) + { + _logger.LogError(dbEx, "Database exception occurred while updating OrganizationId: {OrganizationId}", id); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "A database exception occurred", 500)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Unhandled exception occurred while updating OrganizationId: {OrganizationId}", id); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "An internal exception occurred", 500)); + } + } #endregion diff --git a/Marco.Pms.Services/Controllers/TenantController.cs b/Marco.Pms.Services/Controllers/TenantController.cs index 521a6be..0c11daf 100644 --- a/Marco.Pms.Services/Controllers/TenantController.cs +++ b/Marco.Pms.Services/Controllers/TenantController.cs @@ -549,7 +549,11 @@ namespace Marco.Pms.Services.Controllers ApplicationUserId = applicationUser.Id, JobRole = adminJobRole, // Link to the newly created role CurrentAddress = model.BillingAddress, - OrganizationId = organization.Id + IsActive = true, + IsSystem = false, + IsPrimary = true, + OrganizationId = organization.Id, + HasApplicationAccess = true }; _context.Employees.Add(employeeUser); @@ -636,13 +640,16 @@ namespace Marco.Pms.Services.Controllers _context.ProjectAllocations.Add(projectAllocation); // Map organization services - var serviceOrgMappings = model.ServiceIds.Select(s => new OrgServiceMapping + if (model.ServiceIds?.Any() ?? false) { - ServiceId = s, - OrganizationId = organization.Id - }).ToList(); + var serviceOrgMappings = model.ServiceIds.Select(s => new OrgServiceMapping + { + ServiceId = s, + OrganizationId = organization.Id + }).ToList(); - _context.OrgServiceMappings.AddRange(serviceOrgMappings); + _context.OrgServiceMappings.AddRange(serviceOrgMappings); + } // All entities are now added to the context. Save them all in a single database operation. await _context.SaveChangesAsync(); diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 2dad4ba..a7aee31 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -47,6 +47,7 @@ namespace Marco.Pms.Services.MappingProfiles CreateMap(); CreateMap(); CreateMap(); + CreateMap(); CreateMap() .ForMember( dest => dest.Id, @@ -127,6 +128,7 @@ namespace Marco.Pms.Services.MappingProfiles CreateMap(); CreateMap(); CreateMap(); + CreateMap(); CreateMap() .ForMember( dest => dest.Id,