[ОТВЕТИТЬ]
16.04.2018 11:04
m1n1mal
 
Приветствую.
Есть необходимость в создании отчета, описание отчета во вложении.
16.04.2018 14:25
Starter
 
Как тут принято, немного покритикую :)

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

т.е. хочется, чтобы пользователь в БА сперва выбрал произвольный период (пусть за последний год). Затем рассчитал прибыль. затем еще раз выбрал уже другой период и внутри этого второго выбранного периода рассчитал число дней, когда товара не было ?

А то, что в бизнес анализе выбрать можно только один период - не смущает ?
16.04.2018 14:37
m1n1mal
 
Согласен, задать только можно один период. По этому и обратился к Вам, возможно ли такое сделать.
Если использовать всегда только один период, то ABC и XYZ будут рассчитаны только в его рамках. Тут цель рассчитать анализ например за 6 месяцев, так менеджеры хотят получить более точный данные ABC и XYZ.
16.04.2018 14:51
Starter
 
Два периода указать в БА не получится.
Можно сделать с помощью fast reports, либо с помощью другого построителя отчётов.
либо можно сделать в БА, но смириться с тем, что второй период будет всегда жестко задан (ну, к примеру, 14 дней от текущего дня).
либо в БА сделать 2 или 3 задачи и это число дней "зашить" в код. будут 3 задачи с числом дней =7, с числом дней = 14, с числом дней = 28.

И, потом - как доходность считается ? то, что на скриншоте в "ТЗ" - как-то совсем не то, что, видимо, хотелось.
т.е. алгоритм, насколько я понял следующий - пользователь выбирает большой период (365 дней от тек. числа). считается доход за эти 365 дней. пусть будет 365 р. т.е. в среднем 1 руб в день.
затем берется маленький период (пусть будет 7 дней от тек числа). Доход за этот период как то участвует в расчётах ? Пусть за этот маленький период 2 дня товара не было.
т.е. упущенный доход = 1*2 = 2 р ?
или упущенный доход = 2* (средний дневной доход за короткий промежуток) ?
16.04.2018 15:02
m1n1mal
 
Большой период нужен только для ABC и XYZ анализа, для других целей мы его не задействуем. Все расчеты мы делаем в рамках малого периода, именно в нем рассчитываем доходность, ищем отсутствие товара в днях и недополученный доход.
16.04.2018 15:35
Starter
 
Варианты -
1. Обратиться в С+ и попросить их сделать доработку бизнес анализа в плане добавления произвольных параметров в фильтр и возможности их дальнейшего использования в формулах. Думаю, многие будут благодарны, если С+ такое сделает.
2. Забить на БА и делать отчёт в том же Fast Reports.
3. Смириться с тем, что один из периодов (по крайней мере, число дней) будет зафиксирован (причем маленький). Делать несколько задач.
4. Пытаться решать задачу сторонними средствами. Разные системы отчетности, скрипты, прочее. К примеру, у нас - есть mail parser. Менеджер, либо кто еще шлет туда письмо с определенными ключевыми словами в заголовке. Парсер разбирает письмо, вычленяет из него параметры, запускает скрипт, параметры передаёт в скрипт. Скрипт - собирает данные из оракла (sql+), преобразует их (в эксель, html), высылает обратно менеджеру. Супермаг не нужен, рдп не нужен, лицензии не тратятся, с почтой работать все более-менее умеют. Либо, как вариант - рассылка тех же отчетов по расписанию.
16.04.2018 15:45
m1n1mal
 
2. Забить на БА и делать отчёт в том же Fast Reports.
3. Смириться с тем, что один из периодов (по крайней мере, число дней) будет зафиксирован (причем маленький). Делать несколько задач.

Если остановится на одном из этих пунктов, какой из них будет менее трудозатратен в реализации?
И сможете ли Вы помочь с формированием данного отчета?
16.04.2018 15:55
Starter
 
менее трудозатратен, разумеется, п. 3. Фактически, там делать особо ничего не нужно, добавить в стандартную задачу несколько полей. Правда, поля основаны на самописных функциях, но самую главную, по вычислению числа дней я уже в другой теме указывал (сылка на тему с функцией), нужно добавить её в базу и всех делов, можно и самостоятельно делать.
п.2 - тут помочь не могу, могу только порекомендовать обратиться по контактам, указанным в заголовке форума, там найдут профессионалов.
16.04.2018 17:43
m1n1mal
 
Функцию я ту пробовал, я тогда и пытался дополнить отчет. У меня к сожалению при использовании этой функции не все данные корректно возвращают значения. Например, параметр с "нулевым остатком" возвращал общее количество дней в периоде, кроме дней когда остаток был отрицательным.
Например, на стенде задал временной диапазон в с 20-07-15 по 10-08-15 (стенд редко в использовании)
Он вернул мне следующие параметры в среднесуточной реализации



(0,07Мб)

А в отчет, с использованием параметра AWhat = 3, он выводит значение 22, т.е. просто общее количество дней, а по идее должен был вывести значение 9.
16.04.2018 19:24
Starter
 
Думаю, что то с отчётом, функцию проверяли неоднократно, пока вроде отрабатывала корректно.
Попробуйте связаться со мной, посмотрю задачу БА, если самим не получается.
Только вот не знаю как - правилами запрещено распространять личную информацию, e-mail выкладывать и всё такое :)
18.04.2018 07:08
Starter
 
Цитата:
m1n1mal Функцию я ту пробовал, я тогда и пытался дополнить отчет. У меня к сожалению при использовании этой функции не все данные корректно возвращают значения. Например, параметр с "нулевым остатком" возвращал общее количество дней в периоде, кроме дней когда остаток был отрицательным.
А можете задачу свою приложить ? То, что сделали ?
18.04.2018 11:28
m1n1mal
 
Да, конечно, отчет во вложении.
https://storage.olegon.ru/supermag/u...04/отчет БА.7z
(0Мб)

Количество дней с продажей, тот что с параметром "1", показывает верно, а вот с параметром "3", отсутствие, показывает к сожалению не верно.
18.04.2018 12:55
Starter
 
А можно скриншоты - фильтр задачи, результат исполнения, с параметром =3 и скрин ССР для карточки с неверным результатом расчёта ? Желательно в фильтре БА только один спорный артикул указать. У нас просто всё отрабатывает корректно.
18.04.2018 15:14
m1n1mal
 
Фильтр


(0,03Мб)

Результат


(0,15Мб)

ССР


(0,06Мб)
18.04.2018 16:52
Starter
 
А можете код выполнить в SQL+ - что вернёт ?

код:
SQL код:
select nvl(sum(nvl(d.quantity0)) over (order by c.dat) + 40) as quantity,
                
nvl(sum(nvl(d.salequantity0)) over (partition by c.dat), 0) as salequantity
              from
                
