gifts2017

Продажи по оплате или в поисках истины…

Опубликовал script Мальчинко (script) в раздел Программирование - Практика программирования

Отчет "Валовая прибыль по оплате" и история его разработки для УТ 10, УПП 1.3 , УТП 1.2 (Россия, Украина, и наверное, для других стран тоже).

Это начало истории. После него появилось продолжение Валовая прибыль по оплате

Скачать отчет можно в продолжении.


Все началось с обычного запроса.

Утро. Приходит сообщение, что в конфигурации УПП отчет «Продажи по оплате» не показывает продажи по некоторым клиентам. И вообще очень долго формируется – ждать иногда приходится 30-40 мин. При личном осмотре «тело» демонстрировало одни и те же симптомы, т.е. ошибка явно воспроизводилась, несмотря на то, что по контрагенту была и оплата, и отгрузка на одну и ту же сумму. Перепроводим все документы в хронологическом порядке – результат тот же.

Поскольку лечение пилюлями не дало положительного результата, принимаем решение, что хирургическое вмешательство должно помочь. Идем в конфигуратор – и падаем в обморок от увиденного. Клубок запроса где-то на 100 км. размотать, который  уже не получится. 

Легенда (интернет) говорит, что данный отчет создал некий мудрец, который уже не общается с 1С, и который так и не довел этот отчет до ума.

Прошло много времени. Больше года

Все это время проблема всплывала несколько раз, но решение так не находилось, хотя некоторые нюансы прояснить удалось. Например, что для работы отчета «Продажи по оплате» нужно, что бы в договоре были выставлены фаги «вести по документам расчетов» или, что в отчет считает оплаченными только те продажи, которые были оплачены в период формирования отчета.  Все это постепенно привело к тому, что я решил делать свой отчет по оплаченным продажам

Последней каплей стало, дополнительное требование от пользователей, когда такой отчет должен выводится в произвольной валюте, которую должен выбирать пользователь. Причем есть требование к дате, на которую нужно делать переоценку показателей. Сумма продажи должна переоцениваться на дату оплаты, а себестоимость – на дату партии (документ оприходования).

Сначала были определены основные требования к отчету:

  1. Пользователя устраивает отчет «Валовая прибыль» по составу данных. Нужны: количество, сумма и себестоимость проданного товара, с детализацией до: подразделения, проекта, контрагента, заказа, номенклатуры, по дням, месяцам, годам и т.д.
  2. Отчет должен содержать данные о продажах, которые полностью оплачены.
  3. Оплата должна распределятся по отгрузкам с учетом заказов, если по договору взаиморасчеты ведутся по заказам или счетам. Если взаиморасчеты ведутся по договору в целом, тогда оплата распределяется в целом по договору.
  4. Отчет должен иметь все те же возможности по отбору, сортировке, группировке и т.д. что и отчет «Валовая прибыль». Желательно что бы отчет был разработан на базе «Универсального отчета», что бы сохранить юзабилити интерфейса на привычном уровне.
  5. Нужно иметь возможность указывать в отчете валюту и выполнять переоценку суммы продажи по курсу на дату оплаты, и себестоимости – по курсу на дату партии (документ оприходования). 

После определения требований, началась реализация.

Поскольку пользователи сами меня натолкнули на отчет «Валовая прибыль», возникла идея взять за основу запрос из этого отчета. Можно предположить, что отчет «Валовая прибыль» содержит в себе 100 % продаж за указанный период, из которых некоторая часть X%, это те, которые оплачены, а остальные не оплачены. Другими словами, если из этого отчета убрать записи с неоплаченными отгрузками мы и получим нужный результат.  

Алгоритм оказался не сложным:

  1. Находим на инфостарте шаблон отчета желательно на СКД, и похожим на универсальный. Обрабатываем его напильником для юзабилити.
  2. Создаем схему компановки, источник данных «Объект» (таблица, заполняемая программным путем). Добавляем поля и выполняем настройку внешнего вида отчета, таким образом, что бы он был похож на отчет «Валовая прибыль».
  3. Для того что бы программно заполнить СКД, берем и выдергиваем текст запроса из отчета «Валовая прибыль». Убираем из запроса расчет «валовой прибыли», «рентабельности» и «эффективности», поскольку эти показатели будут рассчитаны в СКД как вычисляемые поля. После таких манипуляций  в запросе останется только левое соединение таблицы регистра «ПродажиСебестоимость» с виртуальной таблицей «ПродажиОбороты», регистра «Продажи»,
  4. Результат запроса выгружаем в таблицу значений – «ТаблицаПродаж».
  5. Из таблицы продаж выгружаем в массивы: «МассивКонтрагентов», «МассивДоговоров» и «МассивЗаказов», соответствующие колонки. Данные массивы будут выступать в роли отбора для следующего запроса, для получения оплат.
  6. Далее выполняем еще один запрос к регистру «ВзаиморасчетыСКонтрагентам», с установленными отборами, для получения оплат (Приходы) по контрагентам, договорам, заказам, регистратору и сумме взаиморасчетов. Результат запроса так же выгружаем в таблицу значений – «ТаблицаОплат».
  7. Описываем процедуру, которая в цикле обойдет, все записи таблицы продаж и таблицы оплат и сформирует промежуточную таблицу – «СводнаяТаблица», которая будет содержать только оплаченные продажи. Погашение отгрузок оплатами будет выполнено по методу FIFO.
  8. Данные из «СводнаяТаблица» загружаем в СКД и программно выполняем компоновку данных и вывод в табличный документ.

После того как я выполнил первые четыре пункта требований я подумал что, вижу свет в конце туннеля, но оказалось что это был зрительный обман, потому что для реализации последнего пункта мне пришлось полностью переделывать отчет еще 4 раза. 

 

УСЛОЖНЯЕМ.

После того как я добился корректной работы отчета в валюте упр. учета, и напильником отшлифовал шаблон универсального отчета на СКД, пришло время и для последней задачи, связанной с переоценкой показателей отчета в произвольную валюту.

Следуя рекомендациям 1С и общей моде, я захотел сделать все «красиво», т.е. запихнуть алгоритм переоценки прямо в запрос. Идея о том, что все будет выполняться одним махом, грела мне душу и жгла руки, и тогда я начал усложнять запрос, добавляя в него, где нужно, левые соединения с таблицей регистра сведений «Курсы валют»

Ох уж эта переоценка

Что бы лучше понять всю сложность требуемых манипуляций с данными, необходимо детально описать весь процесс переоценки по пунктам:

1) Сумма продажи переоценивается в валюту отчета по курсу на дату оплаты. Что это означает для обработки данных? Для полного понимания картины нужно обратиться к истокам - регистрам, в которых эти данные хранятся

Итак, регистры "Продажи" и "Продажи себестоимость". При проведении расходных накладных, программа пишет в эти регистры следующие данные:

 - в регистр "Продажи себестоимость программа записывает себестоимость проданного товара, которая не пересчитывается, а берется, так сказать "готовой", в валюте упр. учета, из регистра учета партий. По сути задачи, данные в регистре "Продажи себестоимость" всегда переоценены в валюту упр. учета по курсу, на дату документа оприходования. Это полностью соответствует нашим требованиям, поэтому останавливаться на этом пункте нет смысла. Единственное что нужно отменить, так это то, что требованиями заложена необходимость иметь возможность пересчитывать себестоимость из валюты упр. учета в валюту отчета, аналогично, по курсу на дату оприходования. Но о том, как это реализовывалось, будет написано ниже.

  - в регистр "Продажи" записывается сумма расходной накладной, которая пересчитывается из валюты документа, в валюту упр. учета по курсу, полученному на дату расходной накладной. Именно на этом я хотел бы заострить внимание. Выходит так, что, если валюта упр. учета равна валюте регл. учета, тогда все решается достаточно просто. Берем сумму продажи и пересчитываем ее в валюту отчета, по курсу, полученному на дату оплаты. Но что будет, если валюта упр. учета будет отличатся от валюты регл. учета? Получится так, что при проведении расходной накладной, суммы в регистре продажи уже будут переоценены из валюты документа, в валюту упр. учета, по курсу продажи, а не оплаты. Поэтому решение, взять и просто пересчитать сумму продажи в валюту отчета, по курсу на дату оплаты, будет не верным, а результат ошибочным.

Для наглядности лучше привести пример и предположим что:

Валюта упр. учета USD
Валюта взаиморасчетов по договору с контрагентом Хк  EUR

 

Дата Валюта Курс
01.01.2014 USD 10 
02.01.2014  USD 15
01.01.2014  EUR 15
02.01.2014  EUR 19

 

Дата  Хоз. операция Сумма Валюта договора
01.01.2014 Продажа 1000.00 EUR
02.01.2014 Оплата 1000.00 EUR

 

Пользователь захотел сформировать отчет в USD, т.е. в валюте упр. учета

