Lag till entity SglSotckEntity som är kopia av Stockentity. förbättrsr utinrn

This commit is contained in:
2026-07-26 18:58:30 +02:00
parent 9eb33afd25
commit c72078db67
12 changed files with 446 additions and 9 deletions

View File

@ -12,6 +12,8 @@ public class AppDbContext : DbContext
public DbSet<TransactionNote> TransactionNotes { get; set; }
public DbSet<StockEquity> Stocks { get; set; }
public DbSet<SglStockEquity> SglStocks { get; set; }
override protected void OnModelCreating(ModelBuilder modelBuilder)
{
@ -28,5 +30,14 @@ public class AppDbContext : DbContext
modelBuilder.Entity<StockEquity>()
.HasIndex(s => new { s.Bought, s.StockCode, s.Quantity })
.IsUnique(false);
modelBuilder.Entity<SglStockEquity>()
.HasIndex(s => new { s.Bought })
.IsUnique(false);
modelBuilder.Entity<SglStockEquity>()
.HasIndex(s => new { s.Sold })
.IsUnique(false);
modelBuilder.Entity<SglStockEquity>()
.HasIndex(s => new { s.Bought, s.StockCode, s.Quantity })
.IsUnique(false);
}
}

View File

@ -22,7 +22,7 @@ public class HandleCsvNotes : IHandleCsvNotes
var transactionNote = new TransactionNote
{
TransactionDate = DateTime.Parse(values[NamePos(Header_table, "Datum")]),
TransactionType = values[NamePos(Header_table, "Typ av transaktion")],
TransactionType = values[NamePos(Header_table, "Typ av transaktion")] == "Köp" ? "KÖP" : "SÄLJ",
StockCode = values[NamePos(Header_table, @"Värdepapper/beskrivning")],
StockPrice = decimal.Parse((values[NamePos(Header_table, "Kurs")] == "") ? "0" : values[NamePos(Header_table, "Kurs")]),
StockQuantity = Math.Abs(int.Parse(values[NamePos(Header_table, "Antal")])),

View File

@ -0,0 +1,141 @@
// <auto-generated />
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("20260725154136_tillaggExtraTemptabell")]
partial class tillaggExtraTemptabell
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
modelBuilder.Entity("StockHistory.Models.SglStockEquity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Bought")
.HasColumnType("TEXT");
b.Property<decimal>("BoughtPrice")
.HasColumnType("TEXT");
b.Property<int>("Quantity")
.HasColumnType("INTEGER");
b.Property<DateTime>("Sold")
.HasColumnType("TEXT");
b.Property<decimal>("SoldPrice")
.HasColumnType("TEXT");
b.Property<int>("SoldQuant")
.HasColumnType("INTEGER");
b.Property<string>("StockCode")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Bought");
b.HasIndex("Sold");
b.HasIndex("Bought", "StockCode", "Quantity");
b.ToTable("SglStocks");
});
modelBuilder.Entity("StockHistory.Models.StockEquity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Bought")
.HasColumnType("TEXT");
b.Property<decimal>("BoughtPrice")
.HasColumnType("TEXT");
b.Property<int>("Quantity")
.HasColumnType("INTEGER");
b.Property<DateTime>("Sold")
.HasColumnType("TEXT");
b.Property<decimal>("SoldPrice")
.HasColumnType("TEXT");
b.Property<int>("SoldQuant")
.HasColumnType("INTEGER");
b.Property<string>("StockCode")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Bought");
b.HasIndex("Sold");
b.HasIndex("Bought", "StockCode", "Quantity");
b.ToTable("Stocks");
});
modelBuilder.Entity("StockHistory.Models.TransactionNote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<decimal>("AmountToPay")
.HasColumnType("TEXT");
b.Property<decimal>("Commission")
.HasColumnType("TEXT");
b.Property<string>("StockCode")
.HasColumnType("TEXT");
b.Property<decimal>("StockPrice")
.HasColumnType("TEXT");
b.Property<int>("StockQuantity")
.HasColumnType("INTEGER");
b.Property<decimal>("TotalAmount")
.HasColumnType("TEXT");
b.Property<DateTime>("TransactionDate")
.HasColumnType("TEXT");
b.Property<string>("TransactionType")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("StockCode", "TransactionDate")
.IsUnique();
b.ToTable("TransactionNotes");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace StockHistory.Migrations
{
/// <inheritdoc />
public partial class tillaggExtraTemptabell : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SglStocks",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
StockCode = table.Column<string>(type: "TEXT", nullable: false),
Bought = table.Column<DateTime>(type: "TEXT", nullable: false),
BoughtPrice = table.Column<decimal>(type: "TEXT", nullable: false),
Quantity = table.Column<int>(type: "INTEGER", nullable: false),
Sold = table.Column<DateTime>(type: "TEXT", nullable: false),
SoldPrice = table.Column<decimal>(type: "TEXT", nullable: false),
SoldQuant = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SglStocks", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_SglStocks_Bought",
table: "SglStocks",
column: "Bought");
migrationBuilder.CreateIndex(
name: "IX_SglStocks_Bought_StockCode_Quantity",
table: "SglStocks",
columns: new[] { "Bought", "StockCode", "Quantity" });
migrationBuilder.CreateIndex(
name: "IX_SglStocks_Sold",
table: "SglStocks",
column: "Sold");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SglStocks");
}
}
}

