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 0000000..08a7062 Binary files /dev/null and b/StockHistory/Stockbrokerage.db differ 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