From 5516aa1362f4c3011cc5db2ed4960ae292a4de02 Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 4 Jul 2025 12:01:26 +0100 Subject: [PATCH] Added Ledger, started removing Holdings --- .../SharePrices/config/applicationhost.config | 1026 +++++++++++++++++ Websites/SharePrices/.vs/SharePrices/v17/.suo | Bin 26624 -> 125952 bytes .../v17/DocumentLayout.backup.json | 132 ++- .../.vs/SharePrices/v17/DocumentLayout.json | 129 ++- Websites/SharePrices/SharePrices.v12.suo | Bin 61952 -> 87040 bytes .../SharePrices/App_Code/DataAccessLayer.vb | 43 +- .../SharePrices/SharePrices/SharePrices.aspx | 46 +- .../SharePrices/SharePrices.aspx.vb | 91 +- .../SharePrices/SharePrices.vbproj | 2 + .../SharePrices/SharePricesStyles.css | 185 +-- .../SharePrices/scripts/SharePrices.js | 317 ++--- .../scripts/SharePrices_Accounts.js | 384 ++++++ .../SharePrices/scripts/SharePrices_Common.js | 11 +- .../scripts/SharePrices_Holdings.js | 700 ++++++++++- .../SharePrices/scripts/SharePrices_Ledger.js | 117 ++ 15 files changed, 2739 insertions(+), 444 deletions(-) create mode 100644 Websites/SharePrices/.vs/SharePrices/config/applicationhost.config create mode 100644 Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js create mode 100644 Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js 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 d7709a5a44ea886932e6334f113555a1811d58b4..f4758ab461034bfac7db480c34b0a7d012602161 100644 GIT binary patch literal 125952 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 Sj`sd2inf-Q`hw}H)V~1pqjd`a 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 f1e3c4e92ea48fbb477415a8fdeac48cd2318b20..afff3a2949c472acddda180352ee5c7e2529c352 100644 GIT binary patch delta 6738 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 HzE}J&O(Ytg delta 4320 zcmc(h3s6+o8G!G(3+uAGkmVsNBCd!D8YIhRLl9P7SU_I#5MnhF>%(hL`q z*gPz<`x7yVnuI1IVB~Q-X=)>E&~_}-M(miRW9-Bboj8f{k&0E5xc&djy}F{AF`4P~ zFdzRp=lEj!rv^TlfmOg52#9XuG!@0WJnU#XbawK9^Duv%#qTh~A1b zD&b6t%I_R@tl{w=e5c!Rn|jhXb?psx4zACrW3AwbEY!?LExIL|5H3aL!9hL_YG+E) zw+%mDhO3#`KD0I%Q;cBZ^{gBmS-JjFI?;L!XJs&&)xfgAk^Mq%(O%|UU(_~P>&ng0 zQ1~c|FaSqxo?eArwDlX^E*QCY0eN)}Y^!FLxnE~8pDq(12ZdsLkIhI+l%$3s!?x^nFP+(W2NuZ=REWq>0m z&nFDKZd}+R7|m%=)2na-S6#f>RZ=T^1+A`(M6VdWZLhj7QR57PMf5CmFjfbS+*;D0i)mjD`!*Zk`SS3<$QM8QIy%z&*pl4CyR4s=2XW4VVAvD=Pc{rJW&n;g z1qF_%rao|{{#SoH^|pC=_>uO%lS)`Lw~}q zbX#l;%F7qPKbk`BIDziw1jAyZrGdd<`!d>BXeN^S^M5REu>Ue@J0!Li~ONY@q54iXF#H7;O zkA*ISAjhXGCwHTt0rc#U7m&|Dls3vhu1*m^ks$f^GHpV`ENtA1&@%tq!Q_aiaL zTqG8G5HTTf2st&aXhBKt5RZ`SCm`f|ii=TfQyN%+EJRWeWw&;$r;;b7d3Dl9F#3k* zbFni|{s1Ap(LNipMtV3XrZdqSO2Gh*M2KJAoqC1AA z?{QKQuP&d!yMApHjj1P@t29-LK!2~nYI^pqTBtA0(g$I6iq|)|R4uRoaFjLpQs6Fy zs|~+llYnj73ANAFi<;&`Ol)zjR)J;ncCg*Ag{H0HQ29zdIz>2yR)mSMFEy#!-8#m^ zciYx6nDh1yh}yjyOzvi~Bo*-6JFnGdz^v_d4>Aru?VJSqu8^>5u8OO`YpCIhk=1xX zO6q{a&m_1Smw2V0RpFb+~`$Uc$iL5wCqX z%`!J3JuYQ#x~s01smP1Tf8k2qw`%f=kwCbXnd!^@u$N#>7Z{78HRP%{ObvAL&7<*7X05qFGWD(XVUel$mlp z8)Xi%7y*mUWP21VE0AJD`8BNsmNNKGfwda-rQ^P#D|SWN>DPn$Aogi|p*}LxUyA*4 z$-M|N^fCD4=iip()nDSsGaOv=&w|t_lk(skjmi=aMOm3k5GKeT49=(KNe=Ao<3GFA zf$Jp)9!3YwVqqfNxQ(f1(Ihkgu_iE*SsO_|VQN=uGQj!(76 zrN=*MC|q4uy(+)Ds$x}5Y+23vjX9Nx#g)IPaQuALy4srJx~dvS*;->wt)sFEgngJ3 zqTTq8%;1BeQH*Kp#`P@%FA5{uczRE?71HA%Ev-pR0O9l&Fn3!RlSZ_>AA^^~LEQWE zK}ZsSZ!Z!ryq?HY780L-^Bo?y8v(XXhhK%1U#huU80;*8luh?SN>>q%^#|v(_lfSV zI)36u=St@S6N6Z{7O4) z{lqaofu;c82T&Skgg*{F9<_{8f>a{h8?a7IZ&T%P)qkb5YYy;EvDKXG>~ZzeA&F-@ zSHj7&L+X|!soL{ZKANKlOr3Lqb$dVCharts 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'] + } + + }); + +}