В процессе работы в 1С:Предприятии 7.7 (особенно на платформе MS SQL Server) иногда возникает неприятная ситуация: документ введен на основании другого документа, ссылка на документ-основание заполнена корректно, однако при открытии контекстного меню Действия — Подчиненные документы или при вызове программного метода ВыбратьПодчиненныеДокументы() подчиненная позиция не появляется в списке.
Разберем внутренние механизмы платформы, выясним причину сбоя и рассмотрим по шагам методы диагностики и исправления ошибки.
Связи подчиненности в 1С 7.7 хранятся в специальной системной таблице _1SCRDOC. В ней содержатся следующие ключевые поля:
PARENTVAL — типизированная ссылка на документ-основание (родительский документ).CHILDID — внутренний идентификатор (IDDOC) подчиненного документа.CHILD_DATE_TIME_IDDOC — служебная строка фиксированной длины (дата + время в 36-ричном формате + IDDOC), определяющая точную хронологическую позицию подчиненного документа.Штатный механизм построения журнала подчиненности и метод ВыбратьПодчиненныеДокументы() работают не по простому равенству идентификаторов, а используют индексное позиционирование по связке PARENTVAL и CHILD_DATE_TIME_IDDOC. Платформа ожидает, что значение поля CHILD_DATE_TIME_IDDOC в таблице _1SCRDOC в точности совпадает со значением поля DATE_TIME_IDDOC в таблице общего журнала _1SJOURN.
Если документ был смещен по шкале времени (например, при проведении нескольких документов в последнюю секунду суток 23:59:59, при сбоях сетевого соединения, блокировок или сторонних механизмов прямого обновления таблиц), таймштамп в _1SJOURN может измениться, в то время как в _1SCRDOC останется старая запись. В результате индексный курсор платформы просто проскакивает рассинхронизированную запись.
Проанализируем состояние таблиц базы данных. Сначала проверим наличие связи по конкретному документу-основанию. Для этого выполним запрос к SQL Server (где вместо значения ' A0URZRAD' подставим IDDOC документа-основания):
SELECT TOP 100 Отбор.MDID AS ВидОтбора,
Отбор.PARENTVAL AS ЗначениеОтбора,
Отбор.CHILD_DATE_TIME_IDDOC AS ПодчДатаВремяИдДок,
Отбор.CHILDID AS Документ_подч,
Журнал.IDDOC AS Документ,
Журнал.DATE_TIME_IDDOC AS Журнал_ДатаВремяИдДок,
Журнал.IDDOCDEF AS Документ_вид
FROM _1SCRDOC AS Отбор WITH (NOLOCK)
INNER JOIN _1SJOURN AS Журнал WITH (NOLOCK) ON Журнал.IDDOC = Отбор.CHILDID
WHERE SUBSTRING(Отбор.PARENTVAL, 7, 9) = ' A0URZRAD';
Если запрос возвращает подчиненный документ, но платформа его не видит, проверим базу на наличие глобальных расхождений между таблицами связей и журнала:
SELECT c.CHILDID,
c.CHILD_DATE_TIME_IDDOC AS Позиция_В_CRDOC,
j.DATE_TIME_IDDOC AS Позиция_В_JOURN
FROM _1SCRDOC c WITH (NOLOCK)
INNER JOIN _1SJOURN j WITH (NOLOCK) ON c.CHILDID = j.IDDOC
WHERE c.CHILD_DATE_TIME_IDDOC <> j.DATE_TIME_IDDOC;
Если запрос находит строки, значит, в базе нарушена целостность служебных хронологических ключей.
Рассмотрим несколько вариантов решения проблемы — от быстрого SQL-исправления до штатных процедур платформы.
Если база данных имеет большой объем и остановить работу пользователей для монопольных процедур невозможно, выполним синхронизацию полей прямым SQL-запросом:
UPDATE c
SET c.CHILD_DATE_TIME_IDDOC = j.DATE_TIME_IDDOC
FROM _1SCRDOC c
INNER JOIN _1SJOURN j ON c.CHILDID = j.IDDOC
WHERE c.CHILD_DATE_TIME_IDDOC <> j.DATE_TIME_IDDOC;
После выполнения данной инструкции штатный журнал подчиненности и метод ВыбратьПодчиненныеДокументы() сразу начнут корректно находить все документы.
Если регламент позволяет запустить монопольный режим, переформируем служебные таблицы штатным путем через конфигуратор. Чтобы не тратить время на полный пересчет итогов регистров на большой базе данных, достаточно запустить процедуру Администрирование — Тестирование и исправление ИБ только со следующими опциями:
_1SCRDOC заново на основе реквизитов документов-оснований).Для единичных случаев достаточно открыть подчиненный документ в режиме 1С:Предприятие, заново перевыбрать документ-основание в соответствующем реквизите и нажать кнопку Записать (или программно вызвать метод Записать()). Платформа при перезаписи обновит строку в _1SCRDOC в соответствии с текущей позицией документа в _1SJOURN.
Если в конфигурации используется внешняя компонента 1С++ и объект ODBCRecordset, напишем универсальную функцию выборки подчиненных документов в обход платформенного бага. В прямом SQL-запросе соединение производится по CHILDID = IDDOC, что гарантирует получение полного списка независимо от сдвигов времени:
Функция глВыбратьПодчиненные(ДокОснование, ВидПодчДокумента = "", НачДата = "", КонДата = "", флПроведенные = "") Экспорт
ТекстЗапроса = "
|SELECT DISTINCT
| Жур.IDDoc [Док $Документ],
| Жур.IDDocDef Док_вид
|FROM
| _1SJourn Жур (NOLOCK)
|INNER JOIN
| _1SCRDOC Отбор ON Отбор.ChildID = Жур.IDDoc
| AND Отбор.MDID = 0
| AND Отбор.ParentVal = :ВыбОснование* ";
Если ПустоеЗначение(НачДата) = 0 Тогда
ТекстЗапроса = ТекстЗапроса + "
| AND Отбор.Child_Date_Time_IDDoc >= :НачДата ";
КонецЕсли;
Если ПустоеЗначение(КонДата) = 0 Тогда
ТекстЗапроса = ТекстЗапроса + "
| AND Отбор.Child_Date_Time_IDDoc <= :КонДата~ ";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|WHERE Жур.ismark = 0";
Если ПустоеЗначение(флПроведенные) = 0 Тогда
ТекстЗапроса = ТекстЗапроса + "
| AND Жур.closed & 1 = :Проведенные";
КонецЕсли;
Если ПустоеЗначение(ВидПодчДокумента) = 0 Тогда
ТекстЗапроса = ТекстЗапроса + "
| AND Жур.IDDocDef = $ВидДокумента." + ВидПодчДокумента;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|ORDER BY Жур.iddoc";
рс = СоздатьОбъект("ODBCRecordset");
Если ПустоеЗначение(НачДата) = 0 Тогда
рс.УстановитьТекстовыйПараметр("НачДата", НачДата);
КонецЕсли;
Если ПустоеЗначение(КонДата) = 0 Тогда
рс.УстановитьТекстовыйПараметр("КонДата", КонДата);
КонецЕсли;
рс.УстановитьТекстовыйПараметр("ВыбОснование", ДокОснование);
Если ПустоеЗначение(флПроведенные) = 0 Тогда
рс.УстановитьТекстовыйПараметр("Проведенные", флПроведенные);
КонецЕсли;
Возврат рс.ВыполнитьИнструкцию(ТекстЗапроса);
КонецФункции
Такой подход обеспечивает устойчивость отчетов и алгоритмов обработки документов к возможным рассинхронизациям служебных таблиц 1С 7.7.