Compare commits

..

99 Commits

Author SHA1 Message Date
964616beec remove comment 2025-10-01 16:22:53 -04:00
326784e18c additional transaction wrapping and consolidated proc to sync tables 2025-10-01 16:13:35 -04:00
9dd85505ea migrate tables and pointers away from fanalysis 2025-10-01 15:24:45 -04:00
2ba73ac036 migrate rlarp to pricing 2025-10-01 15:16:44 -04:00
34c811123a wrap last price rebuild in transaction 2025-10-01 14:31:05 -04:00
31172e7e35 work on targeting a new db 2025-10-01 09:55:56 -04:00
42f8d1b777 include cost section 2025-09-22 17:23:55 -04:00
81736b51b1 add run times 2025-09-22 11:56:19 -04:00
1f9d8b9c85 spelling 2025-09-16 23:07:34 -04:00
28be5a3540 create master refresh scripts 2025-09-16 22:39:14 -04:00
4b477b990f wrap in procedures 2025-09-16 22:34:50 -04:00
12787d297b rename 2025-09-16 22:10:10 -04:00
eb7f200880 wrap rebuild in a proc 2025-09-16 22:09:10 -04:00
5dc970da3f move rebuild scripts and create ddl scripts 2025-09-16 21:41:07 -04:00
619539eac9 update to hold at last price, no drop 2025-09-09 15:39:40 -04:00
5ec3f1d9c1 put target rebuild in a proc 2025-09-08 08:50:28 -04:00
581224dad8 wrap in transaction 2025-09-06 15:59:51 -04:00
af4c7c4853 change visibility level of target calculation 2025-09-05 08:48:42 -04:00
abac006970 work on filter before row number 2025-09-04 15:04:30 -04:00
a5a746c005 change visibility levels to hide target price details 2025-09-04 13:37:31 -04:00
fd930c9969 change guidance to limit to last price paid 2025-09-04 13:37:03 -04:00
7596038b54 remove price drop 2025-09-03 09:35:09 -04:00
1b7ec3635b testing 2025-08-30 08:56:15 -04:00
ca2e9ea3be add approval columns to quote review 2025-08-30 08:55:48 -04:00
a8d83cc042 revised run times 2025-08-28 09:32:32 -04:00
ad6d3d60f0 break our approval versus guidance logic in postgres 2025-08-28 09:08:51 -04:00
2cc7204c7f update guidance logic to be the shown price which is great of target or last price but capped at list 2025-08-28 00:19:27 -04:00
e047e7f39b mirror guidance logic 2025-08-27 23:06:59 -04:00
c5532cd2dd convert to factor and clarify the logical steps 2025-08-27 23:06:08 -04:00
a7945999a2 initial rework of last price so it is clearer to review 2025-08-27 22:41:39 -04:00
37fead59ff Merge branch 'new_targets' 2025-08-27 20:26:36 -04:00
42c5900efe huge increase in run times 2025-08-27 20:06:02 -04:00
c61729e017 point target source to new target tables 2025-08-27 15:08:50 -04:00
ce77ad7de3 flip the labels on last quote and last sale 2025-08-27 15:08:33 -04:00
9a410b82d1 break out base target table with proper index, and create separate refresh script 2025-08-27 15:07:57 -04:00
6bb0172c2d label parameters and adjust last to 5% 2025-08-27 10:45:55 -04:00
3c854ed0a4 repoint postgres targets to new targets 2025-08-27 10:31:37 -04:00
d653917c8b point view to new target math 2025-08-27 09:53:03 -04:00
993a476bea expand core targets and populate 2025-08-26 16:31:57 -04:00
59a28591bd example with detaillevel key 2025-08-26 16:31:31 -04:00
a79a6d973f change last price window to 18 months 2025-08-26 09:51:59 -04:00
11b1a3a2e4 only work off of actual sales, not quotes 2025-08-26 09:50:05 -04:00
3675b8541f introduce a new key-value pair in the ui json that indicates level of visibility to various roles 2025-08-25 21:05:13 -04:00
2a699e8f83 only use actual sales for last price info in the evaluation process 2025-08-25 17:35:19 -04:00
f19bd138e3 gate the row_number function so that only target rows are processes Actuals for sales and Quotes for quotes 2025-08-25 17:31:37 -04:00
8974095341 expand the new target definition into a complete listing 2025-08-25 17:05:15 -04:00
6e2e806f9b add sample build of new target logic 2025-08-21 15:35:20 -04:00
e9bd81f352 new table to store target price components 2025-08-21 15:30:08 -04:00
c77a6643b2 add commented out price build call 2025-08-21 12:01:08 -04:00
c02f7ef765 include base item so calculation works 2025-08-21 12:00:49 -04:00
1d2c2741b1 create function that does not compound pricing 2025-08-21 11:57:38 -04:00
0fdf2e9775 formatting 2025-08-21 11:08:06 -04:00
11e3f419f6 formatting 2025-08-21 11:07:14 -04:00
7067f3d1fa update to reflect shown detail level 2025-08-18 14:36:48 -04:00
7f5994a40d handle nulls 2025-08-18 14:36:33 -04:00
379e6a5160 change to 15% price reduction 2025-08-18 14:35:22 -04:00
b3635695e6 line ending or other non impact 2025-08-18 14:34:28 -04:00
d2f56fcafa examples 2025-08-18 14:33:38 -04:00
f1d4d8a60b cap at ceiling not working becuase _eff list was being set to null 2025-08-14 03:30:25 -04:00
ccf50036ca work on guidance logic output text and parameterize caps and floors 2025-08-14 02:32:01 -04:00
c46d4c25d7 link in cost, rounding, target for last price should be based on last price qty 2025-08-14 01:50:57 -04:00
23b3962313 join in cost and volume, fix last price cross join 2025-08-14 00:50:31 -04:00
3cc82d14c0 last price normalization and refactor 2025-08-13 23:23:35 -04:00
49748383ff return last part code; factor last qty into normalization, better notes on the last price sourcing 2025-08-13 22:23:08 -04:00
7f990ab611 commit: 2025-08-12 23:08:02 2025-08-12 23:08:02 -04:00
f2fe91f078 isolate version 2025-08-12 22:32:47 -04:00
9b2d1bef4d put base price in the note 2025-08-12 09:58:24 -04:00
59b616c2b7 truncate the descriptor on guidance logic 2025-08-12 09:57:48 -04:00
fb462fbcb1 commit: 2025-08-12 01:19:30 2025-08-12 01:19:30 -04:00
d77b042581 formatting on last price normalizarion display 2025-08-11 21:47:57 -04:00
f936b9d31d show bridge to normalize last price 2025-08-11 21:18:22 -04:00
a649924c86 combine panels 2025-08-11 20:47:08 -04:00
3f56f04f8b notes 2025-08-11 20:32:48 -04:00
eb7563f96c include last part code and add item for total target price 2025-08-11 20:24:12 -04:00
062b49e60c commit: 2025-08-11 17:42:43 2025-08-11 17:42:43 -04:00
8624add269 force output if null 2025-08-11 12:10:47 -04:00
82db9a4334 populate something even if there is no record 2025-08-11 12:00:08 -04:00
d46e71a170 shorten the note on last sale 2025-08-11 11:01:04 -04:00
d37aded2c9 history should be in an array 2025-08-11 11:00:40 -04:00
310f1dc8f9 change last sale price flagging 2025-08-11 09:50:37 -04:00
c423116b9d Merge branch 'master' of github.com:The-HC-Companies/price_api 2025-08-11 09:21:18 -04:00
5940300dee refresh 2025-08-11 09:20:40 -04:00
2bf970eb06 convert to materialized view 2025-08-11 09:09:40 -04:00
ab5808d1e8 sequence variable according to population and sync up ms pg 2025-08-11 09:04:31 -04:00
e04021caa1 commit: 2025-08-10 23:04:53 2025-08-10 23:04:53 -04:00
fe2145b7d2 work on broken target linkage 2025-08-10 23:01:48 -04:00
10ca238010 commit: 2025-08-08 17:36:58 2025-08-08 17:36:58 -04:00
68921d9a7f suggested changes 2025-08-08 09:33:20 -04:00
849d176ad0 notes 2025-08-08 02:59:42 -04:00
f8c62e5d27 handle differences quoted product versus product on list if customized or last price if no exact match found 2025-08-08 02:45:06 -04:00
d1ca17b7e4 move quote review infrastructure 2025-08-07 23:59:05 -04:00
da1b97f712 move files 2025-08-07 23:54:07 -04:00
5945364482 commit: 2025-08-07 23:51:14 2025-08-07 23:51:14 -04:00
79a1d2a4ce commit: 2025-08-07 23:47:53 2025-08-07 23:47:53 -04:00
35c7fbee6b example usage 2025-08-07 23:42:26 -04:00
af5e4d202b bring func up to speed 2025-08-07 23:35:09 -04:00
4602db59bb move files 2025-08-07 23:23:40 -04:00
8675120993 move files around 2025-08-07 23:21:09 -04:00
045e050b0e move files around 2025-08-07 23:16:18 -04:00
102 changed files with 3976 additions and 3091 deletions

127
WARP.md Normal file
View File

@ -0,0 +1,127 @@
# 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.

View File

@ -0,0 +1,167 @@
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 Normal file
View File

@ -0,0 +1,184 @@
[
{
"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"
}
]

88
example_usage.ms.sql Normal file
View File

@ -0,0 +1,88 @@
EXEC pricing.single_price_call
@bill = 'GRIF0001',
@ship = 'GRIF0001',
@part = 'XNS0T1G3G18B096',
@v1ds = 'v1:B..PLT..',
@vol = 9600;
EXEC pricing.single_price_call
@bill = 'FARM0001',
@ship = 'KEYB0001',
@part = 'HCA10000B661100',
@v1ds = 'v1:T..CSE..D',
@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
SELECT * FROM CMSInterfaceIN.[CMS.CUSLG].
EXEC pricing.single_price_call
@bill = 'GRIF0001',
@ship = 'GRIF0001',
@part = 'XNS0T1G3G18B096',
@v1ds = 'v1:B..PLT..',
@vol = 9600;
SELECT
guidance_price, ui_json
FROM pricing.single_price_call_fn(
'GRIF0001',
'GRIF0001',
'XNS0T1G3G18B096',
'v1:B..PLT..',
9600
) f
SELECT TOP 10
q.qid,
q.qrn,
q.qcustomer,
q.part,
q.v1ds,
q.units_each,
q.price,
q.targetp,
q.lastsalesprice,
q.finalrecommendedprice,
q.lowerpricelimit,
q.upperpricelimit,
q.curstdus,
p.guidance_price,
p.expl
FROM
rlarp.live_quotes q
OUTER APPLY pricing.single_price_call_fn(
q.billto
,q.shipto
,q.part
,q.v1ds
,q.units_each
) p
WHERE
qstat LIKE 'Submitted%'
SELECT
*
FROM pricequote.single_price_call(
'GRIF0001',
'GRIF0001',
'XNS0T1G3G18B096',
'v1:B..PLT..',
9600
) f

80
example_usage.pg.sql Normal file
View File

@ -0,0 +1,80 @@
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;
-- 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

View File

@ -1,13 +0,0 @@
-- FAnalysis.PRICING.lastprice definition
-- Drop table
-- DROP TABLE FAnalysis.PRICING.lastprice;
CREATE TABLE pricing.lastprice (
customer varchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
mold varchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
part_stats nvarchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
);
CREATE UNIQUE NONCLUSTERED INDEX lastprice_cust_mold ON FAnalysis.PRICING.lastprice (customer ASC, mold ASC) ;

View File

@ -1,59 +0,0 @@
--------------------------------------------------------------------------------
-- Step 1: Rebuild last price history at sales matrix refresh time
--------------------------------------------------------------------------------
DELETE FROM pricing.lastprice;
WITH srt AS (
SELECT
customer,
mold,
part,
version,
qty,
ROUND(sales_usd / qty, 5) AS price,
odate,
oseas,
ordnum,
quoten,
ROW_NUMBER() OVER (
PARTITION BY customer, mold, part, version
ORDER BY odate DESC
) AS rn
FROM rlarp.osm_stack
WHERE
--quotes can't be integrated until we have datasegment or correct part code
version IN ('Actual'/*,'Quotes'*/) AND
customer IS NOT NULL AND
fs_line = '41010' AND
calc_status <> 'CANCELLED' AND
qty <> 0 AND
mold <> ''
),
json_rows AS (
SELECT
customer,
mold,
part,
version,
CONCAT(
'"', part, '":',
(
SELECT version, qty, price, odate, ordnum, quoten
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
) AS part_json
FROM srt
WHERE rn = 1
)
,onerow AS (
SELECT
customer,
mold,
CONCAT('{', STRING_AGG(part_json, ','), '}') AS part_stats
FROM json_rows
GROUP BY customer, mold
)
INSERT INTO pricing.lastprice SELECT * FROM onerow;
--CREATE UNIQUE INDEX lastprice_cust_mold ON pricing.lastprice(customer, mold);

View File

@ -1,26 +0,0 @@
minimal setup to run a single pricing call:
```sql
EXEC pricing.single_price_call
@bill = 'GRIF0001',
@ship = 'GRIF0001',
@part = 'XNS0T1G3G18B096',
@stlc = 'XNS0T1G3',
@v1ds = 'v1:T..PLT..',
@vol = 9600;
```
make sure pricing schema is setup
Target Prices
----------------------
1. create target table: `target_prices.ms.sql`
2. populate targets: `target_prices_copy.ms.sql`
Price History
----------------------
1. create history table: `lastprice.ms.sql`
2. populate history: `make_hist.ms.sql`
Proc Definitions
----------------------
1. create proc: `single_price_call.ms.sql`

View File

@ -1,162 +0,0 @@
CREATE OR ALTER PROCEDURE pricing.single_price_call
@bill VARCHAR(100),
@ship VARCHAR(100),
@part VARCHAR(100),
@stlc VARCHAR(100),
@v1ds VARCHAR(100),
@vol NUMERIC(18,6)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @phist NVARCHAR(MAX);
-- Declare table variable for the input row
DECLARE @queue TABLE (
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
stlc VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
chan VARCHAR(50),
cust VARCHAR(100),
tier VARCHAR(50),
pltq NUMERIC(18,6),
price NUMERIC(18,6),
expl NVARCHAR(MAX),
hist NVARCHAR(MAX),
LAST nvarchar(max)
);
--------------------------------------------------------------------------------
-- Step 1: Insert input row into queue
--------------------------------------------------------------------------------
INSERT INTO @queue (bill, ship, part, stlc, v1ds, vol)
VALUES (@bill, @ship, @part, @stlc, @v1ds, @vol);
--------------------------------------------------------------------------------
-- Step 2: Enrich the row with chan, tier, cust, pltq
--------------------------------------------------------------------------------
UPDATE q
SET
chan =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END,
tier =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE ISNULL(sc.tier, bc.tier)
END,
cust =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE q.bill
END,
pltq = i.mpck
FROM @queue q
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;
--------------------------------------------------------------------------------
-- Step 3: Get last price info directly into @queue columns
--------------------------------------------------------------------------------
UPDATE q
SET
q.hist = (
SELECT TOP 1
j.qty,
j.price,
j.odate,
j.ordnum,
j.quoten
FROM pricing.lastprice lp
OUTER APPLY OPENJSON(lp.part_stats) AS p
OUTER APPLY OPENJSON(p.value)
WITH (
qty NUMERIC(20,5),
price NUMERIC(20,5),
odate DATE,
ordnum INT,
quoten INT
) AS j
WHERE
lp.customer = q.cust
AND lp.mold = SUBSTRING(q.part,1,8)
AND p.[key] COLLATE SQL_Latin1_General_CP1_CI_AS = q.part
ORDER BY j.odate DESC
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
q.last = (
SELECT TOP 1
CAST(ROUND(j.price, 5) AS NVARCHAR(50)) -- must be string to store in NVARCHAR column
FROM pricing.lastprice lp
OUTER APPLY OPENJSON(lp.part_stats) AS p
OUTER APPLY OPENJSON(p.value)
WITH (
qty NUMERIC(20,5),
price NUMERIC(20,5),
odate DATE,
ordnum INT,
quoten INT
) AS j
WHERE
lp.customer = q.cust
AND lp.mold = SUBSTRING(q.part,1,8)
AND p.[key] COLLATE SQL_Latin1_General_CP1_CI_AS = q.part
ORDER BY j.odate DESC
)
FROM @queue q;
--------------------------------------------------------------------------------
-- Step 4: Apply pricing and embed price history + last price from queue columns
--------------------------------------------------------------------------------
UPDATE q
SET
price = tp.price,
expl = (
SELECT
'target price' AS [source],
tp.price AS [target_price],
CAST(q.last AS NUMERIC(20,5)) AS [last_price],
FLOOR(q.vol / NULLIF(q.pltq, 0)) AS [calculated_pallets],
ROUND(q.vol / NULLIF(q.pltq, 0), 5) AS [exact_pallets],
CONCAT(tp.lower_bound, '-', ISNULL(CAST(tp.upper_bound AS VARCHAR), '')) AS [volume range],
q.cust AS [customer],
q.chan AS [channel],
q.tier AS [tier],
JSON_QUERY(tp.math) AS [target math],
JSON_QUERY(q.hist) AS [price history]
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
FROM @queue q
INNER JOIN pricing.target_prices tp ON
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND FLOOR(q.vol / NULLIF(q.pltq, 0)) >= tp.lower_bound
AND (
tp.upper_bound IS NULL OR FLOOR(q.vol / NULLIF(q.pltq, 0)) < tp.upper_bound
);
--------------------------------------------------------------------------------
-- Step Last: Return just the enriched row
--------------------------------------------------------------------------------
SELECT price, expl FROM @queue;
END;

View File

@ -1,23 +0,0 @@
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

View File

@ -1,60 +0,0 @@
{
"details": [
{
"label": "History",
"details": [
{
"label": "Last Sale: 2025-06-12",
"value": 0.10120,
"type": "currency",
"note": "Ord# 1008338"
}
]
},
{
"label": "List",
"details": [
{
"label": "List:GUAU",
"value": 0.11000,
"type": "currency",
"note": "U.AEA.DI"
}
]
},
{
"label": "Target Support",
"details": [
{
"label": "Anchor:XNS0T1G3",
"value": 0.08000,
"type": "currency",
"note": "+"
},
{
"label": "Channel:WHS",
"value": 0.20000,
"type": "percentage",
"note": "x"
},
{
"label": "Volume:1-8",
"value": 0.10000,
"type": "percentage",
"note": "x"
}
]
},
{
"label": "Guidance",
"details": [
{
"label": "Last Price Capped",
"value": 0.10120,
"type": "currency",
"note": "Last price 0.10120 capped at 0.0000000000000%"
}
]
}
]
}

View File

@ -1,280 +0,0 @@
{
"v1:B..CSE..": [
{
"version": "Actual",
"part": "XNS0T1G2G18C140",
"qty": 8960.00,
"price": 0.092910,
"odate": "2016-02-10",
"ordnum": 784718,
"quoten": 28971,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G2G18C140",
"qty": 9100.00,
"price": 0.092910,
"odate": "2016-02-10",
"ordnum": 0,
"quoten": 29543,
"flags": []
}
],
"v1:T..CSE..": [
{
"version": "Quotes",
"part": "XNS0T1G2X03C140",
"qty": 6020.00,
"price": 0.092910,
"odate": "2016-02-10",
"ordnum": 0,
"quoten": 29543,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G2X03C140",
"qty": 6020.00,
"price": 0.092910,
"odate": "2016-02-09",
"ordnum": 784655,
"flags": []
}
],
"v1:B..PLT..": [
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 9600.00,
"price": 0.126130,
"odate": "2024-10-31",
"ordnum": 992442,
"quoten": 79774,
"flags": [
"most_recent_sale",
"largest_volume_sale"
]
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.126130,
"odate": "2024-10-29",
"ordnum": 0,
"quoten": 79774,
"flags": [
"most_recent_quote",
"largest_volume_quote"
]
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 9600.00,
"price": 0.126130,
"odate": "2024-08-02",
"ordnum": 985899,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.128250,
"odate": "2023-12-04",
"ordnum": 969894,
"quoten": 75273,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.128250,
"odate": "2023-12-04",
"ordnum": 0,
"quoten": 75273,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 19200.00,
"price": 0.135000,
"odate": "2023-02-13",
"ordnum": 0,
"quoten": 73602,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.145020,
"odate": "2022-01-26",
"ordnum": 0,
"quoten": 69130,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.125480,
"odate": "2021-07-16",
"ordnum": 922664,
"quoten": 64686,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.125480,
"odate": "2021-07-08",
"ordnum": 0,
"quoten": 64686,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.100390,
"odate": "2020-11-12",
"ordnum": 905400,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.100390,
"odate": "2020-08-13",
"ordnum": 0,
"quoten": 58215,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.100390,
"odate": "2020-06-11",
"ordnum": 0,
"quoten": 57254,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.100390,
"odate": "2020-06-10",
"ordnum": 890715,
"quoten": 57254,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.098910,
"odate": "2019-07-23",
"ordnum": 870300,
"quoten": 51460,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.098910,
"odate": "2019-07-18",
"ordnum": 0,
"quoten": 51460,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 19200.00,
"price": 0.095290,
"odate": "2018-10-09",
"ordnum": 853112,
"quoten": 44775,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G3G18B096",
"qty": 9600.00,
"price": 0.095290,
"odate": "2018-10-09",
"ordnum": 853112,
"quoten": 44775,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 9600.00,
"price": 0.095290,
"odate": "2018-08-21",
"ordnum": 0,
"quoten": 44775,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G3G18B096",
"qty": 38400.00,
"price": 0.094160,
"odate": "2017-10-13",
"ordnum": 0,
"quoten": 38844,
"flags": []
},
{
"version": "Actual",
"part": "XNS0T1G2G18B079",
"qty": 47790.00,
"price": 0.092910,
"odate": "2016-06-10",
"ordnum": 794232,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G2G18B079",
"qty": 47790.00,
"price": 0.092910,
"odate": "2016-02-10",
"ordnum": 0,
"quoten": 29543,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G2G18B079",
"qty": 47790.00,
"price": 0.091200,
"odate": "2015-12-17",
"ordnum": 0,
"quoten": 28971,
"flags": []
},
{
"version": "Quotes",
"part": "XNS0T1G2G18B079",
"qty": 47790.00,
"price": 0.091200,
"odate": "2015-09-21",
"ordnum": 0,
"quoten": 27873,
"flags": []
}
]
}

View File

@ -1,63 +0,0 @@
{
"mrs": {
"version": "Actual",
"datasegment": "v1:T..CSE..",
"part": "XNS0T1G3X18C140",
"qty": 4200.00,
"price": 0.146300,
"odate": "2025-08-01",
"ordnum": 1011917,
"flag": "mrs"
},
"mrq": {
"version": "Quotes",
"datasegment": "v1:B..CSE..",
"part": "XNS0T1G3G18C140",
"qty": 7560.00,
"price": 0.115800,
"odate": "2024-12-10",
"ordnum": 0,
"quoten": 81983,
"flag": "mrq"
},
"lvs": {
"version": "Actual",
"datasegment": "v1:B..PLT..",
"part": "XNS0T1G3G18B096",
"qty": 28800.00,
"price": 0.098360,
"odate": "2025-05-23",
"ordnum": 1006860,
"flag": "lvs"
},
"lvq": {
"version": "Quotes",
"datasegment": "v1:B..CSE..",
"part": "XNS0T1G3G18C140",
"qty": 7560.00,
"price": 0.115800,
"odate": "2024-12-10",
"ordnum": 0,
"quoten": 81983,
"flag": "lvq"
},
"v1:B..CSE..": {
"dss": {
"version": "Actual",
"datasegment": "v1:B..CSE..",
"part": "XNS0T1G3G18C140",
"qty": 2520.00,
"price": 0.106540,
"odate": "2025-07-17",
"ordnum": 1010952
},
"dsq": {
"version": "Quotes",
"datasegment": "v1:B..CSE..",
"part": "XNS0T1G3G18C140",
"qty": 2520.00,
"price": 0.106540,
"odate": "2025-07-17",
"quoten": 81983
}}
}

View File

@ -1,76 +0,0 @@
DELETE FROM pricing.price_queue;
INSERT INTO pricing.price_queue (bill, ship, part, stlc, v1ds, vol) SELECT 'GRIF0001','GRIF0001','XNS0T1G3G18B096','XNS0T1G3','v1:L..PLT..',9600;
SELECT * FROM pricing.price_queue
EXEC pricing.process_queue
EXEC pricing.single_price_call
@bill = 'GRIF0001',
@ship = 'GRIF0001',
@part = 'XNS0T1G3G18B096',
@v1ds = 'v1:B..PLT..',
@vol = 9600;
SELECT * FROM pricing.pick
SELECT * FROM FAnalysis.PRICING.lastpricedetail l OUTER apply pricing.pick_last_price_from_hist_json (part_stats,'v1:B..PLT..') x WHERE customer = 'GRIFFIN' AND partgroup = 'XNS0T1G3'
SELECT
*
FROM pricing.single_price_call_fn(
'GRIF0001',
'GRIF0001',
'XNS0T1G3G18B096',
'XNS0T1G3',
'v1:B..PLT..',
9600
) f
SELECT * INTO #result FROM pricing.price_queue WHERE 0=1
INSERT INTO #result
EXEC pricing.single_price_call_nowrite
@bill = 'GRIF0001',
@ship = 'JRSG0001',
@part = 'XNS0T1G3G18B096',
@stlc = 'XNS0T1G3',
@v1ds = 'v1:T..PLT..',
@vol = 19200;
SELECT * FROM #RESULT
SELECT
q.qid,
q.qrn,
q.qcustomer,
q.part,
q.v1ds,
q.units_each,
q.price,
q.targetp,
q.lastsalesprice,
q.finalrecommendedprice,
q.lowerpricelimit,
q.upperpricelimit,
q.curstdus,
p.price guidance,
p.expl
FROM
rlarp.live_quotes q
OUTER APPLY pricing.single_price_call_fn(
q.billto
,q.shipto
,q.part
,substring(q.part,1,8)
,q.v1ds
,q.units_each
) p
WHERE
qstat LIKE 'Submitted%'

View File

@ -1,52 +0,0 @@
SELECT * FROM
pricequote.single_price_call(
'GRIF0001' ,
'GRIF0001' ,
'XNS0T1G3G18B096' ,
'v1:B..PLT..',
9600
)
SELECT pricing FROM rlarp.osm_stack l WHERE l.customer ='CROS-B-CREST FARM' AND partgroup = 'STG04250'
SELECT * FROM pricequote.lastpricedetail l LEFT JOIN lateral pricequote.pick_last_price_from_hist (part_stats,'v1:B..PLT..') x ON TRUE WHERE customer = 'GRIFFIN' AND partgroup = 'XNS0T1G3'
CALL pricequote.process_queue()
REFRESH MATERIALIZED VIEW rlarp.osm_stack_pretty;
SELECT
q.qid,
q.qrn,
-- q.qcustomer,
q.partbuilt,
i.pricegroup,
q.v1ds,
q.units_each,
q.targetp,
q.lastsalesprice,
-- q.finalrecommendedprice,
-- q.lowerpricelimit,
-- q.upperpricelimit,
q.curstdus,
q.price,
p.guidance_price guidance,
p.expl
FROM
pricequote.live_quotes q
LEFT OUTER JOIN "CMS.CUSLG".itemm i ON
i.item = q.part
LEFT JOIN LATERAL pricequote.single_price_call(
q.billto
,q.shipto
,q.part
,q.v1ds
,q.units_each
) p ON TRUE
WHERE
-- qstat LIKE 'Submitted%'
qid = 111832 --AND q.qrn = 1
ORDER BY
qrn ASC

View File

@ -1,109 +0,0 @@
CREATE OR ALTER PROCEDURE pricing.process_queue
AS
BEGIN
SET NOCOUNT ON;
--------------------------------------------------------------------------------
-- Step 1: Insert input row into real queue table
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Step 2: Enrich the row with chan, tier, cust, pltq
--------------------------------------------------------------------------------
UPDATE q
SET
q.chan =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END,
q.tier =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE ISNULL(sc.tier, bc.tier)
END,
q.cust =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN q.ship
ELSE q.ship
END
ELSE q.bill
END,
q.pltq = i.mpck
FROM
pricing.price_queue q
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;
--------------------------------------------------------------------------------
-- Step 3: Apply pricing from target_prices
--------------------------------------------------------------------------------
DECLARE @updated TABLE (
id BIGINT,
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
stlc VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
chan VARCHAR(50),
cust VARCHAR(100),
tier VARCHAR(50),
pltq NUMERIC(18,6),
price NUMERIC(18,6),
expl NVARCHAR(MAX)
);
UPDATE q
SET
q.price = tp.price,
q.expl = (
SELECT
'target price' AS [source],
FLOOR(q.vol / NULLIF(q.pltq, 0)) AS [calculated_pallets],
ROUND(q.vol / NULLIF(q.pltq, 0), 5) AS [exact_pallets],
CONCAT(tp.lower_bound, '-', ISNULL(CAST(tp.upper_bound AS VARCHAR), '')) AS [volume range],
q.cust AS [customer],
q.chan AS [channel],
q.tier AS [tier],
JSON_QUERY(tp.math) AS [target math] -- important if math is JSON
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
OUTPUT
inserted.id,
inserted.bill,
inserted.ship,
inserted.part,
inserted.stlc,
inserted.v1ds,
inserted.vol,
inserted.chan,
inserted.cust,
inserted.tier,
inserted.pltq,
inserted.price,
inserted.expl
INTO @updated
FROM pricing.price_queue q
INNER JOIN pricing.target_prices tp ON
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND FLOOR(q.vol / NULLIF(q.pltq, 0)) >= tp.lower_bound
AND (
tp.upper_bound IS NULL OR FLOOR(q.vol / NULLIF(q.pltq, 0)) < tp.upper_bound
);
--------------------------------------------------------------------------------
-- Step 4: Return just the enriched row
--------------------------------------------------------------------------------
SELECT * FROM @updated;
END;

View File

@ -1,69 +0,0 @@
CREATE OR REPLACE FUNCTION pricequote.process_queue()
RETURNS SETOF pricequote.price_queue
LANGUAGE plpgsql
AS $$
BEGIN
--------------------------------------------------------------------------------
-- Step 1: Set channel, tier, and pallet quantity
--------------------------------------------------------------------------------
UPDATE pricequote.price_queue s
SET
chan = cr.chan,
tier = cr.tier,
pltq = cr.mpck
FROM (
SELECT
q.bill,
q.ship,
q.part,
i.mpck,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END AS chan,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END AS tier
FROM pricequote.price_queue q
LEFT JOIN rlarp.cust bc ON bc.code = q.bill
LEFT JOIN rlarp.cust sc ON sc.code = q.ship
LEFT JOIN "CMS.CUSLG".itemm i ON i.item = q.part
) cr
WHERE
cr.bill = s.bill AND
COALESCE(cr.ship, '') = COALESCE(s.ship, '') AND
cr.part = s.part;
--------------------------------------------------------------------------------
-- Step 2: Set price and explanation from target_prices, return touched rows
--------------------------------------------------------------------------------
RETURN QUERY
WITH updated AS (
UPDATE pricequote.price_queue q
SET
price = tp.price,
expl = jsonb_build_object(
'source', 'target price',
'calculated_pallets', FLOOR(q.vol / NULLIF(q.pltq, 0))::INT,
'exact_pallets', ROUND(q.vol / NULLIF(q.pltq, 0), 5),
'volume range', tp.vol
)
FROM pricequote.target_prices tp
WHERE
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND tp.vol @> FLOOR(q.vol / NULLIF(q.pltq, 0))::INT
RETURNING q.*
)
SELECT * FROM updated;
END;
$$;

View File

@ -1,69 +0,0 @@
CREATE OR REPLACE PROCEDURE pricequote.process_queue_proc()
LANGUAGE plpgsql
AS $$
BEGIN
--------------------------------------------------------------------------------
-- Step 1: Set channel, tier, and pallet quantity
--------------------------------------------------------------------------------
UPDATE pricequote.price_queue s
SET
chan = cr.chan,
tier = cr.tier,
pltq = cr.mpck,
cust = cr.cust
FROM (
SELECT
q.bill,
q.ship,
q.part,
i.mpck,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END AS chan,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END AS tier,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.dba
ELSE COALESCE(sc.dba, bc.dba)
END AS cust
FROM pricequote.price_queue q
LEFT JOIN rlarp.cust bc ON bc.code = q.bill
LEFT JOIN rlarp.cust sc ON sc.code = q.ship
LEFT JOIN "CMS.CUSLG".itemm i ON i.item = q.part
) cr
WHERE
cr.bill = s.bill AND
COALESCE(cr.ship, '') = COALESCE(s.ship, '') AND
cr.part = s.part;
--------------------------------------------------------------------------------
-- Step 2: Set price and explanation from target_prices
--------------------------------------------------------------------------------
UPDATE pricequote.price_queue q
SET
price = tp.price,
expl = jsonb_build_object(
'source', 'target price',
'calculated_pallets', FLOOR(q.vol / NULLIF(q.pltq, 0))::INT,
'exact_pallets', ROUND(q.vol / NULLIF(q.pltq, 0), 5),
'volume range', tp.vol
)
FROM pricequote.target_prices tp
WHERE
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND tp.vol @> FLOOR(q.vol / NULLIF(q.pltq, 0))::INT;
END;
$$;

View File

@ -1,27 +0,0 @@
CREATE OR ALTER FUNCTION dbo.LEAST_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
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

View File

@ -1,84 +0,0 @@
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 @price NUMERIC(20,5);
DECLARE @reason NVARCHAR(MAX) = '';
DECLARE @floored NUMERIC(20,5);
DECLARE @capped NUMERIC(20,5);
DECLARE @use_last_price BIT = 0;
-- 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
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);
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 @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
INSERT INTO @result VALUES (@price, @reason);
RETURN;
END

View File

@ -1,74 +0,0 @@
CREATE OR REPLACE FUNCTION pricequote.guidance_logic(
_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
) AS $$
DECLARE
_price NUMERIC(20,5);
_reason TEXT := '';
_floored NUMERIC(20,5);
_capped NUMERIC(20,5);
_use_last_price BOOLEAN := FALSE;
BEGIN
-- 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);
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;
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',
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',
to_char(_last_date, 'YYYY-MM-DD')
);
ELSE
_reason := 'No pricing available';
END IF;
END IF;
RETURN QUERY SELECT _price, _reason;
END;
$$ LANGUAGE plpgsql;

View File

