A1sCode v2.3.0 · Кулинарная книга

Примеры суперкода
для не-программистов

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

Создать и провести документ

Самый частый сценарий — создать документ, заполнить шапку, добавить строки в табличную часть и провести. Раньше это 40 строк, теперь 8.

✗ Раньше (классический 1С)
// 40 строк, куча служебного кода
НовыйДок = Документы.ВыпускПродукцииУслуг
    .СоздатьДокумент();
НовыйДок.Дата = ТекущаяДата();
НовыйДок.Организация = Орг;
НовыйДок.Склад = Склад;
НовыйДок.Комментарий = "Загружено из Excel";

НоваяСтрока = НовыйДок.Продукция.Добавить();
НоваяСтрока.Номенклатура = НомСсылка;
НоваяСтрока.Количество = 1;
НоваяСтрока.Сумма = 3500000;
// ... повторить для каждой строки ...

НовыйДок.Записать(
    РежимЗаписиДокумента.Проведение);
СсылкаНаДок = НовыйДок.Ссылка;
✓ Теперь (A1sCode)
// Шапка одной строкой
Дата = '20260201';

Dok = A1sDocs.Of("ВыпускПродукцииУслуг",
    A1sDS.Of(
        "Дата",         Дата,
        "Организация",  Орг,
        "Склад",        Склад,
        "Комментарий",  "Загружено из Excel"));

// Строка ТЧ одной строкой
A1sDocs.AddRow(Dok, "Продукция",
    A1sDS.Of(
        "Номенклатура", НомСсылка,
        "Количество",   1,
        "Сумма",        3500000));

Ссылка = A1sDocs.Post(Dok);
01
Загрузка выпуска продукции из массива строк
Создаём документ, добавляем несколько квартир в ТЧ, проводим. Имена номенклатуры передаём строками — EnsureByType сам найдёт/создаст ссылки.
A1sDocs A1sDS A1sAR
 1С:Предприятие 8.3+
// ─────────────────────────────────────────────────
// Шаг 1: Список квартир — просто строки
// ─────────────────────────────────────────────────
Квартиры = A1sAR.Of(
    A1sDS.Of("Название", "Квартира 101", "Площадь", 42.5, "Цена", 3500000),
    A1sDS.Of("Название", "Квартира 102", "Площадь", 38.0, "Цена", 3100000),
    A1sDS.Of("Название", "Квартира 103", "Площадь", 55.2, "Цена", 4400000));

// ─────────────────────────────────────────────────
// Шаг 2: Создаём документ — шапка одной структурой
// ─────────────────────────────────────────────────
Dok = A1sDocs.Of("ВыпускПродукцииУслуг",
    A1sDS.Of(
        "Дата",        '20260201',
        "Организация", МояОрганизация,   // ссылка из переменной
        "Склад",       СкладГотовойПрод,
        "Комментарий", "Корпус А — февраль 2026"));

// ─────────────────────────────────────────────────
// Шаг 3: Добавляем строки в ТЧ
// AddRowAuto — сам разрешает "Квартира 101" в ссылку
// по типу реквизита Номенклатура из метаданных ТЧ
// ─────────────────────────────────────────────────
Для Каждого Квартира Из Квартиры Цикл
    A1sDocs.AddRowAuto(Dok, "Продукция",
        A1sDS.Of(
            "Номенклатура", Квартира.Название,  // строка → ссылка автоматом
            "Количество",   1,
            "Сумма",        Квартира.Цена));
КонецЦикла;

// ─────────────────────────────────────────────────
// Шаг 4: Провести — одна строка
// ─────────────────────────────────────────────────
СсылкаНаВыпуск = A1sDocs.Post(Dok);
Сообщить("✓ Выпуск создан: " + СсылкаНаВыпуск);
💡
AddRowAuto vs AddRow. Если вы передаёте строку (наименование) в поле-ссылку — используйте AddRowAuto: он посмотрит на тип реквизита в метаданных ТЧ и сам вызовет EnsureByType. Если у вас уже есть ссылки — используйте обычный AddRow, это быстрее.

Загрузка и синхронизация справочника

Получили Excel с квартирами. Нужно найти существующие и создать те, которых нет. Классика — 60+ строк с обработкой ошибок. Теперь 10.