View File

@ -17,6 +17,45 @@ namespace StockHistory.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
modelBuilder.Entity("StockHistory.Models.SglStockEquity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Bought")
.HasColumnType("TEXT");
b.Property<decimal>("BoughtPrice")
.HasColumnType("TEXT");
b.Property<int>("Quantity")
.HasColumnType("INTEGER");
b.Property<DateTime>("Sold")
.HasColumnType("TEXT");
b.Property<decimal>("SoldPrice")
.HasColumnType("TEXT");
b.Property<int>("SoldQuant")
.HasColumnType("INTEGER");
b.Property<string>("StockCode")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Bought");
b.HasIndex("Sold");
b.HasIndex("Bought", "StockCode", "Quantity");
b.ToTable("SglStocks");
});
modelBuilder.Entity("StockHistory.Models.StockEquity", b =>
{
b.Property<int>("Id")

View File

@ -0,0 +1,14 @@
namespace StockHistory.Models
{
public class SglStockEquity
{
public int Id { get; set; }
public string StockCode { get; set; } = string.Empty;
public DateTime Bought { get; set; }
public decimal BoughtPrice { get; set; }
public int Quantity { get; set; }
public DateTime Sold { get; set; }
public decimal SoldPrice { get; set; }
public int SoldQuant { get; set; }
}
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using StockHistory.Data;
using StockHistory.Services;
@ -33,15 +34,22 @@ namespace StockHistory
Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlite("Data Source=Stockbrokerage.db"),
ServiceLifetime.Transient);
services.AddDbContext<AppDbContext>(options => options
.UseSqlite("Data Source=Stockbrokerage.db")
.UseLoggerFactory(LoggerFactory.Create(builder =>
{
builder
.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning)
.AddFilter("Microsoft.EntityFrameworkCore.Query", LogLevel.Warning);
}))
, ServiceLifetime.Transient);
services.AddSingleton<frmStockHistoryInit>();
services.AddTransient<frmStockHistoryAnalyse>();
services.AddTransient<IPdfOpener, PdfOpener>();
services.AddTransient<IPdfFormatter, PdfFormatter>();
services.AddTransient<ITransactionNotesServices, TransactionNotesServices>();
services.AddTransient<ISglStockEquityServices, SglStockEquityServices>();
services.AddTransient<IStockEquityServices, StockEquityServices>();
services.AddTransient<IHandleCsvNotes, HandleCsvNotes>();
});

View File

@ -0,0 +1,12 @@
using StockHistory.Models;
namespace StockHistory.Services
{
public interface ISglStockEquityServices
{
Task GenerateStockVision();
Task<List<SglStockEquity>> GetAllAsync();
Task<List<SglStockEquity>> GetAllAsync(DateTime equityDate);
Task<List<SglStockEquity>> GetByCodeAsync(string stockCode);
}
}

View File

@ -0,0 +1,145 @@
using Microsoft.EntityFrameworkCore;
using StockHistory.Data;
using StockHistory.Models;
using System.Diagnostics;
namespace StockHistory.Services
{
public class SglStockEquityServices : ISglStockEquityServices
{
private readonly AppDbContext _dbContext;
private readonly ITransactionNotesServices _notesServices;
public SglStockEquityServices(AppDbContext dbContext, ITransactionNotesServices notesServices)
{
_dbContext = dbContext;
_notesServices = notesServices;
}
public async Task<List<SglStockEquity>> GetAllAsync()
{
return await _dbContext.SglStocks.ToListAsync();
}
public async Task<List<SglStockEquity>> GetAllAsync(DateTime equityDate)
{
return await _dbContext.SglStocks
.Where(s => s.Bought <= equityDate && (s.Sold > equityDate || s.SoldQuant == 0))
.ToListAsync();
}
public async Task<List<SglStockEquity>> GetByCodeAsync(string stockCode)
{
var searchedNotes = await _notesServices.GetByStockCodeAsync(stockCode);
return await _dbContext.SglStocks
.Where(s => s.StockCode == stockCode)
.OrderBy(s => s.Bought)
.ToListAsync();
}
/// <summary>
/// Skapar en ny lista med aktieinnehav baserat på transaktionsnoteringar.
/// Den rensar först den befintliga listan och bygger sedan upp den igen genom att
/// utgående från alla befintliga notor skapa en lista med aktieinnehav.
/// </summary>
/// <returns></returns>
public async Task GenerateStockVision()
{
await _dbContext.SglStocks.ExecuteDeleteAsync();
await _dbContext.SaveChangesAsync();
var existingNotes = await _notesServices.GetAllAsync();
foreach (var note in existingNotes
.OrderBy(e => e.StockCode)
.ThenBy(e => e.TransactionDate)
//.OrderBy(e => e.TransactionType)
.ToList())
{
// Kontrollera om TransactionType är null och StockCode inte är null
// (om det är fallet, hoppa över denna iteration)
if (note.TransactionType == null && note.StockCode != null) { }
else
// Kontrollera om TransactionType är en köpnotering
// (ignorera null-värden)
if (note.TransactionType.ToLower() == "köp")
{
// Skapa en ny aktieinnehavspost baserat på köpnoteringen
var equity = new SglStockEquity
{
StockCode = note.StockCode,
Bought = note.TransactionDate,
BoughtPrice = note.StockPrice,
Quantity = note.StockQuantity,
Sold = DateTime.MinValue,
SoldPrice = 0m,
SoldQuant = 0
};
_dbContext.SglStocks.Add(equity);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Köpnotering: {equity.StockCode}, {equity.Bought}, {equity.BoughtPrice}, {equity.Quantity}, {equity.BoughtPrice * equity.Quantity}");
}
// Kontrollera om TransactionType är en säljnotering
else if (note.TransactionType.ToLower() == "sälj")
{
var equityList = await _dbContext.SglStocks
.Where(s => s.StockCode == note.StockCode && s.SoldQuant == 0)
.OrderBy(s => s.Quantity).ToListAsync();
if (equityList != null)
{
var soldQuant = Math.Abs(note.StockQuantity);
foreach (var eq in equityList.OrderBy(e => e.Quantity))
{
if (soldQuant == 0)
{
break;
}
else
{
if (eq.Quantity <= soldQuant)
{
eq.Sold = note.TransactionDate;
eq.SoldPrice = note.StockPrice;
eq.SoldQuant = eq.Quantity;
soldQuant -= eq.Quantity;
_dbContext.SglStocks.Update(eq);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering: {eq.StockCode}, {eq.Sold}, {eq.SoldPrice}, {eq.SoldQuant}");
}
else
{
var splitEquity = new SglStockEquity
{
StockCode = eq.StockCode,
Bought = eq.Bought,
BoughtPrice = eq.BoughtPrice,
Quantity = soldQuant,
Sold = note.TransactionDate,
SoldPrice = note.StockPrice,
SoldQuant = soldQuant
};
_dbContext.SglStocks.Add(splitEquity);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering (split): {splitEquity.StockCode}, {splitEquity.Bought},{splitEquity.BoughtPrice},{splitEquity.SoldQuant}, {splitEquity.Sold}, {splitEquity.SoldPrice},{splitEquity.SoldQuant}");
eq.Quantity -= soldQuant;
_dbContext.SglStocks.Update(eq);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering (split): {eq.StockCode}, {eq.Bought},{eq.BoughtPrice},{eq.SoldQuant}, {eq.Sold}, {eq.SoldPrice},{eq.SoldQuant}");
soldQuant = 0;
}
}
}
}
}
}
}
}
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using StockHistory.Data;
using StockHistory.Models;
using System.Diagnostics;
namespace StockHistory.Services;
@ -50,8 +51,9 @@ public class StockEquityServices : IStockEquityServices
var existingNotes = await _notesServices.GetAllAsync();
foreach (var note in existingNotes
.OrderByDescending(e => e.TransactionType)
.OrderBy(e => e.TransactionDate)
.OrderBy(e => e.StockCode)
.ThenBy(e => e.TransactionDate)
//.OrderBy(e => e.TransactionType)
.ToList())
{
// Kontrollera om TransactionType är null och StockCode inte är null
@ -79,11 +81,12 @@ public class StockEquityServices : IStockEquityServices
};
_dbContext.Stocks.Add(equity);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Köpnotering: {equity.StockCode}, {equity.Bought}, {equity.BoughtPrice}, {equity.Quantity}, {equity.BoughtPrice * equity.Quantity}");
}
// Kontrollera om TransactionType är en säljnotering
else if (note.TransactionType.ToLower() == "sälj" || note.TransactionType.ToLower() == "försäljning")
else if (note.TransactionType.ToLower() == "sälj")
{
var equityList = await _dbContext.Stocks
.Where(s => s.StockCode == note.StockCode && s.SoldQuant == 0)
@ -108,6 +111,7 @@ public class StockEquityServices : IStockEquityServices
soldQuant -= eq.Quantity;
_dbContext.Stocks.Update(eq);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering: {eq.StockCode}, {eq.Sold}, {eq.SoldPrice}, {eq.SoldQuant}");
}
else
{
@ -123,9 +127,11 @@ public class StockEquityServices : IStockEquityServices
};
_dbContext.Stocks.Add(splitEquity);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering (split): {splitEquity.StockCode}, {splitEquity.Bought},{splitEquity.BoughtPrice},{splitEquity.SoldQuant}, {splitEquity.Sold}, {splitEquity.SoldPrice},{splitEquity.SoldQuant}");
eq.Quantity -= soldQuant;
_dbContext.Stocks.Update(eq);
await _dbContext.SaveChangesAsync();
Debug.WriteLine($"Säljnotering (split): {eq.StockCode}, {eq.Bought},{eq.BoughtPrice},{eq.SoldQuant}, {eq.Sold}, {eq.SoldPrice},{eq.SoldQuant}");
soldQuant = 0;
}
}

Binary file not shown.

View File

@ -223,9 +223,14 @@ public partial class frmStockHistoryInit : Form
{
foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries))
{
if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ"))
if (word.ToUpper().Contains("KÖP"))
{
trans.TransactionType = word;
trans.TransactionType = "KÖP";
break;
}
if (word.ToUpper().Contains("FÖRSÄLJ"))
{
trans.TransactionType = "SÄLJ";
break;
}
}