Skip to content

Commit

Permalink
Merge pull request #58 from jprouty/main
Browse files Browse the repository at this point in the history
Add 3 API calls/graphql queries to view and manipulate transaction splitting.
  • Loading branch information
grablair authored Jan 4, 2024
2 parents aa4c624 + a66cdf4 commit ce6455d
Show file tree
Hide file tree
Showing 2 changed files with 283 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ As of writing this README, the following methods are supported:
- `get_subscription_details` - gets the Monarch Money account's status (e.g. paid or trial)
- `get_transactions` - gets transaction data, defaults to returning the last 100 transactions; can also be searched by date range
- `get_transaction_categories` - gets all of the categories configured in the account
- `get_transaction_details` - gets detailed transaction data for a single transaction
- `get_transaction_splits` - gets transaction splits for a single transaction
- `get_transaction_tags` - gets all of the tags configured in the account
- `get_cashflow` - gets cashflow data (by category, category group, merchant and a summary)
- `get_cashflow_summary` - gets cashflow summary (income, expense, savings, savings rate)
Expand All @@ -76,6 +78,8 @@ As of writing this README, the following methods are supported:
- `request_accounts_refresh` - requests a syncronization / refresh of all accounts linked to Monarch Money. This is a **non-blocking call**. If the user wants to check on the status afterwards, they must call `is_accounts_refresh_complete`.
- `request_accounts_refresh_and_waid` - requests a syncronization / refresh of all accounts linked to Monarch Money. This is a **blocking call** and will not return until the refresh is complete or no longer running.
- `create_transaction` - creates a transaction with the given attributes
- `update_transaction` - modifes one or more attributes for an existing transaction
- `update_transaction_splits` - modifes how a transaction is split (or not)
- `set_budget_amount` - sets a budget's value to the given amount (date allowed, will only apply to month specified by default). A zero amount value will "unset" or "clear" the budget for the given category.

# Contributing
Expand Down
279 changes: 279 additions & 0 deletions monarchmoney/monarchmoney.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,285 @@ async def get_transaction_tags(self) -> Dict[str, Any]:
operation="GetHouseholdTransactionTags", graphql_query=query
)

async def get_transaction_details(
self, transaction_id: str, redirect_posted: bool = True
) -> Dict[str, Any]:
"""
Returns detailed information about a transaction.
:param transaction_id: the transaction to fetch.
:param redirect_posted: whether to redirect posted transactions. Defaults to True.
"""
query = gql(
"""
query GetTransactionDrawer($id: UUID!, $redirectPosted: Boolean) {
getTransaction(id: $id, redirectPosted: $redirectPosted) {
id
amount
pending
isRecurring
date
originalDate
hideFromReports
needsReview
reviewedAt
reviewedByUser {
id
name
__typename
}
plaidName
notes
hasSplitTransactions
isSplitTransaction
isManual
splitTransactions {
id
...TransactionDrawerSplitMessageFields
__typename
}
originalTransaction {
id
...OriginalTransactionFields
__typename
}
attachments {
id
publicId
extension
sizeBytes
filename
originalAssetUrl
__typename
}
account {
id
...TransactionDrawerAccountSectionFields
__typename
}
category {
id
__typename
}
goal {
id
__typename
}
merchant {
id
name
transactionCount
logoUrl
recurringTransactionStream {
id
__typename
}
__typename
}
tags {
id
name
color
order
__typename
}
needsReviewByUser {
id
__typename
}
__typename
}
myHousehold {
users {
id
name
__typename
}
__typename
}
}
fragment TransactionDrawerSplitMessageFields on Transaction {
id
amount
merchant {
id
name
__typename
}
category {
id
icon
name
__typename
}
__typename
}
fragment OriginalTransactionFields on Transaction {
id
date
amount
merchant {
id
name
__typename
}
__typename
}
fragment TransactionDrawerAccountSectionFields on Account {
id
displayName
icon
logoUrl
id
mask
subtype {
display
__typename
}
__typename
}
"""
)

variables = {
"id": transaction_id,
"redirectPosted": redirect_posted,
}

return await self.gql_call(
operation="GetTransactionDrawer", variables=variables, graphql_query=query
)

async def get_transaction_splits(self, transaction_id: str) -> Dict[str, Any]:
"""
Returns the transaction split information for a transaction.
:param transaction_id: the transaction to query.
"""
query = gql(
"""
query TransactionSplitQuery($id: UUID!) {
getTransaction(id: $id) {
id
amount
category {
id
name
icon
__typename
}
merchant {
id
name
__typename
}
splitTransactions {
id
merchant {
id
name
__typename
}
category {
id
icon
name
__typename
}
amount
notes
__typename
}
__typename
}
}
"""
)

variables = {"id": transaction_id}

return await self.gql_call(
operation="TransactionSplitQuery", variables=variables, graphql_query=query
)

async def update_transaction_splits(
self, transaction_id: str, split_data: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Creates, modifies, or deletes the splits for a given transaction.
Returns the split information for the update transaction.
:param transaction_id: the original transaction to modify.
:param split_data: the splits to create, modify, or delete.
If empty list or None is given, all splits will be deleted.
If split_data is given, all existing splits for transaction_id will be replaced with the new splits.
split_data takes the shape: [{"merchantName": "...", "amount": -12.34, "categoryId": "231"}, split2, split3, ...]
sum([split.amount for split in split_data]) must equal transaction_id.amount.
"""
query = gql(
"""
mutation Common_SplitTransactionMutation($input: UpdateTransactionSplitMutationInput!) {
updateTransactionSplit(input: $input) {
errors {
...PayloadErrorFields
__typename
}
transaction {
id
hasSplitTransactions
splitTransactions {
id
merchant {
id
name
__typename
}
category {
id
icon
name
__typename
}
amount
notes
__typename
}
__typename
}
__typename
}
}
fragment PayloadErrorFields on PayloadError {
fieldErrors {
field
messages
__typename
}
message
code
__typename
}
"""
)

if split_data is None:
split_data = []

variables = {
"input": {"transactionId": transaction_id, "splitData": split_data}
}

return await self.gql_call(
operation="Common_SplitTransactionMutation",
variables=variables,
graphql_query=query,
)

async def get_cashflow(
self,
limit: int = DEFAULT_RECORD_LIMIT,
Expand Down

0 comments on commit ce6455d

Please sign in to comment.