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

View File

@ -12,13 +12,18 @@ import (
const createBudget = `-- name: CreateBudget :one
INSERT INTO budgets
(name, last_modification)
VALUES ($1, NOW())
(name, income_category_id, last_modification)
VALUES ($1, $2, NOW())
RETURNING id, name, last_modification, income_category_id
`
func (q *Queries) CreateBudget(ctx context.Context, name string) (Budget, error) {
row := q.db.QueryRowContext(ctx, createBudget, name)
type CreateBudgetParams struct {
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
err := row.Scan(
&i.ID,

View File

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

View File

@ -1,7 +1,7 @@
-- name: CreateBudget :one
INSERT INTO budgets
(name, last_modification)
VALUES ($1, NOW())
(name, income_category_id, last_modification)
VALUES ($1, $2, NOW())
RETURNING *;
-- 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,
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
ALTER TABLE budgets DROP COLUMN income_category_id;