using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Entities; using Tango.MachineStudio.Common; using System.Data.Entity; using Tango.MachineStudio.Catalogs.Contracts; using Tango.MachineStudio.Common.Notifications; using Tango.Core.Commands; using Tango.BL.Builders; using Tango.Documents; using Microsoft.Win32; using Tango.Core.Helpers; using System.IO; using Tango.MachineStudio.Catalogs.Excel; using Tango.BL.ActionLogs; using Tango.MachineStudio.Common.Authentication; using Tango.BL.Enumerations; using Tango.BL.DTO; namespace Tango.MachineStudio.Catalogs.ViewModels { public class MainViewVM : StudioViewModel { private ObservablesContext _catalogsContext; private ObservablesContext _activeCatalogContext; private INotificationProvider _notification; private IActionLogManager _actionLogManager; private IAuthenticationProvider _authentication; private ColorCatalogDTO _catalogBeforeSave; #region Properties private ObservableCollection _catalogs; /// /// Gets or sets the catalogs. /// public ObservableCollection Catalogs { get { return _catalogs; } set { _catalogs = value; RaisePropertyChangedAuto(); } } private ColorCatalog _selectedCatalog; /// /// Gets or sets the selected catalog. /// public ColorCatalog SelectedCatalog { get { return _selectedCatalog; } set { _selectedCatalog = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private ColorCatalog _activeCatalog; /// /// Gets or sets the active catalog. /// public ColorCatalog ActiveCatalog { get { return _activeCatalog; } set { _activeCatalog = value; RaisePropertyChangedAuto(); } } private ColorCatalogsGroup _selectedGroup; /// /// Gets or sets the selected group. /// public ColorCatalogsGroup SelectedGroup { get { return _selectedGroup; } set { _selectedGroup = value; RaisePropertyChangedAuto(); } } private ColorCatalogsItem _selectedItem; /// /// Gets or sets the selected item. /// public ColorCatalogsItem SelectedItem { get { return _selectedItem; } set { _selectedItem = value; RaisePropertyChangedAuto(); } } private List _rmls; /// /// Gets or sets the RMLS. /// public List RMLS { get { return _rmls; } set { _rmls = value; RaisePropertyChangedAuto(); } } #endregion #region Commands /// /// Gets or sets the new catalog command. /// public RelayCommand NewCatalogCommand { get; set; } /// /// Gets or sets the delete catalog command. /// public RelayCommand DeleteCatalogCommand { get; set; } /// /// Gets or sets the edit catalog command. /// public RelayCommand EditCatalogCommand { get; set; } /// /// Gets or sets the back to catalogs command. /// public RelayCommand BackToCatalogsCommand { get; set; } /// /// Gets or sets the save active catalog command. /// public RelayCommand SaveActiveCatalogCommand { get; set; } /// /// Gets or sets the export excel command. /// public RelayCommand ExportExcelCommand { get; set; } /// /// Gets or sets the import excel command. /// public RelayCommand ImportExcelCommand { get; set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public MainViewVM() { Catalogs = new ObservableCollection(); EditCatalogCommand = new RelayCommand(EditSelectedCatalog, () => SelectedCatalog != null); NewCatalogCommand = new RelayCommand(CreateNewCatalog); DeleteCatalogCommand = new RelayCommand(DeleteSelectedCatalog, () => SelectedCatalog != null); BackToCatalogsCommand = new RelayCommand(BackToCatalogs); SaveActiveCatalogCommand = new RelayCommand(SaveActiveCatalog); ExportExcelCommand = new RelayCommand(ExportCatalogToExcel); ImportExcelCommand = new RelayCommand(ImportCatalogFromExcel); } /// /// Initializes a new instance of the class. /// /// The notification provider. public MainViewVM(INotificationProvider notificationProvider, IActionLogManager actionLogManager, IAuthenticationProvider authenticationProvider) : this() { _actionLogManager = actionLogManager; _notification = notificationProvider; _authentication = authenticationProvider; } #endregion #region Edit / Delete / Create Catalog public async Task LoadCatalogs() { using (_notification.PushTaskItem("Loading color catalogs...")) { IsFree = false; if (_catalogsContext != null) { _catalogsContext.Dispose(); } _catalogsContext = ObservablesContext.CreateDefault(); Catalogs = (await _catalogsContext.ColorCatalogs.Include(x => x.ColorCatalogsGroups.Select(g => g.ColorCatalogsItems)).ToListAsync()).ToSynchronizedObservableCollection(); SelectedCatalog = null; IsFree = true; } } public async void EditSelectedCatalog() { if (SelectedCatalog != null) { using (_notification.PushTaskItem("Loading selected catalog...")) { try { IsFree = false; if (_activeCatalogContext != null) { _activeCatalogContext.Dispose(); } _activeCatalogContext = ObservablesContext.CreateDefault(); ActiveCatalog = await new CatalogBuilder(_activeCatalogContext).Set(SelectedCatalog.Guid).WithGroups().WithItems().WithRecipes().BuildAsync(); SelectedGroup = ActiveCatalog.ColorCatalogsGroups.FirstOrDefault(); RMLS = await _activeCatalogContext.Rmls.ToListAsync(); if (SelectedGroup != null) { SelectedItem = SelectedGroup.ColorCatalogsItems.FirstOrDefault(); } _catalogBeforeSave = ColorCatalogDTO.FromObservable(ActiveCatalog); View.NavigateTo(CatalogsNavigationView.CatalogView); } catch (Exception ex) { _notification.ShowError($"An error occurred while trying to load the selected catalog.\n{ex.Message}"); LogManager.Log(ex, $"Error loading selected catalog '{SelectedCatalog.Name}'."); } finally { IsFree = true; } } } } private async void DeleteSelectedCatalog() { if (SelectedCatalog != null) { if (_notification.ShowQuestion("Are you sure you want to delete the selected catalog ?")) { using (_notification.PushTaskItem("Deleting selected catalog...")) { try { IsFree = false; await SelectedCatalog.DeleteCascadeAsync(_catalogsContext); _actionLogManager.InsertLog(ActionLogType.CatalogDeleted, _authentication.CurrentUser, SelectedCatalog.Name, SelectedCatalog, "Catalog deleted using Machine Studio.", false); Catalogs.Remove(SelectedCatalog); SelectedCatalog = null; } catch (Exception ex) { _notification.ShowError($"An error occurred while trying to delete the selected catalog.\n{ex.Message}"); LogManager.Log(ex, $"Error deleting catalog {SelectedCatalog.Name}."); } finally { IsFree = true; } } } } } private async void CreateNewCatalog() { String name = _notification.ShowTextInput("Please enter a catalog name", "Catalog name"); if (!String.IsNullOrWhiteSpace(name)) { using (_notification.PushTaskItem("Generating catalog...")) { try { IsFree = false; ColorCatalog newCatalog = new ColorCatalog(); newCatalog.Name = name; newCatalog.Description = name; newCatalog.Company = "Twine"; _catalogsContext.ColorCatalogs.Add(newCatalog); await _catalogsContext.SaveChangesAsync(); _actionLogManager.InsertLog(ActionLogType.CatalogCreated, _authentication.CurrentUser, newCatalog.Name, newCatalog, "Catalog created using Machine Studio."); SelectedCatalog = newCatalog; EditSelectedCatalog(); } catch (Exception ex) { _notification.ShowError($"An error occurred while trying to create the catalog.\n{ex.Message}"); LogManager.Log(ex, $"Error creating new catalog."); } finally { IsFree = true; } } } } public void OnGroupDeleted(ColorCatalogsGroup group) { group.Delete(_activeCatalogContext); } public void OnItemDeleted(ColorCatalogsItem item) { item.Delete(_activeCatalogContext); } public void OnRecipeDeleted(ColorCatalogsItemsRecipe recipe) { recipe.Delete(_activeCatalogContext); } private async void SaveActiveCatalog() { if (ActiveCatalog != null) { using (_notification.PushTaskItem("Updating catalog...")) { try { IsFree = false; //Validate color codes. var duplicateCodes = ActiveCatalog .ColorCatalogsGroups .SelectMany(x => x.ColorCatalogsItems) .GroupBy(x => x.Code) .Where(x => x.Count() > 1) .Select(x => x.First().Code) .ToList(); if (duplicateCodes.Count > 0) { throw new InvalidOperationException($"Duplicate color codes found:\n{String.Join(Environment.NewLine, duplicateCodes)}"); } //Validate color names. var duplicateNames = ActiveCatalog .ColorCatalogsGroups .SelectMany(x => x.ColorCatalogsItems) .GroupBy(x => x.Name) .Where(x => x.Count() > 1) .Select(x => x.First().Name) .ToList(); if (duplicateNames.Count > 0) { throw new InvalidOperationException($"Duplicate color names found:\n{String.Join(Environment.NewLine, duplicateNames)}"); } ActiveCatalog.LastUpdated = DateTime.UtcNow; await _activeCatalogContext.SaveChangesAsync(); var activeCatalogDTO = ColorCatalogDTO.FromObservable(ActiveCatalog); _actionLogManager.InsertLog(ActionLogType.CatalogSaved, _authentication.CurrentUser, _catalogBeforeSave.Name, _catalogBeforeSave, activeCatalogDTO, "Catalog saved using Machine Studio."); _catalogBeforeSave = activeCatalogDTO; await LoadCatalogs(); _notification.ShowInfo("Catalog updated successfully."); } catch (Exception ex) { _notification.ShowError($"An error occurred while trying to update the catalog.\n{ex.Message}"); LogManager.Log(ex, $"Error updating catalog {ActiveCatalog.Name}."); } finally { IsFree = true; } } } } private void BackToCatalogs() { View.NavigateTo(CatalogsNavigationView.CatalogsView); } #endregion #region Export / Import Excel public void ExportCatalogToExcel() { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "Excel Documents|*.xlsx"; if (dlg.ShowDialog().Value) { using (_notification.PushTaskItem("Exporting catalog to file...")) { Task.Factory.StartNew(() => { try { IsFree = false; Stream stream = null; bool dispose = false; String file = AssemblyHelper.GetCurrentAssemblyFolder() + "\\Templates\\ExportTemplate.xlsx"; if (File.Exists(file)) { stream = File.OpenRead(file); dispose = true; } else { stream = EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.MachineStudio.Catalogs.Templates.ExportTemplate.xlsx"); } byte[] data = new byte[stream.Length]; stream.Read(data, 0, data.Length); File.WriteAllBytes(dlg.FileName, data); if (dispose) { stream.Dispose(); } ExcelWriter writer = new ExcelWriter(dlg.FileName); List medias = RMLS.Select(x => new Media() { MediaName = x.Name, }).ToList(); writer.WriteData(medias, "RML"); List groups = ActiveCatalog.ColorCatalogsGroups.Select(x => new ColorGroup() { GroupColor = x.Color, GroupName = x.Name, GroupIndex = x.GroupIndex, }).ToList(); writer.WriteData(groups, "Groups"); List items = ActiveCatalog.ColorCatalogsGroups.SelectMany(x => x.ColorCatalogsItems).Select(x => new ColorItem() { ItemColor = x.Color, Group = x.ColorCatalogsGroup.Name, Code = x.Code, Name = x.Name, ItemIndex = x.ItemIndex, Red = x.Red, Green = x.Green, Blue = x.Blue, L = x.L, A = x.A, B = x.B, Cyan = x.Cyan, Magenta = x.Magenta, Yellow = x.Yellow, Black = x.Black, BlueExtra = x.BlueExtra, NavyExtra = x.NavyExtra, OrangeExtra = x.OrangeExtra, RedExtra = x.RedExtra, RubineExtra = x.RubineExtra, VioletExtra = x.VioletExtra, Region = x.ProcessParametersTableIndex, }).ToList(); writer.WriteData(items, "Colors"); List recipes = ActiveCatalog.ColorCatalogsGroups.SelectMany(x => x.ColorCatalogsItems).SelectMany(x => x.ColorCatalogsItemsRecipes).Select(x => new ColorRecipe() { R_Color = x.ColorCatalogsItem.Color, R_Name = x.ColorCatalogsItem.Name, R_Media = x.Rml.Name, R_Cyan = x.Cyan, R_Magenta = x.Magenta, R_Yellow = x.Yellow, R_Black = x.Black, R_Region = x.ProcessParametersTableIndex, }).ToList(); writer.WriteData(recipes, "Recipes"); writer.Dispose(); InvokeUI(() => { _notification.ShowInfo("Catalog exported successfully."); }); } catch (Exception ex) { LogManager.Log(ex, $"Error exporting color catalog to {dlg.FileName}"); InvokeUI(() => { _notification.ShowError($"An error occurred while trying to export the catalog. Make sure the selected excel file is closed and data is valid.\n{ex.FlattenMessage()}"); }); } finally { IsFree = true; } }); } } } public void ImportCatalogFromExcel() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Excel Documents|*.xlsx"; if (dlg.ShowDialog().Value) { using (_notification.PushTaskItem("Merging catalog from file...")) { Task.Factory.StartNew(() => { try { ExcelReader reader = new ExcelReader(dlg.FileName); var groups = reader.GetData("Groups"); reader.Dispose(); reader = new ExcelReader(dlg.FileName); var colors = reader.GetData("Colors"); reader.Dispose(); reader = new ExcelReader(dlg.FileName); var recipes = reader.GetData("Recipes"); reader.Dispose(); foreach (var group in groups) { ColorCatalogsGroup existinGroup = ActiveCatalog.ColorCatalogsGroups.SingleOrDefault(x => x.Name == group.GroupName); bool inserting = false; if (existinGroup == null) { existinGroup = new ColorCatalogsGroup(); inserting = true; } existinGroup.Name = group.GroupName; existinGroup.GroupIndex = group.GroupIndex; if (inserting) { ActiveCatalog.ColorCatalogsGroups.Add(existinGroup); } } foreach (var item in colors) { var existinItem = ActiveCatalog.ColorCatalogsGroups.SelectMany(x => x.ColorCatalogsItems).SingleOrDefault(x => x.Code == item.Code); var group = ActiveCatalog.ColorCatalogsGroups.SingleOrDefault(x => x.Name == item.Group); bool inserting = false; if (existinItem == null) { existinItem = new ColorCatalogsItem(); inserting = true; } else { if (existinItem.ColorCatalogsGroup != group) { existinItem.ColorCatalogsGroup.ColorCatalogsItems.Remove(existinItem); group.ColorCatalogsItems.Add(existinItem); } } existinItem.ColorCatalogsGroup = group; existinItem.Code = item.Code; existinItem.Name = item.Name; existinItem.ItemIndex = item.ItemIndex; existinItem.Red = item.Red; existinItem.Green = item.Green; existinItem.Blue = item.Blue; existinItem.L = item.L; existinItem.A = item.A; existinItem.B = item.B; existinItem.Cyan = item.Cyan; existinItem.Magenta = item.Magenta; existinItem.Yellow = item.Yellow; existinItem.Black = item.Black; existinItem.BlueExtra = item.BlueExtra; existinItem.NavyExtra = item.NavyExtra; existinItem.OrangeExtra = item.OrangeExtra; existinItem.RedExtra = item.RedExtra; existinItem.RubineExtra = item.RubineExtra; existinItem.VioletExtra = item.VioletExtra; existinItem.ProcessParametersTableIndex = item.Region; if (inserting) { _activeCatalogContext.ColorCatalogsItems.Add(existinItem); } } foreach (var recipe in recipes) { var existingRecipe = ActiveCatalog.ColorCatalogsGroups.SelectMany(x => x.ColorCatalogsItems).SelectMany(x => x.ColorCatalogsItemsRecipes).SingleOrDefault(x => x.ColorCatalogsItem.Name == recipe.R_Name && x.Rml.Name == recipe.R_Media); bool inserting = false; if (existingRecipe == null) { existingRecipe = new ColorCatalogsItemsRecipe(); existingRecipe.Rml = RMLS.SingleOrDefault(x => x.Name == recipe.R_Media); existingRecipe.ColorCatalogsItem = ActiveCatalog.ColorCatalogsGroups.SelectMany(x => x.ColorCatalogsItems).SingleOrDefault(x => x.Name == recipe.R_Name); inserting = true; } existingRecipe.Cyan = recipe.R_Cyan; existingRecipe.Magenta = recipe.R_Magenta; existingRecipe.Yellow = recipe.R_Yellow; existingRecipe.Black = recipe.R_Black; existingRecipe.ProcessParametersTableIndex = recipe.R_Region; if (inserting) { _activeCatalogContext.ColorCatalogsItemsRecipes.Add(existingRecipe); } } InvokeUI(() => { _notification.ShowInfo("Catalog imported successfully."); }); } catch (Exception ex) { LogManager.Log(ex, $"Error importing color catalog to {dlg.FileName}"); InvokeUI(() => { _notification.ShowError($"An error occurred while trying to merge the catalog. Please check your data.\n{ex.FlattenMessage()}"); }); } finally { IsFree = true; } }); } } } #endregion #region Application Ready public async override void OnApplicationReady() { await LoadCatalogs(); } #endregion } }