Compare commits

...

26 Commits

Author SHA1 Message Date
b996102a86 refactor(appsettings.Job): add coma 2025-11-14 10:35:55 +01:00
4b1d316b46 remove Finalizer.Win 2025-11-14 09:59:19 +01:00
51234cda2a refactor: simplify report creation by removing DoCreateReport and inlining logic 2025-11-14 09:51:08 +01:00
e719dded48 refactor(FinalizeDocumentJob): Add service container configuration code as a comment 2025-11-14 09:43:37 +01:00
6195d99fab refactor: remove usePdfBurner parameter from AddEnvelopeGeneratorServices 2025-11-12 16:55:06 +01:00
50a372a224 feat(CCommonServicces.FinalizeDocument.PDFMerger): update to use SaveToStream instead of ConvertToPDFA 2025-11-12 16:27:20 +01:00
35dd2e8e07 refactor(email): replace Task.WhenAll with foreach in SendEmailBehavior
Changed the SendFinalEmailToReceiversAsync method to use a sequential `foreach` loop
instead of `Task.WhenAll` for sending history commands. Added a null check for
receiver emails to prevent potential exceptions.
2025-11-12 16:14:32 +01:00
fac5419589 refactor: simplify email sending logic and remove debug checks
- Removed unused `EnvelopeGenerator.Domain.Entities` import.
- Changed `SendFinalEmailWithAttachment` calls to explicitly cast envelope email type to `int`.
- Removed `Debug` conditional checks around history creation; history commands are now always sent.
- Made `SendFinalEmailWithAttachment` a static method for clarity.
2025-11-12 15:44:37 +01:00
d442ad0ce0 refactor(SendEmailBehavior): update to add history recors when request is not debug 2025-11-12 15:35:23 +01:00
cbe2acc37d feat(SendEmailBehavior): make SendEmailBehavior fully async and record history
- Added ISender dependency to send history commands
- Refactored SendFinalEmailToCreator and SendFinalEmailToReceivers to async
- Log attachment inclusion for emails
- Use Task.WhenAll for sending to multiple receivers
2025-11-12 15:32:06 +01:00
40cc467b47 feat(SendEmailBehavior): add SendEmailBehavior for sending final emails after PDF burn
- Implements IPipelineBehavior<BurnPdfCommand, byte[]> to send emails to creator and receivers.
- Logs debug information when sending emails and warnings if email sending is skipped.
- Uses Envelope's FinalEmailType to determine whether to send emails.
2025-11-12 15:12:04 +01:00
d1513dab5e feat(WritePdfBehavior): add WritePdfBehavior to handle PDF writing pipeline
- Implements WritePdfBehavior as an IPipelineBehavior for BurnPdfCommand
- Writes generated PDF to configured export path
- Automatically creates export directory if missing
- Adds logging for export path and file operations
2025-11-12 14:55:35 +01:00
dee424e7db feat(DefaultReadConfigQuery): add DefaultReadConfigQuery with caching and AutoMapper support
- Implemented DefaultReadConfigQuery and its handler for reading default configuration.
- Added memory caching using IMemoryCache to improve performance.
- Integrated AutoMapper to map Config entities to ConfigDto.
- Included extension method for easier MediatR query invocation.
- Added error handling for missing configuration records.
2025-11-12 14:34:30 +01:00
e57e9e1834 fix(PdfMergeBehavior): improve error diagnostics and exception messages in PdfMergeBehavior
- Added detailed request context to all MergeDocumentException messages
- Clarified conversion error message from "converted to PDF/A" to "converted to byte"
- Removed unused base64 conversion and redundant variable assignment
- Improved code readability and string concatenation formatting
2025-11-12 14:02:40 +01:00
5b30465126 refactor(pdf): simplify and inline PDF merge logic in PdfMergeBehavior
- Removed MergeDocuments static method; integrated merge process directly in Handle()
- Added null check for request.Report before merging
- Cleaned up redundant return and improved flow for memory stream handling
- Added DevExpress.XtraReports using directive
- Clarified comment and exception messages for PDF loading and merging
2025-11-12 13:59:35 +01:00
2dfa1de7e1 refactor(PdfMergeBehavior): update to use SaveToStream instead of ConvertToPDFA 2025-11-12 13:51:49 +01:00
e68965543e refactor(BurnPdfCommand): add Report property to forward report.
- Reverse the order in which behaviors are added.
2025-11-12 12:47:19 +01:00
958bcdfc42 chore: wrap AddReportBehavior with #if WINDOWS directive 2025-11-12 11:08:09 +01:00
d88ed324be refactor(Pdf\Behaviors): Handle the history recording process in another behavior named CreateHistoryBehavior 2025-11-12 11:00:24 +01:00
380b141738 refactor(project): add net8.0-windows target and unify net8.0 package references
- Updated <TargetFrameworks> to include net8.0-windows
- Consolidated package references for net8.0 and net8.0-windows into a single ItemGroup
2025-11-12 10:51:04 +01:00
f3a15216a8 chore: update TargetFramework to net8.0-windows in project file 2025-11-12 10:43:18 +01:00
8a48bf6b51 feat(EnvelopeGenerator.Finalizer.Win): Create to process window-oriented services 2025-11-12 10:17:37 +01:00
0038eeed76 feat(EGDbContextBase): add EnvelopeReports-property 2025-11-11 23:31:35 +01:00
4ed118ed2b update to add as closed behavior 2025-11-11 23:24:51 +01:00
Developer 02
350aa259c8 chore(Application): add DevExpress.Reporting.Core 2025-11-11 23:16:36 +01:00
Developer 02
737de2202e remove unnecessary projects 2025-11-11 23:08:50 +01:00
17 changed files with 468 additions and 73 deletions

