НаСервере НаКлиенте, директивы компиляции и инструкции препроцессору в 1С 8.x

Инструкции препроцессору

Синтаксис инструкций препроцессору

   #Если <Логическое выражение> Тогда
   #ИначеЕсли <Логическое выражение> Тогда
   …
   #Иначе
   #КонецЕсли
   где: 
   <Логическое выражение> = [НЕ] <Символ препроцессора> [<Булева операция> [НЕ] <Символ препроцессора> [<Булева операция> [НЕ] <Символ препроцессора>]…]
   <Символ препроцессора> = {НаКлиенте | НаСервере | ТолстыйКлиентОбычноеПриложение | ТолстыйКлиентУправляемоеПриложение | Клиент | Сервер | ВнешнееСоединение }
   <Булева операция> = {И | ИЛИ}

Процесс компиляции и исполнения программного кода проходит в 1С в несколько стадий. Препроцессор вырезает все "лишнее" из процедуры (для данного контекста), и передает на компиляцию, компилятор производит компиляцию в машинный код, интерпретатор получает машинный код и исполняет его. Некоторые куски кода могут быть намерено объявлены программистом как "лишние" для сервера. Таким образом один и тот же модуль может быть выполнен в двух разных вариантах, например при исполнении на клиенте он будет содержать блок интерактивного взаимодействия с пользователем, а при исполнении на сервере нет, в этом нет никакого смысла, более того модуль может быть завершен с ошибкой исполнения из-за недоступности интерактивного метода на сервере.

  • Препроцессор — специальная программа, которая перерабатывает программный код из «вида» удобного для работы программиста, в «вид», удобный для работы копилятора;
  • Компилятор — специальная программа, которая умеет перерабатывать программный код в «машинный» код — выполняемый непосредственно процессором компьютера;
  • Интерпретатор — специальная программа, которая вместо компилирования кода в машинный код для процессора, выполняет его самостоятельно. Интерпретатор с предварительной компиляцией — компилирует программу не в машинный код, а в специальный «байт-код» удобный для последующего выполнения интерпретатором.

О том как код написанный программистом превращается в байт код интересно описано в статьте на хабре https://habr.com/ru/post/489392/

Рассмотрим конкретный пример. Например у нас запущена конфигурация в режиме толстого клиента (без управляемых форм). В модуле объекта мы сделали предупреждение о нехватке остатка на складе (не стоит так делать в реальных базах).

Как видим на рисунке все работает и выполняется. Но проблема в том, что если данный документ будет проводится какой-то регламентной работой, которая будет выполняться на сервере 1С:Предприятие, то модуль просто не будет скомпилирован. Создадим новый общий модуль и выставим у него "Сервер" и "Вызов сервера":

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

Процедура СоздатьИПровестиДокумент() Экспорт 
	ДокОбъект = Документы.РеализацияТоваров.СоздатьДокумент();
	ДокОбъект.Дата = ТекущаяДата();
	НоваяСтрока = ДокОбъект.Товары.Добавить();
	НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Сапоги");
	НоваяСтрока.Количество = 30;
	НоваяСтрока.Цена = 1;
	НоваяСтрока.Сумма = 30;
	
	ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);	
КонецПроцедуры

Затем в какой нибудь обработке вызовем эту экспортную процедуру, в файловом режиме ошибки не будет и все выполняется как обычно (следствие того что сервер эмулируется платформой, в этом же приложении), но как только мы запустим базу в клиент-серверном варианте, то увидим следующее

В контексте серверного модуля прекрасно создается документ, но метод предупреждение не работает. Идем в справку для метода "Предупреждение" (ctrl-F1), нас интересует в справке раздел "доступность". В разделе доступность нет слова "Сервер", значит этот метод в серверных модулях не может быть использован, модуль попросту не скомпилируется.

Как поступить если необходимо часть модуля выполнять только там где он доступен, а остальной код трогать не хочется? Надо использовать инструкции препроцессору, т.е. объяснить программе что кусочек кода должен присутствовать\исполняться только тогда когда он может быть выполнен.

Переделаем наш пример с инструкцией препроцессору:

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	Для каждого СтрокаТовары Из Товары Цикл
		Остаток = ПолучитьОстаток(СтрокаТовары.Номенклатура);		
		Если Остаток < СтрокаТовары.Количество Тогда
			//после # идет инструкция, код внутри Если...КонецЕсли не будет включен в модуль в случае серверного выполнения
			#Если Клиент Тогда
				Предупреждение("Нет остатка");			
			#КонецЕсли			
		КонецЕсли;
	КонецЦикла;		
КонецПроцедуры

Пример использования в типовой конфигурации (см.ниже), кусок выдернут из общего модуля, у модуля установлены флажки "сервер" и "клиент", т.е. модуль одновременно будет скомпилирован и в клиентском приложении и сервером 1с. Вопрос же задается только если шапка нового документа заполняется "на клиенте", или по-другому кусок кода будет присутствовать в модуле, только в случае когда этот же общий модуль будет воспроизводиться на клиенте.

