Обсуждение накопленного

Автор Харлампий Дымба, 14 октября 2025, 01:31

« назад - далее »

Харлампий Дымба

Вот Сергей копит в копилку, а обсуждать там не разрешает.  Пусть будет тема для небольшого полуфлуда - обо всём и ни о чём. Шепотом.
Не только для копилки - потому что иногда натыкаешься на какую-нибудь интересную неожиданную ерунду, забавный пустяк, а может кому пригодится.

Вот про то, что Записать недокументировано возвращает значение, если применяется к элементу / документу локального контекста (а не созданному через СоздатьОбъект), оказалось интересно. Даже никогда не задумывался, что будет, если не заморачиваться с
Если Форма.ТолькоПросмотр() = 1 Тогда
 Форма.КнопкаОК.Доступность(0);
КонецЕсли;
А оказалось, что ничего не будет. Если форма открыта в режиме чтения (только на просмотр), то и Записать() и #Записать будут проигнорированы.  Так что использование Доступность(0) - просто способ показать пользователю, что нажимать кнопку бесполезно.

Ну и кстати #Записать и Записать() неравнозначны, вопреки тому что написано в als (там так: "данный метод отрабатывает те же действия, что и интерактивное нажатие пользователем кнопки с формулой ''#Записать''")
Записать() не вызывает ПриЗаписи() и игнорирует некоторые установки записи, типа СохранениеПериодическихРеквизитов(). Но вот ПриЗаписиПерепроводить(1) и АвтоВремя..(), а также регистрацию события в "Журнале регистрации" оба варианта отрабатывают одинаково. Ещё раз акцентирую, что Записать() - именно в локальном контексте, а не к созданному через СоздатьОбъект().

Записать() возвращает ноль не только, если форма открыта в режиме чтения, но и при нарушении уникальности кода. Правда, в этом случае всё равно выдаётся интерактивное окошко предупреждения "Код не уникальный", поэтому использовать Записать() для программной проверки возможности записи - не очень хорошо.

Харлампий Дымба

Написал, перечитал, задумался. Если ПриЗаписиПерепроводить(1) действует на Записать() локального контекста, то, значит я всё это время неправильно делал запись с перепроводением из формы документа:
Записать();
Если Проведен() = 1 Тогда
 Провести();
КонецЕсли;
В этом коде две проблемы:
1) двойное перепроведение, если ПриЗаписиПерепроводить() было установлено в единичку;
2) надо городить огород с единой транзакцией, чтобы не получилось, что реквизиты в документ записаны новые, а проведение документа, в случае неудачи, осталось по старым.
Подобную реализацию можно увидеть в процедуре ПечатьЧека() в ПКО и РКО в типовой Тис, например.

Правильный код для записи с перепроведением из модуля формы документа такой:
ЗапомнимПриЗаписиПерепроводить = ПриЗаписиПерепроводить(1);
Результат = Записать();//записывает и при необходимости перепроводит
ПриЗаписиПерепроводить(ЗапомнимПриЗаписиПерепроводить);
В этом случае документ будет при необходимости перепроведён, а в случае неудачного перепроведения - запись не состоится. И, что замечательно, Записать() вернёт 0 в случае неудачного перепроводения, а не только в случае неудачной записи.

Харлампий Дымба

Наткнулся у себя на баг 1С, про который не слышал раньше: итог по колонке может быть у нечисловой колонки.
Воспроизведение:
  • Создаем реквизит табличной части документа типа "Число" и ставим галку "Итог по колонке";
  • Сохраняем конфигурацию;
  • Меняем тип реквизита на какой-нибудь справочник;
  • Сохраняем конфигурацию.
При втором сохранении получаем сообщение, что изменено построение итогов по колонке, в диалоговой форме реквизита галка "Итог по колонке" визуализирована сброшенной. Но - это не так: в описании структуры метаданных "ИтогПоКолонке" равен 1, а проблемный реквизит присутствует в заголовочной таблице документа DH*.DBF. То есть итоги продолжают "считаться".

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

