Урок 11. Экспорт данных в документ

В этом уроке мы рассмотрим возможности платформы Workflow Technology по работе с текстовыми документами для выгрузки данных. А также познакомимся с механизмом отправки e-mail и настройкой почтового агента.

Для кнопок экспорта и отправки писем нам понадобятся иконки. Скачайте архив с изображениями и разархивируйте его в папку проекта \Template\Projects\1. Template\Forms\Images\24x24.

Если хотите начать практику с этого урока, то вам необходимо развернуть учебный проект по инструкции в статье Разворачивание проекта.

При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела Ответы прошлого урока. Скопируйте папки Forms, Workflow и Patterns в папку с развернутым проектом, например, в папку D:\WT\Projects\Template\Projects\1. Template.

Инструкция по подключению шаблонов находится по ссылке.

Экспорт в документ

Для экспорта данных в документ нам понадобится шаблон самого документа и команда типа ExportToDocxCommand или ExportToExcelCommand, которая будет подставлять в документ все необходимые данные.

Шаблон документа

Шаблон документа можете скачать по ссылке ниже:

Скачайте и сохраните его в папку \Template\Projects\1. Template\Forms\Templates, предварительно создав ее.

В шаблоне используются переменные двух видов:

  • <#VariableName#> - для отображения скалярных значений. Например, <#OrderNumber#>, <#ClientName#> и другие;

  • <%VariableName%> - для отображения массива (используются только в таблицах). Например, <%OrderPositionTitle%>, <%OrderPositionUnitPrice%> и другие.

При использовании переменных-массивов команда тиражирует строку таблицы столько раз, сколько составляет длина наибольшего массива среди используемых в строке переменных.

Подробнее про используемые переменные и правила экспорта данных в документы можно прочитать в синтаксисе команд ExportToDocxCommand и ExportToExcelCommand.

Команда экспорта

Перейдем в файл TemplateOrderEdit.xml и создадим команду типа ExportToDocxCommand:

TemplateOrderEdit.xml
<Command Name="OrderExportToDocxCommand" Type="ExportToDocxCommand" Assembly="Commands">
  <TemplateFileName>Templates\TemplateOrder.docx</TemplateFileName>
  <Parameters>
    <!-- Скалярные переменные -->
    <Parameter Name="OrderNumber"></Parameter>
    <Parameter Name="OrderDateDay"></Parameter>
    <Parameter Name="OrderDescription"></Parameter>
    
    <Parameter Name="ClientName"></Parameter>
    <Parameter Name="ClientCity"></Parameter>
    <Parameter Name="ClientPhone"></Parameter>
    <Parameter Name="ClientEmail"></Parameter>
    
    <!-- Переменные массивов -->
    <Parameter Name="OrderPositionRowNumber"></Parameter>
    <Parameter Name="OrderPositionTitle"></Parameter>
    <Parameter Name="OrderPositionQuantity"></Parameter>
    <Parameter Name="OrderPositionUnitPrice"></Parameter>
    <Parameter Name="OrderPositionTotalPrice"></Parameter>
    
    <Parameter Name="OrderTotalPrice"></Parameter>
  </Parameters>
</Command>

В тэге <TemplateFileName> указан относительный путь до нашего шаблона.

В тэге <Parameters> перечислены параметры, значения которых будут подставляться в переменные в документе. Имя параметра в команде должно совпадает с именем переменной в документе.

Параметры OrderNumber, OrderDateDay и OrderDescription заполняем на основе соответствующих полей заказа:

<Parameter Name="OrderNumber">
  <Object Name="OrderNumberTextBox" />
</Parameter>
<Parameter Name="OrderDateDay">
  <DataTypeFormat Type="DateTimeDataType" Format="dd.MM.yyyy">
    <Object Name="OrderDateDateTimePicker" />
  </DataTypeFormat>
</Parameter>
<Parameter Name="OrderDescription">
  <Object Name="DescriptionTextBox" />
</Parameter>

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

Для скалярных переменных ClientName, ClientPhone и ClientEmail будем использовать ClientSecondaryGetDataConnection. Добавьте в него недостающее поле Email.

