forecast_api/route_sql/addmonth_vupd.sql
PhilRunninger 47d1baffc5 Handle the case where baseline is zero (units or value).
This happens when a customer places identical orders and specifies that
they be shipped in different seasons. We end up with one order shipped
in 2023 and one open for 2024. Under our current operating procedures,
open orders are subtracted from shipped orders and are presented as
baseline for the next forecast. The zero that appears in the baseline in
this case was causing all kinds of issues in the SQL scripts for
inserting the adjustments, ranging from finding zero rows to adjust to
division by zero.

Another change required to correct this was updating the iter value of
the open orders from 'actuals' to 'copy':

    UPDATE rlarp.osm_pool
    SET iter = 'copy'
    WHERE tag = 'open-orders'
2023-05-17 05:28:21 -04:00

575 lines
16 KiB
SQL

WITH
/*
the volume must be expressed in terms of units, since that is what it will be scaling
*/
target AS (select target_volume vincr, target_price pincr)
,testv AS (
SELECT
sum(units) tot
,sum(units) FILTER (WHERE iter IN ('copy','plan','diff')) base
,COALESCE(sum(units) FILTER (WHERE module = 'new basket'),0) newpart
,coalesce(sum(value_loc *r_rate),0) totsales
,coalesce(sum(value_loc *r_rate) FILTER (WHERE iter IN ('plan','diff','copy')),0) basesales
,COALESCE(sum(value_loc *r_rate) FILTER (WHERE module = 'new basket'),0) newpartsales
FROM
rlarp.osm_pool
WHERE
-----------------scenario----------------------------
where_clause
-----------------additional params-------------------
AND calc_status||flag <> 'CLOSEDREMAINDER' --exclude short ships when building order adjustments
AND (order_date <= ship_date OR tag = 'open-orders')
)
-- select * from testv
--
,flagv AS (
SELECT
tot
,base
,newpart
,CASE WHEN tot = 0 THEN
CASE WHEN base = 0 THEN
CASE WHEN newpart = 0 THEN
'unclean data. tested -> does not exist'
ELSE
'scale new part'
END
ELSE
'scale copy'
END
ELSE
'scale all'
END flag
,CASE WHEN totsales = 0 THEN
CASE WHEN basesales = 0 THEN
CASE WHEN newpartsales = 0 THEN
'no price'
ELSE
'scale new part'
END
ELSE
'scale copy'
END
ELSE
'scale all'
END flagsales
FROM
testv
)
-- select * from flagv
--
,GLD AS MATERIALIZED (
SELECT
N1COMP COMP
,N1CCYY FSYR
,KPMAXP PERDS
,N1FSPP PERD
,to_char(N1FSYP,'FM0000') FSPR
,N1SD01 SDAT
,N1ED01 EDAT
,daterange(n1sd01, n1ed01,'[]') drange
,to_char(N1ED01,'yymm') CAPR
,N1ED01 - N1SD01 +1 NDAYS
,CASE WHEN EXTRACT(MONTH FROM N1ED01) >= 6 THEN EXTRACT(YEAR FROM N1ED01) + 1 ELSE EXTRACT(YEAR FROM N1ED01) END SSYR
,to_char(CASE WHEN EXTRACT(MONTH FROM N1ED01) >= 6 THEN EXTRACT(MONTH FROM N1ED01) -5 ELSE EXTRACT(MONTH FROM N1ED01) +7 END,'FM00') SSPR
FROM
LGDAT.GLDATREF
INNER JOIN LGDAT.GLDATE ON
KPCOMP = N1COMP AND
KPCCYY = N1CCYY
WHERE
N1COMP = 93
)
-- select * from gld
--
,mseq AS (
SELECT * FROM
(
VALUES
('01 - Jun',1,6,-1)
,('02 - Jul',2,7,-1)
,('03 - Aug',3,8,-1)
,('04 - Sep',4,9,-1)
,('05 - Oct',5,10,-1)
,('06 - Nov',6,11,-1)
,('07 - Dec',7,12,-1)
,('08 - Jan',8,1,0)
,('09 - Feb',9,2,0)
,('10 - Mar',10,3,0)
,('11 - Apr',11,4,0)
,('12 - May',12,5,0)
) x(m,s,cal,yr)
)
-- select * from mseq
--
,alldates AS MATERIALIZED(
SELECT
promo
,terms
,order_month
,mseq.s seq
,order_date
,request_date
,ship_date
,sum(CASE (SELECT flagsales FROM flagv) WHEN 'no price'THEN 1.0 ELSE value_usd END) value_usd
FROM
rlarp.osm_pool
LEFT OUTER JOIN mseq ON
mseq.m = order_month
WHERE
-----------------scenario----------------------------
where_clause
-----------------additional params-------------------
AND CASE (SELECT flag FROM flagv)
WHEN 'scale all' THEN true
WHEN 'scale copy' THEN iter IN ('plan','diff','copy')
WHEN 'scale new part' THEN module = 'new basket'
END
AND calc_status||flag <> 'CLOSEDREMAINDER' --exclude short ships when building order adjustments
AND (order_date <= ship_date AND tag <> 'open-orders')
GROUP BY
promo
,terms
,order_month
,mseq.s
,order_date
,request_date
,ship_date
-- HAVING
-- sum(CASE (SELECT flagsales FROM flagv) WHEN 'no price'THEN 1.0 ELSE value_usd END) <> 0
)
-- select * from alldates
--
,dom AS (
SELECT
extract(day FROM order_date) DOM
,sum(value_usd) value_usd
FROM
alldates
GROUP BY
extract(day FROM order_date)
)
-- select * from dom
--
---------------------may want ot look at a top-5 mix solution in the future facilitated by sum() over (order by sales desc)---------------
,mmix AS (
SELECT
to_char(order_date,'Mon') _month
,seq
,promo
,sum(extract(day from order_date)*value_usd) dom_wa
--,request_date-order_date rlag
,sum((request_date-order_date)*(value_usd)) rlag_wa
--,ship_date - request_date slag
,sum((ship_date - request_date)*(value_usd)) slag_wa
,coalesce(nullif(sum(value_usd),0),1) value_usd
FROM
alldates
GROUP BY
to_char(order_date,'Mon')
,seq
,promo
)
-- select * from mmix
--
,targm AS (select s, m from mseq where m = '04 - Sep' )
-- select * from targm
--
,mmixp AS (
SELECT
_month
,seq
,promo
,greatest(least(round((dom_wa/value_usd)::numeric,0)::int,28),1) odom
,round((rlag_wa/value_usd)::numeric,0)::int rlag
,round((slag_wa/value_usd)::numeric,0)::int slag
,value_usd/sum(value_usd) over (partition by _month) momix
FROM
mmix
)
-- select * from mmixp
--
,closest AS (
SELECT
_month, targm.s, m
FROM
mmixp
CROSS JOIN targm
ORDER BY
abs(seq - targm.s) ASC
LIMIT 1
)
-- select * from closest
--
---------------------the role of basemix here is to get non-dated info which is then dated in the next step---------------------
,basemix AS (
SELECT
--fspr in next step
o.plnt
--promo in next step
--terms in next step
,c.bvterm terms
,o.bill_cust_descr
,o.ship_cust_descr
,o.dsm
,o.quota_rep_descr
,o.director
,o.billto_group
,o.shipto_group
,o.chan
,o.chansub
,o.chan_retail
,o.part
,o.part_descr
,o.part_group
,o.branding
,o.majg_descr
,o.ming_descr
,o.majs_descr
,o.mins_descr
,o.segm
,o.substance
,o.fs_line
,o.r_currency
,o.r_rate
,o.c_currency
,o.c_rate
,sum(coalesce(o.units,0)) units
,sum(coalesce(o.value_loc,0)) value_loc
,sum(coalesce(o.value_usd,0)) value_usd
,sum(coalesce(o.cost_loc,0)) cost_loc
,sum(coalesce(o.cost_usd,0)) cost_usd
,sum(coalesce(o.pounds,0)) pounds
,o.calc_status
,o.flag
FROM
rlarp.osm_pool o
LEFT OUTER JOIN lgdat.cust c ON
c.bvcust = rtrim(substr(o.bill_cust_descr,1,8))
WHERE
-----------------scenario----------------------------
where_clause
-----------------additional params-------------------
AND CASE (SELECT flag FROM flagv)
WHEN 'scale all' THEN true
WHEN 'scale copy' THEN iter IN ('plan','diff','copy')
WHEN 'scale new part' THEN module = 'new basket'
END
AND calc_status||flag <> 'CLOSEDREMAINDER' --exclude short ships when building order adjustments
AND (order_date <= ship_date AND tag <> 'open-orders')
GROUP BY
--fspr in next step
o.plnt
,c.bvterm
--promo in next step
--terms in next step
,o.bill_cust_descr
,o.ship_cust_descr
,o.dsm
,o.quota_rep_descr
,o.director
,o.billto_group
,o.shipto_group
,o.chan
,o.chansub
,o.chan_retail
,o.part
,o.part_descr
,o.part_group
,o.branding
,o.majg_descr
,o.ming_descr
,o.majs_descr
,o.mins_descr
,o.segm
,o.substance
,o.fs_line
,o.r_currency
,o.r_rate
,o.c_currency
,o.c_rate
,o.calc_status
,o.flag
)
-- select * from basemix
--
,vscale AS (
SELECT
(SELECT vincr::numeric FROM target) incr
,(SELECT sum(units) FROM basemix) base
-- If sum(units) = 0, then we're likely working with a single row. We can't
-- scale 0, no matter how we try, so we just use mod_volume to set the value we want.
-- When sum(units) <> 0, then we use factor to scale units.
,CASE
WHEN (SELECT sum(units)::numeric FROM basemix) = 0
THEN 0
ELSE (SELECT vincr::numeric FROM target)/(SELECT sum(units)::numeric FROM basemix)
END AS factor
,CASE
WHEN (SELECT sum(units)::numeric FROM basemix) = 0
THEN (SELECT vincr::numeric FROM target)
ELSE 0
END AS mod_volume
)
-- select * from vscale
--
,log AS (
INSERT INTO rlarp.osm_log(doc) SELECT $$replace_iterdef$$::jsonb doc RETURNING *
)
-- select * from log
--
,volume AS (
SELECT
sd.fspr
,b.plnt
,m.promo
,b.terms
,b.bill_cust_descr
,b.ship_cust_descr
,b.dsm
,b.quota_rep_descr
,b.director
,b.billto_group
,b.shipto_group
,b.chan
,b.chansub
,b.chan_retail
,b.part
,b.part_descr
,b.part_group
,b.branding
,b.majg_descr
,b.ming_descr
,b.majs_descr
,b.mins_descr
,b.segm
,b.substance
,b.fs_line
,b.r_currency
,b.r_rate
,b.c_currency
,b.c_rate
,round((CASE WHEN s.factor = 0 THEN s.mod_volume ELSE b.units*s.factor END)*m.momix, 2) units
,round(b.value_loc*s.factor*m.momix,2) value_loc
,round(b.value_usd*s.factor*m.momix,2) value_usd
,round(b.cost_loc*s.factor*m.momix ,2) cost_loc
,round(b.cost_usd*s.factor*m.momix ,2) cost_usd
,b.calc_status
,b.flag
,make_date(mseq.yr + 2024,mseq.cal,m.odom) order_date
,od.sspr || ' - ' || to_char(make_date(mseq.yr + 2024,mseq.cal,m.odom),'Mon') order_month
,od.ssyr order_season
,make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag request_date
,rd.sspr || ' - ' ||to_char(make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag,'Mon') request_month
,rd.ssyr request_season
,make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag + slag ship_date
,sd.sspr || ' - ' || to_char(make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag + slag,'Mon') ship_month
,sd.ssyr ship_season
,'replace_version' "version"
,'replace_source'||' volume' iter
,log.id
,COALESCE(log.doc->>'tag','') "tag"
,log.doc->>'message' "comment"
,log.doc->>'type' module
,round(CASE
WHEN s.factor = 0 THEN s.mod_volume * i.nwht * (CASE i.nwun
WHEN 'KG' THEN 2.2046
ELSE 1 END)
ELSE b.pounds*s.factor END, 2) pounds
FROM
basemix b
CROSS JOIN vscale s
CROSS JOIN mmixp m
CROSS JOIN closest
CROSS JOIN log
LEFT OUTER JOIN mseq ON
mseq.m = closest.m
LEFT OUTER JOIN gld od ON
make_date(mseq.yr + 2024,mseq.cal,m.odom) BETWEEN od.sdat AND od.edat
LEFT OUTER JOIN gld rd ON
make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag BETWEEN rd.sdat AND rd.edat
LEFT OUTER JOIN gld sd ON
make_date(mseq.yr + 2024,mseq.cal,m.odom) + rlag + slag BETWEEN sd.sdat AND sd.edat
LEFT OUTER JOIN "CMS.CUSLG".itemm i ON i.item = b.part
WHERE
m._month = (SELECT _month FROM closest)
)
-- select * from volume
--
,pscale AS (
SELECT
CASE
WHEN (SELECT coalesce(sum(value_loc),0) FROM volume) = 0 AND (SELECT sum(units) FROM volume) = 0 THEN 'both'
WHEN (SELECT coalesce(sum(value_loc),0) FROM volume) = 0 THEN 'cost'
ELSE 'other'
END zero_values
,CASE
WHEN (SELECT coalesce(sum(value_loc),0) FROM volume) = 0 AND (SELECT coalesce(sum(units),0) FROM volume) = 0 -- Split pincr evenly between rows.
THEN (SELECT pincr::numeric FROM target) / (SELECT count(*) FROM volume)
WHEN (SELECT coalesce(sum(value_loc),0) FROM volume) = 0 -- Get a per-unit pincr value
THEN (SELECT pincr::numeric FROM target) / (SELECT sum(units) FROM volume)
ELSE -- Find percent change for existing value_loc
(SELECT pincr::numeric FROM target) / (SELECT nullif(sum(value_loc * r_rate),0) FROM volume) - 1
END factor
)
-- select * from pscale
--
,price AS (
SELECT
b.fspr
,b.plnt
,b.promo
,b.terms
,b.bill_cust_descr
,b.ship_cust_descr
,b.dsm
,b.quota_rep_descr
,b.director
,b.billto_group
,b.shipto_group
,b.chan
,b.chansub
,b.chan_retail
,b.part
,b.part_descr
,b.part_group
,b.branding
,b.majg_descr
,b.ming_descr
,b.majs_descr
,b.mins_descr
,b.segm
,b.substance
,b.fs_line
,b.r_currency
,b.r_rate
,b.c_currency
,b.c_rate
,0::numeric units
,round(CASE p.zero_values
WHEN 'both' THEN p.factor / b.r_rate
WHEN 'cost' THEN b.units * p.factor / b.r_rate
WHEN 'other' THEN b.value_loc * p.factor
END, 2) AS value_loc
,round(CASE p.zero_values
WHEN 'both' THEN p.factor
WHEN 'cost' THEN b.units * p.factor
WHEN 'other' THEN b.value_usd * p.factor
END, 2) AS value_usd
,0::numeric cost_loc
,0::numeric cost_usd
,b.calc_status
,b.flag
,b.order_date
,b.order_month
,b.order_season
,b.request_date
,b.request_month
,b.request_season
,b.ship_date
,b.ship_month
,b.ship_season
,'replace_version' "version"
,'replace_source'||' price' iter
,log.id
,COALESCE(log.doc->>'tag','') "tag"
,log.doc->>'message' "comment"
,log.doc->>'type' module
,0::numeric pounds
FROM
volume b
CROSS JOIN pscale p
CROSS JOIN log
WHERE
p.factor <> 0
)
-- SELECT * FROM price UNION ALL SELECT * FROM volume
--
, ins AS (
INSERT INTO rlarp.osm_pool (SELECT * FROM price UNION ALL SELECT * FROM volume) RETURNING *
)
,insagg AS (
SELECT
---------customer info-----------------
bill_cust_descr
,billto_group
,ship_cust_descr
,shipto_group
,quota_rep_descr
,director
,segm
,substance
,chan
,chansub
---------product info------------------
,majg_descr
,ming_descr
,majs_descr
,mins_descr
--,brand
--,part_family
,part_group
,branding
--,color
,part_descr
---------dates-------------------------
,order_season
,order_month
,ship_season
,ship_month
,request_season
,request_month
,promo
,version
,iter
,logid
,tag
,comment
--------values-------------------------
,sum(value_loc) value_loc
,sum(value_usd) value_usd
,sum(cost_loc) cost_loc
,sum(cost_usd) cost_usd
,sum(units) units
,sum(pounds) pounds
FROM
ins
GROUP BY
---------customer info-----------------
bill_cust_descr
,billto_group
,ship_cust_descr
,shipto_group
,quota_rep_descr
,director
,segm
,substance
,chan
,chansub
---------product info------------------
,majg_descr
,ming_descr
,majs_descr
,mins_descr
--,brand
--,part_family
,part_group
,branding
--,color
,part_descr
---------dates-------------------------
,order_season
,order_month
,ship_season
,ship_month
,request_season
,request_month
,promo
,version
,iter
,logid
,tag
,comment
)
SELECT json_agg(row_to_json(insagg)) x from insagg