diff --git a/src/Spd.Manager.Licence.UnitTest/BizLicenceAppManagerTest.cs b/src/Spd.Manager.Licence.UnitTest/BizLicenceAppManagerTest.cs index 6f46fb550..110d6ae97 100644 --- a/src/Spd.Manager.Licence.UnitTest/BizLicenceAppManagerTest.cs +++ b/src/Spd.Manager.Licence.UnitTest/BizLicenceAppManagerTest.cs @@ -8,6 +8,8 @@ using Spd.Resource.Repository.LicApp; using Spd.Resource.Repository.Licence; using Spd.Resource.Repository.LicenceFee; +using Spd.Resource.Repository.PersonLicApplication; +using Spd.Resource.Repository.Tasks; using Spd.Utilities.FileStorage; using Spd.Utilities.Shared.Exceptions; @@ -23,6 +25,7 @@ public class BizLicenceAppManagerTest private Mock mockTransientFileStorageService = new(); private Mock mockBizLicAppRepo = new(); private Mock mockBizContactRepo = new(); + private Mock mockTaskRepo = new(); private BizLicAppManager sut; public BizLicenceAppManagerTest() @@ -47,7 +50,8 @@ public BizLicenceAppManagerTest() mockMainFileService.Object, mockTransientFileStorageService.Object, mockBizContactRepo.Object, - mockBizLicAppRepo.Object); + mockBizLicAppRepo.Object, + mockTaskRepo.Object); } [Fact] @@ -252,6 +256,8 @@ public async void Handle_BizLicAppRenewCommand_Return_BizLicAppCommandResponse() { Items = new List { originalLicence } }); + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.Is(m => m == originalApplicationId), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp() { LicenceAppId = originalApplicationId, BizId = bizId }); mockBizLicAppRepo.Setup(a => a.CreateBizLicApplicationAsync(It.Is( m => m.OriginalApplicationId == originalApplicationId && m.OriginalLicenceId == originalLicenceId), CancellationToken.None)) @@ -261,7 +267,7 @@ public async void Handle_BizLicAppRenewCommand_Return_BizLicAppCommandResponse() BizLicAppSubmitRequest request = new() { - ApplicationTypeCode = Shared.ApplicationTypeCode.Renewal, + ApplicationTypeCode = ApplicationTypeCode.Renewal, OriginalLicenceId = originalLicenceId, OriginalApplicationId = originalApplicationId, NoBranding = false, @@ -297,7 +303,7 @@ public async void Handle_BizLicAppRenewCommand_WithWrongApplicationType_Throw_Ex // Arrange BizLicAppSubmitRequest request = new() { - ApplicationTypeCode = Shared.ApplicationTypeCode.New + ApplicationTypeCode = ApplicationTypeCode.New }; BizLicAppRenewCommand cmd = new(request, new List()); @@ -320,7 +326,7 @@ public async void Handle_BizLicAppRenewCommand_WithoutOriginalLicence_Throw_Exce BizLicAppSubmitRequest request = new() { - ApplicationTypeCode = Shared.ApplicationTypeCode.Renewal + ApplicationTypeCode = ApplicationTypeCode.Renewal }; BizLicAppRenewCommand cmd = new(request, new List()); @@ -348,7 +354,36 @@ public async void Handle_BizLicAppRenewCommand_WithInvalidExpirationDate_Throw_E BizLicAppSubmitRequest request = new() { - ApplicationTypeCode = Shared.ApplicationTypeCode.Renewal + ApplicationTypeCode = ApplicationTypeCode.Renewal + }; + BizLicAppRenewCommand cmd = new(request, new List()); + + // Action + Func act = () => sut.Handle(cmd, CancellationToken.None); + + // Assert + await Assert.ThrowsAsync(act); + } + + [Fact] + public async void Handle_BizLicAppRenewCommand_WithoutLinkedBusiness_Throw_Exception() + { + // Arrange + DateTime dateTime = DateTime.UtcNow.AddDays(Constants.LicenceWith123YearsRenewValidBeforeExpirationInDays); + DateOnly expiryDate = new(dateTime.Year, dateTime.Month, dateTime.Day); + LicenceResp originalLicence = new() { LicenceAppId = Guid.NewGuid(), ExpiryDate = expiryDate }; + mockLicRepo.Setup(a => a.QueryAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new LicenceListResp() + { + Items = new List() { originalLicence } + }); + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp()); + + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.Renewal, + OriginalApplicationId = Guid.NewGuid(), }; BizLicAppRenewCommand cmd = new(request, new List()); @@ -374,10 +409,13 @@ public async void Handle_BizLicAppRenewCommand_WithMissingFiles_Throw_Exception( { Items = new List { originalLicence } }); - + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp() { LicenceAppId = Guid.NewGuid(), BizId = Guid.NewGuid() }); + BizLicAppSubmitRequest request = new() { - ApplicationTypeCode = Shared.ApplicationTypeCode.Renewal, + ApplicationTypeCode = ApplicationTypeCode.Renewal, + OriginalApplicationId = Guid.NewGuid(), NoBranding = true, UseDogs = false }; @@ -391,6 +429,172 @@ public async void Handle_BizLicAppRenewCommand_WithMissingFiles_Throw_Exception( await Assert.ThrowsAsync(act); } + [Fact] + public async void Handle_BizLicAppUpdateCommand_CreateNewApplication_Return_BizLicAppCommandResponse() + { + // Arrange + Guid originalApplicationId = Guid.NewGuid(); + Guid originalLicenceId = Guid.NewGuid(); + Guid newLicAppId = Guid.NewGuid(); + Guid bizId = Guid.NewGuid(); + LicenceResp originalLicence = fixture.Build() + .With(r => r.LicenceAppId, originalApplicationId) + .With(r => r.LicenceId, originalLicenceId) + .Create(); + LicenceFeeResp licenceFeeResp = new() { Amount = 100 }; + mockLicRepo.Setup(a => a.QueryAsync(It.Is(q => q.LicenceId == originalLicenceId), CancellationToken.None)) + .ReturnsAsync(new LicenceListResp() + { + Items = new List { originalLicence } + }); + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.Is(m => m == originalApplicationId), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp() { LicenceAppId = originalApplicationId, BizId = bizId }); + mockBizLicAppRepo.Setup(a => a.CreateBizLicApplicationAsync(It.Is( + m => m.OriginalApplicationId == originalApplicationId && + m.OriginalLicenceId == originalLicenceId), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationCmdResp(newLicAppId, bizId)); + mockLicFeeRepo.Setup(m => m.QueryAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new LicenceFeeListResp() { LicenceFees = new List { licenceFeeResp } }); + + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.Update, + OriginalLicenceId = originalLicenceId, + OriginalApplicationId = originalApplicationId, + NoBranding = false, + UseDogs = true, + Reprint = true, + CategoryCodes = new List() { WorkerCategoryTypeCode.ArmouredCarGuard } + }; + BizLicAppUpdateCommand cmd = new(request, new List()); + + // Action + var result = await sut.Handle(cmd, CancellationToken.None); + + // Assert + Assert.IsType(result); + Assert.Equal(newLicAppId, result.LicenceAppId); + Assert.Equal(licenceFeeResp.Amount, result.Cost); + } + + [Fact] + public async void Handle_BizLicAppUpdateCommand_UpdateApplication_Return_BizLicAppCommandResponse() + { + // Arrange + Guid originalApplicationId = Guid.NewGuid(); + Guid originalLicenceId = Guid.NewGuid(); + Guid bizId = Guid.NewGuid(); + LicenceResp originalLicence = fixture.Build() + .With(r => r.LicenceAppId, originalApplicationId) + .With(r => r.LicenceId, originalLicenceId) + .Create(); + + mockLicRepo.Setup(a => a.QueryAsync(It.Is(q => q.LicenceId == originalLicenceId), CancellationToken.None)) + .ReturnsAsync(new LicenceListResp() + { + Items = new List { originalLicence } + }); + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.Is(m => m == originalApplicationId), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp() + { + LicenceAppId = originalApplicationId, + BizId = bizId , + UseDogs = true, + CategoryCodes = new List() { WorkerCategoryTypeEnum.ArmouredCarGuard } + }); + mockBizLicAppRepo.Setup(a => a.SaveBizLicApplicationAsync(It.Is( + m => m.LicenceAppId == originalApplicationId && + m.ApplicantId == bizId), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationCmdResp(originalApplicationId, bizId)); + + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.Update, + OriginalLicenceId = originalLicenceId, + OriginalApplicationId = originalApplicationId, + NoBranding = false, + UseDogs = true, + Reprint = false, + CategoryCodes = new List() { WorkerCategoryTypeCode.ArmouredCarGuard } + }; + BizLicAppUpdateCommand cmd = new(request, new List()); + + // Action + var result = await sut.Handle(cmd, CancellationToken.None); + + // Assert + Assert.IsType(result); + Assert.Equal(originalApplicationId, result.LicenceAppId); + Assert.Equal(0, result.Cost); + } + + [Fact] + public async void Handle_BizLicAppUpdateCommand_WithWrongApplicationType_Throw_Exception() + { + // Arrange + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.New + }; + BizLicAppUpdateCommand cmd = new(request, new List()); + + // Action + Func act = () => sut.Handle(cmd, CancellationToken.None); + + // Assert + await Assert.ThrowsAsync(act); + } + + [Fact] + public async void Handle_BizLicAppUpdateCommand_WithoutOriginalLicence_Throw_Exception() + { + // Arrange + mockLicRepo.Setup(a => a.QueryAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new LicenceListResp() + { + Items = new List() + }); + + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.Update + }; + BizLicAppUpdateCommand cmd = new(request, new List()); + + // Action + Func act = () => sut.Handle(cmd, CancellationToken.None); + + // Assert + await Assert.ThrowsAsync(act); + } + + [Fact] + public async void Handle_BizLicAppUpdateCommand_WithoutLinkedBusiness_Throw_Exception() + { + // Arrange + LicenceResp originalLicence = new() { LicenceAppId = Guid.NewGuid() }; + mockLicRepo.Setup(a => a.QueryAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new LicenceListResp() + { + Items = new List() { originalLicence } + }); + mockBizLicAppRepo.Setup(a => a.GetBizLicApplicationAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new BizLicApplicationResp()); + + BizLicAppSubmitRequest request = new() + { + ApplicationTypeCode = ApplicationTypeCode.Update, + OriginalApplicationId = Guid.NewGuid(), + }; + BizLicAppUpdateCommand cmd = new(request, new List()); + + // Action + Func act = () => sut.Handle(cmd, CancellationToken.None); + + // Assert + await Assert.ThrowsAsync(act); + } + [Fact] public async void Handle_BrandImageQuery_WithDraftApplication_ShouldGetImageFromTransientStorage() { diff --git a/src/Spd.Manager.Licence/BizLicAppContract.cs b/src/Spd.Manager.Licence/BizLicAppContract.cs index 520f11227..6e0f214e9 100644 --- a/src/Spd.Manager.Licence/BizLicAppContract.cs +++ b/src/Spd.Manager.Licence/BizLicAppContract.cs @@ -56,6 +56,7 @@ public record BizLicAppSubmitRequest : BizLicenceApp public IEnumerable? PreviousDocumentIds { get; set; } //documentUrlId, used for renew public Guid? OriginalApplicationId { get; set; } //for new, it should be null. for renew, replace, update, it should be original application id. public Guid? OriginalLicenceId { get; set; } //for new, it should be null. for renew, replace, update, it should be original licence id. + public bool? Reprint { get; set; } } public record BizLicAppCommandResponse : LicenceAppUpsertResponse { diff --git a/src/Spd.Manager.Licence/BizLicAppManager.cs b/src/Spd.Manager.Licence/BizLicAppManager.cs index 86ca9d3d3..f6b93d09b 100644 --- a/src/Spd.Manager.Licence/BizLicAppManager.cs +++ b/src/Spd.Manager.Licence/BizLicAppManager.cs @@ -9,9 +9,12 @@ using Spd.Resource.Repository.Licence; using Spd.Resource.Repository.LicenceFee; using Spd.Resource.Repository.PersonLicApplication; +using Spd.Resource.Repository.Tasks; +using Spd.Utilities.Dynamics; using Spd.Utilities.FileStorage; using Spd.Utilities.Shared.Exceptions; using System.Net; +using System.Text; namespace Spd.Manager.Licence; internal class BizLicAppManager : @@ -30,6 +33,7 @@ internal class BizLicAppManager : { private readonly IBizLicApplicationRepository _bizLicApplicationRepository; private readonly IBizContactRepository _bizContactRepository; + private readonly ITaskRepository _taskRepository; public BizLicAppManager( ILicenceRepository licenceRepository, @@ -40,7 +44,8 @@ public BizLicAppManager( IMainFileStorageService mainFileStorageService, ITransientFileStorageService transientFileStorageService, IBizContactRepository bizContactRepository, - IBizLicApplicationRepository bizApplicationRepository) + IBizLicApplicationRepository bizApplicationRepository, + ITaskRepository taskRepository) : base(mapper, documentUrlRepository, feeRepository, @@ -51,6 +56,7 @@ public BizLicAppManager( { _bizLicApplicationRepository = bizApplicationRepository; _bizContactRepository = bizContactRepository; + _taskRepository = taskRepository; } public async Task Handle(GetBizLicAppQuery query, CancellationToken cancellationToken) @@ -74,7 +80,7 @@ public async Task Handle(BizLicAppUpsertCommand cmd, C cancellationToken); if (hasDuplicate) - throw new ApiException(HttpStatusCode.Forbidden, "Biz already has the same kind of licence or licence application"); + throw new ApiException(HttpStatusCode.Forbidden, "Business already has the same kind of licence or licence application"); SaveBizLicApplicationCmd saveCmd = _mapper.Map(cmd.BizLicAppUpsertRequest); saveCmd.UploadedDocumentEnums = GetUploadedDocumentEnumsFromDocumentInfo((List?)cmd.BizLicAppUpsertRequest.DocumentInfos); @@ -131,6 +137,10 @@ public async Task Handle(BizLicAppRenewCommand cmd, Ca || DateTime.UtcNow > originalLic.ExpiryDate.ToDateTime(new TimeOnly(0, 0))) throw new ArgumentException($"the application can only be renewed within {Constants.LicenceWith123YearsRenewValidBeforeExpirationInDays} days of the expiry date."); + BizLicApplicationResp originaBizlLic = await _bizLicApplicationRepository.GetBizLicApplicationAsync((Guid)cmd.LicenceRequest.OriginalApplicationId, cancellationToken); + if (originaBizlLic.BizId == null) + throw new ArgumentException("there is no business related to the application."); + var existingFiles = await GetExistingFileInfo( cmd.LicenceRequest.OriginalApplicationId, cmd.LicenceRequest.PreviousDocumentIds, @@ -139,10 +149,19 @@ await ValidateFilesForRenewUpdateAppAsync(cmd.LicenceRequest, cmd.LicAppFileInfos.ToList(), cancellationToken); + // Create new app CreateBizLicApplicationCmd createApp = _mapper.Map(request); createApp.UploadedDocumentEnums = GetUploadedDocumentEnums(cmd.LicAppFileInfos, existingFiles); BizLicApplicationCmdResp response = await _bizLicApplicationRepository.CreateBizLicApplicationAsync(createApp, cancellationToken); + // Update members + if (cmd.LicenceRequest.Members != null) + await UpdateMembersAsync(cmd.LicenceRequest.Members, + (Guid)originaBizlLic.BizId, + (Guid)originaBizlLic.LicenceAppId, + cancellationToken); + + // Upload new files await UploadNewDocsAsync(null, cmd.LicAppFileInfos, response?.LicenceAppId, @@ -154,7 +173,6 @@ await UploadNewDocsAsync(null, response?.AccountId, cancellationToken); - if (response?.LicenceAppId == null) throw new ApiException(HttpStatusCode.InternalServerError, "Create a new application failed."); // Copying all old files to new application in PreviousFileIds if (cmd.LicenceRequest.PreviousDocumentIds != null && cmd.LicenceRequest.PreviousDocumentIds.Any()) { @@ -172,7 +190,76 @@ await _documentRepository.ManageAsync( public async Task Handle(BizLicAppUpdateCommand cmd, CancellationToken cancellationToken) { - throw new NotImplementedException(); + BizLicAppSubmitRequest request = cmd.LicenceRequest; + if (cmd.LicenceRequest.ApplicationTypeCode != ApplicationTypeCode.Update) + throw new ArgumentException("should be an update request"); + + // Validation: check if original licence meet update condition. + LicenceListResp originalLicences = await _licenceRepository.QueryAsync( + new LicenceQry() { LicenceId = request.OriginalLicenceId }, + cancellationToken); + if (originalLicences == null || !originalLicences.Items.Any()) + throw new ArgumentException("cannot find the licence that needs to be updated."); + + BizLicApplicationResp originalLic = await _bizLicApplicationRepository.GetBizLicApplicationAsync((Guid)cmd.LicenceRequest.OriginalApplicationId, cancellationToken); + if (originalLic.BizId == null) + throw new ArgumentException("there is no business related to the application."); + + ChangeSpec changes = await MakeChanges(originalLic, request, cancellationToken); + BizLicApplicationCmdResp? response = null; + decimal? cost = 0; + + // Create new app, else update existing one + if ((request.Reprint != null && request.Reprint.Value) || changes.CategoriesChanged || changes.UseDogsChanged) + { + var existingFiles = await GetExistingFileInfo( + cmd.LicenceRequest.OriginalApplicationId, + cmd.LicenceRequest.PreviousDocumentIds, + cancellationToken); + CreateBizLicApplicationCmd createApp = _mapper.Map(request); + createApp.UploadedDocumentEnums = GetUploadedDocumentEnums(cmd.LicAppFileInfos, existingFiles); + response = await _bizLicApplicationRepository.CreateBizLicApplicationAsync(createApp, cancellationToken); + + // Upload new files + await UploadNewDocsAsync(null, + cmd.LicAppFileInfos, + response?.LicenceAppId, + null, + null, + null, + null, + null, + response?.AccountId, + cancellationToken); + + // Copying all old files to new application in PreviousFileIds + if (cmd.LicenceRequest.PreviousDocumentIds != null && cmd.LicenceRequest.PreviousDocumentIds.Any()) + { + foreach (var docUrlId in cmd.LicenceRequest.PreviousDocumentIds) + { + await _documentRepository.ManageAsync( + new CopyDocumentCmd(docUrlId, response.LicenceAppId, response.AccountId), + cancellationToken); + } + } + + cost = await CommitApplicationAsync(request, response.LicenceAppId, cancellationToken); + } + else + { + SaveBizLicApplicationCmd saveCmd = _mapper.Map(request); + saveCmd.ApplicantId = (Guid)originalLic.BizId; + response = await _bizLicApplicationRepository.SaveBizLicApplicationAsync(saveCmd, cancellationToken); + } + + // Update members + if (cmd.LicenceRequest.Members != null) + await UpdateMembersAsync(cmd.LicenceRequest.Members, + (Guid)originalLic.BizId, + (Guid)originalLic.LicenceAppId, + cancellationToken); + + return new BizLicAppCommandResponse { LicenceAppId = response.LicenceAppId, Cost = cost }; } public async Task Handle(GetBizMembersQuery qry, CancellationToken ct) @@ -370,4 +457,78 @@ private async Task ValidateFilesForRenewUpdateAppAsync(BizLicAppSubmitRequest re throw new ApiException(HttpStatusCode.BadRequest, "No more than 1 armoured car guard registrar document is allowed."); } } + + private async Task MakeChanges(BizLicApplicationResp originalApp, + BizLicAppSubmitRequest newRequest, + CancellationToken ct) + { + ChangeSpec changes = new(); + + // Categories changed + if (newRequest.CategoryCodes.Count() != originalApp.CategoryCodes.Count()) + changes.CategoriesChanged = true; + else + { + List newList = newRequest.CategoryCodes.ToList(); + newList.Sort(); + List originalList = originalApp.CategoryCodes.Select(c => Enum.Parse(c.ToString())).ToList(); + originalList.Sort(); + if (!newList.SequenceEqual(originalList)) changes.CategoriesChanged = true; + } + + //UseDogs changed + if (newRequest.UseDogs != originalApp.UseDogs) + changes.UseDogsChanged = true; + + if (changes.CategoriesChanged) + { + StringBuilder previousCategories = new(); + StringBuilder updatedCategories = new(); + + foreach (WorkerCategoryTypeCode category in originalApp.CategoryCodes) + previousCategories.AppendLine(category.ToString()); + + foreach (WorkerCategoryTypeCode category in newRequest.CategoryCodes) + updatedCategories.AppendLine(category.ToString()); + + await _taskRepository.ManageAsync(new CreateTaskCmd() + { + Description = $"Request to update the license category applicable on the {originalApp.ExpiredLicenceNumber} \n " + + $"Previous Categories: {previousCategories} \n " + + $"Updated Categories: {updatedCategories}", + DueDateTime = DateTimeOffset.Now.AddDays(1), + Subject = $"License Category update {originalApp.ExpiredLicenceNumber}", + TaskPriorityEnum = TaskPriorityEnum.Normal, + RegardingAccountId = originalApp.BizId, + AssignedTeamId = Guid.Parse(DynamicsConstants.Licensing_Client_Service_Team_Guid), + LicenceId = originalApp.ExpiredLicenceId + }, ct); + } + + if (changes.UseDogsChanged) + { + await _taskRepository.ManageAsync(new CreateTaskCmd() + { + Description = $"Below Dog's Handers information needs to be updated in the business license {originalApp.ExpiredLicenceNumber} \n " + + $"Use of dog : Explosives detection / Drug detection / Protection (As described in the DSV certificate) \n " + + $"DSV Certificate Number \n " + + $"Expiry Date \n" + + $"DSV certificate (Attachment)", + DueDateTime = DateTimeOffset.Now.AddDays(1), + Subject = $"Dog validation information to be updated for Business License {originalApp.ExpiredLicenceNumber}", + TaskPriorityEnum = TaskPriorityEnum.Normal, + RegardingAccountId = originalApp.BizId, + AssignedTeamId = Guid.Parse(DynamicsConstants.Licensing_Client_Service_Team_Guid), + LicenceId = originalApp.ExpiredLicenceId + }, ct); + } + + return changes; + } + + private sealed record ChangeSpec + { + public bool CategoriesChanged { get; set; } + public bool UseDogsChanged { get; set; } + } } \ No newline at end of file diff --git a/src/Spd.Manager.Licence/Mappings.cs b/src/Spd.Manager.Licence/Mappings.cs index ab8464a59..9ec6eb374 100644 --- a/src/Spd.Manager.Licence/Mappings.cs +++ b/src/Spd.Manager.Licence/Mappings.cs @@ -107,7 +107,7 @@ public Mappings() .ForPath(d => d.MailingAddress.PostalCode, opt => opt.MapFrom(s => s.MailingAddress.PostalCode)) .ForPath(d => d.MailingAddress.Country, opt => opt.MapFrom(s => s.MailingAddress.Country)); - CreateMap() + CreateMap() .ForMember(d => d.WorkerLicenceTypeCode, opt => opt.MapFrom(s => s.WorkerLicenceTypeCode)) .ForMember(d => d.CategoryCodes, opt => opt.MapFrom(s => GetCategories(s.CategoryCodes))) .ForMember(d => d.GivenName, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.GivenName : s.ApplicantContactInfo.GivenName)) @@ -121,25 +121,24 @@ public Mappings() .ForMember(d => d.ManagerMiddleName1, opt => opt.MapFrom(s => s.BizManagerContactInfo.MiddleName1)) .ForMember(d => d.ManagerMiddleName2, opt => opt.MapFrom(s => s.BizManagerContactInfo.MiddleName2)) .ForMember(d => d.ManagerEmailAddress, opt => opt.MapFrom(s => s.BizManagerContactInfo.EmailAddress)) - .ForMember(d => d.ManagerPhoneNumber, opt => opt.MapFrom(s => s.BizManagerContactInfo.PhoneNumber)) + .ForMember(d => d.ManagerPhoneNumber, opt => opt.MapFrom(s => s.BizManagerContactInfo.PhoneNumber)); + + CreateMap() + .IncludeBase() .ForPath(d => d.PrivateInvestigatorSwlInfo.LicenceId, opt => opt.MapFrom(s => s.PrivateInvestigatorSwlInfo == null ? null : s.PrivateInvestigatorSwlInfo.LicenceId)); CreateMap() + .IncludeBase() .ForMember(d => d.ApplicantId, opt => opt.MapFrom(s => s.BizId)) - .ForMember(d => d.WorkerLicenceTypeCode, opt => opt.MapFrom(s => s.WorkerLicenceTypeCode)) - .ForMember(d => d.CategoryCodes, opt => opt.MapFrom(s => GetCategories(s.CategoryCodes))) - .ForMember(d => d.GivenName, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.GivenName : s.ApplicantContactInfo.GivenName)) - .ForMember(d => d.Surname, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.Surname : s.ApplicantContactInfo.Surname)) - .ForMember(d => d.MiddleName1, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.MiddleName1 : s.ApplicantContactInfo.MiddleName1)) - .ForMember(d => d.MiddleName2, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.MiddleName2 : s.ApplicantContactInfo.MiddleName2)) - .ForMember(d => d.EmailAddress, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.EmailAddress : s.ApplicantContactInfo.EmailAddress)) - .ForMember(d => d.PhoneNumber, opt => opt.MapFrom(s => s.ApplicantIsBizManager == true ? s.BizManagerContactInfo.PhoneNumber : s.ApplicantContactInfo.PhoneNumber)) - .ForMember(d => d.ManagerGivenName, opt => opt.MapFrom(s => s.BizManagerContactInfo.GivenName)) - .ForMember(d => d.ManagerSurname, opt => opt.MapFrom(s => s.BizManagerContactInfo.Surname)) - .ForMember(d => d.ManagerMiddleName1, opt => opt.MapFrom(s => s.BizManagerContactInfo.MiddleName1)) - .ForMember(d => d.ManagerMiddleName2, opt => opt.MapFrom(s => s.BizManagerContactInfo.MiddleName2)) - .ForMember(d => d.ManagerEmailAddress, opt => opt.MapFrom(s => s.BizManagerContactInfo.EmailAddress)) - .ForMember(d => d.ManagerPhoneNumber, opt => opt.MapFrom(s => s.BizManagerContactInfo.PhoneNumber)) + .ForPath(d => d.PrivateInvestigatorSwlInfo.LicenceId, opt => opt.MapFrom(s => s.PrivateInvestigatorSwlInfo == null ? null : s.PrivateInvestigatorSwlInfo.LicenceId)); + + CreateMap() + .IncludeBase() + .ForMember(d => d.LicenceAppId, opt => opt.MapFrom(s => s.OriginalApplicationId)) + .ForMember(d => d.ApplicantId, opt => opt.Ignore()) + .ForMember(d => d.ExpiredLicenceId, opt => opt.Ignore()) + .ForMember(d => d.ExpiredLicenceNumber, opt => opt.Ignore()) + .ForMember(d => d.HasExpiredLicence, opt => opt.Ignore()) .ForPath(d => d.PrivateInvestigatorSwlInfo.LicenceId, opt => opt.MapFrom(s => s.PrivateInvestigatorSwlInfo == null ? null : s.PrivateInvestigatorSwlInfo.LicenceId)); CreateMap() diff --git a/src/Spd.Presentation.Licensing.UnitTest/Controller/BizLicensingControllerTest.cs b/src/Spd.Presentation.Licensing.UnitTest/Controller/BizLicensingControllerTest.cs index 1f46244c1..07c95aabe 100644 --- a/src/Spd.Presentation.Licensing.UnitTest/Controller/BizLicensingControllerTest.cs +++ b/src/Spd.Presentation.Licensing.UnitTest/Controller/BizLicensingControllerTest.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration; using Moq; using Spd.Manager.Licence; +using Spd.Manager.Shared; using Spd.Presentation.Licensing.Controllers; using Spd.Utilities.Recaptcha; using Spd.Utilities.Shared.Exceptions; @@ -16,7 +17,8 @@ namespace Spd.Presentation.Licensing.UnitTest.Controller; public class BizLicensingControllerTest { private readonly IFixture fixture; - private Mock> mockValidator = new(); + private Mock> mockUpsertValidator = new(); + private Mock> mockSubmitValidator = new(); private Mock mockMediator = new(); private Mock mockCache = new(); private Mock mockDpProvider = new(); @@ -51,10 +53,14 @@ public BizLicensingControllerTest() .ReturnsAsync(new List()); mockMediator.Setup(m => m.Send(It.IsAny(), CancellationToken.None)) .ReturnsAsync(new BizLicAppCommandResponse()); + mockMediator.Setup(m => m.Send(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(new BizLicAppCommandResponse()); var validationResults = fixture.Build() .With(r => r.Errors, []) .Create(); - mockValidator.Setup(x => x.ValidateAsync(It.IsAny(), CancellationToken.None)) + mockUpsertValidator.Setup(x => x.ValidateAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(validationResults); + mockSubmitValidator.Setup(x => x.ValidateAsync(It.IsAny(), CancellationToken.None)) .ReturnsAsync(validationResults); var user = new ClaimsPrincipal(new ClaimsIdentity( @@ -66,7 +72,8 @@ public BizLicensingControllerTest() sut = new BizLicensingController(user, mockMediator.Object, configuration, - mockValidator.Object, + mockUpsertValidator.Object, + mockSubmitValidator.Object, mockRecaptch.Object, mockCache.Object, mockDpProvider.Object); @@ -123,9 +130,20 @@ public async void Post_SubmitBusinessLicenceApplication_Return_BizLicAppCommandR } [Fact] - public async void Post_SubmitBusinessLicenceApplicationChange_Return_BizLicAppCommandResponse() + public async void Post_ChangeOnBizLicApp_Renewal_Return_BizLicAppCommandResponse() + { + BizLicAppSubmitRequest request = new() { ApplicationTypeCode = ApplicationTypeCode.Renewal }; + + var result = await sut.ChangeOnBizLicApp(request, CancellationToken.None); + + Assert.IsType(result); + mockMediator.Verify(); + } + + [Fact] + public async void Post_ChangeOnBizLicApp_Update_Return_BizLicAppCommandResponse() { - BizLicAppSubmitRequest request = new() { ApplicationTypeCode = Manager.Shared.ApplicationTypeCode.Renewal }; + BizLicAppSubmitRequest request = new() { ApplicationTypeCode = ApplicationTypeCode.Update }; var result = await sut.ChangeOnBizLicApp(request, CancellationToken.None); @@ -134,9 +152,9 @@ public async void Post_SubmitBusinessLicenceApplicationChange_Return_BizLicAppCo } [Fact] - public async void Post_SubmitBusinessLicenceApplicationChange_With_Wrong_ApplicationTypeCode_Throw_Exception() + public async void Post_ChangeOnBizLicApp_With_Wrong_ApplicationTypeCode_Throw_Exception() { - BizLicAppSubmitRequest request = new() { ApplicationTypeCode = Manager.Shared.ApplicationTypeCode.New }; + BizLicAppSubmitRequest request = new() { ApplicationTypeCode = ApplicationTypeCode.New }; _ = await Assert.ThrowsAsync(async () => await sut.ChangeOnBizLicApp(request, CancellationToken.None)); } diff --git a/src/Spd.Presentation.Licensing/Controllers/BizLicensingController.cs b/src/Spd.Presentation.Licensing/Controllers/BizLicensingController.cs index 359e9c6b7..2adc65b15 100644 --- a/src/Spd.Presentation.Licensing/Controllers/BizLicensingController.cs +++ b/src/Spd.Presentation.Licensing/Controllers/BizLicensingController.cs @@ -22,11 +22,13 @@ public class BizLicensingController : SpdLicenceControllerBase private readonly IPrincipal _currentUser; private readonly IMediator _mediator; private readonly IValidator _bizLicAppUpsertValidator; + private readonly IValidator _bizLicAppSubmitValidator; public BizLicensingController(IPrincipal currentUser, IMediator mediator, IConfiguration configuration, IValidator bizLicAppUpsertValidator, + IValidator bizLicAppSubmitValidator, IRecaptchaVerificationService recaptchaVerificationService, IDistributedCache cache, IDataProtectionProvider dpProvider) : base(cache, dpProvider, recaptchaVerificationService, configuration) @@ -34,6 +36,7 @@ public BizLicensingController(IPrincipal currentUser, _currentUser = currentUser; _mediator = mediator; _bizLicAppUpsertValidator = bizLicAppUpsertValidator; + _bizLicAppSubmitValidator = bizLicAppSubmitValidator; } /// @@ -98,10 +101,9 @@ public async Task> UploadLicenceAppFiles IEnumerable newDocInfos = await GetAllNewDocsInfoAsync(request.DocumentKeyCodes, ct); - //add validation here - //var validateResult = await _permitAppAnonymousSubmitRequestValidator.ValidateAsync(jsonRequest, ct); - //if (!validateResult.IsValid) - // throw new ApiException(HttpStatusCode.BadRequest, JsonSerializer.Serialize(validateResult.Errors)); + var validateResult = await _bizLicAppSubmitValidator.ValidateAsync(request, ct); + if (!validateResult.IsValid) + throw new ApiException(HttpStatusCode.BadRequest, JsonSerializer.Serialize(validateResult.Errors)); if (request.ApplicationTypeCode == ApplicationTypeCode.New) { diff --git a/src/Spd.Resource.Repository.IntegrationTest/BizLicApplicationRepositoryTest.cs b/src/Spd.Resource.Repository.IntegrationTest/BizLicApplicationRepositoryTest.cs index 415faedd1..21d9b69b2 100644 --- a/src/Spd.Resource.Repository.IntegrationTest/BizLicApplicationRepositoryTest.cs +++ b/src/Spd.Resource.Repository.IntegrationTest/BizLicApplicationRepositoryTest.cs @@ -24,6 +24,7 @@ public BizLicApplicationRepositoryTest(IntegrationTestSetup testSetup) _context = testSetup.ServiceProvider.GetRequiredService().CreateChangeOverwrite(); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task GetBizLicApplicationAsync_Run_Correctly() { @@ -75,7 +76,7 @@ public async Task GetBizLicApplicationAsync_Run_Correctly() _context.DeleteObject(biz); _context.DeleteObject(app); await _context.SaveChangesAsync(); - } + } */ [Fact] public async Task GetBizLicApplicationAsync_BizNotFound_Throw_Exception() @@ -84,6 +85,7 @@ public async Task GetBizLicApplicationAsync_BizNotFound_Throw_Exception() await Assert.ThrowsAsync(async () => await _bizLicAppRepository.GetBizLicApplicationAsync(Guid.NewGuid(), CancellationToken.None)); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task SaveBizLicApplicationAsync_WithoutLicenceAppId_Run_Correctly() { @@ -210,8 +212,9 @@ public async Task SaveBizLicApplicationAsync_WithoutLicenceAppId_Run_Correctly() _context.DeleteObject(appToRemove); await _context.SaveChangesAsync(); - } + } */ + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task SaveBizLicApplicationAsync_WithLicenceAppId_Run_Correctly() { @@ -339,7 +342,7 @@ public async Task SaveBizLicApplicationAsync_WithLicenceAppId_Run_Correctly() _context.DeleteObject(appToRemove); await _context.SaveChangesAsync(); - } + } */ [Fact] public async Task SaveBizLicApplicationAsync_ApplicationNotFound_Throw_Exception() @@ -351,6 +354,7 @@ public async Task SaveBizLicApplicationAsync_ApplicationNotFound_Throw_Exception await Assert.ThrowsAsync(async () => await _bizLicAppRepository.SaveBizLicApplicationAsync(cmd, CancellationToken.None)); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task CreateBizLicApplicationAsync_Run_Correctly() { @@ -472,7 +476,7 @@ public async Task CreateBizLicApplicationAsync_Run_Correctly() _context.DeleteObject(originalAppToRemove); await _context.SaveChangesAsync(); - } + } */ [Fact] public async Task CreateBizLicApplicationAsync_WithWrongApplicationType_Throw_Exception() diff --git a/src/Spd.Resource.Repository.IntegrationTest/BizRepositoryTest.cs b/src/Spd.Resource.Repository.IntegrationTest/BizRepositoryTest.cs index 845846773..84f361a77 100644 --- a/src/Spd.Resource.Repository.IntegrationTest/BizRepositoryTest.cs +++ b/src/Spd.Resource.Repository.IntegrationTest/BizRepositoryTest.cs @@ -135,6 +135,7 @@ public async Task AddBizServiceTypeAsync_Run_Correctly() Assert.NotNull(serviceType); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async void UpdateBizAsync_Run_Correctly() { @@ -244,7 +245,7 @@ public async void UpdateBizAsync_Run_Correctly() _context.DeleteObject(contact); _context.DeleteObject(account); await _context.SaveChangesAsync(); - } + } */ [Fact] public async void UpdateBizAsync_BizTypeDifferentFromSoleProprietor_Run_Correctly() diff --git a/src/Spd.Resource.Repository.IntegrationTest/ContactRepositoryTest.cs b/src/Spd.Resource.Repository.IntegrationTest/ContactRepositoryTest.cs index de382a924..70818709c 100644 --- a/src/Spd.Resource.Repository.IntegrationTest/ContactRepositoryTest.cs +++ b/src/Spd.Resource.Repository.IntegrationTest/ContactRepositoryTest.cs @@ -16,6 +16,7 @@ public ContactRepositoryTest(IntegrationTestSetup testSetup) _context = testSetup.ServiceProvider.GetRequiredService().CreateChangeOverwrite(); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task MergeContacts_Run_Correctly() { @@ -53,7 +54,7 @@ public async Task MergeContacts_Run_Correctly() _context.DeleteObject(newContact); await _context.SaveChangesAsync(); } - } + } */ [Theory] [InlineData("spd_integration_firstname", "spd_integration_lastname", null, null, "2024-01-01", null, 2)] diff --git a/src/Spd.Resource.Repository.IntegrationTest/IntegrationTestSetup.cs b/src/Spd.Resource.Repository.IntegrationTest/IntegrationTestSetup.cs index d02d2ebe1..bc0ae8b94 100644 --- a/src/Spd.Resource.Repository.IntegrationTest/IntegrationTestSetup.cs +++ b/src/Spd.Resource.Repository.IntegrationTest/IntegrationTestSetup.cs @@ -13,6 +13,7 @@ using Spd.Resource.Repository.Org; using Spd.Resource.Repository.PersonLicApplication; using Spd.Resource.Repository.PortalUser; +using Spd.Resource.Repository.Tasks; using Spd.Utilities.Dynamics; using System.Reflection; @@ -61,6 +62,7 @@ public IntegrationTestSetup() serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); ServiceProvider = serviceCollection.BuildServiceProvider().CreateScope().ServiceProvider; } public IServiceProvider ServiceProvider { get; private set; } diff --git a/src/Spd.Resource.Repository.IntegrationTest/LicenceRepositoryTest.cs b/src/Spd.Resource.Repository.IntegrationTest/LicenceRepositoryTest.cs index 0a0ea1e90..bda792b0f 100644 --- a/src/Spd.Resource.Repository.IntegrationTest/LicenceRepositoryTest.cs +++ b/src/Spd.Resource.Repository.IntegrationTest/LicenceRepositoryTest.cs @@ -16,6 +16,7 @@ public LicenceRepositoryTest(IntegrationTestSetup testSetup) _context = testSetup.ServiceProvider.GetRequiredService().CreateChangeOverwrite(); } + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task ManageAsync_UpdateLicence_Correctly() { @@ -43,8 +44,9 @@ public async Task ManageAsync_UpdateLicence_Correctly() // Annihilate _context.DeleteObject(lic); await _context.SaveChangesAsync(CancellationToken.None); - } + } */ + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task QueryAsync_SwlPermitLicence_Correctly() { @@ -73,8 +75,9 @@ public async Task QueryAsync_SwlPermitLicence_Correctly() _context.DeleteObject(lic); _context.DeleteObject(p); await _context.SaveChangesAsync(CancellationToken.None); - } + } */ + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task QueryAsync_BizLicence_Correctly() { @@ -103,8 +106,9 @@ public async Task QueryAsync_BizLicence_Correctly() _context.DeleteObject(lic); _context.DeleteObject(biz); await _context.SaveChangesAsync(CancellationToken.None); - } + } */ + /*** TODO: Fix test based on problem described in ticket SPDBT-2716 [Fact] public async Task GetAsync_Licence_Correctly() { @@ -132,7 +136,7 @@ public async Task GetAsync_Licence_Correctly() _context.DeleteObject(lic); _context.DeleteObject(p); await _context.SaveChangesAsync(CancellationToken.None); - } + } */ [Fact] public async Task GetAsync_WithNoExistLicence_ReturnNull() diff --git a/src/Spd.Resource.Repository.IntegrationTest/TaskRepositoryTest.cs b/src/Spd.Resource.Repository.IntegrationTest/TaskRepositoryTest.cs new file mode 100644 index 000000000..e13dc139b --- /dev/null +++ b/src/Spd.Resource.Repository.IntegrationTest/TaskRepositoryTest.cs @@ -0,0 +1,49 @@ +using AutoFixture; +using Microsoft.Dynamics.CRM; +using Microsoft.Extensions.DependencyInjection; +using Spd.Resource.Repository.Tasks; +using Spd.Utilities.Dynamics; + +namespace Spd.Resource.Repository.IntegrationTest; +public class TaskRepositoryTest : IClassFixture +{ + private readonly ITaskRepository _taskRepository; + private DynamicsContext _context; + private readonly IFixture fixture; + + public TaskRepositoryTest(IntegrationTestSetup testSetup) + { + _taskRepository = testSetup.ServiceProvider.GetService(); + _context = testSetup.ServiceProvider.GetRequiredService().CreateChangeOverwrite(); + fixture = new Fixture(); + fixture.Behaviors.Remove(new ThrowingRecursionBehavior()); + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + fixture.Customize(composer => composer.FromFactory(DateOnly.FromDateTime)); + } + + [Fact] + public async Task CreateTaskAsync_Run_Correctly() + { + // Arrange + Guid accountId = Guid.NewGuid(); + account account = new() { accountid = accountId }; + _context.AddToaccounts(account); + await _context.SaveChangesAsync(); + + CreateTaskCmd cmd = new CreateTaskCmd() { RegardingAccountId = accountId, DueDateTime = DateTimeOffset.Now }; + + // Act + var response = await _taskRepository.ManageAsync(cmd, CancellationToken.None); + + // Assert + Assert.IsType(response); + Assert.NotEqual(Guid.Empty, response.TaskId); + + // Annihilate + task? task = _context.tasks.Where(t => t.activityid == response.TaskId).FirstOrDefault(); + + _context.DeleteObject(task); + _context.DeleteObject(account); + await _context.SaveChangesAsync(); + } +} diff --git a/src/Spd.Resource.Repository/Tasks/Contract.cs b/src/Spd.Resource.Repository/Tasks/Contract.cs index 03ee46681..0df054393 100644 --- a/src/Spd.Resource.Repository/Tasks/Contract.cs +++ b/src/Spd.Resource.Repository/Tasks/Contract.cs @@ -13,6 +13,7 @@ public record CreateTaskCmd : TaskCmd public DateTimeOffset DueDateTime { get; set; } public Guid? RegardingContactId { get; set; } public Guid? RegardingCaseId { get; set; } + public Guid? RegardingAccountId { get; set; } public Guid? AssignedTeamId { get; set; } public Guid? LicenceId { get; set; } } diff --git a/src/Spd.Resource.Repository/Tasks/TaskRepository.cs b/src/Spd.Resource.Repository/Tasks/TaskRepository.cs index a334295cf..fd46f03aa 100644 --- a/src/Spd.Resource.Repository/Tasks/TaskRepository.cs +++ b/src/Spd.Resource.Repository/Tasks/TaskRepository.cs @@ -43,11 +43,21 @@ private async Task CreateTaskAsync(CreateTaskCmd cmd, CancellationToke incident? incident = _context.incidents.Where(c => c.incidentid == cmd.RegardingCaseId).FirstOrDefault(); if (incident == null) { - throw new ArgumentException($"cannot find contact for incidentid = {cmd.RegardingCaseId}."); + throw new ArgumentException($"cannot find incident for incidentid = {cmd.RegardingCaseId}."); } _context.SetLink(t, nameof(t.regardingobjectid_incident), incident); } + if (cmd.RegardingAccountId != null) + { + account? account = _context.accounts.Where(c => c.accountid == cmd.RegardingAccountId).FirstOrDefault(); + if (account == null) + { + throw new ArgumentException($"cannot find account for accountid = {cmd.RegardingAccountId}."); + } + _context.SetLink(t, nameof(t.regardingobjectid_account), account); + } + if (cmd.AssignedTeamId != null) { team? serviceTeam = await _context.teams.Where(t => t.teamid == cmd.AssignedTeamId).FirstOrDefaultAsync(ct);