02
Find-or-Create: загрузить список номенклатуры
Ensure — ищет по наименованию, создаёт если не найден. Идеально для загрузки из внешних источников.
A1sCatalogs A1sDS A1sAR
 Загрузка номенклатуры
// ─────────────────────────────────────────────────
// Шаг 1: Убедимся, что группа существует
// EnsureGroup — найдёт или создаст группу
// ─────────────────────────────────────────────────
ГрКвартиры = A1sCatalogs.EnsureGroup(
    "Номенклатура",
    "Квартиры — корпус А");

// ─────────────────────────────────────────────────
// Шаг 2: Для каждой квартиры из Excel:
// Ensure = найти по наименованию ИЛИ создать
// ─────────────────────────────────────────────────
Данные = A1sAR.Of(
    A1sDS.Of("Наименование", "Квартира 101", "Площадь", 42.5, "Этаж", 3),
    A1sDS.Of("Наименование", "Квартира 102", "Площадь", 38.0, "Этаж", 3),
    A1sDS.Of("Наименование", "Квартира 201", "Площадь", 42.5, "Этаж", 4));

Для Каждого Строка Из Данные Цикл
    Ссылка = A1sCatalogs.Ensure(
        "Номенклатура",
        Строка.Наименование,
        A1sDS.Of(                     // реквизиты — только при создании
            "Родитель",  ГрКвартиры,
            "Площадь",   Строка.Площадь,
            "Этаж",      Строка.Этаж));

    Сообщить(Строка.Наименование + " → " + Ссылка);
КонецЦикла;
03
EnsureBatch — массово, одной командой
Если нужны просто ссылки без дополнительных реквизитов — EnsureBatch возвращает массив ссылок параллельно входному массиву имён.
A1sCatalogs new v2.1
 Массовое создание
// Нужны ссылки на номенклатурные группы — просто передаём имена
Имена = A1sAR.Of(
    "ПРОЖИВАНИЕ",
    "ПИТАНИЕ",
    "КОММУНАЛЬНЫЕ УСЛУГИ",
    "ПРОЧИЕ УСЛУГИ");

// EnsureBatch: для каждого имени — найти или создать
// Возвращает массив ссылок в том же порядке
Ссылки = A1sCatalogs.EnsureBatch(
    "НоменклатурныеГруппы",
    Имена);

// Результат: массив [Ссылка, Ссылка, Ссылка, Ссылка]
// Ссылки[0] = группа "ПРОЖИВАНИЕ"
// Ссылки[1] = группа "ПИТАНИЕ" и т.д.
ГрПроживание = Ссылки[0];
ГрПитание    = Ссылки[1];
04
EnsureUpdate — Upsert: обновить или создать
Для регулярной синхронизации — прайс-листы, ГИС ЖКХ, выгрузки из CRM. В отличие от Ensure, EnsureUpdate всегда обновляет реквизиты найденного элемента.
A1sCatalogs new v2.1
 Upsert-синхронизация
// Прайс-лист из внешней системы (например, из JSON)
// Задача: обновить цены у существующих, создать новые

ПрайсЛист = A1sAR.Of(
    A1sDS.Of("Наименование", "Квартира 101", "Цена", 3750000, "Площадь", 42.5),
    A1sDS.Of("Наименование", "Квартира 102", "Цена", 3250000, "Площадь", 38.0),
    A1sDS.Of("Наименование", "Квартира 305", "Цена", 5100000, "Площадь", 67.0));  // новая!

Для Каждого Позиция Из ПрайсЛист Цикл
    // EnsureUpdate: нашёл → обновит Цену и Площадь; не нашёл → создаст
    A1sCatalogs.EnsureUpdate(
        "Номенклатура",
        Позиция.Наименование,
        A1sDS.Of(
            "Цена",    Позиция.Цена,
            "Площадь", Позиция.Площадь,
            "Родитель", ГрКвартиры));
КонецЦикла;

Сообщить("✓ Синхронизировано: " + ПрайсЛист.Количество() + " позиций");

Массовые операции над документами

PostAll, UnpostAll, UpdateAll — провести пачку документов за раз, обновить поле у всех, получить статистику ошибок.

05
PostAll — провести все документы за период
Получаем список непроведённых выпусков за месяц и проводим все. PostAll возвращает структуру {Success, Failed} — удобно для отчёта.
A1sDocs A1sDS
 Пакетное проведение
