Compare commits

..

11 Commits

Author SHA1 Message Date
bcc17f6def Bump version to 3.11.0 in EnvelopeGenerator.Web.csproj
Updated <Version>, <AssemblyVersion>, and <FileVersion> properties from 3.10.0 to 3.11.0 to reflect the new release. No other changes were made.
2026-03-04 16:49:07 +01:00
8e3c334fa3 Update final status to 'Lesebestätigung' for read confirm
Changed the [FINAL_STATUS] placeholder value from "Bestätigung" to the more specific "Lesebestätigung" when an envelope is read and confirmed, improving clarity in status messaging.
2026-03-04 16:19:45 +01:00
08299451bb Customize email placeholders by envelope action type
Added logic to set email template placeholders based on whether the envelope requires "Read and Confirm" or "Sign" actions. Placeholders such as [SIGNATURE_TYPE], [DOCUMENT_PROCESS], [FINAL_STATUS], [FINAL_ACTION], [REJECTED_BY_OTHERS], and [RECEIVER_ACTION] are now dynamically set to reflect the correct process and status, improving the accuracy and clarity of notification emails.
2026-03-04 16:19:33 +01:00
59d6d25bdd Add ChangedWhen property to History entity
Added a [NotMapped] ChangedWhen property to the History entity as a wrapper for ActionDate, providing alternative get/set access without affecting the database schema.
2026-03-04 16:09:50 +01:00
9a516ab3c9 Update and align NuGet dependencies; drop net7.0 from Web
- Downgrade Microsoft.Data.SqlClient to 5.2.2 in Application and Infrastructure projects
- Downgrade Microsoft.Extensions.DependencyInjection to 8.0.1 for net8.0 in Application
- Remove net7.0 target and related packages from Web project
- Upgrade OpenApi and Caching.SqlServer to 8.0.17 (net8.0) and 9.0.6 (net9.0) in Web
- Ensures consistent, compatible package versions across solution
2026-03-04 16:09:40 +01:00
8fed342dc5 Remove [SIGNATURE_TYPE] placeholder from MailParams
The [SIGNATURE_TYPE] placeholder and its value ("signieren") were removed from the MailParams section in appsettings.Mail.json. This cleans up unused or unnecessary configuration.
2026-03-04 15:17:47 +01:00
f44643aa3e Add dynamic email placeholders for confirm/sign workflows
Refactored CreatePlaceholders to set email template values based on whether the envelope requires "read and confirm" or signature. Renamed method parameter for clarity and updated usages. Added a TODO for future method unification. Improves email content accuracy for different envelope actions.
2026-03-04 15:02:04 +01:00
86c99596c4 Implement IEnvelope interface in EnvelopeDto
EnvelopeDto now implements the IEnvelope interface, enabling it to be used wherever IEnvelope is required. No other modifications were made to the class.
2026-03-04 14:19:11 +01:00
2d0c08b2ce Implement IEnvelope and update Envelope.ReadOnly property
Envelope now implements the IEnvelope interface. The ReadOnly property is marked as [Obsolete] and delegates to the IsReadAndConfirm extension method instead of directly checking EnvelopeTypeId. Added a using directive for EnvelopeGenerator.Domain.Interfaces.
2026-03-04 14:19:02 +01:00
e0aa963184 Add IEnvelope interface and IsReadAndConfirm extension
Introduced the IEnvelope interface with an EnvelopeTypeId property in the new EnvelopeGenerator.Domain.Interfaces namespace. Added the EnvelopeExtensions static class with an IsReadAndConfirm extension method to check if EnvelopeTypeId equals 2.
2026-03-04 14:18:28 +01:00
6d2ec4cc0b Update DI and Logging packages to v8, adjust binding redirects
Upgraded Microsoft.Extensions.DependencyInjection.Abstractions to 8.0.2 and Microsoft.Extensions.Logging.Abstractions to 8.0.3 in project references and packages.config. Updated related binding redirects in App.config. Also modified the solution file to change the build configuration for project {83ED2617-B398-4859-8F59-B38F8807E83E} so Debug|Any CPU now maps to Release|Any CPU. Adjusted System.Memory and System.Buffers binding redirect ranges.
2026-02-23 15:54:52 +01:00
26 changed files with 250 additions and 222 deletions

