diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5fcada --- /dev/null +++ b/.gitignore @@ -0,0 +1,55 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Maven +target/ +dist/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + +.vs +*/bin +*/obj +Drab/.config +Drab/drab.db diff --git a/Drab.Core/Configuration/DrabSettings.cs b/Drab.Core/Configuration/DrabSettings.cs new file mode 100644 index 0000000..5fdd3e0 --- /dev/null +++ b/Drab.Core/Configuration/DrabSettings.cs @@ -0,0 +1,9 @@ +namespace Drab.Core.Configuration; + +public class DrabSettings : IDrabSettings +{ + public const string SectionName = "DrabSettings"; + public int DbPollingFrequencyInSeconds { get; set; } + public int PrinterTimeoutSeconds { get; set; } + public string IgnoreOrdersBefore { get; set; } +} \ No newline at end of file diff --git a/Drab.Core/Configuration/IDrabSettings.cs b/Drab.Core/Configuration/IDrabSettings.cs new file mode 100644 index 0000000..8a42878 --- /dev/null +++ b/Drab.Core/Configuration/IDrabSettings.cs @@ -0,0 +1,8 @@ +namespace Drab.Core.Configuration; + +public interface IDrabSettings +{ + int DbPollingFrequencyInSeconds { get; } + int PrinterTimeoutSeconds { get; } + public string IgnoreOrdersBefore { get; set; } +} \ No newline at end of file diff --git a/Drab.Core/Configuration/LocalDbConfiguration.cs b/Drab.Core/Configuration/LocalDbConfiguration.cs new file mode 100644 index 0000000..cfa3026 --- /dev/null +++ b/Drab.Core/Configuration/LocalDbConfiguration.cs @@ -0,0 +1,9 @@ +using Drab.LocalDb.IoC; + +namespace Drab.Core.Configuration; + +public class LocalDbConfiguration : ILocalDbConfiguration +{ + public const string SectionName = "LocalDbConnection"; + public string ConnectionString { get; set; } +} \ No newline at end of file diff --git a/Drab.Core/Configuration/PcmDbConfiguration.cs b/Drab.Core/Configuration/PcmDbConfiguration.cs new file mode 100644 index 0000000..966b943 --- /dev/null +++ b/Drab.Core/Configuration/PcmDbConfiguration.cs @@ -0,0 +1,13 @@ +using Pcm.Db.Ioc; + +namespace Drab.Core.Configuration; + +public class PcmDbConfiguration : IDbConfiguration +{ + public const string SectionName = "PcmDbSettings"; + public string Host { get; set; } + public string Port { get; set; } + public string User { get; set; } + public string Password { get; set; } + public string Database { get; set; } +} \ No newline at end of file diff --git a/Drab.Core/Drab.Core.csproj b/Drab.Core/Drab.Core.csproj new file mode 100644 index 0000000..8c03505 --- /dev/null +++ b/Drab.Core/Drab.Core.csproj @@ -0,0 +1,17 @@ + + + + net9.0-windows + false + + + + + + + + + + + + diff --git a/Drab.Core/Ioc/IocCoreRegister.cs b/Drab.Core/Ioc/IocCoreRegister.cs new file mode 100644 index 0000000..7b73f85 --- /dev/null +++ b/Drab.Core/Ioc/IocCoreRegister.cs @@ -0,0 +1,13 @@ +using Drab.Core.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Drab.Core.Ioc; + +public static class IocCoreRegister +{ + public static IServiceCollection AddDrabCore(this IServiceCollection services, IDrabSettings settings) + { + services.AddSingleton(settings); + return services; + } +} \ No newline at end of file diff --git a/Drab.Core/Models/Result.cs b/Drab.Core/Models/Result.cs new file mode 100644 index 0000000..4231941 --- /dev/null +++ b/Drab.Core/Models/Result.cs @@ -0,0 +1,82 @@ +using System; + +namespace Drab.Core.Models; + +public class Result +{ + private TBad _error; + private TOk _ok; + public bool IsOk { get; } + public bool IsNotOk => !IsOk; + + public Result(bool isOk, TOk ok, TBad bad) + { + IsOk = isOk; + + if (IsOk) + { + if (ok == null) + throw new ArgumentNullException(nameof(ok), "If IsOk flag is set to true parameter 'ok' needs to be non null"); + + Value = ok; + } + else + { + if (bad == null) + throw new ArgumentNullException(nameof(bad), "If IsOk flag is set to false parameter 'bad' needs to be non null"); + + Error = bad; + } + } + + public TBad Error + { + get + { + if (IsOk) + throw new InvalidOperationException("Result has IsOk flag set to true only Value property is available"); + return _error; + } + private set => _error = value; + } + + public TOk Value + { + get + { + if (!IsOk) + throw new InvalidOperationException("Result has IsOk flag set to false only Error property is available"); + return _ok; + } + private set => _ok = value; + } + + public Result Map(Func map) where TNew : class + { + if (IsOk) + return new Result(IsOk, map(Value), default); + else + return new Result(false, default, Error); + } + + public TOk ValueWithDefault(Func defaultCreator) => IsOk ? Value : defaultCreator(Error); + + public void Do(Action action) + { + if (IsOk) + action(Value); + } +} + +public class Result +{ + public static Result Failed(TK wrong) + { + return new Result(false, default(T), wrong); + } + + public static Result Ok(T ok) + { + return new Result(true, ok, default(TK)); + } +} \ No newline at end of file diff --git a/Drab.LocalDb/Drab.LocalDb.csproj b/Drab.LocalDb/Drab.LocalDb.csproj new file mode 100644 index 0000000..b2b8cb4 --- /dev/null +++ b/Drab.LocalDb/Drab.LocalDb.csproj @@ -0,0 +1,19 @@ + + + + net9.0-windows + false + + + + + + + + + + + + + + diff --git a/Drab.LocalDb/Entities/OrderDb.cs b/Drab.LocalDb/Entities/OrderDb.cs new file mode 100644 index 0000000..6dc9376 --- /dev/null +++ b/Drab.LocalDb/Entities/OrderDb.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Drab.LocalDb.Entities; + +public class OrderDb +{ + [Key] + public long OrderId { get; set; } + public long DokId { get; set; } + public DateTime Created { get; set; } + public bool IsPrinted { get; set; } + public string Filename { get; set; } + public string Shop { get; set; } + public string OrderNumber { get; set; } +} \ No newline at end of file diff --git a/Drab.LocalDb/IoC/ILocalDbConfiguration.cs b/Drab.LocalDb/IoC/ILocalDbConfiguration.cs new file mode 100644 index 0000000..91f6b50 --- /dev/null +++ b/Drab.LocalDb/IoC/ILocalDbConfiguration.cs @@ -0,0 +1,6 @@ +namespace Drab.LocalDb.IoC; + +public interface ILocalDbConfiguration +{ + public string ConnectionString { get; set; } +} \ No newline at end of file diff --git a/Drab.LocalDb/IoC/IocLocalDbRegister.cs b/Drab.LocalDb/IoC/IocLocalDbRegister.cs new file mode 100644 index 0000000..b14e1ad --- /dev/null +++ b/Drab.LocalDb/IoC/IocLocalDbRegister.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Drab.LocalDb.IoC; + +public static class IocLocalDbRegister +{ + public static IServiceCollection AddLocalDatabase(this IServiceCollection services, ILocalDbConfiguration localDbConfiguration) + { + services.AddDbContext(cx => cx.UseSqlite(localDbConfiguration.ConnectionString)); + return services; + } +} \ No newline at end of file diff --git a/Drab.LocalDb/LocalDbContext.cs b/Drab.LocalDb/LocalDbContext.cs new file mode 100644 index 0000000..cab5712 --- /dev/null +++ b/Drab.LocalDb/LocalDbContext.cs @@ -0,0 +1,18 @@ +using Drab.LocalDb.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Drab.LocalDb; + +public class LocalDbContext : DbContext +{ + public LocalDbContext() + { + } + + public LocalDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Orders { get; set; } +} \ No newline at end of file diff --git a/Drab.LocalDb/Migrations/20211101180516_Initial.Designer.cs b/Drab.LocalDb/Migrations/20211101180516_Initial.Designer.cs new file mode 100644 index 0000000..2d54a1d --- /dev/null +++ b/Drab.LocalDb/Migrations/20211101180516_Initial.Designer.cs @@ -0,0 +1,52 @@ +// +using System; +using Drab.LocalDb; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Drab.LocalDb.Migrations +{ + [DbContext(typeof(LocalDbContext))] + [Migration("20211101180516_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("Drab.LocalDb.Entities.OrderDb", b => + { + b.Property("OrderId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("DokId") + .HasColumnType("INTEGER"); + + b.Property("Filename") + .HasColumnType("TEXT"); + + b.Property("IsPrinted") + .HasColumnType("INTEGER"); + + b.Property("OrderNumber") + .HasColumnType("TEXT"); + + b.Property("Shop") + .HasColumnType("TEXT"); + + b.HasKey("OrderId"); + + b.ToTable("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Drab.LocalDb/Migrations/20211101180516_Initial.cs b/Drab.LocalDb/Migrations/20211101180516_Initial.cs new file mode 100644 index 0000000..736a739 --- /dev/null +++ b/Drab.LocalDb/Migrations/20211101180516_Initial.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Drab.LocalDb.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + OrderId = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DokId = table.Column(type: "INTEGER", nullable: false), + Created = table.Column(type: "TEXT", nullable: false), + IsPrinted = table.Column(type: "INTEGER", nullable: false), + Filename = table.Column(type: "TEXT", nullable: true), + Shop = table.Column(type: "TEXT", nullable: true), + OrderNumber = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.OrderId); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Orders"); + } + } +} diff --git a/Drab.LocalDb/Migrations/LocalDbContextModelSnapshot.cs b/Drab.LocalDb/Migrations/LocalDbContextModelSnapshot.cs new file mode 100644 index 0000000..ca5fb17 --- /dev/null +++ b/Drab.LocalDb/Migrations/LocalDbContextModelSnapshot.cs @@ -0,0 +1,50 @@ +// +using System; +using Drab.LocalDb; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Drab.LocalDb.Migrations +{ + [DbContext(typeof(LocalDbContext))] + partial class LocalDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("Drab.LocalDb.Entities.OrderDb", b => + { + b.Property("OrderId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("DokId") + .HasColumnType("INTEGER"); + + b.Property("Filename") + .HasColumnType("TEXT"); + + b.Property("IsPrinted") + .HasColumnType("INTEGER"); + + b.Property("OrderNumber") + .HasColumnType("TEXT"); + + b.Property("Shop") + .HasColumnType("TEXT"); + + b.HasKey("OrderId"); + + b.ToTable("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Drab.Logic/Constants.cs b/Drab.Logic/Constants.cs new file mode 100644 index 0000000..181dcce --- /dev/null +++ b/Drab.Logic/Constants.cs @@ -0,0 +1,11 @@ +using Drab.Logic.Services; +using System.IO; +using System.Reflection; + +namespace Drab.Logic; + +internal static class Constants +{ + internal static readonly string ReportsTemplatesPath = Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(OrderPdfGenerator)).Location), "Templates"); + internal static readonly string ReportsOutputPath = Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(OrderPdfGenerator)).Location), "Orders"); +} \ No newline at end of file diff --git a/Drab.Logic/Drab.Logic.csproj b/Drab.Logic/Drab.Logic.csproj new file mode 100644 index 0000000..848c1b8 --- /dev/null +++ b/Drab.Logic/Drab.Logic.csproj @@ -0,0 +1,42 @@ + + + + net9.0-windows + false + + + + ./System.Printing.dll + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/Drab.Logic/Dtos/DokDto.cs b/Drab.Logic/Dtos/DokDto.cs new file mode 100644 index 0000000..a61b2a0 --- /dev/null +++ b/Drab.Logic/Dtos/DokDto.cs @@ -0,0 +1,32 @@ +using Pcm.Db.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Drab.Logic.Dtos; + +public class DokDto +{ + public long DokId { get; set; } + public string NrDok { get; set; } + public DateTime Data { get; set; } + public string Sklep { get; set; } + public IEnumerable PozDok { get; set; } + public string Opis { get; set; } +} + +internal static class DokExtensions +{ + internal static DokDto ToDokDto(this Dok dok) + { + return new DokDto + { + DokId = (long)dok.DokId, + Data = dok.Zmiana, + NrDok = dok.NrDok, + Sklep = dok.DokKontr.Kontr.Nazwa, + Opis = string.Join(' ', dok.TekstDoks.Select(x => x.Tekst)), + PozDok = dok.PozDoks.Select(x => x.ToPozDokDto()).ToList() + }; + } +} \ No newline at end of file diff --git a/Drab.Logic/Dtos/MagDto.cs b/Drab.Logic/Dtos/MagDto.cs new file mode 100644 index 0000000..9222b99 --- /dev/null +++ b/Drab.Logic/Dtos/MagDto.cs @@ -0,0 +1,23 @@ +using Pcm.Db.Entities; + +namespace Drab.Logic.Dtos; + +public class MagDto +{ + public long MagId { get; set; } + public string Nazwa { get; set; } + public short Numer { get; set; } +} + +internal static class MagazynExtensions +{ + internal static MagDto ToMagDto(this Magazyn magazyn) + { + return new MagDto + { + MagId = (long)magazyn.MagId, + Numer = magazyn.Numer ?? 0, + Nazwa = magazyn.Nazwa + }; + } +} \ No newline at end of file diff --git a/Drab.Logic/Dtos/PozDokDto.cs b/Drab.Logic/Dtos/PozDokDto.cs new file mode 100644 index 0000000..61d1730 --- /dev/null +++ b/Drab.Logic/Dtos/PozDokDto.cs @@ -0,0 +1,26 @@ +using System.Linq; +using Pcm.Db.Entities; + +namespace Drab.Logic.Dtos; + +public class PozDokDto +{ + public long DokId { get; set; } + public TowarDto Towar { get; set; } + public decimal Ilosc { get; set; } + public string Komentarz { get; set; } +} + +internal static class PozDokExtensions +{ + internal static PozDokDto ToPozDokDto(this PozDok pozDok) + { + return new PozDokDto + { + DokId = (long)pozDok.DokId, + Ilosc = pozDok.IloscPlus, + Towar = pozDok.Tow.ToTowarDto(), + Komentarz = string.Join("; ", pozDok.TekstPozs.Select(x => x.Tekst.Trim())) + }; + } +} \ No newline at end of file diff --git a/Drab.Logic/Dtos/TowarDto.cs b/Drab.Logic/Dtos/TowarDto.cs new file mode 100644 index 0000000..e0bea02 --- /dev/null +++ b/Drab.Logic/Dtos/TowarDto.cs @@ -0,0 +1,25 @@ +using Pcm.Db.Entities; + +namespace Drab.Logic.Dtos; + +public class TowarDto +{ + public long TowId { get; set; } + public string Nazwa { get; set; } + public string Kod { get; set; } + public string Jm { get; set; } +} + +internal static class TowarExtension +{ + internal static TowarDto ToTowarDto(this Towar towar) + { + return new TowarDto + { + TowId = (long)towar.TowId, + Nazwa = towar.Nazwa, + Kod = towar.Kod, + Jm = towar.Jm.Nazwa + }; + } +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/IDbFetcher.cs b/Drab.Logic/Interfaces/IDbFetcher.cs new file mode 100644 index 0000000..f817294 --- /dev/null +++ b/Drab.Logic/Interfaces/IDbFetcher.cs @@ -0,0 +1,8 @@ +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface IDbFetcher +{ + Task Start(); +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/ILocalOrderStore.cs b/Drab.Logic/Interfaces/ILocalOrderStore.cs new file mode 100644 index 0000000..96f35dd --- /dev/null +++ b/Drab.Logic/Interfaces/ILocalOrderStore.cs @@ -0,0 +1,12 @@ +using Drab.LocalDb.Entities; +using Drab.Logic.Dtos; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface ILocalOrderStore +{ + public Task> GetAll(); + public Task GetOrderById(long dokId); +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/IOrderPdfGenerator.cs b/Drab.Logic/Interfaces/IOrderPdfGenerator.cs new file mode 100644 index 0000000..8c5e53e --- /dev/null +++ b/Drab.Logic/Interfaces/IOrderPdfGenerator.cs @@ -0,0 +1,9 @@ +using Drab.Logic.Dtos; +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface IOrderPdfGenerator +{ + Task GenerateOrder(DokDto order); +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/IOrderProcessor.cs b/Drab.Logic/Interfaces/IOrderProcessor.cs new file mode 100644 index 0000000..7e948e2 --- /dev/null +++ b/Drab.Logic/Interfaces/IOrderProcessor.cs @@ -0,0 +1,9 @@ +using Drab.Logic.Dtos; +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface IOrderProcessor +{ + Task ProcessOrder(DokDto order); +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/IOrderStore.cs b/Drab.Logic/Interfaces/IOrderStore.cs new file mode 100644 index 0000000..92af7ae --- /dev/null +++ b/Drab.Logic/Interfaces/IOrderStore.cs @@ -0,0 +1,12 @@ +using Drab.Core.Models; +using Drab.Logic.Dtos; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface IOrdersStore : IDisposable +{ + Task, string>> FetchOrders(); +} \ No newline at end of file diff --git a/Drab.Logic/Interfaces/IPrintService.cs b/Drab.Logic/Interfaces/IPrintService.cs new file mode 100644 index 0000000..8c5b044 --- /dev/null +++ b/Drab.Logic/Interfaces/IPrintService.cs @@ -0,0 +1,9 @@ +using Drab.Logic.Models; +using System.Threading.Tasks; + +namespace Drab.Logic.Interfaces; + +public interface IPrintService +{ + Task PrintPdf(PrintDocumentRequest request); +} \ No newline at end of file diff --git a/Drab.Logic/Ioc/IocLogicRegister.cs b/Drab.Logic/Ioc/IocLogicRegister.cs new file mode 100644 index 0000000..7021f62 --- /dev/null +++ b/Drab.Logic/Ioc/IocLogicRegister.cs @@ -0,0 +1,21 @@ +using Drab.Logic.Interfaces; +using Drab.Logic.Services; +using Drab.Logic.Utils; +using Microsoft.Extensions.DependencyInjection; + +namespace Drab.Logic.Ioc; + +public static class IocLogicRegister +{ + public static IServiceCollection AddDrabLogic(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddHostedService(); + services.AddTransient(); + return services; + } +} \ No newline at end of file diff --git a/Drab.Logic/Models/PrintDocument.cs b/Drab.Logic/Models/PrintDocument.cs new file mode 100644 index 0000000..0115232 --- /dev/null +++ b/Drab.Logic/Models/PrintDocument.cs @@ -0,0 +1,7 @@ +using System.Drawing; + +namespace Drab.Logic.Models; + +public record PrintDocumentRequest(string FilePath, long DokId); + +public record PrintDocumentResult(bool IsSuccess, string Message); \ No newline at end of file diff --git a/Drab.Logic/Services/LocalOrderStore.cs b/Drab.Logic/Services/LocalOrderStore.cs new file mode 100644 index 0000000..c25fd41 --- /dev/null +++ b/Drab.Logic/Services/LocalOrderStore.cs @@ -0,0 +1,47 @@ +using System; +using Drab.LocalDb; +using Drab.LocalDb.Entities; +using Drab.Logic.Dtos; +using Drab.Logic.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using NLog; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Drab.Logic.Services; + +public class LocalOrderStore : ILocalOrderStore +{ + + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public LocalOrderStore(IServiceScopeFactory serviceScopeFactory) + { + _logger = LogManager.GetCurrentClassLogger(); + _serviceScopeFactory = serviceScopeFactory; + } + + public async Task> GetAll() + { + using var scope = _serviceScopeFactory.CreateScope(); + await using var dbContext = scope.ServiceProvider.GetService(); + var fromDate = DateTime.UtcNow.AddDays(-30); + + var orders = dbContext.Orders + .Where(x => x.Created >= fromDate) + .OrderByDescending(x => x.Created) + .ToList(); + return orders; + } + + public async Task GetOrderById(long dokId) + { + using var scope = _serviceScopeFactory.CreateScope(); + await using var dbContext = scope.ServiceProvider.GetService(); + var order = dbContext.Orders.FirstOrDefault(x => x.DokId == dokId); + + return new DokDto(); + } +} \ No newline at end of file diff --git a/Drab.Logic/Services/OldOrdersCleanupService.cs b/Drab.Logic/Services/OldOrdersCleanupService.cs new file mode 100644 index 0000000..3266a09 --- /dev/null +++ b/Drab.Logic/Services/OldOrdersCleanupService.cs @@ -0,0 +1,58 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Drab.LocalDb; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Drab.Logic.Services; + +public class OldOrdersCleanupService : BackgroundService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly TimeSpan _interval = TimeSpan.FromHours(24); + + public OldOrdersCleanupService(IServiceProvider services, ILogger logger) + { + _services = services; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + using var scope = _services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var threshold = DateTime.Now.AddDays(-45); + var oldOrders = dbContext.Orders + .Where(o => o.Created < threshold) + .ToList(); + + if (oldOrders.Count != 0) + { + dbContext.Orders.RemoveRange(oldOrders); + await dbContext.SaveChangesAsync(stoppingToken); + + _logger.LogInformation("Usunięto {OldOrdersCount} starych zamówień.", oldOrders.Count); + } + else + { + _logger.LogInformation("Nie znaleziono starych zamówień do usunięcia."); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Błąd podczas usuwania starych zamówień."); + } + + await Task.Delay(_interval, stoppingToken); + } + } +} \ No newline at end of file diff --git a/Drab.Logic/Services/OrderEventBus.cs b/Drab.Logic/Services/OrderEventBus.cs new file mode 100644 index 0000000..d9132f1 --- /dev/null +++ b/Drab.Logic/Services/OrderEventBus.cs @@ -0,0 +1,13 @@ +using System; + +namespace Drab.Logic.Services; + +public class OrderEventBus +{ + public event Action? OrdersChanged; + + public void NotifyOrdersChanged() + { + OrdersChanged?.Invoke(); + } +} \ No newline at end of file diff --git a/Drab.Logic/Services/OrderPdfGenerator.cs b/Drab.Logic/Services/OrderPdfGenerator.cs new file mode 100644 index 0000000..78903e4 --- /dev/null +++ b/Drab.Logic/Services/OrderPdfGenerator.cs @@ -0,0 +1,51 @@ +using Drab.Logic.Dtos; +using Drab.Logic.Interfaces; +using FastReport; +using FastReport.Export.PdfSimple; +using NLog; +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; + +namespace Drab.Logic.Services; + +internal class OrderPdfGenerator : IOrderPdfGenerator +{ + private readonly ILogger _logger; + + public OrderPdfGenerator() + { + _logger = LogManager.GetCurrentClassLogger(); + } + + public Task GenerateOrder(DokDto order) + { + _logger.Info($"Generate pdf document: {order.NrDok}"); + var cleanFilename = order.NrDok.Replace('/', '-'); + var orderFilename = $"Zam_{cleanFilename}_{order.Data:yyyy-MM-dd}_{order.Sklep}_{order.DokId}.pdf"; + var templatePath = Path.Combine(Constants.ReportsTemplatesPath, "Order.frx"); + var outputFile = Path.Combine(Constants.ReportsOutputPath, orderFilename); + var version = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; + var generatedAt = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"; + + var data = new[] { order }; + try + { + var report = new Report(); + report.Load(templatePath); + report.SetParameterValue("generatedAt", generatedAt); + report.SetParameterValue("version", version); + report.RegisterData(data, "Order"); + report.Prepare(); + var pdfExport = new PDFSimpleExport(); + pdfExport.Export(report, outputFile); + return Task.FromResult(orderFilename); + } + catch (Exception e) + { + _logger.Error(e, "Error on generating order document: {OrderNo}", order.NrDok); + return Task.FromResult(string.Empty); + } + } +} \ No newline at end of file diff --git a/Drab.Logic/Services/OrderProcessor.cs b/Drab.Logic/Services/OrderProcessor.cs new file mode 100644 index 0000000..65959e1 --- /dev/null +++ b/Drab.Logic/Services/OrderProcessor.cs @@ -0,0 +1,154 @@ +using Drab.LocalDb; +using Drab.LocalDb.Entities; +using Drab.Logic.Dtos; +using Drab.Logic.Interfaces; +using Drab.Logic.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NLog; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Drab.Logic.Services; + +public class OrderProcessor : IOrderProcessor +{ + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IPrintService _printService; + private readonly IOrderPdfGenerator _orderPdfGenerator; + private readonly OrderEventBus _eventBus; + + public OrderProcessor(IServiceScopeFactory serviceScopeFactory, + IPrintService printService, + IOrderPdfGenerator orderPdfGenerator, + OrderEventBus eventBus) + { + _logger = LogManager.GetCurrentClassLogger(); + _serviceScopeFactory = serviceScopeFactory; + _printService = printService; + _orderPdfGenerator = orderPdfGenerator; + _eventBus = eventBus; + } + + public async Task ProcessOrder(DokDto order) + { + var dok = await GetOrCreateOrder(order); + + if (dok.IsPrinted) + return; + + var filePath = await EnsureOrderPdfExists(order, dok); + + var printResult = await PrintOrder(order, filePath); + + if (printResult.IsSuccess) + { + dok.IsPrinted = true; + await SaveOrderIfChanged(dok); + } + } + + private async Task GetOrCreateOrder(DokDto order) + { + var dok = await GetOrder(order.DokId); + + if (dok != null) + return dok; + + _logger.Info("Creating new order - {OrderNo}", order.NrDok); + + dok = new OrderDb + { + DokId = order.DokId, + Created = order.Data, + OrderId = order.DokId, + Filename = string.Empty, + IsPrinted = false, + Shop = order.Sklep, + OrderNumber = order.NrDok + }; + + await SaveOrderIfChanged(dok); + return dok; + } + + private async Task EnsureOrderPdfExists(DokDto order, OrderDb dok) + { + if (!string.IsNullOrEmpty(dok.Filename)) + { + var fullPath = Path.Combine(Constants.ReportsOutputPath, dok.Filename); + if (File.Exists(fullPath)) + return fullPath; + } + + _logger.Info("Generating order file: {OrderNo}", order.NrDok); + + var generatedFilename = await _orderPdfGenerator.GenerateOrder(order); + var newPath = Path.Combine(Constants.ReportsOutputPath, generatedFilename); + + if (string.Equals(dok.Filename, generatedFilename, StringComparison.OrdinalIgnoreCase)) + return newPath; + dok.Filename = generatedFilename; + await SaveOrderIfChanged(dok); + + return newPath; + } + + private async Task PrintOrder(DokDto order, string filePath) + { + _logger.Info("Printing order: {OrderNo}", order.NrDok); + + var result = await _printService.PrintPdf(new PrintDocumentRequest(filePath, order.DokId)); + + _logger.Info("Printing result - OrderNo: {OrderNo}, Success: {IsSuccess}, Message: {Message}", + order.NrDok, result.IsSuccess, result.Message); + + return result; + } + + private async Task GetOrder(long dokId) + { + try + { + using var scope = _serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + return await context.Orders + .FirstOrDefaultAsync(x => x.DokId == dokId); + } + catch (Exception e) + { + _logger.Error(e, "Error retrieving order from database"); + return null; + } + } + + private async Task SaveOrderIfChanged(OrderDb order) + { + using var scope = _serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + var existing = await context.Orders.FirstOrDefaultAsync(x => x.DokId == order.DokId); + + if (existing == null) + { + await context.Orders.AddAsync(order); + await context.SaveChangesAsync(); + _eventBus.NotifyOrdersChanged(); + return; + } + + if (existing.IsPrinted == order.IsPrinted && existing.Filename == order.Filename) + return; + + existing.IsPrinted = order.IsPrinted; + existing.Filename = order.Filename; + + context.Orders.Update(existing); + await context.SaveChangesAsync(); + + _eventBus.NotifyOrdersChanged(); + } +} diff --git a/Drab.Logic/Services/OrdersStore.cs b/Drab.Logic/Services/OrdersStore.cs new file mode 100644 index 0000000..5284a33 --- /dev/null +++ b/Drab.Logic/Services/OrdersStore.cs @@ -0,0 +1,78 @@ +using Drab.Core.Models; +using Drab.Logic.Dtos; +using Drab.Logic.Interfaces; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NLog; +using Pcm.Db; +using Pcm.Db.Enums; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Drab.Core.Configuration; + +namespace Drab.Logic.Services; + +public class OrdersStore : IOrdersStore, IDisposable +{ + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IDrabSettings _drabSettings; + + public OrdersStore(IServiceScopeFactory serviceScopeFactory, IDrabSettings drabSettings) + { + _serviceScopeFactory = serviceScopeFactory; + _drabSettings = drabSettings; + _logger = LogManager.GetCurrentClassLogger(); + } + + public async Task, string>> FetchOrders() + { + _logger.Info("Fetch new orders"); + + DateTime date; + try + { + date = DateTime.ParseExact(_drabSettings.IgnoreOrdersBefore, "yyyy-MM-dd", CultureInfo.InvariantCulture); + } + catch + { + date = DateTime.Now - TimeSpan.FromDays(30); + } + + using var scope = _serviceScopeFactory.CreateScope(); + await using var dbContext = scope.ServiceProvider.GetService(); + try + { + var orders = await dbContext.Dokumenty + .Where(x => x.Aktywny == 1 + && x.TypDok == (short)TypDok.RejestracjaZamowieniaOdOdbiorcy + && x.Opcja1 == 0 + && x.Data >= date) + .Include(x => x.DokKontr) + .ThenInclude(x => x.Kontr) + .Include(x => x.TekstDoks.Where(y => + y.Znaczenie == (short)TekstDokZnaczenie.InfoDoZamowienia || + y.Znaczenie == (short)TekstDokZnaczenie.TekstDod2)) + .Include(x => x.PozDoks) + .ThenInclude(x => x.TekstPozs.Where(y => y.Znaczenie == 0 || y.Znaczenie == 13)) + .Include(x => x.PozDoks) + .ThenInclude(x => x.Tow) + .ThenInclude(x => x.Jm) + .ToListAsync(); + + return Result.Ok, string>(orders.Select(x => x.ToDokDto()).ToList()); + } + catch (Exception e) + { + _logger.Error(e, "Error on fetching orders"); + return Result.Failed, string>(e.Message); + } + } + + public void Dispose() + { + } +} \ No newline at end of file diff --git a/Drab.Logic/Services/PrintService.cs b/Drab.Logic/Services/PrintService.cs new file mode 100644 index 0000000..145eb20 --- /dev/null +++ b/Drab.Logic/Services/PrintService.cs @@ -0,0 +1,71 @@ +using Drab.Core.Configuration; +using Drab.Logic.Interfaces; +using Drab.Logic.Models; +using PdfiumViewer; +using System; +using System.Collections.Generic; +using System.Drawing.Printing; +using System.IO; +using System.Linq; +using System.Printing; +using System.Threading.Tasks; + +namespace Drab.Logic.Services; + +public class PrintService : IPrintService +{ + private readonly IDrabSettings _drabSettings; + + public PrintService(IDrabSettings drabSettings) + { + _drabSettings = drabSettings; + } + + public async Task PrintPdf(PrintDocumentRequest request) + { + try + { + var oldJobs = GetPrintJobs(request.DokId.ToString()); + if (oldJobs.Any(x => !x.IsRetained)) + { + oldJobs.ForEach(x => x.Cancel()); + await Task.Delay(TimeSpan.FromSeconds(2)); + } + + var (filePath, dokId) = request; + using (var document = PdfDocument.Load(filePath)) + { + using (var printDocument = document.CreatePrintDocument()) + { + var fileName = Path.GetFileName(filePath); + + printDocument.PrinterSettings.PrintFileName = fileName; + printDocument.DocumentName = dokId.ToString(); + printDocument.PrintController = new StandardPrintController(); + printDocument.Print(); + } + } + + await Task.Delay(TimeSpan.FromSeconds(_drabSettings.PrinterTimeoutSeconds)); + + var jobs = GetPrintJobs(request.DokId.ToString()); + if (jobs.Count == 0) + return new PrintDocumentResult(true, "Print success."); + + jobs.ForEach(x => x.Cancel()); + return new PrintDocumentResult(false, "Print failed - timeout expired."); + } + catch (Exception e) + { + return new PrintDocumentResult(false, $"Print failed - {e.Message}"); + } + } + + private static List GetPrintJobs(string jobName) + { + var printQueue = LocalPrintServer.GetDefaultPrintQueue(); + return printQueue.GetPrintJobInfoCollection() + .Where(x => x.Name == jobName) + .ToList(); + } +} \ No newline at end of file diff --git a/Drab.Logic/System.Printing.dll b/Drab.Logic/System.Printing.dll new file mode 100644 index 0000000..d2bc9b7 Binary files /dev/null and b/Drab.Logic/System.Printing.dll differ diff --git a/Drab.Logic/Templates/Order.frx b/Drab.Logic/Templates/Order.frx new file mode 100644 index 0000000..53694e6 --- /dev/null +++ b/Drab.Logic/Templates/Order.frx @@ -0,0 +1,90 @@ + + + +