Кулинарная книга A1sCode · Часть 4

Паттерны —
сквозные сценарии

Части 1–3 показали отдельные функции. Здесь — полные рабочие сценарии: от получения данных до проведённого документа в базе. Каждый паттерн можно скопировать и адаптировать под свою конфигурацию.

8 паттернов Готово к копированию Реальные сценарии Все модули в связке
Загрузка данных
ПАТТЕРН 1

Excel → документ → провести

«У меня таблица Excel с квартирами. Хочу создать документ выпуска.»

Самый частый сценарий в строительном учёте. Данные приходят из Excel: наименования квартир, площади, цены. Нужно создать документ выпуска, заполнить ТЧ, провести. Весь сценарий — 25 строк.

1
Подготовить структуру данных из Excel
В реальности — выгрузка из табличного документа. Здесь показываем как данные выглядят.
2
Убедиться что группы и единицы измерения существуют
EnsureGroup + EnsureBatch — создадут если нет.
3
Создать документ с шапкой
A1sDocs.Of — объект в памяти, ещё не в базе.
4
Загрузить строки ТЧ с авторазрешением
LoadRows — строки «Квартира 101» → ссылки автоматически.
5
Провести и получить ссылку
A1sDocs.Post — один вызов вместо Записать(РежимЗаписиДокумента.Проведение).
Excel → ВыпускПродукцииУслуг · полный сценарий ~30 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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);
Сообщить("✓ Выпуск создан и проведён: " + Ссылка);
✓ 3 квартиры загружены, справочник пополнен, документ проведён — 30 строк кода
💡
EnsureBatch до цикла — ключевой приём. Вызываем один раз со всеми именами перед загрузкой строк. Тогда LoadRows найдёт уже готовые ссылки в кэше — без N дополнительных запросов внутри цикла.

ПАТТЕРН 2

JSON с чужими именами полей → документ

«API возвращает "item", "qty", "price" — а в ТЧ реквизиты "Номенклатура", "Количество", "Цена".»

Стандартная ситуация при интеграции с внешними системами. Одна функция LoadRowsMapped заменяет цикл с ручным маппингом, Ensure для каждой ссылки и AddRow.

JSON API → РеализацияТоваровУслуг · LoadRowsMapped ~25 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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);
Сообщить("✓ Реализация: " + Ссылка);
ℹ️
LoadRowsMapped внутри вызывает A1sDS.RenameFields (переименовать), затем LoadRows (добавить с авторазрешением). Можно вызывать эти два шага отдельно если нужна промежуточная обработка данных между ними.

ПАТТЕРН 3

MapRow — декларативный маппинг строк

«Разные поля — из разных справочников. Нужен контроль над каждым полем отдельно.»

A1sCatalogs.MapRow — альтернатива LoadRowsMapped когда нужна тонкая настройка. Схема явно указывает имя справочника для каждого поля. В отличие от LoadRowsMapped, который определяет справочник из метаданных ТЧ, MapRow принимает имя справочника напрямую — больше контроля, больше явности.

MapRow — явная схема: поле → справочник ~30 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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, "Продукция", СтрокаДляДока);
⚠️
MapRow vs LoadRowsMapped — когда что. LoadRowsMapped — один вызов для всего массива, определяет справочник из метаданных ТЧ автоматически. MapRow — на каждую строку отдельно, справочник указываете явно. Используйте MapRow когда нужна логика между строками или явный контроль над именами справочников.
Синхронизация
ПАТТЕРН 4

Ночная синхронизация прайс-листа

«Каждую ночь приходит CSV с ценами. Существующие — обновить. Новые — создать.»

Классический upsert. EnsureUpdate находит элемент и обновляет его реквизиты, или создаёт новый если не нашёл. Счётчики через A1sDS.OfKeys, обработка ошибок через Попытка.

Ночная синхронизация · EnsureUpdate + счётчики + ошибки ~40 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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(Ошибки, "; "));
КонецЕсли;
1
вызов EnsureUpdate
на строку
0
ручных Если-нашёл/
не-нашёл
запусков без страха
задублировать

ПАТТЕРН 5

PickFromDocument — постобработка после загрузки

«После загрузки актов нужно обновить статус всех квартир, которые в них упоминаются.»

PickFromDocument собирает уникальные ссылки из поля ТЧ документа — без написания запроса вручную. Идеально для постобработки: загрузили пачку документов → обновили все упомянутые объекты.

PickFromDocument → UpdateAll — постобработка пачки ~35 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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.Количество());
КонецЕсли;
Массовые операции
ПАТТЕРН 6

500 документов пачками по 50

«Нужно создать и провести 500 документов выпуска. Без зависаний и без потери данных при ошибке.»

A1sAR.Chunk разбивает список на пачки. Каждая пачка — отдельная транзакция. Если пачка упала — потеряли только её, не весь массив. PostAll проводит всю пачку и возвращает статистику.

Chunk + PostAll — 500 документов без зависаний ~50 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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 сначала, Post потом. Сначала записываем все документы (Write) — это быстро. Потом проводим пачками (PostAll). Если проведение одного упало — документ остался в базе записанным, его можно перепровести отдельно. Это безопаснее чем создавать-и-сразу-проводить в одном цикле.

ПАТТЕРН 7

Найти и исправить непроведённые

«Вечером обнаружили что за февраль 12 документов не проведены. Нужно разобраться.»

Диагностический паттерн. Находим непроведённые, проверяем причины, пытаемся исправить, формируем отчёт. Использует AllUnposted, Details, PostAll, A1sAR.AddIf + Join.

Диагностика + исправление непроведённых · полный цикл ~45 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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');
Сообщить("════ После обработки: непроведённых " + ОстаётсяНепроведённых.Количество() + " ════");
Копирование и шаблоны
ПАТТЕРН 8

Ежемесячный шаблон-документ

«Каждый месяц нужен одинаковый выпуск, только дата меняется. Хочу не делать это руками.»

A1sDocs.Copy копирует документ целиком — шапку и все ТЧ. Передаёте только то, что хотите изменить. Остальное берётся из оригинала. Плюс паттерн проверки: не создавать дубль если документ за этот месяц уже есть.

Copy + Exists — ежемесячный шаблон с защитой от дублей ~35 строк
// ════════════════════════════════════════════════════════════════════════════
// ПАТТЕРН 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. Exists — защита от дублей. Last — безопасно взять последний элемент без проверки размера массива. Copy — скопировать с минимальным overrides. Эта тройка встречается в большинстве шаблонных операций.