1С вам об этом не скажет: 20 ошибок, которые не ловит ни конфигуратор, ни тесты

22.09.26

Разработка - Инструментарий разработчика

Ошибки, которые падают, находит кто угодно за пять минут. Опаснее те, что возвращают правдоподобный результат: код компилируется, синтаксический контроль проходит, BSL Language Server молчит, тесты зеленые, а цифры неверные. Двадцать таких мест. Минус из Excel, который тихо становится нулем. Реквизит формы, который на 8.3 падает, а на 8.5 работает. Пересобранная обработка, которая выполняет старый код. Состояние() на сервере: в толстом клиенте молчит, в тонком не дает собрать модуль. Все выполнимое выполнено на живой Бухгалтерии 3.0 и повторено на двух платформах, 8.3.27.2214 и 8.5.1.1343. Пять пунктов черновика не подтвердились. Они не выброшены, а переписаны по тому, что показала платформа, и в двух местах правда оказалась интереснее заготовки. К статье приложены две обработки: восемь проверок по XML-выгрузке и та, которой проверялись утверждения статьи, двадцать шесть проверок на вашей базе одной кнопкой.

Бесплатные

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Узнавайте о новых бесплатных решениях в нашей телеграм-группе Инфостарт БЕСПЛАТНО

Наименование Скачано Бесплатно
Тихие ошибки: восемь проверок по XML-выгрузке
.epf 15,90Kb
4 Скачать бесплатно
Проверка поведения платформы: 26 проверок на вашей базе
.epf 14,71Kb
6 Скачать бесплатно

Вы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

Самая дорогая ошибка в моей практике стоила месяца и выглядела так: в отчете по остаткам не сходилась сумма. Не падало ничего. Обработка отрабатывала, документы записывались, пользователь работал. Просто цифра была другая.

Искали втроем. Нашли случайно: в имени переменной затесалась латинская o. КоличествoПроводки и КоличествоПроводки — на экране это одно слово, для платформы два разных. Одно получало значение, второе оставалось пустым.

С тех пор я делю ошибки не на «простые и сложные», а на две категории: те, которые падают, и те, которые возвращают правдоподобный результат. Первые находит кто угодно за пять минут. Вторые живут в базе месяцами, потому что о них никто не сообщает: код компилируется, синтаксический контроль проходит, BSL Language Server молчит, тесты зеленые. Узнаете вы о них от бухгалтера, и не сразу.

Ниже — двадцать таких мест. Разделы идут по механизму отказа, а не по темам: важно понимать, чего бояться, а не в каком разделе синтакс-помощника это лежит. Самое дорогое — первым.

Справочник без этого не стоит ничего, поэтому сразу про доверие к тексту. Все, что можно было выполнить, я выполнил в живой Бухгалтерии 3.0 и повторил на двух платформах: 8.3.27.2214 и 8.5.1.1343. Чтобы не проверять по двадцать раз одно и то же, я сложил проверки в одну обработку — она приложена к статье, можете открыть у себя и посмотреть, что ответит ваша платформа.

Пять пунктов из черновика не подтвердились. Я их не выбросил, а переписал по тому, что увидел, и в двух местах правда оказалась интереснее заготовки.

 

1. Молчаливая потеря данных

Худшая категория. Программа работает, данные портятся.

1.1. Минус из Excel превращается в ноль

Что пишут. Читают число из текста ячейки — так, как это делают в каждой второй загрузке из файла:

Попытка
    Сумма = Число(ТекстЯчейки);
Исключение
    Сумма = 0;
КонецПопытки;

Что происходит. Минус в выгрузке из Excel — это часто не дефис (-, U+002D), а типографский знак минуса (, U+2212) или короткое тире (, U+2013): так его расставляет сам Excel при форматировании. Число() на такой строке падает — и обертка Попытка честно превращает падение в ноль. Все отрицательные суммы в загрузке становятся нулями, ни одна строка не сообщает об ошибке, итог не сходится на сумму всех минусов.

Какой именно символ ломает преобразование, надо знать поименно, потому что интуиция здесь ошибается. Я прогнал все шесть вариантов в живой базе:

Сообщить(Число("-1500"));                      // -1500, обычный дефис
Сообщить(Число("W22;1500"));                      // исключение: знак минуса U+2212
Сообщить(Число("–1500"));                      // исключение: короткое тире U+2013
Сообщить(Число("-1" + Символ(160) + "500"));   // -1500, неразрывный пробел проходит
Сообщить(Число("-1 500"));                     // исключение: обычный пробел не проходит
Сообщить(Число("-1500,25"));                   // -1500,25, запятая проходит

Неразрывный пробел между разрядами платформа понимает и молча выбрасывает, а точно такой же на вид обычный пробел — нет. То есть строка, скопированная из Excel, может преобразоваться, а она же после прогона через любую чистку пробелов — упасть. Одинаково на 8.3.27.2214 и 8.5.1.1343.

Как правильно. Нормализовать строку до преобразования и не глотать исключение в ноль:

Текст = СтрЗаменить(ТекстЯчейки, Символ(8722), "-");   // минус U+2212
Текст = СтрЗаменить(Текст, Символ(8211), "-");         // тире U+2013
Текст = СтрЗаменить(Текст, Символ(160), "");           // неразрывный пробел, на всякий случай
Текст = СтрЗаменить(Текст, " ", "");                   // а вот этот обязателен
Сумма = Число(Текст);   // без Попытки: пусть падает, если все еще не число

Чем ловится. Только тестом на файле с отрицательными значениями, сохраненном из настоящего Excel — а не набранном руками в блокноте. Если в тестовых данных минусов нет, вы не увидите этого никогда.

1.2. Реквизит с ограничением знака обнуляет минус

Что пишут.

Движение.Сумма = -1500;
Набор.Записать();

Что происходит. Если у реквизита в конфигураторе стоит галка «Неотрицательное» (в XML выгрузки это AllowedSign со значением Nonnegative), записанное отрицательное значение становится нулем. Без сообщения, без исключения, без отметки в журнале регистрации. Записали −1500 — прочитали 0.

Причем обнуляется значение не при записи, а уже при присваивании: Объект.Реквизит = -1500; Сообщить(Объект.Реквизит); печатает 0 еще до всякого Записать(). Я проверил это в типовой Бухгалтерии на реквизите справочника с таким ограничением, в транзакции с откатом: ноль в объекте появляется в ту же секунду, что и присваивание.

Как правильно. Перед тем как писать в реквизит отрицательное значение, посмотреть его свойства в конфигураторе. Если минусы нужны — снять ограничение знака; если реквизит чужой и трогать нельзя — хранить знак отдельно или использовать другой реквизит.