<Parameter Name="ClientName">
  <DataConnection SourceDataConnection="ClientSecondaryGetDataConnection">
    <Fields>
      <Field Name="ClientTitle" />
    </Fields>
  </DataConnection>
</Parameter>
<Parameter Name="ClientCity">
  <DataConnection SourceDataConnection="ClientSecondaryGetDataConnection">
    <Fields>
      <Field Name="CityTitle" />
    </Fields>
  </DataConnection>
</Parameter>
<Parameter Name="ClientPhone">
  <DataConnection SourceDataConnection="ClientSecondaryGetDataConnection">
    <Fields>
      <Field Name="Phone" />
    </Fields>
  </DataConnection>
</Parameter>
<Parameter Name="ClientEmail">
  <DataConnection SourceDataConnection="ClientSecondaryGetDataConnection">
    <Fields>
      <Field Name="Email" />
    </Fields>
  </DataConnection>
</Parameter>

Переменные массивов OrderPositionRowNumber, OrderPositionTitle, OrderPositionQuantity, OrderPositionUnitPrice и OrderPositionTotalPrice будем заполнять на основе значений в соответствующих колонках таблицы OrderPositionDatabaseTable.

<Parameter Name="OrderPositionRowNumber">
  <Object Name="OrderPositionDatabaseTable">
    <Property Name="Column">
      <Parameters>
        <Parameter Name="ColumnName">RowNumber</Parameter>
      </Parameters>
    </Property>
  </Object>
</Parameter>
<Parameter Name="OrderPositionTitle">
  <Object Name="OrderPositionDatabaseTable">
    <Property Name="Column">
      <Parameters>
        <Parameter Name="ColumnName">Title</Parameter>
      </Parameters>
    </Property>
  </Object>
</Parameter>
<Parameter Name="OrderPositionQuantity">
  <DataTypeFormat Type="DecimalDataType" Format="N2">
    <Object Name="OrderPositionDatabaseTable">
      <Property Name="Column">
        <Parameters>
          <Parameter Name="ColumnName">Quantity</Parameter>
        </Parameters>
      </Property>
    </Object>
  </DataTypeFormat>
</Parameter>
<Parameter Name="OrderPositionUnitPrice">
  <DataTypeFormat Type="DecimalDataType" Format="N2">
    <Object Name="OrderPositionDatabaseTable">
      <Property Name="Column">
        <Parameters>
          <Parameter Name="ColumnName">UnitPrice</Parameter>
        </Parameters>
      </Property>
    </Object>
  </DataTypeFormat>
</Parameter>
<Parameter Name="OrderPositionTotalPrice">
  <DataTypeFormat Type="DecimalDataType" Format="N2">
    <Object Name="OrderPositionDatabaseTable">
      <Property Name="Column">
        <Parameters>
          <Parameter Name="ColumnName">TotalPrice</Parameter>
        </Parameters>
      </Property>
    </Object>
  </DataTypeFormat>
</Parameter>

И последний параметр OrderTotalPrice заполним из переменной TotalSumToPayVariable, которую используем для поля "Всего к оплате":

<Parameter Name="OrderTotalPrice">
  <DataTypeFormat Type="DecimalDataType" Format="N2">
    <Object Name="TotalSumToPayVariable" />
  </DataTypeFormat>
</Parameter>

Отлично! У нас есть команда экспорта данных заказа в текстовый документ. Теперь осталось реализовать кнопку, по которой будет происходить выполнение этой команды.

Сохранение данных

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

Переделаем логику сохранения данных и начнем с кнопки "Сохранить".

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

Создадим команду ValueSetCommand, в которой через set-проперти FormChanged будем сбрасывать признак изменения объектов на форме:

TemplateOrderEdit.xml
<Command Name="FormChangedFalseValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Form>
    <Property Name="FormChanged">False</Property>
  </Form>
</Command>

Теперь добавим ее вызов в описание команды SaveSequentialCommand.

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

TemplateOrderEdit.xml
<Command Name="EditTrueValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="Edit">True</Parameter>
</Command>

Итоговый синтаксис команды сохранения заказа будет иметь вид:

TemplateOrderEdit.xml
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="OrderUpdateSaveCommand" />
    <Command Name="UpdatedTrueValueSetCommand" />
    <Command Name="EditTrueValueSetCommand" />
    <Command Name="FormChangedFalseValueSetCommand" />
  </Commands>
</Command>

Теперь мы должны добавить команду FormCloseCommand в Execution, который срабатывает, если пользователь хочет сохранить изменения при закрытии формы по крестику:

TemplateOrderEdit.xml
<Execution>
  <ConditionExpression>
    <Command Name="SaveOnCloseMessageBoxCommand" Parameter="Yes" />
  </ConditionExpression>
  <Commands>
    <Command Name="SaveSequentialCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>

На кнопке SaveButton изменим условие активности, добавив проверку на наличие изменений на форме через условие FormChangedEqualCondition:

TemplateOrderEdit.xml
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="FootPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>5</Item>
      </Minus>
    </Formula>
  </Right>
  <Height>30</Height>
  <Width>200</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\content-save.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Сохранить</Text>
  <TabIndex>1</TabIndex>
  <Enabled>
    <And>
      <Condition Name="MandatoryFieldsAreFilledEqualCondition" />
      <Condition Name="FormChangedEqualCondition" />
    </And>
  </Enabled>
  <Commands>
    <Command Name="SaveSequentialCommand" />
  </Commands>
  <DisabledMode>
    <Condition Name="FormChangedEqualCondition" />
  </DisabledMode>
  <DisabledText>Одно или несколько полей заполнены некорректно.</DisabledText>
</MyObject>

Кнопка печати

Создадим команду вывода сообщения с предупреждением о несохраненных изменениях:

TemplateOrderEdit.xml
<Command Name="SaveOnPrintMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Сохранение</Caption>
  <Text>Форма содержит несохраненные изменения.\rХотите сохранить и продолжить?</Text>
  <Icon Type="Question" />
  <Buttons Type="YesNo" />
</Command>

В FootPanel добавим описание кнопки печати документа:

TemplateOrderEdit.xml
<MyObject Name="PrintButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="SaveButton">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>10</Left>
  <Height>
    <Object Name="SaveButton">
      <Property Name="Height" />
    </Object>
  </Height>
  <Width>
    <Object Name="SaveButton">
      <Property Name="Width" />
    </Object>
  </Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\printer.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Печать</Text>
  <TabIndex>2</TabIndex>
  <Enabled>
    <Condition Name="MandatoryFieldsAreFilledEqualCondition" />
  </Enabled>
  <Commands>
    <If>
      <When>
        <Condition Name="FormChangedEqualCondition" />
      </When>
      <Then>
        <Command Name="SaveOnPrintMessageBoxCommand" />
        <If>
          <When>
            <Command Name="SaveOnPrintMessageBoxCommand" Parameter="Yes" />
          </When>
          <Then>
            <Command Name="SaveSequentialCommand" />
            <Command Name="OrderExportToDocxCommand" />
          </Then>
        </If>
      </Then>
      <Else>
        <Command Name="OrderExportToDocxCommand" />
      </Else>
    </If>
  </Commands>
  <DisabledMode>True</DisabledMode>
  <DisabledText>Одно или несколько полей заполнены некорректно.</DisabledText>
</MyObject>

Запустите приложение и проверьте открытие карточки заказа и печать документа.

Отлично! Теперь можем реализовать отправку этого документа на почту клиенту.

Отправка письма на почту

Настройки

Создадим в базе данных таблицу для хранения настроек почтового агента:

CREATE SEQUENCE template.settings_id_seq;
CREATE TABLE template.settings
(
  settings_id smallint NOT NULL DEFAULT nextval('template.settings_id_seq'::regclass),
  smtp_server character varying,
  smtp_port integer,
  email_address character varying,
  email_password character varying,
  ssl boolean NOT NULL DEFAULT false,  
  CONSTRAINT pk_settings_id PRIMARY KEY (settings_id)
);

Перейдем в файл серверной xml (Template.xml) и добавим запросы на получение и обновление настроек почтового агента:

Template.xml
<SqlQuery Name="SettingsSelectSqlQuery">
  <Text>
    SELECT
      smtp_server AS "SmtpServerAddress",
      smtp_port AS "SmtpServerPort",
      ssl AS "SSL",
      email_address AS "Email",
      email_password AS "EmailPassword"
    FROM template.settings
    LIMIT 1;
  </Text>
</SqlQuery>
  
<SqlQuery Name="SettingsUpdateSqlQuery">
  <Text>
    UPDATE template.settings
    SET
      smtp_server = {SmtpServerAddress},
      smtp_port = {SmtpServerPort},
      ssl = {SSL},
      email_address = {Email},
      email_password = {EmailPassword};
  </Text>
</SqlQuery>

Так как у нас нет запроса на вставку новых строк в таблицу template.settings, то необходимо создать пустую запись, которую будем обновлять через запрос SettingsUpdateSqlQuery.

INSERT INTO template.settings DEFAULT VALUES;

Форма настроек

Перейдем в файл главной формы (TemplateStart.xml), где первым делом в главное меню добавим пункт Администрирование -> Настройки..., по которому будет открываться форма настроек для почтового агента.

Создайте самостоятельно форму настроек (TemplateSettings.xml).

Паттерны EmptyForm и EmptyFormWithFooter не поддерживают паттерн onClose, который мы рассматривали на 4ом уроке. В архиве ниже есть необходимые файлы паттерна onClose, который вы можете применять в редакторе. Скачайте новый архив с шаблонами и замените содержимое папки проекта \Template\Projects\1. Template\Patterns.

У вас должна получиться форма вида:

  • Адрес SMTP-сервера (SmtpServerAddressTextBox) - адрес почтового сервера;

  • Порт SMTP-сервера (SmtpServerPortTextBox) - порт почтового сервера;

  • SSL (SSLCheckBox) - защита соединения;

  • Почтовый ящик (EmailAddressTextBox) - ящик, с которого будет осуществляться отправка писем;

  • Пароль (EmailPasswordTextBox) - пароль для авторизации на почтовом сервере.

Для текстового поля SmtpServerPortTextBox используйте свойство AllowedSymbols, для того чтобы задать ограничения на ввод цифр. Для поля EmailPasswordTextBox используйте свойство Password со значением True для перевода текстового поля в режим ввода пароля.

На рисунке выше показаны параметры для настройки работы приложения с почтовым сервисом Яндекса. Подробнее о настройке сторонних почтовых программ можете прочитать по ссылке.

Команда отправки писем

Вернемся в файл TemplateOrderEdit.xml и создадим соединение с данными для загрузки настроек почтового сервиса:

TemplateOrderEdit.xml
<DataConnection Name="SettingsPrimaryGetDataConnection" Type="PrimaryGetDataConnection" Assembly="DataConnections">
  <SqlQuery Name="SettingsSelectSqlQuery" Type="Select">
    <Workflow Name="Template" />
    <Fields>
      <Field Name="SmtpServerAddress" />
      <Field Name="SmtpServerPort" />
      <Field Name="SSL" />
      <Field Name="Email" />
      <Field Name="EmailPassword" />
    </Fields>
  </SqlQuery>
</DataConnection>

Чтобы отправить документ на электронную почту клиенту, документ нужно сформировать. В нашем случае вызвать команду OrderExportToDocxCommand.

По умолчанию команда ExportToDocxCommand открывает файл в текстовом редакторе после того как его сформирует и заменит переменные. Чтобы файл не открывался, когда вызываем команду для формирования документа для отправки на почту, добавим в описание команды OrderExportToDocxCommand вложенный тэг <Open>:

<Command Name="OrderExportToDocxCommand" Type="ExportToDocxCommand" Assembly="Commands">
  <TemplateFileName>Templates\TemplateOrder.docx</TemplateFileName>
  <Open>
    <Input Name="Open">True</Input>
  </Open>
  <Parameters>
    <!-- Параметры команды -->
  </Parameters>
</Command>

В тэге <Open> укажем конструкцию Input со значением по умолчанию. Это значение будет использоваться, если при вызове команды мы не будем указывать Input для передачи нового значения.

