На прошлом уроке создали экран редактирования заказа, но реализацию возможности изменять клиента и редактировать позиции заказа оставил на этот урок.
Скачайте архив с изображениями и разархивируйте его в папку проекта \Template\Projects\1. Template\MobileForms\Images.
Редактирование клиента
Так как список клиентов может быть большим и неудобным для использования ComboBox, то договорились для выбора клиента использовать отдельный экран. В прошлом уроке на экран редактирования заказа справа от поля с именем клиента добавили кнопку, которую планировали использовать для перехода на форму со списком клиентов. Самостоятельно создайте пустую форму списка клиентов и команду открытия этой формы, которая будет вызываться по ранее созданной кнопке:
На форму списка клиентов передаем значение ClientId текущего клиента в заказе, полученного из базы данных. Помним, что запрос на получение списка должен возвращать все неархивные записи и запись, на которую ссылается редактируемый заказ.
Строка поиска
Чтобы пользователю было удобно работать с потенциально большим списком, добавим возможность поиска клиента по имени.
Строка поиска будет представлена объектом типа Panel с именем SearchPanel, содержащим элементы:
SearchButton - хоть и является объектом типа Button, но это просто изображение лупы, не выполняющее никакой команды
SearchTextBox - текстовое поле для ввода строки поиска
ClearSearchButton - кнопка с изображением повернутого крестика для очистки строки поиска
Объект SearchPanel будет иметь настройки BorderCorner и BackColor аналогичные тем, что делали для панелей блоков на экране заказа. Высота панели 40 единиц.
Самостоятельно создайте объект SearchTextBox, который будет справа от SearchButton без отступов. По высоте текстовое поле должно совпадать с высотой панели, следовательно отступ сверху будет нулевым. Ширина текстового поля должна учитывать размер кнопки ClearSearchButton и оставлять отступ справа в 10 единиц. Для объекта SearchTextBox текст-подсказки будет "Поиск". А его цвет зависит от оформления системы: для темной темы используется DarkGray, а для светлой темы - LightGray. Цвет основного текста (тэг <ForeColor>) тоже зависит от выбранного оформления системы: для темной темы используется WhiteSmoke, а для светлой темы - MostlyBlack. Значение тэга <BackColor> можно тянуть из аналогичного свойства объекта SearchPanel. В тэге <FontStyle> укажем значение TextFont. По умолчанию TextBox имеет рамку, чтобы ее убрать используем тэг <Multiline> со значением True.
Кнопка ClearSearchButton для сброса значения объекта SearchTextBox будет иметь вид:
Самостоятельно создайте команду ResetSearchStringValueSetCommand.
Запустите приложение и проверьте отображение строки поиска на экране выбора клиента.
Отлично!
Самостоятельно добавьте на форму список с клиентами ClientCollectionView. В качестве источника данных используйте SecondaryGetDataConnection, в котором должны быть отфильтрованные по строке поиска данный из PrimaryGetDataConnection.
Запрос на получение списка клиентов должен содержать поля:
В результате у вас должна получиться форма вида:
Запустите приложение и проверьте работу фильтра списка:
Выбор клиента
Добавим на форму параметры, в которых будем возвращать на родительскую форму данные выбранного клиента:
Как видите на форме используем паттерн Updated - добавьте в файл параметр Updated и создайте команду для его обновления.
Замена клиента в заказе
Вернемся в файл описания карточки заказа и реализуем обновления данных о клиенте в OrderPrimaryGetDataConnection. Самостоятельно создайте команду типа ValueSetCommand, в которой заменяйте значения в полях DataConnection соответствующих возвращаемым параметрам. Реализуйте Execution, в котором будет вызываться эта команда, используя паттерн Updated.
Запустите приложение и проверьте функциональность смены клиента в заказе.
Дополнительно: на кнопку смены клиента наложите ограничение, если заказ оплачен.
Редактирование позиций заказа
Начнем с реализации возможности добавление позиций в заказ и изменение кол-ва товара в позиции. Для этого будем использовать отдельную форму со списком ТМЦ. А затем добавим удаление позиций с помощью свайпа в карточке заказа.
Подготовка
Первым делом создадим ConvertDataConnection, который будет преобразовывать OrderPositionPrimaryGetDataConnection:
Здесь мы добавляем поле ID, которое будет временным идентификатором, который будем передавать на форму со списком товаров, чтобы правильно выставить флаги Added, Upated и Deleted. О них подробнее позже. Так же добавили поле IsNew, флаг которым будем помечать позиции заказа не сохраненные в базе данных.
В качестве значения нового поля указали объект CounterVariable, который является переменной-счетчиком и каждый раз при обращении увеличивает текущее значение на единицу и возвращает его.
На форму каталога передаем параметр MaterialArray с массивом добавленных товаров в заказ.
На экране редактирования заказа добавьте кнопку Изменить, укажите для нее команду MaterialListFormShowCommand. Кнопку разместите справа над списком позиций заказов:
Список товаров
Перейдем в файл новой формы и в него добавим строку поиска, как делали на форме выбора клиента.
С родительской формы передаем параметр MaterialArray, добавим его на форму, а чтобы было удобно работать с передаваемым массивом данных создадим ArrayGetDataConnection:
Создайте самостоятельно MaterialPrimaryGetDataConnection, который будет получать данные из запроса:
Template.xml
<SqlQuery Name="AppMaterialSelectSqlQuery">
<Text>
SELECT
M.material_id AS "MaterialId",
M.title AS "Title",
M.material_category_id AS "MaterialCategoryId",
MC.title AS "MaterialCategoryTitle",
U.short_title AS "UnitShortTitle",
M.unit_price AS "UnitPrice",
trim(to_char(M.unit_price, '999G999G999D00')) || ' руб./' || U.short_title AS "UnitPriceString",
M.material_id != ALL({MaterialId}::bigint[]) AS "NotUse",
M.material_id = ANY({MaterialId}::bigint[]) AS "Selected"
FROM
template.material M
LEFT JOIN template.material_category MC USING(material_category_id)
LEFT JOIN template.unit U USING(unit_id)
WHERE
NOT M.archive OR M.material_id = ANY({MaterialId}::bigint[])
ORDER BY M.title;
</Text>
</SqlQuery>
В качестве параметра в запрос передаем массив MaterialId, полученных с родительской формы.
Здесь так же возвращаем два поля с ценой за единицу товара: UnitPrice и UnitPriceString - значение из первого будем возвращать на родительскую форму и использовать для расчета итоговой стоимости, а второе для отображения в карточке товара.
Так же возвращаем два флага:
NotUse - true, если в заказе нет позиции с этим товаром, по этому флагу будет отображаться кнопка "Добавить" в карточке товара,
Selected - true, если в заказе есть позиция с этим товаром, по этому флагу будет отображаться количество товара и кнопки изменения.
Обернем загружающее соединение в ConvertDataConnection и добавим новые поля:
Поле TotalPrice необходимо для хранения итоговой суммы по выбранному товару.
В полях Quantity и ID подставляем соответствующие значения из массива полученного от родительской формы.
Поля Added, Updated и Deleted необходимы, чтобы при передачи на родительскую форму различать новые товары, которые нужно добавить через set-проперти AddRows, позиции в которых нужно обновить количество товара, и позиции подлежащие удалению из заказа.
Создайте MaterialCollectionView для отображения списка товаров. Добавьте в шаблон карточки элемента списка описание полей для наименования товара, его категории и цены за единицу.
Сразу создадим условие, в котором будем проверять наличие значения в поле ID:
В команде находим индекс строки в MaterialConvertDataConnection по значению MaterialId элемента, по кнопке которого сделали тап, и в найденной строке меняем значения флагов и ставим количество равное единице. В результате кнопка исчезнет из карточки и вместо нее должны появиться элементы управления количеством товара в заказе.
Так же изменяем значения в полях Added, Updated и Deleted. Новые значения для Added и Updated зависят от наличия временного идентификатора на тот случай, если сначала удалили товар, и сразу нажали на кнопку Добавить. Если у товара есть временный ID, то мы должны рассматривать изменение существующего, а не добавление нового.
В условие QuantityOfSelectedMaterialGreater1Condition проверяем количество на больше единицы, чтобы определить, какую команду следует выполнить. Если количество больше единицы, то будем его уменьшать на единицу, а если равно единицы, то меняем значения флагов NotUse и Selected на противоположные, а количество сбрасываем на 0.
Самостоятельно напишите условие и обе команды. Не забудьте менять значения в полях Added, Updated и Deleted, учитывая при этом временный ID. Если у товара нет временного идентификатора, то можно просто сбросить количество и не помечать его на удаление из карточки заказа. При уменьшении количества товара важно правильно
Текущее количество товара будем отображать в объекте:
Первой будет команда для параметра MaterialArrayToAdd. В нем будем получать из MaterialConvertDataConnection все записи, у которых в поле Added стоит значение True:
В параметр MaterialArrayToEdit будем добавлять те записи, у которых в поле Updated стоит значение True и дополнительно будем проверять значение в поле Added, чтобы оно было равно False:
Не забываем про паттерн Updated, чтобы на родительском экране корректно отработал Execution.
Внесение изменений
Вернемся на родительскую форму чтобы добавить логику обработки полученных значений и внесения изменений в список позиций заказа.
Первым делом создайте условие MaterialListFormUpdatedTrueEqualCondition для проверки возвращаемого параметра Updated и Execution, который будет выполняться по этому условию.
Далее будет описывать команды на внесение изменений в OrderPositionConvertDataConnection и добавлять их в созданный Execution.
Здесь разбираем данные из параметра MaterialArrayToAdd с помощью <Array> и добавляем новые строки в OrderPositionConvertDataConnection. Новые позиции помечаем в поле IsNew значением True.
Объединяем два массива по временному идентификатору записей, и через селектор Number получаем индексы строк. Дальше в командах будем проверять значение во втором поле результирующей таблицы и по нему выделять позиции заказа сохраненные в базе данных.
Первом создадим команду для отмечания позиций на удаление из базы данных:
Запустим приложение и проверим функциональность на добавление, изменение и удаление позиций заказа. Позже добавим логику сохранения изменений в базе данных, а пока реализуем логику удаления позиции заказа из списка через свайп-меню.
Удаление позиции заказа
Для удаления позиции заказа из списка будем использовать контекстное меню, вызываемое свайпом карточки влево. Направление свайпа и элементы меню настраиваются - поэтому при необходимости можете настроить вызов меню через свайп вправо, или даже реализовать два разных меню на каждое направление свайпа.
Свайп-меню настраивается в тэге <SwipeMenu>, определенном в CollectionView. Добавим в описание объекта OrderPositionCollectionView следующий код:
Тэг <RightItems> содержит список элементов меню <SwipeItem>, которые будут отображаться справа от карточки при свайпе влево. Если необходимо отображать меню слева от карточки (при свайпе вправо), то используем тэг <LeftItems>.
Элементы свайп-меню описываются тегом <SwipeItem>, в котором указывается текст надписи, путь до изображения, цвет фона и последовательность команд.
Добавим в файл описание используемого цвета для кнопки удаления:
Запустим приложение, чтобы проверить отображение и работу свайп-меню для удаления позиции заказа:
s
Итоги
На уроке мы рассмотрели процесс аутентификации пользователя, .
Ответы
В архиве присутствуют xml-файлы форм для мобильного приложения и серверный xml-файл, также лежит бэкап базы данных и файл с запросами на изменение структуры базы данных - с помощью файлов можете проверить себя.
Архив не содержит xml-файлы форм десктопного приложения.