// Шаг 1: Получаем все НЕПРОВЕДЁННЫЕ выпуски за февраль
НепроведённыеВыпуски = A1sDocs.AllUnposted(
    "ВыпускПродукцииУслуг",
    '20260201',   // начало периода
    '20260228');  // конец периода

Сообщить("Найдено непроведённых: " + НепроведённыеВыпуски.Количество());

// Шаг 2: Провести все одной командой
// StopOnError = Ложь — продолжать даже если один упадёт
Итог = A1sDocs.PostAll(НепроведённыеВыпуски, Ложь);

// Шаг 3: Статистика
Сообщить("✓ Проведено: "    + Итог.Success.Количество());
Сообщить("✗ С ошибками: "  + Итог.Failed.Количество());

// Вывести что именно упало
Для Каждого ДокСсылка Из Итог.Failed Цикл
    Сообщить("  Ошибка: " + ДокСсылка);
КонецЦикла;
06
UpdateAll — изменить поле у нескольких документов сразу
Нужно переназначить ответственного у всех документов за квартал — одна строка вместо цикла с GetObject/Записать.
A1sDocs
 Массовое обновление реквизитов
// Все выпуски за 1-й квартал 2026
ВыпускиQ1 = A1sDocs.AllInPeriod(
    "ВыпускПродукцииУслуг",
    '20260101',
    '20260331');

// Переназначить ответственного всем документам разом
A1sDocs.UpdateAll(
    ВыпускиQ1,
    A1sDS.Of("Ответственный", НовыйОтветственный));

// Можно обновить несколько реквизитов сразу
A1sDocs.UpdateAll(
    ВыпускиQ1,
    A1sDS.Of(
        "Комментарий",   "Q1 2026 — скорректировано",
        "Ответственный", НовыйОтветственный));

Fluent-цепочки — код как предложение

Fluent API — это когда каждый метод возвращает обёртку и позволяет вызывать следующий. Читается как инструкция на русском языке.

07
Создать документ цепочкой .On().Set().AddRows().Post()
Fluent-обёртка A1sDP_Docs: создаём документ, добавляем реквизиты, загружаем ТЧ из таблицы, проводим — в одном выражении.
A1sDocs Fluent
 Fluent API документов
// Таблица значений уже готова (например, выгрузили из Excel)
// ТЗКвартиры содержит колонки: Номенклатура, Количество, Сумма

СсылкаНаДок = A1sDocs.On(                     // создаём обёртку
        "ВыпускПродукцииУслуг",
        A1sDS.Of(
            "Дата",       '20260201',
            "Организация", МояОрг))
    .Set("Комментарий", "Квартиры — корпус А")   // добавляем реквизит
    .Set("Склад", СкладГотовой)               // ещё один
    .AddRows("Продукция", ТЗКвартиры)          // загружаем ТЧ
    .Post();                                    // провести и вернуть ссылку

Сообщить("Документ создан: " + СсылкаНаДок);
08
Fluent для справочников: A1sCatalogs.On()
Создать или обновить элемент справочника шаг за шагом, используя цепочку .Set().Write().
A1sCatalogs Fluent
 Fluent API справочников
// Создать элемент справочника шаг за шагом
КварталСсылка = A1sCatalogs.On("Номенклатура")
    .Set("Наименование", "Квартира 401")
    .Set("Родитель",     ГрКвартиры)
    .Set("Площадь",     48.5)
    .Set("Этаж",        5)
    .Write();

Сообщить("Создана: " + КварталСсылка);

// Или Fluent для структур — A1sDS.On()
// Удобно строить структуру условно
Параметры = A1sDS.On()
    .Set("Организация", МояОрг)
    .Set("Дата",        ТекущаяДата())
    .Set("Режим",       "Авто")
    .Done();   // → обычная Структура

Загрузка из JSON — MapRow + EnsureMapByNameQ

Получили JSON из API с наименованиями вместо ссылок. Правильный паттерн: сначала один запрос кэшируем все справочники через EnsureMapByNameQ, затем MapRow декларативно собирает строки — без N+1 запросов.

09
MapRow + EnsureMapByNameQ — быстрая загрузка из JSON
Двухэтапный паттерн: (1) кэшируем все справочники пакетом до цикла, (2) в цикле MapRow только достаёт из кэша. Ноль лишних запросов к БД.
A1sCatalogs A1sDocs new v2.3
 Кэш + MapRow: эталонный паттерн загрузки
