Почему в 1С 7.7 самопроизвольно удаляются и задваиваются элементы справочника Номенклатура?

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

Каждый разработчик, поддерживающий конфигурации на платформе 1С:Предприятие 7.7, рано или поздно сталкивается с загадочными явлениями. Одно из самых опасных и труднообъяснимых — это внезапное исчезновение позиций номенклатуры (или элементов других справочников) с одновременным появлением «битых» дублей других элементов. При этом в Журнале регистрации нет никаких записей об удалении или изменении этих объектов, тестирование и исправление (ТИИ) не находит ошибок, а стандартная переиндексация базы данных не решает проблему.

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

---

Симптомы проблемы: как проявляется сбой

Проанализируем типичный сценарий проявления этой ошибки на примере справочника Номенклатура:

  1. С определенной периодичностью из базы полностью пропадает позиция (это может быть как отдельный элемент, так и целая группа).
  2. Одновременно с этим визуально «задваивается» другой элемент справочника. В списке появляются две строки с одинаковым наименованием.
  3. Оба появившихся «дубля» оказываются поврежденными («битыми»). У них могут отсутствовать реквизиты, не выводятся периодические реквизиты (например, цены или размеры), а при попытке сформировать отчеты движения по ним двоятся или отображаются некорректно.
  4. В Журнале регистрации по пострадавшим объектам отсутствуют какие-либо записи, подтверждающие ручное вмешательство пользователей или работу обработок обмена данными.

Выясним, почему стандартные средства СУБД и встроенные механизмы 1С 7.7 не сигнализируют о проблеме. Дело в том, что физического удаления строки из таблицы базы данных не происходит. Происходит некорректная перезапись существующей строки данных другой информацией, из-за чего нарушается целостность индексов и логических связей.

---

Причина сбоя: коварный метод ИспользоватьСписокЭлементов()

Причиной этой проблемы является общеизвестный, но официально так и не исправленный платформенный баг ядра 1С 7.7. Он напрямую связан с совместным использованием метода ИспользоватьСписокЭлементов() и интерактивного изменения данных в форме списка.

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


Процедура ПоискПоПодстроке(Реж)
    Если (ПустоеЗначение(СокрЛП(ПодСтрока))=1) и (Реж=1) Тогда
        ИспользоватьСписокЭлементов();
        Возврат;
    КонецЕсли;
    СохрИсторию();    
    
    Если Реж=1 Тогда
        СписокНом = глПоискПоСтрокеНомВозвратСписка(ПодСтрока);
        СписокНом.Сортировать();
        Если ПустоеЗначение(СписокНом)=0 Тогда
            ИерархическийСписок(0);
            ИспользоватьСписокЭлементов(СписокНом);
        КонецЕсли;
    ИначеЕсли Реж=2 Тогда
        Параметр = СоздатьОбъект("СписокЗначений");
        Параметр.ДобавитьЗначение(СокрЛП(Подстрока),"Подстрока");
        Параметр.ДобавитьЗначение("Справочник.Номенклатура","Источник");
        Параметр.ДобавитьЗначение("Номенклатура","ВидИсточника");
        ОткрытьФормуМодально("Отчет",Параметр,КаталогИБ() + "ExtForms\мои\НоменклатураОбрабГруппТиповая.ert");
        
        Если ТипЗначенияСтр(Параметр) <> "СписокЗначений" Тогда
            Возврат;
        КонецЕсли;
        Подстрока = Параметр.Получить("Подстрока");
        СписокВрем = Параметр.Получить("СписокТоваров");
        Если ТипЗначенияСтр(СписокВрем)="СписокЗначений" Тогда
            Если СписокВрем.РазмерСписка()>0 Тогда
                СписокВрем.Сортировать();
                Если ПустоеЗначение(СписокВрем)=0 Тогда
                    ИерархическийСписок(0);
                    ИспользоватьСписокЭлементов(СписокВрем);
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Когда мы накладываем на форму списка фильтр с помощью метода ИспользоватьСписокЭлементов(СписокНом), платформа строит виртуальное представление данных. Если в этот момент пользователь решает отредактировать элемент непосредственно в списке, создать новый элемент копированием или просто открыть карточку элемента на редактирование и нажать кнопку "Записать", внутренний механизм позиционирования СУБД дает сбой.

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

---

Как восстановить поврежденные данные вручную

