Примеры кода на встроенном языке 1С 8.3

· 9 мин

Содержание

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

Ниже — рабочие фрагменты для 1С 8.3: от объявления переменной до соединения таблиц в запросе. Каждый пример можно скопировать кнопкой в углу блока и вставить в модуль. Синтаксис русский, но всё сказанное верно и для англоязычного варианта конфигуратора.

Синтаксис и структура: с чего состоит код на 1С

Код живёт в модулях — модуль формы, модуль объекта, общий модуль, модуль менеджера. Внутри модуля — процедуры и функции, между ними может стоять область объявления переменных.

Базовые правила, которые экономят часы отладки:

  • 🔹 каждая инструкция заканчивается точкой с запятой ;;
  • 🔹 регистр в ключевых словах не важен: Если, ЕСЛИ, если — одно и то же;
  • 🔹 комментарий — две косые черты // до конца строки;
  • 🔹 строки соединяются плюсом: "Итого: " + Сумма;
  • 🔹 имена переменных — по-русски или латиницей, но без пробелов.
// Это комментарий
Процедура ПриветМир()
    Сообщить("Привет, 1С!"); // вывод в окно сообщений
КонецПроцедуры

Метод Сообщить() устарел в клиент-серверном контексте. В новых конфигурациях используйте объект Новый СообщениеПользователю, но для быстрых примеров Сообщить() подходит.

Переменные и типы данных: практический код

Переменные в 1С типизируются динамически — тип определяется присвоенным значением, объявлять его заранее не нужно.

Количество = 10;              // Число
Наименование = "Ноутбук";     // Строка
ДатаПродажи = '20240115';     // Дата (формат ГГГГММДД)
ЕстьСкидка   = Истина;        // Булево
Ссылка       = Неопределено;  // пустое значение

Часто встречающиеся типы и их пустые значения:

ТипПример литералаПустое значение
Число123.450
Строка"текст"""
Дата'20240115120000''00010101'
БулевоИстина / Ложь
СсылкаСправочники.Номенклатура.ПустаяСсылка()

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

Перем КэшКурсов; // объявление в начале модуля

Условия и ветвление: оператор Если и тернарный оператор

Полная форма ветвления — Если … ИначеЕсли … Иначе … КонецЕсли.

Если Сумма > 100000 Тогда
    Скидка = 15;
ИначеЕсли Сумма > 50000 Тогда
    Скидка = 10;
ИначеЕсли Сумма > 10000 Тогда
    Скидка = 5;
Иначе
    Скидка = 0;
КонецЕсли;

Когда веток всего две, короче написать тернарный оператор ?(условие, значение1, значение2):

Скидка = ?(Сумма > 100000, 15, 0);
Знак   = ?(Остаток < 0, "долг", "аванс");

Логические операторы пишутся словами: И, ИЛИ, НЕ.

Если Клиент.Активен И НЕ Клиент.ПометкаУдаления Тогда
    // работаем с клиентом
КонецЕсли;

Циклы Для и Пока: примеры перебора

Три вида циклов покрывают почти любую задачу.

Цикл со счётчиком — когда известно число повторений:

Для Индекс = 1 По 10 Цикл
    Сообщить("Строка № " + Индекс);
КонецЦикла;

Цикл обхода коллекции Для Каждого — перебор таблицы значений, массива, выборки:

Итого = 0;
Для Каждого Строка Из ТаблицаТоваров Цикл
    Итого = Итого + Строка.Сумма;
КонецЦикла;

Цикл с условием Пока — пока условие истинно:

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

Управление ходом цикла — два ключевых слова:

  • Продолжить — пропустить текущую итерацию, перейти к следующей;
  • Прервать — выйти из цикла полностью.
Для Каждого Строка Из ТаблицаТоваров Цикл
    Если Строка.Количество = 0 Тогда
        Продолжить; // пустые строки пропускаем
    КонецЕсли;
    Если Строка.Сумма > Лимит Тогда
        Прервать; // нашли превышение — выходим
    КонецЕсли;
    ОбработатьСтроку(Строка);
КонецЦикла;

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

Процедуры и функции: исходный код с комментариями

Процедура выполняет действия и ничего не возвращает. Функция возвращает значение через Возврат.

// Функция считает и возвращает результат
Функция ВычислитьСумму(Цена, Количество) Экспорт
    Возврат Цена * Количество;
КонецФункции

// Процедура выполняет действие
Процедура ПоказатьИтог(Знач Сумма)
    Сообщить("Итого: " + Формат(Сумма, "ЧЦ=15; ЧДЦ=2"));
КонецПроцедуры

Что здесь важно:

  • 📌 Экспорт делает метод видимым из других модулей;
  • 📌 Знач перед параметром запрещает менять его внутри — передаётся копия;
  • 📌 параметру можно задать значение по умолчанию: Функция Расчёт(Ставка = 20).

Обработка ошибок: Попытка и Исключение

Опасный код (деление, разбор строки, обращение к внешнему ресурсу) оборачивают в Попытка … Исключение … КонецПопытки.

Попытка
    Результат = Числитель / Делитель;
Исключение
    Результат = 0;
    Сообщить("Ошибка расчёта: " + ОписаниеОшибки());
КонецПопытки;

Без такой обёртки исключение прерывает всю операцию. Внутри блока Исключение доступна функция ОписаниеОшибки() с текстом проблемы.

Мини-чек-лист хорошей процедуры
Выполнено: 0 / 5

Работа с запросами: код решений на языке запросов

В учётной базе данные лежат в таблицах СУБД, и доставать их циклом по всем объектам — медленно и неправильно. Правильный инструмент — запрос.

Базовая структура запроса

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

Вертикальная черта | в начале строк — это не синтаксис запроса, а способ 1С хранить многострочный текст. Псевдоним через КАК задаёт имя колонки в результате.

Параметры в запросах

Значения из кода передают только через параметры — подстановка строк в текст запроса открывает дыру и ломается на кавычках.

Запрос.Текст =
    "ВЫБРАТЬ Продажи.Номенклатура, Продажи.Сумма
    |ИЗ РегистрНакопления.Продажи.Обороты КАК Продажи
    |ГДЕ Продажи.Период МЕЖДУ &Начало И &Конец";

Запрос.УстановитьПараметр("Начало", НачалоДня(ДатаОтчёта));
Запрос.УстановитьПараметр("Конец",  КонецДня(ДатаОтчёта));

Соединение таблиц

Данные из двух таблиц связывают соединением. Самое частое — ЛЕВОЕ СОЕДИНЕНИЕ: берём все строки первой таблицы и подтягиваем совпадения из второй.

"ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    Товары.Количество КАК Количество,
|    Цены.Цена КАК Цена
|ИЗ
|    Документ.Реализация.Товары КАК Товары
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
|    ПО Товары.Номенклатура = Цены.Номенклатура"

Агрегирование и группировка

Итоги считают агрегатными функциями СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ вместе с СГРУППИРОВАТЬ ПО.

"ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    СУММА(Товары.Сумма) КАК ВсегоСумма,
|    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Ссылка) КАК ЧислоДокументов
|ИЗ Документ.Реализация.Товары КАК Товары
|СГРУППИРОВАТЬ ПО Товары.Номенклатура
|УПОРЯДОЧИТЬ ПО ВсегоСумма УБЫВ"

Временные таблицы

Сложную выборку разбивают на шаги через временные таблицы: сначала ПОМЕСТИТЬ, потом обращаемся к результату как к обычной таблице.

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
    "ВЫБРАТЬ Товары.Номенклатура, СУММА(Товары.Сумма) КАК Сумма
    |ПОМЕСТИТЬ ВТ_Продажи
    |ИЗ Документ.Реализация.Товары КАК Товары
    |СГРУППИРОВАТЬ ПО Товары.Номенклатура";
Запрос.Выполнить();

Быстрые советы по оптимизации запроса:

  • ✅ фильтруйте в ГДЕ, а не в цикле после выборки;
  • ✅ для остатков и оборотов берите виртуальные таблицы (.Остатки, .Обороты), а не таблицу движений;
  • ✅ не пишите ВЫБРАТЬ * — перечисляйте только нужные поля;
  • ✅ избегайте соединений с вложенными запросами — выносите их во временные таблицы.

Работа с формами и объектами: частые приёмы

Клиент и сервер: директивы компиляции

Код на форме исполняется в двух контекстах. На клиенте нельзя обращаться к базе, на сервере — к элементам интерфейса. Контекст задаётся директивой над процедурой.

