public async Task <SuperHSProduct> CreateModifiedMonitoredSuperProductNotification(MonitoredProductFieldModifiedNotification notification, VerifiedUserContext user)
        {
            if (notification == null || notification?.Product == null)
            {
                throw new Exception("Unable to process notification with no product");
            }
            var _product = await _oc.Products.PatchAsync(notification.Product.ID, new PartialProduct { Active = false }, user.AccessToken);

            var document = new Document <MonitoredProductFieldModifiedNotification>();

            document.Doc = notification;
            document.ID  = $"{notification.Product.ID}_{CosmosInteropID.New()}";
            // Create notifictaion in the cms
            await _cms.Documents.Create(_documentSchemaID, document, await GetAdminToken());

            // Assign the notification to the product
            // TODO: this doesn't work because need to own thing being assigned to AND have DocumentAdmin and we don't want to give suppliers DocumentAdmin
            // await _cms.Documents.SaveAssignment("MonitoredProductFieldModifiedNotification", new DocumentAssignment() { DocumentID = document.ID, ResourceType = ResourceType.Products, ResourceID = _product.ID }, user.AccessToken);
            return(await _productCommand.Get(_product.ID, user.AccessToken));
        }
        public async Task <SuperHSProduct> Post(SuperHSProduct superProduct, VerifiedUserContext user)
        {
            // Determine ID up front so price schedule ID can match
            superProduct.Product.ID = superProduct.Product.ID ?? CosmosInteropID.New();

            await ValidateVariantsAsync(superProduct, user.AccessToken);

            // Create Specs
            var defaultSpecOptions = new List <DefaultOptionSpecAssignment>();
            var specRequests       = await Throttler.RunAsync(superProduct.Specs, 100, 5, s =>
            {
                defaultSpecOptions.Add(new DefaultOptionSpecAssignment {
                    SpecID = s.ID, OptionID = s.DefaultOptionID
                });
                s.DefaultOptionID = null;
                return(_oc.Specs.SaveAsync <Spec>(s.ID, s, accessToken: user.AccessToken));
            });

            // Create Spec Options
            foreach (Spec spec in superProduct.Specs)
            {
                await Throttler.RunAsync(spec.Options, 100, 5, o => _oc.Specs.SaveOptionAsync(spec.ID, o.ID, o, accessToken: user.AccessToken));
            }
            // Patch Specs with requested DefaultOptionID
            await Throttler.RunAsync(defaultSpecOptions, 100, 10, a => _oc.Specs.PatchAsync(a.SpecID, new PartialSpec {
                DefaultOptionID = a.OptionID
            }, accessToken: user.AccessToken));

            // Create Price Schedule
            PriceSchedule _priceSchedule = null;

            //All products must have a price schedule for orders to be submitted.  The front end provides a default Price of $0 for quote products that don't have one.
            superProduct.PriceSchedule.ID = superProduct.Product.ID;
            try
            {
                _priceSchedule = await _oc.PriceSchedules.CreateAsync <PriceSchedule>(superProduct.PriceSchedule, user.AccessToken);
            }
            catch (OrderCloudException ex)
            {
                if (ex.HttpStatus == System.Net.HttpStatusCode.Conflict)
                {
                    throw new Exception($"Product SKU {superProduct.PriceSchedule.ID} already exists.  Please try a different SKU.");
                }
            }
            superProduct.Product.DefaultPriceScheduleID = _priceSchedule.ID;
            // Create Product
            if (user.Supplier != null)
            {
                var supplierName = await GetSupplierNameForXpFacet(user.Supplier.ID, user.AccessToken);

                superProduct.Product.xp.Facets.Add("supplier", new List <string>()
                {
                    supplierName
                });
            }
            var _product = await _oc.Products.CreateAsync <HSProduct>(superProduct.Product, user.AccessToken);

            // Make Spec Product Assignments
            await Throttler.RunAsync(superProduct.Specs, 100, 5, s => _oc.Specs.SaveProductAssignmentAsync(new SpecProductAssignment {
                ProductID = _product.ID, SpecID = s.ID
            }, accessToken: user.AccessToken));

            // Generate Variants
            await _oc.Products.GenerateVariantsAsync(_product.ID, accessToken : user.AccessToken);

            // Patch Variants with the User Specified ID(SKU) AND necessary display xp values
            await Throttler.RunAsync(superProduct.Variants, 100, 5, v =>
            {
                var oldVariantID = v.ID;
                v.ID             = v.xp.NewID ?? v.ID;
                v.Name           = v.xp.NewID ?? v.ID;

                if ((superProduct?.Product?.Inventory?.VariantLevelTracking) == true && v.Inventory == null)
                {
                    v.Inventory = new PartialVariantInventory {
                        QuantityAvailable = 0
                    };
                }
                if (superProduct.Product?.Inventory == null)
                {
                    //If Inventory doesn't exist on the product, don't patch variants with inventory either.
                    return(_oc.Products.PatchVariantAsync(_product.ID, oldVariantID, new PartialVariant {
                        ID = v.ID, Name = v.Name, xp = v.xp
                    }, accessToken: user.AccessToken));
                }
                else
                {
                    return(_oc.Products.PatchVariantAsync(_product.ID, oldVariantID, new PartialVariant {
                        ID = v.ID, Name = v.Name, xp = v.xp, Inventory = v.Inventory
                    }, accessToken: user.AccessToken));
                }
            });


            // List Variants
            var _variants = await _oc.Products.ListVariantsAsync <HSVariant>(_product.ID, accessToken : user.AccessToken);

            // List Product Specs
            var _specs = await _oc.Products.ListSpecsAsync <Spec>(_product.ID, accessToken : user.AccessToken);

            // Return the SuperProduct
            return(new SuperHSProduct
            {
                Product = _product,
                PriceSchedule = _priceSchedule,
                Specs = _specs.Items,
                Variants = _variants.Items,
            });
        }