// JSON из API выглядит так:
// [ { "Номенклатура": "Квартира 101", "Единица": "шт", "Количество": 1, "Сумма": 3500000 },
//   { "Номенклатура": "Квартира 102", "Единица": "шт", "Количество": 1, "Сумма": 3100000 } ]

// ─────────────────────────────────────────────────
// ШАГ 1: Pre-fetch кэша (ДО цикла, 1 запрос на каждый справочник)
// Собираем уникальные имена из всего пакета сразу
// ─────────────────────────────────────────────────
ИменаНом     = A1sAR.Distinct(A1sAR.Pick(ДанныеJSON, "Номенклатура"));
ИменаЕдиниц  = A1sAR.Distinct(A1sAR.Pick(ДанныеJSON, "Единица"));

// EnsureMapByNameQ: один SQL-запрос → Соответствие {Имя → Ссылка}
// Отсутствующие элементы создаются автоматически
КэшНом     = A1sCatalogs.EnsureMapByNameQ("Номенклатура",                    ИменаНом);
КэшЕдиниц  = A1sCatalogs.EnsureMapByNameQ("КлассификаторЕдиницИзмерения",    ИменаЕдиниц);

// ─────────────────────────────────────────────────
// ШАГ 2: Схема маппинга — декларативно, меняем только здесь
// Ключ = поле в JSON, Значение = Соответствие-кэш (Map)
// ─────────────────────────────────────────────────
СхемаМаппинга = A1sDS.Of(
    "Номенклатура", КэшНом,       // строка → ссылка из кэша (O(1))
    "Единица",      КэшЕдиниц);   // строка → ссылка из кэша (O(1))

// ─────────────────────────────────────────────────
// ШАГ 3: Создаём документ и загружаем строки
// MapRow в цикле только читает из кэша — запросов к БД нет!
// ─────────────────────────────────────────────────
Dok = A1sDocs.Of("РеализацияТоваровУслуг",
    A1sDS.Of("Дата", ТекущаяДата(), "Контрагент", КонтрагентСсылка));

Для Каждого СтрокаJSON Из ДанныеJSON Цикл
    // MapRow: строки → ссылки из кэша, числа/даты — как есть
    СтрокаДляДока = A1sCatalogs.MapRow(СтрокаJSON, СхемаМаппинга);
    A1sDocs.AddRow(Dok, "Товары", СтрокаДляДока);
КонецЦикла;

Ссылка = A1sDocs.Post(Dok);
🚀
Почему это быстро. Без кэша MapRow делал бы Ensure на каждую строку — 1000 строк × 2 справочника = 2000 запросов. С EnsureMapByNameQ до цикла — ровно 2 запроса на весь пакет, независимо от его размера.
10
PickFromDocument — постобработка после загрузки
Собрали все загруженные документы. Нужно обновить статус у всей номенклатуры, которая в них встречается.
A1sCatalogs new v2.1
 Постобработка после загрузки
// После загрузки 20 документов нужно поставить статус "Сдан"
// всем квартирам, которые упоминаются в документах

Для Каждого ДокСсылка Из ЗагруженныеДокументы Цикл

    // Собираем уникальные ссылки на номенклатуру из ТЧ "Продукция"
    НоменклатураВДоке = A1sCatalogs.PickFromDocument(
        ДокСсылка,
        "Продукция",
        "Номенклатура");

    // Всем найденным — поставить статус и этап строительства
    A1sCatalogs.UpdateAll(
        НоменклатураВДоке,
        A1sDS.Of(
            "Статус",            ПредопрСтатус.Сдан,
            "ЭтапСтроительства", ЭтапСдан));

КонецЦикла;

Поиск и анализ

Найти документ по номеру, проверить существование, получить реквизиты одним запросом без открытия объекта.

11
Найти документ и получить его реквизиты
Details() — получить несколько реквизитов документа одним запросом, без GetObject(). Быстро и экономично.
A1sDocs
 Поиск и чтение реквизитов
// Найти выпуск по номеру
ВыпускСсылка = A1sDocs.ByNumber(
    "ВыпускПродукцииУслуг",
    "ВП-000042");

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

// Получить нужные реквизиты ОДНИМ ЗАПРОСОМ — без открытия объекта
Реквизиты = A1sDocs.Details(
    ВыпускСсылка,
    "Организация, Склад, Комментарий, Ответственный");