Чем ловится. Поиском AllowedSign в XML-выгрузке. В типовой Бухгалтерии таких реквизитов 233 в 119 справочниках — так что вероятность наткнуться выше, чем кажется. Считать надо именно реквизиты объектов: у реквизитов форм ограничение знака тоже встречается, но там оно безобидное, в базу они не пишутся.

1.3. КонецДня() делает пустую дату заполненной

Что пишут. Границу периода приводят к концу дня:

Если ЗначениеЗаполнено(ДатаПо) Тогда
    ДатаПо = КонецДня(ДатаПо);
КонецЕсли;

А потом где-то раньше по коду ДатаПо уже прошла через КонецДня() или через арифметику с секундами.

Что происходит. Пустая дата в 1С — это 01.01.0001 00:00:00. После КонецДня() она становится 01.01.0001 23:59:59, а это уже заполненная дата. ЗначениеЗаполнено возвращает Истина, проверка «пользователь не указал период» перестает работать, и запрос молча отбирает пустоту.

Пример:

Пустая = '00010101';
Сообщить(ЗначениеЗаполнено(Пустая));              // Ложь
Сообщить(ЗначениеЗаполнено(КонецДня(Пустая)));    // Истина
Сообщить(ЗначениеЗаполнено(Пустая + 5));          // Истина

Как правильно. Проверять заполненность до любых преобразований и запоминать результат в отдельную переменную.

Чем ловится. Сценарием с незаполненным периодом. В ручном тестировании период обычно заполняют — поэтому и не ловится.

1.4. Субконто в виртуальной таблице роняет счет из выборки

Что пишут. В виртуальную таблицу остатков добавляют субконто, рассчитывая просто получить его в выборке:

ВЫБРАТЬ
    Остатки.Счет,
    Остатки.Субконто1,
    Остатки.СуммаОстатокДт
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Остатки(
        &Дата,
        Счет В ИЕРАРХИИ(&Счета),
        &ВидыСубконто           // <- вот здесь
    ) КАК Остатки

Что происходит. Счета, у которых такого вида субконто нет, пропадают из результата целиком. Не с нулями — их просто не будет. Отчет при этом выглядит нормальным: строки есть, суммы правдоподобные, итог меньше настоящего ровно на те счета, о которых вы не узнали.

Как правильно. Либо не указывать виды субконто в параметрах виртуальной таблицы и получать разрез отдельным соединением, либо собирать две выборки — со субконто и без — и объединять их.

Чем ловится. Только сверкой итога отчета с оборотно-сальдовой ведомостью за тот же период. По коду это не видно: запрос синтаксически безупречен.

 

2. Падает там, где не ждешь

Эти как раз падают — но в момент, когда вы этого не ожидаете, и с текстом, который не подсказывает причину.

2.1. XMLСтрока не берет наборы записей

Что пишут. Делают снимок набора записей, чтобы сохранить его до изменения:

Снимок = СериализаторXDTO.XMLСтрока(НаборЗаписей);

Что происходит. Исключение: «Отсутствует отображение для типа РегистрСведенийНаборЗаписей.<Имя>». Сериализатор пишет значение без явного указания типа, а для набора записей такого отображения просто нет.

Как правильно.

Снимок = ОбщегоНазначения.ЗначениеВСтрокуXML(НаборЗаписей);

Эта функция БСП делает то же самое с явным назначением типа — так БСП версионирует наборы записей у себя.

Чем ловится. Первым же запуском на наборе записей. Коварство в другом: на объектных типах XMLСтрока работает прекрасно, и переход на набор записей выглядит безобидным рефакторингом.

2.2. Обращение к члену у результата Новый не компилируется

Что пишут. Сокращают две строки в одну:

ИмяФайла = Новый Файл(Путь).ПолноеИмя;

Что происходит. Это не ошибка выполнения, это ошибка компиляции модуля: «Неопознанный оператор». Модуль не собирается целиком — обработка не откроется вовсе. И это не подсвечивает ни редактор, ни BSL Language Server: конструкция выглядит абсолютно нормальной для человека, пришедшего из любого другого языка.

Как правильно. Через переменную, всегда:

Файл = Новый Файл(Путь);
ИмяФайла = Файл.ПолноеИмя;

Чем ловится. Попыткой открыть обработку. Если конструкция попала в редко используемую ветку большого модуля — узнаете нескоро.

2.3. Имя глобального объекта в переменной

Что пишут. Называют локальную переменную удачным по смыслу словом:

ДопустимаяДлина = Метаданные.Справочники.Номенклатура.ДлинаНаименования;

Что происходит. Компилятор молчит. BSL Language Server молчит. В рантайме: «Поле объекта недоступно для записи (ДопустимаяДлина)». Имя занято системным перечислением платформы.

У меня на этом не записалось 49 элементов из 49 — цикл падал на первой же итерации, а сообщение об ошибке про длину наименования не говорило ничего.

Имена занимает не только платформа. Пока я собирал проверки к этой статье, я дважды поймал себя на том же: переменная Знач не компилируется вообще (это модификатор параметра «по значению»), а процедура с именем Открыть в модуле управляемой формы дает «Процедура или функция с указанным именем уже определена» — так называется собственный метод формы.

Как правильно. Переименовать. Имя платформенного объекта, ключевое слово или имя метода самой формы в левой части присваивания — всегда ошибка.

Чем ловится. Только запуском. Список таких имен нигде не опубликован одним куском, и построить его автоматически по коду конфигурации, как выяснилось, не получается: «имя всегда употребляется с точкой и ни разу не присваивается» — эвристика, которая на типовой дает десятки тысяч кандидатов и при этом теряет настоящие платформенные объекты.

2.4. Латинская буква внутри кириллического имени

Что пишут. Ничего особенного. Буква попадает сама — при копировании из письма, из документа, из чужого кода, при переключении раскладки.

Что происходит. КоличествoПроводки с латинской o — это другое имя, чем КоличествоПроводки. Два разных идентификатора, визуально неотличимых. Одна переменная получает значение, вторая остается пустой.

Пример — вставьте в консоль как есть:

ИтогоКоличество = 0;
Для Счетчик = 1 По 3 Цикл
    ИтогoКоличество = ИтогоКоличество + Счетчик;   // слева латинская o
КонецЦикла;
Сообщить(ИтогоКоличество);                          // 0, а не 6

Накопление уходит в переменную с латинской буквой, а печатается кириллическая, которая так и осталась нулем. Ни одна строка не падает.

Как правильно. Ловить автоматически. Важная тонкость: ругаться на любую латиницу внутри кириллического имени нельзя — СебестоимостьERP, ЗначениеВСтрокуXML, СохранитьВExcel совершенно законны, и такое правило на типовой ERP дает тысячи ложных срабатываний.