View File

@@ -2,6 +2,7 @@
using DigitalData.UserManager.Application.DTOs.User;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Domain.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.Common.Dto;
@@ -10,7 +11,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public record EnvelopeDto
public record EnvelopeDto : IEnvelope
{
/// <summary>
///

View File

@@ -29,12 +29,15 @@ public class DocStatusHandler : INotificationHandler<DocSignedNotification>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand()
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
{
EnvelopeId = notification.EnvelopeId,
ReceiverId = notification.ReceiverId,
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
await _sender.Send(new SaveDocStatusCommand()
{
Envelope = new() { Id = notification.EnvelopeId },
Receiver = new() { Id = notification.ReceiverId},
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
: BlankAnnotationJson
}, cancel);
}, cancel);
}
}

View File

@@ -3,6 +3,7 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.Extensions.Options;
using EnvelopeGenerator.Domain.Interfaces;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
@@ -45,6 +46,25 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
{ "[DOCUMENT_TITLE]", notification.Envelope?.Title ?? string.Empty },
};
if (notification.Envelope.IsReadAndConfirm())
{
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
}
else
{
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
placeHolders["[FINAL_STATUS]"] = "Signatur";
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
}
return placeHolders;
}
}

View File

@@ -8,22 +8,12 @@ namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
public record CreateDocStatusCommand : IRequest<DocumentStatus>
public record CreateDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatus>
{
/// <summary>
///
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
/// </summary>
public int EnvelopeId { get; set; }
/// <summary>
///
/// </summary>
public int ReceiverId { get; set; }
/// <summary>
/// Gets or sets the display value associated with the status.
/// </summary>
public string? Value { get; set; }
public DateTime AddedWhen => StatusChangedWhen;
}
/// <summary>

View File

@@ -0,0 +1,54 @@
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
public record ModifyDocStatusCommandBase : EnvelopeReceiverQueryBase
{
/// <summary>
///
/// </summary>
public int? EnvelopeId => Envelope.Id;
/// <summary>
///
/// </summary>
public int? ReceiverId => Receiver.Id;
/// <summary>
///
/// </summary>
public override ReceiverQueryBase Receiver { get => base.Receiver; set => base.Receiver = value; }
/// <summary>
/// Gets the current status code.
/// </summary>
public DocumentStatus Status => Value is null ? DocumentStatus.Created : DocumentStatus.Signed;
/// <summary>
/// Gets or sets the display value associated with the status.
/// </summary>
public string? Value { get; set; }
/// <summary>
/// Gets timestamp when this record was added.
/// </summary>
public DateTime StatusChangedWhen { get; } = DateTime.Now;
/// <summary>
/// Maps the current command to a new instance of the specified type.
/// </summary>
/// <typeparam name="TDest"></typeparam>
/// <returns></returns>
public TDest To<TDest>() where TDest : ModifyDocStatusCommandBase, new()
=> new()
{
Key = Key,
Envelope = Envelope,
Receiver = Receiver,
Value = Value
};
}

View File

