diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs index 029d621a..1222c343 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs @@ -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; /// /// [ApiExplorerSettings(IgnoreApi = true)] -public record EnvelopeDto +public record EnvelopeDto : IEnvelope { /// /// @@ -74,10 +75,12 @@ public record EnvelopeDto /// public int? EnvelopeTypeId { get; set; } + // TODO: use ReadAndConfirm property name /// /// /// - public bool ReadOnly => EnvelopeTypeId == 2; + [Obsolete("Use EnvelopeExtensions.IsReadAndConfirm extension metot instead.")] + public bool ReadOnly => this.IsReadAndConfirm(); /// /// diff --git a/EnvelopeGenerator.Application/Common/Extensions/AutoMapperAuditingExtensions.cs b/EnvelopeGenerator.Application/Common/Extensions/AutoMapperAuditingExtensions.cs index 4ce31eb1..72f2abef 100644 --- a/EnvelopeGenerator.Application/Common/Extensions/AutoMapperAuditingExtensions.cs +++ b/EnvelopeGenerator.Application/Common/Extensions/AutoMapperAuditingExtensions.cs @@ -13,12 +13,12 @@ public static class AutoMapperAuditingExtensions /// public static IMappingExpression MapAddedWhen(this IMappingExpression expression) where TDestination : IHasAddedWhen - => expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow)); + => expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now)); /// /// Maps to the current UTC time. /// public static IMappingExpression MapChangedWhen(this IMappingExpression expression) where TDestination : IHasChangedWhen - => expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow)); + => expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.Now)); } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/DocStatusHandler.cs b/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/DocStatusHandler.cs index adae66cf..dd90d3c5 100644 --- a/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/DocStatusHandler.cs +++ b/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/DocStatusHandler.cs @@ -29,15 +29,12 @@ public class DocStatusHandler : INotificationHandler /// /// /// - public async Task Handle(DocSignedNotification notification, CancellationToken cancel) + public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand() { - 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) + EnvelopeId = notification.EnvelopeId, + ReceiverId = notification.ReceiverId, + Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot + ? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations) : BlankAnnotationJson - }, cancel); - } + }, cancel); } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/SendSignedMailHandler.cs b/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/SendSignedMailHandler.cs index 73ae86ba..8b7aa4f1 100644 --- a/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/SendSignedMailHandler.cs +++ b/EnvelopeGenerator.Application/Common/Notifications/DocSigned/Handlers/SendSignedMailHandler.cs @@ -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 { "[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; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Common/Notifications/SendMailHandler.cs b/EnvelopeGenerator.Application/Common/Notifications/SendMailHandler.cs index 5a97ef03..50cf3662 100644 --- a/EnvelopeGenerator.Application/Common/Notifications/SendMailHandler.cs +++ b/EnvelopeGenerator.Application/Common/Notifications/SendMailHandler.cs @@ -113,7 +113,7 @@ public abstract class SendMailHandler : INotificationHandler /// /// -public record CreateDocStatusCommand : ModifyDocStatusCommandBase, IRequest +public record CreateDocStatusCommand : IRequest { /// - /// Gets timestamp when this record was added. Returns the StatusChangedWhen value. + /// /// - public DateTime AddedWhen => StatusChangedWhen; + public int EnvelopeId { get; set; } + + /// + /// + /// + public int ReceiverId { get; set; } + + /// + /// Gets or sets the display value associated with the status. + /// + public string? Value { get; set; } } /// diff --git a/EnvelopeGenerator.Application/DocStatus/Commands/ModifyDocStatusCommandBase.cs b/EnvelopeGenerator.Application/DocStatus/Commands/ModifyDocStatusCommandBase.cs deleted file mode 100644 index 259cb500..00000000 --- a/EnvelopeGenerator.Application/DocStatus/Commands/ModifyDocStatusCommandBase.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EnvelopeGenerator.Application.Common.Query; -using EnvelopeGenerator.Domain.Constants; - -namespace EnvelopeGenerator.Application.DocStatus.Commands; - -/// -/// -/// -public record ModifyDocStatusCommandBase : EnvelopeReceiverQueryBase -{ - /// - /// - /// - public int? EnvelopeId => Envelope.Id; - - /// - /// - /// - public int? ReceiverId => Receiver.Id; - - /// - /// - /// - public override ReceiverQueryBase Receiver { get => base.Receiver; set => base.Receiver = value; } - - /// - /// Gets the current status code. - /// - public DocumentStatus Status => Value is null ? DocumentStatus.Created : DocumentStatus.Signed; - - /// - /// Gets or sets the display value associated with the status. - /// - public string? Value { get; set; } - - /// - /// Gets timestamp when this record was added. - /// - public DateTime StatusChangedWhen { get; } = DateTime.Now; - - /// - /// Maps the current command to a new instance of the specified type. - /// - /// - /// - public TDest To() where TDest : ModifyDocStatusCommandBase, new() - => new() - { - Key = Key, - Envelope = Envelope, - Receiver = Receiver, - Value = Value - }; -} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DocStatus/Commands/SaveDocStatusCommand.cs b/EnvelopeGenerator.Application/DocStatus/Commands/SaveDocStatusCommand.cs deleted file mode 100644 index 0f041e8a..00000000 --- a/EnvelopeGenerator.Application/DocStatus/Commands/SaveDocStatusCommand.cs +++ /dev/null @@ -1,77 +0,0 @@ -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; - -/// -/// 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. -/// -public record SaveDocStatusCommand : ModifyDocStatusCommandBase, IRequest; - -/// -/// -/// -public class SaveDocStatusCommandHandler : IRequestHandler -{ - private readonly IMapper _mapper; - - private readonly IRepository _repo; - - private readonly IRepository _envRepo; - - private readonly IRepository _rcvRepo; - - /// - /// - /// - /// - /// - /// - /// - public SaveDocStatusCommandHandler(IMapper mapper, IRepository repo, IRepository rcvRepo, IRepository envRepo) - { - _mapper = mapper; - _repo = repo; - _rcvRepo = rcvRepo; - _envRepo = envRepo; - } - - /// - /// - /// - /// - /// - /// - public async Task 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(); - await _repo.UpdateAsync(uReq, q => q.Where(request), cancel); - } - else - { - var cReq = request.To(); - await _repo.CreateAsync(cReq, cancel); - } - - var docStatus = await _repo.ReadOnly().Where(request).SingleOrDefaultAsync(cancel); - - return _mapper.Map(docStatus); - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DocStatus/Commands/UpdateDocStatusCommand.cs b/EnvelopeGenerator.Application/DocStatus/Commands/UpdateDocStatusCommand.cs index f6c3b57f..dcdd8b18 100644 --- a/EnvelopeGenerator.Application/DocStatus/Commands/UpdateDocStatusCommand.cs +++ b/EnvelopeGenerator.Application/DocStatus/Commands/UpdateDocStatusCommand.cs @@ -1,14 +1,41 @@ -using EnvelopeGenerator.Domain; +using EnvelopeGenerator.Application.Common.Commands; +using EnvelopeGenerator.Domain.Entities; +using System.Linq.Expressions; namespace EnvelopeGenerator.Application.DocStatus.Commands; /// /// /// -public record UpdateDocStatusCommand : ModifyDocStatusCommandBase +/// +public record DocStatusUpdateDto(string? Value); + +/// +/// +/// +public record UpdateDocStatusCommand : UpdateCommand { /// - /// Gets timestamp when this record was added. Returns the StatusChangedWhen value. + /// /// - public DateTime? ChangedWhen => StatusChangedWhen; + public int EnvelopeId { get; set; } + + /// + /// + /// + public int ReceiverId { get; set; } + + /// + /// Gets or sets the display value associated with the status. + /// + public string? Value { get; set; } + + /// + /// + /// + /// + public override Expression> BuildQueryExpression() + { + return ds => ds.EnvelopeId == EnvelopeId && ds.ReceiverId == ReceiverId; + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DocStatus/MappingProfile.cs b/EnvelopeGenerator.Application/DocStatus/MappingProfile.cs index e1474d7e..01b1b57f 100644 --- a/EnvelopeGenerator.Application/DocStatus/MappingProfile.cs +++ b/EnvelopeGenerator.Application/DocStatus/MappingProfile.cs @@ -18,11 +18,16 @@ public class MappingProfile : Profile CreateMap() .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() .ForMember(dest => dest.Envelope, opt => opt.Ignore()) .ForMember(dest => dest.Receiver, opt => opt.Ignore()) + .ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.Now)) .MapChangedWhen(); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 0da15ec9..ba181291 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -37,7 +37,7 @@ - + @@ -80,7 +80,7 @@ - + 7.0.5 @@ -88,7 +88,6 @@ - 8.1.1 @@ -96,7 +95,6 @@ - 8.1.1 diff --git a/EnvelopeGenerator.Application/Histories/MappingProfile.cs b/EnvelopeGenerator.Application/Histories/MappingProfile.cs index 6261d5db..71e8916f 100644 --- a/EnvelopeGenerator.Application/Histories/MappingProfile.cs +++ b/EnvelopeGenerator.Application/Histories/MappingProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using EnvelopeGenerator.Application.Common.Extensions; using EnvelopeGenerator.Application.Histories.Commands; using EnvelopeGenerator.Domain.Entities; @@ -17,6 +18,7 @@ public class MappingProfile: Profile CreateMap() .ForMember(dest => dest.Envelope, opt => opt.Ignore()) .ForMember(dest => dest.Sender, opt => opt.Ignore()) - .ForMember(dest => dest.Receiver, opt => opt.Ignore()); + .ForMember(dest => dest.Receiver, opt => opt.Ignore()) + .MapAddedWhen(); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.cs b/EnvelopeGenerator.Application/Resources/Resource.cs index 53c11359..13223106 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.cs +++ b/EnvelopeGenerator.Application/Resources/Resource.cs @@ -190,6 +190,13 @@ public static class Extensions /// public static string SignDoc(this IStringLocalizer localizer) => localizer[nameof(SignDoc)].Value; + /// + /// + /// + /// + /// + public static string ConfirmDoc(this IStringLocalizer localizer) => localizer[nameof(ConfirmDoc)].Value; + /// /// /// @@ -204,6 +211,13 @@ public static class Extensions /// public static string DocSigned(this IStringLocalizer localizer) => localizer[nameof(DocSigned)].Value; + /// + /// + /// + /// + /// + public static string DocConfirmed(this IStringLocalizer localizer) => localizer[nameof(DocConfirmed)].Value; + /// /// /// @@ -239,6 +253,13 @@ public static class Extensions /// public static string SigAgree(this IStringLocalizer localizer) => localizer[nameof(SigAgree)].Value; + /// + /// + /// + /// + /// + public static string ConfirmAgree(this IStringLocalizer localizer) => localizer[nameof(ConfirmAgree)].Value; + /// /// /// @@ -267,6 +288,13 @@ public static class Extensions /// public static string RejectionInfo1(this IStringLocalizer localizer) => localizer[nameof(RejectionInfo1)].Value; + /// + /// + /// + /// + /// + public static string RejectionInfo1ForConfirmation(this IStringLocalizer localizer) => localizer[nameof(RejectionInfo1ForConfirmation)].Value; + /// /// /// @@ -295,6 +323,13 @@ public static class Extensions /// public static string SigningProcessTitle(this IStringLocalizer localizer) => localizer[nameof(SigningProcessTitle)].Value; + /// + /// + /// + /// + /// + public static string ConfirmationProcessTitle(this IStringLocalizer localizer) => localizer[nameof(ConfirmationProcessTitle)].Value; + /// /// /// diff --git a/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx index e16727d0..16fb00f5 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx @@ -250,7 +250,7 @@ Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen. - Das Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen. + Der Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen. Bitte geben Sie einen Grund an: @@ -447,4 +447,34 @@ Dokument wurde zurückgesetzt. + + Dokument erfolgreich gelesen und bestätigt! + + + Sie haben das Dokument gelesen und bestätigt. Im Anschluss erhalten Sie eine schriftliche Bestätigung. + + + Bestätigen + + + Dieser Bestätigungsvorgang wurde abgelehnt! + + + Dokument bestätigen + + + Dokument bestätigt + + + Durch Klick auf Abschließen bestätige ich, das Dokument gelesen und zur Kenntnis genommen zu haben. + + + Bestätigt von + + + Titel des Lesebetätigungs-Vorgangs + + + Bestätigungen + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.en-US.resx b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx index e12eb3bc..fa6c5afd 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.en-US.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx @@ -447,4 +447,34 @@ Document has been reset. + + Document successfully read and confirmed! + + + You have read and confirmed the document. You will receive a written confirmation afterwards. + + + Confirm + + + This confirmation process has been rejected! + + + Confirm Document + + + Document confirmed + + + By clicking on “Complete”, I confirm that I have read and taken note of the document. + + + Confirmed by + + + Title of the read confirmation process + + + Confirmations + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx b/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx index 766e4fb9..be60d7bb 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx @@ -447,4 +447,34 @@ Le document a été réinitialisé. + + Document lu et confirmé avec succès ! + + + Vous avez lu et confirmé le document. Vous recevrez une confirmation écrite par la suite. + + + Confirmer + + + Cette procédure de confirmation a été rejetée ! + + + Confirmer le document + + + Document confirmé + + + En cliquant sur « Terminer », je confirme avoir lu et pris connaissance du document. + + + Confirmé par + + + Titre de la procédure de confirmation de lecture + + + Confirmations + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeMailService.cs b/EnvelopeGenerator.Application/Services/EnvelopeMailService.cs index 0bfc7a4c..ad154298 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeMailService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeMailService.cs @@ -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> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null) + private async Task> 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> 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> 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().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) diff --git a/EnvelopeGenerator.BBTests/EnvelopeGenerator.BBTests.vbproj b/EnvelopeGenerator.BBTests/EnvelopeGenerator.BBTests.vbproj index de126637..63b3db6d 100644 --- a/EnvelopeGenerator.BBTests/EnvelopeGenerator.BBTests.vbproj +++ b/EnvelopeGenerator.BBTests/EnvelopeGenerator.BBTests.vbproj @@ -487,10 +487,6 @@ {6EA0C51F-C2B1-4462-8198-3DE0B32B74F8} EnvelopeGenerator.CommonServices - - {6ea0c51f-c2b1-4462-8198-3de0b32b74f8} - EnvelopeGenerator.CommonServices - {4f32a98d-e6f0-4a09-bd97-1cf26107e837} EnvelopeGenerator.Domain diff --git a/EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb b/EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb index 5a2794e2..2cd0616b 100644 --- a/EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb +++ b/EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb @@ -27,6 +27,7 @@ Partial Class frmFinalizePDF Me.Label2 = New System.Windows.Forms.Label() Me.Button1 = New System.Windows.Forms.Button() Me.Button2 = New System.Windows.Forms.Button() + Me.Button3 = New System.Windows.Forms.Button() Me.txtResult = New System.Windows.Forms.TextBox() Me.txtEnvelope = New System.Windows.Forms.TextBox() Me.SuspendLayout() @@ -75,6 +76,15 @@ Partial Class frmFinalizePDF Me.Button2.Text = "Merge Json" Me.Button2.UseVisualStyleBackColor = True ' + 'Button3 + ' + Me.Button3.Location = New System.Drawing.Point(15, 160) + Me.Button3.Name = "Button3" + Me.Button3.Size = New System.Drawing.Size(166, 23) + Me.Button3.TabIndex = 5 + Me.Button3.Text = "Full Finalize Test" + Me.Button3.UseVisualStyleBackColor = True + ' 'txtResult ' Me.txtResult.Location = New System.Drawing.Point(333, 12) @@ -97,6 +107,7 @@ Partial Class frmFinalizePDF Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(800, 450) Me.Controls.Add(Me.txtResult) + Me.Controls.Add(Me.Button3) Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.Label2) @@ -116,5 +127,6 @@ Partial Class frmFinalizePDF Friend WithEvents Label2 As Label Friend WithEvents Button1 As Button Friend WithEvents Button2 As Button + Friend WithEvents Button3 As Button Friend WithEvents txtResult As TextBox End Class diff --git a/EnvelopeGenerator.BBTests/frmFinalizePDF.vb b/EnvelopeGenerator.BBTests/frmFinalizePDF.vb index e6466c73..24847ec0 100644 --- a/EnvelopeGenerator.BBTests/frmFinalizePDF.vb +++ b/EnvelopeGenerator.BBTests/frmFinalizePDF.vb @@ -7,11 +7,12 @@ Imports GdPicture14 Imports Newtonsoft.Json.Linq Imports EnvelopeGenerator.Infrastructure Imports Microsoft.EntityFrameworkCore -Imports System.Text Imports DigitalData.Core.Abstractions +Imports DigitalData.Core.Abstraction.Application.Repository +Imports EnvelopeGenerator.Domain.Entities Public Class frmFinalizePDF - Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" + Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" + "Encrypt=True;TrustServerCertificate=True;" Private Database As MSSQLServer Private LogConfig As LogConfig @@ -93,56 +94,118 @@ Public Class frmFinalizePDF End Function Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click - Try + Dim oTable = LoadAnnotationDataForEnvelope() + Dim oJsonList = oTable.Rows. + Cast(Of DataRow). + Select(Function(r As DataRow) r.Item("VALUE").ToString()). + ToList() - Dim oTable = LoadAnnotationDataForEnvelope() - Dim oJsonList = oTable.Rows. - Cast(Of DataRow). - Select(Function(r As DataRow) r.Item("VALUE").ToString()). - ToList() + Dim envelopeId As Integer = CInt(txtEnvelope.Text) + Dim oBuffer As Byte() = ReadEnvelope(envelopeId) + Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId) + Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf") - Dim envelopeId As Integer = CInt(txtEnvelope.Text) - Dim oBuffer As Byte() = ReadEnvelope(envelopeId) - Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId) - Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) - Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf") - - File.WriteAllBytes(oNewPath, oNewBuffer) - - Process.Start(oNewPath) - Catch ex As Exception - Dim exMsg As StringBuilder = New StringBuilder(ex.Message).AppendLine() - - Dim innerEx = ex.InnerException - While (innerEx IsNot Nothing) - exMsg.AppendLine(innerEx.Message) - innerEx = innerEx.InnerException - End While - - MsgBox(exMsg.ToString(), MsgBoxStyle.Critical) - End Try + File.WriteAllBytes(oNewPath, oNewBuffer) + Process.Start(oNewPath) End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click + Dim oTable = LoadAnnotationDataForEnvelope() + Dim oJsonList = oTable.Rows. + Cast(Of DataRow). + Select(Function(r As DataRow) r.Item("VALUE").ToString()). + Select(Function(s As String) JObject.Parse(s)). + ToList() + + Dim oJObject1 = oJsonList.First() + Dim oJObject2 = oJsonList.ElementAt(1) + + oJObject1.Merge(oJObject2) + + txtResult.Text = oJObject1.ToString() + End Sub + + Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click Try + Dim envelopeId As Integer = CInt(txtEnvelope.Text) + Dim log As New System.Text.StringBuilder() + + ' 1. Load annotation JSON data (same as Service) Dim oTable = LoadAnnotationDataForEnvelope() Dim oJsonList = oTable.Rows. Cast(Of DataRow). Select(Function(r As DataRow) r.Item("VALUE").ToString()). - Select(Function(s As String) JObject.Parse(s)). ToList() + log.AppendLine($"Annotation JSON count: {oJsonList.Count}") - Dim oJObject1 = oJsonList.First() - Dim oJObject2 = oJsonList.ElementAt(1) + ' 2. Load document bytes (same as Service) + Dim oBuffer As Byte() = ReadEnvelope(envelopeId) + log.AppendLine($"Document bytes: {oBuffer.Length}") - oJObject1.Merge(oJObject2) + ' 3. Check what BurnAnnotsToPDF will do internally + Using scope = Factory.Shared.ScopeFactory.CreateScope() + Dim envRepo = scope.ServiceProvider.Repository(Of Envelope)() + Dim envelope = envRepo.Where(Function(env) env.Id = envelopeId).FirstOrDefault() - txtResult.Text = oJObject1.ToString() + If envelope Is Nothing Then + log.AppendLine("ERROR: Envelope not found in EF Core!") + txtResult.Text = log.ToString() + Return + End If + log.AppendLine($"Envelope found: Id={envelope.Id}, EnvelopeTypeId={envelope.EnvelopeTypeId}") + log.AppendLine($"ReadOnly (IsReadAndConfirm): {envelope.ReadOnly}") + + If envelope.ReadOnly Then + log.AppendLine(">>> EARLY RETURN: ReadOnly=True, original PDF returned without burning") + txtResult.Text = log.ToString() + Return + End If + + Dim sigRepo = scope.ServiceProvider.Repository(Of Signature)() + Dim elements = sigRepo _ + .Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _ + .Include(Function(sig) sig.Annotations) _ + .ToList() + + log.AppendLine($"Elements (Signature) count: {elements.Count}") + + If elements.Any() Then + log.AppendLine(">>> PATH: BurnElementAnnotsToPDF (new element-based path)") + For Each elem In elements + Dim annotCount = If(elem.Annotations IsNot Nothing, elem.Annotations.Count(), 0) + log.AppendLine($" Element Id={elem.Id}, Page={elem.Page}, X={elem.X}, Y={elem.Y}, W={elem.Width}, H={elem.Height}, Annotations={annotCount}") + If elem.Annotations IsNot Nothing Then + For Each annot In elem.Annotations + log.AppendLine($" Annot: Name={annot.Name}, Type={annot.Type}, X={annot.X}, Y={annot.Y}, W={annot.Width}, H={annot.Height}") + Next + End If + Next + Else + log.AppendLine(">>> PATH: BurnInstantJSONAnnotsToPDF (old JSON-based path)") + End If + End Using + + ' 4. Actually call BurnAnnotsToPDF (same as Service) + log.AppendLine("") + log.AppendLine("Calling BurnAnnotsToPDF...") + Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId) + log.AppendLine($"Result bytes: {oNewBuffer.Length}") + log.AppendLine($"Same as input: {oBuffer.Length = oNewBuffer.Length AndAlso oBuffer.SequenceEqual(oNewBuffer)}") + + ' 5. Write output + Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}_FullTest.burned.pdf") + File.WriteAllBytes(oNewPath, oNewBuffer) + log.AppendLine($"Output: {oNewPath}") + + txtResult.Text = log.ToString() + Process.Start(oNewPath) Catch ex As Exception - MsgBox(ex.Message, MsgBoxStyle.Critical) + txtResult.Text = $"ERROR: {ex.Message}{vbCrLf}{vbCrLf}{ex.ToString()}" End Try End Sub End Class \ No newline at end of file diff --git a/EnvelopeGenerator.BBTests/packages.config b/EnvelopeGenerator.BBTests/packages.config index 1114c525..2b6489de 100644 --- a/EnvelopeGenerator.BBTests/packages.config +++ b/EnvelopeGenerator.BBTests/packages.config @@ -15,13 +15,13 @@ - + - + @@ -62,10 +62,10 @@ - + - - + + @@ -74,23 +74,23 @@ - + - - + + - - + + - + - - + + - - + + \ No newline at end of file diff --git a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/FinalizeDocumentJob.vb b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/FinalizeDocumentJob.vb index cfd2af5e..9b0ca0fe 100644 --- a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/FinalizeDocumentJob.vb +++ b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/FinalizeDocumentJob.vb @@ -15,6 +15,7 @@ Imports DigitalData.Core.Abstraction.Application Imports EnvelopeGenerator.Infrastructure Imports Microsoft.EntityFrameworkCore Imports DigitalData.Core.Abstractions +Imports EnvelopeGenerator.Domain.Interfaces Namespace Jobs Public Class FinalizeDocumentJob @@ -230,6 +231,29 @@ Namespace Jobs Return Task.FromResult(True) End Function + +#Region "From BBTests" + Private Function ReadEnvelope(pEnvID As Integer) As Byte() + Dim strSql As String = "Select [BYTE_DATA] from [TBSIG_ENVELOPE_DOCUMENT] WHERE ENVELOPE_ID = " & pEnvID + Dim obyteDB = Database.GetScalarValue(strSql) + If Not IsDBNull(obyteDB) Then + Dim fileData As Byte() = DirectCast(Database.GetScalarValue(strSql), Byte()) + If fileData IsNot Nothing Then + Return fileData + End If + End If + + Throw New InvalidOperationException($"Byte data is null. Envelope ID: {pEnvID}") + + End Function + + Private Function LoadAnnotationDataForEnvelope(pEnvID As Integer) As DataTable + Dim oSql = $"SELECT VALUE FROM [TBSIG_DOCUMENT_STATUS] WHERE ENVELOPE_ID = {pEnvID}" + Return Database.GetDatatable(oSql) + + End Function +#End Region + Private Sub Update_File_DB(pFilePath As String, pEnvelopeID As Long) Dim SqlCom As SqlCommand Dim imageData As Byte() @@ -327,7 +351,7 @@ Namespace Jobs Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ") End If - If oMailToReceivers <> FinalEmailType.No Then + If oMailToReceivers <> FinalEmailType.No And pEnvelope.IsReadAndSign() Then Logger.Debug("Sending emails to receivers..") SendFinalEmailToReceivers(pEnvelope) ', pAttachment Else @@ -418,7 +442,18 @@ Namespace Jobs End Try End If - Return PDFBurner.BurnAnnotsToPDF(oInputDocumentBuffer, oAnnotations, pEnvelopeData.EnvelopeId) +#Region "From BBTests" + Dim oTable = LoadAnnotationDataForEnvelope(pEnvelopeId) + Dim oJsonList = oTable.Rows. + Cast(Of DataRow). + Select(Function(r As DataRow) r.Item("VALUE").ToString()). + ToList() + + Dim oBuffer As Byte() = ReadEnvelope(pEnvelopeId) + +#End Region + + Return PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, pEnvelopeId) End Function Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData diff --git a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFBurner.vb b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFBurner.vb index 781615ac..97347a72 100644 --- a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFBurner.vb +++ b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFBurner.vb @@ -85,16 +85,37 @@ Namespace Jobs.FinalizeDocument 'Add annotations For Each element In elements + If element Is Nothing Then + Continue For + End If + + Dim elementAnnotations = If(element.Annotations, Enumerable.Empty(Of ElementAnnotation)()) + If Not elementAnnotations.Any() Then + Continue For + End If Dim frameX = (element.Left - 0.7 - margin) - Dim frame = element.Annotations.FirstOrDefault(Function(a) a.Name = "frame") + Dim frame = elementAnnotations.FirstOrDefault(Function(a) a.Name = "frame") Dim frameY = element.Top - 0.5 - margin - Dim frameYShift = frame.Y - frameY * inchFactor - Dim frameXShift = frame.X - frameX * inchFactor + Dim frameYShift As Double = 0 + Dim frameXShift As Double = 0 + + If frame IsNot Nothing Then + frameYShift = frame.Y - frameY * inchFactor + frameXShift = frame.X - frameX * inchFactor + End If + + For Each annot In elementAnnotations + If annot Is Nothing Then + Continue For + End If + + Dim yOffsetofFF As Double = 0 + If Not String.IsNullOrEmpty(annot.Name) Then + yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF) + End If - For Each annot In element.Annotations - Dim yOffsetofFF As Double = If(yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF), yOffsetofFF, 0) Dim y = frameY + yOffsetofFF If annot.Type = AnnotationType.FormField Then diff --git a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFMerger.vb b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFMerger.vb index 207a916c..f0c3f963 100644 --- a/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFMerger.vb +++ b/EnvelopeGenerator.CommonServices/Jobs/FinalizeDocument/PDFMerger.vb @@ -56,7 +56,7 @@ Public Class PDFMerger ' Convert to PDF/A oMergedPDF.ConvertToPDFA(oFinalStream, PDFAConformanceLevel, ALLOW_VECTORIZATION, ALLOW_RASTERIZATION) - oStatus = oDocumentPDF.GetStat() + oStatus = oMergedPDF.GetStat() If oStatus <> GdPictureStatus.OK Then Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}") End If diff --git a/EnvelopeGenerator.CommonServices/Models/ReceiverModel.vb b/EnvelopeGenerator.CommonServices/Models/ReceiverModel.vb index 2d2395c9..78408d40 100644 --- a/EnvelopeGenerator.CommonServices/Models/ReceiverModel.vb +++ b/EnvelopeGenerator.CommonServices/Models/ReceiverModel.vb @@ -270,12 +270,26 @@ Public Class ReceiverModel Private Function GetSignedDate(pEmailAddress As String, pEnvelopeId As Integer) As Date Try Dim oStatusInt As Integer = EnvelopeStatus.DocumentSigned - Return Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId} + Dim value = Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId} And USER_REFERENCE = '{pEmailAddress}' AND [STATUS] = {oStatusInt}") + If value Is Nothing OrElse value Is DBNull.Value Then + Return DateTime.MinValue + End If + + If TypeOf value Is DateTime Then + Return DirectCast(value, DateTime) + End If + + Dim parsedDate As DateTime + If DateTime.TryParse(value.ToString(), parsedDate) Then + Return parsedDate + End If + + Return DateTime.MinValue Catch ex As Exception Logger.Error(ex) - Return Nothing + Return DateTime.MinValue End Try End Function diff --git a/EnvelopeGenerator.CommonServices/Strings/Model.Designer.vb b/EnvelopeGenerator.CommonServices/Strings/Model.Designer.vb index b121b4ef..ed819c29 100644 --- a/EnvelopeGenerator.CommonServices/Strings/Model.Designer.vb +++ b/EnvelopeGenerator.CommonServices/Strings/Model.Designer.vb @@ -1,10 +1,10 @@ '------------------------------------------------------------------------------ ' -' Dieser Code wurde von einem Tool generiert. -' Laufzeitversion:4.0.30319.42000 +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 ' -' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -' der Code erneut generiert wird. +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. ' '------------------------------------------------------------------------------ @@ -15,12 +15,12 @@ Imports System Namespace My.Resources - 'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - '-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - 'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - 'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. ''' - ''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + ''' A strongly-typed resource class, for looking up localized strings, etc. ''' - ''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + ''' Returns the cached ResourceManager instance used by this class. ''' _ Public Shared ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager @@ -51,8 +51,8 @@ Namespace My.Resources End Property ''' - ''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - ''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. ''' _ Public Shared Property Culture() As Global.System.Globalization.CultureInfo @@ -65,7 +65,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt. + ''' Looks up a localized string similar to Zugriffscode korrekt eingegeben. ''' Public Shared ReadOnly Property AccessCodeCorrect() As String Get @@ -74,7 +74,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt. + ''' Looks up a localized string similar to Zugriffscode falsch eingegeben. ''' Public Shared ReadOnly Property AccessCodeIncorrect() As String Get @@ -83,7 +83,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt. + ''' Looks up a localized string similar to Zugriffscode angefordert. ''' Public Shared ReadOnly Property AccessCodeRequested() As String Get @@ -92,7 +92,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt. + ''' Looks up a localized string similar to Fortgeschrittene Elektronische Signatur. ''' Public Shared ReadOnly Property AdvancedElectronicSignature() As String Get @@ -101,7 +101,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt. + ''' Looks up a localized string similar to Abgeschlossen. ''' Public Shared ReadOnly Property Completed() As String Get @@ -110,7 +110,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt. + ''' Looks up a localized string similar to Vollständig bestätigt. + ''' + Public Shared ReadOnly Property CompletelyConfirmed() As String + Get + Return ResourceManager.GetString("CompletelyConfirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Vollständig signiert. ''' Public Shared ReadOnly Property CompletelySigned() As String Get @@ -119,7 +128,25 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt. + ''' Looks up a localized string similar to Lesebestätigung. + ''' + Public Shared ReadOnly Property Confirmation() As String + Get + Return ResourceManager.GetString("Confirmation", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Gelesen und Bestätigt. + ''' + Public Shared ReadOnly Property Confirmed() As String + Get + Return ResourceManager.GetString("Confirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Vertrag. ''' Public Shared ReadOnly Property Contract() As String Get @@ -128,7 +155,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt. + ''' Looks up a localized string similar to Erstellt. ''' Public Shared ReadOnly Property Created() As String Get @@ -137,7 +164,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt. + ''' Looks up a localized string similar to Dokument gelesen und bestätigt. + ''' + Public Shared ReadOnly Property DocumentConfirmed() As String + Get + Return ResourceManager.GetString("DocumentConfirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Dokument Rotation geändert. ''' Public Shared ReadOnly Property DocumentMod_Rotation() As String Get @@ -146,7 +182,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt. + ''' Looks up a localized string similar to Dokument geöffnet. ''' Public Shared ReadOnly Property DocumentOpened() As String Get @@ -155,7 +191,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt. + ''' Looks up a localized string similar to Unterzeichnung abgelehnt. ''' Public Shared ReadOnly Property DocumentRejected() As String Get @@ -164,7 +200,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt. + ''' Looks up a localized string similar to Lesebestätigung abgelehnt. + ''' + Public Shared ReadOnly Property DocumentRejectedRaC() As String + Get + Return ResourceManager.GetString("DocumentRejectedRaC", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Dokument unterzeichnet. ''' Public Shared ReadOnly Property DocumentSigned() As String Get @@ -173,7 +218,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt. + ''' Looks up a localized string similar to Entwurf. ''' Public Shared ReadOnly Property Draft() As String Get @@ -182,7 +227,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt. + ''' Looks up a localized string similar to Archiviert. ''' Public Shared ReadOnly Property EnvelopeArchived() As String Get @@ -191,7 +236,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt. + ''' Looks up a localized string similar to Vollständig bestätigt. + ''' + Public Shared ReadOnly Property EnvelopeCompletelyConfirmed() As String + Get + Return ResourceManager.GetString("EnvelopeCompletelyConfirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Vollständig signiert. ''' Public Shared ReadOnly Property EnvelopeCompletelySigned() As String Get @@ -200,7 +254,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt. + ''' Looks up a localized string similar to Umschlag Erstellt. ''' Public Shared ReadOnly Property EnvelopeCreated() As String Get @@ -209,7 +263,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt. + ''' Looks up a localized string similar to Umschlag Gelöscht. ''' Public Shared ReadOnly Property EnvelopeDeleted() As String Get @@ -218,7 +272,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt. + ''' Looks up a localized string similar to Teil-Bestätigt. + ''' + Public Shared ReadOnly Property EnvelopePartlyConfirmed() As String + Get + Return ResourceManager.GetString("EnvelopePartlyConfirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Teil-Signiert. ''' Public Shared ReadOnly Property EnvelopePartlySigned() As String Get @@ -227,7 +290,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt. + ''' Looks up a localized string similar to Umschlag in Queue. ''' Public Shared ReadOnly Property EnvelopeQueued() As String Get @@ -236,7 +299,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt. + ''' Looks up a localized string similar to Umschlag abgelehnt. ''' Public Shared ReadOnly Property EnvelopeRejected() As String Get @@ -245,7 +308,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt. + ''' Looks up a localized string similar to Signierungszertifikat erstellt. ''' Public Shared ReadOnly Property EnvelopeReportCreated() As String Get @@ -254,7 +317,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt. + ''' Looks up a localized string similar to Lesebestätigungszertifikat erstellt. + ''' + Public Shared ReadOnly Property EnvelopeReportCreatedRaC() As String + Get + Return ResourceManager.GetString("EnvelopeReportCreatedRaC", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Gespeichert. ''' Public Shared ReadOnly Property EnvelopeSaved() As String Get @@ -263,7 +335,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt. + ''' Looks up a localized string similar to Gesendet. ''' Public Shared ReadOnly Property EnvelopeSent() As String Get @@ -272,7 +344,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt. + ''' Looks up a localized string similar to Umschlag zurückgezogen. ''' Public Shared ReadOnly Property EnvelopeWithdrawn() As String Get @@ -281,7 +353,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt. + ''' Looks up a localized string similar to Zugriffscode versendet. ''' Public Shared ReadOnly Property MessageAccessCodeSent() As String Get @@ -290,7 +362,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt. + ''' Looks up a localized string similar to Abschlussemail versendet. ''' Public Shared ReadOnly Property MessageCompletionSent() As String Get @@ -299,7 +371,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt. + ''' Looks up a localized string similar to Signaturbestätigung versendet. ''' Public Shared ReadOnly Property MessageConfirmationSent() As String Get @@ -308,7 +380,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt. + ''' Looks up a localized string similar to Lesebestätigung versendet. + ''' + Public Shared ReadOnly Property MessageConfirmationSentRaC() As String + Get + Return ResourceManager.GetString("MessageConfirmationSentRaC", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Löschinformation versendet. ''' Public Shared ReadOnly Property MessageDeletionSent() As String Get @@ -317,7 +398,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt. + ''' Looks up a localized string similar to Dokumentenlink versendet. ''' Public Shared ReadOnly Property MessageInvitationSent() As String Get @@ -326,7 +407,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt. + ''' Looks up a localized string similar to Nein. ''' Public Shared ReadOnly Property No() As String Get @@ -335,7 +416,16 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt. + ''' Looks up a localized string similar to Teil-Bestätigt. + ''' + Public Shared ReadOnly Property PartlyConfirmed() As String + Get + Return ResourceManager.GetString("PartlyConfirmed", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Teil-Signiert. ''' Public Shared ReadOnly Property PartlySigned() As String Get @@ -344,7 +434,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt. + ''' Looks up a localized string similar to Qualifizierte Signatur. ''' Public Shared ReadOnly Property QualifiedSignature() As String Get @@ -353,7 +443,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt. + ''' Looks up a localized string similar to Lesebestätigung. ''' Public Shared ReadOnly Property ReadAndSign() As String Get @@ -362,7 +452,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! ähnelt. + ''' Looks up a localized string similar to Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!. ''' Public Shared ReadOnly Property ResetTOTPUser() As String Get @@ -371,7 +461,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt. + ''' Looks up a localized string similar to Gespeichert. ''' Public Shared ReadOnly Property Saved() As String Get @@ -380,7 +470,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt. + ''' Looks up a localized string similar to Gesendet. ''' Public Shared ReadOnly Property Sent() As String Get @@ -389,7 +479,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt. + ''' Looks up a localized string similar to Signatur. ''' Public Shared ReadOnly Property Signature() As String Get @@ -398,7 +488,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt. + ''' Looks up a localized string similar to Abschluss bestätigt. ''' Public Shared ReadOnly Property SignatureConfirmed() As String Get @@ -407,7 +497,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt. + ''' Looks up a localized string similar to Signiert. ''' Public Shared ReadOnly Property Signed() As String Get @@ -416,7 +506,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt. + ''' Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen.. ''' Public Shared ReadOnly Property Success_FormClose() As String Get @@ -425,7 +515,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt. + ''' Looks up a localized string similar to Unsigniert. ''' Public Shared ReadOnly Property Unsigned() As String Get @@ -434,7 +524,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt. + ''' Looks up a localized string similar to Ja. ''' Public Shared ReadOnly Property Yes() As String Get @@ -443,7 +533,7 @@ Namespace My.Resources End Property ''' - ''' Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt. + ''' Looks up a localized string similar to Ja, mit Anhang. ''' Public Shared ReadOnly Property YesWithAttachment() As String Get diff --git a/EnvelopeGenerator.CommonServices/Strings/Model.en.resx b/EnvelopeGenerator.CommonServices/Strings/Model.en.resx index eaa87da2..968bdc87 100644 --- a/EnvelopeGenerator.CommonServices/Strings/Model.en.resx +++ b/EnvelopeGenerator.CommonServices/Strings/Model.en.resx @@ -132,12 +132,27 @@ Completed + + Completely confirmed + + + Completely signed + + + Read Confirmation + + + Read and signed + Contract Created + + Document read and confirmed + Document rotation adapted @@ -147,6 +162,9 @@ Signing rejected + + Read confirmation rejected + Document signed @@ -156,6 +174,9 @@ Archived + + Completely confirmed + Completely signed @@ -165,8 +186,11 @@ Envelope Deleted + + Partially confirmed + - Partly signed + Partially signed Envelope Queued @@ -177,6 +201,9 @@ Signature certificate created + + Read Confirmation Certificate Created + Saved @@ -195,6 +222,9 @@ Confirmation Sent + + Read Confirmation Sent + Deletion Notice Sent @@ -204,6 +234,12 @@ No + + Partially confirmed + + + Partially signed + Qualified Signature @@ -223,7 +259,7 @@ Signature - Signature confirmed + Finalization confirmed Signed diff --git a/EnvelopeGenerator.CommonServices/Strings/Model.resx b/EnvelopeGenerator.CommonServices/Strings/Model.resx index 5e60d70b..55a32cb7 100644 --- a/EnvelopeGenerator.CommonServices/Strings/Model.resx +++ b/EnvelopeGenerator.CommonServices/Strings/Model.resx @@ -132,8 +132,17 @@ Abgeschlossen + + Vollständig bestätigt + - Vollständig Signiert + Vollständig signiert + + + Lesebestätigung + + + Gelesen und Bestätigt Vertrag @@ -141,6 +150,9 @@ Erstellt + + Dokument gelesen und bestätigt + Dokument Rotation geändert @@ -150,6 +162,9 @@ Unterzeichnung abgelehnt + + Lesebestätigung abgelehnt + Dokument unterzeichnet @@ -159,6 +174,9 @@ Archiviert + + Vollständig bestätigt + Vollständig signiert @@ -168,6 +186,9 @@ Umschlag Gelöscht + + Teil-Bestätigt + Teil-Signiert @@ -180,6 +201,9 @@ Signierungszertifikat erstellt + + Lesebestätigungszertifikat erstellt + Gespeichert @@ -198,6 +222,9 @@ Signaturbestätigung versendet + + Lesebestätigung versendet + Löschinformation versendet @@ -207,6 +234,9 @@ Nein + + Teil-Bestätigt + Teil-Signiert @@ -214,7 +244,7 @@ Qualifizierte Signatur - Arbeitsanweisung + Lesebestätigung Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! @@ -229,7 +259,7 @@ Signatur - Signatur bestätigt + Abschluss bestätigt Signiert diff --git a/EnvelopeGenerator.Domain/Entities/DocumentStatus.cs b/EnvelopeGenerator.Domain/Entities/DocumentStatus.cs index c61bb4cd..7a3a8fcd 100644 --- a/EnvelopeGenerator.Domain/Entities/DocumentStatus.cs +++ b/EnvelopeGenerator.Domain/Entities/DocumentStatus.cs @@ -35,6 +35,9 @@ namespace EnvelopeGenerator.Domain.Entities [Column("STATUS")] public Constants.DocumentStatus Status { get; set; } + [Column("STATUS_CHANGED_WHEN", TypeName = "datetime")] + public DateTime? StatusChangedWhen { get; set; } + [Required] [Column("ADDED_WHEN", TypeName = "datetime")] public DateTime AddedWhen { get; set; } diff --git a/EnvelopeGenerator.Domain/Entities/Envelope.cs b/EnvelopeGenerator.Domain/Entities/Envelope.cs index 21e52a9d..5ba90338 100644 --- a/EnvelopeGenerator.Domain/Entities/Envelope.cs +++ b/EnvelopeGenerator.Domain/Entities/Envelope.cs @@ -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; } diff --git a/EnvelopeGenerator.Domain/Entities/History.cs b/EnvelopeGenerator.Domain/Entities/History.cs index aae01cce..b7a6bc4c 100644 --- a/EnvelopeGenerator.Domain/Entities/History.cs +++ b/EnvelopeGenerator.Domain/Entities/History.cs @@ -35,8 +35,11 @@ namespace EnvelopeGenerator.Domain.Entities public DateTime AddedWhen { get; set; } [Column("ACTION_DATE", TypeName = "datetime")] - public DateTime? ChangedWhen { get; set; } - + public DateTime? ActionDate { get; set; } + + [NotMapped] + public DateTime? ChangedWhen { get => ActionDate; set => ActionDate = value; } + [Column("COMMENT", TypeName = "nvarchar(max)")] public string #if nullable diff --git a/EnvelopeGenerator.Domain/Interfaces/IEnvelope.cs b/EnvelopeGenerator.Domain/Interfaces/IEnvelope.cs new file mode 100644 index 00000000..f4647701 --- /dev/null +++ b/EnvelopeGenerator.Domain/Interfaces/IEnvelope.cs @@ -0,0 +1,20 @@ +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; + } + + public static bool IsReadAndSign(this IEnvelope envelope) + { + return envelope.EnvelopeTypeId != 2; + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/Resources/Model.Designer.cs b/EnvelopeGenerator.Domain/Resources/Model.Designer.cs index fb0cdf39..5708d48d 100644 --- a/EnvelopeGenerator.Domain/Resources/Model.Designer.cs +++ b/EnvelopeGenerator.Domain/Resources/Model.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace My.Resources { /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace My.Resources { } /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace My.Resources { } /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt. + /// Looks up a localized string similar to Zugriffscode korrekt eingegeben. /// public static string AccessCodeCorrect { get { @@ -70,7 +70,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt. + /// Looks up a localized string similar to Zugriffscode falsch eingegeben. /// public static string AccessCodeIncorrect { get { @@ -79,7 +79,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt. + /// Looks up a localized string similar to Zugriffscode angefordert. /// public static string AccessCodeRequested { get { @@ -88,7 +88,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt. + /// Looks up a localized string similar to Fortgeschrittene Elektronische Signatur. /// public static string AdvancedElectronicSignature { get { @@ -97,7 +97,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt. + /// Looks up a localized string similar to Abgeschlossen. /// public static string Completed { get { @@ -106,7 +106,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt. + /// Looks up a localized string similar to Vollständig bestätigt. + /// + public static string CompletelyConfirmed { + get { + return ResourceManager.GetString("CompletelyConfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vollständig signiert. /// public static string CompletelySigned { get { @@ -115,7 +124,25 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt. + /// Looks up a localized string similar to Lesebestätigung. + /// + public static string Confirmation { + get { + return ResourceManager.GetString("Confirmation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gelesen und bestätigt. + /// + public static string Confirmed { + get { + return ResourceManager.GetString("Confirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vertrag. /// public static string Contract { get { @@ -124,7 +151,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt. + /// Looks up a localized string similar to Erstellt. /// public static string Created { get { @@ -133,7 +160,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt. + /// Looks up a localized string similar to Dokument gelesen und bestätigt. + /// + public static string DocumentConfirmed { + get { + return ResourceManager.GetString("DocumentConfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dokument Rotation geändert. /// public static string DocumentMod_Rotation { get { @@ -142,7 +178,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt. + /// Looks up a localized string similar to Dokument geöffnet. /// public static string DocumentOpened { get { @@ -151,7 +187,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt. + /// Looks up a localized string similar to Unterzeichnung abgelehnt. /// public static string DocumentRejected { get { @@ -160,7 +196,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt. + /// Looks up a localized string similar to Lesebestätigung abgelehnt. + /// + public static string DocumentRejectedRaC { + get { + return ResourceManager.GetString("DocumentRejectedRaC", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dokument unterzeichnet. /// public static string DocumentSigned { get { @@ -169,7 +214,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt. + /// Looks up a localized string similar to Entwurf. /// public static string Draft { get { @@ -178,7 +223,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt. + /// Looks up a localized string similar to Archiviert. /// public static string EnvelopeArchived { get { @@ -187,7 +232,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt. + /// Looks up a localized string similar to Vollständig gelesen und bestätigt. + /// + public static string EnvelopeCompletelyConfirmed { + get { + return ResourceManager.GetString("EnvelopeCompletelyConfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vollständig signiert. /// public static string EnvelopeCompletelySigned { get { @@ -196,7 +250,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt. + /// Looks up a localized string similar to Umschlag Erstellt. /// public static string EnvelopeCreated { get { @@ -205,7 +259,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt. + /// Looks up a localized string similar to Umschlag Gelöscht. /// public static string EnvelopeDeleted { get { @@ -214,7 +268,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt. + /// Looks up a localized string similar to Teil-Bestätigt. + /// + public static string EnvelopePartlyConfirmed { + get { + return ResourceManager.GetString("EnvelopePartlyConfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Teil-Signiert. /// public static string EnvelopePartlySigned { get { @@ -223,7 +286,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt. + /// Looks up a localized string similar to Umschlag in Queue. /// public static string EnvelopeQueued { get { @@ -232,7 +295,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt. + /// Looks up a localized string similar to Umschlag abgelehnt. /// public static string EnvelopeRejected { get { @@ -241,7 +304,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt. + /// Looks up a localized string similar to Signierungszertifikat erstellt. /// public static string EnvelopeReportCreated { get { @@ -250,7 +313,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt. + /// Looks up a localized string similar to Lesebestätigungszertifikat erstellt. + /// + public static string EnvelopeReportCreatedRaC { + get { + return ResourceManager.GetString("EnvelopeReportCreatedRaC", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gespeichert. /// public static string EnvelopeSaved { get { @@ -259,7 +331,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt. + /// Looks up a localized string similar to Gesendet. /// public static string EnvelopeSent { get { @@ -268,7 +340,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt. + /// Looks up a localized string similar to Umschlag zurückgezogen. /// public static string EnvelopeWithdrawn { get { @@ -277,7 +349,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt. + /// Looks up a localized string similar to Zugriffscode versendet. /// public static string MessageAccessCodeSent { get { @@ -286,7 +358,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt. + /// Looks up a localized string similar to Abschlussemail versendet. /// public static string MessageCompletionSent { get { @@ -295,7 +367,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt. + /// Looks up a localized string similar to Abschlussbestätigung versendet. /// public static string MessageConfirmationSent { get { @@ -304,7 +376,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt. + /// Looks up a localized string similar to Löschinformation versendet. /// public static string MessageDeletionSent { get { @@ -313,7 +385,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt. + /// Looks up a localized string similar to Dokumentenlink versendet. /// public static string MessageInvitationSent { get { @@ -322,7 +394,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt. + /// Looks up a localized string similar to Nein. /// public static string No { get { @@ -331,7 +403,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt. + /// Looks up a localized string similar to Teil-Bestätigt. + /// + public static string PartlyConfirmed { + get { + return ResourceManager.GetString("PartlyConfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Teil-Signiert. /// public static string PartlySigned { get { @@ -340,7 +421,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt. + /// Looks up a localized string similar to Qualifizierte Signatur. /// public static string QualifiedSignature { get { @@ -349,7 +430,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt. + /// Looks up a localized string similar to Lesebestätigung. /// public static string ReadAndSign { get { @@ -358,7 +439,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! ähnelt. + /// Looks up a localized string similar to Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!. /// public static string ResetTOTPUser { get { @@ -367,7 +448,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt. + /// Looks up a localized string similar to Gespeichert. /// public static string Saved { get { @@ -376,7 +457,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt. + /// Looks up a localized string similar to Gesendet. /// public static string Sent { get { @@ -385,7 +466,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt. + /// Looks up a localized string similar to Signatur. /// public static string Signature { get { @@ -394,7 +475,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt. + /// Looks up a localized string similar to Abschluss bestätigt. /// public static string SignatureConfirmed { get { @@ -403,7 +484,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt. + /// Looks up a localized string similar to Signiert. /// public static string Signed { get { @@ -412,7 +493,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt. + /// Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen.. /// public static string Success_FormClose { get { @@ -421,7 +502,16 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt. + /// Looks up a localized string similar to Unbestätigt. + /// + public static string Unconfirmed { + get { + return ResourceManager.GetString("Unconfirmed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsigniert. /// public static string Unsigned { get { @@ -430,7 +520,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt. + /// Looks up a localized string similar to Ja. /// public static string Yes { get { @@ -439,7 +529,7 @@ namespace My.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt. + /// Looks up a localized string similar to Ja, mit Anhang. /// public static string YesWithAttachment { get { diff --git a/EnvelopeGenerator.Domain/Resources/Model.en.resx b/EnvelopeGenerator.Domain/Resources/Model.en.resx index d304b4b5..47255b7c 100644 --- a/EnvelopeGenerator.Domain/Resources/Model.en.resx +++ b/EnvelopeGenerator.Domain/Resources/Model.en.resx @@ -132,15 +132,27 @@ Completed + + Completely confirmed + Completely signed + + Read Confirmation + + + Read and signed + Contract Created + + Document read and signed + Document rotation adapted @@ -150,6 +162,9 @@ Signing rejected + + Read confirmation rejected + Document signed @@ -159,6 +174,9 @@ Archived + + Completely confirmed + Completely signed @@ -168,8 +186,11 @@ Envelope Deleted + + Partially confirmed + - Partly signed + Partially signed Envelope Queued @@ -180,6 +201,9 @@ Signature certificate created + + Read confirmartion certificate created + Saved @@ -196,7 +220,7 @@ Final email sent - Confirmation Sent + Finalization Confirmation Sent Deletion Notice Sent @@ -207,6 +231,12 @@ No + + Partially confirmed + + + Partially signed + Qualified Signature @@ -226,7 +256,7 @@ Signature - Signature confirmed + Finalization confirmed Signed @@ -234,6 +264,9 @@ Successful! Dialog is closed.successful! Dialog is closed. + + Unconfirmed + Unsigned diff --git a/EnvelopeGenerator.Domain/Resources/Model.resx b/EnvelopeGenerator.Domain/Resources/Model.resx index 5e60d70b..3c18817d 100644 --- a/EnvelopeGenerator.Domain/Resources/Model.resx +++ b/EnvelopeGenerator.Domain/Resources/Model.resx @@ -132,8 +132,17 @@ Abgeschlossen + + Vollständig bestätigt + - Vollständig Signiert + Vollständig signiert + + + Lesebestätigung + + + Gelesen und bestätigt Vertrag @@ -141,6 +150,9 @@ Erstellt + + Dokument gelesen und bestätigt + Dokument Rotation geändert @@ -150,6 +162,9 @@ Unterzeichnung abgelehnt + + Lesebestätigung abgelehnt + Dokument unterzeichnet @@ -159,6 +174,9 @@ Archiviert + + Vollständig gelesen und bestätigt + Vollständig signiert @@ -168,6 +186,9 @@ Umschlag Gelöscht + + Teil-Bestätigt + Teil-Signiert @@ -180,6 +201,9 @@ Signierungszertifikat erstellt + + Lesebestätigungszertifikat erstellt + Gespeichert @@ -196,7 +220,7 @@ Abschlussemail versendet - Signaturbestätigung versendet + Abschlussbestätigung versendet Löschinformation versendet @@ -207,6 +231,9 @@ Nein + + Teil-Bestätigt + Teil-Signiert @@ -214,7 +241,7 @@ Qualifizierte Signatur - Arbeitsanweisung + Lesebestätigung Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! @@ -229,7 +256,7 @@ Signatur - Signatur bestätigt + Abschluss bestätigt Signiert @@ -237,6 +264,9 @@ Erfolgreich! Dialog wird geschlossen. + + Unbestätigt + Unsigniert diff --git a/EnvelopeGenerator.Infrastructure/EGDbContext.cs b/EnvelopeGenerator.Infrastructure/EGDbContext.cs index 468aaa4c..cff50672 100644 --- a/EnvelopeGenerator.Infrastructure/EGDbContext.cs +++ b/EnvelopeGenerator.Infrastructure/EGDbContext.cs @@ -47,6 +47,8 @@ public abstract class EGDbContextBase : DbContext public DbSet DocumentReceiverElements { get; set; } + public DbSet DocumentReceiverElementAnnotations { get; set; } + public DbSet DocumentStatus { get; set; } public DbSet EmailTemplate { get; set; } diff --git a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj index f3f03d30..23ff4950 100644 --- a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj +++ b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj @@ -25,7 +25,7 @@ - + diff --git a/EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj b/EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj deleted file mode 100644 index 1779441d..00000000 --- a/EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - - - - - - diff --git a/EnvelopeGenerator.Jobs/Jobs/APIBackendJobs/APIEnvelopeJob.cs b/EnvelopeGenerator.Jobs/Jobs/APIBackendJobs/APIEnvelopeJob.cs deleted file mode 100644 index 7f8fac2b..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/APIBackendJobs/APIEnvelopeJob.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.Threading.Tasks; -using EnvelopeGenerator.Domain.Constants; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Quartz; - -namespace EnvelopeGenerator.Jobs.APIBackendJobs; - -public class APIEnvelopeJob(ILogger? logger = null) : IJob -{ - private readonly ILogger _logger = logger ?? NullLogger.Instance; - - public async Task Execute(IJobExecutionContext context) - { - var jobId = context.JobDetail.Key.ToString(); - _logger.LogDebug("API Envelopes - Starting job {JobId}", jobId); - - try - { - var connectionString = context.MergedJobDataMap.GetString(Value.DATABASE); - if (string.IsNullOrWhiteSpace(connectionString)) - { - _logger.LogWarning("API Envelopes - Connection string missing"); - return; - } - - await using var connection = new SqlConnection(connectionString); - await connection.OpenAsync(context.CancellationToken); - - await ProcessInvitationsAsync(connection, context.CancellationToken); - await ProcessWithdrawnAsync(connection, context.CancellationToken); - - _logger.LogDebug("API Envelopes - Completed job {JobId} successfully", jobId); - } - catch (System.Exception ex) - { - _logger.LogError(ex, "API Envelopes job failed"); - } - finally - { - _logger.LogDebug("API Envelopes execution for {JobId} ended", jobId); - } - } - - private async Task ProcessInvitationsAsync(SqlConnection connection, System.Threading.CancellationToken cancellationToken) - { - const string sql = "SELECT GUID FROM TBSIG_ENVELOPE WHERE SOURCE = 'API' AND STATUS = 1003 ORDER BY GUID"; - var envelopeIds = new List(); - - await using (var command = new SqlCommand(sql, connection)) - await using (var reader = await command.ExecuteReaderAsync(cancellationToken)) - { - while (await reader.ReadAsync(cancellationToken)) - { - if (reader[0] is int id) - { - envelopeIds.Add(id); - } - } - } - - if (envelopeIds.Count == 0) - { - _logger.LogDebug("SendInvMail - No envelopes found"); - return; - } - - _logger.LogInformation("SendInvMail - Found {Count} envelopes", envelopeIds.Count); - var total = envelopeIds.Count; - var current = 1; - - foreach (var id in envelopeIds) - { - _logger.LogInformation("SendInvMail - Processing Envelope {EnvelopeId} ({Current}/{Total})", id, current, total); - try - { - // Placeholder for invitation email sending logic. - _logger.LogDebug("SendInvMail - Marking envelope {EnvelopeId} as queued", id); - const string updateSql = "UPDATE TBSIG_ENVELOPE SET CURRENT_WORK_APP = @App WHERE GUID = @Id"; - await using var updateCommand = new SqlCommand(updateSql, connection); - updateCommand.Parameters.AddWithValue("@App", "signFLOW_API_EnvJob_InvMail"); - updateCommand.Parameters.AddWithValue("@Id", id); - await updateCommand.ExecuteNonQueryAsync(cancellationToken); - } - catch (System.Exception ex) - { - _logger.LogWarning(ex, "SendInvMail - Unhandled exception while working envelope {EnvelopeId}", id); - } - - current++; - _logger.LogInformation("SendInvMail - Envelope finalized"); - } - } - - private async Task ProcessWithdrawnAsync(SqlConnection connection, System.Threading.CancellationToken cancellationToken) - { - const string sql = @"SELECT ENV.GUID, REJ.COMMENT AS REJECTION_REASON FROM - (SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = 1009 AND SOURCE = 'API') ENV INNER JOIN - (SELECT MAX(GUID) GUID, ENVELOPE_ID, MAX(ADDED_WHEN) ADDED_WHEN, MAX(ACTION_DATE) ACTION_DATE, COMMENT FROM TBSIG_ENVELOPE_HISTORY WHERE STATUS = 1009 GROUP BY ENVELOPE_ID, COMMENT ) REJ ON ENV.GUID = REJ.ENVELOPE_ID LEFT JOIN - (SELECT * FROM TBSIG_ENVELOPE_HISTORY WHERE STATUS = 3004 ) M_Send ON ENV.GUID = M_Send.ENVELOPE_ID - WHERE M_Send.GUID IS NULL"; - - var withdrawn = new List<(int EnvelopeId, string Reason)>(); - await using (var command = new SqlCommand(sql, connection)) - await using (var reader = await command.ExecuteReaderAsync(cancellationToken)) - { - while (await reader.ReadAsync(cancellationToken)) - { - var id = reader.GetInt32(0); - var reason = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); - withdrawn.Add((id, reason)); - } - } - - if (withdrawn.Count == 0) - { - _logger.LogDebug("WithdrawnEnv - No envelopes found"); - return; - } - - _logger.LogInformation("WithdrawnEnv - Found {Count} envelopes", withdrawn.Count); - var total = withdrawn.Count; - var current = 1; - - foreach (var (envelopeId, reason) in withdrawn) - { - _logger.LogInformation("WithdrawnEnv - Processing Envelope {EnvelopeId} ({Current}/{Total})", envelopeId, current, total); - try - { - // Log withdrawn mail trigger placeholder - const string insertHistory = "INSERT INTO TBSIG_ENVELOPE_HISTORY (ENVELOPE_ID, STATUS, USER_REFERENCE, ADDED_WHEN, ACTION_DATE, COMMENT) VALUES (@EnvelopeId, @Status, @UserReference, GETDATE(), GETDATE(), @Comment)"; - await using var insertCommand = new SqlCommand(insertHistory, connection); - insertCommand.Parameters.AddWithValue("@EnvelopeId", envelopeId); - insertCommand.Parameters.AddWithValue("@Status", 3004); - insertCommand.Parameters.AddWithValue("@UserReference", "API"); - insertCommand.Parameters.AddWithValue("@Comment", reason ?? string.Empty); - await insertCommand.ExecuteNonQueryAsync(cancellationToken); - } - catch (System.Exception ex) - { - _logger.LogWarning(ex, "WithdrawnEnv - Unhandled exception while working envelope {EnvelopeId}", envelopeId); - } - - current++; - _logger.LogInformation("WithdrawnEnv - Envelope finalized"); - } - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/DataRowExtensions.cs b/EnvelopeGenerator.Jobs/Jobs/DataRowExtensions.cs deleted file mode 100644 index 805f6f1f..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/DataRowExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Data; - -namespace EnvelopeGenerator.Jobs; - -public static class DataRowExtensions -{ - public static T? GetValueOrDefault(this DataRow row, string columnName, T? defaultValue = default) - { - if (!row.Table.Columns.Contains(columnName)) - { - return defaultValue; - } - - var value = row[columnName]; - if (value == DBNull.Value) - { - return defaultValue; - } - - try - { - return (T)Convert.ChangeType(value, typeof(T)); - } - catch - { - return defaultValue; - } - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentExceptions.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentExceptions.cs deleted file mode 100644 index 847ab06b..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentExceptions.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public static class FinalizeDocumentExceptions -{ - public class MergeDocumentException : ApplicationException - { - public MergeDocumentException(string message) : base(message) { } - public MergeDocumentException(string message, Exception innerException) : base(message, innerException) { } - } - - public class BurnAnnotationException : ApplicationException - { - public BurnAnnotationException(string message) : base(message) { } - public BurnAnnotationException(string message, Exception innerException) : base(message, innerException) { } - } - - public class CreateReportException : ApplicationException - { - public CreateReportException(string message) : base(message) { } - public CreateReportException(string message, Exception innerException) : base(message, innerException) { } - } - - public class ExportDocumentException : ApplicationException - { - public ExportDocumentException(string message) : base(message) { } - public ExportDocumentException(string message, Exception innerException) : base(message, innerException) { } - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentJob.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentJob.cs deleted file mode 100644 index 2ddb4e1d..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentJob.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EnvelopeGenerator.Domain.Constants; -using EnvelopeGenerator.Domain.Entities; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Logging; -using Quartz; -using static EnvelopeGenerator.Jobs.FinalizeDocument.FinalizeDocumentExceptions; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class FinalizeDocumentJob : IJob -{ - private readonly ILogger _logger; - private readonly PDFBurner _pdfBurner; - private readonly PDFMerger _pdfMerger; - private readonly ReportCreator _reportCreator; - - private record ConfigSettings(string DocumentPath, string DocumentPathOrigin, string ExportPath); - - public FinalizeDocumentJob( - ILogger logger, - PDFBurner pdfBurner, - PDFMerger pdfMerger, - ReportCreator reportCreator) - { - _logger = logger; - _pdfBurner = pdfBurner; - _pdfMerger = pdfMerger; - _reportCreator = reportCreator; - } - - public async Task Execute(IJobExecutionContext context) - { - var jobId = context.JobDetail.Key.ToString(); - _logger.LogDebug("Starting job {JobId}", jobId); - - try - { - var connectionString = context.MergedJobDataMap.GetString(Value.DATABASE); - if (string.IsNullOrWhiteSpace(connectionString)) - { - _logger.LogWarning("FinalizeDocument - Connection string missing"); - return; - } - - await using var connection = new SqlConnection(connectionString); - await connection.OpenAsync(context.CancellationToken); - - var config = await LoadConfigurationAsync(connection, context.CancellationToken); - var envelopes = await LoadCompletedEnvelopesAsync(connection, context.CancellationToken); - - if (envelopes.Count == 0) - { - _logger.LogInformation("No completed envelopes found"); - return; - } - - var total = envelopes.Count; - var current = 1; - - foreach (var envelopeId in envelopes) - { - _logger.LogInformation("Finalizing Envelope {EnvelopeId} ({Current}/{Total})", envelopeId, current, total); - try - { - var envelopeData = await GetEnvelopeDataAsync(connection, envelopeId, context.CancellationToken); - if (envelopeData is null) - { - _logger.LogWarning("Envelope data not found for {EnvelopeId}", envelopeId); - continue; - } - - var data = envelopeData.Value; - - var envelope = new Envelope - { - Id = envelopeId, - Uuid = data.EnvelopeUuid ?? string.Empty, - Title = data.Title ?? string.Empty, - FinalEmailToCreator = (int)FinalEmailType.No, - FinalEmailToReceivers = (int)FinalEmailType.No - }; - - var burned = _pdfBurner.BurnAnnotsToPDF(data.DocumentBytes, data.AnnotationData, envelopeId); - var report = _reportCreator.CreateReport(connection, envelope); - var merged = _pdfMerger.MergeDocuments(burned, report); - - var outputDirectory = Path.Combine(config.ExportPath, data.ParentFolderUid); - Directory.CreateDirectory(outputDirectory); - var outputPath = Path.Combine(outputDirectory, $"{envelope.Uuid}.pdf"); - await File.WriteAllBytesAsync(outputPath, merged, context.CancellationToken); - - await UpdateDocumentResultAsync(connection, envelopeId, merged, context.CancellationToken); - await ArchiveEnvelopeAsync(connection, envelopeId, context.CancellationToken); - } - catch (MergeDocumentException ex) - { - _logger.LogWarning(ex, "Certificate Document job failed at merging documents"); - } - catch (ExportDocumentException ex) - { - _logger.LogWarning(ex, "Certificate Document job failed at exporting document"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Unhandled exception while working envelope {EnvelopeId}", envelopeId); - } - - current++; - _logger.LogInformation("Envelope {EnvelopeId} finalized", envelopeId); - } - - _logger.LogDebug("Completed job {JobId} successfully", jobId); - } - catch (Exception ex) - { - _logger.LogError(ex, "Certificate Document job failed"); - } - finally - { - _logger.LogDebug("Job execution for {JobId} ended", jobId); - } - } - - private async Task LoadConfigurationAsync(SqlConnection connection, CancellationToken cancellationToken) - { - const string sql = "SELECT TOP 1 DOCUMENT_PATH, EXPORT_PATH FROM TBSIG_CONFIG"; - await using var command = new SqlCommand(sql, connection); - await using var reader = await command.ExecuteReaderAsync(cancellationToken); - if (await reader.ReadAsync(cancellationToken)) - { - var documentPath = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); - var exportPath = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); - return new ConfigSettings(documentPath, documentPath, exportPath); - } - - return new ConfigSettings(string.Empty, string.Empty, Path.GetTempPath()); - } - - private async Task> LoadCompletedEnvelopesAsync(SqlConnection connection, CancellationToken cancellationToken) - { - const string sql = "SELECT GUID FROM TBSIG_ENVELOPE WHERE STATUS = @Status AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= 1 ORDER BY GUID"; - var ids = new List(); - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@Status", (int)EnvelopeStatus.EnvelopeCompletelySigned); - await using var reader = await command.ExecuteReaderAsync(cancellationToken); - while (await reader.ReadAsync(cancellationToken)) - { - ids.Add(reader.GetInt32(0)); - } - - return ids; - } - - private async Task<(int EnvelopeId, string? EnvelopeUuid, string? Title, byte[] DocumentBytes, List AnnotationData, string ParentFolderUid)?> GetEnvelopeDataAsync(SqlConnection connection, int envelopeId, CancellationToken cancellationToken) - { - const string sql = @"SELECT T.GUID, T.ENVELOPE_UUID, T.TITLE, T2.FILEPATH, T2.BYTE_DATA FROM [dbo].[TBSIG_ENVELOPE] T - JOIN TBSIG_ENVELOPE_DOCUMENT T2 ON T.GUID = T2.ENVELOPE_ID - WHERE T.GUID = @EnvelopeId"; - - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@EnvelopeId", envelopeId); - await using var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken); - if (!await reader.ReadAsync(cancellationToken)) - { - return null; - } - - var envelopeUuid = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); - var title = reader.IsDBNull(2) ? string.Empty : reader.GetString(2); - var filePath = reader.IsDBNull(3) ? string.Empty : reader.GetString(3); - var bytes = reader.IsDBNull(4) ? Array.Empty() : (byte[])reader[4]; - await reader.CloseAsync(); - - if (bytes.Length == 0 && !string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath)) - { - bytes = await File.ReadAllBytesAsync(filePath, cancellationToken); - } - - var annotations = await GetAnnotationDataAsync(connection, envelopeId, cancellationToken); - - var parentFolderUid = !string.IsNullOrWhiteSpace(filePath) - ? Path.GetFileName(Path.GetDirectoryName(filePath) ?? string.Empty) - : envelopeUuid; - - return (envelopeId, envelopeUuid, title, bytes, annotations, parentFolderUid ?? envelopeUuid ?? envelopeId.ToString()); - } - - private async Task> GetAnnotationDataAsync(SqlConnection connection, int envelopeId, CancellationToken cancellationToken) - { - const string sql = "SELECT VALUE FROM TBSIG_DOCUMENT_STATUS WHERE ENVELOPE_ID = @EnvelopeId"; - var result = new List(); - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@EnvelopeId", envelopeId); - await using var reader = await command.ExecuteReaderAsync(cancellationToken); - while (await reader.ReadAsync(cancellationToken)) - { - if (!reader.IsDBNull(0)) - { - result.Add(reader.GetString(0)); - } - } - - return result; - } - - private static async Task UpdateDocumentResultAsync(SqlConnection connection, int envelopeId, byte[] bytes, CancellationToken cancellationToken) - { - const string sql = "UPDATE TBSIG_ENVELOPE SET DOC_RESULT = @ImageData WHERE GUID = @EnvelopeId"; - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@ImageData", bytes); - command.Parameters.AddWithValue("@EnvelopeId", envelopeId); - await command.ExecuteNonQueryAsync(cancellationToken); - } - - private static async Task ArchiveEnvelopeAsync(SqlConnection connection, int envelopeId, CancellationToken cancellationToken) - { - const string sql = "UPDATE TBSIG_ENVELOPE SET STATUS = @Status, CHANGED_WHEN = GETDATE() WHERE GUID = @EnvelopeId"; - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@Status", (int)EnvelopeStatus.EnvelopeArchived); - command.Parameters.AddWithValue("@EnvelopeId", envelopeId); - await command.ExecuteNonQueryAsync(cancellationToken); - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurner.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurner.cs deleted file mode 100644 index 0d04846c..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurner.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using iText.IO.Image; -using iText.Kernel.Colors; -using iText.Kernel.Pdf; -using iText.Kernel.Pdf.Canvas; -using iText.Layout; -using iText.Layout.Element; -using iText.Layout.Font; -using iText.Layout.Properties; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Newtonsoft.Json; -using static EnvelopeGenerator.Jobs.FinalizeDocument.FinalizeDocumentExceptions; -using LayoutImage = iText.Layout.Element.Image; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class PDFBurner -{ - private static readonly FontProvider FontProvider = CreateFontProvider(); - private readonly ILogger _logger; - private readonly PDFBurnerParams _pdfBurnerParams; - - public PDFBurner() : this(NullLogger.Instance, new PDFBurnerParams()) - { - } - - public PDFBurner(ILogger logger, PDFBurnerParams? pdfBurnerParams = null) - { - _logger = logger; - _pdfBurnerParams = pdfBurnerParams ?? new PDFBurnerParams(); - } - - public byte[] BurnAnnotsToPDF(byte[] sourceBuffer, IList instantJsonList, int envelopeId) - { - if (sourceBuffer is null || sourceBuffer.Length == 0) - { - throw new BurnAnnotationException("Source document is empty"); - } - - try - { - using var inputStream = new MemoryStream(sourceBuffer); - using var outputStream = new MemoryStream(); - using var reader = new PdfReader(inputStream); - using var writer = new PdfWriter(outputStream); - using var pdf = new PdfDocument(reader, writer); - - foreach (var json in instantJsonList ?? Enumerable.Empty()) - { - if (string.IsNullOrWhiteSpace(json)) - { - continue; - } - - var annotationData = JsonConvert.DeserializeObject(json); - if (annotationData?.annotations is null) - { - continue; - } - - annotationData.annotations.Reverse(); - - foreach (var annotation in annotationData.annotations) - { - try - { - switch (annotation.type) - { - case AnnotationType.Image: - AddImageAnnotation(pdf, annotation, annotationData.attachments); - break; - case AnnotationType.Ink: - AddInkAnnotation(pdf, annotation); - break; - case AnnotationType.Widget: - var formFieldValue = annotationData.formFieldValues?.FirstOrDefault(fv => fv.name == annotation.id); - if (formFieldValue is not null && !_pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value)) - { - AddFormFieldValue(pdf, annotation, formFieldValue.value); - } - break; - } - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Error applying annotation {AnnotationId} on envelope {EnvelopeId}", annotation.id, envelopeId); - throw new BurnAnnotationException("Adding annotation failed", ex); - } - } - } - - pdf.Close(); - return outputStream.ToArray(); - } - catch (BurnAnnotationException) - { - throw; - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to burn annotations for envelope {EnvelopeId}", envelopeId); - throw new BurnAnnotationException("Annotations could not be burned", ex); - } - } - - private void AddImageAnnotation(PdfDocument pdf, Annotation annotation, Dictionary? attachments) - { - if (attachments is null || string.IsNullOrWhiteSpace(annotation.imageAttachmentId) || !attachments.TryGetValue(annotation.imageAttachmentId, out var attachment)) - { - return; - } - - var page = pdf.GetPage(annotation.pageIndex + 1); - var bounds = annotation.bbox.Select(ToInches).ToList(); - var x = (float)bounds[0]; - var y = (float)bounds[1]; - var width = (float)bounds[2]; - var height = (float)bounds[3]; - - var imageBytes = Convert.FromBase64String(attachment.binary); - var imageData = ImageDataFactory.Create(imageBytes); - var image = new LayoutImage(imageData) - .ScaleAbsolute(width, height) - .SetFixedPosition(annotation.pageIndex + 1, x, y); - - using var canvas = new Canvas(new PdfCanvas(page), page.GetPageSize()); - canvas.Add(image); - } - - private void AddInkAnnotation(PdfDocument pdf, Annotation annotation) - { - if (annotation.lines?.points is null) - { - return; - } - - var page = pdf.GetPage(annotation.pageIndex + 1); - var canvas = new PdfCanvas(page); - var color = ParseColor(annotation.strokeColor); - canvas.SetStrokeColor(color); - canvas.SetLineWidth(1); - - foreach (var segment in annotation.lines.points) - { - var first = true; - foreach (var point in segment) - { - var (px, py) = (ToInches(point[0]), ToInches(point[1])); - if (first) - { - canvas.MoveTo(px, py); - first = false; - } - else - { - canvas.LineTo(px, py); - } - } - canvas.Stroke(); - } - } - - private static FontProvider CreateFontProvider() - { - var provider = new FontProvider(); - provider.AddStandardPdfFonts(); - provider.AddSystemFonts(); - return provider; - } - - private void AddFormFieldValue(PdfDocument pdf, Annotation annotation, string value) - { - var bounds = annotation.bbox.Select(ToInches).ToList(); - var x = (float)bounds[0]; - var y = (float)bounds[1]; - var width = (float)bounds[2]; - var height = (float)bounds[3]; - - var page = pdf.GetPage(annotation.pageIndex + 1); - var canvas = new Canvas(new PdfCanvas(page), page.GetPageSize()); - canvas.SetProperty(Property.FONT_PROVIDER, FontProvider); - canvas.SetProperty(Property.FONT, FontProvider.GetFontSet()); - - var paragraph = new Paragraph(value) - .SetFontSize(_pdfBurnerParams.FontSize) - .SetFontColor(ColorConstants.BLACK) - .SetFontFamily(_pdfBurnerParams.FontName); - - if (_pdfBurnerParams.FontStyle.HasFlag(FontStyle.Italic)) - { - paragraph.SetItalic(); - } - - if (_pdfBurnerParams.FontStyle.HasFlag(FontStyle.Bold)) - { - paragraph.SetBold(); - } - - canvas.ShowTextAligned( - paragraph, - x + (float)_pdfBurnerParams.TopMargin, - y + (float)_pdfBurnerParams.YOffset, - annotation.pageIndex + 1, - iText.Layout.Properties.TextAlignment.LEFT, - iText.Layout.Properties.VerticalAlignment.TOP, - 0); - } - - private static DeviceRgb ParseColor(string? color) - { - if (string.IsNullOrWhiteSpace(color)) - { - return new DeviceRgb(0, 0, 0); - } - - try - { - var drawingColor = ColorTranslator.FromHtml(color); - return new DeviceRgb(drawingColor.R, drawingColor.G, drawingColor.B); - } - catch - { - return new DeviceRgb(0, 0, 0); - } - } - - private static double ToInches(double value) => value / 72d; - private static double ToInches(float value) => value / 72d; - - #region Model - private static class AnnotationType - { - public const string Image = "pspdfkit/image"; - public const string Ink = "pspdfkit/ink"; - public const string Widget = "pspdfkit/widget"; - } - - private sealed class AnnotationData - { - public List? annotations { get; set; } - public Dictionary? attachments { get; set; } - public List? formFieldValues { get; set; } - } - - private sealed class Annotation - { - public string id { get; set; } = string.Empty; - public List bbox { get; set; } = new(); - public string type { get; set; } = string.Empty; - public string imageAttachmentId { get; set; } = string.Empty; - public Lines? lines { get; set; } - public int pageIndex { get; set; } - public string strokeColor { get; set; } = string.Empty; - public string egName { get; set; } = string.Empty; - } - - private sealed class Lines - { - public List>> points { get; set; } = new(); - } - - private sealed class Attachment - { - public string binary { get; set; } = string.Empty; - public string contentType { get; set; } = string.Empty; - } - - private sealed class FormFieldValue - { - public string name { get; set; } = string.Empty; - public string value { get; set; } = string.Empty; - } - #endregion -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurnerParams.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurnerParams.cs deleted file mode 100644 index 0b9536fa..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurnerParams.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class PDFBurnerParams -{ - public List IgnoredLabels { get; } = new() { "Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung" }; - - public double TopMargin { get; set; } = 0.1; - - public double YOffset { get; set; } = -0.3; - - public string FontName { get; set; } = "Arial"; - - public int FontSize { get; set; } = 8; - - public FontStyle FontStyle { get; set; } = FontStyle.Italic; -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFMerger.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFMerger.cs deleted file mode 100644 index 2306fac7..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFMerger.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.IO; -using iText.Kernel.Pdf; -using iText.Kernel.Utils; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using static EnvelopeGenerator.Jobs.FinalizeDocument.FinalizeDocumentExceptions; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class PDFMerger -{ - private readonly ILogger _logger; - - public PDFMerger() : this(NullLogger.Instance) - { - } - - public PDFMerger(ILogger logger) - { - _logger = logger; - } - - public byte[] MergeDocuments(byte[] document, byte[] report) - { - try - { - using var finalStream = new MemoryStream(); - using var documentReader = new PdfReader(new MemoryStream(document)); - using var reportReader = new PdfReader(new MemoryStream(report)); - using var writer = new PdfWriter(finalStream); - using var targetDoc = new PdfDocument(documentReader, writer); - using var reportDoc = new PdfDocument(reportReader); - - var merger = new PdfMerger(targetDoc); - merger.Merge(reportDoc, 1, reportDoc.GetNumberOfPages()); - - targetDoc.Close(); - return finalStream.ToArray(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to merge PDF documents"); - throw new MergeDocumentException("Documents could not be merged", ex); - } - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportCreator.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportCreator.cs deleted file mode 100644 index b0e4aa08..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportCreator.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Data; -using System.IO; -using EnvelopeGenerator.Domain.Entities; -using iText.Kernel.Pdf; -using iText.Layout.Element; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using static EnvelopeGenerator.Jobs.FinalizeDocument.FinalizeDocumentExceptions; -using LayoutDocument = iText.Layout.Document; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class ReportCreator -{ - private readonly ILogger _logger; - - public ReportCreator() : this(NullLogger.Instance) - { - } - - public ReportCreator(ILogger logger) - { - _logger = logger; - } - - public byte[] CreateReport(SqlConnection connection, Envelope envelope) - { - try - { - var reportItems = LoadReportItems(connection, envelope.Id); - using var stream = new MemoryStream(); - using var writer = new PdfWriter(stream); - using var pdf = new PdfDocument(writer); - using var document = new LayoutDocument(pdf); - - document.Add(new Paragraph("Envelope Finalization Report").SetFontSize(16)); - document.Add(new Paragraph($"Envelope Id: {envelope.Id}")); - document.Add(new Paragraph($"UUID: {envelope.Uuid}")); - document.Add(new Paragraph($"Title: {envelope.Title}")); - document.Add(new Paragraph($"Subject: {envelope.Comment}")); - document.Add(new Paragraph($"Generated: {DateTime.UtcNow:O}")); - document.Add(new Paragraph(" ")); - - var table = new Table(4).UseAllAvailableWidth(); - table.AddHeaderCell("Date"); - table.AddHeaderCell("Status"); - table.AddHeaderCell("User"); - table.AddHeaderCell("EnvelopeId"); - - foreach (var item in reportItems.OrderByDescending(r => r.ItemDate)) - { - table.AddCell(item.ItemDate.ToString("u")); - table.AddCell(item.ItemStatus.ToString()); - table.AddCell(item.ItemUserReference); - table.AddCell(item.EnvelopeId.ToString()); - } - - document.Add(table); - document.Close(); - return stream.ToArray(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Could not create report for envelope {EnvelopeId}", envelope.Id); - throw new CreateReportException("Could not prepare report data", ex); - } - } - - private List LoadReportItems(SqlConnection connection, int envelopeId) - { - const string sql = "SELECT ENVELOPE_ID, POS_WHEN, POS_STATUS, POS_WHO FROM VWSIG_ENVELOPE_REPORT WHERE ENVELOPE_ID = @EnvelopeId"; - var result = new List(); - - using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@EnvelopeId", envelopeId); - using var reader = command.ExecuteReader(); - while (reader.Read()) - { - result.Add(new ReportItem - { - EnvelopeId = reader.GetInt32(0), - ItemDate = reader.IsDBNull(1) ? DateTime.MinValue : reader.GetDateTime(1), - ItemStatus = reader.IsDBNull(2) ? default : (EnvelopeGenerator.Domain.Constants.EnvelopeStatus)reader.GetInt32(2), - ItemUserReference = reader.IsDBNull(3) ? string.Empty : reader.GetString(3) - }); - } - - return result; - } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportItem.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportItem.cs deleted file mode 100644 index c8e0d52e..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportItem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EnvelopeGenerator.Domain.Constants; -using EnvelopeGenerator.Domain.Entities; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class ReportItem -{ - public Envelope? Envelope { get; set; } - public int EnvelopeId { get; set; } - public string EnvelopeTitle { get; set; } = string.Empty; - public string EnvelopeSubject { get; set; } = string.Empty; - - public EnvelopeStatus ItemStatus { get; set; } - - public string ItemStatusTranslated => ItemStatus.ToString(); - - public string ItemUserReference { get; set; } = string.Empty; - public DateTime ItemDate { get; set; } -} diff --git a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportSource.cs b/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportSource.cs deleted file mode 100644 index 47afd920..00000000 --- a/EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportSource.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace EnvelopeGenerator.Jobs.FinalizeDocument; - -public class ReportSource -{ - public List Items { get; set; } = new(); -} diff --git a/EnvelopeGenerator.PdfEditor/Pdf.cs b/EnvelopeGenerator.PdfEditor/Pdf.cs index 03b629f0..22011f72 100644 --- a/EnvelopeGenerator.PdfEditor/Pdf.cs +++ b/EnvelopeGenerator.PdfEditor/Pdf.cs @@ -105,17 +105,13 @@ namespace EnvelopeGenerator.PdfEditor public Pdf Background(IEnumerable signatures, double widthPx = 1.9500000000000002, double heightPx = 2.52) where TSignature : ISignature { - // once per page - Page(page => - { - var canvas = new PdfCanvas(page); - canvas.ConcatMatrix(1, 0, 0, -1, 0, page.GetPageSize().GetHeight()); - }); - foreach (var signature in signatures) Page(signature.Page, page => { var canvas = new PdfCanvas(page); + canvas.SaveState(); + canvas.ConcatMatrix(1, 0, 0, -1, 0, page.GetPageSize().GetHeight()); + double inchFactor = 72; double magin = .2; double x = (signature.X - .7 - magin) * inchFactor; @@ -134,6 +130,8 @@ namespace EnvelopeGenerator.PdfEditor canvas.SetFillColor(new DeviceRgb(204, 202, 198)) .Rectangle(x, y + height - bottomLineLength, width, bottomLineLength) .Fill(); + + canvas.RestoreState(); }); return this; diff --git a/EnvelopeGenerator.Service/App.config b/EnvelopeGenerator.Service/App.config index 2d238195..9349ae8f 100644 --- a/EnvelopeGenerator.Service/App.config +++ b/EnvelopeGenerator.Service/App.config @@ -15,7 +15,7 @@ - + @@ -23,15 +23,15 @@ - + - + - + @@ -39,7 +39,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -67,7 +67,7 @@ - + @@ -135,7 +135,7 @@ - + diff --git a/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj b/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj index 061d21a6..17e5ead9 100644 --- a/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj +++ b/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj @@ -1,4 +1,4 @@ - + @@ -17,7 +17,8 @@ - publish\ + false + P:\Install .Net\0 DD - Smart UP\signFLOW\Finalization\%24%28Version%29\ true Disk false @@ -27,10 +28,11 @@ false false true + true 0 1.0.0.%2a - false false + true true @@ -65,6 +67,18 @@ On + + 905CDA07C40CC715F90A2F2A0C5C0E5BAACE138B + + + EnvelopeGenerator.Service_TemporaryKey.pfx + + + true + + + true + ..\packages\BouncyCastle.Cryptography.2.6.2\lib\net461\BouncyCastle.Cryptography.dll @@ -181,11 +195,11 @@ ..\packages\Microsoft.Extensions.DependencyInjection.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.8.0.3\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll @@ -212,8 +226,8 @@ ..\packages\RtfPipe.2.0.7677.4303\lib\net45\RtfPipe.dll - - ..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll ..\packages\System.CodeDom.8.0.0\lib\net462\System.CodeDom.dll @@ -221,6 +235,9 @@ ..\packages\System.Collections.Immutable.8.0.0\lib\net462\System.Collections.Immutable.dll + + ..\packages\System.ComponentModel.Annotations.4.7.0\lib\net461\System.ComponentModel.Annotations.dll + @@ -234,18 +251,18 @@ ..\packages\System.IO.Packaging.8.0.1\lib\net462\System.IO.Packaging.dll - - ..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll ..\packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll - - ..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll @@ -258,15 +275,15 @@ ..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll - - ..\packages\System.Text.Json.8.0.5\lib\net462\System.Text.Json.dll + + ..\packages\System.Text.Json.8.0.6\lib\net462\System.Text.Json.dll - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + ..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll - - ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + ..\packages\System.ValueTuple.4.6.1\lib\net462\System.ValueTuple.dll @@ -274,6 +291,63 @@ + + ..\packages\Microsoft.Data.SqlClient.6.1.4\lib\net462\Microsoft.Data.SqlClient.dll + + + ..\packages\AutoMapper.10.1.1\lib\net461\AutoMapper.dll + + + ..\packages\DigitalData.Core.Abstraction.Application.1.6.0\lib\net462\DigitalData.Core.Abstraction.Application.dll + + + ..\packages\Microsoft.Bcl.HashCode.1.1.1\lib\net461\Microsoft.Bcl.HashCode.dll + + + ..\packages\Microsoft.EntityFrameworkCore.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + + + ..\packages\Microsoft.EntityFrameworkCore.Abstractions.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Abstractions.dll + + + ..\packages\Microsoft.EntityFrameworkCore.Relational.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll + + + ..\packages\Microsoft.EntityFrameworkCore.SqlServer.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.SqlServer.dll + + + ..\packages\Microsoft.Extensions.Caching.Abstractions.8.0.0\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Caching.Memory.8.0.1\lib\net462\Microsoft.Extensions.Caching.Memory.dll + + + ..\packages\Microsoft.Extensions.Configuration.7.0.0\lib\net462\Microsoft.Extensions.Configuration.dll + + + ..\packages\Microsoft.Extensions.Configuration.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Configuration.Binder.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Binder.dll + + + ..\packages\Microsoft.Extensions.Logging.7.0.0\lib\net462\Microsoft.Extensions.Logging.dll + + + ..\packages\Microsoft.Extensions.Options.8.0.2\lib\net462\Microsoft.Extensions.Options.dll + + + ..\packages\Microsoft.Extensions.Options.ConfigurationExtensions.7.0.0\lib\net462\Microsoft.Extensions.Options.ConfigurationExtensions.dll + + + ..\packages\Microsoft.Extensions.Primitives.8.0.0\lib\net462\Microsoft.Extensions.Primitives.dll + + + ..\packages\System.Data.Common.4.3.0\lib\net451\System.Data.Common.dll + + + ..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll + @@ -337,6 +411,7 @@ + MyApplicationCodeGenerator Application.Designer.vb @@ -380,6 +455,8 @@ + + \ No newline at end of file diff --git a/EnvelopeGenerator.Service/packages.config b/EnvelopeGenerator.Service/packages.config index 571ea90c..86c8073d 100644 --- a/EnvelopeGenerator.Service/packages.config +++ b/EnvelopeGenerator.Service/packages.config @@ -1,6 +1,8 @@  + + @@ -11,11 +13,26 @@ + + + + + + + + + + + + - - - + + + + + + @@ -24,18 +41,20 @@ - + + + - - - + + + - - - + + + \ No newline at end of file diff --git a/EnvelopeGenerator.Tests/Application/Fake.cs b/EnvelopeGenerator.Tests/Application/Fake.cs index 1281e7bb..66b3e168 100644 --- a/EnvelopeGenerator.Tests/Application/Fake.cs +++ b/EnvelopeGenerator.Tests/Application/Fake.cs @@ -215,7 +215,7 @@ public static class Extensions Title = faker.Lorem.Paragraph(faker.Random.Number(1, 2)), Message = faker.Lorem.Paragraph(faker.Random.Number(2, 5)), TfaEnabled = tfaEnabled, - AddedWhen = DateTime.UtcNow, + AddedWhen = DateTime.Now, CertificationType = (int)CertificationType.AdvancedElectronicSignature, UseAccessCode = false, ContractType = (int)ContractType.Contract, @@ -273,9 +273,9 @@ public static class Extensions EnvelopeId = envelopeId, ReceiverId = receiverId, Status = ReceiverStatus.Unsigned, - AddedWhen = DateTime.UtcNow, + AddedWhen = DateTime.Now, AccessCode = faker.Random.Number(1000, 9999).ToString(), - ChangedWhen = DateTime.UtcNow, + ChangedWhen = DateTime.Now, CompanyName = faker.Company.CompanyName(), JobTitle = faker.Name.JobTitle(), Name = faker.Name.FullName(), diff --git a/EnvelopeGenerator.Web/Controllers/AnnotationController.cs b/EnvelopeGenerator.Web/Controllers/AnnotationController.cs index e2d64966..0cecccf0 100644 --- a/EnvelopeGenerator.Web/Controllers/AnnotationController.cs +++ b/EnvelopeGenerator.Web/Controllers/AnnotationController.cs @@ -6,6 +6,7 @@ using EnvelopeGenerator.Application.Common.Notifications.DocSigned; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries; using EnvelopeGenerator.Application.Histories.Queries; using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Interfaces; using EnvelopeGenerator.Web.Extensions; using MediatR; using Microsoft.AspNetCore.Authentication; @@ -65,7 +66,7 @@ public class AnnotationController : ControllerBase // Again check if receiver has already signed if (await _mediator.IsSignedAsync(uuid, signature, cancel)) return Problem(statusCode: 409); - else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel)) + else if (er.Envelope.IsReadAndSign() && await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel)) return Problem(statusCode: 423); var docSignedNotification = await _mediator @@ -80,6 +81,7 @@ public class AnnotationController : ControllerBase return Ok(); } + //TODO: add logic to check if it is already rejected or signed [Authorize(Roles = Role.ReceiverFull)] [HttpPost("reject")] [Obsolete("Use DigitalData.Core.Exceptions and .Middleware")] @@ -100,7 +102,7 @@ public class AnnotationController : ControllerBase if (envRcvRes.IsFailed) { _logger.LogNotice(envRcvRes.Notices); - return Unauthorized("you are not authirized"); + return Unauthorized(); } var histRes = await _histService.RecordAsync(envRcvRes.Data.EnvelopeId, userReference: mail, EnvelopeStatus.DocumentRejected, comment: reason); diff --git a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs index fed685c4..e8068bd2 100644 --- a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs @@ -7,6 +7,7 @@ using EnvelopeGenerator.Application.Common.Interfaces.Services; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries; using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Interfaces; using EnvelopeGenerator.PdfEditor; using EnvelopeGenerator.Web.Extensions; using EnvelopeGenerator.Web.Models; @@ -78,20 +79,25 @@ public class EnvelopeController : ViewControllerBase return this.ViewEnvelopeNotFound(); #region Rejected or Signed - //check rejection var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id); if (rejRcvrs.Any()) { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - ViewBag.IsExt = !rejRcvrs.Contains(er.Receiver); //external if the current user is not rejected - return View("EnvelopeRejected", er); + var isExt = !rejRcvrs.Where(rcv => rcv.Id == er.Receiver!.Id).Any(); //external if the current user is not rejected + + if (er.Envelope.IsReadAndSign() || !isExt) + { + //TODO: Normally assigned to the isExt variable. However, since the relevant keys are not defined in the resx files, it was assigned false. Fix this. + ViewBag.IsExt = true; + return View("EnvelopeRejected", er); + } } //check if it has already signed if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress)) { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - return View("EnvelopeSigned"); + return View("EnvelopeSigned", er); } #endregion @@ -162,13 +168,19 @@ public class EnvelopeController : ViewControllerBase } var er_secret = er_secret_res.Data; - //check rejection var rejRcvrs = await _historyService.ReadRejectingReceivers(er_secret.Envelope!.Id); if (rejRcvrs.Any()) { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - ViewBag.IsExt = !rejRcvrs.Contains(er_secret.Receiver); //external if the current user is not rejected - return View("EnvelopeRejected", er_secret); + var isExt = !rejRcvrs.Where(rcv => rcv.Id == er_secret.Receiver!.Id).Any(); //external if the current user is not rejected + + //check rejection if the envelope is read-and-sign or non-external (internal) + if (er_secret.Envelope.IsReadAndSign() || !isExt) + { + //TODO: Normally assigned to the isExt variable. However, since the relevant keys are not defined in the resx files, it was assigned false. Fix this. + ViewBag.IsExt = true; + return View("EnvelopeRejected", er_secret); + } } // show envelope if already logged in diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs index a4a5058b..011af296 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs @@ -1,56 +1,32 @@ -using EnvelopeGenerator.Domain.Entities; -using Microsoft.AspNetCore.Mvc; -using EnvelopeGenerator.Application.Common.Dto; +using Microsoft.AspNetCore.Mvc; using EnvelopeGenerator.Application.Common.Extensions; -using EnvelopeGenerator.Application.Common.Interfaces.Services; +using MediatR; +using EnvelopeGenerator.Application.Envelopes.Queries; namespace EnvelopeGenerator.Web.Controllers.Test; [Obsolete("Use MediatR")] -public class TestEnvelopeController : TestControllerBase +public class TestEnvelopeController : ControllerBase { - public TestEnvelopeController(ILogger logger, IEnvelopeService service) : base(logger, service) - { - } + private readonly IMediator _mediator; - [NonAction] - public override Task GetAll() => base.GetAll(); + /// + /// + /// + /// + public TestEnvelopeController(IMediator mediator) + { + _mediator = mediator; + } [HttpGet] - public async Task GetAll([FromQuery] string? envelopeKey = default, [FromQuery] bool withDocuments = false, [FromQuery] bool withHistory = false, [FromQuery] bool withDocumentReceiverElement = false, [FromQuery] bool withUser = false, [FromQuery] bool withAll = true) - { - if (envelopeKey is not null) - { - (var uuid, var signature) = envelopeKey.DecodeEnvelopeReceiverId(); - if (uuid is null) - return BadRequest("UUID is null"); - var envlopeServiceResult = await _service.ReadByUuidAsync( - uuid: uuid, - withDocuments: withDocuments, withHistory: withHistory, withDocumentReceiverElement: withDocumentReceiverElement, withUser: withUser, withAll: withAll); - - if (envlopeServiceResult.IsSuccess) - { - return Ok(envlopeServiceResult.Data); - } - return NotFound(); - } - - var result = await _service.ReadAllWithAsync(documents: withDocuments, history: withHistory); - if (result.IsSuccess) - { - return Ok(result); - } - return NotFound(result); - } + public async Task GetAll([FromQuery] ReadEnvelopeQuery query, CancellationToken cancel) => Ok(await _mediator.Send(query, cancel)); [HttpGet("decode")] - public IActionResult DecodeEnvelopeReceiverId(string envelopeReceiverId, int type = 0) + public IActionResult DecodeEnvelopeReceiverId(string envelopeReceiverId, int type = 0) => type switch { - return type switch - { - 1 => Ok(envelopeReceiverId.GetEnvelopeUuid()), - 2 => Ok(envelopeReceiverId.GetReceiverSignature()), - _ => Ok(envelopeReceiverId.DecodeEnvelopeReceiverId()), - }; - } + 1 => Ok(envelopeReceiverId.GetEnvelopeUuid()), + 2 => Ok(envelopeReceiverId.GetReceiverSignature()), + _ => Ok(envelopeReceiverId.DecodeEnvelopeReceiverId()), + }; } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index fa60b73d..6c0e5d59 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -12,9 +12,9 @@ digital data envelope generator web 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. Assets\icon.ico - 3.10.0 - 3.10.0.0 - 3.10.0.0 + 3.12.3 + 3.12.3.0 + 3.12.3.0 Copyright © 2025 Digital Data GmbH. All rights reserved. @@ -626,7 +626,6 @@ - @@ -2094,22 +2093,22 @@ - + - + - - - - - - - - + + + + + + + + @@ -2124,8 +2123,8 @@ - - + + @@ -2149,8 +2148,8 @@ - - + + diff --git a/EnvelopeGenerator.Web/Views/Envelope/EnvelopeRejected.cshtml b/EnvelopeGenerator.Web/Views/Envelope/EnvelopeRejected.cshtml index 218dd2c0..2ee0a73b 100644 --- a/EnvelopeGenerator.Web/Views/Envelope/EnvelopeRejected.cshtml +++ b/EnvelopeGenerator.Web/Views/Envelope/EnvelopeRejected.cshtml @@ -9,12 +9,14 @@ @using EnvelopeGenerator.Web.Extensions @using Newtonsoft.Json @using Newtonsoft.Json.Serialization +@using EnvelopeGenerator.Domain.Interfaces; @model EnvelopeReceiverDto; @{ var envelope = Model.Envelope; var document = Model.Envelope?.Documents?.FirstOrDefault(); var sender = Model.Envelope?.User; var isExt = ViewBag.IsExt ?? false; + bool IsReadAndConfirm = Model!.Envelope!.IsReadAndConfirm(); }
@@ -54,7 +56,11 @@ c-5.791,5.79-15.176,5.79-20.969,0l-30.32-30.322l-11.676,11.676l30.32,30.32c5.79,5.79,5.79,15.178,0,20.969L299.11,404.045z"/>
-