@ -1,89 +0,0 @@
-- JSON-based helper function for last price selection
CREATE OR ALTER FUNCTION pricing.pick_last_price_from_hist_json (
@part_stats NVARCHAR(MAX),
@v1ds NVARCHAR(100)
)
RETURNS @result TABLE (
price NUMERIC(20,5),
source NVARCHAR(10),
odate DATE,
qty NUMERIC(20,5),
dataseg NVARCHAR(100),
ord NVARCHAR(20),
quote NVARCHAR(20)
)
AS
BEGIN
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);
SELECT @dsq = segflags.dsq, @dss = segflags.dss
FROM OPENJSON(@part_stats, '$."' + @v1ds + '"')
WITH (dss NVARCHAR(MAX) AS JSON, dsq NVARCHAR(MAX) AS JSON) segflags;
SELECT @mrq = flags.mrq, @mrs = flags.mrs
FROM OPENJSON(@part_stats)
WITH (mrq NVARCHAR(MAX) AS JSON, mrs NVARCHAR(MAX) AS JSON) flags;
-- Helper to extract fields from a JSON object
DECLARE @dsq_price NUMERIC(20,5), @dsq_date DATE, @dsq_qty NUMERIC(20,5), @dsq_dataseg NVARCHAR(100), @dsq_ord NVARCHAR(20), @dsq_quote NVARCHAR(20);
DECLARE @dss_price NUMERIC(20,5), @dss_date DATE, @dss_qty NUMERIC(20,5), @dss_dataseg NVARCHAR(100), @dss_ord NVARCHAR(20), @dss_quote NVARCHAR(20);
DECLARE @mrq_price NUMERIC(20,5), @mrq_date DATE, @mrq_qty NUMERIC(20,5), @mrq_dataseg NVARCHAR(100), @mrq_ord NVARCHAR(20), @mrq_quote NVARCHAR(20);
DECLARE @mrs_price NUMERIC(20,5), @mrs_date DATE, @mrs_qty NUMERIC(20,5), @mrs_dataseg NVARCHAR(100), @mrs_ord NVARCHAR(20), @mrs_quote NVARCHAR(20);
IF @dsq IS NOT NULL
SELECT @dsq_price = price, @dsq_date = odate, @dsq_qty = qty, @dsq_dataseg = datasegment, @dsq_ord = ordnum, @dsq_quote = quoten
FROM OPENJSON(@dsq)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20));
IF @dss IS NOT NULL
SELECT @dss_price = price, @dss_date = odate, @dss_qty = qty, @dss_dataseg = datasegment, @dss_ord = ordnum, @dss_quote = quoten
FROM OPENJSON(@dss)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20));
IF @mrq IS NOT NULL
SELECT @mrq_price = price, @mrq_date = odate, @mrq_qty = qty, @mrq_dataseg = datasegment, @mrq_ord = ordnum, @mrq_quote = quoten
FROM OPENJSON(@mrq)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20));
IF @mrs IS NOT NULL
SELECT @mrs_price = price, @mrs_date = odate, @mrs_qty = qty, @mrs_dataseg = datasegment, @mrs_ord = ordnum, @mrs_quote = quoten
FROM OPENJSON(@mrs)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20));
-- Use the same selection logic as before
-- 1. Prefer the most recent of dss/dsq if either is within the age threshold
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);
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);
RETURN;
END
-- 2. If both dss/dsq are older than the threshold, use the most recent of mrs/mrq if either exists
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);
ELSE IF @mrs_date IS NOT NULL
INSERT INTO @result VALUES (@mrs_price, 'mrs', @mrs_date, @mrs_qty, @mrs_dataseg, @mrs_ord, @mrs_quote);
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;
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;
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;
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;
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;
IF @best_price IS NOT NULL
INSERT INTO @result VALUES (@best_price, @best_source, @best_date, @best_qty, @best_dataseg, @best_ord, @best_quote);
RETURN;
END

View File

@ -1,76 +0,0 @@
CREATE OR REPLACE FUNCTION pricequote.pick_last_price_from_hist(
hist JSONB,
v1ds TEXT
)
RETURNS JSONB AS $$
DECLARE
dsq JSONB := (hist -> v1ds) -> 'dsq';
dss JSONB := (hist -> v1ds) -> 'dss';
mrq JSONB := hist -> 'mrq';
mrs JSONB := hist -> 'mrs';
result JSONB;
BEGIN
-- Central control for age threshold
DECLARE
age_threshold INTERVAL := INTERVAL '1 year';
dsq_date DATE := NULL;
dss_date DATE := NULL;
mrq_date DATE := NULL;
mrs_date DATE := NULL;
best JSONB := NULL;
best_date DATE := NULL;
BEGIN
IF dsq IS NOT NULL AND (dsq->>'price') IS NOT NULL THEN
dsq_date := (dsq->>'odate')::date;
END IF;
IF dss IS NOT NULL AND (dss->>'price') IS NOT NULL THEN
dss_date := (dss->>'odate')::date;
END IF;
IF mrq IS NOT NULL AND (mrq->>'price') IS NOT NULL THEN
mrq_date := (mrq->>'odate')::date;
END IF;
IF mrs IS NOT NULL AND (mrs->>'price') IS NOT NULL THEN
mrs_date := (mrs->>'odate')::date;
END IF;
-- 1. Prefer the most recent of dss/dsq if either is within the age threshold
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 (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;
result := best;
END IF;
RETURN result;
END;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

View File

@ -1,301 +0,0 @@
/*
====================================================================================
Script: single_price_call.pg.sql
Purpose: Single price call logic for a specific scenario (PostgreSQL)
-----------------------------------------------------------------------------------
Description:
- Accepts a single pricing scenario (bill, ship, part, v1ds, vol)
- Enriches with customer, channel, tier, pack quantity, price level, and part group
- Looks up and applies target price, price history, list price, and guidance logic
- Builds a JSON explanation and UI JSON for the scenario
Inputs:
- bill, ship, part, v1ds, vol (function arguments)
- Reference tables: pricequote.target_prices, pricequote.lastpricedetail, pricequote.pricelist_ranged
- Customer/item reference: rlarp.cust, CMS.CUSLG.itemm, CMS.CUSLG.IPRCBHC
Outputs:
- Returns a single enriched row with all pricing and explanation fields
Key Business Logic:
- Channel/tier/customer resolution based on bill/ship codes
- Target price and math lookup by segment, channel, tier, and volume
- Price history precedence and extraction via helper function
- List price selection: lowest valid price for the scenario
- Guidance logic: computed from target, last, and list prices
Dependencies:
- pricequote.guidance_logic (function)
- pricequote.pick_last_price_from_hist (function)
Notes:
- Designed for single-row pricing queries (API or UI)
- Assumes all referenced tables and functions exist
- See also: matrix_guidance.pg.sql for batch/matrix logic
====================================================================================
*/
CREATE OR REPLACE FUNCTION pricequote.single_price_call(
_bill TEXT,
_ship TEXT,
_part TEXT,
_v1ds TEXT,
_vol NUMERIC
)
RETURNS TABLE (
bill TEXT,
ship TEXT,
part TEXT,
stlc TEXT,
v1ds TEXT,
vol NUMERIC,
chan TEXT,
cust TEXT,
tier TEXT,
pltq NUMERIC,
plevel TEXT,
partgroup TEXT,
-- history JSONB,
last_price NUMERIC,
last_qty NUMERIC,
last_dataseg TEXT,
last_date DATE,
last_order TEXT,
last_quote TEXT,
last_source TEXT,
tprice NUMERIC,
tmath JSONB,
volume_range TEXT,
listprice NUMERIC,
listcode TEXT,
guidance_price NUMERIC,
guidance_reason TEXT,
expl JSONB,
ui_json JSONB
) AS $$
DECLARE
_pltq NUMERIC;
_chan TEXT;
_tier TEXT;
_cust TEXT;
_plevel TEXT;
_partgroup TEXT;
_stlc TEXT;
_tprice NUMERIC;
_tmath JSONB;
_volume_range TEXT;
_list_price NUMERIC;
_list_code TEXT;
_guidance_price NUMERIC;
_guidance_reason TEXT;
_hist JSONB := '{}'::jsonb;
-- No intermediate price history variables needed
-- Precedence chain
_last_price NUMERIC;
_last_qty NUMERIC;
_last_dataseg TEXT;
_last_date DATE;
_last_order TEXT;
_last_quote TEXT;
_last_source TEXT;
_expl JSONB := '{}'::jsonb;
_ui_json JSONB := '{}'::jsonb;
BEGIN
------------------------------------------------------------------
-- Step 1: Resolve customer metadata
------------------------------------------------------------------
SELECT
i.mpck,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE bc.dba
END,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN sc.plevel
ELSE bc.plevel
END
ELSE bc.plevel
END,
i.partgroup,
substring(_part,1,8)
INTO _pltq, _chan, _tier, _cust, _plevel, _partgroup, _stlc
FROM rlarp.cust bc
LEFT JOIN rlarp.cust sc ON sc.code = _ship
LEFT JOIN "CMS.CUSLG".itemm i ON i.item = _part
WHERE bc.code = _bill;
------------------------------------------------------------------
-- Step 2: Target price logic
------------------------------------------------------------------
SELECT tp.price,
to_json(tp.math),
tp.vol::text
INTO _tprice, _tmath, _volume_range
FROM pricequote.target_prices tp
WHERE tp.stlc = _stlc
AND tp.ds = _v1ds
AND tp.chan = _chan
AND tp.tier = _tier
AND FLOOR(_vol / NULLIF(_pltq, 0))::int <@ tp.vol;
------------------------------------------------------------------
-- Step 3: Last sale/quote/volume/segment data
------------------------------------------------------------------
SELECT
lp.part_stats
INTO
_hist
FROM pricequote.lastpricedetail lp
WHERE lp.customer = _cust
AND lp.partgroup = _partgroup;
-- No extraction of price history keys needed; handled in helper
-- Use helper function to select the best last price point and extract attributes
DECLARE
_last JSONB;
BEGIN
_last := pricequote.pick_last_price_from_hist(_hist, _v1ds);
_last_price := (_last->>'price')::numeric;
_last_qty := (_last->>'qty')::numeric;
_last_dataseg := _last->>'datasegment';
_last_date := (_last->>'odate')::date;
_last_order := _last->>'ordnum';
_last_quote := _last->>'quoten';
_last_source := _last->>'source';
END;
------------------------------------------------------------------
-- Step 4: List price
------------------------------------------------------------------
SELECT
pr.price::numeric(20,5), pr.jcplcd
INTO
_list_price, _list_code
FROM
"CMS.CUSLG".IPRCBHC i
JOIN pricequote.pricelist_ranged pr
ON pr.jcplcd = TRIM(i.jbplcd)
AND pr.jcpart = _part
AND _vol >= pr.vb_from
AND (_vol < pr.vb_to OR pr.vb_to IS NULL)
WHERE TRIM(i.jbplvl) = TRIM(_plevel)
AND CURRENT_DATE BETWEEN i.jbfdat AND i.jbtdat
ORDER BY pr.price ASC
LIMIT 1;
------------------------------------------------------------------
-- Step 5: Compute guidance price and embed it
------------------------------------------------------------------
SELECT
gl.guidance_price, gl.guidance_reason
INTO
_guidance_price, _guidance_reason
FROM
pricequote.guidance_logic(_tprice, _last_price, _list_price, _last_date) gl;
------------------------------------------------------------------
-- Step 6: Build explanation JSON
------------------------------------------------------------------
_expl := jsonb_build_object(
'last_price', _last_price,
'last_qty', _last_qty,
'last_dataseg', _last_dataseg,
'last_source', _last_source,
'last_date', _last_date,
'last_order', _last_order,
'last_quote', _last_quote,
'target_price', _tprice,
'target_math', _tmath,
'calculated_pallets', FLOOR(_vol / NULLIF(_pltq, 0)),
'exact_pallets', ROUND(_vol / NULLIF(_pltq, 0), 5),
'customer', _cust,
'channel', _chan,
'tier', TRIM(_tier),
'list_price', _list_price,
'list_code', _list_code,
'guidance_price', _guidance_price,
'guidance_reason', _guidance_reason
);
------------------------------------------------------------------
-- Step 7: Build UI JSON (optional, similar to MSSQL)
------------------------------------------------------------------
_ui_json := jsonb_build_object(
'details', jsonb_build_array(
jsonb_build_object(
'label', 'History',
'details', jsonb_build_array(
jsonb_build_object(
'label', CASE WHEN _last_price IS NOT NULL THEN 'Last Sale: ' || _last_date ELSE 'No Recent' END,
'value', COALESCE(_last_price,0),
'type', 'currency',
'note', CASE WHEN _last_price IS NOT NULL THEN _last_source ||
CASE WHEN COALESCE(_last_order, '0') = '0' THEN ' Qt# ' || COALESCE(_last_quote, '') ELSE ' Ord# ' || COALESCE(_last_order, '') END
ELSE NULL END
)
)
),
jsonb_build_object(
'label', 'List',
'details', jsonb_build_array(
jsonb_build_object(
'label', 'List:' || COALESCE(_list_code, ''),
'value', _list_price,
'type', 'currency',
'note', _plevel
)
)
),
jsonb_build_object(
'label', 'Target Support',
'details', _tmath -- You may need to transform this to match the MSSQL panel
),
jsonb_build_object(
'label', 'Guidance',
'details', jsonb_build_array(
jsonb_build_object(
'label', 'Price',
'value', _guidance_price,
'type', 'currency',
'note', _guidance_reason
)
)
)
),
'data', _expl
);
------------------------------------------------------------------
-- Final: Return row
------------------------------------------------------------------
RETURN QUERY
SELECT
_bill, _ship, _part, _stlc, _v1ds, _vol,
_chan, _cust, _tier, _pltq, _plevel, _partgroup,
-- _hist,
_last_price, _last_qty, _last_dataseg, _last_date, _last_order, _last_quote, _last_source,
_tprice, _tmath, _volume_range,
_list_price, _list_code,
_guidance_price, _guidance_reason,
_expl, _ui_json;
END;
$$ LANGUAGE plpgsql;

View File

@ -1,308 +0,0 @@
DROP FUNCTION pricing.single_price_call_fn;
CREATE FUNCTION pricing.single_price_call_fn (
@bill VARCHAR(100),
@ship VARCHAR(100),
@part VARCHAR(100),
@v1ds VARCHAR(100),
@vol NUMERIC(18,6)
)
RETURNS @queue TABLE (
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
stlc VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
chan VARCHAR(50),
cust VARCHAR(100),
tier VARCHAR(50),
pltq NUMERIC(18,6),
volume_range TEXT,
plevel NVARCHAR(20),
listprice NUMERIC(20,5),
listcode VARCHAR(10),
hist NVARCHAR(MAX),
last_price NUMERIC(20,5),
last_date DATE,
last_order NVARCHAR(10),
last_quote NVARCHAR(10),
tprice NUMERIC(20,5),
guidance_price NUMERIC(20,5),
guidance_reason NVARCHAR(MAX),
expl NVARCHAR(MAX),
ui_json NVARCHAR(MAX)
)
AS
BEGIN
--------------------------------------------------------------------------------
-- Step 1: Seed the queue with input row
--------------------------------------------------------------------------------
INSERT INTO @queue (bill, ship, part, v1ds, vol, expl)
VALUES (@bill, @ship, @part, @v1ds, @vol, '{}');
--------------------------------------------------------------------------------
-- Step 2: Enrich with channel, tier, customer, pack quantity, and price level
--------------------------------------------------------------------------------
UPDATE q
SET
chan =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END,
tier =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE ISNULL(sc.tier, bc.tier)
END,
cust =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE q.bill
END,
pltq = i.mpck,
plevel =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN sc.plevel
ELSE bc.plevel
END
ELSE bc.plevel
END,
stlc = substring(q.part,1,8)
FROM @queue q
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;
--------------------------------------------------------------------------------
-- Step 3: Apply target price and embed metadata as JSON
--------------------------------------------------------------------------------
UPDATE q
SET
tprice = tp.price,
expl = (
SELECT
'target price' AS [source],
tp.price AS [target_price],
FLOOR(q.vol / NULLIF(q.pltq, 0)) AS [calculated_pallets],
CAST(ROUND(q.vol / NULLIF(q.pltq, 0), 5) AS NUMERIC(20,5)) AS [exact_pallets],
CONCAT(tp.lower_bound, '-', ISNULL(CAST(tp.upper_bound AS VARCHAR), '')) AS [volume_range],
q.cust AS [customer],
q.chan AS [channel],
TRIM(q.tier) AS [tier],
JSON_QUERY(tp.math) AS [target_math]
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
volume_range = CONCAT(tp.lower_bound, '-', ISNULL(CAST(tp.upper_bound AS VARCHAR), ''))
FROM @queue q
INNER JOIN pricing.target_prices tp ON
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND FLOOR(q.vol / NULLIF(q.pltq, 0)) >= tp.lower_bound
AND (
tp.upper_bound IS NULL OR FLOOR(q.vol / NULLIF(q.pltq, 0)) < tp.upper_bound
);
--------------------------------------------------------------------------------
-- Step 4: Pull last sale data and embed in columns and JSON
--------------------------------------------------------------------------------
UPDATE q
SET
hist = JSON_MODIFY('{}', '$.full_history', JSON_QUERY(lp.part_stats)),
last_price = j.price,
last_date = j.odate,
last_order = j.ordnum,
last_quote = j.quoten,
expl = JSON_MODIFY(
JSON_MODIFY(
JSON_MODIFY(
JSON_MODIFY(
ISNULL(q.expl, '{}'),
'$.last_price', j.price
),
'$.last_date', CONVERT(NVARCHAR(10), j.odate, 23)
),
'$.last_order', j.ordnum
),
'$.last_quote', j.quoten
)
FROM @queue q
JOIN pricing.lastprice lp
ON lp.customer = q.cust
AND lp.mold = SUBSTRING(q.part, 1, 8)
OUTER APPLY (
SELECT TOP 1 *
FROM OPENJSON(lp.part_stats) AS p
OUTER APPLY OPENJSON(p.value)
WITH (
qty NUMERIC(20,5),
price NUMERIC(20,5),
odate DATE,
ordnum INT,
quoten INT
) AS j
WHERE p.[key] COLLATE SQL_Latin1_General_CP1_CI_AS = q.part
ORDER BY j.odate DESC
) AS j;
--------------------------------------------------------------------------------
-- Step 5: Add list price info from external pricelist
--------------------------------------------------------------------------------
WITH ranked_prices AS (
SELECT
q.bill, q.ship, q.part, q.stlc, q.v1ds, q.vol,
CAST(p.price AS NUMERIC(20,5)) AS price,
p.jcplcd,
ROW_NUMBER() OVER (
PARTITION BY q.bill, q.ship, q.part, q.stlc, q.v1ds, q.vol
ORDER BY p.price ASC
) AS rn
FROM @queue q
INNER JOIN CMSInterfaceIn."CMS.CUSLG".IPRCBHC i
ON TRIM(i.jbplvl) = TRIM(q.plevel)
AND CAST(GETDATE() AS DATE) BETWEEN i.jbfdat AND i.jbtdat
INNER JOIN pricing.pricelist_ranged p
ON p.jcplcd = TRIM(i.jbplcd)
AND p.jcpart = q.part
AND q.vol >= p.vb_from
AND (p.vb_to IS NULL OR q.vol < p.vb_to)
)
UPDATE q
SET expl = JSON_MODIFY(
JSON_MODIFY(
ISNULL(q.expl, '{}'),
'$.list_price', rp.price
),
'$.list_code', rp.jcplcd
)
,listcode = rp.jcplcd
,listprice = rp.price
FROM @queue q
JOIN ranked_prices rp
ON q.bill = rp.bill
AND q.ship = rp.ship
AND q.part = rp.part
AND q.stlc = rp.stlc
AND q.v1ds = rp.v1ds
AND q.vol = rp.vol
AND rp.rn = 1;
--------------------------------------------------------------------------------
-- Step 6: Compute guidance price and logic, and embed in JSON
--------------------------------------------------------------------------------
UPDATE q
SET
guidance_price = g.guidance_price,
guidance_reason = g.guidance_reason,
expl = JSON_MODIFY(
JSON_MODIFY(
ISNULL(q.expl, '{}'),
'$.guidance_reason',
g.guidance_reason
),
'$.guidance_price',
g.guidance_price
)
FROM @queue q
CROSS APPLY pricing.guidance_logic(
CAST(JSON_VALUE(q.expl, '$.target_price') AS NUMERIC(20,5)),
CAST(JSON_VALUE(q.expl, '$.last_price') AS NUMERIC(20,5)),
CAST(JSON_VALUE(q.expl, '$.list_price') AS NUMERIC(20,5)),
CAST(JSON_VALUE(q.expl, '$.last_date') AS DATE)
) g;
--------------------------------------------------------------------------------
-- Step 7: Clean up for UI
--------------------------------------------------------------------------------
UPDATE q
SET ui_json = (
SELECT
panel.label,
JSON_QUERY(panel.details) AS details
FROM (
-- History Panel
SELECT
'History' AS label,
(
SELECT
'Last Sale: ' + CAST(q.last_date AS varchar(10)) AS label,
q.last_price AS value,
'currency' AS type,
'Ord# ' + q.last_order AS note
FOR JSON PATH
) AS details
UNION ALL
-- List Panel
SELECT
'List' AS label,
(
SELECT
'List:' + q.listcode AS label,
q.listprice AS value,
'currency' AS type,
q.plevel AS note
FOR JSON PATH
)
UNION ALL
-- Target Support Panel
SELECT
'Target Support' AS label,
(
SELECT
-- parse each item in target_math
RTRIM(SUBSTRING(value,1,18)) label,
TRY_CAST(SUBSTRING(value,23,7) AS NUMERIC(20,5)) + CASE SUBSTRING(value,19,1) WHEN '+' THEN 0 ELSE -1 END AS value,
CASE SUBSTRING(value,19,1) WHEN '+' THEN 'currency' ELSE 'percentage' END AS type,
SUBSTRING(value,19,1) AS note
FROM OPENJSON(q.expl, '$.target_math')
WITH (value NVARCHAR(MAX) '$')
FOR JSON PATH
) AS details
UNION ALL
-- Guidance Panel
SELECT
'Guidance' AS label,
(
SELECT
'Last Price Capped' AS label,
q.guidance_price AS value,
'currency' AS type,
CONCAT(
'Last price ', q.last_price,
' capped at ',
ROUND((q.last_price - q.guidance_price) / NULLIF(q.last_price, 0) * 100, 2),
'%'
) AS note
FOR JSON PATH
)
) AS panel
FOR JSON PATH, ROOT('details')
)
FROM @queue q;
--------------------------------------------------------------------------------
-- Final: Return the enriched result row
--------------------------------------------------------------------------------
RETURN;
END;

View File

@ -1,75 +0,0 @@
{
"details": [
{
"label": "Model Inputs",
"details": [
{
"label": "Base Cost",
"value": 1.22446,
"type": "currency"
}
]
},
{
"label": "Peer Target",
"details": [
{
"label": "Peer Median Margin",
"value": 36.873,
"type": "percent",
"note": "DIR|102 - HANGING POTS|110 - INJECTION|Tier 2"
}
]
},
{
"label": "Target Support",
"details": [
{
"label": "Tier 1 Truckload",
"value": 80,
"type": "currency",
"note": "reviewed floor price"
},
{
"label": "Warehouse",
"value": 1.2,
"type": "percent"
}
]
},
{
"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"
}
]
}
]
}

View File

@ -1,15 +0,0 @@
SELECT
lq.*
,t.*
FROM
rlarp.live_quotes lq
OUTER APPLY pricing.fn_single_price_call(
lq.billto,
lq.shipto,
lq.part,
substring(lq.part,1,8),
lq.v1ds,
lq.units_each
) t
--WHERE
-- qid = 112794

View File

@ -1,327 +0,0 @@
DROP TABLE IF EXISTS pricequote.queue;
CREATE TABLE pricequote.queue (
bill TEXT,
ship TEXT,
part TEXT,
stlc TEXT,
v1ds TEXT,
vol NUMERIC,
chan TEXT,
cust TEXT,
tier TEXT,
pltq NUMERIC,
plevel TEXT,
last_price NUMERIC,
last_qty NUMERIC,
last_dataseg TEXT,
last_date DATE,
last_order TEXT,
last_quote TEXT,
last_source TEXT,
tprice NUMERIC,
tmath JSONB,
volume_range TEXT,
list_price NUMERIC,
list_code TEXT,
guidance_price NUMERIC,
guidance_reason TEXT,
expl JSONB,
ui_json JSONB,
partgroup TEXT
);
/*
====================================================================================
Script: matrix_guidance.pg.sql
Purpose: Batch pricing logic for sales matrix (PostgreSQL)
-----------------------------------------------------------------------------------
Description:
- Seeds a queue table with distinct pricing scenarios from rlarp.osm_stack
- Enriches each scenario with customer, channel, tier, pack quantity, and price level
- Looks up and applies target price, price history, list price, and guidance logic
- Builds a JSON explanation for each scenario
- Merges results back into the main sales matrix table
Inputs:
- Source table: rlarp.osm_stack
- Pricing reference tables: pricequote.target_prices, pricequote.lastpricedetail, pricequote.pricelist_ranged
- Customer/item reference: rlarp.cust, CMS.CUSLG.itemm, CMS.CUSLG.IPRCBHC
Outputs:
- Updates rlarp.osm_stack.pricing with a JSON explanation for each scenario
- All intermediate results are stored in pricequote.queue
Key Business Logic:
- Channel/tier/customer resolution based on bill/ship codes
- Target price and math lookup by segment, channel, tier, and volume
- Price history precedence and extraction via helper function
- List price selection: lowest valid price for the scenario
- Guidance logic: computed from target, last, and list prices
Dependencies:
- pricequote.guidance_logic (function)
- pricequote.pick_last_price_from_hist (function)
Notes:
- Designed for batch/matrix pricing updates
- Assumes all referenced tables and functions exist
- See also: single_price_call.pg.sql for single-row logic
====================================================================================
*/
CREATE INDEX idx_osm_stack_merge
ON rlarp.osm_stack (
bill_cust,
ship_cust,
part,
stlc,
dataseg,
qtyord
);
CREATE INDEX idx_queue_merge
ON pricequote.queue (
bill,
ship,
part,
stlc,
v1ds,
vol
);
--DROP PROCEDURE IF EXISTS pricequote.process_queue;
CREATE OR REPLACE PROCEDURE pricequote.process_queue()
LANGUAGE plpgsql
AS $$
BEGIN
-----------------------------------------------------------------------
-- Step 1: Seed the queue table with distinct pricing scenarios
-----------------------------------------------------------------------
DELETE FROM pricequote.queue;
INSERT INTO pricequote.queue (bill, ship, part, stlc, v1ds, vol, expl)
SELECT DISTINCT
o.bill_cust AS bill,
o.ship_cust AS ship,
o.part,
o.stlc,
o.dataseg AS v1ds,
o.qtyord AS vol,
'{}'::jsonb AS expl
FROM rlarp.osm_stack o
WHERE
o.fs_line = '41010'
AND o.calc_status <> 'CANCELLED'
AND o.version IN ('Actual', 'Forecast', 'Quotes')
AND o.part IS NOT NULL
AND substring(o.glec, 1, 1) <= '2';
-- 44 seconds
-----------------------------------------------------------------------
-- Step 2: Enrich customer, tier, channel, pack quantity, and level
-----------------------------------------------------------------------
MERGE INTO pricequote.queue q
USING (
SELECT
q.ctid,
-- Determine sales channel
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END AS chan,
-- Determine pricing tier
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END AS tier,
-- Resolve customer DBA name
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE bc.dba
END AS cust,
-- Pack quantity
i.mpck AS pltq,
-- Price level
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN sc.plevel
ELSE bc.plevel
END
ELSE bc.plevel
END AS plevel,
i.partgroup
FROM pricequote.queue q
JOIN rlarp.cust bc ON bc.code = q.bill
LEFT JOIN rlarp.cust sc ON sc.code = q.ship
LEFT JOIN "CMS.CUSLG".itemm i ON i.item = q.part
) src
ON (q.ctid = src.ctid)
WHEN MATCHED THEN
UPDATE SET
chan = src.chan,
tier = src.tier,
cust = src.cust,
pltq = src.pltq,
plevel = src.plevel,
partgroup = src.partgroup;
-- 17 seconds
-----------------------------------------------------------------------
-- Step 3: Apply target prices and embed target metadata
-----------------------------------------------------------------------
UPDATE pricequote.queue q
SET
tprice = tp.price,
expl = q.expl || jsonb_build_object(
'target_price', tp.price,
'calculated_pallets', FLOOR(q.vol / NULLIF(q.pltq, 0)),
'exact_pallets', ROUND(q.vol / NULLIF(q.pltq, 0), 5),
'volume range', tp.vol::TEXT,
'customer', q.cust,
'channel', q.chan,
'tier', TRIM(q.tier),
'target math', tp.math
)
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;
-- 20 seconds
-----------------------------------------------------------------------
-- Step 4: Lookup price history and embed all relevant keys and precedence
-----------------------------------------------------------------------
-- Use the helper function to extract last price precedence and build JSON explanation as in single_price_call
UPDATE pricequote.queue q
SET
last_price = (last_json->>'price')::numeric,
last_qty = (last_json->>'qty')::numeric,
last_dataseg = last_json->>'datasegment',
last_date = (last_json->>'odate')::date,
last_order = last_json->>'ordnum',
last_quote = last_json->>'quoten',
last_source = last_json->>'source',
expl = q.expl || jsonb_build_object(
'last_price', (last_json->>'price')::numeric,
'last_qty', (last_json->>'qty')::numeric,
'last_dataseg', last_json->>'datasegment',
'last_source', last_json->>'source',
'last_date', (last_json->>'odate')::date,
'last_order', last_json->>'ordnum',
'last_quote', last_json->>'quoten'
)
FROM (
SELECT q.ctid, pricequote.pick_last_price_from_hist(lp.part_stats, q.v1ds) AS last_json
FROM pricequote.queue q
JOIN pricequote.lastpricedetail lp
ON lp.customer = q.cust AND lp.partgroup = q.partgroup
) sub
WHERE q.ctid = sub.ctid;
-- 2 minutes 36 seconds
-----------------------------------------------------------------------
-- Step 5: Resolve best list price and insert it with list code
-----------------------------------------------------------------------
WITH ranked_prices AS (
SELECT
q.ctid,
pr.price,
pr.jcplcd,
ROW_NUMBER() OVER (PARTITION BY q.ctid ORDER BY pr.price ASC) AS rn
FROM pricequote.queue q
JOIN "CMS.CUSLG".IPRCBHC i
ON TRIM(i.jbplvl) = TRIM(q.plevel)
AND CURRENT_DATE BETWEEN i.jbfdat AND i.jbtdat
JOIN pricequote.pricelist_ranged pr
ON pr.jcplcd = TRIM(i.jbplcd)
AND pr.jcpart = q.part
AND q.vol >= pr.vb_from
AND q.vol < pr.vb_to
),
best_price AS (
SELECT * FROM ranked_prices WHERE rn = 1
)
UPDATE pricequote.queue q
SET
list_price = p.price,
list_code = p.jcplcd,
expl = q.expl || jsonb_build_object(
'list_price', p.price,
'list_code', p.jcplcd
)
FROM best_price p
WHERE q.ctid = p.ctid;
-- 15 seconds
-----------------------------------------------------------------------
-- Step 6: Compute guidance price using logic function
-----------------------------------------------------------------------
UPDATE pricequote.queue q
SET
guidance_price = g.guidance_price,
guidance_reason = g.guidance_reason,
expl = q.expl || jsonb_build_object(
'guidance_price', g.guidance_price,
'guidance_reason', g.guidance_reason
)
FROM (
SELECT
q.ctid,
g.guidance_price,
g.guidance_reason
FROM pricequote.queue q
JOIN LATERAL pricequote.guidance_logic(
q.tprice,
q.last_price,
q.list_price,
q.last_date
) g ON TRUE
) g
WHERE q.ctid = g.ctid;
-- 27 seconds
-----------------------------------------------------------------------
-- Step 7: merge the results back into sales matrix
-----------------------------------------------------------------------
UPDATE rlarp.osm_stack o
SET pricing = pricing || q.expl
FROM pricequote.queue q
WHERE
o.bill_cust = q.bill
AND o.ship_cust IS NOT DISTINCT FROM q.ship
AND o.part = q.part
AND o.stlc = q.stlc
AND o.dataseg = q.v1ds
AND o.qtyord = q.vol
AND o.fs_line = '41010'
AND o.calc_status <> 'CANCELLED'
AND o.version IN ('Actual', 'Forecast', 'Quotes')
AND o.part IS NOT NULL
AND substring(o.glec, 1, 1) <= '2';
-- 6 minutes 31 seconds
-----------------------------------------------------------------------
-- Done
-----------------------------------------------------------------------
RAISE NOTICE 'Queue processing complete.';
END;
$$;

View File

@ -1,184 +0,0 @@
--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);

View File

@ -1,237 +0,0 @@
--------------------------------------------------------------------------------
-- 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
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, 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;

View File

@ -1,14 +0,0 @@
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 FAnalysis.PRICING.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;

View File

@ -1,18 +0,0 @@
DROP TABLE pricing.target_prices;
CREATE TABLE pricing.target_prices (
stlc nvarchar(8) NOT NULL,
ds nvarchar(20) NOT NULL,
chan nvarchar(3) NOT NULL,
tier nvarchar(1) NOT NULL,
vol nvarchar(20) NOT NULL,
lower_bound int NOT NULL,
upper_bound int NULL,
price numeric(28,6) NOT NULL,
math nvarchar(MAX) NULL
);
ALTER TABLE pricing.target_prices
ADD CONSTRAINT uq_target_prices_unique_combo
UNIQUE (stlc, ds, chan, tier, vol, lower_bound);

View File

@ -1,23 +0,0 @@
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

View File

@ -1,63 +0,0 @@
{
"details": [
{
"label": "History",
"details": [
{
"label": "Last Sale",
"value": 0.081,
"type": "currency"
},
{
"label": "Last Quote",
"value": 0.079,
"type": "currency"
}
]
},
{
"label": "List",
"details": [
{
"label": "Code",
"value": "GUAU",
"type": "text",
"note": "Griffin East - US A List"
}
]
},
{
"label": "Target Support",
"details": [
{
"label": "Anchor",
"value": 0.080,
"type": "currency",
"note": "Tier 1 Truckload Black"
},
{
"label": "Color :L",
"value": 0.10,
"type": "percent",
"note": " x "
},
{
"label": "Tier 2",
"value": 0.02,
"type": "percent",
"note": " x "
}
]
},
{
"label": "Guidance",
"details": [
{
"label": "Last Price Capped",
"value": 0.080,
"type": "Last price 0.079 capped at 1%"
}
]
}
]
}

View File

@ -0,0 +1,81 @@
-- 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;

View File

@ -0,0 +1,72 @@
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 $$;

View File

@ -0,0 +1,61 @@
-- 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
)
RETURNS @ret TABLE (
guidance_price numeric(20,5),
guidance_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);
-- 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
BEGIN
SET @base_price = @last_norm;
SET @reason = N'Last price';
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);
RETURN;
END;

View File

@ -0,0 +1,56 @@
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
)
RETURNS TABLE (
guidance_price numeric(20,5),
guidance_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);
-- 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;
-- 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';
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 $$;

View File