@@ -0,0 +1,77 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
/// Represents a command to save the status of a document, either by creating a new status or updating an existing one based on the provided envelope and receiver identifiers.
/// It returns the identifier of the saved document status.
/// </summary>
public record SaveDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatusDto?>;
/// <summary>
///
/// </summary>
public class SaveDocStatusCommandHandler : IRequestHandler<SaveDocStatusCommand, DocumentStatusDto?>
{
private readonly IMapper _mapper;
private readonly IRepository<DocumentStatus> _repo;
private readonly IRepository<Envelope> _envRepo;
private readonly IRepository<Receiver> _rcvRepo;
/// <summary>
///
/// </summary>
/// <param name="mapper"></param>
/// <param name="repo"></param>
/// <param name="rcvRepo"></param>
/// <param name="envRepo"></param>
public SaveDocStatusCommandHandler(IMapper mapper, IRepository<DocumentStatus> repo, IRepository<Receiver> rcvRepo, IRepository<Envelope> envRepo)
{
_mapper = mapper;
_repo = repo;
_rcvRepo = rcvRepo;
_envRepo = envRepo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<DocumentStatusDto?> Handle(SaveDocStatusCommand request, CancellationToken cancel)
{
// ceck if exists
bool isExists = await _repo.ReadOnly().Where(request).AnyAsync(cancel);
var env = await _envRepo.ReadOnly().Where(request.Envelope).FirstAsync(cancel);
var rcv = await _rcvRepo.ReadOnly().Where(request.Receiver).FirstAsync(cancel);
request.Envelope.Id = env.Id;
request.Receiver.Id = rcv.Id;
if (isExists)
{
var uReq = request.To<UpdateDocStatusCommand>();
await _repo.UpdateAsync(uReq, q => q.Where(request), cancel);
}
else
{
var cReq = request.To<CreateDocStatusCommand>();
await _repo.CreateAsync(cReq, cancel);
}
var docStatus = await _repo.ReadOnly().Where(request).SingleOrDefaultAsync(cancel);
return _mapper.Map<DocumentStatusDto>(docStatus);
}
}

View File

@@ -1,41 +1,14 @@
using EnvelopeGenerator.Application.Common.Commands;
using EnvelopeGenerator.Domain.Entities;
using System.Linq.Expressions;
using EnvelopeGenerator.Domain;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
/// <param name="Value"></param>
public record DocStatusUpdateDto(string? Value);
/// <summary>
///
/// </summary>
public record UpdateDocStatusCommand : UpdateCommand<DocStatusUpdateDto, DocumentStatus>
public record UpdateDocStatusCommand : ModifyDocStatusCommandBase
{
/// <summary>
///
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
/// </summary>
public int EnvelopeId { get; set; }
/// <summary>
///
/// </summary>
public int ReceiverId { get; set; }
/// <summary>
/// Gets or sets the display value associated with the status.
/// </summary>
public string? Value { get; set; }
/// <summary>
///
/// </summary>
/// <returns></returns>
public override Expression<Func<DocumentStatus, bool>> BuildQueryExpression()
{
return ds => ds.EnvelopeId == EnvelopeId && ds.ReceiverId == ReceiverId;
}
public DateTime? ChangedWhen => StatusChangedWhen;
}

View File

@@ -18,16 +18,11 @@ public class MappingProfile : Profile
CreateMap<CreateDocStatusCommand, DocumentStatus>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
.ForMember(dest => dest.Status, opt => opt.MapFrom(
src => src.Value == null
? Domain.Constants.DocumentStatus.Created
: Domain.Constants.DocumentStatus.Signed))
.MapAddedWhen();
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.UtcNow))
.MapChangedWhen();
}
}

View File

@@ -37,7 +37,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
@@ -80,7 +80,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
<PackageReference Include="CommandDotNet">
<Version>7.0.5</Version>
</PackageReference>
@@ -88,7 +88,6 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageReference Include="CommandDotNet">
<Version>8.1.1</Version>
</PackageReference>
@@ -96,7 +95,6 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageReference Include="CommandDotNet">
<Version>8.1.1</Version>
</PackageReference>

View File

@@ -34,7 +34,7 @@ public record CreateHistoryCommand : EnvelopeReceiverQueryBase, IRequest<History
/// <summary>
///
/// </summary>
public DateTime AddedWhen { get; } = DateTime.UtcNow;
public DateTime AddedWhen { get; } = DateTime.Now;
/// <summary>
///

View File

@@ -1,5 +1,4 @@
using AutoMapper;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Entities;
@@ -18,7 +17,6 @@ public class MappingProfile: Profile
CreateMap<CreateHistoryCommand, History>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Sender, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
.MapAddedWhen();
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
}
}

View File

