Compare commits

...

4 Commits

Author SHA1 Message Date
57422a481c feat(PDFBurner): support unstructured annotations and simplify processing
- Added `UnstructuredAnnotations` property to handle annotations without structured IDs
- Changed default `hasStructuredID` to `False`, set to `True` only after successful parsing
- Updated `AddInstantJSONAnnotationToPDF` to process annotations directly instead of grouping by receiver
- Simplified logic for reversing annotations and applying seal positioning
2025-10-08 16:27:28 +02:00
e96523b786 update to use IRepository<T> 2025-10-08 13:36:09 +02:00
3b7d0e1321 refactor(RemoveSignatureNotification): create with handlers to remove signatures of a document 2025-10-08 12:41:14 +02:00
9adc1ea4e7 refactor(annotation): remove unnecessary loop 2025-10-08 10:26:25 +02:00
10 changed files with 164 additions and 41 deletions

View File

@@ -0,0 +1,32 @@
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveAnnotationHandler : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<Domain.Entities.DocumentStatus> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveAnnotationHandler(IRepository<Domain.Entities.DocumentStatus> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
await _repo.DeleteAsync(s => s.Envelope!.Uuid == notification.EnvelopeUuid, cancel);
}
}

View File

@@ -0,0 +1,40 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveHistoryHandler : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<Domain.Entities.History> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveHistoryHandler(IRepository<Domain.Entities.History> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
await _repo.DeleteAsync(hists
=> hists
.Where(hist => hist.Envelope!.Uuid == notification.EnvelopeUuid)
.Where(hist => hist.Status == EnvelopeStatus.DocumentSigned),
cancel);
}
}

View File

@@ -0,0 +1,9 @@
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
/// <summary>
///
/// </summary>
/// <param name="EnvelopeUuid"></param>
public record RemoveSignatureNotification(string EnvelopeUuid) : INotification;

View File

@@ -8,6 +8,7 @@ Imports Newtonsoft.Json.Linq
Imports DigitalData.Core.Abstraction.Application
Imports EnvelopeGenerator.Infrastructure
Imports Microsoft.EntityFrameworkCore
Imports System.Text
Public Class frmFinalizePDF
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;"
@@ -104,7 +105,15 @@ Public Class frmFinalizePDF
Process.Start(oNewPath)
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
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
End Sub

View File

@@ -71,42 +71,39 @@ Namespace Jobs.FinalizeDocument
Private Sub AddInstantJSONAnnotationToPDF(pInstantJSON As String)
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
For Each annots In oAnnotationData.AnnotationsByReceiver
annots.Reverse()
oAnnotationData.annotations.Reverse()
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim yPosOfSigAnnot = annots.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim formFieldIndex = 0
For Each oAnnotation In oAnnotationData.annotations
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Dim formFieldIndex = 0
For Each oAnnotation In annots
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Select Case oAnnotation.type
Case ANNOTATION_TYPE_IMAGE
Select Case oAnnotation.type
Case ANNOTATION_TYPE_IMAGE
If (isSeal) Then
oAnnotation.bbox.Item(1) = yPosOfSigAnnot
End If
If (isSeal) Then
oAnnotation.bbox.Item(1) = yPosOfSigAnnot
End If
AddImageAnnotation(oAnnotation, oAnnotationData.attachments)
Exit Select
AddImageAnnotation(oAnnotation, oAnnotationData.attachments)
Exit Select
Case ANNOTATION_TYPE_INK
AddInkAnnotation(oAnnotation)
Exit Select
Case ANNOTATION_TYPE_INK
AddInkAnnotation(oAnnotation)
Exit Select
Case ANNOTATION_TYPE_WIDGET
'Add form field values
Dim formFieldValue = oAnnotationData.formFieldValues.FirstOrDefault(Function(fv) fv.name = oAnnotation.id)
If formFieldValue IsNot Nothing AndAlso Not _pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value) Then
AddFormFieldValue(oAnnotation, formFieldValue, formFieldIndex)
formFieldIndex += 1
End If
Exit Select
End Select
Case ANNOTATION_TYPE_WIDGET
'Add form field values
Dim formFieldValue = oAnnotationData.formFieldValues.FirstOrDefault(Function(fv) fv.name = oAnnotation.id)
If formFieldValue IsNot Nothing AndAlso Not _pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value) Then
AddFormFieldValue(oAnnotation, formFieldValue, formFieldIndex)
formFieldIndex += 1
End If
Exit Select
End Select
isSeal = False
Next
isSeal = False
Next
End Sub
@@ -186,6 +183,15 @@ Namespace Jobs.FinalizeDocument
End Get
End Property
Public ReadOnly Property UnstructuredAnnotations As IEnumerable(Of List(Of Annotation))
Get
Return annotations _
.Where(Function(annot) Not annot.hasStructuredID).ToList() _
.GroupBy(Function(a) a.receiverId) _
.Select(Function(g) g.ToList())
End Get
End Property
Public Property attachments As Dictionary(Of String, Attachment)
Public Property formFieldValues As List(Of FormFieldValue)
End Class
@@ -202,7 +208,7 @@ Namespace Jobs.FinalizeDocument
Public internalType As String = Nothing
Public hasStructuredID As Boolean = True
Public hasStructuredID As Boolean = False
Public Property id As String
Get
@@ -211,14 +217,13 @@ Namespace Jobs.FinalizeDocument
Set(value As String)
_id = value
If String.IsNullOrWhiteSpace(id) Then
If String.IsNullOrWhiteSpace(_id) Then
Throw New BurnAnnotationException("The identifier of annotation is null or empty.")
End If
Dim parts As String() = value.Split("#"c)
If (parts.Length <> 4) Then
hasStructuredID = False
Return
'Throw New BurnAnnotationException($"The identifier of annotation has more or less than 4 sub-part. Id: {_id}")
End If
@@ -236,6 +241,8 @@ Namespace Jobs.FinalizeDocument
End If
internalType = parts(3)
hasStructuredID = True
End Set
End Property

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DigitalData.Core.Abstractions.Interfaces;
using EnvelopeGenerator.Domain.Interfaces;
#if NETFRAMEWORK
@@ -14,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_DOCUMENT_STATUS", Schema = "dbo")]
public class DocumentStatus : IHasEnvelope, IHasReceiver
public class DocumentStatus : IHasEnvelope, IHasReceiver, IEntity
{
public DocumentStatus()
{

View File

@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Interfaces;
using EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Abstractions.Interfaces;
#if NETFRAMEWORK
@@ -17,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")]
public class History : IHasEnvelope, IHasReceiver
public class History : IHasEnvelope, IHasReceiver, IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

View File

@@ -0,0 +1,30 @@
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers.Test;
[Route("api/[controller]")]
[ApiController]
public class TestAnnotationController : ControllerBase
{
private IMediator _mediator;
public TestAnnotationController(IMediator mediator)
{
_mediator = mediator;
}
[HttpDelete("{envelopeKey}")]
public async Task<IActionResult> Delete([FromRoute] string envelopeKey)
{
var uuid = envelopeKey.GetEnvelopeUuid();
if (uuid == null)
return BadRequest();
await _mediator.Publish(new RemoveSignatureNotification(uuid));
return Ok();
}
}

View File

@@ -7,11 +7,6 @@ function generateId(envelopeId, receiverId, annotationType) {
async function createAnnotations(document, envelopeId, receiverId) {
const signatures = [];
for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1
}
for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1

View File

@@ -275,7 +275,6 @@ class App {
});
}
async validateAnnotations(totalSignatures) {
const annotations = await getAnnotations(this.pdfKit)
const filtered = annotations