Это самый показательный пример, потому что, на первый взгляд, кажется, что здесь даже нечего переоценивать, однако это не так. Причина в том, что именно запишет расходная накладная в регистр "Продажи". А запишет она следующее: 1000.00 $ превратятся в 1500€ по кросс-курсу на 01.01.2014, т.е., на дату продажи. Идем дальше. 02.01.2014 пройдет оплата на 1000$, но программа запишет в упр.учет 1266.67€ по кросс-курсу на дату оплаты. В итоге имеем картину, когда в управленческом учете, продано на 1500€, а оплачено только на 1266.67€, хотя на самом деле взаиморасчеты закрыты.

Нужно понимать, что сравнивать приходится данные регистров: "Продажи" и "ВзаиморасчетыСКонтрагентами".

Кому-то может показаться, что и продажи можно взять из регистра "ВзаиморасчетыСКонтрагентами", только в валюте взаиморасчетов (договора), а не в валюте упр. учета, но это не так. Описывать причину этого утверждения я не буду, поскольку это еще больше усложнит понимание процесса. Предлагаю поверить мне на слово, а тем, кому нужны доказательства предлагаю подумать самим.  Намекну только, что регистратором для регистра  "ВзаиморасчетыСКонтрагентами" может быть документ, который вообще не относится к продажам, например корректировка долга, а в требованиях указано, что погашать оплатами нужно именно документы отгрузки. 

Вернемся к нашему примеру.

Выходит так, что при сравнении оплат и отгрузок программа подумает что, продажа не оплачена и данные не попадут в отчет. Так что же нужно предпринять, что бы получить нужный результат? Сначала нужно пересчитать сумму продажи из регистра "Продажи", из валюты упр. учета, в валюту взаиморасчетов, по курсу на дату продажи, т.е. "вернуть все в зад" на 01.01.2014. И только после этого можно будет сравнивать отгрузки с оплатами, в той валюте, в которой они реально происходили.  А теперь представим, что пользователь выбрал в отчете валюту, например, рубли? В этом случае предстоит еще раз пересчитать сумму продажи из валюты взаиморасчетов, в валюту отчета по курсу на дату оплаты. На самом деле вторую переоценку пройдется выполнять в любом случае, независимо от того какую валюту выберет пользователь.  

А теперь вспоминаем, что все это должно выполняться в запросе, одним махом. 

Облом первый

Оказалось, что запросом получить курс валюты на дату, которая вычисляется из второй таблицы, практически не возможно. Почему практически? Потому что теоретически это возможно. Это делается в два прохода, при помощи вложенного запроса или временной таблицы. Суть метода заключается в том, что бы обратится к регистру "курсы валют" и получить все записи, период у которых, меньше или равен, той дате, на которую нужно получить курс. После это выполнить агрегатную функцию «Максимум» с полем «Период» и таким образом запрос рассчитает максимальную дату (до нужной даты), на которую в регистре есть запись о курсе. Далее данный запрос соединяется с еще одним запросом к регистру «курсы валют» из которого, на полученную ранее дату, извлекается собственно курс и кратность нужной валюты.

Чтобы долго не писать проще показать -вот пример:

|ВЫБРАТЬ
|   Таб1.СсылкаДок,
|   Таб1.ДатаДок,
|   Таб1.ВалютаДок,
|   Таб2.Курс
|ИЗ
|   (ВЫБРАТЬ
|   ЗаказПокупателя.Ссылка КАК СсылкаДок,
|    ЗаказПокупателя.Дата КАК ДатаДок,
|    ЗаказПокупателя.ВалютаДокумента КАК ВалютаДок,
|    МАКСИМУМ(Валюты.Период) КАК ПериодДок
|   ИЗ
|    Документ.ЗаказПокупателя КАК ЗаказПокупателя
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Валюты
|      ПО Валюты.Период

|      Валюты.Валюта = ЗаказПокупателя.ВалютаДокумента
|   СГРУППИРОВАТЬ ПО
|    ЗаказПокупателя.Ссылка) КАК Таб1
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Таб2
|    ПО Таб1.ПериодДок = Таб2.Период И Таб1.ВалютаДок = Таб2.Валюта

После этого можно соединять результат этих вычислений, представленный как вложенный запрос или временная таблица с любой другой таблицей, левым соединением и выполнять переоценку нужных полей.  Но как оказалось, в этом «красивом» методе зарыта мина, которая просто убивает понятие производительность.

В результирующей сводной таблице продаж, к которой я применил этот метод переоценки, у меня было около 100 000 записей. Для каждой этой записи нужно в этом же запросе выполнять переоценку, причем несколько раз. Данный метод работает так, что для получения курса, запрос выбирает все записи из регистра "курсы валют", до даты продажи или оплаты (из примера - до 01.01.2014, и еще раз до 02.01.2014). Таких записей у меня оказалось 730. Т.е в регистре курсы валют хранились записи за каждый рабочий день, начиная 01.01.2011, потому что в настройках пользователей указана, необходимость загружать курсы при входе в программу. И это типичная картина для большинства компаний, которые работают с валютой. А если учесть что, в некоторых случая, в один проход нужно делать две переоценки, уже получаем 1460 лишних записей, которые добавляются к каждой записи из таблицы продаж. Итого, для того что бы переоценить 100 000 записей запрос должен обработать 100 000 * 1460 = 146 000 000 записей. В итоге мой отчет просто перестал формироваться. Т.е. запрос выполнялся, но дождаться окончания я ни разу не смог, при чем, что один раз, я оставил формирование отчета на всю ночь, а на утро обнаружил, что 1С до сих пор о чем то думает. 

Сначала меня удивило, что не было переполнения памяти, но потом я понял, что эти 1460 записей не сохраняются в памяти постоянно, а выбираются порциями при каждом левом соединении, для расчета агрегатной функции "Максимум", а потом удаляются из памяти. Таким образом, в памяти постоянно находиться только 100 000 записей, а остальные загружаются и после использования удаляются. Но из-за того что это выполняется для каждой строки сводной таблицы продаж, производительность падает в сотни раз. По сути, происходит выполнение запроса внутри цикла при обходе записей другого запроса

Облом второй.

После такого поражения я, все-таки не отказался от идеи составить запрос, который бы мог полностью решить поставленную задачу в один проход. Поиск в интернете указал на еще один вариант.

Суть метода заключается в следующем. В регистр сведений "КурсыВалют" необходимо добавить новый реквизит «СледующийПериод» тип значения дата. Именно реквизит, а не ресурс или измерение. После этого можно добавить новый общий модуль, например  «script_КурсыВалют» и подписку на событие «ПриЗаписи», регистра курсы валют. Дальше, в модуле общего модуля описать экспортную процедуру, которая будет отлавливать событие записи в регистр курсов, и заполнять новый реквизит «СледующийПериод». Другими словами, при вводе или загрузке нового курса на новую дату, программа будет дописывать в предыдущую запись, дату текущей записи. Т.е. каждая запись регистра будет содержать не только дату, но еще и дату следующей записи. Соответственно самая поздняя запись, в реквизите "СледующийПериод" будет содержать дату своей записи. 

После данных манипуляций отчет действительно заработал, причем довольно шустро. Это и понятно. Запрос теперь может сразу позиционироваться на нужную запись в регистре курсы валют, даже без необходимости выполнять агрегатные функции. В этот раз за производительность пришлось заплатить универсальностью.

Кому будет интересно пощупать, текст общего модуля лежит внизу, а вот пример использования:

|ВЫБРАТЬ
|   ПродажиСебестоимость.Номенклатура КАК Номенклатура,
|   ПродажиСебестоимость.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|   ПродажиСебестоимость.ЗаказПокупателя КАК ЗаказПокупателя,
|   ВЫБОР
|       КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
|           ТОГДА ПродажиСебестоимость.ДокументДвижения
|       ИНАЧЕ ПродажиСебестоимость.Регистратор
|   КОНЕЦ КАК Регистратор,
|   ВЫБОР
|       КОГДА &ВалютаОтчета = &ВалютаУпр
|           ТОГДА ПродажиСебестоимость.Стоимость
|       ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
|   КОНЕЦ КАК СтоимостьОборот,
|   ВЫБОР
|       КОГДА &ВалютаОтчета = &ВалютаУпр
|           ТОГДА ПродажиСебестоимость.Стоимость
|       ИНАЧЕ ПродажиСебестоимость.НДС * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
|   КОНЕЦ КАК НДСОборот
|ИЗ
|   РегистрНакопления.ПродажиСебестоимость КАК ПродажиСебестоимость
|       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалютОтчета
|       ПО ПродажиСебестоимость.ДокументОприходования.Дата >= КурсыВалютОтчета.Период
|           И ПродажиСебестоимость.ДокументОприходования.Дата < КурсыВалютОтчета.СледующийПериод
|           И (&ВалютаОтчета = КурсыВалютОтчета.Валюта)
|       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалютУпр
|       ПО (&ВалютаУпр = КурсыВалютУпр.Валюта)
|           И ПродажиСебестоимость.ДокументОприходования.Дата >= КурсыВалютУпр.Период
|           И ПродажиСебестоимость.ДокументОприходования.Дата < КурсыВалютУпр.СледующийПериод
|ГДЕ
|   ПродажиСебестоимость.Период МЕЖДУ &НачПериода И &КонПериода
|
|СГРУППИРОВАТЬ ПО
|   ПродажиСебестоимость.Номенклатура,
|   ПродажиСебестоимость.ХарактеристикаНоменклатуры,
|   ПродажиСебестоимость.ЗаказПокупателя,
|   ВЫБОР
|       КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
|           ТОГДА ПродажиСебестоимость.ДокументДвижения
|       ИНАЧЕ ПродажиСебестоимость.Регистратор
|   КОНЕЦ,
|   ВЫБОР
|       КОГДА &ВалютаОтчета = &ВалютаУпр
|           ТОГДА ПродажиСебестоимость.Стоимость
|       ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
|   КОНЕЦ,
|   ВЫБОР
|       КОГДА &ВалютаОтчета = &ВалютаУпр
|           ТОГДА ПродажиСебестоимость.Стоимость
|       ИНАЧЕ ПродажиСебестоимость.НДС * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
|   КОНЕЦ"

