1C-Code → Основы 1С → Модули: какой когда выполняется
Верный код, написанный не в тот модуль, не работает. Разбор всех видов: что где живёт, что откуда видно и в каком порядке всё это выполняется.
Глава 21 из 36 · чтение примерно 30 минут
В главе «Где живёт код» мы условились: модуль — это файл с кодом, и у каждого объекта их несколько. Здесь разбираем каждый: когда выполняется, что ему видно и какие ошибки получаются, если положить функцию не туда.
Это самая частая причина «у меня всё написано, но не работает» в первые месяцы.
| Модуль | Когда выполняется | Что в нём пишут |
|---|---|---|
| Модуль объекта | при работе с объектом: запись, проведение | ОбработкаПроведения, ПередЗаписью, проверки |
| Модуль формы | пока форма открыта | реакция на действия человека |
| Модуль менеджера | при обращении к объекту в целом | общие функции по этому виду: печать, подбор |
| Общий модуль | когда его позвали | то, что нужно многим |
| Модуль сеанса | один раз при старте сеанса | установка параметров сеанса |
| Модуль приложения | при запуске и завершении | ПриНачалеРаботыСистемы |
Здесь живёт поведение одного экземпляра: конкретной накладной, конкретного элемента справочника.
Ключевые процедуры и порядок, в котором платформа их зовёт при записи документа:
ОбработкаЗаполнения — при создании нового. ОбработкаПроверкиЗаполнения — проверка обязательных полей. ПередЗаписью — последний шанс что-то поправить или отказаться. ОбработкаПроведения — движения. ПриЗаписи — уже записано, но транзакция ещё открыта.
Важное: всё это выполняется на сервере. Никаких вопросов пользователю отсюда задать нельзя — его там нет.
Транзакция ещё открыта
ПриЗаписи и ОбработкаПроведения выполняются внутри транзакции. Значит:
Ошибка здесь откатывает всё — и запись, и движения. Это хорошо: половинчатых результатов не бывает.
Долгие операции здесь запрещены. Отправка HTTP-запроса при проведении держит транзакцию открытой всё время ожидания и блокирует таблицы для остальных. Классическая причина «база встала».
Живёт, пока форма открыта, и знает то, чего не знает модуль объекта: что человек нажал и что он видит на экране.
Здесь важно различать реквизиты формы и реквизиты объекта. Форма работает не с самим документом, а с его копией в реквизите Объект. Пока форма не записана, база о правках ничего не знает.
Отсюда следствие, которое ловит каждого: код в модуле формы меняет Объект.Сумма — а запрос, выполненный рядом, по-прежнему видит старое значение. Он ходит в базу, а правка ещё в памяти формы.
Про вид объекта целиком, а не про экземпляр. «Напечатать накладную» — функция менеджера: она получает список ссылок и печатает их все. «Заполнить по основанию» тоже часто здесь.
Правило выбора: нужен конкретный объект — модуль объекта; нужен вид объектов — модуль менеджера.
Сюда выносят то, что нужно многим. У общего модуля есть флажки, и они определяют, где он вообще существует:
Сервер, Клиент (управляемое приложение), Внешнее соединение — где модуль доступен. Вызов сервера — можно ли звать его серверные функции прямо с клиента. Привилегированный — выполняется без проверки прав.
Общий модуль, у которого отмечены и клиент, и сервер, компилируется дважды. Функция в нём должна работать в обоих местах — а значит, не может обращаться ни к базе, ни к экрану.
На чём спотыкаются
Кладут в общий модуль код про конкретный документ. Правило: код про документ живёт у документа. В общий модуль выносят только то, что нужно нескольким.
Забывают Экспорт. Функция без него не видна снаружи модуля — и ошибка звучит как «процедура не обнаружена», хотя она вот, перед глазами.
Ставят «Привилегированный» ради удобства. Такой модуль не проверяет права вообще: кладовщик через него увидит зарплату. Привилегированными делают узкие служебные функции, а не всё подряд.
Зовут функцию модуля формы из модуля объекта. Нельзя: объект не знает, открыта ли форма и какая. Данные передают параметрами в одну сторону — от формы к объекту.
Есть два модуля, которые выполняются до всего остального, и знать про них нужно хотя бы затем, чтобы понимать, почему база иногда «не запускается».
Модуль сеанса — самый первый. Выполняется на сервере, до того как появился пользователь. В нём только одно: УстановкаПараметровСеанса. Ошибка здесь означает, что база не откроется вообще ни у кого.
Модуль управляемого приложения — при запуске клиента. ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы, ПередЗавершениемРаботыСистемы. Здесь показывают начальную страницу, проверяют версию, спрашивают про обновления.
Оба — плохое место для «просто полезного кода»: он будет выполняться у каждого при каждом входе.
Ошибка в модуле сеанса закрывает базу
Это тот редкий случай, когда неудачная правка не даёт войти и исправить её: конфигуратор поднимается, а предприятие — нет.
Поэтому в модуле сеанса пишут ровно столько, сколько нужно, и оборачивают рискованное в Попытку. Особенно обращения к данным: параметры сеанса устанавливаются до того, как проверены права, и ошибка прав тут выглядит как «база сломалась».
Модуль менеджера объекта путают с модулем объекта чаще всего. Разница простая: модуль объекта — про один экземпляр, модуль менеджера — про вид целиком.
«Посчитать сумму этого документа» — модуль объекта: есть конкретный документ. «Найти все просроченные документы», «собрать печатную форму для списка», «отдать данные для отчёта» — модуль менеджера: конкретного экземпляра нет.
Практический признак: если функции нужен ЭтотОбъект — ей место в модуле объекта. Если она принимает ссылку или массив ссылок параметром — в модуле менеджера.
// Модуль ОБЪЕКТА: работаем с собой
Функция СуммаДокумента() Экспорт
Возврат Товары.Итог("Сумма");
КонецФункции
// Модуль МЕНЕДЖЕРА: работаем с чужими ссылками
Функция СуммыДокументов(МассивСсылок) Экспорт
// один запрос по всем сразу
КонецФункции
| Часть | Что означает |
|---|---|
Товары.Итог | в модуле объекта табличная часть доступна прямо по имени — это и есть «мы внутри объекта» |
МассивСсылок | менеджер не знает «себя», он работает с тем, что дали |
вызов | Док.СуммаДокумента() против Документы.Реализация.СуммыДокументов(Массив) |
почему это важно | функция в модуле объекта заставляет ПРОЧИТАТЬ объект целиком; менеджер обходится запросом |
Чтение объекта — дорого
Ссылка.ПолучитьОбъект() читает из базы все реквизиты и все табличные части. Для одного документа это незаметно; в цикле по тысяче — это тысяча тяжёлых чтений.
Поэтому в отчётах и обработках берут запросом то, что нужно, а объект читают только когда собираются его менять.
Из той же серии: Ссылка.Реквизит в цикле — это обращение к базе на каждый реквизит. Один запрос с нужными полями дешевле, и это то самое «запрос вместо цикла» из главы про циклы.
В модуле формы легко написать то, что формы не касается. Признак — функция ничего не берёт из Элементы и Объект, а просто считает.
Такой код переносят: расчёт — в общий модуль, работу с объектом — в модуль объекта. Причина не в эстетике: код в модуле формы доступен только этой форме. Понадобился он второй раз — его скопируют, и дальше два экземпляра начнут расходиться.
Обратное тоже верно: работа с элементами, видимостью и доступностью живёт только в модуле формы. В модуле объекта Элементы не существует.
| Что делает функция | Где ей место |
|---|---|
| меняет видимость поля, реагирует на нажатие | модуль формы |
| проверяет и меняет сам объект при записи | модуль объекта |
| работает с видом целиком: поиск, печать, отбор | модуль менеджера |
| считает и не знает про 1С («разобрать строку») | общий модуль |
| нужна и клиенту, и серверу без данных формы | общий модуль с двумя флагами |
| выполняется при запуске у всех | модуль приложения — и подумайте ещё раз |
Копия вместо вызова
Самая дорогая ошибка в размещении кода — не «положил не туда», а «скопировал». Две копии расходятся молча: починили в одной, вторая продолжает ошибаться.
Признак, что сейчас произойдёт копирование: вы ищете, откуда бы взять «такую же функцию, как в том документе». Правильный ход — вынести её в общий модуль до того, как копировать.
Это ровно то, что мы ловим у себя: два места, описывающие одно правило, всегда расходятся — вопрос только когда.
Типовую конфигурацию менять напрямую нельзя — она на поддержке, и правки перетрутся при обновлении. Поэтому свой код кладут в расширение, а к чужим модулям цепляются через аннотации.
&Перед("ИмяПроцедуры") — выполнить своё до типовой процедуры. &После(…) — после. &Вместо(…) — вместо неё целиком.
Правило, которое стоит запомнить сразу: «Вместо» — крайняя мера. Заменив типовую процедуру своей, вы отказались от всех будущих исправлений в ней, и узнаете об этом на первом же обновлении.
Упражнение 2. Модуль объекта или менеджера
Напишите Модуль(Что): «себя» — функция работает с текущим объектом → объекта; «многих» — принимает массив ссылок → менеджера; «элементы» — меняет вид формы → формы; всё остальное → общий. Проверка спрашивает про «многих».
Решается в тренажёре: код запускается и проверяется сразу.
Упражнение 1. Куда положить функцию
Функция ГдеПисать(Что) получает описание того, что надо сделать, и возвращает модуль: "МодульОбъекта", "МодульФормы", "МодульМенеджера" или "ОбщийМодуль". Правила: есть "проведени" — модуль объекта; есть "нажал" — модуль формы; есть "печать" — модуль менеджера; остальное — общий.
Решается в тренажёре: код запускается и проверяется сразу.
Модуль объекта — поведение экземпляра, выполняется на сервере внутри транзакции: долгим операциям там не место. Модуль формы — то, что происходит на экране, и он работает с копией объекта, а не с базой. Модуль менеджера — про вид объектов целиком. Общий модуль — то, что нужно многим; его флажки решают, где он вообще существует. Без Экспорт функция не видна снаружи, и ошибка об этом звучит обманчиво.
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.
← Регистры сведений: значение на дату и разрез
→ Клиент и сервер: директивы компиляции