Handle circular required keys

Use a dummy-value at first and update it later.
Deferrable doesn't seem to work for NOT NULL - only
for FOREIGN KEYs.
This commit is contained in:
Jan Bader 2021-12-14 14:13:19 +00:00
parent 9e01be699a
commit a8bd03a805
5 changed files with 51 additions and 28 deletions

View File

@ -1,6 +1,7 @@
package http package http
import ( import (
"fmt"
"net/http" "net/http"
"time" "time"
@ -10,45 +11,50 @@ import (
) )
func (h *Handler) newTransaction(c *gin.Context) { func (h *Handler) newTransaction(c *gin.Context) {
transactionMemo, succ := c.GetPostForm("memo") transactionMemo, _ := c.GetPostForm("memo")
if !succ {
c.AbortWithStatus(http.StatusNotAcceptable)
return
}
transactionAccount, succ := c.GetPostForm("account_id") transactionAccount, succ := c.GetPostForm("account_id")
if !succ { if !succ {
c.AbortWithStatus(http.StatusNotAcceptable) c.AbortWithError(http.StatusNotAcceptable, fmt.Errorf("account_id missing"))
return return
} }
transactionAccountID, err := uuid.Parse(transactionAccount) transactionAccountID, err := uuid.Parse(transactionAccount)
if !succ { if !succ {
c.AbortWithStatus(http.StatusNotAcceptable) c.AbortWithError(http.StatusNotAcceptable, fmt.Errorf("account_id is not a valid uuid"))
return return
} }
transactionDate, succ := c.GetPostForm("date") transactionDate, succ := c.GetPostForm("date")
if !succ { if !succ {
c.AbortWithStatus(http.StatusNotAcceptable) c.AbortWithError(http.StatusNotAcceptable, fmt.Errorf("date missing"))
return return
} }
transactionDateValue, err := time.Parse("2006-01-02", transactionDate) transactionDateValue, err := time.Parse("2006-01-02", transactionDate)
if err != nil { if err != nil {
c.AbortWithStatus(http.StatusNotAcceptable) c.AbortWithError(http.StatusNotAcceptable, fmt.Errorf("date is not a valid date"))
return return
} }
transactionAmount, succ := c.GetPostForm("amount")
if !succ {
c.AbortWithError(http.StatusNotAcceptable, fmt.Errorf("amount missing"))
return
}
amount := postgres.Numeric{}
amount.Set(transactionAmount)
new := postgres.CreateTransactionParams{ new := postgres.CreateTransactionParams{
Memo: transactionMemo, Memo: transactionMemo,
Date: transactionDateValue, Date: transactionDateValue,
Amount: postgres.Numeric{}, Amount: amount,
AccountID: transactionAccountID, AccountID: transactionAccountID,
PayeeID: uuid.NullUUID{},
CategoryID: uuid.NullUUID{},
} }
_, err = h.Service.CreateTransaction(c.Request.Context(), new) _, err = h.Service.CreateTransaction(c.Request.Context(), new)
if err != nil { if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transaction: %w", err))
return return
} }
} }

View File

@ -12,13 +12,18 @@ import (
const createBudget = `-- name: CreateBudget :one const createBudget = `-- name: CreateBudget :one
INSERT INTO budgets INSERT INTO budgets
(name, last_modification) (name, income_category_id, last_modification)
VALUES ($1, NOW()) VALUES ($1, $2, NOW())
RETURNING id, name, last_modification, income_category_id RETURNING id, name, last_modification, income_category_id
` `
func (q *Queries) CreateBudget(ctx context.Context, name string) (Budget, error) { type CreateBudgetParams struct {
row := q.db.QueryRowContext(ctx, createBudget, name) Name string
IncomeCategoryID uuid.UUID
}
func (q *Queries) CreateBudget(ctx context.Context, arg CreateBudgetParams) (Budget, error) {
row := q.db.QueryRowContext(ctx, createBudget, arg.Name, arg.IncomeCategoryID)
var i Budget var i Budget
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,

View File

@ -3,6 +3,7 @@ package postgres
import ( import (
"context" "context"
"database/sql" "database/sql"
"fmt"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -11,15 +12,18 @@ import (
func (s *Database) NewBudget(context context.Context, name string, userID uuid.UUID) (*Budget, error) { func (s *Database) NewBudget(context context.Context, name string, userID uuid.UUID) (*Budget, error) {
tx, err := s.BeginTx(context, &sql.TxOptions{}) tx, err := s.BeginTx(context, &sql.TxOptions{})
q := s.WithTx(tx) q := s.WithTx(tx)
budget, err := q.CreateBudget(context, name) budget, err := q.CreateBudget(context, CreateBudgetParams{
Name: name,
IncomeCategoryID: uuid.New(),
})
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("create budget: %w", err)
} }
ub := LinkBudgetToUserParams{UserID: userID, BudgetID: budget.ID} ub := LinkBudgetToUserParams{UserID: userID, BudgetID: budget.ID}
_, err = q.LinkBudgetToUser(context, ub) _, err = q.LinkBudgetToUser(context, ub)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("link budget to user: %w", err)
} }
group, err := q.CreateCategoryGroup(context, CreateCategoryGroupParams{ group, err := q.CreateCategoryGroup(context, CreateCategoryGroupParams{
@ -27,18 +31,25 @@ func (s *Database) NewBudget(context context.Context, name string, userID uuid.U
BudgetID: budget.ID, BudgetID: budget.ID,
}) })
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("create inflow category_group: %w", err)
} }
cat, err := q.CreateCategory(context, CreateCategoryParams{ cat, err := q.CreateCategory(context, CreateCategoryParams{
Name: "Ready to assign", Name: "Ready to Assign",
CategoryGroupID: group.ID, CategoryGroupID: group.ID,
}) })
if err != nil {
return nil, fmt.Errorf("create ready to assign category: %w", err)
}
q.SetInflowCategory(context, SetInflowCategoryParams{ err = q.SetInflowCategory(context, SetInflowCategoryParams{
IncomeCategoryID: cat.ID, IncomeCategoryID: cat.ID,
ID: budget.ID, ID: budget.ID,
}) })
if err != nil {
return nil, fmt.Errorf("set inflow category: %w", err)
}
tx.Commit() tx.Commit()
return &budget, nil return &budget, nil

View File

@ -1,7 +1,7 @@
-- name: CreateBudget :one -- name: CreateBudget :one
INSERT INTO budgets INSERT INTO budgets
(name, last_modification) (name, income_category_id, last_modification)
VALUES ($1, NOW()) VALUES ($1, $2, NOW())
RETURNING *; RETURNING *;
-- name: SetInflowCategory :exec -- name: SetInflowCategory :exec

View File

@ -4,7 +4,8 @@ CREATE TABLE categories (
category_group_id uuid NOT NULL REFERENCES category_groups (id) ON DELETE CASCADE, category_group_id uuid NOT NULL REFERENCES category_groups (id) ON DELETE CASCADE,
name varchar(50) NOT NULL name varchar(50) NOT NULL
); );
ALTER TABLE budgets ADD COLUMN income_category_id uuid NOT NULL REFERENCES categories (id) DEFERRABLE; ALTER TABLE budgets ADD COLUMN
income_category_id uuid NOT NULL REFERENCES categories (id) DEFERRABLE INITIALLY DEFERRED;
-- +goose Down -- +goose Down
ALTER TABLE budgets DROP COLUMN income_category_id; ALTER TABLE budgets DROP COLUMN income_category_id;