View File

@@ -0,0 +1,87 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.Extensions.Caching.Memory;
namespace EnvelopeGenerator.Application.Configs;
/// <summary>
///
/// </summary>
public class DefaultReadConfigQuery : IRequest<ConfigDto>
{
/// <summary>
///
/// </summary>
public static readonly Guid MemoryCacheKey = Guid.NewGuid();
}
/// <summary>
///
/// </summary>
public static class DefaultReadConfigQueryExtensions
{
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<ConfigDto> ReadDefaultConfigAsync(this ISender sender, CancellationToken cancel = default)
=> sender.Send(new DefaultReadConfigQuery(), cancel);
}
/// <summary>
///
/// </summary>
public class DefaultReadConfigQueryHandler : IRequestHandler<DefaultReadConfigQuery, ConfigDto>
{
private readonly IRepository<Config> _repo;
private readonly IMapper _mapper;
private readonly IMemoryCache _cache;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
/// <param name="mapper"></param>
/// <param name="cache"></param>
public DefaultReadConfigQueryHandler(IRepository<Config> repo, IMapper mapper, IMemoryCache cache)
{
_repo = repo;
_mapper = mapper;
_cache = cache;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<ConfigDto> Handle(DefaultReadConfigQuery request, CancellationToken cancel)
{
var config = await _cache.GetOrCreateAsync(DefaultReadConfigQuery.MemoryCacheKey, async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
var configs = await _repo.GetAllAsync(cancel);
var defaultConfig = configs.FirstOrDefault();
var defaultConfigDto = _mapper.Map<ConfigDto>(defaultConfig);
return defaultConfigDto;
});
if(config is null)
{
_cache.Remove(DefaultReadConfigQuery.MemoryCacheKey);
throw new NotFoundException("No configuration record is found.");
}
return config;
}
}

View File

