github.com/shoshinnikita/budget-manager@v0.7.1-0.20220131195411-8c46ff1c6778/internal/db/base/spend.go (about) 1 package base 2 3 import ( 4 "context" 5 "database/sql" 6 "time" 7 8 common "github.com/ShoshinNikita/budget-manager/internal/db" 9 "github.com/ShoshinNikita/budget-manager/internal/db/base/internal/sqlx" 10 "github.com/ShoshinNikita/budget-manager/internal/db/base/types" 11 "github.com/ShoshinNikita/budget-manager/internal/pkg/errors" 12 "github.com/ShoshinNikita/budget-manager/internal/pkg/money" 13 ) 14 15 type Spend struct { 16 ID uint `db:"id"` 17 DayID uint `db:"day_id"` 18 Title string `db:"title"` 19 TypeID types.Uint `db:"type_id"` 20 Notes types.String `db:"notes"` 21 Cost money.Money `db:"cost"` 22 23 Type *SpendType `db:"type"` 24 } 25 26 // ToCommon converts Spend to common Spend structure from 27 // "github.com/ShoshinNikita/budget-manager/internal/db" package 28 func (s Spend) ToCommon(year int, month time.Month, day int) common.Spend { 29 return common.Spend{ 30 ID: s.ID, 31 Year: year, 32 Month: month, 33 Day: day, 34 Title: s.Title, 35 Type: s.Type.ToCommon(), 36 Notes: string(s.Notes), 37 Cost: s.Cost, 38 } 39 } 40 41 // AddSpend adds a new Spend 42 func (db DB) AddSpend(ctx context.Context, args common.AddSpendArgs) (id uint, err error) { 43 err = db.db.RunInTransaction(ctx, func(tx *sqlx.Tx) (err error) { 44 if !checkDay(tx, args.DayID) { 45 return common.ErrDayNotExist 46 } 47 if args.TypeID != 0 && !checkSpendType(tx, args.TypeID) { 48 return common.ErrSpendTypeNotExist 49 } 50 51 err = tx.Get( 52 &id, 53 `INSERT INTO spends(day_id, title, notes, type_id, cost) VALUES(?, ?, ?, ?, ?) RETURNING id`, 54 args.DayID, args.Title, args.Notes, types.Uint(args.TypeID), args.Cost, 55 ) 56 if err != nil { 57 return err 58 } 59 60 monthID, err := db.selectMonthIDByDayID(tx, args.DayID) 61 if err != nil { 62 return err 63 } 64 return db.recomputeAndUpdateMonth(tx, monthID) 65 }) 66 if err != nil { 67 return 0, err 68 } 69 70 return id, nil 71 } 72 73 // EditSpend edits existeng Spend 74 func (db DB) EditSpend(ctx context.Context, args common.EditSpendArgs) error { 75 return db.db.RunInTransaction(ctx, func(tx *sqlx.Tx) error { 76 if !checkSpend(tx, args.ID) { 77 return common.ErrSpendNotExist 78 } 79 if args.TypeID != nil && *args.TypeID != 0 && !checkSpendType(tx, *args.TypeID) { 80 return common.ErrSpendTypeNotExist 81 } 82 83 dayID, err := db.selectSpendDayID(tx, args.ID) 84 if err != nil { 85 return err 86 } 87 88 query := newUpdateQueryBuilder("spends", args.ID) 89 if args.Title != nil { 90 query.Set("title", *args.Title) 91 } 92 if args.TypeID != nil { 93 if *args.TypeID == 0 { 94 query.Set("type_id", nil) 95 } else { 96 query.Set("type_id", *args.TypeID) 97 } 98 } 99 if args.Notes != nil { 100 query.Set("notes", *args.Notes) 101 } 102 if args.Cost != nil { 103 query.Set("cost", *args.Cost) 104 } 105 if _, err := tx.ExecQuery(query); err != nil { 106 return err 107 } 108 109 if args.Cost != nil { 110 // Recompute month only when cost has been changed 111 monthID, err := db.selectMonthIDByDayID(tx, dayID) 112 if err != nil { 113 return err 114 } 115 return db.recomputeAndUpdateMonth(tx, monthID) 116 } 117 return nil 118 }) 119 } 120 121 // RemoveSpend removes Spend with passed id 122 func (db DB) RemoveSpend(ctx context.Context, id uint) error { 123 return db.db.RunInTransaction(ctx, func(tx *sqlx.Tx) error { 124 if !checkSpend(tx, id) { 125 return common.ErrSpendNotExist 126 } 127 128 dayID, err := db.selectSpendDayID(tx, id) 129 if err != nil { 130 return err 131 } 132 133 _, err = tx.Exec(`DELETE FROM spends WHERE id = ?`, id) 134 if err != nil { 135 return err 136 } 137 138 monthID, err := db.selectMonthIDByDayID(tx, dayID) 139 if err != nil { 140 return err 141 } 142 return db.recomputeAndUpdateMonth(tx, monthID) 143 }) 144 } 145 146 func (DB) selectSpendDayID(tx *sqlx.Tx, id uint) (dayID uint, err error) { 147 err = tx.Get(&dayID, `SELECT day_id FROM spends WHERE id = ?`, id) 148 if err != nil { 149 return 0, errors.Wrap(err, "couldn't select day id of Spend") 150 } 151 return dayID, nil 152 } 153 154 func (DB) selectMonthIDByDayID(tx *sqlx.Tx, dayID uint) (monthID uint, err error) { 155 err = tx.Get(&monthID, `SELECT month_id FROM days WHERE id = ?`, dayID) 156 if err != nil { 157 if errors.Is(err, sql.ErrNoRows) { 158 return 0, common.ErrDayNotExist 159 } 160 return 0, errors.Wrap(err, "couldn't get Month which contains Day with passed id") 161 } 162 return monthID, nil 163 }