feat(backend): implement order state machine with validation

This commit is contained in:
Tiago Yamamoto 2025-12-27 01:18:00 -03:00
parent 89036d7b39
commit 132fef816c
3 changed files with 40 additions and 0 deletions

View file

@ -395,7 +395,10 @@ const (
OrderStatusPending OrderStatus = "Pendente" OrderStatusPending OrderStatus = "Pendente"
OrderStatusPaid OrderStatus = "Pago" OrderStatusPaid OrderStatus = "Pago"
OrderStatusInvoiced OrderStatus = "Faturado" OrderStatusInvoiced OrderStatus = "Faturado"
OrderStatusShipped OrderStatus = "Enviado"
OrderStatusDelivered OrderStatus = "Entregue" OrderStatusDelivered OrderStatus = "Entregue"
OrderStatusCompleted OrderStatus = "Concluído"
OrderStatusCancelled OrderStatus = "Cancelado"
) )
// PaymentMethod enumerates supported payment types. // PaymentMethod enumerates supported payment types.

View file

@ -337,6 +337,37 @@ func (s *Service) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, er
} }
func (s *Service) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error { func (s *Service) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error {
order, err := s.repo.GetOrder(ctx, id)
if err != nil {
return err
}
// State Machine Logic
switch order.Status {
case domain.OrderStatusPending:
if status != domain.OrderStatusPaid && status != domain.OrderStatusCancelled {
return errors.New("invalid transition from Pending")
}
case domain.OrderStatusPaid:
if status != domain.OrderStatusInvoiced && status != domain.OrderStatusShipped && status != domain.OrderStatusCancelled {
return errors.New("invalid transition from Paid")
}
case domain.OrderStatusInvoiced: // Can go to Shipped
if status != domain.OrderStatusShipped && status != domain.OrderStatusCancelled {
return errors.New("invalid transition from Invoiced")
}
case domain.OrderStatusShipped:
if status != domain.OrderStatusDelivered {
return errors.New("invalid transition from Shipped")
}
case domain.OrderStatusDelivered:
if status != domain.OrderStatusCompleted {
return errors.New("invalid transition from Delivered")
}
case domain.OrderStatusCompleted, domain.OrderStatusCancelled:
return errors.New("order is in terminal state")
}
return s.repo.UpdateOrderStatus(ctx, id, status) return s.repo.UpdateOrderStatus(ctx, id, status)
} }

View file

@ -646,6 +646,12 @@ func TestUpdateOrderStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to update order status: %v", err) t.Fatalf("failed to update order status: %v", err)
} }
// Test invalid transition
err = svc.UpdateOrderStatus(ctx, order.ID, domain.OrderStatusDelivered) // Paid -> Delivered is invalid (skip Shipped)
if err == nil {
t.Error("expected error for invalid transition Paid -> Delivered")
}
} }
// --- User Tests --- // --- User Tests ---