(select dd.createdatsum(dd.quantity) as quantitysum(dd.salequantity) as salequantity
                 from
                   
(select d.createdatsum(s.quantity) as quantityas salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article '406338' and d.locationto 2
                      
and d.docstate >= and d.createdat between trunc(to_date('09.04.2018','dd.mm.yyyy')) and trunc(to_date('18.04.2018','dd.mm.yyyy'))
                    
group by d.createdat
                    union all
                    select d
.createdatsum(-s.quantity) as quantitysum(decode(d.opcode1, -s.quantity0)) as salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article '406338' and d.locationfrom 2
                      
and d.docstate >= and d.createdat between trunc(to_date('09.04.2018','dd.mm.yyyy')) and trunc(to_date('18.04.2018','dd.mm.yyyy'))
                    
group by d.createdatdd
                 group by dd
.createdatd,
                (
select dt.dat
                 from 
                   
(select trunc(to_date('09.04.2018','dd.mm.yyyy')) - level as dat
                    from dual connect by level 
<= trunc(to_date('18.04.2018','dd.mm.yyyy')) - trunc(to_date('09.04.2018','dd.mm.yyyy')) + 1dtc
              where c
.dat d.createdat(+); 


только поменяв d.locationto = 2 и d.locationfrom = 2 на код выбранного места хранения и в строке over (order by c.dat) + 4 вместо 4 поставить остаток по этой карточке на конец дня 08.04.2018
18.04.2018 17:04
m1n1mal
 
Возвращает такие же данные, как и в ССР.


(0,01Мб)
18.04.2018 17:16
Starter
 
А если функцию поменять (чуть модифицированная):

функция:
SQL код:
--Получить статистику по карточке товара
--входные параметры дата начала периодадата окончания периодакод места храненияартикулчто хотим узнать.
--
варианты что =
--
число днейкогда карточка продавалась
--число днейкогда карточка не продавалась
--число днейкогда остаток по карточке был равен нулю
--число днейкогда остаток был не равен нулю
--число днейкогда остаток был отрицательным.
--
число днейкогда остаток был положительным
--
число днейкогда карточка не продавалась и остаток по ней не был равен нулю.
--
суммарный остаток за все дни периода (для вычисления среднего остатка)
--
число днейкогда карточка не продавалась и остаток по ней был меньше или равен нули

CREATE 
OR REPLACE function SUPERMAG.Get_Card_Stat(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
  
AArticle in supermag.smcard.article%typeAWhat in integer) return integer
is
  i integer
;
  
error_param_value exception;

  function 
SaleDaysCount(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return integer is
    res integer 
:= 0;
  
begin
    select count
(distinct d.createdat)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WO''CS') and d.opcode and d.docstate 3
      
and d.createdat between ADateFrom and ADateTo and d.locationfrom ALocID
      
and s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  function 
GetRemainsOnDate(AOnDate in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return number
  is
    res number
;
  
begin
    select nvl
(sum(decode(nvl(d.locationto0), 0, -11) * s.quantity), 0)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WI''WO''IW''CS''CR''PN''PE''PO')
      and 
d.docstate and d.createdat <= AOnDate
      
and nvl(d.locationtod.locationfrom) = ALocID
      
and s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  function 
Calc(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%typeAWhat in integer) return integer
  is
    vRemains number 
:= GetRemainsOnDate(trunc(ADateFrom)-1ALocIDAArticle);
    
res integer := 0;
  
begin
    
for c in (select nvl(sum(nvl(d.quantity0)) over (order by c.dat) + vRemains0) as quantity,
                
nvl(sum(nvl(d.salequantity0)) over (partition by c.dat), 0) as salequantity
              from
                
(select dd.createdatsum(dd.quantity) as quantitysum(dd.salequantity) as salequantity
                 from
                   
(select d.createdatsum(s.quantity) as quantityas salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationto ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdat
                    union all
                    select d
.createdatsum(-s.quantity) as quantitysum(decode(d.opcode1, -s.quantity0)) as salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationfrom ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdatdd
                 group by dd
.createdatd,
                (
select dt.dat
                 from
                   
(select ADateFrom level as dat
                    from dual connect by level 
<= ADateTo ADateFrom 1dtc
              where c
.dat d.createdat(+)) loop
      
if AWhat and c.quantity 0 then -- число днейкогда остаток по карточке был равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity != 0 then -- число днейкогда остаток был не равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был отрицательным
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был положительным
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- cуммостатоккогда остаток был положительным (для расчета оборачиваемости)
        
res := res c.quantity;

      
elsif AWhat and (c.salequantity and c.quantity !=0then -- число днейкогда карточка не продавалась и остаток по ней не был равен нулю
        res 
:= res 1;
      
elsif ((AWhat 9) and (c.salequantity and c.quantity <= 0)) then -- число днейкогда карточка не продавалась и остаток по ней был меньше или равен нулю
        res 
:= res 1;
      
end if;
    
end loop;
    return(
res);
  
exception when no_data_found then
    
return(99);
  
end;

begin
  
if AWhat 1 then -- число днейкогда карточка продавалась
    
return(SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat 2 then -- число днейкогда карточка не продавалась
    
return(trunc(ADateTo) - trunc(ADateFrom) + SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat between 3 and 9 then
    
return(Calc(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticleAWhat));
  else
    
raise_application_error(-20999'Параметр AWhat должен быть в диапазоне от 1 до 9.'true);
  
end if;
end;
/  
commit


И попытаться напрямую через SQL+ запросить
SQL код:
select Get_Card_Stat(to_date('09.04.2018','dd.mm.yyyy'),to_date('18.04.2018','dd.mm.yyyy'),2,'406338',3from dual
только вместо 2 поставить код места хранения.
18.04.2018 17:21
m1n1mal
 
С новой функцией, результат работы возвращает значение "0".
18.04.2018 17:24
Starter
 
А если параметром указывать не 3 а различные другие варианты, типа: 1,2,3,4,5,6,7 ?
что возвращает ?
И - код места хранения кстати какой ?
18.04.2018 17:32
m1n1mal
 
Код МХ=4
Результаты по разным параметрам ниже:
1 - возвращает 3
2 - возвращает 7
3 - возвращает 0
4 - возвращает 10
5 - возвращает 0
6 - возвращает 10
7 - возвращает 7
8 - возвращает 79
9 - возвращает 0

Может ему мешают отрицательные значения?
18.04.2018 18:24
Starter
 
Да нет, отрицательные остатки не должны мешать совсем...

А вот этот код что возвращает:
SQL код:
select nvl(sum(decode(nvl(d.locationto0), 0, -11) * s.quantity), 0)
    
from supermag.smdocuments dsupermag.smspec s
    where d
.doctype in ('WI''WO''IW''CS''CR''PN''PE''PO')
      and 
d.docstate and d.createdat <= to_date('08.04.2018','dd.mm.yyyy')
      and 
nvl(d.locationtod.locationfrom) = 4
      
and s.doctype d.doctype and s.docid d.id and s.article '406338'
19.04.2018 10:28
Starter
 
И - можно ли вот такой вариант попробовать функции ?

функция:
SQL код:
create or replace function Get_Card_Stat(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
  
AArticle in supermag.smcard.article%typeAWhat in integer) return integer 
is
  i integer
;
  
error_param_value exception;
  
  function 
SaleDaysCount(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return integer is
    res integer 
:= 0;
  
begin
    select count
(distinct d.createdat)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WO''CS') and d.opcode and d.docstate 
      
and d.createdat between ADateFrom and ADateTo and d.locationfrom ALocID
      
and s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then    
    
return(0);
  
end;
  function 
GetRemainsOnDate(AOnDate in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return number 
  is
    res number
;
  
begin
    select nvl
(sum(decode(nvl(d.locationto0), 0, -11) * s.quantity), 0)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WI''WO''IW''CS''CR''PN''PE''PO')
      and 
d.docstate >= /*= 3*/ and d.createdat <= AOnDate
      
and nvl(d.locationtod.locationfrom) = ALocID
      
and s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  function 
Calc(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%typeAWhat in integer) return integer
  is
    vRemains number 
:= GetRemainsOnDate(ADateFrom 1ALocIDAArticle);
    
res integer := 0;
  
begin
    
for c in (select nvl(sum(nvl(d.quantity0)) over (order by c.dat) + vRemains0) as quantity,
                
nvl(sum(nvl(d.salequantity0)) over (partition by c.dat), 0) as salequantity
              from
                
(select dd.createdatsum(dd.quantity) as quantitysum(dd.salequantity) as salequantity
                 from
                   
(select d.createdatsum(s.quantity) as quantityas salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationto ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdat
                    union all
                    select d
.createdatsum(-s.quantity) as quantitysum(decode(d.opcode1, -s.quantity0)) as salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationfrom ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdatdd
                 group by dd
.createdatd,
                (
select dt.dat
                 from 
                   
(select ADateFrom level as dat
                    from dual connect by level 
<= ADateTo ADateFrom 1dtc
              where c
.dat d.createdat(+)) loop
      
if AWhat and c.quantity 0 then -- число днейкогда остаток по карточке был равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity != 0 then -- число днейкогда остаток был не равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был отрицательным
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был положительным
        res 
:= res 1;
      
elsif AWhat and c.salequantity and c.quantity != 0 then -- число днейкогда карточка не продавалась и остаток по ней не был равен нулю
        res 
:= res 1;
      
end if;
    
end loop;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  
begin
  
if AWhat 1 then -- число днейкогда карточка продавалась
    
return(SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat 2 then -- число днейкогда карточка не продавалась
    
return(trunc(ADateTo) - trunc(ADateFrom) + SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat between 3 and 7 then
    
return(Calc(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticleAWhat));
  else
    
raise_application_error(-20999'Параметр AWhat должен быть в диапазоне от 1 до 7.'true);
  
end if;
end
19.04.2018 11:20
m1n1mal
 
Здравствуйте.

Цитата:
Starter А вот этот код что возвращает:
Данный запрос возвращает значение = 9

Цитата:
Starter И - можно ли вот такой вариант попробовать функции ?
Заменил функцию, результаты вывод запроса
SQL код:
select Get_Card_Stat(to_date('09.04.2018','dd.mm.yyyy'),to_date('18.04.2018','dd.mm.yyyy'),4,'406338',3from dual
1 - возвращает 3
2 - возвращает 7
3 - возвращает 0
4 - возвращает 10
5 - возвращает 0
6 - возвращает 10
7 - возвращает 7
8 - возвращает "ORA-20999: Параметр AWhat должен быть в диапазоне от 1 до 7."
19.04.2018 14:51
Starter
 
Странно всё это :( Т.е. по частям функция работает, а в собранном виде - нет. Тут только отлаживать и отлаживать.
Если сможете организовать доступ хотя бы к стенду - могу попробовать.
19.04.2018 15:34
m1n1mal
 
Я проверял на "боевом" сервере, стенда к сожалению нет. Мне потребуется какое-то время чтобы его развернуть и подготовить. Как только я все подготовлю, отпишусь Вам.
Для проверки потребуется только БД или рабочий Супермаг+ тоже?
19.04.2018 18:12
Starter
 
БД, думаю, вполне достаточно будет. просто пробросить порт до сервера.
26.04.2018 18:28
Starter
 
В общем, по итогам - в функции был глюк вычисления начального остатка, неправильно обрабатывались документы перемещения.
вот рабочий вариант:

:
SQL код:
------------
--
Получить статистику по карточке товара
--входные параметры дата начала периодадата окончания периодакод места храненияартикулчто хотим узнать.
--
варианты что =
--
число днейкогда карточка продавалась
--число днейкогда карточка не продавалась
--число днейкогда остаток по карточке был равен нулю
--число днейкогда остаток был не равен нулю
--число днейкогда остаток был отрицательным.
--
число днейкогда остаток был положительным
--
число днейкогда карточка не продавалась и остаток по ней не был равен нулю.
--
суммарный остаток за все дни периода (для вычисления среднего остатка)
--
число днейкогда карточка не продавалась и остаток по ней был меньше или равен нули

create 
or replace function Get_Card_Stat(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
  
AArticle in supermag.smcard.article%typeAWhat in integer) return integer 
is
  i integer
;
  
error_param_value exception;
  
  function 
SaleDaysCount(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return integer is
    res integer 
:= 0;
  
begin
    select count
(distinct d.createdat)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WO''CS') and d.opcode and d.docstate 
      
and d.createdat between ADateFrom and ADateTo and d.locationfrom ALocID
      
and s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then    
    
return(0);
  
end;
  function 
GetRemainsOnDate(AOnDate in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%type) return number 
  is
    res number
;
  
begin
    select nvl
(sum((case when(d.locationto=ALocIDthen 1 when (d.locationfrom=ALocIDthen -else 0 end) * s.quantity), 0)
    
into res
    from supermag
.smdocuments dsupermag.smspec s
    where d
.doctype in ('WI''WO''IW''CS''CR''PN''PE''PO')
      and 
d.docstate >= /*= 3*/ and d.createdat <= AOnDate
      
and (d.locationto=ALocID or d.locationfrom ALocID)
      and 
s.doctype d.doctype and s.docid d.id and s.article AArticle;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  function 
Calc(ADateFrom in dateADateTo in dateALocID in supermag.smstorelocations.id%type,
    
AArticle in supermag.smcard.article%typeAWhat in integer) return integer
  is
    vRemains number 
:= GetRemainsOnDate(ADateFrom 1ALocIDAArticle);
    
res integer := 0;
  
begin
    
for c in (select nvl(sum(nvl(d.quantity0)) over (order by c.dat) + vRemains0) as quantity,
                
nvl(sum(nvl(d.salequantity0)) over (partition by c.dat), 0) as salequantity
              from
                
(select dd.createdatsum(dd.quantity) as quantitysum(dd.salequantity) as salequantity
                 from
                   
(select d.createdatsum(s.quantity) as quantityas salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationto ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdat
                    union all
                    select d
.createdatsum(-s.quantity) as quantitysum(decode(d.opcode1, -s.quantity0)) as salequantity
                    from supermag
.smdocuments dsupermag.smspec s
                    where d
.doctype s.doctype and d.id s.docid and s.article AArticle and d.locationfrom ALocID
                      
and d.docstate >= and d.createdat between ADateFrom and ADateTo
                    group by d
.createdatdd
                 group by dd
.createdatd,
                (
select dt.dat
                 from 
                   
(select ADateFrom level as dat
                    from dual connect by level 
<= ADateTo ADateFrom 1dtc
              where c
.dat d.createdat(+)) loop
      
if AWhat and c.quantity 0 then -- число днейкогда остаток по карточке был равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity != 0 then -- число днейкогда остаток был не равен нулю
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был отрицательным
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- число днейкогда остаток был положительным
        res 
:= res 1;
      
elsif AWhat and c.quantity 0 then -- cуммостатоккогда остаток был положительным (для расчета оборачиваемости)
        
res := res c.quantity;
      
elsif AWhat and (c.salequantity and c.quantity !=0then -- число днейкогда карточка не продавалась и остаток по ней не был равен нулю
        res 
:= res 1;
      
elsif ((AWhat 9) and (c.salequantity and c.quantity <= 0)) then -- число днейкогда карточка не продавалась и остаток по ней был меньше или равен нулю
        res 
:= res 1;
      
end if;
    
end loop;
    return(
res);
  
exception when no_data_found then
    
return(0);
  
end;
  
begin
  
if AWhat 1 then -- число днейкогда карточка продавалась
    
return(SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat 2 then -- число днейкогда карточка не продавалась
    
return(trunc(ADateTo) - trunc(ADateFrom) + SaleDaysCount(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticle));
  
elsif AWhat between 3 and 9 then
    
return(Calc(trunc(ADateFrom), trunc(ADateTo), ALocIDAArticleAWhat));
  else
    
raise_application_error(-20999'Параметр AWhat должен быть в диапазоне от 1 до 9.'true);
  
end if;
end Get_Card_Stat;
/  
commit
27.04.2018 10:30
m1n1mal
 
Спасибо, все отлично, функция возвращает одинаковые значения с расчетом ССР.
27.04.2018 12:37
Starter
 
Теперь вторая часть марлезонского балета - расчёт недополученной прибыли ? :)
1. Во-первых, какую цифру брать в статистику - как в "ТЗ" - дней с 0 остатком - можно, но возможна ситуация, когда остаток уйдёт в минус, и его вроде как нет, а в статистику не войдёт. Или остаток вначале дня был, но за день весь товар продался и остаток на конец дня стал нулевым. И день войдёт в статистику, хотя вроде как не должен. Мы для таких случаев смотрим те дни, когда продаж не было и остаток <=0. т.е. параметр в вышеуказанной функции =9.
2. Как и говорилось выше, два периода в БА указать нельзя. Но можно задать в коде маленький период (либо его длину в днях). Пример:



(0,05Мб)
В данном случае - в задаче добавлено поле, в значении которого стоит 14. Остальные поля (доход за период и недополуч) прибыль вычисляемые на основе значения этого поля. Если нужен период 30 дней - меняем значение 14 на 30.

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



(0,06Мб)



(0,02Мб)

Ну и третий вариант - вообще не указываем даты в задаче а зашиваем в коде. Просто обычно редко бывает так, чтобы период был произвольным. обычно это - с начала месяца, прошлый месяц, прошлая неделя, две недели, не считая текущую, и пр. можно узнать у пользователей, какой временной интервал им наиболее интересен и зашить это в код, либо сделать 2 или 3 задачи с разными вшитыми интервалами.
27.04.2018 17:44
m1n1mal
 
Цитата:
Starter 1. Во-первых, какую цифру брать в статистику - как в "ТЗ" - дней с 0 остатком - можно, но возможна ситуация, когда остаток уйдёт в минус, и его вроде как нет, а в статистику не войдёт. Или остаток вначале дня был, но за день весь товар продался и остаток на конец дня стал нулевым. И день войдёт в статистику, хотя вроде как не должен. Мы для таких случаев смотрим те дни, когда продаж не было и остаток <=0. т.е. параметр в вышеуказанной функции =9.
В текущей Вашей функции мы опробовали оба параметра и 3 и 9.
В данном случае однозначного решения какой будут использовать нет, интересны оба. Тут интересен как вариант с остатком = 0 (так как тут однозначно свидетельствует об отсутствии товара), так и вариант когда отсутствовали продажи и остаток был <=0 (пересорты и тп.)

Цитата:
Starter 2. Как и говорилось выше, два периода в БА указать нельзя. Но можно задать в коде маленький период (либо его длину в днях). Пример:
Отличным вариантом выглядит тот, в котором мы задаем дата1 и дата2, такой вариант сможет закрыть разные диапазоны без создания дополнительных задач.


Опции темы


Часовой пояс GMT +3, время: 13:34.

 

Форум сделан на основе vBulletin®
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd. Перевод: zCarot и OlegON
В случае заимствования информации гипертекстовая индексируемая ссылка на Форум обязательна.