Completed db rewrite
This commit is contained in:
parent
54d7d14ef9
commit
cf4b8e0119
19 changed files with 481 additions and 276 deletions
12
core/debts.go
Normal file
12
core/debts.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package core
|
||||
|
||||
func (d *Debt) Create(db SqlExecutor) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO debts(expense_id, debtor_id, amount, paid) VALUES (?,?,?,?)",
|
||||
d.ExpenseId,
|
||||
d.DebtorId,
|
||||
d.Amount,
|
||||
d.Paid,
|
||||
)
|
||||
return err
|
||||
}
|
||||
159
core/expenses.go
159
core/expenses.go
|
|
@ -1,11 +1,12 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DebtType int
|
||||
|
|
@ -44,27 +45,42 @@ type DebtSpec struct {
|
|||
DebtType DebtType `json:"debt_type"`
|
||||
}
|
||||
|
||||
func CreateExpense(db *gorm.DB, plan Plan, payer Member, amount decimal.Decimal) (*Expense, error) {
|
||||
func ExpenseCreate(db *sql.DB, plan Plan, payer Member, amount decimal.Decimal) (*Expense, error) {
|
||||
expense := new(Expense)
|
||||
expense.Plan = plan
|
||||
expense.Payer = payer
|
||||
expense.PlanId = plan.Id
|
||||
expense.PayerId = payer.Id
|
||||
expense.Amount = amount
|
||||
err := db.Create(&expense).Error
|
||||
if err != nil {
|
||||
row := db.QueryRow(
|
||||
"INSERT INTO expenses(plan_id,payer_id,amount) VALUES (?,?,?) RETURNING id",
|
||||
expense.PlanId,
|
||||
expense.PayerId,
|
||||
expense.Amount,
|
||||
)
|
||||
if err := row.Scan(&expense.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return expense, nil
|
||||
}
|
||||
|
||||
func (e *Expense) GetDebt(db *gorm.DB) ([]Debt, error) {
|
||||
func (e *Expense) GetDebt(db *sql.DB) ([]Debt, error) {
|
||||
var debts []Debt
|
||||
if err := db.Model(e).Association("Debts").Find(&debts); err != nil {
|
||||
return []Debt{}, err
|
||||
rows, err := db.Query("SELECT expense_id,debtor_id,amount,paid FROM debts WHERE expense_id=?")
|
||||
if err != nil {
|
||||
return debts, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var d Debt
|
||||
err = rows.Scan(&d.ExpenseId, &d.DebtorId, &d.Amount, &d.Paid)
|
||||
if err != nil {
|
||||
return []Debt{}, err
|
||||
}
|
||||
debts = append(debts, d)
|
||||
}
|
||||
return debts, nil
|
||||
}
|
||||
|
||||
func (e *Expense) SetDebt(db *gorm.DB, debtSpec []DebtSpec) error {
|
||||
func (e *Expense) SetDebt(db *sql.DB, debtSpec []DebtSpec) error {
|
||||
abs_paid := decimal.Decimal{}
|
||||
debts := make([]Debt, 0)
|
||||
var prop_payers int64 = 0
|
||||
|
|
@ -75,8 +91,8 @@ func (e *Expense) SetDebt(db *gorm.DB, debtSpec []DebtSpec) error {
|
|||
debts = append(debts, Debt{
|
||||
Amount: debt.Amount,
|
||||
Paid: decimal.Decimal{},
|
||||
ExpenseID: e.ID,
|
||||
DebtorID: debt.Member.ID,
|
||||
ExpenseId: e.Id,
|
||||
DebtorId: debt.Member.Id,
|
||||
})
|
||||
abs_paid.Add(debt.Amount)
|
||||
}
|
||||
|
|
@ -86,52 +102,95 @@ func (e *Expense) SetDebt(db *gorm.DB, debtSpec []DebtSpec) error {
|
|||
}
|
||||
|
||||
prop_debt := e.Amount.Sub(abs_paid).DivRound(decimal.NewFromInt(prop_payers), 2)
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
for _, debt := range debts {
|
||||
if err := db.Create(&debt).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, debt := range debtSpec {
|
||||
if debt.DebtType != ProportionalDebt {
|
||||
continue
|
||||
}
|
||||
if err := db.Create(&Debt{
|
||||
Amount: prop_debt,
|
||||
Paid: decimal.Decimal{},
|
||||
ExpenseID: e.ID,
|
||||
DebtorID: debt.Member.ID,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (e *Expense) Delete(db *gorm.DB) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := db.Model(e).Association("Debts").Delete(&[]Debt{}); err != nil {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
for _, debt := range debts {
|
||||
if err := debt.Create(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Delete(e, e).Error
|
||||
})
|
||||
}
|
||||
|
||||
for _, debt := range debtSpec {
|
||||
if debt.DebtType != ProportionalDebt {
|
||||
continue
|
||||
}
|
||||
debtObj := Debt{
|
||||
Amount: prop_debt,
|
||||
Paid: decimal.Decimal{},
|
||||
ExpenseId: e.Id,
|
||||
DebtorId: debt.Member.Id,
|
||||
}
|
||||
if err := debtObj.Create(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func ListExpenses(db *gorm.DB, plan Plan) ([]Expense, error) {
|
||||
func (e *Expense) Delete(db *sql.DB) error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
_, err = tx.Exec("DELETE FROM debts WHERE expense_id=?", e.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to delete debts for expense %d: %w", e.Id, err)
|
||||
}
|
||||
_, err = tx.Exec("DELETE FROM expenses WHERE id=?", e.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to delete expense %d: %w", e.Id, err)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to commit transaction when deleting expense %d: %w",
|
||||
e.Id,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExpensesList(db *sql.DB, plan Plan) ([]Expense, error) {
|
||||
var expenses []Expense
|
||||
if err := db.Where("plan_id = ?", plan.ID).Find(&expenses).Error; err != nil {
|
||||
return []Expense{}, err
|
||||
rows, err := db.Query(
|
||||
"SELECT id,plan_id,payer_id,amount FROM expenses WHERE plan_id=$1",
|
||||
plan.Id,
|
||||
)
|
||||
if err != nil {
|
||||
return expenses, fmt.Errorf(
|
||||
"Unable to query for expenses in plan %d: %w",
|
||||
plan.Id,
|
||||
err,
|
||||
)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
e := Expense{}
|
||||
err = rows.Scan(&e.Id, e.PlanId, e.PayerId, e.Amount)
|
||||
if err != nil {
|
||||
return expenses, fmt.Errorf(
|
||||
"Unable to scan expense during list: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
expenses = append(expenses, e)
|
||||
}
|
||||
return expenses, nil
|
||||
}
|
||||
|
||||
func GetExpense(db *gorm.DB, expense_id uint) (*Expense, error) {
|
||||
expense := new(Expense)
|
||||
expense.ID = expense_id
|
||||
if err := db.Take(expense).Error; err != nil {
|
||||
return nil, err
|
||||
func ExpensesGet(db *sql.DB, expense_id uint) (*Expense, error) {
|
||||
e := Expense{}
|
||||
row := db.QueryRow(
|
||||
"SELECT id,plan_id,payer_id,amount FROM expenses WHERE id=?",
|
||||
expense_id,
|
||||
)
|
||||
if err := row.Scan(&e.Id, &e.PlanId, &e.PayerId, &e.Amount); err != nil {
|
||||
return nil, fmt.Errorf("Unable to get expense %d: %w", expense_id, err)
|
||||
}
|
||||
return expense, nil
|
||||
return &e, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,64 +3,52 @@ package core
|
|||
import "github.com/shopspring/decimal"
|
||||
|
||||
type User struct {
|
||||
Username string `gorm:"primaryKey" json:"username"`
|
||||
Password string `json:"password"`
|
||||
OwnedPlans []Plan `gorm:"foreignKey:Owner;references:Username" json:"-"`
|
||||
MemberOf []Member `json:"-"`
|
||||
Votes []Vote `gorm:"foreignKey:UsernameID" json:"-"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement:true" json:"-"`
|
||||
PlanID uint `json:"-"`
|
||||
Plan Plan `json:"-"`
|
||||
Name string `gorm:"check:user_id IS NOT NULL OR name IS NOT NULL" json:"name,omitempty"`
|
||||
UserID string `json:"username,omitempty"`
|
||||
User User `gorm:"foreignKey:UserID" json:"-"`
|
||||
Id uint `json:"-"`
|
||||
PlanId uint `json:"-"`
|
||||
Name string `json:"name,omitempty"`
|
||||
UserId *string `json:"username,omitempty"`
|
||||
}
|
||||
|
||||
// CREATE TABLE plans(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, owner STRING, FOREIGN KEY(owner) REFERENCES users(username))
|
||||
// CREATE TABLE plan_user_relations(username STRING, plan INTEGER, PRIMARY KEY(username, plan), FOREIGN KEY username REFERENCES user(username), FOREIGN KEY plan REFERENCES plans(id))
|
||||
type Plan struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
Description string `json:"description"`
|
||||
JoinCode string `gorm:"not null" json:"join_code,omitempty"`
|
||||
Members []Member `json:"-"`
|
||||
Polls []Poll `gorm:"foreignKey:PlanID;references:ID" json:"-"`
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
Description string `json:"description"`
|
||||
JoinCode string `json:"join_code,omitempty"`
|
||||
}
|
||||
|
||||
type Expense struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
|
||||
PlanID uint `json:"-"`
|
||||
Plan Plan `json:"-"`
|
||||
PayerID uint `json:"-"`
|
||||
Payer Member `gorm:"foreignKey:PayerID" json:"-"`
|
||||
Id uint `json:"id"`
|
||||
PlanId uint `json:"-"`
|
||||
PayerId uint `json:"-"`
|
||||
Amount decimal.Decimal `json:"amount"`
|
||||
Debts []Debt `gorm:"foreignKey:ExpenseID" json:"debts,omitempty"`
|
||||
}
|
||||
|
||||
type Debt struct {
|
||||
ExpenseID uint `gorm:"primaryKey" json:"-"`
|
||||
Expense Expense `gorm:"foreignKey:ExpenseID"`
|
||||
DebtorID uint `gorm:"primaryKey" json:"-"`
|
||||
Debtor Member `gorm:"foreignKey:DebtorID"`
|
||||
Id uint
|
||||
ExpenseId uint `json:"-"`
|
||||
DebtorId uint `json:"-"`
|
||||
Amount decimal.Decimal
|
||||
Paid decimal.Decimal
|
||||
}
|
||||
|
||||
// CREATE TABLE polls(id INTEGER PRIMARY KEY AUTOINCREMENT, plan INTEGER, name STRING, options JSON, FOREIGN KEY plan REFERENCES plans(id))
|
||||
type Poll struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
|
||||
PlanID uint `json:"-"`
|
||||
Id uint `json:"id"`
|
||||
PlanId uint `json:"-"`
|
||||
Options string `json:"options"`
|
||||
Votes []Vote `gorm:"foreignKey:PollID;references:ID" json:"-"`
|
||||
}
|
||||
|
||||
// CREATE TABLE votes(id INTEGER, poll INTEGER, user STRING, value JSON, FOREIGN KEY poll REFERENCES polls(id), FOREIGN KEY user REFERENCES user(username))
|
||||
type Vote struct {
|
||||
PollID uint `gorm:"primaryKey" json:"-"`
|
||||
UsernameID string `gorm:"primaryKey" json:"username_id"`
|
||||
Value string `json:"value"`
|
||||
PollId uint `json:"-"`
|
||||
MemberId uint `json:"member_id"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
|
|
|||
118
core/plans.go
118
core/plans.go
|
|
@ -2,13 +2,13 @@ package core
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func CreatePlan(db *gorm.DB, user *User, name string) (*Plan, error) {
|
||||
func PlanCreate(db *sql.DB, user *User, name string) (*Plan, error) {
|
||||
join_code := make([]byte, 32)
|
||||
_, err := rand.Read(join_code)
|
||||
if err != nil {
|
||||
|
|
@ -16,100 +16,101 @@ func CreatePlan(db *gorm.DB, user *User, name string) (*Plan, error) {
|
|||
}
|
||||
|
||||
var plan Plan = Plan{
|
||||
Name: name,
|
||||
Owner: user.Username,
|
||||
Members: []Member{
|
||||
{
|
||||
UserID: user.Username,
|
||||
},
|
||||
},
|
||||
Name: name,
|
||||
Owner: user.Username,
|
||||
JoinCode: base64.URLEncoding.EncodeToString(join_code),
|
||||
}
|
||||
result := db.Create(&plan)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
row := db.QueryRow(
|
||||
"INSERT INTO plans(name, owner, description, join_jode) VALUES (?, ?, '', ?) RETURNING id",
|
||||
plan.Name,
|
||||
plan.Owner,
|
||||
plan.JoinCode,
|
||||
)
|
||||
if err := row.Scan(&plan.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &plan, nil
|
||||
}
|
||||
|
||||
func GetPlan(orm *gorm.DB, id uint) (*Plan, error) {
|
||||
var plan Plan = Plan{
|
||||
ID: id,
|
||||
func PlanGet(db *sql.DB, id uint) (*Plan, error) {
|
||||
plan := Plan{}
|
||||
row := db.QueryRow("SELECT name,owner,description,join_code FROM plans WHERE id=?", id)
|
||||
if err := row.Scan(&plan.Name, &plan.Owner, &plan.Description, &plan.JoinCode); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("Unexpected database error: %w", err)
|
||||
}
|
||||
result := orm.Take(&plan)
|
||||
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrNotFound
|
||||
} else if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return &plan, nil
|
||||
}
|
||||
|
||||
func (p *Plan) GetAllUsers(orm *gorm.DB) ([]Member, error) {
|
||||
var members []Member
|
||||
err := orm.Model(p).Association("Members").Find(&members)
|
||||
func (p *Plan) GetAllUsers(db *sql.DB) ([]Member, error) {
|
||||
members := []Member{}
|
||||
rows, err := db.Query("SELECT id,plan_id,name,user_id FROM members WHERE plan_id=?", p.Id)
|
||||
if err != nil {
|
||||
return members, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var m Member
|
||||
rows.Scan(&m.Id, &m.PlanId, &m.Name, &m.UserId)
|
||||
members = append(members, m)
|
||||
}
|
||||
return members, err
|
||||
}
|
||||
|
||||
func (p *Plan) IsMember(orm *gorm.DB, u *User) (bool, error) {
|
||||
var member_count int64
|
||||
result := orm.
|
||||
Table("members").
|
||||
Where("user_id=? AND plan_id=?", u.Username, p.ID).
|
||||
Count(&member_count).Error
|
||||
if result != nil {
|
||||
return false, ErrNotMember
|
||||
func (p *Plan) IsMember(db *sql.DB, u *User) (bool, error) {
|
||||
var member_count int
|
||||
row := db.QueryRow("SELECT count(1) FROM members WHERE plan_id=? AND user_id=?", p.Id, u.Username)
|
||||
if err := row.Scan(&member_count); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return member_count == 1, nil
|
||||
}
|
||||
|
||||
func (p *Plan) GetMember(orm *gorm.DB, u *User) (*Member, error) {
|
||||
func (p *Plan) GetMember(db *sql.DB, u *User) (*Member, error) {
|
||||
var m Member
|
||||
result := orm.
|
||||
Table("members").
|
||||
Where("user_id=? AND plan_id=?", u.Username, p.ID).
|
||||
Take(&m).Error
|
||||
if result != nil {
|
||||
return nil, ErrNotMember
|
||||
row := db.QueryRow("SELECT id,plan_id,name,user_id FROM members WHERE plan_id=? AND user_id=?", p.Id, u.Username)
|
||||
if err := row.Scan(&m.Id, &m.PlanId, &m.Name, &m.UserId); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotMember
|
||||
}
|
||||
return nil, fmt.Errorf("Unable to check if user %s is member in plan %d: %w", u.Username, p.Id, err)
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (p *Plan) HasNonUser(orm *gorm.DB, name string) (bool, error) {
|
||||
var member_count int64
|
||||
result := orm.
|
||||
Table("members").
|
||||
Where("plan_id=? AND name=?", p.ID, name).
|
||||
Count(&member_count).Error
|
||||
if result != nil {
|
||||
return false, result
|
||||
func (p *Plan) HasNonUser(db *sql.DB, name string) (bool, error) {
|
||||
var member_count int
|
||||
row := db.QueryRow("SELECT count(1) FROM members WHERE plan_id=? AND name=?", p.Id, name)
|
||||
if err := row.Scan(&member_count); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return member_count == 1, nil
|
||||
}
|
||||
|
||||
func (p *Plan) AddMember(orm *gorm.DB, new_member *Member) error {
|
||||
func (p *Plan) AddMember(db *sql.DB, new_member *Member) error {
|
||||
if new_member == nil {
|
||||
return errors.New("Member is nil")
|
||||
}
|
||||
new_member.PlanID = p.ID
|
||||
new_member.PlanId = p.Id
|
||||
if new_member.Name != "" {
|
||||
found, err := p.HasNonUser(orm, new_member.Name)
|
||||
found, err := p.HasNonUser(db, new_member.Name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if found {
|
||||
return errors.New("Non user name taken")
|
||||
}
|
||||
return orm.Create(&new_member).Error
|
||||
} else if new_member.UserID != "" {
|
||||
user, err := GetUser(orm, new_member.UserID)
|
||||
_, err = db.Exec("INSERT INTO members(plan_id, name) VALUES (?,?)", new_member.PlanId, new_member.Name)
|
||||
return err
|
||||
} else if *new_member.UserId != "" {
|
||||
user, err := UserGet(db, *new_member.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := p.IsMember(orm, &user)
|
||||
found, err := p.IsMember(db, &user)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -117,7 +118,8 @@ func (p *Plan) AddMember(orm *gorm.DB, new_member *Member) error {
|
|||
return errors.New("User already is member")
|
||||
}
|
||||
|
||||
return orm.Create(&new_member).Error
|
||||
_, err = db.Exec("INSERT INTO members(plan_id, user_id) VALUES (?,?)", new_member.PlanId, new_member.UserId)
|
||||
return err
|
||||
} else {
|
||||
return errors.New("Member object requires one of Name or UserID to be filled")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,59 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetPoll(orm *gorm.DB, user User, id uint) (*Poll, error) {
|
||||
var poll Poll = Poll{
|
||||
ID: id,
|
||||
func (p *Poll) Create(db *sql.DB) error {
|
||||
row := db.QueryRow(
|
||||
"INSERT INTO polls(plan_id, options) VALUES(?,?) RETURNING id",
|
||||
p.PlanId,
|
||||
p.Options,
|
||||
)
|
||||
if err := row.Scan(&p.Id); err != nil {
|
||||
return fmt.Errorf("Unable to create new poll for plan %d: %w", p.PlanId, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if result := orm.Take(&poll); result.Error != nil {
|
||||
return nil, result.Error
|
||||
func PollGet(db *sql.DB, user User, id uint) (*Poll, error) {
|
||||
var poll Poll
|
||||
row := db.QueryRow("SELECT id,plan_id,options FROM polls WHERE id=?", id)
|
||||
if err := row.Scan(&poll.Id, &poll.PlanId, &poll.Options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", poll.PlanID)
|
||||
|
||||
return &poll, nil
|
||||
}
|
||||
|
||||
func (p *Poll) SetVote(orm *gorm.DB, user User, option string) error {
|
||||
found := false
|
||||
func PollsList(db *sql.DB, plan_id int) ([]Poll, error) {
|
||||
var polls []Poll
|
||||
rows, err := db.Query("SELECT id,plan_id,options FROM polls WHERE plan_id=?", plan_id)
|
||||
if err != nil {
|
||||
return polls, fmt.Errorf("Unable to query polls for plan %d: %w", plan_id, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var poll Poll
|
||||
if err := rows.Scan(&poll.Id, &poll.PlanId, &poll.Options); err != nil {
|
||||
return nil, fmt.Errorf("Unable to scan polls for plan %d: %w", plan_id, err)
|
||||
}
|
||||
polls = append(polls, poll)
|
||||
}
|
||||
|
||||
return polls, nil
|
||||
}
|
||||
|
||||
func (p *Poll) SetVote(db *sql.DB, member Member, option string) error {
|
||||
options := strings.Split(p.Options, ",")
|
||||
|
||||
for _, opt := range options {
|
||||
if opt == option {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return ErrInvalidOption
|
||||
if !slices.Contains(options, option) {
|
||||
return fmt.Errorf("%s is not a valid option (%s): %w", option, options, ErrInvalidOption)
|
||||
}
|
||||
_, err := db.Exec("INSERT INTO votes(poll_id,member_id,value) VALUES (?,?,?)", p.Id, member.Id, option)
|
||||
|
||||
if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
9
core/sqlexecutor.go
Normal file
9
core/sqlexecutor.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package core
|
||||
|
||||
import "database/sql"
|
||||
|
||||
type SqlExecutor interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
|
@ -1,31 +1,49 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (u *User) ListPlans(orm *gorm.DB) ([]Plan, error) {
|
||||
func (u *User) ListPlans(db *sql.DB) ([]Plan, error) {
|
||||
var plans []Plan
|
||||
err := orm.Debug().Table("plans p").
|
||||
Select("p.*").
|
||||
Joins("JOIN members m ON m.plan_id=p.id").
|
||||
Where("m.user_id=?", u.Username).
|
||||
Find(&plans)
|
||||
return plans, err.Error
|
||||
rows, err := db.Query(
|
||||
"SELECT p.id,p.name,p.owner,p.description,p.join_code FROM plans p "+
|
||||
"JOIN members m ON m.plan_id=p.id "+
|
||||
"WHERE m.user_id=?",
|
||||
u.Username,
|
||||
)
|
||||
if err != nil {
|
||||
return plans, fmt.Errorf(
|
||||
"Unable to query plans for user %s: %w",
|
||||
u.Username,
|
||||
err,
|
||||
)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
p := Plan{}
|
||||
err = rows.Scan(&p.Id, &p.Name, &p.Owner, &p.Description, &p.JoinCode)
|
||||
if err != nil {
|
||||
return plans, fmt.Errorf(
|
||||
"Unable to scan plan for user %s: %w",
|
||||
u.Username,
|
||||
err,
|
||||
)
|
||||
}
|
||||
plans = append(plans, p)
|
||||
}
|
||||
return plans, nil
|
||||
}
|
||||
|
||||
func (u *User) GetPlan(db *gorm.DB, plan_id uint) (Plan, error) {
|
||||
var plan Plan = Plan{
|
||||
ID: plan_id,
|
||||
}
|
||||
result := db.Take(&plan)
|
||||
func (u *User) GetPlan(db *sql.DB, plan_id uint) (*Plan, error) {
|
||||
plan, err := PlanGet(db, plan_id)
|
||||
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return plan, ErrNotFound
|
||||
} else if result.Error != nil {
|
||||
return plan, result.Error
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if plan.Owner == u.Username {
|
||||
|
|
@ -33,18 +51,43 @@ func (u *User) GetPlan(db *gorm.DB, plan_id uint) (Plan, error) {
|
|||
}
|
||||
|
||||
isMember, err := plan.IsMember(db, u)
|
||||
|
||||
if !isMember || err != nil {
|
||||
return plan, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func GetUser(orm *gorm.DB, username string) (User, error) {
|
||||
user := User{Username: username}
|
||||
if err := orm.Take(&user).Error; err != nil {
|
||||
return user, err
|
||||
func (u *User) GetMemberFromPlan(db *sql.DB, p Plan) (*Member, error) {
|
||||
m := Member{}
|
||||
row := db.QueryRow(
|
||||
"SELECT id,plan_id,name,user_id FROM members WHERE user_id=? AND plan_id=?",
|
||||
u.Username,
|
||||
p.Id,
|
||||
)
|
||||
if err := row.Scan(&m.Id, &m.PlanId, &m.Name, &m.UserId); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Unable to get member from user %s and plan %d: %w",
|
||||
u.Username,
|
||||
p.Id,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return user, nil
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func UserGet(db *sql.DB, username string) (User, error) {
|
||||
var user User
|
||||
row := db.QueryRow("SELECT username, password FROM users WHERE username=?", username)
|
||||
err := row.Scan(&user.Username, &user.Password)
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (u *User) Create(db *sql.DB) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO users(username, password) VALUES (?,?)",
|
||||
u.Username,
|
||||
u.Password,
|
||||
)
|
||||
return fmt.Errorf("Unable to create user %s: %w", u.Username, err)
|
||||
}
|
||||
|
|
|
|||
23
core/votes.go
Normal file
23
core/votes.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func VotesList(db *sql.DB, poll_id int) ([]Vote, error) {
|
||||
votes := []Vote{}
|
||||
rows, err := db.Query("SELECT poll_id,member_id,value FROM polls WHERE poll_id=?", poll_id)
|
||||
if err != nil {
|
||||
return votes, fmt.Errorf("Unable to get votes for poll %d: %w", poll_id, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
v := Vote{}
|
||||
err = rows.Scan(&v.PollId, &v.MemberId, &v.Value)
|
||||
if err != nil {
|
||||
return votes, fmt.Errorf("Unable to scan vote for poll %d: %w", poll_id, err)
|
||||
}
|
||||
}
|
||||
return votes, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue