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