Сообщить("Организация: " + Реквизиты.Организация);
Сообщить("Комментарий: " + Реквизиты.Комментарий);
Сообщить("Проведён:    " + A1sDocs.IsPosted(ВыпускСсылка));

// Поиск по реквизиту — найти первый выпуск с нужной организацией
ПервыйВыпуск = A1sDocs.ByAttr(
    "ВыпускПродукцииУслуг",
    "Организация",
    МояОрганизация);

// Запрос с произвольным WHERE — без написания запроса вручную
ВыпускиСклада = A1sDocs.Refs(
    "ВыпускПродукцииУслуг",
    "Склад = &Склад И Дата >= &НачалоМесяца",
    A1sDS.Of(
        "Склад",        МойСклад,
        "НачалоМесяца", '20260201'));
12
EnsureMapByNameQ — Map «имя → ссылка» за один запрос
Находит элементы по наименованию и создаёт отсутствующие. Возвращает Соответствие для всего пакета — 1 SQL-запрос вместо N. Основной инструмент кэширования перед массовой загрузкой.
A1sCatalogs new v2.3
 EnsureMapByNameQ — Upsert Map
// ─────────────────────────────────────────────────
// Задача: подготовить кэш перед загрузкой пакета данных.
// EnsureMapByNameQ = найти ИЛИ создать + вернуть Map
// Результат: Соответствие {Имя → Ссылка}
// ─────────────────────────────────────────────────

СписокГрупп = A1sAR.Of(
    "ПРОЖИВАНИЕ",
    "ПИТАНИЕ",
    "ПРОЧИЕ УСЛУГИ");

// Один запрос к БД на весь пакет.
// Группы, которых нет — будут созданы автоматически.
МапГрупп = A1sCatalogs.EnsureMapByNameQ(
    "НоменклатурныеГруппы",
    СписокГрупп);

// Доступ по имени — O(1), без дополнительных запросов
ГрПроживание = МапГрупп["ПРОЖИВАНИЕ"];
ГрПитание    = МапГрупп["ПИТАНИЕ"];

// ─────────────────────────────────────────────────
// Типичный паттерн: собрать уникальные имена из пакета,
// передать в EnsureMapByNameQ — классика любых загрузок
// ─────────────────────────────────────────────────
ВсеИменаЕдиниц = A1sAR.Distinct(
    A1sAR.Pick(ДанныеИмпорта, "unit_name"));   // ["шт", "кг", "м²"]

КэшЕдиниц = A1sCatalogs.EnsureMapByNameQ(
    "КлассификаторЕдиницИзмерения",
    ВсеИменаЕдиниц);

Для Каждого Строка Из ДанныеИмпорта Цикл
    // В цикле — только обращение к кэшу, запросов к БД нет
    Единица = КэшЕдиниц[Строка.unit_name];   // мгновенно
КонецЦикла;
Было PickByName — стало EnsureMapByNameQ. Старый PickByName только искал и возвращал Неопределено для отсутствующих. EnsureMapByNameQ гарантирует: каждый ключ в Map будет заполнен ссылкой — отсутствующие элементы создаются. Если нужен режим «только чтение» без создания — используйте FindMapByNameQ (пример ниже).
12b
FindMapByNameQ — Map только для существующих (read-only)
Не создаёт элементы. Возвращает Map только для найденных имён. Используйте для валидации входящих данных — узнать, что уже есть в базе, а что нет.
A1sCatalogs read-only new v2.3
 FindMapByNameQ — только поиск
// Задача: проверить, есть ли уже эти контрагенты в базе.
// НЕ создаём — только читаем. Нет в базе → не будет в Map.

ИменаКА = A1sAR.Of(
    "ООО Ромашка",
    "ИП Иванов",
    "АО Незнакомец");     // этого нет в базе

// FindMapByNameQ: 1 запрос, только найденные попадут в Map
МапКА = A1sCatalogs.FindMapByNameQ(
    "Контрагенты",
    ИменаКА);

// Разбить на найденные и не найденные
Для Каждого Имя Из ИменаКА Цикл
    Если МапКА.Получить(Имя) <> Неопределено Тогда
        Сообщить("✓ Найден: " + Имя);
    Иначе
        Сообщить("✗ Не найден: " + Имя);
    КонецЕсли;
КонецЦикла;

