Summering av aktiebidrag så att endast en post för varje aktie visas. Sortering genom click på rubrik i listviews
This commit is contained in:
62
StockHistory/ListViewExtensions.cs
Normal file
62
StockHistory/ListViewExtensions.cs
Normal file
@ -0,0 +1,62 @@
|
||||
namespace StockHistory;
|
||||
|
||||
public static class ListViewExtensions
|
||||
{
|
||||
public static void EnableSmartSorting(this ListView lv)
|
||||
{
|
||||
lv.ColumnClick += (s, e) =>
|
||||
{
|
||||
if (lv.Tag is not SortState state)
|
||||
{
|
||||
state = new SortState();
|
||||
lv.Tag = state;
|
||||
}
|
||||
|
||||
if (state.Column == e.Column)
|
||||
{
|
||||
state.Order = state.Order == SortOrder.Ascending
|
||||
? SortOrder.Descending
|
||||
: SortOrder.Ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Column = e.Column;
|
||||
state.Order = SortOrder.Ascending;
|
||||
}
|
||||
|
||||
lv.ListViewItemSorter = new SmartListViewComparer(state.Column, state.Order);
|
||||
lv.Sort();
|
||||
|
||||
UpdateHeaderArrows(lv, state.Column, state.Order);
|
||||
HighlightSortedColumn(lv, state.Column);
|
||||
};
|
||||
}
|
||||
|
||||
private static void UpdateHeaderArrows(ListView lv, int col, SortOrder order)
|
||||
{
|
||||
foreach (ColumnHeader ch in lv.Columns)
|
||||
ch.Text = ch.Text.Replace(" ▲", "").Replace(" ▼", "");
|
||||
|
||||
string arrow = order == SortOrder.Ascending ? " ▲" : " ▼";
|
||||
lv.Columns[col].Text += arrow;
|
||||
}
|
||||
|
||||
private static void HighlightSortedColumn(ListView lv, int col)
|
||||
{
|
||||
foreach (ListViewItem item in lv.Items)
|
||||
{
|
||||
for (int i = 0; i < item.SubItems.Count; i++)
|
||||
{
|
||||
item.SubItems[i].BackColor = i == col
|
||||
? Color.FromArgb(235, 245, 255)
|
||||
: Color.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SortState
|
||||
{
|
||||
public int Column = -1;
|
||||
public SortOrder Order = SortOrder.None;
|
||||
}
|
||||
}
|
||||
31
StockHistory/ListViewItemComparer.cs
Normal file
31
StockHistory/ListViewItemComparer.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace StockHistory;
|
||||
|
||||
public class ListViewItemComparer : IComparer
|
||||
{
|
||||
private int col;
|
||||
private SortOrder order;
|
||||
|
||||
public ListViewItemComparer(int column, SortOrder order)
|
||||
{
|
||||
col = column;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
string a = ((ListViewItem)x).SubItems[col].Text;
|
||||
string b = ((ListViewItem)y).SubItems[col].Text;
|
||||
|
||||
int result;
|
||||
|
||||
// Försök sortera som tal först
|
||||
if (double.TryParse(a, out double da) && double.TryParse(b, out double db))
|
||||
result = da.CompareTo(db);
|
||||
else
|
||||
result = String.Compare(a, b);
|
||||
|
||||
return order == SortOrder.Ascending ? result : -result;
|
||||
}
|
||||
}
|
||||
10
StockHistory/Models/EquityTemp.cs
Normal file
10
StockHistory/Models/EquityTemp.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace StockHistory.Models;
|
||||
|
||||
public class EquityTemp
|
||||
{
|
||||
public string StockCode { get; set; } = string.Empty;
|
||||
public DateTime Bought { get; set; }
|
||||
public decimal BoughtPrice { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal TotalValue => BoughtPrice * Quantity;
|
||||
}
|
||||
@ -38,7 +38,7 @@ namespace StockHistory
|
||||
ServiceLifetime.Transient);
|
||||
|
||||
services.AddSingleton<frmStockHistoryInit>();
|
||||
services.AddScoped<frmStockHistoryAnalyse>();
|
||||
services.AddTransient<frmStockHistoryAnalyse>();
|
||||
services.AddTransient<IPdfOpener, PdfOpener>();
|
||||
services.AddTransient<IPdfFormatter, PdfFormatter>();
|
||||
services.AddTransient<ITransactionNotesServices, TransactionNotesServices>();
|
||||
|
||||
@ -22,17 +22,20 @@ public class StockEquityServices : IStockEquityServices
|
||||
public async Task<List<StockEquity>> GetAllAsync(DateTime equityDate)
|
||||
{
|
||||
return await _dbContext.Stocks
|
||||
.Where(s => s.Bought <= equityDate && s.Sold > equityDate)
|
||||
.Where(s => s.Bought <= equityDate && (s.Sold > equityDate || s.SoldQuant == 0))
|
||||
.ToListAsync();
|
||||
}
|
||||
public async Task GenerateStockVision()
|
||||
{
|
||||
var existingNotes = await _notesServices.GetAllAsync();
|
||||
// foreach (var note in await _notesServices.GetAllAsync())
|
||||
|
||||
foreach (var note in existingNotes)
|
||||
await _dbContext.Stocks.ExecuteDeleteAsync();
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
var existingNotes = await _notesServices.GetAllAsync();
|
||||
|
||||
foreach (var note in existingNotes.OrderBy(e => e.TransactionDate).ToList())
|
||||
{
|
||||
if (note.TransactionType == null) { }
|
||||
if (note.TransactionType == null && note.StockCode != null) { }
|
||||
else
|
||||
if (note.TransactionType.ToLower() == "köp")
|
||||
{
|
||||
@ -51,31 +54,49 @@ public class StockEquityServices : IStockEquityServices
|
||||
}
|
||||
else if (note.TransactionType.ToLower() == "sälj" || note.TransactionType.ToLower() == "försäljning")
|
||||
{
|
||||
var equity = await _dbContext.Stocks.FirstOrDefaultAsync(s => s.StockCode == note.StockCode && s.SoldQuant == 0);
|
||||
var equityList = await _dbContext.Stocks.Where(s => s.StockCode == note.StockCode && s.SoldQuant == 0).OrderBy(s => s.Quantity).ToListAsync();
|
||||
|
||||
if (equity != null)
|
||||
if (equityList != null)
|
||||
{
|
||||
if (note.StockQuantity < equity.Quantity)
|
||||
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.Stocks.Update(eq);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var splitEquity = new StockEquity
|
||||
{
|
||||
StockCode = equity.StockCode,
|
||||
Bought = equity.Bought,
|
||||
BoughtPrice = equity.BoughtPrice,
|
||||
Quantity = equity.Quantity - Math.Abs(note.StockQuantity),
|
||||
Sold = DateTime.MinValue,
|
||||
SoldPrice = 0m,
|
||||
SoldQuant = 0
|
||||
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();
|
||||
}
|
||||
equity.Quantity = Math.Abs(note.StockQuantity);
|
||||
equity.Sold = note.TransactionDate;
|
||||
equity.SoldPrice = note.StockPrice;
|
||||
equity.SoldQuant = Math.Abs(note.StockQuantity);
|
||||
_dbContext.Stocks.Update(equity);
|
||||
eq.Quantity -= soldQuant;
|
||||
_dbContext.Stocks.Update(eq);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
soldQuant = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
StockHistory/SmartListViewComparer.cs
Normal file
63
StockHistory/SmartListViewComparer.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StockHistory;
|
||||
|
||||
public class SmartListViewComparer : IComparer
|
||||
{
|
||||
private readonly int col;
|
||||
private readonly SortOrder order;
|
||||
|
||||
public SmartListViewComparer(int column, SortOrder order)
|
||||
{
|
||||
col = column;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
string a = ((ListViewItem)x).SubItems[col].Text;
|
||||
string b = ((ListViewItem)y).SubItems[col].Text;
|
||||
|
||||
// Tomma värden sist
|
||||
if (string.IsNullOrWhiteSpace(a) && string.IsNullOrWhiteSpace(b)) return 0;
|
||||
if (string.IsNullOrWhiteSpace(a)) return order == SortOrder.Ascending ? 1 : -1;
|
||||
if (string.IsNullOrWhiteSpace(b)) return order == SortOrder.Ascending ? -1 : 1;
|
||||
|
||||
int result = CompareSmart(a, b);
|
||||
return order == SortOrder.Ascending ? result : -result;
|
||||
}
|
||||
|
||||
private int CompareSmart(string a, string b)
|
||||
{
|
||||
// Procent
|
||||
if (a.EndsWith("%") && b.EndsWith("%") &&
|
||||
double.TryParse(a.TrimEnd('%'), out double pa) &&
|
||||
double.TryParse(b.TrimEnd('%'), out double pb))
|
||||
return pa.CompareTo(pb);
|
||||
|
||||
// Valuta
|
||||
string ac = a.Replace("kr", "").Replace("$", "").Replace("€", "").Trim();
|
||||
string bc = b.Replace("kr", "").Replace("$", "").Replace("€", "").Trim();
|
||||
|
||||
if (double.TryParse(ac, out double ca) && double.TryParse(bc, out double cb))
|
||||
return ca.CompareTo(cb);
|
||||
|
||||
// Datum
|
||||
if (DateTime.TryParse(a, out DateTime da) && DateTime.TryParse(b, out DateTime db))
|
||||
return da.CompareTo(db);
|
||||
|
||||
// Tal
|
||||
if (double.TryParse(a, out double na) && double.TryParse(b, out double nb))
|
||||
return na.CompareTo(nb);
|
||||
|
||||
// Naturlig sortering
|
||||
return StrCmpLogicalW(a, b);
|
||||
}
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int StrCmpLogicalW(string x, string y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
20
StockHistory/frmStockHistoryAnalyse.Designer.cs
generated
20
StockHistory/frmStockHistoryAnalyse.Designer.cs
generated
@ -37,6 +37,8 @@
|
||||
chBought = new ColumnHeader();
|
||||
chPrize = new ColumnHeader();
|
||||
chNumber = new ColumnHeader();
|
||||
lblTotalValue = new Label();
|
||||
chValue = new ColumnHeader();
|
||||
SuspendLayout();
|
||||
//
|
||||
// lblHistAnalyseHeader
|
||||
@ -87,7 +89,7 @@
|
||||
//
|
||||
// lwStocks
|
||||
//
|
||||
lwStocks.Columns.AddRange(new ColumnHeader[] { chStock, chBought, chPrize, chNumber });
|
||||
lwStocks.Columns.AddRange(new ColumnHeader[] { chStock, chBought, chPrize, chNumber, chValue });
|
||||
lwStocks.Location = new Point(31, 110);
|
||||
lwStocks.Name = "lwStocks";
|
||||
lwStocks.Size = new Size(676, 172);
|
||||
@ -115,11 +117,25 @@
|
||||
chNumber.Text = "Antal";
|
||||
chNumber.Width = 100;
|
||||
//
|
||||
// lblTotalValue
|
||||
//
|
||||
lblTotalValue.AutoSize = true;
|
||||
lblTotalValue.Location = new Point(345, 295);
|
||||
lblTotalValue.Name = "lblTotalValue";
|
||||
lblTotalValue.Size = new Size(16, 15);
|
||||
lblTotalValue.TabIndex = 10;
|
||||
lblTotalValue.Text = "...";
|
||||
//
|
||||
// chValue
|
||||
//
|
||||
chValue.Text = "Total";
|
||||
//
|
||||
// frmStockHistoryAnalyse
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(7F, 15F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(800, 450);
|
||||
Controls.Add(lblTotalValue);
|
||||
Controls.Add(lwStocks);
|
||||
Controls.Add(btnClose);
|
||||
Controls.Add(dtpChosenDate);
|
||||
@ -142,5 +158,7 @@
|
||||
private ColumnHeader chBought;
|
||||
private ColumnHeader chPrize;
|
||||
private ColumnHeader chNumber;
|
||||
private Label lblTotalValue;
|
||||
private ColumnHeader chValue;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using StockHistory.Services;
|
||||
using StockHistory.Models;
|
||||
using StockHistory.Services;
|
||||
|
||||
namespace StockHistory;
|
||||
|
||||
@ -10,16 +11,20 @@ public partial class frmStockHistoryAnalyse : Form
|
||||
{
|
||||
InitializeComponent();
|
||||
_equityServices = equityServices;
|
||||
lwStocks.EnableSmartSorting();
|
||||
|
||||
}
|
||||
|
||||
private void btnGenerateStockScheme_Click(object sender, EventArgs e)
|
||||
{
|
||||
_equityServices.GenerateStockVision().Wait();
|
||||
dtpChosenDate.Value = DateTime.Today;
|
||||
LoadStockListAsync(dtpChosenDate.Value).Wait();
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
this.Hide();
|
||||
}
|
||||
|
||||
private void dtpChosenDate_ValueChanged(object sender, EventArgs e)
|
||||
@ -34,14 +39,60 @@ public partial class frmStockHistoryAnalyse : Form
|
||||
// var stocks = await _equityServices.GetAllAsync();
|
||||
|
||||
lwStocks.Items.Clear();
|
||||
foreach (var t in stocks)
|
||||
var dTotal = stocks.Sum(s => s.BoughtPrice * s.Quantity);
|
||||
lblTotalValue.Text = dTotal.ToString("C");
|
||||
EquityTemp equityTemp = new EquityTemp
|
||||
{
|
||||
var item = new ListViewItem(t.StockCode);
|
||||
item.SubItems.Add(t.Bought.ToString());
|
||||
item.SubItems.Add(t.BoughtPrice.ToString());
|
||||
item.SubItems.Add(t.Quantity.ToString());
|
||||
StockCode = "Total",
|
||||
Bought = DateTime.MinValue,
|
||||
BoughtPrice = 0,
|
||||
Quantity = 0
|
||||
};
|
||||
|
||||
foreach (var t in stocks.OrderBy(s => s.StockCode + s.Bought).ToList())
|
||||
{
|
||||
if (t.StockCode != equityTemp.StockCode)
|
||||
{
|
||||
if (equityTemp.StockCode != "Total")
|
||||
{
|
||||
var item = new ListViewItem(equityTemp.StockCode);
|
||||
item.SubItems.Add(equityTemp.Bought.ToString());
|
||||
item.SubItems.Add(equityTemp.BoughtPrice.ToString());
|
||||
item.SubItems.Add(equityTemp.Quantity.ToString());
|
||||
item.SubItems.Add((equityTemp.TotalValue).ToString("C"));
|
||||
lwStocks.Items.Add(item);
|
||||
}
|
||||
equityTemp.StockCode = t.StockCode;
|
||||
equityTemp.Bought = t.Bought;
|
||||
equityTemp.BoughtPrice = t.BoughtPrice;
|
||||
equityTemp.Quantity = t.Quantity;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t.Bought < equityTemp.Bought)
|
||||
equityTemp.Bought = t.Bought;
|
||||
if (t.BoughtPrice < equityTemp.BoughtPrice || equityTemp.BoughtPrice == 0)
|
||||
equityTemp.BoughtPrice = t.BoughtPrice;
|
||||
equityTemp.Quantity += t.Quantity;
|
||||
}
|
||||
|
||||
}
|
||||
var l_item = new ListViewItem(equityTemp.StockCode);
|
||||
l_item.SubItems.Add(equityTemp.Bought.ToString());
|
||||
l_item.SubItems.Add(equityTemp.BoughtPrice.ToString());
|
||||
l_item.SubItems.Add(equityTemp.Quantity.ToString());
|
||||
l_item.SubItems.Add((equityTemp.TotalValue).ToString("C"));
|
||||
lwStocks.Items.Add(l_item);
|
||||
}
|
||||
|
||||
protected override void OnVisibleChanged(EventArgs e)
|
||||
{
|
||||
base.OnVisibleChanged(e);
|
||||
if (this.Visible)
|
||||
{
|
||||
dtpChosenDate.Value = DateTime.Today;
|
||||
LoadStockListAsync(dtpChosenDate.Value).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
StockHistory/frmStockHistoryInit.Designer.cs
generated
3
StockHistory/frmStockHistoryInit.Designer.cs
generated
@ -122,6 +122,7 @@
|
||||
lwTransactions.TabIndex = 6;
|
||||
lwTransactions.UseCompatibleStateImageBehavior = false;
|
||||
lwTransactions.View = View.Details;
|
||||
// lwTransactions.ColumnClick += lwTransactions_ColumnClick;
|
||||
//
|
||||
// StockCodeHeader
|
||||
//
|
||||
@ -224,7 +225,7 @@
|
||||
Controls.Add(btnChooseFile);
|
||||
Margin = new Padding(3, 2, 3, 2);
|
||||
Name = "frmStockHistoryInit";
|
||||
Text = "StockBrokerage";
|
||||
Text = "29";
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
@ -20,6 +20,9 @@ public partial class frmStockHistoryInit : Form
|
||||
private TransactionNote trans = new();
|
||||
private bool SHB = false;
|
||||
private string pdfFilePath = string.Empty;
|
||||
// Sortering
|
||||
private int _sortColumn = -1;
|
||||
private SortOrder _sortOrder = SortOrder.None;
|
||||
|
||||
public frmStockHistoryInit(
|
||||
IPdfOpener pdfOpener,
|
||||
@ -30,6 +33,7 @@ public partial class frmStockHistoryInit : Form
|
||||
)
|
||||
{
|
||||
InitializeComponent();
|
||||
lwTransactions.EnableSmartSorting();
|
||||
_pdfOpener = pdfOpener;
|
||||
_pdfFormatter = pdfFormatter;
|
||||
_transactionNotes = transactionNotes;
|
||||
@ -299,5 +303,7 @@ public partial class frmStockHistoryInit : Form
|
||||
{
|
||||
_analyseForm.Show();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user