Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor biz member management api #1368

Merged
merged 6 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions src/Spd.Manager.Licence/BizMemberContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,30 @@ namespace Spd.Manager.Licence;
public interface IBizMemberManager
{
public Task<Members> Handle(GetBizMembersQuery query, CancellationToken ct);
public Task<BizMemberResponse> Handle(CreateBizEmployeeCommand cmd, CancellationToken ct);
public Task<BizMemberResponse> Handle(CreateBizSwlControllingMemberCommand cmd, CancellationToken ct);
public Task<BizMemberResponse> Handle(CreateBizNonSwlControllingMemberCommand cmd, CancellationToken ct);
public Task<BizMemberResponse> Handle(UpdateBizNonSwlControllingMemberCommand cmd, CancellationToken ct);
public Task<Unit> Handle(DeleteBizMemberCommand cmd, CancellationToken ct);
public Task<Unit> Handle(UpsertBizMembersCommand cmd, CancellationToken ct);
public Task<ControllingMemberInvitesCreateResponse> Handle(BizControllingMemberNewInviteCommand command, CancellationToken ct);
public Task<ControllingMemberAppInviteVerifyResponse> Handle(VerifyBizControllingMemberInviteCommand command, CancellationToken ct);
}

public record BizControllingMemberNewInviteCommand(Guid BizContactId, Guid UserId, string HostUrl) : IRequest<ControllingMemberInvitesCreateResponse>;
public record VerifyBizControllingMemberInviteCommand(string InviteEncryptedCode) : IRequest<ControllingMemberAppInviteVerifyResponse>;

public record GetBizMembersQuery(Guid BizId, Guid? AppId = null) : IRequest<Members>;

public record UpsertBizMembersCommand(
Guid BizId,
Guid? ApplicationId,
Members Members,
IEnumerable<LicAppFileInfo> LicAppFileInfos) : IRequest<Unit>; //deprecated
public record CreateBizEmployeeCommand(Guid BizId, SwlContactInfo Employee) : IRequest<BizMemberResponse>;
public record CreateBizSwlControllingMemberCommand(Guid BizId, SwlContactInfo SwlControllingMember) : IRequest<BizMemberResponse>;
public record CreateBizNonSwlControllingMemberCommand(Guid BizId, NonSwlContactInfo NonSwlControllingMember) : IRequest<BizMemberResponse>;
public record UpdateBizNonSwlControllingMemberCommand(Guid BizId, Guid BizContactId, NonSwlContactInfo NonSwlControllingMember) : IRequest<BizMemberResponse>;
public record DeleteBizMemberCommand(Guid BizId, Guid BizContactId) : IRequest<Unit>;
public record BizMemberResponse(Guid? bizContactId);
public record Members
{
public IEnumerable<SwlContactInfo> SwlControllingMembers { get; set; }
Expand All @@ -27,12 +41,6 @@ public record MembersRequest : Members
public IEnumerable<Guid> ControllingMemberDocumentKeyCodes { get; set; } = Array.Empty<Guid>();//the document is saved in cache.
}

public record UpsertBizMembersCommand(
Guid BizId,
Guid? ApplicationId,
Members Members,
IEnumerable<LicAppFileInfo> LicAppFileInfos) : IRequest<Unit>;