Точный признак другой: одно имя встречается в файле в двух написаниях. Нормализуйте латинские буквы-двойники в кириллические (ABCEHKMOPTXYАВСЕНКМОРТХУ, acekmoptxyасекмортху) и ищите нормализованные формы, у которых набралось больше одного исходного написания. Пока написание одно — код работает, имя просто выглядит странно. Вред начинается со второго.

Нормализацию делайте до понижения регистра: пары B/В и H/Н неразличимы только в верхнем регистре, а b/в и h/н — уже разные буквы.

Чем ловится. Только сравнением алфавитов. Ни глазами, ни поиском, ни линтером.

2.5. Подстановка в закомментированную строку запроса

Что пишут. Патчат текст запроса кодом, используя комментарий как якорь:

ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
    "//ВСТАВИТЬ_ПОЛЯ", ДополнительныеПоля);

Что происходит. Если подставляемый текст многострочный, под комментарием остается только его первая строка. Хвост выходит из-под // и становится частью запроса — в произвольном месте. Запрос либо падает с невнятной ошибкой синтаксиса, либо, что хуже, выполняется и возвращает не то.

Как правильно. Якорь ставить не в комментарии, а в самом теле запроса, и подставлять в том порядке, в котором части запроса идут: сначала группировка, потом выборка. Сам патч прогонять на симуляции — собрать итоговый текст и посмотреть на него глазами до отправки в базу.

Чем ловится. Печатью получившегося текста запроса. Отладчик показывает его уже собранным, так что достаточно один раз посмотреть.

 

3. Формы

3.1. Реквизит формы ХранилищеЗначения: на 8.3 не работает, на 8.5 работает

Что пишут. Добавляют на форму реквизит типа ХранилищеЗначения, чтобы положить туда картинку, таблицу или произвольные данные.

Что происходит. Зависит от версии платформы, и это самое неприятное. Я собрал одну и ту же обработку с таким реквизитом и запустил на двух.

На 8.3.27.2214 реквизит объявляется, обработка открывается, но реквизитом он только называется. ТипЗнч у него не ХранилищеЗначения, а ДанныеФормыСтруктура. Присвоить нельзя:

Ошибка при установке значения атрибута контекста (Хранилище):
Нельзя изменять поле, содержащее объект данных формы

а на клиенте у него нет и метода: «Метод объекта не обнаружен (Получить)».

На 8.5.1.1343 тот же файл работает как задумано: присваивание проходит, ТипЗнч возвращает Хранилище значения, значение доезжает до клиента, Получить() отдает то, что положили на сервере.

То есть код, написанный и проверенный на 8.5, у заказчика на 8.3 упадет, и упадет не там, где вы его писали, а при первом же присваивании.

Как правильно. Не зависеть от версии: держать данные во временном хранилище, а на форме — только адрес строкой.

АдресДанных = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
// ...
Данные = ПолучитьИзВременногоХранилища(АдресДанных);

Так, судя по всему, и поступают все: в выгрузке типовой Бухгалтерии 3.0 я просмотрел 7981 управляемую форму и не нашел ни одного реквизита типа ХранилищеЗначения.

Чем ловится. Разбором Form.xml: ищите реквизиты, у которых тип оканчивается на ValueStorage. И прогоном на той платформе, которая стоит у заказчика, а не у вас.

3.2. Расширения элементов формы — не объекты

Что пишут. По аналогии с другими свойствами:

Элементы.Поле.РасширениеПоляВвода.КнопкаВыбора = Ложь;

Что происходит. Не работает, и причина не в синтаксисе: такого объекта не существует вообще. «Расширение поля ввода» — это способ, которым синтакс-помощник сгруппировал свойства, доступные элементу формы определенного вида. Группировка в документации, а не сущность в платформе.

Проверить можно, не запуская 1С: имена объектов платформы лежат в библиотеках каталога bin (см. пункт 5.4). КнопкаВыбора там есть, РасширениеПоляВвода — нет.

Как правильно. Свойство задается прямо на элементе:

Элементы.Поле.КнопкаВыбора = Ложь;

Чем ловится. Тем же поиском по бинарникам. Ошибка тут в первую очередь в голове: структура справки читается как структура объектов, а это не одно и то же.

3.3. Булева колонка рисуется текстом «Да» / «Нет»

Что пишут. Добавляют в таблицу на форме колонку булева типа.

Что происходит. Колонка выводится полем ввода, а не флажком. На экране пользователь видит слова «Да» и «Нет», читает их как вид операции или как статус и не догадывается, что по колонке можно щелкнуть. Это не ошибка — платформа ничего не нарушила, просто вид поля по умолчанию оказался не тем, которого вы ждали.

Как правильно. Явно задать вид «Поле флажка» в конфигураторе.

Чем ловится. Разбором Form.xml — и признак там абсолютно точный. Две колонки одного и того же булева типа выглядят в XML так:

<CheckBoxField name="Пометить" id="52">
    <DataPath>Дубли.Пометить</DataPath>
</CheckBoxField>

<InputField name="ЕстьВЭРП" id="70">
    <DataPath>Дубли.ЕстьВЭРП</DataPath>
</InputField>

Тип реквизита при этом один и тот же, и лежит он не рядом с элементом, а в разделе <Attributes>: для колонок таблиц и деревьев значений — внутри <Columns> у самого реквизита таблицы. Ищите InputField, чей путь к данным ведет на реквизит с типом xs:boolean.

Проверяя это на своих обработках, я нашел пять таких колонок — и все пять пользователь все это время читал как текст, не подозревая, что по ним можно щелкнуть.

 

4. Внешние обработки и отчеты

4.1. Фоновые задания внешней обработке недоступны

Что пишут. Длинную операцию заворачивают в механизм длительных операций БСП, чтобы пользователь видел прогресс и мог прервать.

Что происходит. Не работает — если обработка открыта через «Файл → Открыть». Фоновое задание может выполнить только метод, который платформа умеет найти по имени: процедуру общего модуля конфигурации или команду обработки, подключенной через подсистему БСП «Дополнительные отчеты и обработки». Файл на диске для фонового задания не существует.

Так что «оберну в ДлительныеОперации.ВыполнитьВФоне и все» работает ровно до момента, когда обработку у заказчика откроют не из справочника дополнительных обработок, а с рабочего стола.

Как правильно. Есть два пути, и первый обычно переоценивают. Фоновое задание все-таки можно запустить — если целиком спрятать обработку за процедуру общего модуля конфигурации: задание вызывает ее, она внутри поднимает обработку из файла через ВнешниеОбработки.Создать и дергает нужный метод. Так устроена подсистема «Дополнительные отчеты и обработки». Цена: свой общий модуль в конфигурации заказчика, файл, доступный серверу, и безопасный режим. Для обработки, которую открывают с рабочего стола, это обычно неподъемно.

