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