@(isExt ? _localizer.RejectionInfo1Ext() : _localizer.RejectionInfo1())

+

@(isExt + ? _localizer.RejectionInfo1Ext() + : IsReadAndConfirm + ? _localizer.RejectionInfo1ForConfirmation() + : _localizer.RejectionInfo1())

diff --git a/EnvelopeGenerator.Web/Views/Envelope/EnvelopeSigned.cshtml b/EnvelopeGenerator.Web/Views/Envelope/EnvelopeSigned.cshtml index e2c5ae4d..0aed7cf2 100644 --- a/EnvelopeGenerator.Web/Views/Envelope/EnvelopeSigned.cshtml +++ b/EnvelopeGenerator.Web/Views/Envelope/EnvelopeSigned.cshtml @@ -1,5 +1,11 @@ @{ - ViewData["Title"] = _localizer.DocSigned(); + @using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; + @using EnvelopeGenerator.Domain.Interfaces; + @model EnvelopeReceiverDto; + bool IsReadAndConfirm = Model!.Envelope!.IsReadAndConfirm(); + ViewData["Title"] = IsReadAndConfirm + ? _localizer.DocConfirmed() + : _localizer.DocSigned(); }
@@ -9,9 +15,13 @@
-

@_localizer["DocumentSuccessfullySigned"]