Второй путь проще и работает всегда: резать работу на порции и гонять их с клиента: клиентская процедура берет очередную порцию, вызывает сервер, обновляет состояние и переподключает себя обработчиком ожидания:

&НаКлиенте
Процедура ОбработатьОчереднуюПорцию()
    Обработано = ОбработатьПорциюНаСервере(Обработано);
    Если Обработано >= Всего Тогда
        Возврат;
    КонецЕсли;
    Состояние("Обработано: " + Обработано + " из " + Всего);
    ПодключитьОбработчикОжидания("ОбработатьОчереднуюПорцию", 0.2, Истина);
КонецПроцедуры

Чем ловится. Запуском на объеме. На трех тестовых строках все выглядит прекрасно.

4.2. Состояние() в серверном коде: где молчит, а где не компилируется

Что пишут. Показывают прогресс из процедуры, собранной для обеих сторон — так делают, когда одну и ту же порцию хотят считать и на клиенте, и на сервере:

&НаКлиентеНаСервереБезКонтекста
Процедура ОбработатьВсе(Данные)
    Счетчик = 0;
    Для Каждого Строка Из Данные Цикл
        Счетчик = Счетчик + 1;
        Состояние("Обработано: " + Счетчик);
        ОбработкаПрерыванияПользователя();
    КонецЦикла;
КонецПроцедуры

Что происходит. Состояние() и ОбработкаПрерыванияПользователя() — клиентские методы, сервера в списке их доступности нет. А вот что из этого выходит на практике, зависит от клиента, и я развел это опытом: одна и та же обработка, две директивы, два клиента.

Директива Толстый клиент Тонкий клиент
&НаСервере компилируется, отрабатывает молча ошибка компиляции
&НаКлиентеНаСервереБезКонтекста компилируется, с сервера молча ошибка компиляции

В тонком клиенте это громко и сразу:

{ВнешняяОбработка.Проба.Форма.Форма.Форма(36,2)}:
Процедура или функция с указанным именем не определена (Состояние)
        <<?>>Состояние("проба индикатора");

Модуль не собирается целиком, и вы узнаете об этом при первом же открытии. Неприятно, зато сразу.

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

Отсюда и обратная ловушка: написали и проверили в толстом клиенте — у заказчика на тонком обработка просто не откроется. Это ровно тот же разлом, что и в пункте 4.4, только повернутый другой стороной.

Как правильно. См. предыдущий пункт: прогресс и прерывание живут на клиенте, а сервер только считает порцию и возвращает результат.

Чем ловится. Разбором директив компиляции — ищите эти два вызова под &НаСервере, &НаСервереБезКонтекста и &НаКлиентеНаСервереБезКонтекста. И запуском в том клиенте, который стоит у заказчика.

4.3. Пересобрали обработку, а выполняется прежняя версия

Что пишут. Правят обработку, пересобирают файл, открывают заново — и видят старое поведение. Правят еще раз. Снова старое. Народное объяснение: платформа кэширует внешнюю обработку по имени объекта, поэтому надо версионировать имя объекта внутри файла.

Что происходит. Объяснение неверное, я его проверил и оно не подтвердилось. Никакого кэша по имени объекта нет. Две сборки одной обработки с одинаковым именем объекта и одинаковыми идентификаторами, отличается только текст модуля: ВнешниеОбработки.Создать по пути отдает новую версию сразу, в том же сеансе. Подмена файла по тому же пути, тот же файл по другому пути, другое имя объекта по тому же пути — во всех случаях выполняется то, что лежит в файле сейчас.

А вот что действительно залипает — уже открытое окно формы. В одном сеансе:

// шаг 1: подключили версию 1, открыли форму - ПриСозданииНаСервере отработала
ОткрытьФорму("ВнешняяОбработка." + Имя + ".Форма");

// шаг 2: подключили версию 2 тем же именем, открыли ту же форму
ОткрытьФорму("ВнешняяОбработка." + Имя + ".Форма");

На втором шаге модуль объекта уже отвечает «версия 2», а ПриСозданииНаСервере формы не выполняется вообще: платформа не создает вторую форму с тем же ключом, а показывает ту, что уже открыта, — со старым кодом внутри. Добавьте признак уникальности — и та же строка создаст новую форму, и новый код выполнится:

ОткрытьФорму("ВнешняяОбработка." + Имя + ".Форма", , , Истина);

Одинаково на 8.3.27.2214 и 8.5.1.1343.

Как правильно. Закрывать окно обработки перед тем, как открыть пересобранный файл. Переименовывать объект внутри файла не нужно — это лечение несуществующей болезни, которое стоит вам версионирования имен.

Чем ловится. Симптом знакомый: добавили Сообщить("сюда дошли"), перезапустили, сообщения нет. Вывод «код не вызывается» верен буквально — но не потому, что выполняется прежняя версия файла, а потому что форму никто не создавал заново.

4.4. ТекущаяДата() на клиенте, и почему замена на ТекущаяДатаСеанса() не компилируется

Что пишут.

&НаКлиенте
Процедура СформироватьИмяФайла()
    Имя = "выгрузка_" + Формат(ТекущаяДата(), "ДФ='yyyyMMdd_HHmmss'");
КонецПроцедуры

Что происходит. Берется время рабочей станции. Пользователь волен перевести часы, часовой пояс может отличаться от серверного, и метки времени в ваших файлах и журналах перестают быть сопоставимыми с документами, которые датирует сервер.

BSL Language Server на эту строку ругается и предлагает замену: «Используйте "ТекущаяДатаСеанса" вместо устаревшего "ТекущаяДата"». Совет разумный и повсеместный. Только если ему последовать прямо здесь, модуль перестанет компилироваться в тонком клиенте:

{ВнешняяОбработка.Проверка.Форма.Форма.Форма(28,16)}:
Процедура или функция с указанным именем не определена (ТекущаяДатаСеанса)
        ДатаСеанса = <<?>>ТекущаяДатаСеанса();

ТекущаяДатаСеанса() объявлена доступной на сервере, в толстом клиенте, во внешнем соединении и на мобильном сервере. Тонкого и веб-клиента в этом списке нет. Я поймал это на себе ровно так: обработка, собранная и проверенная запуском через 1cv8.exe — а это толстый клиент, — открылась и отработала; та же обработка в 1cv8c.exe не скомпилировалась вовсе.

Чтобы не списать это на конфигурацию, я прогнал один и тот же вызов в двух базах и двумя клиентами:

База, режим совместимости Версия8_3_27 1cv8.exe, толстый 1cv8c.exe, тонкий
Бухгалтерия 3.0.206.19 доступна недоступна
Управление торговлей 11.5.22.149 доступна недоступна

Платформа одна, 8.5.1.1343. Дело не в конфигурации и не в режиме совместимости, а ровно в клиенте. ТекущаяДата() при этом доступна везде.

Сорт ошибки тут важнее самой ошибки. Вызов стоял внутри Попытка, и это не помогло: ошибка компиляции, а не выполнения, — она уносит весь модуль целиком, вместе с двадцатью остальными проверками. Попытка ловит только то, что уже скомпилировалось.

Как правильно. Не подставлять одну функцию вместо другой, а перенести вычисление туда, где ему место. Время берет сервер и отдает клиенту готовым значением:

&НаКлиенте
Процедура СформироватьИмяФайла()
    Имя = "выгрузка_" + Формат(ДатаСеансаССервера(), "ДФ='yyyyMMdd_HHmmss'");
КонецПроцедуры

&НаСервереБезКонтекста
Функция ДатаСеансаССервера()
    Возврат ТекущаяДатаСеанса();
КонецФункции

Лишний вызов сервера — цена того, что в данных не окажется времени с чужих часов. Если очень нужно проверить, доступна ли функция именно у вас, не ломая модуль, — оберните ее в Выполнить(): он компилируется в момент выполнения.

Чем ловится. Разбором директив компиляции — и обязательно запуском в том клиенте, в котором работает заказчик. Толстый клиент прощает то, на чем тонкий падает, а 1cv8.exe в файловом режиме — это именно толстый.

 

5. Файлы и выгрузка

5.1. Собранную обработку проверить нечем: Конфигуратор молчит

Что пишут. Сборку и проверку внешней обработки ставят на поток: собрать файл из исходников Конфигуратором, прогнать его же синтаксической проверкой, запустить в Предприятии ключом /Execute и забрать результат из файла.

1cv8 DESIGNER /LoadExternalDataProcessorOrReportFromFiles src\Обработка.xml Обработка.epf
1cv8 DESIGNER /CheckModules -ExternalDataProcessor Обработка.epf
1cv8 ENTERPRISE /Execute Обработка.epf

Что происходит. Я положил в модуль формы одну строку с ошибкой, которая не видна глазами, — присваивание переменной с именем Знач. Это зарезервированное слово, модификатор параметра «по значению», и переменной с таким именем быть не может.

Знач = 1;
Сообщить(Знач);

Сборка: «Загрузка завершена», файл создан. Проверка: «Синтаксических ошибок не обнаружено!». Модули форм внешних обработок /CheckModules не компилирует, и молчание тут ничего не значит.

Запуск: процесс не завершается, файл с результатом не появляется, код возврата взять неоткуда. Снаружи это неотличимо от зависания в вашем коде, и искать вы будете бесконечный цикл. А на экране в это время висит окно:

Ошибка инициализации модуля: ВнешняяОбработка.ПробаСломанная.Форма.Форма.Форма
по причине:
{ВнешняяОбработка.ПробаСломанная.Форма.Форма.Форма(6,2)}: Неопознанный оператор
	<<?>>Знач = 1;

Беда в том, что окно это видно только тому, кто сидит перед машиной. Если сборку собирает билд-сервер или вы просто свернули лишнее, диагноза нет ни в логе, ни в коде возврата. На обеих платформах одно и то же.

Как правильно. Проверять модули форм тем, что их действительно разбирает. BSL Language Server на этом же файле дает три ParseError ровно на нужных строках. Получается наоборот привычному: сторонний линтер видит то, чего не видит родная проверка платформы, — и ставить его в сборку надо до Конфигуратора, а не после.

И не доверять тишине. Если запуск ключом /Execute не завершился за разумное время, это не «долго считает», это чаще всего окно с ошибкой, которого вы не видите. Откройте тот же файл руками и посмотрите на экран.

Чем ловится. Ничем из того, на что вы рассчитываете: ни сборкой, ни /CheckModules, ни кодом возврата. Только линтером до сборки и живым запуском после.

5.2. Табличный документ из xlsx: листы склеиваются, а Значение недоступно

Что пишут.

ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ПутьКФайлу);
Значение = ТабДок.Область("R2C3").Значение;

Что происходит. Ходовое объяснение «читается только первый лист» неверно, я его проверил. Книга из двух листов читается целиком: платформа кладет листы в одну таблицу подряд, второй под первым. На моем файле вышла таблица 2 на 3, маркер первого листа в R1C1, маркер второго — в R2C1.

От этого не легче, а хуже. Ничего не потерялось — зато пропала граница между листами. Если на первом листе двести строк, а на втором своя шапка и еще сто, вы получите триста строк подряд, где двести первая внезапно окажется заголовком, и никакого признака, что здесь начался другой лист. Разбор, написанный в расчете на один лист, не упадет: он просто прочитает шапку второго листа как строку данных. На обеих платформах одинаково.

Второе верно: Значение у прочитанной ячейки недоступно для чтения, платформа отвечает «Поле объекта недоступно для чтения (Значение)». Доступен только Текст. То есть числа и даты вы разбираете из строк, и пункт 1.1 этой статьи начинается ровно здесь.

Как правильно. Если листов может быть больше одного — не полагаться на Прочитать: восстановить границы в прочитанном документе нечем. Читать через zip, это следующий пункт.

Чем ловится. Файлом из двух листов с разными шапками. На однолистовом все работает.

5.3. Многолистовой xlsx разбирается через zip

Что пишут. Ищут способ добраться до конкретного листа табличным документом. Прочитать его выйдет (см. предыдущий пункт), но вперемешку с остальными и без единого разделителя, а это обычно не то, что нужно.

Как правильно. xlsx - это zip-архив с XML внутри, и он читается штатными средствами платформы, без единой внешней компоненты. Ниже рабочая функция: на вход путь и номер листа, на выходе таблица непустых ячеек. Я прогнал ее на книге из двух листов на 8.3.27.2214 и 8.5.1.1343.

// Читает один лист книги xlsx и возвращает таблицу его непустых ячеек.
//
// Возвращаемое значение:
//   ТаблицаЗначений - колонки Строка, Колонка (числа с единицы) и Значение
//                     (Число или Строка - так, как значение лежит в книге).
Функция ПрочитатьЛистXLSX(Знач ПутьКниги, Знач НомерЛиста) Экспорт

    Каталог = ПолучитьИмяВременногоФайла();
    СоздатьКаталог(Каталог);

    Попытка
        Архив = Новый ЧтениеZipФайла(ПутьКниги);
        Архив.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.Восстанавливать);
        Архив.Закрыть();

        // Строковые значения лежат общим списком, в самом листе - только номера.
        Общие = ОбщиеСтроки(Каталог + "\xl\sharedStrings.xml");
        Ячейки = ЯчейкиЛиста(Каталог + "\xl\worksheets\sheet"
            + Формат(НомерЛиста, "ЧГ=") + ".xml", Общие);
    Исключение
        УдалитьФайлы(Каталог);
        ВызватьИсключение;
    КонецПопытки;

    УдалитьФайлы(Каталог);
    Возврат Ячейки;

КонецФункции

Функция ОбщиеСтроки(Знач Путь)

    Список = Новый Массив;
    Файл = Новый Файл(Путь);
    Если НЕ Файл.Существует() Тогда
        Возврат Список;   // книга без строковых значений - такое бывает
    КонецЕсли;

    Чтение = Новый ЧтениеXML;
    Чтение.ОткрытьФайл(Путь);
    Текущая = Неопределено;
    Собираем = Ложь;
    Пока Чтение.Прочитать() Цикл
        Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            Если Чтение.Имя = "si" Тогда
                Текущая = "";
            ИначеЕсли Чтение.Имя = "t" Тогда
                Собираем = Истина;
            КонецЕсли;
        ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            Если Чтение.Имя = "si" Тогда
                Список.Добавить(Текущая);
                Текущая = Неопределено;
            ИначеЕсли Чтение.Имя = "t" Тогда
                Собираем = Ложь;
            КонецЕсли;
        ИначеЕсли Собираем И Текущая <> Неопределено Тогда
            Текущая = Текущая + Чтение.Значение;
        КонецЕсли;
    КонецЦикла;
    Чтение.Закрыть();
    Возврат Список;

КонецФункции

Функция ЯчейкиЛиста(Знач Путь, Знач Общие)

    Ячейки = Новый ТаблицаЗначений;
    Ячейки.Колонки.Добавить("Строка");
    Ячейки.Колонки.Добавить("Колонка");
    Ячейки.Колонки.Добавить("Значение");

    Чтение = Новый ЧтениеXML;
    Чтение.ОткрытьФайл(Путь);
    Адрес = "";
    ТипЯчейки = "";
    Сырое = "";
    Собираем = Ложь;

    Пока Чтение.Прочитать() Цикл
        Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            Если Чтение.Имя = "c" Тогда
                Адрес = Чтение.ПолучитьАтрибут("r");
                ТипЯчейки = Чтение.ПолучитьАтрибут("t");
                Сырое = "";
            ИначеЕсли Чтение.Имя = "v" ИЛИ Чтение.Имя = "t" Тогда
                Собираем = Истина;   // формулу в <f> при этом не берем
            КонецЕсли;
        ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            Если Чтение.Имя = "v" ИЛИ Чтение.Имя = "t" Тогда
                Собираем = Ложь;
            ИначеЕсли Чтение.Имя = "c" И ЗначениеЗаполнено(Сырое) Тогда
                СтрокаЯчейки = Ячейки.Добавить();
                СтрокаЯчейки.Строка = НомерСтрокиПоАдресу(Адрес);
                СтрокаЯчейки.Колонка = НомерКолонкиПоАдресу(Адрес);
                СтрокаЯчейки.Значение = ЗначениеЯчейки(Сырое, ТипЯчейки, Общие);
            КонецЕсли;
        ИначеЕсли Собираем Тогда
            Сырое = Сырое + Чтение.Значение;
        КонецЕсли;
    КонецЦикла;
    Чтение.Закрыть();
    Возврат Ячейки;

КонецФункции

Функция ЗначениеЯчейки(Знач Сырое, Знач ТипЯчейки, Знач Общие)

    Если ТипЯчейки = "s" Тогда
        Индекс = Число(Сырое);
        Возврат ?(Индекс < Общие.Количество(), Общие[Индекс], "");
    ИначеЕсли ТипЯчейки = "inlineStr" ИЛИ ТипЯчейки = "str" Тогда
        Возврат Сырое;
    КонецЕсли;

    // Числа в файле всегда с точкой и без разделителей разрядов.
    Попытка
        Возврат Число(СтрЗаменить(Сырое, ".", ","));
    Исключение
        Возврат Сырое;
    КонецПопытки;

КонецФункции

Функция НомерКолонкиПоАдресу(Знач Адрес)

    Номер = 0;
    Для Поз = 1 По СтрДлина(Адрес) Цикл
        Знак = ВРег(Сред(Адрес, Поз, 1));
        Если Знак >= "0" И Знак <= "9" Тогда
            Прервать;
        КонецЕсли;
        Номер = Номер * 26 + КодСимвола(Знак) - КодСимвола("A") + 1;
    КонецЦикла;
    Возврат Номер;

КонецФункции

Функция НомерСтрокиПоАдресу(Знач Адрес)

    Цифры = "";
    Для Поз = 1 По СтрДлина(Адрес) Цикл
        Знак = Сред(Адрес, Поз, 1);
        Если Знак >= "0" И Знак <= "9" Тогда
            Цифры = Цифры + Знак;
        КонецЕсли;
    КонецЦикла;
    Возврат ?(ПустаяСтрока(Цифры), 0, Число(Цифры));

КонецФункции

На моей книге из двух листов это дает вот что:

=== лист 1 ===
  R1C1 = [ЛИСТ1-МАРКЕР] тип Строка
  R1C2 = [-1 500] тип Число
  R1C3 = [42,5] тип Число
=== лист 2 ===
  R1C1 = [ЛИСТ2-МАРКЕР] тип Строка
  R1C2 = [777] тип Число

Обратите внимание на типы. Там, где табличный документ отдал бы строку и оставил вам пункт 1.1 со всеми его минусами, из zip приходит готовое отрицательное число: в самом файле оно записано как -1500, всегда с точкой в качестве разделителя и без пробелов между разрядами, независимо от того, что видел в Excel пользователь.

Две оговорки. Имя sheet с номером совпадает с порядком вкладок только в простых книгах - строго говоря, соответствие надо брать из xl/workbook.xml и xl/_rels/workbook.xml.rels. И даты в xlsx хранятся числом дней от 30.12.1899, и отличить дату от обычного числа можно только по стилю ячейки из xl/styles.xml.

Чем ловится. Требованием заказчика, у которого книга из пяти листов.

5.4. Есть ли такой метод у платформы — проверяется по бинарникам

Что пишут. Метод, который «точно был». Или которого не было никогда.

Что происходит. Модуль не компилируется, и вы теряете время, гадая, перепутали вы имя или версию платформы.

Как правильно. Спросить у самой платформы. Имена методов лежат в библиотеках в каталоге bin в кодировке UTF-16LE, и наличие имени проверяется поиском по ним:

имя = 'ОбходФайловЦиклом'.encode('utf-16-le')
for п in glob.glob(r'C:\Program Files\1cv8\<версия>\bin\*.dll'):
    if имя in open(п, 'rb').read():
        print('есть'); break

Файл справки .hbk для этого не годится — он сжат и поиском не берется.

Одна оговорка, без которой способ подводит. Имя в библиотеке означает, что платформа его знает, — но не означает, что оно доступно вам. Живой пример из этой же статьи: ЧасовойПоясИнформационнойБазы в бинарниках есть, а компилятор отвечает «Процедура или функция с указанным именем не определена». Отсутствие имени — приговор, наличие — только основание посмотреть справку.

Чем ловится. Этим же способом. Я проверил так шестнадцать имен перед тем, как писать код, и одно из них — как раз ОбходФайловЦиклом — в платформе отсутствует: его нет ни в одной из 279 библиотек и программ каталога bin, тогда как НайтиФайлы находится сразу в нескольких. Пять минут проверки против часа отладки.

 

Два мифа

Напоследок — то, что ошибками не является, хотя регулярно правится на код-ревью.

Переменная с именем Строка не мешает вызвать функцию Строка(). Раздражает — да, читается плохо — да, но кода не ломает. Переименовывать имеет смысл ради читаемости, а не ради корректности.

Совпадение номеров в Form.xml — не коллизия. Пулы идентификаторов раздельные для элементов, реквизитов и команд. Элемент с id="19" и реквизит с id="19" прекрасно уживаются: в типовой Бухгалтерии 3.0 такие совпадения есть в 3462 формах из первых четырех тысяч, то есть почти в каждой. Если вы «чините» это вручную, вы ломаете рабочую форму.

 

Шпаргалка

Все двадцать в одной таблице — чтобы не перечитывать статью, а пробежать глазами перед код-ревью.

Признак Что ломается Чем ловится
1.1 Попытка Число(...) Исключение 0 на данных из Excel минусы становятся нулями тест на файле с отрицательными из настоящего Excel
1.2 галка «Неотрицательное» у реквизита минус записывается как ноль поиск AllowedSign в выгрузке
1.3 КонецДня() или + секунды до проверки заполненности пустой период считается заданным сценарий с незаполненной датой
1.4 виды субконто в параметрах виртуальной таблицы счета без этого субконто выпадают сверка итога с ОСВ
2.1 СериализаторXDTO.XMLСтрока(Набор) исключение на наборе записей первый запуск на наборе
2.2 Новый Тип(...).Свойство модуль не компилируется попытка открыть обработку
2.3 ИмяПлатформенногоОбъекта = ... падение в рантайме только запуск
2.4 одно имя в двух написаниях накопление уходит в другую переменную сравнение алфавитов, поиск пар
2.5 многострочная вставка в //якорь запроса хвост вставки выходит из-под комментария печать собранного текста запроса
3.1 реквизит формы ХранилищеЗначения на 8.3 падает при присваивании, на 8.5 работает поиск ValueStorage в Form.xml
3.2 Элемент.РасширениеПоляВвода.Свойство такого объекта нет поиск имени по бинарникам
3.3 булева колонка без явного вида рисуется текстом «Да»/«Нет» InputField на xs:boolean в Form.xml
4.1 длительная операция из внешней обработки не запускается без подключения через БСП запуск обработки через «Файл → Открыть»
4.2 Состояние() в серверном контексте в толстом клиенте молчит, в тонком модуль не компилируется директивы компиляции, запуск в 1cv8c.exe
4.3 пересобрали обработку, окно не закрыли форма не создается заново, работает старый код Сообщить() не печатается
4.4 ТекущаяДатаСеанса() в клиентском коде в тонком клиенте модуль не компилируется запуск в 1cv8c.exe, а не в 1cv8.exe
5.1 ошибка компиляции в модуле формы обработки /CheckModules молчит, /Execute виснет без следов BSL LS до сборки, живой запуск после
5.2 ТабличныйДокумент.Прочитать(xlsx) листы склеиваются в один, Значение недоступно файл из двух листов с разными шапками
5.3 нужен отдельный лист xlsx границы листов в прочитанном документе нет ЧтениеZipФайла + ЧтениеXML
5.4 метод, который «точно был» не компилируется поиск имени по bin\*.dll в UTF-16LE

Что со всем этим делать

Ни один из двадцати не подсвечивается редактором конфигуратора. Большая часть не находится никаким статическим анализом в принципе: это свойства выполнения или вопросы к смыслу кода, а не к его форме.

Но кое-что видно прямо в выгрузке: ограничение знака у реквизита, два написания одного имени, реквизит формы с хранилищем значения, булева колонка без явного вида. Под такие вещи я написал восемь проверок по XML-выгрузке и прошел ими по типовым ERP и Бухгалтерии: семнадцать гигабайт выгрузки, сорок семь тысяч модулей. Обработка с этими восемью проверками приложена к статье: открывается в любой конфигурации через «Файл → Открыть», на вход — каталог выгрузки, на выход — таблица находок и TSV. Каждое правило отключается галкой. Результат прогона оказался поучительнее самих находок.

Вторым файлом приложена «Проверка поведения платформы» — та самая обработка, которой я проверял утверждения этой статьи. Открывается так же, через «Файл → Открыть», кнопка «Проверить все» выполняет двадцать шесть проверок на вашей базе и показывает, что ответила ваша платформа, а кнопка рядом выгружает результат в TSV. Проверок двадцать шесть, а пунктов двадцать: некоторые распались на несколько отдельных замеров, один только пункт 1.1 дал семь. Два пункта осознанно трогают базу и об этом написано прямо в форме: проверка по пункту 1.2 пишет элемент справочника в транзакции и откатывает ее, проверка по пункту 4.1 запускает и сразу отменяет фоновое задание. Следов не остается.

 

Обработка после нажатия «Проверить все». 1С:Бухгалтерия 3.0, платформа 8.5.1.1343, толстый клиент

 

Сверьте свой результат с моим: полностью он совпасть и не должен. В толстом клиенте у меня 23 подтверждено и 3 опровергнуто, в тонком перевернется пункт 4.4б, а на конфигурации без подходящего реквизита и без бухгалтерских итогов пункты 1.2 и 1.4 честно скажут «не проверено». Именно поэтому первой строкой обработка пишет, где она выполнялась.

Если у вас получится другой результат — это интереснее моего, напишите в комментариях, какая платформа и какой пункт.

Из восьми правил четыре были неверными. Ни одно не упало на тестах — все юнит-тесты были зелеными.

Три ошибки вскрылись только на объеме чужого кода. Правило про омоглифы ругалось на любую латинскую букву среди кириллицы и выдало четыре тысячи срабатываний, почти сплошь законные технические суффиксы. Правило про концы строк насчитало пятнадцать тысяч испорченных файлов, которые на поверку были в полном порядке: голый перевод строки сидел внутри текстов запросов. С ним заодно рассыпалось и само утверждение, ради которого правило писалось. Я собрал обработку из исходников, где снял BOM и заменил CRLF на голый перевод строки, — Конфигуратор загрузил ее на обеих платформах и не испортил ни одной кириллической буквы. Пункт про BOM из списка пришлось убрать, а правило в обработке я оставил: пусть считает, но верить ему на слово теперь не надо. Правило про глобальные имена работало по списку, собранному на другой конфигурации, и триста семьдесят девять раз обругало обычные переменные.

Четвертую я нашел, запустив проверки на них же самих. Инструмент отыскал в себе две ошибки, которых не было: он принял собственные образцы поиска, лежащие строковыми литералами, за настоящие вызовы.

Это, собственно, и есть главная мысль, только уже про инструменты, а не про код. Молчаливая ошибка не выдает себя ничем — включая зеленые тесты и включая тесты той проверки, которая должна ее ловить. Единственное, что ее вытаскивает, — прогон на данных, которых вы не писали.

Если у вас есть пункты в эту коллекцию — пишите в комментариях, дополню статью с указанием автора.

Вступайте в нашу телеграмм-группу Инфостарт

отладка подводные камни качество кода стандарты разработки ошибки разработчика BSL Language Server внешние обработки управляемые формы директивы компиляции тонкий клиент ошибка компиляции загрузка из Excel xlsx ТабличныйДокумент ЧтениеZipФайла ТекущаяДатаСеанса фоновые задания XML-выгрузка рефакторинг Бухгалтерия предприятия 3.0

См. также

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Системный администратор Разработчик Руководитель проекта 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях (УТ, БП, ERP, УНФ, КА и т.д.). Поддерживаются серверные и файловые базы, управляемые и обычные формы, интерфейс 8.5. Может выполнять свертку одновременно в несколько потоков, а также без непосредственного участия пользователя. Решение в Реестре отечественного ПО.

24900 руб.

20.08.2024    79713    405    171    

341

Инструментарий разработчика Роли и права Запросы СКД Разработчик Руководитель проекта 1С:Предприятие 8 Платные (руб)

Инструменты для разработчиков 1С 8.3 и 8.5: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

16500 руб.

02.09.2020    278293    1571    423    

1206

Разработка Инструментарий разработчика Групповая разработка (Git, хранилище) Разработчик Платные (руб)

Практический курс по работе с AI-агентами для разработчиков 1С уровня middle и senior, а также тимлидов команд: от постановки задач и передачи контекста до создания расширений, интеграций, MCP-инструментов, тестов и проверки готового решения.

50000 руб.

10.09.2026    2953    19    0    

12

Пакетная печать Печатные формы Инструментарий разработчика Разработчик 1С:Предприятие 8 Платные (руб)

Расширение для создания и редактирования печатных форм в системе 1С:Предприятие 8.3. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и прозрачность разработки, а также навести порядок в многообразии корпоративных печатных форм. Обновление версии от 21.04.26

22570 руб.

06.10.2023    42524    118    54    

131

Инструментарий разработчика Разработка Администрирование веб-серверов Системный администратор Разработчик Аналитик Руководитель проекта 1С 8.3 Платные (руб)

Analyzer 1C сводит выгрузку 1С — основную конфигурацию и все расширения — в единый граф знаний. Любой запрос по связям за доли секунды, с пометками «Доб.» / «Заимств.» / «Переопределено». Новое в 2.0 — обновление поставки: сравнение и объединение версий деревом «как в Конфигураторе» с выгрузкой плана решений; поиск конфликтов из-за перехватов расширений и висячих ссылок; загрузка из бинарных .cf/.cfe; циклические зависимости. Плюс анализ влияния, запросы BSL, роли и RLS, граф вызовов. Минута на развёртывание через Docker без необходимости подключения к Интернет. Любая 1С:Предприятие 8.3+.

14000 руб.

17.04.2026    13463    51    62    

60

Инструментарий разработчика Нейросети Платные (руб)

Первые попытки разработки на 1С с использованием больших языковых моделей (LLM) могут разочаровать. LLMки сильно галлюцинируют, потому что не знают устройства конфигураций 1С, не знают нюансов синтаксиса. Но если дать им подсказки с помощью MCP, то результат получается кардинально лучше. Далее в публикации: MCP для поиска по метаданным 1С, справке синтакс-помощника и проверки синтаксиса.

15250 руб.

25.08.2025    70903    141    41    

149

Инструменты администратора БД Инструментарий разработчика Роли и права Разработчик 1С:Предприятие 8 1C:Бухгалтерия Россия Платные (руб)

Расширение позволяет без изменения кода конфигурации выполнять проверки при вводе данных, скрывать от пользователя недоступные ему данные, выполнять код в обработчиках. Не изменяет данные конфигурации, легко устанавливается практически на любую конфигурацию на управляемых формах.

17000 руб.

10.11.2023    27839    101    46    

107

Мастера заполнения Поиск данных Инструментарий разработчика Подбор и обработка объектов 1С 8.3 1С 8.5 Платные (руб)

Infostart MagicInput улучшает подбор в полях ввода 1С: ищет по любой части названия и по нескольким ключевым фрагментам, распознаёт ввод в другой раскладке и показывает иконки/статусы объектов прямо в списке. Поддерживает вставку навигационной ссылки/представления документа для автоподбора; для разработчиков доступны поиск по GUID и полному имени предопределённого. Работает в управляемых формах и подключается в большинстве конфигураций 1С 8.3/8.5. Работает на возможностях платформы, внешние компоненты не используются.

6000 руб.

25.02.2026    6100    21    1    

21
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. coollerinc 189 22.09.26 17:23 Сейчас в теме
Вообще частая ошибка использование ЗаполнитьЗначениеСвойств, где надо и не надо, без указания нужного набора свойств. Если нет цели, универсального заполнения, лучше явно указать, какие свойства должны заполнятся или просто по классике, присваивать через Равно. Сэкономив 2-3 строчки кода, можно, получить много неочевидных багов.
Если свойства явно не указать, то вы и не узнаете, что это какое то свойство уже переименовали и заполнение уже как месяц не происходит. Если описать свойства в 3 параметре, то будет ошибка и вы сразу это заметите.

Так же бесит, что 1с не выдает ошибку, когда в один ссылочный реквизит, пытаются записать, что то другое. То реквизит просто очищается. Лучше бы исключение выходило бы.
Для отправки сообщения требуется регистрация/авторизация