&НаКлиенте
Процедура ЗаполнитьНажатие(Команда)
    ЗагрузитьДанныеНаСервере(); // вызываем серверный метод
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьДанныеНаСервере()
    // здесь доступны запросы и объекты базы
КонецПроцедуры

Определение вида объекта и составной тип

Когда реквизит хранит составной тип (например, «получатель» — то ли контрагент, то ли сотрудник), проверяют конкретный тип функцией ТипЗнч().

Если ТипЗнч(Реквизит) = Тип("СправочникСсылка.Контрагенты") Тогда
    Сообщить("Это контрагент");
ИначеЕсли ТипЗнч(Реквизит) = Тип("СправочникСсылка.Сотрудники") Тогда
    Сообщить("Это сотрудник");
КонецЕсли;

Запись в журнал регистрации

Служебные события удобно логировать в журнал регистрации — потом их видно в Все функции → Журнал регистрации.

ЗаписьЖурналаРегистрации(
    "Обработка.Загрузка",                       // источник
    УровеньЖурналаРегистрации.Информация,        // уровень
    , , ,
    "Загружено строк: " + КоличествоСтрок);      // текст

Работа со строками таблицы значений

// Добавить строку
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = ВыбраннаяНоменклатура;
НоваяСтрока.Количество   = 1;

// Удалить строки по условию (перебор с конца!)
Для Индекс = Таблица.Количество() - 1 По 0 Цикл
    Если Таблица[Индекс].Количество = 0 Тогда
        Таблица.Удалить(Индекс);
    КонецЕсли;
КонецЦикла;

Удаляя строки в цикле, идите с конца к началу. Если удалять по возрастанию индекса, коллекция «съезжает» и часть строк пропускается.

Типичные ошибки в коде на 1С и как их избежать

Ошибки, на которые уходит больше всего времени у новичков:

  • 🔴 Забыли ; в конце строки → «Ожидается конец оператора». Смотрите на строку выше указанной в ошибке.
  • 🔴 Для Каждого вместо Для Каждого — опечатка в ключевом слове не подсвечивается заранее, вылезает при запуске.
  • 🔴 Обращение к базе на клиенте → «Обращение к процедуре сервера на клиенте». Перенесите код под &НаСервере.
  • 🔴 Строки в текст запроса конкатенацией вместо УстановитьПараметр — падает на кавычках и датах.
  • 🔴 Сравнение с Неопределено через = для несуществующего реквизита — проверяйте ЗначениеЗаполнено(Реквизит).
  • 🔴 Цикл по всей базе вместо запроса — на тысячах объектов это минуты вместо миллисекунд.
Проверьте себя
Чем заканчивается описание функции в 1С?
Как передать значение из кода в текст запроса?
Какая директива нужна процедуре, которая обращается к базе данных?
В какую сторону перебирать строки таблицы при удалении?

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

На каком языке пишут код в 1С 8.3?
На встроенном языке 1С (внутреннее название — BSL, «1С:Предприятие»). Ключевые слова есть в русском и английском вариантах, работают оба. Для выборки данных используется отдельный язык запросов.
Где писать код — в Конфигураторе или в пользовательском режиме?
Основной код — в Конфигураторе (или в EDT), в модулях объектов и форм. Разово выполнить фрагмент можно через внешнюю обработку с командой «Выполнить» в режиме «1С:Предприятие», без изменения конфигурации.
Чем процедура отличается от функции?
Функция возвращает значение оператором Возврат и её результат можно присвоить переменной. Процедура выполняет действия и ничего не возвращает. Всё остальное — параметры, Экспорт, тело — одинаково.
Нужно ли объявлять тип переменной?
Нет. Тип определяется автоматически по присвоенному значению. Объявление через Перем задаёт только имя, но не тип. Одна и та же переменная может по ходу кода хранить число, потом строку.
Почему запрос быстрее цикла по справочнику?
Запрос выполняется на стороне СУБД, которая оптимизирована под выборку и использует индексы. Перебор всех объектов в цикле тянет каждый объект в память по одному — на больших данных это в сотни раз медленнее.
Что означает вертикальная черта в тексте запроса?
Это не часть языка запросов, а разделитель строк многострочной строковой константы в коде 1С. Внутри самого запроса он игнорируется — можно писать текст и одной строкой без черт.