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 DigitalData.Core.Abstraction.Application
Imports EnvelopeGenerator.Infrastructure Imports EnvelopeGenerator.Infrastructure
Imports Microsoft.EntityFrameworkCore Imports Microsoft.EntityFrameworkCore
Imports System.Text
Public Class frmFinalizePDF 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=;"
@@ -104,7 +105,15 @@ Public Class frmFinalizePDF
Process.Start(oNewPath) Process.Start(oNewPath)
Catch ex As Exception 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 Try
End Sub End Sub

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Interfaces; using EnvelopeGenerator.Domain.Interfaces;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Abstractions.Interfaces;
#if NETFRAMEWORK #if NETFRAMEWORK
@@ -17,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif #endif
[Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")] [Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")]
public class History : IHasEnvelope, IHasReceiver public class History : IHasEnvelope, IHasReceiver, IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [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) { async function createAnnotations(document, envelopeId, receiverId) {
const signatures = []; 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) { for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top); const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1 const page = element.page - 1

View File

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