Процедура ЗаполнитьШапкуДокумента(ДокументОбъект, ТекПользователь, ВалютаРегламентированногоУчета = Неопределено, ТипОперации = "", ПараметрОбъектКопирования = Неопределено, ПараметрОснование = неопределено, СтруктураПараметровДляПолученияДоговора = Неопределено) Экспорт

	МетаданныеДокумента = ДокументОбъект.Метаданные();

	Если ОбщегоНазначения.ЕстьРеквизитДокумента("Ответственный", МетаданныеДокумента) Тогда
		ДокументОбъект.Ответственный = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(ТекПользователь, "ОсновнойОтветственный");
	КонецЕсли;

	#Если Клиент Тогда
	Если ПараметрОбъектКопирования <> Неопределено 
	   И ОбщегоНазначения.ЕстьРеквизитДокумента("КурсВзаиморасчетов", МетаданныеДокумента) Тогда
	    СтруктураКурсаДокумента = МодульВалютногоУчета.ПолучитьКурсВалюты(ДокументОбъект.ВалютаДокумента, ДокументОбъект.Дата);
		Если СтруктураКурсаДокумента.Курс <> ДокументОбъект.КурсВзаиморасчетов 
		 Или СтруктураКурсаДокумента.Кратность <> ДокументОбъект.КратностьВзаиморасчетов Тогда
			Ответ = Вопрос("Курс валюты изменился. Поменять курс в документе?", РежимДиалогаВопрос.ДаНет);
			Если Ответ = КодВозвратаДиалога.Да Тогда
				ДокументОбъект.КурсВзаиморасчетов = СтруктураКурсаДокумента.Курс;
				Если ОбщегоНазначения.ЕстьРеквизитДокумента("КратностьВзаиморасчетов", МетаданныеДокумента) Тогда
					ДокументОбъект.КратностьВзаиморасчетов = СтруктураКурсаДокумента.Кратность;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	#КонецЕсли

Инструкции могут включать целиком процедуру или несколько процедур

Используемые термы (ключевые слова):

   #Если (#If)
   #Тогда (#Then)
   #ИначеЕсли (#ElsIf)
   #Иначе (#Else)
   #КонецЕсли (#EndIf)
   #Область (#Region)
   #КонецОбласти (#EndRegion)
   #Вставка (#Insert)
   #КонецВставки (#EndInsert)
   #Удаление (#Delete)
   #КонецУдаления (#EndDelete)

   Клиент (Client)
   НаКлиенте (AtClient)
   НаСервере (AtServer)
   МобильноеПриложениеКлиент (MobileAppClient)
   МобильноеПриложениеСервер (MobileAppServer)
   МобильныйКлиент (MobileClient)
   ТолстыйКлиентОбычноеПриложение(ThickClientOrdinaryApplication)
   ТолстыйКлиентУправляемоеПриложение (ThickClientManagedApplication)
   Сервер (Server)
   ВнешнееСоединение (ExternalConnection)
   ТонкийКлиент (ThinClient)
   ВебКлиент (WebClient)   
   И (AND)
   ИЛИ  (OR) 
   НЕ (NOT)

 

Директивы комплиляции

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

  • &НаКлиенте - определяет клиентскую процедуру (функцию).
  • &НаСервере - определяет серверную процедуру (функцию) с контекстом.
  • &НаСервереБезКонтекста - определяет серверную процедуру (функцию) без контекста.
  • &НаКлиентеНаСервереБезКонтекста - определяет процедуру (функцию), исполняемую в модуле формы на клиенте и на сервере.
  • &НаКлиентеНаСервере - определяет процедуру (функцию), исполняемую в модуле команды, выполняемую на клиенте и на сервере.
    Без контекста - это означает без доступа к данным формы.

Директивы &НаКлиенте и подобные принципиально отличаются от инструкций препроцессору. Инструкция, как правило, просто вырезает кусок кода перед тем как он будет передан на компиляцию, а с директивой программа точно знает что такая функция все же есть, но будет выполнена на стороне сервера\клиента.

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

 

Совместное использование инструкций и директив

Ошибки использования директив компилятору

Излишне частое и неоправданное выполнение контекстных вызовов &НаСервере

В любом деле, а особенно в таком интеллектуальном как программирование, надо шевелить извилинами. Не стоит бездумно использовать директивы &НаСервере, если есть возможность использования &НаСервереБезКонтекста. Визуально может показаться что работают они одинаково хорошо, но между ними большая разница.

Процесс исполнения серверного вызова выглядит примерно следующим образом: форма упаковывается в контейнер (!), переправляется по каналу tcp\ip на сервер, распаковывается сервером, выполняется код целевой процедуры, форма запаковывается обратно в контейнер, передается на клиент, распаковывается клиентом, отображается на экране.

Сразу становится понятным что такие вещи стоит делать как можно реже, и уж точно не в цикле! В случае с "..БезКонтекста" платформа выполняет код "налегке", т.е. на сервер передаются только параметры функции, а обратно прилетает только результат выполнения.

Использование &НаСервере вместо &НаКлиенте

Иногда программист помещает весь код в процедуру "на сервере" хотя код может быть выполнен на клиенте. Если ведется работа с простыми типами (доступными на клиенте), намного лучше весь код выполнить на клиенте. Если необходимо что-то получить с сервера, создать дополнительную функцию "на сервере без контекста" для получения данных, которые невозможно получить в текущей клиентской функции.

Добавить комментарий