Создадим команду типа EmailSendCommand:

TemplateOrderEdit.xml
<Command Name="OrderEmailSendCommand" Type="EmailSendCommand" Assembly="Commands">
  <!-- Настройки почтового агента -->
  <AuthorSmtpServerAddress>
    <DataConnection SourceDataConnection="SettingsPrimaryGetDataConnection">
      <Fields>
        <Field Name="SmtpServerAddress" />
      </Fields>
    </DataConnection>
  </AuthorSmtpServerAddress>
  <AuthorSmtpServerPort>
    <DataConnection SourceDataConnection="SettingsPrimaryGetDataConnection">
      <Fields>
        <Field Name="SmtpServerPort" />
      </Fields>
    </DataConnection>
  </AuthorSmtpServerPort>
  <EnableSSL>
    <DataConnection SourceDataConnection="SettingsPrimaryGetDataConnection">
      <Fields>
        <Field Name="SSL" />
      </Fields>
    </DataConnection>
  </EnableSSL>
  <AuthorEmailAddress>
    <DataConnection SourceDataConnection="SettingsPrimaryGetDataConnection">
      <Fields>
        <Field Name="Email" />
      </Fields>
    </DataConnection>
  </AuthorEmailAddress>
  <AuthorEmailPassword>
    <DataConnection SourceDataConnection="SettingsPrimaryGetDataConnection">
      <Fields>
        <Field Name="EmailPassword" />
      </Fields>
    </DataConnection>
  </AuthorEmailPassword>

  <!-- Дополнительные настройки -->
  <Timeout>20000</Timeout>

  <!-- Параметры письма -->
  <Subject>
    <String>
      <Format>Счет заказа №{0} от {1}</Format>
      <Items>
        <Item>
          <Object Name="OrderNumberTextBox" />
        </Item>
        <Item>
          <DataTypeFormat Type="DateTimeDataType" Format="dd.MM.yyyy">
            <Object Name="OrderDateDateTimePicker" />
          </DataTypeFormat>
        </Item>
      </Items>
    </String>
  </Subject>
  <Text>
    <String>
      <Format>По заказу №{0} от {1} сформирован счет.</Format>
      <Items>
        <Item>
          <Object Name="OrderNumberTextBox" />
        </Item>
        <Item>
          <DataTypeFormat Type="DateTimeDataType" Format="dd.MM.yyyy">
            <Object Name="OrderDateDateTimePicker" />
          </DataTypeFormat>
        </Item>
      </Items>
    </String>
  </Text>
  <Addressees>
    <DataConnection SourceDataConnection="ClientSecondaryGetDataConnection">
      <Fields>
        <Field Name="Email" />
      </Fields>
    </DataConnection>
  </Addressees>
  <Files>
    <Command Name="OrderExportToDocxCommand" />
  </Files>
</Command>

Команда EmailSendCommand формирует письмо с заголовком из тэга <Subject> и текстом из тэга <Text>, прикрепляет к письму файлы из тэга <Files> и рассылает копию письма на все адреса, переданные в тэг <Addressees>.

Результатом выполнения команды ExportToDocxCommand будет полный путь с названием файла, который сформирован из указанного шаблона. Этот результат мы передаем в команду EmailSendCommand.

Если письмо успешно отправлено, то результатом команды будет значение 0. Создадим условие для проверки результата команды, которое будем использовать, чтобы уведомлять пользователя об успешности отправки:

TemplateOrderEdit.xml
<Condition Name="OrderEmailSentSuccessfullyEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Command Name="OrderEmailSendCommand" />
    </Item>
    <Item>0</Item>
  </Items>
  <DataType Type="IntegerDataType" />
</Condition>

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

TemplateOrderEdit.xml
<Command Name="EmailSendSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="OrderExportToDocxCommand">
      <Input Name="Open">False</Input>
    </Command>
    <Command Name="OrderEmailSendCommand" />
    <If>
      <When>
        <Condition Name="OrderEmailSentSuccessfullyEqualCondition" />
      </When>
      <Then>
        <!-- Письмо успешно отправлено -->
        <Command Name="OrderEmailSentSuccessfullyMessageBoxCommand" />
      </Then>
    </If>
  </Commands>