// Итог: "✓ Найден: ООО Ромашка", "✓ Найден: ИП Иванов"
//       "✗ Не найден: АО Незнакомец"  ← не создан, просто отсутствует
📌
Когда что использовать: EnsureMapByNameQ — при загрузке данных (справочники должны существовать, создаём недостающие). FindMapByNameQ — при валидации и проверке (нужно знать, что уже есть, ничего не создаём).

Структуры и массивы как инструмент

A1sDS и A1sAR — фундамент всей библиотеки. Несколько мощных паттернов для работы с данными.

13
Фильтрация, трансформация и агрегация массивов
Отобрать только заполненные ссылки, убрать дубликаты, склеить в строку — без ручных циклов.
A1sAR
 Работа с массивами
// Список из формы может содержать пустые значения и дубликаты
СырыеДанные = A1sAR.Of(
    Ном1, Неопределено, Ном2, Ном1, Неопределено, Ном3);

// Очистить: убрать пустые и дубликаты
ЧистыйМассив = A1sAR.PickFilled(                   // → убрать пустые
    A1sAR.RemoveDuplicates(СырыеДанные));          // → убрать дубликаты
// Результат: [Ном1, Ном2, Ном3]

// Объединить несколько массивов в один уникальный
ВсяНоменклатура = A1sAR.Concatenate(
    НомДокумент1, НомДокумент2, НомДокумент3,
    /*ТолькоУникальные =*/ Истина);

// Разбить массив на порции по 100 — для пакетной загрузки
Порции = A1sAR.Chunk(ВсяНоменклатура, 100);
Для Каждого Порция Из Порции Цикл
    // ... обработать порцию ...
КонецЦикла;

// Получить только первый и последний — без доступа по индексу
Первый  = A1sAR.First(ВсяНоменклатура);
Последний = A1sAR.Last(ВсяНоменклатура);

// Склеить имена в строку через запятую (для сообщений)
ИменаВСтрочку = A1sAR.Join(
    A1sAR.Of("Квартира 101", "Квартира 102", "Квартира 103"),
    ", ");
// Результат: "Квартира 101, Квартира 102, Квартира 103"
14
A1sDS — строить параметры запросов и конфигурацию
Merge, Defaults, Pick — формируем структуры данных без лишнего кода.
A1sDS
 Работа со структурами
// Значения по умолчанию + пользовательские данные
// Defaults заполняет только ОТСУТСТВУЮЩИЕ ключи
Умолчания = A1sDS.Of(
    "Валюта",    "RUB",
    "НДС",       Истина,
    "Проводить", Истина);

ДанныеПользователя = A1sDS.Of(
    "Валюта", "USD",   // переопределяем
    "Режим",  "Авто"); // новый ключ

Итоговые = A1sDS.Defaults(ДанныеПользователя, Умолчания);
// Итог: {Валюта:"USD", Режим:"Авто", НДС:Истина, Проводить:Истина}

// Переименовать поля (например, API возвращает другие имена)
ДанныеAPI = A1sDS.Of("name", "Квартира 101", "price", 3500000);
Переименовано = A1sDS.Rename(
    ДанныеAPI,
    A1sDS.Of("name", "Наименование", "price", "ЦенаПродажи"));
// Итог: {Наименование:"Квартира 101", ЦенаПродажи:3500000}

// Выбрать только нужные поля из большой структуры
ТолькоНужные = A1sDS.Pick(БольшаяСтруктура, "Дата, Организация, Сумма");

// Проверить что все обязательные поля заполнены
Если НЕ A1sDS.HasKeys(Данные, "Организация, Склад, Дата") Тогда
    ВызватьИсключение "Не заполнены обязательные поля";
КонецЕсли;
📌
Золотое правило. Если вы пишете цикл вручную — спросите себя: нет ли в A1sAR/A1sDS функции, которая делает то же самое? Chunk, Flatten, PickFilled, Join, Merge, Defaults — всё это убирает типовые циклы и делает код декларативным.

Fluent Logging — логирование внутри цепочки

Новый Boilerplate в A1sDP_Catalogs и A1sDP_Docs: методы .Comment(), .LogInfo(), .LogError() встраиваются прямо в Fluent-цепочку. Код не просто пишет данные — он описывает историю того, что делает.

15
.Comment() и .ShowComment() — аудиторский след в цепочке
Накапливаем пояснения прямо внутри Fluent-цепочки. При ShowComment() — выводятся пользователю, при LogComment() — пишутся в Журнал Регистрации.
A1sCatalogs Fluent new v2.3
 Comment + ShowComment
