228 lines
6.7 KiB
Go
228 lines
6.7 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"database/sql"
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/go-chi/chi/v5"
|
||
|
|
)
|
||
|
|
|
||
|
|
type OrderHandler struct {
|
||
|
|
DB *sql.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewOrderHandler(db *sql.DB) *OrderHandler {
|
||
|
|
return &OrderHandler{DB: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
type Order struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
CustomerID string `json:"customer_id"`
|
||
|
|
QuoteID *string `json:"quote_id"`
|
||
|
|
OrderNumber string `json:"order_number"`
|
||
|
|
Title string `json:"title"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
Amount float64 `json:"amount"`
|
||
|
|
TaxAmount float64 `json:"tax_amount"`
|
||
|
|
Currency string `json:"currency"`
|
||
|
|
DeliveryDate *time.Time `json:"delivery_date"`
|
||
|
|
ShippedAt *time.Time `json:"shipped_at"`
|
||
|
|
DeliveredAt *time.Time `json:"delivered_at"`
|
||
|
|
TrackingNumber string `json:"tracking_number"`
|
||
|
|
Notes string `json:"notes"`
|
||
|
|
CreatedAt time.Time `json:"created_at"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) ListOrders(w http.ResponseWriter, r *http.Request) {
|
||
|
|
status := r.URL.Query().Get("status")
|
||
|
|
if status == "" {
|
||
|
|
status = "all"
|
||
|
|
}
|
||
|
|
|
||
|
|
var query string
|
||
|
|
var args []interface{}
|
||
|
|
if status == "all" {
|
||
|
|
query = `SELECT id, customer_id, quote_id, order_number, title, status, amount, currency, delivery_date, shipped_at, delivered_at, tracking_number, created_at FROM boc_orders ORDER BY created_at DESC LIMIT 100`
|
||
|
|
} else {
|
||
|
|
query = `SELECT id, customer_id, quote_id, order_number, title, status, amount, currency, delivery_date, shipped_at, delivered_at, tracking_number, created_at FROM boc_orders WHERE status = $1 ORDER BY created_at DESC LIMIT 100`
|
||
|
|
args = append(args, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
rows, err := h.DB.Query(query, args...)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "database error")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
orders := []Order{}
|
||
|
|
for rows.Next() {
|
||
|
|
var o Order
|
||
|
|
if err := rows.Scan(&o.ID, &o.CustomerID, &o.QuoteID, &o.OrderNumber, &o.Title, &o.Status, &o.Amount, &o.Currency, &o.DeliveryDate, &o.ShippedAt, &o.DeliveredAt, &o.TrackingNumber, &o.CreatedAt); err != nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
orders = append(orders, o)
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"orders": orders,
|
||
|
|
"total": len(orders),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var req struct {
|
||
|
|
CustomerID string `json:"customer_id"`
|
||
|
|
Title string `json:"title"`
|
||
|
|
DeliveryDate *time.Time `json:"delivery_date"`
|
||
|
|
Notes string `json:"notes"`
|
||
|
|
Items []struct {
|
||
|
|
ProductID string `json:"product_id"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Quantity float64 `json:"quantity"`
|
||
|
|
UnitPrice float64 `json:"unit_price"`
|
||
|
|
TaxRate float64 `json:"tax_rate"`
|
||
|
|
} `json:"items"`
|
||
|
|
}
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
writeError(w, http.StatusBadRequest, "invalid request")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
orderNumber := "O-" + time.Now().Format("20060102-150405")
|
||
|
|
|
||
|
|
var totalAmount, totalTax float64
|
||
|
|
for _, item := range req.Items {
|
||
|
|
itemTotal := item.Quantity * item.UnitPrice
|
||
|
|
itemTax := itemTotal * (item.TaxRate / 100)
|
||
|
|
totalAmount += itemTotal
|
||
|
|
totalTax += itemTax
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := h.DB.Begin()
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "transaction error")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer tx.Rollback()
|
||
|
|
|
||
|
|
var id string
|
||
|
|
err = tx.QueryRow(`
|
||
|
|
INSERT INTO boc_orders (customer_id, order_number, title, amount, tax_amount, currency, delivery_date, notes)
|
||
|
|
VALUES ($1, $2, $3, $4, $5, 'USD', $6, $7)
|
||
|
|
RETURNING id
|
||
|
|
`, req.CustomerID, orderNumber, req.Title, totalAmount, totalTax, req.DeliveryDate, req.Notes).Scan(&id)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to create order")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, item := range req.Items {
|
||
|
|
itemTotal := item.Quantity * item.UnitPrice
|
||
|
|
_, err = tx.Exec(`
|
||
|
|
INSERT INTO boc_order_items (order_id, product_id, description, quantity, unit_price, tax_rate, total)
|
||
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||
|
|
`, id, item.ProductID, item.Description, item.Quantity, item.UnitPrice, item.TaxRate, itemTotal)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to create order items")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := tx.Commit(); err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "commit failed")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusCreated, map[string]interface{}{
|
||
|
|
"id": id,
|
||
|
|
"number": orderNumber,
|
||
|
|
"message": "Order created",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) GetOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
|
id := chi.URLParam(r, "id")
|
||
|
|
|
||
|
|
var o Order
|
||
|
|
err := h.DB.QueryRow(`
|
||
|
|
SELECT id, customer_id, quote_id, order_number, title, status, amount, tax_amount, currency, delivery_date, shipped_at, delivered_at, tracking_number, notes, created_at
|
||
|
|
FROM boc_orders WHERE id = $1
|
||
|
|
`, id).Scan(&o.ID, &o.CustomerID, &o.QuoteID, &o.OrderNumber, &o.Title, &o.Status, &o.Amount, &o.TaxAmount, &o.Currency, &o.DeliveryDate, &o.ShippedAt, &o.DeliveredAt, &o.TrackingNumber, &o.Notes, &o.CreatedAt)
|
||
|
|
|
||
|
|
if err == sql.ErrNoRows {
|
||
|
|
writeError(w, http.StatusNotFound, "order not found")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "database error")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, o)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) UpdateOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
|
id := chi.URLParam(r, "id")
|
||
|
|
|
||
|
|
var req Order
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
writeError(w, http.StatusBadRequest, "invalid request")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err := h.DB.Exec(`
|
||
|
|
UPDATE boc_orders
|
||
|
|
SET status = $1, delivery_date = $2, tracking_number = $3, notes = $4
|
||
|
|
WHERE id = $5
|
||
|
|
`, req.Status, req.DeliveryDate, req.TrackingNumber, req.Notes, id)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to update order")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"message": "Order updated",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) ShipOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
|
id := chi.URLParam(r, "id")
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
TrackingNumber string `json:"tracking_number"`
|
||
|
|
}
|
||
|
|
json.NewDecoder(r.Body).Decode(&req)
|
||
|
|
|
||
|
|
_, err := h.DB.Exec(`
|
||
|
|
UPDATE boc_orders SET status = 'shipped', shipped_at = NOW(), tracking_number = $1 WHERE id = $2
|
||
|
|
`, req.TrackingNumber, id)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to ship order")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"message": "Order shipped",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *OrderHandler) DeliverOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
|
id := chi.URLParam(r, "id")
|
||
|
|
|
||
|
|
_, err := h.DB.Exec(`
|
||
|
|
UPDATE boc_orders SET status = 'delivered', delivered_at = NOW() WHERE id = $1
|
||
|
|
`, id)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to deliver order")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"message": "Order delivered",
|
||
|
|
})
|
||
|
|
}
|