OrderRequestService.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using Microsoft.AspNetCore.Identity;
  2. using Microsoft.AspNetCore.WebUtilities;
  3. using Microsoft.EntityFrameworkCore;
  4. using Microsoft.Extensions.Configuration;
  5. using MTWorkHR.Application.Identity;
  6. using MTWorkHR.Application.Mapper;
  7. using MTWorkHR.Application.Models;
  8. using MTWorkHR.Core.Global;
  9. using MTWorkHR.Core.IRepositories;
  10. using MTWorkHR.Core.UnitOfWork;
  11. using MTWorkHR.Infrastructure.Entities;
  12. using MTWorkHR.Application.Services.Interfaces;
  13. using MTWorkHR.Core.Email;
  14. using MTWorkHR.Core.Entities;
  15. using MTWorkHR.Infrastructure.UnitOfWorks;
  16. using System.Linq.Dynamic.Core;
  17. using Microsoft.AspNetCore.Http;
  18. using MTWorkHR.Core.Entities.User;
  19. using MTWorkHR.Core.Entities.Base;
  20. using System.Linq;
  21. namespace MTWorkHR.Application.Services
  22. {
  23. public class OrderRequestService : BaseService<OrderRequest, OrderRequestDto, OrderRequestDto>, IOrderRequestService
  24. {
  25. private readonly IUnitOfWork _unitOfWork;
  26. private readonly IMailSender _emailSender;
  27. private readonly ApplicationUserManager _userManager;
  28. private readonly GlobalInfo _globalInfo;
  29. private readonly IUserService _userService;
  30. public OrderRequestService(IUnitOfWork unitOfWork, IMailSender emailSender, ApplicationUserManager userManager, GlobalInfo globalInfo, IUserService userService) : base(unitOfWork)
  31. {
  32. _unitOfWork = unitOfWork;
  33. _emailSender = emailSender;
  34. _userManager = userManager;
  35. _globalInfo = globalInfo;
  36. _userService = userService;
  37. }
  38. public override async Task<OrderRequestDto> GetById(long id)
  39. {
  40. var entity = await _unitOfWork.OrderRequest.GetByIdWithAllChildren(id);
  41. if(entity == null)
  42. {
  43. throw new AppException(ExceptionEnum.RecordNotExist);
  44. }
  45. var response = MapperObject.Mapper.Map<OrderRequestDto>(entity);
  46. response.Employee = await GetEmployeeDto(entity.RequestingEmployeeId);
  47. return response;
  48. }
  49. public async Task<OverTimeDto> GetOverTime(long id)
  50. {
  51. var entity = await _unitOfWork.OrderRequest.GetByIdWithAllChildren(id);
  52. if (entity == null)
  53. {
  54. throw new AppException(ExceptionEnum.RecordNotExist);
  55. }
  56. var response = MapperObject.Mapper.Map<OverTimeDto>(entity);
  57. response.Employee = await GetEmployeeDto(entity.RequestingEmployeeId);
  58. return response;
  59. }
  60. public async Task<BusinessTripDto> GetBusinessTrip(long id)
  61. {
  62. var entity = await _unitOfWork.OrderRequest.GetByIdWithAllChildren(id);
  63. if (entity == null)
  64. {
  65. throw new AppException(ExceptionEnum.RecordNotExist);
  66. }
  67. var response = MapperObject.Mapper.Map<BusinessTripDto>(entity);
  68. response.Employee = await GetEmployeeDto(entity.RequestingEmployeeId);
  69. return response;
  70. }
  71. public async Task<ServiceCertificateRequestDto> GetServiceCertificate(long id)
  72. {
  73. var entity = await _unitOfWork.OrderRequest.GetByIdWithAllChildren(id);
  74. if (entity == null)
  75. {
  76. throw new AppException(ExceptionEnum.RecordNotExist);
  77. }
  78. var response = MapperObject.Mapper.Map<ServiceCertificateRequestDto>(entity);
  79. response.Employee = await GetEmployeeDto(entity.RequestingEmployeeId);
  80. return response;
  81. }
  82. private async Task<EmployeeDto> GetEmployeeDto(string employeeId)
  83. {
  84. var user = await _userService.GetUserById(employeeId);
  85. var image = await _userService.GetProfileImage(employeeId);
  86. return new EmployeeDto
  87. {
  88. Id = employeeId,
  89. FirstName = user.FirstName,
  90. LastName = user.LastName,
  91. Email = user.Email,
  92. ProfileImagePath = image
  93. };
  94. }
  95. public async Task<PagingResultDto<OrderRequestDto>> GetAll(OrderPagingInputDto PagingInputDto)
  96. {
  97. var res = await _unitOfWork.OrderRequest.GetAllWithChildrenAsync();
  98. var query = res.Item1;
  99. if (_globalInfo.UserType != UserTypeEnum.Business)
  100. {
  101. query = query.Where(m => m.RequestingEmployeeId != null && m.RequestingEmployeeId == _globalInfo.UserId);
  102. }
  103. if (PagingInputDto.Filter != null)
  104. {
  105. var filter = PagingInputDto.Filter;
  106. query = query.Where(u => u.RequestComments!.Contains(filter)
  107. || u.OrderType.NameEn!.Contains(filter)
  108. || u.OrderType.NameAr!.Contains(filter)
  109. || u.LeaveType!.NameEn!.Contains(filter)
  110. || u.LeaveType!.NameAr!.Contains(filter));
  111. }
  112. if (PagingInputDto.SearchDate != null)
  113. {
  114. // query = query.Where(u => u.StartDate.Date <= PagingInputDto.SearchDate.Value.Date && u.EndDate!.Value.Date >= PagingInputDto.SearchDate.Value.Date) ;
  115. query = query.Where(u => u.CreateDate.Date == PagingInputDto.SearchDate.Value.Date) ;
  116. }
  117. if (PagingInputDto.OrderTypeId != null)
  118. {
  119. query = query.Where(u => u.OrderTypeId == PagingInputDto.OrderTypeId);
  120. }
  121. if (PagingInputDto.LeaveTypeId != null)
  122. {
  123. query = query.Where(u => u.LeaveTypeId == PagingInputDto.LeaveTypeId);
  124. }
  125. if (PagingInputDto.StatusId != null)
  126. {
  127. query = query.Where(u => (long?)u.OrderStatus == PagingInputDto.StatusId);
  128. }
  129. // UserName filter (join with users table)
  130. if (!string.IsNullOrEmpty(PagingInputDto.UserName))
  131. {
  132. var filter = PagingInputDto.UserName.ToLower();
  133. query = query.Join(
  134. _userManager.Users,
  135. order => order.RequestingEmployeeId,
  136. user => user.Id,
  137. (order, user) => new { Order = order, User = user }
  138. )
  139. .Where(joined =>
  140. (joined.User.FirstName + " " + joined.User.LastName).ToLower().Contains(filter)
  141. // ||joined.User.Email.ToLower().Contains(filter)
  142. )
  143. .Select(joined => joined.Order);
  144. }
  145. var total = await query.CountAsync();
  146. var order = query.OrderBy(PagingInputDto.OrderByField + " " + PagingInputDto.OrderType);
  147. var page = order.Skip((PagingInputDto.PageNumber * PagingInputDto.PageSize) - PagingInputDto.PageSize).Take(PagingInputDto.PageSize);
  148. var list = await MapToOrderRequestDtos(page);
  149. var response = new PagingResultDto<OrderRequestDto>
  150. {
  151. Result = list,
  152. Total = total
  153. };
  154. return response;
  155. }
  156. private async Task<List<OrderRequestDto>> MapToOrderRequestDtos(IQueryable<OrderRequest> page)
  157. {
  158. var list = MapperObject.Mapper.Map<List<OrderRequestDto>>(await page.ToListAsync());
  159. // Batch load user data to improve performance
  160. var employeeIds = list.Where(x => x.RequestingEmployeeId != null)
  161. .Select(x => x.RequestingEmployeeId)
  162. .Distinct()
  163. .ToList();
  164. if (employeeIds.Any())
  165. {
  166. var users = await _userService.GetUsersWithAttachmentsByIds(employeeIds);
  167. if (users != null)
  168. {
  169. var userLookup = users.ToDictionary(u => u.Id, u => u);
  170. foreach (var item in list)
  171. {
  172. if (item.RequestingEmployeeId != null && userLookup.TryGetValue(item.RequestingEmployeeId, out var user))
  173. {
  174. item.Employee = new EmployeeDto
  175. {
  176. Id = user.Id,
  177. FirstName = user.FirstName,
  178. LastName = user.LastName,
  179. Email = user.Email,
  180. Position = user.Position ?? user.JobTitleName,
  181. EmployeeName = $"{user.FirstName} {user.LastName}",
  182. ProfileImagePath = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9)?.FilePath
  183. };
  184. }
  185. }
  186. }
  187. }
  188. return list;
  189. }
  190. public override async Task<OrderRequestDto> Create(OrderRequestDto input)
  191. {
  192. var period = DateTime.Now.Year;
  193. input.LeaveTypeId = (input.LeaveTypeId != null && input.LeaveTypeId > 0 ) ? input.LeaveTypeId : null;
  194. input.OrderStatus = (input.OrderStatus != null && input.OrderStatus > 0 ) ? input.OrderStatus : ApprovalStatusEnum.Pending;
  195. if (string.IsNullOrEmpty( input.RequestingEmployeeId))
  196. {
  197. input.RequestingEmployeeId = _globalInfo.UserId;
  198. }
  199. if (input is BusinessTripDto)
  200. {
  201. input.OrderTypeId = 3;
  202. }
  203. else if (input is OverTimeDto)
  204. {
  205. input.OrderTypeId = 2;
  206. }
  207. else if (input is ServiceCertificateRequestDto)
  208. {
  209. input.OrderTypeId = 5;
  210. }
  211. var contract = await _unitOfWork.Contract.GetLatestActiveContract(input.RequestingEmployeeId);
  212. if(contract!= null)
  213. input.ContractId = contract.Id;
  214. if (input.OrderTypeId == 1 && input.LeaveTypeId == 1) // Annual vacation in contract
  215. {
  216. if (contract != null)
  217. {
  218. var allocation = await _unitOfWork.OrderAllocation.GetUserAllocations(input.RequestingEmployeeId, input.OrderTypeId, input.LeaveTypeId, contract.Id, period);
  219. if (allocation is null)
  220. {
  221. throw new AppException(ExceptionEnum.NoVacationBalance, "You do not have any allocations for this leave type.");
  222. }
  223. else
  224. {
  225. int sub = (int)(input.EndDate.Date - input.StartDate.Date).TotalDays;
  226. int daysRequested = input.TotalDays > 0 ? (int)input.TotalDays : sub;
  227. if (daysRequested > allocation.NumberOfDays)
  228. {
  229. throw new AppException(ExceptionEnum.NoVacationBalance, "You do not have enough days for this request");
  230. }
  231. }
  232. }
  233. }
  234. var orderRequest = MapperObject.Mapper.Map<OrderRequest>(input);
  235. orderRequest = await _unitOfWork.OrderRequest.AddAsync(orderRequest);
  236. await _unitOfWork.CompleteAsync();
  237. try
  238. {
  239. var requestingEmployee = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == input.RequestingEmployeeId);
  240. var sendMailResult = await _emailSender.SendEmail(new EmailMessage
  241. {
  242. To = requestingEmployee.Email,
  243. Body = $"Your leave request for {input.StartDate:D} to {input.EndDate:D} " +
  244. $"has been submitted successfully.",
  245. Subject = "Leave Request Submitted",
  246. userId = input.RequestingEmployeeId
  247. });
  248. if (!sendMailResult)
  249. {
  250. throw new AppException("User created, but could not send the email!");
  251. }
  252. }
  253. catch (Exception ex)
  254. {
  255. //// Log or handle error, but don't throw...
  256. }
  257. var res = await _unitOfWork.OrderRequest.GetByIdWithTypes(orderRequest.Id);
  258. var response = MapperObject.Mapper.Map<OrderRequestDto>(res);
  259. if (response.RequestingEmployeeId != null)
  260. {
  261. var user = await _userService.GetUserWithAttachmentById(response.RequestingEmployeeId);
  262. if (user != null)
  263. {
  264. response.Employee = new EmployeeDto { Id = response.RequestingEmployeeId, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email };
  265. var attach = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
  266. response.Employee.ProfileImagePath = attach?.FilePath;
  267. //if (attach != null)
  268. // using (var stream = new MemoryStream(attach?.Content))
  269. // {
  270. // var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName)
  271. // {
  272. // Headers = new HeaderDictionary(),
  273. // ContentType = attach?.ContentType,
  274. // };
  275. // System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
  276. // {
  277. // FileName = file.FileName
  278. // };
  279. // file.ContentDisposition = cd.ToString();
  280. // response.Employee.ProfileImage = file;
  281. // }
  282. }
  283. }
  284. return response;
  285. }
  286. public async Task<OrderRequestDto> ChangeStatus(long id, int statusId )
  287. {
  288. var orderRequest = await _unitOfWork.OrderRequest.GetByIdAsync(id);
  289. if (orderRequest == null)
  290. {
  291. throw new AppException(ExceptionEnum.RecordNotExist);
  292. }
  293. orderRequest.OrderStatus = (ApprovalStatusEnum)statusId;
  294. if (orderRequest.OrderStatus == ApprovalStatusEnum.Approved)
  295. {
  296. long? contractId = orderRequest.ContractId;
  297. if (contractId == null || contractId == 0)
  298. {
  299. var contract = await _unitOfWork.Contract.GetLatestActiveContract(orderRequest.RequestingEmployeeId);
  300. contractId = contract.Id;
  301. }
  302. var allocation = await _unitOfWork.OrderAllocation.GetUserAllocations(orderRequest.RequestingEmployeeId, orderRequest.OrderTypeId, orderRequest.LeaveTypeId, contractId.Value, DateTime.Now.Year);
  303. if (allocation != null)
  304. {
  305. int daysRequested = !orderRequest.EndDate.HasValue ? 1 : (int)(orderRequest.EndDate.Value - orderRequest.StartDate).TotalDays;
  306. allocation.NumberOfDays -= daysRequested;
  307. }
  308. }
  309. await _unitOfWork.CompleteAsync();
  310. var response = MapperObject.Mapper.Map<OrderRequestDto>(orderRequest);
  311. return response;
  312. }
  313. }
  314. }