Модули в 1С 8.3: виды, контекст выполнения и код

· 9 мин

Содержание

Модуль в 1С — это текстовый файл с кодом на встроенном языке, привязанный к конкретному объекту конфигурации или к приложению целиком. Код попадает в нужный модуль не произвольно: от того, где лежит процедура, зависит, на клиенте она выполнится или на сервере, увидит ли реквизиты объекта и можно ли вызвать её из другого места. Писать модуль «как получится» — верный способ получить ошибку компиляции или метод, который работает в отладчике и падает у пользователя.

Разберём на 1С 8.3 (платформа «Такси», управляемые формы), где какой код размещать, чем общий модуль отличается от модуля объекта, какие директивы компиляции ставить перед процедурами и как потом всё это отладить.

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

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

  • &НаКлиенте — код работает на стороне пользователя. Отсюда недоступны запросы к базе и чтение данных напрямую.
  • &НаСервере — код выполняется на сервере, видит реквизиты формы и может обращаться к базе. Между клиентом и сервером передаётся контекст формы, это дорого.
  • &НаСервереБезКонтекста — самый быстрый серверный вызов: контекст формы не передаётся, метод получает только свои параметры. Внутри нельзя обращаться к реквизитам формы напрямую.
  • &НаКлиентеНаСервереБезКонтекста — один и тот же код доступен и клиенту, и серверу без контекста. Удобно для вспомогательных расчётов.
  • &НаКлиентеНаСервере — только в модуле команды и общих модулях с определённым набором флагов.

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

Запрос к базе данных на клиенте выполнить нельзя — это всегда приводит к ошибке компиляции. Если видите текст вроде Обращение к методу объекта не обнаружено (Выполнить) при работе с запросом, первым делом проверьте директиву перед процедурой.

Есть и обратное ограничение: при вызове сервера с клиента параметры и возвращаемое значение должны быть сериализуемыми. Числа, строки, даты, булево, ссылки, структуры и массивы из таких же значений — передаются. А вот ТаблицаЗначений, объект Запрос, выборку или открытый файл через границу клиент-сервер не протащить — платформа выдаст ошибку сериализации. Поэтому выборку запроса разбирают на сервере и возвращают на клиент уже в виде структуры или массива структур.

Общие модули: где живёт переиспользуемый код

Общий модуль — контейнер для процедур и функций, которые не привязаны к одному объекту и вызываются из разных мест. Это основное место для бизнес-логики. Создаётся в дереве конфигурации: ветка Общие → Общие модули, команда добавления нового элемента.

Поведение модуля задают флаги в его свойствах:

ФлагЧто даёт
Клиент (управляемое приложение)методы доступны в клиентском контексте
Серверметоды доступны на сервере
Вызов сервераклиент может вызвать серверную функцию модуля
Внешнее соединениемодуль работает через COM-соединение
Привилегированныйкод выполняется без контроля прав доступа
Повторное использование возвращаемых значенийрезультат функции кэшируется

Чтобы метод можно было вызвать снаружи, после его имени ставят ключевое слово Экспорт:

// Общий модуль "РаботаСКонтрагентами", флаг "Сервер" + "Вызов сервера"
Функция ДанныеКонтрагента(Контрагент) Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	Контрагенты.Наименование КАК Наименование,
		|	Контрагенты.ИНН КАК ИНН
		|ИЗ
		|	Справочник.Контрагенты КАК Контрагенты
		|ГДЕ
		|	Контрагенты.Ссылка = &Контрагент";
	Запрос.УстановитьПараметр("Контрагент", Контрагент);

	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка;
	КонецЕсли;

	Возврат Неопределено;

КонецФункции

Вызов такого метода из любого места: РаботаСКонтрагентами.ДанныеКонтрагента(Ссылка).

Флаг «Привилегированный» отключает проверку прав доступа ко всем данным внутри модуля. Ставьте его только там, где это осознанно нужно (служебные операции), иначе пользователь увидит то, что ему видеть не положено.

Флаг «Повторное использование возвращаемых значений» (на время вызова или на время сеанса) полезен для функций, которые при одинаковых параметрах всегда возвращают одно и то же — например, получение настройки или константы. Платформа запомнит результат и не будет гонять запрос повторно. Для функций, результат которых меняется, этот флаг — источник плавающих ошибок.

Модуль объекта: обработчики жизненного цикла записи

У справочников, документов, планов видов характеристик и других прикладных объектов есть собственный модуль — модуль объекта. Он открывается из окна редактирования объекта конфигурации по ссылке на модуль объекта. Здесь размещают события, которые платформа вызывает сама на разных этапах работы с записью.

Ключевые обработчики справочника и документа:

  1. ОбработкаЗаполнения — вызывается при создании нового элемента, заполняет значения по умолчанию.
  2. ОбработкаПроверкиЗаполнения — проверяет, заполнены ли обязательные поля перед записью.
  3. ПередЗаписью — последний момент поправить данные до сохранения; здесь же можно отменить запись через параметр Отказ.
  4. ПриЗаписи — объект уже пишется в базу, в этой же транзакции.
  5. ПередУдалением — контроль перед удалением.

Для документов добавляется главный обработчик — ОбработкаПроведения. Именно в нём формируются движения по регистрам:

// Модуль объекта документа
Процедура ОбработкаПроведения(Отказ, РежимПроведения)

	Движения.ОстаткиТоваров.Записывать = Истина;

	Для Каждого СтрокаТовары Из Товары Цикл
		Движение = Движения.ОстаткиТоваров.Добавить();
		Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
		Движение.Период = Дата;
		Движение.Номенклатура = СтрокаТовары.Номенклатура;
		Движение.Количество = СтрокаТовары.Количество;
	КонецЦикла;

КонецПроцедуры

Модуль объекта по умолчанию компилируется на сервере — отдельные директивы контекста здесь ставить не нужно. Клиентского кода в нём не бывает.

Чтобы вызвать свой экспортный метод модуля объекта снаружи, сначала получают сам объект: ОбъектДокумента = Ссылка.ПолучитьОбъект(), затем ОбъектДокумента.МойМетод(). Записью объект заблокируется для других сеансов — не держите блокировку дольше, чем нужно.

Модуль формы: связь кода с интерфейсом

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

Стандартные события формы:

  • ПриСозданииНаСервере — форма готовится на сервере до показа пользователю (&НаСервере).
  • ПриОткрытии — форма уже на клиенте, можно работать с интерфейсом (&НаКлиенте).
  • ПриИзмененииНаКлиенте у реквизита — реакция на ввод пользователя.
  • ОбработкаОповещения — приём сообщений от других форм.

Типовой приём — обработчик кнопки на клиенте, который вызывает серверный метод без контекста:

&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
	ЗаполнитьЦеныНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьЦеныНаСервере()
	Для Каждого Строка Из Объект.Товары Цикл
		Строка.Цена = РаботаСЦенами.АктуальнаяЦена(Строка.Номенклатура, Объект.Дата);
	КонецЦикла;
КонецПроцедуры

Обратите внимание: данные формы лежат в реквизите Объект, а не в самом объекте базы. Серверный метод с контекстом видит Объект, метод без контекста — нет, ему данные нужно передать параметрами.

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

Модуль команды и модуль менеджера: две разные задачи

Эти два модуля часто путают, хотя задачи у них противоположные.

Модуль команды содержит ровно одну процедуру — ОбработкаКоманды. Команда — это пункт, который пользователь видит в интерфейсе (в форме списка, в форме элемента, на рабочем столе). Код обычно клиентский:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
	// ПараметрКоманды — ссылка на объект, из которого запущена команда
	ОткрытьФорму("Справочник.Контрагенты.Форма.ФормаСверки",
		Новый Структура("Контрагент", ПараметрКоманды));
КонецПроцедуры

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

// Модуль менеджера справочника "Номенклатура"
Функция НайтиПоАртикулу(Артикул) Экспорт
	Возврат Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Артикул);
КонецФункции

Вызов: Справочники.Номенклатура.НайтиПоАртикулу("А-100").

Различие по смыслу простое: модуль объекта работает с одной записью (её реквизитами, движениями), модуль менеджера — с объектом как с коллекцией всех записей. Поэтому в модуле менеджера нет Товары, Дата и прочих реквизитов — их там просто нет контекста.

