products.go (118 lines of code) (raw):
package main
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
type Product struct {
SKU string `json:"sku"`
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Stock int `json:"stock"`
Cost int `json:"cost"`
SellingPrice int `json:"selling_price"`
Sold int `json:"sold,omitempty"`
TypeID int `json:"type_id,omitempty"`
TypeName string `json:"type_name,omitempty"`
}
type ProductType struct {
ID int `json:"id"`
Name string `json:"name"`
}
func getProducts(ctx context.Context, db *sqlx.DB) ([]Product, error) {
return queryProducts(ctx, db, nil)
}
func getTopProducts(ctx context.Context, db *sqlx.DB) ([]Product, error) {
const limit = 3 // top 3 best-selling products
queryString := `SELECT
id, sku, name, stock, SUM(order_lines.amount) AS sold
FROM products JOIN order_lines ON id=product_id GROUP BY products.id ORDER BY sold DESC
`
queryString += fmt.Sprintf("LIMIT %d\n", limit)
rows, err := db.QueryContext(ctx, queryString)
if err != nil {
return nil, errors.Wrap(err, "querying top products")
}
defer rows.Close()
var products []Product
for rows.Next() {
var p Product
if err := rows.Scan(&p.ID, &p.SKU, &p.Name, &p.Stock, &p.Sold); err != nil {
return nil, err
}
products = append(products, p)
}
return products, rows.Err()
}
func getProduct(ctx context.Context, db *sqlx.DB, id int) (*Product, error) {
products, err := queryProducts(ctx, db, &id)
if err != nil || len(products) == 0 {
return nil, err
}
return &products[0], nil
}
func queryProducts(ctx context.Context, db *sqlx.DB, id *int) ([]Product, error) {
var args []interface{}
queryString := `SELECT
products.id, products.sku, products.name, products.description,
products.stock, products.cost, products.selling_price,
products.type_id, product_types.name
FROM products JOIN product_types ON type_id=product_types.id
`
if id != nil {
queryString += "WHERE products.id=?\n"
args = append(args, *id)
}
rows, err := db.QueryContext(ctx, db.Rebind(queryString), args...)
if err != nil {
return nil, errors.Wrap(err, "querying products")
}
defer rows.Close()
var products []Product
for rows.Next() {
var p Product
if err := rows.Scan(
&p.ID, &p.SKU, &p.Name, &p.Description,
&p.Stock, &p.Cost, &p.SellingPrice,
&p.TypeID, &p.TypeName,
); err != nil {
return nil, err
}
products = append(products, p)
}
return products, rows.Err()
}
func getProductTypes(ctx context.Context, db *sqlx.DB) ([]ProductType, error) {
return queryProductTypes(ctx, db, nil)
}
func getProductType(ctx context.Context, db *sqlx.DB, id int) (*ProductType, error) {
productTypes, err := queryProductTypes(ctx, db, &id)
if err != nil || len(productTypes) == 0 {
return nil, err
}
return &productTypes[0], nil
}
func queryProductTypes(ctx context.Context, db *sqlx.DB, id *int) ([]ProductType, error) {
var args []interface{}
queryString := "SELECT id, name FROM product_types"
if id != nil {
queryString += " WHERE id=?"
args = append(args, *id)
}
rows, err := db.QueryContext(ctx, db.Rebind(queryString), args...)
if err != nil {
return nil, errors.Wrap(err, "querying product types")
}
defer rows.Close()
var productTypes []ProductType
for rows.Next() {
var pt ProductType
if err := rows.Scan(&pt.ID, &pt.Name); err != nil {
return nil, err
}
productTypes = append(productTypes, pt)
}
return productTypes, rows.Err()
}