</Command>

Команда OrderEmailSentSuccessfullyMessageBoxCommand - команда типа MessageBoxCommand, которая будет выводить пользователю сообщение о результате выполнения команды отправки письма. Создайте ее самостоятельно.

Добавим в FootPanel кнопку, по которой будем отправлять письма:

TemplateOrderEdit.xml
<MyObject Name="EmailSendButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="SaveButton">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="PrintButton">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Height>
    <Object Name="SaveButton">
      <Property Name="Height" />
    </Object>
  </Height>
  <Width>
    <Object Name="SaveButton">
      <Property Name="Width" />
    </Object>
  </Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\email-send.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Отправить клиенту</Text>
  <TabIndex>3</TabIndex>
  <Enabled>
    <Condition Name="MandatoryFieldsAreFilledEqualCondition" />
  </Enabled>
  <Commands>
    <If>
      <When>
        <Condition Name="FormChangedEqualCondition" />
      </When>
      <Then>
        <Command Name="SaveOnPrintMessageBoxCommand" />
        <If>
          <When>
            <Command Name="SaveOnPrintMessageBoxCommand" Parameter="Yes" />
          </When>
          <Then>
            <Command Name="SaveSequentialCommand" />
            <Command Name="EmailSendSequentialCommand" />
          </Then>
        </If>
      </Then>
      <Else>
        <Command Name="EmailSendSequentialCommand" />
      </Else>
    </If>
  </Commands>
  <DisabledMode>True</DisabledMode>
  <DisabledText>Одно или несколько полей заполнены некорректно.</DisabledText>
</MyObject>

Запустите приложение и проверьте работу команд на формирование документа и отправку письма на почту клиента.

Группы обязательных полей

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

Следовательно, в зависимости от действия будем проверять разные данные. Для этого в платформе реализована возможность разбиения проверок на группы. Тэг <Checking> имеет вложенный тэг <Group>, с мощью которого осуществляется разбиение.

Назначим Checking для объектов OrderNumberTextBox и ClientComboBox группу Saving:

TemplateOrderEdit.xml
<Checking>
  <Object Name="OrderNumberTextBox" />
  <Group Name="Saving" />
  <ConditionExpression>
    <Condition Name="OrderNumberTextBoxIsNullOrEmptyCondition" />
  </ConditionExpression>
  <AsteriskHint>Пожалуйста, заполните это поле</AsteriskHint>
</Checking>

<Checking>
  <Object Name="ClientComboBox" />
  <Group Name="Saving" />
  <ConditionExpression>
    <Condition Name="ClientComboBoxIsNullOrEmptyCondition" />
  </ConditionExpression>
  <AsteriskHint>Пожалуйста, заполните это поле</AsteriskHint>
</Checking>

И создадим новый Checking для таблицы OrderPositionDatabaseTable, который назначим в группу Printing:

TemplateOrderEdit.xml
<Checking>
  <Object Name="OrderPositionDatabaseTable" />
  <Group Name="Printing" />
  <ConditionExpression>
    <And>
      <Condition Name="OrderPositionDatabaseTableIsEmptyEqualCondition" />
      <Object Name="ShowAsteriskHintVariable" />
    </And>
  </ConditionExpression>
  <AsteriskHint>Пожалуйста, заполните таблицу</AsteriskHint>
</Checking>

В условии OrderPositionDatabaseTableIsEmptyEqualCondition проверяется значение get-проперти RowsCount на равенство нулю.

Переменная ShowAsteriskHintVariable необходима для того, чтобы подсветка ошибки заполнения объекта происходила в конкретный момент, а не сразу после открытия формы.

TemplateOrderEdit.xml
<MyObject Name="ShowAsteriskHintVariable" Type="Variable" Assembly="SimpleControls" ChangeForm="False">
  <Value>False</Value>
</MyObject>

Создадим команду для изменения значения переменной:

TemplateOrderEdit.xml
<Command Name="ShowAsteriskHintValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="ShowAsteriskHintVariable">True</Object>
</Command>

Добавим команду ShowAsteriskHintValueSetCommand в начало последовательности команд кнопок PrintButton и EmailSendButton.

