private async Task ValidateVariantsAsync(SuperHSProduct superProduct, string token) { List <Variant> allVariants = new List <Variant>(); if (superProduct.Variants == null || !superProduct.Variants.Any()) { return; } try { var allProducts = await _oc.Products.ListAllAsync(accessToken : token); if (allProducts == null || !allProducts.Any()) { return; } foreach (Product product in allProducts) { if (product.VariantCount > 0 && product.ID != superProduct.Product.ID) { allVariants.AddRange((await _oc.Products.ListVariantsAsync(productID: product.ID, pageSize: 100, accessToken: token)).Items); } } } catch (Exception ex) { return; } foreach (Variant variant in superProduct.Variants) { if (!allVariants.Any()) { return; } List <Variant> duplicateSpecNames = allVariants.Where(currVariant => IsDifferentVariantWithSameName(variant, currVariant)).ToList(); if (duplicateSpecNames.Any()) { throw new Exception($"{duplicateSpecNames.First().ID} already exists on a variant. Please use unique names for SKUS and try again."); } } }
public async Task <SuperHSProduct> Put([FromBody] SuperHSProduct obj, string id) { return(await _command.Put(id, obj, UserContext.AccessToken)); }
public async Task <SuperHSProduct> Post([FromBody] SuperHSProduct obj) { return(await _command.Post(obj, UserContext)); }
public async Task <SuperHSProduct> Put(string id, SuperHSProduct superProduct, string token) { // Update the Product itself var _updatedProduct = await _oc.Products.SaveAsync <HSProduct>(superProduct.Product.ID, superProduct.Product, token); // Two spec lists to compare (requestSpecs and existingSpecs) IList <Spec> requestSpecs = superProduct.Specs.ToList(); IList <Spec> existingSpecs = (await _oc.Products.ListSpecsAsync(id, accessToken: token)).Items.ToList(); // Two variant lists to compare (requestVariants and existingVariants) IList <HSVariant> requestVariants = superProduct.Variants; IList <Variant> existingVariants = (await _oc.Products.ListVariantsAsync(id, pageSize: 100, accessToken: token)).Items.ToList(); // Calculate differences in specs - specs to add, and specs to delete var specsToAdd = requestSpecs.Where(s => !existingSpecs.Any(s2 => s2.ID == s.ID)).ToList(); var specsToDelete = existingSpecs.Where(s => !requestSpecs.Any(s2 => s2.ID == s.ID)).ToList(); // Get spec options to add -- WAIT ON THESE, RUN PARALLEL THE ADD AND DELETE SPEC REQUESTS foreach (var rSpec in requestSpecs) { foreach (var eSpec in existingSpecs) { if (rSpec.ID == eSpec.ID) { await Throttler.RunAsync(rSpec.Options.Where(rso => !eSpec.Options.Any(eso => eso.ID == rso.ID)), 100, 5, o => _oc.Specs.CreateOptionAsync(rSpec.ID, o, accessToken: token)); await Throttler.RunAsync(eSpec.Options.Where(eso => !rSpec.Options.Any(rso => rso.ID == eso.ID)), 100, 5, o => _oc.Specs.DeleteOptionAsync(rSpec.ID, o.ID, accessToken: token)); } } ; } ; // Create new specs and Delete removed specs var defaultSpecOptions = new List <DefaultOptionSpecAssignment>(); await Throttler.RunAsync(specsToAdd, 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: token)); }); await Throttler.RunAsync(specsToDelete, 100, 5, s => _oc.Specs.DeleteAsync(s.ID, accessToken: token)); // Add spec options for new specs foreach (var spec in specsToAdd) { await Throttler.RunAsync(spec.Options, 100, 5, o => _oc.Specs.CreateOptionAsync(spec.ID, o, accessToken: token)); } // Patch Specs with requested DefaultOptionID await Throttler.RunAsync(defaultSpecOptions, 100, 10, a => _oc.Specs.PatchAsync(a.SpecID, new PartialSpec { DefaultOptionID = a.OptionID }, accessToken: token)); // Make assignments for the new specs await Throttler.RunAsync(specsToAdd, 100, 5, s => _oc.Specs.SaveProductAssignmentAsync(new SpecProductAssignment { ProductID = id, SpecID = s.ID }, accessToken: token)); HandleSpecOptionChanges(requestSpecs, existingSpecs, token); // Check if Variants differ var variantsAdded = requestVariants.Any(v => !existingVariants.Any(v2 => v2.ID == v.ID)); var variantsRemoved = existingVariants.Any(v => !requestVariants.Any(v2 => v2.ID == v.ID)); bool hasVariantChange = false; foreach (Variant variant in requestVariants) { ValidateRequestVariant(variant); var currVariant = existingVariants.Where(v => v.ID == variant.ID); if (currVariant == null || currVariant.Count() < 1) { continue; } hasVariantChange = HasVariantChange(variant, currVariant.First()); if (hasVariantChange) { break; } } // IF variants differ, then re-generate variants and re-patch IDs to match the user input. if (variantsAdded || variantsRemoved || hasVariantChange || requestVariants.Any(v => v.xp.NewID != null)) { //validate variant names before continuing saving. await ValidateVariantsAsync(superProduct, token); // Re-generate Variants await _oc.Products.GenerateVariantsAsync(id, overwriteExisting : true, accessToken : token); // Patch NEW variants with the User Specified ID (Name,ID), and correct xp values (SKU) await Throttler.RunAsync(superProduct.Variants, 100, 5, v => { 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(id, $"{superProduct.Product.ID}-{v.xp.SpecCombo}", new PartialVariant { ID = v.ID, Name = v.Name, xp = v.xp, Active = v.Active }, accessToken: token)); } else { return(_oc.Products.PatchVariantAsync(id, $"{superProduct.Product.ID}-{v.xp.SpecCombo}", new PartialVariant { ID = v.ID, Name = v.Name, xp = v.xp, Active = v.Active, Inventory = v.Inventory }, accessToken: token)); } }); } ; // If applicable, update OR create the Product PriceSchedule var tasks = new List <Task>(); Task <PriceSchedule> _priceScheduleReq = null; if (superProduct.PriceSchedule != null) { _priceScheduleReq = UpdateRelatedPriceSchedules(superProduct.PriceSchedule, token); tasks.Add(_priceScheduleReq); } // List Variants var _variantsReq = _oc.Products.ListVariantsAsync <HSVariant>(id, pageSize: 100, accessToken: token); tasks.Add(_variantsReq); // List Product Specs var _specsReq = _oc.Products.ListSpecsAsync <Spec>(id, accessToken: token); tasks.Add(_specsReq); await Task.WhenAll(tasks); return(new SuperHSProduct { Product = _updatedProduct, PriceSchedule = _priceScheduleReq?.Result, Specs = _specsReq?.Result?.Items, Variants = _variantsReq?.Result?.Items, }); }
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, }); }
public async Task <ListPage <Document <MonitoredProductFieldModifiedNotification> > > ReadMonitoredSuperProductNotificationList(SuperHSProduct product, VerifiedUserContext user) { var token = await GetAdminToken(); ListArgs <Document <MonitoredProductFieldModifiedNotification> > args; var queryParams = new Tuple <string, string>("ID", $"{product.Product.ID}_*"); args = new ListArgs <Document <MonitoredProductFieldModifiedNotification> >() { PageSize = 100 }; args.Filters.Add(new ListFilter() { QueryParams = new List <Tuple <string, string> > { queryParams } }); var document = await GetDocumentsByPageAsync(args, token); return(document); }
public async Task <ListPage <Document <MonitoredProductFieldModifiedNotification> > > ReadMonitoredSuperProductNotificationStatus([FromBody] SuperHSProduct product) { return(await _command.ReadMonitoredSuperProductNotificationList(product, VerifiedUserContext)); }
public async Task <SuperHSProduct> PostHydratedProduct([FromBody] SuperHSProduct obj) { return(await _command.SaveToQueue(obj, this.VerifiedUserContext, this.VerifiedUserContext.SupplierID)); }