mirror of
https://github.com/aclindsa/moneygo.git
synced 2025-07-02 20:28:38 -04:00
Split accounts and transactions into models
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
@ -10,141 +9,17 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Split.Status
|
||||
const (
|
||||
Imported int64 = 1
|
||||
Entered = 2
|
||||
Cleared = 3
|
||||
Reconciled = 4
|
||||
Voided = 5
|
||||
)
|
||||
|
||||
// Split.ImportSplitType
|
||||
const (
|
||||
Default int64 = 0
|
||||
ImportAccount = 1 // This split belongs to the main account being imported
|
||||
SubAccount = 2 // This split belongs to a sub-account of that being imported
|
||||
ExternalAccount = 3
|
||||
TradingAccount = 4
|
||||
Commission = 5
|
||||
Taxes = 6
|
||||
Fees = 7
|
||||
Load = 8
|
||||
IncomeAccount = 9
|
||||
ExpenseAccount = 10
|
||||
)
|
||||
|
||||
type Split struct {
|
||||
SplitId int64
|
||||
TransactionId int64
|
||||
Status int64
|
||||
ImportSplitType int64
|
||||
|
||||
// One of AccountId and SecurityId must be -1
|
||||
// In normal splits, AccountId will be valid and SecurityId will be -1. The
|
||||
// only case where this is reversed is for transactions that have been
|
||||
// imported and not yet associated with an account.
|
||||
AccountId int64
|
||||
SecurityId int64
|
||||
|
||||
RemoteId string // unique ID from server, for detecting duplicates
|
||||
Number string // Check or reference number
|
||||
Memo string
|
||||
Amount string // String representation of decimal, suitable for passing to big.Rat.SetString()
|
||||
}
|
||||
|
||||
func GetBigAmount(amt string) (*big.Rat, error) {
|
||||
var r big.Rat
|
||||
_, success := r.SetString(amt)
|
||||
if !success {
|
||||
return nil, errors.New("Couldn't convert string amount to big.Rat via SetString()")
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (s *Split) GetAmount() (*big.Rat, error) {
|
||||
return GetBigAmount(s.Amount)
|
||||
}
|
||||
|
||||
func (s *Split) Valid() bool {
|
||||
if (s.AccountId == -1) == (s.SecurityId == -1) {
|
||||
return false
|
||||
}
|
||||
_, err := s.GetAmount()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *Split) AlreadyImported(tx *Tx) (bool, error) {
|
||||
func SplitAlreadyImported(tx *Tx, s *models.Split) (bool, error) {
|
||||
count, err := tx.SelectInt("SELECT COUNT(*) from splits where RemoteId=? and AccountId=?", s.RemoteId, s.AccountId)
|
||||
return count == 1, err
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
TransactionId int64
|
||||
UserId int64
|
||||
Description string
|
||||
Date time.Time
|
||||
Splits []*Split `db:"-"`
|
||||
}
|
||||
|
||||
type TransactionList struct {
|
||||
Transactions *[]Transaction `json:"transactions"`
|
||||
}
|
||||
|
||||
type AccountTransactionsList struct {
|
||||
Account *Account
|
||||
Transactions *[]Transaction
|
||||
TotalTransactions int64
|
||||
BeginningBalance string
|
||||
EndingBalance string
|
||||
}
|
||||
|
||||
func (t *Transaction) Write(w http.ResponseWriter) error {
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(t)
|
||||
}
|
||||
|
||||
func (t *Transaction) Read(json_str string) error {
|
||||
dec := json.NewDecoder(strings.NewReader(json_str))
|
||||
return dec.Decode(t)
|
||||
}
|
||||
|
||||
func (tl *TransactionList) Write(w http.ResponseWriter) error {
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(tl)
|
||||
}
|
||||
|
||||
func (tl *TransactionList) Read(json_str string) error {
|
||||
dec := json.NewDecoder(strings.NewReader(json_str))
|
||||
return dec.Decode(tl)
|
||||
}
|
||||
|
||||
func (atl *AccountTransactionsList) Write(w http.ResponseWriter) error {
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(atl)
|
||||
}
|
||||
|
||||
func (atl *AccountTransactionsList) Read(json_str string) error {
|
||||
dec := json.NewDecoder(strings.NewReader(json_str))
|
||||
return dec.Decode(atl)
|
||||
}
|
||||
|
||||
func (t *Transaction) Valid() bool {
|
||||
for i := range t.Splits {
|
||||
if !t.Splits[i].Valid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Return a map of security ID's to big.Rat's containing the amount that
|
||||
// security is imbalanced by
|
||||
func (t *Transaction) GetImbalances(tx *Tx) (map[int64]big.Rat, error) {
|
||||
func GetTransactionImbalances(tx *Tx, t *models.Transaction) (map[int64]big.Rat, error) {
|
||||
sums := make(map[int64]big.Rat)
|
||||
|
||||
if !t.Valid() {
|
||||
@ -155,7 +30,7 @@ func (t *Transaction) GetImbalances(tx *Tx) (map[int64]big.Rat, error) {
|
||||
securityid := t.Splits[i].SecurityId
|
||||
if t.Splits[i].AccountId != -1 {
|
||||
var err error
|
||||
var account *Account
|
||||
var account *models.Account
|
||||
account, err = GetAccount(tx, t.Splits[i].AccountId, t.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -172,10 +47,10 @@ func (t *Transaction) GetImbalances(tx *Tx) (map[int64]big.Rat, error) {
|
||||
|
||||
// Returns true if all securities contained in this transaction are balanced,
|
||||
// false otherwise
|
||||
func (t *Transaction) Balanced(tx *Tx) (bool, error) {
|
||||
func TransactionBalanced(tx *Tx, t *models.Transaction) (bool, error) {
|
||||
var zero big.Rat
|
||||
|
||||
sums, err := t.GetImbalances(tx)
|
||||
sums, err := GetTransactionImbalances(tx, t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -188,8 +63,8 @@ func (t *Transaction) Balanced(tx *Tx) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func GetTransaction(tx *Tx, transactionid int64, userid int64) (*Transaction, error) {
|
||||
var t Transaction
|
||||
func GetTransaction(tx *Tx, transactionid int64, userid int64) (*models.Transaction, error) {
|
||||
var t models.Transaction
|
||||
|
||||
err := tx.SelectOne(&t, "SELECT * from transactions where UserId=? AND TransactionId=?", userid, transactionid)
|
||||
if err != nil {
|
||||
@ -204,8 +79,8 @@ func GetTransaction(tx *Tx, transactionid int64, userid int64) (*Transaction, er
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func GetTransactions(tx *Tx, userid int64) (*[]Transaction, error) {
|
||||
var transactions []Transaction
|
||||
func GetTransactions(tx *Tx, userid int64) (*[]models.Transaction, error) {
|
||||
var transactions []models.Transaction
|
||||
|
||||
_, err := tx.Select(&transactions, "SELECT * from transactions where UserId=?", userid)
|
||||
if err != nil {
|
||||
@ -246,7 +121,7 @@ func (ame AccountMissingError) Error() string {
|
||||
return "Account missing"
|
||||
}
|
||||
|
||||
func InsertTransaction(tx *Tx, t *Transaction, user *models.User) error {
|
||||
func InsertTransaction(tx *Tx, t *models.Transaction, user *models.User) error {
|
||||
// Map of any accounts with transaction splits being added
|
||||
a_map := make(map[int64]bool)
|
||||
for i := range t.Splits {
|
||||
@ -296,8 +171,8 @@ func InsertTransaction(tx *Tx, t *Transaction, user *models.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateTransaction(tx *Tx, t *Transaction, user *models.User) error {
|
||||
var existing_splits []*Split
|
||||
func UpdateTransaction(tx *Tx, t *models.Transaction, user *models.User) error {
|
||||
var existing_splits []*models.Split
|
||||
|
||||
_, err := tx.Select(&existing_splits, "SELECT * from splits where TransactionId=?", t.TransactionId)
|
||||
if err != nil {
|
||||
@ -373,7 +248,7 @@ func UpdateTransaction(tx *Tx, t *Transaction, user *models.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTransaction(tx *Tx, t *Transaction, user *models.User) error {
|
||||
func DeleteTransaction(tx *Tx, t *models.Transaction, user *models.User) error {
|
||||
var accountids []int64
|
||||
_, err := tx.Select(&accountids, "SELECT DISTINCT AccountId FROM splits WHERE TransactionId=? AND AccountId != -1", t.TransactionId)
|
||||
if err != nil {
|
||||
@ -408,7 +283,7 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
}
|
||||
|
||||
if r.Method == "POST" {
|
||||
var transaction Transaction
|
||||
var transaction models.Transaction
|
||||
if err := ReadJSON(r, &transaction); err != nil {
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
@ -427,7 +302,7 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
}
|
||||
}
|
||||
|
||||
balanced, err := transaction.Balanced(context.Tx)
|
||||
balanced, err := TransactionBalanced(context.Tx, &transaction)
|
||||
if err != nil {
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
@ -449,7 +324,7 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
} else if r.Method == "GET" {
|
||||
if context.LastLevel() {
|
||||
//Return all Transactions
|
||||
var al TransactionList
|
||||
var al models.TransactionList
|
||||
transactions, err := GetTransactions(context.Tx, user.UserId)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@ -475,13 +350,13 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
if r.Method == "PUT" {
|
||||
var transaction Transaction
|
||||
var transaction models.Transaction
|
||||
if err := ReadJSON(r, &transaction); err != nil || transaction.TransactionId != transactionid {
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
transaction.UserId = user.UserId
|
||||
|
||||
balanced, err := transaction.Balanced(context.Tx)
|
||||
balanced, err := TransactionBalanced(context.Tx, &transaction)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return NewError(999 /*Internal Error*/)
|
||||
@ -526,7 +401,7 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
func TransactionsBalanceDifference(tx *Tx, accountid int64, transactions []Transaction) (*big.Rat, error) {
|
||||
func TransactionsBalanceDifference(tx *Tx, accountid int64, transactions []models.Transaction) (*big.Rat, error) {
|
||||
var pageDifference, tmp big.Rat
|
||||
for i := range transactions {
|
||||
_, err := tx.Select(&transactions[i].Splits, "SELECT * FROM splits where TransactionId=?", transactions[i].TransactionId)
|
||||
@ -538,7 +413,7 @@ func TransactionsBalanceDifference(tx *Tx, accountid int64, transactions []Trans
|
||||
// an ending balance
|
||||
for j := range transactions[i].Splits {
|
||||
if transactions[i].Splits[j].AccountId == accountid {
|
||||
rat_amount, err := GetBigAmount(transactions[i].Splits[j].Amount)
|
||||
rat_amount, err := models.GetBigAmount(transactions[i].Splits[j].Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -551,7 +426,7 @@ func TransactionsBalanceDifference(tx *Tx, accountid int64, transactions []Trans
|
||||
}
|
||||
|
||||
func GetAccountBalance(tx *Tx, user *models.User, accountid int64) (*big.Rat, error) {
|
||||
var splits []Split
|
||||
var splits []models.Split
|
||||
|
||||
sql := "SELECT DISTINCT splits.* FROM splits INNER JOIN transactions ON transactions.TransactionId = splits.TransactionId WHERE splits.AccountId=? AND transactions.UserId=?"
|
||||
_, err := tx.Select(&splits, sql, accountid, user.UserId)
|
||||
@ -561,7 +436,7 @@ func GetAccountBalance(tx *Tx, user *models.User, accountid int64) (*big.Rat, er
|
||||
|
||||
var balance, tmp big.Rat
|
||||
for _, s := range splits {
|
||||
rat_amount, err := GetBigAmount(s.Amount)
|
||||
rat_amount, err := models.GetBigAmount(s.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -574,7 +449,7 @@ func GetAccountBalance(tx *Tx, user *models.User, accountid int64) (*big.Rat, er
|
||||
|
||||
// Assumes accountid is valid and is owned by the current user
|
||||
func GetAccountBalanceDate(tx *Tx, user *models.User, accountid int64, date *time.Time) (*big.Rat, error) {
|
||||
var splits []Split
|
||||
var splits []models.Split
|
||||
|
||||
sql := "SELECT DISTINCT splits.* FROM splits INNER JOIN transactions ON transactions.TransactionId = splits.TransactionId WHERE splits.AccountId=? AND transactions.UserId=? AND transactions.Date < ?"
|
||||
_, err := tx.Select(&splits, sql, accountid, user.UserId, date)
|
||||
@ -584,7 +459,7 @@ func GetAccountBalanceDate(tx *Tx, user *models.User, accountid int64, date *tim
|
||||
|
||||
var balance, tmp big.Rat
|
||||
for _, s := range splits {
|
||||
rat_amount, err := GetBigAmount(s.Amount)
|
||||
rat_amount, err := models.GetBigAmount(s.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -596,7 +471,7 @@ func GetAccountBalanceDate(tx *Tx, user *models.User, accountid int64, date *tim
|
||||
}
|
||||
|
||||
func GetAccountBalanceDateRange(tx *Tx, user *models.User, accountid int64, begin, end *time.Time) (*big.Rat, error) {
|
||||
var splits []Split
|
||||
var splits []models.Split
|
||||
|
||||
sql := "SELECT DISTINCT splits.* FROM splits INNER JOIN transactions ON transactions.TransactionId = splits.TransactionId WHERE splits.AccountId=? AND transactions.UserId=? AND transactions.Date >= ? AND transactions.Date < ?"
|
||||
_, err := tx.Select(&splits, sql, accountid, user.UserId, begin, end)
|
||||
@ -606,7 +481,7 @@ func GetAccountBalanceDateRange(tx *Tx, user *models.User, accountid int64, begi
|
||||
|
||||
var balance, tmp big.Rat
|
||||
for _, s := range splits {
|
||||
rat_amount, err := GetBigAmount(s.Amount)
|
||||
rat_amount, err := models.GetBigAmount(s.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -617,9 +492,9 @@ func GetAccountBalanceDateRange(tx *Tx, user *models.User, accountid int64, begi
|
||||
return &balance, nil
|
||||
}
|
||||
|
||||
func GetAccountTransactions(tx *Tx, user *models.User, accountid int64, sort string, page uint64, limit uint64) (*AccountTransactionsList, error) {
|
||||
var transactions []Transaction
|
||||
var atl AccountTransactionsList
|
||||
func GetAccountTransactions(tx *Tx, user *models.User, accountid int64, sort string, page uint64, limit uint64) (*models.AccountTransactionsList, error) {
|
||||
var transactions []models.Transaction
|
||||
var atl models.AccountTransactionsList
|
||||
|
||||
var sqlsort, balanceLimitOffset string
|
||||
var balanceLimitOffsetArg uint64
|
||||
@ -685,7 +560,7 @@ func GetAccountTransactions(tx *Tx, user *models.User, accountid int64, sort str
|
||||
|
||||
var tmp, balance big.Rat
|
||||
for _, amount := range amounts {
|
||||
rat_amount, err := GetBigAmount(amount)
|
||||
rat_amount, err := models.GetBigAmount(amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user