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