package ynab import ( "fmt" "net/url" "time" ) // Reference: https://api.ynab.com/v1#/Transactions/ type Transaction struct { Type string `json:"type,omitempty"` ParentTransactionID interface{} `json:"parent_transaction_id,omitempty"` ID string `json:"id,omitempty"` Date string `json:"date,omitempty"` Amount int `json:"amount,omitempty"` Memo string `json:"memo,omitempty"` Cleared string `json:"cleared,omitempty"` Approved bool `json:"approved,omitempty"` FlagColor interface{} `json:"flag_color,omitempty"` AccountID string `json:"account_id,omitempty"` AccountName string `json:"account_name,omitempty"` PayeeID string `json:"payee_id,omitempty"` PayeeName string `json:"payee_name,omitempty"` CategoryID string `json:"category_id,omitempty"` CategoryName string `json:"category_name,omitempty"` TransferAccountID interface{} `json:"transfer_account_id,omitempty"` TransferTransactionID interface{} `json:"transfer_transaction_id,omitempty"` MatchedTransactionID interface{} `json:"matched_transaction_id,omitempty"` ImportID string `json:"import_id,omitempty"` Deleted bool `json:"deleted,omitempty"` } // Used for single transaction requests type TransactionRequest struct { Transaction Transaction `json:"transaction,omitempty"` } // Used for single transaction responses type TransactionResponse struct { Data struct { TransactionIDs []string `json:"transaction_ids,omitempty"` Transaction Transaction `json:"transaction"` ServerKnowledge int `json:"server_knowledge,omitempty"` } } // Used for multiple transaction requests / responses type TransactionsResponse struct { Data struct { Transactions []Transaction `json:"transactions"` ServerKnowledge int `json:"server_knowledge"` } `json:"data"` } // Accepts a YNAB account ID and timestamp and returns all transactions in that account // since the date provided func (c *Client) GetAccountTransactions(accountID string, sinceDate time.Time) (*TransactionsResponse, error) { response := TransactionsResponse{} urlQuery := url.Values{} urlQuery.Add("since_date", sinceDate.Format("2006-01-02")) err := c.get(fmt.Sprintf("/accounts/%s/transactions", accountID), &response, urlQuery) if err != nil { return nil, fmt.Errorf("failed to get account transactions: %v", err) } return &response, nil } // Accepts a YNAB account ID and returns the transaction ID, amount and an error for the // the first transaction found with Payee Name "Capital Gains or Losses" func (c *Client) getTodayYnabCapitalGainsTransaction(accountID string) (string, int, error) { ynabTransactions, err := c.GetAccountTransactions(accountID, time.Now().In(c.loc)) if err != nil { return "", 0, fmt.Errorf("failed to get ynab transactions: %v", err) } for _, transaction := range ynabTransactions.Data.Transactions { if transaction.PayeeName != "Capital Gains or Losses" { continue } return transaction.ID, transaction.Amount, nil } return "", 0, nil } // Accepts a YNAB account ID, transaction ID and transaction amount and updates the YNAB transaction with the matching ID // If transaction ID is blank, a new transaction is created for the amount specified func (c *Client) updateTodayYNABCapitalGainsTransaction(accountID string, transactionID string, amount int) error { request := TransactionRequest{ Transaction: Transaction{ AccountID: accountID, Amount: amount, Date: time.Now().In(c.loc).Format("2006-01-02"), Cleared: "cleared", Approved: true, PayeeName: "Capital Gains or Losses", Memo: fmt.Sprintf("Quoted at: %s", time.Now().In(c.loc).Format("2006-01-02 15:04:05")), }, } response := &TransactionResponse{} var err error if transactionID == "" { // create transaction err = c.post("/transactions", response, request) } else { // update existing transaction err = c.put(fmt.Sprintf("/transactions/%s", transactionID), response, request) } if err != nil { return fmt.Errorf("request failed: %v", err) } return nil }