На основе запроса OrderSelectSqlQuery, который описан в серверном xml-файле, создадим запрос для получения списка заказов для мобильного приложения:
Template.xml
<SqlQueryName="AppOrderSelectSqlQuery"> <Text> WITH _filter AS ( SELECT date_trunc('week', CURRENT_DATE::timestamp) AS start_date, date_trunc('week', CURRENT_DATE::timestamp) + interval '7d - 1s' AS end_date ), _order AS ( SELECT O.order_id, O.client_id, C.city_id, O.order_number, O.order_date FROM template.order O LEFT JOIN template.client C USING(client_id) LEFT JOIN _filter ON true WHERE O.added AND NOT O.deleted AND O.order_date BETWEEN convert_date_filter(_filter.start_date) AND convert_date_filter(_filter.end_date) ), _order_position AS ( SELECT O.order_id, SUM(OP.quantity * OP.unit_price) AS total_cost, COUNT(O.order_id) AS position_count FROM _order O JOIN template.order_position OP USING(order_id) LEFT JOIN template.material M USING(material_id) GROUP BY O.order_id ), _payment AS ( SELECT O.order_id, SUM(C.summ) AS total_payment_summ FROM _order O JOIN template.order_payment OP USING(order_id) JOIN template.cash C USING(cash_id) WHERE NOT C.deleted GROUP BY O.order_id ) SELECT O.order_id AS "OrderId", '№ ' || O.order_number AS "OrderNumber", 'от ' || to_char(convert_date_to_user_timezone(O.order_date), 'dd.mm.yyyy') AS "OrderDate", client.title AS "ClientTitle", OP.position_count AS "PositionCount", 'Сумма ' || trim(to_char(COALESCE(OP.total_cost, 0), '999G999G999D00') || ' руб.') AS "TotalOrderCost", (COALESCE(OP.total_cost, 0) - COALESCE(P.total_payment_summ, 0)) <![CDATA[<=]]> 0 AS "IsPaid" FROM _order O LEFT JOIN _order_position OP USING(order_id) LEFT JOIN _payment P USING(order_id) LEFT JOIN template.client USING(client_id) ORDER BY O.order_number ASC; </Text></SqlQuery>
В качестве периода отображения заказов будем использовать текущую неделю - позже реализуем фильтр по датам, как делали на главной форме в приложении для ПК.
Номер и дату заказа (OrderNumber и OrderDate) выводим разными строками, заранее задав им нужный формат. Обратите внимание, что дату заказа сразу приводим к часовой зоне пользователя, чтобы она отображалась корректно. Как вы помните, при передаче даты со временем в формате строки, платформа не делает автоматическое приведение временных зон. И без ручного приведения временной зоны, даты со временем останется во временной зоне сервера. Работа с датами со временем подробно рассматривается в статье Временные зоны.
В запрос добавили поле PositionCount, хранящее количество позиций заказа, и поле IsPaid - признак полной оплаты заказа. Теперь возвращаем имена клиентов вместо идентификаторов, т.к. в отличии от таблицы DatabaseTable список CollectionView не поддерживает Substitution.
Перейдем в файл главной формы (TemplateMainForm.xml).
Самостоятельно создайте соответствующий PrimaryGetDataConnection.
Теперь создадим объект типа CollectionView, который представляет данные в виде списка карточек:
В тэге <CardStyle> настраивается стиль карточки списка. В тэге <BackColor> задали цвет фона карточки, а в тэге <BorderColor> - цвет границ карточки. Оба цвета зависят от выбранной темы оформления системы. Так же можно задать радиус скругления углов карточки.
В тэге <CardTemplate> описываются элементы шаблона карточки списка. К таким элементам относятся текстовые поля, иконки, кнопки и прочее. Далее будем настраивать шаблон карточек.
Шаблон карточки
Добавим на форму описание цветов, которые понадобятся при описании шаблона карточки:
Для начала добавим в шаблон карточки два поля: номер заказа, должен отображаться в верхнем левом углу, и дата заказа, должна отображаться в правом верхнем углу.
Номер заказа - это простое текстовое поле, для него будем использовать элемент типа TemplateItemLabel:
В тэгах <Top> и <Left> задаются абсолютные значения отступов от границ контейнера, с учетом внутренних отступов контейнера для его содержимого.
В тэге <Text> указывается текст, отображаемый в поле на карточке. При этом можно указать текст значением тэга, тогда такой текст будет одинаков на всех карточках. А можно указать атрибут Binding с именем одного из полей источника данных из тэга <SourceDataConnection>, в таком случае значение будет индивидуально для каждой карточки. Значение тэга <Text> и атрибут Binding вместе не работают - предпочтение отдается атрибуту.
В тэгах <FontStyle> и <ForeColor> настраиваем стиль текста, его цвет и размер.
Для дата заказа также будем использовать элемент типа TemplateItemLabel:
Так как поле должно отображаться в правом верхнем углу, то координату <Left> переведем из абсолютного значения в пропорциональное. Для этого используем тэг <AbsoluteLayoutFlags> со значением XProportional. Теперь в тэге <Left> указываем не величину сдвига, а коэффициент, который будет использоваться для расчета координаты по формуле:
Аналогичным образом можно задавать пропорциональное значение для координаты <Top> - YProportional, а так же одновременно для координат <Top> и <Left> - PositionProportional. Пропорции можно задавать для ширины и высоты элемента - WidthProportional и HeightProportional соответственно, а так же одновременно для высоты и ширины - SizeProportional. Если необходимо для всех четырех значений использовать пропорциональные, то следует указывать All.
Запустите приложение и проверьте расположение объектов в карточке списка.
Проверьте. как будет отображаться экран, если в списке заказов не будет записей:
Добавьте несколько заказов в десктопном приложении. Будет хорошо, если хотя бы один будет полностью оплачен.
Под номером и датой заказа добавим поля с именем клиента и суммой заказа:
В этот раз для тэга <AbsoluteLayoutFlags> используем значение PositionProportional, чтобы в тэге <Top> так же указывать пропорциональное значения. Таким образом элемент будет расположен с правого края по середине вертикали.
В качестве условия видимости элемента в атрибуте Binding тэга <Visible> укажем поле IsPaid.
Чтобы статус визуально выделялся, зададим цвет фона текста в тэге <BackColor> и радиус скругления углов фона в тэге <BorderCorner>. Зададим высоту элемента в 25 единиц.
Стоит помнить, что по умолчанию текст всегда выравнивается по верхней части своего блока. Чтобы текст был по центру блока, используем тэг <TextAlignment>, с указанием правил выравнивания для вертикали и горизонтали.
Тэг <Padding> добавит для текста внутренние отступы от границ фона - так блок будет выглядеть симпатичнее. Отступы будут только слева и справа.
Отлично! Запустим приложение, чтобы проверить отображение всех элементов карточки.
Объекты типа Panel поддерживают тэг <RefreshCommand>, в котором описывается последовательность команд, необходимых для обновления данных. Вызывается эта последовательность смахиванием сверху вниз по панели.
Давайте создадим команду на обновления OrderPrimaryGetDataConnection:
В тэге <RefreshCommand> новой панели указали команду OrderDataConnectionRefreshCommand.
Создание панели-контейнера для списка позволит отделить его от элементов фильтров, которые не будут сдвигаться на экране при смахивании списка вниз, и индикатор загрузки будет отображаться поверх списка.
Клик по карточке
Создадим форму, которую будем использовать для редактирования заказа, выбранного в списке. Пока достаточно пустой формы, чтобы проверить переход на новый экран при выборе заказа в списке.
Чтобы получить значение поля из строки источника данных, соответствующей выделенной карточке, будем использовать get-проперти SelectedItemValueByFieldName, которое принимает один параметр FieldName с именем этого поля. Например, получим значение из поля OrderId:
Перейдем в файл TemplateOrderEdit.xml и наполним форму редактирования заказа необходимыми полями и списком позиций заказа. Пока форму оставим только для чтения без возможности вносить изменения и сохранять их на сервер - это сделаем в следующем уроке.
Основная информация
Самостоятельно создайте DataConnection для данных о заказе - OrderPrimaryGetDataConnection.
Добавим стили шрифтов полей с данными о заказе и клиенте и стили для шаблона списка:
Так как список позиций заказа может выходить за границы экрана, то для удобной работы в объект ContentPanel добавим тэг <VerticalScroll> со значением True.
Первым добавим на экран номер заказа, поместив поле с заголовком в панель:
Создадим блок с данными о клиенте. Блок будет представлен объектом Panel и копировать стиль панели OrderNumberPanel, но теперь он будет иметь заголовок в виде объекта Label:
Так как список клиентов может быть очень большим, то использование ComboBox затруднит поиск нужного. Поэтому для выбора клиента будем использовать отдельный экран, на котором список клиентов будет занимать большую часть пространства. В таком случае, для отображения имени клиента в заказе будет достаточно объекта типа Label, а справа от него добавим кнопку для перехода на форму выбора клиента:
Для нее нам понадобится изображение (скачайте его и скопируйте в папку \MobileForms\Images):
Самостоятельно добавьте поля для города, телефона и email клиента. Расстояние между заголовками полей делайте в 10 единиц. В результате должна получиться форма вида:
На скриншоте можете увидеть объекты-заглушки с текстом "Не указан". Так как телефон и email являются необязательными полями в карточке клиента, и чтобы на экране не было пустых мест, будем использовать такие объекты-заглушки. Самостоятельно создайте необходимые объекты и условия отображения основного текста и заглушки. Шрифт у объектов-заглушек будет Label16Font, как и у основных объектов, а цвет текста будет Gray.
Теперь перейдем к созданию блока оплат. Здесь по аналогии с предыдущим блоком создайте заголовок блока PaymentPartTitleLabel и панель PaymentPanel с необходимыми полями:
Статус "Оплачен" будем отображать вместо значения задолженности:
Плашка статуса - это объект Panel со скругленными углами и цветным фоном, внутри которого размещен объект Label:
Создайте DataConnection для списка позиций заказа (OrderPositionPrimaryGetDataConnection), который будет получать данные из SQL-запроса:
Template.xml
<SqlQueryName="AppOrderPositionByOrderIdSelectSqlQuery"> <Text> SELECT OP.order_position_id AS "OrderPositionId", OP.material_id AS "MaterialId", material.title AS "MaterialTitle", material_category.title AS "MaterialCategoryTitle", unit.short_title AS "UnitShortTitle", OP.quantity AS "Quantity", OP.quantity || ' ' || unit.short_title AS "QuantityString", trim(to_char(OP.unit_price, '999G999G999D00')) || ' руб./' || unit.short_title AS "UnitPrice", trim(to_char(OP.unit_price * OP.quantity, '999G999G999D00')) || ' руб.' AS "TotalPrice" FROM template.order_position OP LEFT JOIN template.material USING(material_id) LEFT JOIN template.material_category USING(material_category_id) LEFT JOIN template.unit USING(unit_id) WHERE OP.order_id = {OrderId} ORDER BY OP.order_position_id; </Text></SqlQuery>
В запросе возвращаем два поля с количеством товара: Quantity и QuantityString - первое пригодится в следующем уроке, когда будем реализовывать экран изменения позиций заказа, а второе будем отображать в карточке списка позиций.
Для списка позиций заказа не будем использовать панель - достаточно задать заголовок блока и определить объект CollectionView.
Самостоятельно создайте список позиций заказа с именем OrderPositionCollectionView. Должна получиться форма вида:
Итоги
На уроке мы рассмотрели .
Ответы
В архиве присутствуют xml-файлы форм для мобильного приложения и серверный xml-файл, также лежит бэкап базы данных и файл с запросами на изменение структуры базы данных - с помощью файлов можете проверить себя.
Архив не содержит xml-файлы форм десктопного приложения.