@@ -25,10 +25,9 @@ public static class DependencyInjection
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="usePdfBurner"></param>
/// <returns></returns> /// <returns></returns>
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public static IServiceCollection AddEnvelopeGeneratorServices(this IServiceCollection services, IConfiguration config, bool usePdfBurner = false) public static IServiceCollection AddEnvelopeGeneratorServices(this IServiceCollection services, IConfiguration config)
{ {
//Inject CRUD Service and repositoriesad //Inject CRUD Service and repositoriesad
services.TryAddScoped<IConfigService, ConfigService>(); services.TryAddScoped<IConfigService, ConfigService>();
@@ -53,30 +52,29 @@ public static class DependencyInjection
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams))); services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams))); services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
if (usePdfBurner) #region PDF Burner
services.Configure<PDFBurnerParams>(config.GetSection(nameof(PDFBurnerParams)));
services.AddOptions<GdPictureParams>()
.Configure((GdPictureParams opt, IServiceProvider provider) =>
{ {
services.Configure<PDFBurnerParams>(config.GetSection(nameof(PDFBurnerParams))); opt.License = config["GdPictureLicenseKey"]
services.AddOptions<GdPictureParams>() ?? provider.GetRequiredService<IMediator>().ReadThirdPartyModuleLicenseAsync("GDPICTURE").GetAwaiter().GetResult()
.Configure((GdPictureParams opt, IServiceProvider provider) => ?? throw new InvalidOperationException($"License record not found for key: {"GDPICTURE"}");
{ });
opt.License = config["GdPictureLicenseKey"] services.AddSingleton(provider =>
?? provider.GetRequiredService<IMediator>().ReadThirdPartyModuleLicenseAsync("GDPICTURE").GetAwaiter().GetResult() {
?? throw new InvalidOperationException($"License record not found for key: {"GDPICTURE"}"); var license = provider.GetRequiredService<IOptions<GdPictureParams>>().Value.License;
}); var licenseManager = new LicenseManager();
services.AddSingleton(provider => licenseManager.RegisterKEY(license);
{ return licenseManager;
var license = provider.GetRequiredService<IOptions<GdPictureParams>>().Value.License; });
var licenseManager = new LicenseManager(); services.AddTransient(provider =>
licenseManager.RegisterKEY(license); {
return licenseManager; // Ensure LicenseManager is resolved so that its constructor is called
}); _ = provider.GetRequiredService<LicenseManager>();
services.AddTransient(provider => return new AnnotationManager();
{ });
// Ensure LicenseManager is resolved so that its constructor is called #endregion PDF Burner
_ = provider.GetRequiredService<LicenseManager>();
return new AnnotationManager();
});
}
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams))); services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
services.TryAddSingleton<ISmsSender, GTXSmsSender>(); services.TryAddSingleton<ISmsSender, GTXSmsSender>();
@@ -87,7 +85,15 @@ public static class DependencyInjection
services.AddMediatR(cfg => services.AddMediatR(cfg =>
{ {
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
cfg.AddOpenBehaviors(new Type[] { typeof(AddReportBehavior), typeof(SavePdfBehavior) });
cfg.AddBehavior<CreateHistoryBehavior>();
cfg.AddBehavior<SavePdfBehavior>();
#if WINDOWS
cfg.AddBehavior<SendEmailBehavior>();
cfg.AddBehavior<WritePdfBehavior>();
cfg.AddBehavior<PdfMergeBehavior>();
cfg.AddBehavior<AddReportBehavior>();
#endif
}); });
// Add memory cache // Add memory cache

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net7.0;net8.0;net9.0;net8.0-windows</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DevExpress.Reporting.Core" Version="21.2.4" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" /> <PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" /> <PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
@@ -35,7 +36,7 @@
<PackageReference Include="GdPicture" Version="14.3.19.1" /> <PackageReference Include="GdPicture" Version="14.3.19.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8.0-windows'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" /> <PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
<PackageReference Include="GdPicture" Version="14.3.19.1" /> <PackageReference Include="GdPicture" Version="14.3.19.1" />
@@ -52,7 +53,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application.VB\EnvelopeGenerator.Application.VB.vbproj" />
<ProjectReference Include="..\EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj" /> <ProjectReference Include="..\EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj" />
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" /> <ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" /> <ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
@@ -93,7 +93,7 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8.0-windows'">
<PackageReference Include="AutoMapper" Version="14.0.0" /> <PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" /> <PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageReference Include="CommandDotNet"> <PackageReference Include="CommandDotNet">

View File