После этого можно было и остановится, но оставались еще некоторые шероховатости, которые хотелось убрать. Например, группировка, сортировка и отбор данных, выполнялся средствами СКД, но запрос, который формировал сводную таблицу продаж, ограничивался только периодом отчета. Даже если пользователь накладывал отбор, все равно запрос формировал полную таблицу без отборов, а отбор накладывался уже при выполнении СКД. Для того что бы ограничить запрос пришлось писать код, который читал, используемые элементы отбора в компоновщике настроек СКД, и динамически изменял сам запрос, дополняя его отборами. В общем, получилось, но не красиво. Тогда мне и закралась мысль вместо запроса тоже использовать СКД, а результат уже выгрузить в сводную таблицу значений. Дальнейший алгоритм остался прежним. Из сводной таблицы удаляем те записи, которые не относятся к оплаченным отгрузкам, и загружаем ее в другую СКД, в которой уже рассчитываем валовую прибыль, эффективность и рентабельность, а так же настраиваем внешний вид

Золотая середина.

Даже после реализации всего описанного выше, меня не покидало желание отказаться от доработки регистра курсов валют, для того что бы данный отчет был универсальным. И я решил уйти от решений красивых и перейти к решениям функциональным.   Пришлось опять начинать все сначала.  Итак

Запрос, который формировал сводную таблицу, а потом перерос в СКД, я вообще разделил на два отдельных. Отдельно СКД с запросом к регистру  "Продажи" и отдельно СКД с запросом к регистру "Продажи себестоимость".  Это позволило вообще не париться с отборами. В форме настройки отчета присутствует отбор, который связан с компоновщиком настроек, которые просто копируются во все СКД.  

В результате выполнения обеих СКД формируются две таблицы значений: "Продажи" и "Себестоимость". 

Далее для каждой таблицы выполняется обход всех строк обычным циклом, и производятся все необходимые манипуляции по переоценке. Для переоценки используется обычное обращение к виртуальной таблице среза последних значений, регистра курсов валют, методом «ПолучитьПоследнее». Было так же опробовано выполнять эту операцию запросом, но это оказалось менее производительным решением. Поэтому я от него отказался. Хочется отметить, что этот пункт выполняется дольше всего, но не существенно дольше, чем запрос, к доработанному регистру курсов. Причем на столько, что я даже не ожидал - на 38 сек. Весь проход таблицы обычным циклом «Для каждого», и переоценка всей таблицы в запросе, выполнились, с разницей в 38 сек.

После этого у меня исчезли все сомнения по поводу красивых и функциональных решений.  После того как с таблицами были произведены все необходимые манипуляции, помещаем их в менеджер временных таблиц и строим запрос к временным таблицам.

В запросе объединяем эти таблицы, как это сделано в запросе из отчета валовая прибыль и полученный результат выгружаем в сводную таблицу значений, предварительно почистив кэш и закрыв, менеджер ВТ.

Дальше обычным запросом выбираем оплаты из регистра ВзаиморасчетыСКонтрагентами и выгружаем их в таблицу значений «Оплаты».

После этого, передаем сводную таблицу и таблицу оплат в процедуру, которая выполняет расчет оплаченных отгрузок, и возвращает таблицу, в которой содержатся только полностью оплаченные продажи.

Данные из этой таблицы загружаются в основную СКД, в которой рассчитывается валовая прибыль и формируется внешний вид отчета. 

Хочу немного прояснить, что происходит, когда определяются оплаченные продажи. Проще всего будет показать на простом примере, итак:

Дата Хоз. операция Сумма Валюта
10.01.2014 продажа 20 000.00 руб.
11.01.2014 оплата 1 15 000.00 руб.
12.01.2014 оплата 2   5 000.00 руб.

 

Имеем две оплаты, которые вместе, полностью погашают продажу. 

То, что алгоритм распределяет эти оплаты на отгрузку по методу FIFI, я думаю и так понято, поэтому на этом моменте я останавливаться не буду. Только отмечу, что алгоритм выполнит переоценку суммы продажи частями. Одна часть на 15 000 переоценится по курсу на дату 11.01.2014, вторая часть на 5 000 по курсу на дату 12.01.2014. Но это еще не все, дальше интереснее.  

Основная суть заключается в том, что отчет формируется до номенклатуры. А как распределить частичную оплату на номенклатуру? Какая номенклатура в этом случае оплачена, а какая нет?

Так вот алгоритм считает, что вся номенклатура оплачена, но не на все количество. В процессе вычислений алгоритм рассчитывает коэффициент. Это отношение суммы платежа, к сумме продажи (15 000 / 20 000 = 0,75 и 5 000 / 20 000 = 0,25 соответственно). Получается, что программа будет знать, что накладная оплачена двумя платежами на 75% и 25%. Дальше алгоритм перемножит каждую продажу (сумма количество, себестоимость) на каждый коэффициент (0,75 и 0,25). Каждый из полученных результатов будет пересчитан в валюту отчета по своему курсу, и только после этого вся сводная таблица будет помещена в СКД, где разложенные строки свернутся по, выбранным пользователем, группировкам.

Как это ни странно, но отчет формируется примерно столько времени, сколько и при выполнении запросом, с доработкой регистра курсов валют, и несравненно быстрее, чем стандартный отчет Продажи по оплате. При том что в отчете Продажи по оплате, оплаты выбираются только за период формирования отчета. а в моем - от начала ведения учета в базе, до даты окончания отчета. К тому же появились явные преимущества - это универсальность и простота отладки и доработки

На разработку потрачено 6 рабочих дней или 50 часов

В принципе можно попытаться реализовать получение и распределение оплат не процедурой, а запросом, но это будет позже. А пока у меня все.

 

P. S.

На третий день разработки у меня был комичный случай. Вечером второго дня я лег спать около полуночи, сразу после окончания одного из вариантов этого отчета с чувством полной моральной удовлетворенности. Ночью мне приснился сон, в котором я показываю этот отчет пользователю. Пользователь формирует отчет по контрагенту JBS, взаиморасчеты с которым ведутся в USD, и находит, что сумма продажи рассчитывается не правильно. Тогда мы с пользователем выписываем все исходные данные на бумагу и находим ошибку в алгоритме переоценки, которую я описал выше в примере. Проснулся я в 6 утра, в состоянии головняка и сразу двинулся к компьютеру. Запустил 1С, сформировал отчет и увидел именно ту же ошибку, с теми же данными и у этого же контрагента, только причину я уже знал....

 

 

 

 

 

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Текст общего модуля
.txt 9,21Kb
20.05.14
18
.txt 1 9,21Kb 18 Скачать

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Владимир Кравчук (Power Team) (krava_vlad) 21.05.14 02:45
недавно делал что то подобное.

Нужно было менеджерам насчитать премию от заказов которые отгружены и оплачены, даже если частично, главное условие премия начисляется от валовой прибыли по оплаченой части на которую есть отгрузка.

Если заказ на 10 000
отгрузка на 6 000
Оплата на 4 000
Валовая прибыль по отгрузке 1 500

то есть премия начисляется от 1000 грн.

Так пожелания чтобы сверить расчеты по вашему отчету было бы не плохо в отчет добавить показатели отгружено, оплачено, возврат оплаты, возврат отгрузки.

А как ваш отчет посчитает прибыль если будут такие условия
Если заказ на 10 000
отгрузка на 6 000
Оплата на 8 000
Валовая прибыль по отгрузке 1 500