Код для проверки на наличие проблемных реквизитов:
Для СчДокументов = 1 По Метаданные.Документ() Цикл
    МетаДок = Метаданные.Документ(СчДокументов);
    Для СчРеквизитов = 1 По МетаДок.РеквизитТабличнойЧасти() Цикл
        МетаРекв = МетаДок.РеквизитТабличнойЧасти(СчРеквизитов);
        Если (МетаРекв.Тип <> "Число") и (МетаРекв.ИтогПоКолонке = 1) Тогда
            Сообщить(МетаДок.Идентификатор + "." + МетаРекв.Идентификатор);
        КонецЕсли;
    КонецЦикла;
КонецЦикла;
   

Злоп

Мсье знает толк в извращениях!

Харлампий Дымба

Цитата: Злоп от 22 октября 2025, 13:46Мсье знает толк в извращениях!
Согласен. Описал так, как будто это было намеренное издевательство над конфигуратором и здравым смыслом. Но всё было наоборот, это 1С поиздевалось надо моим здравым смыслом:
Увидел опечатку в комментарии к реквизиту табличной части, ну типа "Поздразделение ЦФО" и решил поправить. При сохранении - сообщение "изменено построение итогов по колонке...". И меня стопарнуло: какой пересчет итогов, если у я лишь поменял Комментарий к реквизиту типа "Справочник.Подразделения"?!
Просто такие баги могут быть достаточно вредными, система тебе визуально показывает одно, а сама делает другое. Тратит ресурсы на сохранение и подсчет итогов, там где их отродясь не было и не будет.

Вот ещё одни вредный баг из этой же серии (показываю одно, делаю другое):
Думаю зачем мне две формы списка справочника "ФормаСпискаОсновная" и "ФормаСпискаДляВыбора"? Удаляю "ФормаСпискаДляВыбора", при этом галочка "основная форма для выбора" перескакивает на "ФормаСпискаОсновная". Сохраняюсь, всё отлично, теперь у меня единая форма для обоих режимов работы.
А вот и нет - "основная форма для выбора" не установлена и система рисует пользователям свою корявую форму "по умолчанию".

Ну а в целом изврат, правда. Просто иногда есть рабочее время и желание не работать. На днях чУдно провёл время разбираясь с багом, известным в основном, как "Перестал отображаться автор в общем журнале". Вылечить - 2 минуты, придумать отчетик, проверяющий базу на наличие проблемы - 2 часа, определить причину возникновения - 2 дня. Зато - кайф, раскрыл ещё один мало кому уже нужный секрет.

Злоп

И почему перестал отображаться автор в ОЖ?

Злоп

Я на ряд ошибок и граблей просто тупо не наступал. Просто потому что делал как надо, а как не надо - не делал...

Злоп

Кстати, а итог-то по нечисловым колонкам считает? По идее тупо суммировать должен и варианта два: либо рухнет в ошибку, либо проссуммирует и получит 0 если содержимое чисто текстовое или какое о число если колонка содержит строковый реквизит, в котором строки типа
123
Петров.
.
А вот как суммирует если агрегатный тип - это интереснее: либо по преобразованию в текстовое представление либо тупо значение ссылки, а там вполне возможно число может быть в начале ссылки...?

Харлампий Дымба

Цитата: Злоп от 22 октября 2025, 17:35стати, а итог-то по нечисловым колонкам считает?
Похоже, что так же как и функция Итог() в локальном контексте документа - ставит ноль, если реквизит нечисловой, и считает сумму (кстати, независимо от галки "Итог по колонке" и длины числа). Т.е. для колонки "Мест" Число1.0 функция Итог("Мест") даст 5+5=10.
А вот .Итог() примененный к этому же документу, но полученному через СоздатьОбъект уже полезет в таблицу DH и вернет 5+5=9, либо ошибку, если галка "Итог по колонке" не установлена.

Цитата: Злоп от 22 октября 2025, 17:31...не наступал. Просто потому что делал как надо, а как не надо - не делал...
Враки. Иначе откуда бы ты узнал, как не надо ;) . Однажды наступив - долго обходишь стороной. Зачем разбираться с проблемой, если её можно обходить. Но, чтоб не получалось как в древнем баяне "Тут так принято" про обезьян, бананы и шланг с холодной водой, порой стоит вернуться и, при желании и наличии времени, разобраться: а что именно ты обходишь, надо ли это обходить, и каков радиус. Ну и интересно иногда походить по нехоженным дорожкам.