+

@(IsReadAndConfirm + ? _localizer["DocumentSuccessfullyConfirmed"] + : _localizer["DocumentSuccessfullySigned"])

-

@_localizer["DocumentSignedConfirmationMessage"]

+

@(IsReadAndConfirm + ? _localizer["DocumentConfirmedConfirmationMessage"] + : _localizer["DocumentSignedConfirmationMessage"])

\ No newline at end of file diff --git a/EnvelopeGenerator.Web/Views/Envelope/ShowEnvelope.cshtml b/EnvelopeGenerator.Web/Views/Envelope/ShowEnvelope.cshtml index b710f772..8a454993 100644 --- a/EnvelopeGenerator.Web/Views/Envelope/ShowEnvelope.cshtml +++ b/EnvelopeGenerator.Web/Views/Envelope/ShowEnvelope.cshtml @@ -9,6 +9,7 @@ @using EnvelopeGenerator.Web.Extensions @using Newtonsoft.Json @using Newtonsoft.Json.Serialization +@using EnvelopeGenerator.Domain.Interfaces @model EnvelopeReceiverDto; @{ var userCulture = ViewData["UserCulture"] as Culture; @@ -24,7 +25,13 @@ if (ViewData["IsReadOnly"] is bool isReadOnly_bool) isReadOnly = isReadOnly_bool; - ViewData["Title"] = isReadOnly ? _localizer.ViewDoc() : _localizer.SignDoc(); + var isReadAndConfirm = envelope.IsReadAndConfirm(); + + ViewData["Title"] = isReadOnly + ? _localizer.ViewDoc() + : isReadAndConfirm + ? _localizer.ConfirmDoc() + : _localizer.SignDoc(); }
@if (!isReadOnly) @@ -62,7 +69,9 @@
- 0/@signatureCount @_localizer["Signatures"] + 0/@signatureCount @(isReadAndConfirm + ? _localizer["Confirmations"] + : _localizer["Signatures"])
} diff --git a/EnvelopeGenerator.Web/appsettings.Mail.json b/EnvelopeGenerator.Web/appsettings.Mail.json index 61dc2518..22b0e1be 100644 --- a/EnvelopeGenerator.Web/appsettings.Mail.json +++ b/EnvelopeGenerator.Web/appsettings.Mail.json @@ -8,7 +8,6 @@ "MailParams": { "Placeholders": { "[NAME_PORTAL]": "signFlow", - "[SIGNATURE_TYPE]": "signieren", "[REASON]": "" } } diff --git a/EnvelopeGenerator.Web/wwwroot/js/annotation.js b/EnvelopeGenerator.Web/wwwroot/js/annotation.js index 55b53758..debb5e88 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/annotation.js +++ b/EnvelopeGenerator.Web/wwwroot/js/annotation.js @@ -275,7 +275,7 @@ async function createAnnotationFrameBlob(receiverName, receiverSignature, timest ctx.fillStyle = 'black' ctx.font = `${fontSize * scale}px sans-serif` - ctx.fillText(localized.signedBy ?? 'Signed by', 15 * scale, 10 * scale) + ctx.fillText((READ_AND_CONFIRM ? localized.confirmedBy : localized.signedBy) ?? 'Signed by', 15 * scale, 10 * scale) ctx.fillText(receiverName, 15 * scale, 60 * scale) ctx.fillText(signatureString, 15 * scale, 70 * scale) diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index 3ae36e46..62a329e9 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -255,7 +255,7 @@ class App { return Swal.fire({ title: localized.confirmation, - html: `
${localized.sigAgree}
`, + html: `
${READ_AND_CONFIRM ? localized.confirmAgree : localized.sigAgree}
`, icon: "question", showCancelButton: true, confirmButtonColor: "#3085d6", diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.min.js b/EnvelopeGenerator.Web/wwwroot/js/app.min.js index c07a1e10..8d9a209a 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.min.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.min.js @@ -1,3 +1,3 @@ const formatLocalized=(n,...t)=>typeof n=="string"?n.replace(/\{(\d+)\}/g,(n,i)=>t[i]??""):"";class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.pdfKit=null;this.currentDocument=t.envelope.documents[0];this.currentReceiver=t.receiver;this.signatureCount=t.envelope.documents[0].elements.length;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){if(this.pdfKit=await loadPSPDFKit(this.documentBytes,this.container,this.licenseKey,this.locale),addToolbarItems(this.pdfKit,this.handleClick.bind(this)),this.pdfKit.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this)),this.pdfKit.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this)),this.pdfKit.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this)),this.pdfKit.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()}),!READ_AND_CONFIRM)try{let n=await createAnnotations(this.currentDocument,this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId);await this.pdfKit.create(n)}catch(n){console.error("Error loading annotations:",n)}READ_AND_CONFIRM||[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.pdfKit.createAttachment(s),c=createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h,generateId(this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId,this.fakeElementId--,"signed"));this.pdfKit.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:localized.success,text:localized.documentReset,icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/Envelope/${this.envelopeKey}`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`
${localized.rejectionReasonQ}
`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(` Request failed: ${t} - `)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?reload():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify(localized.copyLinkSuccess,{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify(localized.copyLinkFailure??localized.unexpectedErrorTitle,{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOGOUT":await logout()}}async handleFinish(){let n=undefined;if(READ_AND_CONFIRM){const n=JSON.parse(sessionStorage.getItem("pspdf_all_pages_rendered")||"false")===!0;if(!n){const n=JSON.parse(sessionStorage.getItem("pspdf_unviewed_pages")||"[]"),t=n.length?formatLocalized(localized.viewRemainingPages,n.join(", ")):localized.viewAllPages;return await Swal.fire({title:localized.warning,text:t,icon:"warning"}),!1}}else{const i=await this.pdfKit.exportInstantJSON(),r=i.formFieldValues,u=r.filter(n=>isFieldRequired(n)),f=u.some(n=>n.value===undefined||n.value===null||n.value==="");if(f)return Swal.fire({title:localized.warning,text:localized.locationFieldsRequired,icon:"warning"}),!1;const e=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),o=r.filter(n=>isCityField(n));for(var t of o)if(!IS_MOBILE_DEVICE&&!e.test(t.value))return Swal.fire({title:localized.warning,text:formatLocalized(localized.cityFormatInvalid,t.value),icon:"warning"}),!1;const s=await this.validateAnnotations(this.signatureCount);if(s===!1)return Swal.fire({title:localized.warning,text:localized.missingSignatures,icon:"warning"}),!1;n={instant:i,structured:mapSignature(i)}}return Swal.fire({title:localized.confirmation,html:`
${localized.sigAgree}
`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.pdfKit.save()}catch(i){return Swal.fire({title:localized.warning,text:localized.envelopeSignError,icon:"error"}),!1}try{const t=READ_AND_CONFIRM?await signEnvelope():await signEnvelope(n);if(t.ok)return!0;if(t.status===409)return Swal.fire({title:localized.warning,text:localized.envelopeUnavailable,icon:"warning"}),!1;if(t.status===423)Swal.fire({title:localized.info??localized.warning,text:localized.envelopeRejectedRedirect,icon:"info",timer:2e3,showConfirmButton:!1}).then(()=>{location.reload()});else throw new Error;}catch(i){return Swal.fire({title:localized.warning,text:localized.envelopeSignError,icon:"error"}),!1}}else return!1})}async validateAnnotations(n){const t=await getAnnotations(this.pdfKit),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n<=i.length}async handleReset(){const n=Swal.fire({title:localized.resetConfirmTitle,text:localized.resetConfirmText,icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await deleteAnnotations(this.pdfKit)}return n}fakeElementId=0;} \ No newline at end of file + `)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?reload():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify(localized.copyLinkSuccess,{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify(localized.copyLinkFailure??localized.unexpectedErrorTitle,{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOGOUT":await logout()}}async handleFinish(){let n=undefined;if(READ_AND_CONFIRM){const n=JSON.parse(sessionStorage.getItem("pspdf_all_pages_rendered")||"false")===!0;if(!n){const n=JSON.parse(sessionStorage.getItem("pspdf_unviewed_pages")||"[]"),t=n.length?formatLocalized(localized.viewRemainingPages,n.join(", ")):localized.viewAllPages;return await Swal.fire({title:localized.warning,text:t,icon:"warning"}),!1}}else{const i=await this.pdfKit.exportInstantJSON(),r=i.formFieldValues,u=r.filter(n=>isFieldRequired(n)),f=u.some(n=>n.value===undefined||n.value===null||n.value==="");if(f)return Swal.fire({title:localized.warning,text:localized.locationFieldsRequired,icon:"warning"}),!1;const e=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),o=r.filter(n=>isCityField(n));for(var t of o)if(!IS_MOBILE_DEVICE&&!e.test(t.value))return Swal.fire({title:localized.warning,text:formatLocalized(localized.cityFormatInvalid,t.value),icon:"warning"}),!1;const s=await this.validateAnnotations(this.signatureCount);if(s===!1)return Swal.fire({title:localized.warning,text:localized.missingSignatures,icon:"warning"}),!1;n={instant:i,structured:mapSignature(i)}}return Swal.fire({title:localized.confirmation,html:`
${READ_AND_CONFIRM?localized.confirmAgree:localized.sigAgree}
`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.pdfKit.save()}catch(i){return Swal.fire({title:localized.warning,text:localized.envelopeSignError,icon:"error"}),!1}try{const t=READ_AND_CONFIRM?await signEnvelope():await signEnvelope(n);if(t.ok)return!0;if(t.status===409)return Swal.fire({title:localized.warning,text:localized.envelopeUnavailable,icon:"warning"}),!1;if(t.status===423)Swal.fire({title:localized.info??localized.warning,text:localized.envelopeRejectedRedirect,icon:"info",timer:2e3,showConfirmButton:!1}).then(()=>{location.reload()});else throw new Error;}catch(i){return Swal.fire({title:localized.warning,text:localized.envelopeSignError,icon:"error"}),!1}}else return!1})}async validateAnnotations(n){const t=await getAnnotations(this.pdfKit),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n<=i.length}async handleReset(){const n=Swal.fire({title:localized.resetConfirmTitle,text:localized.resetConfirmText,icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await deleteAnnotations(this.pdfKit)}return n}fakeElementId=0;} \ No newline at end of file diff --git a/EnvelopeGenerator.WorkerService/Configuration/WorkerSettings.cs b/EnvelopeGenerator.WorkerService/Configuration/WorkerSettings.cs deleted file mode 100644 index dc4d2c91..00000000 --- a/EnvelopeGenerator.WorkerService/Configuration/WorkerSettings.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EnvelopeGenerator.Jobs.FinalizeDocument; - -namespace EnvelopeGenerator.WorkerService.Configuration; - -public sealed class WorkerSettings -{ - public string ConnectionString { get; set; } = string.Empty; - - public bool Debug { get; set; } - - public int IntervalMinutes { get; set; } = 1; - - public PDFBurnerParams PdfBurner { get; set; } = new(); -} diff --git a/EnvelopeGenerator.WorkerService/EnvelopeGenerator.WorkerService.csproj b/EnvelopeGenerator.WorkerService/EnvelopeGenerator.WorkerService.csproj deleted file mode 100644 index aec7186d..00000000 --- a/EnvelopeGenerator.WorkerService/EnvelopeGenerator.WorkerService.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - net8.0 - enable - enable - dotnet-EnvelopeGenerator.WorkerService-0636abb8-6085-477d-9f56-1a9787e84dde - - - - - - - - - - - - - - - diff --git a/EnvelopeGenerator.WorkerService/Program.cs b/EnvelopeGenerator.WorkerService/Program.cs deleted file mode 100644 index 03e8e055..00000000 --- a/EnvelopeGenerator.WorkerService/Program.cs +++ /dev/null @@ -1,67 +0,0 @@ -using EnvelopeGenerator.Domain.Constants; -using EnvelopeGenerator.Jobs.APIBackendJobs; -using EnvelopeGenerator.Jobs.FinalizeDocument; -using EnvelopeGenerator.WorkerService; -using EnvelopeGenerator.WorkerService.Configuration; -using EnvelopeGenerator.WorkerService.Services; -using Quartz; - -var builder = Host.CreateApplicationBuilder(args); - -builder.Services.Configure(builder.Configuration.GetSection("WorkerSettings")); - -builder.Services.AddSingleton(); -builder.Services.AddSingleton(provider => -{ - var settings = provider.GetRequiredService>().Value; - var logger = provider.GetRequiredService>(); - return new PDFBurner(logger, settings.PdfBurner); -}); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); - -builder.Services.AddQuartz(q => -{ - q.UseMicrosoftDependencyInjectionJobFactory(); - q.UseDefaultThreadPool(tp => tp.MaxConcurrency = 5); - - var settings = new WorkerSettings(); - builder.Configuration.GetSection("WorkerSettings").Bind(settings); - var intervalMinutes = Math.Max(1, settings.IntervalMinutes); - - var finalizeJobKey = new JobKey("FinalizeDocumentJob"); - q.AddJob(opts => opts - .WithIdentity(finalizeJobKey) - .UsingJobData(Value.DATABASE, settings.ConnectionString)); - - q.AddTrigger(opts => opts - .ForJob(finalizeJobKey) - .WithIdentity("FinalizeDocumentJob-trigger") - .StartNow() - .WithSimpleSchedule(x => x - .WithIntervalInMinutes(intervalMinutes) - .RepeatForever())); - - var apiJobKey = new JobKey("APIEnvelopeJob"); - q.AddJob(opts => opts - .WithIdentity(apiJobKey) - .UsingJobData(Value.DATABASE, settings.ConnectionString)); - - q.AddTrigger(opts => opts - .ForJob(apiJobKey) - .WithIdentity("APIEnvelopeJob-trigger") - .StartNow() - .WithSimpleSchedule(x => x - .WithIntervalInMinutes(intervalMinutes) - .RepeatForever())); -}); - -builder.Services.AddQuartzHostedService(options => -{ - options.WaitForJobsToComplete = true; -}); - -builder.Services.AddHostedService(); - -var host = builder.Build(); -host.Run(); diff --git a/EnvelopeGenerator.WorkerService/Properties/launchSettings.json b/EnvelopeGenerator.WorkerService/Properties/launchSettings.json deleted file mode 100644 index ddb5ece8..00000000 --- a/EnvelopeGenerator.WorkerService/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "EnvelopeGenerator.WorkerService": { - "commandName": "Project", - "dotnetRunMessages": true, - "environmentVariables": { - "DOTNET_ENVIRONMENT": "Development" - } - } - } -} diff --git a/EnvelopeGenerator.WorkerService/Services/TempFileManager.cs b/EnvelopeGenerator.WorkerService/Services/TempFileManager.cs deleted file mode 100644 index f98e1c09..00000000 --- a/EnvelopeGenerator.WorkerService/Services/TempFileManager.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.IO; -using Microsoft.Extensions.Logging; - -namespace EnvelopeGenerator.WorkerService.Services; - -public sealed class TempFileManager -{ - private readonly ILogger _logger; - - public TempFileManager(ILogger logger) - { - _logger = logger; - TempPath = Path.Combine(Path.GetTempPath(), "EnvelopeGenerator"); - } - - public string TempPath { get; } - - public Task CreateAsync(CancellationToken cancellationToken = default) - { - try - { - if (!Directory.Exists(TempPath)) - { - Directory.CreateDirectory(TempPath); - _logger.LogDebug("Created temp folder {TempPath}", TempPath); - } - else - { - CleanUpFiles(); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to create temp folder {TempPath}", TempPath); - throw; - } - - return Task.CompletedTask; - } - - public Task CleanupAsync(CancellationToken cancellationToken = default) - { - try - { - if (Directory.Exists(TempPath)) - { - _logger.LogDebug("Deleting temp folder {TempPath}", TempPath); - Directory.Delete(TempPath, recursive: true); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to clean up temp folder {TempPath}", TempPath); - } - - return Task.CompletedTask; - } - - private void CleanUpFiles() - { - foreach (var file in Directory.GetFiles(TempPath)) - { - try - { - _logger.LogDebug("Deleting temp file {File}", file); - File.Delete(file); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to delete temp file {File}", file); - } - } - } -} diff --git a/EnvelopeGenerator.WorkerService/Worker.cs b/EnvelopeGenerator.WorkerService/Worker.cs deleted file mode 100644 index d88c3e63..00000000 --- a/EnvelopeGenerator.WorkerService/Worker.cs +++ /dev/null @@ -1,71 +0,0 @@ -using EnvelopeGenerator.WorkerService.Configuration; -using EnvelopeGenerator.WorkerService.Services; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Options; - -namespace EnvelopeGenerator.WorkerService; - -public class Worker : BackgroundService -{ - private readonly ILogger _logger; - private readonly WorkerSettings _settings; - private readonly TempFileManager _tempFiles; - - public Worker( - ILogger logger, - IOptions settings, - TempFileManager tempFiles) - { - _logger = logger; - _settings = settings.Value; - _tempFiles = tempFiles; - } - - public override async Task StartAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("Starting EnvelopeGenerator worker..."); - _logger.LogInformation("Debug mode: {Debug}", _settings.Debug); - - ValidateConfiguration(); - await EnsureDatabaseConnectionAsync(cancellationToken); - await _tempFiles.CreateAsync(cancellationToken); - - await base.StartAsync(cancellationToken); - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - _logger.LogInformation("EnvelopeGenerator worker is running. Jobs are scheduled every {Interval} minute(s).", Math.Max(1, _settings.IntervalMinutes)); - await Task.Delay(Timeout.Infinite, stoppingToken); - } - - public override async Task StopAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("Stopping EnvelopeGenerator worker..."); - await _tempFiles.CleanupAsync(cancellationToken); - await base.StopAsync(cancellationToken); - } - - private void ValidateConfiguration() - { - if (string.IsNullOrWhiteSpace(_settings.ConnectionString)) - { - throw new InvalidOperationException("Connection string cannot be empty. Configure 'WorkerSettings:ConnectionString'."); - } - } - - private async Task EnsureDatabaseConnectionAsync(CancellationToken cancellationToken) - { - try - { - await using var connection = new SqlConnection(_settings.ConnectionString); - await connection.OpenAsync(cancellationToken); - _logger.LogInformation("Database connection established successfully."); - } - catch (Exception ex) - { - _logger.LogError(ex, "Database connection could not be established."); - throw; - } - } -} diff --git a/EnvelopeGenerator.WorkerService/appsettings.Development.json b/EnvelopeGenerator.WorkerService/appsettings.Development.json deleted file mode 100644 index cfa751f3..00000000 --- a/EnvelopeGenerator.WorkerService/appsettings.Development.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "WorkerSettings": { - "ConnectionString": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;", - "Debug": true, - "IntervalMinutes": 1, - "PdfBurner": { - "IgnoredLabels": [ - "Date", - "Datum", - "ZIP", - "PLZ", - "Place", - "Ort", - "Position", - "Stellung" - ], - "TopMargin": 0.1, - "YOffset": -0.3, - "FontName": "Arial", - "FontSize": 8, - "FontStyle": "Italic" - } - } -} diff --git a/EnvelopeGenerator.WorkerService/appsettings.json b/EnvelopeGenerator.WorkerService/appsettings.json deleted file mode 100644 index abd01790..00000000 --- a/EnvelopeGenerator.WorkerService/appsettings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "WorkerSettings": { - "ConnectionString": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;", - "Debug": false, - "IntervalMinutes": 1, - "PdfBurner": { - "IgnoredLabels": ["Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung"], - "TopMargin": 0.1, - "YOffset": -0.3, - "FontName": "Arial", - "FontSize": 8, - "FontStyle": "Italic" - } - } -} diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 0db77ff0..f7e7ac2f 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -33,10 +33,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.PdfEditor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Tests", "EnvelopeGenerator.Tests\EnvelopeGenerator.Tests.csproj", "{224C4845-1CDE-22B7-F3A9-1FF9297F70E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Jobs", "EnvelopeGenerator.Jobs\EnvelopeGenerator.Jobs.csproj", "{3D0514EA-2681-4B13-AD71-35CC6363DBD7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.WorkerService", "EnvelopeGenerator.WorkerService\EnvelopeGenerator.WorkerService.csproj", "{E3676510-7030-4E85-86E1-51E483E2A3B6}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.API", "EnvelopeGenerator.API\EnvelopeGenerator.API.csproj", "{EC768913-6270-14F4-1DD3-69C87A659462}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "receiverUI", "receiverUI", "{73D8F466-90AA-4F95-9BD1-7CDBB8565162}" @@ -63,8 +59,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 @@ -91,14 +87,6 @@ Global {224C4845-1CDE-22B7-F3A9-1FF9297F70E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {224C4845-1CDE-22B7-F3A9-1FF9297F70E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {224C4845-1CDE-22B7-F3A9-1FF9297F70E8}.Release|Any CPU.Build.0 = Release|Any CPU - {3D0514EA-2681-4B13-AD71-35CC6363DBD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3D0514EA-2681-4B13-AD71-35CC6363DBD7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3D0514EA-2681-4B13-AD71-35CC6363DBD7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3D0514EA-2681-4B13-AD71-35CC6363DBD7}.Release|Any CPU.Build.0 = Release|Any CPU - {E3676510-7030-4E85-86E1-51E483E2A3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E3676510-7030-4E85-86E1-51E483E2A3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.Build.0 = Release|Any CPU {EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -129,8 +117,6 @@ Global {6D56C01F-D6CB-4D8A-BD3D-4FD34326998C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} {224C4845-1CDE-22B7-F3A9-1FF9297F70E8} = {0CBC2432-A561-4440-89BC-671B66A24146} - {3D0514EA-2681-4B13-AD71-35CC6363DBD7} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} - {E3676510-7030-4E85-86E1-51E483E2A3B6} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} {EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {73D8F466-90AA-4F95-9BD1-7CDBB8565162} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {AAD4720E-D175-44B5-B431-DB0BA636CD20} = {73D8F466-90AA-4F95-9BD1-7CDBB8565162}