public record ControllingMemberInvitesCreateResponse(Guid BizContactId)
{
public bool CreateSuccess { get; set; }
Expand Down
43 changes: 42 additions & 1 deletion src/Spd.Manager.Licence/BizMemberManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ internal class BizMemberManager :
IRequestHandler<UpsertBizMembersCommand, Unit>,
IRequestHandler<BizControllingMemberNewInviteCommand, ControllingMemberInvitesCreateResponse>,
IRequestHandler<VerifyBizControllingMemberInviteCommand, ControllingMemberAppInviteVerifyResponse>,
IRequestHandler<CreateBizEmployeeCommand, BizMemberResponse>,
IRequestHandler<CreateBizSwlControllingMemberCommand, BizMemberResponse>,
IRequestHandler<CreateBizNonSwlControllingMemberCommand, BizMemberResponse>,
IRequestHandler<UpdateBizNonSwlControllingMemberCommand, BizMemberResponse>,
IRequestHandler<DeleteBizMemberCommand, Unit>,
IBizMemberManager
{
private readonly IBizLicApplicationRepository _bizLicApplicationRepository;
Expand Down Expand Up @@ -106,7 +111,6 @@ public async Task<ControllingMemberInvitesCreateResponse> Handle(BizControllingM
throw new ApiException(HttpStatusCode.BadRequest, "Cannot send out invitation when there is no email address provided.");
if (contactResp.LatestControllingMemberCrcAppPortalStatusEnum != null)
throw new ApiException(HttpStatusCode.BadRequest, "This business contact already has a CRC application");
//todo : how can we check if the CRC approved but it has been expired.

var createCmd = _mapper.Map<ControllingMemberInviteCreateCmd>(contactResp);
createCmd.CreatedByUserId = cmd.UserId;
Expand Down Expand Up @@ -137,6 +141,43 @@ public async Task<Members> Handle(GetBizMembersQuery qry, CancellationToken ct)
return members;
}

public async Task<BizMemberResponse> Handle(CreateBizEmployeeCommand cmd, CancellationToken ct)
{
BizContact bizContact = _mapper.Map<BizContact>(cmd.Employee);
bizContact.BizContactRoleCode = BizContactRoleEnum.Employee;
bizContact.BizId = cmd.BizId;
Guid? bizContactId = await _bizContactRepository.ManageBizContactsAsync(new BizContactCreateCmd(bizContact), ct);
return new BizMemberResponse(bizContactId);
}
public async Task<BizMemberResponse> Handle(CreateBizSwlControllingMemberCommand cmd, CancellationToken ct)
{
BizContact bizContact = _mapper.Map<BizContact>(cmd.SwlControllingMember);
bizContact.BizContactRoleCode = BizContactRoleEnum.ControllingMember;
bizContact.BizId = cmd.BizId;
Guid? bizContactId = await _bizContactRepository.ManageBizContactsAsync(new BizContactCreateCmd(bizContact), ct);
return new BizMemberResponse(bizContactId);
}
public async Task<BizMemberResponse> Handle(CreateBizNonSwlControllingMemberCommand cmd, CancellationToken ct)
{
BizContact bizContact = _mapper.Map<BizContact>(cmd.NonSwlControllingMember);
bizContact.BizContactRoleCode = BizContactRoleEnum.ControllingMember;
bizContact.BizId = cmd.BizId;
Guid? bizContactId = await _bizContactRepository.ManageBizContactsAsync(new BizContactCreateCmd(bizContact), ct);
return new BizMemberResponse(bizContactId);
}
public async Task<Unit> Handle(DeleteBizMemberCommand cmd, CancellationToken ct)
{
await _bizContactRepository.ManageBizContactsAsync(new BizContactDeleteCmd(cmd.BizContactId), ct);
return default;
}
public async Task<BizMemberResponse> Handle(UpdateBizNonSwlControllingMemberCommand cmd, CancellationToken ct)
{
BizContact bizContact = _mapper.Map<BizContact>(cmd.NonSwlControllingMember);
bizContact.BizContactRoleCode = BizContactRoleEnum.ControllingMember;
bizContact.BizId = cmd.BizId;
Guid? bizContactId = await _bizContactRepository.ManageBizContactsAsync(new BizContactUpdateCmd(cmd.BizContactId, bizContact), ct);
return new BizMemberResponse(bizContactId);
}
public async Task<Unit> Handle(UpsertBizMembersCommand cmd, CancellationToken ct)
{
await UpdateMembersAsync(cmd.Members, cmd.BizId, ct);
Expand Down
4 changes: 2 additions & 2 deletions src/Spd.Manager.Licence/Mappings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ public Mappings()
CreateMap<BizContactResp, ControllingMemberInviteCreateCmd>()
.IncludeBase<BizContactResp, ControllingMemberInvite>()
.ForMember(d => d.HostUrl, opt => opt.Ignore());

CreateMap<SwlContactInfo, BizContact>();
CreateMap<NonSwlContactInfo, BizContact>();
CreateMap<ControllingMemberCrcApplicationResp, ControllingMemberCrcAppResponse>()
.ForMember(d => d.ResidentialAddress, opt => opt.MapFrom(s => s.ResidentialAddressData))
.ForPath(d => d.ResidentialAddress.AddressLine1, opt => opt.MapFrom(s => s.ResidentialAddressData.AddressLine1))
Expand Down Expand Up @@ -647,7 +648,6 @@ private static List<BranchAddr> GetBranchAddr(IEnumerable<BranchInfo> branchInfo
{LicenceDocumentTypeCode.CorporateRegistryDocument, DocumentTypeEnum.CorporateRegistryDocument }
}.ToImmutableDictionary();


private static readonly ImmutableDictionary<LicenceDocumentTypeCode, DocumentTypeEnum> LicenceDocumentType2Dictionary = new Dictionary<LicenceDocumentTypeCode, DocumentTypeEnum>()
{
{LicenceDocumentTypeCode.BcServicesCard, DocumentTypeEnum.AdditionalGovIdDocument},
Expand Down
82 changes: 79 additions & 3 deletions src/Spd.Presentation.Licensing/Controllers/BizMembersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public BizMembersController(IPrincipal currentUser,
/// <param name="applicationId"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business-licence-application/{bizId}/members")]
[Route("api/business/{bizId}/members")]
[HttpGet]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<Members> GetMembers([FromRoute] Guid bizId, CancellationToken ct)
Expand All @@ -48,13 +48,13 @@ public async Task<Members> GetMembers([FromRoute] Guid bizId, CancellationToken
}

/// <summary>
/// Upsert Biz Application controlling members and employees, controlling members include swl and non-swl
/// Deprecated. Upsert Biz Application controlling members and employees, controlling members include swl and non-swl
/// </summary>
/// <param name="bizId"></param>
/// <param name="applicationId"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business-licence-application/{bizId}/members")]
[Route("api/business/{bizId}/members")]
[HttpPost]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<ActionResult> UpsertMembers([FromRoute] Guid bizId, [FromBody] MembersRequest members, CancellationToken ct)
Expand All @@ -68,6 +68,82 @@ public async Task<ActionResult> UpsertMembers([FromRoute] Guid bizId, [FromBody]
return Ok();
}

/// <summary>
/// Create Biz employee
/// </summary>
/// <param name="bizId"></param>
/// <param name="employee"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business/{bizId}/employees")]
[HttpPost]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<BizMemberResponse> CreateEmployee([FromRoute] Guid bizId, [FromBody] SwlContactInfo employee, CancellationToken ct)
{
return await _mediator.Send(new CreateBizEmployeeCommand(bizId, employee), ct);
}

/// <summary>
/// Create Biz swl controlling member
/// </summary>
/// <param name="bizId"></param>
/// <param name="controllingMember"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business/{bizId}/swl-controlling-members")]
[HttpPost]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<BizMemberResponse> CreateSwlControllingMember([FromRoute] Guid bizId, [FromBody] SwlContactInfo controllingMember, CancellationToken ct)
{
return await _mediator.Send(new CreateBizSwlControllingMemberCommand(bizId, controllingMember), ct);
}

/// <summary>
/// Create Biz swl controlling member
/// </summary>
/// <param name="bizId"></param>
/// <param name="employee"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business/{bizId}/non-swl-controlling-members")]
[HttpPost]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<BizMemberResponse> CreateNonSwlControllingMember([FromRoute] Guid bizId, [FromBody] NonSwlContactInfo controllingMember, CancellationToken ct)
{
return await _mediator.Send(new CreateBizNonSwlControllingMemberCommand(bizId, controllingMember), ct);
}

/// <summary>
/// Update Non swl biz controlling member
/// </summary>
/// <param name="bizId"></param>
/// <param name="bizContactId"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business/{bizId}/non-swl-controlling-members/{bizContactId}")]
[HttpPut]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<BizMemberResponse> UpdateNonSwlControllingMember([FromRoute] Guid bizId, [FromRoute] Guid bizContactId, NonSwlContactInfo controllingMember, CancellationToken ct)
{
return await _mediator.Send(new UpdateBizNonSwlControllingMemberCommand(bizId, bizContactId, controllingMember), ct);
}

/// <summary>
/// Delete Biz swl controlling member
/// </summary>
/// <param name="bizId"></param>
/// <param name="bizContactId"></param>
/// <param name="ct"></param>
/// <returns></returns>
[Route("api/business/{bizId}/members/{bizContactId}")]
[HttpDelete]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
public async Task<ActionResult> DeleteBizMember([FromRoute] Guid bizId, [FromRoute] Guid bizContactId, CancellationToken ct)
{
await _mediator.Send(new DeleteBizMemberCommand(bizId, bizContactId), ct);
return Ok();
}

/// <summary>
/// Create controlling member crc invitation for this biz contact
/// </summary>
Expand Down
69 changes: 66 additions & 3 deletions src/Spd.Resource.Repository/BizContact/BizContactRepository.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using AutoMapper;
using MediatR;
using Microsoft.Dynamics.CRM;
using Microsoft.Extensions.Logging;
using Spd.Utilities.Dynamics;
Expand Down Expand Up @@ -52,7 +51,71 @@ public async Task<IEnumerable<BizContactResp>> QueryBizContactsAsync(BizContactQ
return _mapper.Map<IEnumerable<BizContactResp>>(bizContacts.ToList());
}

public async Task<Unit> ManageBizContactsAsync(BizContactUpsertCmd cmd, CancellationToken ct)
public async Task<Guid?> ManageBizContactsAsync(BizContactCmd cmd, CancellationToken ct)
{
return cmd switch
{
BizContactCreateCmd c => await CreateBizContactAsync(c, ct),
BizContactUpdateCmd c => await UpdateBizContactAsync(c, ct),
BizContactDeleteCmd c => await DeleteBizContactAsync(c, ct),
_ => throw new NotSupportedException($"{cmd.GetType().Name} is not supported")
};
}

private async Task<Guid?> CreateBizContactAsync(BizContactCreateCmd cmd, CancellationToken ct)
{
account? biz = await _context.GetOrgById(cmd.BizContact.BizId, ct);
spd_businesscontact bizContact = _mapper.Map<spd_businesscontact>(cmd.BizContact);
bizContact.spd_businesscontactid = Guid.NewGuid();
contact? c = null;
if (cmd.BizContact.ContactId != null)
{
c = await _context.GetContactById((Guid)cmd.BizContact.ContactId, ct);
if (c == null)
throw new ApiException(HttpStatusCode.BadRequest, $"invalid contact {cmd.BizContact.ContactId.Value}");
bizContact.spd_fullname = $"{c.lastname},{c.firstname}";
}
_context.AddTospd_businesscontacts(bizContact);
if (c != null)
_context.SetLink(bizContact, nameof(bizContact.spd_ContactId), c);
if (cmd.BizContact.LicenceId != null)
{
spd_licence? swlLic = _context.spd_licences.Where(l => l.spd_licenceid == cmd.BizContact.LicenceId && l.statecode == DynamicsConstants.StateCode_Active).FirstOrDefault();
Guid swlServiceTypeId = DynamicsContextLookupHelpers.ServiceTypeGuidDictionary[ServiceTypeEnum.SecurityWorkerLicence.ToString()];
//only swl can be linked to.
if (swlLic == null || swlLic._spd_licencetype_value != swlServiceTypeId)
throw new ApiException(System.Net.HttpStatusCode.BadRequest, $"invalid licence {cmd.BizContact.LicenceId}");
_context.SetLink(bizContact, nameof(bizContact.spd_SWLNumber), swlLic);
}
_context.SetLink(bizContact, nameof(bizContact.spd_OrganizationId), biz);
await _context.SaveChangesAsync(ct);
return bizContact.spd_businesscontactid;
}

private async Task<Guid?> DeleteBizContactAsync(BizContactDeleteCmd cmd, CancellationToken ct)
{
spd_businesscontact? bizContact = await _context.GetBizContactById(cmd.BizContactId, ct);
bizContact.statecode = DynamicsConstants.StateCode_Inactive;
_context.UpdateObject(bizContact);
await _context.SaveChangesAsync(ct);
return null;
}

private async Task<Guid?> UpdateBizContactAsync(BizContactUpdateCmd cmd, CancellationToken ct)
{
spd_businesscontact? bizContact = await _context.GetBizContactById(cmd.BizContactId, ct);
if (bizContact == null) throw new ApiException(HttpStatusCode.BadRequest, "Cannot find the member.");
if (bizContact.spd_role != (int)BizContactRoleOptionSet.ControllingMember)
throw new ApiException(HttpStatusCode.BadRequest, "Cannot update non-controlling member.");
if (bizContact._spd_swlnumber_value != null)
throw new ApiException(HttpStatusCode.BadRequest, "Cannot update controlling member with secure worker licence.");
_mapper.Map<BizContact, spd_businesscontact>(cmd.BizContact, bizContact);
_context.UpdateObject(bizContact);
await _context.SaveChangesAsync(ct);
return cmd.BizContactId;
}

private async Task<Guid?> UpsertBizContacts(BizContactUpsertCmd cmd, CancellationToken ct)
{
IQueryable<spd_businesscontact> bizContacts = _context.spd_businesscontacts
.Expand(b => b.spd_businesscontact_spd_application)
Expand Down Expand Up @@ -116,7 +179,7 @@ public async Task<Unit> ManageBizContactsAsync(BizContactUpsertCmd cmd, Cancella
}
}
await _context.SaveChangesAsync(ct);
return default;
return null;
}

}
Expand Down
Loading