diff --git a/Websites/SharePrices/.vs/SharePrices/config/applicationhost.config b/Websites/SharePrices/.vs/SharePrices/config/applicationhost.config new file mode 100644 index 0000000..cdd2df8 --- /dev/null +++ b/Websites/SharePrices/.vs/SharePrices/config/applicationhost.config @@ -0,0 +1,1026 @@ + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Websites/SharePrices/.vs/SharePrices/v17/.suo b/Websites/SharePrices/.vs/SharePrices/v17/.suo index d7709a5..f4758ab 100644 Binary files a/Websites/SharePrices/.vs/SharePrices/v17/.suo and b/Websites/SharePrices/.vs/SharePrices/v17/.suo differ diff --git a/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.backup.json b/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.backup.json index c1073ec..38dd948 100644 --- a/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.backup.json +++ b/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.backup.json @@ -1,12 +1,138 @@ { "Version": 1, - "WorkspaceRootPath": "\\\\OZHOST1\\d$\\Documents\\Visual Studio Projects\\SharePrices\\", - "Documents": [], + "WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|c:\\users\\tsuser\\sources\\repos\\steves_code\\websites\\shareprices\\shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}" + } + ], "DocumentGroupContainers": [ { "Orientation": 0, "VerticalTabListWidth": 256, - "DocumentGroups": [] + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 6, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "SharePricesStyles.css", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css", + "RelativeDocumentMoniker": "SharePrices\\SharePricesStyles.css", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css", + "RelativeToolTip": "SharePrices\\SharePricesStyles.css", + "ViewState": "AgIAAFYAAAAAAAAAAADgv2wAAAAHAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2025-05-15T16:16:53.007Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 5, + "Title": "SharePrices_Accounts.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Accounts.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Accounts.js", + "ViewState": "AgIAAPoAAAAAAAAAAAAAAPoAAAAeAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-05-12T12:30:52.565Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "SharePrices_Ledger.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Ledger.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js*", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Ledger.js*", + "ViewState": "AgIAAEsAAACAmZmZmVklwG4AAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-05-11T19:04:49.08Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 6, + "Title": "SharePrices.aspx.vb", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb", + "RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx.vb", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb", + "RelativeToolTip": "SharePrices\\SharePrices.aspx.vb", + "ViewState": "AgIAALUAAAAAAAAAAAAQwH4BAAA2AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003361|", + "WhenOpened": "2025-02-10T17:47:33.143Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "SharePrices_Holdings.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Holdings.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Holdings.js", + "ViewState": "AgIAAFIEAAAA8P////8BwMADAAAsAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-02-10T17:39:46.261Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "SharePrices.aspx", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx", + "RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx", + "RelativeToolTip": "SharePrices\\SharePrices.aspx", + "LogicalView": "7651a703-06e5-11d1-8ebd-00a0c90f26ea", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000195|", + "WhenOpened": "2025-01-06T10:39:09.87Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "SharePrices.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices.js", + "ViewState": "AgIAADwAAADAZmZmZqYowEwBAAANAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-01-06T10:28:28.952Z", + "EditorCaption": "" + } + ] + } + ] } ] } \ No newline at end of file diff --git a/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.json b/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.json index 7176f60..497e93e 100644 --- a/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.json +++ b/Websites/SharePrices/.vs/SharePrices/v17/DocumentLayout.json @@ -1,12 +1,135 @@ { "Version": 1, - "WorkspaceRootPath": "C:\\Users\\Steve.OZDOMAIN\\source\\repos\\Steves_Code\\Websites\\SharePrices\\", - "Documents": [], + "WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|c:\\users\\tsuser\\sources\\repos\\steves_code\\websites\\shareprices\\shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + }, + { + "AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}", + "RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}" + } + ], "DocumentGroupContainers": [ { "Orientation": 0, "VerticalTabListWidth": 256, - "DocumentGroups": [] + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 4, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "SharePricesStyles.css", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css", + "RelativeDocumentMoniker": "SharePrices\\SharePricesStyles.css", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css", + "RelativeToolTip": "SharePrices\\SharePricesStyles.css", + "ViewState": "AgIAAFYAAAAAAAAAAADgv2wAAAAHAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2025-05-15T16:16:53.007Z" + }, + { + "$type": "Document", + "DocumentIndex": 5, + "Title": "SharePrices_Accounts.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Accounts.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Accounts.js", + "ViewState": "AgIAAPoAAAAAAAAAAAAAAPoAAAAeAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-05-12T12:30:52.565Z" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "SharePrices_Ledger.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Ledger.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Ledger.js", + "ViewState": "AgIAAEsAAACAmZmZmVklwG4AAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-05-11T19:04:49.08Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 6, + "Title": "SharePrices.aspx.vb", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb", + "RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx.vb", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb", + "RelativeToolTip": "SharePrices\\SharePrices.aspx.vb", + "ViewState": "AgIAALUAAAAAAAAAAAAQwH4BAAA2AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003361|", + "WhenOpened": "2025-02-10T17:47:33.143Z" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "SharePrices_Holdings.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Holdings.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices_Holdings.js", + "ViewState": "AgIAAJcEAAAAyAAAAEAgwLgEAAARAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-02-10T17:39:46.261Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "SharePrices.aspx", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx", + "RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx", + "RelativeToolTip": "SharePrices\\SharePrices.aspx", + "LogicalView": "7651a703-06e5-11d1-8ebd-00a0c90f26ea", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000195|", + "WhenOpened": "2025-01-06T10:39:09.87Z" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "SharePrices.js", + "DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js", + "RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices.js", + "ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js", + "RelativeToolTip": "SharePrices\\scripts\\SharePrices.js", + "ViewState": "AgIAADwAAADAZmZmZqYowEwBAAANAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2025-01-06T10:28:28.952Z", + "EditorCaption": "" + } + ] + } + ] } ] } \ No newline at end of file diff --git a/Websites/SharePrices/SharePrices.v12.suo b/Websites/SharePrices/SharePrices.v12.suo index f1e3c4e..afff3a2 100644 Binary files a/Websites/SharePrices/SharePrices.v12.suo and b/Websites/SharePrices/SharePrices.v12.suo differ diff --git a/Websites/SharePrices/SharePrices/App_Code/DataAccessLayer.vb b/Websites/SharePrices/SharePrices/App_Code/DataAccessLayer.vb index d75d361..aa883c5 100644 --- a/Websites/SharePrices/SharePrices/App_Code/DataAccessLayer.vb +++ b/Websites/SharePrices/SharePrices/App_Code/DataAccessLayer.vb @@ -12,20 +12,36 @@ Public Class DataAccessLayer Public Volume As Int64 End Class - Public Shared Function GetInstruments() As String - Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments") - Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader()) - c = GetSQLCommand("usp_GetHoldings") - Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader()) - c = GetSQLCommand("usp_GetHoldingsHistory") - Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader()) - c = GetSQLCommand("usp_GetAccounts") - Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader()) - GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}" - DisposeSQLCommand(c) - End Function + Public Shared Function GetInstruments() As String + Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments") + Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader()) + c = GetSQLCommand("usp_GetHoldings") + Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader()) + c = GetSQLCommand("usp_GetHoldingsHistory") + Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader()) + c = GetSQLCommand("usp_GetAccounts") + Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader()) + GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}" + DisposeSQLCommand(c) + End Function - Public Shared Function AddInstrument(Symbol As String, FullName As String) As String + Public Shared Function GetAggregatedCurrentHoldings() As String + Dim c As SqlCommand = GetSQLCommand("usp_GetAggregatedCurrentHoldings") + GetAggregatedCurrentHoldings = DataReaderToJSONString(c.ExecuteReader()) + DisposeSQLCommand(c) + End Function + Public Shared Function GetCurrentHoldings() As String + Dim c As SqlCommand = GetSQLCommand("usp_GetCurrentHoldings") + GetCurrentHoldings = DataReaderToJSONString(c.ExecuteReader()) + DisposeSQLCommand(c) + End Function + Public Shared Function GetTradeHistory() As String + Dim c As SqlCommand = GetSQLCommand("usp_GetTradeHistory") + GetTradeHistory = DataReaderToJSONString(c.ExecuteReader()) + DisposeSQLCommand(c) + End Function + + Public Shared Function AddInstrument(Symbol As String, FullName As String) As String Dim c As SqlCommand = GetSQLCommand("usp_InsertInstrument") c.Parameters.AddWithValue("Symbol", Symbol) c.Parameters.AddWithValue("FullName", FullName) @@ -258,6 +274,7 @@ Public Class DataAccessLayer Select Case dr.GetDataTypeName(fieldNo).ToUpper Case "CHAR", "VARCHAR" : thisRow += """" + dr(fieldNo) + """" Case "DATETIME", "SMALLDATETIME", "DATETIME2", "DATE" : thisRow += ToEpochTime(dr(fieldNo)).ToString() + Case "BIT" : If (dr(fieldNo)) Then thisRow += "true" Else thisRow += "false" Case Else : thisRow += dr(fieldNo).ToString() End Select End If diff --git a/Websites/SharePrices/SharePrices/SharePrices.aspx b/Websites/SharePrices/SharePrices/SharePrices.aspx index 1253868..0e599ce 100644 --- a/Websites/SharePrices/SharePrices/SharePrices.aspx +++ b/Websites/SharePrices/SharePrices/SharePrices.aspx @@ -40,7 +40,8 @@ Charts Holdings Accounts - Analysis + Ledger + History Log @@ -73,12 +74,26 @@
+ +
Aggregated Holdings table
+
+
+ + + + +
+
New Holdings table
+
Watchlist table
+
@@ -101,9 +117,14 @@
+
+
Analysis
+
+
History Chart
@@ -141,6 +162,7 @@
@@ -209,7 +235,6 @@ import { addInstrument, createSharesTable, - createSharesTableRow, getInstruments, redrawAllSharesCharts, refreshHistoryChart, @@ -220,10 +245,12 @@ showModalDialog_FullScreenChart, showModalDialog_SoldHolding } from "./scripts/SharePrices.js"; + + import { updateLedgerTable } from "./scripts/SharePrices_Ledger.js"; + importedFunctions = { addInstrument: addInstrument, createSharesTable: createSharesTable, - createSharesTableRow: createSharesTableRow, getInstruments: getInstruments, redrawAllSharesCharts: redrawAllSharesCharts, refreshHistoryChart: refreshHistoryChart, @@ -232,7 +259,8 @@ showModalDialog_AddInstrument: showModalDialog_AddInstrument, showModalDialog_AddHolding: showModalDialog_AddHolding, showModalDialog_FullScreenChart: showModalDialog_FullScreenChart, - showModalDialog_SoldHolding: showModalDialog_SoldHolding + showModalDialog_SoldHolding: showModalDialog_SoldHolding, + updateLedgerTable: updateLedgerTable } function initPage() { @@ -258,7 +286,13 @@ $("#groupBySymbol").prop('checked', (c == 'true') ? true : false); c = Cookies.get('hideEmptyAccounts'); - $("#hideEmptyAccounts").prop('checked', (c == 'true') ? true : false); + $("#hideEmptyAccounts").prop('checked', (c == 'true') ? true : false) + + c = Cookies.get('showSubtotals'); + $("#showSubtotals").prop('checked', (c == 'true') ? true : false); + + c = Cookies.get('showCryptoHoldings'); + $("#showCryptoHoldings").prop('checked', (c == 'true') ? true : false); getInstruments(); } diff --git a/Websites/SharePrices/SharePrices/SharePrices.aspx.vb b/Websites/SharePrices/SharePrices/SharePrices.aspx.vb index 8bbcbf2..e23c185 100644 --- a/Websites/SharePrices/SharePrices/SharePrices.aspx.vb +++ b/Websites/SharePrices/SharePrices/SharePrices.aspx.vb @@ -105,19 +105,29 @@ Public Class SharePrices 'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitDailyData(" + Symbol + ") completed. Duration = " + DateDiff(DateInterval.Second, startDT, endDT).ToString()) End Sub - - Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long) - 'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData)) - Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol) + + Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long) + 'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData)) + Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol) - DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd)) + DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd)) - Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices) - If responseText = "" Then responseText = "[]" - SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) - End Sub + Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices) + If responseText = "" Then responseText = "[]" + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + End Sub - + + Public Shared Sub UpdateInstrument(Symbol As String, GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long) + Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.UpdateInstrument webmethod: " + Symbol) + + DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd)) + + Dim responseText As String = "{ ""Result"": ""Success"" }" + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + End Sub + + Public Shared Sub SwapInstrumentDisplayOrders(Symbol1 As String, Symbol2 As String) Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SwapInstrumentDisplayOrders webmethod: " + Symbol1 + ", " + Symbol2) @@ -152,7 +162,8 @@ Public Class SharePrices 'webClient.Proxy = New System.Net.WebProxy("127.0.0.1", 8888) 'Set headers required by Yahoo Finance - webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0") + 'webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0") + webClient.Headers.Add("User-Agent", "MMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36") webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5") 'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br") @@ -163,6 +174,10 @@ Public Class SharePrices webClient.Headers.Add("Sec-Fetch-Site", "none()") webClient.Headers.Add("Sec-Fetch-User", "?1") + webClient.Headers.Add("sec-ch-ua", "Google Chrome"";v=""135"", ""Not-A.Brand"";v=""8"", ""Chromium"";v=""135""") + webClient.Headers.Add("sec-ch-ua-mobile", "70") + webClient.Headers.Add("sec-ch-ua-platform", "Windows") + DownloadYahooFinanceWebString = webClient.DownloadString(queryString) End Function @@ -355,21 +370,22 @@ Public Class SharePrices Public Shared Sub SearchYahooFinanceShares(SearchString As String) Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SearchYahooFinanceShares webmethod: " + SearchString) - Dim webClient As New System.Net.WebClient + 'Dim webClient As New System.Net.WebClient - webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0") - webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") - webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5") - 'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br") - 'Connection: keep -alive - webClient.Headers.Add("Upgrade-Insecure-Requests", "1") - webClient.Headers.Add("Sec-Fetch-Dest", "document()") - webClient.Headers.Add("Sec-Fetch-Mode", "navigate()") - webClient.Headers.Add("Sec-Fetch-Site", "none()") - webClient.Headers.Add("Sec-Fetch-User", "?1") + 'webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0") + 'webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") + 'webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5") + ''webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br") + ''Connection: keep -alive + 'webClient.Headers.Add("Upgrade-Insecure-Requests", "1") + 'webClient.Headers.Add("Sec-Fetch-Dest", "document()") + 'webClient.Headers.Add("Sec-Fetch-Mode", "navigate()") + 'webClient.Headers.Add("Sec-Fetch-Site", "none()") + 'webClient.Headers.Add("Sec-Fetch-User", "?1") - Dim responseText As String = webClient.DownloadString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600") - SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + 'Dim responseText As String = webClient.DownloadString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600") + Dim responseText As String = DownloadYahooFinanceWebString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600") + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) End Sub @@ -390,11 +406,36 @@ Public Class SharePrices + + Public Shared Sub GetAggregatedCurrentHoldings() + 'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetAggregatedCurrentHoldings webmethod") + + Dim responseText As String = DataAccessLayer.GetAggregatedCurrentHoldings() + If responseText = "" Then responseText = "[]" + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + End Sub + + Public Shared Sub GetCurrentHoldings() + 'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetCurrentHoldings webmethod") + + Dim responseText As String = DataAccessLayer.GetCurrentHoldings() + If responseText = "" Then responseText = "[]" + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + End Sub + + Public Shared Sub GetTradeHistory() + 'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetTradeHistory webmethod") + + Dim responseText As String = DataAccessLayer.GetTradeHistory() + If responseText = "" Then responseText = "[]" + SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText) + End Sub - + + Public Shared Sub TestWebMethod(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData)) Dim startDT As Date = Now() Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.TestWebMethod: " + Symbol) diff --git a/Websites/SharePrices/SharePrices/SharePrices.vbproj b/Websites/SharePrices/SharePrices/SharePrices.vbproj index 99e8bca..a564db9 100644 --- a/Websites/SharePrices/SharePrices/SharePrices.vbproj +++ b/Websites/SharePrices/SharePrices/SharePrices.vbproj @@ -260,8 +260,10 @@ + + diff --git a/Websites/SharePrices/SharePrices/SharePricesStyles.css b/Websites/SharePrices/SharePrices/SharePricesStyles.css index 8b19179..2d36b51 100644 --- a/Websites/SharePrices/SharePrices/SharePricesStyles.css +++ b/Websites/SharePrices/SharePrices/SharePricesStyles.css @@ -39,6 +39,11 @@ td {vertical-align: top;} #spnCurrencies a { color: white; } #spnCurrencies a:visited { color: white; } +.open a:link { color: white; } +.open a:visited { color: white; } +.closed a:link { color: #757575; } +.closed a:visited { color: #757575; } + a:link { color: #f473ff; } a:visited { color: #9a34b7; } @@ -47,20 +52,49 @@ a:visited { color: #9a34b7; } .miniHoldings th { font-size: x-small; border: 1px solid #505050; } .soldHolding { text-decoration: line-through; } -/*tr.highlighted, td.highlighted { background-color: gold; }*/ -.highlighted { background-color: gold; } - .mainHoldings { font-size: small; } table.mainHoldings { width: 100%; } +table.mainHoldings th { background-color: #101040; } table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; } table.mainHoldings tr:hover { background-color: #404090; } +.mainHoldingsSpacer { font-size: 2pt; } +.holdingsSubtotal td { font-weight: bold; padding-top: 6px; padding-bottom: 12px; } +/*.holdingsSubtotal td { font-weight: bold; }*/ +.holdingsSubtotal { font-weight: bold; } +/*.holdingsGrandTotal td { font-weight: bold; padding-top: 10px; padding-bottom: 12px; }*/ +/*.holdingsGrandTotal td { font-weight: bold; }*/ +.holdingsGrandTotal { font-weight: bold; } +.holdingsRow { background-color: #10101a } -.watchlist { font-size: small; } +/*tr.highlighted, td.highlighted { background-color: gold; }*/ +.highlighted { background-color: gold; } +.highlightedProfit { color:black; background-color: #07b200; } +.highlightedLoss { color:white; background-color: #ff4141; } + +.tablesorter .filtered { display: none; } + +.ledger { font-size: x-small; } +table.ledger { width: 100%; } +table.ledger th { background-color: #101040; } +table.ledger th, table.ledger td { padding-left: 5px; padding-right: 5px; } +table.ledger tr:hover { background-color: #404090; } +.ledgerRow.odd { background-color: #28293b; } +/*.ledgerRow.even { background-color: #090b25 }*/ + +/* +.newHoldingsTable { font-size: small; } +table.newHoldingsTable { width: 100%; } +table.newHoldingsTable th, table.newHoldingsTable td { padding-left: 5px; padding-right: 5px; } +table.newHoldingsTable tr:hover { background-color: #404090; } +*/ +.watchlist { + font-size: small; +} /*table.watchlist { width: 100%; }*/ table.watchlist th, table.watchlist td { padding-left: 5px; padding-right: 5px; } table.watchlist tr:hover { background-color: #404090; } -.accounts { font-size: small; } +.accounts { font-size: 9pt; } /*table.accounts { width: 100%; }*/ table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; } table.accounts tr:hover { background-color: #404090; } @@ -137,8 +171,6 @@ td.no-border { border: 0px; text-align: right; } border-radius: 4px; } - - .deletebutton { color: red; cursor: pointer; } span.addHolding { cursor: pointer; } @@ -147,142 +179,3 @@ span.addHolding { cursor: pointer; } .marquee { width: 600px; overflow: hidden; font-size: small; /*border: 1px solid #ccc;*/ /*background: #ccc;*/ } .marquee div { padding: 1px 0px 1px 0px; } - - -/* -.highcharts-hollowcandlestick-series .highcharts-point-down { - fill: #ff4141; - stroke: #ff4141; -}*/ - -/*.highcharts-hollowcandlestick-series .highcharts-point-down-bearish-up { - fill: #35bd00; - stroke: #35bd00; -}*/ -/* -.highcharts-hollowcandlestick-series .highcharts-point-up { - fill: #07b200; - stroke: #07b200; -}*/ - - -/* -html, body { min-height: 100% !important; height: 100%; width: 100%; } -body { font-family: Verdana, Arial, Helvetica, sans-serif; margin-left: 0px; background-color: black; color: #B0B0B0; } - - / * The navigation bar * / -.navbar { overflow: hidden; background-color: #333; position: fixed; top: 0; width: 100%; z-index: 100; } -.navbar a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 7px 16px; text-decoration: none; } -.navbar label { color: #f2f2f2; } -.navbar a:hover { background: #ddd; color: white; } -.activenav { float: left; display: block; color: #f2f2f2 !important; background-color: #307D30 !important; text-align: center; padding: 7px 16px; text-decoration: none; } -.main { height: 100%; width: 100%; margin-top: 34px; / * Add a top margin to avoid content overlay * / } -/ * #chartDiv { height: 100%; overflow: scroll; } * / -#chartDiv { margin-top: 34px; } -#holdingsDiv { margin-top: 34px; } -#accoountsDiv { margin-top: 34px; } -#analDiv { margin-top: 34px; } - -div.activetab {position: absolute; top: 0; height: 100%; width: 100%; z-index: 2; background-color: black; overflow: scroll; / *display: inline;* /} -div.inactivetab {position: absolute; top: 0; height:100%; width: 100%; z-index: 0; overflow: scroll; / *display: none;* /} - -td {vertical-align: top;} - -.shareRow { / *background-color: #ffffff;* / } -.altShareRow { background-color: #212121; / *#2a2a2a;* / } - -.instrumentName { color: white; } -#spnTotalHoldings { color: white; padding-left: 50px; } - -a:link { color: #f473ff; } -a:visited { color: #9a34b7; } - -.miniHoldings { border-collapse: collapse; border-spacing: 0; } -.miniHoldings td { font-size: x-small; border: 1px solid #505050; } -.miniHoldings th { font-size: x-small; border: 1px solid #505050; } -.soldHolding { text-decoration: line-through; } - -tr.highlighted, td.highlighted { background-color: gold; } - -.mainHoldings { font-size: small; } -table.mainHoldings { width: 100%; } -table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; } -table.mainHoldings tr:hover { background-color: #404090; } - -.accounts { font-size: small; } -/ *table.accounts { width: 100%; }* / -table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; } -table.accounts tr:hover { background-color: #404090; } - -.analysis { font-size: small; } -table.analysis { width: 100%; } -table.analysis th, table.analysis td { padding-left: 5px; padding-right: 5px; } -table.analysis tbody tr { white-space: nowrap; } -table.analysis tr:hover { background-color: #404090; } - -.pcLabel { position: relative; background-color: transparent; text-align: center; z-index: 2; } -.pcBackground { position: absolute; z-index: 1; } - -.price-summary { font-size: x-small; } - -.current-value { font-size: medium; } - -.num {text-align: right;} - -.summary { width: 100%; border-collapse: collapse; } - -.spacer { padding-top: 6px; } - -.profit { color: #07b200; / * limegreen * /} -.loss { color: #ee2727; / *red;* / / * firebrick * /} - -td.no-border { border: 0px; text-align: right; } - -.flot-tick-label { color: #b0b0b0 } - -.LongSummary { font-size: x-small; } -.LongChart { height: 150px; width: 300px; } - -.MidSummary { font-size: x-small; } -.MidChart { height: 150px; width: 300px; } - -.ShortSummary { font-size: x-small; } -.ShortChart { height: 150px; width: 300px; } - -.DaySummary { font-size: x-small; } -.DayChart { height: 136px; width: 300px; } - -.FullScreenChart { height: 300px; width: 400px; } - -.HoldingCurrenciesChart { height: 250px; width: 250px; } -/ *#divHoldingCurrenciesChart .pieLabel { color: red !important; background-color: black; }* / -#divHoldingCurrenciesChart div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black } -#divHoldingCurrenciesChartCurrentValue div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black } -#divAccountBookCost div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black } -#divAccountValue div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black } -.flotTip - { - padding: 3px 5px; - background-color: #000; - z-index: 101; - color: #fff; - box-shadow: 0 0 10px #555; - opacity: .7; - filter: alpha(opacity=70); - border: 2px solid #fff; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - } - -.deletebutton { color: red; cursor: pointer; } - -span.addHolding { cursor: pointer; } - -.flot-text { font-size: x-small !important; } - -.chart-tooltip { position: absolute; border: 1px solid #fdd; padding: 2px; background-color: #fee; opacity: 0.80; display: none; z-index: 6; } - -#selectInstrument2 { width: 100%; } - -*/ \ No newline at end of file diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices.js b/Websites/SharePrices/SharePrices/scripts/SharePrices.js index 3a9b357..3d249aa 100644 --- a/Websites/SharePrices/SharePrices/scripts/SharePrices.js +++ b/Websites/SharePrices/SharePrices/scripts/SharePrices.js @@ -1,12 +1,12 @@ // @ts-check -//Testing gitea.copeland-bowen.com #3 import {formatAmount, generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js"; import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot, createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart, createFullScreenComparisonChart_Flot } from "./SharePrices_Charts.js"; -import {createHoldingsTable} from "./SharePrices_Holdings.js"; +import { updateHoldingsTables } from "./SharePrices_Holdings.js"; +import { createAccountsTables } from "./SharePrices_Accounts.js"; 'use strict'; @@ -26,14 +26,43 @@ var vars = { previousDay: 0, previousClose: 0, previousWeek: 0, - previousWeekClose:0 + previousWeekClose: 0, + currentHoldings: {}, + currentAggHoldingsRows: { + Data: [], + rowID: function (r) { + return (r.Symbol).replaceAll(' ', '_').replaceAll('=', '_'); + }, + indexOfRowID: function (rowID) { + return this.Data.map(function (item) { + return vars.currentAggHoldingsRows.rowID(item.attributes) + }).indexOf(rowID) + }, + GetRow: function (r) { + return this.Data[this.indexOfRowID(this.rowID(r))] + } + }, + currentHoldingsRows: { + Data: [], + rowID: function (r) { + return (r.TaxBucket + '_' + r.AccountName + '_' + r.Symbol).replaceAll(' ', '_').replaceAll('=', '_'); + }, + indexOfRowID: function (rowID) { + return this.Data.map(function (item) { + return vars.currentHoldingsRows.rowID(item.attributes) + }).indexOf(rowID) + }, + GetRow: function (r) { + return this.Data[this.indexOfRowID(this.rowID(r))] + } + } } //var timespans = { oneSecond: 1000, oneMinute: 60 * 1000, oneHour: 60 * 60 * 1000, oneDay: 24 * 60 * 60 * 1000, oneWeek: 7 * 24 * 60 * 60 * 1000, oneMonth: 31 * 24 * 60 * 60 * 1000 }; //var lastSuccessfulFetch; var currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }); var instrumentSearchResults = []; -var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true }; +var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 15000, timer: 0, updateNeeded: true }; var indexMarquee = { newContent: '', currentContent: '', @@ -127,7 +156,7 @@ var Instruments = { let lastFetchedDate = this.Data[0].lastFetchedIntraday; let lastFetchedIndex = 0; for (let i = 1; i < this.Data.length; i++) { - if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') { + if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp' && this.Data[i].TrackPriceHistory == 1) { //if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = this.Data[i].MaxIntradayDate }; if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) }; if (lastFetchedDate > this.Data[i].lastFetchedIntraday) { @@ -247,6 +276,14 @@ Date.prototype.yyyymmddhhmmss = function () { var ss = this.getSeconds().toString(); return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]) + " " + (hh[1] ? hh : "0" + hh[0]) + ":" + (nn[1] ? nn : "0" + nn[0]) + ":" + (ss[1] ? ss : "0" + ss[0]); }; +Date.prototype.yyyymmddhhmm = function () { + var yyyy = this.getFullYear().toString(); + var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based + var dd = this.getDate().toString(); + var hh = this.getHours().toString(); + var nn = this.getMinutes().toString(); + return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]) + " " + (hh[1] ? hh : "0" + hh[0]) + ":" + (nn[1] ? nn : "0" + nn[0]); +}; Date.prototype.yyyymmdd = function () { var yyyy = this.getFullYear().toString(); var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based @@ -289,7 +326,11 @@ Number.prototype.autoScale = function () { return this.toFixed(1); } else { if (a < 0.001) { - return this.toExponential(2); + if (a == 0) { + return '0'; + } else { + return this.toExponential(2); + } } else { return this.toFixed(2); } @@ -383,7 +424,7 @@ export function createSharesTable() { logError({ MSG: "Error in createSharesTable", err: err }); } }; -export function createSharesTableRow(instrument) { +function createSharesTableRow(instrument) { function getHoldingsTable(Instrument) { if (Instrument.Holdings.length > 0) { let totalUnits = 0; @@ -769,7 +810,7 @@ function getYahooDailyData(Instrument) { createLongChart(Instrument); } else { //console.info("No data received for symbol: " + Instrument.Symbol); - logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response }) + logWarning({ MSG: "getYahooDailyData - No data received for symbol: " + Instrument.Symbol, response: response }) } }, failure: function (response) { @@ -829,14 +870,14 @@ function getYahooIntradayData(Instrument) { createMidChart(Instrument); createShortChart(Instrument); } else { - console.info("No NEW data received for symbol: " + Instrument.Symbol); + console.info("getYahooIntradayData - No NEW data received for symbol: " + Instrument.Symbol); } } else { //logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response }); } } else { //console.warn("No data received for symbol: " + Instrument.Symbol); - logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response }); + logWarning({ MSG: "getYahooIntradayData - No data received for symbol: " + Instrument.Symbol, response: response }); } }, failure: function (response) { @@ -859,10 +900,15 @@ function getYahooSingleDayData(Instrument) { success: function (response) { vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime; let newData = []; + let dbUpdateNeeded = false; + if (response.chart && response.chart.result) { let dataSeries = response.chart.result[0]; if (dataSeries.meta) { + if (Instrument.CurrentPrice != dataSeries.meta.regularMarketPrice) { + dbUpdateNeeded = true; + } Instrument.SingleDayPreviousClose = dataSeries.meta.previousClose; Instrument.SingleDayStartDate = dataSeries.meta.currentTradingPeriod.regular.start * 1000; Instrument.SingleDayEndDate = dataSeries.meta.currentTradingPeriod.regular.end * 1000; @@ -908,12 +954,26 @@ function getYahooSingleDayData(Instrument) { } else { //console.warn("No timestamp series received for symbol: " + Instrument.Symbol); //logWarning("No timestamp series received for symbol: " + Instrument.Symbol); - logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response }); + logWarning({ MSG: "getYahooSingleDayData - No timestamp series received for symbol: " + Instrument.Symbol, response: response }); + } + + if (dbUpdateNeeded) { + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/UpdateInstrument?S=" + Instrument.Symbol, + dataType: "json", + data: JSON.stringify({ Symbol: Instrument.Symbol, GMTOffset: Instrument.GMTOffset, Currency: Instrument.Currency, CurrentPrice: Instrument.CurrentPrice, InstrumentType: Instrument.InstrumentType, TradeDayStart: Instrument.SingleDayStartDate, TradeDayEnd: Instrument.SingleDayEndDate }), + success: function (response) { + //console.info('UpdateInstrument success: ' + Instrument.Symbol + ' - ' + JSON.stringify(response)); + }, + failure: function (response) { console.error("getYahooSingleDayData - UpdateInstrument error: " + JSON.stringify(response)); } + }); } } else { //console.info("No data received for symbol: " + Instrument.Symbol); //logInfo("No data received for symbol: " + Instrument.Symbol); - logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response }); + logWarning({ MSG: "getYahooSingleDayData - No data received for symbol: " + Instrument.Symbol, response: response }); } }, failure: function (response) { @@ -1014,6 +1074,34 @@ function submitNewDailyData(Instrument, NewData) { } function submitNewIntradayData(Instrument, NewData) { //console.info("submitNewIntradayData"); + + //Submit the data in small chunks to avoid the HTTP payload getting too big + let noSubmitted = 0; + while (noSubmitted < NewData.length) { + let thisSubmission = []; + for (let x = noSubmitted; x < NewData.length && thisSubmission.length < 300; x++) { + thisSubmission.push(NewData[x]); + } + noSubmitted += thisSubmission.length; + + //if (noSubmitted < NewData.length) { + // console.info("Need another round"); + //} + + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/SubmitIntradayData?S=" + Instrument.Symbol, + dataType: "json", + data: JSON.stringify({ Symbol: Instrument.Symbol, DailyPrices: thisSubmission, GMTOffset: Instrument.GMTOffset, Currency: Instrument.Currency, CurrentPrice: Instrument.CurrentPrice, InstrumentType: Instrument.InstrumentType, TradeDayStart: Instrument.SingleDayStartDate, TradeDayEnd: Instrument.SingleDayEndDate }), + success: function (response) { + //console.info('SubmitIntradayData success: ' + Instrument.Symbol + ' - ' + JSON.stringify(response)); + }, + failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); } + }); + } + + /* $.ajax({ type: "POST", contentType: "application/json", @@ -1026,6 +1114,7 @@ function submitNewIntradayData(Instrument, NewData) { }, failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); } }); + */ //Add new data to Instrument let currentSummary = {}; @@ -1050,207 +1139,20 @@ function submitNewIntradayData(Instrument, NewData) { } function updateTables() { - //Only update the tables every 5 seconds (unless lastTablesUpdate has been reset) + //Only update the tables every x seconds if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) { tablesUpdateTimings.lastUpdate = new Date().getTime(); - createHoldingsTable(Instruments, vars); - createAccountsTables(); + //createHoldingsTable(Instruments, vars); + createAccountsTables(Instruments); createAnalysisTable(); createIndexMarquee(); updateCurrenciesSpan(); + + updateHoldingsTables(Instruments, vars); } } -function createAccountsTables() { - function getAccounts() { - let accounts = []; - function indexOfAccount(accountName) { return accounts.map(function (item) { return item.accountName; }).indexOf(accountName); }; - function getAccount(accountName) { return accounts[indexOfAccount(accountName)]; }; - function addAccountHolding(instrument, exchangeRate, holding) { - let a = getAccount(holding.AccountName); - if (!a) { - a = { accountName: holding.AccountName, holdings: [], totalCostGBP: 0, totalValueGBP: 0, totalGainGBP: 0, totalCashGBP: 0 }; - accounts.push(a); - } - let h = { - instrumentName: instrument.InstrumentName, - symbol: instrument.Symbol, - instrumentType: instrument.InstrumentType, - currency: instrument.Currency, - purchaseDate: holding.PurchaseDate, - purchasePricePerUnit: holding.PurchasePricePerUnit, - noUnits: holding.NoUnits, - cost: holding.NoUnits * holding.PurchasePricePerUnit - } - //if (exchangeRate) { - //h.costGBP = h.cost / exchangeRate; - h.costGBP = holding.BookCostGBP; - if (instrument.InstrumentType != 'CURRENCY') { - a.totalCostGBP += holding.BookCostGBP; - } - //} - if (instrument.CurrentPrice) { - h.currentPrice = instrument.CurrentPrice; - h.currentValue = holding.NoUnits * instrument.CurrentPrice; - h.gain = h.currentValue - h.cost; - if (exchangeRate) { - h.currentValueGBP = h.currentValue / exchangeRate; - //h.gainGBP = h.gain / exchangeRate; - h.gainGBP = h.currentValueGBP - h.costGBP; - if (instrument.InstrumentType != 'CURRENCY') { - a.totalGainGBP += h.gainGBP; - a.totalValueGBP += h.currentValueGBP; - } - } - } - if (instrument.InstrumentType == 'CURRENCY') { - //Don't add cash holdings - just add the cash value to the account - a.totalCashGBP += h.currentValueGBP; - } else { - a.holdings.push(h); - } - } - - for (let ix = 0; ix < Instruments.Data.length; ix++) { - let i = Instruments.Data[ix]; - let exchangegRate = Instruments.GetExchangeRate(i.Currency, 'GBP'); - for (let hx = 0; hx < i.Holdings.length; hx++) { - let h = i.Holdings[hx]; - if (h.SoldDate == null) { - addAccountHolding(i, exchangegRate, h); - } - } - } - - return accounts; - } - - let accounts = getAccounts(); - - //Sort the accounts array by accountName - accounts.sort(function (a, b) { - if (a.accountName < b.accountName) return -1; - if (a.accountName > b.accountName) return 1; - return 0; - }); - - //let summaryTable = ""; - let summaryTable = "
Account NameNo. HoldingsTotal Cost (GBP)Current ValueTotal Gain
" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""; - let accountsTables = '' - let tableIDs = ['accountsSummaryTable']; - let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked'); - let altRow = 1; - for (let ax = 0; ax < accounts.length; ax++) { - let a = accounts[ax]; - let accountGainsGBP = 0; - let accountLossesGBP = 0; - let accountValueGBP = 0; - let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit'; - if (a.holdings.length > 0) { - altRow = !altRow; - - accountsTables += '
' + a.accountName + '
' - let tableID = 'accountTable' + ax; - tableIDs.push(tableID); - accountsTables += '
Account NameNo. HoldingsTotal Cost (GBP)Total Gains (GBP)Total Losses (GBP)Current Investements ValueNET GainCash Balance GBPTotal Value GBP
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - ''; - let accountAltRow = 1; - for (let hx = 0; hx < a.holdings.length; hx++) { - let h = a.holdings[hx]; - accountAltRow = !accountAltRow; - let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit'; - let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit'; - if (h.gainGBP > 0) { - accountGainsGBP += h.gainGBP; - } else { - accountLossesGBP += h.gainGBP; - } - accountValueGBP += h.currentValueGBP; - accountsTables += '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - //'' + - '' + - ''; - } - //Add the Total Gains - accountAltRow = !accountAltRow; - accountsTables += '' + - '' + - '' + - '' + - ''; - //Add the Total Losses - accountAltRow = !accountAltRow; - accountsTables += '' + - '' + - '' + - '' + - ''; - //Add the NET Gains summary line - accountAltRow = !accountAltRow; - accountsTables += '' + - '' + - '' + - '' + - '' + - '' + - ''; - accountsTables += '
NameSymbolBuy DateNo UnitsBuy PriceCurrent PriceBook CostApprox Book Cost £Current ValueCurrent Value £GainGain £Gain %
' + h.instrumentName + '' + h.symbol + '' + new Date(h.purchaseDate).yyyymmdd() + '' + h.noUnits + '' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '' + formatAmount(h.currentPrice, h.currency, 2) + '' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '' + formatAmount(h.costGBP, 'GBP', 2) + '' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '' + formatAmount(h.currentValueGBP, 'GBP', 2) + '' + formatAmount(h.gain, h.currency, 2) + '' + formatAmount(h.gainGBP, 'GBP', 2) + '' + ((h.gain / h.cost) * 100).toFixed(1) + '%' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%
Total Gains' + formatAmount(accountGainsGBP, 'GBP', 2) + ' 
Total Losses' + formatAmount(accountLossesGBP, 'GBP', 2) + ' 
Account Value:' + formatAmount(accountValueGBP, 'GBP', 2) + 'NET Gain' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + ' 
'; - } - - //Add an entry to the summary table - if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) { - summaryTable += "" + a.accountName + "" + - "" + a.holdings.length + "" + - "" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "" + - "" + formatAmount(accountGainsGBP, 'GBP', 2) + "" + - "" + formatAmount(accountLossesGBP, 'GBP', 2) + "" + - "" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "" + - "" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "" + - "" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "" + - "" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "" + - ""; - } - } - summaryTable += ""; - $("#accountsSummaryDiv").html(summaryTable + '
' + accountsTables); -} function createAnalysisTable() { function getHighestPriceBeforeDate(Instrument, maxDT) { if (Instrument.DailyData) { @@ -1465,15 +1367,16 @@ function createIndexMarquee() { //if (i.InstrumentType == 'INDEX' || i.InstrumentType == 'FUTURE') { if (i.ShowInMarquee == 'A') { let openOrClosed = Instruments.MarketIsOpen(i) == 1 ? 'open' : 'closed'; + let label = '' + i.DisplayName + ''; if (i.SingleDayPreviousClose) { let percentChange = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100; if (percentChange < 0) { - t.push('' + i.DisplayName + '
' + i.CurrentPrice + '
' + percentChange.toFixed(1) + '%'); + t.push('' + label + '
' + i.CurrentPrice + '
' + percentChange.toFixed(1) + '%'); } else { - t.push('' + i.DisplayName + '
' + i.CurrentPrice + '
+' + percentChange.toFixed(1) + '%'); + t.push('' + label + '
' + i.CurrentPrice + '
+' + percentChange.toFixed(1) + '%'); } } else { - t.push('' + i.DisplayName + '
' + i.CurrentPrice + '
'); + t.push('' + label + '
' + i.CurrentPrice + '
'); } } } diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js b/Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js new file mode 100644 index 0000000..2800a9e --- /dev/null +++ b/Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js @@ -0,0 +1,384 @@ +'use strict'; + +import { formatAmount, getPercentCell } from "./SharePrices_Common.js"; + +/* +function createAccountsTables() { + function getAccounts() { + let accounts = []; + function indexOfAccount(accountName) { return accounts.map(function (item) { return item.accountName; }).indexOf(accountName); }; + function getAccount(accountName) { return accounts[indexOfAccount(accountName)]; }; + function addAccountHolding(instrument, exchangeRate, holding) { + let a = getAccount(holding.AccountName); + if (!a) { + a = { accountName: holding.AccountName, holdings: [], totalCostGBP: 0, totalValueGBP: 0, totalGainGBP: 0, totalCashGBP: 0 }; + accounts.push(a); + } + let h = { + instrumentName: instrument.InstrumentName, + symbol: instrument.Symbol, + instrumentType: instrument.InstrumentType, + currency: instrument.Currency, + purchaseDate: holding.PurchaseDate, + purchasePricePerUnit: holding.PurchasePricePerUnit, + noUnits: holding.NoUnits, + cost: holding.NoUnits * holding.PurchasePricePerUnit + } + //if (exchangeRate) { + //h.costGBP = h.cost / exchangeRate; + h.costGBP = holding.BookCostGBP; + if (instrument.InstrumentType != 'CURRENCY') { + a.totalCostGBP += holding.BookCostGBP; + } + //} + if (instrument.CurrentPrice) { + h.currentPrice = instrument.CurrentPrice; + h.currentValue = holding.NoUnits * instrument.CurrentPrice; + h.gain = h.currentValue - h.cost; + if (exchangeRate) { + h.currentValueGBP = h.currentValue / exchangeRate; + //h.gainGBP = h.gain / exchangeRate; + h.gainGBP = h.currentValueGBP - h.costGBP; + if (instrument.InstrumentType != 'CURRENCY') { + a.totalGainGBP += h.gainGBP; + a.totalValueGBP += h.currentValueGBP; + } + } + } + if (instrument.InstrumentType == 'CURRENCY') { + //Don't add cash holdings - just add the cash value to the account + a.totalCashGBP += h.currentValueGBP; + } else { + a.holdings.push(h); + } + } + + for (let ix = 0; ix < Instruments.Data.length; ix++) { + let i = Instruments.Data[ix]; + let exchangegRate = Instruments.GetExchangeRate(i.Currency, 'GBP'); + for (let hx = 0; hx < i.Holdings.length; hx++) { + let h = i.Holdings[hx]; + if (h.SoldDate == null) { + addAccountHolding(i, exchangegRate, h); + } + } + } + + return accounts; + } + + let accounts = getAccounts(); + + //Sort the accounts array by accountName + accounts.sort(function (a, b) { + if (a.accountName < b.accountName) return -1; + if (a.accountName > b.accountName) return 1; + return 0; + }); + + //let summaryTable = ""; + let summaryTable = "
Account NameNo. HoldingsTotal Cost (GBP)Current ValueTotal Gain
" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + let accountsTables = '' + let tableIDs = ['accountsSummaryTable']; + let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked'); + let altRow = 1; + for (let ax = 0; ax < accounts.length; ax++) { + let a = accounts[ax]; + let accountGainsGBP = 0; + let accountLossesGBP = 0; + let accountValueGBP = 0; + let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit'; + if (a.holdings.length > 0) { + altRow = !altRow; + + accountsTables += '
' + a.accountName + '
' + let tableID = 'accountTable' + ax; + tableIDs.push(tableID); + accountsTables += '
Account NameNo. HoldingsTotal Cost (GBP)Total Gains (GBP)Total Losses (GBP)Current Investements ValueNET GainCash Balance GBPTotal Value GBP
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + let accountAltRow = 1; + for (let hx = 0; hx < a.holdings.length; hx++) { + let h = a.holdings[hx]; + accountAltRow = !accountAltRow; + let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit'; + let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit'; + if (h.gainGBP > 0) { + accountGainsGBP += h.gainGBP; + } else { + accountLossesGBP += h.gainGBP; + } + accountValueGBP += h.currentValueGBP; + accountsTables += '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + //'' + + '' + + ''; + } + //Add the Total Gains + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + ''; + //Add the Total Losses + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + ''; + //Add the NET Gains summary line + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + '' + + '' + + ''; + accountsTables += '
NameSymbolBuy DateNo UnitsBuy PriceCurrent PriceBook CostApprox Book Cost £Current ValueCurrent Value £GainGain £Gain %
' + h.instrumentName + '' + h.symbol + '' + new Date(h.purchaseDate).yyyymmdd() + '' + h.noUnits + '' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '' + formatAmount(h.currentPrice, h.currency, 2) + '' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '' + formatAmount(h.costGBP, 'GBP', 2) + '' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '' + formatAmount(h.currentValueGBP, 'GBP', 2) + '' + formatAmount(h.gain, h.currency, 2) + '' + formatAmount(h.gainGBP, 'GBP', 2) + '' + ((h.gain / h.cost) * 100).toFixed(1) + '%' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%
Total Gains' + formatAmount(accountGainsGBP, 'GBP', 2) + ' 
Total Losses' + formatAmount(accountLossesGBP, 'GBP', 2) + ' 
Account Value:' + formatAmount(accountValueGBP, 'GBP', 2) + 'NET Gain' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + ' 
'; + } + + //Add an entry to the summary table + if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) { + summaryTable += "" + a.accountName + "" + + "" + a.holdings.length + "" + + "" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(accountGainsGBP, 'GBP', 2) + "" + + "" + formatAmount(accountLossesGBP, 'GBP', 2) + "" + + "" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "" + + ""; + } + } + summaryTable += ""; + $("#accountsSummaryDiv").html(summaryTable + '
' + accountsTables); +} +*/ +export function createAccountsTables(Instruments) { + function getAccounts() { + let accounts = []; + function indexOfAccount(accountName) { return accounts.map(function (item) { return item.accountName; }).indexOf(accountName); }; + function getAccount(accountName) { return accounts[indexOfAccount(accountName)]; }; + function addAccountHolding(instrument, exchangeRate, holding) { + let a = getAccount(holding.AccountName); + if (!a) { + a = { accountName: holding.AccountName, holdings: [], totalCostGBP: 0, totalValueGBP: 0, totalGainGBP: 0, totalCashGBP: 0 }; + accounts.push(a); + } + let h = { + instrumentName: instrument.InstrumentName, + symbol: instrument.Symbol, + instrumentType: instrument.InstrumentType, + currency: instrument.Currency, + purchaseDate: holding.PurchaseDate, + purchasePricePerUnit: holding.PurchasePricePerUnit, + noUnits: holding.NoUnits, + cost: holding.NoUnits * holding.PurchasePricePerUnit + } + //if (exchangeRate) { + //h.costGBP = h.cost / exchangeRate; + h.costGBP = holding.BookCostGBP; + if (instrument.InstrumentType != 'CURRENCY') { + a.totalCostGBP += holding.BookCostGBP; + } + //} + if (instrument.CurrentPrice) { + h.currentPrice = instrument.CurrentPrice; + h.currentValue = holding.NoUnits * instrument.CurrentPrice; + h.gain = h.currentValue - h.cost; + if (exchangeRate) { + h.currentValueGBP = h.currentValue / exchangeRate; + //h.gainGBP = h.gain / exchangeRate; + h.gainGBP = h.currentValueGBP - h.costGBP; + if (instrument.InstrumentType != 'CURRENCY') { + a.totalGainGBP += h.gainGBP; + a.totalValueGBP += h.currentValueGBP; + } + } + } + if (instrument.InstrumentType == 'CURRENCY') { + //Don't add cash holdings - just add the cash value to the account + a.totalCashGBP += h.currentValueGBP; + } else { + a.holdings.push(h); + } + } + + for (let ix = 0; ix < Instruments.Data.length; ix++) { + let i = Instruments.Data[ix]; + let exchangegRate = Instruments.GetExchangeRate(i.Currency, 'GBP'); + for (let hx = 0; hx < i.Holdings.length; hx++) { + let h = i.Holdings[hx]; + if (h.SoldDate == null) { + addAccountHolding(i, exchangegRate, h); + } + } + } + + return accounts; + } + + let accounts = getAccounts(); + + //Sort the accounts array by accountName + accounts.sort(function (a, b) { + if (a.accountName < b.accountName) return -1; + if (a.accountName > b.accountName) return 1; + return 0; + }); + + //let summaryTable = ""; + let summaryTable = "
Account NameNo. HoldingsTotal Cost (GBP)Current ValueTotal Gain
" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + let accountsTables = '' + let tableIDs = ['accountsSummaryTable']; + let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked'); + let altRow = 1; + for (let ax = 0; ax < accounts.length; ax++) { + let a = accounts[ax]; + let accountGainsGBP = 0; + let accountLossesGBP = 0; + let accountValueGBP = 0; + let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit'; + if (a.holdings.length > 0) { + altRow = !altRow; + + accountsTables += '
' + a.accountName + '
' + let tableID = 'accountTable' + ax; + tableIDs.push(tableID); + accountsTables += '
Account NameNo. HoldingsTotal Cost (GBP)Total Gains (GBP)Total Losses (GBP)Current Investements ValueNET GainCash Balance GBPTotal Value GBP
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + let accountAltRow = 1; + for (let hx = 0; hx < a.holdings.length; hx++) { + let h = a.holdings[hx]; + accountAltRow = !accountAltRow; + let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit'; + let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit'; + if (h.gainGBP > 0) { + accountGainsGBP += h.gainGBP; + } else { + accountLossesGBP += h.gainGBP; + } + accountValueGBP += h.currentValueGBP; + accountsTables += '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + //'' + + '' + + ''; + } + //Add the Total Gains + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + ''; + //Add the Total Losses + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + ''; + //Add the NET Gains summary line + accountAltRow = !accountAltRow; + accountsTables += '' + + '' + + '' + + '' + + '' + + '' + + ''; + accountsTables += '
NameSymbolBuy DateNo UnitsBuy PriceCurrent PriceBook CostApprox Book Cost £Current ValueCurrent Value £GainGain £Gain %
' + h.instrumentName + '' + h.symbol + '' + new Date(h.purchaseDate).yyyymmdd() + '' + h.noUnits + '' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '' + formatAmount(h.currentPrice, h.currency, 2) + '' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '' + formatAmount(h.costGBP, 'GBP', 2) + '' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '' + formatAmount(h.currentValueGBP, 'GBP', 2) + '' + formatAmount(h.gain, h.currency, 2) + '' + formatAmount(h.gainGBP, 'GBP', 2) + '' + ((h.gain / h.cost) * 100).toFixed(1) + '%' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%
Total Gains' + formatAmount(accountGainsGBP, 'GBP', 2) + ' 
Total Losses' + formatAmount(accountLossesGBP, 'GBP', 2) + ' 
Account Value:' + formatAmount(accountValueGBP, 'GBP', 2) + 'NET Gain' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + ' 
'; + } + + //Add an entry to the summary table + if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) { + summaryTable += "" + a.accountName + "" + + "" + a.holdings.length + "" + + "" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(accountGainsGBP, 'GBP', 2) + "" + + "" + formatAmount(accountLossesGBP, 'GBP', 2) + "" + + "" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "" + + "" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "" + + ""; + } + } + summaryTable += ""; + $("#accountsSummaryDiv").html(summaryTable + '
' + accountsTables); +} diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices_Common.js b/Websites/SharePrices/SharePrices/scripts/SharePrices_Common.js index 0aea3eb..0252f2d 100644 --- a/Websites/SharePrices/SharePrices/scripts/SharePrices_Common.js +++ b/Websites/SharePrices/SharePrices/scripts/SharePrices_Common.js @@ -84,4 +84,13 @@ export function generateAxisBreaks(data, maxGapSize, breakSize) { } } return breaks; -} \ No newline at end of file +} + +export function getPercentCell(backgroundColor, cellWidth, valuePercent, label) { + let labelText = label ? label : (valuePercent.toFixed(1) + '%'); + let barWidth = ((valuePercent / 100) * cellWidth); + return '' + + '
 
' + + '
' + labelText + '
' + + ''; +} diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js b/Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js index 694603a..dc08e44 100644 --- a/Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js +++ b/Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js @@ -1,7 +1,7 @@ 'use strict'; -import { formatAmount } from "./SharePrices_Common.js"; - +import { formatAmount, getPercentCell } from "./SharePrices_Common.js"; +/* export function createHoldingsTable(Instruments, vars) { function getHoldings() { function aggregateHoldings(i) { @@ -302,12 +302,17 @@ export function createHoldingsTable(Instruments, vars) { $('#holdingsTableDiv').html(tbl); $("#tblMainHoldings").tablesorter({ ignoreCase: false }); - $("#tblMainHoldings").trigger("sorton", Cookies.get('sortHoldings')); + + //let sortOrder = Cookies.get('sortHoldings'); + let sortOrder = JSON.parse(Cookies.get('sortHoldings')); + $("#tblMainHoldings").trigger("sorton", [sortOrder]); + $('#tblMainHoldings').on('sortEnd', function (event) { // Prints the current sort order to the console if (event.target.config.sortList.length > 3) { console.info({ "Setting sortHoldings Cookie (array too long?)": event.target.config.sortList }); } + //console.info({MSG: "Saving sortHolding cookie", Cookie: event.target.config.sortList}); Cookies.set('sortHoldings', event.target.config.sortList, { 'sameSite': 'strict' }); }); } @@ -354,14 +359,16 @@ export function createHoldingsTable(Instruments, vars) { //$("#" + rowID).addClass("highlighted"); } } - function getPercentCell(backgroundColor, cellWidth, valuePercent, label) { - let labelText = label ? label : (valuePercent.toFixed(1) + '%'); - let barWidth = ((valuePercent / 100) * cellWidth); - return '' + - '
 
' + - '
' + labelText + '
' + - ''; - } + + //function getPercentCell(backgroundColor, cellWidth, valuePercent, label) { + // let labelText = label ? label : (valuePercent.toFixed(1) + '%'); + // let barWidth = ((valuePercent / 100) * cellWidth); + // return '' + + // '
 
' + + // '
' + labelText + '
' + + // ''; + //} + function profitOrLoss(value) { return value >= 0 ? "profit" : "loss"; } @@ -495,35 +502,6 @@ export function createHoldingsTable(Instruments, vars) { var resort = true; $("#tblMainHoldings").trigger("update", [resort]); - //Update the daily holdings value table in the DB - if (totalValueGBP != vars.lastTotalHoldingsValue) { - $.ajax({ - type: "POST", - contentType: "application/json", - url: "SharePrices.aspx/SetTotalHoldingsValue", - dataType: "json", - data: JSON.stringify({ TotalValue: totalValueGBP }), - success: function (response) { - vars.previousDay = response.PreviousDay; - vars.previousClose = response.PreviousClose; - vars.previousWeek = response.PreviousWeekDate; - vars.previousWeekClose = response.PreviousWeekClose; - - //Update the total holdings span in the site banner - let formattedAmount = formatAmount(totalValueGBP, "GBP", 0); - let holdingsHTML = 'Total holdings: ' + '' + formattedAmount + ' '; - holdingsHTML += '+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + ' (d)'; - holdingsHTML += ' / +' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + ' (w)'; - $("#spnTotalHoldings").html(holdingsHTML); - //Set the page title - document.title = formattedAmount + ' - ' + vars.initialPageTitle; - - vars.lastTotalHoldingsValue = totalValueGBP; - }, - failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); } - }); - } - //Create / update the currency allocation pie chart function labelFormatter(label, series) { return "
" + label + "
" + Math.round(series.percent) + "%
" + formatAmount(series.data[0][1], 'GBP', 0) + "
"; @@ -589,3 +567,645 @@ function createCategoryAggregator() { }; return aggregator; } +*/ + +export async function updateHoldingsTables(instruments, vars) { + createNewHoldingsTable(instruments, vars); + createAggregatedHoldingsTable(instruments, vars); + createWatchlistTable(instruments); +} +async function createNewHoldingsTable(instruments, vars) { + const noColumns = 16; + + function createNewHoldingsTable() { + let table = $('#newHoldingsTable'); + if (table.length == 0) { + let tblDef = '' + + getHeaderRow() + + '' + + '' + + '' + + '' + + '
 
New Holdings Table Grand Totals Row
New Holdings Cash Total Row
New Holdings Grand Total Row
'; + $('#newHoldingsTableDiv').html(tblDef); + } + } + function createTaxBucketSubTable(taxBucket) { + let subtableID = 'taxBucket_' + taxBucket.replaceAll(' ', '_'); + //let subtable = $('#' + subtableID); + let subtable = $('#' + subtableID + '_Summary'); + if (subtable.length == 0) { + //let rowsDef = '' + taxBucket + '\n' + + //let rowsDef = getHeaderRow() + + //let dt = (new Date()).yyyymmddhhmmss(); + //let rowsDef = ' ' + dt + '\n' + //Spacer row + let rowsDef = ' '; //Sub Table Summary Row + //$(rowsDef).insertBefore('#newHoldingsTable_InvestmentsGrandTotals'); + $(rowsDef).insertBefore('#newHoldingsTable_EndSpacer'); + } + } + function getHeaderRow() { + return '' + + 'Account ' + + 'Type ' + + 'Name ' + + 'Symbol ' + + 'Ccy ' + + 'No Units ' + + 'Avg Buy
Price ' + + 'Previous
Close ' + + 'Current
Price ' + + 'Base Cost ' + + 'Current Value ' + + 'Gain ' + + 'Gain % ' + + 'Allocation ' + + 'Today's
Gain £' + + 'Today's
Gain % ' + + '' + } + function getContentRow(row, instrument, scaledAllocation, allocationPercent) { + let openOrClosed = instruments.MarketIsOpen(instrument) == 1 ? 'open' : 'closed'; + let rowID = 'newHoldingsRow_' + vars.currentHoldingsRows.rowID(row); + + let type = row.InstrumentType == 'CURRENCY' ? '$' : row.InstrumentType.substring(0, 1); + let totalProfitOrLoss = row.UnrealisedGainGBP < 0 ? 'loss' : 'profit'; + + let todaysGainGBP = 0; + let todaysGainPct = 0; + let todaysProfitOrLoss = 'profit'; + if (instrument.SingleDayPreviousClose) { + todaysGainGBP = (row.CurrentPriceGBP - (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) * row.NoUnits; + todaysGainPct = ((row.CurrentPriceGBP / (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) - 1) * 100; + todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit'; + } + + let cryptoHoldingClass = instrument.InstrumentType == 'CRYPTOCURRENCY' ? ' cryptoHoldingRow' : ''; + + return '' + + '' + row.AccountName + '' + + '' + type + '' + + '' + row.InstrumentName + '' + + //'' + row.Symbol + '' + + '' + instrument.Symbol + '' + '' + + '' + row.InstrumentCurrency + '' + + '' + row.NoUnits.autoScale() + '' + + '' + formatAmount(row.AvgPriceGBP, 'GBP', 2) + '' + + '' + formatAmount(instrument.SingleDayPreviousClose / row.CurrentExchangeRate, 'GBP', 2) + '' + + '' + formatAmount(row.CurrentPriceGBP, 'GBP', 2) + '' + + '' + formatAmount(row.BaseCostGBP, 'GBP', 0) + '' + + '' + formatAmount(row.CurrentValueGBP, 'GBP', 0) + '' + + '' + formatAmount(row.UnrealisedGainGBP, 'GBP', 0) + '' + + '' + formatAmount(row.UnrealisedGainPct, '', 1) + '%' + //Gain % + //'' + ' ' + '' + //Allocation + getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation + '' + ((todaysGainGBP == 0) ? ' ' : formatAmount(todaysGainGBP, 'GBP', 0)) + '' + //Today's Gain £ + '' + ((todaysGainPct == 0) ? ' ' : formatAmount(todaysGainPct, '', 1) + '%') + '' + //Today's Gain % + '' + } + function updateTaxBucketSubtotals(taxBucket, baseCostGBP, currentValueGBP, todaysGain) { + let subtableID = 'taxBucket_' + taxBucket.replaceAll(' ', '_'); + let rowID = subtableID + '_Summary'; + let profitOrLoss = currentValueGBP < baseCostGBP ? 'loss' : 'profit'; + let rowDef = '' + + '' + taxBucket + ' Sutotal' + + '' + formatAmount(baseCostGBP, 'GBP', 2) + '' + //BaseCost + '' + formatAmount(currentValueGBP, 'GBP', 2) + '' + //CurrentValue + '' + formatAmount(currentValueGBP - baseCostGBP, 'GBP', 0) + '' + //GainGBP + '' + formatAmount((currentValueGBP - baseCostGBP) / baseCostGBP * 100, '', 1) + '%' + //Gain% + ' ' + + '' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '' + + ' ' + + ''; + + $('#' + rowID).replaceWith(rowDef); + } + function updateInvestmentsGrandTotals(totalBaseCostGBP, totalCurrentValueGBP, todaysGain) { + let profitOrLoss = totalCurrentValueGBP < totalBaseCostGBP ? 'loss' : 'profit'; + let rowID = 'newHoldingsTable_InvestmentsGrandTotals'; + let rowDef = '' + + 'Investments Total' + + '' + formatAmount(totalBaseCostGBP, 'GBP', 0) + '' + //BaseCost + '' + formatAmount(totalCurrentValueGBP, 'GBP', 0) + '' + //CurrentValue + '' + formatAmount(totalCurrentValueGBP - totalBaseCostGBP, 'GBP', 0) + '' + //GainGBP + '' + formatAmount((totalCurrentValueGBP - totalBaseCostGBP) / totalBaseCostGBP * 100, '', 1) + '%' + //Gain% + ' ' + + '' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateCashTotal(totalCashGBP) { + let rowID = 'newHoldingsTable_CashTotal'; + //let rowDef = '' + + let rowDef = '' + + 'Cash Total' + + ' ' + + '' + formatAmount(totalCashGBP, 'GBP', 0) + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateGrandTotal(grandTotalGBP) { + let rowID = 'newHoldingsTable_GrandTotal'; + let rowDef = '' + + 'Grand Total' + + ' ' + + '' + formatAmount(grandTotalGBP, 'GBP', 0) + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateTableRow(row, newContent) { + let rowID = 'newHoldingsRow_' + vars.currentHoldingsRows.rowID(row).replaceAll('.', '\\.'); + let tr = $('#' + rowID); + if (tr.length > 0) { + //Row exists... update content + $(tr).replaceWith(newContent); + } else { + //Need to add row + createNewHoldingsTable(); + createTaxBucketSubTable(row.TaxBucket); + $(newContent).insertBefore('#taxBucket_' + row.TaxBucket.replaceAll(' ', '_') + '_Summary'); + } + //Add the highlighted class so the updated row will flash + $('#' + rowID).addClass("highlighted"); + } + + let holdings = {}; + try { + holdings = await $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/GetCurrentHoldings" + }); + } + catch (error) { + console.error({ MSG: "Error getting current holdings", Error: error }); + } + + //Save the current holdings for use elsewehere + vars.currentHoldings = holdings; + + //Calculate totals before creating table rows + let totalCashGBP = 0; + let totalBaseCost = 0; + let totalCurrentValue = 0; + let maxCurrentValue = 0; + for (let rowNo = 0; rowNo < holdings.length; rowNo++) { + let r = holdings[rowNo]; + + if(r.InstrumentType == 'CURRENCY') { + totalCashGBP += r.CurrentValueGBP; + } else { + totalBaseCost += r.BaseCostGBP; + totalCurrentValue += r.CurrentValueGBP; + if (r.CurrentValueGBP > maxCurrentValue) maxCurrentValue = r.CurrentValueGBP; + } + } + let maxAllocationPercent = (maxCurrentValue / totalCurrentValue) * 100; + let allocationScaleFactor = 100 / maxAllocationPercent; + + //Create the holdings table rows + let currentTaxBucket = ''; + let bucketBaseCost = 0; + let bucketCurrentValue = 0; + let bucketTodaysGain = 0; + let totalTodaysGain = 0; + for (let rowNo = 0; rowNo < holdings.length; rowNo++) { + let r = holdings[rowNo]; + if(r.InstrumentType != 'CURRENCY') { + let instrument = instruments.GetSymbol(r.Symbol); + + let allocationPercent = r.CurrentValueGBP / totalCurrentValue * 100; + let scaledAllocation = allocationScaleFactor * allocationPercent; + + if (r.TaxBucket != currentTaxBucket) { + if (currentTaxBucket != '') { + //Update the subtotal row for the previous tax bucket + updateTaxBucketSubtotals(currentTaxBucket, bucketBaseCost, bucketCurrentValue, bucketTodaysGain); + } + + currentTaxBucket = r.TaxBucket; + bucketBaseCost = 0; + bucketCurrentValue = 0; + bucketTodaysGain = 0; + } + bucketBaseCost += r.BaseCostGBP; + bucketCurrentValue += r.CurrentValueGBP; + let todaysGain = instrument.SingleDayPreviousClose ? (r.CurrentPriceGBP - (instrument.SingleDayPreviousClose / r.CurrentExchangeRate)) * r.NoUnits : 0; + bucketTodaysGain += todaysGain; + + totalTodaysGain += todaysGain; + + //Has this row changed? + let currentRow = vars.currentHoldingsRows.GetRow(r); + let newContent = getContentRow(r, instrument, scaledAllocation, allocationPercent); + if (!currentRow) { + vars.currentHoldingsRows.Data.push({ attributes: r, content: newContent }); + updateTableRow(r, newContent); + } else if (currentRow.content != newContent) { + currentRow.attributes = r; + currentRow.content = newContent; + updateTableRow(r, newContent); + } + } + + /* + TaxBucket + AccountName + IsTaxable + InstrumentType + Symbol + InstrumentName + InstrumentCurrency + NoUnits + BaseCostGBP + AvgPriceGBP + CurrentPriceGBP + CurrentValueGBP + UnrealisedGainGBP + */ + } + + let totalValueGBP = totalCurrentValue + totalCashGBP; + updateTaxBucketSubtotals(currentTaxBucket, bucketBaseCost, bucketCurrentValue, bucketTodaysGain); + updateInvestmentsGrandTotals(totalBaseCost, totalCurrentValue, totalTodaysGain); + updateCashTotal(totalCashGBP); + updateGrandTotal(totalValueGBP); + + //Show or hide the Tax Bucket subtotals + let subtotalsDisplay = Cookies.get('showSubtotals'); + if (subtotalsDisplay == 'true') { + $("#newHoldingsTable tr.holdingsSubtotal").show(); + } else { + $("#newHoldingsTable tr.holdingsSubtotal").hide(); + } + + //Show or hide cryptocurrency holdings + let cryptoDisplay = Cookies.get('showCryptoHoldings'); + if (cryptoDisplay == 'true') { + $("#newHoldingsTable tr.cryptoHoldingRow").show(); + } else { + $("#newHoldingsTable tr.cryptoHoldingRow").hide(); + } + + //Remove the highlight from all rows/cells + $("#newHoldingsTable").find(".highlighted").removeClass("highlighted", 1200); + + //Update the daily holdings value table in the DB + if (totalValueGBP > 0 && totalValueGBP != vars.lastTotalHoldingsValue) { + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/SetTotalHoldingsValue", + dataType: "json", + data: JSON.stringify({ TotalValue: totalValueGBP }), + success: function (response) { + vars.previousDay = response.PreviousDay; + vars.previousClose = response.PreviousClose; + vars.previousWeek = response.PreviousWeekDate; + vars.previousWeekClose = response.PreviousWeekClose; + + //Update the total holdings span in the site banner + let formattedAmount = formatAmount(totalValueGBP, "GBP", 0); + let holdingsHTML = 'Total holdings: ' + '' + formattedAmount + ' '; + holdingsHTML += '+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + ' (d)'; + holdingsHTML += ' / +' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + ' (w)'; + $("#spnTotalHoldings").html(holdingsHTML); + //Set the page title + document.title = formattedAmount + ' - ' + vars.initialPageTitle; + + vars.lastTotalHoldingsValue = totalValueGBP; + }, + failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); } + }); + } + +} +async function createAggregatedHoldingsTable(instruments, vars) { + const noColumns = 15; + + function createAggHoldingsTable() { + let table = $('#aggHoldingsTable'); + if (table.length == 0) { + let tblDef = '' + + getHeaderRow() + + '' + + '' + + '' + + '' + + '' + + '
 
