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. Эта тройка встречается в большинстве шаблонных операций.