При оформлении торговых операций часто возникает необходимость кастомизировать печатные формы под конкретные требования законодательства или особенности бизнес-процессов компании. Типичный пример — продажа лома и отходов черных или цветных металлов. Согласно Федеральному закону № 335-ФЗ, при реализации таких товаров продавец обязан выставлять первичные документы и счета-фактуры с пометкой «НДС исчисляется налоговым агентом».
В этой статье мы подробно разберем, как с помощью запроса быстро проверить, присутствует ли в табличной части документа «Реализация товаров и услуг» номенклатура из определенной группы (например, «Металлолом»), и на основе этого скорректировать вывод информации в печатных формах ТОРГ-12 и Счет-фактура. Мы проанализируем ошибки начинающих разработчиков, рассмотрим оптимальные методы написания запросов и научимся интегрировать проверку в механизмы БСП (Библиотеки Стандартных Подсистем) без нарушения стандартов разработки.
Прежде чем переходить к программированию, важно отметить, что в типовых конфигурациях 1С (например, в 1С:Бухгалтерия 8 КОРП или 1С:ERP) данный функционал уже автоматизирован:
Если же вы используете версию ПРОФ, где этот учет ограничен, либо вам требуется реализовать собственную уникальную логику проверки позиций в чеках или документах, то без доработки кода не обойтись. Рассмотрим, как решить эту задачу программно.
Начинающие разработчики часто пытаются решить задачу «в лоб», используя обход табличной части документа в цикле. Проанализируем пример такого неоптимального кода:
Металлалом = Справочники.Номенклатура.TargetGroup; // Получение группы
Металл = Ложь;
СФ = МассивОбъектов[0];
Если ТипЗнч(СФ) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
Для Каждого стрТЗ Из СФ.Товары Цикл
Если стрТЗ.Номенклатура.ПринадлежитЭлементу(Металлалом) Тогда
Металл = Истина;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Почему этот подход считается плохим?
СФ.Товары) заставляет платформу считывать весь документ из базы данных в память сервера. Если в документе сотни строк, это приведет к просадке производительности.ПринадлежитЭлементу() в цикле: этот метод выполняет отдельный неявный запрос к базе данных на каждой итерации цикла для проверки иерархии номенклатуры, что критически замедляет работу системы.Для быстрой проверки наличия хотя бы одной позиции из целевой группы (или конкретного элемента) в табличной части документа правильнее всего использовать запрос с конструкцией ВЫБРАТЬ ПЕРВЫЕ 1. Это позволит СУБД прекратить поиск сразу же после нахождения первого совпадения, минимизируя нагрузку.
Рассмотрим структуру оптимального запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ИСТИНА КАК ЕстьМеталл
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| РеализацияТоваровУслугТовары.Ссылка = &Документ
| И РеализацияТоваровУслугТовары.Номенклатура В ИЕРАРХИИ (&ГруппаНоменклатуры)";
Запрос.УстановитьПараметр("Документ", СсылкаНаДокумент);
Запрос.УстановитьПараметр("ГруппаНоменклатуры", ЦелеваяГруппаНоменклатуры);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
// Номенклатура найдена, выполняем необходимые действия
КонецЕсли;
Разберем ключевые особенности этого запроса подробнее:
ВЫБРАТЬ ПЕРВЫЕ 1 ИСТИНА: запрос возвращает только одну строку со значением Истина, если условие выполняется. Нам не нужно выбирать саму номенклатуру, достаточно самого факта наличия записи.В ИЕРАРХИИ: это условие позволяет проверять вхождение номенклатуры не только на равенство конкретному элементу, но и определять, входит ли позиция в указанную папку (группу) номенклатуры или любые её подпапки.В типовых конфигурациях печать ТОРГ-12 обычно выполняется через общий модуль (например, ПечатьТорговыхДокументов) в функции ПечатьТОРГ12. Нам необходимо передать ссылку на печатаемый документ в параметры нашего запроса.
Разберем пример интеграции в обработчик печати:
// Внутри функции ПечатьТОРГ12(СведенияТОРГ12, ОбъектыПечати, ПараметрыПечати)
// Получаем ссылку на текущий обрабатываемый документ из структуры СведенияТОРГ12
СсылкаНаДокумент = СведенияТОРГ12[0].Документ;
// Рекомендуется избегать поиска по коду, но для наглядности используем этот метод
ГруппаМеталлолом = Справочники.Номенклатура.НайтиПоКоду("00009724");
ЗапросПроверки = Новый Запрос;
ЗапросПроверки.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ИСТИНА КАК ЕстьМеталл
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| РеализацияТоваровУслугТовары.Ссылка = &Документ
| И РеализацияТоваровУслугТовары.Номенклатура В ИЕРАРХИИ (&Номенклатура)";
ЗапросПроверки.УстановитьПараметр("Документ", СсылкаНаДокумент);
ЗапросПроверки.УстановитьПараметр("Номенклатура", ГруппаМеталлолом);
Если Не ЗапросПроверки.Выполнить().Пустой() Тогда
// Если металлолом найден, меняем параметры вывода ставки НДС в структуре печати
ПараметрыСтроки.Вставить("СтавкаНДС", "НДС исчисляется налоговым агентом");
КонецЕсли;
В кодовой базе разработчика с форума присутствует серьезная архитектурная ошибка: использование индекса СведенияТОРГ12[0]. Если пользователь выделит в списке документов несколько реализаций и отправит их на печать одновременно, ваш алгоритм проверит на наличие металлолома только самый первый документ из списка, а для остальных выведет неверные данные.
Рассмотрим, как правильно реализовать пакетную проверку для массива документов:
// Собираем все документы из входящей структуры в массив
МассивДокументов = Новый Массив;
Для Каждого СведенияИзДокумента Из СведенияТОРГ12 Цикл
МассивДокументов.Добавить(СведенияИзДокумента.Документ);
КонецЦикла;
ЗапросПакетный = Новый Запрос;
ЗапросПакетный.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| РеализацияТоваровУслугТовары.Ссылка КАК Документ
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| РеализацияТоваровУслугТовары.Ссылка В (&МассивДокументов)
| И РеализацияТоваровУслугТовары.Номенклатура В ИЕРАРХИИ (&Номенклатура)";
ЗапросПакетный.УстановитьПараметр("МассивДокументов", МассивДокументов);
ЗапросПакетный.УстановитьПараметр("Номенклатура", ГруппаМеталлолом);
// Выгружаем список документов, в которых действительно есть металлолом
ТаблицаДокументовСМеталлом = ЗапросПакетный.Выполнить().Выгрузить();
// Теперь при обходе печатных форм мы можем быстро сверяться с полученной таблицей
Для Каждого СтрокаСведений Из СведенияТОРГ12 Цикл
ДокументСодержитМеталл = (ТаблицаДокументовСМеталлом.Найти(СтрокаСведений.Документ, "Документ") <> Неопределено);
Если ДокументСодержитМеталл Тогда
// Применяем настройки для налогового агента
КонецЕсли;
КонецЦикла;
Счет-фактура выставляется на основании документа реализации. Чтобы выполнить проверку для печатной формы Счет-фактура, нам сначала необходимо получить документ-основание. Проанализируем, как это сделать с помощью запроса:
// Предположим, что СФ - это ссылка на СчетФактураВыданный
СсылкаНаСФ = МассивОбъектов[0];
ЗапросСФ = Новый Запрос;
ЗапросСФ.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ИСТИНА КАК ЕстьМеталл
|ИЗ
| Документ.СчетФактураВыданный.ДокументыОснования КАК СФОснования
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТовары
| ПО СФОснования.ДокументОснование = РеализацияТовары.Ссылка
|ГДЕ
| СФОснования.Ссылка = &СсылкаНаСФ
| И РеализацияТовары.Номенклатура В ИЕРАРХИИ (&Номенклатура)";
ЗапросСФ.УстановитьПараметр("СсылкаНаСФ", СсылкаНаСФ);
ЗапросСФ.УстановитьПараметр("Номенклатура", ГруппаМеталлолом);
Металл = Не ЗапросСФ.Perform().IsEmpty(); // Проверяем результат выполнения
Если Металл Тогда
// Заполняем область макета Счета-фактуры требуемым текстом
ОбластьМакета.Параметры.СтавкаНДС = "НДС исчисляется налоговым агентом";
КонецЕсли;
Использование ВНУТРЕННЕЕ СОЕДИНЕНИЕ позволяет за один проход СУБД связать таблицу оснований счета-фактуры с табличной частью документа реализации и мгновенно проверить номенклатуру без распаковки объектов в памяти 1С.
Для того чтобы ваш код оставался поддерживаемым и не "сломался" при первом же обновлении конфигурации, придерживайтесь следующих правил:
НайтиПоКоду("00009724") крайне ненадежен. Код элемента может измениться, либо база данных может быть очищена и перенесена. Вместо этого добавьте в справочник «Номенклатура» дополнительный реквизит (например, типа Булево «Это металлолом») или используйте привязку к «Виду номенклатуры». Это позволит проверять свойство объекта прямо в запросе.&После или &Вместо для перехвата стандартных процедур формирования печатных форм. Это сохранит возможность автоматического обновления базы данных.Применение описанных в данной статье методик позволит вам создавать быстрые, отказоустойчивые и легко обновляемые алгоритмы для любых печатных форм в вашей системе 1С.