Как пишется код в 1С 8.3: от первой процедуры до транзакции
Содержание
Код в 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);
КонецФункции
Процедура ЗаполнитьИтог(Знач Сумма, Результат)
Результат = СуммаСНДС(Сумма);
КонецПроцедуры
Здесь видны три правила:
Экспортоткрывает функцию для вызова из других модулей. Без него она видна только внутри своего модуля.СтавкаПроцент = 20— параметр со значением по умолчанию. ВызовСуммаСНДС(1000)вернёт 1200.- Параметры передаются по ссылке. Если процедура меняет параметр, меняется и переменная вызывающего кода. Слово
Значперед параметром отключает это: внутри будет копия.
У общего модуля есть флажки в свойствах: Клиент, Сервер, Вызов сервера, Внешнее соединение, Привилегированный, Повторное использование возвращаемых значений. Они определяют, где модуль доступен. Ошибка с ними — классика: процедуру из модуля с одним флажком Сервер нельзя вызвать с клиента напрямую.
Директивы компиляции: где выполняется каждая строка
Без этого раздела код 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С и как их быстро найти
Самые частые сообщения, которые видит начинающий разработчик, и что за ними стоит.
ЗначениеЗаполнено.Как ловить ошибки до пользователя:
- Перед сохранением выполните синтаксическую проверку модуля из контекстного меню. Она находит незакрытые блоки и необъявленные переменные.
- Запустите отладку клавишей
F5, поставьте точку останова клавишейF9и смотрите значения переменных в табло. - Проверяйте конфигурацию целиком:
Конфигурация → Проверка конфигурации. Она ловит вызовы, недоступные в нужном режиме.
Не экспериментируйте с кодом в рабочей базе. Для пробы есть внешняя обработка (Файл → Новый → Внешняя обработка): она не меняет конфигурацию и открывается в режиме Предприятия через Файл → Открыть.
Как ускорить код: приёмы оптимизации
Медленный код в 1С почти всегда тормозит по одной из четырёх причин.
| Проблема | Как выглядит | Как исправить |
|---|---|---|
| Запрос в цикле | Для Каждого по строкам, внутри Запрос.Выполнить() | Один запрос на все строки, отбор В (&Список) |
| Обращение через точку в цикле | Строка.Номенклатура.Артикул у тысячи строк | Получить реквизиты одним запросом, разложить в Соответствие |
| Лишние серверные вызовы | Клиентская процедура трижды вызывает сервер | Собрать данные за один вызов в Структура |
| Тяжёлый отбор после виртуальной таблицы | Условие в ГДЕ вместо параметров таблицы | Перенести отбор в параметры Остатки(...) |
Каждое Ссылка.Реквизит на сервере — отдельное чтение объекта из базы. Пять таких обращений в цикле по 2000 строк — это 10 000 обращений. Один запрос с соединением сделает то же за одно обращение.
Ещё приёмы:
- ♻️ Данные, которые редко меняются (настройки, константы), получайте через общий модуль с флажком «Повторное использование возвращаемых значений» — на время сеанса они кэшируются.
- 📏 Не гадайте, что тормозит. Запустите отладку и включите замер производительности из меню
Отладка— он покажет время по каждой строке. - 🧮 Для итогов по таблице значений используйте
Итог("Сумма"), для поиска —НайтиСтрокис индексом по колонке, а не вложенные циклы.
Частые вопросы
Где писать код 1С, если нет доступа к Конфигуратору?
Файл → Открыть, если у пользователя есть право на интерактивное открытие внешних обработок.