Харлампий Дымба

Ну и дополню: Итог() по таблице значений работает так:
Если тип значения колонки "Число" - суммирует независимо от заданной длины числа
Если тип значения не задан, то суммирует только если ВСЕ значения в колонке - явно заданные числа.
Во всех остальных случаях - ноль.

Харлампий Дымба

Функции - "ложные друзья программиста":
.ОбратныйПорядок() - без параметра.
Для справочника и документа - не делает ничего, постоянно наступаю на эти грабли;
Для периодического - устанавливает обратный порядок текущей (а если нет текущей - то последующей) выборки;
Для регистра - устанавливает обратный порядок последующей выборки.

ВыбратьДокументы(), ВыбратьЭлементы(), ВыбратьЗначения() - строят выборки и возвращают 0, если выборка пуста;
ВыбратьДвижения() - ничего не делает (ну только фиксирует флажки типа "ОбратныйПорядок"), всегда возвращает 1,  даже если движений нет.


Харлампий Дымба

GComp. Может открою Америку, но как-то с легкой руки Фёдора всегда считал, что Container.Profile - лишний мусор в мдшнике,  ну типа формы элемента для справочников, редактируемого только "в списке".
Оказалось, что это установки по работе с таблицей из диалоговой формы отчета/обработки (ну и пароли при необходимости) и бездумно удалять их не надо.

Состав Container.Profile:
Действия - Свойства формы
MoxelName (Использовать таблицу):
Пустую - "␁Blank"
Для ввода данных - Имя таблицы
MoxelPos (Положение):
0 - во всё окно (по умолчанию)
2 - снизу
3 - сверху
4 - справа
5 - слева
MoxelNextMode (Для ввода данных - Переход по строкам):
0 - нет
1 - по строкам (по умолчанию)
2 - по столбцам
Действия - Установить пароль
UUID:
"D41D8CD98F00B204E9800998ECF8427E" - пароль не установлен
Любые другие значения - хэш пароля?
Entry (Запрашивать пароль при исполнении):
0 - не запрашивать
1 - запрашивать (по умолчанию)


Харлампий Дымба

Баг «Не показывается колонка общего реквизита в журналах» aka «Исчез автор в общем журнале»
Описание: В непонятное время по непонятным причинам у пользователей перестаёт отоборажаться колонка с общим реквизитом, в конфигураторе – всё чётко.
Лечение: Удалить проблемную колонку – Закрыть форму списка (ЭТО ВАЖНО) – Сохранить – Добавить колонку.
Код для проверки наличия проблемы (программно открываем и закрываем все журналы и смотрим: есть/нет?):
СписокДляПроверки = СоздатьОбъект("СписокЗначений");
Для СчЖурналов = 1 По Метаданные.Журнал() Цикл
    МетаЖурнал = Метаданные.Журнал(СчЖурналов);
    ИдЖурнала = МетаЖурнал.Идентификатор;
    Для СчФорм = 1 По МетаЖурнал.ФормаСписка() Цикл
        ИдФормы = МетаЖурнал.ФормаСписка(СчФорм).Идентификатор;
        СписокДляПроверки.УдалитьВсе();
        Для СчОбщихРеквизитов = 1 По Метаданные.ОбщийРеквизитДокумента() Цикл
            СписокДляПроверки.ДобавитьЗначение(Метаданные.ОбщийРеквизитДокумента(СчОбщихРеквизитов).Идентификатор);
        КонецЦикла;    
        Для СчГраф = 1 По МетаЖурнал.Графа() Цикл
            СписокДляПроверки.ДобавитьЗначение(МетаЖурнал.Графа(СчГраф).Идентификатор);
        КонецЦикла;    
        Конт = "";
        ОткрытьФорму("Журнал."+ИдЖурнала+"."+ИдФормы+"#",Конт);
        Если ТипЗначения(Конт) = 100 Тогда
            Для Сч = 1 По СписокДляПроверки.РазмерСписка() Цикл
                ИмяКолонки = СписокДляПроверки.ПолучитьЗначение(Сч);
                Попытка
                    Конт.Форма.ПолучитьАтрибут(ИмяКолонки);
                Исключение
                    Продолжить;
                КонецПопытки;
                Конт.Форма.ПолучитьАтрибут(ИмяКолонки).Видимость(1);
                Конт.Активизировать(ИмяКолонки,1);
                Если Конт.Форма.ТекущаяКолонка() <> ИмяКолонки Тогда
                    Сообщить("Проблема отображения: "+"Журнал."+ИдЖурнала+"."+ИдФормы+"."+ИмяКолонки);
                КонецЕсли;
            КонецЦикла;
            Конт.Форма.Закрыть(0);
            Конт="";
        КонецЕсли;    
    КонецЦикла;
