Compare commits
No commits in common. "master" and "refactor_batch" have entirely different histories.
master
...
refactor_b
127
WARP.md
127
WARP.md
@ -1,127 +0,0 @@
|
||||
# WARP.md
|
||||
|
||||
This file provides guidance to WARP (warp.dev) when working with code in this repository.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This is a **pricing engine** that computes product pricing recommendations based on multiple data sources (target prices, historical sales, list prices). The system operates with both **SQL Server** and **PostgreSQL** implementations, providing pricing guidance through structured JSON responses.
|
||||
|
||||
### Core Components
|
||||
|
||||
**Database-First Architecture**: The system is primarily database-driven with stored procedures/functions as the main business logic layer. Each major component has both SQL Server (`.ms.sql`) and PostgreSQL (`.pg.sql`) implementations.
|
||||
|
||||
**Single Price Call Flow**: The main entry point is `single_price_call` which processes one pricing scenario through these stages:
|
||||
1. **Input enrichment** - Resolves customer/channel/tier from bill-to/ship-to codes
|
||||
2. **Historical price lookup** - Finds most recent/relevant sales from `lastpricedetail`
|
||||
3. **Target price lookup** - Gets configured target pricing from `target_prices`
|
||||
4. **Cost normalization** - Adjusts historical prices across different product data segments
|
||||
5. **List price lookup** - Retrieves volume-banded list prices from `pricelist_ranged`
|
||||
6. **Guidance logic** - Computes final recommended price using `guidance_logic`
|
||||
7. **JSON generation** - Creates structured `ui_json` for frontend consumption
|
||||
|
||||
**Multi-Database Support**: Core logic exists in parallel implementations:
|
||||
- **SQL Server**: `procs/*.ms.sql`, `tables/*.ms.sql`
|
||||
- **PostgreSQL**: `procs/*.pg.sql`, `tables/*.pg.sql`
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Testing Individual Pricing Scenarios
|
||||
```sql
|
||||
-- SQL Server
|
||||
EXEC pricing.single_price_call
|
||||
@bill = 'GRIF0001',
|
||||
@ship = 'GRIF0001',
|
||||
@part = 'XNS0T1G3G18B096',
|
||||
@v1ds = 'v1:B..PLT..',
|
||||
@vol = 9600;
|
||||
|
||||
-- PostgreSQL
|
||||
SELECT ui_json->'data'
|
||||
FROM pricequote.single_price_call(
|
||||
'GRIF0001',
|
||||
'GRIF0001',
|
||||
'XNS0T1G3G18B096',
|
||||
'v1:B..PLT..',
|
||||
9600
|
||||
);
|
||||
```
|
||||
|
||||
### Running Test Examples
|
||||
Execute the complete example scenarios:
|
||||
```powershell
|
||||
# SQL Server examples
|
||||
sqlcmd -S server -d database -i example_usage.ms.sql
|
||||
|
||||
# PostgreSQL examples
|
||||
psql -d database -f example_usage.pg.sql
|
||||
```
|
||||
|
||||
### Database Schema Updates
|
||||
Deploy schema changes in this order:
|
||||
```powershell
|
||||
# 1. Tables first
|
||||
sqlcmd -S server -d database -i tables/target_prices.ms.sql
|
||||
sqlcmd -S server -d database -i tables/pricelist_ranged.ms.sql
|
||||
sqlcmd -S server -d database -i tables/lastpricedetail.ms.sql
|
||||
|
||||
# 2. Functions/procedures second
|
||||
sqlcmd -S server -d database -i procs/guidance_logic.ms.sql
|
||||
sqlcmd -S server -d database -i procs/single_price_call.ms.sql
|
||||
```
|
||||
|
||||
### Data Rebuilds
|
||||
Refresh core pricing data:
|
||||
```sql
|
||||
-- Rebuild price list with UOM conversions
|
||||
EXEC [script from pricelist_ranged.ms.sql]
|
||||
|
||||
-- Refresh historical price summaries
|
||||
EXEC [script from lastpricedetail.ms.sql]
|
||||
|
||||
-- Update target price configurations
|
||||
INSERT INTO pricing.target_prices SELECT * FROM remote.target_prices_view;
|
||||
```
|
||||
|
||||
## Key Data Structures
|
||||
|
||||
**Input Parameters**: All pricing calls require:
|
||||
- `bill` - Bill-to customer code
|
||||
- `ship` - Ship-to customer code
|
||||
- `part` - Product part number
|
||||
- `v1ds` - Product data segment (version 1)
|
||||
- `vol` - Volume quantity
|
||||
|
||||
**UI JSON Structure**: The output `ui_json` contains:
|
||||
- `details[]` - Array of pricing panels (History, List, Target Calculation, Guidance)
|
||||
- `data` - Raw `expl` JSON with all calculation details
|
||||
|
||||
**Product Data Segments**: Products have versioned data segments (v1ds/v0ds) that affect pricing:
|
||||
- Format: `v1:B..PLT..` (color tier, packaging, etc.)
|
||||
- Cross-segment price normalization uses target price ratios or cost ratios
|
||||
|
||||
## Database Schemas
|
||||
|
||||
**SQL Server**: Uses `pricing.*` schema
|
||||
- `pricing.single_price_call` - Main pricing procedure
|
||||
- `pricing.target_prices` - Configured target pricing rules
|
||||
- `pricing.lastpricedetail` - Historical price summaries per customer/product group
|
||||
|
||||
**PostgreSQL**: Uses `pricequote.*` schema
|
||||
- `pricequote.single_price_call()` - Main pricing function
|
||||
- `pricequote.target_prices` - Target pricing configurations
|
||||
- `pricequote.lastpricedetail` - Historical price data
|
||||
|
||||
## Key Business Logic
|
||||
|
||||
**Guidance Logic**: Located in `guidance_logic.*sql`, determines final price using precedence:
|
||||
1. Target price (if available)
|
||||
2. Last normalized price (prevents price drops)
|
||||
3. List price (as ceiling)
|
||||
|
||||
**Price Normalization**: When historical prices are from different product segments, they're adjusted using either target price ratios or cost ratios to enable fair comparison.
|
||||
|
||||
**Volume Banding**: List prices and target prices use volume ranges. The `pricelist_ranged` tables normalize all unit-of-measure to "PC" (pieces) for consistent volume comparisons.
|
||||
|
||||
## Archive Directory
|
||||
|
||||
Contains legacy TypeScript implementations and older SQL approaches. The `archive/apply_guidance.ts` shows the previous client-side pricing logic that has been moved to database stored procedures/functions.
|
@ -1,167 +0,0 @@
|
||||
DROP FUNCTION pricequote.build_pricing_path_base;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pricequote.build_pricing_path_base(
|
||||
_json JSONB
|
||||
)
|
||||
RETURNS TABLE (
|
||||
stlc TEXT
|
||||
,seq BIGINT
|
||||
,srtcode TEXT
|
||||
,ds TEXT
|
||||
,chan TEXT
|
||||
,tier TEXT
|
||||
,vol INT4RANGE
|
||||
,func TEXT
|
||||
,val NUMERIC
|
||||
,price NUMERIC
|
||||
,math TEXT[]
|
||||
,lastflag BOOLEAN
|
||||
)
|
||||
LANGUAGE plpgsql AS
|
||||
$$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
|
||||
WITH RECURSIVE
|
||||
-- 1️⃣ Parse JSONB into rows of (entity, attr, val)
|
||||
parsed AS (
|
||||
SELECT
|
||||
e.entity,
|
||||
COALESCE(e.attr, '') AS attr,
|
||||
e.val,
|
||||
e.func
|
||||
FROM jsonb_to_recordset(_json)
|
||||
AS e(entity TEXT, attr TEXT, val NUMERIC, func TEXT)
|
||||
),
|
||||
-- 2️⃣ Attach sequence & func from master option_sequence table
|
||||
sequenced AS (
|
||||
SELECT
|
||||
p.entity,
|
||||
p.attr,
|
||||
p.val,
|
||||
p.func,
|
||||
s.DOMAIN,
|
||||
DENSE_RANK() OVER (ORDER BY s.seq) AS seq,
|
||||
ROW_NUMBER() OVER (PARTITION BY p.entity ORDER BY COALESCE(p.val,0) ASC) srt
|
||||
FROM parsed p
|
||||
JOIN pricequote.option_sequence s
|
||||
ON p.entity = s.entity
|
||||
)
|
||||
--select * from sequenced ORDER BY seq, srt
|
||||
-- 3️⃣ Recursively accumulate pricing path
|
||||
,combos AS (
|
||||
-- 🚀 Base case: first in sequence
|
||||
SELECT
|
||||
s.entity,
|
||||
s.attr,
|
||||
s.seq,
|
||||
to_char(s.srt,'FM000') srtcode,
|
||||
'' ds,
|
||||
'' chan,
|
||||
'' tier,
|
||||
null::TEXT vol,
|
||||
s.func,
|
||||
s.val,
|
||||
s.val agg,
|
||||
CASE WHEN s.func = 'Price' THEN s.val ELSE NULL::NUMERIC END AS base,
|
||||
ARRAY[
|
||||
CASE
|
||||
WHEN s.func = 'Price' THEN
|
||||
RPAD(s.entity || ':' || s.attr, 17, ' ') || ' + ' || LPAD(to_char(s.val, 'FM9999999990.00000'), 10, ' ')
|
||||
WHEN s.func = 'Factor' THEN
|
||||
RPAD(s.entity || ':' || s.attr, 17, ' ') || ' + ' || LPAD(to_char(COALESCE(NULLIF(CASE WHEN s.func = 'Price' THEN s.val END, NULL), 0) * (s.val - 1), 'FM9999999990.00000'), 10, ' ')
|
||||
ELSE
|
||||
RPAD(s.entity || ':' || s.attr, 17, ' ') || ' ' || LPAD(to_char(s.val, 'FM9999999990.00000'), 10, ' ')
|
||||
END
|
||||
] math
|
||||
FROM
|
||||
sequenced s
|
||||
WHERE
|
||||
s.seq = (SELECT MIN(x.seq) FROM sequenced x)
|
||||
UNION ALL
|
||||
-- 🔁 Recursive step: process next in sequence
|
||||
SELECT
|
||||
c.entity,
|
||||
c.attr,
|
||||
o.seq,
|
||||
c.srtcode || '.' || to_char(o.srt,'FM000'),
|
||||
c.ds || CASE WHEN o.DOMAIN = 'Product' THEN '.' || o.attr ELSE '' END,
|
||||
CASE WHEN o.DOMAIN = 'Channel' THEN o.attr ELSE c.chan END chan,
|
||||
CASE WHEN o.DOMAIN = 'Tier' THEN o.attr ELSE c.tier END tier,
|
||||
CASE WHEN o.DOMAIN = 'Volume' THEN o.attr ELSE c.vol END vol,
|
||||
o.func,
|
||||
o.val,
|
||||
CASE
|
||||
WHEN o.func = 'Price' THEN c.agg + o.val
|
||||
WHEN o.func = 'Factor' THEN c.agg + COALESCE(c.base, 0) * (o.val - 1)
|
||||
END agg,
|
||||
COALESCE(c.base, CASE WHEN o.func = 'Price' THEN o.val ELSE NULL::NUMERIC END) AS base,
|
||||
CASE WHEN (o.func = 'Price' AND o.val <> 0) OR (o.func = 'Factor' AND o.val <> 1) THEN
|
||||
c.math ||
|
||||
ARRAY[
|
||||
CASE
|
||||
WHEN o.func = 'Price' THEN
|
||||
RPAD(o.entity || ':' || o.attr, 17, ' ') || ' + ' || LPAD(to_char(o.val, 'FM9999999990.00000'), 10, ' ')
|
||||
WHEN o.func = 'Factor' THEN
|
||||
RPAD(o.entity || ':' || o.attr, 17, ' ') || ' + ' || LPAD(to_char(COALESCE(c.base, 0) * (o.val - 1), 'FM9999999990.00000'), 10, ' ')
|
||||
ELSE
|
||||
RPAD(o.entity || ':' || o.attr, 17, ' ') || ' ' || LPAD(to_char(o.val, 'FM9999999990.00000'), 10, ' ')
|
||||
END
|
||||
]
|
||||
ELSE
|
||||
c.math
|
||||
END math
|
||||
FROM
|
||||
combos c
|
||||
JOIN sequenced o ON
|
||||
o.seq = c.seq + 1
|
||||
)
|
||||
SELECT
|
||||
-- c.entity
|
||||
c.attr
|
||||
,c.seq
|
||||
,c.srtcode
|
||||
,'v1:'||SUBSTRING(c.ds,2,100) ds
|
||||
,c.chan
|
||||
,c.tier
|
||||
-- ,c.vol
|
||||
,CASE
|
||||
WHEN c.vol ~ '^[0-9]+-[0-9]+$' THEN
|
||||
int4range(
|
||||
split_part(c.vol, '-', 1)::int,
|
||||
split_part(c.vol, '-', 2)::int,
|
||||
'[)'
|
||||
)
|
||||
WHEN c.vol ~ '^[0-9]+$' THEN
|
||||
int4range(
|
||||
c.vol::int,
|
||||
NULL,
|
||||
'[)'
|
||||
)
|
||||
ELSE NULL
|
||||
END AS vol
|
||||
,c.func
|
||||
,c.val
|
||||
,c.agg
|
||||
,c.math
|
||||
,c.seq = (SELECT max(x.seq) FROM sequenced x) lastflag
|
||||
FROM
|
||||
combos c /*WHERE seq = (SELECT max(seq) FROM sequenced)*/
|
||||
ORDER BY
|
||||
c.srtcode ASC;
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
/*
|
||||
Anchor:EU170S50 + 0.08
|
||||
Color Tier:P x 1.30
|
||||
Branding: + 0.00
|
||||
Packaging:SLV + 0.00
|
||||
Suffix:PCR x 1.00
|
||||
Accessories: + 0.00
|
||||
Channel:WHS + 0.00
|
||||
Volume:8 x 1.00
|
||||
-----------------------
|
||||
0.104
|
||||
*/
|
184
esc.json
184
esc.json
@ -1,184 +0,0 @@
|
||||
[
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Anchor",
|
||||
"attr": "BM.03000",
|
||||
"val": 0.15,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "B",
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "C",
|
||||
"val": 1.2,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "L",
|
||||
"val": 1.1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "M",
|
||||
"val": 1.2,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "P",
|
||||
"val": 1.2,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Color Tier",
|
||||
"attr": "T",
|
||||
"val": 1.1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Branding",
|
||||
"attr": null,
|
||||
"val": 0,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Branding",
|
||||
"attr": "L",
|
||||
"val": 0.04,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Branding",
|
||||
"attr": "P",
|
||||
"val": 0.1,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Packaging",
|
||||
"attr": "BDL",
|
||||
"val": 0.005,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Packaging",
|
||||
"attr": "CSE",
|
||||
"val": 0.01,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Packaging",
|
||||
"attr": "PLT",
|
||||
"val": 0,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Packaging",
|
||||
"attr": "SLV",
|
||||
"val": 0.005,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Suffix",
|
||||
"attr": null,
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Accessories",
|
||||
"attr": null,
|
||||
"val": 0,
|
||||
"func": "Price"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Channel",
|
||||
"attr": "DIR",
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Channel",
|
||||
"attr": "DRP",
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Channel",
|
||||
"attr": "WHS",
|
||||
"val": 1.1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Tier",
|
||||
"attr": 1,
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Tier",
|
||||
"attr": 2,
|
||||
"val": 1.05,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Tier",
|
||||
"attr": 3,
|
||||
"val": 1.1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Volume",
|
||||
"attr": "00-01",
|
||||
"val": 1.35,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Volume",
|
||||
"attr": "01-08",
|
||||
"val": 1.1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Volume",
|
||||
"attr": "24",
|
||||
"val": 1,
|
||||
"func": "Factor"
|
||||
},
|
||||
{
|
||||
"folder": "01 Gal Can",
|
||||
"entity": "Volume",
|
||||
"attr": "08-24",
|
||||
"val": 1.05,
|
||||
"func": "Factor"
|
||||
}
|
||||
]
|
@ -7,22 +7,12 @@ EXEC pricing.single_price_call
|
||||
@vol = 9600;
|
||||
|
||||
EXEC pricing.single_price_call
|
||||
@bill = 'FARM0001',
|
||||
@ship = 'KEYB0001',
|
||||
@part = 'HCA10000B661100',
|
||||
@v1ds = 'v1:T..CSE..D',
|
||||
@bill = 'BFGS0001',
|
||||
@ship = 'BOBS0002',
|
||||
@part = 'HTI10754B12B024LXB04',
|
||||
@v1ds = 'v1:L.L.PLT..',
|
||||
@vol = 172000;
|
||||
|
||||
SELECT g.*
|
||||
FROM (SELECT
|
||||
TRY_CAST(.33275 AS NUMERIC(20,5)) AS tprice,
|
||||
TRY_CAST(.758 AS NUMERIC(20,5)) AS last_price_norm,
|
||||
TRY_CAST(null AS NUMERIC(20,5)) AS listprice_eff,
|
||||
TRY_CAST('2025-06-01' AS DATE) AS last_date
|
||||
) q
|
||||
CROSS APPLY pricing.guidance_logic(q.tprice, q.last_price_norm, q.listprice_eff, q.last_date, 0.05, 1.0, 1.0) g;
|
||||
|
||||
|
||||
SELECT * FROM pricing.lastpricedetail l WHERE customer = 'HYBELS' AND l.partgroup = 'HZP3E100'
|
||||
|
||||
SELECT * FROM pricing.pricelist_ranged pr WHERE pr.jcpart = 'XNS0T1G3G18B096' AND
|
||||
|
@ -1,80 +1,42 @@
|
||||
SELECT
|
||||
ui_json->'data'
|
||||
FROM pricequote.single_price_call(
|
||||
'FARM0001',
|
||||
'KEYB0001',
|
||||
'HCA10000B661100',
|
||||
'v1:T..CSE..D',
|
||||
50000
|
||||
) f;
|
||||
|
||||
|
||||
SELECT
|
||||
*, ui_json->'data'
|
||||
FROM pricequote.single_price_call(
|
||||
'BFGS0001',
|
||||
'SPRI0019',
|
||||
'INT12040G18C100',
|
||||
'v1:B..CSE..',
|
||||
7037
|
||||
) f;
|
||||
|
||||
|
||||
SELECT
|
||||
*, ui_json->'data'
|
||||
FROM pricequote.single_price_call(
|
||||
'BELL0039',
|
||||
'BELL0039',
|
||||
'XNS0T1G3X19B096PZBND',
|
||||
'v1:L.P.PLT..',
|
||||
82420
|
||||
) f;
|
||||
|
||||
SELECT * FROM rlarp.cust WHERE code = 'PACK0009'
|
||||
|
||||
-- SELECT * FROM pricequote.lastpricedetail l WHERE customer = 'HYBELS' AND l.partgroup = 'HZP3E100';
|
||||
--
|
||||
|
||||
SELECT
|
||||
q.billto, q.shipto, q.part, q.v1ds, q.units_each, pc.tprice, pc.tmath
|
||||
FROM
|
||||
pricequote.live_quotes q
|
||||
LEFT JOIN LATERAL pricequote.single_price_call(
|
||||
billto, shipto, part, v1ds, units_each
|
||||
) pc ON TRUE
|
||||
WHERE
|
||||
qid = 113761
|
||||
-- AND qrn = 4;
|
||||
|
||||
create table pricequote.target_prices_base as (
|
||||
with
|
||||
expand AS (
|
||||
SELECT
|
||||
c.compset,
|
||||
c.stlc,
|
||||
c.floor,
|
||||
b.ds,
|
||||
b.chan,
|
||||
b.tier,
|
||||
b.vol,
|
||||
b.val,
|
||||
b.price,
|
||||
json_pretty(to_json(b.math)) math
|
||||
FROM
|
||||
pricequote.core_target c
|
||||
LEFT JOIN LATERAL pricequote.build_pricing_path_base (options||jsonb_build_object('entity','Anchor','attr',c.stlc,'val',c.floor,'func','Price')) b ON b.lastflag
|
||||
)
|
||||
-- select count(*) from expand
|
||||
select * from expand
|
||||
) with data;
|
||||
ui_json->'details'
|
||||
FROM pricequote.single_price_call(
|
||||
'FARM0001',
|
||||
'KEYB0001',
|
||||
'HZP3E103E21D050',
|
||||
'v1:C..BDL..',
|
||||
50000
|
||||
) f
|
||||
|
||||
-- SELECT
|
||||
-- stlc, ds, chan, tier, vol, price, math
|
||||
-- FROM
|
||||
-- pricequote.build_pricing_path_base(
|
||||
-- $$
|
||||
-- [{"folder":"01 Gal Can","entity":"Anchor","attr":"BM.03000","val":0.15,"func":"Price"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"B","val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"C","val":1.2,"func":"Factor"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"L","val":1.1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"M","val":1.2,"func":"Factor"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"P","val":1.2,"func":"Factor"},{"folder":"01 Gal Can","entity":"Color Tier","attr":"T","val":1.1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Branding","attr":null,"val":0,"func":"Price"},{"folder":"01 Gal Can","entity":"Branding","attr":"L","val":0.04,"func":"Price"},{"folder":"01 Gal Can","entity":"Branding","attr":"P","val":0.1,"func":"Price"},{"folder":"01 Gal Can","entity":"Packaging","attr":"BDL","val":0.005,"func":"Price"},{"folder":"01 Gal Can","entity":"Packaging","attr":"CSE","val":0.01,"func":"Price"},{"folder":"01 Gal Can","entity":"Packaging","attr":"PLT","val":0,"func":"Price"},{"folder":"01 Gal Can","entity":"Packaging","attr":"SLV","val":0.005,"func":"Price"},{"folder":"01 Gal Can","entity":"Suffix","attr":null,"val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Accessories","attr":null,"val":0,"func":"Price"},{"folder":"01 Gal Can","entity":"Channel","attr":"DIR","val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Channel","attr":"DRP","val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Channel","attr":"WHS","val":1.1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Tier","attr":1,"val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Tier","attr":2,"val":1.05,"func":"Factor"},{"folder":"01 Gal Can","entity":"Tier","attr":3,"val":1.1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Volume","attr":"00-01","val":1.35,"func":"Factor"},{"folder":"01 Gal Can","entity":"Volume","attr":"01-08","val":1.1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Volume","attr":"24","val":1,"func":"Factor"},{"folder":"01 Gal Can","entity":"Volume","attr":"08-24","val":1.05,"func":"Factor"}]
|
||||
-- $$
|
||||
-- )
|
||||
-- WHERE
|
||||
-- lastflag
|
||||
SELECT
|
||||
ui_json->'details'
|
||||
FROM pricequote.single_price_call(
|
||||
'FARM0001',
|
||||
'KEYB0001',
|
||||
'HZP3E103E21D050',
|
||||
'v1:C..BDL..',
|
||||
50000
|
||||
) f
|
||||
|
||||
SELECT
|
||||
ui_json
|
||||
FROM pricequote.single_price_call(
|
||||
'BFGS0001',
|
||||
'BOBS0002',
|
||||
'HTI10754B12B024LXB04',
|
||||
'v1:L.L.PLT..',
|
||||
172000
|
||||
) f
|
||||
|
||||
SELECT * FROM pricequote.lastpricedetail l WHERE customer = 'HYBELS' AND l.partgroup = 'HZP3E100'
|
||||
|
||||
SELECT
|
||||
pc.expl
|
||||
FROM
|
||||
pricequote.live_quotes
|
||||
LEFT JOIN LATERAL pricequote.single_price_call(
|
||||
billto, shipto, part, v1ds, units_each
|
||||
) pc ON TRUE
|
||||
WHERE
|
||||
qid = 113278
|
||||
AND qrn = 5
|
@ -1,81 +0,0 @@
|
||||
-- CREATE OR ALTER FUNCTION pricing.approval_logic;
|
||||
|
||||
CREATE OR ALTER FUNCTION pricing.approval_logic(
|
||||
@target numeric(20,5),
|
||||
@last_norm numeric(20,5),
|
||||
@list_eff numeric(20,5),
|
||||
@last_date date,
|
||||
@floor_pct numeric(10,5) = 0.95,
|
||||
@cap_last_pct numeric(10,5) = 1.00,
|
||||
@cap_list_pct numeric(10,5) = 1.00
|
||||
)
|
||||
RETURNS @ret TABLE (
|
||||
approval_price numeric(20,5),
|
||||
approval_reason nvarchar(4000)
|
||||
)
|
||||
AS
|
||||
BEGIN
|
||||
DECLARE
|
||||
@base_price numeric(20,5), -- starting point (target if available, else last_norm, else list_eff)
|
||||
@after_floor numeric(20,5), -- base but limited to x% lower than last price
|
||||
@after_cap_last numeric(20,5), -- previous step but limited to x% higher than last price
|
||||
@final_price numeric(20,5), -- previous step but limited to x% higher than list price
|
||||
@reason nvarchar(4000) = N''; -- logic source of price
|
||||
|
||||
-- Early exit if nothing to work with
|
||||
IF @target IS NULL AND @last_norm IS NULL AND @list_eff IS NULL
|
||||
BEGIN
|
||||
INSERT INTO @ret VALUES (NULL, N'No target, last, or list available');
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
-- Pick starting base price
|
||||
SET @base_price = COALESCE(@target, @last_norm, @list_eff);
|
||||
|
||||
-- Step 1: use base price unless it's more than x% below last price
|
||||
SET @after_floor = @base_price;
|
||||
IF @last_norm IS NOT NULL
|
||||
SET @after_floor = ROUND(
|
||||
CASE WHEN @base_price >= @last_norm*@floor_pct
|
||||
THEN @base_price
|
||||
ELSE @last_norm*@floor_pct
|
||||
END, 5);
|
||||
|
||||
-- Step 2: use price from previous step but don't allow it to be x% above last price
|
||||
SET @after_cap_last = @after_floor;
|
||||
IF @last_norm IS NOT NULL
|
||||
SET @after_cap_last = ROUND(
|
||||
CASE WHEN @after_floor <= @last_norm*@cap_last_pct
|
||||
THEN @after_floor
|
||||
ELSE @last_norm*@cap_last_pct
|
||||
END, 5);
|
||||
|
||||
-- Step 3: use price from last step, but don't allow it to be more than x% above list price
|
||||
SET @final_price = @after_cap_last;
|
||||
IF @list_eff IS NOT NULL
|
||||
SET @final_price = ROUND(
|
||||
CASE WHEN @after_cap_last <= @list_eff*@cap_list_pct
|
||||
THEN @after_cap_last
|
||||
ELSE @list_eff*@cap_list_pct
|
||||
END, 5);
|
||||
|
||||
-- Reason text
|
||||
SET @reason =
|
||||
CASE
|
||||
WHEN @target IS NOT NULL THEN N'Using target price'
|
||||
WHEN @last_norm IS NOT NULL THEN N'Using last price as base'
|
||||
WHEN @list_eff IS NOT NULL THEN N'Using list price as base'
|
||||
END;
|
||||
|
||||
IF @last_norm IS NOT NULL AND @after_floor > @base_price
|
||||
SET @reason = N'Last price drop limit';
|
||||
|
||||
IF @last_norm IS NOT NULL AND @after_cap_last < @after_floor
|
||||
SET @reason = N'Last price increase limit';
|
||||
|
||||
IF @list_eff IS NOT NULL AND @final_price < @after_cap_last
|
||||
SET @reason = N'List price ceiling';
|
||||
|
||||
INSERT INTO @ret VALUES (@final_price, @reason);
|
||||
RETURN;
|
||||
END;
|
@ -1,72 +0,0 @@
|
||||
CREATE OR REPLACE FUNCTION pricequote.approval_logic(
|
||||
_target numeric(20,5),
|
||||
_last_norm numeric(20,5),
|
||||
_list_eff numeric(20,5),
|
||||
_last_date date,
|
||||
_floor_pct numeric(10,5) DEFAULT 0.95,
|
||||
_cap_last_pct numeric(10,5) DEFAULT 1.00,
|
||||
_cap_list_pct numeric(10,5) DEFAULT 1.00
|
||||
)
|
||||
RETURNS TABLE (
|
||||
approval_price numeric(20,5),
|
||||
approval_reason text
|
||||
)
|
||||
LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
base_price numeric(20,5);
|
||||
after_floor numeric(20,5);
|
||||
after_cap_last numeric(20,5);
|
||||
final_price numeric(20,5);
|
||||
reason text := '';
|
||||
BEGIN
|
||||
|
||||
-- Early exit if nothing to work with
|
||||
IF _target IS NULL AND _last_norm IS NULL AND _list_eff IS NULL THEN
|
||||
RETURN QUERY SELECT NULL::numeric, 'No target, last, or list available';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
|
||||
-- Pick starting base price
|
||||
base_price := COALESCE(_target, _last_norm, _list_eff);
|
||||
|
||||
-- Step 1: use base price unless it's more than x% below last price
|
||||
after_floor := base_price;
|
||||
IF _last_norm IS NOT NULL THEN
|
||||
after_floor := GREATEST(base_price, ROUND(_last_norm*_floor_pct,5));
|
||||
END IF;
|
||||
|
||||
-- Step 2: use price from previous step but don't allow it to be x% above last price
|
||||
after_cap_last := after_floor;
|
||||
IF _last_norm IS NOT NULL THEN
|
||||
after_cap_last := LEAST(after_floor, ROUND(_last_norm*_cap_last_pct,5));
|
||||
END IF;
|
||||
|
||||
-- cap to list (binds if it lowers price)
|
||||
final_price := after_cap_last;
|
||||
IF _list_eff IS NOT NULL THEN
|
||||
final_price := LEAST(after_cap_last, ROUND(_list_eff*_cap_list_pct,5));
|
||||
END IF;
|
||||
|
||||
-- Reason text
|
||||
reason :=
|
||||
CASE
|
||||
WHEN _target IS NOT NULL THEN 'Using target price'
|
||||
WHEN _last_norm IS NOT NULL THEN 'Using last price as base'
|
||||
WHEN _list_eff IS NOT NULL THEN 'Using list price as base'
|
||||
END;
|
||||
|
||||
IF _last_norm IS NOT NULL AND after_floor > base_price THEN
|
||||
reason := 'Last price drop limit';
|
||||
END IF;
|
||||
|
||||
IF _last_norm IS NOT NULL AND after_cap_last < after_floor THEN
|
||||
reason := 'Last price increase limit';
|
||||
END IF;
|
||||
|
||||
IF _list_eff IS NOT NULL AND final_price < after_cap_last THEN
|
||||
reason := 'List price ceiling';
|
||||
END IF;
|
||||
|
||||
RETURN QUERY SELECT final_price, reason;
|
||||
END $$;
|
@ -1,61 +1,119 @@
|
||||
-- CREATE OR ALTER FUNCTION pricing.guidance_logic;
|
||||
|
||||
CREATE OR ALTER FUNCTION pricing.guidance_logic(
|
||||
@target numeric(20,5),
|
||||
@last_norm numeric(20,5),
|
||||
@list_eff numeric(20,5),
|
||||
@last_date date,
|
||||
@floor_pct numeric(10,5) = 0.95,
|
||||
@cap_last_pct numeric(10,5) = 1.00,
|
||||
@cap_list_pct numeric(10,5) = 1.00
|
||||
-- This function returns the least of two NUMERIC(20,5) values.
|
||||
CREATE OR ALTER FUNCTION dbo.LEAST_NUMERIC205(
|
||||
@a NUMERIC(20,5),
|
||||
@b NUMERIC(20,5)
|
||||
)
|
||||
RETURNS @ret TABLE (
|
||||
guidance_price numeric(20,5),
|
||||
guidance_reason nvarchar(4000)
|
||||
RETURNS NUMERIC(20,5)
|
||||
AS
|
||||
BEGIN
|
||||
RETURN CASE
|
||||
WHEN @a IS NULL THEN @b
|
||||
WHEN @b IS NULL THEN @a
|
||||
WHEN @a < @b THEN @a ELSE @b
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
-- This function returns the greatest of two NUMERIC(20,5) values.
|
||||
CREATE OR ALTER FUNCTION dbo.GREATEST_NUMERIC205(
|
||||
@a NUMERIC(20,5),
|
||||
@b NUMERIC(20,5)
|
||||
)
|
||||
RETURNS NUMERIC(20,5)
|
||||
AS
|
||||
BEGIN
|
||||
RETURN CASE
|
||||
WHEN @a IS NULL THEN @b
|
||||
WHEN @b IS NULL THEN @a
|
||||
WHEN @a > @b THEN @a ELSE @b
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
-- This function implements the guidance logic for pricing based on target, last, and list prices.
|
||||
CREATE OR ALTER FUNCTION pricing.guidance_logic (
|
||||
@target_price NUMERIC(20,5),
|
||||
@last_price NUMERIC(20,5),
|
||||
@list_price NUMERIC(20,5),
|
||||
@last_date DATE
|
||||
)
|
||||
RETURNS @result TABLE (
|
||||
guidance_price NUMERIC(20,5),
|
||||
guidance_reason NVARCHAR(MAX)
|
||||
)
|
||||
AS
|
||||
BEGIN
|
||||
DECLARE
|
||||
@base_price numeric(20,5), -- starting point (target if available, else last_norm, else list_eff)
|
||||
@after_floor numeric(20,5), -- base but limited to x% lower than last price
|
||||
@after_cap_last numeric(20,5), -- previous step but limited to x% higher than last price
|
||||
@final_price numeric(20,5), -- previous step but limited to x% higher than list price
|
||||
@reason nvarchar(4000) = N''; -- logic source of price
|
||||
DECLARE @price NUMERIC(20,5);
|
||||
DECLARE @reason NVARCHAR(MAX) = '';
|
||||
DECLARE @floored NUMERIC(20,5);
|
||||
DECLARE @capped NUMERIC(20,5);
|
||||
DECLARE @use_last_price BIT = 0;
|
||||
|
||||
-- Early exit if nothing to work with
|
||||
IF @target IS NULL AND @last_norm IS NULL AND @list_eff IS NULL
|
||||
-- Determine if last price is recent (within last 2 years)
|
||||
IF @last_price IS NOT NULL AND @last_date IS NOT NULL AND @last_date > DATEADD(YEAR, -2, CAST(GETDATE() AS DATE))
|
||||
SET @use_last_price = 1;
|
||||
|
||||
IF @target_price IS NOT NULL AND @use_last_price = 1
|
||||
BEGIN
|
||||
INSERT INTO @ret VALUES (NULL, N'No target, last, or list available');
|
||||
RETURN;
|
||||
END;
|
||||
SET @floored = dbo.GREATEST_NUMERIC205(@target_price, @last_price * 0.95);
|
||||
SET @capped = dbo.LEAST_NUMERIC205(@floored, @last_price);
|
||||
SET @price = dbo.LEAST_NUMERIC205(ISNULL(@list_price, 1e9), @capped);
|
||||
|
||||
-- Pick starting base price
|
||||
SET @base_price = COALESCE(@target, @last_norm, @list_eff);
|
||||
-- Reason text
|
||||
SET @reason =
|
||||
CASE
|
||||
WHEN @target IS NOT NULL THEN N'Using target price'
|
||||
WHEN @last_norm IS NOT NULL THEN N'Using last price as base'
|
||||
WHEN @list_eff IS NOT NULL THEN N'Using list price as base'
|
||||
END;
|
||||
|
||||
-- Step 1: use base price less than last price, use last price
|
||||
IF @base_price < @last_norm
|
||||
IF @price = @last_price
|
||||
BEGIN
|
||||
SET @reason = 'Cap at last price';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @reason = 'Using target price';
|
||||
IF @target_price < @last_price * 0.95
|
||||
SET @reason += ', floored to 5% below last price';
|
||||
IF @target_price > @last_price
|
||||
SET @reason += ', capped to not exceed last price';
|
||||
IF @list_price IS NOT NULL AND @price = @list_price AND @target_price > @list_price
|
||||
SET @reason += ', capped to not exceed list price';
|
||||
END
|
||||
END
|
||||
ELSE IF @use_last_price = 1
|
||||
BEGIN
|
||||
SET @base_price = @last_norm;
|
||||
SET @reason = N'Last price';
|
||||
SET @price = @last_price;
|
||||
SET @reason = 'Last price - no target';
|
||||
END
|
||||
ELSE IF @target_price IS NOT NULL
|
||||
BEGIN
|
||||
SET @price = @target_price;
|
||||
IF @last_price IS NOT NULL AND @last_date IS NOT NULL
|
||||
BEGIN
|
||||
SET @reason = CONCAT(
|
||||
'Last price ignored (too old: ',
|
||||
CONVERT(NVARCHAR(10), @last_date, 120),
|
||||
'), using target price'
|
||||
);
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @reason = 'Target price - no prior sale';
|
||||
END
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @price = NULL;
|
||||
IF @last_price IS NOT NULL AND @last_date IS NOT NULL
|
||||
BEGIN
|
||||
SET @reason = CONCAT(
|
||||
'Last price ignored (too old: ',
|
||||
CONVERT(NVARCHAR(10), @last_date, 120),
|
||||
'), no pricing available'
|
||||
);
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @reason = 'No pricing available';
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
-- Step 2: use price from previous step but don't allow it to be x% above last price
|
||||
IF @base_price > @list_eff
|
||||
BEGIN
|
||||
SET @base_price = @list_eff;
|
||||
SET @reason = N'List price ceiling';
|
||||
END
|
||||
|
||||
SET @final_price = @base_price;
|
||||
|
||||
INSERT INTO @ret VALUES (@final_price, @reason);
|
||||
INSERT INTO @result VALUES (@price, @reason);
|
||||
RETURN;
|
||||
END;
|
||||
END
|
||||
GO
|
@ -1,56 +1,76 @@
|
||||
CREATE OR REPLACE FUNCTION pricequote.guidance_logic(
|
||||
_target numeric(20,5),
|
||||
_last_norm numeric(20,5),
|
||||
_list_eff numeric(20,5),
|
||||
_last_date date,
|
||||
_floor_pct numeric(10,5) DEFAULT 0.95,
|
||||
_cap_last_pct numeric(10,5) DEFAULT 1.00,
|
||||
_cap_list_pct numeric(10,5) DEFAULT 1.00
|
||||
_target_price NUMERIC(20,5),
|
||||
_last_price NUMERIC(20,5),
|
||||
_list_price NUMERIC(20,5),
|
||||
_last_date DATE
|
||||
)
|
||||
RETURNS TABLE (
|
||||
guidance_price numeric(20,5),
|
||||
guidance_reason text
|
||||
)
|
||||
LANGUAGE plpgsql AS $$
|
||||
guidance_price NUMERIC(20,5),
|
||||
guidance_reason TEXT
|
||||
) AS $$
|
||||
DECLARE
|
||||
base_price numeric(20,5);
|
||||
after_floor numeric(20,5);
|
||||
after_cap_last numeric(20,5);
|
||||
final_price numeric(20,5);
|
||||
reason text := '';
|
||||
|
||||
_price NUMERIC(20,5);
|
||||
_reason TEXT := '';
|
||||
_floored NUMERIC(20,5);
|
||||
_capped NUMERIC(20,5);
|
||||
_use_last_price BOOLEAN := FALSE;
|
||||
BEGIN
|
||||
|
||||
-- Early exit if nothing to work with
|
||||
IF _target IS NULL AND _last_norm IS NULL AND _list_eff IS NULL THEN
|
||||
RETURN QUERY SELECT NULL::numeric, 'No target, last, or list available';
|
||||
RETURN;
|
||||
-- Evaluate whether last price is recent enough
|
||||
IF _last_price IS NOT NULL AND _last_date IS NOT NULL AND _last_date > CURRENT_DATE - INTERVAL '2 years' THEN
|
||||
_use_last_price := TRUE;
|
||||
END IF;
|
||||
|
||||
IF _target_price IS NOT NULL AND _use_last_price THEN
|
||||
_floored := GREATEST(_target_price, _last_price * 0.95);
|
||||
_capped := LEAST(_floored, _last_price);
|
||||
_price := LEAST(COALESCE(_list_price, 1e9), _capped);
|
||||
|
||||
-- Pick starting base price
|
||||
base_price := COALESCE(_target, _last_norm, _list_eff);
|
||||
-- Reason text
|
||||
reason :=
|
||||
CASE
|
||||
WHEN _target IS NOT NULL THEN 'Using target price'
|
||||
WHEN _last_norm IS NOT NULL THEN 'Using last price as base'
|
||||
WHEN _list_eff IS NOT NULL THEN 'Using list price as base'
|
||||
END;
|
||||
IF _price = _last_price THEN
|
||||
_reason := 'Cap at last price';
|
||||
ELSE
|
||||
_reason := 'Using target price';
|
||||
IF _target_price < _last_price * 0.95 THEN
|
||||
_reason := _reason || ', floored to 5% below last price';
|
||||
END IF;
|
||||
IF _target_price > _last_price THEN
|
||||
_reason := _reason || ', capped to not exceed last price';
|
||||
END IF;
|
||||
IF _list_price IS NOT NULL AND _price = _list_price AND _target_price > _list_price THEN
|
||||
_reason := _reason || ', capped to not exceed list price';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Step 1: use base price less than last price, use last price
|
||||
IF base_price < _last_norm THEN
|
||||
base_price := _last_norm;
|
||||
reason := 'Last price';
|
||||
ELSIF _use_last_price THEN
|
||||
_price := _last_price;
|
||||
_reason := 'Last price - no target';
|
||||
|
||||
ELSIF _target_price IS NOT NULL THEN
|
||||
_price := _target_price;
|
||||
|
||||
IF _last_price IS NOT NULL AND _last_date IS NOT NULL THEN
|
||||
_reason := format(
|
||||
-- 'Last price ignored (too old: %s), using target price',
|
||||
'Last price ignored, using target price',
|
||||
to_char(_last_date, 'YYYY-MM-DD')
|
||||
);
|
||||
ELSE
|
||||
_reason := 'Target price - no prior sale';
|
||||
END IF;
|
||||
|
||||
ELSE
|
||||
_price := NULL;
|
||||
|
||||
IF _last_price IS NOT NULL AND _last_date IS NOT NULL THEN
|
||||
_reason := format(
|
||||
-- 'Last price ignored (too old: %s), no pricing available',
|
||||
'Last price too old, no pricing available',
|
||||
to_char(_last_date, 'YYYY-MM-DD')
|
||||
);
|
||||
ELSE
|
||||
_reason := 'No pricing available';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Step 2: use price from previous step but don't allow it to be x% above last price
|
||||
IF base_price > _list_eff THEN
|
||||
base_price := _list_eff;
|
||||
reason := 'List price ceiling';
|
||||
END IF;
|
||||
|
||||
final_price := base_price;
|
||||
|
||||
RETURN QUERY SELECT final_price, reason;
|
||||
END $$;
|
||||
RETURN QUERY SELECT _price, _reason;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
@ -15,7 +15,7 @@ RETURNS @result TABLE (
|
||||
)
|
||||
AS
|
||||
BEGIN
|
||||
DECLARE @age_threshold DATE = DATEADD(month, -18, GETDATE());
|
||||
DECLARE @age_threshold DATE = DATEADD(year, -1, GETDATE());
|
||||
|
||||
-- Extract all relevant objects from JSON
|
||||
DECLARE @dsq NVARCHAR(MAX), @dss NVARCHAR(MAX), @mrq NVARCHAR(MAX), @mrs NVARCHAR(MAX);
|
||||
@ -51,57 +51,39 @@ BEGIN
|
||||
|
||||
-- Use the same selection logic as before
|
||||
-- 1. Prefer the most recent of dss/dsq if either is within the age threshold
|
||||
IF (@dss_date IS NOT NULL AND @dss_date > @age_threshold)
|
||||
BEGIN
|
||||
INSERT INTO @result VALUES (@dss_price, 'dss', @dss_date, @dss_qty, @dss_dataseg, @dss_ord, @dss_quote, @dss_part);
|
||||
RETURN;
|
||||
END
|
||||
-- IF (@dsq_date IS NOT NULL AND @dsq_date > @age_threshold)
|
||||
-- OR (@dss_date IS NOT NULL AND @dss_date > @age_threshold)
|
||||
-- BEGIN
|
||||
-- IF @dsq_date IS NOT NULL AND (@dss_date IS NULL OR @dsq_date >= @dss_date) AND @dsq_date > @age_threshold
|
||||
-- INSERT INTO @result VALUES (@dsq_price, 'dsq', @dsq_date, @dsq_qty, @dsq_dataseg, @dsq_ord, @dsq_quote, @dsq_part);
|
||||
-- ELSE IF @dss_date IS NOT NULL AND @dss_date > @age_threshold
|
||||
-- INSERT INTO @result VALUES (@dss_price, 'dss', @dss_date, @dss_qty, @dss_dataseg, @dss_ord, @dss_quote, @dss_part);
|
||||
-- RETURN;
|
||||
-- END
|
||||
IF (@dsq_date IS NOT NULL AND @dsq_date > @age_threshold)
|
||||
OR (@dss_date IS NOT NULL AND @dss_date > @age_threshold)
|
||||
BEGIN
|
||||
IF @dsq_date IS NOT NULL AND (@dss_date IS NULL OR @dsq_date >= @dss_date) AND @dsq_date > @age_threshold
|
||||
INSERT INTO @result VALUES (@dsq_price, 'dsq', @dsq_date, @dsq_qty, @dsq_dataseg, @dsq_ord, @dsq_quote, @dsq_part);
|
||||
ELSE IF @dss_date IS NOT NULL AND @dss_date > @age_threshold
|
||||
INSERT INTO @result VALUES (@dss_price, 'dss', @dss_date, @dss_qty, @dss_dataseg, @dss_ord, @dss_quote, @dss_part);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- 2. If both dss/dsq are older than the threshold, use the most recent of mrs/mrq if either exists
|
||||
IF @mrs_date IS NOT NULL AND @mrs_date > @age_threshold
|
||||
BEGIN
|
||||
INSERT INTO @result VALUES (@mrs_price, 'mrs', @mrs_date, @mrs_qty, @mrs_dataseg, @mrs_ord, @mrs_quote, @mrs_part);
|
||||
RETURN;
|
||||
END
|
||||
-- IF (@mrq_date IS NOT NULL OR @mrs_date IS NOT NULL)
|
||||
-- BEGIN
|
||||
-- IF @mrq_date IS NOT NULL AND (@mrs_date IS NULL OR @mrq_date >= @mrs_date)
|
||||
-- INSERT INTO @result VALUES (@mrq_price, 'mrq', @mrq_date, @mrq_qty, @mrq_dataseg, @mrq_ord, @mrq_quote, @mrq_part);
|
||||
-- ELSE IF @mrs_date IS NOT NULL
|
||||
-- INSERT INTO @result VALUES (@mrs_price, 'mrs', @mrs_date, @mrs_qty, @mrs_dataseg, @mrs_ord, @mrs_quote, @mrs_part);
|
||||
-- RETURN;
|
||||
-- END
|
||||
IF (@mrq_date IS NOT NULL OR @mrs_date IS NOT NULL)
|
||||
BEGIN
|
||||
IF @mrq_date IS NOT NULL AND (@mrs_date IS NULL OR @mrq_date >= @mrs_date)
|
||||
INSERT INTO @result VALUES (@mrq_price, 'mrq', @mrq_date, @mrq_qty, @mrq_dataseg, @mrq_ord, @mrq_quote, @mrq_part);
|
||||
ELSE IF @mrs_date IS NOT NULL
|
||||
INSERT INTO @result VALUES (@mrs_price, 'mrs', @mrs_date, @mrs_qty, @mrs_dataseg, @mrs_ord, @mrs_quote, @mrs_part);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- 3. If all are at least as old as the threshold, pick the least oldest price available
|
||||
-- DECLARE
|
||||
-- @best_price NUMERIC(20,5) = NULL
|
||||
-- ,@best_source NVARCHAR(10) = NULL
|
||||
-- ,@best_date DATE = NULL
|
||||
-- ,@best_qty NUMERIC(20,5) = NULL
|
||||
-- ,@best_dataseg NVARCHAR(100) = NULL
|
||||
-- ,@best_ord NVARCHAR(20) = NULL
|
||||
-- ,@best_quote NVARCHAR(20) = NULL
|
||||
-- ,@best_part NVARCHAR(100) = NULL;
|
||||
-- IF @dsq_date IS NOT NULL
|
||||
-- SELECT @best_price = @dsq_price, @best_source = 'dsq', @best_date = @dsq_date, @best_qty = @dsq_qty, @best_dataseg = @dsq_dataseg, @best_ord = @dsq_ord, @best_quote = @dsq_quote, @best_part = @dsq_part;
|
||||
-- IF @dss_date IS NOT NULL AND (@best_date IS NULL OR @dss_date > @best_date)
|
||||
-- SELECT @best_price = @dss_price, @best_source = 'dss', @best_date = @dss_date, @best_qty = @dss_qty, @best_dataseg = @dss_dataseg, @best_ord = @dss_ord, @best_quote = @dss_quote, @best_part = @dss_part;
|
||||
-- IF @mrq_date IS NOT NULL AND (@best_date IS NULL OR @mrq_date > @best_date)
|
||||
-- SELECT @best_price = @mrq_price, @best_source = 'mrq', @best_date = @mrq_date, @best_qty = @mrq_qty, @best_dataseg = @mrq_dataseg, @best_ord = @mrq_ord, @best_quote = @mrq_quote, @best_part = @mrq_part;
|
||||
-- IF @mrs_date IS NOT NULL AND (@best_date IS NULL OR @mrs_date > @best_date)
|
||||
-- SELECT @best_price = @mrs_price, @best_source = 'mrs', @best_date = @mrs_date, @best_qty = @mrs_qty, @best_dataseg = @mrs_dataseg, @best_ord = @mrs_ord, @best_quote = @mrs_quote, @best_part = @mrs_part;
|
||||
DECLARE @best_price NUMERIC(20,5) = NULL, @best_source NVARCHAR(10) = NULL, @best_date DATE = NULL, @best_qty NUMERIC(20,5) = NULL, @best_dataseg NVARCHAR(100) = NULL, @best_ord NVARCHAR(20) = NULL, @best_quote NVARCHAR(20) = NULL, @best_part NVARCHAR(100) = NULL;
|
||||
IF @dsq_date IS NOT NULL
|
||||
SELECT @best_price = @dsq_price, @best_source = 'dsq', @best_date = @dsq_date, @best_qty = @dsq_qty, @best_dataseg = @dsq_dataseg, @best_ord = @dsq_ord, @best_quote = @dsq_quote, @best_part = @dsq_part;
|
||||
IF @dss_date IS NOT NULL AND (@best_date IS NULL OR @dss_date > @best_date)
|
||||
SELECT @best_price = @dss_price, @best_source = 'dss', @best_date = @dss_date, @best_qty = @dss_qty, @best_dataseg = @dss_dataseg, @best_ord = @dss_ord, @best_quote = @dss_quote, @best_part = @dss_part;
|
||||
IF @mrq_date IS NOT NULL AND (@best_date IS NULL OR @mrq_date > @best_date)
|
||||
SELECT @best_price = @mrq_price, @best_source = 'mrq', @best_date = @mrq_date, @best_qty = @mrq_qty, @best_dataseg = @mrq_dataseg, @best_ord = @mrq_ord, @best_quote = @mrq_quote, @best_part = @mrq_part;
|
||||
IF @mrs_date IS NOT NULL AND (@best_date IS NULL OR @mrs_date > @best_date)
|
||||
SELECT @best_price = @mrs_price, @best_source = 'mrs', @best_date = @mrs_date, @best_qty = @mrs_qty, @best_dataseg = @mrs_dataseg, @best_ord = @mrs_ord, @best_quote = @mrs_quote, @best_part = @mrs_part;
|
||||
|
||||
-- IF @best_price IS NOT NULL
|
||||
-- INSERT INTO @result VALUES (@best_price, @best_source, @best_date, @best_qty, @best_dataseg, @best_ord, @best_quote, @best_part);
|
||||
IF @best_price IS NOT NULL
|
||||
INSERT INTO @result VALUES (@best_price, @best_source, @best_date, @best_qty, @best_dataseg, @best_ord, @best_quote, @best_part);
|
||||
|
||||
RETURN;
|
||||
END
|
||||
|
@ -12,7 +12,7 @@ DECLARE
|
||||
BEGIN
|
||||
-- Central control for age threshold
|
||||
DECLARE
|
||||
age_threshold INTERVAL := INTERVAL '18 months';
|
||||
age_threshold INTERVAL := INTERVAL '1 year';
|
||||
dsq_date DATE := NULL;
|
||||
dss_date DATE := NULL;
|
||||
mrq_date DATE := NULL;
|
||||
@ -35,31 +35,40 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- 1. Prefer the most recent of dss/dsq if either is within the age threshold
|
||||
IF dss_date IS NOT NULL AND dss_date > (CURRENT_DATE - age_threshold) THEN
|
||||
result := dss || jsonb_build_object('source', 'dss');
|
||||
IF (dsq_date IS NOT NULL AND dsq_date > (CURRENT_DATE - age_threshold))
|
||||
OR (dss_date IS NOT NULL AND dss_date > (CURRENT_DATE - age_threshold)) THEN
|
||||
IF dsq_date IS NOT NULL AND (dss_date IS NULL OR dsq_date >= dss_date) AND dsq_date > (CURRENT_DATE - age_threshold) THEN
|
||||
result := dsq || jsonb_build_object('source', 'dsq');
|
||||
ELSIF dss_date IS NOT NULL AND dss_date > (CURRENT_DATE - age_threshold) THEN
|
||||
result := dss || jsonb_build_object('source', 'dss');
|
||||
END IF;
|
||||
-- 2. If both dss/dsq are older than the threshold, use the most recent of mrs/mrq if either exists
|
||||
ELSIF mrs_date IS NOT NULL AND mrs_date > (CURRENT_DATE - age_threshold) THEN
|
||||
result := mrs || jsonb_build_object('source', 'mrs');
|
||||
ELSIF (mrq_date IS NOT NULL OR mrs_date IS NOT NULL) THEN
|
||||
IF mrq_date IS NOT NULL AND (mrs_date IS NULL OR mrq_date >= mrs_date) THEN
|
||||
result := mrq || jsonb_build_object('source', 'mrq');
|
||||
ELSIF mrs_date IS NOT NULL THEN
|
||||
result := mrs || jsonb_build_object('source', 'mrs');
|
||||
END IF;
|
||||
-- 3. If all are at least as old as the threshold, pick the least oldest price available
|
||||
ELSE
|
||||
best := NULL;
|
||||
best_date := NULL;
|
||||
-- IF dsq_date IS NOT NULL THEN
|
||||
-- best := dsq || jsonb_build_object('source', 'dsq');
|
||||
-- best_date := dsq_date;
|
||||
-- END IF;
|
||||
-- IF dss_date IS NOT NULL AND (best_date IS NULL OR dss_date > best_date) THEN
|
||||
-- best := dss || jsonb_build_object('source', 'dss');
|
||||
-- best_date := dss_date;
|
||||
-- END IF;
|
||||
-- IF mrq_date IS NOT NULL AND (best_date IS NULL OR mrq_date > best_date) THEN
|
||||
-- best := mrq || jsonb_build_object('source', 'mrq');
|
||||
-- best_date := mrq_date;
|
||||
-- END IF;
|
||||
-- IF mrs_date IS NOT NULL AND (best_date IS NULL OR mrs_date > best_date) THEN
|
||||
-- best := mrs || jsonb_build_object('source', 'mrs');
|
||||
-- best_date := mrs_date;
|
||||
-- END IF;
|
||||
IF dsq_date IS NOT NULL THEN
|
||||
best := dsq || jsonb_build_object('source', 'dsq');
|
||||
best_date := dsq_date;
|
||||
END IF;
|
||||
IF dss_date IS NOT NULL AND (best_date IS NULL OR dss_date > best_date) THEN
|
||||
best := dss || jsonb_build_object('source', 'dss');
|
||||
best_date := dss_date;
|
||||
END IF;
|
||||
IF mrq_date IS NOT NULL AND (best_date IS NULL OR mrq_date > best_date) THEN
|
||||
best := mrq || jsonb_build_object('source', 'mrq');
|
||||
best_date := mrq_date;
|
||||
END IF;
|
||||
IF mrs_date IS NOT NULL AND (best_date IS NULL OR mrs_date > best_date) THEN
|
||||
best := mrs || jsonb_build_object('source', 'mrs');
|
||||
best_date := mrs_date;
|
||||
END IF;
|
||||
result := best;
|
||||
END IF;
|
||||
RETURN result;
|
||||
|
@ -46,8 +46,6 @@ CREATE TABLE pricequote.queue (
|
||||
list_relevance TEXT,
|
||||
guidance_price NUMERIC,
|
||||
guidance_reason TEXT,
|
||||
approval_price NUMERIC,
|
||||
approval_reason TEXT,
|
||||
expl JSONB,
|
||||
ui_json JSONB
|
||||
);
|
||||
@ -68,7 +66,6 @@ BEGIN
|
||||
-- 1) Seed queue from matrix
|
||||
--------------------------------------------------------------------
|
||||
DELETE FROM pricequote.queue;
|
||||
-- 1:30
|
||||
|
||||
INSERT INTO pricequote.queue (bill, ship, part, stlc, v1ds, vol, expl, ui_json)
|
||||
SELECT DISTINCT
|
||||
@ -87,7 +84,7 @@ BEGIN
|
||||
AND o.version IN ('Actual', 'Forecast', 'Quotes')
|
||||
AND o.part IS NOT NULL
|
||||
AND SUBSTRING(o.glec, 1, 1) <= '2';
|
||||
-- 2:12 0:38
|
||||
-- 46 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 2) Enrich: chan, tier, cust, pltq, plevel, partgroup (+stlc fix)
|
||||
@ -129,7 +126,7 @@ BEGIN
|
||||
plevel = s.plevel,
|
||||
partgroup = s.partgroup,
|
||||
stlc = COALESCE(q.stlc, s.stlc_fix);
|
||||
-- 4:51 0:17
|
||||
-- 16 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 3) Scenario fields from item master: part_v1ds, v0ds, orig costs
|
||||
@ -149,7 +146,7 @@ BEGIN
|
||||
END
|
||||
FROM "CMS.CUSLG".itemm i0
|
||||
WHERE i0.item = q.part;
|
||||
-- 3:21 0:20
|
||||
-- 16 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 4) History: store hist, extract last_* with precedence helper
|
||||
@ -187,7 +184,7 @@ BEGIN
|
||||
AND lp2.partgroup = q2.partgroup
|
||||
) AS x
|
||||
WHERE q.ctid = x.ctid;
|
||||
-- 7:32 1:31
|
||||
-- 2 min 3 sec
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 5) Target (requested v1ds): tprice, tmath, volume_range
|
||||
@ -197,14 +194,14 @@ BEGIN
|
||||
tprice = tp.price,
|
||||
tmath = to_json(tp.math),
|
||||
volume_range = tp.vol::TEXT
|
||||
FROM pricequote.target_prices_base tp
|
||||
FROM pricequote.target_prices tp
|
||||
WHERE
|
||||
tp.stlc = q.stlc
|
||||
AND tp.ds = q.v1ds
|
||||
AND tp.chan = q.chan
|
||||
AND tp.tier = q.tier
|
||||
AND FLOOR(q.vol / NULLIF(q.pltq, 0))::INT <@ tp.vol;
|
||||
-- 2:51 0:15
|
||||
-- 22 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 6) Target for last_dataseg (tprice_last)
|
||||
@ -212,15 +209,15 @@ BEGIN
|
||||
UPDATE pricequote.queue q
|
||||
SET
|
||||
tprice_last = tp2.price
|
||||
FROM pricequote.target_prices_base tp2
|
||||
FROM pricequote.target_prices tp2
|
||||
WHERE
|
||||
q.last_dataseg IS NOT NULL
|
||||
AND tp2.stlc = q.stlc
|
||||
AND tp2.ds = q.last_dataseg
|
||||
AND tp2.chan = q.chan
|
||||
AND tp2.tier = q.tier
|
||||
AND FLOOR(q.last_qty / NULLIF(q.pltq, 0))::INT <@ tp2.vol;
|
||||
-- 1:26 0:08
|
||||
AND FLOOR(q.vol / NULLIF(q.pltq, 0))::INT <@ tp2.vol;
|
||||
-- 17 sec
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 7) Cost data for requested v1ds and last_dataseg
|
||||
@ -253,7 +250,7 @@ BEGIN
|
||||
ON v0l.stlc = q2.stlc AND v0l.v0ds = q2.last_v0ds
|
||||
) AS s
|
||||
WHERE q.ctid = s.ctid;
|
||||
-- 4:15 0:25
|
||||
-- 28 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 8) List price (lowest valid); allow open-ended ranges (vb_to IS NULL)
|
||||
@ -283,7 +280,7 @@ BEGIN
|
||||
listcode = p.jcplcd
|
||||
FROM best_price p
|
||||
WHERE q.ctid = p.ctid;
|
||||
-- 2:48 0:18
|
||||
-- 18 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 9) Normalize last (when last_dataseg != v1ds) + effective list flags
|
||||
@ -300,7 +297,7 @@ BEGIN
|
||||
AND q.curstd_last IS NOT NULL
|
||||
AND q.curstd IS NOT NULL
|
||||
AND q.curstd_last <> 0
|
||||
THEN ROUND(q.curstd / q.curstd_last,5)
|
||||
THEN q.curstd / q.curstd_last
|
||||
END,
|
||||
last_premium_method = CASE
|
||||
WHEN q.last_isdiff IS NOT NULL
|
||||
@ -331,7 +328,7 @@ BEGIN
|
||||
END,
|
||||
listprice_eff = CASE WHEN q.customized <> '' THEN NULL ELSE q.listprice END,
|
||||
list_relevance = CASE WHEN q.customized <> '' THEN 'Ignore - Customized' ELSE '' END;
|
||||
-- 2:22 0:23
|
||||
-- 21 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 10) Guidance using normalized last + effective list
|
||||
@ -339,35 +336,22 @@ BEGIN
|
||||
UPDATE pricequote.queue q
|
||||
SET
|
||||
guidance_price = s.guidance_price,
|
||||
guidance_reason = s.guidance_reason,
|
||||
approval_price = s.approval_price,
|
||||
approval_reason = s.approval_reason
|
||||
guidance_reason = s.guidance_reason
|
||||
FROM (
|
||||
SELECT
|
||||
q2.ctid,
|
||||
g.guidance_price,
|
||||
g.guidance_reason,
|
||||
a.approval_price,
|
||||
a.approval_reason
|
||||
FROM
|
||||
pricequote.queue q2
|
||||
JOIN LATERAL pricequote.guidance_logic(
|
||||
q2.tprice,
|
||||
q2.last_price_norm,
|
||||
q2.listprice_eff,
|
||||
q2.last_date,
|
||||
1.0, 1.0, 1.0
|
||||
) g ON TRUE
|
||||
JOIN LATERAL pricequote.approval_logic(
|
||||
q2.tprice,
|
||||
q2.last_price_norm,
|
||||
q2.listprice_eff,
|
||||
q2.last_date,
|
||||
1.0, 1.0, 1.0
|
||||
) a ON TRUE
|
||||
g.guidance_reason
|
||||
FROM pricequote.queue q2
|
||||
JOIN LATERAL pricequote.guidance_logic(
|
||||
q2.tprice,
|
||||
q2.last_price_norm,
|
||||
q2.listprice_eff,
|
||||
q2.last_date
|
||||
) g ON TRUE
|
||||
) s
|
||||
WHERE q.ctid = s.ctid;
|
||||
-- 4:33 0:39
|
||||
-- 31 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 11) Build expl and ui_json identical to single_price_call
|
||||
@ -422,9 +406,7 @@ BEGIN
|
||||
'list_relevance', q.list_relevance
|
||||
),
|
||||
'guidance_price', q.guidance_price,
|
||||
'guidance_reason', q.guidance_reason,
|
||||
'approval_price', q.approval_price,
|
||||
'approval_reason', q.approval_reason
|
||||
'guidance_reason', q.guidance_reason
|
||||
),
|
||||
ui_json = jsonb_build_object(
|
||||
'details', jsonb_build_array(
|
||||
@ -515,7 +497,7 @@ BEGIN
|
||||
),
|
||||
'data', q.expl
|
||||
);
|
||||
-- 7:59 2:17
|
||||
-- 2 minutes 33 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 12) Merge back into matrix (store both expl and ui)
|
||||
@ -539,7 +521,7 @@ BEGIN
|
||||
AND o.version IN ('Actual', 'Forecast', 'Quotes')
|
||||
AND o.part IS NOT NULL
|
||||
AND SUBSTRING(o.glec, 1, 1) <= '2';
|
||||
-- 14:13 10:09
|
||||
-- 9 minutes 35 seconds
|
||||
|
||||
RAISE NOTICE 'Queue processing complete.';
|
||||
END;
|
||||
|
@ -136,8 +136,6 @@ BEGIN
|
||||
------------step 6 compute guidance------------
|
||||
guidance_price NUMERIC(20,5),
|
||||
guidance_reason NVARCHAR(MAX),
|
||||
approval_price NUMERIC(20,5),
|
||||
approval_reason NVARCHAR(MAX),
|
||||
------------step 7 build json------------------
|
||||
expl NVARCHAR(MAX),
|
||||
ui_json NVARCHAR(MAX)
|
||||
@ -175,7 +173,7 @@ BEGIN
|
||||
WHEN 'DIS' THEN bc.dba
|
||||
ELSE sc.dba
|
||||
END
|
||||
ELSE bc.dba
|
||||
ELSE q.bill
|
||||
END,
|
||||
pltq = i.mpck,
|
||||
plevel =
|
||||
@ -200,8 +198,8 @@ BEGIN
|
||||
calculated_pallets = FLOOR(q.vol / NULLIF(i.mpck, 0)),
|
||||
exact_pallets = CAST(ROUND(q.vol / NULLIF(i.mpck, 0), 5) AS NUMERIC(20,5))
|
||||
FROM @queue q
|
||||
LEFT JOIN pricing.cust bc ON bc.code = q.bill
|
||||
LEFT JOIN pricing.cust sc ON sc.code = q.ship
|
||||
LEFT JOIN rlarp.cust bc ON bc.code = q.bill
|
||||
LEFT JOIN rlarp.cust sc ON sc.code = q.ship
|
||||
LEFT JOIN CMSInterfaceIn.[CMS.CUSLG].itemm i ON i.item = q.part;
|
||||
|
||||
|
||||
@ -279,16 +277,16 @@ BEGIN
|
||||
,curstd_last = CASE WHEN last_isdiff = '' THEN q.curstd_orig ELSE COALESCE(v1l.curstdus, v0l.curstdus) END
|
||||
,futstd_last = CASE WHEN last_isdiff = '' THEN q.futstd_orig ELSE COALESCE(v1l.futstdus, v0l.futstdus) END
|
||||
FROM @queue q
|
||||
LEFT JOIN pricing.cost_v1ds v1 ON
|
||||
LEFT JOIN rlarp.cost_v1ds v1 ON
|
||||
v1.stlc = q.stlc
|
||||
AND v1.v1ds = q.v1ds
|
||||
LEFT JOIN pricing.cost_v0ds v0 ON
|
||||
LEFT JOIN rlarp.cost_v0ds v0 ON
|
||||
v0.stlc = q.stlc
|
||||
AND v0.v0ds = q.v0ds
|
||||
LEFT JOIN pricing.cost_v1ds v1l ON
|
||||
LEFT JOIN rlarp.cost_v1ds v1l ON
|
||||
v1l.stlc = q.stlc
|
||||
AND v1l.v1ds = q.last_dataseg
|
||||
LEFT JOIN pricing.cost_v0ds v0l ON
|
||||
LEFT JOIN rlarp.cost_v0ds v0l ON
|
||||
v0l.stlc = q.stlc
|
||||
AND v0l.v0ds = q.last_v0ds;
|
||||
|
||||
@ -358,7 +356,7 @@ BEGIN
|
||||
SET
|
||||
listcode = rp.jcplcd
|
||||
,listprice = rp.price
|
||||
,listprice_eff = CASE WHEN q.customized <> '' THEN NULL ELSE rp.price END
|
||||
,listprice_eff = CASE WHEN q.customized <> '' THEN NULL ELSE q.listprice END
|
||||
,list_relevance = CASE WHEN q.customized <> '' THEN 'Ignore - Customized' ELSE '' END
|
||||
,list_from = vb_from
|
||||
FROM @queue q
|
||||
@ -379,33 +377,13 @@ BEGIN
|
||||
SET
|
||||
guidance_price = g.guidance_price
|
||||
,guidance_reason = g.guidance_reason
|
||||
,approval_price = a.approval_price
|
||||
,approval_reason = a.approval_reason
|
||||
FROM @queue q
|
||||
CROSS APPLY pricing.guidance_logic(
|
||||
TRY_CAST(q.tprice AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.last_price_norm AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.listprice_eff AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.last_date AS DATE),
|
||||
--allowable price drop percent
|
||||
1.0,
|
||||
--cap on last price
|
||||
1.0,
|
||||
--cap on list percent
|
||||
1.0
|
||||
) g
|
||||
CROSS APPLY pricing.approval_logic(
|
||||
TRY_CAST(q.tprice AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.last_price_norm AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.listprice_eff AS NUMERIC(20,5)),
|
||||
TRY_CAST(q.last_date AS DATE),
|
||||
--allowable price drop percent
|
||||
1.0,
|
||||
--cap on last price
|
||||
1.0,
|
||||
--cap on list percent
|
||||
1.0
|
||||
) a;
|
||||
TRY_CAST(q.last_date AS DATE)
|
||||
) g;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 8: Assemble structured 'expl' JSON from populated columns.
|
||||
@ -452,8 +430,6 @@ BEGIN
|
||||
,q.list_relevance AS list_relevance
|
||||
,q.guidance_price AS guidance_price
|
||||
,q.guidance_reason AS guidance_reason
|
||||
,q.approval_price AS approval_price
|
||||
,q.approval_reason AS approval_reason
|
||||
-- JSON_QUERY(hist) AS [history]
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
)
|
||||
@ -468,28 +444,11 @@ BEGIN
|
||||
(
|
||||
SELECT
|
||||
panel.label,
|
||||
panel.detailLevel,
|
||||
JSON_QUERY(panel.details) AS details
|
||||
FROM (
|
||||
-- Cost
|
||||
SELECT
|
||||
'Cost' AS label,
|
||||
5 AS detailLevel,
|
||||
(
|
||||
SELECT
|
||||
'Current Std' AS label,
|
||||
5 AS detailLevel,
|
||||
COALESCE(q.curstd,0) AS value,
|
||||
'currency' AS type,
|
||||
'' AS note
|
||||
FOR JSON PATH
|
||||
) AS details
|
||||
|
||||
UNION ALL
|
||||
-- History Panel
|
||||
SELECT
|
||||
'History' AS label,
|
||||
1 as detailLevel,
|
||||
(
|
||||
SELECT
|
||||
----------------------label------------------------------------------------
|
||||
@ -499,14 +458,12 @@ BEGIN
|
||||
CASE ISNULL(q.last_source, '')
|
||||
WHEN 'mrq' THEN 'Similar Quote'
|
||||
WHEN 'mrs' THEN 'Similar Sale'
|
||||
WHEN 'dsq' THEN 'Last Quote'
|
||||
WHEN 'dss' THEN 'Last Sale'
|
||||
WHEN 'dsq' THEN 'Last Sale'
|
||||
WHEN 'dss' THEN 'Last Quote'
|
||||
ELSE ''
|
||||
END
|
||||
ELSE 'No Recent'
|
||||
END AS label,
|
||||
----------------------detail-----------------------------------------------
|
||||
1 AS detailLevel,
|
||||
----------------------value------------------------------------------------
|
||||
ISNULL(q.last_price, 0) AS value,
|
||||
----------------------type-------------------------------------------------
|
||||
@ -528,12 +485,8 @@ BEGIN
|
||||
END,
|
||||
ISNULL(' | ' + CONVERT(varchar(10), q.last_date, 120), ''),
|
||||
' | Qty ' + format(q.last_qty,'#,###'),
|
||||
CASE WHEN COALESCE(last_isdiff,'') <> ''
|
||||
THEN
|
||||
' | Normalized To: ' + cast(last_price_norm AS varchar(10))
|
||||
+ ' | ' /*+ q.last_premium_method*/ + ' Last Target = ' + format(q.tprice_last,'0.0####') + ' | Current Target = ' + format(q.tprice,'0.0####')
|
||||
ELSE ''
|
||||
END
|
||||
CASE WHEN last_isdiff <> '' THEN ' | Normalized To: ' + cast(last_price_norm AS varchar(10)) ELSE '' END,
|
||||
' | ' /*+ q.last_premium_method*/ + ' Last Target = ' + format(q.tprice_last,'0.0####') + ' | Current Target ' + format(q.tprice,'0.0####')
|
||||
)
|
||||
ELSE
|
||||
''
|
||||
@ -547,30 +500,25 @@ BEGIN
|
||||
-- List Panel
|
||||
SELECT
|
||||
'List' AS label,
|
||||
1 AS detailLevel,
|
||||
(
|
||||
SELECT
|
||||
COALESCE('Code: ' + q.listcode,'No List') AS label,
|
||||
1 AS detailLevel,
|
||||
COALESCE(q.listprice,0) AS value,
|
||||
'currency' AS type,
|
||||
COALESCE('List Min Qty: ' + format(q.list_from,'#,###'),'') + CASE WHEN q.list_relevance = '' THEN '' ELSE ' (' + q.list_relevance + ')' END AS note
|
||||
FOR JSON PATH
|
||||
) AS details
|
||||
)
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Target Support Panel
|
||||
SELECT
|
||||
'Target Calculation' AS label,
|
||||
5 AS detailLevel,
|
||||
(
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
----------------------label------------------------------------------------
|
||||
CASE WHEN value <> '' THEN replace(RTRIM(SUBSTRING(value,1,18)),'Anchor:', '') ELSE 'No Target' END AS label,
|
||||
----------------------detailLevel------------------------------------------
|
||||
10 AS detailLevel,
|
||||
----------------------value------------------------------------------------
|
||||
CASE WHEN value <> '' THEN
|
||||
TRY_CAST(SUBSTRING(value,23,7) AS NUMERIC(20,5))
|
||||
@ -595,8 +543,6 @@ BEGIN
|
||||
SELECT
|
||||
----------------------label------------------------------------------------
|
||||
'Target' AS label,
|
||||
----------------------detailLevel------------------------------------------
|
||||
5 AS detailLevel,
|
||||
----------------------value------------------------------------------------
|
||||
tprice AS value,
|
||||
----------------------type-------------------------------------------------
|
||||
@ -613,11 +559,9 @@ BEGIN
|
||||
-- Guidance Panel
|
||||
SELECT
|
||||
'Guidance' AS label,
|
||||
1 AS detailLevel,
|
||||
(
|
||||
SELECT
|
||||
'Price' AS label,
|
||||
1 AS detailLevel,
|
||||
COALESCE(q.guidance_price,0) AS value,
|
||||
'currency' AS type,
|
||||
q.guidance_reason AS note
|
||||
@ -634,5 +578,5 @@ BEGIN
|
||||
--------------------------------------------------------------------------------
|
||||
-- Final: Return all calculated fields and JSON payloads.
|
||||
--------------------------------------------------------------------------------
|
||||
SELECT guidance_price, ui_json FROM @queue;
|
||||
SELECT guidance_price, ui_json FROM @queue;
|
||||
END;
|
||||
|
@ -35,7 +35,7 @@
|
||||
====================================================================================
|
||||
*/
|
||||
|
||||
-- DROP FUNCTION pricequote.single_price_call(text,text,text,text,numeric) CASCADE;
|
||||
--DROP FUNCTION pricequote.single_price_call(text,text,text,text,numeric) CASCADE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pricequote.single_price_call(
|
||||
_bill TEXT,
|
||||
@ -89,8 +89,6 @@ RETURNS TABLE (
|
||||
list_relevance TEXT,
|
||||
guidance_price NUMERIC,
|
||||
guidance_reason TEXT,
|
||||
approval_price NUMERIC,
|
||||
approval_reason TEXT,
|
||||
expl JSONB,
|
||||
ui_json JSONB
|
||||
) AS $$
|
||||
@ -150,8 +148,6 @@ DECLARE
|
||||
------------step 6 compute guidance------------
|
||||
_guidance_price NUMERIC;
|
||||
_guidance_reason TEXT;
|
||||
_approval_price NUMERIC;
|
||||
_approval_reason TEXT;
|
||||
------------step 7 build json------------------
|
||||
_expl JSONB := '{}'::jsonb;
|
||||
_ui_json JSONB := '{}'::jsonb;
|
||||
@ -269,7 +265,7 @@ BEGIN
|
||||
,_tmath
|
||||
,_volume_range
|
||||
FROM
|
||||
pricequote.target_prices_base tp
|
||||
pricequote.target_prices tp
|
||||
WHERE
|
||||
tp.stlc = _stlc
|
||||
AND tp.ds = _v1ds
|
||||
@ -287,36 +283,38 @@ BEGIN
|
||||
INTO
|
||||
_tprice_last
|
||||
FROM
|
||||
pricequote.target_prices_base tp
|
||||
pricequote.target_prices tp
|
||||
WHERE
|
||||
tp.stlc = _stlc
|
||||
AND tp.ds = _last_dataseg
|
||||
AND tp.chan = _chan
|
||||
AND tp.tier = _tier
|
||||
AND FLOOR(_last_qty / NULLIF(_pltq, 0))::int <@ tp.vol;
|
||||
AND FLOOR(_vol / NULLIF(_pltq, 0))::int <@ tp.vol;
|
||||
|
||||
------------------------------------------------------------------
|
||||
-- Step 4: Cost data for normalization
|
||||
------------------------------------------------------------------
|
||||
-- Current/future standard for requested v1ds
|
||||
SELECT
|
||||
curstdus, futstdus
|
||||
INTO
|
||||
_curstd, _futstd
|
||||
FROM
|
||||
"CMS.CUSLG".itemm i
|
||||
WHERE
|
||||
i.item = _part
|
||||
AND i.v1ds = _v1ds;
|
||||
|
||||
SELECT
|
||||
ROUND(CASE WHEN COALESCE(_customized,'') = '' THEN _curstd_orig ELSE COALESCE(v1.curstdus, v0.curstdus) END,5) AS curstd,
|
||||
ROUND(CASE WHEN COALESCE(_customized,'') = '' THEN _futstd_orig ELSE COALESCE(v1.futstdus, v0.futstdus) END,5) AS futstd,
|
||||
ROUND(CASE WHEN COALESCE(_last_isdiff,'') = '' THEN _curstd_orig ELSE COALESCE(v1l.curstdus, v0l.curstdus) END,5) AS curstd_last,
|
||||
ROUND(CASE WHEN COALESCE(_last_isdiff,'') = '' THEN _futstd_orig ELSE COALESCE(v1l.futstdus, v0l.futstdus) END,5) AS futstd_last
|
||||
INTO
|
||||
_curstd, _futstd, _curstd_last, _futstd_last
|
||||
FROM (VALUES (1)) AS x(dummy)
|
||||
LEFT JOIN rlarp.cost_v1ds v1
|
||||
ON v1.stlc = _stlc AND v1.v1ds = _v1ds
|
||||
LEFT JOIN rlarp.cost_v0ds v0
|
||||
ON v0.stlc = _stlc AND v0.v0ds = _v0ds
|
||||
LEFT JOIN rlarp.cost_v1ds v1l
|
||||
ON v1l.stlc = _stlc AND v1l.v1ds = _last_dataseg
|
||||
LEFT JOIN rlarp.cost_v0ds v0l
|
||||
ON v0l.stlc = _stlc AND v0l.v0ds = _last_v0ds
|
||||
LIMIT 1;
|
||||
|
||||
-- Current/future standard for last_dataseg
|
||||
SELECT
|
||||
curstdus, futstdus
|
||||
INTO
|
||||
_curstd_last, _futstd_last
|
||||
FROM
|
||||
"CMS.CUSLG".itemm i
|
||||
WHERE
|
||||
i.item = _part
|
||||
AND i.v1ds = _last_dataseg;
|
||||
|
||||
------------------------------------------------------------------
|
||||
-- Step 5: Normalize last price if needed
|
||||
@ -327,7 +325,7 @@ BEGIN
|
||||
_last_price_norm := ROUND(_last_price * (_tprice / _tprice_last), 5);
|
||||
_last_premium_method := 'Target Price Ratio';
|
||||
ELSIF _curstd_last IS NOT NULL AND _curstd IS NOT NULL AND _curstd_last <> 0 THEN
|
||||
_last_premium := ROUND(_curstd / _curstd_last, 5);
|
||||
_last_premium := _curstd / _curstd_last;
|
||||
_last_price_norm := ROUND(_last_price * (_curstd / _curstd_last), 5);
|
||||
_last_premium_method := 'Cost Ratio';
|
||||
ELSE
|
||||
@ -376,16 +374,10 @@ BEGIN
|
||||
SELECT
|
||||
gl.guidance_price
|
||||
,gl.guidance_reason
|
||||
,al.approval_price
|
||||
,al.approval_reason
|
||||
INTO
|
||||
_guidance_price
|
||||
,_guidance_reason
|
||||
,_approval_price
|
||||
,_approval_reason
|
||||
FROM
|
||||
pricequote.guidance_logic(_tprice, _last_price_norm, _listprice_eff, _last_date, 1.0, 1.0, 1.0) gl
|
||||
CROSS JOIN pricequote.approval_logic(_tprice, _last_price_norm, _listprice_eff, _last_date, 1.0, 1.0, 1.0) al;
|
||||
FROM pricequote.guidance_logic(_tprice, _last_price_norm, _listprice_eff, _last_date) gl;
|
||||
|
||||
------------------------------------------------------------------
|
||||
-- Step 8: Build explanation JSON
|
||||
@ -433,8 +425,7 @@ BEGIN
|
||||
'targets',
|
||||
jsonb_build_object(
|
||||
'target_price', _tprice,
|
||||
'target_math', _tmath,
|
||||
'volume_range', _volume_range
|
||||
'target_math', _tmath
|
||||
),
|
||||
'list',
|
||||
jsonb_build_object(
|
||||
@ -444,9 +435,7 @@ BEGIN
|
||||
'list_relevance', _list_relevance
|
||||
),
|
||||
'guidance_price', _guidance_price,
|
||||
'guidance_reason', _guidance_reason,
|
||||
'approval_price', _approval_price,
|
||||
'approval_reason', _approval_reason
|
||||
'guidance_reason', _guidance_reason
|
||||
);
|
||||
|
||||
------------------------------------------------------------------
|
||||
@ -459,11 +448,9 @@ BEGIN
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'History',
|
||||
10, 'detailLevel',
|
||||
'details', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'label', CASE WHEN _last_price IS NOT NULL THEN 'Last Sale: ' || _last_date ELSE 'No Recent' END,
|
||||
'detailLevel', 10,
|
||||
'value', COALESCE(_last_price,0),
|
||||
'type', 'currency',
|
||||
'note', CASE WHEN _last_price IS NOT NULL THEN
|
||||
@ -481,7 +468,6 @@ BEGIN
|
||||
||CASE WHEN COALESCE(_last_premium,1) <> 1 THEN
|
||||
COALESCE(jsonb_build_array(jsonb_build_object(
|
||||
'label','Price Difference',
|
||||
'detailLevel',10,
|
||||
'value', _last_premium,
|
||||
'type','percent',
|
||||
'note', _last_premium_method
|
||||
@ -492,7 +478,6 @@ BEGIN
|
||||
||CASE WHEN COALESCE(_last_premium,1) <> 1 THEN
|
||||
COALESCE(jsonb_build_array(jsonb_build_object(
|
||||
'label','Adjusted Price',
|
||||
'detailLevel',10,
|
||||
'value', _last_price_norm,
|
||||
'type','currency',
|
||||
'note','normalized to ' || _v1ds
|
||||
@ -506,11 +491,9 @@ BEGIN
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'List',
|
||||
'detailLevel',10,
|
||||
'details', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'label', 'List:' || COALESCE(_list_code, ''),
|
||||
'detailLevel',10,
|
||||
'value', _list_price,
|
||||
'type', 'currency',
|
||||
'note', _list_relevance
|
||||
@ -522,7 +505,6 @@ BEGIN
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'Target Calculation',
|
||||
'detailLevel',10,
|
||||
'details',
|
||||
-- jsonb_build_array(
|
||||
(
|
||||
@ -531,8 +513,6 @@ BEGIN
|
||||
jsonb_build_object(
|
||||
----------------------label------------------------------------------------
|
||||
'label',CASE WHEN value <> '' THEN RTRIM(SUBSTRING(value,1,18)) ELSE 'No Target' END,
|
||||
----------------------detailLevel------------------------------------------
|
||||
'detailLevel',10,
|
||||
----------------------value------------------------------------------------
|
||||
'value',CASE WHEN value <> '' THEN
|
||||
SUBSTRING(value,23,7)::NUMERIC(20,5) +
|
||||
@ -566,11 +546,9 @@ BEGIN
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'Guidance',
|
||||
'detailLevel',10,
|
||||
'details', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'label', 'Price',
|
||||
'detailLevel',10,
|
||||
'value', COALESCE(_guidance_price,0),
|
||||
'type', 'currency',
|
||||
'note', COALESCE(_guidance_reason,'')
|
||||
@ -594,7 +572,6 @@ BEGIN
|
||||
_tprice, _tmath, _volume_range,
|
||||
_list_price, _list_code, _listprice_eff, _list_relevance,
|
||||
_guidance_price, _guidance_reason,
|
||||
_approval_price, _approval_reason,
|
||||
_expl, _ui_json;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
@ -105,9 +105,6 @@ lq AS MATERIALIZED (
|
||||
-- ,jsonb_pretty(pricing) pricing
|
||||
,p.guidance_price
|
||||
,p.guidance_reason
|
||||
,p.approval_price
|
||||
,p.approval_reason
|
||||
,p.tprice guidance_target
|
||||
,jsonb_pretty(p.ui_json->'data') expl
|
||||
FROM
|
||||
lq
|
||||
@ -178,4 +175,4 @@ lq AS MATERIALIZED (
|
||||
WHERE
|
||||
COALESCE(g.bestprice,1) = 1
|
||||
)
|
||||
SELECT * FROM hist --LIMIT 1000--WHERE qid = 108655
|
||||
SELECT * FROM hist --WHERE qid = 108655
|
||||
|
@ -1,275 +0,0 @@
|
||||
CREATE OR ALTER PROCEDURE pricing.rebuild_lastprice
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON; -- auto-rollback on most errors
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRAN; -- start transaction
|
||||
--------------------------------------------------------------------------------
|
||||
-- Reset target tables
|
||||
--------------------------------------------------------------------------------
|
||||
--DROP TABLE IF EXISTS pricing.lastpricedetail;
|
||||
DELETE FROM pricing.lastpricedetail;
|
||||
DROP TABLE IF EXISTS #flagged;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 1: Load cleaned input rows
|
||||
-- Filters out irrelevant quotes/orders and calculates unit prices
|
||||
--------------------------------------------------------------------------------
|
||||
WITH base AS (
|
||||
SELECT
|
||||
o."Customer" AS customer,
|
||||
o."Part Group" AS partgroup,
|
||||
RTRIM(i.V1DS) AS dataseg,
|
||||
o."Data Source" AS version,
|
||||
o."Part Code" AS part,
|
||||
o."Units" AS qty,
|
||||
CASE
|
||||
WHEN o."Units" = 0 THEN NULL
|
||||
ELSE ROUND(o.[Value USD] / NULLIF(o."Units", 0), 5)
|
||||
END AS price,
|
||||
o.[Order Date] AS odate,
|
||||
o.[Order Number] AS ordnum,
|
||||
o.[Quote Number] AS quoten
|
||||
FROM
|
||||
fanalysis.rlarp.osm_stack_pretty o
|
||||
INNER JOIN CMSInterfaceIn.[CMS.CUSLG].ITEMM i
|
||||
ON i.item = o.[Part Code]
|
||||
WHERE
|
||||
o.[Data Source] IN ('Actual', 'Quotes')
|
||||
AND o."Customer" IS NOT NULL
|
||||
AND o."Financial Statement Line" = '41010'
|
||||
AND o."Order Status" <> 'CANCELLED'
|
||||
AND o."Units" > 0
|
||||
AND o."Part Group" <> ''
|
||||
-- Optional filter for testing
|
||||
-- AND o."Customer" = 'ESBENSHADES GREENHOUSE'
|
||||
),
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 2: Rank each row based on recency and volume rules
|
||||
-- Flags include:
|
||||
-- - rn_mrs: most recent sale
|
||||
-- - rn_mrq: most recent quote
|
||||
-- - rn_lvs: largest sale in last year
|
||||
-- - rn_lvq: largest quote in last year
|
||||
-- - rn_dss: most recent sale per dataseg
|
||||
-- - rn_dsq: most recent quote per dataseg
|
||||
--------------------------------------------------------------------------------
|
||||
ranked AS (
|
||||
SELECT
|
||||
b.*
|
||||
-- Most recent sale (Actuals only)
|
||||
,CASE WHEN b.version = 'Actual' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY b.odate DESC
|
||||
)
|
||||
END AS rn_mrs
|
||||
-- Most recent quote (Quotes only)
|
||||
,CASE WHEN b.version = 'Quotes' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY b.odate DESC
|
||||
)
|
||||
END AS rn_mrq
|
||||
-- Largest volume sale (Actuals only; last 12 months prioritized)
|
||||
,CASE WHEN b.version = 'Actual' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY
|
||||
CASE WHEN b.version = 'Actual' AND b.odate >= DATEADD(YEAR, -1, GETDATE()) THEN 1 ELSE 0 END DESC,
|
||||
b.qty DESC
|
||||
)
|
||||
END AS rn_lvs
|
||||
-- Largest volume quote (Quotes only; last 12 months prioritized)
|
||||
,CASE WHEN b.version = 'Quotes' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY
|
||||
CASE WHEN b.version = 'Quotes' AND b.odate >= DATEADD(YEAR, -1, GETDATE()) THEN 1 ELSE 0 END DESC,
|
||||
b.qty DESC
|
||||
)
|
||||
END AS rn_lvq
|
||||
,CASE WHEN b.version = 'Actual' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup, b.dataseg, b.version
|
||||
ORDER BY b.odate DESC
|
||||
)
|
||||
END AS rn_dss
|
||||
,CASE WHEN b.version = 'Quotes' THEN
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup, b.dataseg, b.version
|
||||
ORDER BY b.odate DESC
|
||||
)
|
||||
END AS rn_dsq
|
||||
FROM base b
|
||||
)
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 2.5: Save only rows that meet any of the above criteria
|
||||
-- and annotate each with global-level flag (mrs, mrq, lvs, lvq)
|
||||
--------------------------------------------------------------------------------
|
||||
SELECT
|
||||
*,
|
||||
CASE WHEN rn_mrs = 1 THEN 'mrs' END AS f1,
|
||||
CASE WHEN rn_mrq = 1 THEN 'mrq' END AS f2,
|
||||
CASE WHEN rn_lvs = 1 THEN 'lvs' END AS f3,
|
||||
CASE WHEN rn_lvq = 1 THEN 'lvq' END AS f4,
|
||||
CASE WHEN rn_dss = 1 AND version = 'Actual' THEN 'dss' END AS f5,
|
||||
CASE WHEN rn_dsq = 1 AND version = 'Quotes' THEN 'dsq' END AS f6
|
||||
INTO #flagged
|
||||
FROM ranked
|
||||
WHERE
|
||||
rn_mrs = 1
|
||||
OR rn_mrq = 1
|
||||
OR rn_lvs = 1
|
||||
OR rn_lvq = 1
|
||||
OR (rn_dss = 1 AND version = 'Actual')
|
||||
OR (rn_dsq = 1 AND version = 'Quotes');
|
||||
|
||||
CREATE NONCLUSTERED INDEX ix_flagged_lookup
|
||||
ON #flagged(customer, partgroup, dataseg, version, part, qty, price, odate, ordnum, quoten);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 3: Build JSON from flagged rows
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Step 3.1: Explode all flags from the #flagged table
|
||||
WITH exploded_flags AS (
|
||||
SELECT
|
||||
customer, partgroup, part, dataseg, version, qty, price, odate, ordnum, quoten,
|
||||
flag
|
||||
FROM #flagged
|
||||
CROSS APPLY (VALUES (f1), (f2), (f3), (f4), (f5), (f6)) AS f(flag)
|
||||
WHERE flag IS NOT NULL
|
||||
)
|
||||
--SELECT * FROM exploded_flags
|
||||
-- Step 3.2: Serialize each row into its JSON snippet
|
||||
-- Carry odate and version for deduplication in seg_pieces
|
||||
,serialized_flags AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
flag,
|
||||
odate,
|
||||
version,
|
||||
CONCAT(
|
||||
'"', flag, '":',
|
||||
JSON_QUERY((
|
||||
SELECT
|
||||
version,
|
||||
dataseg AS datasegment,
|
||||
part,
|
||||
qty,
|
||||
price,
|
||||
odate,
|
||||
ordnum,
|
||||
quoten,
|
||||
flag
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
))
|
||||
) AS json_piece
|
||||
FROM exploded_flags
|
||||
)
|
||||
--SELECT * FROM serialized_flags
|
||||
-- Step 3.3: Collect all global-level flags (mrs, mrq, lvs, lvq)
|
||||
,flag_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('mrs', 'mrq', 'lvs', 'lvq')
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM flag_json
|
||||
-- Step 3.4: Nest dss/dsq under each dataseg
|
||||
-- Only keep the most recent dss/dsq per dataseg/version (prevents duplicate keys)
|
||||
,seg_pieces AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
STRING_AGG(json_piece, ',') AS inner_json
|
||||
FROM (
|
||||
SELECT sf.*
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, flag
|
||||
ORDER BY odate DESC,
|
||||
CASE WHEN version = 'Actual' THEN 1 ELSE 0 END DESC
|
||||
) AS rn
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('dss', 'dsq')
|
||||
) sf
|
||||
WHERE sf.rn = 1
|
||||
) deduped
|
||||
GROUP BY customer, partgroup, dataseg
|
||||
)
|
||||
--SELECT * FROM seg_pieces
|
||||
-- Step 3.5: Wrap the inner_json under dataseg key
|
||||
,wrapped_segs AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
CONCAT(
|
||||
'"', dataseg, '": {', inner_json, '}'
|
||||
) AS json_piece
|
||||
FROM seg_pieces
|
||||
)
|
||||
-- Step 3.6: Aggregate all dataseg entries into one JSON block per customer/partgroup
|
||||
,seg_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM wrapped_segs
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM seg_json
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 4: Merge flags and segment blocks into a single JSON object
|
||||
-- Write final pricing history to pricing.lastpricedetail
|
||||
--------------------------------------------------------------------------------
|
||||
INSERT INTO
|
||||
pricing.lastpricedetail
|
||||
SELECT
|
||||
COALESCE(f.customer, s.customer) AS customer,
|
||||
COALESCE(f.partgroup, s.partgroup) AS partgroup,
|
||||
CONCAT(
|
||||
'{',
|
||||
COALESCE(f.json_block, ''),
|
||||
CASE
|
||||
WHEN f.json_block IS NOT NULL AND s.json_block IS NOT NULL THEN ','
|
||||
ELSE ''
|
||||
END,
|
||||
COALESCE(s.json_block, ''),
|
||||
'}'
|
||||
) AS part_stats
|
||||
FROM flag_json f
|
||||
FULL OUTER JOIN seg_json s
|
||||
ON f.customer = s.customer AND f.partgroup = s.partgroup;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Commit if everything succeeded
|
||||
--------------------------------------------------------------------------------
|
||||
COMMIT TRAN;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
-- Ensure transaction is rolled back
|
||||
IF XACT_STATE() <> 0
|
||||
BEGIN
|
||||
ROLLBACK TRAN;
|
||||
END
|
||||
|
||||
-- Optional: cleanup temp table (temp table persists for session until proc end,
|
||||
-- but explicit drop is helpful if you reuse names or want to free resources)
|
||||
IF OBJECT_ID('tempdb..#flagged') IS NOT NULL
|
||||
DROP TABLE #flagged;
|
||||
|
||||
-- Rethrow the original error to the caller
|
||||
THROW;
|
||||
END CATCH;
|
||||
END;
|
@ -1 +0,0 @@
|
||||
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
|
@ -1,6 +0,0 @@
|
||||
EXEC pricing.pricing.rebuild_lastprice;
|
||||
--2:45
|
||||
EXEC pricing.pricing.rebuild_pricelist;
|
||||
--14 secconds
|
||||
EXEC pricing.pricing.rebuild_targets;
|
||||
--12:49
|
@ -1,6 +0,0 @@
|
||||
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
|
||||
--37 seconds
|
||||
CALL pricequote.rebuild_pricelist();
|
||||
--32 seconds
|
||||
CALL pricequote.refresh_target_prices_base();
|
||||
--45 seconds
|
@ -1,226 +0,0 @@
|
||||
CREATE OR ALTER PROCEDURE pricing.rebuild_pricelist
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON; -- auto-rollback on most errors
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRAN;
|
||||
|
||||
DROP TABLE pricing.pricelist_ranged;
|
||||
|
||||
CREATE TABLE pricing.pricelist_ranged (
|
||||
jcplcd varchar(5) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcpart varchar(20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcunit varchar(3) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcvoll numeric(12,5) NOT NULL,
|
||||
jcpric numeric(12,5) NOT NULL,
|
||||
vb_from float NULL,
|
||||
vb_to float NULL,
|
||||
price float NOT NULL
|
||||
);
|
||||
|
||||
CREATE NONCLUSTERED INDEX pricelist_ranged_idx ON PRICING.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;
|
||||
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-----------------------------------------------------------traverse unit of measure graph-----------------------------------------------------------------------
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-------------setup table to hold target conversions---------------------
|
||||
|
||||
|
||||
SELECT DISTINCT
|
||||
jcpart partn
|
||||
,jcunit fu
|
||||
,'PC' tu
|
||||
,cast(null as numeric) factor
|
||||
INTO
|
||||
#anchor
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc
|
||||
WHERE
|
||||
1=1;
|
||||
|
||||
--SELECT * FROM #anchor
|
||||
|
||||
-------pre-build punit stacked on itself with the columns flipped so you can go either direction per join---------
|
||||
SELECT
|
||||
*
|
||||
INTO
|
||||
#g
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT1) IHUNT1,
|
||||
rtrim(IHUNT2) IHUNT2,
|
||||
IHCNV1 IHCNV1,
|
||||
IHCNV2 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
UNION
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT2) IHUNT1,
|
||||
rtrim(IHUNT1) IHUNT2,
|
||||
IHCNV2 IHCNV1,
|
||||
IHCNV1 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
) x ;
|
||||
|
||||
CREATE INDEX g_idx on #g(ihpart,ihunt1);
|
||||
|
||||
WITH
|
||||
--------do the expansion on all paths until the target uom is matched----------------------------------------------
|
||||
--(complains about types not matching between anchor and recursion, explicitly just casting everything)
|
||||
uom (partn, partx, lvl, mastf, mastt, xf, xt, factor, xfactor, xnum, xden, id, uom_list) AS
|
||||
(
|
||||
SELECT
|
||||
cast(partn as varchar(20)) --partn
|
||||
,cast(partn as varchar(20)) --partx
|
||||
,cast(0 as int) --lvl
|
||||
,fu --mastf
|
||||
,tu --mastt
|
||||
,cast(fu as varchar(3)) --xf
|
||||
,cast(fu as varchar(3)) --xt
|
||||
,CAST(1 AS FLOAT) --factor
|
||||
,CAST(1 AS FLOAT) --xfactor
|
||||
,CAST(1 AS FLOAT) --xnum
|
||||
,CAST(1 AS FLOAT) --xden
|
||||
,format(row_number() over (ORDER BY partn),'000000')
|
||||
,cast(trim(fu) as varchar(max))
|
||||
FROM
|
||||
#anchor
|
||||
UNION ALL
|
||||
SELECT
|
||||
cast(uom.partn as varchar(20)) --partn
|
||||
,cast(ihpart as varchar(20)) --partx
|
||||
,CAST(uom.lvl + 1 AS INT) --lvl
|
||||
,uom.mastf --mastf
|
||||
,uom.mastt --mastt
|
||||
,cast(p.ihunt1 as varchar(3)) --xf
|
||||
,cast(p.ihunt2 as varchar(3)) --xt
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) --factor
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) * uom.xfactor --xfactor
|
||||
,p.ihcnv2 * uom.xnum --xnum
|
||||
,p.ihcnv1 * uom.xden --xden
|
||||
,uom.id + '.' + format(row_number() over (PARTITION BY uom.id ORDER BY partn),'00')
|
||||
,uom.uom_list + '.' + trim(p.ihunt2)
|
||||
FROM
|
||||
uom
|
||||
INNER JOIN #g p ON
|
||||
p.ihpart IN (uom.partn,'&&GLOBAL')
|
||||
AND p.ihunt1 = uom.xt
|
||||
WHERE
|
||||
1=1
|
||||
--AND p.ihunt2 not in ('BD','BG','BU','BX','CA','CS','PA','PL','SL','C','K','DOZ','PR')
|
||||
AND p.ihunt1 <> uom.mastt
|
||||
--prevent recursion: newest joined UOM can't be in the history
|
||||
AND charindex(p.ihunt2,uom.uom_list) = 0
|
||||
)
|
||||
--SELECT COUNT(*) FROM UOM
|
||||
--------------uom is going to have multiple rows per requested conversion, need to use row_number to pick the best row------------------------------
|
||||
,sorted AS (
|
||||
SELECT
|
||||
partn, mastf from_uom, xt to_uom, xfactor factor, lvl steps, row_number() OVER (PARTITION BY partn, mastf, mastt ORDER BY lvl ASC, factor ASC) rn
|
||||
FROM
|
||||
uom
|
||||
WHERE
|
||||
xt = mastt
|
||||
)
|
||||
SELECT * INTO #uom FROM sorted WHERE rn = 1;
|
||||
--so far so good
|
||||
|
||||
drop table #anchor;
|
||||
drop table #g;
|
||||
|
||||
TRUNCATE TABLE pricing.pricelist_ranged;
|
||||
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.factor,
|
||||
-- Normalize volume and price to PC
|
||||
p.jcvoll * u.factor AS vol_pc,
|
||||
p.jcpric / u.factor AS price_pc
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc p
|
||||
INNER JOIN #uom u
|
||||
ON u.partn = p.jcpart
|
||||
AND u.from_uom = p.jcunit
|
||||
AND u.to_uom = 'PC'
|
||||
),
|
||||
sorted AS (
|
||||
SELECT
|
||||
c.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY c.jcplcd, c.jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv c
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
INSERT INTO
|
||||
pricing.pricelist_ranged
|
||||
SELECT
|
||||
RTRIM(jcplcd) jcplcd,
|
||||
RTRIM(jcpart) jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price_pc AS price
|
||||
FROM
|
||||
ranged;
|
||||
|
||||
--CREATE INDEX pricelist_ranged_idx ON pricing.pricelist_ranged(jcpart, jcplcd, vb_from, vb_to);
|
||||
|
||||
COMMIT TRAN;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
-- Ensure transaction is rolled back
|
||||
IF XACT_STATE() <> 0
|
||||
BEGIN
|
||||
ROLLBACK TRAN;
|
||||
END
|
||||
|
||||
IF OBJECT_ID('tempdb..#anchor') IS NOT NULL DROP TABLE #anchor;
|
||||
IF OBJECT_ID('tempdb..#g') IS NOT NULL DROP TABLE #g;
|
||||
IF OBJECT_ID('tempdb..#uom') IS NOT NULL DROP TABLE #uom;
|
||||
|
||||
-- Re-throw original error
|
||||
THROW;
|
||||
END CATCH;
|
||||
END;
|
@ -1,108 +0,0 @@
|
||||
CREATE OR REPLACE PROCEDURE pricequote.rebuild_pricelist()
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
|
||||
DROP TABLE IF EXISTS uomc;
|
||||
|
||||
CREATE TEMP TABLE uomc AS (
|
||||
WITH
|
||||
uom AS (
|
||||
SELECT
|
||||
uom.p part
|
||||
,uom.f fu
|
||||
,uom.t tu
|
||||
,uom.nm/uom.dm conv
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
jsonb_agg(row_to_json(d)::jsonb) jdoc
|
||||
FROM
|
||||
(
|
||||
select distinct
|
||||
jcpart partn
|
||||
, jcunit fu
|
||||
, 'PC' tu
|
||||
from
|
||||
lgdat.iprcc
|
||||
WHERE
|
||||
jcpart <> ''
|
||||
) d
|
||||
) c
|
||||
JOIN LATERAL rlarp.uom_array(c.jdoc) uom ON TRUE
|
||||
)
|
||||
SELECT * FROM uom
|
||||
) WITH DATA;
|
||||
|
||||
CREATE INDEX uom_idx ON uomc (part, fu, tu);
|
||||
|
||||
|
||||
-- Clear the output table
|
||||
TRUNCATE TABLE pricequote.pricelist_ranged;
|
||||
|
||||
--DROP TABLE pricequote.pricelist_ranged;
|
||||
|
||||
-- Compute normalized volume/price and ranges
|
||||
--CREATE TABLE pricequote.pricelist_ranged AS (
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.conv,
|
||||
(p.jcvoll * u.conv) AS vol_pc,
|
||||
(p.jcpric / NULLIF(u.conv, 0)) AS price_pc
|
||||
FROM
|
||||
lgdat.iprcc p
|
||||
INNER JOIN uomc u
|
||||
ON u.part = p.jcpart
|
||||
AND u.fu = p.jcunit
|
||||
AND u.tu = 'PC'
|
||||
),
|
||||
--SELECT * FROM conv LIMIT 1000
|
||||
sorted AS (
|
||||
SELECT
|
||||
*,
|
||||
ROW_NUMBER() OVER (PARTITION BY jcplcd, jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc price,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
--SELECT * FROM ranged
|
||||
INSERT INTO pricequote.pricelist_ranged (
|
||||
jcplcd, jcpart, jcunit, jcvoll, jcpric, vb_from, vb_to, price
|
||||
)
|
||||
SELECT
|
||||
jcplcd,
|
||||
jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price
|
||||
FROM ranged;
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
--CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;
|
@ -1,42 +0,0 @@
|
||||
CREATE OR ALTER PROCEDURE pricing.rebuild_targets
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON; -- auto-rollback on most runtime errors
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRAN;
|
||||
|
||||
DELETE FROM pricing.target_prices;
|
||||
|
||||
INSERT INTO
|
||||
pricing.target_prices
|
||||
SELECT
|
||||
stlc,
|
||||
ds,
|
||||
chan,
|
||||
tier,
|
||||
vol,
|
||||
-- Extract lower bound: text between '[' and ','
|
||||
TRY_CAST(SUBSTRING(vol, 2, CHARINDEX(',', vol) - 2) AS INT) AS lower_bound,
|
||||
-- Extract upper bound: text between ',' and ')'
|
||||
CASE
|
||||
WHEN RIGHT(vol, 2) = ',)' THEN NULL
|
||||
ELSE TRY_CAST(SUBSTRING(vol, CHARINDEX(',', vol) + 1, LEN(vol) - CHARINDEX(',', vol) - 1) AS INT)
|
||||
END AS upper_bound,
|
||||
price,
|
||||
math
|
||||
FROM
|
||||
usmidsap02.ubm.pricequote.target_prices_view;
|
||||
|
||||
COMMIT TRAN;
|
||||
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF XACT_STATE() <> 0
|
||||
ROLLBACK TRAN;
|
||||
|
||||
-- Rethrow original error
|
||||
THROW;
|
||||
END CATCH;
|
||||
END
|
@ -1,28 +0,0 @@
|
||||
CREATE OR REPLACE PROCEDURE pricequote.refresh_target_prices_base()
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
DELETE FROM pricequote.target_prices_base;
|
||||
|
||||
WITH expand AS (
|
||||
SELECT
|
||||
c.compset,
|
||||
c.stlc,
|
||||
c.floor,
|
||||
b.ds,
|
||||
b.chan,
|
||||
b.tier,
|
||||
b.vol,
|
||||
b.val,
|
||||
b.price,
|
||||
b.math AS math
|
||||
FROM pricequote.core_target c
|
||||
LEFT JOIN LATERAL pricequote.build_pricing_path_base(
|
||||
c.options || jsonb_build_object('entity','Anchor','attr',c.stlc,'val',c.floor,'func','Price')
|
||||
) AS b
|
||||
ON b.lastflag
|
||||
)
|
||||
INSERT INTO pricequote.target_prices_base
|
||||
SELECT * FROM expand;
|
||||
END;
|
||||
$$;
|
@ -1,66 +0,0 @@
|
||||
CREATE OR ALTER PROCEDURE pricing.sync_external
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON; -- causes runtime errors to auto-rollback
|
||||
|
||||
BEGIN TRY
|
||||
|
||||
BEGIN TRAN;
|
||||
-- Refresh pricing.ffcret
|
||||
TRUNCATE TABLE pricing.ffcret;
|
||||
INSERT INTO pricing.ffcret
|
||||
SELECT *
|
||||
FROM fanalysis.rlarp.ffcret;
|
||||
|
||||
-- Refresh pricing.ffterr
|
||||
TRUNCATE TABLE pricing.ffterr;
|
||||
INSERT INTO pricing.ffterr
|
||||
SELECT *
|
||||
FROM fanalysis.rlarp.ffterr;
|
||||
|
||||
-- Refresh pricing.gld
|
||||
TRUNCATE TABLE pricing.gld;
|
||||
INSERT INTO pricing.gld
|
||||
SELECT *
|
||||
FROM fanalysis.rlarp.gld;
|
||||
|
||||
-- Refresh pricing.qrh
|
||||
TRUNCATE TABLE pricing.qrh;
|
||||
INSERT INTO pricing.qrh
|
||||
SELECT *
|
||||
FROM fanalysis.rlarp.qrh;
|
||||
|
||||
-- Refresh pricing.sach
|
||||
TRUNCATE TABLE pricing.sach;
|
||||
INSERT INTO pricing.sach
|
||||
SELECT *
|
||||
FROM fanalysis.lgdat.sach;
|
||||
|
||||
COMMIT TRAN;
|
||||
|
||||
-- rebuild last price
|
||||
EXEC pricing.rebuild_lastprice;
|
||||
|
||||
-- rebuild ranged price list
|
||||
EXEC pricing.rebuild_pricelist;
|
||||
|
||||
-- rebuild target prices
|
||||
EXEC pricing.rebuild_targets;
|
||||
|
||||
END TRY
|
||||
|
||||
BEGIN CATCH
|
||||
-- Rollback if any error
|
||||
IF XACT_STATE() <> 0
|
||||
ROLLBACK TRAN;
|
||||
|
||||
-- rethrow original error with context
|
||||
DECLARE @ErrMsg NVARCHAR(4000) = ERROR_MESSAGE();
|
||||
DECLARE @ErrSec NVARCHAR(200) = ERROR_SEVERITY();
|
||||
DECLARE @ErrState INT = ERROR_STATE();
|
||||
RAISERROR('refresh_pricing_tables failed: %s', 16, 1, @ErrMsg);
|
||||
THROW; -- rethrow original error for callers to handle
|
||||
END CATCH;
|
||||
|
||||
END
|
@ -1,28 +0,0 @@
|
||||
CREATE OR ALTER VIEW pricing.arcstx AS
|
||||
SELECT
|
||||
v6part PART,
|
||||
v6plnt plnt,
|
||||
v6stat stat,
|
||||
v6rpln rpln,
|
||||
v6unti unit,
|
||||
COALESCE(cnsdat, cosdat, y3sdat) sdate,
|
||||
COALESCE(cnstcs,costcs, y3stcs) std,
|
||||
COALESCE(cnmats,costcs,y3smat + y3soc + y3sshc) mat,
|
||||
COALESCE(cnlabs,y3slab,0) lab,
|
||||
COALESCE(cnbrvs,y3svbr,0) var,
|
||||
COALESCE(cnbrfs,y3sfbr,0) fix,
|
||||
COALESCE(cnstoc,y3sotc,0) oth
|
||||
FROM
|
||||
CMSInterfaceIN.lgdat.stka
|
||||
LEFT OUTER JOIN CMSInterfaceIN.ARCHIVE.ftcstm_2510 ftcstm ON
|
||||
cnpart = v6part
|
||||
AND cnplnt = v6plnt
|
||||
LEFT OUTER JOIN CMSInterfaceIN.ARCHIVE.ftcstp_2510 ftcstp ON
|
||||
copart = v6part
|
||||
AND coplnt = v6plnt
|
||||
LEFT OUTER JOIN CMSInterfaceIN.ARCHIVE.ftcstr_2510 ftcstr ON
|
||||
y3part = v6part
|
||||
AND y3plnt = v6plnt
|
||||
WHERE
|
||||
v6plnt IN ('152','154','155','112','113');
|
||||
|
@ -1,16 +0,0 @@
|
||||
|
||||
DROP TABLE IF EXISTS pricequote.core_target;
|
||||
|
||||
CREATE TABLE pricequote.core_target (
|
||||
compset TEXT NOT NULL,
|
||||
stlc TEXT NOT NULL,
|
||||
floor NUMERIC NOT NULL,
|
||||
options JSONB NOT NULL,
|
||||
PRIMARY KEY (stlc)
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON pricequote.target_prices TO PUBLIC;
|
||||
|
||||
DROP TABLE IF EXISTS import.core_target;
|
||||
|
||||
CREATE TABLE import.core_target AS (SELECT * FROM pricequote.core_target);
|
@ -1,85 +0,0 @@
|
||||
DROP TABLE pricing.cost_v1ds
|
||||
DROP TABLE pricing.cost_v0ds
|
||||
|
||||
-- Final tables (one-time create)
|
||||
CREATE TABLE pricing.cost_v1ds (
|
||||
stlc varchar(50) NOT NULL,
|
||||
v1ds varchar(50) NOT NULL,
|
||||
curstdus decimal(19,6) NULL,
|
||||
futstdus decimal(19,6) NULL,
|
||||
CONSTRAINT PK_cost_v1ds PRIMARY KEY (stlc, v1ds)
|
||||
);
|
||||
|
||||
CREATE INDEX IX_cost_v1ds_cur ON pricing.cost_v1ds(stlc, v1ds, curstdus);
|
||||
|
||||
CREATE TABLE pricing.cost_v0ds (
|
||||
stlc varchar(50) NOT NULL,
|
||||
v0ds varchar(50) NOT NULL,
|
||||
curstdus decimal(19,6) NULL,
|
||||
futstdus decimal(19,6) NULL,
|
||||
CONSTRAINT PK_cost_v0ds PRIMARY KEY (stlc, v0ds)
|
||||
);
|
||||
|
||||
CREATE INDEX IX_cost_v0ds_cur ON pricing.cost_v0ds(stlc, v0ds, curstdus);
|
||||
|
||||
CREATE OR ALTER PROCEDURE pricing.refresh_cost_rollups
|
||||
AS
|
||||
BEGIN
|
||||
DELETE FROM pricing.cost_v1ds;
|
||||
|
||||
INSERT INTO
|
||||
pricing.cost_v1ds
|
||||
SELECT
|
||||
trim(stlc) stlc
|
||||
,trim(v1ds) v1ds
|
||||
,avg(curstdus) curstdus
|
||||
,avg(futstdus) futstdus
|
||||
FROM
|
||||
CMSInterfaceIN.[CMS.CUSLG].ITEMM i
|
||||
LEFT OUTER JOIN pricing.arcstx a ON
|
||||
a.part = i.item
|
||||
AND a.plnt = i.dplt
|
||||
LEFT OUTER JOIN pricing.plpr p ON
|
||||
p.plnt = i.dplt
|
||||
LEFT OUTER JOIN pricing.ffcret x ON
|
||||
x.fcur = p.curr
|
||||
AND x.tcur = 'US'
|
||||
AND x.perd = p.ic
|
||||
AND x.rtyp = 'ME'
|
||||
WHERE
|
||||
aplnt <> 'I'
|
||||
AND stlc <> ''
|
||||
AND substring(glec,1,1) <= '1'
|
||||
GROUP BY
|
||||
trim(stlc)
|
||||
,trim(v1ds);
|
||||
|
||||
DELETE FROM pricing.cost_v0ds
|
||||
|
||||
INSERT INTO
|
||||
pricing.cost_v0ds
|
||||
SELECT
|
||||
trim(stlc) stlc
|
||||
,trim(colgrp)+trim(substring(branding,1,1)) v0ds
|
||||
,avg(curstdus) curstdus
|
||||
,avg(futstdus) futstdus
|
||||
FROM
|
||||
CMSInterfaceIN.[CMS.CUSLG].ITEMM i
|
||||
LEFT OUTER JOIN pricing.arcstx a ON
|
||||
a.part = i.item
|
||||
AND a.plnt = i.dplt
|
||||
LEFT OUTER JOIN pricing.plpr p ON
|
||||
p.plnt = i.dplt
|
||||
LEFT OUTER JOIN pricing.ffcret x ON
|
||||
x.fcur = p.curr
|
||||
AND x.tcur = 'US'
|
||||
AND x.perd = p.ic
|
||||
AND x.rtyp = 'ME'
|
||||
WHERE
|
||||
aplnt <> 'I'
|
||||
AND stlc <> ''
|
||||
AND substring(glec,1,1) <= '1'
|
||||
GROUP BY
|
||||
trim(stlc)
|
||||
,trim(colgrp)+trim(substring(branding,1,1));
|
||||
END
|
@ -1,54 +0,0 @@
|
||||
CREATE OR ALTER VIEW pricing.cust AS
|
||||
SELECT
|
||||
bvcust code,
|
||||
bvbill default_billto,
|
||||
bvname descr,
|
||||
CASE WHEN bvadr6 = '' THEN bvname ELSE bvadr6 END dba,
|
||||
bvctry country,
|
||||
bvprcd province,
|
||||
bvcity city,
|
||||
bvcomp remit_to,
|
||||
bvclas cclass,
|
||||
bvstat status,
|
||||
bvtype ctype,
|
||||
RTRIM(bvschl) plevel,
|
||||
s.bk7des3 folder,
|
||||
-- pl.lists lists,
|
||||
dr.repp default_rep,
|
||||
rr.repp retail_rep,
|
||||
gr.repp inside_rep,
|
||||
nr.repp keyaccount_rep,
|
||||
u.mfresp tier
|
||||
FROM
|
||||
cmsinterfacein.lgdat.cust c
|
||||
LEFT OUTER JOIN cmsinterfacein.lgpgm.usrcust ON
|
||||
cucust = bvcust
|
||||
LEFT OUTER JOIN pricing.repc dr ON
|
||||
dr.rcode = bvsalm
|
||||
LEFT OUTER JOIN pricing.repc rr ON
|
||||
rr.rcode = currep
|
||||
LEFT OUTER JOIN pricing.repc gr ON
|
||||
gr.rcode = cugrep
|
||||
LEFT OUTER JOIN pricing.repc nr ON
|
||||
nr.rcode = cunrep
|
||||
LEFT OUTER JOIN pricing.sach s ON
|
||||
s.bk7code = c.bvschl
|
||||
LEFT OUTER JOIN pricing.ffterr t ON
|
||||
t.prov = bvprcd
|
||||
AND t.ctry = bvctry
|
||||
LEFT OUTER JOIN pricing.ffcret x ON
|
||||
x.fcur = c.bvcurr
|
||||
AND x.tcur = 'US'
|
||||
AND x.perd = (SELECT fspr FROM pricing.gld WHERE GETDATE() BETWEEN sdat AND edat)
|
||||
AND x.rtyp = 'MA'
|
||||
LEFT OUTER JOIN cmsinterfacein.lgdat.usrc u ON
|
||||
u.mfsrce = 'MN'
|
||||
AND u.mfent# = 12
|
||||
AND u.mfkey2 = c.bvcust
|
||||
-- LEFT OUTER JOIN (
|
||||
-- SELECT jbplvl, STRING_AGG(JBPLCD, ', ') AS lists
|
||||
-- FROM CMSinterfacein.[CMS.CUSLG].iprcbhc
|
||||
-- GROUP BY jbplvl
|
||||
-- ) AS pl ON
|
||||
-- pl.jbplvl = c.bvschl
|
||||
-- WHERE CASE WHEN bvadr6 = '' THEN bvname ELSE bvadr6 END = 'GRIFFIN'
|
@ -1 +0,0 @@
|
||||
SELECT * INTO pricing.ffcret FROM fanalysis.rlarp.ffcret
|
@ -1 +0,0 @@
|
||||
SELECT * INTO pricing.ffterr FROM fanalysis.rlarp.ffterr
|
@ -1 +0,0 @@
|
||||
SELECT * INTO pricing.gld FROM fanalysis.rlarp.gld
|
@ -1,7 +1,237 @@
|
||||
CREATE TABLE pricing.lastpricedetail (
|
||||
customer varchar(255),
|
||||
partgroup varchar(10),
|
||||
part_stats nvarchar(MAX)
|
||||
);
|
||||
--------------------------------------------------------------------------------
|
||||
-- Reset target tables
|
||||
--------------------------------------------------------------------------------
|
||||
--DROP TABLE IF EXISTS pricing.lastpricedetail;
|
||||
DELETE FROM pricing.lastpricedetail;
|
||||
DROP TABLE IF EXISTS #flagged;
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX lastprice_cust_partgroup ON pricing.lastpricedetail ( customer ASC , partgroup ASC );
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 1: Load cleaned input rows
|
||||
-- Filters out irrelevant quotes/orders and calculates unit prices
|
||||
--------------------------------------------------------------------------------
|
||||
WITH base AS (
|
||||
SELECT
|
||||
o."Customer" AS customer,
|
||||
o."Part Group" AS partgroup,
|
||||
RTRIM(i.V1DS) AS dataseg,
|
||||
o."Data Source" AS version,
|
||||
o."Part Code" AS part,
|
||||
o."Units" AS qty,
|
||||
CASE
|
||||
WHEN o."Units" = 0 THEN NULL
|
||||
ELSE ROUND(o.[Value USD] / NULLIF(o."Units", 0), 5)
|
||||
END AS price,
|
||||
o.[Order Date] AS odate,
|
||||
o.[Order Number] AS ordnum,
|
||||
o.[Quote Number] AS quoten
|
||||
FROM
|
||||
rlarp.osm_stack_pretty o
|
||||
INNER JOIN CMSInterfaceIn.[CMS.CUSLG].ITEMM i
|
||||
ON i.item = o.[Part Code]
|
||||
WHERE
|
||||
o.[Data Source] IN ('Actual', 'Quotes')
|
||||
AND o."Customer" IS NOT NULL
|
||||
AND o."Financial Statement Line" = '41010'
|
||||
AND o."Order Status" <> 'CANCELLED'
|
||||
AND o."Units" > 0
|
||||
AND o."Part Group" <> ''
|
||||
-- Optional filter for testing
|
||||
-- AND o."Customer" = 'ESBENSHADES GREENHOUSE'
|
||||
),
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 2: Rank each row based on recency and volume rules
|
||||
-- Flags include:
|
||||
-- - rn_mrs: most recent sale
|
||||
-- - rn_mrq: most recent quote
|
||||
-- - rn_lvs: largest sale in last year
|
||||
-- - rn_lvq: largest quote in last year
|
||||
-- - rn_dss: most recent sale per dataseg
|
||||
-- - rn_dsq: most recent quote per dataseg
|
||||
--------------------------------------------------------------------------------
|
||||
ranked AS (
|
||||
SELECT
|
||||
b.*
|
||||
-- Most recent sale
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY CASE WHEN b.version = 'Actual' THEN b.odate ELSE NULL END DESC
|
||||
) AS rn_mrs
|
||||
-- Most recent quote
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY CASE WHEN b.version = 'Quotes' THEN b.odate ELSE NULL END DESC
|
||||
) AS rn_mrq
|
||||
-- Largest volume sale (last 12 months)
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY CASE
|
||||
WHEN b.version = 'Actual' AND b.odate >= DATEADD(YEAR, -1, GETDATE())
|
||||
THEN b.qty ELSE NULL
|
||||
END DESC
|
||||
) AS rn_lvs
|
||||
-- Largest volume quote (last 12 months)
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup
|
||||
ORDER BY CASE
|
||||
WHEN b.version = 'Quotes' AND b.odate >= DATEADD(YEAR, -1, GETDATE())
|
||||
THEN b.qty ELSE NULL
|
||||
END DESC
|
||||
) AS rn_lvq
|
||||
-- Most recent sale per data segment
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup, b.dataseg, b.version
|
||||
ORDER BY CASE WHEN b.version = 'Actual' THEN b.odate ELSE NULL END DESC
|
||||
) AS rn_dss
|
||||
-- Most recent quote per data segment
|
||||
,ROW_NUMBER() OVER (
|
||||
PARTITION BY b.customer, b.partgroup, b.dataseg, b.version
|
||||
ORDER BY CASE WHEN b.version = 'Quotes' THEN b.odate ELSE NULL END DESC
|
||||
) AS rn_dsq
|
||||
FROM base b
|
||||
)
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 2.5: Save only rows that meet any of the above criteria
|
||||
-- and annotate each with global-level flag (mrs, mrq, lvs, lvq)
|
||||
--------------------------------------------------------------------------------
|
||||
SELECT
|
||||
*,
|
||||
CASE WHEN rn_mrs = 1 THEN 'mrs' END AS f1,
|
||||
CASE WHEN rn_mrq = 1 THEN 'mrq' END AS f2,
|
||||
CASE WHEN rn_lvs = 1 THEN 'lvs' END AS f3,
|
||||
CASE WHEN rn_lvq = 1 THEN 'lvq' END AS f4,
|
||||
CASE WHEN rn_dss = 1 AND version = 'Actual' THEN 'dss' END AS f5,
|
||||
CASE WHEN rn_dsq = 1 AND version = 'Quotes' THEN 'dsq' END AS f6
|
||||
INTO #flagged
|
||||
FROM ranked
|
||||
WHERE
|
||||
rn_mrs = 1
|
||||
OR rn_mrq = 1
|
||||
OR rn_lvs = 1
|
||||
OR rn_lvq = 1
|
||||
OR (rn_dss = 1 AND version = 'Actual')
|
||||
OR (rn_dsq = 1 AND version = 'Quotes');
|
||||
|
||||
CREATE NONCLUSTERED INDEX ix_flagged_lookup
|
||||
ON #flagged(customer, partgroup, dataseg, version, part, qty, price, odate, ordnum, quoten);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 3: Build JSON from flagged rows
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Step 3.1: Explode all flags from the #flagged table
|
||||
WITH exploded_flags AS (
|
||||
SELECT
|
||||
customer, partgroup, part, dataseg, version, part, qty, price, odate, ordnum, quoten,
|
||||
flag
|
||||
FROM #flagged
|
||||
CROSS APPLY (VALUES (f1), (f2), (f3), (f4), (f5), (f6)) AS f(flag)
|
||||
WHERE flag IS NOT NULL
|
||||
)
|
||||
--SELECT * FROM exploded_flags
|
||||
-- Step 3.2: Serialize each row into its JSON snippet
|
||||
-- Carry odate and version for deduplication in seg_pieces
|
||||
,serialized_flags AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
flag,
|
||||
odate,
|
||||
version,
|
||||
CONCAT(
|
||||
'"', flag, '":',
|
||||
JSON_QUERY((
|
||||
SELECT
|
||||
version,
|
||||
dataseg AS datasegment,
|
||||
part,
|
||||
qty,
|
||||
price,
|
||||
odate,
|
||||
ordnum,
|
||||
quoten,
|
||||
flag
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
))
|
||||
) AS json_piece
|
||||
FROM exploded_flags
|
||||
)
|
||||
--SELECT * FROM serialized_flags
|
||||
-- Step 3.3: Collect all global-level flags (mrs, mrq, lvs, lvq)
|
||||
,flag_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('mrs', 'mrq', 'lvs', 'lvq')
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM flag_json
|
||||
-- Step 3.4: Nest dss/dsq under each dataseg
|
||||
-- Only keep the most recent dss/dsq per dataseg/version (prevents duplicate keys)
|
||||
,seg_pieces AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
STRING_AGG(json_piece, ',') AS inner_json
|
||||
FROM (
|
||||
SELECT sf.*
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, flag
|
||||
ORDER BY odate DESC,
|
||||
CASE WHEN version = 'Actual' THEN 1 ELSE 0 END DESC
|
||||
) AS rn
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('dss', 'dsq')
|
||||
) sf
|
||||
WHERE sf.rn = 1
|
||||
) deduped
|
||||
GROUP BY customer, partgroup, dataseg
|
||||
)
|
||||
--SELECT * FROM seg_pieces
|
||||
-- Step 3.5: Wrap the inner_json under dataseg key
|
||||
,wrapped_segs AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
CONCAT(
|
||||
'"', dataseg, '": {', inner_json, '}'
|
||||
) AS json_piece
|
||||
FROM seg_pieces
|
||||
)
|
||||
-- Step 3.6: Aggregate all dataseg entries into one JSON block per customer/partgroup
|
||||
,seg_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM wrapped_segs
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM seg_json
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 4: Merge flags and segment blocks into a single JSON object
|
||||
-- Write final pricing history to pricing.lastpricedetail
|
||||
--------------------------------------------------------------------------------
|
||||
INSERT INTO
|
||||
pricing.lastpricedetail
|
||||
SELECT
|
||||
COALESCE(f.customer, s.customer) AS customer,
|
||||
COALESCE(f.partgroup, s.partgroup) AS partgroup,
|
||||
CONCAT(
|
||||
'{',
|
||||
COALESCE(f.json_block, ''),
|
||||
CASE
|
||||
WHEN f.json_block IS NOT NULL AND s.json_block IS NOT NULL THEN ','
|
||||
ELSE ''
|
||||
END,
|
||||
COALESCE(s.json_block, ''),
|
||||
'}'
|
||||
) AS part_stats
|
||||
FROM flag_json f
|
||||
FULL OUTER JOIN seg_json s
|
||||
ON f.customer = s.customer AND f.partgroup = s.partgroup;
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
|
||||
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
|
||||
|
||||
DROP MATERIALIZED VIEW pricequote.lastpricedetail;
|
||||
--DROP MATERIALIZED VIEW pricequote.lastpricedetail
|
||||
|
||||
CREATE MATERIALIZED VIEW pricequote.lastpricedetail AS
|
||||
WITH base AS (
|
||||
@ -28,38 +28,38 @@ WITH base AS (
|
||||
ranked AS (
|
||||
SELECT b.*,
|
||||
-- Most recent sale (Actuals first, newest date first)
|
||||
CASE WHEN version = 'Actual' THEN ROW_NUMBER() OVER (
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Actual') DESC,
|
||||
odate DESC NULLS LAST
|
||||
) END AS rn_mrs,
|
||||
) AS rn_mrs,
|
||||
-- Most recent quote (Quotes first, newest date first)
|
||||
CASE WHEN version = 'Quotes' THEN ROW_NUMBER() OVER (
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Quotes') DESC,
|
||||
odate DESC NULLS LAST
|
||||
) END AS rn_mrq,
|
||||
) AS rn_mrq,
|
||||
-- Largest volume sale in last year (those inside window first)
|
||||
CASE WHEN version = 'Actual' THEN ROW_NUMBER() OVER (
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Actual' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
|
||||
qty DESC NULLS LAST
|
||||
) END AS rn_lvs,
|
||||
) AS rn_lvs,
|
||||
-- Largest volume quote in last year (those inside window first)
|
||||
CASE WHEN version = 'Quotes' THEN ROW_NUMBER() OVER (
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Quotes' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
|
||||
qty DESC NULLS LAST
|
||||
) END AS rn_lvq,
|
||||
) AS rn_lvq,
|
||||
-- Per dataseg/version: most recent (version fixed in partition, so just date)
|
||||
CASE WHEN version = 'Actual' THEN ROW_NUMBER() OVER (
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, version
|
||||
ORDER BY odate DESC NULLS LAST
|
||||
) END AS rn_dss,
|
||||
CASE WHEN version = 'Quotes' THEN ROW_NUMBER() OVER (
|
||||
) AS rn_dss,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, version
|
||||
ORDER BY odate DESC NULLS LAST
|
||||
) END AS rn_dsq
|
||||
) AS rn_dsq
|
||||
FROM base b
|
||||
),
|
||||
flagged AS (
|
||||
|
@ -1,20 +0,0 @@
|
||||
CREATE OR ALTER VIEW pricing.plpr AS
|
||||
SELECT
|
||||
yaplnt plnt,
|
||||
LTRIM(RTRIM(a9)) AS comp,
|
||||
a30 AS descr,
|
||||
SUBSTRING(a249, 242, 2) curr,
|
||||
SUBSTRING(a249, 32, 4) AS gl,
|
||||
SUBSTRING(a249, 190, 4) AS ar,
|
||||
SUBSTRING(a249, 182, 4) AS ap,
|
||||
SUBSTRING(a249, 198, 4) AS fa,
|
||||
SUBSTRING(a249, 238, 4) AS ic
|
||||
FROM
|
||||
CMSInterfaceIN.lgdat.plnt
|
||||
INNER JOIN CMSInterfaceIN.lgdat.code ON
|
||||
yacomp = LTRIM(RTRIM(a9))
|
||||
LEFT OUTER JOIN CMSInterfaceIN.lgdat.name ON
|
||||
'C0000' + LTRIM(RTRIM(a9)) = a7
|
||||
WHERE
|
||||
a2 = 'AA'
|
||||
OR a2 IS NULL;
|
@ -1,12 +1,199 @@
|
||||
DROP TABLE pricing.pricelist_ranged;
|
||||
|
||||
CREATE TABLE pricing.pricelist_ranged (
|
||||
jcplcd varchar(5) ,
|
||||
jcpart varchar(20) ,
|
||||
jcunit varchar(3) ,
|
||||
jcvoll numeric(12,5) ,
|
||||
jcpric numeric(12,5) ,
|
||||
vb_from float ,
|
||||
vb_to float ,
|
||||
price float
|
||||
jcplcd varchar(5) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcpart varchar(20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcunit varchar(3) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
jcvoll numeric(12,5) NOT NULL,
|
||||
jcpric numeric(12,5) NOT NULL,
|
||||
vb_from float NULL,
|
||||
vb_to float NULL,
|
||||
price float NOT NULL
|
||||
);
|
||||
|
||||
CREATE NONCLUSTERED INDEX pricelist_ranged_idx ON pricing.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC );
|
||||
CREATE NONCLUSTERED INDEX pricelist_ranged_idx ON FAnalysis.PRICING.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;
|
||||
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-----------------------------------------------------------traverse unit of measure graph-----------------------------------------------------------------------
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-------------setup table to hold target conversions---------------------
|
||||
|
||||
|
||||
SELECT DISTINCT
|
||||
jcpart partn
|
||||
,jcunit fu
|
||||
,'PC' tu
|
||||
,cast(null as numeric) factor
|
||||
INTO
|
||||
#anchor
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc
|
||||
WHERE
|
||||
1=1;
|
||||
|
||||
--SELECT * FROM #anchor
|
||||
|
||||
-------pre-build punit stacked on itself with the columns flipped so you can go either direction per join---------
|
||||
SELECT
|
||||
*
|
||||
INTO
|
||||
#g
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT1) IHUNT1,
|
||||
rtrim(IHUNT2) IHUNT2,
|
||||
IHCNV1 IHCNV1,
|
||||
IHCNV2 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
UNION
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT2) IHUNT1,
|
||||
rtrim(IHUNT1) IHUNT2,
|
||||
IHCNV2 IHCNV1,
|
||||
IHCNV1 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
) x ;
|
||||
|
||||
CREATE INDEX g_idx on #g(ihpart,ihunt1);
|
||||
|
||||
WITH
|
||||
--------do the expansion on all paths until the target uom is matched----------------------------------------------
|
||||
--(complains about types not matching between anchor and recursion, explicitly just casting everything)
|
||||
uom (partn, partx, lvl, mastf, mastt, xf, xt, factor, xfactor, xnum, xden, id, uom_list) AS
|
||||
(
|
||||
SELECT
|
||||
cast(partn as varchar(20)) --partn
|
||||
,cast(partn as varchar(20)) --partx
|
||||
,cast(0 as int) --lvl
|
||||
,fu --mastf
|
||||
,tu --mastt
|
||||
,cast(fu as varchar(3)) --xf
|
||||
,cast(fu as varchar(3)) --xt
|
||||
,CAST(1 AS FLOAT) --factor
|
||||
,CAST(1 AS FLOAT) --xfactor
|
||||
,CAST(1 AS FLOAT) --xnum
|
||||
,CAST(1 AS FLOAT) --xden
|
||||
,format(row_number() over (ORDER BY partn),'000000')
|
||||
,cast(trim(fu) as varchar(max))
|
||||
FROM
|
||||
#anchor
|
||||
UNION ALL
|
||||
SELECT
|
||||
cast(uom.partn as varchar(20)) --partn
|
||||
,cast(ihpart as varchar(20)) --partx
|
||||
,CAST(uom.lvl + 1 AS INT) --lvl
|
||||
,uom.mastf --mastf
|
||||
,uom.mastt --mastt
|
||||
,cast(p.ihunt1 as varchar(3)) --xf
|
||||
,cast(p.ihunt2 as varchar(3)) --xt
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) --factor
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) * uom.xfactor --xfactor
|
||||
,p.ihcnv2 * uom.xnum --xnum
|
||||
,p.ihcnv1 * uom.xden --xden
|
||||
,uom.id + '.' + format(row_number() over (PARTITION BY uom.id ORDER BY partn),'00')
|
||||
,uom.uom_list + '.' + trim(p.ihunt2)
|
||||
FROM
|
||||
uom
|
||||
INNER JOIN #g p ON
|
||||
p.ihpart IN (uom.partn,'&&GLOBAL')
|
||||
AND p.ihunt1 = uom.xt
|
||||
WHERE
|
||||
1=1
|
||||
--AND p.ihunt2 not in ('BD','BG','BU','BX','CA','CS','PA','PL','SL','C','K','DOZ','PR')
|
||||
AND p.ihunt1 <> uom.mastt
|
||||
--prevent recursion: newest joined UOM can't be in the history
|
||||
AND charindex(p.ihunt2,uom.uom_list) = 0
|
||||
)
|
||||
--SELECT COUNT(*) FROM UOM
|
||||
--------------uom is going to have multiple rows per requested conversion, need to use row_number to pick the best row------------------------------
|
||||
,sorted AS (
|
||||
SELECT
|
||||
partn, mastf from_uom, xt to_uom, xfactor factor, lvl steps, row_number() OVER (PARTITION BY partn, mastf, mastt ORDER BY lvl ASC, factor ASC) rn
|
||||
FROM
|
||||
uom
|
||||
WHERE
|
||||
xt = mastt
|
||||
)
|
||||
SELECT * INTO #uom FROM sorted WHERE rn = 1;
|
||||
--so far so good
|
||||
|
||||
drop table #anchor;
|
||||
drop table #g;
|
||||
|
||||
TRUNCATE TABLE pricing.pricelist_ranged;
|
||||
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.factor,
|
||||
-- Normalize volume and price to PC
|
||||
p.jcvoll * u.factor AS vol_pc,
|
||||
p.jcpric / u.factor AS price_pc
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc p
|
||||
INNER JOIN #uom u
|
||||
ON u.partn = p.jcpart
|
||||
AND u.from_uom = p.jcunit
|
||||
AND u.to_uom = 'PC'
|
||||
),
|
||||
sorted AS (
|
||||
SELECT
|
||||
c.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY c.jcplcd, c.jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv c
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
INSERT INTO
|
||||
pricing.pricelist_ranged
|
||||
SELECT
|
||||
RTRIM(jcplcd) jcplcd,
|
||||
RTRIM(jcpart) jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price_pc AS price
|
||||
FROM
|
||||
ranged;
|
||||
|
||||
--CREATE INDEX pricelist_ranged_idx ON pricing.pricelist_ranged(jcpart, jcplcd, vb_from, vb_to);
|
||||
|
@ -1,11 +1,101 @@
|
||||
CREATE TABLE pricelist_ranged (
|
||||
jcplcd text NULL,
|
||||
jcpart text NULL,
|
||||
jcunit text NULL,
|
||||
jcvoll numeric(12, 5) NULL,
|
||||
jcpric numeric(12, 5) NULL,
|
||||
vb_from numeric NULL,
|
||||
vb_to numeric NULL,
|
||||
price numeric NULL
|
||||
);
|
||||
CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged USING btree (jcpart, jcplcd, vb_from, vb_to);
|
||||
DROP TABLE IF EXISTS uomc;
|
||||
|
||||
CREATE TEMP TABLE uomc AS (
|
||||
WITH
|
||||
uom AS (
|
||||
SELECT
|
||||
uom.p part
|
||||
,uom.f fu
|
||||
,uom.t tu
|
||||
,uom.nm/uom.dm conv
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
jsonb_agg(row_to_json(d)::jsonb) jdoc
|
||||
FROM
|
||||
(
|
||||
select distinct
|
||||
jcpart partn
|
||||
, jcunit fu
|
||||
, 'PC' tu
|
||||
from
|
||||
lgdat.iprcc
|
||||
WHERE
|
||||
jcpart <> ''
|
||||
) d
|
||||
) c
|
||||
JOIN LATERAL rlarp.uom_array(c.jdoc) uom ON TRUE
|
||||
)
|
||||
SELECT * FROM uom
|
||||
) WITH DATA;
|
||||
|
||||
CREATE INDEX uom_idx ON uomc (part, fu, tu);
|
||||
|
||||
|
||||
-- Clear the output table
|
||||
TRUNCATE TABLE pricequote.pricelist_ranged;
|
||||
|
||||
--DROP TABLE pricequote.pricelist_ranged;
|
||||
|
||||
-- Compute normalized volume/price and ranges
|
||||
--CREATE TABLE pricequote.pricelist_ranged AS (
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.conv,
|
||||
(p.jcvoll * u.conv) AS vol_pc,
|
||||
(p.jcpric / NULLIF(u.conv, 0)) AS price_pc
|
||||
FROM
|
||||
lgdat.iprcc p
|
||||
INNER JOIN uomc u
|
||||
ON u.part = p.jcpart
|
||||
AND u.fu = p.jcunit
|
||||
AND u.tu = 'PC'
|
||||
),
|
||||
--SELECT * FROM conv LIMIT 1000
|
||||
sorted AS (
|
||||
SELECT
|
||||
*,
|
||||
ROW_NUMBER() OVER (PARTITION BY jcplcd, jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc price,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
--SELECT * FROM ranged
|
||||
INSERT INTO pricequote.pricelist_ranged (
|
||||
jcplcd, jcpart, jcunit, jcvoll, jcpric, vb_from, vb_to, price
|
||||
)
|
||||
SELECT
|
||||
jcplcd,
|
||||
jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price
|
||||
FROM ranged;
|
||||
|
||||
|
||||
CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;
|
@ -1 +0,0 @@
|
||||
SELECT * INTO pricing.pricing.qrh FROM fanalysis.RLARP.QRH q
|
@ -1,18 +0,0 @@
|
||||
CREATE OR ALTER VIEW pricing.repc AS
|
||||
WITH
|
||||
code AS (
|
||||
SELECT
|
||||
ltrim(rtrim(c.a9)) rcode
|
||||
,(ltrim(rtrim(c.a9)) + ' - ') + c.a30 repp
|
||||
FROM
|
||||
CMSInterfaceIN.lgdat.code c
|
||||
WHERE c.a2 = 'MM'
|
||||
)
|
||||
SELECT
|
||||
COALESCE(c.rcode,q.qr) rcode
|
||||
,COALESCE(c.repp,q.qr) repp
|
||||
,COALESCE(q.dir,'Other') director
|
||||
FROM
|
||||
code c
|
||||
FULL OUTER JOIN pricing.qrh q ON
|
||||
q.qr = c.rcode
|
@ -1 +0,0 @@
|
||||
SELECT * INTO pricing.sach FROM fanalysis.lgdat.sach;
|
@ -16,4 +16,26 @@ ALTER TABLE pricing.target_prices
|
||||
ADD CONSTRAINT uq_target_prices_unique_combo
|
||||
UNIQUE (stlc, ds, chan, tier, vol, lower_bound);
|
||||
|
||||
DELETE FROM pricing.target_prices;
|
||||
|
||||
INSERT INTO
|
||||
pricing.target_prices
|
||||
SELECT
|
||||
stlc,
|
||||
ds,
|
||||
chan,
|
||||
tier,
|
||||
vol,
|
||||
-- Extract lower bound: text between '[' and ','
|
||||
TRY_CAST(SUBSTRING(vol, 2, CHARINDEX(',', vol) - 2) AS INT) AS lower_bound,
|
||||
-- Extract upper bound: text between ',' and ')'
|
||||
CASE
|
||||
WHEN RIGHT(vol, 2) = ',)' THEN NULL
|
||||
ELSE TRY_CAST(SUBSTRING(vol, CHARINDEX(',', vol) + 1, LEN(vol) - CHARINDEX(',', vol) - 1) AS INT)
|
||||
END AS upper_bound,
|
||||
price,
|
||||
math
|
||||
FROM
|
||||
usmidsap02.ubm.pricequote.target_prices_view;
|
||||
|
||||
--SELECT COUNT(*) FROM pricing.target_prices
|
||||
|
@ -1,17 +0,0 @@
|
||||
DROP TABLE pricequote.target_prices_base CASCADE;
|
||||
|
||||
CREATE TABLE pricequote.target_prices_base (
|
||||
compset TEXT NOT NULL,
|
||||
stlc TEXT NOT NULL,
|
||||
floor NUMERIC NOT NULL,
|
||||
ds TEXT NOT NULL,
|
||||
chan TEXT NOT NULL,
|
||||
tier TEXT NOT NULL,
|
||||
vol INT4RANGE NOT NULL,
|
||||
val NUMERIC NOT NULL,
|
||||
price NUMERIC,
|
||||
math TEXT[],
|
||||
PRIMARY KEY (stlc, ds, chan, tier, vol)
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON pricequote.target_prices_base TO PUBLIC;
|
@ -8,4 +8,4 @@ SELECT
|
||||
,price
|
||||
,to_jsonb(math)::text AS math
|
||||
FROM
|
||||
pricequote.target_prices_base;
|
||||
pricequote.target_prices;
|
132
ui_schema.json
132
ui_schema.json
@ -1,77 +1,75 @@
|
||||
{
|
||||
"details": [
|
||||
{
|
||||
"label": "History",
|
||||
"detailLevel": 10,
|
||||
"details": [
|
||||
"details": [
|
||||
{
|
||||
"label": "Last Quote",
|
||||
"detailLevel": 10,
|
||||
"value": 0.1012,
|
||||
"type": "currency",
|
||||
"note": "XNS0T1G3G18B096 | Ord# 1008338 | 2025-06-12 | Qty 19,200"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "List",
|
||||
"detailLevel": 10,
|
||||
"details": [
|
||||
{
|
||||
"label": "Code: GUAU",
|
||||
"detailLevel": 10,
|
||||
"value": 0.11,
|
||||
"type": "currency",
|
||||
"note": "List Min Qty: 9,600"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Target Calculation",
|
||||
"detailLevel": 10,
|
||||
"details": [
|
||||
{
|
||||
"label": "XNS0T1G3",
|
||||
"detailLevel": 10,
|
||||
"value": 0.08,
|
||||
"type": "currency",
|
||||
"note": "Base Floor"
|
||||
"label": "Model Inputs",
|
||||
"details": [
|
||||
{
|
||||
"label": "Base Cost",
|
||||
"value": 1.22446,
|
||||
"type": "currency"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Channel:WHS",
|
||||
"detailLevel": 10,
|
||||
"value": 0.2,
|
||||
"type": "Percent",
|
||||
"note": "Premium"
|
||||
"label": "Peer Target",
|
||||
"details": [
|
||||
{
|
||||
"label": "Peer Median Margin",
|
||||
"value": 36.873,
|
||||
"type": "percent",
|
||||
"note": "DIR|102 - HANGING POTS|110 - INJECTION|Tier 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Volume:1-8",
|
||||
"detailLevel": 10,
|
||||
"value": 0.1,
|
||||
"type": "Percent",
|
||||
"note": "Premium"
|
||||
"label": "Target Support",
|
||||
"details": [
|
||||
{
|
||||
"label": "Tier 1 Truckload",
|
||||
"value": 80,
|
||||
"type": "currency",
|
||||
"note": "reviewed floor price"
|
||||
},
|
||||
{
|
||||
"label": "Warehouse",
|
||||
"value": 1.2,
|
||||
"type": "percent"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Target",
|
||||
"detailLevel": 10,
|
||||
"value": 0.1056,
|
||||
"type": "currency",
|
||||
"note": "Total"
|
||||
"label": "Factor Adjustment",
|
||||
"details": [
|
||||
{
|
||||
"label": "Package UOM",
|
||||
"value": -0.01,
|
||||
"type": "percent"
|
||||
},
|
||||
{
|
||||
"label": "# of Pallets",
|
||||
"value": 0.02,
|
||||
"type": "percent",
|
||||
"note": "0.03"
|
||||
},
|
||||
{
|
||||
"label": "Urgency",
|
||||
"value": 0.03,
|
||||
"type": "percent",
|
||||
"note": "Top Priority"
|
||||
},
|
||||
{
|
||||
"label": "State Adder/Subtractor",
|
||||
"value": -0.02,
|
||||
"type": "percent",
|
||||
"note": "OH"
|
||||
},
|
||||
{
|
||||
"label": "Branding",
|
||||
"value": 0,
|
||||
"type": "currency"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Guidance",
|
||||
"detailLevel": 10,
|
||||
"details": [
|
||||
{
|
||||
"label": "Price",
|
||||
"detailLevel": 10,
|
||||
"value": 0.1012,
|
||||
"type": "currency",
|
||||
"note": "Using target price, capped to not exceed last price"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user