Суперкод без стены листингов
Выберите бизнес-сценарий слева. Сначала поймите намерение и контракт, затем прочитайте один активный пример.
Создать и провести документ
Самый частый сценарий — создать документ, заполнить шапку, добавить строки в табличную часть и провести. Раньше это 40 строк, теперь 8.
До / послеСравнить с классическим кодом 1С+
// 40 строк, куча служебного кода НовыйДок = Документы.ВыпускПродукцииУслуг .СоздатьДокумент(); НовыйДок.Дата = ТекущаяДата(); НовыйДок.Организация = Орг; НовыйДок.Склад = Склад; НовыйДок.Комментарий = "Загружено из Excel"; НоваяСтрока = НовыйДок.Продукция.Добавить(); НоваяСтрока.Номенклатура = НомСсылка; НоваяСтрока.Количество = 1; НоваяСтрока.Сумма = 3500000; // ... повторить для каждой строки ... НовыйДок.Записать( РежимЗаписиДокумента.Проведение); СсылкаНаДок = НовыйДок.Ссылка;
// MILA · Намерение: собрать и провести выпуск без ручного управления объектом // Результат: шапка, строки и проведение выражены тремя самостоятельными действиями // Описываем шапку документа как данные, а не как последовательность присваиваний Док = A1sDocs.Of("ВыпускПродукцииУслуг", A1sDS.Of( "Дата", '20260201', "Организация", Орг, "Склад", Склад, "Комментарий", "Загружено из Excel")); // Загружаем весь набор строк одной операцией: исходная таблица остаётся источником истины A1sDocs.LoadRows(Док, "Продукция", ДанныеExcel); // Проводим только полностью подготовленный документ и получаем ссылку Ссылка = A1sDocs.Post(Док);
// ─────────────────────────────────────────────────
// Шаг 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: он посмотрит на тип реквизита в метаданных ТЧ и сам вызовет EnsureByType. Если у вас уже есть ссылки — используйте обычный AddRow, это быстрее.Загрузка и синхронизация справочника
Получили Excel с квартирами. Нужно найти существующие и создать те, которых нет. Классика — 60+ строк с обработкой ошибок. Теперь 10.
// ─────────────────────────────────────────────────
// Шаг 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( // реквизиты — только при создании
"Родитель", ГрКвартиры,
"Площадь", Строка.Площадь,
"Этаж", Строка.Этаж));
Сообщить(Строка.Наименование + " → " + Ссылка);
КонецЦикла;
// Нужны ссылки на номенклатурные группы — просто передаём имена
Имена = A1sAR.Of(
"ПРОЖИВАНИЕ",
"ПИТАНИЕ",
"КОММУНАЛЬНЫЕ УСЛУГИ",
"ПРОЧИЕ УСЛУГИ");
// EnsureBatch: для каждого имени — найти или создать
// Возвращает массив ссылок в том же порядке
Ссылки = A1sCatalogs.EnsureBatch(
"НоменклатурныеГруппы",
Имена);
// Результат: массив [Ссылка, Ссылка, Ссылка, Ссылка]
// Ссылки[0] = группа "ПРОЖИВАНИЕ"
// Ссылки[1] = группа "ПИТАНИЕ" и т.д.
ГрПроживание = Ссылки[0];
ГрПитание = Ссылки[1];
// Прайс-лист из внешней системы (например, из 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 — провести пачку документов за раз, обновить поле у всех, получить статистику ошибок.
// Шаг 1: Получаем все НЕПРОВЕДЁННЫЕ выпуски за февраль
НепроведённыеВыпуски = A1sDocs.AllUnposted(
"ВыпускПродукцииУслуг",
'20260201', // начало периода
'20260228'); // конец периода
Сообщить("Найдено непроведённых: " + НепроведённыеВыпуски.Количество());
// Шаг 2: Провести все одной командой
// StopOnError = Ложь — продолжать даже если один упадёт
Итог = A1sDocs.PostAll(НепроведённыеВыпуски, Ложь);
// Шаг 3: Статистика
Сообщить("✓ Проведено: " + Итог.Success.Количество());
Сообщить("✗ С ошибками: " + Итог.Failed.Количество());
// Вывести что именно упало
Для Каждого ДокСсылка Из Итог.Failed Цикл
Сообщить(" Ошибка: " + ДокСсылка);
КонецЦикла;
// Все выпуски за 1-й квартал 2026
ВыпускиQ1 = A1sDocs.AllInPeriod(
"ВыпускПродукцииУслуг",
'20260101',
'20260331');
// Переназначить ответственного всем документам разом
A1sDocs.UpdateAll(
ВыпускиQ1,
A1sDS.Of("Ответственный", НовыйОтветственный));
// Можно обновить несколько реквизитов сразу
A1sDocs.UpdateAll(
ВыпускиQ1,
A1sDS.Of(
"Комментарий", "Q1 2026 — скорректировано",
"Ответственный", НовыйОтветственный));
Fluent-цепочки — код как предложение
Fluent API — это когда каждый метод возвращает обёртку и позволяет вызывать следующий. Читается как инструкция на русском языке.
// Таблица значений уже готова (например, выгрузили из Excel)
// ТЗКвартиры содержит колонки: Номенклатура, Количество, Сумма
СсылкаНаДок = A1sDocs.On( // создаём обёртку
"ВыпускПродукцииУслуг",
A1sDS.Of(
"Дата", '20260201',
"Организация", МояОрг))
.Set("Комментарий", "Квартиры — корпус А") // добавляем реквизит
.Set("Склад", СкладГотовой) // ещё один
.AddRows("Продукция", ТЗКвартиры) // загружаем ТЧ
.Post(); // провести и вернуть ссылку
Сообщить("Документ создан: " + СсылкаНаДок);
// Создать элемент справочника шаг за шагом
КварталСсылка = 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 запросов.
// 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);
Ensure на каждую строку — 1000 строк × 2 справочника = 2000 запросов.
С EnsureMapByNameQ до цикла — ровно 2 запроса на весь пакет, независимо от его размера.
// После загрузки 20 документов нужно поставить статус "Сдан"
// всем квартирам, которые упоминаются в документах
Для Каждого ДокСсылка Из ЗагруженныеДокументы Цикл
// Собираем уникальные ссылки на номенклатуру из ТЧ "Продукция"
НоменклатураВДоке = A1sCatalogs.PickFromDocument(
ДокСсылка,
"Продукция",
"Номенклатура");
// Всем найденным — поставить статус и этап строительства
A1sCatalogs.UpdateAll(
НоменклатураВДоке,
A1sDS.Of(
"Статус", ПредопрСтатус.Сдан,
"ЭтапСтроительства", ЭтапСдан));
КонецЦикла;
Поиск и анализ
Найти документ по номеру, проверить существование, получить реквизиты одним запросом без открытия объекта.
// Найти выпуск по номеру
ВыпускСсылка = A1sDocs.ByNumber(
"ВыпускПродукцииУслуг",
"ВП-000042");
Если НЕ ЗначениеЗаполнено(ВыпускСсылка) Тогда
Сообщить("Документ не найден");
Возврат;
КонецЕсли;
// Получить нужные реквизиты ОДНИМ ЗАПРОСОМ — без открытия объекта
Реквизиты = A1sDocs.Details(
ВыпускСсылка,
"Организация, Склад, Комментарий, Ответственный");
Сообщить("Организация: " + Реквизиты.Организация);
Сообщить("Комментарий: " + Реквизиты.Комментарий);
Сообщить("Проведён: " + A1sDocs.IsPosted(ВыпускСсылка));
// Поиск по реквизиту — найти первый выпуск с нужной организацией
ПервыйВыпуск = A1sDocs.ByAttr(
"ВыпускПродукцииУслуг",
"Организация",
МояОрганизация);
// Запрос с произвольным WHERE — без написания запроса вручную
ВыпускиСклада = A1sDocs.Refs(
"ВыпускПродукцииУслуг",
"Склад = &Склад И Дата >= &НачалоМесяца",
A1sDS.Of(
"Склад", МойСклад,
"НачалоМесяца", '20260201'));
// ─────────────────────────────────────────────────
// Задача: подготовить кэш перед загрузкой пакета данных.
// 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 (пример ниже).
// Задача: проверить, есть ли уже эти контрагенты в базе.
// НЕ создаём — только читаем. Нет в базе → не будет в Map.
ИменаКА = A1sAR.Of(
"ООО Ромашка",
"ИП Иванов",
"АО Незнакомец"); // этого нет в базе
// FindMapByNameQ: 1 запрос, только найденные попадут в Map
МапКА = A1sCatalogs.FindMapByNameQ(
"Контрагенты",
ИменаКА);
// Разбить на найденные и не найденные
Для Каждого Имя Из ИменаКА Цикл
Если МапКА.Получить(Имя) <> Неопределено Тогда
Сообщить("✓ Найден: " + Имя);
Иначе
Сообщить("✗ Не найден: " + Имя);
КонецЕсли;
КонецЦикла;
// Итог: "✓ Найден: ООО Ромашка", "✓ Найден: ИП Иванов"
// "✗ Не найден: АО Незнакомец" ← не создан, просто отсутствует
EnsureMapByNameQ — при загрузке данных (справочники должны существовать, создаём недостающие).
FindMapByNameQ — при валидации и проверке (нужно знать, что уже есть, ничего не создаём).
Структуры и массивы как инструмент
A1sDS и 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"
// Значения по умолчанию + пользовательские данные
// 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(Данные, "Организация, Склад, Дата") Тогда
ВызватьИсключение "Не заполнены обязательные поля";
КонецЕсли;
Fluent Logging — логирование внутри цепочки
Новый Boilerplate в A1sDP_Catalogs и A1sDP_Docs: методы .Comment(), .LogInfo(), .LogError() встраиваются прямо в Fluent-цепочку. Код не просто пишет данные — он описывает историю того, что делает.
До / послеСравнить с классическим кодом 1С+
A1sCatalogs.OnRef(Ссылка) .ClearRows("Штрихкоды") .LoadTabRows("Штрихкоды", Данные) .Write(); // Отдельно, после цепочки :( Сообщить("Обновлено: " + Ссылка); A1sLog.Info("Номенклатура", "Штрихкоды обновлены");
A1sCatalogs.OnRef(Ссылка) .ClearRows("Штрихкоды") .Comment("Старые коды очищены") .LoadTabRows("Штрихкоды", Данные) .Comment("Загружено: " + N) .LogComment() // → ЖР .ShowComment() // → пользователю .Write();
ShowComment() — выводятся пользователю, при LogComment() — пишутся в Журнал Регистрации.// Было: логирование отдельными строками вне цепочки
// Стало: комментарий встроен туда, где происходит действие
A1sCatalogs.OnRef(СсылкаНоменклатуры)
.Comment("Начало обновления штрихкодов") // накапливается в буфере
.ClearRows("Штрихкоды")
.Comment("Очищены старые коды") // добавляется через "; "
.LoadTabRows("Штрихкоды", ТоварИзJSON.barcodes)
.Comment("Загружено " + ТоварИзJSON.barcodes.Количество() + " кодов")
.ShowComment() // → сообщение пользователю
.Write();
// Вывод: "[Номенклатура] Ноутбук Pro: Начало обновления штрихкодов;
// Очищены старые коды; Загружено 2 кодов"
// Если нужен тихий режим — только в Журнал Регистрации
A1sCatalogs.OnRef(СсылкаНоменклатуры)
.Comment("Синхронизация цены: " + НоваяЦена)
.Set("Цена", НоваяЦена)
.LogComment() // → A1sLog.Info (ЖР, без окна)
.Write();
Comment, эти методы пишут в Журнал Регистрации сразу — не накапливая. Идеально для диагностики и аудита операций.// Пример 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() накапливает текст в буфере — выводится только при явном ShowComment() или LogComment().
.LogInfo() / .LogWarning() / .LogError() пишут в A1sLog немедленно, без буферизации.
Используйте Comment для составных операций, Log — для событий с однозначным моментом возникновения.
Сквозная синхронизация — эталонный пример
Полный production-ready пример: загрузка каталога из JSON, кэширование справочников через EnsureMapByNameQ, Upsert через EnsureUpdate с AutoResolve, загрузка штрихкодов через Fluent-цепочку с логированием. Объединяет все техники на одном сценарии.
// =========================================================================
// Функция: ВыполнитьСинхронизациюНоменклатуры
// Паттерн: 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;
КонецПопытки;
КонецЦикла;
Сообщить("✓ Обновлено: " + Статистика.Обновлено + ", ошибок: " + Статистика.Ошибок);
Возврат Статистика;
КонецФункции