From 85c12f049a03ca89252bf726ffedef6bd8e7923b Mon Sep 17 00:00:00 2001
From: Christoph Massmann <c.massmann@vianetz.com>
Date: Thu, 18 Aug 2022 12:33:29 +0200
Subject: [PATCH 1/2] all accounts with no bankIdentifier are now summed up
 also

---
 moneymoney-sum-by-bank.scpt | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/moneymoney-sum-by-bank.scpt b/moneymoney-sum-by-bank.scpt
index 43dc6df..a095e30 100644
--- a/moneymoney-sum-by-bank.scpt
+++ b/moneymoney-sum-by-bank.scpt
@@ -83,20 +83,23 @@ on SumBankBalancesFromPlist(accountsPropertyListFile)
 	tell application "System Events"
 		tell property list file accountsPropertyListFile
 			repeat with i from 1 to number of property list items
-				set bankIdentifier to ""
-				set accountName to ""
+				set accountName to value of property list item "name" of property list item i
+
+				set isGroup to value of property list item "group" of property list item i
+				if isGroup is false then
+					try
+						set bankIdentifier to value of property list item "bankIdentifier" of property list item "attributes" of property list item i
+					on error errStr number errorNumber
+						log "WARNING: " & errStr & ". MoneyMoney Attribute 'bankIdentifier' not set for account " & accountName
+						set bankIdentifier to "Sonstige"
+					end try
 
-				try
-					set accountName to value of property list item "name" of property list item i
-					set bankIdentifier to value of property list item "bankIdentifier" of property list item "attributes" of property list item i
 					set balances to value of property list item "balance" of property list item i
 					repeat with balance in balances
 						-- @todo make addition work also for different currencies, at the moment we assume all is the same currency
 						my IncreaseBankBalance(bankIdentifier, get first item of balance, balancePerBankList)
 					end repeat
-				on error errStr number errorNumber
-					log "WARNING: " & errStr & ". Probably MoneyMoney Attribute 'bankIdentifier' not set for account " & accountName & ". Skipping.."
-				end try
+				end if
 			end repeat
 		end tell
 	end tell

From 1273608f4ab5f7c3afc35d7bdac34f61865a03ee Mon Sep 17 00:00:00 2001
From: Christoph Massmann <cm@vianetz.com>
Date: Thu, 4 Jan 2024 11:27:44 +0100
Subject: [PATCH 2/2] migrated Apple Script to Python for easier maintenance

---
 README.md                   |  55 ++++++------
 moneymoney-sum-by-bank.py   |  49 +++++++++++
 moneymoney-sum-by-bank.scpt | 162 ------------------------------------
 requirements.txt            |   1 +
 4 files changed, 74 insertions(+), 193 deletions(-)
 create mode 100644 moneymoney-sum-by-bank.py
 delete mode 100644 moneymoney-sum-by-bank.scpt
 create mode 100644 requirements.txt

diff --git a/README.md b/README.md
index 8ccfbb4..f48496f 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,43 @@
 # MoneyMoney Extension: Sum by Bank
 