Прочие модули: приложения, сеанса, набора записей

Полная картина мест, где может лежать код, шире четырёх основных модулей:

  • Модуль управляемого приложения — запускается при старте клиента. Обработчики ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы. Сюда кладут инициализацию, которую видит пользователь.
  • Модуль сеанса — выполняется раньше всех, на сервере, при установке сеанса. Единственное событие — УстановкаПараметровСеанса. Здесь заполняют параметры сеанса (текущий пользователь, организация по умолчанию).
  • Модуль внешнего соединения — аналог модуля приложения, но для работы через COM.
  • Модуль набора записей регистра — обработчики ПередЗаписью и ПриЗаписи для движений.
  • Модуль менеджера значения — у констант.

Если нужно что-то выполнить один раз при входе пользователя в программу — это модуль приложения. Если подготовить данные ещё до старта интерфейса, на сервере — модуль сеанса.

Отладка модуля и поиск узких мест

Писать модуль — половина дела; вторая половина в том, чтобы понять, почему он работает не так. Инструменты отладки живут в конфигураторе.

Базовый цикл отладки:

  1. Поставьте точку останова на нужной строке — клавиша F9 или клик по левому полю редактора.
  2. Запустите 1С:Предприятие в режиме отладки (Отладка → Начать отладку, клавиша F5).
  3. Когда выполнение дойдёт до точки останова, оно встанет. Пошагово двигайтесь клавишами F10 (через процедуру) и F11 (внутрь процедуры).
  4. Значения переменных смотрите в окне «Табло» или наведением курсора на имя переменной.

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

Когда точка останова срабатывает слишком часто (например, внутри цикла по тысячам строк), пользуйтесь условием останова: в свойствах точки задаётся выражение, и выполнение встанет, только когда оно истинно — скажем, при нужном коде номенклатуры. Это избавляет от сотни бесполезных нажатий F5. Там же задаётся счётчик срабатываний, если нужна конкретная итерация.

Когда код работает верно, но медленно, подключают два инструмента:

  • Замер производительности (Отладка → Замер производительности) показывает, сколько времени заняла каждая строка и сколько раз выполнилась. Сразу видно, какой участок съедает секунды.
  • Журнал регистрации фиксирует длительные операции и ошибки на боевой базе, где отладчик не подключить.

Частый источник тормозов — запрос внутри цикла. Если видите в замере, что одна строка с запросом выполнилась сотни раз, перепишите логику: соберите нужные данные одним запросом до цикла, а в цикле обращайтесь к готовой выборке.

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

Ещё один приём для сложных ошибок — расширения конфигурации (появились в платформе 8.3.6). Они позволяют добавить свой код к типовой конфигурации, не снимая её с поддержки и не меняя оригинальные модули. Загрузка .cfe и обновление конфигурации базы данных — два отдельных шага.

Частые вопросы

Почему процедура из общего модуля не видна из другого места?
Скорее всего, у метода нет ключевого слова Экспорт после имени, либо у общего модуля не стоит нужный флаг контекста (клиент/сервер). Экспортными снаружи видны только методы с Экспорт.
В чём разница между `&НаСервере` и `&НаСервереБезКонтекста`?
Первый передаёт на сервер весь контекст формы (её реквизиты) и может их менять — это дорого. Второй контекст не передаёт и работает только со своими параметрами, зато выполняется заметно быстрее. Для вычислений без обращения к форме всегда берите вариант без контекста.
Можно ли выполнить запрос к базе в модуле формы?
Да, но только в методе с директивой &НаСервере или &НаСервереБезКонтекста. На клиенте (&НаКлиенте) объект Запрос недоступен — будет ошибка компиляции.
Куда писать проведение документа?
В обработчик ОбработкаПроведения модуля объекта документа. Движения по регистрам формируются через коллекцию Движения. Клиентского кода в проведении быть не может — оно всегда идёт на сервере.
Чем модуль менеджера отличается от модуля объекта?
Модуль объекта работает с одной конкретной записью и её реквизитами. Модуль менеджера относится к объекту как к типу (ко всем элементам сразу) — туда кладут поиск, создание по шаблону, печать. Реквизитов записи в модуле менеджера нет.