Enhance envelope creation and update functionality

Added new optional properties to `CreateEnvelopeCommand` for detailed configuration, including `EnvelopeId`, `Language`, `UseAccessCode`, and expiration settings. Introduced `ResolveEnvelopeSettingsAsync` to centralize settings resolution logic. Updated `CreateEnvelopeCommandHandler` to handle new properties and improve envelope creation/update flow. Added `EnvelopeSettings` record for structured settings management. Enhanced SQL update logic with `UseSQLExecutor` support. Improved handling of language resolution, reminders, and expiration rules.
This commit is contained in:
2026-10-01 15:52:51 +02:00
parent 48302a374f
commit 3b5075fe44
2 changed files with 251 additions and 3 deletions

View File

@@ -12,8 +12,66 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands;
/// </summary> /// </summary>
public record CreateEnvelopeCommand : IRequest<EnvelopeDto?> public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
{ {
/// <summary>
/// Optionale Umschlag-ID fuer Aktualisierungsvorgaenge.
/// </summary>
public int? EnvelopeId { get; init; } public int? EnvelopeId { get; init; }
/// <summary>
/// Kennung des ausgewaehlten Umschlagtyps (Vorlage).
/// </summary>
public int? EnvelopeTypeId { get; set; }
/// <summary>
/// Sprachcode fuer Umschlaginhalt und Benachrichtigungen.
/// </summary>
public string? Language { get; set; }
/// <summary>
/// Gibt an, ob der Zugriffscode fuer Empfaenger aktiviert ist.
/// </summary>
public bool? UseAccessCode { get; set; }
/// <summary>
/// Zertifizierungstyp fuer den Signaturprozess.
/// </summary>
public int? CertificationType { get; set; }
/// <summary>
/// Finale E-Mail-Einstellung fuer den Ersteller.
/// </summary>
public int? FinalEmailToCreator { get; set; }
/// <summary>
/// Finale E-Mail-Einstellung fuer die Empfaenger.
/// </summary>
public int? FinalEmailToReceivers { get; set; }
/// <summary>
/// Gibt an, ob Erinnerungs-E-Mails aktiviert sind.
/// </summary>
public bool? SendReminderEmails { get; set; }
/// <summary>
/// Anzahl der Tage bis zur ersten Erinnerung.
/// </summary>
public int? FirstReminderDays { get; set; }
/// <summary>
/// Intervall in Tagen zwischen Erinnerungs-E-Mails.
/// </summary>
public int? ReminderIntervalDays { get; set; }
/// <summary>
/// Anzahl der Tage bis zum Ablauf des Umschlags.
/// </summary>
public int? ExpiresWhenDays { get; set; }
/// <summary>
/// Anzahl der Tage vor Ablauf, ab denen gewarnt wird.
/// </summary>
public int? ExpiresWarningWhenDays { get; set; }
/// <summary> /// <summary>
/// Der Titel des Umschlags. Dies ist ein Pflichtfeld. /// Der Titel des Umschlags. Dies ist ein Pflichtfeld.
/// </summary> /// </summary>
@@ -29,10 +87,19 @@ public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
/// </summary> /// </summary>
public bool TFAEnabled { get; set; } = false; public bool TFAEnabled { get; set; } = false;
/// <summary>
/// Gibt an, ob der Umschlag nach dem Speichern sofort versendet werden soll.
/// </summary>
public bool Send { get; init; } public bool Send { get; init; }
/// <summary>
/// Optionaler PDF-Dokumentinhalt als Base64.
/// </summary>
public CreateEnvelopeDocumentDto? Document { get; init; } public CreateEnvelopeDocumentDto? Document { get; init; }
/// <summary>
/// Empfaengerliste inklusive optionaler Signaturfeld-Koordinaten.
/// </summary>
public List<CreateEnvelopeReceiverDto> Receivers { get; init; } = new(); public List<CreateEnvelopeReceiverDto> Receivers { get; init; } = new();
/// <summary> /// <summary>

View File

@@ -33,6 +33,8 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
private IRepository<EnvelopeReceiver> EnvelopeReceiverRepository => _provider.GetRequiredService<IRepository<EnvelopeReceiver>>(); private IRepository<EnvelopeReceiver> EnvelopeReceiverRepository => _provider.GetRequiredService<IRepository<EnvelopeReceiver>>();
private IRepository<EnvelopeType> EnvelopeTypeRepository => _provider.GetRequiredService<IRepository<EnvelopeType>>();
private IRepository<Receiver> ReceiverRepository => _provider.GetRequiredService<IRepository<Receiver>>(); private IRepository<Receiver> ReceiverRepository => _provider.GetRequiredService<IRepository<Receiver>>();
private IRepository<DocReceiverElement> DocReceiverElementRepository => _provider.GetRequiredService<IRepository<DocReceiverElement>>(); private IRepository<DocReceiverElement> DocReceiverElementRepository => _provider.GetRequiredService<IRepository<DocReceiverElement>>();
@@ -73,6 +75,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
var now = DateTime.Now; var now = DateTime.Now;
var title = request.Title?.Trim() ?? string.Empty; var title = request.Title?.Trim() ?? string.Empty;
var message = request.Message?.Trim() ?? string.Empty; var message = request.Message?.Trim() ?? string.Empty;
var settings = await ResolveEnvelopeSettingsAsync(request, existing: null, cancel);
Envelope envelope; Envelope envelope;
if (request.EnvelopeId is int envelopeId) if (request.EnvelopeId is int envelopeId)
@@ -85,11 +88,27 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
if (envelope.UserId != request.UserId) if (envelope.UserId != request.UserId)
throw new ForbiddenException(); throw new ForbiddenException();
settings = await ResolveEnvelopeSettingsAsync(request, envelope, cancel);
await Repository.UpdateAsync(e => await Repository.UpdateAsync(e =>
{ {
e.Title = title; e.Title = title;
e.Message = message; e.Message = message;
e.TfaEnabled = request.TFAEnabled; e.TfaEnabled = request.TFAEnabled;
e.EnvelopeTypeId = settings.EnvelopeTypeId;
e.Language = settings.Language;
e.UseAccessCode = settings.UseAccessCode;
e.CertificationType = settings.CertificationType;
e.FinalEmailToCreator = settings.FinalEmailToCreator;
e.FinalEmailToReceivers = settings.FinalEmailToReceivers;
e.SendReminderEmails = settings.SendReminderEmails;
e.FirstReminderDays = settings.FirstReminderDays;
e.ReminderIntervalDays = settings.ReminderIntervalDays;
e.ExpiresWhenDays = settings.ExpiresWhenDays;
e.ExpiresWarningWhenDays = settings.ExpiresWarningWhenDays;
e.ContractType = settings.ContractType;
e.ExpiresWhen = settings.ExpiresWhen;
e.ExpiresWarningWhen = settings.ExpiresWarningWhen;
e.Status = EnvelopeStatus.EnvelopeSaved; e.Status = EnvelopeStatus.EnvelopeSaved;
e.ChangedWhen = now; e.ChangedWhen = now;
}, e => e.Id == envelopeId, cancel); }, e => e.Id == envelopeId, cancel);
@@ -97,6 +116,20 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
envelope.Title = title; envelope.Title = title;
envelope.Message = message; envelope.Message = message;
envelope.TfaEnabled = request.TFAEnabled; envelope.TfaEnabled = request.TFAEnabled;
envelope.EnvelopeTypeId = settings.EnvelopeTypeId;
envelope.Language = settings.Language;
envelope.UseAccessCode = settings.UseAccessCode;
envelope.CertificationType = settings.CertificationType;
envelope.FinalEmailToCreator = settings.FinalEmailToCreator;
envelope.FinalEmailToReceivers = settings.FinalEmailToReceivers;
envelope.SendReminderEmails = settings.SendReminderEmails;
envelope.FirstReminderDays = settings.FirstReminderDays;
envelope.ReminderIntervalDays = settings.ReminderIntervalDays;
envelope.ExpiresWhenDays = settings.ExpiresWhenDays;
envelope.ExpiresWarningWhenDays = settings.ExpiresWarningWhenDays;
envelope.ContractType = settings.ContractType;
envelope.ExpiresWhen = settings.ExpiresWhen;
envelope.ExpiresWarningWhen = settings.ExpiresWarningWhen;
envelope.Status = EnvelopeStatus.EnvelopeSaved; envelope.Status = EnvelopeStatus.EnvelopeSaved;
envelope.ChangedWhen = now; envelope.ChangedWhen = now;
} }
@@ -114,12 +147,61 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
AddedWhen = now, AddedWhen = now,
ChangedWhen = now, ChangedWhen = now,
Comment = string.Empty, Comment = string.Empty,
Language = "de-DE", EnvelopeTypeId = settings.EnvelopeTypeId,
Language = settings.Language,
TfaEnabled = request.TFAEnabled, TfaEnabled = request.TFAEnabled,
UseAccessCode = false, UseAccessCode = settings.UseAccessCode,
SendReminderEmails = false CertificationType = settings.CertificationType,
FinalEmailToCreator = settings.FinalEmailToCreator,
FinalEmailToReceivers = settings.FinalEmailToReceivers,
SendReminderEmails = settings.SendReminderEmails,
FirstReminderDays = settings.FirstReminderDays,
ReminderIntervalDays = settings.ReminderIntervalDays,
ExpiresWhenDays = settings.ExpiresWhenDays,
ExpiresWarningWhenDays = settings.ExpiresWarningWhenDays,
ExpiresWhen = settings.ExpiresWhen,
ExpiresWarningWhen = settings.ExpiresWarningWhen,
ContractType = settings.ContractType
}, cancel); }, cancel);
if (request.UseSQLExecutor)
{
await Repository.UpdateAsync(e =>
{
e.EnvelopeTypeId = settings.EnvelopeTypeId;
e.Language = settings.Language;
e.UseAccessCode = settings.UseAccessCode;
e.CertificationType = settings.CertificationType;
e.FinalEmailToCreator = settings.FinalEmailToCreator;
e.FinalEmailToReceivers = settings.FinalEmailToReceivers;
e.SendReminderEmails = settings.SendReminderEmails;
e.FirstReminderDays = settings.FirstReminderDays;
e.ReminderIntervalDays = settings.ReminderIntervalDays;
e.ExpiresWhenDays = settings.ExpiresWhenDays;
e.ExpiresWarningWhenDays = settings.ExpiresWarningWhenDays;
e.ExpiresWhen = settings.ExpiresWhen;
e.ExpiresWarningWhen = settings.ExpiresWarningWhen;
e.ContractType = settings.ContractType;
e.ChangedWhen = now;
}, e => e.Id == envelope.Id, cancel);
envelope.EnvelopeTypeId = settings.EnvelopeTypeId;
envelope.Language = settings.Language;
envelope.UseAccessCode = settings.UseAccessCode;
envelope.CertificationType = settings.CertificationType;
envelope.FinalEmailToCreator = settings.FinalEmailToCreator;
envelope.FinalEmailToReceivers = settings.FinalEmailToReceivers;
envelope.SendReminderEmails = settings.SendReminderEmails;
envelope.FirstReminderDays = settings.FirstReminderDays;
envelope.ReminderIntervalDays = settings.ReminderIntervalDays;
envelope.ExpiresWhenDays = settings.ExpiresWhenDays;
envelope.ExpiresWarningWhenDays = settings.ExpiresWarningWhenDays;
envelope.ExpiresWhen = settings.ExpiresWhen;
envelope.ExpiresWarningWhen = settings.ExpiresWarningWhen;
envelope.ContractType = settings.ContractType;
envelope.ChangedWhen = now;
}
await Mediator.Send(new CreateHistoryCommand await Mediator.Send(new CreateHistoryCommand
{ {
EnvelopeId = envelope.Id, EnvelopeId = envelope.Id,
@@ -143,6 +225,105 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
return _mapper.Map<EnvelopeDto>(envelope); return _mapper.Map<EnvelopeDto>(envelope);
} }
private async Task<EnvelopeSettings> ResolveEnvelopeSettingsAsync(CreateEnvelopeCommand request, Envelope? existing, CancellationToken cancel)
{
EnvelopeType? selectedType = null;
if (request.EnvelopeTypeId is int envelopeTypeId && envelopeTypeId > 0)
{
selectedType = await EnvelopeTypeRepository.Query
.FirstOrDefaultAsync(t => t.Id == envelopeTypeId, cancel)
?? throw new BadRequestException($"Envelope type {envelopeTypeId} not found.");
}
var expiresWhenDays = Math.Max(0,
request.ExpiresWhenDays
?? selectedType?.ExpiresWhenDays
?? existing?.ExpiresWhenDays
?? 0);
var expiresWarningWhenDays = Math.Max(0,
request.ExpiresWarningWhenDays
?? selectedType?.ExpiresWarningWhenDays
?? existing?.ExpiresWarningWhenDays
?? 0);
var useAccessCode = request.UseAccessCode
?? selectedType?.UseAccessCode
?? existing?.UseAccessCode
?? false;
if (request.TFAEnabled)
useAccessCode = true;
return new EnvelopeSettings(
EnvelopeTypeId: request.EnvelopeTypeId ?? existing?.EnvelopeTypeId,
Language: ResolveLanguage(request.Language, selectedType?.Language, existing?.Language),
UseAccessCode: useAccessCode,
CertificationType: request.CertificationType
?? (int?)selectedType?.CertificationType
?? existing?.CertificationType
?? (int)CertificationType.AdvancedElectronicSignature,
FinalEmailToCreator: request.FinalEmailToCreator
?? (int?)selectedType?.FinalEmailToCreator
?? existing?.FinalEmailToCreator
?? (int)FinalEmailType.No,
FinalEmailToReceivers: request.FinalEmailToReceivers
?? (int?)selectedType?.FinalEmailToReceivers
?? existing?.FinalEmailToReceivers
?? (int)FinalEmailType.No,
SendReminderEmails: request.SendReminderEmails
?? selectedType?.SendReminderEmails
?? existing?.SendReminderEmails
?? false,
FirstReminderDays: Math.Max(0,
request.FirstReminderDays
?? selectedType?.FirstReminderDays
?? existing?.FirstReminderDays
?? 0),
ReminderIntervalDays: Math.Max(0,
request.ReminderIntervalDays
?? selectedType?.ReminderIntervalDays
?? existing?.ReminderIntervalDays
?? 0),
ExpiresWhenDays: expiresWhenDays,
ExpiresWarningWhenDays: expiresWarningWhenDays,
ExpiresWhen: expiresWhenDays > 0 ? DateTime.Now.AddDays(expiresWhenDays) : null,
ExpiresWarningWhen: expiresWarningWhenDays > 0 ? DateTime.Now.AddDays(expiresWarningWhenDays) : null,
ContractType: existing?.ContractType
?? selectedType?.ContractType
?? (int)ContractType.Contract);
}
private static string ResolveLanguage(string? preferred, string? fallback1, string? fallback2)
{
if (!string.IsNullOrWhiteSpace(preferred))
return preferred.Trim();
if (!string.IsNullOrWhiteSpace(fallback1))
return fallback1.Trim();
if (!string.IsNullOrWhiteSpace(fallback2))
return fallback2.Trim();
return "de-DE";
}
private sealed record EnvelopeSettings(
int? EnvelopeTypeId,
string Language,
bool UseAccessCode,
int CertificationType,
int FinalEmailToCreator,
int FinalEmailToReceivers,
bool SendReminderEmails,
int FirstReminderDays,
int ReminderIntervalDays,
int ExpiresWhenDays,
int ExpiresWarningWhenDays,
DateTime? ExpiresWhen,
DateTime? ExpiresWarningWhen,
int ContractType);
private async Task<string> ResolveSenderReferenceAsync(Envelope envelope, CancellationToken cancel) private async Task<string> ResolveSenderReferenceAsync(Envelope envelope, CancellationToken cancel)
{ {
if (!string.IsNullOrWhiteSpace(envelope.User?.Email)) if (!string.IsNullOrWhiteSpace(envelope.User?.Email))