@@ -250,7 +250,7 @@
<value>Sie können bei Bedarf mit {0}, &lt;a href="mailto:{1}?subject={2}&amp;body=Sehr geehrte(r)%20{0},%0A%0A%0A"&gt;{1}&lt;/a&gt; Kontakt aufnehmen.</value>
</data>
<data name="RejectionInfo2_ext" xml:space="preserve">
<value>Der Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, &lt;a href="mailto:{1}?subject={2}&amp;body=Sehr geehrte(r)%20{0},%0A%0A%0A"&gt;{1}&lt;/a&gt; Kontakt aufnehmen.</value>
<value>Das Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, &lt;a href="mailto:{1}?subject={2}&amp;body=Sehr geehrte(r)%20{0},%0A%0A%0A"&gt;{1}&lt;/a&gt; Kontakt aufnehmen.</value>
</data>
<data name="RejectionReasonQ" xml:space="preserve">
<value>Bitte geben Sie einen Grund an:</value>
@@ -261,6 +261,9 @@
<data name="SignDoc" xml:space="preserve">
<value>Dokument unterschreiben</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Titel des Unterzeichnungs-Vorgangs</value>
</data>
<data name="UnexpectedError" xml:space="preserve">
<value>Ein unerwarteter Fehler ist aufgetreten.</value>
</data>
@@ -444,37 +447,4 @@
<data name="DocumentReset" xml:space="preserve">
<value>Dokument wurde zurückgesetzt.</value>
</data>
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
<value>Dokument erfolgreich gelesen und bestätigt!</value>
</data>
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
<value>Sie haben das Dokument gelesen und bestätigt. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</value>
</data>
<data name="Confirm" xml:space="preserve">
<value>Bestätigen</value>
</data>
<data name="RejectionInfoConfirmation" xml:space="preserve">
<value>Dieser Bestätigungsvorgang wurde abgelehnt!</value>
</data>
<data name="ConfirmDoc" xml:space="preserve">
<value>Dokument bestätigen</value>
</data>
<data name="DocConfirmed" xml:space="preserve">
<value>Dokument bestätigt</value>
</data>
<data name="ConfirmAgree" xml:space="preserve">
<value>Durch Klick auf Abschließen bestätige ich, das Dokument gelesen und zur Kenntnis genommen zu haben.</value>
</data>
<data name="ConfirmedBy" xml:space="preserve">
<value>Bestätigt von</value>
</data>
<data name="ConfirmationProcessTitle" xml:space="preserve">
<value>Titel des Lesebetätigungs-Vorgangs</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Titel des Unterzeichnungs-Vorgangs</value>
</data>
<data name="Confirmations" xml:space="preserve">
<value>Bestätigungen</value>
</data>
</root>

View File

@@ -261,6 +261,9 @@
<data name="SignDoc" xml:space="preserve">
<value>Sign document</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Title of the signing process</value>
</data>
<data name="UnexpectedError" xml:space="preserve">
<value>An unexpected error has occurred.</value>
</data>
@@ -444,37 +447,4 @@
<data name="DocumentReset" xml:space="preserve">
<value>Document has been reset.</value>
</data>
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
<value>Document successfully red and confirmed!</value>
</data>
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
<value>You have read and confirmed the document. You will receive a written confirmation afterwards.</value>
</data>
<data name="Confirm" xml:space="preserve">
<value>Confirm</value>
</data>
<data name="RejectionInfoConfirmation" xml:space="preserve">
<value>This confirmation process has been rejected!</value>
</data>
<data name="ConfirmDoc" xml:space="preserve">
<value>Confirm Document</value>
</data>
<data name="DocConfirmed" xml:space="preserve">
<value>Document confirmed</value>
</data>
<data name="ConfirmAgree" xml:space="preserve">
<value>By clicking on “Complete”, I confirm that I have read and taken note of the document.</value>
</data>
<data name="ConfirmedBy" xml:space="preserve">
<value>Confirmed by</value>
</data>
<data name="ConfirmationProcessTitle" xml:space="preserve">
<value>Title of the read confirmation process</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Title of the signing process</value>
</data>
<data name="Confirmations" xml:space="preserve">
<value>Confirmations</value>
</data>
</root>

