1C-CodeОсновы 1С → Модули: какой когда выполняется

Модули: какой когда выполняется

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

Глава 21 из 36 · чтение примерно 30 минут

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

Это самая частая причина «у меня всё написано, но не работает» в первые месяцы.

Модули, которые встречаются каждый день

МодульКогда выполняетсяЧто в нём пишут
Модуль объектапри работе с объектом: запись, проведениеОбработкаПроведения, ПередЗаписью, проверки
Модуль формыпока форма открытареакция на действия человека
Модуль менеджерапри обращении к объекту в целомобщие функции по этому виду: печать, подбор
Общий модулькогда его позвалито, что нужно многим
Модуль сеансаодин раз при старте сеансаустановка параметров сеанса
Модуль приложенияпри запуске и завершенииПриНачалеРаботыСистемы

Модуль объекта

Здесь живёт поведение одного экземпляра: конкретной накладной, конкретного элемента справочника.

Ключевые процедуры и порядок, в котором платформа их зовёт при записи документа:

ОбработкаЗаполнения — при создании нового. ОбработкаПроверкиЗаполнения — проверка обязательных полей. ПередЗаписью — последний шанс что-то поправить или отказаться. ОбработкаПроведения — движения. ПриЗаписи — уже записано, но транзакция ещё открыта.

Важное: всё это выполняется на сервере. Никаких вопросов пользователю отсюда задать нельзя — его там нет.

Транзакция ещё открыта

ПриЗаписи и ОбработкаПроведения выполняются внутри транзакции. Значит:

Ошибка здесь откатывает всё — и запись, и движения. Это хорошо: половинчатых результатов не бывает.

Долгие операции здесь запрещены. Отправка HTTP-запроса при проведении держит транзакцию открытой всё время ожидания и блокирует таблицы для остальных. Классическая причина «база встала».

Модуль формы

Живёт, пока форма открыта, и знает то, чего не знает модуль объекта: что человек нажал и что он видит на экране.

Здесь важно различать реквизиты формы и реквизиты объекта. Форма работает не с самим документом, а с его копией в реквизите Объект. Пока форма не записана, база о правках ничего не знает.

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

Модуль менеджера

Про вид объекта целиком, а не про экземпляр. «Напечатать накладную» — функция менеджера: она получает список ссылок и печатает их все. «Заполнить по основанию» тоже часто здесь.

Правило выбора: нужен конкретный объект — модуль объекта; нужен вид объектов — модуль менеджера.

Общий модуль

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

Сервер, Клиент (управляемое приложение), Внешнее соединение — где модуль доступен. Вызов сервера — можно ли звать его серверные функции прямо с клиента. Привилегированный — выполняется без проверки прав.

Общий модуль, у которого отмечены и клиент, и сервер, компилируется дважды. Функция в нём должна работать в обоих местах — а значит, не может обращаться ни к базе, ни к экрану.

На чём спотыкаются

Кладут в общий модуль код про конкретный документ. Правило: код про документ живёт у документа. В общий модуль выносят только то, что нужно нескольким.

Забывают Экспорт. Функция без него не видна снаружи модуля — и ошибка звучит как «процедура не обнаружена», хотя она вот, перед глазами.

Ставят «Привилегированный» ради удобства. Такой модуль не проверяет права вообще: кладовщик через него увидит зарплату. Привилегированными делают узкие служебные функции, а не всё подряд.

Зовут функцию модуля формы из модуля объекта. Нельзя: объект не знает, открыта ли форма и какая. Данные передают параметрами в одну сторону — от формы к объекту.

Модуль приложения и сеанса

Есть два модуля, которые выполняются до всего остального, и знать про них нужно хотя бы затем, чтобы понимать, почему база иногда «не запускается».

Модуль сеанса — самый первый. Выполняется на сервере, до того как появился пользователь. В нём только одно: УстановкаПараметровСеанса. Ошибка здесь означает, что база не откроется вообще ни у кого.

Модуль управляемого приложения — при запуске клиента. ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы, ПередЗавершениемРаботыСистемы. Здесь показывают начальную страницу, проверяют версию, спрашивают про обновления.

Оба — плохое место для «просто полезного кода»: он будет выполняться у каждого при каждом входе.

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

Это тот редкий случай, когда неудачная правка не даёт войти и исправить её: конфигуратор поднимается, а предприятие — нет.