@@ -1,11 +1,10 @@
using MediatR; #if WINDOWS
using EnvelopeGenerator.Application.Histories.Commands; using MediatR;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.EnvelopeReports; using EnvelopeGenerator.Application.EnvelopeReports;
using EnvelopeGenerator.Application.Exceptions; using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using EnvelopeGenerator.Application.VB; using EnvelopeGenerator.CommonServices;
namespace EnvelopeGenerator.Application.Pdf.Behaviors; namespace EnvelopeGenerator.Application.Pdf.Behaviors;
@@ -40,17 +39,7 @@ public class AddReportBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{ {
var docResult = await next(cancel); var docResult = await next(cancel);
if (!request.Debug) request.Report = await CreateReport(request.Envelope!, cancel);
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = request.EnvelopeId,
UserReference = "System",
Status = EnvelopeStatus.EnvelopeReportCreated,
}, cancel);
docResult = await CreateReport(request.Envelope!, cancel);
var base64 = Convert.ToBase64String(docResult);
return docResult; return docResult;
} }
@@ -71,13 +60,6 @@ public class AddReportBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
throw new CreateReportException("No report data found!"); throw new CreateReportException("No report data found!");
} }
var oBuffer = AddReportBehavior.DoCreateReport(oItems);
return oBuffer;
}
private static byte[] DoCreateReport(IEnumerable<EnvelopeReport> oItems)
{
var oSource = new ReportSource { Items = oItems }; var oSource = new ReportSource { Items = oItems };
var oReport = new rptEnvelopeHistory var oReport = new rptEnvelopeHistory
{ {
@@ -106,4 +88,5 @@ public class AddReportBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
/// </summary> /// </summary>
public required IEnumerable<EnvelopeReport> Items { get; init; } public required IEnumerable<EnvelopeReport> Items { get; init; }
} }
} }
#endif

View File

@@ -0,0 +1,49 @@
using MediatR;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class CreateHistoryBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
private readonly ISender _sender;
private readonly ILogger<CreateHistoryBehavior> _logger;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="logger"></param>
public CreateHistoryBehavior(ISender sender, ILogger<CreateHistoryBehavior> logger)
{
_sender = sender;
_logger = logger;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var doc = await next(cancel);
if (!request.Debug)
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = request.EnvelopeId,
UserReference = "System",
Status = EnvelopeStatus.EnvelopeReportCreated,
}, cancel);
return doc;
}
}

View File

@@ -0,0 +1,71 @@
#if WINDOWS
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Domain.Constants;
using GdPicture14;
using MediatR;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class PdfMergeBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var doc = await next(cancel);
if (request.Report is null)
throw new InvalidOperationException("The final document report could not be merged."
+ "There may be an error related to the behavior register order."
+ "Request details:\n" + request.ToJson(Format.Json.ForDiagnostics));
using var oDocumentStream = new MemoryStream(doc);
using var oReportStream = new MemoryStream(request.Report);
using var oFinalStream = new MemoryStream();
using var oDocumentPDF = new GdPicturePDF();
using var oReportPDF = new GdPicturePDF();
GdPictureStatus oStatus = GdPictureStatus.OK;
// Load the source file into memory
oDocumentPDF.LoadFromStream(oDocumentStream, true);
oStatus = oDocumentPDF.GetStat();
if (oStatus != GdPictureStatus.OK)
throw new MergeDocumentException($"Document could not be loaded: {oStatus}."
+ "Request details:\n" + request.ToJson(Format.Json.ForDiagnostics));
// Load the report file into memory
oReportPDF.LoadFromStream(oReportStream, true);
oStatus = oReportPDF.GetStat();
if (oStatus != GdPictureStatus.OK)
throw new MergeDocumentException($"Report could not be loaded: {oStatus}."
+ "Request details:\n" + request.ToJson(Format.Json.ForDiagnostics));
// Merge the documents
var oMergedPDF = oDocumentPDF.Merge2Documents(oDocumentPDF, oReportPDF);
oStatus = oMergedPDF.GetStat();
if (oStatus != GdPictureStatus.OK)
throw new MergeDocumentException($"Documents could not be merged: {oStatus}."
+ "Request details:\n" + request.ToJson(Format.Json.ForDiagnostics));
// Convert to byte
oMergedPDF.SaveToStream(oFinalStream);
oStatus = oDocumentPDF.GetStat();
if (oStatus != GdPictureStatus.OK)
throw new MergeDocumentException($"Document could not be converted to byte: {oStatus}."
+ "Request details:\n" + request.ToJson(Format.Json.ForDiagnostics));
return oFinalStream.ToArray();
}
}
#endif