View File

@@ -261,6 +261,9 @@
<data name="SignDoc" xml:space="preserve">
<value>Signer le document</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Titre du processus de signature</value>
</data>
<data name="UnexpectedError" xml:space="preserve">
<value>Une erreur inattendue sest produite.</value>
</data>
@@ -444,37 +447,4 @@
<data name="DocumentReset" xml:space="preserve">
<value>Le document a été réinitialisé.</value>
</data>
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
<value>Document lu et confirmé avec succès !</value>
</data>
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
<value>Vous avez lu et confirmé le document. Vous recevrez une confirmation écrite par la suite.</value>
</data>
<data name="Confirm" xml:space="preserve">
<value>Confirmer</value>
</data>
<data name="RejectionInfoConfirmation" xml:space="preserve">
<value>Cette procédure de confirmation a été rejetée !</value>
</data>
<data name="ConfirmDoc" xml:space="preserve">
<value>Confirmer le document</value>
</data>
<data name="DocConfirmed" xml:space="preserve">
<value>Document confirmé</value>
</data>
<data name="ConfirmAgree" xml:space="preserve">
<value>En cliquant sur « Terminer », je confirme avoir lu et pris connaissance du document.</value>
</data>
<data name="ConfirmedBy" xml:space="preserve">
<value>Confirmé par</value>
</data>
<data name="ConfirmationProcessTitle" xml:space="preserve">
<value>Titre de la procédure de confirmation de lecture</value>
</data>
<data name="SigningProcessTitle" xml:space="preserve">
<value>Titre du processus de signature</value>
</data>
<data name="Confirmations" xml:space="preserve">
<value>Confirmations</value>
</data>
</root>

View File

