diff --git a/StockHistory/HandleCsvNotes.cs b/StockHistory/HandleCsvNotes.cs index bd120b6..3d6c841 100644 --- a/StockHistory/HandleCsvNotes.cs +++ b/StockHistory/HandleCsvNotes.cs @@ -1,4 +1,5 @@ using StockHistory.Models; +using System.Globalization; namespace StockHistory; @@ -19,18 +20,26 @@ public class HandleCsvNotes : IHandleCsvNotes var test = values[NamePos(Header_table, "Typ av transaktion")]; if (test == "Köp" || test == "Sälj") { - var transactionNote = new TransactionNote + try { - TransactionDate = DateTime.Parse(values[NamePos(Header_table, "Datum")]), - 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")])), - Commission = decimal.Parse((values[NamePos(Header_table, "Courtage")] == "") ? "0" : values[NamePos(Header_table, "Courtage")]), - TotalAmount = Math.Abs(decimal.Parse((values[NamePos(Header_table, "Belopp")] == "") ? "0" : values[NamePos(Header_table, "Belopp")])) - decimal.Parse((values[NamePos(Header_table, "Courtage")] == "") ? "0" : values[NamePos(Header_table, "Courtage")]), - AmountToPay = Math.Abs(decimal.Parse((values[NamePos(Header_table, "Belopp")] == "") ? "0" : values[NamePos(Header_table, "Belopp")])) - }; - transactionNotes.Add(transactionNote); + var transactionNote = new TransactionNote + { + TransactionDate = DateTime.Parse(values[NamePos(Header_table, "Datum")]), + 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(ParseCsvNumber(values[NamePos(Header_table, "Antal")])), + Commission = decimal.Parse((values[NamePos(Header_table, "Courtage")] == "") ? "0" : "0" + values[NamePos(Header_table, "Courtage")]), + TotalAmount = Math.Abs(decimal.Parse((values[NamePos(Header_table, "Belopp")] == "") ? "0" : values[NamePos(Header_table, "Belopp")])) - decimal.Parse((values[NamePos(Header_table, "Courtage")] == "") ? "0" : values[NamePos(Header_table, "Courtage")]), + AmountToPay = Math.Abs(decimal.Parse((values[NamePos(Header_table, "Belopp")] == "") ? "0" : values[NamePos(Header_table, "Belopp")])) + }; + transactionNotes.Add(transactionNote); + } + catch (Exception ex) + { + MessageBox.Show($"Warning: '{ex.Message}'"); + // throw; + } } } return transactionNotes; @@ -45,8 +54,41 @@ public class HandleCsvNotes : IHandleCsvNotes } else { - Console.WriteLine($"Warning: Unable to find '{value}'"); + MessageBox.Show($"Warning: Unable to find '{value}'"); return 0; } } + + public static int ParseCsvNumber(string input) + { + if (string.IsNullOrWhiteSpace(input)) + throw new ArgumentException("Input is empty"); + + // Försök först som int (snabbast) + if (int.TryParse(input, out int intValue)) + return intValue; + + // Försök som decimal (t.ex. "123.45" eller "123,45") + if (decimal.TryParse( + input, + NumberStyles.Any, + CultureInfo.InvariantCulture, + out decimal decValue)) + { + return (int)Math.Round(decValue, MidpointRounding.AwayFromZero); + } + + // Försök med svensk kultur (CSV från Excel brukar använda komma) + if (decimal.TryParse( + input, + NumberStyles.Any, + new CultureInfo("sv-SE"), + out decValue)) + { + return (int)Math.Round(decValue, MidpointRounding.AwayFromZero); + } + + throw new FormatException($"Kan inte tolka talet: '{input}'"); + } + } \ No newline at end of file diff --git a/StockHistory/Models/TransactionNote.cs b/StockHistory/Models/TransactionNote.cs index 57716b6..0aea10c 100644 --- a/StockHistory/Models/TransactionNote.cs +++ b/StockHistory/Models/TransactionNote.cs @@ -7,6 +7,8 @@ public class TransactionNote public string? TransactionType { get; set; } public string? StockCode { get; set; } public decimal StockPrice { get; set; } + public decimal ExchangeCours { get; set; } + public string Valuta { get; set; } public int StockQuantity { get; set; } public decimal TotalAmount { get; set; } public decimal Commission { get; set; } @@ -14,7 +16,8 @@ public class TransactionNote public override string ToString() { - return $"TransactionDate: {TransactionDate},\r\nTransactionType: {TransactionType},\r\nStockCode: {StockCode},\r\nStockPrice: {StockPrice},\r\nStockQuantity: {StockQuantity},\r\nTotalAmount: {TotalAmount},\r\nCommission: {Commission},\r\nAmountToPay: {AmountToPay}\r\n"; + //return $"TransactionDate: {TransactionDate},\r\nTransactionType: {TransactionType},\r\nStockCode: {StockCode},\r\nStockPrice: {StockPrice},\r\nStockQuantity: {StockQuantity},\r\nTotalAmount: {TotalAmount},\r\nCommission: {Commission},\r\nAmountToPay: {AmountToPay}\r\n"; + return $" {TransactionDate} | {TransactionType} | {StockCode} | {StockPrice} | {StockQuantity} | {TotalAmount} | {Commission} | {AmountToPay}"; } } diff --git a/StockHistory/Services/IStockEquityServices.cs b/StockHistory/Services/IStockEquityServices.cs index 6328a00..030f86e 100644 --- a/StockHistory/Services/IStockEquityServices.cs +++ b/StockHistory/Services/IStockEquityServices.cs @@ -4,7 +4,7 @@ namespace StockHistory.Services { public interface IStockEquityServices { - Task GenerateStockVision(); + Task> GenerateStockVision(); Task> GetAllAsync(); Task> GetAllAsync(DateTime equityDate); Task> GetByCodeAsync(string stockCode); diff --git a/StockHistory/Services/StockEquityServices.cs b/StockHistory/Services/StockEquityServices.cs index fec0622..56336eb 100644 --- a/StockHistory/Services/StockEquityServices.cs +++ b/StockHistory/Services/StockEquityServices.cs @@ -42,8 +42,11 @@ public class StockEquityServices : IStockEquityServices /// utgående från alla befintliga notor skapa en lista med aktieinnehav. /// /// - public async Task GenerateStockVision() + public async Task> GenerateStockVision() { + var oldStock = "OldStock"; + var nyAktie = false; + List equityWarningList = new List(); await _dbContext.Stocks.ExecuteDeleteAsync(); await _dbContext.SaveChangesAsync(); @@ -58,6 +61,16 @@ public class StockEquityServices : IStockEquityServices { // Kontrollera om TransactionType är null och StockCode inte är null // (om det är fallet, hoppa över denna iteration) + Debug.WriteLine(note.ToString()); + + nyAktie = false; + if (note.StockCode != null && + note.StockCode != oldStock) + { + oldStock = note.StockCode; + nyAktie = true; + } + if (note.TransactionType == null && note.StockCode != null) { } else @@ -81,58 +94,87 @@ 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}"); + // 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.Stocks - .Where(s => s.StockCode == note.StockCode && s.SoldQuant == 0) - .OrderBy(s => s.Quantity).ToListAsync(); - - if (equityList != null) + if (nyAktie) { - var soldQuant = Math.Abs(note.StockQuantity); - foreach (var eq in equityList.OrderBy(e => e.Quantity)) + Debug.WriteLine("<--" + note.ToString()); + + if (note.StockCode != null) { - if (soldQuant == 0) + equityWarningList.Add(new StockEquity { - break; - } - else + //StockCode = note.StockCode, + //Bought = DateTime.MinValue, + //BoughtPrice = 0, + //Quantity = 0, + //Sold = note.TransactionDate, + //SoldPrice = note.StockPrice, + //SoldQuant = note.StockQuantity + StockCode = note.StockCode, + Bought = note.TransactionDate, + BoughtPrice = note.StockPrice * -1, + Quantity = note.StockQuantity, + Sold = DateTime.MinValue, + SoldPrice = 0m, + SoldQuant = 0 + }); + } + ; + } + else + { + var equityList = await _dbContext.Stocks + .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 (eq.Quantity <= soldQuant) + if (soldQuant == 0) { - eq.Sold = note.TransactionDate; - eq.SoldPrice = note.StockPrice; - eq.SoldQuant = eq.Quantity; - soldQuant -= eq.Quantity; - _dbContext.Stocks.Update(eq); - await _dbContext.SaveChangesAsync(); - Debug.WriteLine($"Säljnotering: {eq.StockCode} | {eq.Sold} | {eq.SoldPrice} | {eq.SoldQuant}"); + break; } else { - var splitEquity = new StockEquity + if (eq.Quantity <= soldQuant) { - StockCode = eq.StockCode, - Bought = eq.Bought, - BoughtPrice = eq.BoughtPrice, - Quantity = soldQuant, - Sold = note.TransactionDate, - SoldPrice = note.StockPrice, - SoldQuant = soldQuant - }; - _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; + eq.Sold = note.TransactionDate; + eq.SoldPrice = note.StockPrice; + eq.SoldQuant = eq.Quantity; + soldQuant -= eq.Quantity; + _dbContext.Stocks.Update(eq); + await _dbContext.SaveChangesAsync(); + //Debug.WriteLine($"Säljnotering: {eq.StockCode} | {eq.Sold} | {eq.SoldPrice} | {eq.SoldQuant}"); + } + else + { + var splitEquity = new StockEquity + { + StockCode = eq.StockCode, + Bought = eq.Bought, + BoughtPrice = eq.BoughtPrice, + Quantity = soldQuant, + Sold = note.TransactionDate, + SoldPrice = note.StockPrice, + SoldQuant = soldQuant + }; + _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; + } } } } @@ -140,5 +182,6 @@ public class StockEquityServices : IStockEquityServices } } } + return equityWarningList; } } diff --git a/StockHistory/Services/TransactionNotesServices.cs b/StockHistory/Services/TransactionNotesServices.cs index 83f9bc8..3db480e 100644 --- a/StockHistory/Services/TransactionNotesServices.cs +++ b/StockHistory/Services/TransactionNotesServices.cs @@ -20,7 +20,10 @@ public class TransactionNotesServices : ITransactionNotesServices public async Task> GetAllAsync() { - return await _dbContext.TransactionNotes.ToListAsync(); + return await _dbContext.TransactionNotes + .OrderBy(t => t.StockCode) + .ThenBy(t => t.TransactionDate) + .ToListAsync(); } public async Task GetByIdAsync(int id) diff --git a/StockHistory/StockHistory.csproj b/StockHistory/StockHistory.csproj index 6328792..7c37198 100644 --- a/StockHistory/StockHistory.csproj +++ b/StockHistory/StockHistory.csproj @@ -35,9 +35,4 @@ Resources.Designer.cs - - - PreserveNewest - - \ No newline at end of file diff --git a/StockHistory/Stockbrokerage.db b/StockHistory/Stockbrokerage.db deleted file mode 100644 index f4a51e0..0000000 Binary files a/StockHistory/Stockbrokerage.db and /dev/null differ diff --git a/StockHistory/frmStockHistoryAnalyse.Designer.cs b/StockHistory/frmStockHistoryAnalyse.Designer.cs index 06d4abd..a01cef7 100644 --- a/StockHistory/frmStockHistoryAnalyse.Designer.cs +++ b/StockHistory/frmStockHistoryAnalyse.Designer.cs @@ -47,6 +47,7 @@ histTotBelopp = new ColumnHeader(); histType = new ColumnHeader(); btnStockHistory = new Button(); + lblVarning = new Label(); SuspendLayout(); // // lblHistAnalyseHeader @@ -201,11 +202,21 @@ btnStockHistory.UseVisualStyleBackColor = false; btnStockHistory.Click += btnStockHistory_Click; // + // lblVarning + // + lblVarning.AutoSize = true; + lblVarning.Location = new Point(30, 313); + lblVarning.Name = "lblVarning"; + lblVarning.Size = new Size(47, 15); + lblVarning.TabIndex = 13; + lblVarning.Text = "OBS-->"; + // // frmStockHistoryAnalyse // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; ClientSize = new Size(818, 520); + Controls.Add(lblVarning); Controls.Add(btnStockHistory); Controls.Add(lwStockHistory); Controls.Add(lblTotalValue); @@ -241,5 +252,6 @@ private ColumnHeader histPris; private ColumnHeader histTotBelopp; private ColumnHeader histType; + private Label lblVarning; } } \ No newline at end of file diff --git a/StockHistory/frmStockHistoryAnalyse.cs b/StockHistory/frmStockHistoryAnalyse.cs index 33a38d7..8d4bb85 100644 --- a/StockHistory/frmStockHistoryAnalyse.cs +++ b/StockHistory/frmStockHistoryAnalyse.cs @@ -16,11 +16,14 @@ public partial class frmStockHistoryAnalyse : Form } - private void btnGenerateStockScheme_Click(object sender, EventArgs e) + private async void btnGenerateStockScheme_Click(object sender, EventArgs e) { - _equityServices.GenerateStockVision().Wait(); + var warningList = await _equityServices.GenerateStockVision(); dtpChosenDate.Value = DateTime.Today; LoadStockListAsync(dtpChosenDate.Value).Wait(); + if (warningList != null) + LoadHistoryRecsWarning(warningList); + } private void btnClose_Click(object sender, EventArgs e) @@ -116,6 +119,8 @@ public partial class frmStockHistoryAnalyse : Form var result = _equityServices.GetByCodeAsync(stockName); if (result != null) { + lblVarning.Visible = false; + lwStockHistory.Items.Clear(); foreach (var histRec in await result) { @@ -132,5 +137,26 @@ public partial class frmStockHistoryAnalyse : Form } lwStockHistory.Refresh(); } + private void LoadHistoryRecsWarning(List warningList) //, DateTime equityDate) + { + if (warningList != null) + { + lblVarning.Visible = true; + lwStockHistory.Items.Clear(); + foreach (var histRec in warningList) + { + var l_item = new ListViewItem(histRec.StockCode); + l_item.SubItems.Add(histRec.Bought.ToString()); + l_item.SubItems.Add(histRec.Quantity.ToString()); + l_item.SubItems.Add(histRec.BoughtPrice.ToString()); + Debug.WriteLine(histRec.ToString()); + var totval = (histRec.SoldQuant > 0) ? histRec.SoldQuant * histRec.SoldPrice - histRec.BoughtPrice * histRec.Quantity : histRec.BoughtPrice * histRec.Quantity; + l_item.SubItems.Add((totval).ToString("C")); + l_item.SubItems.Add((histRec.SoldQuant > 0) ? "B" : "K"); + lwStockHistory.Items.Add(l_item); + } + } + lwStockHistory.Refresh(); + } } diff --git a/StockHistory/frmStockHistoryInit.cs b/StockHistory/frmStockHistoryInit.cs index 0694899..2fec8a6 100644 --- a/StockHistory/frmStockHistoryInit.cs +++ b/StockHistory/frmStockHistoryInit.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using StockHistory.Models; using StockHistory.Services; using System.ComponentModel; +using System.Diagnostics; namespace StockHistory; @@ -301,6 +302,7 @@ public partial class frmStockHistoryInit : Form item.SubItems.Add(t.Commission.ToString()); item.SubItems.Add(t.AmountToPay.ToString()); lwTransactions.Items.Add(item); + Debug.WriteLine(t.ToString()); } }