Чтобы проверить, сработал ли хотя бы один Checking какой-либо группы, необходимо в get-проперти CheckingFired передать имя нужной группы. Создадим соответствующие условия проверки:

TemplateOrderEdit.xml
<Condition Name="MandatoryFieldsAllowedSavingNestedCondition" Type="NestedCondition" Assembly="Conditions">
  <ConditionExpression>
    <Not>
      <Form>
        <Property Name="CheckingFired">Saving</Property>
      </Form>
    </Not>
  </ConditionExpression>
</Condition>

<Condition Name="MandatoryFieldsAllowedPrintingNestedCondition" Type="NestedCondition" Assembly="Conditions">
  <ConditionExpression>
    <Not>
      <Form>
        <Property Name="CheckingFired">Printing</Property>
      </Form>
    </Not>
  </ConditionExpression>
</Condition>

Условие MandatoryFieldsAreFilledEqualCondition удалим. В Execution на сохранение изменений при закрытии формы и на кнопке SaveButton будем проверять условие для группы Saving - MandatoryFieldsAllowedSavingNestedCondition. А на кнопках PrintButton и EmailSendButton будем проверять оба условия.

При этом проверку этих условий перенесем из тэга <Enabled> в последовательность команд. Таким образом, синтаксис кнопки PrintButton будет иметь вид:

TemplateOrderEdit.xml
<MyObject Name="PrintButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="SaveButton">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>10</Left>
  <Height>
    <Object Name="SaveButton">
      <Property Name="Height" />
    </Object>
  </Height>
  <Width>
    <Object Name="SaveButton">
      <Property Name="Width" />
    </Object>
  </Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\printer.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Печать</Text>
  <TabIndex>2</TabIndex>
  <Commands>
    <Command Name="ShowAsteriskHintValueSetCommand" />
    <If>
      <When>
        <And>
          <Condition Name="MandatoryFieldsAllowedSavingNestedCondition" />
          <Condition Name="MandatoryFieldsAllowedPrintingNestedCondition" />
        </And>
      </When>
      <Then>
        <If>
          <When>
            <Condition Name="FormChangedEqualCondition" />
          </When>
          <Then>
            <Command Name="SaveOnPrintMessageBoxCommand" />
            <If>
              <When>
                <Command Name="SaveOnPrintMessageBoxCommand" Parameter="Yes" />
              </When>
              <Then>
                <Command Name="SaveSequentialCommand" />
                <Command Name="OrderExportToDocxCommand" />
              </Then>
            </If>
          </Then>
          <Else>
            <Command Name="OrderExportToDocxCommand" />
          </Else>
        </If>
      </Then>
      <Else>
        <Command Name="PrintingMessageBoxCommand">Для печати счета необходимо заполнить обязательные поля</Command>
      </Else>
    </If>
  </Commands>
</MyObject>

Где команда PrintingMessageBoxCommand имеет вид:

TemplateOrderEdit.xml
<Command Name="PrintingMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Внимание!</Caption>
  <Text>
    <Input />
  </Text>
  <Icon Type="Information" />
  <Buttons Type="Ok" />
</Command>

Аналогичным образом измените описание кнопки EmailSendButton, скорректировав текст сообщения для команды PrintingMessageBoxCommand.

Откройте карточку заказа для создания нового заказа и убедитесь, что подсвечиваются только поля "Клиент" и "Номер заказа":

Заполните одно или оба обязательных поля и проверьте логику работы кнопки "Печать":

Самостоятельно

У нас на форме не реализованы проверки на наличие электронной почты в карточке клиента, а также нет проверки на полноту и корректность настроек почтового сервиса.

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

Итоги

В этом уроке мы познакомились с командой ExportToDocxCommand, которая позволяет передавать в текстовый документ данные с формы, используя заранее подготовленный шаблон документа.

Также создали форму для настроек почтового агента и познакомились с командой EmailSendCommand для отправки e-mail на почту клиентов.

На следующем уроке реализуем кассовые операции, добавим отчет по бюджету и выгрузку его в Excel файл.

Ответы

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

Last updated