Support decimal currency amounts #74
| @@ -568,9 +568,9 @@ func TestCompleteTask(t *testing.T) { | ||||
| 	allowances := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(3) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| 	allowances.Value(0).Object().Value("progress").Number().IsEqual(31) | ||||
| 	allowances.Value(0).Object().Value("progress").Number().InDelta(30.34, 0.01) | ||||
| 	allowances.Value(1).Object().Value("id").Number().IsEqual(1) | ||||
| 	allowances.Value(1).Object().Value("progress").Number().IsEqual(60) | ||||
| 	allowances.Value(1).Object().Value("progress").Number().InDelta(60.66, 0.01) | ||||
| 	allowances.Value(2).Object().Value("id").Number().IsEqual(2) | ||||
| 	allowances.Value(2).Object().Value("progress").Number().IsEqual(10) | ||||
|  | ||||
| @@ -696,7 +696,7 @@ func getDelta(base time.Time, delta float64) (time.Time, time.Time) { | ||||
| 	return start, end | ||||
| } | ||||
|  | ||||
| func createTestAllowance(e *httpexpect.Expect, name string, target int, weight float64) { | ||||
| func createTestAllowance(e *httpexpect.Expect, name string, target float64, weight float64) { | ||||
| 	e.POST("/user/1/allowance").WithJSON(CreateAllowanceRequest{ | ||||
| 		Name:   name, | ||||
| 		Target: target, | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package main | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"log" | ||||
| 	"math" | ||||
| 	"time" | ||||
|  | ||||
| 	"gitea.seeseepuff.be/seeseemelk/mysqlite" | ||||
| @@ -49,8 +50,10 @@ func (db *Db) GetUsers() ([]User, error) { | ||||
| func (db *Db) GetUser(id int) (*UserWithAllowance, error) { | ||||
| 	user := &UserWithAllowance{} | ||||
|  | ||||
| 	var allowance int | ||||
| 	err := db.db.Query("select u.id, u.name, (select ifnull(sum(h.amount), 0) from history h where h.user_id = u.id) from users u where u.id = ?"). | ||||
| 		Bind(id).ScanSingle(&user.ID, &user.Name, &user.Allowance) | ||||
| 		Bind(id).ScanSingle(&user.ID, &user.Name, &allowance) | ||||
| 	user.Allowance = float64(allowance) / 100.0 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -70,18 +73,23 @@ func (db *Db) UserExists(userId int) (bool, error) { | ||||
| func (db *Db) GetUserAllowances(userId int) ([]Allowance, error) { | ||||
| 	allowances := make([]Allowance, 0) | ||||
| 	var err error | ||||
| 	var progress int64 | ||||
|  | ||||
| 	totalAllowance := Allowance{} | ||||
| 	err = db.db.Query("select balance, weight from users where id = ?").Bind(userId).ScanSingle(&totalAllowance.Progress, &totalAllowance.Weight) | ||||
| 	err = db.db.Query("select balance, weight from users where id = ?").Bind(userId).ScanSingle(&progress, &totalAllowance.Weight) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	totalAllowance.Progress = float64(progress) / 100.0 | ||||
| 	allowances = append(allowances, totalAllowance) | ||||
|  | ||||
| 	for row := range db.db.Query("select id, name, target, balance, weight from allowances where user_id = ?"). | ||||
| 		Bind(userId).Range(&err) { | ||||
| 		allowance := Allowance{} | ||||
| 		err = row.Scan(&allowance.ID, &allowance.Name, &allowance.Target, &allowance.Progress, &allowance.Weight) | ||||
| 		var target, progress int | ||||
| 		err = row.Scan(&allowance.ID, &allowance.Name, &target, &progress, &allowance.Weight) | ||||
| 		allowance.Target = float64(target) / 100.0 | ||||
| 		allowance.Progress = float64(progress) / 100.0 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -96,15 +104,20 @@ func (db *Db) GetUserAllowances(userId int) ([]Allowance, error) { | ||||
| func (db *Db) GetUserAllowanceById(userId int, allowanceId int) (*Allowance, error) { | ||||
| 	allowance := &Allowance{} | ||||
| 	if allowanceId == 0 { | ||||
| 		var progress int64 | ||||
| 		err := db.db.Query("select balance, weight from users where id = ?"). | ||||
| 			Bind(userId).ScanSingle(&allowance.Progress, &allowance.Weight) | ||||
| 			Bind(userId).ScanSingle(&progress, &allowance.Weight) | ||||
| 		allowance.Progress = float64(progress) / 100.0 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		var target, progress int64 | ||||
| 		err := db.db.Query("select id, name, target, balance, weight from allowances where user_id = ? and id = ?"). | ||||
| 			Bind(userId, allowanceId). | ||||
| 			ScanSingle(&allowance.ID, &allowance.Name, &allowance.Target, &allowance.Progress, &allowance.Weight) | ||||
| 			ScanSingle(&allowance.ID, &allowance.Name, &target, &progress, &allowance.Weight) | ||||
| 		allowance.Target = float64(target) / 100.0 | ||||
| 		allowance.Progress = float64(progress) / 100.0 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -130,7 +143,7 @@ func (db *Db) CreateAllowance(userId int, allowance *CreateAllowanceRequest) (in | ||||
|  | ||||
| 	// Insert the new allowance | ||||
| 	err = tx.Query("insert into allowances (user_id, name, target, weight) values (?, ?, ?, ?)"). | ||||
| 		Bind(userId, allowance.Name, allowance.Target, allowance.Weight). | ||||
| 		Bind(userId, allowance.Name, int(math.Round(allowance.Target*100.0)), allowance.Weight). | ||||
| 		Exec() | ||||
|  | ||||
| 	if err != nil { | ||||
| @@ -242,8 +255,9 @@ func (db *Db) UpdateAllowance(userId int, allowanceId int, allowance *UpdateAllo | ||||
| 	} | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	target := int(math.Round(allowance.Target * 100.0)) | ||||
| 	err = tx.Query("update allowances set name=?, target=?, weight=? where id = ? and user_id = ?"). | ||||
| 		Bind(allowance.Name, allowance.Target, allowance.Weight, allowanceId, userId). | ||||
| 		Bind(allowance.Name, target, allowance.Weight, allowanceId, userId). | ||||
| 		Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -284,8 +298,9 @@ func (db *Db) CreateTask(task *CreateTaskRequest) (int, error) { | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	// Insert the new task | ||||
| 	reward := int(math.Round(task.Reward * 100.0)) | ||||
| 	err = tx.Query("insert into tasks (name, reward, assigned) values (?, ?, ?)"). | ||||
| 		Bind(task.Name, task.Reward, task.Assigned). | ||||
| 		Bind(task.Name, reward, task.Assigned). | ||||
| 		Exec() | ||||
|  | ||||
| 	if err != nil { | ||||
| @@ -314,7 +329,9 @@ func (db *Db) GetTasks() ([]Task, error) { | ||||
|  | ||||
| 	for row := range db.db.Query("select id, name, reward, assigned from tasks").Range(&err) { | ||||
| 		task := Task{} | ||||
| 		err = row.Scan(&task.ID, &task.Name, &task.Reward, &task.Assigned) | ||||
| 		var reward int64 | ||||
| 		err = row.Scan(&task.ID, &task.Name, &reward, &task.Assigned) | ||||
| 		task.Reward = float64(reward) / 100.0 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -329,8 +346,10 @@ func (db *Db) GetTasks() ([]Task, error) { | ||||
| func (db *Db) GetTask(id int) (Task, error) { | ||||
| 	task := Task{} | ||||
|  | ||||
| 	var reward int64 | ||||
| 	err := db.db.Query("select id, name, reward, assigned from tasks where id = ?"). | ||||
| 		Bind(id).ScanSingle(&task.ID, &task.Name, &task.Reward, &task.Assigned) | ||||
| 		Bind(id).ScanSingle(&task.ID, &task.Name, &reward, &task.Assigned) | ||||
| 	task.Reward = float64(reward) / 100.0 | ||||
| 	if err != nil { | ||||
| 		return Task{}, err | ||||
| 	} | ||||
| @@ -369,8 +388,9 @@ func (db *Db) UpdateTask(id int, task *CreateTaskRequest) error { | ||||
| 	} | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	reward := int(math.Round(task.Reward * 100.0)) | ||||
| 	err = tx.Query("update tasks set name=?, reward=?, assigned=? where id = ?"). | ||||
| 		Bind(task.Name, task.Reward, task.Assigned, id). | ||||
| 		Bind(task.Name, reward, task.Assigned, id). | ||||
| 		Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -464,8 +484,9 @@ func (db *Db) AddHistory(userId int, allowance *PostHistory) error { | ||||
| 	} | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	amount := int(math.Round(allowance.Allowance * 100.0)) | ||||
| 	err = tx.Query("insert into history (user_id, timestamp, amount) values (?, ?, ?)"). | ||||
| 		Bind(userId, time.Now().Unix(), allowance.Allowance). | ||||
| 		Bind(userId, time.Now().Unix(), amount). | ||||
| 		Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -480,11 +501,12 @@ func (db *Db) GetHistory(userId int) ([]History, error) { | ||||
| 	for row := range db.db.Query("select amount, `timestamp` from history where user_id = ? order by `timestamp` desc"). | ||||
| 		Bind(userId).Range(&err) { | ||||
| 		allowance := History{} | ||||
| 		var timestamp int64 | ||||
| 		err = row.Scan(&allowance.Allowance, ×tamp) | ||||
| 		var timestamp, amount int64 | ||||
| 		err = row.Scan(&amount, ×tamp) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		allowance.Allowance = float64(amount) / 100.0 | ||||
| 		allowance.Timestamp = time.Unix(timestamp, 0) | ||||
| 		history = append(history, allowance) | ||||
| 	} | ||||
|   | ||||
| @@ -8,45 +8,45 @@ type User struct { | ||||
| } | ||||
|  | ||||
| type UserWithAllowance struct { | ||||
| 	ID        int    `json:"id"` | ||||
| 	Name      string `json:"name"` | ||||
| 	Allowance int    `json:"allowance"` | ||||
| 	ID        int     `json:"id"` | ||||
| 	Name      string  `json:"name"` | ||||
| 	Allowance float64 `json:"allowance"` | ||||
| } | ||||
|  | ||||
| type History struct { | ||||
| 	Allowance int       `json:"allowance"` | ||||
| 	Allowance float64   `json:"allowance"` | ||||
| 	Timestamp time.Time `json:"timestamp"` | ||||
| } | ||||
|  | ||||
| type PostHistory struct { | ||||
| 	Allowance int `json:"allowance"` | ||||
| 	Allowance float64 `json:"allowance"` | ||||
| } | ||||
|  | ||||
| // Task represents a task in the system. | ||||
| type Task struct { | ||||
| 	ID       int    `json:"id"` | ||||
| 	Name     string `json:"name"` | ||||
| 	Reward   int    `json:"reward"` | ||||
| 	Assigned *int   `json:"assigned"` // Pointer to allow null | ||||
| 	ID       int     `json:"id"` | ||||
| 	Name     string  `json:"name"` | ||||
| 	Reward   float64 `json:"reward"` | ||||
| 	Assigned *int    `json:"assigned"` // Pointer to allow null | ||||
| } | ||||
|  | ||||
| type Allowance struct { | ||||
| 	ID       int     `json:"id"` | ||||
| 	Name     string  `json:"name"` | ||||
| 	Target   int     `json:"target"` | ||||
| 	Progress int     `json:"progress"` | ||||
| 	Target   float64 `json:"target"` | ||||
| 	Progress float64 `json:"progress"` | ||||
| 	Weight   float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type CreateAllowanceRequest struct { | ||||
| 	Name   string  `json:"name"` | ||||
| 	Target int     `json:"target"` | ||||
| 	Target float64 `json:"target"` | ||||
| 	Weight float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type UpdateAllowanceRequest struct { | ||||
| 	Name   string  `json:"name"` | ||||
| 	Target int     `json:"target"` | ||||
| 	Target float64 `json:"target"` | ||||
| 	Weight float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| @@ -60,9 +60,9 @@ type CreateGoalResponse struct { | ||||
| } | ||||
|  | ||||
| type CreateTaskRequest struct { | ||||
| 	Name     string `json:"name" binding:"required"` | ||||
| 	Reward   int    `json:"reward"` | ||||
| 	Assigned *int   `json:"assigned"` | ||||
| 	Name     string  `json:"name" binding:"required"` | ||||
| 	Reward   float64 `json:"reward"` | ||||
| 	Assigned *int    `json:"assigned"` | ||||
| } | ||||
|  | ||||
| type CreateTaskResponse struct { | ||||
|   | ||||
| @@ -49,7 +49,7 @@ func renderCreateTask(c *gin.Context) { | ||||
|  | ||||
| 	name := c.PostForm("name") | ||||
| 	rewardStr := c.PostForm("reward") | ||||
| 	reward, err := strconv.Atoi(rewardStr) | ||||
| 	reward, err := strconv.ParseFloat(rewardStr, 64) | ||||
| 	if err != nil { | ||||
| 		renderError(c, http.StatusBadRequest, err) | ||||
| 		return | ||||
| @@ -96,7 +96,7 @@ func renderCreateAllowance(c *gin.Context) { | ||||
|  | ||||
| 	name := c.PostForm("name") | ||||
| 	targetStr := c.PostForm("target") | ||||
| 	target, err := strconv.Atoi(targetStr) | ||||
| 	target, err := strconv.ParseFloat(targetStr, 64) | ||||
| 	if err != nil { | ||||
| 		renderError(c, http.StatusBadRequest, err) | ||||
| 		return | ||||
|   | ||||
		Reference in New Issue
	
	Block a user