2. script Мальчинко (script) 21.05.14 03:37
Отчет выводит только полностью оплаченные отгрузки. Продажи проверяются за период отчета, оплата проверяется за весь период (без ограничений)
Сумма заказа не учитывается, но сам заказ участвует в распределении оплаты по отгрузкам.
Если было продано на 10 000, а возвращено на 2 000, тогда программа посчитает что продано на 8 000.
Если отчет обнаружит оплату по данному контрагенту + договору + заказу, на 8000 и более, эти данные будут отображены в отчете, иначе - нет.
Кроме того отчет не расчитывает прибыль по какому то своему алгоритму. Принцып полностью идентичен отчету Валовая прибыль.
Считайте что это отчет валовая прибыль, из которого убрали те продажи, которые не были полностью оплаченными.

В вашем примере оплата больше чем отгрузка, значит данные попадут в отчет. А прибыль расчитается как сумма продажи - себестоимость.
Суть в том что если вы будете делать отчет в валюте, тогда сумма продажи пересчитается по курсу на дату оплаты, а себестоимость на дату партии.
3. юрий гулидов (gull22) 21.05.14 12:53
4. Сергей (ildarovich) 21.05.14 14:04
Задача интересная, комплексная. И распределение оплаты по FIFO и интерполяция курсов валюты. И решение, на мой взгляд, хорошее, практичное. Без вкусовщины. Что-то циклом, что-то запросом, что-то в СКД. Так на самом деле и нужно на практике. Ну а в теории комбинаций вариантов много и для предельного ускорения еще не все перепробованы.
Немного оказалось в кучу все перемешано, но тут, чтобы все по полочкам разложить еще столько же времени, наверное, понадобится.

Если обсуждать отдельно переоценку, то хочу спросить: а не файловая ли база была, когда решения не дождались? В SQL такого, кажется, быть не должно. По своей сути эта задача вроде бы не затратная по времени. Кроме того, этой проблемы бы не было вообще, если бы сначала построить и проиндексировать таблицу курсов валют на все возможные даты заданного диапазона. В худшем случае (без использования индекса по периоду для поиска даты актуального курса) должна быть примерно 730х730/2 трудоемкость. Это существенно меньше, чем 100000х730/2.

Метод распределения оплаченности по номенклатуре нравится. Более того, я предлагаю такой же метод применять для распределения оплаты между документами и если их время совпадает (ссылка).
5. DAnry (DAnry) 21.05.14 20:36
Делал похожий отчет (правда без переоценки). Дополнительно рассчитывалось кимисионное вознаграждение менеджера, как процент от валовой прибыли (каждый менеджер имел свой процент вознаграждения). Результат еще группировался по менеджерам. В моем варианте надо было учитывать и частично оплаченные продажи. Делал следующим образом рассчитывал коэффициент оплаты документа продажи, полную валовую прибыль от продажи, после этого валовую прибыль по оплате - умножением полной валовой прибыли от продажи на коэффициент оплаты. Да, в отчете учитывались возвраты от покупателей и бартерные сделки (взаимозачет между договорами поставки и продажи).
6. script Мальчинко (script) 22.05.14 00:29
(4) ildarovich,

Решил попробовать переписать отчет для того что бы загнать переоценку в запрос с индексированием ВТ.
В первом случае переоценка выполняеться в запросе с применением временных таблиц и индексирования.
Во втором случае результат запроса выгружается в таблицу значений. После этого выполняется обход строк в цикле и переоценка, через стандартную процедуру общего модуля.

База файловая = 4 ГБ. Перед формированием отчета выполнялся перезапуск приложения.
Итоги при формировании отчета за один квартал:
- переоценка в запросе с индексированием = 59 сек.
- переоценка в цикле при обходе результата запроса = 60 сек.
Итоги при формировании отчета за один год:
- переоценка в запросе с индексированием = 6 мин. 35 сек.
- переоценка в цикле при обходе результата запроса = 4 мин 25 сек.
Получили замедление при использовании переоценки в запросе с индексированием ВТ.
Можно добавить в статью как третий облом
7. Сергей (ildarovich) 22.05.14 03:20
(6) Там что-то еще вмешивается. Не может (кажется) построение таблицы валют на каждую дату за два года с индексированием и затем обращение к этой таблице по индексу период-валюта для поиска значения курса отнимать две с лишним минуты. Приведите, пожалуйста, фрагмент запроса построения таблицы курсов валют. Возможно, вы не уловили идею. Сколько он в консоли выполняется?
8. script Мальчинко (script) 22.05.14 09:58
Запрос.Текст = "ВЫБРАТЬ
	|	КурсыВалют.Период КАК Период,
	|	КурсыВалют.Валюта КАК Валюта,
	|	КурсыВалют.Курс,
	|	КурсыВалют.Кратность
	|ПОМЕСТИТЬ КурсыВалют
	|ИЗ
	|	РегистрСведений.КурсыВалют КАК КурсыВалют
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Период,
	|	Валюта
	|;
	|
	|////////////////////////////////////////////////////////////­////////////////////
	|ВЫБРАТЬ
	|	КурсыВалют.Период КАК НачалоПериода,
	|	МИНИМУМ(ЕСТЬNULL(КурсыВалютКопия.Период, ДАТАВРЕМЯ(3999, 1, 1))) КАК КонецПериода,
	|	КурсыВалют.Валюта КАК Валюта,
	|	КурсыВалют.Курс,
	|	КурсыВалют.Кратность
	|ПОМЕСТИТЬ ТаблицаКурсов
	|ИЗ
	|	КурсыВалют КАК КурсыВалют
	|		ЛЕВОЕ СОЕДИНЕНИЕ КурсыВалют КАК КурсыВалютКопия
	|		ПО (КурсыВалютКопия.Период > КурсыВалют.Период)
	|			И (КурсыВалютКопия.Валюта = КурсыВалют.Валюта)
	|
	|СГРУППИРОВАТЬ ПО
	|	КурсыВалют.Период,
	|	КурсыВалют.Валюта,
	|	КурсыВалют.Курс,
	|	КурсыВалют.Кратность
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	НачалоПериода,
	|	КонецПериода,
	|	Валюта
	|;
	|
	|////////////////////////////////////////////////////////////­////////////////////
...Показать Скрыть
9. script Мальчинко (script) 22.05.14 10:02
Вот так использую. Пробовал и через ВНУТРЕННЕЕ СОЕДИНЕНИЕ, особенной разницы не заметил.
	|			РегистрНакопления.ПродажиСебестоимость КАК ПродажиСебестоимость
	|				ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютУпр
	|				ПО (КурсыВалютУпр.Валюта = &ВалютаУпр)
	|					И (КурсыВалютУпр.НачалоПериода <= ПродажиСебестоимость.ДокументОприходования.Дата)
	|					И (КурсыВалютУпр.КонецПериода > ПродажиСебестоимость.ДокументОприходования.Дата)
	|				ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютОтчета
	|				ПО (КурсыВалютОтчета.Валюта = &ВалютаОтчета)
	|					И (КурсыВалютОтчета.НачалоПериода <= ПродажиСебестоимость.ДокументОприходования.Дата)
	|					И (КурсыВалютОтчета.КонецПериода > ПродажиСебестоимость.ДокументОприходования.Дата)
...Показать Скрыть
10. script Мальчинко (script) 22.05.14 10:06
Разницу, которуя описал это разница выполнения всего отчета, просто остальной алгортм не изменяется - изменяется только способ получения продаж. Запросом с переоценкой или двумя разными СКД, выгрузкой в ТЗ и переоценкой в цикле.

В отчете присутствует функция "ПолучитьТаблицуПродаж" - возвращает таблицу значений сформированную по регистрам: "продажи" и "продажи себестоимость" (запрос такой же как в отчете валовая прибыль, но без рассчета рентабельности эффективности и самой валовой прибыли). Внутри процедуры нужно выполнить переоценку сумм из валюты упр. учета в валюту отчета с условиями.
Сумма продажи переоценивается из валюты упр. учета в валюту взаиморасчетов по договору на дату продажи. Себестоимость пересчитывается из валюты. упр. учета в валюту отчета на дату документа оприходования.

Вариант 1: все сделано в одном запросе с индексированной таблицей. Пример постом выше.
Результат (Левое соединение) 2 мин. 08 сек.
Результат (Внутр соединение) 2 мин. 60 сек.

Вариант 2: Отдельно работает СКД с таблицей регистра "ПродажиОбороты". Отдельно - с таблицей регистра "Продажи себестоимость". Далее результаты обеих СКД выгружаются в таблицы значений. Далее обычный цикл и переоценка. Далее, таблицы помещаются в МВТ и объединяются запросом и опаять выгружаются в ТЗ.
Результат 1 мин. 15 сек.

Удивительное рядом...
11. script Мальчинко (script) 22.05.14 11:15
Весь текст запроса, который формирует переоцененную таблицу продаж. (Можно закинуть в консоль отчетов)
ВЫБРАТЬ
	КурсыВалют.Период КАК Период,
	КурсыВалют.Валюта КАК Валюта,
	КурсыВалют.Курс,
	КурсыВалют.Кратность
ПОМЕСТИТЬ КурсыВалют
ИЗ
	РегистрСведений.КурсыВалют КАК КурсыВалют

ИНДЕКСИРОВАТЬ ПО
	Период,
	Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	КурсыВалют.Период КАК НачалоПериода,
	МИНИМУМ(ЕСТЬNULL(КурсыВалютКопия.Период, ДАТАВРЕМЯ(3999, 1, 1))) КАК КонецПериода,
	КурсыВалют.Валюта КАК Валюта,
	КурсыВалют.Курс,
	КурсыВалют.Кратность
ПОМЕСТИТЬ ТаблицаКурсов
ИЗ
	КурсыВалют КАК КурсыВалют
		ЛЕВОЕ СОЕДИНЕНИЕ КурсыВалют КАК КурсыВалютКопия
		ПО (КурсыВалютКопия.Период > КурсыВалют.Период)
			И (КурсыВалютКопия.Валюта = КурсыВалют.Валюта)

СГРУППИРОВАТЬ ПО
	КурсыВалют.Период,
	КурсыВалют.Валюта,
	КурсыВалют.Курс,
	КурсыВалют.Кратность

ИНДЕКСИРОВАТЬ ПО
	НачалоПериода,
	КонецПериода,
	Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиСебестоимость.Номенклатура КАК Номенклатура,
	ПродажиСебестоимость.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
	ПродажиСебестоимость.ЗаказПокупателя КАК ЗаказПокупателя,
	ВЫБОР
		КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
			ТОГДА ПродажиСебестоимость.ДокументДвижения
		ИНАЧЕ ПродажиСебестоимость.Регистратор
	КОНЕЦ КАК Регистратор,
	ВЫБОР
		КОГДА &ВалютаОтчета = &ВалютаУпр
			ТОГДА ПродажиСебестоимость.Стоимость
		ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
	КОНЕЦ КАК СтоимостьОборот,
	ПродажиСебестоимость.ДокументОприходования.Дата КАК ДокументОприходованияДата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютУпр.Валюта КАК Валюта1,
	КурсыВалютУпр.НачалоПериода,
	КурсыВалютУпр.КонецПериода,
	КурсыВалютОтчета.НачалоПериода КАК НачалоПериода1,
	КурсыВалютОтчета.КонецПериода КАК КонецПериода1
ПОМЕСТИТЬ ТаблицаРегистраПродажиСебестоимость
ИЗ
	РегистрНакопления.ПродажиСебестоимость КАК ПродажиСебестоимость
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютУпр
		ПО (КурсыВалютУпр.Валюта = &ВалютаУпр)
			И (КурсыВалютУпр.НачалоПериода <= ПродажиСебестоимость.ДокументОприходования.Дата)
			И (КурсыВалютУпр.КонецПериода > ПродажиСебестоимость.ДокументОприходования.Дата)
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютОтчета
		ПО (КурсыВалютОтчета.Валюта = &ВалютаОтчета)
			И (КурсыВалютОтчета.НачалоПериода <= ПродажиСебестоимость.ДокументОприходования.Дата)
			И (КурсыВалютОтчета.КонецПериода > ПродажиСебестоимость.ДокументОприходования.Дата)
ГДЕ
	ПродажиСебестоимость.Период МЕЖДУ &НачПериода И &КонПериода

СГРУППИРОВАТЬ ПО
	ПродажиСебестоимость.Номенклатура,
	ПродажиСебестоимость.ХарактеристикаНоменклатуры,
	ПродажиСебестоимость.ЗаказПокупателя,
	ВЫБОР
		КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
			ТОГДА ПродажиСебестоимость.ДокументДвижения
		ИНАЧЕ ПродажиСебестоимость.Регистратор
	КОНЕЦ,
	ВЫБОР
		КОГДА &ВалютаОтчета = &ВалютаУпр
			ТОГДА ПродажиСебестоимость.Стоимость
		ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
	КОНЕЦ,
	ПродажиСебестоимость.ДокументОприходования.Дата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютУпр.Валюта,
	КурсыВалютУпр.НачалоПериода,
	КурсыВалютУпр.КонецПериода,
	КурсыВалютОтчета.НачалоПериода,
	КурсыВалютОтчета.КонецПериода

ИНДЕКСИРОВАТЬ ПО
	ДокументОприходованияДата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютОтчета.НачалоПериода,
	КурсыВалютОтчета.КонецПериода,
	КурсыВалютУпр.НачалоПериода,
	КурсыВалютУпр.КонецПериода,
	КурсыВалютУпр.Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиОбороты.Проект КАК Проект,
	ПродажиОбороты.Подразделение КАК Подразделение,
	ПродажиОбороты.Контрагент КАК Покупатель,
	ПродажиОбороты.ДоговорКонтрагента КАК ДоговорПокупателя,
	ПродажиОбороты.Номенклатура КАК Номенклатура,
	ПродажиОбороты.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
	ПродажиОбороты.ЗаказПокупателя КАК ЗаказПокупателя,
	ПродажиОбороты.Организация КАК Организация,
	ПродажиОбороты.Регистратор КАК Регистратор,
	ПродажиОбороты.Период КАК Период,
	ПродажиОбороты.КоличествоОборот КАК Количество,
	ПродажиОбороты.КоличествоОборот * ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) / ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаДляОтчетов.Коэффициент, 1) КАК КоличествоЕдиницОтчетов,
	ПродажиОбороты.КоличествоОборот * ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоБазовыхЕдиниц,
	ПродажиОбороты.СтоимостьОборот КАК Стоимость,
	ПродажиОбороты.НДСОборот КАК СтоимостьНДС,
	ПродажиОбороты.СтоимостьОборот + ПродажиОбороты.НДСОборот КАК СтоимостьСНДС,
	ПродажиОбороты.СтоимостьОборот + ПродажиОбороты.НДСОборот КАК СтоимостьВВалютеДоговора,
	ЕСТЬNULL(ТаблицаРегистраПродажиСебестоимость.СтоимостьОборот, 0) КАК Себестоимость
ПОМЕСТИТЬ ПродажиИСебестоимость
ИЗ
	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, {(Организация).* КАК Организация, (Проект).* КАК Проект, (Подразделение).* КАК Подразделение, (Контрагент).* КАК Покупатель, (ДоговорКонтрагента).* КАК ДоговорПокупателя, (ЗаказПокупателя).* КАК ЗаказПокупателя}) КАК ПродажиОбороты
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаРегистраПродажиСебестоимость КАК ТаблицаРегистраПродажиСебестоимость
		ПО (ТаблицаРегистраПродажиСебестоимость.Номенклатура = ПродажиОбороты.Номенклатура)
			И (ТаблицаРегистраПродажиСебестоимость.ХарактеристикаНоменклатуры = ПродажиОбороты.ХарактеристикаНоменклатуры)
			И (ТаблицаРегистраПродажиСебестоимость.ЗаказПокупателя = ПродажиОбороты.ЗаказПокупателя)
			И (ТаблицаРегистраПродажиСебестоимость.Регистратор = ПродажиОбороты.Регистратор)

ИНДЕКСИРОВАТЬ ПО
	Номенклатура,
	ХарактеристикаНоменклатуры,
	ЗаказПокупателя,
	Регистратор
;

////////////////////////////////////////////////////////////­////////////////////
УНИЧТОЖИТЬ ТаблицаРегистраПродажиСебестоимость
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВложенныйЗапрос.Проект,
	ВложенныйЗапрос.Подразделение,
	ВложенныйЗапрос.Покупатель,
	ВложенныйЗапрос.ДоговорПокупателя КАК ДоговорПокупателя,
	ВложенныйЗапрос.Номенклатура,
	ВложенныйЗапрос.ХарактеристикаНоменклатуры,
	ВложенныйЗапрос.ЗаказПокупателя,
	ВложенныйЗапрос.Организация,
	ВложенныйЗапрос.Регистратор,
	ВложенныйЗапрос.Период КАК Период,
	СУММА(ВложенныйЗапрос.Количество) КАК Количество,
	СУММА(ВложенныйЗапрос.КоличествоЕдиницОтчетов) КАК КоличествоЕдиницОтчетов,
	СУММА(ВложенныйЗапрос.КоличествоБазовыхЕдиниц) КАК КоличествоБазовыхЕдиниц,
	СУММА(ВложенныйЗапрос.Стоимость) КАК Стоимость,
	СУММА(ВложенныйЗапрос.СтоимостьНДС) КАК СтоимостьНДС,
	СУММА(ВложенныйЗапрос.СтоимостьСНДС) КАК СтоимостьСНДС,
	СУММА(ВложенныйЗапрос.СтоимостьВВалютеДоговора * (КурсыВалютУпр.Курс * КурсыВалютВзаиморасчетов.Кратность) / (КурсыВалютВзаиморасчетов.Курс * КурсыВалютУпр.Кратность)) КАК СтоимостьВВалютеДоговора,
	СУММА(ВложенныйЗапрос.Себестоимость) КАК Себестоимость,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕНЬ) КАК ПериодДень,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, НЕДЕЛЯ) КАК ПериодНеделя,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕКАДА) КАК ПериодДекада,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, МЕСЯЦ) КАК ПериодМесяц,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, КВАРТАЛ) КАК ПериодКвартал,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ПОЛУГОДИЕ) КАК ПериодПолугодие,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ГОД) КАК ПериодГод
ПОМЕСТИТЬ ПродажиИСебестоимостьУровень2
ИЗ
	ПродажиИСебестоимость КАК ВложенныйЗапрос
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютВзаиморасчетов
		ПО ВложенныйЗапрос.ДоговорПокупателя.ВалютаВзаиморасчетов = КурсыВалютВзаиморасчетов.Валюта
			И (КурсыВалютВзаиморасчетов.НачалоПериода <= ВложенныйЗапрос.Период)
			И (КурсыВалютВзаиморасчетов.КонецПериода > ВложенныйЗапрос.Период)
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютУпр
		ПО (КурсыВалютУпр.Валюта = &ВалютаУпр)
			И (КурсыВалютУпр.НачалоПериода <= ВложенныйЗапрос.Период)
			И (КурсыВалютУпр.КонецПериода > ВложенныйЗапрос.Период)

СГРУППИРОВАТЬ ПО
	ВложенныйЗапрос.Проект,
	ВложенныйЗапрос.Подразделение,
	ВложенныйЗапрос.Покупатель,
	ВложенныйЗапрос.ДоговорПокупателя,
	ВложенныйЗапрос.Номенклатура,
	ВложенныйЗапрос.ХарактеристикаНоменклатуры,
	ВложенныйЗапрос.ЗаказПокупателя,
	ВложенныйЗапрос.Период,
	ВложенныйЗапрос.Организация,
	ВложенныйЗапрос.Регистратор

ИМЕЮЩИЕ
	(СУММА(ВложенныйЗапрос.Количество) <> 0
		ИЛИ СУММА(ВложенныйЗапрос.Стоимость) <> 0
		ИЛИ СУММА(ЕСТЬNULL(ВложенныйЗапрос.Себестоимость, 0)) <> 0)

ИНДЕКСИРОВАТЬ ПО
	Период,
	ДоговорПокупателя
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиИСебестоимостьУровень2.Проект,
	ПродажиИСебестоимостьУровень2.Подразделение,
	ПродажиИСебестоимостьУровень2.Покупатель,
	ПродажиИСебестоимостьУровень2.ДоговорПокупателя,
	ПродажиИСебестоимостьУровень2.Номенклатура,
	ПродажиИСебестоимостьУровень2.ХарактеристикаНоменклатуры,
	ПродажиИСебестоимостьУровень2.ЗаказПокупателя,
	ПродажиИСебестоимостьУровень2.Организация,
	ПродажиИСебестоимостьУровень2.Регистратор,
	ПродажиИСебестоимостьУровень2.Период,
	ПродажиИСебестоимостьУровень2.Количество,
	ПродажиИСебестоимостьУровень2.КоличествоЕдиницОтчетов,
	ПродажиИСебестоимостьУровень2.КоличествоБазовыхЕдиниц,
	ПродажиИСебестоимостьУровень2.Стоимость,
	ПродажиИСебестоимостьУровень2.СтоимостьНДС,
	ПродажиИСебестоимостьУровень2.СтоимостьСНДС,
	ВЫРАЗИТЬ(ПродажиИСебестоимостьУровень2.СтоимостьВВалютеДоговора КАК ЧИСЛО(15, 2)) КАК СтоимостьВВалютеДоговора,
	ПродажиИСебестоимостьУровень2.Себестоимость,
	ПродажиИСебестоимостьУровень2.ПериодДень,
	ПродажиИСебестоимостьУровень2.ПериодНеделя,
	ПродажиИСебестоимостьУровень2.ПериодДекада,
	ПродажиИСебестоимостьУровень2.ПериодМесяц,
	ПродажиИСебестоимостьУровень2.ПериодКвартал,
	ПродажиИСебестоимостьУровень2.ПериодПолугодие,
	ПродажиИСебестоимостьУровень2.ПериодГод
ИЗ
	ПродажиИСебестоимостьУровень2 КАК ПродажиИСебестоимостьУровень2
...Показать Скрыть
12. Сергей (ildarovich) 22.05.14 16:14
(11) script, я имел ввиду другой метод, вот его примерная реализация на примере вашего запроса. Главное в том, чтобы для каждой нужной даты заранее посчитать курсы. Чтобы не использовать неравенство в соединении. Неравенство в соединении в файловой базе - это наверняка всегда поиск по всей таблице без помощи индексов.

ВЫБРАТЬ РАЗЛИЧНЫЕ
	ДниПродаж.Период
ПОМЕСТИТЬ ДниПродаж
ИЗ
	РегистрНакопления.ПродажиСебестоимость.Обороты(, , День, ) КАК ДниПродаж
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ДниПродаж.Период,
	КурсыВалют.Валюта КАК Валюта,
	МАКСИМУМ(КурсыВалют.Период) КАК ПериодКурса
ПОМЕСТИТЬ ДниКурсовПродаж
ИЗ
	ДниПродаж КАК ДниПродаж
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
		ПО (КурсыВалют.Период <= ДниПродаж.Период)

СГРУППИРОВАТЬ ПО
	ДниПродаж.Период,
	КурсыВалют.Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ДниКурсовПродаж.Период,
	ДниКурсовПродаж.Валюта,
	ЕСТЬNULL(КурсыВалют.Курс, 0) КАК Курс,
	ЕСТЬNULL(КурсыВалют.Кратность, 1) КАК Кратность
ПОМЕСТИТЬ ТаблицаКурсов
ИЗ
	ДниКурсовПродаж КАК ДниКурсовПродаж
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
		ПО ДниКурсовПродаж.ПериодКурса = КурсыВалют.Период
ИНДЕКСИРОВАТЬ ПО
	Период,
	Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиСебестоимость.Номенклатура КАК Номенклатура,
	ПродажиСебестоимость.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
	ПродажиСебестоимость.ЗаказПокупателя КАК ЗаказПокупателя,
	ВЫБОР
		КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
			ТОГДА ПродажиСебестоимость.ДокументДвижения
		ИНАЧЕ ПродажиСебестоимость.Регистратор
	КОНЕЦ КАК Регистратор,
	ВЫБОР
		КОГДА &ВалютаОтчета = &ВалютаУпр
			ТОГДА ПродажиСебестоимость.Стоимость
		ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
	КОНЕЦ КАК СтоимостьОборот,
	ПродажиСебестоимость.ДокументОприходования.Дата КАК ДокументОприходованияДата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютУпр.Валюта КАК Валюта1,
	КурсыВалютУпр.Период,
	КурсыВалютОтчета.Период КАК Период1
ПОМЕСТИТЬ ТаблицаРегистраПродажиСебестоимость
ИЗ
	РегистрНакопления.ПродажиСебестоимость КАК ПродажиСебестоимость
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютУпр
		ПО (КурсыВалютУпр.Валюта = &ВалютаУпр)
			И (КурсыВалютУпр.Период = НАЧАЛОПЕРИОДА(ПродажиСебестоимость.ДокументОприходования.Дата, ДЕНЬ))
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютОтчета
		ПО (КурсыВалютОтчета.Валюта = &ВалютаОтчета)
			И (КурсыВалютОтчета.Период = НАЧАЛОПЕРИОДА(ПродажиСебестоимость.ДокументОприходования.Дата, ДЕНЬ))
ГДЕ
	ПродажиСебестоимость.Период МЕЖДУ &НачалоПериода И &КонецПериода

СГРУППИРОВАТЬ ПО
	ПродажиСебестоимость.Номенклатура,
	ПродажиСебестоимость.ХарактеристикаНоменклатуры,
	ПродажиСебестоимость.ЗаказПокупателя,
	ВЫБОР
		КОГДА ПродажиСебестоимость.ДокументДвижения <> НЕОПРЕДЕЛЕНО
			ТОГДА ПродажиСебестоимость.ДокументДвижения
		ИНАЧЕ ПродажиСебестоимость.Регистратор
	КОНЕЦ,
	ВЫБОР
		КОГДА &ВалютаОтчета = &ВалютаУпр
			ТОГДА ПродажиСебестоимость.Стоимость
		ИНАЧЕ ПродажиСебестоимость.Стоимость * КурсыВалютУпр.Курс * КурсыВалютОтчета.Кратность / (КурсыВалютОтчета.Курс * КурсыВалютУпр.Кратность)
	КОНЕЦ,
	ПродажиСебестоимость.ДокументОприходования.Дата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютУпр.Валюта,
	КурсыВалютУпр.Период,
	КурсыВалютОтчета.Период

ИНДЕКСИРОВАТЬ ПО
	ДокументОприходованияДата,
	КурсыВалютОтчета.Валюта,
	КурсыВалютОтчета.Период,
	КурсыВалютУпр.Период,
	КурсыВалютУпр.Валюта
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиОбороты.Проект КАК Проект,
	ПродажиОбороты.Подразделение КАК Подразделение,
	ПродажиОбороты.Контрагент КАК Покупатель,
	ПродажиОбороты.ДоговорКонтрагента КАК ДоговорПокупателя,
	ПродажиОбороты.Номенклатура КАК Номенклатура,
	ПродажиОбороты.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
	ПродажиОбороты.ЗаказПокупателя КАК ЗаказПокупателя,
	ПродажиОбороты.Организация КАК Организация,
	ПродажиОбороты.Регистратор КАК Регистратор,
	ПродажиОбороты.Период КАК Период,
	ПродажиОбороты.КоличествоОборот КАК Количество,
	ПродажиОбороты.КоличествоОборот * ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) / ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаДляОтчетов.Коэффициент, 1) КАК КоличествоЕдиницОтчетов,
	ПродажиОбороты.КоличествоОборот * ЕСТЬNULL(ПродажиОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоБазовыхЕдиниц,
	ПродажиОбороты.СтоимостьОборот КАК Стоимость,
	ПродажиОбороты.НДСОборот КАК СтоимостьНДС,
	ПродажиОбороты.СтоимостьОборот + ПродажиОбороты.НДСОборот КАК СтоимостьСНДС,
	ПродажиОбороты.СтоимостьОборот + ПродажиОбороты.НДСОборот КАК СтоимостьВВалютеДоговора,
	ЕСТЬNULL(ТаблицаРегистраПродажиСебестоимость.СтоимостьОборот, 0) КАК Себестоимость
ПОМЕСТИТЬ ПродажиИСебестоимость
ИЗ
	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, {(Организация).* КАК Организация, (Проект).* КАК Проект, (Подразделение).* КАК Подразделение, (Контрагент).* КАК Покупатель, (ДоговорКонтрагента).* КАК ДоговорПокупателя, (ЗаказПокупателя).* КАК ЗаказПокупателя}) КАК ПродажиОбороты
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаРегистраПродажиСебестоимость КАК ТаблицаРегистраПродажиСебестоимость
		ПО (ТаблицаРегистраПродажиСебестоимость.Номенклатура = ПродажиОбороты.Номенклатура)
			И (ТаблицаРегистраПродажиСебестоимость.ХарактеристикаНоменклатуры = ПродажиОбороты.ХарактеристикаНоменклатуры)
			И (ТаблицаРегистраПродажиСебестоимость.ЗаказПокупателя = ПродажиОбороты.ЗаказПокупателя)
			И (ТаблицаРегистраПродажиСебестоимость.Регистратор = ПродажиОбороты.Регистратор)

ИНДЕКСИРОВАТЬ ПО
	Номенклатура,
	ХарактеристикаНоменклатуры,
	ЗаказПокупателя,
	Регистратор
;

////////////////////////////////////////////////////////////­////////////////////
УНИЧТОЖИТЬ ТаблицаРегистраПродажиСебестоимость
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВложенныйЗапрос.Проект,
	ВложенныйЗапрос.Подразделение,
	ВложенныйЗапрос.Покупатель,
	ВложенныйЗапрос.ДоговорПокупателя КАК ДоговорПокупателя,
	ВложенныйЗапрос.Номенклатура,
	ВложенныйЗапрос.ХарактеристикаНоменклатуры,
	ВложенныйЗапрос.ЗаказПокупателя,
	ВложенныйЗапрос.Организация,
	ВложенныйЗапрос.Регистратор,
	ВложенныйЗапрос.Период КАК Период,
	СУММА(ВложенныйЗапрос.Количество) КАК Количество,
	СУММА(ВложенныйЗапрос.КоличествоЕдиницОтчетов) КАК КоличествоЕдиницОтчетов,
	СУММА(ВложенныйЗапрос.КоличествоБазовыхЕдиниц) КАК КоличествоБазовыхЕдиниц,
	СУММА(ВложенныйЗапрос.Стоимость) КАК Стоимость,
	СУММА(ВложенныйЗапрос.СтоимостьНДС) КАК СтоимостьНДС,
	СУММА(ВложенныйЗапрос.СтоимостьСНДС) КАК СтоимостьСНДС,
	СУММА(ВложенныйЗапрос.СтоимостьВВалютеДоговора * (КурсыВалютУпр.Курс * КурсыВалютВзаиморасчетов.Кратность) / (КурсыВалютВзаиморасчетов.Курс * КурсыВалютУпр.Кратность)) КАК СтоимостьВВалютеДоговора,
	СУММА(ВложенныйЗапрос.Себестоимость) КАК Себестоимость,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕНЬ) КАК ПериодДень,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, НЕДЕЛЯ) КАК ПериодНеделя,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕКАДА) КАК ПериодДекада,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, МЕСЯЦ) КАК ПериодМесяц,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, КВАРТАЛ) КАК ПериодКвартал,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ПОЛУГОДИЕ) КАК ПериодПолугодие,
	НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ГОД) КАК ПериодГод
ПОМЕСТИТЬ ПродажиИСебестоимостьУровень2
ИЗ
	ПродажиИСебестоимость КАК ВложенныйЗапрос
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютВзаиморасчетов
		ПО ВложенныйЗапрос.ДоговорПокупателя.ВалютаВзаиморасчетов = КурсыВалютВзаиморасчетов.Валюта
			И (КурсыВалютВзаиморасчетов.Период = НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕНЬ))
		ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаКурсов КАК КурсыВалютУпр
		ПО (КурсыВалютУпр.Валюта = &ВалютаУпр)
			И (КурсыВалютУпр.Период = НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Период, ДЕНЬ))

СГРУППИРОВАТЬ ПО
	ВложенныйЗапрос.Проект,
	ВложенныйЗапрос.Подразделение,
	ВложенныйЗапрос.Покупатель,
	ВложенныйЗапрос.ДоговорПокупателя,
	ВложенныйЗапрос.Номенклатура,
	ВложенныйЗапрос.ХарактеристикаНоменклатуры,
	ВложенныйЗапрос.ЗаказПокупателя,
	ВложенныйЗапрос.Период,
	ВложенныйЗапрос.Организация,
	ВложенныйЗапрос.Регистратор

ИМЕЮЩИЕ
	(СУММА(ВложенныйЗапрос.Количество) <> 0
		ИЛИ СУММА(ВложенныйЗапрос.Стоимость) <> 0
		ИЛИ СУММА(ЕСТЬNULL(ВложенныйЗапрос.Себестоимость, 0)) <> 0)

ИНДЕКСИРОВАТЬ ПО
	Период,
	ДоговорПокупателя
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ПродажиИСебестоимостьУровень2.Проект,
	ПродажиИСебестоимостьУровень2.Подразделение,
	ПродажиИСебестоимостьУровень2.Покупатель,
	ПродажиИСебестоимостьУровень2.ДоговорПокупателя,
	ПродажиИСебестоимостьУровень2.Номенклатура,
	ПродажиИСебестоимостьУровень2.ХарактеристикаНоменклатуры,
	ПродажиИСебестоимостьУровень2.ЗаказПокупателя,
	ПродажиИСебестоимостьУровень2.Организация,
	ПродажиИСебестоимостьУровень2.Регистратор,
	ПродажиИСебестоимостьУровень2.Период,
	ПродажиИСебестоимостьУровень2.Количество,
	ПродажиИСебестоимостьУровень2.КоличествоЕдиницОтчетов,
	ПродажиИСебестоимостьУровень2.КоличествоБазовыхЕдиниц,
	ПродажиИСебестоимостьУровень2.Стоимость,
	ПродажиИСебестоимостьУровень2.СтоимостьНДС,
	ПродажиИСебестоимостьУровень2.СтоимостьСНДС,
	ВЫРАЗИТЬ(ПродажиИСебестоимостьУровень2.СтоимостьВВалютеДоговора КАК ЧИСЛО(15, 2)) КАК СтоимостьВВалютеДоговора,
	ПродажиИСебестоимостьУровень2.Себестоимость,
	ПродажиИСебестоимостьУровень2.ПериодДень,
	ПродажиИСебестоимостьУровень2.ПериодНеделя,
	ПродажиИСебестоимостьУровень2.ПериодДекада,
	ПродажиИСебестоимостьУровень2.ПериодМесяц,
	ПродажиИСебестоимостьУровень2.ПериодКвартал,
	ПродажиИСебестоимостьУровень2.ПериодПолугодие,
	ПродажиИСебестоимостьУровень2.ПериодГод
ИЗ
	ПродажиИСебестоимостьУровень2 КАК ПродажиИСебестоимостьУровень2
...Показать Скрыть


Отлаживать не стал, думаю, идея понятная. Если дальше оптимизировать именно эту часть, то нужно предварительно обсчитать во временной таблице не курсы двух валют на нужную дату, а коэффициент пересчета на эту дату, чтобы не делать двух соединений каждый раз. Нужно ограничить диапазон выборки дат в первом запросе, "различные" не нужны. Еще в нижней части запроса нужно бы разобраться (я подробно не вникал) - там индексирование может быть лишним.
13. script Мальчинко (script) 23.05.14 20:56
(12) ildarovich,
Правильно ли я понял?
Суть метода в том, что бы, сначала, подготовить таблицу в которой, заранее, собрать курсы да дату каждой продажи. А потом просто соединятся с ней левым соединением с условием таблицакурсов.период =продажа. период?

Получиться так, что таблица курсов будет иметь записи только за период продаж, а не за весь период до даты окончания отчета. Это хорошая идея - нужно пробовать. Спасибо.
14. Taras Варварич (itar59) 25.06.14 16:29
Хотелось бы уточнить -- валютный пересчет идет по какому курсу: на день оплаты? на день выписки счета? на момент отгрузки?
у нвс эти даты могут очень сильно (до полугода) отличаться
мы бы заказали внедрение, но этот аспект должен быть учтен.
15. Taras Варварич (itar59) 25.06.14 16:30
да, и как насчет оплаты безналом от юр лица? в гривнах
16. Taras Варварич (itar59) 25.06.14 16:33
Попадут ли в отчет товары отгруженные РАНЕЕ отчетного периода, но оплаченные в текущем?
17. script Мальчинко (script) 25.06.14 17:46
В этот не попадут.
На этой неделе я создам новую публикацию, в которой выложу новый отчет, который решает именно эту задачу, а так же описпние, как продолжение истории из этой публикации.
18. script Мальчинко (script) 25.06.14 18:06
(14) itar59,
Пример:
01.01.2014 - оплата = 1000
01.02.2014 - отгрузка = 1500
10.03.2014 - оплата = 500.00

Действия отчета:
Если период в отчете будет установлен такой, в который попадает дата 10.03.2014. данные попадут в отчет
потому что именно в эту дату закрылась сделка.
Сумма отгрузки будет переоценена в валюту отчета следующим образом:
1500 будут разделены на две суммы 1000 и 500 соответственно.
1000 грн переоценится на дату 01.01.2014 г.
500 грн.переоценится на дату 10.03.2014 г.

Здесь нужно пояснить что в отчет выводится и номенклатура, по этому разделить отгрузку нужно до номенклатуры.
Но в оплатах нет данных какая номенклатура оплачена. Для этого применяется спец. прием.
Считается что каждый платеж оплачивает всю номенклатуру из накладной, но не на все количество.
Т.е. если в накладной будет одна строка с количеством = 1 шт. на сумму 1500.00, тогда программа составит соотношения по количеству платежей
500 :1500 = 0,33333333 = 500 * курс на (01.01.2014)
1000:1500 = 0,66666666 = 1000 * курс на (10.03.2014)
В дальнейшем количество (1 шт.) и сумма (1500.00) будет умножена на эти коэффициенты по порядку (по дате платежа), Полученный результат и будет означать какая часть накладной оплачена. Именно эта часть будет переоценена на дату платежа.

Себестоимость переоценится на дату партии. Если партий было несколько, тогда на дату каждой партии отдельно.
Остальные показатели - как обычно.

19. script Мальчинко (script) 25.06.14 18:18
Как программ определяет когда сделка полностью закрылась.

1. В отчете указывается некоторый период (01.01.2014 - 10.03.2014)
2. На дату начала отчета (01.01.2014), порграмма получает сальдо по контрагенту, договору, сделке(если взаиморасчеы ведутся по заказам/счетам - иначе по пустой сделке). Данные выгружаются в таблицу.
3. Далее, для комбинации Контрагент\Договор\Сделка, программа получает все "Оплаты", по контрагентам, договорам и сделкам из таблицы в п.1. (за все периоды работы в программе по дату окончания отчета 10.03.2014) - это движения в рег. взаиморасчеты со знком "минус".
4. Для комбинации Контрагент\Договор\Заказ покупателя\Номенклатура\ .... программа получает все "Продажи" (за все периоды работы в программе по дату окончания отчета 10.03.2014) - это движения в регистре "Продажи", по тому же принципу.
Пункты 1-4 выполняются одним пакетны запросом и индексированием.
5. После получения данных об отгрузках и оплтах, порграмма выполняет погашение отгрузок оплатами по ФИФО.
6. Те даты, когда очередная отгрузка была полностью погашена очередной оплатой, программа запоминает как "ДатуЗакрытия"
7. Кроме погашения по ФИФО алгоритм выполняет переоценку сумм и заполняет итоговую таблицу, которая содержит полностью оплаченные продажи, а также дату когда эта продажа была полностью оплачена (колонка "Дата закрытия").
8. Итоговая таблица подается процессору СКД, в котором наложен фильтр на колонку "Дата закрытия", данные которого (фильтра) беруться из даты начала и окончания отчета.
20. Taras Варварич (itar59) 26.06.14 12:15
Спасибо за ясный подробный ответ.
в принципе мы готовы купить, когда появится вариант, о котором вы упоминали ранее..

как насчет оплаты безналом от юр лица? в гривнах
21. Taras Варварич (itar59) 26.06.14 13:50
- в регистр "Продажи себестоимость программа записывает себестоимость проданного товара, которая не пересчитывается, а берется, так сказать "готовой", в валюте упр. учета, из регистра учета партий. По сути задачи, данные в регистре "Продажи себестоимость" всегда переоценены в валюту упр. учета по курсу, на дату документа оприходования.

по существу (у нас товар импортный) мы будем иметь таможенную стоимость?
22. script Мальчинко (script) 26.06.14 15:14
(21) itar59,
КАК РАБОТАЕТ УТП, УТ, УПП.
Пример:
- Приход - 01.01.2014 - Товар1 - Прих. накл1 - 1шт. - 2000грн.
- Приход - 01.02.2014 - Товар1 - Прих. накл2 - 1шт - 200 EUR.
- Продажа 10.02.2014 - Товар1 - Расх. накл1 - 2шт. - 5000грн.
Валюта упр. учета установлена. USD.
// ПОКУПКА
При проведении прих. накл. в регистры Продажи и продажи себестоимость ничего не записывается.
но записывается в регистр Партиит товаров упр. учет.
- Приход - Прих. накл1 - товар1 - 1шт. = (2000грн / курс USD * кратность) на 01.01.2014 (дата прих1)
- Приход - Прих. накл2 - товар1 - 1шт. = (200EUR пересчитается по крос курсу в USD) на 01.02.2014 (дата прих2)
В итоге в регистре партии товаров в упр. учете будет содержаться информация о двух партиях товара1 в USD.
//ПРОДАЖА
При проведении расх. накл1, программа записывает в регистры:
1.Партии товаров(упр.): списание товаров будет произмедено по готовой, рассчитаной ранее USD стоимости.
- Расход - Прих. накл1 - товар1 - 1шт. = (2000грн / курс USD * кратность) на 01.01.2014
- Расход - Прих. накл2 - товар1 - 1шт. = (200EUR пересчитается по крос курсу в USD) на 01.02.2014
2.Продажи: Товар1 - 2шт. (5000грн. * курс USD / кратность) на дату продажи (10.02.2014)
3.Продажи себестоимость: здесь интереснее...
Программа просто скопирует, те движения, которые сделает расх.накл1 в регистре Партии товаров упр. учет. в п.1.

// КАК СРАБОТАЕТ МОЙ ОТЧЕТ ПРИ ОПРЕДЕЛЕНИИ СЕБЕСТОИМОСТИ, ЕСЛИ:
- валюта отчета = валюте упр. учета (USD).
В этом случае отчет ничего пересчитывать не будет, а возьмет готовые цыфры и только просумирует себестоимость 2шт.
- валюта отчета отличается от валюты упр. учета (напр. EUR)
В этом случает отчет пересчитает сумму каждой партии из валюты упр. учета USD в валюту отчета EUR по крос-курсу на дату партии. После этого полученные себестоимости так же суммируются.

Все это выполняется одним пакетным запросом с индексированием.
После выполнения запроса данные выгружаются в таюблицу значений "Таблица себестоимости".
После этого похожим образов формируется таблица "Продажи"
Далее две таблицы загружаются в МенеджерВременныхТаблиц и передаются в запрос, в которым таблицы себестоимости и продаж объединяются, аналогично тому, как это сделано в отчете "Валовая прибиль".
После выполнения такого запроса, данные из него выгружаются в таблицу значений "Сводная таблица", по которой и происходит погашение оплатами по методу ФИФО. По сути сводная таблица содержит все продажи и дальнейшая обработка, удаляет из нее записи о продажах, которые не оплачены. В итоге в "сводной таблице" остаются только оплаченные продажи, по тем контрагентам, по которым было сальдо на начало отчета, и по тем, по котовым были движения в периоде формирования отчета.
23. Taras Варварич (itar59) 08.07.14 18:20
(17(17) script,
каковы перспективы доработаной версии?
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа