Сверка, которая сходится и ничего не доказывает
Отчет по закрытым заказам для УТ 11.5 стоит на правиле: заказ относится к месяцу, в котором закрылся полностью, а дата закрытия — максимум из даты отгрузки и даты, на которую оплата взяла порог. Как выводилось правило, разбирал отдельно; про то, как компоновка данных выводит не то, что ей задали, — тоже отдельно. Здесь — про проверку: про сверку, которая сходилась раз за разом и ничего при этом не доказывала.
Здесь про самое неприятное. Отчет был написан, числа сходились, сверка показывала ноль расхождений. Так было пять раз подряд, и каждый раз следующая ревизия находила дефект — в том числе в самой сверке. Ниже разобрано, какие именно проверки выглядели убедительно и ничего не проверяли, и почему файла к этим статьям нет.
Все числа из прогонов на своей демобазе УТ 11.5.22.149, платформа 8.3.27.2214. Код заказчика не используется.
Числа: что сходится и почему это не доказательство
Данные заводил так, чтобы отчет расходился с типовой: совпадение ничего бы не доказало — два отчета могут сойтись просто потому, что в базе нет ни одного случая, который их различает. К четвертому кругу в базе девятнадцать помеченных заказов, каждый под свою ветку кода.
Две контрольные цифры, на которые смотрю после каждой сборки.
Сумма двенадцати месяцев против года. Отчет за каждый месяц 2026 года плюс отчет за год целиком: выручка 598 333,33 и там, и там, валовая прибыль 592 333,33 и там, и там. На нынешнем наборе данных сходится. Но называть это доказательством правила «сделка учтена один раз» больше не буду, и вот почему.
Раньше эта же проверка сходилась при неверном отчете, а я держал ее за доказательство. Она выполнялась, пока в данных не было ни возврата, ни отгрузки по цене, отличной от цены заказа. Когда такие данные появились, год перестал сходиться с суммой месяцев — это был самый тяжелый из найденных дефектов. Причин у него оказалось две: полнота отгрузки судилась по деньгам, а база, от которой считается процент оплаты, зависела от длины периода отчета.
Совпадение суммы месяцев с годом — необходимое условие, а не доказательство. Числа сходились и тогда, когда отчет был неправ.
Себестоимость на исторических данных. Год 2023, четыре закрытых заказа:
| Заказ | Выручка без НДС | Себестоимость | Валовая прибыль |
|---|---|---|---|
| ТД00-000001 от 15.06.2023 | 2 016,00 | 1 083,34 | 932,66 |
| ТД00-000003 от 15.07.2023 | 41 986,67 | 34 666,67 | 7 320,00 |
| ТД00-000004 от 18.07.2023 | 11 950,45 | 11 050,88 | 899,57 |
| ТД00-000012 от 02.10.2023 | 618 750,00 | 67 905,37 | 550 844,63 |
| Итого | 674 703,12 | 114 706,26 | 559 996,86 |
До одной из правок эта таблица выглядела иначе, и по одному заказу прибыль была минус 1 310,61 вместо плюс 899,57. Причина обыкновенная и от этого неприятная: выручку отчет брал без НДС, а себестоимость и расходы — с НДС. По одному заказу числа расходились в девятнадцать раз, а по одному сменился знак. Показывать минус там, где плюс, для отчета о прибыли приговор, и поймала это не арифметика, а сверка с отдельно посчитанным эталоном.
Счет
Пять ревизий. Пять раз сверка показывала ноль расхождений. Пять раз следующая ревизия находила дефект. На пятый раз — в самой сверке.
Эталон, который ни разу не покраснел, ничего не проверяет
Раз уж речь о сверке. За пять кругов было шесть эталонов, и каждый следующий закрывал дыру предыдущего — и открывал свою. Разбираю три первых: на них видно все три класса промаха, четвертый и пятый отличались от третьего только охватом данных, а шестой — в разделе про пятую ревизию. Самый поучительный провал — первый: контрольный запрос брал тот же ресурс регистра, что и проверяемый отчет. Он сходился всегда и не мог не сходиться, а я писал «совпало до копейки».
Дальше было тоньше. Эталон второй версии сверял числа только на 2026 годе, где себестоимость нулевая, а состав строк — только на 2023, где числа ненулевые, но их никто не сверял. Две проверки не пересекались, и вся расходная половина отчета осталась без контроля.
Эталон третьей версии складывал строки в соответствие по заказу. На сборке, где каждая строка отчета возвращалась дважды, покупатель увидел бы выручку 1 196 666,66 вместо 598 333,33, а эталон печатал правильные 598 333,33: дубли в соответствии схлопывались. Три намеренно сломанных сборки, все три грузятся и врут, эталон на всех трех — ноль ошибок.
Вывод, который вынес и который дороже самого отчета: эталон, который ни разу не покраснел, ничего не проверяет. Мы пишем сверку, чтобы она подтвердила нашу правоту, и она честно подтверждает — вот только это ничего не значит, пока мы не показали, что она умеет возражать. Прежде чем верить эталону, надо внести в отчет заведомую ошибку и убедиться, что сверка ее видит. Таких ломаных сборок теперь восемь, и прогон всех восьми до одной — обязательный тест перед каждой сдачей. Во что обошлось то, что сначала гонял не все восемь, — в разделе про пятую ревизию.
Чем именно эталон был слеп
Разбирать «эталон плохой» бессмысленно, пока не названо, что конкретно он пропускает. Вышло четыре отдельных слепых пятна, и каждое закрывалось своей проверкой.
Он не видел размножения строк. Эталон складывал строки отчета в соответствие по ключу заказа:
Итог.Вставить(Заказ, С); // второй такой же ключ затирает первый
Дальше сверка шла по своему списку заказов и достава из отчета по одному значению на заказ. Поэтому ломаная сборка, возвращавшая каждый заказ дважды, прошла приемку с нулем ошибок — при том что отчет отдавал 22 строки на 11 заказов, и у покупателя удваивались все итоги. Лечится отдельной проверкой мощности: сырых строк больше, чем различных ключей, — значит дубли. Ставить ее надо во все варианты, а не в один: дубли лезли и в «Основной», и в «Незакрытые».
Он не читал одну колонку вовсе. «Выручка с НДС» в сверке не участвовала, и сломать ее можно было безнаказанно. Поймалось простым соотношением: при ненулевой выручке она не может равняться выручке без НДС.
Он не сверял текстовую колонку ни с чем. «Причина незакрытия» — текст, сравнивать его с эталонным значением неудобно, поэтому он не сравнивался. Поймалось через правдоподобие: один и тот же текст на пять и более заказов невозможен.
Тут стоит сказать про порог, потому что первая версия этой проверки покрасила рабочую сборку. Она краснела при трех одинаковых текстах, а три незакрытых заказа честно имели одну причину. Разделитель нашелся в цифрах: у рабочей сборки на пяти строках три разных текста, у ломаной на тех же пяти — один. Порог пять, и обе стороны разошлись чисто. Порог надо подбирать по данным, а не по ощущению, иначе проверка краснеет на правде.
И четвертое, самое обидное: ноль ошибок потому, что проверка смотрит не туда. Одна ломаная сборка портила списание долга, а сценарий для него жил в ноябре 2025 года, тогда как эталон сверял только 2023 и 2026. Пока ноябрь не был добавлен, сборка проходила зеленой. Это была не слепота к классу дефекта, а дыра в покрытии по датам — и по отчету сверки они выглядят совершенно одинаково.
Чего отчет не делает
Границы применимости.
Заказы с порядком расчетов «По договорам» закрытыми, скорее всего, не станут. Объектом расчетов там выступает договор, который покрывает много заказов, и способа разложить такой платеж по заказам я не нашел: в самих движениях нет заказа. Сразу оговорюсь: сценария с расчетами по договорам не заводил, это вывод из состава движений, а не прогон. Пропасть они, по той же логике, не пропадут — попадут в вариант «Незакрытые заказы и причина», — но в прибыль по месяцу закрытия не войдут. Чтобы их считать, нужна другая постановка задачи, с распределением платежей по договору.
Рядом стоит поучительная история. «И по расчетным документам тоже не попадают» — так и написал сначала, по рассуждению, не прогоняя. Ревизия — отдельный прогон готового текста и отчета на поиск таких мест — прогнала такой заказ, и оплата, введенная на основании реализации, определилась полностью: 72 280,00 и сто процентов. Ограничение было выдумано, причем выдумано уверенно и жирным шрифтом.
Дальше короче:
- Выручка, пришедшая не через «Реализацию товаров и услуг», с оплатой не связывается. Связка «накладная — заказ» построена на реквизите
ЗаказКлиентадокумента реализации, и по коду отчет комиссионера с отчетом о розничных продажах через нее не проходят. Сценариев с ними не заводил. - Частичные отгрузки не делятся: заказ либо закрыт целиком, либо его в закрытых нет. Для второго случая есть отдельный вариант с причиной, почему не закрыт.
- Себестоимость показывается такой, как лежит в регистре. Отчет ее не считает и считать не должен.
- Условного оформления в схеме нет. Сверхоплату вывел отдельной колонкой, а не цветом: моя схема с цветом, заданным через элемент стиля, платформой не читалась. Ошибка при загрузке — «Исключение XDTO произошло при чтении файла Template.xml», без указания места. Нашел бисекцией по девяти урезанным вариантам схемы и оформление выкинул.
- У запроса нет нижней границы периода ни в одном шаге: выручка, расчеты с клиентами и прочие расходы читаются за всю историю. На демобазе это незаметно, на базе с пятью годами истории каждое формирование будет читать весь регистр целиком. Не мерял и считаю это долгом.
Про нулевую себестоимость на свежих данных
Маленький сюжет, который стоит отдельно, потому что дважды объяснял его неправильно.
На тестовых продажах 2026 года себестоимость нулевая: двадцать две строки реализаций, выручка без НДС 1 150 000, стоимость ровно ноль. Сначала решил, что дело в организации: скопированный «Расчет себестоимости товаров» относился не к той. Пересоздал — не помогло. Тогда написал, что нужна полная цепочка закрытия месяца, и с этим объяснением прожил два круга.
Оно тоже неверное. Оба документа «Расчет себестоимости товаров» за март и апрель 2026 в базе есть и проведены, а строк со стоимостью за 2026 нет ни одной: расчет выполнен, стоимость по-прежнему ноль.
Зато нашлось вот что: последнее проведенное «Приобретение товаров и услуг» в этой базе датировано 18 апреля 2025 года. Товар 2026 года продан из ничего: партий под него, по-видимому, нет — регистр партий отдельно не смотрел, вывод сделан по дате последнего приобретения. Похоже, себестоимость просто неоткуда взять, и лечится это не расчетом, а поступлением товара.
И тут останавливаюсь, потому что третий раз наступать на те же грабли не хочу. Объяснение номер три прогоном не подтверждено: проверка тут одна — завести поступление в 2026, посчитать себестоимость и посмотреть, стала ли колонка ненулевой. Не делал: закрытие месяца пишет в десяток регистров, и чистого отката на демобазе не вышло бы. Так что пока это гипотеза, у которой есть одно измеренное основание.
Дважды выдать догадку за причину в одном и том же месте — и на третьем объяснении спохватиться — это, пожалуй, главный мой личный итог всей работы.
Чего не проверял
Отдельно, чтобы никто не принял мою уверенность за проверенное.
- Колонки «Доставка» и «Погашено неденежно» на демоданных не заполняются: пять доставшихся с базой документов «Корректировка задолженности» есть, но ни один из них не привязан к объекту расчетов заказа клиента. Ненулевое значение колонка получила только на документе, который завел под сценарий;
после прогона его удалил. То есть поведение колонки проверено, но в базе этого случая сейчас нет — перед приемкой документ надо заводить заново.
- Механизм, по которому компоновщик прячет одно поле из временной таблицы прочих расходов, не нашел. Пять гипотез проверил, все опроверг.
- Валютный случай проверил наполовину. Единственный валютный заказ оплачен в той же валюте, и процент вышел верным: выручка в валюте взаиморасчетов совпала с суммой заказа до копейки при курсе 101,6496. Что будет при заказе в евро и платеже в рублях, не знаю.
- Обнуление прибыли по комиссионному товару не делается, и осознанного решения тут нет.
- Производительность не мерил вовсе. См. выше про отсутствие нижней границы периода.
- Синтаксический контроль по модулям так и не прогнал, и выяснилось, что проверять нечего: в модуле объекта отчета ноль строк кода, одни пустые области. Вся работа в схеме.
Пятая ревизия: дефект нашелся и в отчете, и в самой сверке
Текст выше дописал и уже собирался выкладывать, когда решил проверить одну вещь. Из восьми ломаных сборок четыре ни разу не прогонял через эталон третьей версии. Прогнал. Все четыре прошли зеленым.
Пришлось дописывать сверку: подсчет строк против числа различных заказов, колонка «Выручка с НДС» против «Выручки», единообразие текстов причины и отдельная проверка ноября 2025 года, где живет сценарий со списанием долга. После этого все восемь краснеют, а рабочая сборка держит ноль.
Оговорка к этой фразе: единым замером это не снято. Ломаные гонял до восстановления тестовых данных, рабочую — после, и состав базы между этими прогонами отличается на два заказа. Утверждать «все восемь краснеют в одних условиях» не могу, пока не прогоню их одним заходом.
Дальше позвал пятую ревизию, уже против новой сверки. Она нашла живой дефект.
Поле «Рентабельность, %» объявлено вычисляемым, но в состав итоговых полей схемы не внесено. В детальных строках оно считается, а в строке месяца и в «Итого» — пустое. Отчет про прибыль, и главный показатель отсутствует ровно там, где собственник его и берет: за месяц и за период. Пять редакций и четыре ревизии прошли мимо.
Причина ровно та же, о которой вся эта статья. Мой эталон собирает строки отчета в соответствие по ключу заказа, а у строк группировок и общего итога ключ пустой — и они выбрасываются на входе. Свои контрольные суммы он складывает сам из детальных строк. То есть проверка «сумма двенадцати месяцев против года» все это время сравнивала две величины, посчитанные из одних и тех же деталей, и к тому, что отчет показывает в «Итого», отношения не имела.
И еще: во всех восьми ломаных сборках сломан именно текст запроса. Ни одна не трогает настройки схемы — состав итоговых полей, вычисляемые поля, порядок колонок. Целый слой отчета никогда не был под приемкой, и живой дефект нашелся именно там.
Пятая ревизия подряд нашла то, чего не видел никто до нее. Пять из пяти — это уже не случайность.
Почему файла к статье нет
Теперь то, с чего начал.
Отчет написан, собран и считает. Чтобы довод ниже не читался как «автор сам себе не верит», сначала перечислю, что по нему доказано.
Отчет открывали живым клиентом мышью, и все три варианта настроек отрисовались: по месяцам закрытия, списком и «Незакрытые заказы и причина». Восемь намеренно сломанных сборок краснеют — с оговоркой из раздела про пятую ревизию: единым замером это не снято. Пять дефектов из тех, что описаны выше, закрыты сценарием, который без правки падает.
Сюда же просились еще два пункта, и убрал их, потому что сам же выше объяснил, почему они ничего не доказывают. Сумма двенадцати месяцев совпадает с годом — это необходимое условие, и оно выполнялось при неверном отчете. Себестоимость 2023 года сходится с формулой типовой по тем же ресурсам — а эталон на том же ресурсе не может не сойтись. Ставить их в список доказанного значило бы ровно ту ошибку, на которой сидел все пять кругов.
По-хорошему на этом месте должна быть ссылка.
Ее нет, и вот причина. Пять кругов ревизий подряд находили в отчете дефекты, и каждый раз это были не опечатки, а вещи, из-за которых собственник увидел бы неверные деньги: минус вместо плюса по заказу, закрытая сделка там, где половину долга списали, исчезающий из отчета заказ, меняющееся прошлое, пустая рентабельность в итоговых строках. Ни один из этих дефектов не нашел сам — находили проверки, которые ставил после того, как считал работу законченной. Пять раз из пяти.
Отсюда простая арифметика. Если каждый круг проверки что-то находит, разумнее всего предположить, что следующий тоже найдет. Отчет считает деньги, по которым принимают решения, и продавать его, пока не уверен, не хочется — ни за десять стартмани, ни за одну.
Так что здесь методика и грабли, а файла нет. Регистр, порядок шагов, условия, границы, ошибки и их симптомы старался описать так, чтобы этого хватило на свою сборку. И та же оговорка, что на отчете, относится к описанию: оно вышло из того же процесса, где каждый круг проверки что-то находил. Собирая по этой статье, проверяйте так же, как проверял я: сломанной сборкой, которая обязана покраснеть.
А вот что мне интересно: у кого-то же расчеты ведутся по договорам, и вопрос «в каком месяце прибыль» от этого не исчезает. Как вы его закрываете?
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.