orders.go (126 lines of code) (raw):
package main
import (
"context"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
type Order struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at"`
CustomerID int `json:"customer_id"`
CustomerName string `json:"customer_name,omitempty"`
Lines []ProductOrderLine `json:"lines,omitempty"`
}
type ProductOrderLine struct {
Product
Amount int `json:"amount"`
}
func getOrders(ctx context.Context, db *sqlx.DB) ([]Order, error) {
const limit = 1000
queryString := `SELECT
orders.id, orders.created_at,
customers.id, customers.full_name
FROM orders JOIN customers ON orders.customer_id=customers.id
`
queryString += fmt.Sprintf("LIMIT %d\n", limit)
rows, err := db.QueryContext(ctx, queryString)
if err != nil {
return nil, errors.Wrap(err, "querying orders")
}
defer rows.Close()
var orders []Order
for rows.Next() {
var o Order
if err := rows.Scan(
&o.ID, &o.CreatedAt,
&o.CustomerID, &o.CustomerName,
); err != nil {
return nil, err
}
orders = append(orders, o)
}
return orders, rows.Err()
}
func getOrder(ctx context.Context, db *sqlx.DB, id int) (*Order, error) {
queryString := db.Rebind(`SELECT
orders.id, orders.created_at, customer_id
FROM orders WHERE orders.id=?`)
row := db.QueryRowContext(ctx, queryString, id)
var order Order
if err := row.Scan(&order.ID, &order.CreatedAt, &order.CustomerID); err != nil {
return nil, errors.Wrap(err, "querying order")
}
queryString = db.Rebind(`SELECT
product_id, amount,
products.sku, products.name, products.description,
products.type_id, products.stock, products.cost, products.selling_price
FROM products JOIN order_lines ON products.id=order_lines.product_id
WHERE order_lines.order_id=?`)
rows, err := db.QueryContext(ctx, queryString, id)
if err != nil {
return nil, errors.Wrap(err, "querying product order lines")
}
defer rows.Close()
var lines []ProductOrderLine
for rows.Next() {
var l ProductOrderLine
if err := rows.Scan(
&l.ID, &l.Amount,
&l.SKU, &l.Name, &l.Description,
&l.TypeID, &l.Stock, &l.Cost, &l.SellingPrice,
); err != nil {
return nil, err
}
lines = append(lines, l)
}
order.Lines = lines
return &order, rows.Err()
}
func createOrder(ctx context.Context, db *sqlx.DB, customer *Customer, lines []ProductOrderLine) (int, error) {
tx, err := db.BeginTxx(ctx, nil)
if err != nil {
return -1, err
}
defer tx.Rollback()
driver := db.DriverName()
returningID := "RETURNING id"
if driver == "sqlite3" {
returningID = ""
}
insertOrderStmt := db.Rebind("INSERT INTO orders (customer_id) VALUES (?) " + returningID)
insertOrderLineStmt, err := tx.PrepareContext(ctx, db.Rebind(
"INSERT INTO order_lines (order_id, product_id, amount) VALUES(?, ?, ?)",
))
if err != nil {
return -1, errors.Wrap(err, "failed to prepare insert order lines statement")
}
defer insertOrderLineStmt.Close()
var orderID int
if returningID == "" {
result, err := tx.ExecContext(ctx, insertOrderStmt, customer.ID)
if err != nil {
return -1, err
}
rowID, err := result.LastInsertId()
if err != nil {
return -1, err
}
orderID = int(rowID)
} else {
err := tx.QueryRowContext(ctx, insertOrderStmt, customer.ID).Scan(&orderID)
if err != nil {
return -1, err
}
}
for _, line := range lines {
if _, err := insertOrderLineStmt.ExecContext(ctx, orderID, line.Product.ID, line.Amount); err != nil {
return -1, err
}
}
if err := tx.Commit(); err != nil {
return -1, err
}
return orderID, nil
}