cs_edit_product2.md 23 KB

Добавление/редактирование продукции

Необходимо добавить возможность редактирования данных существующей продукции, а также добавление новой продукции в новом окне - форме для добавления/редактирования продукции.

Переходы на данное окно должны быть реализованы из главной формы списка: для редактирования - при нажатии на конкретный элемент, для добавления - при нажатии кнопки “Добавить продукцию”.

На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание (с возможностью многострочного ввода).

Также необходимо реализовать вывод списка материалов, используемых при производстве продукции, с указанием количества. В список можно добавлять новые позиции и удалять существующие. При добавлении материалы должны выбираться из выпадающего списка с возможностью поиска по наименованию.

При открытии формы для редактирования все поля выбранного объекта должны быть подгружены в соответствующие поля из базы данных, а таблица заполнена актуальными значениями.

Стоимость продукции может включать сотые части, а также не может быть отрицательной. Система должна проверять существование продукта с введенным артикулом и не давать использовать один артикул для нескольких продуктов.

Пользователь может добавить/заменить изображение у продукции.

Для того чтобы администратор случайно не изменял несколько продуктов, предусмотрите невозможность открытия более одного окна редактирования.

В окне редактирования продукта должна присутствовать кнопка “Удалить”, которая удаляет продукт из базы данных. При этом должны соблюдаться следующие условия. Если у продукта есть информация о материалах, используемых при его производстве, или история изменения цен, то эта информация должна быть удалена вместе с продуктом. Но если у продукта есть информация о его продажах агентами, то удаление продукта из базы данных должно быть запрещено. После удаления продукта система должна сразу вернуть пользователя обратно к списку продукции.

После редактирования/добавления/удаления продукции данные в окне списка продукции должны быть обновлены.

Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции. Содержание Основы языка Kotlin
Критерий Баллы
Реализован переход на окно добавления 0.1
Реализован переход на окно редактирования выбранного объекта 0.2
Присутствуют все поля для заполнения 0.5
При редактировании продукции в поля для ввода загружены данные из БД 0.3
Выбор типа продукта реализован в виде выпадающего списка со значениями из БД 0.3
Для ввода описания продукции предусмотрено многострочное поле для ввода 0.2
Реализован список используемых материалов для текущего продукта 0.3
В списке присутствует название материала и используемое количество 0.2
При редактировании продукции список материалов заполнен значениями из БД 0.2
В список можно добавлять новые позиции 0.3
Из списка можно удалять существующие позиции 0.2
При добавлении материалы выбираются из выпадающего списка со значениями из БД 0.3
В списке материалов реализована возможность поиска по наименованию 0.2
Список используемых материалов сохраняется в БД при добавлении 0.5
Список используемых материалов сохраняется в БД при редактировании 0.5
Стоимость продукции не может быть отрицательной 0.1
Стоимость продукции записывается только с точностью до сотых 0.2
Реализована проверка артикула на уникальность 0.3
Есть возможность выбрать изображение 0.2
Изображение продукции подгружается из БД при редактировании 0.2
Есть возможность заменить изображение 0.1
Данные при добавлении сохраняются в БД 0.5
Данные при редактировании изменяются в БД 0.5
Открывается только одно окно редактирования 0.1
Реализовано удаление выбранного продукта, у которого не заполнен список используемых материалов 0.2
Реализовано удаление продукта вместе с информацией об используемых материалах 0.5
Запрещено удаление продукта, по которому были выполнены продажи агентом 0.3
После удаления реализован автоматический переход обратно в список 0.1
После закрытия окна данные в таблице обновляются 0.3
Итого 7.9

Создание окна редактирования продукции

Для добавления и редактирования мы будем использовать одно и то же окно. Название окна будем вычислять по наличию ID у продукции (у новой записи это поле = 0)

  1. Создайте новое окно: EditProductWindow (в папке Windows - не забываем про логическую структуру проекта)

  2. В классе окна EditProductWindow добавьте свойство CurrentProduct, в котором будет храниться добавляемый/редактируемый экземпляр продукции:

    public Product CurrentProduct { get; set; }
    

    И геттер для названия окна:

    public string WindowName {
        get {
            return CurrentProduct.Id == 0 ? "Новый продукт" : "Редактирование продукта";
        }
    }
    
  3. В конструктор окна добавьте параметр типа Product и присвойте его ранее объявленному свойству:

    public EditWindow(Product EditProduct)
    {
        InitializeComponent();
        DataContext = this;
        CurrentProduct = EditProduct;
    }
    
  4. В разметке окна вместо фиксированного названия вставьте привязку к свойству WindowName

    <Window
        ...
        Title="{Binding WindowName}">
    
  5. В окне редактирования продукции создайте сетку из трёх колонок: в первой у нас будет изображение, во второй редактируемые поля продукта, а в третей список материалов

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="auto"/>
    </Grid.ColumnDefinitions>
    
  6. Во вторую колонку добавьте StackPanel с границами (чтобы визуальные компоненты не прилипали к границам окна) и в этом списке разместите редактируемые элементы

    На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание

    <StackPanel
        Grid.Column="1" 
        Margin="5">
    
        <Label Content="Артикул"/>
        <TextBox Text="{Binding CurrentProduct.ArticleNumber}"/>
    
        <Label Content="Наименование продукта"/>
        <TextBox Text="{Binding CurrentProduct.Title}"/>
    
        ...
    
    </StackPanel>
    

    Обычные поля наклепайте по шаблону сами, а я подробнее остановлюсь на полях: тип продукта, изображение и описание:

    • Выбор типа продукта из списка

      В классе окна объявляем свойство ProductTypes - список типов продукции

      public IEnumerable<ProductType> ProductTypeList { get; set; }
      

      И в конструкторе получаем его из контекста БД:

      using (var context = new ksmirnovContext())
      {
          ProductTypeList = context.ProductTypes.ToList();
      }        
      

      В вёрстке окна редактирования продукции мы можем использовать выпадающий список, атрибут SelectedItem которого позволяет отобразить текущее значение редактируемого элемента

      <ComboBox 
          ItemsSource="{Binding ProductTypeList}"
          SelectedItem="{Binding CurrentProduct.ProductType}"/>
      

      Из документации: *Класс ComboBox выполняет поиск указанного объекта с помощью IndexOf метода. Этот метод использует Equals метод для определения равенства*.

      А метод Equals сравнивает объекты по уникальному идентификатору, т.е. свойство ProductType у экземпляра продукции НЕ РАВНО экземпляру элемента списка ProductTypeList

      Чтобы исправить эту неприятность нужно переопределить метод Equals у класса ProductType:

      public override bool Equals(object obj)
      {
          return (obj != null) && 
              (obj is ProductType) && 
              (this.Id == (obj as ProductType).Id);
              
      }
      

      Здесь мы проверяем определён ли вообще объект (у нового продукта его ещё нет), нужного ли он типа и совпадет ли его Id с Id текущего типа продукции

    • смена изображения продукции

      Вывод изображения производится как и в главном окне

      <Image
          Width="200" 
          Height="200"
          Source="{Binding CurrentProduct.ImagePreview,TargetNullValue={StaticResource defaultImage}}" />
      

      А для смены изображения используем стандартный диалог Windows, повесив его на кнопку Сменить картинку (кнопку добавьте сами в StackPanel)

      Обработчик кнопки:

      private void ChangeImage_Click(object sender, RoutedEventArgs e)
      {
          OpenFileDialog GetImageDialog = new OpenFileDialog();
          // задаем фильтр для выбираемых файлов
          // до символа "|" идет произвольный текст, а после него шаблоны файлов разделенные точкой с запятой
          GetImageDialog.Filter = "Файлы изображений: (*.png, *.jpg)|*.png;*.jpg";
          // чтобы не искать по всему диску задаем начальный каталог
          GetImageDialog.InitialDirectory = Environment.CurrentDirectory;
          if (GetImageDialog.ShowDialog() == true)
          {
              // перед присвоением пути к картинке обрезаем начало строки, т.к. диалог возвращает полный путь
              CurrentProduct.Image = GetImageDialog.FileName.Substring(Environment.CurrentDirectory.Length);
              // обратите внимание, это другое окно и другой Invalidate, который реализуйте сами
              Invalidate();
          }
      }
      
    • Многострочное описание

      Тут просто - разрешаем переносы и задаем высоту элемента

      <Label Content="Описание продукта"/>
      <TextBox 
          AcceptsReturn="True"
          Height="2cm"
          Text="{Binding CurrentProduct.Description}"/>
      
  7. Сохранение введенных данных

    В разметку добавьте кнопку Сохранить и напишите обработчик

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        using (var context = new ksmirnovContext())
        {
            // вся работа с БД должна быть завернута в исключения
            try
            {
                Product product = null;
                if (CurrentProduct.Id != 0)
                    product = context.Products.Find(CurrentProduct.Id);
                else
                    product = new Product();
    
                if (product != null)
                {
                    // сюда добавлять проверки
    
                    product.Title = CurrentProduct.Title;
                    product.ArticleNumber = CurrentProduct.ArticleNumber;
                    product.ProductTypeId = CurrentProduct.ProductType.Id;
                    product.MinCostForAgent = 100;
    
                    if (product.Id==0)
                        context.Products.Add(product);
                    else
                        context.Products.Update(product);
    
                    if (context.SaveChanges() > 0)
                    {
                        DialogResult = true;
                    }
                }
            }
            catch (Exception ex)
            {
                if(ex.InnerException != null)
                    MessageBox.Show(ex.InnerException.Message);
                else
                    MessageBox.Show(ex.Message);            
            }
        }        
    }
    
  8. Открытие окна редактирования для существующей и новой продукции

    • для редактирования существующей продукции в списке продукции реализуем обработчик двойного клика

      private void ProductListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
      {
          // в создаваемое окно передаем выбранный продукт
          var NewEditWindow = new EditProductWindow(ProductListView.SelectedItem as Product);
          if ((bool)NewEditWindow.ShowDialog())
          {
              // при успешном сохранении продукта перечитываем список продукции
              using (var context = new ksmirnovContext()) {
                  ProductList = context.Products
                      .Include(product => product.ProductType)
                      .Include(product => product.ProductMaterials)
                      .ToList();
              }
          }
      }
      
    • для создания нового продукта в разметке главного окна создайте кнопку "Добавить продукцию" (в левой панели) и в её обработчике создайте новый экземпляр продукта

      var NewEditWindow = new EditWindow(new Product());
      ...
      
  9. Проверки перед сохранением продукта

    Все проверки вставляем в обработчик кнопки "Сохранить" окна редактирования, т.к. это относится к бизнес-логике, до вызова метода сохранения продукта

    • Стоимость продукции не может быть отрицательной

      if (ChangedProduct.MinCostForAgent < 0)
          throw new Exception("Цена продукта не может быть отрицательной");
      
    • Стоимость продукции записывается только с точностью до сотых

    • Реализована проверка артикула на уникальность

      Тут по идее надо делать запрос к базе, но у нас есть метод получения списка продукции и мы можем искать в нём используя LINQ-запросы

  10. Удаление продукции

    В окне редактирования продукта должна присутствовать кнопка “Удалить”, которая удаляет продукт из базы данных. При этом должны соблюдаться следующие условия. Если у продукта есть информация о материалах, используемых при его производстве, или история изменения цен, то эта информация должна быть удалена вместе с продуктом. Но если у продукта есть информация о его продажах агентами, то удаление продукта из базы данных должно быть запрещено. После удаления продукта система должна сразу вернуть пользователя обратно к списку продукции.

    Добавьте кнопку удалить в среднюю колоку (рядом с кнопкой сохранить). Атрибут Visibility привяжите к Id продукта (т.е. у нового продукта кнопки удалить быть не должно).

    И реализуйте обработчик клика по этой кнопке

    private void DeleteProductButton_Click(object sender, RoutedEventArgs e)
    {
        using (var context = new ksmirnovContext())
        {
            try
            {
                // тут вставить проверки, требуемые по ТЗ
                // + исключение, если есть продажи
                var saleCount = context.ProductSales
                    .Where(ps => ps.ProductId==CurrentProduct.Id).Count();
    
                if (saleCount > 0)
                    throw new Exception("Нельзя удалять продукт с продажами");
    
                // - удаление списа материалов продукта
                // - удаление истории изменения цен
    
                var product = context.Products.Find(CurrentProduct.Id);
                context.Products.Remove(product);
                if (context.SaveChanges() > 0)
                {
                    DialogResult = true;
                }
            }
            catch (Exception ex)
            {
                if(ex.InnerException != null)
                    MessageBox.Show(ex.InnerException.Message);
                else
                    MessageBox.Show(ex.Message);            
            }
        }
    }
    
Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции. Содержание Основы языка Kotlin