private bool HasRequiredFields(ProductCsvImportLine csvLine, CsvProductInfo csvProduct) { if (string.IsNullOrEmpty(csvProduct.ImportAction)) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg = "ImportAction is required"; return(false); } else if (importAction == ImportActionType.UNKNOWN) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg = "Unknown ImportAction. Valid values are: 'C', 'R', 'U', 'D'"; return(false); } if (string.IsNullOrEmpty(csvProduct.Name)) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg = "Name is required"; return(false); } if (string.IsNullOrEmpty(csvProduct.UrlName)) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg = "UrlName is required"; return(false); } return(true); }
private void CreateProduct(CsvProductInfo csvProduct) { var p = new Product(); p.StoreId = storeId; ReplaceProduct(p, csvProduct); }
/// <summary> /// Get the file paths for Product Photos to be imported. /// Will grab photos from the CSV field and also photos with matching SKUs (StartsWith) from the ImportFiles directory. /// </summary> /// <param name="p"></param> /// <param name="csvProduct"></param> /// <returns></returns> private IEnumerable <string> GetPhotoFilePathsForImport(Product p, CsvProductInfo csvProduct) { List <string> filenames = new List <string>(); // CSV field filenames.AddRange(csvProduct.PhotoFilenames.ToList(",", true)); // ImportFiles Directory - match by SKU if (!string.IsNullOrEmpty(p.Sku)) { filenames.AddRange(filesInImportDirectory.FindAll(f => f.StartsWith(p.Sku) && (f.EndsWith(".jpg") || f.EndsWith(".gif") || f.EndsWith(".png")) )); } return(filenames.ConvertAll(f => Path.Combine(importFilesFolderFileRoot, f))); }
/// <summary> /// Updates an existing product. /// A field is only updated if the CSV field is non-empty. /// Existing Product Categories are kept, and new Categories are added from the CSV file. /// Existing Product Photos are kept, and new Photos are added. /// </summary> /// <param name="p"></param> /// <param name="csvProduct"></param> private void UpdateProduct(Product p, CsvProductInfo csvProduct) { if (NotEmpty(csvProduct.Name)) { p.Name = csvProduct.Name; } if (NotEmpty(csvProduct.Sku)) { p.Sku = csvProduct.Sku; } if (NotEmpty(csvProduct.UrlName)) { p.Slug = csvProduct.UrlName.IsValidSlug() ? csvProduct.UrlName : csvProduct.UrlName.CreateSlug(); } if (csvProduct.Price.HasValue) { p.Price = csvProduct.Price; } if (csvProduct.Weight.HasValue) { p.Weight = csvProduct.Weight; } if (NotEmpty(csvProduct.SeoTitle)) { p.SeoTitle = csvProduct.SeoTitle; } if (NotEmpty(csvProduct.SeoDescription)) { p.SeoDescription = csvProduct.SeoDescription; } if (NotEmpty(csvProduct.SeoKeywords)) { p.SeoKeywords = csvProduct.SeoKeywords; } if (csvProduct.IsActive.HasValue) { p.IsActive = csvProduct.IsActive; } if (csvProduct.TaxableItem.HasValue) { p.IsTaxable = csvProduct.TaxableItem; } if (csvProduct.ShowPrice.HasValue) { p.IsPriceDisplayed = csvProduct.ShowPrice; } if (csvProduct.AvailableForPurchase.HasValue) { p.IsAvailableForPurchase = csvProduct.AvailableForPurchase; } if (csvProduct.EnableInventoryManagement.HasValue) { p.InventoryIsEnabled = csvProduct.EnableInventoryManagement; } p.InventoryQtyInStock = csvProduct.StockLevel.HasValue ? csvProduct.StockLevel : null; if (csvProduct.AllowNegativeStock.HasValue) { p.InventoryAllowNegativeStockLevel = csvProduct.AllowNegativeStock; } if (!p.Slug.IsValidSlug()) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg += string.Format(@"Invalid Slug '{0}' must match the pattern '{1}'", p.Slug, RegexPatterns.IsValidSlug); return; } if (!string.IsNullOrEmpty(p.Sku) && !p.Sku.IsValidSku()) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg += string.Format(@"Invalid Sku '{0}' must match the pattern '{1}'", p.Sku, RegexPatterns.IsValidSku); return; } //--- Digital File if (NotEmpty(csvProduct.DigitalFilename)) { if (NotEmpty(p.DigitalFilename)) { File.Delete(Path.Combine(productFilesFolderFileRoot, p.DigitalFilename)); } ImportDigitalFile(p, csvProduct.DigitalFilename); } //--- Descriptors (update/add if different, but preserve existing) using (esTransactionScope transaction = new esTransactionScope()) { var existingDescriptors = p.GetProductDescriptors(); var newDescriptors = new Dictionary <int, DescriptorInfo>(); for (short i = 0; i < 5; i++) { //var descriptor = (i < existingDescriptors.Count) ? existingDescriptors[i] : new ProductDescriptor(); //descriptor.SortOrder = i; //newDescriptors[i] = descriptor; if (i < existingDescriptors.Count) { newDescriptors[i] = new DescriptorInfo() { Name = existingDescriptors[i].Name, Text = existingDescriptors[i].Text }; } else { newDescriptors[i] = new DescriptorInfo(); } } if (NotEmpty(csvProduct.Desc1Name)) { newDescriptors[0].Name = csvProduct.Desc1Name; } if (NotEmpty(csvProduct.Desc2Name)) { newDescriptors[1].Name = csvProduct.Desc2Name; } if (NotEmpty(csvProduct.Desc3Name)) { newDescriptors[2].Name = csvProduct.Desc3Name; } if (NotEmpty(csvProduct.Desc4Name)) { newDescriptors[3].Name = csvProduct.Desc4Name; } if (NotEmpty(csvProduct.Desc5Name)) { newDescriptors[4].Name = csvProduct.Desc5Name; } if (NotEmpty(csvProduct.Desc1Html)) { newDescriptors[0].Text = csvProduct.Desc1Html; } if (NotEmpty(csvProduct.Desc2Html)) { newDescriptors[1].Text = csvProduct.Desc2Html; } if (NotEmpty(csvProduct.Desc3Html)) { newDescriptors[2].Text = csvProduct.Desc3Html; } if (NotEmpty(csvProduct.Desc4Html)) { newDescriptors[3].Text = csvProduct.Desc4Html; } if (NotEmpty(csvProduct.Desc5Html)) { newDescriptors[4].Text = csvProduct.Desc5Html; } p.ProductDescriptorCollectionByProductId.MarkAllAsDeleted(); for (short i = 0; i < newDescriptors.Count; i++) { var descr = newDescriptors[i]; if (NotEmpty(descr.Name) || NotEmpty(descr.Text)) { AddProductDescriptor(p, i, descr.Name, descr.Text); } } transaction.Complete(); } //--- Categories (add new, but don't delete existing) List <Category> existingProductCategories = p.GetCategories(true); List <string> newCategoryNames = csvProduct.CategoryNames.ToList(",", true); newCategoryNames.RemoveAll(x => existingProductCategories.Exists(c => c.Name == x)); foreach (string newCat in newCategoryNames) { var c = Category.GetByName(storeId, newCat) ?? CreateCategory(newCat); p.AddCategory(c); } //--- Photos (add new, but don't delete existing) IEnumerable <string> importPhotoFiles = GetPhotoFilePathsForImport(p, csvProduct); foreach (string filepath in importPhotoFiles) { AddPhoto(p, filepath); } p.Save(); }
/// <summary> /// Replaces/overwrites fields on an existing Product. /// ALL matching fields are replaced/overwritten with the values from the CSV file (including empty fields). /// Existing Product Categories are first DELETED, and then re-added from the CSV file. /// Existing Product Photos are DELETED, and then re-added from the CSV. /// </summary> /// <param name="p"></param> /// <param name="csvProduct"></param> private void ReplaceProduct(Product p, CsvProductInfo csvProduct) { p.Name = csvProduct.Name; p.Sku = csvProduct.Sku; p.Slug = csvProduct.UrlName.IsValidSlug() ? csvProduct.UrlName : csvProduct.UrlName.CreateSlug(); p.Price = csvProduct.Price; p.Weight = csvProduct.Weight; p.SeoTitle = csvProduct.SeoTitle; p.SeoDescription = csvProduct.SeoDescription; p.SeoKeywords = csvProduct.SeoKeywords; p.IsActive = csvProduct.IsActive.GetValueOrDefault(true); p.IsTaxable = csvProduct.TaxableItem.GetValueOrDefault(true); p.IsPriceDisplayed = csvProduct.ShowPrice.GetValueOrDefault(true); p.IsAvailableForPurchase = csvProduct.AvailableForPurchase.GetValueOrDefault(true); p.InventoryIsEnabled = csvProduct.EnableInventoryManagement.GetValueOrDefault(false); p.InventoryQtyInStock = csvProduct.StockLevel; p.InventoryAllowNegativeStockLevel = csvProduct.AllowNegativeStock.GetValueOrDefault(false); if (!p.Slug.IsValidSlug()) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg += string.Format(@"Invalid Slug '{0}' must match the pattern '{1}'", p.Slug, RegexPatterns.IsValidSlug); return; } if (!string.IsNullOrEmpty(p.Sku) && !p.Sku.IsValidSku()) { csvLine.Status = ProductImportStatus.Error; csvLine.StatusMsg += string.Format(@"Invalid Sku '{0}' must match the pattern '{1}'", p.Sku, RegexPatterns.IsValidSku); return; } // save now so we can get an ID for new products p.Save(); //--- Digital File if (NotEmpty(p.DigitalFilename)) { File.Delete(Path.Combine(productFilesFolderFileRoot, p.DigitalFilename)); p.DigitalFilename = ""; p.DigitalFileDisplayName = ""; p.DeliveryMethodId = (short)ProductDeliveryMethod.Shipped; } ImportDigitalFile(p, csvProduct.DigitalFilename); //--- Descriptors (delete and re-add) using (esTransactionScope transaction = new esTransactionScope()) { p.ProductDescriptorCollectionByProductId.MarkAllAsDeleted(); AddProductDescriptor(p, 1, csvProduct.Desc1Name, csvProduct.Desc1Html); // always add the 1st tab if (!string.IsNullOrEmpty(csvProduct.Desc2Name)) { AddProductDescriptor(p, 2, csvProduct.Desc2Name, csvProduct.Desc2Html); } if (!string.IsNullOrEmpty(csvProduct.Desc3Name)) { AddProductDescriptor(p, 3, csvProduct.Desc3Name, csvProduct.Desc3Html); } if (!string.IsNullOrEmpty(csvProduct.Desc4Name)) { AddProductDescriptor(p, 4, csvProduct.Desc4Name, csvProduct.Desc4Html); } if (!string.IsNullOrEmpty(csvProduct.Desc5Name)) { AddProductDescriptor(p, 5, csvProduct.Desc5Name, csvProduct.Desc5Html); } transaction.Complete(); } //--- Categories (delete and then re-associate/add) p.ProductCategoryCollectionByProductId.MarkAllAsDeleted(); // remove existing product categories //p.ProductCategoryCollectionByProductId.Save(); List <string> newCategoryNames = csvProduct.CategoryNames.ToList(",", true); foreach (string newCat in newCategoryNames) { var c = Category.GetByName(storeId, newCat) ?? CreateCategory(newCat); p.AddCategory(c); } //--- Photos (delete and then re-add) p.DeleteAllPhotos(productPhotoFolderFileRoot); IEnumerable <string> importPhotoFiles = GetPhotoFilePathsForImport(p, csvProduct); foreach (string filepath in importPhotoFiles) { AddPhoto(p, filepath); } p.Save(); }
protected void btnDownloadCsv_Click(object sender, EventArgs e) { var productList = DataModel.ProductCollection.GetAll(StoreContext.CurrentStore.Id.Value); productList.Sort((left, right) => left.Id.Value.CompareTo(right.Id.Value)); List <CsvProductInfo> csvProducts = new List <CsvProductInfo>(); foreach (Product p in productList) { var csv = new CsvProductInfo() { ImportAction = "", ProductId = p.Id.Value, Name = p.Name, Sku = p.Sku, UrlName = p.Slug, Price = p.Price, Weight = p.Weight, SeoTitle = p.SeoTitle, SeoDescription = p.SeoDescription, SeoKeywords = p.SeoKeywords, IsActive = p.IsActive, CategoryNames = p.GetCategories(true).ConvertAll(c => c.Name).ToDelimitedString(", "), PhotoFilenames = p.GetAllPhotosInSortOrder().ConvertAll(x => x.Filename).ToDelimitedString(", "), DigitalFilename = p.DigitalFilename, TaxableItem = p.IsTaxable, ShowPrice = p.IsPriceDisplayed, AvailableForPurchase = p.IsAvailableForPurchase, EnableInventoryManagement = p.InventoryIsEnabled, StockLevel = p.InventoryQtyInStock, AllowNegativeStock = p.InventoryAllowNegativeStockLevel }; var descriptors = p.GetProductDescriptors(); if (descriptors.Count >= 1) { csv.Desc1Name = descriptors[0].Name; csv.Desc1Html = descriptors[0].TextHtmlDecoded; } if (descriptors.Count >= 2) { csv.Desc2Name = descriptors[1].Name; csv.Desc2Html = descriptors[1].TextHtmlDecoded; } if (descriptors.Count >= 3) { csv.Desc3Name = descriptors[2].Name; csv.Desc3Html = descriptors[2].TextHtmlDecoded; } if (descriptors.Count >= 4) { csv.Desc4Name = descriptors[3].Name; csv.Desc4Html = descriptors[3].TextHtmlDecoded; } if (descriptors.Count >= 5) { csv.Desc5Name = descriptors[4].Name; csv.Desc5Html = descriptors[4].TextHtmlDecoded; } csvProducts.Add(csv); } Response.Clear(); Response.ClearHeaders(); Response.ContentType = "text/csv"; Response.AddHeader("Content-Disposition", "attachment; filename=Product-Export.csv"); var exporter = new ProductCsvImporter(StoreContext.CurrentStore.Id.Value); exporter.ExportProducts(csvProducts, Response.OutputStream); Response.Flush(); Response.End(); }