КонецЦикла;
Сообщить("Проверка завершена.");   

Пространно: заморочка с этим глюком в том, что он появлялся как бы из ниоткуда и в неизвестный момент времени. Юзеры не знают, что в журнале должен быть этот столбец, а программист не знает, что его там не стало. Первый раз, когда столкнулся, списал на рудимент от 7.5, потому что конфа была типовая комплексная 3, в которой осталось 4 журнала, перекочевавших, видимо, через преобразование из 7.5 из первого релиза Комплексной. А недавно, с удивлением, обнаружил этот глюк в своей нетленке. Поэтому, взяв ежедневные бэкапы баз за 23 года, установил день, когда исчез столбец, и проанализировав два соседних мдшника, понял механизм возникновения глюка.
Исходная конфигурация (ИК), два параллельных прямых потомка (П1 и П2). В П1 добавляем любой объект. В П2 добавляем общий реквизит и выводим его в форму списка журнала. Загружаем в П1 через объединение конфигурации. Готово: в П1 битый журнал. В форме списка журнала в конфигураторе столбец виден, а в режиме «1С: Предприятие» не показывается.
Тестовый стенд в пустой пробной: создать документ, журнал, форму списка журнала, разделить на 2 потомка, в первом создать константу, во втором - общий реквизит документов и добавить его в форму списка журнала, накатить мдшник на первый потомок через объединение - баг проявился.
Если разложить проблемный мдшник, то в столбце в значении ИдентификаторМетаданных будет содержать мусорный id (а если «повезёт», то существующий, но от другого объекта).
С какого-то релиза разработчики о глюке узнали, но механизм его возникновения не поняли, и в момент открытия журнала стали просто делать проблемный столбец невидимым. А может и поняли, но по сумме характеристик, решили, что проще так, чем исправлять.
Возникновение глюка вполне укладывается в распространённый механизм обновления типовых, когда свои изменения (и в их числе, например, общий реквизит «Автор», которого так не хватало в типовой бухии) переносишь в новый релиз типовой через объединение, а потом накатываешь на рабочую базу.
То есть причина - в некорректной работе объедения конфигурации: когда замещаешь форму списка журнала документа, то ИдентификаторМетаданных не изменяется вслед за изменением Идентификатора исходного объекта (например, общего реквизита «Автор»), а остаётся как был в накатываемой конфигурации.
Лечение историческое: добрый Ёпрст gcompом(?) менял страждущим ИдентификаторМетаданных на правильный;
Лечение хирургическое: убрать столбец вообще: «вам это не надо»;
Лечение паллиативное: делать такие столбцы текстовыми;
Лечение правильное: в шапке. Но – зараза – если форму списка после удаления не закрыть, то столбец 1С вставит обратно проблемный - из кэша. Обо что многие и спотыкались. И это ещё одна, другая, багофича.

Злоп

Вот этого не понял

 ИдентификаторМетаданных не изменяется вслед за изменением Идентификатора исходного объекта (например, общего реквизита «Автор»), а остаётся как был в накатываемой конфигурации.

В П1 реквизита автор не было. В П2 - есть, общий реквизит, в П2 положили общий реквизит на журнал. Объединили с П1. Теперь в П1 столбец Автор в журнале в конфигураторе есть ИлентификаторМетаданных "автор" ?), но в базе в нем лежит кривой ид ?

Что за упомянутый "идентификатор исходного объекта"???

ИдентификаторМетаданных - это имя реквизита в конфиге или id в пользовательском режиме? Идентификатор исходного объекта - это в конфигураторе или id в пользовательском режиме?

Злоп

По идее трабла не должно быть если в обновляемой конфиге (НА которую накатываем) в базе нет документов. По мере создания документов в уже обновленной базе - все должно быть ок