Aggregated Holdings Table Grand Totals Row
Aggregated Holdings Cash Total Row
Aggregated Holdings Grand Total Row
'; + $('#aggHoldingsTableDiv').html(tblDef); + } + } + function getHeaderRow() { + return '' + + 'Type ' + + 'Name ' + + 'Symbol ' + + 'Ccy ' + + 'No Units ' + + 'Avg Buy
Price ' + + 'Previous
Close ' + + 'Current
Price ' + + 'Base Cost ' + + 'Current Value ' + + 'Gain ' + + 'Gain % ' + + 'Allocation ' + + 'Today's
Gain £' + + 'Today's
Gain % ' + + '' + } + function getContentRow(row, instrument, scaledAllocation, allocationPercent) { + let openOrClosed = instruments.MarketIsOpen(instrument) == 1 ? 'open' : 'closed'; + let rowID = 'aggHoldingsRow_' + vars.currentAggHoldingsRows.rowID(row); + + let type = row.InstrumentType == 'CURRENCY' ? '$' : row.InstrumentType.substring(0, 1); + let totalProfitOrLoss = row.UnrealisedGainGBP < 0 ? 'loss' : 'profit'; + + let todaysGainGBP = 0; + let todaysGainPct = 0; + let todaysProfitOrLoss = 'profit'; + if (instrument.SingleDayPreviousClose) { + todaysGainGBP = (row.CurrentPriceGBP - (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) * row.NoUnits; + todaysGainPct = ((row.CurrentPriceGBP / (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) - 1) * 100; + todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit'; + } + + return '' + + '' + type + '' + + '' + row.InstrumentName + '' + + //'' + row.Symbol + '' + + '' + instrument.Symbol + '' + '' + + '' + row.InstrumentCurrency + '' + + '' + row.NoUnits.autoScale() + '' + + '' + formatAmount(row.AvgPriceGBP, 'GBP', 2) + '' + + '' + formatAmount(instrument.SingleDayPreviousClose / row.CurrentExchangeRate, 'GBP', 2) + '' + + '' + formatAmount(row.CurrentPriceGBP, 'GBP', 2) + '' + + '' + formatAmount(row.BaseCostGBP, 'GBP', 0) + '' + + '' + formatAmount(row.CurrentValueGBP, 'GBP', 0) + '' + + '' + formatAmount(row.UnrealisedGainGBP, 'GBP', 0) + '' + + '' + formatAmount(row.UnrealisedGainPct, '', 1) + '%' + //Gain % + //'' + ' ' + '' + //Allocation + getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation + '' + ((todaysGainGBP == 0) ? ' ' : formatAmount(todaysGainGBP, 'GBP', 0)) + '' + //Today's Gain £ + '' + ((todaysGainPct == 0) ? ' ' : formatAmount(todaysGainPct, '', 1) + '%') + '' + //Today's Gain % + '' + } + function updateInvestmentsGrandTotals(totalBaseCostGBP, totalCurrentValueGBP, todaysGain) { + let profitOrLoss = totalCurrentValueGBP < totalBaseCostGBP ? 'loss' : 'profit'; + let rowID = 'aggHoldingsTable_InvestmentsGrandTotals'; + let rowDef = '' + + 'Investments Total' + + '' + formatAmount(totalBaseCostGBP, 'GBP', 0) + '' + //BaseCost + '' + formatAmount(totalCurrentValueGBP, 'GBP', 0) + '' + //CurrentValue + '' + formatAmount(totalCurrentValueGBP - totalBaseCostGBP, 'GBP', 0) + '' + //GainGBP + '' + formatAmount((totalCurrentValueGBP - totalBaseCostGBP) / totalBaseCostGBP * 100, '', 1) + '%' + //Gain% + ' ' + + '' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateCashTotal(totalCashGBP) { + let rowID = 'aggHoldingsTable_CashTotal'; + let rowDef = '' + + 'Cash Total' + + ' ' + + '' + formatAmount(totalCashGBP, 'GBP', 0) + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateGrandTotal(grandTotalGBP) { + let rowID = 'aggHoldingsTable_GrandTotal'; + + let lSF = ''; + if (vars.fetchTiming.lastSuccessfulFetch) { + let lastFetchAge = ((new Date()) - vars.fetchTiming.lastSuccessfulFetch); + let staleFetch = ((new Date()) - vars.fetchTiming.lastSuccessfulFetch) > (vars.fetchTiming.fetchIntervalSingleDay * 1.5); + lSF = (vars.fetchTiming.lastSuccessfulFetch ? ' - Last Successful Fetch: ' + (staleFetch ? '' : '') + vars.fetchTiming.lastSuccessfulFetch.yyyymmddhhmmss() + (staleFetch ? '' : '') : ''); + } + //let lastUpdatedCell = 'Updated: ' + new Date().yyyymmddhhmmss() + lSF + ''; + + + let rowDef = '' + + //'Grand Total' + + 'Updated: ' + new Date().yyyymmddhhmmss() + lSF + '' + + 'Grand Total' + + ' ' + + '' + formatAmount(grandTotalGBP, 'GBP', 0) + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + $('#' + rowID).replaceWith(rowDef); + } + function updateTableRow(row, newContent) { + let rowID = 'aggHoldingsRow_' + vars.currentAggHoldingsRows.rowID(row).replaceAll('.', '\\.'); + let tr = $('#' + rowID); + if (tr.length > 0) { + //Row exists... update content + $(tr).replaceWith(newContent); + } else { + //Need to add row + createAggHoldingsTable(); + //$(newContent).insertBefore('#aggHoldingsTable_InvestmentsGrandTotals'); + $(newContent).insertBefore('#aggHoldingsTable_EndSpacer'); + } + //Add the highlighted class so the updated row will flash + $('#' + rowID).addClass("highlighted"); + } + + let holdings = {}; + try { + holdings = await $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/GetAggregatedCurrentHoldings" + }); + } + catch (error) { + console.error({ MSG: "Error getting aggregated current holdings", Error: error }); + } + + //Calculate totals before creating table rows + let totalCashGBP = 0; + let totalBaseCost = 0; + let totalCurrentValue = 0; + let maxCurrentValue = 0; + for (let rowNo = 0; rowNo < holdings.length; rowNo++) { + let r = holdings[rowNo]; + + if (r.InstrumentType == 'CURRENCY') { + totalCashGBP += r.CurrentValueGBP; + } else { + totalBaseCost += r.BaseCostGBP; + totalCurrentValue += r.CurrentValueGBP; + if (r.CurrentValueGBP > maxCurrentValue) maxCurrentValue = r.CurrentValueGBP; + } + } + let maxAllocationPercent = (maxCurrentValue / totalCurrentValue) * 100; + let allocationScaleFactor = 100 / maxAllocationPercent; + + //Create the holdings table rows + let totalTodaysGain = 0; + for (let rowNo = 0; rowNo < holdings.length; rowNo++) { + let r = holdings[rowNo]; + if (r.InstrumentType != 'CURRENCY') { + let instrument = instruments.GetSymbol(r.Symbol); + + let allocationPercent = r.CurrentValueGBP / totalCurrentValue * 100; + let scaledAllocation = allocationScaleFactor * allocationPercent; + + let todaysGain = instrument.SingleDayPreviousClose ? (r.CurrentPriceGBP - (instrument.SingleDayPreviousClose / r.CurrentExchangeRate)) * r.NoUnits : 0; + totalTodaysGain += todaysGain; + + //Has this row changed? + let currentRow = vars.currentAggHoldingsRows.GetRow(r); + let newContent = getContentRow(r, instrument, scaledAllocation, allocationPercent); + if (!currentRow) { + vars.currentAggHoldingsRows.Data.push({ attributes: r, content: newContent }); + updateTableRow(r, newContent); + } else if (currentRow.content != newContent) { + currentRow.attributes = r; + currentRow.content = newContent; + updateTableRow(r, newContent); + } + } + + /* + InstrumentType + Symbol + InstrumentName + InstrumentCurrency + NoUnits + BaseCostGBP + AvgPriceGBP + CurrentPriceGBP + CurrentValueGBP + UnrealisedGainGBP + */ + } + + let totalValueGBP = totalCurrentValue + totalCashGBP; + updateInvestmentsGrandTotals(totalBaseCost, totalCurrentValue, totalTodaysGain); + updateCashTotal(totalCashGBP); + updateGrandTotal(totalValueGBP); + + /* + //Show or hide the Tax Bucket subtotals + let subtotalsDisplay = Cookies.get('showSubtotals'); + if (subtotalsDisplay == 'true') { + $("#newHoldingsTable tr.holdingsSubtotal").show(); + } else { + $("#newHoldingsTable tr.holdingsSubtotal").hide(); + } + */ + + //Remove the highlight from all rows/cells + $("#aggHoldingsTable").find(".highlighted").removeClass("highlighted", 1200); + +} +function createWatchlistTable(Instruments) { + function addTableRow(rowID, content, tbodyID, highlightUpdates) { + let r = $("#" + rowID); + let newText = $(content).text(); //$(content).text(); + if (r.length) { + let currentContent = r.text(); + if (currentContent != newText) { //Only update row if the content has changed + $("#" + rowID).replaceWith(content); + if (highlightUpdates) { + $("#" + rowID).addClass("highlighted"); + } + } + } else { + $("#" + tbodyID).append(content); //.addClass("highlighted"); + //$("#" + rowID).addClass("highlighted"); + } + } + + let t = $("#tblWatchlist"); + if (!t.length) /* + return; + } else */{ + let tbl = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
TypeNameSymbolCcyCurrent PriceToday's GainToday's Gain %Since Last Sold' + + '
'; + + $('#watchlistTableDiv').html(tbl); + + $("#tblWatchlist").tablesorter({ ignoreCase: false }); + $("#tblWatchlist").trigger("sorton", Cookies.get('sortWatchlist')); + $('#tblWatchlist').on('sortEnd', function (event) { + // Prints the current sort order to the console + Cookies.set('sorWatchlist', event.target.config.sortList, { 'sameSite': 'strict' }); + }); + } + + let altRow = 1; + for (let n = 0; n < Instruments.Data.length; n++) { + if (Instruments.Data[n].WatchlistID) { + altRow = !altRow; + let wi = Instruments.Data[n]; + try { + let type = wi.InstrumentType == 'CURRENCY' ? '$' : wi.InstrumentType.substring(0, 1); + let openOrClosed = Instruments.MarketIsOpen(wi) == 1 ? 'open' : 'closed'; + let profitOrLoss = wi.CurrentPrice < wi.SingleDayPreviousClose ? 'loss' : 'profit'; + let rowID = "watchlistRow_" + wi.DisplayOrder; + + let sinceLastSold = ''; + if (wi.LastSoldPrice) { + let delta = (wi.CurrentPrice - wi.LastSoldPrice) / wi.LastSoldPrice * 100; + let tooltip = new Date(wi.LastSoldDate).yyyymmdd() + ' @ ' + wi.LastSoldPrice.toFixed(2); + sinceLastSold = '' + (delta).toFixed(1) + '%'; + } + + //Type, Name, Symbol, Ccy, Current Price, Today's Gain, Today's Gain % + let row = '' + + '' + type + '' + + //'' + wi.InstrumentName + '' + + '' + wi.DisplayName + '' + + '' + wi.Symbol + '' + '' + + '' + wi.Currency + '' + + '' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice, wi.Currency, 2) : '') + '' + //Current Price + (Instruments.marketIsOpen == 0 ? '' : '' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice - wi.SingleDayPreviousClose, wi.Currency, 2) : '') + '') + //Today's Gain + (Instruments.marketIsOpen == 0 ? '' : '' + (wi.CurrentPrice ? ((wi.CurrentPrice - wi.SingleDayPreviousClose) / wi.SingleDayPreviousClose * 100).toFixed(1) : '') + '') + //Today's Gain % + //'' + (new Date(wi.LastSoldDate)).yyyymmdd() + '' + + sinceLastSold + + ''; + addTableRow(rowID, row, "tbWatchlist", true); + } + catch (err) { + logError("Error adding " + wi.symbol + " to watchlist table: " + err.message); + } + } + } + + //Remove the highlight from all rows/cells + $("#tbWatchlist").find(".highlighted").removeClass("highlighted", 1200); +} \ No newline at end of file diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js b/Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js new file mode 100644 index 0000000..d6206be --- /dev/null +++ b/Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js @@ -0,0 +1,117 @@ +'use strict'; + +import { formatAmount, getPercentCell } from "./SharePrices_Common.js"; + +export async function updateLedgerTable(instruments, vars) { + const noColumns = 15; + + function createLedgerTable() { + let table = $('#ledgerTable'); + //if (table.length == 0) { + let tblDef = '' + + '' + getHeaderRow() + '' + + '' + + '
'; + $('#ledgerDiv').html(tblDef); + //} + } + function getHeaderRow() { + return '' + + 'Trade DT' + + 'Tax Bucket' + + 'Account' + + 'Symbol' + + 'Instrument Name' + + //'Instrument Currency' + + 'Action
Type' + + //'No Units' + + //'Price Per Unit GBP' + + 'Units' + + 'Trade
Value' + + 'New Pool
Total Units' + + 'New Pool
Base Cost' + + 'Realised
Gain' + + 'Is Final
Position' + + 'Notes' + + '' + } + function getContentRow(row) { + let rowID = 'ledgerRow_' + row.TradeLedgerID.toString(); + + return '' + + '' + (new Date(row.TradeDT)).yyyymmddhhmm() + '' + + '' + row.TaxBucket + '' + + '' + row.AccountName + '' + + '' + row.Symbol + '' + + '' + row.InstrumentName + '' + + //'' + row.InstrumentCurrency + '' + + '' + row.ActionType + '' + + //'' + row.NoUnits.autoScale() + '' + + //'' + formatAmount(row.PricePerUnitGBP, 'GBP', 2) + '' + + '' + row.NoUnits.autoScale() + ' @ ' + formatAmount(row.PricePerUnitGBP, 'GBP', 2) + '' + + '' + formatAmount(row.TradeValueGBP, 'GBP', 2) + '' + + '' + row.NewPoolTotalUnits.autoScale() + '' + + '' + formatAmount(row.NewPoolBaseCostGBP, 'GBP', 2) + '' + + '' + formatAmount(row.RealisedGainGBP, 'GBP', 2) + '' + + '' + row.IsFinalPosition.toString() + '' + + '' + (row.Notes == null ? '' : row.Notes) + '' + + '' + } + + //Fetch the ledger data + let ledger = {}; + try { + ledger = await $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/GetTradeHistory" + }); + } + catch (error) { + console.error({ MSG: "Error getting trade history", Error: error }); + } + + createLedgerTable(); + + //Create the ledger table rows + for (let rowNo = 0; rowNo < ledger.length; rowNo++) { + let r = ledger[rowNo]; + let newContent = getContentRow(r); + $("#ledgerTable tbody").append(newContent); + } + + //Add the tableSorter filter widget + let $table = $("#ledgerTable").tablesorter({ + //theme: 'blue', + // this is the default setting + //cssChildRow: "tablesorter-childRow", + + // initialize zebra and filter widgets + widgets: ["zebra", "filter", "stickyHeaders"], + + widgetOptions: { + // output default: '{page}/{totalPages}' + // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + //pager_output: '{startRow} - {endRow} / {filteredRows} ({totalRows})', + //pager_removeRows: false, + + // include child row content while filtering, if true + //filter_childRows: true, + // class name applied to filter row and each input + /*filter_cssFilter: 'tablesorter-filter', + + filter_filteredRow: 'filtered', + + // search from beginning + filter_startsWith: false, + // Set this option to false to make the searches case sensitive + filter_ignoreCase: true, + */ + stickyHeaders_attachTo: '#ledgerDiv', + //stickyHeaders_zIndex: 2, + zebra: [ 'odd', 'even'] + } + + }); + +}