269 lines
10 KiB
C#
269 lines
10 KiB
C#
using DigitalData.Core.Abstraction.Application.Repository;
|
|
using DigitalData.Core.Exceptions;
|
|
using EnvelopeGenerator.Domain;
|
|
using EnvelopeGenerator.Domain.Constants;
|
|
using EnvelopeGenerator.Domain.Entities;
|
|
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
|
|
|
public class SaveEnvelopeDraftCommandHandler : IRequestHandler<SaveEnvelopeDraftCommand, SaveEnvelopeDraftResultDto>
|
|
{
|
|
private readonly IRepository<Envelope> _envelopeRepository;
|
|
private readonly IRepository<Document> _documentRepository;
|
|
private readonly IRepository<EnvelopeReceiver> _envelopeReceiverRepository;
|
|
private readonly IRepository<Receiver> _receiverRepository;
|
|
private readonly IRepository<DocReceiverElement> _docReceiverElementRepository;
|
|
|
|
public SaveEnvelopeDraftCommandHandler(
|
|
IRepository<Envelope> envelopeRepository,
|
|
IRepository<Document> documentRepository,
|
|
IRepository<EnvelopeReceiver> envelopeReceiverRepository,
|
|
IRepository<Receiver> receiverRepository,
|
|
IRepository<DocReceiverElement> docReceiverElementRepository)
|
|
{
|
|
_envelopeRepository = envelopeRepository;
|
|
_documentRepository = documentRepository;
|
|
_envelopeReceiverRepository = envelopeReceiverRepository;
|
|
_receiverRepository = receiverRepository;
|
|
_docReceiverElementRepository = docReceiverElementRepository;
|
|
}
|
|
|
|
public async Task<SaveEnvelopeDraftResultDto> Handle(SaveEnvelopeDraftCommand request, CancellationToken cancel)
|
|
{
|
|
var now = DateTime.Now;
|
|
var title = request.Title?.Trim() ?? string.Empty;
|
|
var message = request.Message?.Trim() ?? string.Empty;
|
|
int targetEnvelopeId;
|
|
|
|
if (request.EnvelopeId is int envelopeId)
|
|
{
|
|
var envelopeInfo = await _envelopeRepository.Query
|
|
.Where(e => e.Id == envelopeId)
|
|
.Select(e => new { e.Id, e.UserId })
|
|
.FirstOrDefaultAsync(cancel)
|
|
?? throw new NotFoundException($"Envelope {envelopeId} not found.");
|
|
|
|
if (envelopeInfo.UserId != request.UserId)
|
|
throw new ForbiddenException();
|
|
|
|
await _envelopeRepository.UpdateAsync(e =>
|
|
{
|
|
e.Title = title;
|
|
e.Message = message;
|
|
e.TfaEnabled = request.TFAEnabled;
|
|
e.Status = EnvelopeStatus.EnvelopeSaved;
|
|
e.ChangedWhen = now;
|
|
}, e => e.Id == envelopeId, cancel);
|
|
|
|
targetEnvelopeId = envelopeInfo.Id;
|
|
}
|
|
else
|
|
{
|
|
var envelope = new Envelope
|
|
{
|
|
UserId = request.UserId,
|
|
Status = EnvelopeStatus.EnvelopeSaved,
|
|
Uuid = Guid.NewGuid().ToString(),
|
|
Title = title,
|
|
Message = message,
|
|
AddedWhen = now,
|
|
ChangedWhen = now,
|
|
Comment = string.Empty,
|
|
Language = "de-DE",
|
|
TfaEnabled = request.TFAEnabled,
|
|
UseAccessCode = false,
|
|
SendReminderEmails = false
|
|
};
|
|
|
|
envelope = await _envelopeRepository.CreateAsync(envelope, cancel);
|
|
targetEnvelopeId = envelope.Id;
|
|
}
|
|
|
|
await UpsertDocumentAsync(targetEnvelopeId, request.Document, now, cancel);
|
|
await UpsertReceiversAndFieldsAsync(targetEnvelopeId, request.Receivers, now, cancel);
|
|
|
|
return new SaveEnvelopeDraftResultDto(targetEnvelopeId, EnvelopeStatus.EnvelopeSaved);
|
|
}
|
|
|
|
private async Task UpsertDocumentAsync(int envelopeId, SaveEnvelopeDraftDocumentDto? document, DateTime now, CancellationToken cancel)
|
|
{
|
|
if (document is null || string.IsNullOrWhiteSpace(document.DataAsBase64))
|
|
return;
|
|
|
|
byte[] bytes;
|
|
try
|
|
{
|
|
bytes = Convert.FromBase64String(document.DataAsBase64);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
throw new BadRequestException("Document payload is not valid base64.");
|
|
}
|
|
|
|
var existingDocumentId = await _documentRepository.Query
|
|
.Where(d => d.EnvelopeId == envelopeId)
|
|
.Select(d => (int?)d.Id)
|
|
.FirstOrDefaultAsync(cancel);
|
|
|
|
if (existingDocumentId is null)
|
|
{
|
|
await _documentRepository.CreateAsync(new Document
|
|
{
|
|
EnvelopeId = envelopeId,
|
|
AddedWhen = now,
|
|
ByteData = bytes,
|
|
Filename = string.Empty,
|
|
Filepath = string.Empty,
|
|
FileNameOriginal = string.Empty
|
|
}, cancel);
|
|
|
|
return;
|
|
}
|
|
|
|
await _documentRepository.UpdateAsync(d =>
|
|
{
|
|
d.ByteData = bytes;
|
|
}, d => d.Id == existingDocumentId.Value, cancel);
|
|
}
|
|
|
|
private async Task UpsertReceiversAndFieldsAsync(int envelopeId, IReadOnlyCollection<SaveEnvelopeDraftReceiverDto> receivers, DateTime now, CancellationToken cancel)
|
|
{
|
|
var filteredReceivers = receivers
|
|
.Where(r => !string.IsNullOrWhiteSpace(r.EmailAddress))
|
|
.Select(r => r with
|
|
{
|
|
EmailAddress = r.EmailAddress.Trim().ToLowerInvariant(),
|
|
Name = r.Name?.Trim(),
|
|
PhoneNumber = string.IsNullOrWhiteSpace(r.PhoneNumber) ? null : r.PhoneNumber.Trim(),
|
|
Fields = r.Fields ?? new List<SaveEnvelopeDraftFieldDto>()
|
|
})
|
|
.GroupBy(r => r.EmailAddress, StringComparer.OrdinalIgnoreCase)
|
|
.Select(g => g.First())
|
|
.ToList();
|
|
|
|
if (filteredReceivers.Count == 0)
|
|
return;
|
|
|
|
var document = await _documentRepository.Query
|
|
.FirstOrDefaultAsync(d => d.EnvelopeId == envelopeId, cancel);
|
|
|
|
if (document is null)
|
|
return;
|
|
|
|
var currentLinks = await _envelopeReceiverRepository.Query
|
|
.Include(er => er.Receiver)
|
|
.Where(er => er.EnvelopeId == envelopeId)
|
|
.ToListAsync(cancel);
|
|
|
|
var linksByReceiverId = currentLinks.ToDictionary(er => er.ReceiverId, er => er);
|
|
var linksByEmail = currentLinks
|
|
.Where(er => !string.IsNullOrWhiteSpace(er.Receiver?.EmailAddress))
|
|
.GroupBy(er => er.Receiver!.EmailAddress, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Key.ToLowerInvariant(), g => g.First(), StringComparer.OrdinalIgnoreCase);
|
|
|
|
var sequence = currentLinks.Count > 0 ? currentLinks.Max(er => er.Sequence) : 0;
|
|
|
|
foreach (var receiverDraft in filteredReceivers)
|
|
{
|
|
var linkResult = await GetOrCreateEnvelopeReceiverAsync(envelopeId, receiverDraft, linksByEmail, linksByReceiverId, now, sequence, cancel);
|
|
var link = linkResult.Link;
|
|
sequence = linkResult.NextSequence;
|
|
|
|
await _docReceiverElementRepository.DeleteAsync(query => query
|
|
.Where(e => e.DocumentId == document.Id)
|
|
.Where(e => e.ReceiverId == link.ReceiverId), cancel);
|
|
|
|
foreach (var field in receiverDraft.Fields.Where(f => f.Page > 0))
|
|
{
|
|
await _docReceiverElementRepository.CreateAsync(new DocReceiverElement
|
|
{
|
|
DocumentId = document.Id,
|
|
ReceiverId = link.ReceiverId,
|
|
ElementType = 1,
|
|
X = field.X,
|
|
Y = field.Y,
|
|
Width = 1.77,
|
|
Height = 1.96,
|
|
Page = field.Page,
|
|
Required = true,
|
|
ReadOnly = false,
|
|
AnnotationIndex = 0,
|
|
AddedWhen = now,
|
|
FullName = link.Name,
|
|
Position = string.Empty,
|
|
Place = string.Empty,
|
|
Tooltip = string.Empty
|
|
}, cancel);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<(EnvelopeReceiver Link, int NextSequence)> GetOrCreateEnvelopeReceiverAsync(
|
|
int envelopeId,
|
|
SaveEnvelopeDraftReceiverDto receiverDraft,
|
|
IDictionary<string, EnvelopeReceiver> linksByEmail,
|
|
IDictionary<int, EnvelopeReceiver> linksByReceiverId,
|
|
DateTime now,
|
|
int sequence,
|
|
CancellationToken cancel)
|
|
{
|
|
if (linksByEmail.TryGetValue(receiverDraft.EmailAddress, out var existingLink))
|
|
{
|
|
var nextName = string.IsNullOrWhiteSpace(receiverDraft.Name) ? existingLink.Name : receiverDraft.Name!;
|
|
var nextPhoneNumber = receiverDraft.PhoneNumber ?? existingLink.PhoneNumber;
|
|
|
|
existingLink.Name = nextName;
|
|
existingLink.PhoneNumber = nextPhoneNumber;
|
|
existingLink.ChangedWhen = now;
|
|
|
|
await _envelopeReceiverRepository.UpdateAsync(er =>
|
|
{
|
|
er.Name = nextName;
|
|
er.PhoneNumber = nextPhoneNumber;
|
|
er.ChangedWhen = now;
|
|
}, er => er.EnvelopeId == existingLink.EnvelopeId && er.ReceiverId == existingLink.ReceiverId, cancel);
|
|
return (existingLink, sequence);
|
|
}
|
|
|
|
var receiver = await _receiverRepository.Query
|
|
.FirstOrDefaultAsync(r => r.EmailAddress != null && r.EmailAddress.ToLower() == receiverDraft.EmailAddress, cancel);
|
|
|
|
if (receiver is null)
|
|
{
|
|
receiver = await _receiverRepository.CreateAsync(new Receiver
|
|
{
|
|
EmailAddress = receiverDraft.EmailAddress,
|
|
Signature = receiverDraft.EmailAddress.ToUpperInvariant().GetChecksum(),
|
|
AddedWhen = now,
|
|
TotpSecretkey = string.Empty,
|
|
TfaRegDeadline = null
|
|
}, cancel);
|
|
}
|
|
|
|
sequence++;
|
|
var link = new EnvelopeReceiver
|
|
{
|
|
EnvelopeId = envelopeId,
|
|
ReceiverId = receiver.Id,
|
|
Sequence = sequence,
|
|
Name = receiverDraft.Name ?? receiverDraft.EmailAddress,
|
|
JobTitle = string.Empty,
|
|
CompanyName = string.Empty,
|
|
PrivateMessage = string.Empty,
|
|
AccessCode = string.Empty,
|
|
AddedWhen = now,
|
|
ChangedWhen = now,
|
|
PhoneNumber = receiverDraft.PhoneNumber ?? string.Empty
|
|
};
|
|
|
|
link = await _envelopeReceiverRepository.CreateAsync(link, cancel);
|
|
link.Receiver = receiver;
|
|
|
|
linksByReceiverId[link.ReceiverId] = link;
|
|
linksByEmail[receiverDraft.EmailAddress] = link;
|
|
return (link, sequence);
|
|
}
|
|
}
|