flamingo.me/flamingo-commerce/v3@v3.11.0/order/domain/orderDecorator.go (about)

     1  package domain
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  
     7  	"flamingo.me/flamingo-commerce/v3/product/domain"
     8  	"flamingo.me/flamingo/v3/framework/flamingo"
     9  )
    10  
    11  type (
    12  	// OrderDecoratorInterface defines the interface of the order decorator
    13  	OrderDecoratorInterface interface {
    14  		Create(context.Context, *Order) *DecoratedOrder
    15  	}
    16  
    17  	// OrderDecorator struct defines the order decorator
    18  	OrderDecorator struct {
    19  		ProductService domain.ProductService `inject:""`
    20  		Logger         flamingo.Logger       `inject:""`
    21  	}
    22  
    23  	// DecoratedOrder struct
    24  	DecoratedOrder struct {
    25  		Order          *Order
    26  		DecoratedItems []*DecoratedOrderItem
    27  	}
    28  
    29  	// DecoratedOrderItem struct
    30  	DecoratedOrderItem struct {
    31  		Item    *OrderItem
    32  		Product domain.BasicProduct
    33  	}
    34  
    35  	// GroupedDecoratedOrder struct
    36  	GroupedDecoratedOrder struct {
    37  		Order  *DecoratedOrder
    38  		Groups []*GroupedDecoratedOrderItems
    39  	}
    40  
    41  	// GroupedDecoratedOrderItems struct
    42  	GroupedDecoratedOrderItems struct {
    43  		DecoratedItems []*DecoratedOrderItem
    44  		Group          string
    45  	}
    46  )
    47  
    48  // check interface implementation
    49  var _ OrderDecoratorInterface = (*OrderDecorator)(nil)
    50  
    51  // Create creates a new decorated order
    52  func (rd *OrderDecorator) Create(ctx context.Context, order *Order) *DecoratedOrder {
    53  	result := &DecoratedOrder{Order: order}
    54  	result.DecoratedItems = rd.createDecoratedItems(ctx, order.OrderItems)
    55  
    56  	return result
    57  }
    58  
    59  func (rd *OrderDecorator) createDecoratedItems(ctx context.Context, items []*OrderItem) []*DecoratedOrderItem {
    60  	result := make([]*DecoratedOrderItem, len(items))
    61  	for i, item := range items {
    62  		result[i] = rd.createDecoratedItem(ctx, item)
    63  	}
    64  
    65  	return result
    66  }
    67  
    68  func (rd *OrderDecorator) createDecoratedItem(ctx context.Context, item *OrderItem) *DecoratedOrderItem {
    69  	result := &DecoratedOrderItem{
    70  		Item: item,
    71  	}
    72  
    73  	product, err := rd.ProductService.Get(ctx, item.MarketplaceCode)
    74  	switch {
    75  	case err != nil:
    76  		rd.Logger.WithContext(ctx).Error("order.decorator - no product for item", err)
    77  		// fallback to return something the frontend still could use
    78  		product = rd.createFallbackProduct(item)
    79  	case product.Type() == domain.TypeConfigurable && item.VariantMarketplaceCode != "":
    80  		configurable, ok := product.(domain.ConfigurableProduct)
    81  		if !ok {
    82  			// not a usable configrable
    83  			break
    84  		}
    85  
    86  		variant, err := configurable.GetConfigurableWithActiveVariant(item.VariantMarketplaceCode)
    87  		if err == nil {
    88  			product = variant
    89  		} else {
    90  			product = rd.createFallbackProduct(item)
    91  		}
    92  	}
    93  	result.Product = product
    94  
    95  	return result
    96  }
    97  
    98  func (rd *OrderDecorator) createFallbackProduct(item *OrderItem) *domain.SimpleProduct {
    99  	return &domain.SimpleProduct{
   100  		BasicProductData: domain.BasicProductData{
   101  			Title: item.Name,
   102  		},
   103  		Saleable: domain.Saleable{
   104  			IsSaleable: false,
   105  		},
   106  	}
   107  }
   108  
   109  // IsConfigurable - checks if current order item is a configurable product
   110  func (doi DecoratedOrderItem) IsConfigurable() bool {
   111  	return doi.Product.Type() == domain.TypeConfigurableWithActiveVariant
   112  }
   113  
   114  // GetVariant getter
   115  func (doi DecoratedOrderItem) GetVariant() (*domain.Variant, error) {
   116  	return doi.Product.(domain.ConfigurableProductWithActiveVariant).Variant(doi.Item.VariantMarketplaceCode)
   117  }
   118  
   119  // GetDisplayTitle getter
   120  func (doi DecoratedOrderItem) GetDisplayTitle() string {
   121  	if doi.IsConfigurable() {
   122  		variant, e := doi.GetVariant()
   123  		if e != nil {
   124  			return "Error Getting Variant"
   125  		}
   126  		return variant.Title
   127  	}
   128  	return doi.Product.BaseData().Title
   129  }
   130  
   131  // GetDisplayMarketplaceCode getter
   132  func (doi DecoratedOrderItem) GetDisplayMarketplaceCode() string {
   133  	if doi.IsConfigurable() {
   134  		variant, e := doi.GetVariant()
   135  		if e != nil {
   136  			return "Error Getting Variant"
   137  		}
   138  		return variant.MarketPlaceCode
   139  	}
   140  	return doi.Product.BaseData().MarketPlaceCode
   141  }
   142  
   143  // GetVariantsVariationAttributes gets the decorated order item variant attributes
   144  func (doi DecoratedOrderItem) GetVariantsVariationAttributes() domain.Attributes {
   145  	attributes := domain.Attributes{}
   146  	if doi.IsConfigurable() {
   147  		variant, _ := doi.GetVariant()
   148  
   149  		for _, attributeName := range doi.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes {
   150  			attributes[attributeName] = variant.BaseData().Attributes[attributeName]
   151  		}
   152  	}
   153  	return attributes
   154  }
   155  
   156  // GetVariantsVariationAttributeCodes gets the decorated order item variant variation attributes
   157  func (doi DecoratedOrderItem) GetVariantsVariationAttributeCodes() []string {
   158  	if doi.Product.Type() == domain.TypeConfigurableWithActiveVariant {
   159  		return doi.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes
   160  	}
   161  	return nil
   162  }
   163  
   164  // GetGroupedBy groups the decorated order into a *GroupedDecoratedOrder
   165  func (rd *DecoratedOrder) GetGroupedBy(group string, sortGroup bool) *GroupedDecoratedOrder {
   166  	result := &GroupedDecoratedOrder{
   167  		Order: rd,
   168  	}
   169  	groupedItemsCollection := make(map[string]*GroupedDecoratedOrderItems)
   170  	var groupedItemKeys []string
   171  
   172  	var groupKey string
   173  	for _, item := range rd.DecoratedItems {
   174  		switch group {
   175  		case "retailer_code":
   176  			groupKey = item.Product.BaseData().RetailerCode
   177  		default:
   178  			groupKey = "default"
   179  		}
   180  
   181  		if _, ok := groupedItemsCollection[groupKey]; !ok {
   182  			groupedItemsCollection[groupKey] = &GroupedDecoratedOrderItems{
   183  				Group: groupKey,
   184  			}
   185  
   186  			groupedItemKeys = append(groupedItemKeys, groupKey)
   187  		}
   188  
   189  		groupedItemsEntry := groupedItemsCollection[groupKey]
   190  		groupedItemsEntry.DecoratedItems = append(groupedItemsEntry.DecoratedItems, item)
   191  	}
   192  
   193  	if sortGroup {
   194  		sort.Strings(groupedItemKeys)
   195  	}
   196  
   197  	groups := make([]*GroupedDecoratedOrderItems, len(groupedItemKeys))
   198  	for i, key := range groupedItemKeys {
   199  		groupedItemsEntry := groupedItemsCollection[key]
   200  		groups[i] = groupedItemsEntry
   201  	}
   202  	result.Groups = groups
   203  
   204  	return result
   205  }
   206  
   207  // GetSourceIds collects the source ids of the items of the group
   208  func (i *GroupedDecoratedOrderItems) GetSourceIds() []string {
   209  	// the group has at least one group in there
   210  	sourceIds := make(map[string]bool, 1)
   211  	result := make([]string, 1)
   212  	for _, item := range i.DecoratedItems {
   213  		sourceID := item.Item.SourceID
   214  		if _, ok := sourceIds[sourceID]; ok {
   215  			continue
   216  		}
   217  
   218  		sourceIds[sourceID] = true
   219  		result = append(result, sourceID)
   220  	}
   221  
   222  	return result
   223  }