-This is an AppleScript extension for the great [MoneyMoney software](https://moneymoney-app.com/) to generate an Excel list with all sums by bank account.  
+This is a Pyton script for the great [MoneyMoney software](https://moneymoney-app.com/) to generate a list with all balances by bank account.  
 This is useful e.g. to export the total values per bank to a summary Excel document or to monitor certain threshold values per bank.
 
 ## Installation
 
-For this AppleScript to work it is required to add a custom attribute `bankIdentifier` to each account in MoneyMoney
-that you want to track. The total values are then summed up by this chosen bank identifier.
+```
+pip install -r requirements.txt
+```
 
-At the beginning of the Apple Script file `moneymney-sum-by-bank.scpt` you can then customize the behaviour of the script:
-
-|Variable|Description|
-|--------|-----------|
-|`exportFileName`|The name of the resulting Excel file. You can either use an existing file or leave it to missing value to create a new one.|
-|`startRowIndex`/`startColumnIndex`|The index of the cell row/column to start the table.|
-|`isSortDescending`|Whether to sort the resulting sums in a descending order or not.|
-|`isCloseExcel`|Whether to close Excel after the export.|
-|`cellThresholdValue`|Threshold value above that the cell is colored|
-|`cellThresholdColor`|Threshold background color value|
-
-If you optionally want to use this AppleScript within the services menu of the _MoneyMoney_ application, the best way is to use the [Mac Automator](https://support.apple.com/de-de/guide/automator/aut73234890a/mac): 
+On Mac if you (optionally) want to use this Python script within the services menu of the _MoneyMoney_ application, the best way is to use the [Mac Automator](https://support.apple.com/de-de/guide/automator/aut73234890a/mac): 
 1. Create a new "Quick Action Workflow" in Automator 
-1. Choose "No Input" in "MoneyMoney"
-1. Add the action "Execute AppleScript" and paste the contents of the file `moneymoney-sum-by-bank.scpt` into the text box
-1. Save the workflow
+2. Choose "No Input" in "MoneyMoney"
+3. Add the action "Execute AppleScript" and paste this script into the text box and adapt the script path accordingly:
+   ```applescript
+   on run {input, parameters}
+       tell application "Terminal"
+           do script "python3 ~/path/to/script/moneymoney-sum-by-bank.py && read -s -n 1 key && exit 0"
+       end tell
+       
+       return input
+   end run
+   ```
+4. Save the workflow
 
 Then you have a new menu item with the chosen name in _MoneyMoney > Services_.
 
 ## Usage
 
-After click on the new menu item in _MoneyMoney > Services_, Microsoft Excel will open and show all bank sums in a descending order:  
-![Excel file with sums by bank account](moneymoney-sum-by-bank.png "Excel file with sums by bank account")
-
-As an alternative you can also simply double click on the `moneymney-sum-by-bank.scpt` script to execute it manually.
+**Note:** The MoneyMoney application has to be unlocked when executing the script otherwise an error will be thrown.
 
-For more information see also [my blog post](https://dev-investor.de/finanz-apps/money-money/maximum-pro-bank-extension/).
-
-## Notes
+This Python script sums all account balances from MoneyMoney by BIC. If a BIC is not available (e.g. in case of an offline or a credit card account),
+the script looks for a custom attribute `bankIdentifier` in the account settings and uses this as reference.  
+If neither of this information is available on the account and it has a balance, the value is added to "other".
 
-- Tested with Excel for Mac 16.43/16.64
-- The MoneyMoney application has to be unlocked when executing the script otherwise an error will be thrown
-- Basically a better way would be to automatically group by account bic but not all accounts do have a bic (e.g. credit cards).
+The result of the Python script can e.g. be used in Excel:  
+![Excel file with sums by bank account](moneymoney-sum-by-bank.png "Excel file with sums by bank account")
 
-## Known Limitations
-- Support only for Euro currency
-- Accounts with no `bankIdentifier` attribute are ignored in export file
+For more information see also [my blog post](https://dev-investor.de/finanz-apps/money-money/maximum-pro-bank-extension/).
 
 ## License
 
diff --git a/moneymoney-sum-by-bank.py b/moneymoney-sum-by-bank.py
new file mode 100644
index 0000000..78a4207
--- /dev/null
+++ b/moneymoney-sum-by-bank.py
@@ -0,0 +1,49 @@
+import pandas as pd
+import plistlib
+import subprocess
+import logging
+
+
+def run_apple_script(script):
+    command = ['osascript', '-e', script]
+    with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as pipe:
+        result = pipe.communicate()
+        if result[1]:
+            raise Exception('Could not run Apple Script: %s' % result[1].decode().strip())
+
+        return result
+
+
+# @see https://moneymoney-app.com/applescript/
+def fetch_moneymoney_accounts() -> {}:
+    result = run_apple_script('tell application "MoneyMoney" to export accounts')
+
+    # Parse XML property list.
+    try:
+        plist = plistlib.loads(result[0])
+    except plistlib.InvalidFileException as exception:
+        raise Exception('Could not parse XML property list. %s' % repr(exception))
+
+    return plist
+
+
+def moneymoney_sum_by_account() -> pd.DataFrame:
+    balance_per_bank_and_currency = []
+    for account in fetch_moneymoney_accounts():
+        if account['portfolio'] is True or account['group'] is True:
+            continue
+
+        if not account['bankCode']:
+            account['bankCode'] = account['attributes']['bankIdentifier'] if 'bankIdentifier' in account['attributes'] else 'other'
+            logging.debug("Account %s has no bank code, using '%s'" % (account["name"], account['bankCode']))
+
+        for balance in account['balance']:
+            balance_per_bank_and_currency.append([account['bankCode'], balance[0], balance[1]])
+
+    df = pd.DataFrame(balance_per_bank_and_currency, columns=['bank', 'balance', 'currency'])
+    return df.groupby(['bank', 'currency']).agg({'balance': 'sum'})
+
+
+#logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
+df = moneymoney_sum_by_account()
+print(df[df['balance'] > 0].sort_values(by='balance', ascending=False))
diff --git a/moneymoney-sum-by-bank.scpt b/moneymoney-sum-by-bank.scpt
deleted file mode 100644
index a095e30..0000000
--- a/moneymoney-sum-by-bank.scpt
+++ /dev/null
@@ -1,162 +0,0 @@
-------------------------
--- CONFIGURATION
-
-global exportFileName, startRowIndex, startColumnIndex, isSortDescending, isCloseExcel, cellThresholdValue, cellThresholdColor
-
--- The name of the resulting Excel file. You can either use an existing file or leave it to missing value to create a new one.
-set exportFileName to missing value
-
--- The index of the cell row/column to start the table.
-set startRowIndex to 1
-set startColumnIndex to 1
-
--- Whether to sort the resulting sums in a descending order or not.
-set isSortDescending to true
-
--- Whether to close Excel after the export.
-set isCloseExcel to false
-
--- Threshold value above that the cell is colored
-set cellThresholdValue to 100000
-set cellThresholdColor to {255, 244, 233}
-------------------------
-
-global tmpDir
--- Note: we do not use "path to temporary items from user domain" because - despite the call - this is not in the user space!
-set tmpDir to (path to library folder from user domain as text) & "Caches:"
-
--- Export all accounts from MoneyMoney application into temporary folder.
--- The temporary folder is necessary because the plist result of the "export accounts" command cannot be processed easily
--- with AppleScript otherwise.
-on ExportAccounts()
-	tell application "MoneyMoney"
-		set accounts to export accounts
-	end tell
-
-	-- @todo check if there is any other way than to temporary safe the plist file
-	set UUID to do shell script "uuidgen"
-	set accountsPropertyListFile to (tmpDir & UUID & ".plist")
-	set accountsPropertyListFilePosix to POSIX path of accountsPropertyListFile
-
-	open for access file the accountsPropertyListFile with write permission
-	write (accounts) to file the accountsPropertyListFile as «class utf8»
-	close access file the accountsPropertyListFile
-
-	log "Accounts file has been generated to " & accountsPropertyListFilePosix
-
-	return accountsPropertyListFilePosix
-end ExportAccounts
-
-on DeleteFile(fileName)
-	log "INFO: removing temporary file " & fileName
-
-	-- this statement returns an error if parameter fileName is not a real file
-	-- this way we prohibit removing a whole directory
-	do shell script "test -f " & fileName as POSIX file
-
-	tell application "System Events" to delete alias fileName
-end DeleteFile
-
--- Increase the balance value of the given bank in the balancePerBankList list
-on IncreaseBankBalance(bankIdentifier, balance, balancePerBankList)
-	log "INFO: Increase bank balance of " & bankIdentifier & " by " & balance
-
-	repeat with a from 1 to the count of balancePerBankList
-		if bankIdentifier of item a of balancePerBankList is bankIdentifier then
-			set newBalance to (balance of item a of balancePerBankList) + balance
-			log "DEBUG: Found existing balance. Set new balance to " & newBalance
-			set item a of balancePerBankList to {bankIdentifier:bankIdentifier, balance:newBalance}
-
-			return balancePerBankList
-		end if
-	end repeat
-
-	set the end of balancePerBankList to {bankIdentifier:bankIdentifier, balance:balance}
-	return balancePerBankList
-end IncreaseBankBalance
-
--- Sum up all bank balances in the given plist file from MoneyMoney
-on SumBankBalancesFromPlist(accountsPropertyListFile)
-	-- balancePerBankList is an object like {{bankIdentifier: "DKB", balance: 200, bankIdentifier: "Commerzbank", balance: 99, ..}
-	set balancePerBankList to {}
-
-	tell application "System Events"
-		tell property list file accountsPropertyListFile
-			repeat with i from 1 to number of property list items
-				set accountName to value of property list item "name" of property list item i
-
-				set isGroup to value of property list item "group" of property list item i
-				if isGroup is false then
-					try
-						set bankIdentifier to value of property list item "bankIdentifier" of property list item "attributes" of property list item i
-					on error errStr number errorNumber
-						log "WARNING: " & errStr & ". MoneyMoney Attribute 'bankIdentifier' not set for account " & accountName
-						set bankIdentifier to "Sonstige"
-					end try
-
-					set balances to value of property list item "balance" of property list item i
-					repeat with balance in balances
-						-- @todo make addition work also for different currencies, at the moment we assume all is the same currency
-						my IncreaseBankBalance(bankIdentifier, get first item of balance, balancePerBankList)
-					end repeat
-				end if
-			end repeat
-		end tell
-	end tell
-
-	if balancePerBankList is {} then
-		error "Temporary property list file " & accountsPropertyListFile & " could not be read or no MoneyMoney accounts with attribute 'bankIdentifier' exists."
-	end if
-
-	return balancePerBankList
-end SumBankBalancesFromPlist
-
--- Open Microsoft Excel application, insert bank sums and format and sort accordingly
-on OpenExcelWithData(bankBalances)
-	tell application "Microsoft Excel"
-		activate
-		if (exportFileName is missing value) then
-			make new workbook
-		else
-			open exportFileName
-		end if
-		--
-		set x to startRowIndex
-
-		repeat with balanceData in bankBalances
-			set balance to balance of balanceData
-			set bank to bankIdentifier of balanceData
-
-			set value of cell x of column startColumnIndex to balance
-			set value of cell x of column (startColumnIndex + 1) to bank
-
-			if (balance > cellThresholdValue) then
-				set color of interior object of cell x of column startColumnIndex to cellThresholdColor
-			end if
-
-			set x to (x + 1)
-		end repeat
-
-		set number format of column startColumnIndex to "#,##0.00 €"
-
-		if (isSortDescending is true) then
-			set sortingRange to "" & startRowIndex & ":" & x
-			sort range sortingRange key1 cell 1 of column startColumnIndex order1 sort descending
-		end if
-
-		if (exportFileName is not missing value) then
-			save yes
-		end if
-
-		if (isCloseExcel is true) then
-			close active workbook
-		end if
-	end tell
-end OpenExcelWithData
-
-set accountsPropertyListFile to ExportAccounts()
-set bankBalances to SumBankBalancesFromPlist(accountsPropertyListFile)
-
-OpenExcelWithData(bankBalances)
-
-DeleteFile(accountsPropertyListFile)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..1411a4a
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+pandas
\ No newline at end of file