При разработке обработок по автоматическому созданию или обновлению документов часто возникает ситуация, когда нужно игнорировать объекты, которые пользователь пометил на удаление. В 1С документ, помеченный на удаление, физически еще присутствует в базе, поэтому обычные механизмы поиска (например, НайтиПоРеквизиту) будут его находить. Рассмотрим подробнее, как правильно организовать фильтрацию таких документов в запросах и программном коде.
Разберем ситуацию, когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Если мы просто добавим условие в секцию ГДЕ, как показано в примере ниже, результат может нас разочаровать:
|ГДЕ
| ОтчетОРозничныхПродажахТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И ПриходныйКассовыйОрдер.ПометкаУдаления = ЛОЖЬ
Проанализируем ситуацию: использование условия по «правой» таблице в секции ГДЕ фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ. Если соответствующий ПриходныйКассовыйОрдер (ПКО) не найден (результат NULL) или он помечен на удаление, то вся строка, включая данные из Отчета о розничных продажах, просто пропадет из выборки. Но наша задача — увидеть отчет, но понять, что «живого» ПКО для него нет.
Самый правильный и производительный способ — перенести проверку пометки удаления непосредственно в условие связи ПО. Выясним причину: в этом случае система сначала отсеет помеченные на удаление ПКО, а затем попытается соединить оставшиеся с основной таблицей. Если «живого» документа нет, мы получим NULL, что и позволит нам принять решение о создании нового документа.
Рассмотрим правильный пример текста запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ОтчетТовары.Ссылка КАК СсылкаОснование,
ОтчетТовары.Ссылка.Дата КАК ДатаОснования,
ПриходныйКассовыйОрдер.Ссылка КАК СсылкаПКО,
ПриходныйКассовыйОрдер.Проведен КАК ПКОПроведен
ИЗ
Документ.ОтчетОРозничныхПродажах.Товары КАК ОтчетТовары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
ПО (ОтчетТовары.Ссылка = ПриходныйКассовыйОрдер.ДокументОснование)
И (ПриходныйКассовыйОрдер.ПометкаУдаления = ЛОЖЬ)
ГДЕ
ОтчетТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
В этом случае, если ПКО помечен на удаление, поле СсылкаПКО будет содержать NULL, и ваша логика создания нового документа отработает корректно.
Если по каким-то причинам вы уже получили выборку документов и вам нужно отфильтровать их непосредственно при обработке в цикле Пока ... Цикл, используйте свойство объекта или ссылки ПометкаУдаления. Посмотрим на пример кода:
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Если мы получили ссылку на документ в запросе
Если ВыборкаДетальныеЗаписи.СсылкаПКО <> NULL И ВыборкаДетальныеЗаписи.СсылкаПКО.ПометкаУдаления Тогда
Сообщить("Документ " + ВыборкаДетальныеЗаписи.СсылкаПКО + " помечен на удаление, пропускаем.");
Продолжить;
КонецЕсли;
// Дальнейшая логика обработки...
КонецЦикла;
Важный момент: обращение к свойству через точку от ссылки (СсылкаПКО.ПометкаУдаления) вызывает отдельный неявный запрос к базе данных. Если в выборке тысячи строк, это может существенно замедлить работу. Поэтому старайтесь всегда выносить это поле в основной запрос.
Проанализируем исходный код студента. Там используется конструкция НайтиПоРеквизиту внутри цикла. Это классическая ошибка «запрос в цикле», которая критически замедляет систему. Разберем, как сделать правильно, объединив все проверки в одном месте.
Вместо того чтобы искать документ в цикле, мы сразу получаем информацию о его состоянии (существует, проведен, помечен на удаление) в запросе. Для этого используем функцию ЕСТЬNULL, чтобы корректно обрабатывать отсутствие документа.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ОтчетТовары.Ссылка КАК Ссылка,
| ОтчетТовары.Ссылка.Дата КАК Дата,
| ОтчетТовары.Субконто КАК Субконто,
| ЕСТЬNULL(ПКО.Ссылка, ЗНАЧЕНИЕ(Документ.ПриходныйКассовыйОрдер.ПустаяСсылка)) КАК СсылкаПКО,
| ЕСТЬNULL(ПКО.Проведен, ЛОЖЬ) КАК ПКОПроведен,
| ЕСТЬNULL(ПКО.ПометкаУдаления, ЛОЖЬ) КАК ПКОПометкаУдаления
|ИЗ
| Документ.ОтчетОРозничныхПродажах.Товары КАК ОтчетТовары
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйКассовыйОрдер КАК ПКО
| ПО ОтчетТовары.Ссылка = ПКО.ДокументОснование
|ГДЕ
| ОтчетТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Логика: если ПКО нет или он помечен на удаление - создаем новый/обрабатываем иначе
Если Выборка.СсылкаПКО.Пустая() Или Выборка.ПКОПометкаУдаления Тогда
// Создание нового документа
НовыйДокумент = Документы.ПриходныйКассовыйОрдер.СоздатьДокумент();
// Заполнение...
Иначе
// Редактирование существующего "живого" документа
СуществующийДокумент = Выборка.СсылкаПКО.ПолучитьОбъект();
// Заполнение и запись...
КонецЕсли;
КонецЦикла;
Для качественной автоматизации необходимо различать три состояния документа, так как логика работы с ними обычно разная:
В запросе для этого удобно использовать следующие конструкции:
ПКО.Проведен — возвращает ИСТИНА, если документ проведен.ПКО.ПометкаУдаления — возвращает ИСТИНА, если документ в корзине.Совет: При записи документа в режиме проведения (РежимЗаписиДокумента.Проведение) обязательно используйте конструкцию Попытка ... Исключение. Если документ помечен на удаление, платформа может выдать ошибку при попытке его проведения без предварительного снятия пометки.
Таким образом, мы рассмотрели, что наиболее эффективным методом является фильтрация на уровне условий связи запроса. Это позволяет избежать лишних действий в цикле и делает код чище и быстрее.