Если сбой уже произошел, восстановить потерянный элемент стандартным «Тестированием и исправлением» невозможно. Нам придется прибегнуть к прямому редактированию файлов базы данных с помощью DBF-редактора (например, DBF Navigator или аналогичного софта). Выполним восстановление по шагам:

  1. Определяем системный ID пропавшего элемента. В старой (резервной или вчерашней) копии базы находим пропавший элемент и с помощью любой обработки просмотра реквизитов или отладчика определяем его внутреннее значение ID (например, это 36-ричный код вида " 1B34").
  2. Находим поврежденные записи в боевой базе. Открываем файл таблицы справочника номенклатуры (например, SC123.DBF, предварительно определив имя файла таблицы в словаре данных 1CV7.DD) в DBF-редакторе. Находим строки по коду задвоившейся номенклатуры.
  3. Идентифицируем «битую» строку. У поврежденной строки внутренний ID в колонке физического идентификатора таблицы не будет соответствовать логическим реквизитам, либо в нем будет содержаться тот самый старый ID пропавшей позиции (в нашем примере " 1B34"), тогда как все текстовые поля (наименование, артикул) затерты данными «задвоенного» элемента.
  4. Реставрируем данные. Сверяясь со старой резервной копией, вручную переносим текстовые реквизиты, значения периодических реквизитов и ссылки пропавшего элемента в поврежденную строку боевого DBF-файла. Особое внимание уделяем сохранению оригинального ID записи.
  5. Удаляем индексы и переиндексируем базу. Закрываем DBF-редактор, физически удаляем все файлы с расширением *.CDX из каталога базы данных и запускаем 1С:Предприятие в монопольном режиме для полной переиндексации.

Важное предупреждение: Перед любыми манипуляциями с файлами таблиц напрямую обязательно делайте резервную копию всей базы данных!

---

Как гарантированно защитить базу от повторения бага

Поскольку платформа 1С 7.7 давно снята с поддержки и обновлений ядра ожидать не приходится, разработчикам необходимо программно исключить условия возникновения этого бага.

Решение 1. Полный запрет редактирования в списке

Самый простой и надежный административный способ борьбы со сбоем:

Решение 2. Программный сброс фильтра перед открытием карточки

Если пользователям жизненно необходимо редактировать элементы, мы можем программно отключать опасный фильтр в момент открытия формы элемента и возвращать его обратно после закрытия. Рассмотрим, как это реализовать.

В форме списка справочника перехватываем открытие элемента и перед вызовом формы сбрасываем фильтрацию:


Процедура ПриОткрытииРедактирования()
    // Перед тем как открыть карточку элемента на редактирование,
    // временно отключаем список элементов во избежание платформенного бага
    ИспользоватьСписокЭлементов(); 
КонецПроцедуры

После закрытия формы редактирования элемента (например, при активизации формы списка или при ее обновлении) мы можем снова наложить фильтр ИспользоватьСписокЭлементов(СписокНом). Это обезопасит базу данных от ложного позиционирования драйвера СУБД.

Решение 3. Использование Таблицы Значений на форме списка

Если вам требуется реализовать сложный поиск или подбор элементов, откажитесь от стандартного отображения справочника в пользу Таблицы Значений (ТЗ), размещенной на форме:

  1. Создаем стандартную форму, но вместо дерева и списка справочника размещаем элемент диалога типа «ТаблицаЗначений».
  2. При поиске или фильтрации программно выбираем элементы справочника (запросом или перебором) и заполняем ими ТЗ.
  3. Пользователь видит только отфильтрованные строки в ТЗ. При двойном клике на строку мы позиционируемся на реальный элемент справочника программно с помощью метода НайтиЭлемент() и открываем его форму: ОткрытьФорму(ТекущийЭлемент);

Этот подход на 100% защищает физические таблицы базы данных, так как метод ИспользоватьСписокЭлементов() вообще не вызывается.

Решение 4. Переход на внешние компоненты (1С++ и FormEx)

Для крупных и высоконагруженных систем лучшим решением является использование внешней компоненты 1С++. С помощью класса ТабличноеПоле можно связать сетку на форме с прямым SQL-запросом к базе данных (даже если она работает в DBF-формате через драйверы). Это не только навсегда решает проблему порчи данных, но и ускоряет работу интерфейса подбора номенклатуры в десятки раз.

---

Резюме

Платформенная ошибка с методом ИспользоватьСписокЭлементов() в 1С 7.7 — классический пример того, как удобный встроенный функционал может стать причиной критического повреждения базы данных. Используя предложенные нами архитектурные обходы (запрет редактирования в списке, программный сброс фильтров или вывод данных через ТЗ), вы сможете гарантировать абсолютную сохранность данных и стабильную работу вашей системы.

← На главную