Excel → документ → провести
Самый частый сценарий в строительном учёте. Данные приходят из Excel: наименования квартир, площади, цены. Нужно создать документ выпуска, заполнить ТЧ, провести. Весь сценарий — 25 строк.
EnsureGroup +
EnsureBatch —
создадут если нет.
A1sDocs.Of —
объект в памяти, ещё не в базе.LoadRows —
строки «Квартира 101» → ссылки автоматически.A1sDocs.Post
— один вызов вместо Записать(РежимЗаписиДокумента.Проведение).// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 1: Excel → Выпуск продукции → Провести // ════════════════════════════════════════════════════════════════════════════ // ── Шаг 1: Данные из Excel (в реальности — цикл по ТД) ────────────────── ДанныеExcel = A1sAR.Of( A1sDS.Of("Номенклатура", "Квартира 101", "Количество", 1, "Сумма", 3500000, "Площадь", 42.5), A1sDS.Of("Номенклатура", "Квартира 102", "Количество", 1, "Сумма", 3100000, "Площадь", 38.0), A1sDS.Of("Номенклатура", "Квартира 201", "Количество", 1, "Сумма", 4400000, "Площадь", 55.2)); // ── Шаг 2: Убедиться что справочники готовы ─────────────────────────────── ГрКвартиры = A1sCatalogs.EnsureGroup("Номенклатура", "Квартиры — корпус А"); // Список уникальных наименований из данных ИменаНом = Новый Массив; Для Каждого Стр Из ДанныеExcel Цикл ИменаНом.Добавить(Стр.Номенклатура); КонецЦикла; // Find-or-create сразу все — один раз до цикла загрузки A1sCatalogs.EnsureBatch("Номенклатура", A1sAR.RemoveDuplicates(ИменаНом), A1sDS.Of("Родитель", ГрКвартиры)); // ── Шаг 3: Создать документ ─────────────────────────────────────────────── Dok = A1sDocs.Of("ВыпускПродукцииУслуг", A1sDS.Of( "Дата", '20260201', "Организация", МояОрг, "Склад", СкладГП, "Комментарий", "Корпус А — февраль 2026")); // ── Шаг 4: Загрузить строки — строки → ссылки автоматически ────────────── КолСтрок = A1sDocs.LoadRows(Dok, "Продукция", ДанныеExcel); Сообщить("Загружено строк: " + КолСтрок); // ── Шаг 5: Провести ─────────────────────────────────────────────────────── Ссылка = A1sDocs.Post(Dok); Сообщить("✓ Выпуск создан и проведён: " + Ссылка);
LoadRows найдёт уже готовые ссылки в кэше — без N дополнительных запросов внутри цикла.
JSON с чужими именами полей → документ
Стандартная ситуация при интеграции с внешними системами. Одна функция
LoadRowsMapped
заменяет цикл с ручным маппингом, Ensure для каждой ссылки и AddRow.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 2: JSON с чужими именами → документ реализации // ════════════════════════════════════════════════════════════════════════════ // ── Входные данные из API (поля с английскими именами) ──────────────────── ДанныеAPI = A1sAR.Of( A1sDS.Of("item", "Квартира 101", "unit", "шт", "qty", 1, "price", 3500000, "vat", 20), A1sDS.Of("item", "Квартира 102", "unit", "шт", "qty", 1, "price", 3100000, "vat", 20)); // ── Схема маппинга: ключ = имя в JSON, значение = реквизит ТЧ ───────────── // Объявляем ОДИН РАЗ — при изменении API меняем только здесь МаппингТЧ = A1sDS.Of( "item", "Номенклатура", // строка → ссылка через метаданные ТЧ "unit", "ЕдиницаИзмерения", // строка → ссылка "qty", "Количество", // число → как есть "price", "Цена"); // число → как есть // Поле "vat" не указано в схеме — будет скопировано с именем "vat" // Если в ТЧ нет такого реквизита — платформа пропустит без ошибки // ── Шапка документа ─────────────────────────────────────────────────────── Dok = A1sDocs.Of("РеализацияТоваровУслуг", A1sDS.Of( "Дата", ТекущаяДата(), "Контрагент", КонтрагентСсылка, "Договор", ДоговорСсылка)); // ── LoadRowsMapped: rename + авторазрешение ссылок — одна строка ────────── A1sDocs.LoadRowsMapped(Dok, "Товары", ДанныеAPI, МаппингТЧ); Ссылка = A1sDocs.Post(Dok); Сообщить("✓ Реализация: " + Ссылка);
A1sDS.RenameFields (переименовать), затем
LoadRows (добавить с авторазрешением). Можно вызывать эти два шага отдельно если нужна
промежуточная обработка данных между ними.
MapRow — декларативный маппинг строк
A1sCatalogs.MapRow
— альтернатива LoadRowsMapped когда нужна тонкая настройка.
Схема явно указывает имя справочника для каждого поля.
В отличие от LoadRowsMapped, который определяет справочник из метаданных ТЧ,
MapRow принимает имя справочника напрямую — больше контроля, больше явности.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 3: MapRow — явный маппинг с именами справочников // ════════════════════════════════════════════════════════════════════════════ // ── Данные из CSV/JSON с текстовыми значениями вместо ссылок ───────────── ДанныеCSV = A1sAR.Of( A1sDS.Of("Номенклатура", "Квартира 101", "ЕдиницаИзмерения", "шт", "Количество", 1, "Сумма", 3500000), A1sDS.Of("Номенклатура", "Квартира 102", "ЕдиницаИзмерения", "шт", "Количество", 1, "Сумма", 3100000)); // ── Схема маппинга: ключ = имя поля, значение = ИМЯ СПРАВОЧНИКА ────────── // Отличие от LoadRowsMapped: здесь явно указываем в какой справочник идти СхемаМаппинга = A1sDS.Of( "Номенклатура", "Номенклатура", // строка → справочник Номенклатура "ЕдиницаИзмерения", "КлассификаторЕдиницИзмерения"); // строка → КЕИ // Поля "Количество" и "Сумма" — не в схеме, копируются как есть (числа) // ── Создать документ ────────────────────────────────────────────────────── Dok = A1sDocs.Of("ВыпускПродукцииУслуг", A1sDS.Of("Дата", '20260201', "Организация", МояОрг)); // ── Цикл с MapRow: трансформировать каждую строку перед AddRow ──────────── Для Каждого СтрокаCSV Из ДанныеCSV Цикл // MapRow: строки → ссылки согласно схеме, остальное — как есть ГотоваяСтрока = A1sCatalogs.MapRow(СтрокаCSV, СхемаМаппинга); A1sDocs.AddRow(Dok, "Продукция", ГотоваяСтрока); КонецЦикла; Ссылка = A1sDocs.Post(Dok); Сообщить("✓ Документ: " + Ссылка); // ── MapRow без схемы: просто скопировать все поля как структуру ─────────── // Полезно когда нужно передать строку ТЗ в AddRow без ТаблицыЗначений СтрокаДляДока = A1sCatalogs.MapRow(СтрокаТЗ); // scheme=Неопределено A1sDocs.AddRow(Dok, "Продукция", СтрокаДляДока);
LoadRowsMapped — один вызов для всего
массива, определяет справочник из метаданных ТЧ автоматически. MapRow — на каждую строку
отдельно, справочник указываете явно. Используйте MapRow когда нужна логика между строками
или явный контроль над именами справочников.Ночная синхронизация прайс-листа
Классический upsert. EnsureUpdate
находит элемент и обновляет его реквизиты, или создаёт новый если не нашёл.
Счётчики через A1sDS.OfKeys,
обработка ошибок через Попытка.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 4: Ночная синхронизация прайс-листа // EnsureUpdate: найти → обновить реквизиты. Не найти → создать с реквизитами // ════════════════════════════════════════════════════════════════════════════ // ── Прайс-лист из CSV (в реальности — парсинг файла) ───────────────────── ПрайсЛист = A1sAR.Of( A1sDS.Of("Наименование", "Квартира 101", "Цена", 3750000, "Площадь", 42.5, "Статус", "Свободна"), A1sDS.Of("Наименование", "Квартира 102", "Цена", 3250000, "Площадь", 38.0, "Статус", "Продана"), A1sDS.Of("Наименование", "Квартира 305", "Цена", 5100000, "Площадь", 67.0, "Статус", "Свободна")); // Квартира 305 — новая, её нет в базе // ── Убедиться что группа существует ────────────────────────────────────── ГрКвартиры = A1sCatalogs.EnsureGroup("Номенклатура", "Квартиры — корпус А"); // ── Счётчики операций ───────────────────────────────────────────────────── Счётчик = A1sDS.OfKeys("Обновлено, Создано, Ошибок", 0); Ошибки = Новый Массив; // ── Основной цикл синхронизации ─────────────────────────────────────────── Для Каждого Позиция Из ПрайсЛист Цикл Попытка // EnsureUpdate: нашёл → обновит Цену и Площадь // не нашёл → создаст с этими реквизитами БылаСсылка = A1sCatalogs.ByName("Номенклатура", Позиция.Наименование); ЭтоОбновление = ЗначениеЗаполнено(БылаСсылка); A1sCatalogs.EnsureUpdate( "Номенклатура", Позиция.Наименование, A1sDS.Of( "Цена", Позиция.Цена, // всегда обновляется "Площадь", Позиция.Площадь, "Статус", Позиция.Статус, "Родитель", ГрКвартиры)); // для новых Если ЭтоОбновление Тогда Счётчик.Обновлено = Счётчик.Обновлено + 1; Иначе Счётчик.Создано = Счётчик.Создано + 1; КонецЕсли; Исключение Счётчик.Ошибок = Счётчик.Ошибок + 1; A1sAR.AddIf(Ошибки, Позиция.Наименование + ": " + ОписаниеОшибки(), Истина); КонецПопытки; КонецЦикла; // ── Итоговый отчёт ──────────────────────────────────────────────────────── Сообщить("Синхронизация завершена:"); Сообщить(" ✓ Обновлено: " + Счётчик.Обновлено); Сообщить(" + Создано: " + Счётчик.Создано); Сообщить(" ✗ Ошибок: " + Счётчик.Ошибок); Если НЕ A1sAR.IsEmpty(Ошибки) Тогда Сообщить("Детали ошибок: " + A1sAR.Join(Ошибки, "; ")); КонецЕсли;
на строку
не-нашёл
задублировать
PickFromDocument — постобработка после загрузки
PickFromDocument
собирает уникальные ссылки из поля ТЧ документа — без написания запроса вручную.
Идеально для постобработки: загрузили пачку документов → обновили все упомянутые объекты.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 5: Загрузить акты выпуска → обновить статус квартир // ════════════════════════════════════════════════════════════════════════════ // ── Шаг 1: Данные актов выпуска (массив структур) ───────────────────────── АктыExcel = /* ... выгрузка из Excel ... */; ЗагруженныеДоки = Новый Массив; // ── Шаг 2: Загрузить каждый акт как документ ───────────────────────────── Для Каждого АктСтрока Из АктыExcel Цикл Dok = A1sDocs.Of("ВыпускПродукцииУслуг", A1sDS.Of("Дата", АктСтрока.Дата, "Организация", МояОрг)); A1sDocs.LoadRows(Dok, "Продукция", АктСтрока.Строки); Ссылка = A1sDocs.Post(Dok); ЗагруженныеДоки.Добавить(Ссылка); КонецЦикла; Сообщить("Загружено документов: " + ЗагруженныеДоки.Количество()); // ── Шаг 3: Постобработка — собрать ВСЕ квартиры из ВСЕХ документов ──────── ВсеУникальныеКвартиры = Новый Массив; Для Каждого ДокСсылка Из ЗагруженныеДоки Цикл // PickFromDocument: ВЫБРАТЬ РАЗЛИЧНЫЕ ТЧ.Номенклатура из ТЧ "Продукция" КвартирыДока = A1sCatalogs.PickFromDocument( ДокСсылка, "Продукция", "Номенклатура"); ВсеУникальныеКвартиры = A1sAR.Concatenate(ВсеУникальныеКвартиры, КвартирыДока); КонецЦикла; // Убрать дубли (одна квартира могла быть в нескольких документах) ВсеУникальныеКвартиры = A1sAR.RemoveDuplicates(ВсеУникальныеКвартиры); Сообщить("Уникальных квартир во всех документах: " + ВсеУникальныеКвартиры.Количество()); // ── Шаг 4: Обновить статус всех квартир разом ──────────────────────────── Итог = A1sCatalogs.UpdateAll( ВсеУникальныеКвартиры, A1sDS.Of( "Статус", ПредопрСтатусСдан, "ЭтапСтроительства", ЭтапСдан, "ДатаСдачи", ТекущаяДата())); Сообщить("✓ Статус обновлён у " + Итог.Success.Количество() + " квартир"); Если Итог.Failed.Количество() > 0 Тогда Сообщить("✗ Ошибок: " + Итог.Failed.Количество()); КонецЕсли;
500 документов пачками по 50
A1sAR.Chunk
разбивает список на пачки.
Каждая пачка — отдельная транзакция. Если пачка упала — потеряли только её, не весь массив.
PostAll
проводит всю пачку и возвращает статистику.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 6: Массовое создание и проведение 500+ документов пачками // ════════════════════════════════════════════════════════════════════════════ // ── Шаг 1: Подготовить данные — массив структур-шапок ──────────────────── ВсеАкты = /* ... 500 строк из Excel ... */; Сообщить("Всего актов к загрузке: " + ВсеАкты.Количество()); // ── Шаг 2: Сначала убедиться что вся номенклатура в базе есть ───────────── ВсеИмена = Новый Массив; Для Каждого Акт Из ВсеАкты Цикл Для Каждого Стр Из Акт.Строки Цикл ВсеИмена.Добавить(Стр.Номенклатура); КонецЦикла; КонецЦикла; // Один батч-вызов для всей номенклатуры из всех документов A1sCatalogs.EnsureBatch("Номенклатура", A1sAR.RemoveDuplicates(ВсеИмена), A1sDS.Of("Родитель", ГрКвартиры)); // ── Шаг 3: Создать все документы (они пока только в памяти) ─────────────── СозданныеСсылки = Новый Массив; Для Каждого АктДанные Из ВсеАкты Цикл Dok = A1sDocs.Of("ВыпускПродукцииУслуг", A1sDS.Of("Дата", АктДанные.Дата, "Организация", МояОрг, "Склад", СкладГП)); A1sDocs.LoadRows(Dok, "Продукция", АктДанные.Строки); СозданныеСсылки.Добавить(A1sDocs.Write(Dok)); // записать, но не проводить КонецЦикла; Сообщить("Записано документов: " + СозданныеСсылки.Количество()); // ── Шаг 4: Провести пачками по 50 ───────────────────────────────────────── ВсегоУспешно = 0; ВсегоОшибок = 0; Пачки = A1sAR.Chunk(СозданныеСсылки, 50); Для Каждого Пачка Из Пачки Цикл Итог = A1sDocs.PostAll(Пачка); // провести пачку, не останавливаться на ошибке ВсегоУспешно = ВсегоУспешно + Итог.Success.Количество(); ВсегоОшибок = ВсегоОшибок + Итог.Failed.Количество(); Сообщить("Пачка: +" + Итог.Success.Количество() + " проведено, ✗" + Итог.Failed.Количество() + " ошибок"); ОбработкаПрерыванияПользователя(); // дать платформе «подышать» КонецЦикла; Сообщить("════ Итог ════"); Сообщить("✓ Проведено: " + ВсегоУспешно + " из " + СозданныеСсылки.Количество()); Сообщить("✗ Ошибок: " + ВсегоОшибок);
Write) — это быстро.
Потом проводим пачками (PostAll). Если проведение одного упало — документ остался в базе
записанным, его можно перепровести отдельно. Это безопаснее чем создавать-и-сразу-проводить в одном цикле.
Найти и исправить непроведённые
Диагностический паттерн. Находим непроведённые, проверяем причины, пытаемся исправить,
формируем отчёт. Использует AllUnposted, Details, PostAll,
A1sAR.AddIf + Join.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 7: Найти непроведённые → диагностировать → провести → отчёт // ════════════════════════════════════════════════════════════════════════════ // ── Шаг 1: Найти все непроведённые за месяц ────────────────────────────── Непроведённые = A1sDocs.AllUnposted( "ВыпускПродукцииУслуг", '20260201', '20260228'); Сообщить("Непроведённых документов: " + Непроведённые.Количество()); Если A1sAR.IsEmpty(Непроведённые) Тогда Сообщить("✓ Все документы проведены"); Возврат; КонецЕсли; // ── Шаг 2: Диагностика каждого ─────────────────────────────────────────── ПустаяТЧ = Новый Массив; // документы с пустой ТЧ — главная причина НетСклада = Новый Массив; // не заполнен склад МожноПровести = Новый Массив; // всё ок, просто не нажали "провести" Для Каждого ДокСсылка Из Непроведённые Цикл Инфо = A1sDocs.Details(ДокСсылка, "Склад"); КолСтрок = A1sDocs.RowCount(ДокСсылка, "Продукция"); Если КолСтрок = 0 Тогда ПустаяТЧ.Добавить(ДокСсылка); ИначеЕсли НЕ ЗначениеЗаполнено(Инфо.Склад) Тогда НетСклада.Добавить(ДокСсылка); Иначе МожноПровести.Добавить(ДокСсылка); КонецЕсли; КонецЦикла; Сообщить(" Пустая ТЧ: " + ПустаяТЧ.Количество()); Сообщить(" Нет склада: " + НетСклада.Количество()); Сообщить(" Можно провести: " + МожноПровести.Количество()); // ── Шаг 3: Исправить то что можно — заполнить склад ────────────────────── Если НЕ A1sAR.IsEmpty(НетСклада) Тогда A1sDocs.UpdateAll(НетСклада, A1sDS.Of("Склад", СкладГП)); // Добавить исправленные к очереди на проведение МожноПровести = A1sAR.Concatenate(МожноПровести, НетСклада); КонецЕсли; // ── Шаг 4: Провести все исправленные ───────────────────────────────────── Если НЕ A1sAR.IsEmpty(МожноПровести) Тогда Итог = A1sDocs.PostAll(МожноПровести); Сообщить("✓ Проведено: " + Итог.Success.Количество()); Сообщить("✗ Не удалось: " + Итог.Failed.Количество()); КонецЕсли; // ── Шаг 5: Финальный отчёт ─────────────────────────────────────────────── ОстаётсяНепроведённых = A1sDocs.AllUnposted("ВыпускПродукцииУслуг", '20260201', '20260228'); Сообщить("════ После обработки: непроведённых " + ОстаётсяНепроведённых.Количество() + " ════");
Ежемесячный шаблон-документ
A1sDocs.Copy
копирует документ целиком — шапку и все ТЧ.
Передаёте только то, что хотите изменить. Остальное берётся из оригинала.
Плюс паттерн проверки: не создавать дубль если документ за этот месяц уже есть.
// ════════════════════════════════════════════════════════════════════════════ // ПАТТЕРН 8: Создать выпуск текущего месяца на основе прошлого // ════════════════════════════════════════════════════════════════════════════ // ── Определить период текущего месяца ──────────────────────────────────── НачМесяца = НачалоМесяца(ТекущаяДата()); КонМесяца = КонецМесяца(ТекущаяДата()); // ── Защита от дублей: проверить что ещё не создан ──────────────────────── УжеСуществует = A1sDocs.Exists( "ВыпускПродукцииУслуг", "Дата МЕЖДУ &НачМес И &КонМес И Организация = &Орг", A1sDS.Of("НачМес", НачМесяца, "КонМес", КонМесяца, "Орг", МояОрг)); Если УжеСуществует Тогда Сообщить("✗ Выпуск за текущий месяц уже создан — отмена"); Возврат; КонецЕсли; // ── Найти шаблон — последний проведённый выпуск за прошлый месяц ───────── НачПрошлого = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1)); КонПрошлого = КонецМесяца(ДобавитьМесяц(ТекущаяДата(), -1)); ВыпускиПрошлогоМесяца = A1sDocs.AllPosted( "ВыпускПродукцииУслуг", НачПрошлого, КонПрошлого); Шаблон = A1sAR.Last(ВыпускиПрошлогоМесяца); // последний проведённый Если Шаблон = Неопределено Тогда Сообщить("✗ Шаблон не найден: нет проведённых выпусков за прошлый месяц"); Возврат; КонецЕсли; Сообщить("Шаблон: " + A1sDocs.Details(Шаблон).Number); // ── Скопировать документ, изменить только дату и комментарий ───────────── НовыйВыпуск = A1sDocs.Copy( Шаблон, A1sDS.Of( "Дата", НачМесяца, "Комментарий", "Авто: " + Формат(ТекущаяДата(), "ДФ=ММММ ГГГГ"))); // ТЧ, организация, склад — скопированы из шаблона без изменений // ── Провести ────────────────────────────────────────────────────────────── Ссылка = A1sDocs.Post(НовыйВыпуск); Сообщить("✓ Создан выпуск: " + A1sDocs.Details(Ссылка).Number + " от " + Формат(НачМесяца, "ДФ=дд.ММ.гггг"));
Exists — защита от дублей. Last —
безопасно взять последний элемент без проверки размера массива. Copy — скопировать с
минимальным overrides. Эта тройка встречается в большинстве шаблонных операций.