View File

@@ -0,0 +1,113 @@
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class SendEmailBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
private readonly ILogger<SendEmailBehavior> _logger;
private readonly ISender _sender;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="sender"></param>
public SendEmailBehavior(ILogger<SendEmailBehavior> logger, ISender sender)
{
_logger = logger;
_sender = sender;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var docResult = await next(cancel);
var mailToCreator = request.Envelope!.FinalEmailToCreator;
var mailToReceivers = request.Envelope.FinalEmailToReceivers;
if (mailToCreator is not null && mailToCreator != (int)FinalEmailType.No)
{
_logger.LogDebug("Sending email to creator ...");
await SendFinalEmailToCreatorAsync(request, cancel); // , pAttachment
}
else
{
_logger.LogWarning("No SendFinalEmailToCreatorAsync - mailToCreator [{mailToCreator}] <> [{FinalEmailType.No}] ",
mailToCreator, FinalEmailType.No);
}
if (mailToReceivers != (int)FinalEmailType.No)
{
_logger.LogDebug("Sending emails to receivers...");
await SendFinalEmailToReceiversAsync(request, cancel); // , pAttachment
}
else
{
_logger.LogWarning("No SendFinalEmailToReceiversAsync - mailToReceivers [{mailToReceivers}] <> [{FinalEmailType.No}] ",
mailToReceivers, FinalEmailType.No);
}
return docResult;
}
private async Task SendFinalEmailToCreatorAsync(BurnPdfCommand request, CancellationToken cancel) //, string pAttachment
{
bool oIncludeAttachment = SendFinalEmailWithAttachment((int)request.Envelope!.FinalEmailToCreator!);
// string oAttachment = string.Empty;
_logger.LogDebug("Attachment included: [{oIncludeAttachment}]", oIncludeAttachment);
if (oIncludeAttachment)
{
// oAttachment = pAttachment;
}
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = request.Envelope!.Id,
Status = EnvelopeStatus.MessageCompletionSent,
UserReference = request.Envelope.User.Email,
}, cancel);
}
private async Task SendFinalEmailToReceiversAsync(BurnPdfCommand request, CancellationToken cancel) //, string pAttachment
{
bool oIncludeAttachment = SendFinalEmailWithAttachment((int)request.Envelope!.FinalEmailToReceivers!);
// string oAttachment = string.Empty;
_logger.LogDebug("Attachment included: [{oIncludeAttachment}]", oIncludeAttachment);
if (oIncludeAttachment)
{
// oAttachment = pAttachment;
}
// TODO update CreateHistoryCommand to be able to create all records together
foreach (var receiver in request.Envelope.EnvelopeReceivers!)
{
if (receiver.Receiver?.EmailAddress != null)
{
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = request.Envelope.Id,
Status = EnvelopeStatus.MessageCompletionSent,
UserReference = receiver.Receiver.EmailAddress,
}, cancel);
}
}
}
private static bool SendFinalEmailWithAttachment(int type) => type == (int)FinalEmailType.YesWithAttachment;
}

View File

@@ -0,0 +1,64 @@
#if WINDOWS
using EnvelopeGenerator.Application.Configs;
using MediatR;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class WritePdfBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
private readonly ISender _sender;
private readonly ILogger<WritePdfBehavior> _logger;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="logger"></param>
public WritePdfBehavior(ISender sender, ILogger<WritePdfBehavior> logger)
{
_sender = sender;
_logger = logger;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var docResult = await next(cancel);
var config = await _sender.ReadDefaultConfigAsync(cancel);
var exportPath = config.ExportPath ?? throw new InvalidOperationException(nameof(WritePdfBehavior) + " is not possible."
+ "No export path found in config table.");
var dirPath = Path.Combine(exportPath, request.Envelope!.Uuid);
_logger.LogDebug("dirPath is {dirPath}", dirPath);
if (!Directory.Exists(dirPath))
{
_logger.LogDebug("Directory not existing. Creating ...");
Directory.CreateDirectory(dirPath);
}
var outputFilePath = Path.Combine(dirPath, $"{request.Envelope.Uuid}.pdf");
_logger.LogDebug("Writing finalized Pdf to disk..");
_logger.LogInformation("Output path is {outputFilePath}", outputFilePath);
await File.WriteAllBytesAsync(outputFilePath, docResult, cancel);
return docResult;
}
}
#endif

