From 898097cdb55fe4e3e7d770233d5b55aea8a43691 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 23 Feb 2026 10:56:03 +0100 Subject: [PATCH] Remove EnvelopeGenerator.Jobs project from solution Deleted the EnvelopeGenerator.Jobs project and all its source files. Also removed all references to this project from the solution file, including project entries and build configurations. This cleans up the repository by eliminating unused or deprecated job-related code. --- .../EnvelopeGenerator.Jobs.csproj | 26 -- .../Jobs/APIBackendJobs/APIEnvelopeJob.cs | 151 ---------- .../Jobs/DataRowExtensions.cs | 30 -- .../FinalizeDocumentExceptions.cs | 28 -- .../FinalizeDocument/FinalizeDocumentJob.cs | 229 --------------- .../Jobs/FinalizeDocument/PDFBurner.cs | 277 ------------------ .../Jobs/FinalizeDocument/PDFBurnerParams.cs | 19 -- .../Jobs/FinalizeDocument/PDFMerger.cs | 46 --- .../Jobs/FinalizeDocument/ReportCreator.cs | 91 ------ .../Jobs/FinalizeDocument/ReportItem.cs | 19 -- .../Jobs/FinalizeDocument/ReportSource.cs | 8 - EnvelopeGenerator.sln | 7 - 12 files changed, 931 deletions(-) delete mode 100644 EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj delete mode 100644 EnvelopeGenerator.Jobs/Jobs/APIBackendJobs/APIEnvelopeJob.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/DataRowExtensions.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentExceptions.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/FinalizeDocumentJob.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurner.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFBurnerParams.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/PDFMerger.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportCreator.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportItem.cs delete mode 100644 EnvelopeGenerator.Jobs/Jobs/FinalizeDocument/ReportSource.cs 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.sln b/EnvelopeGenerator.sln index fe942d58..f55cdd56 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -33,8 +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.API", "EnvelopeGenerator.API\EnvelopeGenerator.API.csproj", "{EC768913-6270-14F4-1DD3-69C87A659462}" EndProject Global @@ -83,10 +81,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 {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 @@ -109,7 +103,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} {EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution