Тот же вопрос уже обсуждался на Мисте http://www.forum.mista.ru/topic.php?id=758279 . Предложенные решения еще можно упростить/улучшить. Для того, чтобы сделать это, нужно понимать:
1) Что понимается под "был на складе". В 11:00 поступил, в 11:01 реализовали (переместили) - этот день считается как день, в который "товар был"? А 11:00 и 11:00?
2) Нужно ли считать только рабочие дни или интересуют вообще все дни ("календарные")?
3) Принимаются ли дробные ответы (был полдня, полтора дня и т.п.)?
4) "В месяце" означает, что будет анализироваться только один месяц (либо другой интервал)? Либо нужен отчет, показывающий дней был на складе в январе 5 дней, в феврале 10 дней и так далее, то есть требуются подитоги по месяцам.
5) Чем не устроили три уже приведенных решения?
(1) olo_lo4, ответьте, пожалуйста, на эти вопросы.
(7) insurgut, тоже считаю, что нужно использовать партии.
(1) если нужно просто учитывать сколько лежат на складе текущие остатки, то тут получаем остатки по партиям на текущую дату, затем получаем дату прихода партий, получаем разницу дней между датой прихода партии и текущей датой.
(11) karpik666, мне кажется, вы путаете показатель "возраст остатков", который рассчитывается через время, которое товар в среднем провел (пролежал) на складе и показатель, связанный числом дней, который товар был в наличии. Я понял вопрос так, что интересуют дни наличия. Иначе подошел бы отчет типа "Возраст" остатков номенклатуры, где как раз ФИФО, партии и тому подобное.
Если попробовать обойтись без календаря, то можно сложить все периоды (в секундах) отсутствия товара на складе и вычесть сумму длин этих периодов из длины всего периода, чтобы получить суммарное время наличия товара на складе в секундах за интересующий период. Тогда получится следующий запрос:
ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период,
Движения.КоличествоНачальныйОстаток,
Движения.КоличествоКонечныйОстаток
ПОМЕСТИТЬ Движения
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Секунда, , ) КАК Движения
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период,
Движения.КоличествоНачальныйОстаток,
Движения.КоличествоКонечныйОстаток
ПОМЕСТИТЬ ДополненныеДвижения
ИЗ
Движения КАК Движения
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
НачальныеНули.Склад,
НачальныеНули.Номенклатура,
&НачалоПериода,
0,
0
ИЗ
Движения КАК НачальныеНули
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
КонечныеНули.Склад,
КонечныеНули.Номенклатура,
&КонецПериода,
0,
0
ИЗ
Движения КАК КонечныеНули
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Точки.Склад,
Точки.Номенклатура,
Точки.Период,
МАКСИМУМ(Точки.КоличествоНачальныйОстаток) КАК КоличествоНачальныйОстаток,
МАКСИМУМ(Точки.КоличествоКонечныйОстаток) КАК КоличествоКонечныйОстаток
ПОМЕСТИТЬ ТочкиВремени
ИЗ
ДополненныеДвижения КАК Точки
СГРУППИРОВАТЬ ПО
Точки.Склад,
Точки.Номенклатура,
Точки.Период
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Точки.Склад,
Точки.Номенклатура,
МАКСИМУМ(Слева.Период) КАК НачалоОтсутствия,
Точки.Период
ПОМЕСТИТЬ Интервалы
ИЗ
ТочкиВремени КАК Точки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТочкиВремени КАК Слева
ПО Точки.Склад = Слева.Склад
И Точки.Номенклатура = Слева.Номенклатура
И (Точки.КоличествоНачальныйОстаток = 0)
И (Слева.КоличествоКонечныйОстаток = 0)
И Точки.Период > Слева.Период
СГРУППИРОВАТЬ ПО
Точки.Склад,
Точки.Номенклатура,
Точки.Период
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ РАЗЛИЧНЫЕ
Точки.Склад,
Точки.Номенклатура,
&КонецПериода,
&НачалоПериода
ИЗ
Движения КАК Точки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Интервалы.Склад,
Интервалы.Номенклатура,
СУММА(РАЗНОСТЬДАТ(Интервалы.Период, Интервалы.НачалоОтсутствия, СЕКУНДА)) / 60 / 60 / 24 КАК ДнейНаличия
ИЗ
Интервалы КАК Интервалы
СГРУППИРОВАТЬ ПО
Интервалы.Склад,
Интервалы.Номенклатура
Показать
Чтобы обработать "особые случаи" (когда в начале или в конце интервала остаток будет нулевым), потребовалось добавить записи - "заглушки" по краям интервала. Чтобы учесть возможность, когда периодов отсутствия не было, потребовалось добавить записи полного интервала, из которых при группировке (второй заяц) вычитаются интервалы отсутствия.
Из-за этих особых случаев запрос получился длинным, но по идее он простой. Большим плюсом этого подхода является то, что он замечает движения в течении дня. Если же ориентироваться только на остатки в начале/конце дня, то на складе типа "гардероб" (завезли и тут же продали) товар будет всегда отсутствовать.
Ну и еще вариант того же запроса на один подзапрос и одну строку короче:
ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период,
Движения.КоличествоНачальныйОстаток,
Движения.КоличествоКонечныйОстаток
ПОМЕСТИТЬ Движения
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Секунда, , ) КАК Движения
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период,
Движения.КоличествоНачальныйОстаток,
Движения.КоличествоКонечныйОстаток
ПОМЕСТИТЬ ДополненныеДвижения
ИЗ
Движения КАК Движения
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ РАЗЛИЧНЫЕ
НачальныеНули.Склад,
НачальныеНули.Номенклатура,
&НачалоПериода,
0,
0
ИЗ
Движения КАК НачальныеНули
ГДЕ
НЕ (НачальныеНули.Склад, НачальныеНули.Номенклатура, &НачалоПериода) В
(ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период
ИЗ
Движения)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ РАЗЛИЧНЫЕ
КонечныеНули.Склад,
КонечныеНули.Номенклатура,
&КонецПериода,
0,
0
ИЗ
Движения КАК КонечныеНули
ГДЕ
НЕ (КонечныеНули.Склад, КонечныеНули.Номенклатура, &КонецПериода) В
(ВЫБРАТЬ
Движения.Склад,
Движения.Номенклатура,
Движения.Период
ИЗ
Движения)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Точки.Склад,
Точки.Номенклатура,
МАКСИМУМ(Слева.Период) КАК НачалоОтсутствия,
Точки.Период
ПОМЕСТИТЬ Интервалы
ИЗ
ДополненныеДвижения КАК Точки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ДополненныеДвижения КАК Слева
ПО Точки.Склад = Слева.Склад
И Точки.Номенклатура = Слева.Номенклатура
И (Точки.КоличествоНачальныйОстаток = 0)
И (Слева.КоличествоКонечныйОстаток = 0)
И Точки.Период > Слева.Период
СГРУППИРОВАТЬ ПО
Точки.Склад,
Точки.Номенклатура,
Точки.Период
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ РАЗЛИЧНЫЕ
Точки.Склад,
Точки.Номенклатура,
&КонецПериода,
&НачалоПериода
ИЗ
Движения КАК Точки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Интервалы.Склад,
Интервалы.Номенклатура,
СУММА(РАЗНОСТЬДАТ(Интервалы.Период, Интервалы.НачалоОтсутствия, СЕКУНДА)) / 60 / 60 / 24 КАК ДнейНаличия
ИЗ
Интервалы КАК Интервалы
СГРУППИРОВАТЬ ПО
Интервалы.Склад,
Интервалы.Номенклатура
Показать
Хотя тут есть тэта соединения, периодов отсутствия обычно немного, поэтому запрос не должен "тормозить".
Нетрудно доработать запрос, чтобы считались только "целые дни" отсутствия (с утра до вечера), но лучше бы сначала получить ответы на вопросы из (6).
ВЫБРАТЬ
Точки.Склад,
Точки.Номенклатура,
Точки.Период,
МАКСИМУМ(Точки.КоличествоНачальныйОстаток) КАК КоличествоНачальныйОстаток,
МАКСИМУМ(Точки.КоличествоКонечныйОстаток) КАК КоличествоКонечныйОстаток
ПОМЕСТИТЬ ТочкиВремени
В первом запросе есть ошибка. При наличии записей в регистре на конец периода результат будет неверным.
Чтобы считало правильно для поля КоличествоКонечныйОстаток надо использовать МИНИМУМ:
МИНИМУМ(Точки.КоличествоКонечныйОстаток) КАК КоличествоКонечныйОстаток
А если товар пришел в один день и сразу ушел (в этот же день), это как должно подсчитываться? Или он несколько раз за месяц приходил, то надо брать общее число дней отсутствия, или среднее?
если вы делаете отчет товарооборачиваемость, тогда чтобы он был самым точным, вам нужно не остатками оперировать, а движением: нашли остатки на начало периода, далее уже в цикле смотрим движения: берем остаток * на количество дней до даты движения, далее рассчитываем новый остаток на дату движения, идем к следующему движению....
(12) zarucheisky, в приведенной вами ссылке есть интересная формула расчета среднего: ТЗср = (Т1/2 + Т2 + Т3 + ... + Тн/2) / (н - 1). Если придирчиво рассмотреть эту формулу, окажется, что она была выведена в докомпьютерную эпоху. Когда не было возможности точного расчета среднего и интеграл считался трапециями. Сейчас наши возможности больше и среднее можно считать гораздо точнее. Например, как написано в статье Расчет средних по периодам в запросе - это элементарно!.
Вот запрос для УТ10.3 - считает число дней разрывов(т.е наоборот когда товара не было на складе)
ВЫБРАТЬ
РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
ПОМЕСТИТЬ ТабДат
ИЗ
РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
ГДЕ
РегламентированныйПроизводственныйКалендарь.ДатаКалендаря МЕЖДУ &ДатаНачала И &ДатаОкончания
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СправочникНоменклатура.Ссылка КАК Номенклатура,
ТабДат.ДатаКалендаря,
СправочникНоменклатура.Представление
ПОМЕСТИТЬ ТабНоменклатура
ИЗ
Справочник.Номенклатура КАК СправочникНоменклатура,
ТабДат КАК ТабДат
ГДЕ
НЕ СправочникНоменклатура.Услуга
И НЕ СправочникНоменклатура.ПометкаУдаления
И НЕ СправочникНоменклатура.Ссылка В ИЕРАРХИИ (&НоменклатураБух)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВложенныйЗапрос4.ДатаКалендаря, МЕСЯЦ) КАК Период,
ВложенныйЗапрос4.Номенклатура КАК Номенклатура,
ВЫБОР
КОГДА ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) + ЕСТЬNULL(ВложенныйЗапрос4.Оборот, 0) - (ЕСТЬNULL(ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток, 0) + ЕСТЬNULL(ВложенныйЗапрос5.Оборот, 0)) - (ЕСТЬNULL(ТоварыКПередачеСоСкладовОстатки.КоличествоОстаток, 0) + ЕСТЬNULL(ВложенныйЗапрос6.Оборот, 0)) <= 0
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК Разрывы
ИЗ
(ВЫБРАТЬ
ТабНоменклатура.ДатаКалендаря КАК ДатаКалендаря,
СУММА(ЕСТЬNULL(ТоварыНаСкладахОбороты.КоличествоОборот, 0)) КАК Оборот,
ТабНоменклатура.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕ(ТабНоменклатура.Номенклатура) КАК НоменклатураПредставление
ИЗ
ТабНоменклатура КАК ТабНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Обороты(
&ДатаНачала,
&ДатаОкончания,
День,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыНаСкладахОбороты
ПО ТабНоменклатура.ДатаКалендаря > ТоварыНаСкладахОбороты.Период
И ТабНоменклатура.Номенклатура = ТоварыНаСкладахОбороты.Номенклатура
СГРУППИРОВАТЬ ПО
ТабНоменклатура.ДатаКалендаря,
ТабНоменклатура.Номенклатура) КАК ВложенныйЗапрос4
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
СУММА(ЕСТЬNULL(ТоварыВРезервеНаСкладахОбороты.КоличествоОборот, 0)) КАК Оборот,
ТабНоменклатура.ДатаКалендаря КАК ДатаКалендаря,
ТабНоменклатура.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕ(ТабНоменклатура.Номенклатура) КАК НоменклатураПредставление
ИЗ
ТабНоменклатура КАК ТабНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезервеНаСкладах.Обороты(
&ДатаНачала,
&ДатаОкончания,
День,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыВРезервеНаСкладахОбороты
ПО ТабНоменклатура.ДатаКалендаря > ТоварыВРезервеНаСкладахОбороты.Период
И ТабНоменклатура.Номенклатура = ТоварыВРезервеНаСкладахОбороты.Номенклатура
СГРУППИРОВАТЬ ПО
ТабНоменклатура.ДатаКалендаря,
ТабНоменклатура.Номенклатура) КАК ВложенныйЗапрос5
ПО ВложенныйЗапрос4.ДатаКалендаря = ВложенныйЗапрос5.ДатаКалендаря
И ВложенныйЗапрос4.Номенклатура = ВложенныйЗапрос5.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
СУММА(ЕСТЬNULL(ТоварыКПередачеСоСкладовОбороты.КоличествоОборот, 0)) КАК Оборот,
ТабНоменклатура.ДатаКалендаря КАК ДатаКалендаря,
ТабНоменклатура.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕ(ТабНоменклатура.Номенклатура) КАК НоменклатураПредставление
ИЗ
ТабНоменклатура КАК ТабНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыКПередачеСоСкладов.Обороты(
&ДатаНачала,
&ДатаОкончания,
День,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыКПередачеСоСкладовОбороты
ПО ТабНоменклатура.ДатаКалендаря > ТоварыКПередачеСоСкладовОбороты.Период
И ТабНоменклатура.Номенклатура = ТоварыКПередачеСоСкладовОбороты.Номенклатура
СГРУППИРОВАТЬ ПО
ТабНоменклатура.ДатаКалендаря,
ТабНоменклатура.Номенклатура) КАК ВложенныйЗапрос6
ПО ВложенныйЗапрос4.ДатаКалендаря = ВложенныйЗапрос6.ДатаКалендаря
И ВложенныйЗапрос4.Номенклатура = ВложенныйЗапрос6.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(
&ПредДатаНачала,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыВРезервеНаСкладахОстатки
ПО ВложенныйЗапрос4.Номенклатура = ТоварыВРезервеНаСкладахОстатки.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки(
&ПредДатаНачала,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыКПередачеСоСкладовОстатки
ПО ВложенныйЗапрос4.Номенклатура = ТоварыКПередачеСоСкладовОстатки.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
&ПредДатаНачала,
НЕ Номенклатура.Услуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура В ИЕРАРХИИ (&НоменклатураБух)
И Склад В ИЕРАРХИИ (&ОсновныеСклады)) КАК ТоварыНаСкладахОстатки
ПО ВложенныйЗапрос4.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура
Делал подобное- берешь регистр с датами на каждый день, я брал регл производственный календарь. Можно взять взаиморасчеты с контрагентами.
Связи делаешь с регистром остатков. Ну и обработчик если на день остатки были то ставишь 1, если нет то 0. Потом групируешь---- ПРОФИТ!!!