|
|
@@ -0,0 +1,316 @@
|
|
|
+<table style="width: 100%;"><tr><td style="width: 40%;">
|
|
|
+<a href="../articles/cs_coloring2.md">Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции.
|
|
|
+</a></td><td style="width: 20%;">
|
|
|
+<a href="../readme.md">Содержание
|
|
|
+</a></td><td style="width: 40%;">
|
|
|
+<a href="../articles/kotlin.md">Основы языка Kotlin
|
|
|
+</a></td><tr></table>
|
|
|
+
|
|
|
+# Добавление/редактирование продукции
|
|
|
+
|
|
|
+>Необходимо добавить возможность редактирования данных существующей продукции, а также добавление новой продукции в новом окне - форме для добавления/редактирования продукции.
|
|
|
+>
|
|
|
+>Переходы на данное окно должны быть реализованы из главной формы списка: для редактирования - при нажатии на конкретный элемент, для добавления - при нажатии кнопки “Добавить продукцию”.
|
|
|
+>
|
|
|
+>На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание (с возможностью многострочного ввода).
|
|
|
+>
|
|
|
+>Также необходимо реализовать вывод списка материалов, используемых при производстве продукции, с указанием количества. В список можно добавлять новые позиции и удалять существующие. При добавлении материалы должны выбираться из выпадающего списка с возможностью поиска по наименованию.
|
|
|
+>
|
|
|
+>При открытии формы для редактирования все поля выбранного объекта должны быть подгружены в соответствующие поля из базы данных, а таблица заполнена актуальными значениями.
|
|
|
+>
|
|
|
+>Стоимость продукции может включать сотые части, а также не может быть отрицательной. Система должна проверять существование продукта с введенным артикулом и не давать использовать один
|
|
|
+артикул для нескольких продуктов.
|
|
|
+>
|
|
|
+>Пользователь может добавить/заменить изображение у продукции.
|
|
|
+>
|
|
|
+>Для того чтобы администратор случайно не изменял несколько продуктов, предусмотрите невозможность открытия более одного окна редактирования.
|
|
|
+>
|
|
|
+>В окне редактирования продукта должна присутствовать кнопка “Удалить”, которая удаляет продукт из базы данных. При этом должны соблюдаться следующие условия. Если у продукта есть информация о материалах, используемых при его производстве, или история изменения цен, то эта информация должна быть удалена вместе с продуктом. Но если у продукта есть информация о его продажах агентами, то удаление продукта из базы данных должно быть запрещено. После удаления продукта система должна сразу вернуть пользователя обратно к списку продукции.
|
|
|
+>
|
|
|
+>После редактирования/добавления/удаления продукции данные в окне списка продукции должны быть обновлены.
|
|
|
+
|
|
|
+Критерий | Баллы
|
|
|
+---------|:---:
|
|
|
+Реализован переход на окно добавления | 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*, в котором будет храниться добавляемый/редактируемый экземпляр продукции:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ public Product CurrentProduct { get; set; }
|
|
|
+ ```
|
|
|
+
|
|
|
+ И геттер для названия окна:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ public string WindowName {
|
|
|
+ get {
|
|
|
+ return CurrentProduct.Id == 0 ? "Новый продукт" : "Редактирование продукта";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+3. В конструктор окна добавьте параметр типа **Product** и присвойте его ранее объявленному свойству:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ public EditWindow(Product EditProduct)
|
|
|
+ {
|
|
|
+ InitializeComponent();
|
|
|
+ DataContext = this;
|
|
|
+ CurrentProduct = EditProduct;
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+4. В разметке окна вместо фиксированного названия вставьте привязку к свойству *WindowName*
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <Window
|
|
|
+ ...
|
|
|
+ Title="{Binding WindowName}">
|
|
|
+ ```
|
|
|
+
|
|
|
+5. В окне редактирования продукции создайте сетку из трёх колонок: в первой у нас будет изображение, во второй редактируемые поля продукта, а в третей список материалов
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <Grid.ColumnDefinitions>
|
|
|
+ <ColumnDefinition Width="auto"/>
|
|
|
+ <ColumnDefinition Width="*"/>
|
|
|
+ <ColumnDefinition Width="auto"/>
|
|
|
+ </Grid.ColumnDefinitions>
|
|
|
+ ```
|
|
|
+
|
|
|
+6. Во вторую колонку добавьте **StackPanel** с границами (чтобы визуальные компоненты не прилипали к границам окна) и в этом списке разместите редактируемые элементы
|
|
|
+
|
|
|
+ >На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <StackPanel
|
|
|
+ Grid.Column="1"
|
|
|
+ Margin="5">
|
|
|
+
|
|
|
+ <Label Content="Артикул"/>
|
|
|
+ <TextBox Text="{Binding CurrentProduct.ArticleNumber}"/>
|
|
|
+
|
|
|
+ <Label Content="Наименование продукта"/>
|
|
|
+ <TextBox Text="{Binding CurrentProduct.Title}"/>
|
|
|
+
|
|
|
+ ...
|
|
|
+
|
|
|
+ </StackPanel>
|
|
|
+ ```
|
|
|
+
|
|
|
+ Обычные поля наклепайте по шаблону сами, а я подробнее остановлюсь на полях: *тип продукта*, *изображение* и *описание*:
|
|
|
+
|
|
|
+ * Выбор типа продукта из списка
|
|
|
+
|
|
|
+ В классе окна объявляем свойство *ProductTypes* - список типов продукции
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ public IEnumerable<ProductType> ProductTypeList { get; set; }
|
|
|
+ ```
|
|
|
+
|
|
|
+ И в конструкторе получаем его из контекста БД:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ using (var context = new ksmirnovContext())
|
|
|
+ {
|
|
|
+ ProductTypeList = context.ProductTypes.ToList();
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ В вёрстке окна редактирования продукции мы можем использовать выпадающий список, атрибут *SelectedItem* которого позволяет отобразить текущее значение редактируемого элемента
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <ComboBox
|
|
|
+ ItemsSource="{Binding ProductTypeList}"
|
|
|
+ SelectedItem="{Binding CurrentProduct.ProductType}"/>
|
|
|
+ ```
|
|
|
+
|
|
|
+ >Из документации: *Класс ComboBox выполняет поиск указанного объекта с помощью **IndexOf** метода. Этот метод использует **Equals** метод для определения равенства*.
|
|
|
+
|
|
|
+ А метод **Equals** сравнивает объекты по уникальному идентификатору, т.е. свойство **ProductType** у экземпляра продукции НЕ РАВНО экземпляру элемента списка *ProductTypeList*
|
|
|
+
|
|
|
+ Чтобы исправить эту неприятность нужно переопределить метод **Equals** у класса **ProductType**:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ public override bool Equals(object obj)
|
|
|
+ {
|
|
|
+ return (obj != null) &&
|
|
|
+ (obj is ProductType) &&
|
|
|
+ (this.Id == (obj as ProductType).Id);
|
|
|
+
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ Здесь мы проверяем определён ли вообще объект (у нового продукта его ещё нет), нужного ли он типа и совпадет ли его **Id** с **Id** текущего типа продукции
|
|
|
+
|
|
|
+ * смена изображения продукции
|
|
|
+
|
|
|
+ Вывод изображения производится как и в главном окне
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <Image
|
|
|
+ Width="200"
|
|
|
+ Height="200"
|
|
|
+ Source="{Binding CurrentProduct.ImagePreview,TargetNullValue={StaticResource defaultImage}}" />
|
|
|
+ ```
|
|
|
+
|
|
|
+ А для смены изображения используем стандартный диалог Windows, повесив его на кнопку *Сменить картинку* (кнопку добавьте сами в **StackPanel**)
|
|
|
+
|
|
|
+ Обработчик кнопки:
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ * Многострочное описание
|
|
|
+
|
|
|
+ Тут просто - разрешаем переносы и задаем высоту элемента
|
|
|
+
|
|
|
+ ```xml
|
|
|
+ <Label Content="Описание продукта"/>
|
|
|
+ <TextBox
|
|
|
+ AcceptsReturn="True"
|
|
|
+ Height="2cm"
|
|
|
+ Text="{Binding CurrentProduct.Description}"/>
|
|
|
+ ```
|
|
|
+
|
|
|
+7. Сохранение введенных данных
|
|
|
+
|
|
|
+ В разметку добавьте кнопку **Сохранить** и напишите обработчик
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ private void Button_Click(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ using (var context = new ksmirnovContext())
|
|
|
+ {
|
|
|
+ // вся работа с БД должна быть завернута в исключения
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // сюда добавлять проверки
|
|
|
+
|
|
|
+ /* ПОКА НЕ РАБОТАЕТ !!!
|
|
|
+ if (CurrentProduct.Id != 0)
|
|
|
+ {
|
|
|
+ context.Products.Update(CurrentProduct);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ CurrentProduct.ProductTypeId = CurrentProduct.ProductType.Id;
|
|
|
+ context.Products.Add(CurrentProduct);
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ if (context.SaveChanges() > 0)
|
|
|
+ {
|
|
|
+ DialogResult = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ MessageBox.Show(ex.InnerException.Message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+8. Открытие окна редактирования для существующей и новой продукции
|
|
|
+
|
|
|
+ * для редактирования существующей продукции в списке продукции реализуем обработчик двойного клика
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ * для создания нового продукта в разметке главного окна создайте кнопку "Добавить продукцию" (в левой панели) и в её обработчике создайте новый экземпляр продукта
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ var NewEditWindow = new EditWindow(new Product());
|
|
|
+ ...
|
|
|
+ ```
|
|
|
+9. Проверки перед сохранением продукта
|
|
|
+
|
|
|
+ Все проверки вставляем в обработчик кнопки "Сохранить" окна редактирования, т.к. это относится к бизнес-логике, до вызова метода сохранения продукта
|
|
|
+
|
|
|
+ * Стоимость продукции не может быть отрицательной
|
|
|
+
|
|
|
+ ```cs
|
|
|
+ if (ChangedProduct.MinCostForAgent < 0)
|
|
|
+ throw new Exception("Цена продукта не может быть отрицательной");
|
|
|
+ ```
|
|
|
+
|
|
|
+ * Стоимость продукции записывается только с точностью до сотых
|
|
|
+
|
|
|
+ * Реализована проверка артикула на уникальность
|
|
|
+
|
|
|
+ Тут по идее надо делать запрос к базе, но у нас есть метод получения списка продукции и мы можем искать в нём используя LINQ-запросы
|
|
|
+
|
|
|
+<table style="width: 100%;"><tr><td style="width: 40%;">
|
|
|
+<a href="../articles/cs_coloring2.md">Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции.
|
|
|
+</a></td><td style="width: 20%;">
|
|
|
+<a href="../readme.md">Содержание
|
|
|
+</a></td><td style="width: 40%;">
|
|
|
+<a href="../articles/kotlin.md">Основы языка Kotlin
|
|
|
+</a></td><tr></table>
|