@ -0,0 +1,107 @@
-- JSON-based helper function for last price selection
CREATE OR ALTER FUNCTION pricing.pick_last_price_from_hist_json (
@part_stats NVARCHAR(MAX),
@v1ds NVARCHAR(100)
)
RETURNS @result TABLE (
price NUMERIC(20,5),
source NVARCHAR(10),
odate DATE,
qty NUMERIC(20,5),
dataseg NVARCHAR(100),
ord NVARCHAR(20),
quote NVARCHAR(20),
part NVARCHAR(100)
)
AS
BEGIN
DECLARE @age_threshold DATE = DATEADD(month, -18, GETDATE());
-- Extract all relevant objects from JSON
DECLARE @dsq NVARCHAR(MAX), @dss NVARCHAR(MAX), @mrq NVARCHAR(MAX), @mrs NVARCHAR(MAX);
SELECT @dsq = segflags.dsq, @dss = segflags.dss
FROM OPENJSON(@part_stats, '$."' + @v1ds + '"')
WITH (dss NVARCHAR(MAX) AS JSON, dsq NVARCHAR(MAX) AS JSON) segflags;
SELECT @mrq = flags.mrq, @mrs = flags.mrs
FROM OPENJSON(@part_stats)
WITH (mrq NVARCHAR(MAX) AS JSON, mrs NVARCHAR(MAX) AS JSON) flags;
-- Helper to extract fields from a JSON object
DECLARE @dsq_price NUMERIC(20,5), @dsq_date DATE, @dsq_qty NUMERIC(20,5), @dsq_dataseg NVARCHAR(100), @dsq_ord NVARCHAR(20), @dsq_quote NVARCHAR(20), @dsq_part NVARCHAR(100);
DECLARE @dss_price NUMERIC(20,5), @dss_date DATE, @dss_qty NUMERIC(20,5), @dss_dataseg NVARCHAR(100), @dss_ord NVARCHAR(20), @dss_quote NVARCHAR(20), @dss_part NVARCHAR(100);
DECLARE @mrq_price NUMERIC(20,5), @mrq_date DATE, @mrq_qty NUMERIC(20,5), @mrq_dataseg NVARCHAR(100), @mrq_ord NVARCHAR(20), @mrq_quote NVARCHAR(20), @mrq_part NVARCHAR(100);
DECLARE @mrs_price NUMERIC(20,5), @mrs_date DATE, @mrs_qty NUMERIC(20,5), @mrs_dataseg NVARCHAR(100), @mrs_ord NVARCHAR(20), @mrs_quote NVARCHAR(20), @mrs_part NVARCHAR(100);
IF @dsq IS NOT NULL
SELECT @dsq_price = price, @dsq_date = odate, @dsq_qty = qty, @dsq_dataseg = datasegment, @dsq_ord = ordnum, @dsq_quote = quoten, @dsq_part = part
FROM OPENJSON(@dsq)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20), part NVARCHAR(100));
IF @dss IS NOT NULL
SELECT @dss_price = price, @dss_date = odate, @dss_qty = qty, @dss_dataseg = datasegment, @dss_ord = ordnum, @dss_quote = quoten, @dss_part = part
FROM OPENJSON(@dss)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20), part NVARCHAR(100));
IF @mrq IS NOT NULL
SELECT @mrq_price = price, @mrq_date = odate, @mrq_qty = qty, @mrq_dataseg = datasegment, @mrq_ord = ordnum, @mrq_quote = quoten, @mrq_part = part
FROM OPENJSON(@mrq)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20), part NVARCHAR(100));
IF @mrs IS NOT NULL
SELECT @mrs_price = price, @mrs_date = odate, @mrs_qty = qty, @mrs_dataseg = datasegment, @mrs_ord = ordnum, @mrs_quote = quoten, @mrs_part = part
FROM OPENJSON(@mrs)
WITH (price NUMERIC(20,5), qty NUMERIC(20,5), datasegment NVARCHAR(100), odate DATE, ordnum NVARCHAR(20), quoten NVARCHAR(20), part NVARCHAR(100));
-- 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
-- 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
-- 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;
-- 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

View File

@ -0,0 +1,68 @@
CREATE OR REPLACE FUNCTION pricequote.pick_last_price_from_hist(
hist JSONB,
v1ds TEXT
)
RETURNS JSONB AS $$
DECLARE
dsq JSONB := (hist -> v1ds) -> 'dsq';
dss JSONB := (hist -> v1ds) -> 'dss';
mrq JSONB := hist -> 'mrq';
mrs JSONB := hist -> 'mrs';
result JSONB;
BEGIN
-- Central control for age threshold
DECLARE
age_threshold INTERVAL := INTERVAL '18 months';
dsq_date DATE := NULL;
dss_date DATE := NULL;
mrq_date DATE := NULL;
mrs_date DATE := NULL;
best JSONB := NULL;
best_date DATE := NULL;
BEGIN
-- set dates
IF dsq IS NOT NULL AND (dsq->>'price') IS NOT NULL THEN
dsq_date := (dsq->>'odate')::date;
END IF;
IF dss IS NOT NULL AND (dss->>'price') IS NOT NULL THEN
dss_date := (dss->>'odate')::date;
END IF;
IF mrq IS NOT NULL AND (mrq->>'price') IS NOT NULL THEN
mrq_date := (mrq->>'odate')::date;
END IF;
IF mrs IS NOT NULL AND (mrs->>'price') IS NOT NULL THEN
mrs_date := (mrs->>'odate')::date;
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');
-- 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');
-- 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;
result := best;
END IF;
RETURN result;
END;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

View File

@ -0,0 +1,546 @@
-- Recreate queue with columns matching single_price_call outputs
DROP TABLE IF EXISTS pricequote.queue;
CREATE TABLE pricequote.queue (
bill TEXT,
ship TEXT,
part TEXT,
stlc TEXT,
v1ds TEXT,
vol NUMERIC,
chan TEXT,
cust TEXT,
tier TEXT,
pltq NUMERIC,
plevel TEXT,
partgroup TEXT,
part_v1ds TEXT,
v0ds TEXT,
curstd_orig NUMERIC,
futstd_orig NUMERIC,
curstd NUMERIC,
futstd NUMERIC,
curstd_last NUMERIC,
futstd_last NUMERIC,
customized TEXT,
last_premium NUMERIC,
last_premium_method TEXT,
last_price_norm NUMERIC,
last_isdiff TEXT,
last_v0ds TEXT,
tprice_last NUMERIC,
last_price NUMERIC,
last_qty NUMERIC,
last_dataseg TEXT,
last_date DATE,
last_order TEXT,
last_quote TEXT,
last_source TEXT,
hist JSONB,
tprice NUMERIC,
tmath JSONB,
volume_range TEXT,
listprice NUMERIC,
listcode TEXT,
listprice_eff NUMERIC,
list_relevance TEXT,
guidance_price NUMERIC,
guidance_reason TEXT,
approval_price NUMERIC,
approval_reason TEXT,
expl JSONB,
ui_json JSONB
);
CREATE INDEX IF NOT EXISTS idx_osm_stack_merge
ON rlarp.osm_stack (bill_cust, ship_cust, part, stlc, dataseg, qtyord);
CREATE INDEX IF NOT EXISTS idx_queue_merge
ON pricequote.queue (bill, ship, part, stlc, v1ds, vol);
-- Batch procedure mirroring single_price_call logic (4-space indentation)
--DROP PROCEDURE IF EXISTS pricequote.process_queue;
CREATE OR REPLACE PROCEDURE pricequote.process_queue()
LANGUAGE plpgsql
AS $$
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
o.bill_cust,
o.ship_cust,
o.part,
o.stlc,
o.dataseg,
o.qtyord,
'{}'::jsonb,
'{}'::jsonb
FROM rlarp.osm_stack o
WHERE
o.fs_line = '41010'
AND o.calc_status <> 'CANCELLED'
AND o.version IN ('Actual', 'Forecast', 'Quotes')
AND o.part IS NOT NULL
AND SUBSTRING(o.glec, 1, 1) <= '2';
-- 2:12 0:38
--------------------------------------------------------------------
-- 2) Enrich: chan, tier, cust, pltq, plevel, partgroup (+stlc fix)
--------------------------------------------------------------------
MERGE INTO pricequote.queue q
USING (
SELECT
q.ctid,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3) WHEN 'DIS' THEN 'WHS' ELSE 'DRP' END
ELSE 'DIR'
END AS chan,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END AS tier,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3) WHEN 'DIS' THEN bc.dba ELSE sc.dba END
ELSE bc.dba
END AS cust,
i.mpck AS pltq,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN CASE SUBSTRING(sc.cclass, 2, 3) WHEN 'DIS' THEN sc.plevel ELSE bc.plevel END
ELSE bc.plevel
END AS plevel,
i.partgroup AS partgroup,
SUBSTRING(q.part, 1, 8) AS stlc_fix
FROM pricequote.queue q
JOIN rlarp.cust bc ON bc.code = q.bill
LEFT JOIN rlarp.cust sc ON sc.code = q.ship
LEFT JOIN "CMS.CUSLG".itemm i ON i.item = q.part
) s
ON (q.ctid = s.ctid)
WHEN MATCHED THEN UPDATE SET
chan = s.chan,
tier = s.tier,
cust = s.cust,
pltq = s.pltq,
plevel = s.plevel,
partgroup = s.partgroup,
stlc = COALESCE(q.stlc, s.stlc_fix);
-- 4:51 0:17
--------------------------------------------------------------------
-- 3) Scenario fields from item master: part_v1ds, v0ds, orig costs
-- + customized flag
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
part_v1ds = i0.v1ds,
v0ds = (CASE SUBSTRING(q.v1ds, 4, 1) WHEN 'B' THEN 'B' ELSE 'C' END)
|| (CASE SUBSTRING(q.v1ds, 6, 1) WHEN 'L' THEN 'L' WHEN 'P' THEN 'P' ELSE '' END),
curstd_orig = i0.curstdus,
futstd_orig = i0.futstdus,
customized = CASE
WHEN i0.v1ds IS NOT NULL AND q.v1ds IS NOT NULL AND i0.v1ds <> q.v1ds
THEN 'Customized'
ELSE ''
END
FROM "CMS.CUSLG".itemm i0
WHERE i0.item = q.part;
-- 3:21 0:20
--------------------------------------------------------------------
-- 4) History: store hist, extract last_* with precedence helper
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
hist = x.part_stats, -- from the correlated subquery
last_price = (j->>'price')::NUMERIC,
last_qty = (j->>'qty')::NUMERIC,
last_dataseg = j->>'datasegment',
last_date = (j->>'odate')::DATE,
last_order = j->>'ordnum',
last_quote = j->>'quoten',
last_source = j->>'source',
last_isdiff = CASE
WHEN (j->>'datasegment') IS NOT NULL
AND q.v1ds IS NOT NULL
AND (j->>'datasegment') <> q.v1ds
THEN 'Last Sale Diff Part'
END,
last_v0ds = (CASE SUBSTRING(j->>'datasegment', 4, 1)
WHEN 'B' THEN 'B' ELSE 'C' END)
|| (CASE SUBSTRING(j->>'datasegment', 6, 1)
WHEN 'L' THEN 'L'
WHEN 'P' THEN 'P'
ELSE '' END)
FROM (
SELECT
q2.ctid,
lp2.part_stats,
pricequote.pick_last_price_from_hist(lp2.part_stats, q2.v1ds) AS j
FROM pricequote.queue q2
JOIN pricequote.lastpricedetail lp2
ON lp2.customer = q2.cust
AND lp2.partgroup = q2.partgroup
) AS x
WHERE q.ctid = x.ctid;
-- 7:32 1:31
--------------------------------------------------------------------
-- 5) Target (requested v1ds): tprice, tmath, volume_range
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
tprice = tp.price,
tmath = to_json(tp.math),
volume_range = tp.vol::TEXT
FROM pricequote.target_prices_base 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
--------------------------------------------------------------------
-- 6) Target for last_dataseg (tprice_last)
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
tprice_last = tp2.price
FROM pricequote.target_prices_base 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
--------------------------------------------------------------------
-- 7) Cost data for requested v1ds and last_dataseg
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
curstd = CASE WHEN COALESCE(q.customized,'') = '' THEN q.curstd_orig ELSE COALESCE(s.v1_cur, s.v0_cur) END,
futstd = CASE WHEN COALESCE(q.customized,'') = '' THEN q.futstd_orig ELSE COALESCE(s.v1_fut, s.v0_fut) END,
curstd_last = CASE WHEN COALESCE(q.last_isdiff,'') = '' THEN q.curstd_orig ELSE COALESCE(s.v1l_cur, s.v0l_cur) END,
futstd_last = CASE WHEN COALESCE(q.last_isdiff,'') = '' THEN q.futstd_orig ELSE COALESCE(s.v1l_fut, s.v0l_fut) END
FROM (
SELECT
q2.ctid,
v1.curstdus AS v1_cur,
v1.futstdus AS v1_fut,
v0.curstdus AS v0_cur,
v0.futstdus AS v0_fut,
v1l.curstdus AS v1l_cur,
v1l.futstdus AS v1l_fut,
v0l.curstdus AS v0l_cur,
v0l.futstdus AS v0l_fut
FROM pricequote.queue q2
LEFT JOIN rlarp.cost_v1ds v1
ON v1.stlc = q2.stlc AND v1.v1ds = q2.v1ds
LEFT JOIN rlarp.cost_v0ds v0
ON v0.stlc = q2.stlc AND v0.v0ds = q2.v0ds
LEFT JOIN rlarp.cost_v1ds v1l
ON v1l.stlc = q2.stlc AND v1l.v1ds = q2.last_dataseg
LEFT JOIN rlarp.cost_v0ds v0l
ON v0l.stlc = q2.stlc AND v0l.v0ds = q2.last_v0ds
) AS s
WHERE q.ctid = s.ctid;
-- 4:15 0:25
--------------------------------------------------------------------
-- 8) List price (lowest valid); allow open-ended ranges (vb_to IS NULL)
--------------------------------------------------------------------
WITH ranked_prices AS (
SELECT
q.ctid,
pr.price,
pr.jcplcd,
ROW_NUMBER() OVER (PARTITION BY q.ctid ORDER BY pr.price ASC) AS rn
FROM pricequote.queue q
JOIN "CMS.CUSLG".IPRCBHC i
ON TRIM(i.jbplvl) = TRIM(q.plevel)
AND CURRENT_DATE BETWEEN i.jbfdat AND i.jbtdat
JOIN pricequote.pricelist_ranged pr
ON pr.jcplcd = TRIM(i.jbplcd)
AND pr.jcpart = q.part
AND q.vol >= pr.vb_from
AND (q.vol < pr.vb_to OR pr.vb_to IS NULL)
),
best_price AS (
SELECT * FROM ranked_prices WHERE rn = 1
)
UPDATE pricequote.queue q
SET
listprice = p.price,
listcode = p.jcplcd
FROM best_price p
WHERE q.ctid = p.ctid;
-- 2:48 0:18
--------------------------------------------------------------------
-- 9) Normalize last (when last_dataseg != v1ds) + effective list flags
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
last_premium = CASE
WHEN q.last_isdiff IS NOT NULL
AND q.tprice_last IS NOT NULL
AND q.tprice IS NOT NULL
AND q.tprice_last <> 0
THEN ROUND(q.tprice / q.tprice_last, 5)
WHEN q.last_isdiff IS NOT NULL
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)
END,
last_premium_method = CASE
WHEN q.last_isdiff IS NOT NULL
AND q.tprice_last IS NOT NULL
AND q.tprice IS NOT NULL
AND q.tprice_last <> 0
THEN 'Target Price Ratio'
WHEN q.last_isdiff IS NOT NULL
AND q.curstd_last IS NOT NULL
AND q.curstd IS NOT NULL
AND q.curstd_last <> 0
THEN 'Cost Ratio'
WHEN q.last_isdiff IS NOT NULL
THEN 'Unknown'
END,
last_price_norm = CASE
WHEN q.last_isdiff IS NOT NULL
AND q.tprice_last IS NOT NULL
AND q.tprice IS NOT NULL
AND q.tprice_last <> 0
THEN ROUND(q.last_price * (q.tprice / q.tprice_last), 5)
WHEN q.last_isdiff IS NOT NULL
AND q.curstd_last IS NOT NULL
AND q.curstd IS NOT NULL
AND q.curstd_last <> 0
THEN ROUND(q.last_price * (q.curstd / q.curstd_last), 5)
ELSE q.last_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;
-- 2:22 0:23
--------------------------------------------------------------------
-- 10) Guidance using normalized last + effective list
--------------------------------------------------------------------
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
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
) s
WHERE q.ctid = s.ctid;
-- 4:33 0:39
--------------------------------------------------------------------
-- 11) Build expl and ui_json identical to single_price_call
--------------------------------------------------------------------
UPDATE pricequote.queue q
SET
expl = jsonb_build_object(
'last', jsonb_build_object(
'last_part', (pricequote.pick_last_price_from_hist(q.hist, q.v1ds)->>'part'),
'last_price', q.last_price,
'last_qty', q.last_qty,
'last_dataseg', q.last_dataseg,
'last_v0ds', q.last_v0ds,
'last_source', q.last_source,
'last_date', q.last_date,
'last_order', q.last_order,
'last_quote', q.last_quote,
'last_isdiff', q.last_isdiff,
'last_premium', q.last_premium,
'last_premium_method', q.last_premium_method,
'last_price_norm', q.last_price_norm,
'tprice_last', q.tprice_last
),
'scenario', jsonb_build_object(
'calculated_pallets', FLOOR(q.vol / NULLIF(q.pltq, 0)),
'exact_pallets', ROUND(q.vol / NULLIF(q.pltq, 0), 5),
'customer', q.cust,
'channel', q.chan,
'tier', TRIM(q.tier),
'v1ds', q.v1ds,
'v0ds', q.v0ds,
'part_v1ds', q.part_v1ds,
'customized', q.customized
),
'cost', jsonb_build_object(
'curstd_orig', q.curstd_orig,
'futstd_orig', q.futstd_orig,
'curstd_last', q.curstd_last,
'futstd_last', q.futstd_last,
'curstd', q.curstd,
'futstd', q.futstd
),
'targets', jsonb_build_object(
'target_price', q.tprice,
'target_math', q.tmath,
'volume_range', q.volume_range
),
'list', jsonb_build_object(
'listcode', q.listcode,
'listprice', q.listprice,
'listprice_eff', q.listprice_eff,
'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
),
ui_json = jsonb_build_object(
'details', jsonb_build_array(
jsonb_build_object(
'label', 'History',
'details', jsonb_build_array(
jsonb_build_object(
'label', CASE WHEN q.last_price IS NOT NULL THEN 'Last Sale: ' || q.last_date ELSE 'No Recent' END,
'value', COALESCE(q.last_price, 0),
'type', 'currency',
'note', CASE WHEN q.last_price IS NOT NULL THEN
CASE q.last_source
WHEN 'mrq' THEN 'Recent similar ' || (pricequote.pick_last_price_from_hist(q.hist, q.v1ds)->>'part') || ' qty: ' || q.last_qty
WHEN 'mrs' THEN 'Recent similar ' || (pricequote.pick_last_price_from_hist(q.hist, q.v1ds)->>'part') || ' qty: ' || q.last_qty
WHEN 'dsq' THEN 'Last quote qty: ' || q.last_qty
WHEN 'dss' THEN 'Last sale qty: ' || q.last_qty
ELSE ''
END
|| CASE WHEN COALESCE(q.last_order, '0') = '0'
THEN ' Qt# ' || COALESCE(q.last_quote, '')
ELSE ' Ord# ' || COALESCE(q.last_order, '')
END
END
)
)
|| CASE WHEN COALESCE(q.last_premium, 1) <> 1 THEN
jsonb_build_array(
jsonb_build_object(
'label', 'Price Difference',
'value', q.last_premium,
'type', 'percent',
'note', q.last_premium_method
)
)
ELSE '[]'::jsonb END
|| CASE WHEN COALESCE(q.last_premium, 1) <> 1 THEN
jsonb_build_array(
jsonb_build_object(
'label', 'Adjusted Price',
'value', q.last_price_norm,
'type', 'currency',
'note', 'normalized to ' || q.v1ds
)
)
ELSE '[]'::jsonb END
),
jsonb_build_object(
'label', 'List',
'details', jsonb_build_array(
jsonb_build_object(
'label', 'List:' || COALESCE(q.listcode, ''),
'value', q.listprice,
'type', 'currency',
'note', q.list_relevance
)
)
),
jsonb_build_object(
'label', 'Target Calculation',
'details',
(
SELECT jsonb_agg(
jsonb_build_object(
'label', CASE WHEN v <> '' THEN RTRIM(SUBSTRING(v, 1, 18)) ELSE 'No Target' END,
'value', CASE WHEN v <> '' THEN SUBSTRING(v, 23, 7)::NUMERIC(20,5)
+ CASE SUBSTRING(v, 19, 1) WHEN '+' THEN 0 ELSE -1 END
ELSE 0 END,
'type', CASE WHEN v <> '' THEN CASE SUBSTRING(v, 19, 1) WHEN '+' THEN 'currency' ELSE 'Percent' END ELSE '' END,
'note', CASE WHEN v <> '' THEN CASE SUBSTRING(v, 19, 1) WHEN '+' THEN 'Price' ELSE 'Premium' END ELSE '' END
)
)
FROM jsonb_array_elements_text(COALESCE(q.tmath, '[""]'::jsonb)) AS t(v)
)
|| CASE WHEN q.tprice IS NULL THEN '[]'::jsonb
ELSE jsonb_build_object('label', 'Price', 'value', COALESCE(q.tprice, 0), 'type', 'currency', 'note', 'Total') END
),
jsonb_build_object(
'label', 'Guidance',
'details', jsonb_build_array(
jsonb_build_object(
'label', 'Price',
'value', COALESCE(q.guidance_price, 0),
'type', 'currency',
'note', COALESCE(q.guidance_reason, '')
)
)
)
),
'data', q.expl
);
-- 7:59 2:17
--------------------------------------------------------------------
-- 12) Merge back into matrix (store both expl and ui)
--------------------------------------------------------------------
UPDATE rlarp.osm_stack o
SET pricing = pricing
|| jsonb_build_object(
'expl', q.expl,
'ui', q.ui_json
)
FROM pricequote.queue q
WHERE
o.bill_cust = q.bill
AND o.ship_cust IS NOT DISTINCT FROM q.ship
AND o.part = q.part
AND o.stlc = q.stlc
AND o.dataseg = q.v1ds
AND o.qtyord = q.vol
AND o.fs_line = '41010'
AND o.calc_status <> 'CANCELLED'
AND o.version IN ('Actual', 'Forecast', 'Quotes')
AND o.part IS NOT NULL
AND SUBSTRING(o.glec, 1, 1) <= '2';
-- 14:13 10:09
RAISE NOTICE 'Queue processing complete.';
END;
$$;

View File

@ -0,0 +1,638 @@
/*
====================================================================================
Script: single_price_call.ms.sql
Purpose:
Single price call logic for SQL Server, designed to process a single scenario
(bill-to, ship-to, part, volume, and target data segment) and return enriched
pricing guidance along with explanation JSON for UI or API use.
-----------------------------------------------------------------------------------
Core Workflow:
1. **Seed Input**: Initialize queue with bill, ship, part, v1ds, vol.
2. **Customer & Channel Enrichment**:
- Resolve customer, channel, tier, pack quantity, price level.
- Classify and flag customized part scenarios when v1ds differs from part's own v1ds.
- Capture standard costs (current/future) and derive v0ds from v1ds.
3. **Last Price History**:
- Pull `part_stats` JSON from `pricing.lastpricedetail`.
- Extract most recent sale/quote via `pricing.pick_last_price_from_hist_json`.
- Flag part/dataseg mismatches (`last_isdiff`) and derive last v0ds.
4. **Target Price Application**:
- Lookup current and last target prices (matching respective v1ds).
- Store target price math JSON and pallet volume range.
5. **Cost Substitution & Normalization**:
- For customized or differing dataseg, substitute average costs from v1/v0 sources.
- Compute premiums and normalize last prices for comparison.
- Record calculation method (`Target Price Ratio` or `Cost Ratio`).
6. **List Price Selection**:
- From external `pricelist_ranged`, pick lowest valid list price in volume band.
- Nullify list price when customized, with relevance flag.
7. **Guidance Logic**:
- Pass target, normalized last, and list prices into `pricing.guidance_logic`.
- Return computed guidance price and rationale.
8. **JSON Explanation Build**:
- Assemble all pricing components into `expl` JSON for structured storage.
9. **UI JSON Build**:
- Package human-readable panels (History, List, Target Support, Guidance)
plus raw `expl` JSON into `ui_json`.
-----------------------------------------------------------------------------------
Inputs:
- @bill, @ship, @part, @v1ds, @vol
Reference Tables:
- pricing.target_prices
- pricing.lastpricedetail
- pricing.pricelist_ranged
- rlarp.cust
- CMS.CUSLG.itemm
- CMS.CUSLG.IPRCBHC
- rlarp.cost_v1ds / cost_v0ds
Outputs:
- Single row with:
* Enriched pricing attributes
* Target, last, list, and guidance prices
* Cost data and premiums
* `expl` JSON (raw detail)
* `ui_json` JSON (UI-ready panels)
Dependencies:
- pricing.guidance_logic()
- pricing.pick_last_price_from_hist_json()
Notes:
- Designed for single-row queries; see matrix_guidance.pg.sql for batch mode.
- Last price normalization ensures cross-segment comparisons are cost/target aligned.
- List price is ignored for customized part scenarios.
- Sequencing ensures:
* Historical context (last price) is established before guidance logic
* Target price and cost adjustments precede list price selection
====================================================================================
*/
CREATE OR ALTER PROCEDURE pricing.single_price_call
@bill VARCHAR(100),
@ship VARCHAR(100),
@part VARCHAR(100),
@v1ds VARCHAR(100),
@vol NUMERIC(18,6)
AS
BEGIN
SET NOCOUNT ON;
-- Working table for enriched pricing request
DECLARE @queue TABLE (
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
------------step 1 lookup scenario------------
chan VARCHAR(50),
tier VARCHAR(50),
cust VARCHAR(100),
pltq NUMERIC(18,6),
plevel NVARCHAR(20),
stlc VARCHAR(100),
partgroup VARCHAR(100),
part_v1ds VARCHAR(50),
v0ds VARCHAR(10),
curstd_orig NUMERIC(20,5),
futstd_orig NUMERIC(20,5),
customized VARCHAR(100),
calculated_pallets numeric(20,0),
exact_pallets numeric(20,5),
----------- step 2 last price------------------
hist NVARCHAR(MAX),
last_price NUMERIC(20,5),
last_source NVARCHAR(100),
last_date DATE,
last_qty NUMERIC(20,5),
last_dataseg NVARCHAR(20),
last_v0ds VARCHAR(10),
last_order NVARCHAR(10),
last_quote NVARCHAR(10),
last_isdiff NVARCHAR(100),
last_part NVARCHAR(100),
------------step 3 lookup target---------------
tprice NUMERIC(20,5),
tprice_last NUMERIC(20,5),
tmath nvarchar(MAX),
volume_range VARCHAR(100),
------------step 4 normalize last price--------
curstd NUMERIC(20,5),
futstd NUMERIC(20,5),
curstd_last NUMERIC(20,5),
futstd_last NUMERIC(20,5),
last_premium NUMERIC(20,5),
last_price_norm NUMERIC(20,5),
last_premium_method VARCHAR(100),
------------step 5 list price lookup-----------
listcode VARCHAR(10),
listprice NUMERIC(20,5),
listprice_eff NUMERIC(20,5),
list_relevance NVARCHAR(100),
list_from BIGINT,
------------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)
);
--------------------------------------------------------------------------------
-- Step 1: Seed input
--------------------------------------------------------------------------------
INSERT INTO @queue (bill, ship, part, v1ds, vol, expl)
VALUES (@bill, @ship, @part, @v1ds, @vol, '{}');
--------------------------------------------------------------------------------
-- Step 2: Look up master data & costs
--------------------------------------------------------------------------------
UPDATE q
SET
chan =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END,
tier =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE ISNULL(sc.tier, bc.tier)
END,
cust =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE bc.dba
END,
pltq = i.mpck,
plevel =
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN sc.plevel
ELSE bc.plevel
END
ELSE bc.plevel
END,
stlc = substring(q.part,1,8),
partgroup = TRIM(i.partgroup),
part_v1ds = TRIM(i.v1ds),
v0ds =
CASE substring(q.v1ds,4,1) WHEN 'B' THEN 'B' ELSE 'C' END
+ CASE substring(q.v1ds,6,1) WHEN 'L' THEN 'L' WHEN 'P' THEN 'P' ELSE '' END,
curstd_orig = i.curstdus,
futstd_orig = i.futstdus,
customized = CASE WHEN i.v1ds IS NOT NULL AND q.v1ds IS NOT NULL AND i.v1ds <> q.v1ds
THEN 'Customized' ELSE '' END,
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 CMSInterfaceIn.[CMS.CUSLG].itemm i ON i.item = q.part;
--------------------------------------------------------------------------------
-- Step 3: Lookup Last Price
--------------------------------------------------------------------------------
UPDATE q
SET
hist = lp.part_stats
FROM @queue q
JOIN pricing.lastpricedetail lp
ON lp.customer = q.cust AND lp.partgroup = q.partgroup;
-- Use new helper to select best last price, source, and date directly from JSON
UPDATE q
SET
last_price = b.price,
last_source = b.source,
last_date = b.odate,
last_qty = b.qty,
last_dataseg = b.dataseg,
last_v0ds =
CASE substring(b.dataseg,4,1) WHEN 'B' THEN 'B' ELSE 'C' END
+ CASE substring(b.dataseg,6,1) WHEN 'L' THEN 'L' WHEN 'P' THEN 'P' ELSE '' END,
last_order = b.ord,
last_quote = b.quote,
last_isdiff = CASE WHEN b.dataseg IS NOT NULL AND q.v1ds IS NOT NULL AND b.dataseg <> q.v1ds
THEN 'Last Sale Diff Part' ELSE '' END,
last_part = b.part
FROM @queue q
CROSS APPLY (
SELECT TOP 1 price, source, odate, qty, dataseg, ord, quote, part
FROM pricing.pick_last_price_from_hist_json(q.hist, q.v1ds)
) b;
--------------------------------------------------------------------------------
-- Step 4: Lookup Target Price
--------------------------------------------------------------------------------
UPDATE q
SET
tprice = tp.price
,tprice_last = tpl.price
,tmath = JSON_QUERY(tp.math)
,volume_range = CONCAT(tp.lower_bound, '-', ISNULL(CAST(tp.upper_bound AS VARCHAR), ''))
FROM @queue q
LEFT JOIN pricing.target_prices tp ON
q.stlc = tp.stlc
AND q.v1ds = tp.ds
AND q.chan = tp.chan
AND q.tier = tp.tier
AND q.calculated_pallets >= tp.lower_bound
AND (
tp.upper_bound IS NULL OR q.calculated_pallets < tp.upper_bound
)
LEFT JOIN pricing.target_prices tpl ON
q.stlc = tpl.stlc
AND q.last_dataseg = tpl.ds
AND q.chan = tpl.chan
AND q.tier = tpl.tier
AND (q.last_qty/q.pltq) >= tpl.lower_bound
AND (
tpl.upper_bound IS NULL OR (q.last_qty/q.pltq) < tpl.upper_bound
);
--------------------------------------------------------------------------------
-- Step 5: Normalize last price if different from target product
--------------------------------------------------------------------------------
-- Goal data segment inherits part's original segment; if customized, pull available v1/v0 averages.
UPDATE q
SET
curstd = CASE WHEN customized = '' THEN q.curstd_orig ELSE COALESCE(v1.curstdus, v0.curstdus) END
,futstd = CASE WHEN customized = '' THEN q.futstd_orig ELSE COALESCE(v1.futstdus, v0.futstdus) END
,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
v1.stlc = q.stlc
AND v1.v1ds = q.v1ds
LEFT JOIN pricing.cost_v0ds v0 ON
v0.stlc = q.stlc
AND v0.v0ds = q.v0ds
LEFT JOIN pricing.cost_v1ds v1l ON
v1l.stlc = q.stlc
AND v1l.v1ds = q.last_dataseg
LEFT JOIN pricing.cost_v0ds v0l ON
v0l.stlc = q.stlc
AND v0l.v0ds = q.last_v0ds;
UPDATE q
SET
last_premium =
CASE WHEN q.last_isdiff <> '' THEN
CASE
WHEN tprice_last IS NOT NULL AND tprice IS NOT NULL AND tprice_last <> 0
THEN CAST(tprice / tprice_last AS NUMERIC(20,5))
WHEN curstd_last IS NOT NULL AND curstd IS NOT NULL AND curstd_last <> 0
THEN CAST(curstd / curstd_last AS NUMERIC(20,5))
ELSE NULL
END
ELSE NULL
END
,last_price_norm =
CASE WHEN q.last_isdiff <> '' THEN
CASE
WHEN tprice_last IS NOT NULL AND tprice IS NOT NULL AND tprice_last <> 0
THEN CAST(ROUND(q.last_price * (tprice / tprice_last), 5) AS NUMERIC(20,5))
WHEN curstd_last IS NOT NULL AND curstd IS NOT NULL AND curstd_last <> 0
THEN CAST(ROUND(q.last_price * (curstd / curstd_last), 5) AS NUMERIC(20,5))
ELSE q.last_price
END
ELSE q.last_price
END
,last_premium_method =
CASE
WHEN q.last_isdiff <> '' THEN
CASE
WHEN tprice_last IS NOT NULL AND tprice IS NOT NULL AND tprice_last <> 0
THEN 'Target Price Ratio'
WHEN curstd_last IS NOT NULL AND curstd IS NOT NULL AND curstd_last <> 0
THEN 'Cost Ratio'
ELSE 'Unknown'
END
ELSE NULL
END
FROM @queue q;
--------------------------------------------------------------------------------
-- Step 6: Lookup List Price
--------------------------------------------------------------------------------
WITH ranked_prices AS (
SELECT
q.bill, q.ship, q.part, q.stlc, q.v1ds, q.vol,
CAST(p.price AS NUMERIC(20,5)) AS price,
p.jcplcd,
ROW_NUMBER() OVER (
PARTITION BY q.bill, q.ship, q.part, q.stlc, q.v1ds, q.vol
ORDER BY p.price ASC
) AS rn,
p.vb_from,
p.vb_to
FROM @queue q
INNER JOIN CMSInterfaceIn."CMS.CUSLG".IPRCBHC i
ON TRIM(i.jbplvl) = TRIM(q.plevel)
AND CAST(GETDATE() AS DATE) BETWEEN i.jbfdat AND i.jbtdat
INNER JOIN pricing.pricelist_ranged p
ON p.jcplcd = TRIM(i.jbplcd)
AND p.jcpart = q.part
AND q.vol >= p.vb_from
AND (p.vb_to IS NULL OR q.vol < p.vb_to)
)
UPDATE q
SET
listcode = rp.jcplcd
,listprice = rp.price
,listprice_eff = CASE WHEN q.customized <> '' THEN NULL ELSE rp.price END
,list_relevance = CASE WHEN q.customized <> '' THEN 'Ignore - Customized' ELSE '' END
,list_from = vb_from
FROM @queue q
JOIN ranked_prices rp
ON q.bill = rp.bill
AND q.ship = rp.ship
AND q.part = rp.part
AND q.stlc = rp.stlc
AND q.v1ds = rp.v1ds
AND q.vol = rp.vol
AND rp.rn = 1;
--------------------------------------------------------------------------------
-- Step 7: Compute guidance logic from target, normalized last, list price, and last date.
--------------------------------------------------------------------------------
UPDATE q
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;
--------------------------------------------------------------------------------
-- Step 8: Assemble structured 'expl' JSON from populated columns.
--------------------------------------------------------------------------------
UPDATE q
SET expl = (
SELECT
q.last_price AS last_price
,q.last_qty AS last_qty
,q.last_dataseg AS last_dataseg
,q.last_v0ds AS last_v0ds
,q.last_source AS last_source
,FORMAT(q.last_date, 'yyyy-MM-dd') AS last_date
,q.last_isdiff AS last_isdiff
,q.last_part AS last_part
,q.tprice_last AS tprice_last
,q.tprice AS target_price
,JSON_QUERY(q.tmath) AS target_math
,q.calculated_pallets AS calculated_pallets
,q.exact_pallets AS exact_pallets
,q.cust AS customer
,q.chan AS channel
,q.part AS part
,q.stlc AS stlc
,TRIM(q.tier) AS tier
,q.vol AS vol
,q.pltq AS pltq
,q.v1ds AS v1ds
,q.part_v1ds AS part_v1ds
,q.curstd_orig AS curstd_orig
,q.futstd_orig AS futstd_orig
,q.v0ds AS v0ds
,q.curstd AS curstd
,q.futstd AS futstd
,q.curstd_last AS curstd_last
,q.futstd_last AS futstd_last
,q.customized AS customized
,q.last_premium AS last_premium
,q.last_premium_method AS last_premium_method
,q.last_price_norm AS last_price_norm
,q.listcode AS listcode
,q.listprice AS listprice
,q.listprice_eff AS listprice_eff
,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
)
FROM @queue q;
--------------------------------------------------------------------------------
-- Step 9: Create 'ui_json' with panels (History, List, Target Support, Guidance) and include raw 'expl' JSON.
--------------------------------------------------------------------------------
UPDATE q
SET ui_json = (
SELECT
(
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------------------------------------------------
CASE
WHEN q.last_price IS NOT NULL
THEN
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'
ELSE ''
END
ELSE 'No Recent'
END AS label,
----------------------detail-----------------------------------------------
1 AS detailLevel,
----------------------value------------------------------------------------
ISNULL(q.last_price, 0) AS value,
----------------------type-------------------------------------------------
'currency' AS type,
----------------------note-------------------------------------------------
CASE
WHEN q.last_price IS NOT NULL THEN
CONCAT(
CASE ISNULL(q.last_source, '')
WHEN 'mrq' THEN 'Similar - ' + last_part
WHEN 'mrs' THEN 'Similar - ' + last_part
WHEN 'dsq' THEN last_part
WHEN 'dss' THEN last_part
ELSE ''
END,
CASE WHEN ISNULL(q.last_order, '0') = '0'
THEN ' | Qt# ' + ISNULL(q.last_quote, '')
ELSE ' | Ord# ' + ISNULL(q.last_order, '')
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
)
ELSE
''
END
AS note
FOR JSON PATH -- array with one object (no WITHOUT_ARRAY_WRAPPER)
) AS details
UNION ALL
-- 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))
+ CASE SUBSTRING(value,19,1) WHEN '+' THEN 0 ELSE -1 END
ELSE 0 END AS value,
----------------------type-------------------------------------------------
CASE WHEN value <> '' THEN
CASE SUBSTRING(value,19,1) WHEN '+' THEN 'currency' ELSE 'Percent' END
ELSE '' END AS type,
----------------------note-------------------------------------------------
CASE WHEN value <> '' THEN
CASE WHEN CHARINDEX('Anchor',value) <> 0 THEN
'Base Floor'
ELSE
CASE SUBSTRING(value,19,1) WHEN '+' THEN 'Price' ELSE 'Premium' END
END
ELSE '' END AS note
FROM @queue q
OUTER APPLY OPENJSON(q.expl, '$.target_math')
WITH (value NVARCHAR(MAX) '$')
UNION ALL
SELECT
----------------------label------------------------------------------------
'Target' AS label,
----------------------detailLevel------------------------------------------
5 AS detailLevel,
----------------------value------------------------------------------------
tprice AS value,
----------------------type-------------------------------------------------
'currency' AS type,
----------------------note-------------------------------------------------
'Total' AS note
FROM @queue q
) x
FOR JSON PATH
) AS details
UNION ALL
-- 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
FOR JSON PATH
)
) AS panel
FOR JSON PATH
) AS details,
JSON_QUERY(q.expl) AS data -- 👈 adds the full expl content as a JSON object
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER -- 👈 make it a single JSON object
)
FROM @queue q;
--------------------------------------------------------------------------------
-- Final: Return all calculated fields and JSON payloads.
--------------------------------------------------------------------------------
SELECT guidance_price, ui_json FROM @queue;
END;

View File

@ -0,0 +1,600 @@
/*
====================================================================================
Script: single_price_call.pg.sql
Purpose: Single price call logic for a specific scenario (PostgreSQL)
-----------------------------------------------------------------------------------
Description:
- Accepts a single pricing scenario (bill, ship, part, v1ds, vol)
- Enriches with customer, channel, tier, pack quantity, price level, and part group
- Looks up and applies target price, price history, list price, and guidance logic
- Builds a JSON explanation and UI JSON for the scenario
Inputs:
- bill, ship, part, v1ds, vol (function arguments)
- Reference tables: pricequote.target_prices, pricequote.lastpricedetail, pricequote.pricelist_ranged
- Customer/item reference: rlarp.cust, CMS.CUSLG.itemm, CMS.CUSLG.IPRCBHC
Outputs:
- Returns a single enriched row with all pricing and explanation fields
Key Business Logic:
- Channel/tier/customer resolution based on bill/ship codes
- Target price and math lookup by segment, channel, tier, and volume
- Price history precedence and extraction via helper function
- List price selection: lowest valid price for the scenario
- Guidance logic: computed from target, last, and list prices
Dependencies:
- pricequote.guidance_logic (function)
- pricequote.pick_last_price_from_hist (function)
Notes:
- Designed for single-row pricing queries (API or UI)
- Assumes all referenced tables and functions exist
- See also: matrix_guidance.pg.sql for batch/matrix logic
====================================================================================
*/
-- DROP FUNCTION pricequote.single_price_call(text,text,text,text,numeric) CASCADE;
CREATE OR REPLACE FUNCTION pricequote.single_price_call(
_bill TEXT,
_ship TEXT,
_part TEXT,
_v1ds TEXT,
_vol NUMERIC
)
RETURNS TABLE (
bill TEXT,
ship TEXT,
part TEXT,
stlc TEXT,
v1ds TEXT,
vol NUMERIC,
chan TEXT,
cust TEXT,
tier TEXT,
pltq NUMERIC,
plevel TEXT,
partgroup TEXT,
part_v1ds TEXT,
v0ds TEXT,
curstd_orig NUMERIC,
futstd_orig NUMERIC,
curstd NUMERIC,
futstd NUMERIC,
curstd_last NUMERIC,
futstd_last NUMERIC,
customized TEXT,
last_premium NUMERIC,
last_premium_method TEXT,
last_price_norm NUMERIC,
last_isdiff TEXT,
last_v0ds TEXT,
tprice_last NUMERIC,
last_price NUMERIC,
last_qty NUMERIC,
last_dataseg TEXT,
last_date DATE,
last_order TEXT,
last_quote TEXT,
last_source TEXT,
hist JSONB,
tprice NUMERIC,
tmath JSONB,
volume_range TEXT,
listprice NUMERIC,
listcode TEXT,
listprice_eff NUMERIC,
list_relevance TEXT,
guidance_price NUMERIC,
guidance_reason TEXT,
approval_price NUMERIC,
approval_reason TEXT,
expl JSONB,
ui_json JSONB
) AS $$
DECLARE
-----------input parameters--------------
-- _bill
-- _ship
-- _part
-- _v1ds
-- _vol
------------step 1 lookup scenario------------
_chan TEXT;
_tier TEXT;
_cust TEXT;
_pltq NUMERIC;
_plevel TEXT;
_partgroup TEXT;
_stlc TEXT;
_part_v1ds TEXT;
_v0ds TEXT;
_curstd_orig NUMERIC;
_futstd_orig NUMERIC;
_calculated_pallets INT;
_exact_pallets NUMERIC;
_customized TEXT := '';
----------- step 2 last price------------------
_hist JSONB := '{}'::jsonb;
_last JSONB;
_last_price NUMERIC;
_last_source TEXT;
_last_date DATE;
_last_qty NUMERIC;
_last_dataseg TEXT;
_last_v0ds TEXT;
_last_order TEXT;
_last_quote TEXT;
_last_isdiff TEXT;
_last_part TEXT;
------------step 3 lookup target---------------
_tprice NUMERIC(20,5);
_tmath JSONB;
_volume_range TEXT;
_tprice_last NUMERIC(20,5);
------------step 4 normalize last price--------
_curstd NUMERIC;
_futstd NUMERIC;
_curstd_last NUMERIC;
_futstd_last NUMERIC;
_last_premium NUMERIC;
_last_price_norm NUMERIC;
_last_premium_method TEXT;
------------step 5 list price lookup-----------
_list_price NUMERIC;
_list_code TEXT;
_listprice_eff NUMERIC;
_list_relevance TEXT;
------------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;
BEGIN
------------------------------------------------------------------
-- Step 1: Resolve customer metadata and part master data
------------------------------------------------------------------
SELECT
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN 'WHS'
ELSE 'DRP'
END
ELSE 'DIR'
END chan,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIR' THEN bc.tier
ELSE COALESCE(sc.tier, bc.tier)
END tier,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN bc.dba
ELSE sc.dba
END
ELSE bc.dba
END cust,
i.mpck,
CASE SUBSTRING(bc.cclass, 2, 3)
WHEN 'DIS' THEN
CASE SUBSTRING(sc.cclass, 2, 3)
WHEN 'DIS' THEN sc.plevel
ELSE bc.plevel
END
ELSE bc.plevel
END plevel,
substring(_part,1,8) stlc,
i.partgroup,
i.v1ds part_v1ds,
CASE substring(_v1ds,4,1) WHEN 'B' THEN 'B' ELSE 'C' END || CASE substring(_v1ds,6,1) WHEN 'L' THEN 'L' WHEN 'P' THEN 'P' ELSE '' END v0ds,
i.curstdus,
i.futstdus,
FLOOR(_vol / NULLIF(i.mpck, 0)),
ROUND(_vol / NULLIF(i.mpck, 0), 5)
INTO
_chan
,_tier
,_cust
,_pltq
,_plevel
,_stlc
,_partgroup
,_part_v1ds
,_v0ds
,_curstd_orig
,_futstd_orig
,_calculated_pallets
,_exact_pallets
FROM
rlarp.cust bc
LEFT JOIN rlarp.cust sc ON
sc.code = _ship
LEFT JOIN "CMS.CUSLG".itemm i ON
i.item = _part
WHERE
bc.code = _bill;
-- Customized flag
IF _part_v1ds IS NOT NULL AND _v1ds IS NOT NULL AND _part_v1ds <> _v1ds THEN
_customized := 'Customized';
END IF;
-- RAISE NOTICE 'Debug Inputs => stlc: "%", v1ds: "%", chan: "%", tier: "%", pallets: "%" , pltq: "%"',
-- _stlc, _v1ds, _chan, _tier, _calculated_pallets, _pltq;
--------------------------------------------------------------------------------
-- Step 3: Lookup Last Price
--------------------------------------------------------------------------------
SELECT
lp.part_stats
INTO
_hist
FROM
pricequote.lastpricedetail lp
WHERE
lp.customer = _cust
AND lp.partgroup = _partgroup;
_last := pricequote.pick_last_price_from_hist(_hist, _v1ds);
_last_price := (_last->>'price')::numeric;
_last_source := _last->>'source';
_last_date := (_last->>'odate')::date;
_last_qty := (_last->>'qty')::numeric;
_last_dataseg := _last->>'datasegment';
_last_v0ds :=
CASE substring(_last_dataseg,4,1) WHEN 'B' THEN 'B' ELSE 'C' END ||
CASE substring(_last_dataseg,6,1) WHEN 'L' THEN 'L' WHEN 'P' THEN 'P' ELSE '' END;
_last_order := _last->>'ordnum';
_last_quote := _last->>'quoten';
IF _last_dataseg IS NOT NULL AND _v1ds IS NOT NULL AND _last_dataseg <> _v1ds THEN
_last_isdiff := 'Last Sale Diff Part';
END IF;
_last_part := _last->>'part';
------------------------------------------------------------------
-- Step 2: Target price logic (current and for last_dataseg)
------------------------------------------------------------------
SELECT
tp.price
,to_json(tp.math)
,tp.vol::text
INTO
_tprice
,_tmath
,_volume_range
FROM
pricequote.target_prices_base tp
WHERE
tp.stlc = _stlc
AND tp.ds = _v1ds
AND tp.chan = _chan
AND tp.tier = _tier
AND tp.vol @> _calculated_pallets;
-- RAISE NOTICE 'Debug: tprice=%, tmath=%, volume_range=%',
-- _tprice, _tmath, _volume_range;
-- Target price for last_dataseg
SELECT
tp.price
INTO
_tprice_last
FROM
pricequote.target_prices_base 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;
------------------------------------------------------------------
-- Step 4: Cost data for normalization
------------------------------------------------------------------
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;
------------------------------------------------------------------
-- Step 5: Normalize last price if needed
------------------------------------------------------------------
IF _last_isdiff IS NOT NULL THEN
IF _tprice_last IS NOT NULL AND _tprice IS NOT NULL AND _tprice_last <> 0 THEN
_last_premium := ROUND(_tprice / _tprice_last,5);
_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_price_norm := ROUND(_last_price * (_curstd / _curstd_last), 5);
_last_premium_method := 'Cost Ratio';
ELSE
_last_price_norm := _last_price;
_last_premium_method := 'Unknown';
END IF;
ELSE
_last_price_norm := _last_price;
END IF;
------------------------------------------------------------------
-- Step 6: List price logic
------------------------------------------------------------------
SELECT
pr.price::numeric(20,5)
,pr.jcplcd
INTO
_list_price
,_list_code
FROM
"CMS.CUSLG".IPRCBHC i
JOIN pricequote.pricelist_ranged pr ON
pr.jcplcd = TRIM(i.jbplcd)
AND pr.jcpart = _part
AND _vol >= pr.vb_from
AND (_vol < pr.vb_to OR pr.vb_to IS NULL)
WHERE
TRIM(i.jbplvl) = TRIM(_plevel)
AND CURRENT_DATE BETWEEN i.jbfdat AND i.jbtdat
ORDER BY
pr.price ASC
LIMIT 1;
-- List price relevance
IF _customized <> '' THEN
_listprice_eff := NULL;
_list_relevance := 'Ignore - Customized';
ELSE
_listprice_eff := _list_price;
_list_relevance := '';
END IF;
------------------------------------------------------------------
-- Step 7: Compute guidance price and embed it
------------------------------------------------------------------
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;
------------------------------------------------------------------
-- Step 8: Build explanation JSON
------------------------------------------------------------------
_expl :=
jsonb_build_object(
'last',
jsonb_build_object(
'last_part', _last_part,
'last_price', _last_price,
'last_qty', _last_qty,
'last_dataseg', _last_dataseg,
'last_v0ds', _last_v0ds,
'last_source', _last_source,
'last_date', _last_date,
'last_order', _last_order,
'last_quote', _last_quote,
'last_isdiff', _last_isdiff,
'last_premium', _last_premium,
'last_premium_method', _last_premium_method,
'last_price_norm', _last_price_norm,
'tprice_last', _tprice_last
),
'scenario',
jsonb_build_object(
'calculated_pallets', FLOOR(_vol / NULLIF(_pltq, 0)),
'exact_pallets', ROUND(_vol / NULLIF(_pltq, 0), 5),
'customer', _cust,
'channel', _chan,
'tier', TRIM(_tier),
'v1ds', _v1ds,
'v0ds', _v0ds,
'part_v1ds', _part_v1ds,
'customized', _customized
),
'cost',
jsonb_build_object(
'curstd_orig', _curstd_orig,
'futstd_orig', _futstd_orig,
'curstd_last', _curstd_last,
'futstd_last', _futstd_last,
'curstd', _curstd,
'futstd', _futstd
),
'targets',
jsonb_build_object(
'target_price', _tprice,
'target_math', _tmath,
'volume_range', _volume_range
),
'list',
jsonb_build_object(
'listcode', _list_code,
'listprice', _list_price,
'listprice_eff', _listprice_eff,
'list_relevance', _list_relevance
),
'guidance_price', _guidance_price,
'guidance_reason', _guidance_reason,
'approval_price', _approval_price,
'approval_reason', _approval_reason
);
------------------------------------------------------------------
-- Step 9: Build UI JSON (panels)
------------------------------------------------------------------
_ui_json := jsonb_build_object(
'details', jsonb_build_array(
------------------------------------------
-- history
------------------------------------------
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
CASE _last_source
WHEN 'mrq' THEN 'Recent similar ' || _last_part || ' ' || 'qty: ' || _last_qty
WHEN 'mrs' THEN 'Recent similar ' || _last_part || ' ' || 'qty: ' || _last_qty
WHEN 'dsq' THEN 'Last quote ' || 'qty: ' || _last_qty
WHEN 'dss' THEN 'Last sale ' || 'qty: ' || _last_qty
ELSE ''
END ||
CASE WHEN COALESCE(_last_order, '0') = '0' THEN ' Qt# ' || COALESCE(_last_quote, '') ELSE ' Ord# ' || COALESCE(_last_order, '') END
ELSE NULL END
)
)
||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
)),'[]'::jsonb)
ELSE
'[]'::jsonb
END
||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
)),'[]'::jsonb)
ELSE
'[]'::jsonb
END
),
------------------------------------------
-- price list
------------------------------------------
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
)
)
),
------------------------------------------
-- history
------------------------------------------
jsonb_build_object(
'label', 'Target Calculation',
'detailLevel',10,
'details',
-- jsonb_build_array(
(
SELECT
jsonb_agg(
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) +
CASE SUBSTRING(value,19,1) WHEN '+' THEN 0 ELSE -1 END
ELSE
0
END,
----------------------type-------------------------------------------------
'type', CASE WHEN value <> '' THEN
CASE SUBSTRING(value,19,1)
WHEN '+' THEN 'currency'
ELSE 'Percent'
END
ELSE '' END,
----------------------note-------------------------------------------------
'note',CASE WHEN value <> '' THEN
CASE SUBSTRING(value,19,1)
WHEN '+' THEN 'Price'
ELSE 'Premium'
END
ELSE '' END
)
)
FROM jsonb_array_elements_text(COALESCE(_tmath,'[""]'::jsonb)) ae
)
||CASE WHEN _tprice IS NULL THEN '[]'::jsonb ELSE jsonb_build_object('label','Price','value',COALESCE(_tprice,0),'type','currency','note','Total') END
-- )
),
------------------------------------------
-- history
------------------------------------------
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,'')
)
)
)
),
'data', _expl
);
------------------------------------------------------------------
-- Final: Return row
------------------------------------------------------------------
RETURN QUERY
SELECT
_bill, _ship, _part, _stlc, _v1ds, _vol,
_chan, _cust, _tier, _pltq, _plevel, _partgroup, _part_v1ds, _v0ds,
_curstd_orig, _futstd_orig, _curstd, _futstd, _curstd_last, _futstd_last,
_customized, _last_premium, _last_premium_method, _last_price_norm, _last_isdiff, _last_v0ds, _tprice_last,
_last_price, _last_qty, _last_dataseg, _last_date, _last_order, _last_quote, _last_source, _hist,
_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;

View File

@ -1,81 +1,43 @@
/* CREATE FUNCTION pricing.single_price_call_fn (
====================================================================================
Script: single_price_call.ms.sql
Purpose: Single price call logic for a specific scenario (SQL Server)
-----------------------------------------------------------------------------------
Description:
- Accepts a single pricing scenario (bill, ship, part, v1ds, vol)
- Enriches with customer, channel, tier, pack quantity, price level, and part group
- Looks up and applies target price, price history, list price, and guidance logic
- Builds a JSON explanation and UI JSON for the scenario
Inputs:
- @bill, @ship, @part, @v1ds, @vol (procedure arguments)
- Reference tables: pricing.target_prices, pricing.lastpricedetail, pricing.pricelist_ranged
- Customer/item reference: rlarp.cust, CMS.CUSLG.itemm, CMS.CUSLG.IPRCBHC
Outputs:
- Returns a single enriched row with all pricing and explanation fields
Key Business Logic:
- Channel/tier/customer resolution based on bill/ship codes
- Target price and math lookup by segment, channel, tier, and volume
- Price history precedence and extraction via helper function
- List price selection: lowest valid price for the scenario
- Guidance logic: computed from target, last, and list prices
Dependencies:
- pricing.guidance_logic (function)
- pricing.pick_last_price_from_hist_json (function)
Notes:
- Designed for single-row pricing queries (API or UI)
- Assumes all referenced tables and functions exist
- See also: matrix_guidance.pg.sql for batch/matrix logic
====================================================================================
*/
CREATE OR ALTER PROCEDURE pricing.single_price_call
@bill VARCHAR(100), @bill VARCHAR(100),
@ship VARCHAR(100), @ship VARCHAR(100),
@part VARCHAR(100), @part VARCHAR(100),
@v1ds VARCHAR(100), @v1ds VARCHAR(100),
@vol NUMERIC(18,6) @vol NUMERIC(18,6)
)
RETURNS @queue TABLE (
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
stlc VARCHAR(100),
partgroup VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
chan VARCHAR(50),
cust VARCHAR(100),
tier VARCHAR(50),
pltq NUMERIC(18,6),
volume_range TEXT,
plevel NVARCHAR(20),
listprice NUMERIC(20,5),
listcode VARCHAR(10),
hist NVARCHAR(MAX),
last_price NUMERIC(20,5),
last_qty NUMERIC(20,5),
last_date DATE,
last_order NVARCHAR(10),
last_quote NVARCHAR(10),
last_dataseg NVARCHAR(20),
last_source NVARCHAR(100),
tprice NUMERIC(20,5),
tmath nvarchar(MAX),
guidance_price NUMERIC(20,5),
guidance_reason NVARCHAR(MAX),
expl NVARCHAR(MAX),
ui_json NVARCHAR(MAX)
)
AS AS
BEGIN BEGIN
SET NOCOUNT ON;
-- Working table for enriched pricing request
DECLARE @queue TABLE (
bill VARCHAR(100),
ship VARCHAR(100),
part VARCHAR(100),
stlc VARCHAR(100),
partgroup VARCHAR(100),
v1ds VARCHAR(100),
vol NUMERIC(18,6),
chan VARCHAR(50),
cust VARCHAR(100),
tier VARCHAR(50),
pltq NUMERIC(18,6),
volume_range TEXT,
plevel NVARCHAR(20),
listprice NUMERIC(20,5),
listcode VARCHAR(10),
hist NVARCHAR(MAX),
last_price NUMERIC(20,5),
last_qty NUMERIC(20,5),
last_date DATE,
last_order NVARCHAR(10),
last_quote NVARCHAR(10),
last_dataseg NVARCHAR(20),
last_source NVARCHAR(100),
tprice NUMERIC(20,5),
tmath nvarchar(MAX),
guidance_price NUMERIC(20,5),
guidance_reason NVARCHAR(MAX),
expl NVARCHAR(MAX),
ui_json NVARCHAR(MAX)
);
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Step 1: Seed the queue with input row -- Step 1: Seed the queue with input row
@ -346,12 +308,8 @@ BEGIN
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER -- 👈 make it a single JSON object FOR JSON PATH, WITHOUT_ARRAY_WRAPPER -- 👈 make it a single JSON object
) )
FROM @queue q; FROM @queue q;
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Final: Return the enriched result row -- Final: Return the enriched result row
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
--SELECT guidance_price, hist, expl, ui_json FROM @queue; RETURN;
SELECT * FROM @queue; END;
END;

View File

@ -1,7 +1,7 @@
-- set work_mem TO '4GB'; -- set work_mem TO '4GB';
-- --
DROP VIEW IF EXISTS rlarp.quote_review; DROP VIEW IF EXISTS rlarp.quote_review;
CREATE VIEW rlarp.quote_review AS CREATE OR REPLACE VIEW rlarp.quote_review AS
WITH WITH
---------------------get quote lines from SQL Server--------------------- ---------------------get quote lines from SQL Server---------------------
lq AS MATERIALIZED ( lq AS MATERIALIZED (
@ -105,7 +105,10 @@ lq AS MATERIALIZED (
-- ,jsonb_pretty(pricing) pricing -- ,jsonb_pretty(pricing) pricing
,p.guidance_price ,p.guidance_price
,p.guidance_reason ,p.guidance_reason
,jsonb_pretty(p.expl) expl ,p.approval_price
,p.approval_reason
,p.tprice guidance_target
,jsonb_pretty(p.ui_json->'data') expl
FROM FROM
lq lq
LEFT OUTER JOIN "CMS.CUSLG".itemm i ON LEFT OUTER JOIN "CMS.CUSLG".itemm i ON
@ -131,7 +134,6 @@ lq AS MATERIALIZED (
lq.billto lq.billto
,lq.shipto ,lq.shipto
,lq.part ,lq.part
,substring(lq.part,1,8)
,lq.v1ds ,lq.v1ds
,lq.units_each ,lq.units_each
) p ON TRUE ) p ON TRUE
@ -176,4 +178,4 @@ lq AS MATERIALIZED (
WHERE WHERE
COALESCE(g.bestprice,1) = 1 COALESCE(g.bestprice,1) = 1
) )
SELECT * FROM hist --WHERE qid = 108655 SELECT * FROM hist --LIMIT 1000--WHERE qid = 108655

View File

@ -0,0 +1,275 @@
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;

View File

@ -0,0 +1 @@
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;

View File

@ -0,0 +1,6 @@
EXEC pricing.pricing.rebuild_lastprice;
--2:45
EXEC pricing.pricing.rebuild_pricelist;
--14 secconds
EXEC pricing.pricing.rebuild_targets;
--12:49

View File

@ -0,0 +1,6 @@
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
--37 seconds
CALL pricequote.rebuild_pricelist();
--32 seconds
CALL pricequote.refresh_target_prices_base();
--45 seconds

View File

@ -0,0 +1,226 @@
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;

View File

@ -1,3 +1,8 @@
CREATE OR REPLACE PROCEDURE pricequote.rebuild_pricelist()
LANGUAGE plpgsql
AS $$
BEGIN
DROP TABLE IF EXISTS uomc; DROP TABLE IF EXISTS uomc;
CREATE TEMP TABLE uomc AS ( CREATE TEMP TABLE uomc AS (
@ -97,5 +102,7 @@ SELECT
price price
FROM ranged; FROM ranged;
END;
$$;
CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ; --CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;

View File

@ -0,0 +1,42 @@
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

View File

@ -0,0 +1,28 @@
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;
$$;

View File

@ -0,0 +1,66 @@
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

28
tables/arcstx.ms.sql Normal file
View File

@ -0,0 +1,28 @@
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');

16
tables/core_target.pg.sql Normal file
View File

@ -0,0 +1,16 @@
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);

85
tables/cost.ms.sql Normal file
View File

@ -0,0 +1,85 @@
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

54
tables/cust.ms.sql Normal file
View File

@ -0,0 +1,54 @@
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
tables/ffcret.ms.sql Normal file
View File

@ -0,0 +1 @@
SELECT * INTO pricing.ffcret FROM fanalysis.rlarp.ffcret

1
tables/ffterr.ms.sql Normal file
View File

@ -0,0 +1 @@
SELECT * INTO pricing.ffterr FROM fanalysis.rlarp.ffterr

1
tables/gld.ms.sql Normal file
View File

@ -0,0 +1 @@
SELECT * INTO pricing.gld FROM fanalysis.rlarp.gld

View File

@ -0,0 +1,7 @@
CREATE TABLE pricing.lastpricedetail (
customer varchar(255),
partgroup varchar(10),
part_stats nvarchar(MAX)
);
CREATE UNIQUE NONCLUSTERED INDEX lastprice_cust_partgroup ON pricing.lastpricedetail ( customer ASC , partgroup ASC );

View File

@ -1,8 +1,13 @@
CREATE TABLE pricequote.lastpricedetail AS -- REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
DROP MATERIALIZED VIEW pricequote.lastpricedetail;
CREATE MATERIALIZED VIEW pricequote.lastpricedetail AS
WITH base AS ( WITH base AS (
SELECT SELECT
customer, customer,
partgroup, partgroup,
part,
dataseg, dataseg,
version, version,
qtyord AS qty, qtyord AS qty,
@ -21,32 +26,41 @@ WITH base AS (
AND version IN ('Actual', 'Quotes') AND version IN ('Actual', 'Quotes')
), ),
ranked AS ( ranked AS (
SELECT *, SELECT b.*,
ROW_NUMBER() OVER ( -- Most recent sale (Actuals first, newest date first)
PARTITION BY customer, partgroup CASE WHEN version = 'Actual' THEN ROW_NUMBER() OVER (
ORDER BY CASE WHEN version = 'Actual' THEN odate END DESC PARTITION BY customer, partgroup
) AS rn_mrs, ORDER BY (version = 'Actual') DESC,
ROW_NUMBER() OVER ( odate DESC NULLS LAST
PARTITION BY customer, partgroup ) END AS rn_mrs,
ORDER BY CASE WHEN version = 'Quotes' THEN odate END DESC -- Most recent quote (Quotes first, newest date first)
) AS rn_mrq, CASE WHEN version = 'Quotes' THEN ROW_NUMBER() OVER (
ROW_NUMBER() OVER ( PARTITION BY customer, partgroup
PARTITION BY customer, partgroup ORDER BY (version = 'Quotes') DESC,
ORDER BY CASE WHEN version = 'Actual' AND odate >= (CURRENT_DATE - INTERVAL '1 year') THEN qty END DESC odate DESC NULLS LAST
) AS rn_lvs, ) END AS rn_mrq,
ROW_NUMBER() OVER ( -- Largest volume sale in last year (those inside window first)
PARTITION BY customer, partgroup CASE WHEN version = 'Actual' THEN ROW_NUMBER() OVER (
ORDER BY CASE WHEN version = 'Quotes' AND odate >= (CURRENT_DATE - INTERVAL '1 year') THEN qty END DESC PARTITION BY customer, partgroup
) AS rn_lvq, ORDER BY (version = 'Actual' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
ROW_NUMBER() OVER ( qty DESC NULLS LAST
PARTITION BY customer, partgroup, dataseg, version ) END AS rn_lvs,
ORDER BY CASE WHEN version = 'Actual' THEN odate END DESC -- Largest volume quote in last year (those inside window first)
) AS rn_dss, CASE WHEN version = 'Quotes' THEN ROW_NUMBER() OVER (
ROW_NUMBER() OVER ( PARTITION BY customer, partgroup
PARTITION BY customer, partgroup, dataseg, version ORDER BY (version = 'Quotes' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
ORDER BY CASE WHEN version = 'Quotes' THEN odate END DESC qty DESC NULLS LAST
) AS rn_dsq ) END AS rn_lvq,
FROM base -- Per dataseg/version: most recent (version fixed in partition, so just date)
CASE WHEN version = 'Actual' THEN 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 (
PARTITION BY customer, partgroup, dataseg, version
ORDER BY odate DESC NULLS LAST
) END AS rn_dsq
FROM base b
), ),
flagged AS ( flagged AS (
SELECT *, SELECT *,
@ -54,15 +68,16 @@ flagged AS (
CASE WHEN rn_mrq = 1 THEN 'mrq' END AS f2, CASE WHEN rn_mrq = 1 THEN 'mrq' END AS f2,
CASE WHEN rn_lvs = 1 THEN 'lvs' END AS f3, CASE WHEN rn_lvs = 1 THEN 'lvs' END AS f3,
CASE WHEN rn_lvq = 1 THEN 'lvq' END AS f4, CASE WHEN rn_lvq = 1 THEN 'lvq' END AS f4,
CASE WHEN rn_dss = 1 THEN 'dss' END AS f5, CASE WHEN rn_dss = 1 AND VERSION = 'Actual' THEN 'dss' END AS f5,
CASE WHEN rn_dsq = 1 THEN 'dsq' END AS f6 CASE WHEN rn_dsq = 1 AND VERSION = 'Quotes' THEN 'dsq' END AS f6
FROM ranked FROM ranked
WHERE WHERE
rn_mrs = 1 OR rn_mrq = 1 OR rn_lvs = 1 OR rn_lvq = 1 OR rn_dss = 1 OR rn_dsq = 1 rn_mrs = 1 OR rn_mrq = 1 OR rn_lvs = 1 OR rn_lvq = 1 OR rn_dss = 1 OR rn_dsq = 1
), )
exploded_flags AS ( --SELECT * FROM flagged WHERE customer = 'HYBELS' AND partgroup = 'HZP3E100'
,exploded_flags AS (
SELECT SELECT
customer, partgroup, dataseg, version, qty, price, odate, ordnum, quoten, customer, partgroup, part, dataseg, version, qty, price, odate, ordnum, quoten,
unnest(ARRAY[f1, f2, f3, f4, f5, f6]) AS flag unnest(ARRAY[f1, f2, f3, f4, f5, f6]) AS flag
FROM flagged FROM flagged
), ),
@ -75,6 +90,7 @@ serialized_flags AS (
jsonb_build_object( jsonb_build_object(
'version', version, 'version', version,
'datasegment', dataseg, 'datasegment', dataseg,
'part', part,
'qty', qty, 'qty', qty,
'price', price, 'price', price,
'odate', odate, 'odate', odate,
@ -122,3 +138,5 @@ FULL OUTER JOIN seg_json s
WITH DATA; WITH DATA;
--SELECT * FROM pricequote.lastpricedetail; --SELECT * FROM pricequote.lastpricedetail;
CREATE INDEX lastpricedetail_idx ON pricequote.lastpricedetail(customer, partgroup);

20
tables/plpr.ms.sql Normal file
View File

@ -0,0 +1,20 @@
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;

View File

@ -0,0 +1,12 @@
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
);
CREATE NONCLUSTERED INDEX pricelist_ranged_idx ON pricing.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC );

View File

@ -0,0 +1,11 @@
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);

1
tables/qrh.ms.sql Normal file
View File

@ -0,0 +1 @@
SELECT * INTO pricing.pricing.qrh FROM fanalysis.RLARP.QRH q

18
tables/repc.ms.sql Normal file
View File

@ -0,0 +1,18 @@
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
tables/sach.ms.sql Normal file
View File

@ -0,0 +1 @@
SELECT * INTO pricing.sach FROM fanalysis.lgdat.sach;

View File

@ -16,3 +16,4 @@ ALTER TABLE pricing.target_prices
ADD CONSTRAINT uq_target_prices_unique_combo ADD CONSTRAINT uq_target_prices_unique_combo
UNIQUE (stlc, ds, chan, tier, vol, lower_bound); UNIQUE (stlc, ds, chan, tier, vol, lower_bound);
--SELECT COUNT(*) FROM pricing.target_prices

View File

@ -0,0 +1,17 @@
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;

Some files were not shown because too many files have changed in this diff Show More