Как установить отбор по подчиненному документу в 1С 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

В платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с задачей фильтрации документов в журнале по наличию подчиненных объектов. Классический пример: необходимо вывести в журнале только те документы РасходнаяНакладная, на основании которых уже был введен и проведен документ ПриходныйКассовыйОрдер (ПКО).

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

Почему возникает сложность: ограничения платформы 1С 7.7

Прежде чем перейти к решениям, проанализируем, почему задача вызывает трудности на уровне платформы:

  1. Односторонний индекс подчиненности: В системе 1С 7.7 связи подчиненности хранятся «снизу вверх» — от подчиненного документа к родителю. Стандартный журнал подчиненных документов умеет показывать только те объекты, которые подчинены выбранному. Найти документы-родители по наличию подчиненных стандартными индексами невозможно.
  2. Ограничение на один активный отбор: В общем журнале нельзя одновременно установить отбор по виду документа (например, показывать только Расходные) и по какому-либо другому критерию. Один отбор всегда сбрасывает другой.
  3. Отсутствие отбора по списку: Метод УстановитьОтбор() не поддерживает работу со списком значений. Мы можем отобрать документы только по одному конкретному значению реквизита.

Решение 1. Программная установка флага отбора в документе-родителе

Этот способ основан на добавлении дополнительного реквизита в документ-родитель и его последующей перезаписи при проведении подчиненного документа. Разберем этот алгоритм по шагам:

  1. В конфигураторе откроем свойства документа РасходнаяНакладная и добавим новый реквизит ЕстьПКО (тип: Число, длина: 1). Обязательно установим для него свойство «Отбор по реквизиту».
  2. В модуле проведения документа ПКО (процедура ОбработкаПроведения()) напишем код, который будет находить документ-основание и записывать в него признак наличия подчиненного документа:

// При проведении ПКО находим документ-родитель
ДокОснование = ТекущийДокумент().ДокументОснование;
Если ДокОснование.Выбран() = 1 Тогда
    // Создаем объект для изменения реквизита без нарушения блокировок
    ДокРодитель = СоздатьОбъект("Документ.Расходная");
    Если ДокРодитель.НайтиДокумент(ДокОснование) = 1 Тогда
        ДокРодитель.ЕстьПКО = 1;
        ДокРодитель.Записать();
    КонецЕсли;
КонецЕсли;
  1. При отмене проведения или удалении ПКО необходимо выполнить обратную операцию — сбросить флаг ЕстьПКО в значение 0.
  2. В форме журнала документов РасходнаяНакладная при открытии устанавливаем отбор:

Процедура ПриОткрытии()
    УстановитьОтбор("ЕстьПКО", 1);
КонецПроцедуры

Важное предупреждение: Данный метод имеет критический недостаток. Если документ РасходнаяНакладная в момент проведения ПКО будет открыт другим пользователем на редактирование, то попытка программной записи ДокРодитель.Записать() вызовет ошибку блокировки. Ниже мы рассмотрим, как обойти эту проблему.

Решение 2. Замена журнала Таблицей Значений (ТЗ) на форме

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

Алгоритм реализации выглядит следующим образом:

  1. Создаем обработку, на форму которой добавляем элемент «ТаблицаЗначений» с идентификатором ТабДокументов. Позиционируем на форме поля выбора периода (дата начала и дата окончания).
  2. Заполняем таблицу документов «снизу вверх». Сначала выбираем все проведенные ПКО за указанный период, а затем получаем их документы-основания:

Процедура СформироватьЖурнал()
    ТабДокументов.Очистить();
    ТабДокументов.НоваяКолонка("Документ", "Документ.Расходная");
    ТабДокументов.НоваяКолонка("Дата", "Дата");
    ТабДокументов.НоваяКолонка("Сумма", "Число");
    
    ДокПКО = СоздатьОбъект("Документ.ПКО");
    ДокПКО.ВыбратьДокументы(ДатаНач, ДатаКон);
    Пока ДокПКО.ПолучитьДокумент() = 1 Цикл
        // Проверяем, что ПКО проведен и имеет основание нужного типа
        Если (ДокПКО.Проведен() = 1) И (ДокПКО.ДокументОснование.Вид() = "Расходная") Тогда
            Родитель = ДокПКО.ДокументОснование;
            
            // Исключаем дублирование документов в таблице
            СтрокаНомер = 0;
            Если ТабДокументов.НайтиЗначение(Родитель, СтрокаНомер, "Документ") = 0 Тогда
                ТабДокументов.НоваяСтрока();
                ТабДокументов.Документ = Родитель;
                ТабДокументов.Дата = Родитель.ДатаДок;
                ТабДокументов.Сумма = Родитель.Итог("Сумма");
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры
  1. Для того чтобы пользователь мог открыть документ из нашей таблицы двойным кликом, переопределяем событие выбора строки:

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

Решение 3. Изменение бизнес-логики процесса

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

Выясним у заказчика, для чего нужен этот отбор. Если цель — увидеть, по каким расходным накладным уже получена оплата, то гораздо логичнее открыть журнал документов ПКО. В этом журнале достаточно вывести графу «Документ-основание». Таким образом, пользователь сразу будет видеть список оплат и привязанные к ним накладные без написания сложного программного кода.

Решение 4. Использование прямых запросов 1С++ (для SQL баз)

Для крупных информационных баз на платформе «1С:Предприятие 7.7 SQL» наиболее эффективным решением будет использование внешней компоненты 1С++. Прямой SQL-запрос к системной таблице подчиненности _1SCRDOC отработает за доли секунды даже на миллионах записей.

Связи документов в SQL-версии хранятся в таблице _1SCRDOC, где PARENTID — это идентификатор родительского документа (наша Расходная), а CHILDID — идентификатор подчиненного (ПКО). С помощью класса ODBCRecordset мы можем выбрать все уникальные PARENTID и мгновенно вывести их в виртуальное табличное поле на форме.

Как избежать блокировок при программной записи документов?

Если мы все же выбрали Решение 1 с записью флага отбора, нам необходимо защитить систему от конфликтов блокировок файлов, когда один пользователь проводит ПКО, а у второго в этот момент открыта форма соответствующей Расходной накладной.

Для минимизации этой проблемы переключим глобальный режим открытия объектов в системе:

  1. В настройках пользователя или через код принудительно установим режим, при котором все документы по умолчанию открываются интерактивно только на просмотр (режим редактирования активируется только по кнопке или специальному сочетанию клавиш Shift + Enter).
  2. Если документ открыт пользователем в режиме просмотра, это не блокирует базу данных. Программа сможет без проблем выполнить метод Записать() для этого документа из транзакции проведения ПКО другого пользователя.
  3. Для принудительного открытия документов в режиме просмотра при программном вызове используем третий параметр функции ОткрытьФорму():

// Третий параметр "1" указывает системе открыть форму в режиме "Только просмотр"
ОткрытьФорму(ДокументСсылка, , 1);

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

← На главную