При разработке отчетов или механизмов расчета аренды в 1С программисты часто сталкиваются с ситуацией, когда виртуальная таблица Остатки не возвращает данные за последний день периода, если в этот самый день произошло списание (расход) оборудования. Это происходит из-за особенностей работы платформы с осью времени и моментами документов. Разберем подробно, почему так происходит, и как реализовать корректный расчет остатков на каждый день одним эффективным запросом.
Рассмотрим ситуацию: оборудование возвращено 31 августа в 23:59:59. Если мы запрашиваем остатки на КонецДня (31.08.2018 23:59:59), платформа по умолчанию выдает остаток на начало указанной секунды. Если же мы используем объект Граница с видом ВидГраницы.Включая, запрос покажет состояние после всех движений, совершенных в эту секунду. Так как документ расхода уже "обнулил" остаток, мы получаем 0. В результате день использования оборудования просто "выпадает" из расчета, хотя фактически оно находилось у контрагента весь этот день.
Проанализируем типичную ошибку: использование запроса внутри цикла. Это не только замедляет работу системы, совершая десятки обращений к базе данных вместо одного, но и заставляет разработчика вручную обрабатывать границы каждого дня, что часто приводит к логическим ошибкам.
Вместо того чтобы мучиться с Границей в цикле, наиболее правильным подходом будет использование виртуальной таблицы ОстаткиИОбороты. Она позволяет получить начальный остаток, приход, расход и конечный остаток за каждый период (день, месяц и т.д.) внутри выбранного интервала.
Посмотрим, как настроить параметры этой таблицы:
День. Это заставит систему автоматически разбить данные по датам.ДвиженияИГраницыПериода. Это гарантирует, что в выборку попадут те дни, когда были движения, а также границы периода.Рассмотрим пример запроса, который получает данные по дням:
ВЫБРАТЬ
Регистр.Период КАК Дата,
Регистр.Контрагент,
Регистр.Оборудование,
Регистр.КоличествоНачальныйОстаток,
Регистр.КоличествоКонечныйОстаток,
Регистр.КоличествоРасход
ИЗ
РегистрНакопления.Агро_ПередачаДополнительногоОборудования.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, ДвиженияИГраницыПериода, ) КАК Регистр
Используя КоличествоНачальныйОстаток, мы увидим, что на начало дня сдачи оборудование еще числилось за контрагентом. Это позволяет корректно засчитать этот день в аренду.
Если нам нужно посчитать именно количество дней владения, включая те дни, в которые не было никаких движений по регистру, нам понадобится вспомогательная таблица дат. В 1С для этого идеально подходит РегистрСведений.РегламентированныйПроизводственныйКалендарь.
Разберем по шагам создание комплексного запроса:
Шаг 1: Формируем таблицу всех дней месяца.
Нам нужно получить список всех дат в интервале от &НачалоПериода до &КонецПериода.
ВЫБРАТЬ РАЗЛИЧНЫЕ
НАЧАЛОПЕРИОДА(Календарь.ДатаКалендаря, ДЕНЬ) КАК Дата
ПОМЕСТИТЬ ВТ_Календарь
ИЗ
РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
ГДЕ
Календарь.ДатаКалендаря МЕЖДУ &НачалоПериода И &КонецПериода
;
Шаг 2: Получаем остатки на каждый день.
Для этого мы соединяем нашу таблицу дат с виртуальной таблицей остатков. Важно использовать ЛЕВОЕ СОЕДИНЕНИЕ, чтобы не потерять дни, по которым нет записей в регистре. Однако, виртуальная таблица остатков на конкретную дату в запросе — это дорогостоящая операция. Поэтому лучше получить остатки и обороты, а затем применить механизм получения остатка на каждую дату.
Проанализируем более продвинутый вариант — получение количества дней использования оборудования за период:
ВЫБРАТЬ
ВТ_Календарь.Дата,
ОстаткиИОбороты.Контрагент,
ОстаткиИОбороты.Оборудование,
ОстаткиИОбороты.КоличествоКонечныйОстаток КАК Остаток
ПОМЕСТИТЬ ВТ_ДанныеПоДням
ИЗ
ВТ_Календарь КАК ВТ_Календарь
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Агро_ПередачаДополнительногоОборудования.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ОстаткиИОбороты
ПО ВТ_Календарь.Дата = ОстаткиИОбороты.Период
;
Выясним причину, по которой ОстаткиИОбороты может не показывать данные: если остаток стал нулевым 15-го числа, то на 16-е и последующие числа записей может не быть. Для полноценного отчета программисты часто используют метод "Сумма-дней" или соединение с календарем и последующим заполнением пустых значений в коде (или сложным соединением в запросе).
Если ваша задача — просто посчитать количество дней, когда оборудование было "на руках" (остаток > 0), используйте следующую логику в итоговом запросе:
ВЫБРАТЬ
Данные.Контрагент,
Данные.Оборудование,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Данные.Дата) КАК ДнейИспользования
ИЗ
ВТ_ДанныеПоДням КАК Данные
ГДЕ
Данные.Остаток > 0
СГРУППИРОВАТЬ ПО
Данные.Контрагент,
Данные.Оборудование
Важно понимать, что если вы ведете учет оборудования без серийных номеров (просто количество), то расчет дней становится условным. Например: 1-го числа выдали 10 штук, 10-го числа выдали еще 5 штук, а 15-го вернули 3 штуки. Какие именно единицы вернули — те, что пробыли у клиента 15 дней или 5?
В управленческом учете для таких задач обычно применяется принцип ФИФО (First In, First Out). Это означает, что возвращенное оборудование в первую очередь закрывает самые ранние "долги". Если заказчику требуется абсолютная точность по каждой единице, необходимо переходить на поштучный учет с использованием серийных номеров или справочника конкретных материальных ценностей (каждая единица — отдельный элемент).
1С:Предприятие.ОстаткиИОбороты с нужной периодичностью.КоличествоНачальныйОстаток или используйте Границу с параметром Включая, но помните, что на последнюю секунду дня списания остаток всегда будет равен нулю.Такой подход позволит вам создавать надежные и быстрые механизмы расчета, которые будут корректно учитывать оборудование даже в самые сложные моменты сдачи-приемки.