using qdr.app.studiou.orders2printpack.Extensions; using qdr.app.studiou.orders2printpack.ProductStorage; using qdr.app.studiou.orders2printpack.Properties; using Quadarax.Foundation.Core.IO; using Quadarax.Foundation.Core.Value; using Quadarax.Foundation.Core.Value.Generators; using System.ComponentModel; using System.Data; namespace qdr.app.studiou.orders2printpack { public partial class FormProductEdit : Form { #region *** Enumerations *** public enum DetailType { Product, Variant, Category } #endregion #region *** Private Fields *** private ProductStorage.ProductStorage _storage = new ProductStorage.ProductStorage(); private DetailType _detailType = DetailType.Product; private ProductDto? _currentDetailProduct; private VariantDto? _currentDetailVariant; private CategoryDto? _currentDetailCategory; private TinyHash _hashGenerator = new TinyHash(10); private ProductDto _productClipboard = new ProductDto(); private VariantDto _variantClipboard = new VariantDto(); private CategoryDto _categoryClipboard = new CategoryDto(); #endregion #region *** Constructor *** public FormProductEdit() { InitializeComponent(); _storage.AppendVariantsFromString(AppSettings.Default.PredefinedVariants); _storage.AppendCategoriesFromString(AppSettings.Default.PredefinedCategories); tsslblOutput.Text = "Soubor nezadán"; RefreshViews(); RefreshTools(); } #endregion #region *** Form Handlers *** protected override void OnClosing(CancelEventArgs e) { AppSettings.Default.PredefinedVariants = _storage.GetVariantsAsString(); AppSettings.Default.PredefinedCategories = _storage.GetCategoriesAsString(); AppSettings.Default.Save(); base.OnClosing(e); } #endregion #region *** Toolbar Handlers *** private void tbbutSave_Click(object sender, EventArgs e) { if (tsslblOutput.Text == "Soubor nezadán") { if (dlgSaveFile.ShowDialog() == DialogResult.OK) tsslblOutput.Text = dlgSaveFile.FileName; else return; } SaveProductFile(tsslblOutput.Text); MessageBox.Show($"Data ({_storage.Products.Count()} položek) byla uložena do souboru '{tsslblOutput.Text}'", "Uloženo", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void tbbutNew_Click(object sender, EventArgs e) { _storage = new ProductStorage.ProductStorage(); _storage.AppendVariantsFromString(AppSettings.Default.PredefinedVariants); _storage.AppendCategoriesFromString(AppSettings.Default.PredefinedCategories); _currentDetailProduct = null; if (dlgSaveFile.ShowDialog() == DialogResult.OK) SaveProductFile(dlgSaveFile.FileName); ClearDetail(); RefreshViews(); RefreshTools(); } private void tbbutGenerateRepo_Click(object sender, EventArgs e) { if (dlgDirSelect.ShowDialog() == DialogResult.OK) { GenerateRepository(dlgDirSelect.SelectedPath); } } private void tbbutOpen_Click(object sender, EventArgs e) { if (dlgOpenFile.ShowDialog() == DialogResult.OK) OpenProductFile(dlgOpenFile.FileName); } private void tbbutProduct_Click(object sender, EventArgs e) { tbbutProduct.Checked = !tbbutProduct.Checked; RefreshProductList(); } private void tbbutVariant_Click(object sender, EventArgs e) { tbbutVariant.Checked = !tbbutVariant.Checked; RefreshProductList(); } private void tbbutProdNew_Click(object sender, EventArgs e) { AddNewProduct(); RefreshTools(); } private void tbbutProdVarNew_Click(object sender, EventArgs e) { AddNewProductVariant(); RefreshTools(); } private void tbbutDetSave_Click(object sender, EventArgs e) { switch (_detailType) { case DetailType.Product: SaveDetail(_currentDetailProduct); RefreshProductList(); break; case DetailType.Variant: SaveDetail(_currentDetailVariant); RefreshVariantList(); RefreshProductList(); break; case DetailType.Category: SaveDetail(_currentDetailCategory); RefreshCategoryList(); break; } } private void tbbutProdDelete_Click(object sender, EventArgs e) { var selected = GetSelectedProduct(); if (selected == null) return; if (!ConfirmMessage($"Opravdu chcete smazat {selected.Length} vybraných produktů?")) return; _storage.RemoveProduct(selected.Select(x => x.GetIdentifier()).ToArray()); RefreshProductList(); RefreshTools(); } private void tbbutProdClone_Click(object sender, EventArgs e) { var selected = GetSelectedProduct()?.FirstOrDefault(); if (selected == null) return; CloneProduct(selected); RefreshTools(); } private void tbbutDetCopy_Click(object sender, EventArgs e) { switch (_detailType) { case DetailType.Product: if (_currentDetailProduct == null) return; _currentDetailProduct.CopyTo(_productClipboard); break; case DetailType.Variant: if (_currentDetailVariant == null) return; _currentDetailVariant.CopyTo(_variantClipboard); break; case DetailType.Category: if (_currentDetailCategory == null) return; _currentDetailCategory.CopyTo(_categoryClipboard); break; } } private void tbbutDetPaste_Click(object sender, EventArgs e) { switch (_detailType) { case DetailType.Product: if (_currentDetailProduct == null) return; _productClipboard.Id = _currentDetailProduct.Id; ShowDetail(_productClipboard); break; case DetailType.Variant: if (_currentDetailVariant == null) return; ShowDetail(_variantClipboard); break; case DetailType.Category: if (_currentDetailCategory == null) return; ShowDetail(_categoryClipboard); break; } } private void tbbutVarNew_Click(object sender, EventArgs e) { AddNewVariant(); } private void tbbutVarDelete_Click(object sender, EventArgs e) { var selected = GetSelectedVariants(); if (selected == null) return; if (!ConfirmMessage($"Opravdu chcete smazat {selected.Length} vybraných variant?")) return; _storage.RemoveVariants(selected.Select(x => x.GetIdentifier()).ToArray()); RefreshVariantList(); } private void tbbutVarApply_Click(object sender, EventArgs e) { ApplyProductVariant(); RefreshProductList(); } private void tbbutCatNew_Click(object sender, EventArgs e) { AddNewCategory(); } private void tbbutCatDelete_Click(object sender, EventArgs e) { var selected = GetSelectedCategories(); if (selected == null) return; if (!ConfirmMessage($"Opravdu chcete smazat {selected.Length} vybraných kategorií?")) return; _storage.RemoveCategories(selected.Select(x => x.GetIdentifier()).ToArray()); RefreshCategoryList(); } private void tbbutCatApply_Click(object sender, EventArgs e) { } private void tbbutImport_Click(object sender, EventArgs e) { var dlg = new dlgImport(); if (dlg.ShowDialog() == DialogResult.OK) { ImportProducts(dlg.ImportDir, dlg.UrlPrefix, dlg.ShortDescription, dlg.Description, dlg.UploadDate); } RefreshViews(); RefreshTools(); } #endregion #region *** Refreshes *** private void RefreshTools() { tsslblProducts.Text = $"Produkty: {_storage.Products.Count(x => !x.IsVariant)}"; tsslblVariants.Text = $"Varianty: {_storage.Products.Count(x => x.IsVariant)}"; } private void RefreshViews() { RefreshProductList(); RefreshCategoryList(); RefreshVariantList(); } private void RefreshProductList() { BlockErrorHandled(() => { lvProducts.SaveSelection(); lvProducts.BindData(_storage.GetProductsView(tbbutProduct.Checked, tbbutVariant.Checked), itemCustomProcessCallback: (lvItem, data) => { if (!data.IsValid) lvItem.BackColor = Color.IndianRed; }); lvProducts.RestoreSelection(); }); } private void RefreshCategoryList() { BlockErrorHandled(() => { lvCategories.SaveSelection(); lvCategories.BindData(_storage.Categories); lvCategories.RestoreSelection(); }); } private void RefreshVariantList() { BlockErrorHandled(() => { lvVariants.SaveSelection(); lvVariants.BindData(_storage.Variants); lvVariants.RestoreSelection(); }); } #endregion #region *** ListView Handlers *** private void lvProducts_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (e.Item != null) { var product = _storage.GetProductByIdentifier(e.Item.Tag?.ToString()); ShowDetail(product); _currentDetailProduct = product; } } private void lvVariants_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (e.Item != null) { var variant = _storage.GetVariantByIdentifier(e.Item.Tag?.ToString()); ShowDetail(variant); _currentDetailVariant = variant; } } private void lvCategories_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (e.Item != null) { var category = _storage.GetCategoryByIdentifier(e.Item.Tag?.ToString()); ShowDetail(category); _currentDetailCategory = category; } } #endregion #region *** Operations *** private void OpenProductFile(string fileName) { BlockErrorHandled(() => { tsslblOutput.Text = fileName; if (!File.Exists(fileName)) return; _storage.Load(fileName, ","); RefreshTools(); RefreshViews(); }); } private void SaveProductFile(string? fileName) { BlockErrorHandled(() => { if (fileName == null) return; tsslblOutput.Text = fileName; _storage.Save(fileName, ","); RefreshTools(); }); } private void ClearDetail() { BlockErrorHandled(() => { tbdetId.Text = string.Empty; tbdetName.Text = string.Empty; tbdetSku.Text = string.Empty; tbdetShortDescription.Text = string.Empty; tbdetDescription.Text = string.Empty; cbdetCategories.BeginUpdate(); cbdetCategories.Items.Clear(); foreach (var category in _storage.Categories) cbdetCategories.Items.Add(category.Name); cbdetCategories.EndUpdate(); tbdetPrice.Value = 0; tbdetUri.Text = string.Empty; tbdetMaximal.Text = string.Empty; tbdetMinimal.Text = string.Empty; cbdetParent.BeginUpdate(); cbdetParent.Items.Clear(); foreach (var product in _storage.GetProductsView(true, false).Select(x => x.Sku).Order()) cbdetParent.Items.Add(product!); cbdetParent.EndUpdate(); }); } private void VisibleAllDetail(bool visible) { BlockErrorHandled(() => { tbdetId.Visible = visible; ltbdetId.Visible = visible; tbdetName.Visible = visible; ltbdetName.Visible = visible; tbdetSku.Visible = visible; ltbdetSku.Visible = visible; tbdetShortDescription.Visible = visible; ltbdetShortDescription.Visible = visible; tbdetDescription.Visible = visible; ltbdetDescription.Visible = visible; tbdetPrice.Visible = visible; ltbdetPrice.Visible = visible; tbdetUri.Visible = visible; ltbdetUri.Visible = visible; cbdetCategories.Visible = visible; lcbdetCategories.Visible = visible; cbdetParent.Visible = visible; lcbdetParent.Visible = visible; tbdetMinimal.Visible = visible; ldetMinimal.Visible = visible; tbdetMaximal.Visible = visible; ldetMaximal.Visible = visible; }); } private void ShowDetail(ProductDto? data) { BlockErrorHandled(() => { _detailType = DetailType.Product; detProduct.Visible = false; ClearDetail(); if (data == null) { detProduct.Visible = true; return; } var isVariant = data.IsVariant; tblbCaption.Text = isVariant ? "Varianta produktu" : "Produkt"; VisibleAllDetail(true); if (isVariant) { // variant cbdetCategories.Visible = false; lcbdetCategories.Visible = false; } else { // head product cbdetParent.Visible = false; lcbdetParent.Visible = false; tbdetPrice.Visible = false; ltbdetPrice.Visible = false; cbdetCategories.SelectedIndex = cbdetCategories.FindStringExact(data.Category); tbdetMaximal.Visible = false; tbdetMinimal.Visible = false; ldetMaximal.Visible = false; ldetMinimal.Visible = false; } tbdetId.Text = data.Id.ToString(); tbdetName.Text = data.ProductName; tbdetSku.Text = data.Sku; tbdetShortDescription.Text = data.ShortDescription; tbdetDescription.Text = data.Description; tbdetPrice.Value = data.Price; tbdetUri.Text = data.Uri; tbdetMaximal.Text = data.QuantityMaximum.ToString(); tbdetMinimal.Text = data.QuantityMinimum.ToString(); cbdetParent.SelectedIndex = cbdetParent.FindStringExact(data.ParentName); detProduct.Visible = true; }); } private void ShowDetail(VariantDto? data) { BlockErrorHandled(() => { _detailType = DetailType.Variant; tblbCaption.Text = "Varianta"; detProduct.Visible = false; ClearDetail(); if (data == null) { detProduct.Visible = true; return; } VisibleAllDetail(false); ltbdetName.Visible = true; tbdetName.Visible = true; ltbdetPrice.Visible = true; tbdetPrice.Visible = true; ldetMinimal.Visible = true; ldetMaximal.Visible = true; tbdetMaximal.Visible = true; tbdetMinimal.Visible = true; tbdetName.Text = data.Name; tbdetPrice.Value = data.Price; tbdetMaximal.Text = data.QuantityMaximum.ToString(); tbdetMinimal.Text = data.QuantityMinimum.ToString(); detProduct.Visible = true; }); } private void ShowDetail(CategoryDto? data) { BlockErrorHandled(() => { _detailType = DetailType.Category; tblbCaption.Text = "Kategorie"; detProduct.Visible = false; ClearDetail(); if (data == null) { detProduct.Visible = true; return; } VisibleAllDetail(false); ltbdetName.Visible = true; tbdetName.Visible = true; tbdetName.Text = data.Name; detProduct.Visible = true; }); } private void SaveDetail(ProductDto? data) { BlockErrorHandled(() => { if (data == null) return; data.ProductName = tbdetName.Text; if (!string.Equals(data.Sku, tbdetSku.Text, StringComparison.CurrentCultureIgnoreCase)) { if (_storage.Products.Any(x => string.Equals(x.Sku, tbdetSku.Text, StringComparison.CurrentCultureIgnoreCase))) throw new Exception($"Produkt se zadaným SKU '{tbdetSku.Text}' již existuje!"); if (!data.IsVariant) { foreach (ProductDto item in _storage.Products.Where(x => x.IsVariant && string.Equals(x.ParentName, data.Sku, StringComparison.CurrentCultureIgnoreCase))) { item.ParentName = tbdetSku.Text; } } } data.Sku = tbdetSku.Text; data.ShortDescription = tbdetShortDescription.Text; data.Description = tbdetDescription.Text; data.Price = tbdetPrice.Value; data.Uri = tbdetUri.Text; data.ParentName = cbdetParent.SelectedItem?.ToString() ?? string.Empty; data.Category = cbdetCategories.SelectedItem?.ToString() ?? string.Empty; data.QuantityMinimum = int.TryParse(tbdetMinimal.Text, out var resmin) ? resmin : 0; data.QuantityMaximum = int.TryParse(tbdetMaximal.Text, out var resmax) ? resmax : 0; _storage.CheckProductVariantValidity(); }); } private void SaveDetail(VariantDto? data) { BlockErrorHandled(() => { if (data == null) return; // update products var productsToUpdate = _storage.Products.Where(x => x.IsVariant && string.Equals(x.VariantName, data.Name, StringComparison.InvariantCultureIgnoreCase)); foreach (var product in productsToUpdate) { product.VariantName = tbdetName.Text; product.Price = tbdetPrice.Value; product.QuantityMinimum = int.TryParse(tbdetMinimal.Text, out var resmina) ? resmina : 0; product.QuantityMaximum = int.TryParse(tbdetMaximal.Text, out var resmaxa) ? resmaxa : 0; } data.Name = tbdetName.Text; data.Price = tbdetPrice.Value; data.QuantityMinimum = int.TryParse(tbdetMinimal.Text, out var resmin) ? resmin : 0; data.QuantityMaximum = int.TryParse(tbdetMaximal.Text, out var resmax) ? resmax : 0; }); } private void SaveDetail(CategoryDto? data) { BlockErrorHandled(() => { if (data == null) return; // update products if (!string.Equals(data.Name, tbdetName.Text, StringComparison.InvariantCultureIgnoreCase)) { var productsToUpdate = _storage.Products.Where(x => x.IsVariant && string.Equals(x.Category, data.Name, StringComparison.InvariantCultureIgnoreCase)); foreach (var product in productsToUpdate) { product.Category = tbdetName.Text; } data.Name = tbdetName.Text; } }); } private void GenerateRepository(string selectedPath) { int skipedCnt = 0; int createdCnt = 0; BlockErrorHandled(() => { if (!Directory.Exists(selectedPath)) throw new Exception($"Složka '{selectedPath}' neexistuje."); foreach (var category in _storage.Categories) { var fullPath = Path.Combine(selectedPath, category.Name); if (!Directory.Exists(fullPath)) { Directory.CreateDirectory(fullPath); createdCnt++; } else { skipedCnt++; } foreach (var variant in _storage.Variants) { var variantPath = Path.Combine(fullPath, variant.Name); if (!Directory.Exists(variantPath)) { Directory.CreateDirectory(variantPath); createdCnt++; } else { skipedCnt++; } } } }); MessageBox.Show($"Bylo vytvořeno {createdCnt} složek, {skipedCnt} již existovalo.", "Hotovo", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void AddNewVariant() { BlockErrorHandled(() => { var item = _storage.AddNewVariant(); lvVariants.AddItem(item, true); }); } private void AddNewCategory() { BlockErrorHandled(() => { var item = _storage.AddNewCategory(); lvCategories.AddItem(item, true); }); } private void AddNewProduct() { BlockErrorHandled(() => { var item = _storage.AddNewProduct(); item.ProductName = _hashGenerator.NewId(); item.Sku = item.ProductName; lvProducts.AddItem(item, true); }); } private void AddNewProductVariant() { BlockErrorHandled(() => { var item = _storage.AddNewProductVariant(GetSelectedProduct()?.FirstOrDefault()?.ParentName); item.ProductName = _hashGenerator.NewId(); item.Sku = item.ProductName; lvProducts.AddItem(item, true); }); } private void CloneProduct(ProductDto item) { BlockErrorHandled(() => { var newItem = item.IsVariant ? _storage.AddNewProductVariant(item.ParentName) : _storage.AddNewProduct(); item.CopyTo(newItem); lvProducts.AddItem(newItem, true); }); } private void ApplyProductVariant() { BlockErrorHandled(() => { var variants = GetSelectedVariants(); if (variants == null) return; var totalCnt = 0; foreach (var variant in variants) { var productsToUpdate = _storage.Products.Where(x => x.IsVariant && string.Equals(x.VariantName, variant.Name, StringComparison.InvariantCultureIgnoreCase)); totalCnt += productsToUpdate.Count(); foreach (var product in productsToUpdate) { product.Price = variant.Price; } } MessageBox.Show($"Bylo upraveno {totalCnt} produktů", "Hotovo", MessageBoxButtons.OK, MessageBoxIcon.Information); }); } private ProductDto[]? GetSelectedProduct() { if (lvProducts.SelectedItems.Count == 0) return null; var result = new List(); BlockErrorHandled(() => { foreach (ListViewItem item in lvProducts.SelectedItems) { var product = _storage.GetProductByIdentifier(item.Tag?.ToString()); if (product != null) result.Add(product); } }); return result.ToArray(); } private VariantDto[]? GetSelectedVariants() { if (lvVariants.SelectedItems.Count == 0) return null; var result = new List(); BlockErrorHandled(() => { foreach (ListViewItem item in lvVariants.SelectedItems) { var variant = _storage.GetVariantByIdentifier(item.Tag?.ToString()); if (variant != null) result.Add(variant); } }); return result.ToArray(); } private CategoryDto[]? GetSelectedCategories() { if (lvCategories.SelectedItems.Count == 0) return null; var result = new List(); foreach (ListViewItem item in lvCategories.SelectedItems) { var category = _storage.GetCategoryByIdentifier(item.Tag?.ToString()); if (category != null) result.Add(category); } return result.ToArray(); } private void ImportProducts(string selectedPath, string urlPrefix, string shortDescription, string description, DateTime uploadDate) { BlockErrorHandled(() => { if (!Directory.Exists(selectedPath)) throw new Exception($"Složka '{selectedPath}' neexistuje."); // search for files var fsrch = new FileSearch(selectedPath, true); var filesCache = new List(); var files = fsrch.Search(new[] { AppSettings.Default.SourceSearchPattern, "*" }); if (files.Length == 0) throw new Exception($"Nenalezeny žádné soubory pro zpracování ve složce '{selectedPath}'."); // construct list of files foreach (var file in files) if (!filesCache.Any(x => x == file)) filesCache.Add(file); var tplParams = new Dictionary { { "DD", uploadDate.Day.ToString("00") }, { "MM", uploadDate.Month.ToString("00") }, { "YY", uploadDate.Year.ToString().Substring(2,2) }, { "YYYY", uploadDate.Year.ToString() }, { "file", "file" }, { "ext", "ext" }, { "category", "category" }, { "variant", "variant" } }; foreach (var file in filesCache) { var product = new ProductDto(); var pathParts = file.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); if (pathParts.Length < 3) continue; var indexLast = pathParts.Length - 1; var sku = Path.GetFileNameWithoutExtension(pathParts[indexLast]); var variant = pathParts[indexLast - 1]; var category = pathParts[indexLast - 2]; tplParams["file"] = Path.GetFileNameWithoutExtension(pathParts[indexLast]); tplParams["ext"] = Path.GetExtension(pathParts[indexLast]); tplParams["variant"] = variant; tplParams["category"] = category; var parentDto = _storage.GetProductByName(Path.GetFileNameWithoutExtension(pathParts[indexLast])); if (parentDto == null) { // product has not parent, create new product parentDto = _storage.AddNewProduct(); parentDto.ProductName = Path.GetFileNameWithoutExtension(pathParts[indexLast]); parentDto.Sku = sku + "_" + _hashGenerator.NewId(); parentDto.Category = category; parentDto.ShortDescription = new ExtendedParametrizedString(shortDescription, tplParams, "{", "}").ToString(); parentDto.Description = new ExtendedParametrizedString(description, tplParams, "{", "}").ToString(); parentDto.Uri = new ExtendedParametrizedString(urlPrefix, tplParams, "{", "}").ToString(); parentDto.VariantType = "Formát"; } product = _storage.AddNewProductVariant(parentDto.Sku); product.Uri = new ExtendedParametrizedString(urlPrefix, tplParams, "{", "}").ToString(); product.ProductName = Path.GetFileNameWithoutExtension(pathParts[indexLast]) + " - " + variant; product.Sku = sku + "_" + _hashGenerator.NewId(); product.Price = 0; product.VariantName = variant; product.VariantType = "Formát"; product.ShortDescription = new ExtendedParametrizedString(shortDescription, tplParams, "{", "}").ToString(); product.Description = new ExtendedParametrizedString(description, tplParams, "{", "}").ToString(); } _storage.CheckProductVariantValidity(); foreach (var product in _storage.Products.Where(x => !x.IsVariant)) { product.Hidden2_1 = 1; var variantString = string.Join(",", _storage.Products.Where(x => string.Equals(x.ParentName, product.Sku, StringComparison.InvariantCultureIgnoreCase)).Select(x => x.VariantName)); product.VariantName = variantString; product.ShortDescription = $"{product.Category} - fotka: {product.ProductName}"; product.Description = $"{product.Category} - fotka: {product.ProductName}"; } }); } private bool ConfirmMessage(string message) { return MessageBox.Show(message, "Potvrzení", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes; } private void BlockErrorHandled(Action block) { try { block(); } catch (Exception ex) { // Log("Error: " + ex.Message); MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); RefreshTools(); } } #endregion } }