Отчет по закрытым заказам для УТ 11.5 стоит на правиле: заказ относится к тому месяцу, в котором он закрылся полностью — и отгружен, и оплачен, а дата закрытия это максимум из даты полной отгрузки и даты, на которую оплата дошла до порога. Как это правило выводилось и почему каждое из двух слов оказалось сложнее, чем выглядит, разбирал отдельно.
Правило вывели, запрос написали, числа сошлись. Дальше начинается то, чего мы обычно не ждем: между верным запросом и тем, что видит покупатель, стоит компоновка данных, и она меняет результат молча. Два раза в этой работе она выкинула заказ из выборки, и один раз переставила колонки так, что главный показатель отчета уехал за край экрана.
Замеры — на своей демобазе УТ 11.5.22.149, платформа 8.3.27.2214, контрольный опыт в конце — 8.5.1.1343. Код заказчика в статье не используется: отчет собран заново.
Заказ, который есть в году и нет в марте
А вот как именно считать остаток к отгрузке — самая дорогая история всей работы, и дальше она идет двумя разделами. Первая же сверка показала по марту четыре заказа вместо пяти. На этот раз дело было не в пороге: пропал тот самый заказ, ради которого все затевалось, с авансом 10 февраля и отгрузкой 12 марта. При этом в годовом отчете он присутствовал, с датой закрытия 12 марта. То есть отчет противоречил сам себе на одних и тех же данных — в марте заказа нет, а в году он закрыт в марте.
Разбирался в три шага.
Шаг первый — данные. Выгрузил по этому заказу все, что видит запрос, на границе 31 марта и на границе 31 декабря. Совпало полностью: выручка 43 000 с датой 12 марта, три движения распоряжений на отгрузку, два события расчетов. Значит, дело не в данных.
Шаг второй — запрос. Взял тот же текст запроса и выполнил его напрямую, через Запрос, без всякой компоновки. За март пять строк, включая пропавший заказ. Значит, дело не в запросе.
Шаг третий — макет. Выгрузил текст, который СКД отправляет в базу. И вот что там оказалось на месте моих границ:
РегистрНакопления.РаспоряженияНаОтгрузку.Обороты(
&П,
&П2,
...
======== значения параметров макета
КонецПериода = 31.03.2026 23:59:59
НачалоПериода = 01.03.2026 0:00:00
П = 01.03.2026 0:00:00
П2 = 31.03.2026 23:59:59
Компоновщик подставил в виртуальную таблицу регистра свои границы периода отчета и переписал обе. Оборот считался внутри марта вместо оборота на конец марта.
Дальше картина сходится. Движения по этому заказу такие: приход 43 000 от 5 февраля (заказано), приход 0 от 12 марта (к оформлению) и расход −43 000 от 12 марта (реализация). Внутри марта оборот выходит −43 000, условие «остаток к отгрузке равен нулю» не выполняется, и полностью отгруженный заказ отсекается. В длинном периоде приход и расход попадают в одно окно, оборот ноль, заказ в отчете.
Правка, которая выглядела правильной и не работала
Лечение казалось очевидным: раз нижняя граница подставляется, зададим ее явно, ДАТАВРЕМЯ(1,1,1). Сделал, собрал, прогнал.
Не помогло. Снова выгрузил макет и увидел там те же &П и &П2. В моей схеме компоновщик затер и явно заданную границу. От чего это зависит, осталось непонятным: ни настройки параметров виртуальной таблицы, ни другие варианты настроек не перебирал.
Вот это и был самый дорогой вечер. Правка стояла в схеме, читалась правильно, была синтаксически безупречна и ничего не делала. Если бы тогда посмотрел на текст схемы и сказал «ну вот же она, граница, написана верно» — дефект уехал бы в поставку, а числа за март остались бы неверными. Проверяйте не текст схемы, а то, что ушло в базу.
Рабочее решение — отказаться от виртуальной таблицы. Остаток к отгрузке считается по физической таблице регистра со своим условием:
ВЫБРАТЬ Распоряжения.Распоряжение КАК Заказ, СУММА(Распоряжения.Заказано) КАК ОстатокКОтгрузке ПОМЕСТИТЬ ВТОстаткиОтгрузки ИЗ РегистрНакопления.РаспоряженияНаОтгрузку КАК Распоряжения ГДЕ Распоряжения.Период <= &КонецПериода И Распоряжения.Распоряжение В (ВЫБРАТЬ ВТВыручка.Заказ ИЗ ВТВыручка КАК ВТВыручка) СГРУППИРОВАТЬ ПО Распоряжения.Распоряжение
Регистр оборотный, направление лежит в измерении ВидДвиженияРегистра, а расход в ресурсах уже записан со знаком минус, поэтому на моих данных простая СУММА совпала с оборотом виртуальной таблицы. Ручаться, что так будет на любом наборе движений этого регистра, не стану: сходилось, других видов движения в данных не было. Условия по физической таблице компоновщик не переписал — видимо, подставлять ему туда просто нечего.
После замены март дает пять заказов, пропавший заказ стоит с датой закрытия 12 марта.
Правило тут шире моего случая. Если данные и текст запроса вы уже исключили, а СКД и прямой запрос дают разные числа, идите в макет компоновки и сравнивайте его с текстом схемы. Проверять данные и запрос все равно придется первыми, иначе в макете будет не на что смотреть. Сколько времени это сэкономит в следующий раз, не знаю, но вечер бы сэкономило.
Чем смотреть макет
На всякий случай, потому что искать это по справке дольше, чем написать))
МакетКомпоновщик = Новый КомпоновщикМакетаКомпоновкиДанных; Макет = МакетКомпоновщик.Выполнить(Схема, Компоновщик.Настройки); Для Каждого Набор Из Макет.НаборыДанных Цикл Сообщить(Набор.Запрос); КонецЦикла; Для Каждого Парам Из Макет.ЗначенияПараметров Цикл Сообщить(Парам.Имя + " = " + Строка(Парам.Значение)); КонецЦикла;
Второй цикл тут не для красоты: имя параметра в запросе ничего не говорит, пока не видно, чем его заполнили.
В отчете о прибыли самой прибыли не видно на первом экране
Эту находку считаю самой полезной для тех, кто делает отчеты на СКД, и самой обидной для себя.
Отчет называется «Валовая прибыль по заказам, закрытым в периоде». Открываем его живым клиентом, окно 1 200 пикселей, жмем «Сформировать». На первом экране: заказ клиента, партнер, дата закрытия, рентабельность, процент оплаты, дата отгрузки, дата полной оплаты, валюта, сумма заказа, оплачено. Ни выручки, ни себестоимости, ни валовой прибыли. Рентабельность есть, а из чего она получилась — нет. Отчет про прибыль, и ни одной суммы прибыли в рублях на экране))
В настройках варианта порядок выбранных полей стоит правильный: дата закрытия, потом выручка, себестоимость, доставка, затраты, валовая прибыль, и только потом справочные колонки. Замер того, что выведено, дает другое: все справочные колонки идут первыми, а денежные встают в конец. «Валовая прибыль» оказалась восемнадцатой колонкой из двадцати в одном варианте и двадцать первой из двадцати четырех в другом.
Объяснение, на котором остановился: ресурсами схемы объявлены выручка, себестоимость, доставка, затраты и валовая прибыль, а ресурсы компоновщик выводит последними, и порядок в выборе варианта на это не влияет. «Сумма заказа» и «Оплачено» ресурсами не объявлены — поэтому они и остались на первом экране вместе со справочными. После валовой прибыли идут еще две колонки в одном варианте и три в другом, тоже ресурсы. Пересборка порядка в настройках не сдвинула их ни на позицию. Контрольного опыта, который отделил бы причину от совпадения, не делал: надо было снять с одной колонки признак ресурса и посмотреть, поедет ли она вверх. Так что механизм назвать проверенным не могу. Замерено другое, и этого достаточно, чтобы насторожиться: порядок в настройках правильный, а выводится иначе, и в двух вариантах одинаково.
Как это пропустили: порядок пересобран, у себя записано «исправлено» и живьем после правки не посмотрел. Нашлось это только тогда, когда отчет открыли мышью и сняли скриншот. Проверка «я поправил настройку» и проверка «покупатель видит прибыль на первом экране» — это две разные проверки, и первая ничего не доказывает про вторую. Мы все так делаем: правку внесли, галочку поставили, глазами не посмотрели.
Контрольный опыт: это точно ресурсы?
В первой редакции этой статьи здесь стояла оговорка: механизм назвать проверенным не могу, контрольного опыта не делал. Опыт простой: снять с одной колонки признак ресурса и посмотреть, поедет ли она вверх.
Признак ресурса живет в схеме, в коллекции ПоляИтога, и снимается прямо в памяти, без перекомпоновки отчета:
Схема = ВО.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
// ... замер порядка колонок как есть ...
Поле = Неопределено;
Для Каждого П Из Схема.ПоляИтога Цикл
Если Строка(П.ПутьКДанным) = "Доставка" Тогда
Поле = П;
Прервать;
КонецЕсли;
КонецЦикла;
Схема.ПоляИтога.Удалить(Поле);
// ... замер порядка колонок снова ...
Удаление вынесено за цикл нарочно: менять коллекцию, по которой идет обход, — это способ получить сюрприз, который потом долго искать. Так оно и прогонялось.
Три замера в одном прогоне, вариант «Основной», двадцать колонок в каждом:
| Замер | Где оказалась «Доставка» |
|---|---|
| схема как есть | 16-я из 20, в хвосте среди денежных |
| признак ресурса снят только с нее | 5-я, сразу после даты закрытия |
| ресурсов не осталось ни одного | все денежные встали на места 5–9 |
В третьем замере порядок колонок совпал с порядком выбранных полей варианта: выручка, себестоимость, доставка, затраты, валовая прибыль, и за ними рентабельность. То есть настройки варианта компоновщик читает правильно — ровно до того, как поле объявлено ресурсом. После этого настройки перестают на него влиять.
Теперь это механизм, а не догадка. Заодно видно, что базовый замер на платформе 8.5.1.1343 совпал с прежним замером на 8.3.27.2214 колонка в колонку, все двадцать — значит поведение в этой части между версиями не менялось.
Две колонки, впрочем, остались последними во всех трех замерах. Они и в выбранных полях варианта стоят последними, так что это не исключение, а то же правило: когда ресурсов нет, работает порядок настроек, а он для них такой.
Три слоя, а под приемкой обычно один
Если собрать вместе все, что в этой части пошло не так, видно общее. Отчет на компоновке это не одна вещь, а три, и ломаются они по отдельности.
Слой первый, текст запроса. Его все и правят, когда отчет врет. Его же и ломают нарочно, когда делают контрольные сборки для приемки: поменять знак, убрать условие, сдвинуть границу. Слой привычный, и он действительно самый частый.
Слой второй, схема. Состав ресурсов, вычисляемые поля, набор итоговых полей, параметры и их значения по умолчанию. Здесь текст запроса ни при чем: запрос может быть идеален, а отчет покажет не то. Оба дефекта этой части живут именно тут. Заказ пропал из марта не потому, что в запросе ошибка, запрос напрямую возвращал его исправно. И колонка с прибылью уехала за край не потому, что ее не посчитали.
Слой третий, вариант настроек. Группировки, отборы, порядок, видимость. Тут обычно и ищут, когда покупатель говорит «не вижу колонку», и чаще всего не находят, потому что порядок в варианте не обязан совпадать с тем, что выйдет на экран.
Беда в том, что ломаные сборки для приемки почти всегда делают на первом слое. У меня их было восемь, и все восемь трогали текст запроса. Ни одна не трогала схему. То есть целый слой, в котором живут оба дефекта этой статьи, вообще никогда не был под проверкой, и положил его туда не кто-нибудь, а я сам.
Отдельно стоит сказать, почему этот слой так легко выпадает из проверки. Текст запроса лежит в одном месте и читается подряд, как обычный код: его видно, его можно сравнить с прошлой версией, по нему понятно, что изменилось. Схема же разложена по разным спискам, и ни один из них не читается подряд. Чтобы увидеть, что вычисляемое поле не попало в итоговые, надо открыть два разных списка и сличить их глазами. Никакая привычка читать код тут не помогает, а привычки читать схему у нас обычно нет.
Три проверки, которые закрывают второй слой и делаются за пять минут каждая:
- Параметры виртуальных таблиц. Открыть макет компоновки и посмотреть, какие значения реально ушли в запрос. Не те, что вы написали в схеме, а те, что в макете.
- Порядок колонок. Сравнить порядок в варианте настроек с порядком, который дает схема. Если они расходятся, на экране будет второй.
- Состав итоговых полей. Проверить, что каждое вычисляемое поле, которое читатель будет смотреть в строке группировки, в этот состав внесено. Про то, как такой дефект прожил пять редакций и четыре проверки, — в отдельном разборе про сверку.
Что из этого забрать
Компоновка — отдельный слой, и проверять его надо отдельно. Верный запрос не гарантирует верную выборку: границы виртуальных таблиц она переписывает, а порядок колонок определяет не тем, что вы задали в настройках.
Смотреть надо макет, а не схему. Схема — это то, что вы хотели. Макет — то, что компоновщик решил сделать. Разница между ними и есть место, где живут эти ошибки.
Правка настройки — не проверка. Между «я поправил порядок полей» и «покупатель видит прибыль на первом экране» лежит прогон живого отчета и взгляд на экран. У нас между ними лежала запись «исправлено» в своем же файле, и этого хватило, чтобы дефект дошел до скриншота.
Про сверку — отдельно: как числа сходились пять раз подряд и почему это ничего не доказывало.
Вступайте в нашу телеграмм-группу Инфостарт