// Было: логирование отдельными строками вне цепочки
// Стало: комментарий встроен туда, где происходит действие

A1sCatalogs.OnRef(СсылкаНоменклатуры)
    .Comment("Начало обновления штрихкодов")        // накапливается в буфере
    .ClearRows("Штрихкоды")
    .Comment("Очищены старые коды")                 // добавляется через "; "
    .LoadTabRows("Штрихкоды", ТоварИзJSON.barcodes)
    .Comment("Загружено " + ТоварИзJSON.barcodes.Количество() + " кодов")
    .ShowComment()                                  // → сообщение пользователю
    .Write();
// Вывод: "[Номенклатура] Ноутбук Pro: Начало обновления штрихкодов;
//         Очищены старые коды; Загружено 2 кодов"

// Если нужен тихий режим — только в Журнал Регистрации
A1sCatalogs.OnRef(СсылкаНоменклатуры)
    .Comment("Синхронизация цены: " + НоваяЦена)
    .Set("Цена", НоваяЦена)
    .LogComment()                                   // → A1sLog.Info (ЖР, без окна)
    .Write();
16
.LogInfo() / .LogWarning() / .LogError() — немедленная запись в ЖР
В отличие от Comment, эти методы пишут в Журнал Регистрации сразу — не накапливая. Идеально для диагностики и аудита операций.
A1sCatalogs A1sDocs Fluent new v2.3
 LogInfo / LogWarning / LogError
// Пример 1: Тихое логирование — только ЖР, без окна пользователя
A1sCatalogs.On("Номенклатура")
    .Set("Наименование", "Товар из API")
    .Set("Артикул", "API-001")
    .LogInfo("Создание нового товара из внешнего API")  // → ЖР, уровень Info
    .Write();

// Пример 2: Предупреждение — цена подозрительно низкая
Если НоваяЦена < 1000 Тогда
    A1sCatalogs.OnRef(СсылкаТовар)
        .Set("Цена", НоваяЦена)
        .LogWarning("Аномально низкая цена: " + НоваяЦена)  // → ЖР, уровень Warn
        .Write();
КонецЕсли;

// Пример 3: Обработка ошибки в Попытка/Исключение
Попытка
    A1sDocs.On("РеализацияТоваровУслуг",
            A1sDS.Of("Контрагент", КА, "Дата", ТекущаяДата()))
        .AddRows("Товары", Строки)
        .LogInfo("Создание реализации для: " + КА)
        .Post();
Исключение
    // Логируем ошибку прямо там — не теряем контекст
    A1sDocs.On("РеализацияТоваровУслуг")
        .LogError("Ошибка создания реализации: " + ОписаниеОшибки());  // → ЖР, уровень Error
КонецПопытки;
📌
Разница между Comment и Log. .Comment() накапливает текст в буфере — выводится только при явном ShowComment() или LogComment(). .LogInfo() / .LogWarning() / .LogError() пишут в A1sLog немедленно, без буферизации. Используйте Comment для составных операций, Log — для событий с однозначным моментом возникновения.
✗ Было: логирование разрывает цепочку
A1sCatalogs.OnRef(Ссылка)
    .ClearRows("Штрихкоды")
    .LoadTabRows("Штрихкоды", Данные)
    .Write();
// Отдельно, после цепочки :(
Сообщить("Обновлено: " + Ссылка);
A1sLog.Info("Номенклатура",
    "Штрихкоды обновлены");
✓ Стало: всё в одной цепочке
A1sCatalogs.OnRef(Ссылка)
    .ClearRows("Штрихкоды")
    .Comment("Старые коды очищены")
    .LoadTabRows("Штрихкоды", Данные)
    .Comment("Загружено: " + N)
    .LogComment()  // → ЖР
    .ShowComment() // → пользователю
    .Write();

Сквозная синхронизация — эталонный пример

Полный production-ready пример: загрузка каталога из JSON, кэширование справочников через EnsureMapByNameQ, Upsert через EnsureUpdate с AutoResolve, загрузка штрихкодов через Fluent-цепочку с логированием. Объединяет все техники на одном сценарии.

