Эта статья – про обычные формы платформы «1С:Предприятие 8». Так уж вышло, что «восьмёрка» местами всё ещё сырая, а управляемые формы – и подавно. Поэтому многие компании, особенно в регионах, до сих пор сидят на обычных, чего и вам желаем. Шутка. Наполовину.
Зачем вообще возвращаться в прошлое?
Что такое легаси (ред.: от англ. legacy – наследие), знают все и не понаслышке. У нас типичная история жанра «хорор»: конфигурация старая, очень старая, но обросла таким количеством «добра», что просто перевести её на управляемые формы, по оценкам, стоило бы компании около трёх лет работы и раздувания штата разработчиков в 2-3 раза.
И вот сидим мы, программисты, а современный мир проходит мимо. Git? Ну, есть. А толку-то? Формы не посмотришь, не смёрджишь (ред.: от англ. merge – сливать, соединять) – лежат в репозитории просто для галочки. Пул-реквесты получаются неинформативные: 60-70% изменений происходят на формах (так заведено, и не спрашивайте, почему), и, всё равно, принимать их приходится пачкой через конфигуратор.
А тут ещё и задача локализации подоспела.
В какой-то момент я прикинул, сколько времени теряю на то, что не вижу из пул-реквеста, что именно поменялось. И что не могу даже нормально применить ИИ, а с обычными формами это отдельная сказка: ИИ не видит ни подписки, ни типы данных, ни реквизиты формы. Любая работа с ним превращается в угадайку: а есть ли вообще такой реквизит, и точно ли он того типа, что я думаю?
Не разработка, а сказка, и не та, где все жили долго и счастливо.
Но душа просит полёта, натура программиста требует обрасти ленью, и чтобы ИИ всё делал за тебя. Так и началось наше путешествие в обычные формы.
В итоге – всё хорошо, формы успешно распарсились, всё работает. Но по таск-трекеру на это ушло более 500 часов, и это был не первый мой заход в эту область: я уже лет 5 это ковыряю, но только сейчас появилось достаточно времени. Так что, если кто-то решит повторить подвиг, запасайтесь временем, и никакой ИИ вам тут не помощник (об этом ниже).
Зато пока ковырялся «в кишках», узнал много интересного и понял, почему фирма 1С сама не сделала конвертацию форм в нормальное читаемое представление. Причина простая – а зачем? Мёрджить такие формы как обычный код, всё равно, толком не выйдет. Переименовать реквизит, поменять локализацию, добавить тип – да, это работает. А вот просто добавить ещё одно поле на форму, через текстовый дифф (ред.: от англ. difference – различие) – это уже сложно. Почему? Ну-у-у, координаты, якоря, табличные индексы, ссылки между контролами по числовым индексам (index) – всё уезжает. Это не невозможно, и я это успешно делаю, но надо понимать, с чем вы столкнетесь.
Сейчас предлагаю окунуться в мир особенностей обычных форм, чтобы прочувствовать всю «красоту».
Призраки
Обычные формы иногда хранят призраки – фрагменты старого кода или огрызки данных. Зачем, почему – неизвестно. Если их удалить, ничего не ломается, форма работает как раньше. Но пока я дошёл до понимания, что это именно мусор, а не что-то полезное, лучше бы сериал посмотрел.
Версии
Обычные формы построены на принципе версионирования, и сделано это, в общем-то, до простоты гениально. У того же поля ввода версий – больше пяти, и каждая новая версия просто дописывает поля в конец структуры:
v1: Имя, Заголовок
v2: + Многострочность, + ИконкаКнопкиВыбора
v3: + ...
Из-за этого получается интересный эффект: если открыть форму в старой платформе, она просто проигнорирует всё, что относится к более новым версиям, а при сохранении ещё и обрежет хвост. То есть «откатить конфу на старую платформу» – это, в том числе, и тихая потеря данных формы.
Замечали когда-нибудь, на форме десяток одинаковых полей ввода, и два из них – ну, вот чем-то отличаются? То ли рамка чуть угловатей, то ли поведение чуть другое, при этом в свойствах визуально всё одинаково? Это как раз оно: у них разные внутренние версии, и недостающие свойства подставляются дефолтами по-разному.
Типы
Отдельная история. Открываешь обработку от Розницы в УТ – и вместо ссылок в полях ввода видишь тип «Строка». Хотя справочники с точно такими же именами в УТ есть. Дело в том, что в обычных формах хранится GUID метаданных, а не имя. Если GUID конкретного справочника в конфигурациях не совпадает, ничего не находится. И обратный, более коварный случай: программист переименовал справочник, а рядом создал новый с тем же старым именем – форма молча подхватит «соседа» по совпадению GUID, а узнаете вы об этом уже лишь на проде.
Хвосты
Казалось бы, ну скобочный файл, куда уж хуже? А хуже есть куда, поверьте: внутрь скобочного файла иногда зашиты base64-строки со сжатыми блобами, причём, с разными вариантами переносов и алгоритмов. Внутри блоба – СжатиеДанных (deflate). А внутри уже распакованного блоба – снова структура, описывающая, например, типы данных колонки таблицы.
То есть, «определение типов одной колонки» – это base64 → deflate → ещё одна структура → бинарный хвост. Я нашёл, как это раскручивать, но за это время мог бы посмотреть ещё один сериал. И вишенка на торте – там ещё и разные версии алгоритмов сжатия.
Табличные документы
Это, вообще, отдельная эпопея. Кажется, специально старались сделать сложно: версии вложены в версии, флаги ветвят формат, секции, объединённые ячейки, а ссылки на цвета, шрифты и стили – и вовсе отдельными списками. Прыжки по версиям и форматам – на каждом шагу. Здесь больше всего «знакомств с граблями» и было.
Перечисления
В конфигураторе у свойства красивая надпись «Центрирование: По центру». В скобочном файле – 0, 1, 2, 3. Никаких имён. Чтобы понять, какое число какому варианту соответствует, приходилось идти в конфигуратор, переключать значение, сохранять, дизассемблировать, сравнивать дифф, фиксировать соответствие. Очень хотелось делегировать клики ИИ-агенту, но об этом – в конце.
Версии платформы
Если думаете, что после версий объектов всё проблемное закончилось, то – нет. Сама платформа от релиза к релизу тоже меняет сохранение байтов. Где-то меняется порядок полей, где-то добавляется «мусор» в хвост, где-то перетасовываются флаги. До части этих различий я просто не докопался – оно работало, и ладно.
Неуникальность
Обычные формы каким-то образом позволяют создавать элементы с неуникальными именами. Я встретил такое всего пару десятков раз. Форма при этом работала, но я посчитал это багом – парсер на такое ругается, чтоб потом можно было найти и поправить.
* * *
Историй ещё много, и может показаться, что я жалуюсь (хотя так оно и есть), поэтому давайте перейдём к тому, что же, в конце концов, вышло.
Что получилось?
Как это сейчас работает у меня?
Форма теперь раскладывается в YAML вот в таком виде (ниже – фрагмент, сильно урезан – оригинал длиннее, но логика читается внятно):
_parser: forms-tool@1
form_body:
ver: 18
size: [506, 337]
title: {ru: Версии}
controls:
_root:
uuid: FormPanel
data:
panelVer: 26
pages:
entries:
- {name: Страница1, title: {ru: Страница1}}
children:
НадписьПолныйНомер:
type: Label
data:
Caption: {ru: "Номер версии:"}
layout:
pos: {rect: [8, 34, 99, 53], tabOrder: 3}
ПолныйНомер:
type: InputField
data:
MultiLine: true
ReadOnly: true
Length: 50
typeDescription:
segments:
- {type: String, length: 50, fixed: true}
appearance:
ToolTip: {ru: Полный номер}
ActionBinding:
actions:
- {command: CurrentFormObject_Undefined, bindingType: 4}
events: {OnValidate: OnValidate}
layout:
pos: {rect: [105, 34, 498, 53], tabOrder: 4}
Владелец:
type: InputField
data:
ReadOnly: true
typeDescription:
segments:
- {type: СправочникСсылка.Конфигурации}
appearance:
ToolTip: {ru: Конфигурация}
ActionBinding:
actions:
- {command: CurrentFormObject_Reference, bindingType: 5}
events: {OnValidate: OnValidate}
layout:
pos: {rect: [105, 8, 498, 27], tabOrder: 2}
СобранныеДанные:
type: TableBox
data:
DataRef: {type: СправочникТабличнаяЧасть.Версии.СобранныеДанные}
TableBoxParams:
ColumnSettings:
columns:
- НомерСтроки: {width: 28, title: {ru: N}}
- Расширение:
width: 150
title: {ru: Расширение}
type: InputField
format:
segments:
- {type: СправочникСсылка.Расширения}
- ИмяФлага:
width: 129
title: {ru: Имя флага}
format:
segments:
- {type: String, length: 100, fixed: true}
- ЗначениеФлага:
width: 171
title: {ru: Значение флага}
format:
segments:
- {type: String, length: 10, fixed: true}
- ДатаСбораДанных:
width: 100
title: {ru: Дата сбора данных}
format:
segments:
- {type: Date, dateParts: D}
layout:
pos: {rect: [8, 84, 498, 304], tabOrder: 7}
extra_trees:
AutoTitle: true
DefaultButton: 1
DataSources:
bindings:
- name: СправочникОбъект
dataRef:
segments:
- {type: СправочникОбъект.Версии}
В реальном файле есть ещё _meta-блоки, точные якоря, цвета, шрифты, события на каждое поле – всё то, что нужно для побайтового round-trip обратно в Form.bin. Но читать-то нужно вот это: имена, типы, заголовки, размещение, привязки. Остальное – фоном. И да, каждый раз при разборке, я автоматом делаю сборку и проверяю, что хеш совпал, – так, на всякий случай, а то бывали приколы. Благо, что это быстро, параллельно, и в моём случае занимает от 10 секунд до минуты (зависит от количества потоков).
Что приятно
1. Локализация наконец видна. Все строки, которые надо переводить на другой язык, лежат в одном виде:
title: {ru: Страница1}
Caption: {ru: "Номер версии:"}
ToolTip: {ru: Полный номер}
По диффу сразу видно, что переименовали и где. Чейнджлоги стало писать заметно легче, и документацию обновлять – тоже. А то раньше, как? Один программист переименует что-то, а остальные потом ходят и голову ломают: почему кнопка отсутствует? Ан нет, она есть – её просто переименовали.
2. Имена реквизитов – на поверхности. ИИ теперь видит структуру:
ДатаПубликации: # имя реквизита
...
columns:
– ДатаСбораДанных: ... # имя колонки таблицы
3. Типы стали прозрачными:
- {type: СправочникОбъект.Версии}
- {type: СправочникСсылка.Расширения}
- {type: Date, dateParts: D}
Никаких больше «угадай, что за справочник» – всё видно прямо в форме.
4. События формы – тоже наружу:
events: {OnValidate: OnValidate, OnChange: OnChange}
Можно сразу увидеть, какие обработчики вообще вызываются, даже без открытия модуля и поиска по тексту.
Зачем вообще столько мороки?
Первое – помощь ИИ. КПД у меня вырос в несколько раз, и, в основном, потому что ИИ стал на порядок меньше галлюцинировать. Теперь можно сказать: «посмотри, как сделано в той обработке, и повтори в новой» – и оно действительно работает, потому что ИИ видит реквизиты, типы и структуру, а не играет в угадайку по именам в коде. Аналогично, теперь он понимает, где используется какой справочник, и какие реквизиты вообще у формы есть.
Второе – потенциальные ТЗ для перехода на управляемые формы. Уже сейчас вполне реально сказать ИИ: «сделай как тут, только в новом стиле», и я провёл несколько экспериментов. Справляется неплохо, надо только подкинуть ему контекста про управляемые формы. Скиллы – наше все.
Третье – Git. Теперь по диффу можно прикинуть, что менялось, а иногда даже спокойно смёрджить (в простых случаях – переименования, локализации, правки типов).
Четвёртое – это было увлекательно. Некоторые приёмы я унёс себе как идеи для реализации в других проектах. Например, та же логика версионирования через дописывание полей в конец – она вроде на поверхности лежит, но я сам как-то не думал, что так можно проектировать форматы.
Но самый полезный итог: теперь я понимаю, что это за боль, и как делать точно не стоит.
Что получилось и что не получилось?
Не все поля удалось распарсить – какие-то редкие места до сих пор от меня ускользают, и я не стал в них зарываться. Цель была не повторить парсер 1С один-в-один, а привлечь ИИ к разработке. Те ~99% форм, что удалось разложить полностью, эту задачу закрывают.
Когда парсер заработал, всплыл следующий нюанс: ИИ не умеет думать пространственно. А в обычных формах важны именно координаты на плоскости, а не порядок объявления контролов – тот же tabOrder ничего не говорит о визуальном расположении.
Поэтому следующим шагом стал рендер формы в картинку. Я долго экспериментировал с разными технологиями (и поначалу выбрал не ту), но в итоге остановился на формате SVG: масштабируется без потери качества, открывается, чем угодно. А поверх SVG приделал конвертацию в PNG через resvg. Потому что ИИ всё ещё воспринимает SVG хуже, чем растр. Качество подсказок выросло кратно: модель начала подбирать ширину полей так, чтобы ничего не вылезало, и понимать, на какой странице что лежит. А для страниц – я просто говорю: «выставить свойство постранично» и... Хотел бы так сказать, но тогда, если у вас 100 вкладок, там получается такой хайрез, что не всякий айфон переварит. Поэтому в скиллах я сказал ему просто двигать нужную страницу вверх и смотреть.
Раз уж SVG есть, сама судьба велела сделать его динамическим. Согласитесь, не очень весело – каждый раз компилировать в .bin, обновлять конфу, открывать конфигуратор, искать форму, кликать по вкладкам. Поэтому я сделал расширение для Visual Studio Code: клик по Form.bin – открывается кликабельный превью с переключением вкладок прямо в редакторе. И чтобы вы могли ощутить эту красоту, я решил поделиться ею с вами.
Дальше – а почему бы не встроить это в документацию, чтобы не делать сотню скринов руками? Сделал, и оно даже заработало. Хотя, кто сейчас, кроме ИИ, читает документацию... А ему, как раз, нужны картинки в PNG. Замкнутый круг.
С другой стороны, теперь и аналитики могут брать код, спрашивать по нему, составлять внятное ТЗ и даже попросить ИИ что-то подправить по дизайну и посмотреть, как оно будет. Это реально стало крутым!
Главный урок: ИИ – не серебряная пуля
Изначально идея была красивая: скормить ИИ десяток тысяч форм и сказать «разберись сам». Что-то он действительно нашёл – это дало неплохой стартовый буст, и я уже думал: «ну всё, за неделю справлюсь». А когда начал проверять детально, оказалось, что половину полей и свойств он назвал не так или вообще перепутал. Сейчас, задним числом, понимаю, что на ранних этапах без ИИ было бы быстрее. Он создавал ощущение прогресса, которого по факту не было. И это, на самом деле, относится не только к этой задаче: очень часто программисты переоценивают помощь, которую даёт ИИ.
Второй заход – «дам ИИ конфигуратор и скажу: наклепай мне тысячу форм, в каждой меняй по одному свойству, и пиши в комментарии, что именно изменил». За ночь ИИ-агент сделал штук 20 форм, и в половине из них сами комментарии не сходились с тем, что он на самом деле в форме менял. Управление мышью и окнами для модели оказалось настолько медленным и ненадёжным, что проще было бы набить табличку соответствий руками. С этими картинками платформенными у меня до сих флешбеки, я их так все и не добил – у меня нервов не хватило. На 100+ картинок – я уже готов был бросить вообще весь проект.
Так что, главный вывод: ИИ – мощный инструмент, но не везде и не для всего. Иногда его наделяют качествами, которыми он не обладает (или обладает, но игра не стоит свеч). Понять, где именно проходит эта граница, можно только собственной болью, что я и сделал, потратив на это вечность и еще чуть-чуть.
Бонус: иконки
Параллельно, пока решал проблему «откуда взять стандартные картинки», наткнулся на отличный репозиторий – Jet от 1Ci. Там MIT-лицензия и большой набор стандартных иконок 1С, которые можно свободно использовать. До сих пор удивляюсь, почему сама фирма 1С не выложит в паблик все свои иконки, желательно ещё и в SVG – в некоторых местах PNG-шки уже откровенно режут глаз. Хорошо, хоть ребята из 1C International открыли – глядишь, кто-нибудь и перерисует.
Если коротко: обычные формы – это боль, но познаваемая. Их можно вытащить в текст, увидеть в Git, показать ИИ и даже отрендерить в превью. Это не сделает их управляемыми, но позволит работать с ними хотя бы как в XXI веке. Да, в Git, всё равно, чаще приходится мёрджить в стиле «взять все из одной ветки» (я про формы), но, с другой стороны, это хоть даёт понимание, что вообще там изменилось, а с третьей, «чаще» – не значит «всегда», так что и классический мёрдж тоже вполне себе делаем.
Подводя итоги, попробую ответить на несколько вопросов:
- Стоило ли оно того? Однозначно, да! С одной стороны, я потратил кучу времени, но этим сейчас пользуется 10 разработчиков, так что, если посчитать выхлоп, он однозначно есть, и, со временем, этот баланс будет уходить в плюс всё больше и больше.
- Повторил ли я бы этот подвиг, например, для версии 1С 7.7? Ну, у меня такой задачи не было, однако, если была бы, то думаю, что да. Всё же ИИ даёт значительный буст, и на каждого сотрудника, который не просто является пользователем, а обслуживает конфигурацию (это и разработчики, и аналитики, и тестировщики, и писатели документации, и т.д.) – у них у всех будет значительный буст.
- Как я это делал? Всё просто – открыл конфу, создал сотню форм, изменил одно свойство, выгрузил и посмотрел. Благо, что это была не первая моя попытка, и у меня на момент этой итерации уже было около 500 форм, которые я накликал за 5 лет в тот момент, когда меня уже начинала бесить беспомощность. Так что, я вначале распарсил то, что было, а потом уже точечно досоздал еще штук 100 форм (самыми сложными были картинки). Ну, а потом уже просто поглядывал, т.е. я знал, что я изменил, и смотрел в Git: а что оно выгрузило, и просто поправлял, где и что было не так.
- Не проще ли перейти на управляемые формы? Это, прямо, классический вопрос. И да, я уже раз 10 обращался к руководству клиентов, сидящих на обычных формах, и предлагал это, но увы, ответ всегда был один: «когда-нибудь потом». Даже один клиент как-то пригласил 1С-ников из числа конкурентов, и те оценили длительность перехода в 3-5 лет. И сразу предупредили, что им нужно будет 50% времени собственных разрабов клиента, так как ну, о-о-очень много легаси. А у нас и так времени – ноль. Так что, формально, да – это бы решило все технические проблемы, но какой ценой для бизнеса?
- Как это можно использовать ещё? Моя самая любимая игра – поручить ИИ, чтобы он выровнял все поля под линейку, растянул формы как надо, иногда потыкал привязки, переименовал поля и т.д.
- Как я могу быть уверен, что если разобрал, то соберу обратно? Ну, тут всё просто – я разбираю, потом сразу собираю и сравниваю хеши. Если обратно собрать бит в бит не получается, я выдаю ошибку. Могу ли я быть уверен, что я правильно соберу после того, как что-то отредактируется? Нет, ну, точнее, далеко не на 100%. Хотя это относится и к управляемым формам в xml – разве можно сказать, что после того, как ИИ что-то наменял, форма 100% соберется? Нет, вот и тут так же.
Теперь про само расширение – оно написано для Visual Studio Code, низом запускается конвертатор на на языке Dart. Почему Dart? Мне нужна была скорость, а ИИ сказал, что это самый быстрый вариант, а с точки зрения разработки, какая разница, в чем писать лапшу из If-Else? Вы просто его устанавливаете, выбираете Form.bin файл, и видите такую картинку:

А это оригинал в конфигураторе:

А вот пример того, как оно выглядит вживую на видео:

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