@@ -12,6 +12,7 @@ using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using MediatR;
using EnvelopeGenerator.Domain.Interfaces;
namespace EnvelopeGenerator.Application.Services;
@@ -49,14 +50,33 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
_sender = sender;
}
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? er = null)
{
if (er!.Envelope.IsReadAndConfirm())
{
_placeholders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
_placeholders["[DOCUMENT_PROCESS]"] = string.Empty;
_placeholders["[FINAL_STATUS]"] = "Lesebestätigung";
_placeholders["[FINAL_ACTION]"] = "Empfänger bestätigt";
_placeholders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
_placeholders["[RECEIVER_ACTION]"] = "bestätigt";
}
else
{
_placeholders["[SIGNATURE_TYPE]"] = "Signieren";
_placeholders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
_placeholders["[FINAL_STATUS]"] = "Signatur";
_placeholders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
_placeholders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
_placeholders["[RECEIVER_ACTION]"] = "unterschrieben";
}
if (accessCode is not null)
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
if (envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
if (er?.Envelope is not null && er.Receiver is not null)
{
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey();
var erId = (er.Envelope.Uuid, er.Receiver.Signature).ToEnvelopeKey();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
@@ -66,7 +86,8 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
return _placeholders;
}
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
// TODO: merge the two CreatePlaceholders methods by using a common parameter object containing all the required information to create the place holders.
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
{
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
{
@@ -124,7 +145,7 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
var accessCode = acResult.Data;
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
var placeholders = await CreatePlaceholders(accessCode: accessCode, er: dto);
// Add optional place holders.
if (optionalPlaceholders is not null)

View File

@@ -35,10 +35,6 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("STATUS")]
public Constants.DocumentStatus Status { get; set; }
[Required]
[Column("STATUS_CHANGED_WHEN", TypeName = "datetime")]
public DateTime? StatusChangedWhen { get; set; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public DateTime AddedWhen { get; set; }

View File

@@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Constants;
using Newtonsoft.Json;
using EnvelopeGenerator.Domain.Interfaces.Auditing;
using EnvelopeGenerator.Domain.Interfaces;
#if NETFRAMEWORK
using System.Collections.Generic;
using System.Linq;
@@ -13,7 +14,7 @@ using System.Linq;
namespace EnvelopeGenerator.Domain.Entities
{
[Table("TBSIG_ENVELOPE", Schema = "dbo")]
public class Envelope : IHasAddedWhen, IHasChangedWhen
public class Envelope : IHasAddedWhen, IHasChangedWhen, IEnvelope
{
public Envelope()
{
@@ -106,7 +107,8 @@ namespace EnvelopeGenerator.Domain.Entities
[JsonIgnore]
[NotMapped]
public bool ReadOnly => EnvelopeTypeId == 2;
[Obsolete("Use EnvelopeGenerator.Domain.Interfaces.EnvelopeExtensions.IsReadAndConfirm extension method instead.")]
public bool ReadOnly => this.IsReadAndConfirm();
[Column("CERTIFICATION_TYPE")]
public int? CertificationType { get; set; }

View File

@@ -37,6 +37,7 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("ACTION_DATE", TypeName = "datetime")]
public DateTime? ActionDate { get; set; }
[NotMapped]
public DateTime? ChangedWhen { get => ActionDate; set => ActionDate = value; }
[Column("COMMENT", TypeName = "nvarchar(max)")]

View File

@@ -0,0 +1,15 @@
namespace EnvelopeGenerator.Domain.Interfaces
{
public interface IEnvelope
{
int? EnvelopeTypeId { get; set; }
}
public static class EnvelopeExtensions
{
public static bool IsReadAndConfirm(this IEnvelope envelope)
{
return envelope.EnvelopeTypeId == 2;
}
}
}

View File

@@ -25,7 +25,7 @@
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
<PackageReference Include="QuestPDF" Version="2025.7.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />

View File

@@ -23,7 +23,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -31,7 +31,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -59,7 +59,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.3" newVersion="8.0.0.3" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
@@ -67,7 +67,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />

View File

@@ -181,11 +181,11 @@
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.3, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.8.0.3\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>

View File

@@ -13,8 +13,8 @@
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="8.0.2" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="8.0.3" targetFramework="net462" />
<package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net48" />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>EnvelopeGenerator.Web</PackageId>
@@ -12,9 +12,9 @@
<PackageTags>digital data envelope generator web</PackageTags>
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<Version>3.10.0</Version> <!-- NuGet package version -->
<AssemblyVersion>3.10.0.0</AssemblyVersion> <!-- Assembly version for API compatibility -->
<FileVersion>3.10.0.0</FileVersion> <!-- Windows file version -->
<Version>3.11.0</Version> <!-- NuGet package version -->
<AssemblyVersion>3.11.0.0</AssemblyVersion> <!-- Assembly version for API compatibility -->
<FileVersion>3.11.0.0</FileVersion> <!-- Windows file version -->
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
</PropertyGroup>
@@ -2093,39 +2093,14 @@
<None Include="wwwroot\lib\bootstrap-icons\icons\zoom-out.svg" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
<PackageReference Include="Quartz" Version="3.8.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
<PackageReference Include="Quartz.Plugins" Version="3.8.0" />
<PackageReference Include="Quartz.Serialization.Json" Version="3.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
<PackageReference Include="System.DirectoryServices" Version="7.0.1" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.17" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.17" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
@@ -2149,8 +2124,8 @@
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />

View File

@@ -8,7 +8,6 @@
"MailParams": {
"Placeholders": {
"[NAME_PORTAL]": "signFlow",
"[SIGNATURE_TYPE]": "signieren",
"[REASON]": ""
}
}

View File

@@ -53,8 +53,8 @@ Global
{5E0E17C0-FF5A-4246-BF87-1ADD85376A27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E0E17C0-FF5A-4246-BF87-1ADD85376A27}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{5E0E17C0-FF5A-4246-BF87-1ADD85376A27}.Release|Any CPU.Build.0 = Debug|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.Build.0 = Release|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.Build.0 = Debug|Any CPU
{4F32A98D-E6F0-4A09-BD97-1CF26107E837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU