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

Реальные
проекты

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

6 проектов реальные имена конфигурации готово к адаптации 40–60 строк каждый
Строительство и девелопмент
1С:УНФ
Строительство
ПРОЕКТ 1

ЖК-застройщик: выпуск квартир + реализация

«Сдаём корпус А. Нужно создать выпуск продукции и реализацию покупателю.»

Два связанных документа: сначала выпуск продукции (квартиры сходят со склада строительства), затем реализация конкретному покупателю. Данные из Excel-файла отдела продаж.

ВыпускПродукцииУслуг РеализацияТоваровУслуг Номенклатура Контрагенты ДоговорыКонтрагентов
ЖК-застройщик · выпуск + реализация квартир ~60 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 1: ЖК-застройщик
// Excel: Квартира | Площадь | Покупатель | ИНН | Цена | ДатаДоговора
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Процедура ЗагрузитьСделкиКорпусаА(ДанныеExcel) Экспорт

    // ── Шаг 1: Справочные данные ──────────────────────────────────────────────
    ОргСсылка   = A1sCatalogs.ByName("Организации", "ООО СК Вершина");
    СкладГП     = A1sCatalogs.ByName("Склады", "Готовая продукция");
    СкладОтгрузки = A1sCatalogs.ByName("Склады", "Склад отгрузки");
    ГрКвартиры  = A1sCatalogs.EnsureGroup("Номенклатура", "Квартиры — Корпус А");
    ВидКвартира = A1sCatalogs.ByName("ВидыНоменклатуры", "Квартира");
    ЕдШт        = A1sCatalogs.ByCode("КлассификаторЕдиницИзмерения", "796");
    ВидДог      = A1sCatalogs.ByName("ВидыДоговоровКонтрагентов", "С покупателем");

    // ── Шаг 2: Создать всю номенклатуру одним батчем ─────────────────────────
    ИменаКвартир = Новый Массив;
    Для Каждого Стр Из ДанныеExcel Цикл
        ИменаКвартир.Добавить(Стр.Квартира);
    КонецЦикла;
    A1sCatalogs.EnsureBatch("Номенклатура",
        A1sAR.RemoveDuplicates(ИменаКвартир),
        A1sDS.Of("Родитель", ГрКвартиры, "ВидНоменклатуры", ВидКвартира, "ЕдиницаИзмерения", ЕдШт));

    // ── Шаг 3: Создать выпуск продукции — все квартиры корпуса ───────────────
    СтрокиВыпуска = Новый Массив;
    Для Каждого Стр Из ДанныеExcel Цикл
        СтрокиВыпуска.Добавить(A1sDS.Of(
            "Номенклатура", Стр.Квартира,
            "Количество",   1,
            "Сумма",        Стр.Цена,
            "Площадь",      Стр.Площадь));
    КонецЦикла;

    ВыпускСсылка = A1sDocs.On("ВыпускПродукцииУслуг",
            A1sDS.Of(
                "Дата",        ТекущаяДата(),
                "Организация", ОргСсылка,
                "Склад",       СкладГП,
                "Комментарий", "Корпус А — сдача " + Формат(ТекущаяДата(), "ДФ=ММММ ГГГГ")))
        .LoadRows("Продукция", СтрокиВыпуска)
        .Post();
    Сообщить("✓ Выпуск: " + ВыпускСсылка);

    // ── Шаг 4: Реализация каждому покупателю ─────────────────────────────────
    Счётчик = A1sDS.OfKeys("Проведено, Ошибок", 0);
    Для Каждого Стр Из ДанныеExcel Цикл
        Попытка
            // Найти или создать покупателя и договор
            Покупатель = A1sCatalogs.Ensure("Контрагенты", Стр.Покупатель,
                A1sDS.Of("ИНН", Стр.ИНН, "ВидКонтрагента", ВидФизЛицо));
            Договор = A1sCatalogs.EnsureUpdate("ДоговорыКонтрагентов",
                "ДДУ " + Стр.Квартира,
                A1sDS.Of("Владелец", Покупатель, "ВидДоговора", ВидДог,
                           "Дата", Стр.ДатаДоговора, "Сумма", Стр.Цена));

            РеалСсылка = A1sDocs.On("РеализацияТоваровУслуг",
                    A1sDS.Of(
                        "Дата",       ТекущаяДата(),
                        "Организация", ОргСсылка,
                        "Склад",      СкладОтгрузки,
                        "Контрагент", Покупатель,
                        "Договор",    Договор))
                .AddRow("Товары", A1sDS.Of(
                    "Номенклатура", A1sCatalogs.ByName("Номенклатура", Стр.Квартира),
                    "Количество",   1,
                    "Цена",         Стр.Цена,
                    "Сумма",        Стр.Цена))
                .Post();

            Счётчик.Проведено = Счётчик.Проведено + 1;
        Исключение
            Счётчик.Ошибок = Счётчик.Ошибок + 1;
            Сообщить("✗ " + Стр.Квартира + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;

    Сообщить("✓ Реализаций: " + Счётчик.Проведено + "  ✗ Ошибок: " + Счётчик.Ошибок);
КонецПроцедуры
✓ 1 выпуск + N реализаций (по числу квартир) · все справочники созданы · итог в сообщениях

1С:Бухгалтерия
Строительство
ПРОЕКТ 2

Загрузка актов КС-2 из Excel-выгрузки

«Подрядчик прислал Excel с актами. Поля называются иначе, чем в нашей базе.»

Классический случай: Excel-файл от подрядчика с нестандартными заголовками колонок. LoadRowsMapped переименовывает поля и разрешает ссылки. Один набор настроек на весь файл.

ПоступлениеТоваровУслуг Номенклатура Контрагенты СтавкиНДС
Загрузка КС-2 · LoadRowsMapped с нестандартными заголовками ~45 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 2: Акты КС-2 от подрядчика
// Excel-колонки: Работа | Ед | Кол | ЦенаЗаЕд | СуммаИтого | НДС%
// Наша ТЧ:       Номенклатура | ЕдИзм | Количество | Цена | Сумма | СтавкаНДС
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Функция ЗагрузитьАктКС2(ДанныеExcel, Подрядчик, НомерАкта, ДатаАкта) Экспорт

    // ── Шаг 1: Маппинг колонок Excel → реквизиты ТЧ ──────────────────────────
    // Объявляем один раз — при следующей выгрузке от того же подрядчика
    // достаточно проверить этот блок
    МаппингТЧ = A1sDS.Of(
        "Работа",     "Номенклатура",     // строка → ссылка на номенклатуру
        "Ед",         "ЕдиницаИзмерения", // строка → ссылка на КЕИ
        "Кол",         "Количество",
        "ЦенаЗаЕд",   "Цена",
        "СуммаИтого", "Сумма",
        "НДС%",       "СтавкаНДС");       // строка "20%" → ссылка на ставку НДС

    // ── Шаг 2: Убедиться в наличии группы работ в номенклатуре ───────────────
    ГрРаботы = A1sCatalogs.EnsureGroup("Номенклатура", "Строительные работы");

    // ── Шаг 3: EnsureBatch для уникальных работ ──────────────────────────────
    НазванияРабот = Новый Массив;
    Для Каждого Стр Из ДанныеExcel Цикл
        НазванияРабот.Добавить(Стр.Работа);
    КонецЦикла;
    A1sCatalogs.EnsureBatch("Номенклатура",
        A1sAR.RemoveDuplicates(НазванияРабот),
        A1sDS.Of("Родитель", ГрРаботы, "ВидНоменклатуры", ВидУслуга));

    // ── Шаг 4: Создать документ поступления ──────────────────────────────────
    Договор = A1sCatalogs.Refs("ДоговорыКонтрагентов",
        "Владелец = &К И ВидДоговора.Наименование = &Вид",
        A1sDS.Of("К", Подрядчик, "Вид", "С поставщиком"));
    ДоговорСсылка = A1sAR.First(Договор);

    ДокСсылка = A1sDocs.On("ПоступлениеТоваровУслуг",
            A1sDS.Of(
                "Дата",       ДатаАкта,
                "Номер",      НомерАкта,
                "Контрагент", Подрядчик,
                "Договор",    ДоговорСсылка,
                "Комментарий", "КС-2 от подрядчика"))
        .LoadRowsMapped("Услуги", ДанныеExcel, МаппингТЧ)
        .Post();

    Сообщить("✓ Акт КС-2 загружен: " + ДокСсылка);
    Возврат ДокСсылка;
КонецФункции
💡
Один маппинг на подрядчика. Создайте функцию-обёртку для каждого постоянного подрядчика с его маппингом. Изменился формат Excel — меняете только маппинг, логика загрузки не трогается.
Гостиница и общепит
1С:Отель
Гостиница
ПРОЕКТ 3

Гостиница: ежедневный акт проживания

«Каждую ночь нужно автоматически создавать акты на всех заехавших гостей.»

Получаем из журнала заезда список гостей. Для каждого — проверяем что акт за сегодня ещё не создан (защита от дублей), создаём, проводим. Работа за минуту вместо ручного ввода.

РеализацияТоваровУслуг Контрагенты НоменклатурныеГруппы ДоговорыКонтрагентов
Ежедневный акт проживания · Exists + On + Post ~50 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 3: Ежедневный акт проживания для всех гостей
// Запускается по расписанию в 23:59 или вручную
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Процедура СоздатьЕжедневныеАктыПроживания() Экспорт

    Сегодня     = НачалоДня(ТекущаяДата());
    КонецСегодня = КонецДня(ТекущаяДата());
    ОргСсылка   = A1sCatalogs.ByName("Организации", "ООО Гостиница Меридиан");

    // Номенклатурная группа "Проживание" — для документов
    НГПроживание = A1sCatalogs.EnsureGroup("НоменклатурныеГруппы", "ПРОЖИВАНИЕ");

    // Получаем текущих гостей из журнала заезда
    ТекущиеГости = A1sDocs.Refs("ЗаездГостя",
        "ДатаЗаезда <= &Сегодня И (ДатаВыезда >= &Сегодня ИЛИ ДатаВыезда = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0))",
        A1sDS.Of("Сегодня", Сегодня));

    Счётчик = A1sDS.OfKeys("Создано, Пропущено, Ошибок", 0);

    Для Каждого ЗаездСсылка Из ТекущиеГости Цикл
        Попытка
            ЗаездДанные = A1sDocs.Details(ЗаездСсылка,
                "Гость, ТипНомера, СтоимостьСуток, НомерКомнаты");

            // Защита от дублей: не создавать если акт за сегодня уже есть
            УжеЕсть = A1sDocs.Exists("РеализацияТоваровУслуг",
                "Контрагент = &Гость И Дата МЕЖДУ &Нач И &Кон И НЕ ПометкаУдаления",
                A1sDS.Of("Гость", ЗаездДанные.Гость,
                           "Нач", Сегодня, "Кон", КонецСегодня));
            Если УжеЕсть Тогда
                Счётчик.Пропущено = Счётчик.Пропущено + 1;
                Продолжить;
            КонецЕсли;

            // Найти или создать договор на гостя
            Договор = A1sCatalogs.Ensure("ДоговорыКонтрагентов",
                "Основной договор",
                A1sDS.Of("Владелец", ЗаездДанные.Гость, "Сумма", 0));

            // Услуга "Проживание: Стандарт" или "Проживание: Люкс" и т.д.
            Услуга = A1sCatalogs.Ensure("Номенклатура",
                "Проживание: " + ЗаездДанные.ТипНомера,
                A1sDS.Of("НоменклатурнаяГруппа", НГПроживание));

            A1sDocs.On("РеализацияТоваровУслуг",
                    A1sDS.Of(
                        "Дата",        Сегодня,
                        "Организация", ОргСсылка,
                        "Контрагент", ЗаездДанные.Гость,
                        "Договор",    Договор,
                        "Комментарий", "Номер " + ЗаездДанные.НомерКомнаты))
                .AddRow("Услуги", A1sDS.Of(
                    "Номенклатура", Услуга,
                    "Количество",   1,
                    "Цена",         ЗаездДанные.СтоимостьСуток,
                    "Сумма",        ЗаездДанные.СтоимостьСуток))
                .Post();

            Счётчик.Создано = Счётчик.Создано + 1;
        Исключение
            Счётчик.Ошибок = Счётчик.Ошибок + 1;
            Сообщить("✗ " + ЗаездСсылка + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;

    Сообщить("Акты проживания: создано " + Счётчик.Создано
        + ", пропущено " + Счётчик.Пропущено
        + ", ошибок "   + Счётчик.Ошибок);
КонецПроцедуры

1С:Общепит
Ресторан
ПРОЕКТ 4

Ресторан: загрузка меню из системы POS

«POS-система возвращает JSON с блюдами. Поля: dish_id, name, category, price, unit.»

Синхронизация меню из кассовой системы. Блюда — номенклатура с группировкой по категориям. EnsureUpdate обновляет цены у существующих, создаёт новые. Категории создаются автоматически как группы.

Номенклатура (блюда) НоменклатурныеГруппы КлассификаторЕдиницИзмерения
Синхронизация меню из POS-системы · EnsureUpdate + группировка ~45 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 4: Синхронизация меню из POS
// JSON: [{ "dish_id": "101", "name": "Борщ", "category": "Супы",
//           "price": 350, "unit": "порц" }, ...]
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Процедура СинхронизироватьМенюИзPOS(МенюJSON) Экспорт

    // ── Шаг 1: Собрать уникальные категории и создать группы ─────────────────
    УникальныеКатегории = Новый Массив;
    Для Каждого Блюдо Из МенюJSON Цикл
        УникальныеКатегории.Добавить(Блюдо.category);
    КонецЦикла;
    УникальныеКатегории = A1sAR.RemoveDuplicates(УникальныеКатегории);

    // Создаём группы для всех категорий
    ГруппыМеню = Новый Соответствие; // "Супы" → ссылка на группу
    Для Каждого Категория Из УникальныеКатегории Цикл
        ГруппыМеню.Вставить(Категория,
            A1sCatalogs.EnsureGroup("Номенклатура", Категория));
    КонецЦикла;

    // ── Шаг 2: Единицы измерения — батчем ────────────────────────────────────
    УникальныеЕд = Новый Массив;
    Для Каждого Блюдо Из МенюJSON Цикл
        УникальныеЕд.Добавить(Блюдо.unit);
    КонецЦикла;
    A1sCatalogs.EnsureBatch("КлассификаторЕдиницИзмерения",
        A1sAR.RemoveDuplicates(УникальныеЕд));

    // ── Шаг 3: Синхронизация блюд через EnsureUpdate ─────────────────────────
    Счётчик = A1sDS.OfKeys("Обновлено, Создано, Ошибок", 0);

    Для Каждого Блюдо Из МенюJSON Цикл
        Попытка
            БылаСсылка = A1sCatalogs.ByAttr("Номенклатура", "КодPOS", Блюдо.dish_id);
            ЭтоОбновление = ЗначениеЗаполнено(БылаСсылка);

            A1sCatalogs.EnsureUpdate("Номенклатура", Блюдо.name,
                A1sDS.Of(
                    "КодPOS",          Блюдо.dish_id,
                    "Цена",            Блюдо.price,
                    "ЕдиницаИзмерения", A1sCatalogs.ByName("КлассификаторЕдиницИзмерения", Блюдо.unit),
                    "Родитель",        ГруппыМеню[Блюдо.category],
                    "ВидНоменклатуры", ВидБлюдо));

            Если ЭтоОбновление Тогда
                Счётчик.Обновлено = Счётчик.Обновлено + 1;
            Иначе
                Счётчик.Создано = Счётчик.Создано + 1;
            КонецЕсли;
        Исключение
            Счётчик.Ошибок = Счётчик.Ошибок + 1;
            Сообщить("✗ " + Блюдо.name + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;

    Сообщить("Меню синхронизировано: обновлено " + Счётчик.Обновлено
        + ", добавлено " + Счётчик.Создано
        + ", ошибок "   + Счётчик.Ошибок);
КонецПроцедуры
Управляющая компания ЖКХ
1С:УК ЖКХ
Управляющая компания
ПРОЕКТ 5

УК: ежемесячное начисление коммунальных услуг

«Первого числа каждого месяца нужно создать начисления для всех лицевых счетов.»

Массовое создание документов начисления по данным из справочника лицевых счетов. Защита от дублей через Exists. Пачечное проведение через Chunk + PostAll. Используется Copy для клонирования прошлого месяца — экономит время.

НачислениеКУ ЛицевыеСчета Контрагенты (жильцы) НоменклатурныеГруппы
Ежемесячное начисление КУ · Exists + Chunk + PostAll ~55 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 5: Ежемесячное начисление КУ для всех лицевых счетов
// Документ: НачислениеКУ, ТЧ "УслугиКУ": Услуга|Количество|Тариф|Сумма
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Процедура НачислитьКУЗаМесяц(ДатаНачисления) Экспорт

    НачМесяца = НачалоМесяца(ДатаНачисления);
    КонМесяца = КонецМесяца(ДатаНачисления);
    МесяцСтрока = Формат(ДатаНачисления, "ДФ=ММММ ГГГГ");

    // Все активные лицевые счета — отфильтровать только с жильцами
    ЛицевыеСчета = A1sCatalogs.Refs("ЛицевыеСчетаЖКХ",
        "НЕ ПометкаУдаления И Жилец.ЗаполненностьЖильца = ИСТИНА");
    Сообщить("Лицевых счетов к начислению: " + ЛицевыеСчета.Количество());

    // ── Создать документы (без проведения — сначала Write) ────────────────────
    СозданныеНачисления = Новый Массив;
    СчётчикПропуск = 0;

    Для Каждого ЛС Из ЛицевыеСчета Цикл
        ЛСДанные = A1sCatalogs.Details(ЛС, "Жилец, Квартира, Адрес");

        // Защита от дублей
        УжеЕсть = A1sDocs.Exists("НачислениеКУ",
            "ЛицевойСчет = &ЛС И Дата МЕЖДУ &Нач И &Кон",
            A1sDS.Of("ЛС", ЛС, "Нач", НачМесяца, "Кон", КонМесяца));
        Если УжеЕсть Тогда
            СчётчикПропуск = СчётчикПропуск + 1;
            Продолжить;
        КонецЕсли;

        // Расчёт услуг по нормативам (упрощённо — берём из тарифного плана)
        УслугиЛС = A1sCatalogs.AllByOwner("ТарифыЛицевогоСчета", ЛС);
        СтрокиТЧ = Новый Массив;
        Для Каждого Тариф Из УслугиЛС Цикл
            ТарифДанные = A1sCatalogs.Details(Тариф, "Услуга, Норматив, Тариф");
            СтрокиТЧ.Добавить(A1sDS.Of(
                "Услуга",     ТарифДанные.Услуга,
                "Количество", ТарифДанные.Норматив,
                "Тариф",      ТарифДанные.Тариф,
                "Сумма",      ТарифДанные.Норматив * ТарифДанные.Тариф));
        КонецЦикла;

        Если A1sAR.IsEmpty(СтрокиТЧ) Тогда Продолжить; КонецЕсли;

        Ссылка = A1sDocs.On("НачислениеКУ",
                A1sDS.Of(
                    "Дата",         НачМесяца,
                    "ЛицевойСчет", ЛС,
                    "Жилец",        ЛСДанные.Жилец,
                    "Период",       МесяцСтрока,
                    "Комментарий", ЛСДанные.Адрес))
            .AddRows("УслугиКУ", СтрокиТЧ)
            .Write();  // Записать без проведения
        СозданныеНачисления.Добавить(Ссылка);
    КонецЦикла;

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

    // ── Провести пачками по 100 ───────────────────────────────────────────────
    ВсегоПроведено = 0;
    ВсегоОшибок    = 0;
    Для Каждого Пачка Из A1sAR.Chunk(СозданныеНачисления, 100) Цикл
        Итог = A1sDocs.PostAll(Пачка);
        ВсегоПроведено = ВсегоПроведено + Итог.Success.Количество();
        ВсегоОшибок    = ВсегоОшибок    + Итог.Failed.Количество();
        ОбработкаПрерыванияПользователя();
    КонецЦикла;

    Сообщить("✓ Проведено: " + ВсегоПроведено + "  ✗ Ошибок: " + ВсегоОшибок);
КонецПроцедуры
Интеграции с внешними системами
1С:CRM
Интеграция
ПРОЕКТ 6

Синхронизация контрагентов из CRM по API

«В CRM ведётся единая база клиентов. Нужно синхронизировать её в 1С раз в час.»

Полная двусторонняя синхронизация. CRM отдаёт JSON с клиентами. Ищем по внешнему ID (crm_id). Нашли — обновляем реквизиты. Не нашли — создаём. Отслеживаем удалённых в CRM — помечаем на удаление в 1С.

Контрагенты ГруппыКонтрагентов ДоговорыКонтрагентов КонтактнаяИнформация
CRM → 1С · полная синхронизация контрагентов ~60 строк
// ════════════════════════════════════════════════════════════════════════════
// ПРОЕКТ 6: Синхронизация контрагентов из CRM
// CRM JSON: { "crm_id": "C-001", "name": "ООО Ромашка",
//             "inn": "7707083893", "segment": "Корпоративные",
//             "phone": "+7495...", "email": "info@..." }
// ════════════════════════════════════════════════════════════════════════════
&НаСервере
Процедура СинхронизироватьКонтрагентовИзCRM(КлиентыJSON) Экспорт

    // ── Шаг 1: Подготовить группы-сегменты ───────────────────────────────────
    СегментыJSON = Новый Массив;
    Для Каждого К Из КлиентыJSON Цикл
        СегментыJSON.Добавить(К.segment);
    КонецЦикла;
    УникальныеСегменты = A1sAR.RemoveDuplicates(A1sAR.PickFilled(СегментыJSON));

    МапСегментов = Новый Соответствие;
    Для Каждого Сег Из УникальныеСегменты Цикл
        МапСегментов.Вставить(Сег,
            A1sCatalogs.EnsureGroup("Контрагенты", Сег));
    КонецЦикла;

    // ── Шаг 2: Получить все ID из CRM ─────────────────────────────────────────
    ИДИзCRM = Новый Массив;
    Для Каждого К Из КлиентыJSON Цикл
        ИДИзCRM.Добавить(К.crm_id);
    КонецЦикла;

    // Найти в 1С всех с такими CRM ID — батчевый запрос
    СуществующиеВ1С = Новый Соответствие; // crm_id → СправочникСсылка
    Для Каждого Ссылка Из A1sCatalogs.PickByAttr("Контрагенты", "КодCRM", ИДИзCRM) Цикл
        КОДCRM = Ссылка.КодCRM;
        СуществующиеВ1С.Вставить(КОДCRM, Ссылка);
    КонецЦикла;

    // ── Шаг 3: Upsert каждого контрагента ────────────────────────────────────
    Счётчик = A1sDS.OfKeys("Создано, Обновлено, Ошибок", 0);

    Для Каждого К Из КлиентыJSON Цикл
        Попытка
            РеквизитыДляОбновления = A1sDS.Of(
                "КодCRM",    К.crm_id,
                "ИНН",       К.inn,
                "Родитель", МапСегментов[К.segment]);

            СущСсылка = СуществующиеВ1С[К.crm_id];
            Если ЗначениеЗаполнено(СущСсылка) Тогда
                // Обновить существующего
                A1sCatalogs.Update(СущСсылка, РеквизитыДляОбновления);
                Счётчик.Обновлено = Счётчик.Обновлено + 1;
            Иначе
                // Создать нового
                A1sCatalogs.On("Контрагенты")
                    .Set("Наименование", К.name)
                    .FillAttrs(РеквизитыДляОбновления)
                    .Write();
                Счётчик.Создано = Счётчик.Создано + 1;
            КонецЕсли;
        Исключение
            Счётчик.Ошибок = Счётчик.Ошибок + 1;
            Сообщить("✗ " + К.name + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;

    // ── Шаг 4: Пометить удалённых в CRM ──────────────────────────────────────
    ВсеВ1С = A1sCatalogs.Refs("Контрагенты",
        "ЗначениеЗаполнено(КодCRM) И НЕ ПометкаУдаления");
    УдалённыеВCRM = Новый Массив;
    Для Каждого Ссылка1С Из ВсеВ1С Цикл
        Если НЕ A1sAR.Contains(ИДИзCRM, Ссылка1С.КодCRM) Тогда
            УдалённыеВCRM.Добавить(Ссылка1С);
        КонецЕсли;
    КонецЦикла;
    Если НЕ A1sAR.IsEmpty(УдалённыеВCRM) Тогда
        A1sCatalogs.MarkForDeletionBatch(УдалённыеВCRM);
        Сообщить("Помечено на удаление (нет в CRM): " + УдалённыеВCRM.Количество());
    КонецЕсли;

    Сообщить("CRM-синхронизация: создано " + Счётчик.Создано
        + ", обновлено " + Счётчик.Обновлено
        + ", ошибок "   + Счётчик.Ошибок);
КонецПроцедуры
ℹ️
Паттерн «внешний ID». Для интеграций всегда добавляйте в справочник реквизит с ID из внешней системы (КодCRM, GUID_ERP, id_SAP). Это делает поиск надёжным — не зависит от совпадения наименований, которые могут меняться. Поиск по ByAttr("КодCRM", crm_id) всегда точный.
✓ 6 полных рабочих сценариев · реальные имена объектов · готово к адаптации