From 1e8098996cbd4fe8c07cccd72efbfd6fe27098aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommy=20=C3=96man?= Date: Sat, 25 Apr 2026 12:19:04 +0200 Subject: [PATCH] Add project files. --- StockHistory.sln | 25 ++ StockHistory/Data/AppDbContext.cs | 25 ++ StockHistory/Data/AppDbContextFactory.cs | 18 ++ StockHistory/IPdfFormatter.cs | 7 + StockHistory/IPdfOpener.cs | 8 + .../20260422153418_InitialCreate.Designer.cs | 60 +++++ .../20260422153418_InitialCreate.cs | 42 ++++ .../20260424072448_AddUniqueIndex.Designer.cs | 63 +++++ .../20260424072448_AddUniqueIndex.cs | 28 +++ .../Migrations/AppDbContextModelSnapshot.cs | 60 +++++ StockHistory/Models/TransactionNote.cs | 22 ++ StockHistory/PdfFormatter.cs | 99 ++++++++ StockHistory/PdfOpener.cs | 35 +++ StockHistory/PdfSida.cs | 14 ++ StockHistory/Program.cs | 47 ++++ StockHistory/Properties/Resources.Designer.cs | 63 +++++ StockHistory/Properties/Resources.resx | 120 ++++++++++ .../Services/ITransactionNotesServices.cs | 13 ++ .../Services/TransactionNotesServices.cs | 61 +++++ StockHistory/StockHistory.csproj | 34 +++ StockHistory/Stockbrokerage.db | Bin 0 -> 28672 bytes StockHistory/frmStockHistoryInit.Designer.cs | 209 +++++++++++++++++ StockHistory/frmStockHistoryInit.cs | 218 ++++++++++++++++++ StockHistory/frmStockHistoryInit.resx | 126 ++++++++++ 24 files changed, 1397 insertions(+) create mode 100644 StockHistory.sln create mode 100644 StockHistory/Data/AppDbContext.cs create mode 100644 StockHistory/Data/AppDbContextFactory.cs create mode 100644 StockHistory/IPdfFormatter.cs create mode 100644 StockHistory/IPdfOpener.cs create mode 100644 StockHistory/Migrations/20260422153418_InitialCreate.Designer.cs create mode 100644 StockHistory/Migrations/20260422153418_InitialCreate.cs create mode 100644 StockHistory/Migrations/20260424072448_AddUniqueIndex.Designer.cs create mode 100644 StockHistory/Migrations/20260424072448_AddUniqueIndex.cs create mode 100644 StockHistory/Migrations/AppDbContextModelSnapshot.cs create mode 100644 StockHistory/Models/TransactionNote.cs create mode 100644 StockHistory/PdfFormatter.cs create mode 100644 StockHistory/PdfOpener.cs create mode 100644 StockHistory/PdfSida.cs create mode 100644 StockHistory/Program.cs create mode 100644 StockHistory/Properties/Resources.Designer.cs create mode 100644 StockHistory/Properties/Resources.resx create mode 100644 StockHistory/Services/ITransactionNotesServices.cs create mode 100644 StockHistory/Services/TransactionNotesServices.cs create mode 100644 StockHistory/StockHistory.csproj create mode 100644 StockHistory/Stockbrokerage.db create mode 100644 StockHistory/frmStockHistoryInit.Designer.cs create mode 100644 StockHistory/frmStockHistoryInit.cs create mode 100644 StockHistory/frmStockHistoryInit.resx diff --git a/StockHistory.sln b/StockHistory.sln new file mode 100644 index 0000000..d8b1bcc --- /dev/null +++ b/StockHistory.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.37216.2 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StockHistory", "StockHistory\StockHistory.csproj", "{ECAA07D8-A9B5-4661-A3DE-AFC2CC02AE32}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECAA07D8-A9B5-4661-A3DE-AFC2CC02AE32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECAA07D8-A9B5-4661-A3DE-AFC2CC02AE32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECAA07D8-A9B5-4661-A3DE-AFC2CC02AE32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECAA07D8-A9B5-4661-A3DE-AFC2CC02AE32}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BF6C1E75-5F37-4AB9-A1DB-5A9CBA141626} + EndGlobalSection +EndGlobal diff --git a/StockHistory/Data/AppDbContext.cs b/StockHistory/Data/AppDbContext.cs new file mode 100644 index 0000000..f6a60f4 --- /dev/null +++ b/StockHistory/Data/AppDbContext.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using StockHistory.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace StockHistory.Data; + +public class AppDbContext: DbContext +{ + public AppDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet TransactionNotes { get; set; } + + override protected void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity() + .HasIndex(t => new { t.StockCode, t.TransactionDate }) + .IsUnique(); + } +} diff --git a/StockHistory/Data/AppDbContextFactory.cs b/StockHistory/Data/AppDbContextFactory.cs new file mode 100644 index 0000000..8675ee8 --- /dev/null +++ b/StockHistory/Data/AppDbContextFactory.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace StockHistory.Data +{ + public class AppDbContextFactory : IDesignTimeDbContextFactory + { + public AppDbContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + // Samma connection string som i Program.cs + optionsBuilder.UseSqlite("Data Source=Stockbrokerage.db"); + + return new AppDbContext(optionsBuilder.Options); + } + } +} diff --git a/StockHistory/IPdfFormatter.cs b/StockHistory/IPdfFormatter.cs new file mode 100644 index 0000000..b9810ad --- /dev/null +++ b/StockHistory/IPdfFormatter.cs @@ -0,0 +1,7 @@ +namespace StockHistory +{ + public interface IPdfFormatter + { + string ExtractFormattedTextUsingWords(List sidLista); + } +} \ No newline at end of file diff --git a/StockHistory/IPdfOpener.cs b/StockHistory/IPdfOpener.cs new file mode 100644 index 0000000..3585861 --- /dev/null +++ b/StockHistory/IPdfOpener.cs @@ -0,0 +1,8 @@ +namespace StockHistory; + +public interface IPdfOpener +{ + List PdfSidor { get; } + + void OpenPdf(string filePath); +} \ No newline at end of file diff --git a/StockHistory/Migrations/20260422153418_InitialCreate.Designer.cs b/StockHistory/Migrations/20260422153418_InitialCreate.Designer.cs new file mode 100644 index 0000000..8f0d662 --- /dev/null +++ b/StockHistory/Migrations/20260422153418_InitialCreate.Designer.cs @@ -0,0 +1,60 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using StockHistory.Data; + +#nullable disable + +namespace StockHistory.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260422153418_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.6"); + + modelBuilder.Entity("StockHistory.Models.TransactionNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AmountToPay") + .HasColumnType("TEXT"); + + b.Property("Commission") + .HasColumnType("TEXT"); + + b.Property("StockCode") + .HasColumnType("TEXT"); + + b.Property("StockPrice") + .HasColumnType("TEXT"); + + b.Property("StockQuantity") + .HasColumnType("INTEGER"); + + b.Property("TotalAmount") + .HasColumnType("TEXT"); + + b.Property("TransactionDate") + .HasColumnType("TEXT"); + + b.Property("TransactionType") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TransactionNotes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/StockHistory/Migrations/20260422153418_InitialCreate.cs b/StockHistory/Migrations/20260422153418_InitialCreate.cs new file mode 100644 index 0000000..8ba8167 --- /dev/null +++ b/StockHistory/Migrations/20260422153418_InitialCreate.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace StockHistory.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TransactionNotes", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TransactionDate = table.Column(type: "TEXT", nullable: false), + TransactionType = table.Column(type: "TEXT", nullable: true), + StockCode = table.Column(type: "TEXT", nullable: true), + StockPrice = table.Column(type: "TEXT", nullable: false), + StockQuantity = table.Column(type: "INTEGER", nullable: false), + TotalAmount = table.Column(type: "TEXT", nullable: false), + Commission = table.Column(type: "TEXT", nullable: false), + AmountToPay = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TransactionNotes", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TransactionNotes"); + } + } +} diff --git a/StockHistory/Migrations/20260424072448_AddUniqueIndex.Designer.cs b/StockHistory/Migrations/20260424072448_AddUniqueIndex.Designer.cs new file mode 100644 index 0000000..371263a --- /dev/null +++ b/StockHistory/Migrations/20260424072448_AddUniqueIndex.Designer.cs @@ -0,0 +1,63 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using StockHistory.Data; + +#nullable disable + +namespace StockHistory.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260424072448_AddUniqueIndex")] + partial class AddUniqueIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.6"); + + modelBuilder.Entity("StockHistory.Models.TransactionNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AmountToPay") + .HasColumnType("TEXT"); + + b.Property("Commission") + .HasColumnType("TEXT"); + + b.Property("StockCode") + .HasColumnType("TEXT"); + + b.Property("StockPrice") + .HasColumnType("TEXT"); + + b.Property("StockQuantity") + .HasColumnType("INTEGER"); + + b.Property("TotalAmount") + .HasColumnType("TEXT"); + + b.Property("TransactionDate") + .HasColumnType("TEXT"); + + b.Property("TransactionType") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StockCode", "TransactionDate") + .IsUnique(); + + b.ToTable("TransactionNotes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/StockHistory/Migrations/20260424072448_AddUniqueIndex.cs b/StockHistory/Migrations/20260424072448_AddUniqueIndex.cs new file mode 100644 index 0000000..bcd3f7a --- /dev/null +++ b/StockHistory/Migrations/20260424072448_AddUniqueIndex.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace StockHistory.Migrations +{ + /// + public partial class AddUniqueIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_TransactionNotes_StockCode_TransactionDate", + table: "TransactionNotes", + columns: new[] { "StockCode", "TransactionDate" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_TransactionNotes_StockCode_TransactionDate", + table: "TransactionNotes"); + } + } +} diff --git a/StockHistory/Migrations/AppDbContextModelSnapshot.cs b/StockHistory/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..a6e662d --- /dev/null +++ b/StockHistory/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,60 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using StockHistory.Data; + +#nullable disable + +namespace StockHistory.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.6"); + + modelBuilder.Entity("StockHistory.Models.TransactionNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AmountToPay") + .HasColumnType("TEXT"); + + b.Property("Commission") + .HasColumnType("TEXT"); + + b.Property("StockCode") + .HasColumnType("TEXT"); + + b.Property("StockPrice") + .HasColumnType("TEXT"); + + b.Property("StockQuantity") + .HasColumnType("INTEGER"); + + b.Property("TotalAmount") + .HasColumnType("TEXT"); + + b.Property("TransactionDate") + .HasColumnType("TEXT"); + + b.Property("TransactionType") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StockCode", "TransactionDate") + .IsUnique(); + + b.ToTable("TransactionNotes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/StockHistory/Models/TransactionNote.cs b/StockHistory/Models/TransactionNote.cs new file mode 100644 index 0000000..c5d3906 --- /dev/null +++ b/StockHistory/Models/TransactionNote.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace StockHistory.Models; + +public class TransactionNote +{ + public int Id { get; set; } + public DateTime TransactionDate { get; set; } + public string? TransactionType { get; set; } + public string? StockCode { get; set; } + public decimal StockPrice { get; set; } + public int StockQuantity { get; set; } + public decimal TotalAmount { get; set; } + public decimal Commission { get; set; } + public decimal AmountToPay { get; set; } + + +} + + diff --git a/StockHistory/PdfFormatter.cs b/StockHistory/PdfFormatter.cs new file mode 100644 index 0000000..cdfac45 --- /dev/null +++ b/StockHistory/PdfFormatter.cs @@ -0,0 +1,99 @@ +using UglyToad.PdfPig; +using UglyToad.PdfPig.Content; +using System.Text; +using System.Linq; +using System.Collections.Generic; + + +namespace StockHistory; + + +public class PdfFormatter : IPdfFormatter +{ + public string ExtractFormattedTextUsingWords(List sidLista) + { + var sb = new StringBuilder(); + + //using (var doc = PdfDocument.Open(path)) + { + foreach (var page in sidLista) + { + var words = (page.FoundWords ?? Enumerable.Empty()) + .OrderByDescending(w => w.BoundingBox.Top) + .ThenBy(w => w.BoundingBox.Left) + .ToList(); + + var lines = GroupWordsIntoLines(words); + + foreach (var line in lines) + { + var columns = GroupWordsIntoColumns(line); + + foreach (var col in columns) + { + sb.Append(string.Join(" ", col.Select(w => w.Text))); + sb.Append(" "); // mellanrum mellan kolumner + } + + sb.AppendLine(); + } + + sb.AppendLine(); + } + } + + return sb.ToString(); + } + + private List> GroupWordsIntoLines(List words) + { + var lines = new List>(); + double threshold = 4; // tolerans för radavstÃ¥nd + + foreach (var word in words) + { + var line = lines.FirstOrDefault(l => + Math.Abs(l[0].BoundingBox.Top - word.BoundingBox.Top) < threshold); + + if (line == null) + { + lines.Add(new List { word }); + } + else + { + line.Add(word); + } + } + + // Sortera ord inom varje rad + foreach (var line in lines) + { + line.Sort((a, b) => a.BoundingBox.Left.CompareTo(b.BoundingBox.Left)); + } + + return lines; + } + + private List> GroupWordsIntoColumns(List line) + { + var columns = new List>(); + double threshold = 25; // tolerans för kolumnavstÃ¥nd + + foreach (var word in line) + { + var col = columns.FirstOrDefault(c => + Math.Abs(c[0].BoundingBox.Left - word.BoundingBox.Left) < threshold); + + if (col == null) + { + columns.Add(new List { word }); + } + else + { + col.Add(word); + } + } + + return columns; + } +} diff --git a/StockHistory/PdfOpener.cs b/StockHistory/PdfOpener.cs new file mode 100644 index 0000000..b2f2dfc --- /dev/null +++ b/StockHistory/PdfOpener.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UglyToad.PdfPig; +using UglyToad.PdfPig.Content; + +namespace StockHistory; + +public class PdfOpener : IPdfOpener +{ + public List PdfSidor { get; set; } = new(); + + + public void OpenPdf(string filePath) + { + PdfSidor.Clear(); + + using (PdfDocument document = PdfDocument.Open(filePath)) + { + foreach (Page page in document.GetPages()) + { + var pdfsida = new PdfSida(); + pdfsida.FoundLetters = page.Letters; + string example = string.Join(string.Empty, pdfsida.FoundLetters.Select(x => x.Value)); + + pdfsida.FoundWords = page.GetWords(); + + pdfsida.FoundImages = page.GetImages(); + PdfSidor.Add(pdfsida); + } + + } + } + +} diff --git a/StockHistory/PdfSida.cs b/StockHistory/PdfSida.cs new file mode 100644 index 0000000..32abb0b --- /dev/null +++ b/StockHistory/PdfSida.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UglyToad.PdfPig; +using UglyToad.PdfPig.Content; + +namespace StockHistory; + +public class PdfSida +{ + public IEnumerable? FoundImages { get; set; } + public IReadOnlyList? FoundLetters { get; set; } + public IEnumerable? FoundWords { get; set; } +} diff --git a/StockHistory/Program.cs b/StockHistory/Program.cs new file mode 100644 index 0000000..29c331b --- /dev/null +++ b/StockHistory/Program.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using StockHistory.Data; +using StockHistory.Services; + +namespace StockHistory +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + var host = CreateHostBuilder().Build(); + + using (var scope = host.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.Migrate(); // skapar databasen om den saknas + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + // Starta huvudformuläret via DI + var mainForm = host.Services.GetRequiredService (); + Application.Run(mainForm); + } + static IHostBuilder CreateHostBuilder() => + Host.CreateDefaultBuilder() + .ConfigureServices((context, services) => + { + services.AddDbContext(options => + options.UseSqlite("Data Source=Stockbrokerage.db"), + ServiceLifetime.Transient); + + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + }); + + } +} \ No newline at end of file diff --git a/StockHistory/Properties/Resources.Designer.cs b/StockHistory/Properties/Resources.Designer.cs new file mode 100644 index 0000000..fdff7b8 --- /dev/null +++ b/StockHistory/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace StockHistory.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("StockHistory.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/StockHistory/Properties/Resources.resx b/StockHistory/Properties/Resources.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StockHistory/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StockHistory/Services/ITransactionNotesServices.cs b/StockHistory/Services/ITransactionNotesServices.cs new file mode 100644 index 0000000..ffcc73a --- /dev/null +++ b/StockHistory/Services/ITransactionNotesServices.cs @@ -0,0 +1,13 @@ +using StockHistory.Models; + +namespace StockHistory.Services +{ + public interface ITransactionNotesServices + { + Task AddAsync(TransactionNote transactionNote); + Task DeleteAsync(int id); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task UpdateAsync(TransactionNote transactionNote); + } +} \ No newline at end of file diff --git a/StockHistory/Services/TransactionNotesServices.cs b/StockHistory/Services/TransactionNotesServices.cs new file mode 100644 index 0000000..7c31706 --- /dev/null +++ b/StockHistory/Services/TransactionNotesServices.cs @@ -0,0 +1,61 @@ +using StockHistory.Data; +using StockHistory.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace StockHistory.Services; + +public class TransactionNotesServices : ITransactionNotesServices +{ + private readonly AppDbContext _dbContext; + private readonly IServiceProvider _provider; + + + // Parameterless constructor + public TransactionNotesServices(AppDbContext dbContext, IServiceProvider provider) + { + _dbContext = dbContext; + _provider = provider; + } + + public async Task> GetAllAsync() + { + return await _dbContext.TransactionNotes.ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _dbContext.TransactionNotes.FindAsync(id); + } + + public async Task AddAsync(TransactionNote transactionNote) + { + using var scope = _provider.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + db.TransactionNotes.Add(transactionNote); + await db.SaveChangesAsync(); + } + + public async Task UpdateAsync(TransactionNote transactionNote) + { + _dbContext.TransactionNotes.Update(transactionNote); + await _dbContext.SaveChangesAsync(); + } + + public async Task DeleteAsync(int id) + { + var transactionNote = await _dbContext.TransactionNotes.FindAsync(id); + if (transactionNote != null) + { + _dbContext.TransactionNotes.Remove(transactionNote); + await _dbContext.SaveChangesAsync(); + } + } + +} diff --git a/StockHistory/StockHistory.csproj b/StockHistory/StockHistory.csproj new file mode 100644 index 0000000..8766b0e --- /dev/null +++ b/StockHistory/StockHistory.csproj @@ -0,0 +1,34 @@ + + + + WinExe + net9.0-windows + enable + true + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + \ No newline at end of file diff --git a/StockHistory/Stockbrokerage.db b/StockHistory/Stockbrokerage.db new file mode 100644 index 0000000000000000000000000000000000000000..08a70628ccca58b391973e69efc5bb7234cabb5e GIT binary patch literal 28672 zcmeI)&2HmF00(f}=_b2jTd&kzMY}7+U_ffsI+{3cz625`HN|S`q^X12!^-j|W@XXX z+r*K!+*+hQ0x!ay3*x{laN@!No`SKPbYpK?Ew>i^N44XR`TWf!(zqiZ?6AjuY6HW7{k&v4d!v1%7sL!>7O%e>hndRWAEMgA+erSy}n};lV?m zoghE}0uX=z1Rwwb2teRY37lc26d*-%p=rr?s@SuUa}u#a;8Ap}WaauK!qD$`xdpzxX81 zGhLhMzN2@E*|vz;YqdzTZ5~)%ja?^FXLsH??gEK)y2hT? zJtDjM5z%^9+b~&qd%9_DPP3h>Pb@+#{g7ooEnq<`>-4oNO=Gbt%woJ{zUE$Mj1P3PR^t8wL|UoYf$eQ8O40z44e0!D}L1rnZ8P!^%lCbveI& zb|~Zu)hd79j=Rpb^{@B5S0g7H(&KNuaTttF7hcRHJ8@wgHWsJkS9wNVny5p%Kd;GX zu2W|;ol!8D^uur6(SpXarWu)R;YF3_;xFrT>^hS$h|leX<=CZ#m!moG{phFaOyhHf zQi(s87ihKES3j9Nx+2`<6h(N8^ghG=-2xH=er98NLw6iH!`CTo#&XRQ*%UpVnkwnKmb1Rwwb z2tWV=5P$##AOHafK;RY%e4N?gZ+sFUe3a22Ok*s32yprT{|iq1O}w~;4Mde7009U< z00Izz00bZa0SG_<0{>3T(=9^Tdi^c7NPqX&pE{bht5hmQ zx>lkUwuqnBi(9qmF#4?X)8tQApI#;}$x5A+lm@NNC0DCOC9kMeB}z>dwN@&|>;HN2 zACCPYKmY;|fB*y_009U<00Izz00ba#=LH_~8JVAZ*=78GO!nL%TL1q$C;r8D2oQh( v1Rwwb2tWV=5P$##AOHaf+-`yAORHr*S|kv^@h`D_KzPRPJ-E7;SR3#!4OZ!$ literal 0 HcmV?d00001 diff --git a/StockHistory/frmStockHistoryInit.Designer.cs b/StockHistory/frmStockHistoryInit.Designer.cs new file mode 100644 index 0000000..2b5252a --- /dev/null +++ b/StockHistory/frmStockHistoryInit.Designer.cs @@ -0,0 +1,209 @@ +namespace StockHistory +{ + partial class frmStockHistoryInit + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + btnChooseFile = new Button(); + ofdFiler = new OpenFileDialog(); + lblFileName = new Label(); + btnAnalysera = new Button(); + txtFound = new TextBox(); + btnSaveToDb = new Button(); + lwTransactions = new ListView(); + StockCodeHeader = new ColumnHeader(); + TransactionTypeHeader = new ColumnHeader(); + TransactionDateHeader = new ColumnHeader(); + StockQuantityHeader = new ColumnHeader(); + StockPriceHeader = new ColumnHeader(); + TotalAmountHeader = new ColumnHeader(); + CommissionHeader = new ColumnHeader(); + AmountToPayHeader = new ColumnHeader(); + btnClose = new Button(); + SuspendLayout(); + // + // btnChooseFile + // + btnChooseFile.BackColor = Color.FromArgb(128, 255, 128); + btnChooseFile.FlatStyle = FlatStyle.Flat; + btnChooseFile.Font = new Font("Segoe UI", 13F); + btnChooseFile.Location = new Point(26, 27); + btnChooseFile.Name = "btnChooseFile"; + btnChooseFile.Size = new Size(143, 37); + btnChooseFile.TabIndex = 0; + btnChooseFile.Text = "Välj pdf-fil"; + btnChooseFile.UseVisualStyleBackColor = false; + btnChooseFile.Click += btnChooseFile_Click; + // + // lblFileName + // + lblFileName.AutoSize = true; + lblFileName.Location = new Point(175, 38); + lblFileName.Name = "lblFileName"; + lblFileName.Size = new Size(39, 20); + lblFileName.TabIndex = 1; + lblFileName.Text = "_____"; + lblFileName.TextChanged += lblFileName_TextChanged; + // + // btnAnalysera + // + btnAnalysera.BackColor = Color.FromArgb(128, 255, 128); + btnAnalysera.FlatStyle = FlatStyle.Flat; + btnAnalysera.Font = new Font("Segoe UI", 13F); + btnAnalysera.Location = new Point(26, 86); + btnAnalysera.Name = "btnAnalysera"; + btnAnalysera.Size = new Size(143, 37); + btnAnalysera.TabIndex = 2; + btnAnalysera.Text = "Analysera"; + btnAnalysera.UseVisualStyleBackColor = false; + btnAnalysera.Click += btnAnalysera_Click; + // + // txtFound + // + txtFound.Location = new Point(26, 149); + txtFound.Multiline = true; + txtFound.Name = "txtFound"; + txtFound.Size = new Size(468, 334); + txtFound.TabIndex = 3; + // + // btnSaveToDb + // + btnSaveToDb.BackColor = Color.FromArgb(128, 255, 128); + btnSaveToDb.FlatStyle = FlatStyle.Flat; + btnSaveToDb.Font = new Font("Segoe UI", 13F); + btnSaveToDb.Location = new Point(197, 86); + btnSaveToDb.Name = "btnSaveToDb"; + btnSaveToDb.Size = new Size(143, 37); + btnSaveToDb.TabIndex = 4; + btnSaveToDb.Text = "Spara till DB"; + btnSaveToDb.UseVisualStyleBackColor = false; + btnSaveToDb.Click += btnSaveToDb_Click; + // + // lwTransactions + // + lwTransactions.BackColor = Color.WhiteSmoke; + lwTransactions.Columns.AddRange(new ColumnHeader[] { StockCodeHeader, TransactionTypeHeader, TransactionDateHeader, StockQuantityHeader, StockPriceHeader, TotalAmountHeader, CommissionHeader, AmountToPayHeader }); + lwTransactions.ForeColor = SystemColors.InactiveCaptionText; + lwTransactions.Location = new Point(515, 27); + lwTransactions.Name = "lwTransactions"; + lwTransactions.Size = new Size(857, 456); + lwTransactions.Sorting = SortOrder.Descending; + lwTransactions.TabIndex = 6; + lwTransactions.UseCompatibleStateImageBehavior = false; + lwTransactions.View = View.Details; + // + // StockCodeHeader + // + StockCodeHeader.Text = "StockCode"; + StockCodeHeader.Width = 110; + // + // TransactionTypeHeader + // + TransactionTypeHeader.Text = "TransactionType"; + TransactionTypeHeader.Width = 100; + // + // TransactionDateHeader + // + TransactionDateHeader.Text = "TransactionDate"; + TransactionDateHeader.Width = 120; + // + // StockQuantityHeader + // + StockQuantityHeader.Text = "StockQuantity"; + StockQuantityHeader.Width = 100; + // + // StockPriceHeader + // + StockPriceHeader.Text = "StockPrice"; + StockPriceHeader.Width = 100; + // + // TotalAmountHeader + // + TotalAmountHeader.Text = "TotalAmount"; + TotalAmountHeader.Width = 100; + // + // CommissionHeader + // + CommissionHeader.Text = "Commission"; + // + // AmountToPayHeader + // + AmountToPayHeader.Text = "AmountToPay"; + AmountToPayHeader.Width = 110; + // + // btnClose + // + btnClose.BackColor = Color.Chartreuse; + btnClose.FlatStyle = FlatStyle.Popup; + btnClose.Font = new Font("Segoe UI", 13F, FontStyle.Regular, GraphicsUnit.Point, 1, true); + btnClose.Location = new Point(1248, 503); + btnClose.Name = "btnClose"; + btnClose.Size = new Size(124, 38); + btnClose.TabIndex = 7; + btnClose.Text = "Stäng"; + btnClose.UseVisualStyleBackColor = false; + btnClose.Click += btnClose_Click; + // + // frmStockHistoryInit + // + AutoScaleDimensions = new SizeF(8F, 20F); + AutoScaleMode = AutoScaleMode.Font; + BackColor = Color.LightGray; + ClientSize = new Size(1413, 553); + Controls.Add(btnClose); + Controls.Add(lwTransactions); + Controls.Add(btnSaveToDb); + Controls.Add(txtFound); + Controls.Add(btnAnalysera); + Controls.Add(lblFileName); + Controls.Add(btnChooseFile); + Name = "frmStockHistoryInit"; + Text = "StockBrokerage"; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private Button btnChooseFile; + private OpenFileDialog ofdFiler; + private Label lblFileName; + private Button btnAnalysera; + private TextBox txtFound; + private Button btnSaveToDb; + private ListView lwTransactions; + private ColumnHeader StockCodeHeader; + private ColumnHeader TransactionTypeHeader; + private ColumnHeader TransactionDateHeader; + private ColumnHeader StockQuantityHeader; + private ColumnHeader StockPriceHeader; + private ColumnHeader TotalAmountHeader; + private ColumnHeader CommissionHeader; + private ColumnHeader AmountToPayHeader; + private Button btnClose; + } +} \ No newline at end of file diff --git a/StockHistory/frmStockHistoryInit.cs b/StockHistory/frmStockHistoryInit.cs new file mode 100644 index 0000000..08ab0db --- /dev/null +++ b/StockHistory/frmStockHistoryInit.cs @@ -0,0 +1,218 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using StockHistory.Models; +using StockHistory.Services; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Text; +using System.Windows.Forms; + +namespace StockHistory +{ + public partial class frmStockHistoryInit : Form + { + private readonly IPdfOpener _pdfOpener; + private readonly IPdfFormatter _pdfFormatter; + private readonly ITransactionNotesServices _transactionNotes; + private List _pdfSidor = new(); + + public frmStockHistoryInit( + IPdfOpener pdfOpener, + IPdfFormatter pdfFormatter, + ITransactionNotesServices transactionNotes + ) + { + InitializeComponent(); + _pdfOpener = pdfOpener; + _pdfFormatter = pdfFormatter; + _transactionNotes = transactionNotes; + btnAnalysera.Enabled = false; + Shown += async (s, e) => await LoadBusinessAsync(); + } + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public List Selected { get; set; } = new(); + + private void btnChooseFile_Click(object sender, EventArgs e) + { + ofdFiler.Title = "Välj en PDF-fil"; + ofdFiler.FileName = ""; + ofdFiler.Filter = "PDF-filer (*.pdf)|*.pdf|Alla filer (*.*)|*.*"; + var result = ofdFiler.ShowDialog(); + + if (result == DialogResult.OK) + { + lblFileName.Text = ofdFiler.FileName; + } + } + + private void lblFileName_TextChanged(object sender, EventArgs e) + { + if (lblFileName.Text.Length > 5 && lblFileName.Text.EndsWith(".pdf")) + { + _pdfOpener.OpenPdf(lblFileName.Text); + _pdfSidor = _pdfOpener.PdfSidor; + btnAnalysera.Enabled = true; + } + else + { + btnAnalysera.Enabled = false; + } + } + + private void btnAnalysera_Click(object sender, EventArgs e) + { + string PdfResult = _pdfFormatter.ExtractFormattedTextUsingWords(_pdfSidor); + txtFound.Text = string.Empty; + List result = PdfResult.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(); + //List selected = new(); + + foreach (var line in result) + { + if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("Affärsdag") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Handelstidpunkt") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Värdepapper") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Kurs") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Genomsnittlig") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Antal") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Köpeskilling") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Courtage") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Belopp") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Vi bekräftar") + ) + { + Selected.Add(line); + txtFound.Text += line + Environment.NewLine; + txtFound.Refresh(); + + } + } + btnAnalysera.Enabled = false; + lblFileName.Text = string.Empty; + } + + private async void btnSaveToDb_Click(object sender, EventArgs e) + { + TransactionNote trans = new(); + + foreach (var line in Selected) + { + if (line.StartsWith("Affärsdag")) + { + trans.TransactionDate = DateTime.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); + } + if (line.StartsWith("Värdepapper")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 2) + trans.StockCode = templist[1] + " " + templist[2]; + else + trans.StockCode = templist[1]; + } + if (line.StartsWith("Kurs")) + { + trans.StockPrice = decimal.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]); + } + if (line.StartsWith("Antal")) + { + trans.StockQuantity = int.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); + } + if (line.StartsWith("Köpeskilling")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 3) + trans.TotalAmount = decimal.Parse(templist[2] + templist[3]); + else + trans.TotalAmount = decimal.Parse(templist[2]); + } + if (line.StartsWith("Courtage")) + { + var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + trans.Commission = decimal.Parse(tmpList[tmpList.Length - 1]); + } + if (line.StartsWith("Belopp")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 5) + trans.AmountToPay = decimal.Parse(templist[4] + templist[5]); + else + trans.AmountToPay = decimal.Parse(templist[4]); + } + if (line.StartsWith("Vi bekräftar")) + { + foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries)) + { + if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ")) + { + trans.TransactionType = word; + break; + } + } + } + if (line.StartsWith("Genomsnittlig")) + { + var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + trans.StockPrice = decimal.Parse(tmpList[tmpList.Length - 1]); + } + if (line.StartsWith("Handelstidpunkt")) + { + var time = line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]; + if (time.Contains(".")) + { + var hours = int.Parse(time.Split(".")[0]); + var minutes = int.Parse(time.Split(".")[1]); + trans.TransactionDate = trans.TransactionDate.AddHours(hours).AddMinutes(minutes); + } + } + } + try + { + await _transactionNotes.AddAsync(trans); + txtFound.Text = string.Empty; + } + catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlite && sqlite.SqliteErrorCode == 19) + { + MessageBox.Show("Den här transaktionen finns redan."); + } + catch (Exception ex) + { + MessageBox.Show("Fel vid sparande till databas: " + ex.Message); + } + + Selected.Clear(); + + await LoadBusinessAsync(); + + } + + private async Task LoadBusinessAsync() + { + + var transactions = await _transactionNotes.GetAllAsync(); + + lwTransactions.Items.Clear(); + foreach (var t in transactions) + { + var item = new ListViewItem(t.StockCode); + item.SubItems.Add(t.TransactionType); + item.SubItems.Add(t.TransactionDate.ToString()); + item.SubItems.Add(t.StockQuantity.ToString()); + item.SubItems.Add(t.StockPrice.ToString()); + item.SubItems.Add(t.TotalAmount.ToString()); + item.SubItems.Add(t.Commission.ToString()); + item.SubItems.Add(t.AmountToPay.ToString()); + lwTransactions.Items.Add(item); + } + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + } +} \ No newline at end of file diff --git a/StockHistory/frmStockHistoryInit.resx b/StockHistory/frmStockHistoryInit.resx new file mode 100644 index 0000000..9fe8ebe --- /dev/null +++ b/StockHistory/frmStockHistoryInit.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 25 + + \ No newline at end of file