> For the complete documentation index, see [llms.txt](https://wfsys.gitbook.io/wt-practice/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wfsys.gitbook.io/wt-practice/main/lesson_export_to_document.md).

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

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

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

{% file src="/files/jJgDk775zRwUEwlg0Xec" %}
Иконки для кнопок
{% endfile %}

{% hint style="success" %}
Если хотите начать практику с этого урока, то вам необходимо развернуть учебный проект по инструкции в статье [Разворачивание проекта](https://wfsys.gitbook.io/workflow-technology/setting-up-dev-environment/manual-deployment-project).

При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела [Ответы](/wt-practice/main/lesson_pattern_add-edit.md#answer) прошлого урока. Скопируйте папки *Forms*, *Workflow* и *Patterns* в папку с развернутым проектом, например, в папку *D:\WT\Projects\Template\Projects\1. Template*.

Инструкция по подключению шаблонов находится по [ссылке](/wt-practice/main/lesson_list_form.md#podklyuchenie-shablonov-k-proektu).
{% endhint %}

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

Для экспорта данных в документ нам понадобится шаблон самого документа и команда типа [ExportToDocxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_docx_command) или [ExportToExcelCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_excel_command), которая будет подставлять в документ все необходимые данные.

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

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

{% file src="/files/PutgAS5s4ShANy3QWyEu" %}
Шаблон счета заказа
{% endfile %}

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

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

* `<#VariableName#>` - для отображения скалярных значений. Например, `<#OrderNumber#>`, `<#ClientName#>` и другие;
* `<%VariableName%>` - для отображения массива (используются только в таблицах). Например, `<%OrderPositionTitle%>`, `<%OrderPositionUnitPrice%>` и другие.

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

{% hint style="info" %}
Подробнее про используемые переменные и правила экспорта данных в документы можно прочитать в синтаксисе команд [ExportToDocxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_docx_command) и [ExportToExcelCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_excel_command).
{% endhint %}

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

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

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

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

```xml
<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.

```xml
<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**.

```xml
<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**, которую используем для поля "Всего к оплате":

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

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

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

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

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

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

Создадим команду [ValueSetCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/value_set_command), в которой через set-проперти [FormChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#set_form_changed) будем сбрасывать признак изменения объектов на форме:

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FWFioxDwJqO4E8T4xdClE%2Fimage.png?alt=media&amp;token=12b0eecc-fbb3-4dbc-982d-b0ffe1f5fbfb" alt=""><figcaption></figcaption></figure>

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

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

### Настройки

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

```sql
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) и добавим запросы на получение и обновление настроек почтового агента:

{% code title="Template.xml" %}

```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>
```

{% endcode %}

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

```sql
INSERT INTO template.settings DEFAULT VALUES;
```

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

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2Fb80u4W6Krb8ljAJMSO4N%2F01.png?alt=media&amp;token=cadc7eef-2c72-4e29-ace0-91d6996983e1" alt=""><figcaption></figcaption></figure>

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

Паттерны **EmptyForm** и **EmptyFormWithFooter** не поддерживают паттерн **onClose**, который мы рассматривали на [4ом уроке](https://wfsys.gitbook.io/wt-practice/main/lesson_4#pattern-onclose). В архиве ниже есть необходимые файлы паттерна onClose, который вы можете применять в редакторе. Скачайте новый архив с шаблонами и замените содержимое папки проекта **\Template\Projects\1. Template\Patterns**.

{% file src="/files/gbGYeRYqFJQArM3MjdFu" %}

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FFoobf0MYz7UCeeHviWQT%2Fimage.png?alt=media&amp;token=9baf47e5-7365-42d6-84b3-8f2f5a3fa922" alt=""><figcaption></figcaption></figure>

* Адрес SMTP-сервера (SmtpServerAddressTextBox) - адрес почтового сервера;
* Порт SMTP-сервера (SmtpServerPortTextBox) - порт почтового сервера;
* SSL (SSLCheckBox) - защита соединения;
* Почтовый ящик (EmailAddressTextBox) - ящик, с которого будет осуществляться отправка писем;
* Пароль (EmailPasswordTextBox) - пароль для авторизации на почтовом сервере.

Для текстового поля **SmtpServerPortTextBox** используйте свойство [AllowedSymbols](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/textbox#allowed_symbols), для того чтобы задать ограничения на ввод цифр. Для поля **EmailPasswordTextBox** используйте свойство [Password](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/textbox#password) со значением True для перевода текстового поля в режим ввода пароля.

{% hint style="info" %}
На рисунке выше показаны параметры для настройки работы приложения с почтовым сервисом Яндекса. Подробнее о настройке сторонних почтовых программ можете прочитать по [ссылке](https://yandex.ru/support/mail/mail-clients/others.html).
{% endhint %}

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

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

По умолчанию команда [ExportToDocxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_docx_command) открывает файл в текстовом редакторе после того как его сформирует и заменит переменные. Чтобы файл не открывался, когда вызываем команду для формирования документа для отправки на почту, добавим в описание команды OrderExportToDocxCommand вложенный тэг [`<Open>`](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_docx_command#open):

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

В  тэге `<Open>` укажем конструкцию [Input](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/values/input) со значением по умолчанию. Это значение будет использоваться, если при вызове команды мы *не будем указывать* Input для передачи нового значения.

Создадим команду типа [EmailSendCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/email_send_command):

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

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

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

Для отправки письма на почту создадим команду типа [SequentialCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/sequential_command), в которой будем последовательно вызывать команду на формирование документа и отправку его на почту:

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

Команда OrderEmailSentSuccessfullyMessageBoxCommand - команда типа [MessageBoxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/message_box_command), которая будет выводить пользователю сообщение о результате выполнения команды отправки письма. Создайте ее самостоятельно.

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FC2XDdCu1W5bp7fjf1MoM%2Fimage.png?alt=media&amp;token=c3345e40-a9d1-446c-a223-dfe7bd1020d0" alt=""><figcaption></figcaption></figure>

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

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

Следовательно, в зависимости от действия будем проверять разные данные. Для этого в платформе реализована возможность разбиения проверок на группы. Тэг `<Checking>` имеет вложенный тэг [`<Group>`](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/checkings#group), с мощью которого осуществляется разбиение.&#x20;

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

В условии OrderPositionDatabaseTableIsEmptyEqualCondition проверяется значение get-проперти [RowsCount](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#get_rows_count) на равенство нулю.

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

Чтобы проверить, сработал ли хотя бы один Checking какой-либо группы, необходимо в get-проперти [CheckingFired](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_checking_fired) передать имя нужной группы. Создадим соответствующие условия проверки:

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

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

{% code title="TemplateOrderEdit.xml" %}

```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>
```

{% endcode %}

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

{% code title="TemplateOrderEdit.xml" %}

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

{% endcode %}

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

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2F5Prp59mrDmqNduMIv0Gh%2Fimage.png?alt=media&amp;token=690f8d42-ba24-4688-b006-06cddfd53cab" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FkWneVZmsJit3s0kJDnPi%2F02.png?alt=media&amp;token=d5f5b264-fd62-4b81-97a9-5475245d7c5e" alt=""><figcaption></figcaption></figure>

## Самостоятельно <a href="#self-work" id="self-work"></a>

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

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

## Итоги <a href="#results" id="results"></a>

В этом уроке мы познакомились с командой [ExportToDocxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/export_to_docx_command), которая позволяет передавать в текстовый документ данные с формы, используя заранее подготовленный шаблон документа.

Также создали форму для настроек почтового агента и познакомились с командой [EmailSendCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/email_send_command) для отправки e-mail на почту клиентов.

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

## Ответы <a href="#answer" id="answer"></a>

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

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-type="content-ref"></th></tr></thead><tbody><tr><td>lesson11-answer.zip</td><td><a href="https://wfsys.ru/download/wt_practice_desktop_answers/lesson11-answer.zip">https://wfsys.ru/download/wt_practice_desktop_answers/lesson11-answer.zip</a></td></tr></tbody></table>