View File

@@ -24,6 +24,10 @@ public record BurnPdfCommand(int? EnvelopeId = null, string? EnvelopeUuid = null
internal bool Debug { get; set; } internal bool Debug { get; set; }
internal Envelope? Envelope { get; set; } internal Envelope? Envelope { get; set; }
#if WINDOWS
internal byte[]? Report { get; set; }
#endif
} }
/// <summary> /// <summary>
@@ -106,6 +110,8 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
request.Envelope = await envQuery request.Envelope = await envQuery
.Include(env => env.Documents!).ThenInclude(doc => doc.Elements!).ThenInclude(element => element.Annotations) .Include(env => env.Documents!).ThenInclude(doc => doc.Elements!).ThenInclude(element => element.Annotations)
.Include(env => env.User)
.Include(env => env.EnvelopeReceivers!).ThenInclude(envRcv => envRcv.Receiver)
.FirstOrDefaultAsync(cancel) .FirstOrDefaultAsync(cancel)
?? throw new BadRequestException($"Envelope could not be found. Request details:\n" + ?? throw new BadRequestException($"Envelope could not be found. Request details:\n" +
request.ToJson(Format.Json.ForDiagnostics)); request.ToJson(Format.Json.ForDiagnostics));

View File

@@ -69,6 +69,29 @@ Namespace Jobs
Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Value.DATABASE) Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Value.DATABASE)
Database = New MSSQLServer(LogConfig, MSSQLServer.DecryptConnectionString(oConnectionString)) Database = New MSSQLServer(LogConfig, MSSQLServer.DecryptConnectionString(oConnectionString))
'#Disable Warning BC40000 ' Type or member is obsolete
' Factory.Shared _
' .BehaveOnPostBuild(PostBuildBehavior.Ignore) _
' .AddEGInfrastructureServices(
' Sub(opt)
' opt.AddDbTriggerParams(
' Sub(triggers)
' triggers("Envelope") = New List(Of String) From {"TBSIG_ENVELOPE_AFT_INS"}
' triggers("History") = New List(Of String) From {"TBSIG_ENVELOPE_HISTORY_AFT_INS"}
' triggers("EmailOut") = New List(Of String) From {"TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD"}
' triggers("EnvelopeReceiverReadOnly") = New List(Of String) From {"TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD"}
' triggers("Receiver") = New List(Of String)() ' no tigger
' triggers("EmailTemplate") = New List(Of String) From {"TBSIG_EMAIL_TEMPLATE_AFT_UPD"}
' End Sub)
' opt.AddDbContext(
' Sub(options)
' options.UseSqlServer(oConnectionString) _
' .EnableSensitiveDataLogging() _
' .EnableDetailedErrors()
' End Sub)
' End Sub)
'#Enable Warning BC40000 ' Type or member is obsolete
Logger.Debug("Loading Models & Services") Logger.Debug("Loading Models & Services")
Dim oState = GetState() Dim oState = GetState()
InitializeModels(oState) InitializeModels(oState)

View File

@@ -55,7 +55,7 @@ Public Class PDFMerger
End If End If
' Convert to PDF/A ' Convert to PDF/A
oMergedPDF.ConvertToPDFA(oFinalStream, PDFAConformanceLevel, ALLOW_VECTORIZATION, ALLOW_RASTERIZATION) oMergedPDF.SaveToStream(oFinalStream)
oStatus = oDocumentPDF.GetStat() oStatus = oDocumentPDF.GetStat()
If oStatus <> GdPictureStatus.OK Then If oStatus <> GdPictureStatus.OK Then
Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}") Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}")

View File

