all repos — piggy @ 6eb6efa5eeb457d4ad0b4ce27e48c6b21dc42976

Dead simple finance manager in Go, HTML and JS.

src/app/models.go (view raw)

  1package app
  2
  3import (
  4	"log"
  5	"time"
  6)
  7
  8type Bookmaker struct {
  9	ID        uint      `json:"id"`
 10	CreatedAt time.Time `json:"created_at"`
 11	UpdatedAt time.Time `json:"updated_at"`
 12
 13	Name              string `json:"name" gorm:"not null" `
 14	Exchange          bool   `json:"exchange" gorm:"not null" `
 15	DefaultCommission uint   `json:"default_commission" gorm:"not null"`
 16}
 17
 18type Account struct {
 19	ID        uint      `json:"id"`
 20	CreatedAt time.Time `json:"created_at"`
 21
 22	Name string `json:"name" gorm:"not null"`
 23}
 24
 25type Record struct {
 26	ID        uint      `json:"id" gorm:"primaryKey"`
 27	CreatedAt time.Time `json:"created_at"`
 28	UpdatedAt time.Time `json:"updated_at"`
 29
 30	Done        bool   `json:"done" gorm:"not null"`
 31	Type        string `json:"type" gorm:"not null"`
 32	Description string `json:"description" gorm:"not null"`
 33
 34	Date  *time.Time `json:"date" gorm:"-"`
 35	Value *int       `json:"value" gorm:"-"`
 36
 37	Entries []Entry `json:"entries" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
 38}
 39
 40type Entry struct {
 41	ID        uint      `json:"id"`
 42	CreatedAt time.Time `json:"created_at"`
 43	UpdatedAt time.Time `json:"updated_at"`
 44
 45	RecordID    uint `json:"record_id" gorm:"not null"`
 46	BookmakerID uint `json:"bookmaker_id" gorm:"not null"`
 47	AccountID   uint `json:"account_id" gorm:"not null"`
 48	Amount      uint `json:"amount" gorm:"not null"`     // In cents (ex: 100 = 1.00)
 49	Refund      uint `json:"refund" gorm:"not null"`     // In cents (ex: 100 = 1.00)
 50	Bonus       uint `json:"bonus" gorm:"not null"`      // In cents (ex: 50 = 0.50)
 51	Commission  uint `json:"commission" gorm:"not null"` // In cents (ex: 4.5% = 450)
 52
 53	Odds  *uint      `json:"odds" gorm:"-"`
 54	Won   *bool      `json:"won" gorm:"-"`
 55	Date  *time.Time `json:"date" gorm:"-"`
 56	Value *int       `json:"value" gorm:"-"`
 57
 58	SubEntries []SubEntry `json:"sub_entries" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
 59}
 60
 61type SubEntry struct {
 62	ID        uint      `json:"id"`
 63	CreatedAt time.Time `json:"created_at"`
 64	UpdatedAt time.Time `json:"updated_at"`
 65
 66	EntryID     uint      `json:"entry_id" gorm:"not null"`
 67	Description string    `json:"description" gorm:"not null"`
 68	Odds        uint      `json:"odds" gorm:"not null"` // In cents (ex: 200 = 2.00)
 69	Won         bool      `json:"won" gorm:"not null"`
 70	Date        time.Time `json:"date" gorm:"not null;default:current_timestamp"`
 71
 72	Value *int `json:"value" gorm:"-"`
 73}
 74
 75func (e *Entry) GetOdds() *uint {
 76	v := uint(1)
 77	for _, s := range e.SubEntries {
 78		v *= s.Odds
 79	}
 80	return &v
 81}
 82
 83func (e *Entry) DidWin() *bool {
 84	v := true
 85	for _, s := range e.SubEntries {
 86		if !s.Won {
 87			v = false
 88			return &v
 89		}
 90	}
 91	return &v
 92}
 93
 94func (e *Entry) GetDate() *time.Time {
 95	if len(e.SubEntries) == 0 {
 96		return nil
 97	}
 98
 99	last := e.SubEntries[0].Date
100	for _, s := range e.SubEntries {
101		if s.Date.After(last) {
102			last = s.Date
103		}
104	}
105	return &last
106}
107
108func (e *Entry) GetValue() (value int) {
109	if e.Won == nil || e.Odds == nil {
110		log.Fatalf("please, update e.Won and e.Odds first")
111	}
112
113	if IsExchange(e.BookmakerID) {
114		r := (int(e.Amount) * (int(*e.Odds) - 100)) / 100
115		if *e.Won {
116			value = int(e.Amount) - int(e.Amount)*int(e.Commission)/10000
117		} else {
118			value = int(e.Refund) - r
119		}
120	} else {
121		if *e.Won {
122			value = (int(e.Amount) * int(*e.Odds) / 100) - int(e.Amount)
123		} else {
124			value = -int(e.Amount) + int(e.Refund)
125		}
126	}
127
128	value += int(e.Bonus)
129	return
130}
131
132func (r *Record) GetDate() *time.Time {
133	if len(r.Entries) == 0 {
134		return nil
135	}
136
137	last := *r.Entries[0].Date
138	for _, e := range r.Entries {
139		if e.Date.After(last) {
140			last = *e.Date
141		}
142	}
143	return &last
144}
145
146func FillEntryValues(entries []Entry) ([]Entry, int) {
147	var total int
148	for i := range entries {
149		entries[i].Odds = entries[i].GetOdds()
150		entries[i].Won = entries[i].DidWin()
151		entries[i].Date = entries[i].GetDate()
152		v := entries[i].GetValue()
153		entries[i].Value = &v
154		total += v
155	}
156	return entries, total
157}
158
159func FillRecordValues(records []Record) ([]Record, int) {
160	var total int
161	for i := range records {
162		_, v := FillEntryValues(records[i].Entries)
163		records[i].Date = records[i].GetDate()
164		records[i].Value = &v
165		total += v
166	}
167	return records, total
168}
169
170func GetRecords() (records []Record, total int, err error) {
171	err = DB.Preload("Entries.SubEntries").Find(&records).Error
172	if err != nil {
173		return
174	}
175
176	records, total = FillRecordValues(records)
177	return
178}