Поэтому в модуле сеанса пишут ровно столько, сколько нужно, и оборачивают рискованное в Попытку. Особенно обращения к данным: параметры сеанса устанавливаются до того, как проверены права, и ошибка прав тут выглядит как «база сломалась».

Модуль менеджера: что там на самом деле пишут

Модуль менеджера объекта путают с модулем объекта чаще всего. Разница простая: модуль объекта — про один экземпляр, модуль менеджера — про вид целиком.

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

Практический признак: если функции нужен ЭтотОбъект — ей место в модуле объекта. Если она принимает ссылку или массив ссылок параметром — в модуле менеджера.

Одно и то же по-разному

// Модуль ОБЪЕКТА: работаем с собой
Функция СуммаДокумента() Экспорт
	Возврат Товары.Итог("Сумма");
КонецФункции

// Модуль МЕНЕДЖЕРА: работаем с чужими ссылками
Функция СуммыДокументов(МассивСсылок) Экспорт
	// один запрос по всем сразу
КонецФункции
ЧастьЧто означает
Товары.Итогв модуле объекта табличная часть доступна прямо по имени — это и есть «мы внутри объекта»
МассивСсылокменеджер не знает «себя», он работает с тем, что дали
вызовДок.СуммаДокумента() против Документы.Реализация.СуммыДокументов(Массив)
почему это важнофункция в модуле объекта заставляет ПРОЧИТАТЬ объект целиком; менеджер обходится запросом

Чтение объекта — дорого

Ссылка.ПолучитьОбъект() читает из базы все реквизиты и все табличные части. Для одного документа это незаметно; в цикле по тысяче — это тысяча тяжёлых чтений.

Поэтому в отчётах и обработках берут запросом то, что нужно, а объект читают только когда собираются его менять.

Из той же серии: Ссылка.Реквизит в цикле — это обращение к базе на каждый реквизит. Один запрос с нужными полями дешевле, и это то самое «запрос вместо цикла» из главы про циклы.

Модуль формы: чей это код

В модуле формы легко написать то, что формы не касается. Признак — функция ничего не берёт из Элементы и Объект, а просто считает.

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

Обратное тоже верно: работа с элементами, видимостью и доступностью живёт только в модуле формы. В модуле объекта Элементы не существует.

Куда класть код: короткий ответ

Что делает функцияГде ей место
меняет видимость поля, реагирует на нажатиемодуль формы
проверяет и меняет сам объект при записимодуль объекта
работает с видом целиком: поиск, печать, отбормодуль менеджера
считает и не знает про 1С («разобрать строку»)общий модуль
нужна и клиенту, и серверу без данных формыобщий модуль с двумя флагами
выполняется при запуске у всехмодуль приложения — и подумайте ещё раз

Копия вместо вызова

Самая дорогая ошибка в размещении кода — не «положил не туда», а «скопировал». Две копии расходятся молча: починили в одной, вторая продолжает ошибаться.

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

Это ровно то, что мы ловим у себя: два места, описывающие одно правило, всегда расходятся — вопрос только когда.

Расширения: чужой код, который трогать нельзя

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

&Перед("ИмяПроцедуры") — выполнить своё до типовой процедуры. &После(…) — после. &Вместо(…) — вместо неё целиком.

Правило, которое стоит запомнить сразу: «Вместо» — крайняя мера. Заменив типовую процедуру своей, вы отказались от всех будущих исправлений в ней, и узнаете об этом на первом же обновлении.

Упражнение 2. Модуль объекта или менеджера

Напишите Модуль(Что): «себя» — функция работает с текущим объектом → объекта; «многих» — принимает массив ссылок → менеджера; «элементы» — меняет вид формы → формы; всё остальное → общий. Проверка спрашивает про «многих».

Решается в тренажёре: код запускается и проверяется сразу.

Упражнение 1. Куда положить функцию

Функция ГдеПисать(Что) получает описание того, что надо сделать, и возвращает модуль: "МодульОбъекта", "МодульФормы", "МодульМенеджера" или "ОбщийМодуль". Правила: есть "проведени" — модуль объекта; есть "нажал" — модуль формы; есть "печать" — модуль менеджера; остальное — общий.

Решается в тренажёре: код запускается и проверяется сразу.

Короткий свод

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

Как закрепить

Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.

Открыть эту главу в тренажёре

Регистры сведений: значение на дату и разрез
Клиент и сервер: директивы компиляции