@@ -73,7 +73,7 @@ public static class DependencyInjection
public EGConfiguration AddServices(IConfiguration config, bool usePdfBurner = false) public EGConfiguration AddServices(IConfiguration config, bool usePdfBurner = false)
{ {
#pragma warning disable CS0618 #pragma warning disable CS0618
_serviceRegs.Enqueue(s => s.AddEnvelopeGeneratorServices(config, usePdfBurner)); _serviceRegs.Enqueue(s => s.AddEnvelopeGeneratorServices(config));
#pragma warning restore CS0618 #pragma warning restore CS0618
_addingStatus[nameof(AddServices)] = true; _addingStatus[nameof(AddServices)] = true;
return this; return this;

View File

@@ -1,13 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-EnvelopeGenerator.Finalizer-6d5cc618-4159-4ff2-b600-8a15fbfa8099</UserSecretsId> <UserSecretsId>dotnet-EnvelopeGenerator.Finalizer-6d5cc618-4159-4ff2-b600-8a15fbfa8099</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!--<PackageReference Include="DevExpress.Reporting.Core" Version="25.1.6" />
<PackageReference Include="DevExpress.Win.Navigation" Version="25.1.6" />-->
<PackageReference Include="Quartz.AspNetCore" Version="3.15.1" /> <PackageReference Include="Quartz.AspNetCore" Version="3.15.1" />
<PackageReference Include="Quartzmon" Version="1.0.5" /> <PackageReference Include="Quartzmon" Version="1.0.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />

View File

@@ -1,7 +1,7 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Error",
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
}, },

View File

@@ -1,8 +1,11 @@
{ {
"FinishEnvelopeJob": { "FinishEnvelopeJob": {
"CronExpression": "* * * * * ?" "CronExpression": "0 0/1 * 1/1 * ? *"
}, },
"EnvelopeTaskApiJob": { "EnvelopeTaskApiJob": {
"CronExpression": "* * * * * ?" "CronExpression": "0 0/1 * 1/1 * ? *"
},
"Expressions": {
"PerSec": "* * * * * ?"
} }
} }

View File

@@ -81,6 +81,8 @@ public abstract class EGDbContextBase : DbContext
public DbSet<ThirdPartyModule> ThirdPartyModules { get; set; } public DbSet<ThirdPartyModule> ThirdPartyModules { get; set; }
public DbSet<EnvelopeReport> EnvelopeReports { get; set; }
private readonly DbTriggerParams _triggers; private readonly DbTriggerParams _triggers;
private readonly ILogger private readonly ILogger

View File

@@ -41,10 +41,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dependenc
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Finalizer", "EnvelopeGenerator.Finalizer\EnvelopeGenerator.Finalizer.csproj", "{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Finalizer", "EnvelopeGenerator.Finalizer\EnvelopeGenerator.Finalizer.csproj", "{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}"
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Application.VB", "EnvelopeGenerator.Application.VB\EnvelopeGenerator.Application.VB.vbproj", "{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Application.Abstractions", "EnvelopeGenerator.Application.Abstractions\EnvelopeGenerator.Application.Abstractions.csproj", "{AC34E68D-B660-4E65-9875-4EB8ACAC43AE}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -107,14 +103,6 @@ Global
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.Build.0 = Release|Any CPU {C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.Build.0 = Release|Any CPU
{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6}.Release|Any CPU.Build.0 = Release|Any CPU
{AC34E68D-B660-4E65-9875-4EB8ACAC43AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC34E68D-B660-4E65-9875-4EB8ACAC43AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC34E68D-B660-4E65-9875-4EB8ACAC43AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC34E68D-B660-4E65-9875-4EB8ACAC43AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -137,8 +125,6 @@ Global
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} {211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
{B97DE7DD-3190-4A84-85E9-E57AD735BE61} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {B97DE7DD-3190-4A84-85E9-E57AD735BE61} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {C4970E6C-DB2E-48C5-B3C5-2AF589405ED9} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{9E123E6B-2A94-47C7-8BA1-ECDA5E1422C6} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
{AC34E68D-B660-4E65-9875-4EB8ACAC43AE} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7} SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}