17
High-Performance Sync: JSON → Номенклатура + Штрихкоды
Загружает тысячи позиций за секунды: гибридный кэш для горячих справочников, AutoResolve для динамических полей, Fail-Safe цикл, Fluent-запись штрихкодов с логом.
A1sCatalogs A1sDocs A1sDS A1sAR Fluent new v2.3
 Полный пример: синхронизация каталога
// =========================================================================
// Функция: ВыполнитьСинхронизациюНоменклатуры
// Паттерн: Batch-Cache → Upsert(AutoResolve) → Fluent TabRows → Log
// =========================================================================

&AtServer
Функция ВыполнитьСинхронизацию(ДанныеИмпорта) Экспорт

// ─────────────────────────────────────────────────
// 1. PRE-FETCH КЭША (1 запрос на справочник, ДО цикла)
// Горячие ссылки (группы, единицы) — через Map: максимальная скорость
// Динамические поля (виды, НДС) — через AutoResolve: максимальное удобство
// ─────────────────────────────────────────────────
ИменаГрупп  = A1sAR.Distinct(A1sAR.Pick(ДанныеИмпорта, "group_name"));
ИменаЕдиниц = A1sAR.Distinct(A1sAR.Pick(ДанныеИмпорта, "unit_name"));

// EnsureMapByNameQ: создаёт отсутствующие + возвращает Map за 1 запрос
КэшГрупп  = A1sCatalogs.EnsureMapByNameQ("НоменклатурныеГруппы",            ИменаГрупп);
КэшЕдиниц = A1sCatalogs.EnsureMapByNameQ("КлассификаторЕдиницИзмерения",    ИменаЕдиниц);

// ─────────────────────────────────────────────────
// 2. ОСНОВНОЙ ЦИКЛ — Upsert с гибридным подходом
// ─────────────────────────────────────────────────
Статистика = A1sDS.Of("Обновлено", 0, "Ошибок", 0);

Для Каждого Товар Из ДанныеИмпорта Цикл
    Попытка
        // Горячие поля — из кэша (O(1), без запросов)
        // Строковые поля — оставляем строками (AutoResolve разберёт)
        ДанныеТовара = A1sDS.Of(
            "Родитель",           КэшГрупп[Товар.group_name],   // ссылка из кэша
            "ЕдиницаИзмерения",   КэшЕдиниц[Товар.unit_name],   // ссылка из кэша
            "ВидНоменклатуры",    Товар.type_name,              // строка → AutoResolve
            "СтавкаНДС",          Товар.vat_rate,               // строка → AutoResolve
            "Цена",               Товар.price,
            "Артикул",            Товар.articul);

        // EnsureUpdate: нашёл → обновит; не нашёл → создаст
        // Параметр Истина, Истина = точное совпадение + AutoResolve строк
        Ссылка = A1sCatalogs.EnsureUpdate(
            "Номенклатура",
            Товар.name,
            ДанныеТовара,
            Истина,   // точное совпадение имени
            Истина);  // AutoResolve строковых полей

        // Fluent-обновление штрихкодов с Logging
        // ClearRows → LoadTabRows → Comment → Write
        Если Товар.Свойство("barcodes") И Товар.barcodes.Количество() > 0 Тогда
            A1sCatalogs.OnRef(Ссылка)
                .ClearRows("Штрихкоды")
                .LoadTabRows("Штрихкоды", Товар.barcodes)
                .Comment("Штрихкодов: " + Товар.barcodes.Количество())
                .LogComment()  // в ЖР — тихо, без окна пользователя
                .Write();
        КонецЕсли;

        Статистика.Обновлено = Статистика.Обновлено + 1;

    Исключение
        // Fail-Safe: один кривой товар не ломает весь импорт
        A1sLog.Error("Синхронизация", "Ошибка: " + Товар.name + " | " + ОписаниеОшибки());
        Статистика.Ошибок = Статистика.Ошибок + 1;
    КонецПопытки;
КонецЦикла;

Сообщить("✓ Обновлено: " + Статистика.Обновлено + ", ошибок: " + Статистика.Ошибок);
Возврат Статистика;

КонецФункции
🏆
Почему этот код «гордый». Гибридный кэш: горячие справочники — через Map (1 SQL), динамические — через AutoResolve (удобство). Идемпотентность: EnsureUpdate можно запускать повторно — дублей не создаст. Fail-Safe: Попытка/Исключение внутри цикла — один сбой не рушит всё. Самодокументируемость: код читается как техническое задание.