Как пишется код в 1С 8.3: от первой процедуры до транзакции

· 11 мин

Содержание

Код в 1С пишут на встроенном языке платформы — в Конфигураторе или в 1C:Enterprise Development Tools (EDT), внутри модулей объектов конфигурации. Каждая инструкция заканчивается точкой с запятой, блоки закрываются словами КонецЕсли, КонецЦикла, КонецПроцедуры, регистр букв не важен, а в модуле формы перед каждой процедурой стоит директива: &НаКлиенте или &НаСервере.

Частое заблуждение новичков — «язык 1С это Бейсик по-русски, синтаксис выучил — и пиши». Синтаксис осваивается за вечер. Правильно писать код в 1С учит другое: понимание, где выполняется каждая строка (клиент или сервер), и привычка читать данные запросами, а не перебором объектов.

Чем встроенный язык 1С отличается от привычных языков

Язык 1С ближе всего к Visual Basic и Pascal, но со своими правилами. Если вы пришли из Python, C# или JavaScript, сразу запомните отличия.

  • 🔤 Два набора ключевых слов. Каждое слово есть по-русски и по-английски: Если/If, Цикл/Do, Возврат/Return. В типовых конфигурациях пишут по-русски — так принято, смешивать не стоит.
  • 🔠 Регистр не важен. Сумма, сумма и СУММА — одна переменная. Принятый стиль — каждое слово с заглавной: СуммаДокумента, КоличествоСтрок.
  • ⚖️ Знак = значит и присваивание, и сравнение. Смысл задаётся местом: А = 5; присваивает, Если А = 5 Тогда сравнивает. «Не равно» — <>.
  • 🧩 Блоки без фигурных скобок. Конец блока — отдельное слово: КонецЕсли;, КонецЦикла;, КонецПопытки;. Точка с запятой после них обязательна.
  • 💬 Только однострочные комментарии. Комментарий начинается с //. Блочных комментариев нет: в Конфигураторе выделяют строки и закомментируют их командой контекстного меню.
  • 🔢 Индексы с нуля, строки с единицы. Первый элемент массива — Массив[0], а первый символ строки берут как Сред(Строка, 1, 1).
  • 🔀 Тернарный оператор записывается через знак вопроса: Итог = ?(Скидка > 0, Сумма - Скидка, Сумма);.

Строковые литералы — только в двойных кавычках. Кавычку внутри строки удваивают: "ООО ""Ромашка""". Многострочный текст (чаще всего текст запроса) продолжают на новой строке символом |. Литерал даты пишут в одинарных кавычках: '20261007' — это 7 октября 2026 года, 00:00:00.

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

Где пишется код 1С: модули, процедуры и функции

Код в 1С не живёт «в файле программы». Он привязан к объектам конфигурации, и от модуля зависит, когда и где он выполнится.

МодульГде открытьЧто в нём пишут
Модуль объектаОбъект → Прочее → Модуль объектаОбработчики записи и проведения: ПередЗаписью, ОбработкаПроведения
Модуль менеджераОбъект → Прочее → Модуль менеджераФункции, которые работают со всеми объектами вида: печать, выборки
Модуль формыФорма → вкладка МодульОбработчики кнопок и полей формы
Общий модульВетка Общие → Общие модулиПроцедуры, которые вызываются из разных мест
Модуль сеансаКорень конфигурацииУстановка параметров сеанса при входе пользователя

Внутри модуля код разбит на процедуры и функции. Разница одна: функция возвращает значение через Возврат, процедура — нет.

Функция СуммаСНДС(Сумма, СтавкаПроцент = 20) Экспорт

	Возврат Окр(Сумма * (100 + СтавкаПроцент) / 100, 2);

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

Процедура ЗаполнитьИтог(Знач Сумма, Результат)

	Результат = СуммаСНДС(Сумма);

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

Здесь видны три правила:

  1. Экспорт открывает функцию для вызова из других модулей. Без него она видна только внутри своего модуля.
  2. СтавкаПроцент = 20 — параметр со значением по умолчанию. Вызов СуммаСНДС(1000) вернёт 1200.
  3. Параметры передаются по ссылке. Если процедура меняет параметр, меняется и переменная вызывающего кода. Слово Знач перед параметром отключает это: внутри будет копия.

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

Директивы компиляции: где выполняется каждая строка

Без этого раздела код 1С 8.3 не работает в управляемых формах. Форма живёт одновременно на клиенте (у пользователя) и на сервере (где база). Директива перед процедурой говорит, где её выполнять.

ДирективаГде выполняетсяДоступ к базеДоступ к данным формы
&НаКлиентеУ пользователяНетДа
&НаСервереНа сервереДаДа, форма передаётся на сервер
&НаСервереБезКонтекстаНа сервереДаНет, только параметры

Типичная связка: кнопка обрабатывается на клиенте, а за данными идёт серверная процедура.

&НаКлиенте
Процедура ЗаполнитьИНН(Команда)

	Если НЕ ЗначениеЗаполнено(Объект.Контрагент) Тогда
		Сообщить("Сначала выберите контрагента");
		Возврат;
	КонецЕсли;

	ИННКонтрагента = ПолучитьИНН(Объект.Контрагент);

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

&НаСервереБезКонтекста
Функция ПолучитьИНН(Контрагент)

	Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Контрагент, "ИНН");

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

На клиенте нельзя написать Объект.Контрагент.ИНН: обращение через точку к реквизиту ссылки требует чтения базы, а клиент к ней доступа не имеет. ОбщегоНазначения.ЗначениеРеквизитаОбъекта — функция Библиотеки стандартных подсистем (БСП), она есть во всех типовых конфигурациях на 8.3.

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

Переменные и типы данных в коде 1С

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

Примитивные типы:

  • 🔢 Число — дробное с фиксированной точностью, без погрешности плавающей точки: 0.1 + 0.2 = 0.3 даёт Истину.
  • 🔤 Строка — "Текст", длина не ограничена.
  • 📅 Дата — дата и время до секунды. Разность двух дат — число секунд.
  • ✅ Булево — Истина или Ложь.
  • ⭕ Неопределено — значение «пусто» для переменной без значения.
  • 🗄 Null — пустое значение, которое приходит из запроса (например, при левом соединении без пары).

Неопределено и Null — разные вещи. Проверка Если Значение = Неопределено не поймает Null из запроса. Для «пусто ли поле» удобнее ЗначениеЗаполнено(Значение): функция вернёт Ложь для пустой строки, нуля, пустой даты, пустой ссылки и Неопределено.

Проверить тип значения:

Если ТипЗнч(Значение) = Тип("Число") Тогда
	Итог = Итог + Значение;
КонецЕсли;

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

КоллекцияДля чегоПример создания
МассивСписок значений по индексуСписок = Новый Массив;
СтруктураНабор именованных полейНовый Структура("Код, Сумма", 1, 500)
СоответствиеКлюч → значение, ключ любого типаЦены = Новый Соответствие;
ТаблицаЗначенийТаблица в памяти с колонкамиНовый ТаблицаЗначений + Колонки.Добавить("Сумма")
СписокЗначенийЗначения с представлением и пометкойНужен для выбора в формах

Не склеивайте число со строкой напрямую. "Итого: " + 1500000 даст Итого: 1 500 000 с неразрывными пробелами, и такую строку потом не разобрать обратно в число. Используйте Формат(Сумма, "ЧГ=0") или СтрШаблон("Итого: %1", Формат(Сумма, "ЧГ=0")).

Условия и циклы: особенности синтаксиса 1С

Условие:

Если Сумма > 100000 Тогда
	Уровень = "Крупная";
ИначеЕсли Сумма > 10000 Тогда
	Уровень = "Средняя";
Иначе
	Уровень = "Мелкая";
КонецЕсли;

Логические операции — И, ИЛИ, НЕ. Выражение вычисляется слева направо и останавливается, как только результат ясен. Поэтому запись Если ЗначениеЗаполнено(Ссылка) И Ссылка.ПометкаУдаления Тогда безопасна: при пустой ссылке вторая часть не выполнится.

Три вида цикла:

// По счётчику — границы включительно, шаг всегда +1
Для Номер = 1 По 12 Цикл
	Месяц = ДобавитьМесяц(НачалоГода(ТекущаяДатаСеанса()), Номер - 1);
КонецЦикла;

// По элементам коллекции
Для Каждого Строка Из Объект.Товары Цикл
	Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;

// По условию
Пока Выборка.Следующий() Цикл
	ВсегоСумма = ВсегоСумма + Выборка.Сумма;
КонецЦикла;

Прервать выходит из цикла, Продолжить переходит к следующему шагу. Шага -1 у цикла Для нет. Когда нужно удалять строки из коллекции, идите с конца, а количество зафиксируйте до цикла:

Всего = Объект.Товары.Количество();
Для Сч = 1 По Всего Цикл
	Строка = Объект.Товары[Всего - Сч];
	Если Строка.Количество = 0 Тогда
		Объект.Товары.Удалить(Строка);
	КонецЕсли;
КонецЦикла;

Индексы идут от Всего - 1 до 0. Удаление строки сдвигает только строки после неё, а их мы уже прошли. Удалять внутри Для Каждого нельзя: после удаления цикл перескочит через следующий элемент.

Запросы и транзакции: правильная работа с базой

Данные из базы в 1С читают запросом на языке, похожем на SQL. Перебирать справочник через Справочники.Контрагенты.Выбрать() и проверять поля в цикле — медленно и неправильно.

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

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Сообщить(Выборка.Наименование);
КонецЦикла;

Правила хорошего запроса:

  1. Значения передавайте параметрами через &Имя и УстановитьПараметр. Не вклеивайте их в текст строкой — сломаются даты, кавычки и кэш планов СУБД.
  2. Не пишите текст вручную, если запрос сложный. В Конфигураторе в модуле вызовите конструктор запроса из контекстного меню: он соберёт текст с отступами и символами |.
  3. Выбирайте только нужные поля. ВЫБРАТЬ * в коде не используют.
  4. Для остатков и оборотов берите виртуальные таблицы и отбор ставьте в их параметры: РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Номенклатура В (&Товары)). Отбор в ГДЕ после виртуальной таблицы заставит сначала посчитать остатки по всем товарам.

Транзакция и блокировка

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

НачатьТранзакцию();
Попытка

	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
	ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура);
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
	Блокировка.Заблокировать();

	// здесь читаем остаток и записываем объекты

	ЗафиксироватьТранзакцию();

Исключение
	ОтменитьТранзакцию();
	ВызватьИсключение;
КонецПопытки;

Ключевые моменты:

  • 🔒 Блокировку ставят до чтения данных, которые потом будут изменены. Иначе два пользователя прочитают один остаток и оба его спишут.
  • ↩️ В Исключение транзакцию отменяют и пробрасывают ошибку дальше через ВызватьИсключение. Молча «проглотить» ошибку внутри транзакции нельзя.
  • ⏱ Внутри транзакции не ждите пользователя: никаких вопросов и диалогов. Блокировка держится до фиксации, и остальные пользователи стоят в очереди.

В ОбработкаПроведения документа транзакцию открывать не нужно — платформа уже выполняет проведение в транзакции.

Ошибки в коде 1С и как их быстро найти

Самые частые сообщения, которые видит начинающий разработчик, и что за ними стоит.

Диагностика: выберите симптом

Как ловить ошибки до пользователя:

  1. Перед сохранением выполните синтаксическую проверку модуля из контекстного меню. Она находит незакрытые блоки и необъявленные переменные.
  2. Запустите отладку клавишей F5, поставьте точку останова клавишей F9 и смотрите значения переменных в табло.
  3. Проверяйте конфигурацию целиком: Конфигурация → Проверка конфигурации. Она ловит вызовы, недоступные в нужном режиме.

Не экспериментируйте с кодом в рабочей базе. Для пробы есть внешняя обработка (Файл → Новый → Внешняя обработка): она не меняет конфигурацию и открывается в режиме Предприятия через Файл → Открыть.

Как ускорить код: приёмы оптимизации

Медленный код в 1С почти всегда тормозит по одной из четырёх причин.

ПроблемаКак выглядитКак исправить
Запрос в циклеДля Каждого по строкам, внутри Запрос.Выполнить()Один запрос на все строки, отбор В (&Список)
Обращение через точку в циклеСтрока.Номенклатура.Артикул у тысячи строкПолучить реквизиты одним запросом, разложить в Соответствие
Лишние серверные вызовыКлиентская процедура трижды вызывает серверСобрать данные за один вызов в Структура
Тяжёлый отбор после виртуальной таблицыУсловие в ГДЕ вместо параметров таблицыПеренести отбор в параметры Остатки(...)

Каждое Ссылка.Реквизит на сервере — отдельное чтение объекта из базы. Пять таких обращений в цикле по 2000 строк — это 10 000 обращений. Один запрос с соединением сделает то же за одно обращение.

Ещё приёмы:

  • ♻️ Данные, которые редко меняются (настройки, константы), получайте через общий модуль с флажком «Повторное использование возвращаемых значений» — на время сеанса они кэшируются.
  • 📏 Не гадайте, что тормозит. Запустите отладку и включите замер производительности из меню Отладка — он покажет время по каждой строке.
  • 🧮 Для итогов по таблице значений используйте Итог("Сумма"), для поиска — НайтиСтроки с индексом по колонке, а не вложенные циклы.
Проверка модуля перед сдачей
Выполнено: 0 / 6

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

Где писать код 1С, если нет доступа к Конфигуратору?
Во внешней обработке или внешнем отчёте. Их создают в Конфигураторе любой базы, в том числе учебной, а запускают в рабочей через Файл → Открыть, если у пользователя есть право на интерактивное открытие внешних обработок.
Можно ли писать код 1С по-английски?
Да, у всех ключевых слов и методов есть английские имена. Но типовые конфигурации написаны по-русски, и смешивание языков в одном модуле затрудняет чтение.
Чем EDT отличается от Конфигуратора?
EDT — среда разработки на базе Eclipse: хранит конфигурацию в файлах, работает с Git, даёт рефакторинг и проверки кода. Конфигуратор проще и легче, им по-прежнему пользуется большинство разработчиков.
Как правильно доработать типовую конфигурацию?
Через расширение конфигурации, а не снятием типовой с поддержки. Расширение переживает обновления типовой, правки самой конфигурации приходится переносить вручную при каждом обновлении.
Есть ли в 1С ООП?
Классов и наследования в привычном смысле нет. Роль классов играют объекты конфигурации: справочники, документы, регистры со своими модулями. Общий код выносят в общие модули.