Примеры кода на встроенном языке 1С 8.3
Содержание
Первое заблуждение новичка: «код на 1С — это как в Python, только по-русски». На деле встроенный язык 1С заточен не под алгоритмы вообще, а под учётные данные — справочники, документы, регистры. Отсюда и специфика примеров: половина кода в реальной базе — это не циклы, а запросы.
Ниже — рабочие фрагменты для 1С 8.3: от объявления переменной до соединения таблиц в запросе. Каждый пример можно скопировать кнопкой в углу блока и вставить в модуль. Синтаксис русский, но всё сказанное верно и для англоязычного варианта конфигуратора.
Синтаксис и структура: с чего состоит код на 1С
Код живёт в модулях — модуль формы, модуль объекта, общий модуль, модуль менеджера. Внутри модуля — процедуры и функции, между ними может стоять область объявления переменных.
Базовые правила, которые экономят часы отладки:
- 🔹 каждая инструкция заканчивается точкой с запятой
;; - 🔹 регистр в ключевых словах не важен:
Если,ЕСЛИ,если— одно и то же; - 🔹 комментарий — две косые черты
//до конца строки; - 🔹 строки соединяются плюсом:
"Итого: " + Сумма; - 🔹 имена переменных — по-русски или латиницей, но без пробелов.
// Это комментарий
Процедура ПриветМир()
Сообщить("Привет, 1С!"); // вывод в окно сообщений
КонецПроцедуры
Метод Сообщить() устарел в клиент-серверном контексте. В новых конфигурациях используйте объект Новый СообщениеПользователю, но для быстрых примеров Сообщить() подходит.
Переменные и типы данных: практический код
Переменные в 1С типизируются динамически — тип определяется присвоенным значением, объявлять его заранее не нужно.
Количество = 10; // Число
Наименование = "Ноутбук"; // Строка
ДатаПродажи = '20240115'; // Дата (формат ГГГГММДД)
ЕстьСкидка = Истина; // Булево
Ссылка = Неопределено; // пустое значение
Часто встречающиеся типы и их пустые значения:
| Тип | Пример литерала | Пустое значение |
|---|---|---|
| Число | 123.45 | 0 |
| Строка | "текст" | "" |
| Дата | '20240115120000' | '00010101' |
| Булево | Истина / Ложь | — |
| Ссылка | — | Справочники.Номенклатура.ПустаяСсылка() |
Переменную можно объявить явно через Перем — это нужно, когда значение задаётся ниже по коду или переменная общая для нескольких процедур модуля.
Перем КэшКурсов; // объявление в начале модуля
Условия и ветвление: оператор Если и тернарный оператор
Полная форма ветвления — Если … ИначеЕсли … Иначе … КонецЕсли.
Если Сумма > 100000 Тогда
Скидка = 15;
ИначеЕсли Сумма > 50000 Тогда
Скидка = 10;
ИначеЕсли Сумма > 10000 Тогда
Скидка = 5;
Иначе
Скидка = 0;
КонецЕсли;
Когда веток всего две, короче написать тернарный оператор ?(условие, значение1, значение2):
Скидка = ?(Сумма > 100000, 15, 0);
Знак = ?(Остаток < 0, "долг", "аванс");
Логические операторы пишутся словами: И, ИЛИ, НЕ.
Если Клиент.Активен И НЕ Клиент.ПометкаУдаления Тогда
// работаем с клиентом
КонецЕсли;
Циклы Для и Пока: примеры перебора
Три вида циклов покрывают почти любую задачу.
Цикл со счётчиком — когда известно число повторений:
Для Индекс = 1 По 10 Цикл
Сообщить("Строка № " + Индекс);
КонецЦикла;
Цикл обхода коллекции Для Каждого — перебор таблицы значений, массива, выборки:
Итого = 0;
Для Каждого Строка Из ТаблицаТоваров Цикл
Итого = Итого + Строка.Сумма;
КонецЦикла;
Цикл с условием Пока — пока условие истинно:
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование);
КонецЦикла;
Управление ходом цикла — два ключевых слова:
Продолжить— пропустить текущую итерацию, перейти к следующей;Прервать— выйти из цикла полностью.
Для Каждого Строка Из ТаблицаТоваров Цикл
Если Строка.Количество = 0 Тогда
Продолжить; // пустые строки пропускаем
КонецЕсли;
Если Строка.Сумма > Лимит Тогда
Прервать; // нашли превышение — выходим
КонецЕсли;
ОбработатьСтроку(Строка);
КонецЦикла;
Цикл Пока без условия выхода зависит от вас: забыли изменить условие внутри — база уйдёт в вечный цикл. Всегда проверяйте, что условие когда-нибудь станет ложным.
Процедуры и функции: исходный код с комментариями
Процедура выполняет действия и ничего не возвращает. Функция возвращает значение через Возврат.
// Функция считает и возвращает результат
Функция ВычислитьСумму(Цена, Количество) Экспорт
Возврат Цена * Количество;
КонецФункции
// Процедура выполняет действие
Процедура ПоказатьИтог(Знач Сумма)
Сообщить("Итого: " + Формат(Сумма, "ЧЦ=15; ЧДЦ=2"));
КонецПроцедуры
Что здесь важно:
- 📌
Экспортделает метод видимым из других модулей; - 📌
Значперед параметром запрещает менять его внутри — передаётся копия; - 📌 параметру можно задать значение по умолчанию:
Функция Расчёт(Ставка = 20).
Обработка ошибок: Попытка и Исключение
Опасный код (деление, разбор строки, обращение к внешнему ресурсу) оборачивают в Попытка … Исключение … КонецПопытки.
Попытка
Результат = Числитель / Делитель;
Исключение
Результат = 0;
Сообщить("Ошибка расчёта: " + ОписаниеОшибки());
КонецПопытки;
Без такой обёртки исключение прерывает всю операцию. Внутри блока Исключение доступна функция ОписаниеОшибки() с текстом проблемы.
Работа с запросами: код решений на языке запросов
В учётной базе данные лежат в таблицах СУБД, и доставать их циклом по всем объектам — медленно и неправильно. Правильный инструмент — запрос.
Базовая структура запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Артикул + " — " + Выборка.Наименование);
КонецЦикла;
Вертикальная черта | в начале строк — это не синтаксис запроса, а способ 1С хранить многострочный текст. Псевдоним через КАК задаёт имя колонки в результате.
Параметры в запросах
Значения из кода передают только через параметры — подстановка строк в текст запроса открывает дыру и ломается на кавычках.
Запрос.Текст =
"ВЫБРАТЬ Продажи.Номенклатура, Продажи.Сумма
|ИЗ РегистрНакопления.Продажи.Обороты КАК Продажи
|ГДЕ Продажи.Период МЕЖДУ &Начало И &Конец";
Запрос.УстановитьПараметр("Начало", НачалоДня(ДатаОтчёта));
Запрос.УстановитьПараметр("Конец", КонецДня(ДатаОтчёта));
Соединение таблиц
Данные из двух таблиц связывают соединением. Самое частое — ЛЕВОЕ СОЕДИНЕНИЕ: берём все строки первой таблицы и подтягиваем совпадения из второй.
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Количество КАК Количество,
| Цены.Цена КАК Цена
|ИЗ
| Документ.Реализация.Товары КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
| ПО Товары.Номенклатура = Цены.Номенклатура"
Агрегирование и группировка
Итоги считают агрегатными функциями СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ вместе с СГРУППИРОВАТЬ ПО.
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| СУММА(Товары.Сумма) КАК ВсегоСумма,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Ссылка) КАК ЧислоДокументов
|ИЗ Документ.Реализация.Товары КАК Товары
|СГРУППИРОВАТЬ ПО Товары.Номенклатура
|УПОРЯДОЧИТЬ ПО ВсегоСумма УБЫВ"
Временные таблицы
Сложную выборку разбивают на шаги через временные таблицы: сначала ПОМЕСТИТЬ, потом обращаемся к результату как к обычной таблице.
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ Товары.Номенклатура, СУММА(Товары.Сумма) КАК Сумма
|ПОМЕСТИТЬ ВТ_Продажи
|ИЗ Документ.Реализация.Товары КАК Товары
|СГРУППИРОВАТЬ ПО Товары.Номенклатура";
Запрос.Выполнить();
Быстрые советы по оптимизации запроса:
- ✅ фильтруйте в
ГДЕ, а не в цикле после выборки; - ✅ для остатков и оборотов берите виртуальные таблицы (
.Остатки,.Обороты), а не таблицу движений; - ✅ не пишите
ВЫБРАТЬ *— перечисляйте только нужные поля; - ✅ избегайте соединений с вложенными запросами — выносите их во временные таблицы.
Работа с формами и объектами: частые приёмы
Клиент и сервер: директивы компиляции
Код на форме исполняется в двух контекстах. На клиенте нельзя обращаться к базе, на сервере — к элементам интерфейса. Контекст задаётся директивой над процедурой.
&НаКлиенте
Процедура ЗаполнитьНажатие(Команда)
ЗагрузитьДанныеНаСервере(); // вызываем серверный метод
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьДанныеНаСервере()
// здесь доступны запросы и объекты базы
КонецПроцедуры
Определение вида объекта и составной тип
Когда реквизит хранит составной тип (например, «получатель» — то ли контрагент, то ли сотрудник), проверяют конкретный тип функцией ТипЗнч().
Если ТипЗнч(Реквизит) = Тип("СправочникСсылка.Контрагенты") Тогда
Сообщить("Это контрагент");
ИначеЕсли ТипЗнч(Реквизит) = Тип("СправочникСсылка.Сотрудники") Тогда
Сообщить("Это сотрудник");
КонецЕсли;
Запись в журнал регистрации
Служебные события удобно логировать в журнал регистрации — потом их видно в Все функции → Журнал регистрации.
ЗаписьЖурналаРегистрации(
"Обработка.Загрузка", // источник
УровеньЖурналаРегистрации.Информация, // уровень
, , ,
"Загружено строк: " + КоличествоСтрок); // текст
Работа со строками таблицы значений
// Добавить строку
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = ВыбраннаяНоменклатура;
НоваяСтрока.Количество = 1;
// Удалить строки по условию (перебор с конца!)
Для Индекс = Таблица.Количество() - 1 По 0 Цикл
Если Таблица[Индекс].Количество = 0 Тогда
Таблица.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Удаляя строки в цикле, идите с конца к началу. Если удалять по возрастанию индекса, коллекция «съезжает» и часть строк пропускается.
Типичные ошибки в коде на 1С и как их избежать
Ошибки, на которые уходит больше всего времени у новичков:
- 🔴 Забыли
;в конце строки → «Ожидается конец оператора». Смотрите на строку выше указанной в ошибке. - 🔴
Для КаждоговместоДля Каждого— опечатка в ключевом слове не подсвечивается заранее, вылезает при запуске. - 🔴 Обращение к базе на клиенте → «Обращение к процедуре сервера на клиенте». Перенесите код под
&НаСервере. - 🔴 Строки в текст запроса конкатенацией вместо
УстановитьПараметр— падает на кавычках и датах. - 🔴 Сравнение с
Неопределеночерез=для несуществующего реквизита — проверяйтеЗначениеЗаполнено(Реквизит). - 🔴 Цикл по всей базе вместо запроса — на тысячах объектов это минуты вместо миллисекунд.
Частые вопросы
На каком языке пишут код в 1С 8.3?
Где писать код — в Конфигураторе или в пользовательском режиме?
Чем процедура отличается от функции?
Возврат и её результат можно присвоить переменной. Процедура выполняет действия и ничего не возвращает. Всё остальное — параметры, Экспорт, тело — одинаково.Нужно ли объявлять тип переменной?
Перем задаёт только имя, но не тип. Одна и та же переменная может по ходу кода хранить число, потом строку.