func (engine *Engine) CreateOrder(order *data.Order) error { if order.ID != 0 { return ErrNonEmptyID } if order.State == "" { order.State = data.OrderStateDraft } else if order.State != data.OrderStateDraft { return ErrNotDraft } if order.Time == 0 { order.Time = uint64(time.Now().UTC().Unix()) } else if order.Time < uint64(time.Now().UTC().Add(-PastTimeTreshold).Unix()) { return ErrIsPast } tx, err := engine.storage.Begin() if err != nil { return err } if err := tx.CreateOrder(order); err != nil { return tx.Rollback(err) } populate.Order(order) if err := tx.Commit(); err != nil { return err } return nil }
func TestForestCreateOrder(t *testing.T) { _time := uint64(time.Now().UTC().Unix()) expected := data.Order{ State: data.OrderStateDraft, Time: _time, } populate.Order(&expected) payload := data.Order{ Time: _time, } got := data.Order{} r := getForestClient().POST(t, forest.Path("/orders/{}", "").Content(payload, MimeJSON)) forest.ExpectStatus(t, r, http.StatusCreated) forest.ExpectJSONDocument(t, r, &got) expected.ID = got.ID if !reflect.DeepEqual(got, expected) { t.Errorf("Got: %s\n\nExpected: %s", testutils.JSON(got), testutils.JSON(expected)) } }
func TestForestOrderItemCreateOrderPlaced(t *testing.T) { productInOrder := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&productInOrder) product := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&product) order := data.Order{ Items: []data.OrderItem{ data.OrderItem{ Product: productInOrder, Quantity: 1, }, }, } createOrder(&order) order.State = data.OrderStatePlaced updateOrder(&order) payload := data.OrderItem{ Product: data.Product{ ID: product.ID, }, Quantity: 0, } got := Error{} r := getForestClient().POST(t, forest.Path("/orders/{orderID}/items/{}", order.ID, "").Content(payload, MimeJSON)) // TODO: // forest.ExpectStatus(t, r, http.StatusForbidden) forest.ExpectStatus(t, r, http.StatusInternalServerError) forest.ExpectJSONDocument(t, r, &got) if got.Message == "" { t.Error("Got empty error message") } }
func (engine *Engine) UpdateOrder(order *data.Order) error { if order.ID == 0 { return ErrEmptyID } tx, err := engine.storage.Begin() if err != nil { return err } existingOrder := &data.Order{ ID: order.ID, } if err := tx.LoadOrder(existingOrder); err != nil { return tx.Rollback(err) } if order.Time == 0 { order.Time = existingOrder.Time } else if order.Time < uint64(time.Now().UTC().Add(-PastTimeTreshold).Unix()) { return tx.Rollback(ErrIsPast) } if order.State == "" { order.State = existingOrder.State } else if order.State != existingOrder.State { if existingOrder.State != data.OrderStateDraft { return tx.Rollback(ErrNotDraft) } switch order.State { case data.OrderStatePlaced: if len(existingOrder.Items) < 1 { return tx.Rollback(ErrOrderEmpty) } case data.OrderStateCancelled: default: return tx.Rollback(ErrUnknownState) } } if err := tx.UpdateOrder(order); err != nil { return tx.Rollback(err) } populate.Order(order) if err := tx.Commit(); err != nil { return err } return nil }
func TestForestCreateOrderWithItems(t *testing.T) { _time := uint64(time.Now().UTC().Unix()) expected := data.Order{ State: data.OrderStateDraft, Time: _time, } payload := data.Order{ Time: _time, } for i := 1; i <= 3; i++ { product := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: uint64(100 * i), }, } createProduct(&product) item := data.OrderItem{ Product: product, Quantity: uint64(i), } // newest come first: payload.Items = append([]data.OrderItem{item}, payload.Items...) expected.Items = append([]data.OrderItem{item}, expected.Items...) } populate.Order(&expected) got := data.Order{} r := getForestClient().POST(t, forest.Path("/orders/{}", "").Content(payload, MimeJSON)) forest.ExpectStatus(t, r, http.StatusCreated) forest.ExpectJSONDocument(t, r, &got) expected.ID = got.ID if !reflect.DeepEqual(got, expected) { t.Errorf("Got: %s\n\nExpected: %s", testutils.JSON(got), testutils.JSON(expected)) } }
func TestForestRemoveOrderItemOrderPlaced(t *testing.T) { productInOrder := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&productInOrder) product := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&product) order := data.Order{ Items: []data.OrderItem{ data.OrderItem{ Product: productInOrder, Quantity: 1, }, }, } createOrder(&order) order.State = data.OrderStatePlaced updateOrder(&order) got := Error{} r := getForestClient().DELETE(t, forest.Path("/orders/{orderID}/items/{productID}", order.ID, product.ID)) // TODO: // forest.ExpectStatus(t, r, http.StatusNotFound) forest.ExpectStatus(t, r, http.StatusInternalServerError) forest.ExpectJSONDocument(t, r, &got) if got.Message == "" { t.Error("Got empty error message") } }
func TestForestListOrderItems(t *testing.T) { order := data.Order{} for i := 1; i <= 3; i++ { product := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&product) item := data.OrderItem{ Product: product, Quantity: 1, } order.Items = append([]data.OrderItem{item}, order.Items...) } createOrder(&order) expected := order.Items got := []data.OrderItem{} r := getForestClient().GET(t, forest.Path("/orders/{orderID}/items/{}", order.ID, "").Query("limit", len(expected))) forest.ExpectStatus(t, r, http.StatusOK) forest.ExpectJSONDocument(t, r, &got) if !reflect.DeepEqual(got, expected) { t.Errorf("Got: %s\n\nExpected: %s", testutils.JSON(got), testutils.JSON(expected)) } }
func Order(order *data.Order) { order.Price.Vat = 0 order.Price.Net = 0 order.Price.Gross = 0 if order.Items == nil { order.Items = []data.OrderItem{} } for i, item := range order.Items { OrderItem(&item) order.Price.Net += item.Price.Net order.Price.Gross += item.Price.Gross order.Items[i] = item } }
func TestForestUpdateOrderInvalidStateTransition(t *testing.T) { invalidTransitions := [][2]string{ // [2]string{data.OrderStateDraft, data.OrderStatePlaced}, // valid // [2]string{data.OrderStateDraft, data.OrderStateCancelled}, // valid [2]string{data.OrderStatePlaced, data.OrderStateDraft}, [2]string{data.OrderStatePlaced, data.OrderStateCancelled}, [2]string{data.OrderStateCancelled, data.OrderStateDraft}, [2]string{data.OrderStateCancelled, data.OrderStatePlaced}, [2]string{data.OrderStateDraft, "UNKNOWN"}, [2]string{data.OrderStatePlaced, "UNKNOWN"}, [2]string{data.OrderStateCancelled, "UNKNOWN"}, } product := data.Product{ Name: generateName("Product"), Price: data.Price{ Vat: 20, Net: 100, }, } createProduct(&product) for _, invalidTransition := range invalidTransitions { fromState, toState := invalidTransition[0], invalidTransition[1] order := data.Order{ Items: []data.OrderItem{ data.OrderItem{ Product: product, Quantity: 1, }, }, } createOrder(&order) if fromState != order.State { order.State = fromState updateOrder(&order) } payload := data.Order{ State: toState, } got := Error{} r := getForestClient().PUT(t, forest.Path("/orders/{id}", order.ID).Content(payload, MimeJSON)) // TODO: // forest.ExpectStatus(t, r, http.StatusBadRequest) forest.ExpectStatus(t, r, http.StatusInternalServerError) forest.ExpectJSONDocument(t, r, &got) if got.Message == "" { t.Error("Got empty error message") } } }
func (tx *Tx) LoadOrder(order *data.Order) error { var err error err = tx.tx.QueryRow( ` SELECT state, time FROM orders WHERE id = $1 `, order.ID, ).Scan( &order.State, &order.Time, ) if err != nil { return err } // end SELECT FROM orders // reset item list: order.Items = nil rows, err := tx.tx.Query( ` SELECT products.id, products.name, products.price_vat, products.price_net, order_items.quantity FROM order_items JOIN products ON products.id = order_items.product_id WHERE order_items.order_id = $1 `, order.ID, ) if err != nil { return err } // end SELECT FROM order_items JOIN products for rows.Next() { item := data.OrderItem{} err := rows.Scan( &item.Product.ID, &item.Product.Name, &item.Product.Price.Vat, &item.Product.Price.Net, &item.Quantity, ) if err != nil { return err } order.Items = append(order.Items, item) } // end for rows return nil }
func (tx *Tx) UpdateOrder(order *data.Order) error { err := tx.tx.QueryRow( ` UPDATE orders SET state = $2, time = $3 WHERE id = $1 RETURNING id `, order.ID, order.State, order.Time, ).Scan( &order.ID, ) if err != nil { return err } // end UPDATE orders // reset items list (if method got well-populated order); // TODO: probably, update order items as well (if len > 0)? order.Items = nil rows, err := tx.tx.Query( ` SELECT products.id, products.name, products.price_vat, products.price_net, order_items.quantity FROM order_items JOIN products ON products.id = order_items.product_id WHERE order_items.order_id = $1 `, order.ID, ) if err != nil { return err } // end SELECT FROM order_items JOIN products for rows.Next() { item := data.OrderItem{} err := rows.Scan( &item.Product.ID, &item.Product.Name, &item.Product.Price.Vat, &item.Product.Price.Net, &item.Quantity, ) if err != nil { return err } order.Items = append(order.Items, item) } // end for rows return nil }