Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
8ab61b7199 |
17
.gitignore
vendored
17
.gitignore
vendored
@ -1,17 +1,2 @@
|
||||
.env
|
||||
.vscode/
|
||||
.idea/hc_ubm.iml
|
||||
.idea/modules.xml
|
||||
.idea/workspace.xml
|
||||
.project
|
||||
.dbeaver-data-sources.xml
|
||||
Diagrams/
|
||||
.vs/
|
||||
*.swp
|
||||
*.swo
|
||||
.settings/org.eclipse.core.resources.prefs
|
||||
.vscode/*
|
||||
.dbeaver/
|
||||
.env
|
||||
.obsidian/
|
||||
Scripts/
|
||||
.vscode/
|
@ -52,15 +52,7 @@ router.get('/dseg_price/:billcode/:shipcode/:stlc/:dseg/:qty', async (ctx) => {
|
||||
const { billcode, shipcode, stlc, dseg, qty } = ctx.params;
|
||||
const result = await client.queryObject({args: [billcode, shipcode, stlc, dseg, qty], text: query_dseg} );
|
||||
const ag = apply_guidance(result.rows[0]["doc"]);
|
||||
ctx.response.body = ag.guidance.finalPrice.Snapped;
|
||||
});
|
||||
|
||||
// specific customres codes but generic style code and data segment to accomodate custom colors and branding
|
||||
router.get('/dseg_reason/:billcode/:shipcode/:stlc/:dseg/:qty', async (ctx) => {
|
||||
const { billcode, shipcode, stlc, dseg, qty } = ctx.params;
|
||||
const result = await client.queryObject({args: [billcode, shipcode, stlc, dseg, qty], text: query_dseg} );
|
||||
const ag = apply_guidance(result.rows[0]["doc"]);
|
||||
ctx.response.body = ag.guidance.finalPrice.Snapped + ' (' + ag.guidance.finalPrice.Reason + ')';
|
||||
ctx.response.body = ag.guidance.FinalReason;
|
||||
});
|
||||
|
||||
app.use(router.routes());
|
123
apply_guidance.ts
Normal file
123
apply_guidance.ts
Normal file
@ -0,0 +1,123 @@
|
||||
export function apply_guidance(doc: any) {
|
||||
|
||||
function getAdjValue(number) {
|
||||
const data = [
|
||||
{f: 2.001, t: 1000, snap: 3, adj: 0 },
|
||||
{f: 1.001, t: 2, snap: 2, adj: 0 },
|
||||
{f: 0.1, t: 1, snap: 1, adj: 0 },
|
||||
{f: 0, t: 0.1, snap: 0, adj: 0 },
|
||||
{f: -1, t: -0.00001, snap: -1, adj: 0.05},
|
||||
{f: -2, t: -0.999999, snap: -2, adj: 0.05},
|
||||
{f: -1000, t: -2.001, snap: -3, adj: 0.10},
|
||||
];
|
||||
const match = data.find(row => number >= row.f && number <= row.t);
|
||||
return match ? match.adj : null;
|
||||
}
|
||||
|
||||
//let custPrice null;
|
||||
//let custReason null;
|
||||
//let cvolPrice null;
|
||||
//let cvolReason null;
|
||||
//let markPrice null;
|
||||
//let markReason null;
|
||||
//let targPrice null;
|
||||
//let targReason null;
|
||||
|
||||
const targetPrice = doc.pricing?.v1tp ?? doc.pricing?.v0tp;
|
||||
const priceBand = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv;
|
||||
const earlyPrice = doc.hist?.cust?.[0]?.early_price;
|
||||
const earlySeason = doc.hist?.cust?.[0]?.early_season;
|
||||
const bridgePremium = doc.pricing?.bridgePremium ?? 1.00000;
|
||||
const altHist = doc.hist?.cust?.ds;
|
||||
const iidx = doc.pricing?.iidx;
|
||||
const curr = doc.customer?.curr;
|
||||
const fxrate = doc.customer?.fxrate ?? 1.0;
|
||||
const qty = doc.inputs?.qty;
|
||||
const pltq = doc.product?.pltq;
|
||||
let anchorPrice = null;
|
||||
let anchorSource = null;
|
||||
let custPrice = null;
|
||||
let custSource = null;
|
||||
let guidance = {};
|
||||
let calcCeiling = null;
|
||||
let finalReasonUSD = "";
|
||||
let finalPriceUSD = null;
|
||||
let finalReason = "";
|
||||
let finalPrice = null;
|
||||
let ltp = qty < pltq ? 1.15 : null;
|
||||
let increase = null;
|
||||
const inflation = Math.max(...Object.keys(iidx).map(Number));
|
||||
const inflationFactor = iidx[inflation] + 1;
|
||||
const list = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null;
|
||||
|
||||
//-------set basic customer pricing--------------
|
||||
custPrice = Number((earlyPrice * bridgePremium).toFixed(5));
|
||||
anchorPrice = custPrice;
|
||||
let anchor_sd = priceBand ? ((anchorPrice - targetPrice) / priceBand).toFixed(2) : 0
|
||||
let optimization = getAdjValue(anchor_sd);
|
||||
|
||||
|
||||
// ------if there is not target price just exit---------------
|
||||
if (!targetPrice) {
|
||||
anchorSource = "No target pricing setup";
|
||||
guidance.FinalReason = "No target pricing setup";
|
||||
} else {
|
||||
// if there is no customer anchor price use target
|
||||
if (earlyPrice) {
|
||||
// translate alternate product history to current product quoted
|
||||
// --------if the price needs bridged, add the details to the description--------
|
||||
if (bridgePremium === 1) {
|
||||
anchorSource = earlySeason + ' Price ' + earlyPrice;
|
||||
custSource = anchorSource;
|
||||
} else {
|
||||
anchorSource = earlySeason + ' Similar (' + altHist + ') Price ' + earlyPrice + ' x ' + bridgePremium + ' = ' + anchorPrice;
|
||||
custSource = anchorSource;
|
||||
}
|
||||
// --------after the early price is translated see if target is still less-------
|
||||
if (targetPrice < anchorPrice) {
|
||||
anchorSource = `Target Price ${targetPrice}`;
|
||||
anchorPrice = targetPrice;
|
||||
}
|
||||
} else {
|
||||
anchorPrice = targetPrice;
|
||||
anchorSource = `Target Price ${targetPrice}`;
|
||||
}
|
||||
//------get the most relevant inflation factor number---------------------------------
|
||||
//------anchor x inflation / fxrate---------------------------------------------------
|
||||
let calcPriceUSD = parseFloat((anchorPrice * inflationFactor).toFixed(5));
|
||||
let calcPrice = parseFloat((calcPriceUSD / fxrate).toFixed(5));
|
||||
if (calcPrice >= list && list) {
|
||||
calcCeiling = "Cap At List";
|
||||
//multiply list by FX to get to USD if in CAD
|
||||
finalPrice = list;
|
||||
if (curr === "CA") {
|
||||
finalReason = `${anchorSource} x ${inflationFactor} / ${fxrate} FX = ${calcPrice} CAD, cap at list ${list} CAD`;
|
||||
} else {
|
||||
finalReason = `${anchorSource} x ${inflationFactor} = ${calcPrice}, cap at list ${list}`;
|
||||
}
|
||||
} else {
|
||||
finalPrice = calcPrice;
|
||||
finalPriceUSD = calcPriceUSD;
|
||||
if (curr === "CA") {
|
||||
finalReason = `${anchorSource} x ${inflationFactor} / ${fxrate} FX = ${calcPrice} CAD`;
|
||||
} else {
|
||||
finalReason = `${anchorSource} x ${inflationFactor} = ${calcPrice}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
guidance.AnchorPrice = anchorPrice;
|
||||
guidance.AnchorSource = anchorSource;
|
||||
guidance.CustAnchorPrice = custPrice;
|
||||
guidance.CustAnchorSource = custSource;
|
||||
guidance.InflationFactor = inflationFactor;
|
||||
guidance.Ceiling = calcCeiling;
|
||||
guidance.FinalPriceUSD = finalPriceUSD;
|
||||
guidance.FinalReasonUSD = finalReasonUSD;
|
||||
guidance.FinalPrice = finalPrice;
|
||||
guidance.FinalReason = finalReason;
|
||||
guidance.BridgePremium = bridgePremium;
|
||||
guidance.TargetPrice = targetPrice;
|
||||
guidance.ListPrice = list;
|
||||
doc.guidance = guidance;
|
||||
return doc;
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
export function apply_guidance(doc: any) {
|
||||
|
||||
function sortObjectKeys(obj) {
|
||||
// If the object is not an actual object or is an array, return it as is
|
||||
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Create a new object and sort the keys
|
||||
const sortedObj = {};
|
||||
Object.keys(obj).sort().forEach(key => {
|
||||
// Recursively apply the function for nested objects
|
||||
sortedObj[key] = sortObjectKeys(obj[key]);
|
||||
});
|
||||
|
||||
return sortedObj;
|
||||
}
|
||||
|
||||
function getAdjValue(number) {
|
||||
const data = [
|
||||
{f: 2.001, t: 1000, snap: 3, adj: 0 },
|
||||
{f: 1.001, t: 2, snap: 2, adj: 0 },
|
||||
{f: 0.1, t: 1, snap: 1, adj: 0 },
|
||||
{f: 0, t: 0.1, snap: 0, adj: 0 },
|
||||
{f: -1, t: -0.00001, snap: -1, adj: 0.05},
|
||||
{f: -2, t: -0.999999, snap: -2, adj: 0.05},
|
||||
{f: -1000, t: -2.001, snap: -3, adj: 0.10},
|
||||
];
|
||||
const match = data.find(row => number >= row.f && number <= row.t);
|
||||
return match ? match.adj : null;
|
||||
}
|
||||
|
||||
//-------------------------------set flor prices-------------------------------
|
||||
function getFloor(stlc) {
|
||||
switch (stlc) {
|
||||
case "SDD10000":
|
||||
return 0.045;
|
||||
case "SDD10001":
|
||||
return 0.045;
|
||||
case "SDD12000":
|
||||
return 0.065;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function lowestPrice(priceObject) {
|
||||
let Price = Infinity;
|
||||
let Reason = '';
|
||||
let Source = '';
|
||||
let Snapped = Infinity;
|
||||
|
||||
//console.log(priceObject["targ"][0]);
|
||||
// Iterate over each property in the object
|
||||
for (let key in priceObject) {
|
||||
// Ignore markPrice unless targPrice is null
|
||||
if (key === "mark" && priceObject["targ"][0] !== null) {
|
||||
continue;
|
||||
}
|
||||
if (priceObject.hasOwnProperty(key)) {
|
||||
let [cprice, creason, csource, csnap] = priceObject[key];
|
||||
// Check if the current price is lower than the found so far
|
||||
if (cprice && cprice < Price) {
|
||||
Price = cprice;
|
||||
Reason = creason;
|
||||
Source = csource;
|
||||
Snapped = csnap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {Reason, Price, Source, Snapped};
|
||||
}
|
||||
|
||||
function ceiling(value, significance) {
|
||||
return Math.ceil(value / significance) * significance;
|
||||
}
|
||||
|
||||
function r5(value) {
|
||||
return Number(value.toFixed(5));
|
||||
}
|
||||
|
||||
function pp(value) {
|
||||
// Multiplies by 1000 and rounds to the nearest 2 decimals
|
||||
var result = Math.round(value * 1000 * 100) / 100;
|
||||
|
||||
// Converts the number to a string with commas for thousand separators
|
||||
return result.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
|
||||
// --------------------extract incoming data------------------------------------------------------
|
||||
const targetPrice = doc.pricing?.v1tp ?? doc.pricing?.v0tp;
|
||||
const priceBand = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv;
|
||||
const earlyCustPrice = doc.hist?.cust?.early_price;
|
||||
const earlyCustSeason = doc.hist?.cust?.early_season;
|
||||
const earlyMarkPrice = doc.hist?.market?.early_price;
|
||||
const earlyMarkSeason = doc.hist?.market?.early_season;
|
||||
const bridgePremium = doc.pricing?.bridgePremium;
|
||||
const bridgedPrice = Number((earlyCustPrice * (bridgePremium ?? 1.00)).toFixed(5));
|
||||
const altHist = doc.hist?.cust?.ds;
|
||||
const iidx = doc.pricing?.iidx;
|
||||
const curr = doc.customer?.curr;
|
||||
const fxrate = doc.customer?.fxrate ?? 1.0;
|
||||
const qty = doc.inputs?.qty;
|
||||
const pltq = doc.product?.pltq;
|
||||
const inflation = Math.max(...Object.keys(iidx).map(Number));
|
||||
const inflationFactor = iidx[inflation];
|
||||
const list = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null;
|
||||
const listUSD = list ? list * fxrate :null;
|
||||
const stlc = doc.inputs.stlc;
|
||||
|
||||
// ------------------calculate price adders------------------------------------------------------
|
||||
let ltp = stlc.includes("SDD") || stlc.includes("HZP") ? 0 : (qty < pltq ? 0.15 : null);
|
||||
let anchor_sd = priceBand ? ((bridgedPrice - targetPrice) / priceBand).toFixed(2) : 0
|
||||
let optimization = getAdjValue(anchor_sd);
|
||||
let custAdder = (ltp ?? 0) + optimization + inflationFactor;
|
||||
let markAdder = (ltp ?? 0) + inflationFactor;
|
||||
let inflReason = (inflationFactor ?? 0) !== 0 ? ` + ${(inflationFactor * 100).toFixed(1)}% infl`: "";
|
||||
let ltpReason = ltp ? ` + ${(ltp * 100).toFixed(1)}% ltp` : "";
|
||||
let optReason = (optimization ?? 0) !== 0 ? ` + ${(optimization * 100).toFixed(1)}% opt`: "";
|
||||
let custAddReason = `${inflReason}${ltpReason}${optReason}`;
|
||||
let markAddReason = `${inflReason}${ltpReason}`;
|
||||
let floor = getFloor(stlc);
|
||||
|
||||
// ------------------start building price options------------------------------------------------
|
||||
|
||||
let snap = .0005;
|
||||
|
||||
let custPrice = r5(bridgedPrice * (1 + custAdder));
|
||||
let custSeason = earlyCustSeason;
|
||||
let custReason = bridgePremium
|
||||
? `${custSeason} (similar ${altHist} price ${pp(earlyCustPrice)} x ${bridgePremium} = ${pp(bridgedPrice)})${custAddReason}`
|
||||
: `${custSeason} price ${pp(bridgedPrice)}${custAddReason}`;
|
||||
let markPrice = r5(earlyMarkPrice * (1 + markAdder));
|
||||
let markReason = `${earlyMarkSeason} ASP ${pp(earlyMarkPrice)}${markAddReason}`;
|
||||
let targPrice = targetPrice ? r5(targetPrice * (1 + markAdder)) : null;
|
||||
let targReason = `Target price ${pp(targetPrice)}${markAddReason}`;
|
||||
let listPrice = listUSD;
|
||||
let listReason = fxrate === 1 ? `list ${pp(list)}` : `list ${pp(list)} CAD ${pp(listUSD)} USD`;
|
||||
|
||||
let prices = {
|
||||
cust: [custPrice, custReason, "cust", r5(ceiling(custPrice,snap))],
|
||||
mark: [markPrice, markReason, "mark", r5(ceiling(markPrice,snap))],
|
||||
targ: [targPrice, targReason, "targ", r5(ceiling(targPrice,snap))],
|
||||
list: [listPrice, listReason, "list", r5(ceiling(listPrice,snap))]
|
||||
}
|
||||
|
||||
let finalPrice = lowestPrice(prices);
|
||||
if (floor && floor > finalPrice.Price) {
|
||||
finalPrice.Price = floor;
|
||||
finalPrice.Snapped = floor;
|
||||
finalPrice.Reason = `${finalPrice.Reason} floor at ${floor}`;
|
||||
}
|
||||
let guidance = {
|
||||
prices
|
||||
,finalPrice
|
||||
,targetPrice
|
||||
,listUSD
|
||||
,ltp
|
||||
,inflationFactor
|
||||
,optimization
|
||||
}
|
||||
doc.guidance = guidance;
|
||||
//return doc;
|
||||
return sortObjectKeys(doc);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
DROP TABLE pricing.price_queue
|
||||
|
||||
CREATE TABLE pricing.price_queue (
|
||||
id BIGINT IDENTITY(1,1) PRIMARY KEY,
|
||||
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) -- if storing JSON-like data
|
||||
);
|
@ -1,19 +0,0 @@
|
||||
DROP TABLE pricequote.price_queue CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pricequote.price_queue (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
|
||||
,bill TEXT
|
||||
,ship TEXT
|
||||
,part TEXT
|
||||
,stlc TEXT
|
||||
,v1ds TEXT
|
||||
,vol NUMERIC
|
||||
,cust TEXT
|
||||
,chan TEXT
|
||||
,tier TEXT
|
||||
,pltq NUMERIC
|
||||
,price NUMERIC
|
||||
,expl JSONB
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON pricequote.price_queue TO PUBLIC;
|
@ -1,152 +0,0 @@
|
||||
CREATE OR REPLACE FUNCTION rlarp.guidance_r1(doc jsonb)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plv8
|
||||
AS $function$
|
||||
|
||||
function getAdjValue(number) {
|
||||
const data = [
|
||||
{f: 2.001, t: 1000, snap: 3, adj: 0 },
|
||||
{f: 1.001, t: 2, snap: 2, adj: 0 },
|
||||
{f: 0.1, t: 1, snap: 1, adj: 0 },
|
||||
{f: 0, t: 0.1, snap: 0, adj: 0 },
|
||||
{f: -1, t: -0.00001, snap: -1, adj: 0.05},
|
||||
{f: -2, t: -0.999999, snap: -2, adj: 0.05},
|
||||
{f: -1000, t: -2.001, snap: -3, adj: 0.10},
|
||||
];
|
||||
const match = data.find(row => number >= row.f && number <= row.t);
|
||||
return match ? match.adj : null;
|
||||
}
|
||||
|
||||
//-------------------------------set flor prices-------------------------------
|
||||
function getFloor(stlc) {
|
||||
switch (stlc) {
|
||||
case "SDD10000":
|
||||
return 0.045;
|
||||
case "SDD10001":
|
||||
return 0.045;
|
||||
case "SDD12000":
|
||||
return 0.065;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function lowestPrice(priceObject) {
|
||||
let Price = Infinity;
|
||||
let Reason = '';
|
||||
let Source = '';
|
||||
let Snapped = Infinity;
|
||||
|
||||
//console.log(priceObject["targ"][0]);
|
||||
// Iterate over each property in the object
|
||||
for (let key in priceObject) {
|
||||
// Ignore markPrice unless targPrice is null
|
||||
if (key === "mark" && priceObject["targ"][0] !== null) {
|
||||
continue;
|
||||
}
|
||||
if (priceObject.hasOwnProperty(key)) {
|
||||
let [cprice, creason, csource, csnap] = priceObject[key];
|
||||
// Check if the current price is lower than the found so far
|
||||
if (cprice && cprice < Price) {
|
||||
Price = cprice;
|
||||
Reason = creason;
|
||||
Source = csource;
|
||||
Snapped = csnap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {Reason, Price, Source, Snapped};
|
||||
}
|
||||
|
||||
function ceiling(value, significance) {
|
||||
return Math.ceil(value / significance) * significance;
|
||||
}
|
||||
|
||||
function r5(value) {
|
||||
return Number(value.toFixed(5));
|
||||
}
|
||||
|
||||
function pp(value) {
|
||||
// Multiplies by 1000 and rounds to the nearest 2 decimals
|
||||
var result = Math.round(value * 1000 * 100) / 100;
|
||||
|
||||
// Converts the number to a string with commas for thousand separators
|
||||
return result.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
|
||||
// --------------------extract incoming data------------------------------------------------------
|
||||
const targetPrice = doc.pricing?.v1tp ?? doc.pricing?.v0tp;
|
||||
const priceBand = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv;
|
||||
const earlyCustPrice = doc.hist?.cust?.early_price;
|
||||
const earlyCustSeason = doc.hist?.cust?.early_season;
|
||||
const earlyMarkPrice = doc.hist?.market?.early_price;
|
||||
const earlyMarkSeason = doc.hist?.market?.early_season;
|
||||
const bridgePremium = doc.pricing?.bridgePremium;
|
||||
const bridgedPrice = Number((earlyCustPrice * (bridgePremium ?? 1.00)).toFixed(5));
|
||||
const altHist = doc.hist?.cust?.ds;
|
||||
const iidx = doc.pricing?.iidx;
|
||||
const curr = doc.customer?.curr;
|
||||
const fxrate = doc.customer?.fxrate ?? 1.0;
|
||||
const qty = doc.inputs?.qty;
|
||||
const pltq = doc.product?.pltq;
|
||||
const inflation = iidx ? Math.max(...Object.keys(iidx).map(Number)) : null;
|
||||
const inflationFactor = iidx ? iidx[inflation] : 0;
|
||||
const list = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null;
|
||||
const listUSD = list ? list * fxrate :null;
|
||||
const stlc = doc.inputs.stlc;
|
||||
|
||||
// ------------------calculate price adders------------------------------------------------------
|
||||
let ltp = stlc.includes("SDD") || stlc.includes("HZP") ? 0 : (qty < pltq ? 0.15 : null);
|
||||
let anchor_sd = priceBand ? ((bridgedPrice - targetPrice) / priceBand).toFixed(2) : 0
|
||||
let optimization = getAdjValue(anchor_sd);
|
||||
let custAdder = (ltp ?? 0) + optimization + inflationFactor;
|
||||
let markAdder = (ltp ?? 0) + inflationFactor;
|
||||
let inflReason = (inflationFactor ?? 0) !== 0 ? ` + ${(inflationFactor * 100).toFixed(1)}% infl`: "";
|
||||
let ltpReason = ltp ? ` + ${(ltp * 100).toFixed(1)}% ltp` : "";
|
||||
let optReason = (optimization ?? 0) !== 0 ? ` + ${(optimization * 100).toFixed(1)}% opt`: "";
|
||||
let custAddReason = `${inflReason}${ltpReason}${optReason}`;
|
||||
let markAddReason = `${inflReason}${ltpReason}`;
|
||||
let floor = getFloor(stlc);
|
||||
|
||||
// ------------------start building price options------------------------------------------------
|
||||
|
||||
let snap = .0005;
|
||||
|
||||
let custPrice = r5(bridgedPrice * (1 + custAdder));
|
||||
let custSeason = earlyCustSeason;
|
||||
let custReason = bridgePremium
|
||||
? `${custSeason} (similar ${altHist} price ${pp(earlyCustPrice)} x ${bridgePremium} = ${pp(bridgedPrice)})${custAddReason}`
|
||||
: `${custSeason} price ${pp(bridgedPrice)}${custAddReason}`;
|
||||
let markPrice = r5(earlyMarkPrice * (1 + markAdder));
|
||||
let markReason = `${earlyMarkSeason} ASP ${pp(earlyMarkPrice)}${markAddReason}`;
|
||||
let targPrice = targetPrice ? r5(targetPrice * (1 + markAdder)) : null;
|
||||
let targReason = `Target price ${pp(targetPrice)}${markAddReason}`;
|
||||
let listPrice = listUSD;
|
||||
let listReason = fxrate === 1 ? `list ${pp(list)}` : `list ${pp(list)} CAD ${pp(listUSD)} USD`;
|
||||
|
||||
let prices = {
|
||||
cust: [custPrice, custReason, "cust", r5(ceiling(custPrice,snap))],
|
||||
mark: [markPrice, markReason, "mark", r5(ceiling(markPrice,snap))],
|
||||
targ: [targPrice, targReason, "targ", r5(ceiling(targPrice,snap))],
|
||||
list: [listPrice, listReason, "list", r5(ceiling(listPrice,snap))]
|
||||
}
|
||||
|
||||
let finalPrice = lowestPrice(prices);
|
||||
if (floor && floor > finalPrice.Price) {
|
||||
finalPrice.Price = floor;
|
||||
finalPrice.Snapped = floor;
|
||||
finalPrice.Reason = `${finalPrice.Reason} floor at ${floor}`;
|
||||
}
|
||||
let guidance = {
|
||||
prices
|
||||
,finalPrice
|
||||
,targetPrice
|
||||
,listUSD
|
||||
,ltp
|
||||
,inflationFactor
|
||||
,optimization
|
||||
}
|
||||
doc.guidance = guidance;
|
||||
return doc;
|
||||
|
||||
$function$;
|
@ -1,33 +0,0 @@
|
||||
SELECT
|
||||
gset.cust
|
||||
,gset.vers
|
||||
,gset.chan
|
||||
,gset.nurs
|
||||
,gset.ghse
|
||||
,gset.mold
|
||||
,gset.v1ds
|
||||
,gset.v0ds
|
||||
,je.k
|
||||
,seas.*
|
||||
FROM
|
||||
rlarp.price_pool p
|
||||
LEFT JOIN LATERAL jsonb_to_record(p.gset) AS gset(
|
||||
chan text
|
||||
,mold text
|
||||
,v1ds text
|
||||
,v0ds text
|
||||
,cust text
|
||||
,vers text
|
||||
,nurs text
|
||||
,ghse text
|
||||
) ON TRUE
|
||||
LEFT JOIN LATERAL jsonb_each(p.season) je(k,v) on true
|
||||
LEFT JOIN Lateral jsonb_to_record(je.v) as seas(
|
||||
units numeric
|
||||
,sales_usd numeric
|
||||
,price_usd numeric
|
||||
) ON TRUE
|
||||
WHERE
|
||||
gset @> '{"mold":"TFR001G0","v0ds":"BASE"}'
|
||||
AND agglevel ?| array['chan', 'mold', 'v0ds']
|
||||
-- AND NOT agglevel ?| array['cust','ghse'];
|
@ -1,5 +0,0 @@
|
||||
SELECT
|
||||
(select gg.d from rlarp.get_guidance_dseg('DIAM0004','DIAM0004','AMK06000','v1:B..PLT..',5000,2026) gg(d))
|
||||
FROM
|
||||
pricequote.live_quotes lq
|
||||
LIMIT 1
|
@ -1,79 +0,0 @@
|
||||
----takes 2 DBA's and returns the channel----------
|
||||
CREATE OR REPLACE FUNCTION rlarp.get_cust(billcode text, shipcode text)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
DECLARE
|
||||
_chan text;
|
||||
_chantp text;
|
||||
_bill_class text;
|
||||
_ship_class text;
|
||||
_bill_dba text;
|
||||
_ship_dba text;
|
||||
_cust text;
|
||||
_bill_curr text;
|
||||
_bill_rate numeric;
|
||||
_crec jsonb;
|
||||
_ret jsonb;
|
||||
_record jsonb;
|
||||
|
||||
BEGIN
|
||||
SELECT jsonb_agg(row_to_json(c)::jsonb) INTO _crec FROM rlarp.cust c WHERE code IN (billcode, shipcode);
|
||||
|
||||
--RAISE NOTICE '%', jsonb_pretty(_crec);
|
||||
|
||||
FOR _record IN SELECT * FROM jsonb_array_elements(_crec)
|
||||
LOOP
|
||||
-- Check if the record is for billcode or shipcode and assign values accordingly
|
||||
IF (_record->>'code')::text = billcode THEN
|
||||
_bill_dba := (_record->>'dba')::text;
|
||||
_bill_class := (_record->>'cclass')::text;
|
||||
_bill_curr := (_record->>'currency')::text;
|
||||
_bill_rate := (_record->>'fxcurr')::text;
|
||||
-- Add other billcode related assignments here
|
||||
END IF;
|
||||
|
||||
IF (_record->>'code')::text = shipcode THEN
|
||||
_ship_dba := (_record->>'dba')::text;
|
||||
_ship_class := (_record->>'cclass')::text;
|
||||
-- Add other shipcode related assignments here
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
SELECT
|
||||
CASE WHEN SUBSTRING(_bill_class,2,3) = 'DIS'
|
||||
THEN CASE WHEN SUBSTRING(_ship_class,2,3) = 'DIS' THEN 'WHS' ELSE 'DRP' END
|
||||
ELSE 'DIR'
|
||||
END
|
||||
INTO
|
||||
_chan;
|
||||
|
||||
SELECT
|
||||
CASE WHEN SUBSTRING(_bill_class,2,3) = 'DIS'
|
||||
THEN CASE WHEN SUBSTRING(_ship_class,2,3) = 'DIS' THEN 'DISTRIBUTOR' ELSE 'DISTRIB DROP SHIP' END
|
||||
ELSE 'DIRECT'
|
||||
END
|
||||
INTO
|
||||
_chantp;
|
||||
|
||||
SELECT
|
||||
CASE WHEN _chan = 'DRP' THEN _ship_dba ELSE _bill_dba END
|
||||
INTO
|
||||
_cust;
|
||||
|
||||
_ret := jsonb_build_object('customer',
|
||||
jsonb_build_object(
|
||||
'cust',_cust,
|
||||
'chan',_chan,
|
||||
'chantp',_chantp,
|
||||
'curr',_bill_curr,
|
||||
'fxrate',_bill_rate,
|
||||
'bill_dba',_bill_dba,
|
||||
'ship_dba',_ship_dba
|
||||
)
|
||||
);
|
||||
|
||||
RETURN _ret;
|
||||
|
||||
END
|
||||
$func$;
|
@ -1,2 +0,0 @@
|
||||
-- select gg.d from rlarp.get_guidance_dseg('DIAM0004','DIAM0004','AMK06000','v1:B..PLT..',5000,2026) gg(d);
|
||||
SELECT gg.d doc FROM rlarp.get_guidance_dseg($1,$2, $3,$4, $5, 2024) gg(d);
|
@ -1,23 +0,0 @@
|
||||
-- DELETE FROM pricequote.price_queue;
|
||||
--
|
||||
-- ALTER SEQUENCE pricequote.price_queue_id_seq RESTART WITH 1;
|
||||
--
|
||||
-- INSERT INTO
|
||||
-- pricequote.price_queue (bill, ship, part, stlc, v1ds, vol, chan, tier)
|
||||
-- SELECT DISTINCT
|
||||
-- bill_cust, ship_cust, part, stlc, dataseg, qty, chan, customer_tier
|
||||
-- FROM
|
||||
-- rlarp.osm_stack;
|
||||
|
||||
|
||||
|
||||
UPDATE
|
||||
pricequote.price_queue q
|
||||
SET
|
||||
expl = COALESCE(q.expl, '{}'::jsonb) || jsonb_build_object('price history', l.dataseg_stats)
|
||||
FROM
|
||||
pricequote.lastprice l
|
||||
WHERE
|
||||
q.cust = l.customer
|
||||
AND q.stlc = l.partgroup;
|
||||
|
@ -1,70 +0,0 @@
|
||||
set work_mem TO '4GB';
|
||||
|
||||
DROP VIEW IF EXISTS rlarp.live_quotes_review;
|
||||
CREATE VIEW rlarp.live_quotes_review AS
|
||||
WITH
|
||||
lq AS MATERIALIZED (
|
||||
SELECT
|
||||
lq.*
|
||||
,substring(lq.part,1,8) mold
|
||||
FROM
|
||||
pricequote.live_quotes lq
|
||||
)
|
||||
,lqg AS (
|
||||
SELECT
|
||||
lq.*
|
||||
,pricing->'product'->>'mold' part_group
|
||||
-- ,substring(pricing->'customer'->>'chan',1,1) qchan
|
||||
,pricing->'customer'->>'cust' qcust
|
||||
,pricing->'product'->>'itemrel' item_fit
|
||||
,(pricing->'product'->>'pltq')::numeric pltq
|
||||
,(pricing->'guidance'->'finalPrice'->>'Price')::numeric guidance
|
||||
,pricing->'guidance'->'finalPrice'->>'Reason' reason
|
||||
,(pricing->'product'->>'cstd_usd_ina')::numeric cstd_usd
|
||||
,(pricing->'product'->>'fstd_usd_ina')::numeric fstd_usd
|
||||
,(pricing->'guidance'->>'ltp')::numeric ltp
|
||||
,(pricing->'guidance'->>'optimization')::numeric optimization
|
||||
,(pricing->'guidance'->>'inflationFactor')::numeric inflation
|
||||
,jsonb_pretty(pricing) pricing
|
||||
FROM
|
||||
lq
|
||||
LEFT JOIN LATERAL rlarp.guidance_r1(
|
||||
rlarp.get_guidance_dseg(lq.billto,lq.shipto,substring(lq.part,1,8),lq.v1ds,lq.units_each,2025)
|
||||
) pricing ON TRUE
|
||||
WHERE
|
||||
lq.qstat ~ 'Submitted'
|
||||
)
|
||||
,hist AS (
|
||||
SELECT
|
||||
g.*
|
||||
,gset.chan
|
||||
--,gset.mold moldh
|
||||
,gset.v1ds v1dsh
|
||||
,gset.cust
|
||||
,gset.vers
|
||||
,je.k
|
||||
,seas.*
|
||||
FROM
|
||||
lqg g
|
||||
LEFT OUTER JOIN rlarp.price_pool p ON
|
||||
p.gset @> jsonb_build_object('mold',g.part_group)
|
||||
AND p.gset ? 'cust'
|
||||
AND p.gset ? 'v1ds'
|
||||
LEFT JOIN LATERAL jsonb_to_record(p.gset) AS gset(
|
||||
chan text
|
||||
,mold text
|
||||
,v1ds text
|
||||
,v0ds text
|
||||
,cust text
|
||||
,vers text
|
||||
--,nurs text
|
||||
--,ghse text
|
||||
) ON TRUE
|
||||
LEFT JOIN LATERAL jsonb_each(p.season) je(k,v) on true
|
||||
LEFT JOIN Lateral jsonb_to_record(je.v) as seas(
|
||||
units numeric
|
||||
,sales_usd numeric
|
||||
,price_usd numeric
|
||||
) ON TRUE
|
||||
)
|
||||
SELECT * FROM hist
|
@ -1,55 +0,0 @@
|
||||
--SELECT * INTO rlarp.target_prices FROM usmidsap02.ubm.pricequote.target_prices_view
|
||||
--DROP TABLE pricing.target_prices
|
||||
DELETE FROM 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
|
||||
INTO
|
||||
pricing.target_prices
|
||||
FROM
|
||||
usmidsap02.ubm.pricequote.target_prices_view;
|
||||
|
||||
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 TOP 100 * FROM rlarp.target_prices;
|
||||
|
||||
SELECT * FROM rlarp.target_prices tp
|
||||
WHERE
|
||||
stlc = 'XNS0T1G3'
|
||||
AND ds = 'v1:T..PLT..'
|
||||
AND chan = 'DIR'
|
||||
AND tier = 1
|
||||
AND 1.01 >= lower_bound
|
||||
AND (1.01 < upper_bound OR upper_bound IS NULL);
|
@ -1,164 +0,0 @@
|
||||
DROP FUNCTION pricequote.build_pricing_path;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pricequote.build_pricing_path(
|
||||
_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,
|
||||
-- jsonb_build_array(jsonb_build_object('entity',s.entity,'attr',s.attr,'function',s.func, 'val',s.val,'seq',s.seq)) math
|
||||
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, ' ') || ' x ' || LPAD(to_char(s.val, '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 ds,
|
||||
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 o.func WHEN 'Price' THEN c.agg + o.val WHEN 'Factor' THEN c.agg * o.val END agg,
|
||||
-- c.math || jsonb_build_array(jsonb_build_object('entity',o.entity,'attr',o.attr,'function',o.func, 'val',o.val,'seq',o.seq)) math,
|
||||
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, ' ') || ' x ' || LPAD(to_char(o.val, '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
|
||||
*/
|
@ -1,104 +0,0 @@
|
||||
DROP FUNCTION IF EXISTS rlarp.get_option_costs_priceg;
|
||||
CREATE FUNCTION rlarp.get_option_costs_priceg(_priceg text, _majg text)
|
||||
RETURNS TABLE (
|
||||
stlc text
|
||||
,coltier text
|
||||
,branding text
|
||||
,accs_ps text
|
||||
,acc_list text
|
||||
,suffix text
|
||||
,uomp text
|
||||
,avgsc numeric
|
||||
,avgfc numeric
|
||||
,weight numeric
|
||||
,target numeric
|
||||
,futmargin numeric
|
||||
)
|
||||
LANGUAGE plpgsql AS
|
||||
$function$
|
||||
|
||||
BEGIN
|
||||
|
||||
RETURN QUERY
|
||||
WITH
|
||||
sel AS (
|
||||
SELECT
|
||||
i.stlc
|
||||
,i.coltier
|
||||
,i.branding
|
||||
--,accs
|
||||
,i.accs_ps
|
||||
,i.acc_list
|
||||
,i.suffix
|
||||
--,COALESCE(accs_ps,accs) accs_c
|
||||
--,jsonb_agg(DISTINCT r.aomult) cavitation
|
||||
--,jsonb_agg(DISTINCT r.aoctme) cyclet
|
||||
--,jsonb_arr_aggcd(mino) min_ord
|
||||
,i.uomp
|
||||
--,jsonb_agg(DISTINCT aplnt) aplnt
|
||||
--,jsonb_agg(DISTINCT colc) colc
|
||||
--,jsonb_agg(DISTINCT substring(item,12,case when branding = '' THEN 7 ELSE 4 end)) items
|
||||
--,jsonb_agg(DISTINCT majg) majgs
|
||||
--,jsonb_agg(DISTINCT assc) accs
|
||||
,round(avg(curstdus),5) avgsc
|
||||
,round(avg(futstdus),5) avgfc
|
||||
,round(avg(nwht),5) avgwt
|
||||
,i.v1ds
|
||||
FROM
|
||||
"CMS.CUSLG".itemm i
|
||||
WHERE
|
||||
true
|
||||
--stlc ~ 'TWA10200'
|
||||
--AND branding = ''
|
||||
AND aplnt <> 'I'
|
||||
AND COALESCE(i.uomp,'') <> ''
|
||||
--AND branding = ''
|
||||
GROUP BY
|
||||
i.stlc
|
||||
,i.coltier
|
||||
,i.uomp
|
||||
,i.branding
|
||||
,i.acc_list
|
||||
,i.accs_ps
|
||||
,i.suffix
|
||||
,i.v1ds
|
||||
ORDER BY
|
||||
i.stlc ASC
|
||||
)
|
||||
SELECT
|
||||
sel.stlc
|
||||
,sel.coltier
|
||||
,sel.branding
|
||||
,sel.accs_ps
|
||||
,sel.acc_list
|
||||
,sel.suffix
|
||||
,sel.uomp
|
||||
,sel.avgsc
|
||||
,sel.avgfc
|
||||
,sel.avgwt
|
||||
,t.price
|
||||
,round(CASE WHEN coalesce(t.price,0) <> 0 THEN (t.price-COALESCE(sel.avgsc,0))/t.price ELSE 0 END,3) curmarg
|
||||
FROM
|
||||
sel
|
||||
LEFT OUTER JOIN pricequote.target_prices t ON
|
||||
sel.stlc = t.stlc
|
||||
AND sel.v1ds = t.ds
|
||||
AND t.chan = 'DIR'
|
||||
AND COALESCE(t.tier,'') IN ('1')
|
||||
AND 24 <@ t.vol
|
||||
-- LEFT OUTER JOIN pricequote.market_setavgprice t ON
|
||||
-- t.mold = sel.stlc
|
||||
-- AND t.data_segment = sel.v1ds
|
||||
-- AND t.season = 2025
|
||||
-- AND t.chan = 'DIRECT'
|
||||
-- AND t.country = 'ALL'
|
||||
-- AND t.geo = 'ALL'
|
||||
-- AND t.region = 'ALL'
|
||||
WHERE
|
||||
sel.stlc IN (SELECT DISTINCT m.stlc FROM rlarp.molds m WHERE m.priceg ~ _priceg AND m.majg ~ _majg);
|
||||
|
||||
END
|
||||
|
||||
$function$
|
||||
|
||||
|
@ -1,194 +0,0 @@
|
||||
DROP FUNCTION rlarp.get_options_merged;
|
||||
|
||||
CREATE OR REPLACE FUNCTION rlarp.get_options_merged(
|
||||
_priceg TEXT,
|
||||
_majg TEXT,
|
||||
existing_json JSONB
|
||||
)
|
||||
RETURNS TABLE (
|
||||
entity TEXT,
|
||||
attr TEXT,
|
||||
val NUMERIC,
|
||||
func TEXT
|
||||
)
|
||||
LANGUAGE plpgsql AS
|
||||
$$
|
||||
BEGIN
|
||||
|
||||
RETURN QUERY
|
||||
WITH
|
||||
|
||||
-- 1️⃣ Parse existing pricing from JSONB
|
||||
existing AS (
|
||||
SELECT
|
||||
e.entity,
|
||||
COALESCE(e.attr, '') AS attr,
|
||||
e.val,
|
||||
e.func
|
||||
FROM jsonb_to_recordset(existing_json)
|
||||
AS e(entity TEXT, attr TEXT, val NUMERIC, func TEXT)
|
||||
)
|
||||
,items AS (
|
||||
SELECT
|
||||
item
|
||||
FROM
|
||||
"CMS.CUSLG".itemm
|
||||
WHERE
|
||||
stlc IN (SELECT DISTINCT stlc FROM "CMS.CUSLG".itemm WHERE pricegroup ~ _priceg AND majg ~ _majg)
|
||||
AND aplnt <> 'I'
|
||||
)
|
||||
-- 2️⃣ Build raw stack without hard-coded func
|
||||
,stack AS (
|
||||
-- Anchor
|
||||
SELECT 'Anchor' AS entity, stlc AS attr, 0::numeric AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY stlc
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Color Tier
|
||||
SELECT 'Color Tier' AS entity, coltier AS attr, 1 AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY coltier
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Branding
|
||||
SELECT 'Branding' AS entity, COALESCE(substring(branding,1,1), '') AS attr, 0 AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY COALESCE(substring(branding,1,1), '')
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Packaging
|
||||
SELECT 'Packaging' AS entity, COALESCE(uomp, '') AS attr, 0 AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY COALESCE(uomp, '')
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Accessories
|
||||
SELECT 'Accessories' AS entity, COALESCE(accs_ps, '') AS attr, 0 AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY COALESCE(accs_ps, '')
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Suffix
|
||||
SELECT 'Suffix' AS entity, COALESCE(suffix, '') AS attr, 1 AS val
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (
|
||||
SELECT item
|
||||
FROM "CMS.CUSLG".itemm
|
||||
WHERE item IN (SELECT item FROM items)
|
||||
)
|
||||
GROUP BY COALESCE(suffix, '')
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Channel
|
||||
SELECT 'Channel' AS entity, x.chan AS attr, 0 AS val
|
||||
FROM (VALUES ('DIR'), ('DRP'), ('WHS')) AS x(chan)
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Customer Tier
|
||||
SELECT 'Tier' AS entity, x.tier AS attr, 0 AS val
|
||||
FROM (VALUES ('1'), ('2'), ('3')) AS x(tier)
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Volume
|
||||
SELECT 'Volume' AS entity, x.chan AS attr, 0 AS val
|
||||
FROM (VALUES ('0-1'),('1-8'),('8-24'),('24')) AS x(chan)
|
||||
),
|
||||
|
||||
-- 3️⃣ Attach pricing func and sequence from option_sequence
|
||||
stack_with_meta AS (
|
||||
SELECT
|
||||
s.entity,
|
||||
COALESCE(s.attr, '') AS attr,
|
||||
s.val,
|
||||
os.func,
|
||||
os.seq
|
||||
FROM stack s
|
||||
LEFT JOIN pricequote.option_sequence os
|
||||
ON s.entity = os.entity
|
||||
),
|
||||
|
||||
-- 4️⃣ Merge: master rows with overrides
|
||||
overridden AS (
|
||||
SELECT
|
||||
s.entity,
|
||||
s.attr,
|
||||
COALESCE(e.val, s.val) AS val,
|
||||
s.func,
|
||||
s.seq
|
||||
FROM stack_with_meta s
|
||||
LEFT JOIN existing e
|
||||
ON s.entity = e.entity
|
||||
AND COALESCE(s.attr, '') = COALESCE(e.attr, '')
|
||||
AND s.func = e.func
|
||||
),
|
||||
|
||||
-- 5️⃣ Extras in saved JSON but not in master
|
||||
extras AS (
|
||||
SELECT
|
||||
e.entity,
|
||||
COALESCE(e.attr, '') AS attr,
|
||||
e.val,
|
||||
e.func,
|
||||
os.seq
|
||||
FROM existing e
|
||||
LEFT JOIN stack_with_meta s
|
||||
ON e.entity = s.entity
|
||||
AND COALESCE(e.attr, '') = COALESCE(s.attr, '')
|
||||
AND e.func = s.func
|
||||
LEFT JOIN pricequote.option_sequence os
|
||||
ON e.entity = os.entity
|
||||
WHERE s.entity IS NULL
|
||||
),
|
||||
|
||||
-- 6️⃣ Combine both sources
|
||||
combined AS (
|
||||
SELECT o.entity, o.attr, o.val, o.func, o.seq FROM overridden o
|
||||
UNION ALL
|
||||
SELECT e.entity, e.attr, e.val, e.func, e.seq FROM extras e
|
||||
)
|
||||
|
||||
-- 7️⃣ Return ordered by sequence
|
||||
SELECT
|
||||
combined.entity,
|
||||
combined.attr,
|
||||
combined.val,
|
||||
combined.func
|
||||
FROM combined
|
||||
ORDER BY combined.seq NULLS LAST, combined.entity, combined.attr;
|
||||
|
||||
END;
|
||||
$$;
|
@ -1,42 +0,0 @@
|
||||
CREATE OR REPLACE PROCEDURE pricequote.load_target_prices(input_json JSONB)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
-- 1️⃣ Materialize the function output once
|
||||
CREATE TEMP TABLE temp_new_data ON COMMIT DROP AS
|
||||
SELECT
|
||||
stlc, ds, chan, tier, vol, price, math
|
||||
FROM
|
||||
pricequote.build_pricing_path(input_json)
|
||||
WHERE
|
||||
lastflag;
|
||||
|
||||
-- 2️⃣ Delete matching old rows
|
||||
DELETE FROM pricequote.target_prices t
|
||||
USING (
|
||||
SELECT DISTINCT stlc FROM temp_new_data
|
||||
) to_delete
|
||||
WHERE t.stlc = to_delete.stlc;
|
||||
|
||||
-- 3️⃣ Insert new rows
|
||||
INSERT INTO pricequote.target_prices (stlc, ds, chan, tier, vol, price, math)
|
||||
SELECT stlc, ds, chan, tier, vol, price, math FROM temp_new_data;
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
/*
|
||||
SELECT
|
||||
stlc,
|
||||
ds,
|
||||
chan,
|
||||
tier,
|
||||
vol,
|
||||
price,
|
||||
-- array_to_string(math, E'\n') AS math_text
|
||||
jsonb_build_object('target math',to_jsonb(math)) AS math_text
|
||||
FROM
|
||||
pricequote.build_pricing_path('[{"entity":"Anchor","attr":"JNS0T1G3","val":"0.08","func":"Price"},{"entity":"Anchor","attr":"XNS0T1G3","val":"0.08","func":"Price"},{"entity":"Anchor","attr":"XRD16002","val":"0.085","func":"Price"},{"entity":"Anchor","attr":"EU170S50","val":"0.085","func":"Price"},{"entity":"Anchor","attr":"EU170T50","val":"0.095","func":"Price"},{"entity":"Anchor","attr":"AZN06501","val":"0.12","func":"Price"},{"entity":"Anchor","attr":"1CP07010","val":"0.125","func":"Price"},{"entity":"Anchor","attr":"1CP06060","val":"0.13","func":"Price"},{"entity":"Anchor","attr":"AZA06500","val":"0.15","func":"Price"},{"entity":"Color Tier","attr":"B","val":1,"func":"Factor"},{"entity":"Color Tier","attr":"T","val":1.1,"func":"Factor"},{"entity":"Color Tier","attr":"L","val":1.1,"func":"Factor"},{"entity":"Color Tier","attr":"M","val":1.2,"func":"Factor"},{"entity":"Color Tier","attr":"P","val":1.3,"func":"Factor"},{"entity":"Color Tier","attr":"C","val":1.35,"func":"Factor"},{"entity":"Branding","val":"0","func":"Price"},{"entity":"Branding","attr":"L","val":"0.03","func":"Price"},{"entity":"Branding","attr":"P","val":"0.08","func":"Price"},{"entity":"Packaging","attr":"BDL","val":"0.002","func":"Price"},{"entity":"Packaging","attr":"CSE","val":"0.005","func":"Price"},{"entity":"Packaging","attr":"PC","val":"0.005","func":"Price"},{"entity":"Packaging","attr":"PLT","val":"0","func":"Price"},{"entity":"Packaging","attr":"SLV","val":"0.002","func":"Price"},{"entity":"Suffix","val":1,"func":"Factor"},{"entity":"Accessories","val":"0","func":"Price"},{"entity":"Channel","attr":"DIR","val":1,"func":"Factor"},{"entity":"Channel","attr":"DRP","val":1,"func":"Factor"},{"entity":"Channel","attr":"WHS","val":1.2,"func":"Factor"},{"entity":"Volume","attr":24,"val":1,"func":"Factor"},{"entity":"Volume","attr":"8-24","val":1.05,"func":"Factor"},{"entity":"Volume","attr":"1-8","val":1.1,"func":"Factor"},{"entity":"Volume","attr":"0-1","val":1.2,"func":"Factor"},{"entity":"Tier","attr":1,"val":1,"func":"Factor"},{"entity":"Tier","attr":2,"val":1.05,"func":"Factor"},{"entity":"Tier","attr":3,"val":1.07,"func":"Factor"}]'::jsonb)
|
||||
WHERE
|
||||
lastflag
|
||||
*/
|
@ -1,78 +0,0 @@
|
||||
|
||||
EXEC pricing.single_price_call
|
||||
@bill = 'GRIF0001',
|
||||
@ship = 'GRIF0001',
|
||||
@part = 'XNS0T1G3G18B096',
|
||||
@v1ds = 'v1:B..PLT..',
|
||||
@vol = 9600;
|
||||
|
||||
EXEC pricing.single_price_call
|
||||
@bill = 'BFGS0001',
|
||||
@ship = 'BOBS0002',
|
||||
@part = 'HTI10754B12B024LXB04',
|
||||
@v1ds = 'v1:L.L.PLT..',
|
||||
@vol = 172000;
|
||||
|
||||
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
|
@ -1,42 +0,0 @@
|
||||
SELECT
|
||||
ui_json->'details'
|
||||
FROM pricequote.single_price_call(
|
||||
'FARM0001',
|
||||
'KEYB0001',
|
||||
'HZP3E103E21D050',
|
||||
'v1:C..BDL..',
|
||||
50000
|
||||
) f
|
||||
|
||||
SELECT
|
||||
ui_json->'details'
|
||||
FROM pricequote.single_price_call(
|
||||
'FARM0001',
|
||||
'KEYB0001',
|
||||
'HZP3E103E21D050',
|
||||
'v1:C..BDL..',
|
||||
50000
|
||||
) f
|
||||
|
||||
SELECT
|
||||
ui_json
|
||||
FROM pricequote.single_price_call(
|
||||
'BFGS0001',
|
||||
'BOBS0002',
|
||||
'HTI10754B12B024LXB04',
|
||||
'v1:L.L.PLT..',
|
||||
172000
|
||||
) f
|
||||
|
||||
SELECT * FROM pricequote.lastpricedetail l WHERE customer = 'HYBELS' AND l.partgroup = 'HZP3E100'
|
||||
|
||||
SELECT
|
||||
pc.expl
|
||||
FROM
|
||||
pricequote.live_quotes
|
||||
LEFT JOIN LATERAL pricequote.single_price_call(
|
||||
billto, shipto, part, v1ds, units_each
|
||||
) pc ON TRUE
|
||||
WHERE
|
||||
qid = 113278
|
||||
AND qrn = 5
|
@ -1,83 +0,0 @@
|
||||
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.05, -- e.g., 5%
|
||||
@cap_last_pct numeric(10,5) = 1.00, -- e.g., 100%
|
||||
@cap_list_pct numeric(10,5) = 1.00
|
||||
)
|
||||
RETURNS TABLE
|
||||
AS
|
||||
RETURN
|
||||
WITH params AS (
|
||||
SELECT
|
||||
@target AS base_price,
|
||||
@last_norm AS last_norm,
|
||||
@list_eff AS list_eff,
|
||||
@floor_pct AS floor_pct,
|
||||
@cap_last_pct AS cap_last_pct,
|
||||
-- HARD CEILING at list: clamp to <= 1.00 regardless of input
|
||||
CASE WHEN @cap_list_pct IS NULL OR @cap_list_pct > 1 THEN 1.00 ELSE @cap_list_pct END AS eff_list_cap
|
||||
),
|
||||
step_floor AS (
|
||||
SELECT
|
||||
base_price, last_norm, list_eff, floor_pct, cap_last_pct, eff_list_cap,
|
||||
CAST(
|
||||
CASE
|
||||
WHEN base_price IS NULL OR last_norm IS NULL OR floor_pct <= 0
|
||||
THEN base_price
|
||||
ELSE ROUND(IIF(base_price >= last_norm*(1-floor_pct), base_price, last_norm*(1-floor_pct)), 5)
|
||||
END
|
||||
AS numeric(20,5)) AS after_floor
|
||||
FROM params
|
||||
),
|
||||
step_cap_last AS (
|
||||
SELECT
|
||||
base_price, last_norm, list_eff, floor_pct, cap_last_pct, eff_list_cap, after_floor,
|
||||
CAST(
|
||||
CASE
|
||||
WHEN after_floor IS NULL OR last_norm IS NULL
|
||||
THEN after_floor
|
||||
ELSE ROUND(IIF(after_floor <= last_norm*cap_last_pct, after_floor, last_norm*cap_last_pct), 5)
|
||||
END
|
||||
AS numeric(20,5)) AS after_cap_last
|
||||
FROM step_floor
|
||||
),
|
||||
step_cap_list AS (
|
||||
SELECT
|
||||
base_price, last_norm, list_eff, floor_pct, cap_last_pct, eff_list_cap, after_floor, after_cap_last,
|
||||
CAST(
|
||||
CASE
|
||||
WHEN after_cap_last IS NULL OR list_eff IS NULL
|
||||
THEN after_cap_last
|
||||
ELSE ROUND(IIF(after_cap_last <= list_eff*eff_list_cap, after_cap_last, list_eff*eff_list_cap), 5)
|
||||
END
|
||||
AS numeric(20,5)) AS final_price
|
||||
FROM step_cap_last
|
||||
)
|
||||
SELECT
|
||||
final_price AS guidance_price,
|
||||
CASE
|
||||
WHEN @target IS NULL THEN 'No target price available'
|
||||
WHEN @last_norm IS NULL AND @list_eff IS NULL THEN 'No prior sale or list; using target price'
|
||||
ELSE
|
||||
CONCAT(
|
||||
'Using target price',
|
||||
-- show floor only if it raised price
|
||||
CASE WHEN last_norm IS NOT NULL AND @floor_pct > 0 AND after_floor > base_price
|
||||
THEN CONCAT(', floored to ', FORMAT(@floor_pct*100,'0.##'), '% below last price') ELSE '' END,
|
||||
-- show last cap only if it lowered price
|
||||
CASE WHEN last_norm IS NOT NULL AND after_cap_last < after_floor
|
||||
THEN CASE WHEN @cap_last_pct = 1
|
||||
THEN ', capped to not exceed last price'
|
||||
ELSE CONCAT(', capped to ', FORMAT(@cap_last_pct*100,'0.##'), '% of last price')
|
||||
END
|
||||
ELSE '' END,
|
||||
-- show list cap only if it lowered price (always “not exceed” because eff_list_cap<=1)
|
||||
CASE WHEN list_eff IS NOT NULL AND final_price < after_cap_last
|
||||
THEN ', capped to not exceed list price'
|
||||
ELSE '' END
|
||||
)
|
||||
END AS guidance_reason
|
||||
FROM step_cap_list;
|
@ -1,69 +0,0 @@
|
||||
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.05,
|
||||
_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
|
||||
IF _target IS NULL THEN
|
||||
RETURN QUERY SELECT NULL::numeric, 'No target price available';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
base_price := _target;
|
||||
|
||||
-- floor (binds if it raises price)
|
||||
after_floor := base_price;
|
||||
IF _last_norm IS NOT NULL AND _floor_pct > 0 THEN
|
||||
after_floor := GREATEST(base_price, ROUND(_last_norm*(1-_floor_pct),5));
|
||||
END IF;
|
||||
|
||||
-- cap to last (binds if it lowers 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;
|
||||
|
||||
-- build reason
|
||||
IF _last_norm IS NULL AND _list_eff IS NULL THEN
|
||||
reason := 'No prior sale or list; using target price';
|
||||
ELSE
|
||||
reason := 'Using target price';
|
||||
IF _last_norm IS NOT NULL AND _floor_pct > 0 AND after_floor > base_price THEN
|
||||
reason := reason || format(', floored to %s%% below last price', to_char((_floor_pct*100)::numeric,'FM999990.##'));
|
||||
END IF;
|
||||
IF _last_norm IS NOT NULL AND after_cap_last < after_floor THEN
|
||||
IF _cap_last_pct = 1 THEN
|
||||
reason := reason || ', capped to not exceed last price';
|
||||
ELSE
|
||||
reason := reason || format(', capped to %s%% of last price', to_char((_cap_last_pct*100)::numeric,'FM999990.##'));
|
||||
END IF;
|
||||
END IF;
|
||||
IF _list_eff IS NOT NULL AND final_price < after_cap_last THEN
|
||||
IF _cap_list_pct = 1 THEN
|
||||
reason := reason || ', capped to not exceed list price';
|
||||
ELSE
|
||||
reason := reason || format(', capped to %s%% of list price', to_char((_cap_list_pct*100)::numeric,'FM999990.##'));
|
||||
END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN QUERY SELECT final_price, reason;
|
||||
END $$;
|
@ -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),
|
||||
part NVARCHAR(100)
|
||||
)
|
||||
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), @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 (@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 (@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
|
@ -1,77 +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
|
||||
-- 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 (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;
|
@ -1,222 +0,0 @@
|
||||
CREATE OR ALTER PROCEDURE pricing.batch_price_stack
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 1: Seed temp table from rlarp.osm_stack
|
||||
--------------------------------------------------------------------------------
|
||||
SELECT
|
||||
bill,
|
||||
ship,
|
||||
part,
|
||||
stlc,
|
||||
v1ds,
|
||||
vol,
|
||||
NULL AS chan,
|
||||
NULL AS cust,
|
||||
NULL AS tier,
|
||||
NULL AS pltq,
|
||||
NULL AS plevel,
|
||||
NULL AS hist,
|
||||
NULL AS last_price,
|
||||
NULL AS last_date,
|
||||
NULL AS last_order,
|
||||
NULL AS last_quote,
|
||||
NULL AS tprice,
|
||||
NULL AS guidance_price,
|
||||
NULL AS guidance_reason,
|
||||
'{}' AS expl,
|
||||
NULL AS list_price,
|
||||
NULL AS list_code
|
||||
INTO #queue
|
||||
FROM rlarp.osm_stack;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 2: Enrich
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
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 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
|
||||
)
|
||||
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
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
list_price = rp.price,
|
||||
list_code = rp.jcplcd,
|
||||
expl = JSON_MODIFY(
|
||||
JSON_MODIFY(
|
||||
ISNULL(q.expl, '{}'),
|
||||
'$.list_price', rp.price
|
||||
),
|
||||
'$.list_code', rp.jcplcd
|
||||
)
|
||||
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: Apply guidance logic
|
||||
--------------------------------------------------------------------------------
|
||||
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))
|
||||
) g;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Final output (to result table, or SELECT)
|
||||
--------------------------------------------------------------------------------
|
||||
SELECT * FROM #queue;
|
||||
|
||||
END;
|
||||
|
@ -1,529 +0,0 @@
|
||||
-- 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,
|
||||
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;
|
||||
|
||||
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';
|
||||
-- 46 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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);
|
||||
-- 16 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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;
|
||||
-- 16 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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;
|
||||
-- 2 min 3 sec
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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 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;
|
||||
-- 22 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 6) Target for last_dataseg (tprice_last)
|
||||
--------------------------------------------------------------------
|
||||
UPDATE pricequote.queue q
|
||||
SET
|
||||
tprice_last = tp2.price
|
||||
FROM pricequote.target_prices tp2
|
||||
WHERE
|
||||
q.last_dataseg IS NOT NULL
|
||||
AND tp2.stlc = q.stlc
|
||||
AND tp2.ds = q.last_dataseg
|
||||
AND tp2.chan = q.chan
|
||||
AND tp2.tier = q.tier
|
||||
AND FLOOR(q.last_qty / NULLIF(q.pltq, 0))::INT <@ tp2.vol;
|
||||
-- 17 sec
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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;
|
||||
-- 28 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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;
|
||||
-- 18 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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;
|
||||
-- 21 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 10) Guidance using normalized last + effective list
|
||||
--------------------------------------------------------------------
|
||||
UPDATE pricequote.queue q
|
||||
SET
|
||||
guidance_price = s.guidance_price,
|
||||
guidance_reason = s.guidance_reason
|
||||
FROM (
|
||||
SELECT
|
||||
q2.ctid,
|
||||
g.guidance_price,
|
||||
g.guidance_reason
|
||||
FROM pricequote.queue q2
|
||||
JOIN LATERAL pricequote.guidance_logic(
|
||||
q2.tprice,
|
||||
q2.last_price_norm,
|
||||
q2.listprice_eff,
|
||||
q2.last_date,
|
||||
.05, 1.0, 1.0
|
||||
) g ON TRUE
|
||||
) s
|
||||
WHERE q.ctid = s.ctid;
|
||||
-- 31 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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
|
||||
),
|
||||
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
|
||||
);
|
||||
-- 2 minutes 33 seconds
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- 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';
|
||||
-- 9 minutes 35 seconds
|
||||
|
||||
RAISE NOTICE 'Queue processing complete.';
|
||||
END;
|
||||
$$;
|
@ -1,587 +0,0 @@
|
||||
/*
|
||||
====================================================================================
|
||||
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),
|
||||
------------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 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: 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 rlarp.cost_v1ds v1 ON
|
||||
v1.stlc = q.stlc
|
||||
AND v1.v1ds = q.v1ds
|
||||
LEFT JOIN rlarp.cost_v0ds v0 ON
|
||||
v0.stlc = q.stlc
|
||||
AND v0.v0ds = q.v0ds
|
||||
LEFT JOIN rlarp.cost_v1ds v1l ON
|
||||
v1l.stlc = q.stlc
|
||||
AND v1l.v1ds = q.last_dataseg
|
||||
LEFT JOIN rlarp.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
|
||||
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),
|
||||
.05, 1.0, 1.0
|
||||
) g;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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
|
||||
-- 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,
|
||||
JSON_QUERY(panel.details) AS details
|
||||
FROM (
|
||||
-- History Panel
|
||||
SELECT
|
||||
'History' AS label,
|
||||
(
|
||||
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 Sale'
|
||||
WHEN 'dss' THEN 'Last Quote'
|
||||
ELSE ''
|
||||
END
|
||||
ELSE 'No Recent'
|
||||
END AS label,
|
||||
----------------------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,
|
||||
(
|
||||
SELECT
|
||||
COALESCE('Code: ' + q.listcode,'No List') AS label,
|
||||
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
|
||||
)
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Target Support Panel
|
||||
SELECT
|
||||
'Target Calculation' AS label,
|
||||
(
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
----------------------label------------------------------------------------
|
||||
CASE WHEN value <> '' THEN replace(RTRIM(SUBSTRING(value,1,18)),'Anchor:', '') ELSE 'No Target' END AS label,
|
||||
----------------------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,
|
||||
----------------------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,
|
||||
(
|
||||
SELECT
|
||||
'Price' AS label,
|
||||
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;
|
@ -1,576 +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
|
||||
====================================================================================
|
||||
*/
|
||||
|
||||
--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,
|
||||
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;
|
||||
------------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 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 tp
|
||||
WHERE
|
||||
tp.stlc = _stlc
|
||||
AND tp.ds = _last_dataseg
|
||||
AND tp.chan = _chan
|
||||
AND tp.tier = _tier
|
||||
AND FLOOR(_last_qty / _pltq)::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
|
||||
INTO
|
||||
_guidance_price
|
||||
,_guidance_reason
|
||||
FROM pricequote.guidance_logic(_tprice, _last_price_norm, _listprice_eff, _last_date, .05, 1.0, 1.0) gl;
|
||||
|
||||
------------------------------------------------------------------
|
||||
-- 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
|
||||
);
|
||||
|
||||
------------------------------------------------------------------
|
||||
-- Step 9: Build UI JSON (panels)
|
||||
------------------------------------------------------------------
|
||||
_ui_json := jsonb_build_object(
|
||||
'details', jsonb_build_array(
|
||||
------------------------------------------
|
||||
-- history
|
||||
------------------------------------------
|
||||
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
|
||||
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',
|
||||
'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',
|
||||
'value', _last_price_norm,
|
||||
'type','currency',
|
||||
'note','normalized to ' || _v1ds
|
||||
)),'[]'::jsonb)
|
||||
ELSE
|
||||
'[]'::jsonb
|
||||
END
|
||||
),
|
||||
------------------------------------------
|
||||
-- price list
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'List',
|
||||
'details', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'label', 'List:' || COALESCE(_list_code, ''),
|
||||
'value', _list_price,
|
||||
'type', 'currency',
|
||||
'note', _list_relevance
|
||||
)
|
||||
)
|
||||
),
|
||||
------------------------------------------
|
||||
-- history
|
||||
------------------------------------------
|
||||
jsonb_build_object(
|
||||
'label', 'Target Calculation',
|
||||
'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,
|
||||
----------------------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',
|
||||
'details', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'label', 'Price',
|
||||
'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,
|
||||
_expl, _ui_json;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
@ -1,315 +0,0 @@
|
||||
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),
|
||||
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
|
||||
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),
|
||||
partgroup = i.partgroup
|
||||
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
|
||||
,tmath = JSON_QUERY(tp.math)
|
||||
,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 = lp.part_stats
|
||||
FROM @queue q
|
||||
JOIN pricing.lastpricedetail lp
|
||||
ON lp.customer = q.cust AND lp.partgroup = q.partgroup;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 4b.1: Populate composite fields from precedence chain using JSON-based helper
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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_order = b.ord,
|
||||
last_quote = b.quote
|
||||
FROM @queue q
|
||||
CROSS APPLY (
|
||||
SELECT TOP 1 price, source, odate, qty, dataseg, ord, quote
|
||||
FROM pricing.pick_last_price_from_hist_json(q.hist, q.v1ds)
|
||||
) b;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 4b.2: Build JSON explanation object 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_source AS last_source,
|
||||
FORMAT(q.last_date, 'yyyy-MM-dd') AS last_date,
|
||||
q.tprice AS [target_price],
|
||||
JSON_QUERY(q.tmath) AS [target_math],
|
||||
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],
|
||||
q.cust AS [customer],
|
||||
q.chan AS [channel],
|
||||
TRIM(q.tier) AS [tier]
|
||||
-- JSON_QUERY(hist) AS [history]
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
)
|
||||
FROM @queue q;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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(last_date AS DATE)
|
||||
) g;
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Step 7: Clean up for UI
|
||||
--------------------------------------------------------------------------------
|
||||
UPDATE q
|
||||
SET ui_json = (
|
||||
SELECT
|
||||
(
|
||||
SELECT
|
||||
panel.label,
|
||||
JSON_QUERY(panel.details) AS details
|
||||
FROM (
|
||||
-- History Panel
|
||||
SELECT
|
||||
'History' AS label,
|
||||
(
|
||||
SELECT
|
||||
'Last Price' AS label,
|
||||
q.last_price AS value,
|
||||
'currency' AS type,
|
||||
CONCAT(
|
||||
'Source: ', ISNULL(q.last_source, 'N/A'),
|
||||
' | Date: ', ISNULL(CONVERT(varchar(10), q.last_date, 120), 'N/A'),
|
||||
' | Order: ', ISNULL(q.last_order, 'N/A'),
|
||||
' | Quote: ', ISNULL(q.last_quote, 'N/A'),
|
||||
' | Dataseg: ', ISNULL(q.last_dataseg, 'N/A'),
|
||||
' | Qty: ', ISNULL(CAST(q.last_qty AS varchar(32)), 'N/A')
|
||||
) AS note
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
) 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
|
||||
RTRIM(SUBSTRING(value,1,18)) AS 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,
|
||||
CASE SUBSTRING(value,19,1) WHEN '+' THEN 'Price' ELSE 'Premium' END 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
|
||||
'Price' AS label,
|
||||
q.guidance_price 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 the enriched result row
|
||||
--------------------------------------------------------------------------------
|
||||
RETURN;
|
||||
END;
|
@ -1,178 +0,0 @@
|
||||
-- set work_mem TO '4GB';
|
||||
--
|
||||
DROP VIEW IF EXISTS rlarp.quote_review;
|
||||
CREATE OR REPLACE VIEW rlarp.quote_review AS
|
||||
WITH
|
||||
---------------------get quote lines from SQL Server---------------------
|
||||
lq AS MATERIALIZED (
|
||||
SELECT
|
||||
lq.*
|
||||
,substring(lq.part,1,8) mold
|
||||
FROM
|
||||
pricequote.live_quotes lq
|
||||
WHERE
|
||||
qstat LIKE '%Submitted%'
|
||||
)
|
||||
---------------------guidance logic---------------------------------------
|
||||
,lqg AS MATERIALIZED (
|
||||
SELECT
|
||||
lq.qid
|
||||
,lq.qline
|
||||
,lq.rep
|
||||
,lq.touched
|
||||
,lq.expires
|
||||
,lq.request
|
||||
,lq.qtitle
|
||||
,lq.qstatid
|
||||
,lq.qstat
|
||||
,lq.quotenumber
|
||||
,lq.billto
|
||||
,lq.shipto
|
||||
,lq.qchan
|
||||
,lq.qcustomer
|
||||
,CASE lq.qchan WHEN 'DIR' THEN bc.tier ELSE sc.tier END customer_tier
|
||||
,lq.part
|
||||
,lq.qoptions
|
||||
,lq.partbuilt
|
||||
,lq.colgrp
|
||||
,lq.colc
|
||||
,lq.coltier
|
||||
,lq.brand
|
||||
,lq.dataseg
|
||||
,lq.v1ds
|
||||
,lq.comment
|
||||
,lq.units_each
|
||||
,lq.price
|
||||
,lq.sales
|
||||
,lq.histprice
|
||||
,lq.targetp
|
||||
,lq.lastsalesprice
|
||||
,lq.r_curr
|
||||
,lq.qt_rate
|
||||
,lq.qrn
|
||||
,lq.url
|
||||
,lq.tacticalmodifier
|
||||
,lq.finalrecommendedprice
|
||||
,lq.lowerpricelimit
|
||||
,lq.upperpricelimit
|
||||
,(lq.upperpricelimit + lq.lowerpricelimit) / 2 midrange
|
||||
,nt.price qtarget_price
|
||||
,i.partgroup part_group
|
||||
-- ,pricing->'product'->>'itemrel' item_fit
|
||||
,i.mpck pltq
|
||||
-- ,(pricing->'guidance'->'finalPrice'->>'Price')::numeric guidance
|
||||
-- ,pricing->'guidance'->'finalPrice'->>'Reason' reason
|
||||
,lq.curstdus cstd_usd
|
||||
,lq.futstdus fstd_usd
|
||||
,CASE
|
||||
WHEN i.glec = '1NU' THEN
|
||||
--if more than 8/24 pallets, use floor
|
||||
CASE WHEN units_each >= 24*mpck THEN lq.lowerpricelimit
|
||||
-- if more than a pallet use the target price
|
||||
ELSE CASE WHEN units_each >= 8*mpck THEN (lq.upperpricelimit + lq.lowerpricelimit) / 2
|
||||
-- if more than a pallet use the target price
|
||||
ELSE CASE WHEN units_each >= 2*mpck THEN lq.upperpricelimit
|
||||
-- if more than a pallet use the target price
|
||||
ELSE lq.upperpricelimit * 1.05
|
||||
END END END
|
||||
ELSE
|
||||
CASE WHEN i.pricegroup ~ '(Hanger|Dish)' THEN
|
||||
--at least 1 pallets is lower limit
|
||||
CASE WHEN units_each >= 01*mpck THEN lq.lowerpricelimit
|
||||
--at least 2 pallets is mid range
|
||||
ELSE CASE WHEN units_each >= 0.5*mpck THEN (lq.upperpricelimit + lq.lowerpricelimit) / 2
|
||||
--less than a pallet is upper + 15%
|
||||
ELSE lq.upperpricelimit
|
||||
END END
|
||||
ELSE
|
||||
--at least 8 pallets is lower limit
|
||||
CASE WHEN units_each >= 08*mpck THEN lq.lowerpricelimit
|
||||
--at least 2 pallets is mid range
|
||||
ELSE CASE WHEN units_each >= 2*mpck THEN (lq.upperpricelimit + lq.lowerpricelimit) / 2
|
||||
--at least 1 pallet is upper range
|
||||
ELSE CASE WHEN units_each >= 1*mpck THEN lq.upperpricelimit
|
||||
--less than a pallet is upper + 15%
|
||||
ELSE lq.upperpricelimit * 1.15
|
||||
END END END
|
||||
END
|
||||
END guidance
|
||||
,pl.price pricelist
|
||||
,pl.listcode
|
||||
,row_number() OVER (PARTITION BY qid, qline ORDER BY pl.price ASC) bestprice
|
||||
-- ,(pricing->'guidance'->>'ltp')::numeric ltp
|
||||
-- ,(pricing->'guidance'->>'optimization')::numeric optimization
|
||||
-- ,(pricing->'guidance'->>'inflationFactor')::numeric inflation
|
||||
-- ,jsonb_pretty(pricing) pricing
|
||||
,p.guidance_price
|
||||
,p.guidance_reason
|
||||
,jsonb_pretty(p.ui_json->'data') expl
|
||||
FROM
|
||||
lq
|
||||
LEFT OUTER JOIN "CMS.CUSLG".itemm i ON
|
||||
i.item = lq.part
|
||||
LEFT OUTER JOIN rlarp.cust bc ON
|
||||
bc.code = lq.billto
|
||||
LEFT OUTER JOIN rlarp.cust sc ON
|
||||
sc.code = lq.shipto
|
||||
LEFT OUTER JOIN rlarp.plcore_fullcode_ranged pl ON
|
||||
pl.item = lq.part
|
||||
AND jsonb_build_array(pl.listcode) <@ (COALESCE(sc.lists,bc.lists))
|
||||
AND (
|
||||
lq.units_each >= pl.vb_f
|
||||
AND lq.units_each < pl.vb_t
|
||||
)
|
||||
LEFT OUTER JOIN pricequote.target_prices nt ON
|
||||
nt.stlc = i.stlc
|
||||
AND nt.ds = lq.v1ds
|
||||
AND nt.chan = lq.qchan
|
||||
AND nt.tier = CASE lq.qchan WHEN 'DIR' THEN bc.tier ELSE sc.tier END
|
||||
AND CASE WHEN coalesce(i.mpck,0) = 0 THEN FALSE ELSE floor(lq.units_each/i.mpck)::int <@ nt.vol END
|
||||
LEFT JOIN LATERAL pricequote.single_price_call(
|
||||
lq.billto
|
||||
,lq.shipto
|
||||
,lq.part
|
||||
,lq.v1ds
|
||||
,lq.units_each
|
||||
) p ON TRUE
|
||||
WHERE
|
||||
lq.qstat ~ 'Submitted'
|
||||
)
|
||||
-- SELECT * FROM lqg where bestprice <> 1
|
||||
-- SELECT * FROM lqg WHERE qid = 109352
|
||||
---------------------------link in sales history----------------------------------------------
|
||||
,hist AS (
|
||||
SELECT
|
||||
g.*
|
||||
,gset.chan
|
||||
--,gset.mold moldh
|
||||
,gset.v1ds v1dsh
|
||||
,gset.cust
|
||||
,gset.vers
|
||||
,je.k
|
||||
,seas.*
|
||||
FROM
|
||||
lqg g
|
||||
LEFT OUTER JOIN rlarp.price_pool p ON
|
||||
p.gset @> jsonb_build_object('mold',g.part_group)
|
||||
AND p.gset ? 'cust'
|
||||
AND p.gset ? 'v1ds'
|
||||
LEFT JOIN LATERAL jsonb_to_record(p.gset) AS gset(
|
||||
chan text
|
||||
,mold text
|
||||
,v1ds text
|
||||
,v0ds text
|
||||
,cust text
|
||||
,vers text
|
||||
--,nurs text
|
||||
--,ghse text
|
||||
) ON TRUE
|
||||
LEFT JOIN LATERAL jsonb_each(p.season) je(k,v) on true
|
||||
LEFT JOIN Lateral jsonb_to_record(je.v) as seas(
|
||||
units numeric
|
||||
,sales_usd numeric
|
||||
,price_usd numeric
|
||||
) ON TRUE
|
||||
WHERE
|
||||
COALESCE(g.bestprice,1) = 1
|
||||
)
|
||||
SELECT * FROM hist --WHERE qid = 108655
|
5
sql/db_apply.pg.sql
Normal file
5
sql/db_apply.pg.sql
Normal file
@ -0,0 +1,5 @@
|
||||
SELECT
|
||||
rlarp.get_guidance(lq.billto, lq.shipto, lq.part, lq.units_each, 2024)
|
||||
FROM
|
||||
pricequote.live_quotes lq
|
||||
LIMIT 100
|
2
sql/get_dseg.pg.sql
Normal file
2
sql/get_dseg.pg.sql
Normal file
@ -0,0 +1,2 @@
|
||||
--select gg.d from rlarp.get_guidance('DIAM0004','DIAM0004','AMK06000G18B054',5000,2024) gg(d);
|
||||
SELECT gg.d doc FROM rlarp.get_guidance_dseg($1,$2, $3,$4, $5, 2024) gg(d);
|
@ -42,7 +42,7 @@ BEGIN
|
||||
INTO
|
||||
_mold,_stlc,_v1ds , _v0ds, _iidx
|
||||
FROM
|
||||
"CMS.CUSLG".itemm i
|
||||
"CMS.CUSLG".itemmv i
|
||||
INNER JOIN rlarp.molds m ON
|
||||
m.stlc = i.stlc
|
||||
WHERE
|
@ -1,4 +1,4 @@
|
||||
DROP FUNCTION rlarp.get_guidance_dseg CASCADE;
|
||||
DROP FUNCTION rlarp.get_guidance_dseg;
|
||||
CREATE OR REPLACE FUNCTION rlarp.get_guidance_dseg(_bill text, _ship text, _stlc text, _dseg text, _qty numeric, _seas int)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql AS
|
||||
@ -15,10 +15,6 @@ DECLARE
|
||||
_item text;
|
||||
_unti text;
|
||||
_pltq numeric;
|
||||
_cstd numeric;
|
||||
_cstdina numeric;
|
||||
_fstd numeric;
|
||||
_fstdina numeric;
|
||||
_cust text;
|
||||
_curr text;
|
||||
_rate numeric;
|
||||
@ -69,52 +65,37 @@ BEGIN
|
||||
,idxk
|
||||
,prefer
|
||||
,pltq
|
||||
,curstdus
|
||||
,curstdus_ina
|
||||
,futstdus
|
||||
,futstdus_ina
|
||||
INTO
|
||||
_mold
|
||||
,_item
|
||||
,_iidx
|
||||
,_itemr
|
||||
,_pltq
|
||||
,_cstd
|
||||
,_cstdina
|
||||
,_fstd
|
||||
,_fstdina
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
i.partgroup part_group
|
||||
m.part_group
|
||||
,min(i.item) item
|
||||
,i.stlc
|
||||
,i.v1ds
|
||||
,_ds.dataseg v0ds
|
||||
,i.mpck pltq
|
||||
,avg(i.curstdus) FILTER (WHERE aplnt <> 'I') curstdus
|
||||
,avg(i.curstdus) curstdus_ina
|
||||
,avg(i.futstdus) FILTER (WHERE aplnt <> 'I') futstdus
|
||||
,avg(i.futstdus) futstdus_ina
|
||||
,i.v0ds
|
||||
,i.pltq
|
||||
,jsonb_strip_nulls(jsonb_build_object('assc',CASE WHEN i.assc <> '' THEN i.assc ELSE null::text END,'majg',i.majg::int,'coltier',i.coltier)) idxk
|
||||
,CASE WHEN i.v1ds = _v1ds THEN 2 ELSE CASE WHEN _ds.dataseg = _v0ds THEN 1 ELSE 0 END END prefer
|
||||
,CASE WHEN i.v1ds = _v1ds THEN 2 ELSE CASE WHEN i.v0ds = _v0ds THEN 1 ELSE 0 END END prefer
|
||||
FROM
|
||||
"CMS.CUSLG".itemm i
|
||||
LEFT OUTER JOIN _ds ON
|
||||
_ds.colgrp = i.colgrp
|
||||
AND _ds.brand = SUBSTRING(i.branding,1,1)
|
||||
--INNER JOIN rlarp.molds m ON
|
||||
-- m.stlc = i.stlc
|
||||
"CMS.CUSLG".itemmv i
|
||||
INNER JOIN rlarp.molds m ON
|
||||
m.stlc = i.stlc
|
||||
WHERE
|
||||
i.stlc = _stlc
|
||||
GROUP BY
|
||||
i.partgroup
|
||||
m.part_group
|
||||
,i.stlc
|
||||
,i.v1ds
|
||||
,_ds.dataseg
|
||||
,i.mpck
|
||||
,i.v0ds
|
||||
,i.pltq
|
||||
,jsonb_strip_nulls(jsonb_build_object('assc',CASE WHEN i.assc <> '' THEN i.assc ELSE null::text END,'majg',i.majg::int,'coltier',i.coltier))
|
||||
,CASE WHEN i.v1ds = _v1ds THEN 2 ELSE CASE WHEN _ds.dataseg = _v0ds THEN 1 ELSE 0 END END
|
||||
,CASE WHEN i.v1ds = _v1ds THEN 2 ELSE CASE WHEN i.v0ds = _v0ds THEN 1 ELSE 0 END END
|
||||
) best
|
||||
ORDER BY
|
||||
prefer DESC
|
||||
@ -128,54 +109,49 @@ BEGIN
|
||||
,'itemrel',_itemr
|
||||
,'iidx',_iidx
|
||||
,'pltq',_pltq
|
||||
,'cstd_usd',_cstd
|
||||
,'cstd_usd_ina',_cstdina
|
||||
,'fstd_usd',_fstd
|
||||
,'fstd_usd_ina',_fstdina
|
||||
)
|
||||
);
|
||||
--RAISE NOTICE 'item data %', jsonb_pretty(_product||_input);
|
||||
|
||||
----------------channel-------------------------------------
|
||||
|
||||
SELECT rlarp.get_cust(_bill, _ship) INTO _customer;
|
||||
--_customer := jsonb_build_object('chan',_chan);
|
||||
SELECT rlarp.channel_code(_bill, _ship) INTO _chan;
|
||||
_customer := jsonb_build_object('chan',_chan);
|
||||
|
||||
----------------customer------------------------------------
|
||||
--SELECT dba INTO _cust FROM rlarp.cust WHERE code = CASE WHEN _chan = 'DRP' THEN _ship ELSE _bill END ;
|
||||
--SELECT
|
||||
-- currency,
|
||||
-- (SELECT
|
||||
-- x.rate
|
||||
-- FROM
|
||||
-- rlarp.ffcret x
|
||||
-- WHERE
|
||||
-- x.perd = (select fspr from rlarp.gld where drange @> current_date)
|
||||
-- AND x.rtyp = 'MA'
|
||||
-- and x.fcur = currency
|
||||
-- AND x.tcur = 'US'
|
||||
-- )
|
||||
--INTO
|
||||
-- _curr
|
||||
-- ,_rate
|
||||
--FROM
|
||||
-- rlarp.cust
|
||||
--WHERE
|
||||
-- code = _bill;
|
||||
SELECT dba INTO _cust FROM rlarp.cust WHERE code = CASE WHEN _chan = 'DRP' THEN _ship ELSE _bill END ;
|
||||
SELECT
|
||||
currency,
|
||||
(SELECT
|
||||
x.rate
|
||||
FROM
|
||||
rlarp.ffcret x
|
||||
WHERE
|
||||
x.perd = (select fspr from rlarp.gld where drange @> current_date)
|
||||
AND x.rtyp = 'MA'
|
||||
and x.fcur = currency
|
||||
AND x.tcur = 'US'
|
||||
)
|
||||
INTO
|
||||
_curr
|
||||
,_rate
|
||||
FROM
|
||||
rlarp.cust
|
||||
WHERE
|
||||
code = _bill;
|
||||
|
||||
--_customer := jsonb_build_object(
|
||||
-- 'customer',
|
||||
-- _customer||jsonb_build_object(
|
||||
-- 'cust',_cust
|
||||
-- ,'curr',_curr
|
||||
-- ,'fxrate',_rate
|
||||
-- )
|
||||
--);
|
||||
_customer := jsonb_build_object(
|
||||
'customer',
|
||||
_customer||jsonb_build_object(
|
||||
'cust',_cust
|
||||
,'curr',_curr
|
||||
,'fxrate',_rate
|
||||
)
|
||||
);
|
||||
--RAISE NOTICE 'cust %', jsonb_pretty(_customer);
|
||||
|
||||
----------------price history-------------------------------
|
||||
--RAISE NOTICE 'varb %', _customer;
|
||||
SELECT jsonb_build_object('hist',rlarp.get_hist(_mold, _v1ds, _customer->'customer'->>'cust', substring(_customer->'customer'->>'chan',1,1))) INTO _hist;
|
||||
SELECT jsonb_build_object('hist',rlarp.get_hist(_mold, _v1ds, _cust, substring(_chan,1,1))) INTO _hist;
|
||||
--RAISE NOTICE 'history %', jsonb_pretty(_hist);
|
||||
|
||||
----------------target pricing------------------------------
|
||||
@ -194,8 +170,7 @@ BEGIN
|
||||
mold = _stlc
|
||||
AND season = _seas
|
||||
AND data_segment = _v0ds
|
||||
AND region = 'ALL'
|
||||
AND chan = _customer->'customer'->>'chantp';
|
||||
AND region = 'ALL';
|
||||
----------------target pricing------------------------------
|
||||
SELECT
|
||||
jsonb_build_object(
|
||||
@ -212,8 +187,7 @@ BEGIN
|
||||
mold = _stlc
|
||||
AND season = _seas
|
||||
AND data_segment = _dseg
|
||||
AND region = 'ALL'
|
||||
AND chan = _customer->'customer'->>'chantp';
|
||||
AND region = 'ALL';
|
||||
--RAISE NOTICE 'target: %', jsonb_pretty(_targ);
|
||||
_pricing := (COALESCE(_v0tp,'{}'::jsonb)||COALESCE(_v1tp,'{}'::jsonb));
|
||||
|
||||
@ -247,7 +221,7 @@ BEGIN
|
||||
--RAISE NOTICE 'add list: %', jsonb_pretty(_pricing);
|
||||
|
||||
----------------get premium for quote hist gap--------------
|
||||
SELECT coalesce(rlarp.get_premium(_stlc, _seas, _customer->'customer'->>'chantp',_hist->'hist'->'cust'->>'ds', _v1ds),'{}'::jsonb) INTO _prem;
|
||||
SELECT coalesce(rlarp.get_premium(_stlc, _seas, (SELECT xchan FROM _chx WHERE chan = _chan),_hist->'hist'->'cust'->>'ds', _v1ds),'{}'::jsonb) INTO _prem;
|
||||
_pricing := jsonb_build_object('pricing',_pricing||_prem);
|
||||
--RAISE NOTICE 'add bridge: %', jsonb_pretty(_pricing);
|
||||
|
@ -18,7 +18,7 @@ sort AS (
|
||||
,stats.*
|
||||
,season
|
||||
FROM
|
||||
rlarp.price_pool p
|
||||
rlarp.price_pool_dev p
|
||||
JOIN LATERAL jsonb_to_record(gset) AS gset(
|
||||
chan text
|
||||
,mold text
|
||||
@ -110,7 +110,7 @@ FROM
|
||||
,('customer v0ds other',7)
|
||||
,('customer v0ds vol' ,3)
|
||||
,('customer v1ds other',6)
|
||||
,('customer v1ds vol' ,2) --this will always sort to the top, v0ds will never sort to the top. you will always be getting the highest volume base price
|
||||
,('customer v1ds vol' ,2)
|
||||
,('market exact' ,4)
|
||||
,('market v0ds other' ,9)
|
||||
,('market v0ds vol' ,5)
|
||||
@ -123,29 +123,24 @@ FROM
|
||||
--,flag.source
|
||||
--,rel.prefer
|
||||
--,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC) best
|
||||
jsonb_strip_nulls(
|
||||
jsonb_build_object(
|
||||
flag.source
|
||||
,jsonb_build_object(
|
||||
'relevance' ,relevance
|
||||
,'avgunits' ,avgunits
|
||||
,'avgordcount' ,avgordcount
|
||||
,'avgcustcount' ,avgcustcount
|
||||
,'avgtargetprice' ,avgtargetprice
|
||||
,'early_season' ,early_season
|
||||
,'early_price' ,early_price
|
||||
,'recent_season' ,recent_season
|
||||
,'recent_price' ,recent_price
|
||||
,'last_season' ,last_season
|
||||
,'last_price' ,last_price
|
||||
,'ds' ,COALESCE(v1ds,v0ds)
|
||||
,'rank' ,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC)
|
||||
,'pricinghistory' ,season
|
||||
)
|
||||
)
|
||||
flag.source
|
||||
,jsonb_build_object(
|
||||
'relevance' ,relevance
|
||||
,'avgunits' ,avgunits
|
||||
,'avgordcount' ,avgordcount
|
||||
,'avgcustcount' ,avgcustcount
|
||||
,'avgtargetprice' ,avgtargetprice
|
||||
,'early_season' ,early_season
|
||||
,'early_price' ,early_price
|
||||
,'recent_season' ,recent_season
|
||||
,'recent_price' ,recent_price
|
||||
,'last_season' ,last_season
|
||||
,'last_price' ,last_price
|
||||
,'ds' ,COALESCE(v1ds,v0ds)
|
||||
,'rank' ,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC)
|
||||
,'pricinghistory' ,season
|
||||
) doc
|
||||
,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC) rnk
|
||||
,season
|
||||
FROM
|
||||
flag
|
||||
LEFT OUTER JOIN rel ON
|
||||
@ -154,7 +149,8 @@ FROM
|
||||
relevance ~ 'vol|exact'
|
||||
)
|
||||
--SELECT jsonb_pretty(jsonb_agg(doc)) FROM rel_sort
|
||||
SELECT jsonb_obj_aggc(doc) INTO _result FROM rel_sort WHERE rnk = 1;
|
||||
SELECT jsonb_obj_aggc(doc) INTO _result FROM (SELECT jsonb_build_object(source, jsonb_agg(doc ORDER BY doc->>'rank' ASC)) doc FROM rel_sort GROUP BY source) x;
|
||||
--SELECT jsonb_obj_aggc(doc) INTO _result FROM rel_sort WHERE rnk = 1;
|
||||
|
||||
RETURN _result;
|
||||
|
@ -1,5 +1,5 @@
|
||||
WITH
|
||||
sel AS (select 'v1:T..PLT..' _v1ds, 'KAWAHARA NURSERY' _cust, 'AZE10001' _mold, 'D' _chan)
|
||||
sel AS (select 'v1:P.P.PLT..' _v1ds, 'ALTMAN PLANTS' _cust, 'XNS0T1G3' _mold, 'D' _chan)
|
||||
,sort AS (
|
||||
SELECT
|
||||
p.agglevel
|
||||
@ -11,7 +11,7 @@ sel AS (select 'v1:T..PLT..' _v1ds, 'KAWAHARA NURSERY' _cust, 'AZE10001' _mold,
|
||||
,stats.*
|
||||
,season
|
||||
FROM
|
||||
rlarp.price_pool p
|
||||
rlarp.price_pool_dev p
|
||||
CROSS JOIN sel
|
||||
JOIN LATERAL jsonb_to_record(gset) AS gset(
|
||||
chan text
|
||||
@ -53,19 +53,6 @@ sel AS (select 'v1:T..PLT..' _v1ds, 'KAWAHARA NURSERY' _cust, 'AZE10001' _mold,
|
||||
source ASC
|
||||
,rn ASC
|
||||
)
|
||||
,rel AS (
|
||||
SELECT * FROM (values
|
||||
('customer exact' ,1)
|
||||
,('customer v0ds other',7)
|
||||
,('customer v0ds vol' ,3)
|
||||
,('customer v1ds other',6)
|
||||
,('customer v1ds vol' ,2) --this will always sort to the top, v0ds will never sort to the top. you will always be getting the highest volume base price
|
||||
,('market exact' ,4)
|
||||
,('market v0ds other' ,9)
|
||||
,('market v0ds vol' ,5)
|
||||
,('market v1ds other' ,8)
|
||||
) x (flag,prefer)
|
||||
)
|
||||
,flag AS (
|
||||
SELECT
|
||||
--agglevel
|
||||
@ -97,7 +84,6 @@ SELECT
|
||||
,cust
|
||||
,vers
|
||||
,rn
|
||||
,row_number() OVER (PARTITION BY source ORDER BY rel.prefer ASC) rnk
|
||||
,avgunits
|
||||
,avgordcount
|
||||
,avgcustcount
|
||||
@ -111,25 +97,19 @@ SELECT
|
||||
,season
|
||||
FROM
|
||||
sort
|
||||
LEFT OUTER JOIN rel ON
|
||||
rel.flag = CASE source
|
||||
WHEN 'cust' THEN
|
||||
CASE WHEN v1ds IS NOT NULL THEN
|
||||
CASE WHEN v1ds_match THEN 'customer exact' ELSE
|
||||
CASE WHEN rn = 1 THEN 'customer v1ds vol' ELSE 'customer v1ds other' END
|
||||
END
|
||||
ELSE
|
||||
CASE WHEN rn = 1 THEN 'customer v0ds vol' ELSE 'customer v0ds other' END
|
||||
END
|
||||
ELSE
|
||||
CASE WHEN v1ds IS NOT NULL THEN
|
||||
CASE WHEN v1ds_match THEN 'market exact' ELSE
|
||||
CASE WHEN rn = 1 THEN 'market v1ds vol' ELSE 'market v1ds other' END
|
||||
END
|
||||
ELSE
|
||||
CASE WHEN rn = 1 THEN 'market v0ds vol' ELSE 'market v0ds other' END
|
||||
END
|
||||
END
|
||||
)
|
||||
,rel AS (
|
||||
SELECT * FROM (values
|
||||
('customer exact' ,1)
|
||||
,('customer v0ds other',7)
|
||||
,('customer v0ds vol' ,3)
|
||||
,('customer v1ds other',6)
|
||||
,('customer v1ds vol' ,2)
|
||||
,('market exact' ,4)
|
||||
,('market v0ds other' ,9)
|
||||
,('market v0ds vol' ,5)
|
||||
,('market v1ds other' ,8)
|
||||
) x (flag,prefer)
|
||||
)
|
||||
,rel_sort AS (
|
||||
SELECT
|
||||
@ -137,37 +117,32 @@ FROM
|
||||
--,flag.source
|
||||
--,rel.prefer
|
||||
--,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC) best
|
||||
jsonb_strip_nulls(
|
||||
jsonb_build_object(
|
||||
flag.source
|
||||
,jsonb_build_object(
|
||||
'relevance' ,relevance
|
||||
,'avgunits' ,avgunits
|
||||
,'avgordcount' ,avgordcount
|
||||
,'avgcustcount' ,avgcustcount
|
||||
,'avgtargetprice' ,avgtargetprice
|
||||
,'early_season' ,early_season
|
||||
,'early_price' ,early_price
|
||||
,'recent_season' ,recent_season
|
||||
,'recent_price' ,recent_price
|
||||
,'last_season' ,last_season
|
||||
,'last_price' ,last_price
|
||||
,'ds' ,COALESCE(v1ds,v0ds)
|
||||
,'rank' ,rnk
|
||||
,'pricinghistory' ,season
|
||||
)
|
||||
)
|
||||
flag.source
|
||||
,jsonb_build_object(
|
||||
'relevance' ,relevance
|
||||
,'avgunits' ,avgunits
|
||||
,'avgordcount' ,avgordcount
|
||||
,'avgcustcount' ,avgcustcount
|
||||
,'avgtargetprice' ,avgtargetprice
|
||||
,'early_season' ,early_season
|
||||
,'early_price' ,early_price
|
||||
,'recent_season' ,recent_season
|
||||
,'recent_price' ,recent_price
|
||||
,'last_season' ,last_season
|
||||
,'last_price' ,last_price
|
||||
,'ds' ,COALESCE(v1ds,v0ds)
|
||||
,'rank' ,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC)
|
||||
,'pricinghistory' ,season
|
||||
) doc
|
||||
,rnk
|
||||
,season
|
||||
,row_number() OVER (PARTITION BY flag.source ORDER BY rel.prefer ASC) rnk
|
||||
FROM
|
||||
flag
|
||||
LEFT OUTER JOIN rel ON
|
||||
rel.flag = flag.relevance
|
||||
WHERE
|
||||
relevance ~ 'vol|exact'
|
||||
)
|
||||
--SELECT rnk, jsonb_pretty(doc) doc, jsonb_pretty(season) season FROM rel_sort
|
||||
--SELECT jsonb_pretty(jsonb_agg(doc)) FROM rel_sort
|
||||
--SELECT jsonb_pretty(jsonb_obj_aggc(doc)) FROM rel_sort--INTO _result FROM rel_sort WHERE rnk = 1;
|
||||
--SELECT * FROM rel_sort;
|
||||
SELECT * FROM flag
|
||||
--SELECT jsonb_obj_aggc(doc) FROM rel_sort WHERE rnk = 1;
|
||||
SELECT jsonb_build_object(source, jsonb_agg(doc ORDER BY doc->>'rank' ASC)) FROM rel_sort GROUP BY source;
|
@ -25,7 +25,7 @@ BEGIN
|
||||
stats,
|
||||
(SELECT doc FROM getj) AS gdoc
|
||||
FROM
|
||||
rlarp.price_pool p
|
||||
rlarp.price_pool_dev p
|
||||
WHERE
|
||||
gset @> jsonb_build_object(
|
||||
'mold', (SELECT doc->>'mold' FROM getj),
|
@ -1,5 +1,5 @@
|
||||
WITH
|
||||
sel AS (select 'v1:B..PLT..' _v1ds, 'ALTMAN PLANTS' _cust, 'XPR15CS1' _mold, 'D' _chan)
|
||||
sel AS (select 'v1:P.P.PLT..' _v1ds, 'ALTMAN PLANTS' _cust, 'TFR001G0' _mold, 'D' _chan)
|
||||
,sort AS (
|
||||
SELECT
|
||||
p.agglevel
|
||||
@ -10,7 +10,7 @@ sel AS (select 'v1:B..PLT..' _v1ds, 'ALTMAN PLANTS' _cust, 'XPR15CS1' _mold, 'D'
|
||||
,row_number() OVER (PARTITION BY p.agglevel ORDER BY avgunits DESC) rn
|
||||
,stats.*
|
||||
FROM
|
||||
rlarp.price_pool p
|
||||
rlarp.price_pool_dev p
|
||||
CROSS JOIN sel
|
||||
JOIN LATERAL jsonb_to_record(gset) AS gset(
|
||||
chan text
|
@ -1,9 +1,9 @@
|
||||
--CREATE OR REPLACE PROCEDURE rlarp.price_pool()
|
||||
--LANGUAGE plpgsql AS
|
||||
--$func$
|
||||
--BEGIN;
|
||||
DROP MATERIALIZED VIEW IF EXISTS rlarp.price_pool CASCADE;
|
||||
CREATE MATERIALIZED VIEW rlarp.price_pool AS (
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS rlarp.price_pool_dev;
|
||||
CREATE TABLE IF NOT EXISTS rlarp.price_pool_dev AS (
|
||||
WITH
|
||||
agg AS (
|
||||
SELECT
|
||||
@ -11,13 +11,13 @@ CREATE MATERIALIZED VIEW rlarp.price_pool AS (
|
||||
,substring(o.version,1,1) version
|
||||
,o.chanwide
|
||||
,o.nursery_region
|
||||
,o.greenhouse_region
|
||||
,o.partgroup baseitem
|
||||
,o.majgd
|
||||
,o.assc
|
||||
,o.coltierd
|
||||
,o.dataseg v1dataseg
|
||||
,o.v0dataseg v0dataseg
|
||||
,c.greenhouse_region
|
||||
,m.part_group baseitem
|
||||
,m.majg
|
||||
,m.assc
|
||||
,i.coltier
|
||||
,'v1:' || rtrim(COALESCE(i.coltier, ''))|| '.' || rtrim(substring(COALESCE(i.branding, ''), 1, 1))|| '.' || rtrim(COALESCE(i.uomp, ''))|| '.' || rtrim(COALESCE(i.suffix, ''))|| '.' || rtrim(COALESCE(i.accs_ps, '')) v1dataseg
|
||||
,_ds.dataseg v0dataseg
|
||||
,o.oseas oseas
|
||||
,round(sum(o.qty),0) units
|
||||
,round(sum(o.sales_usd),0) sales_usd
|
||||
@ -25,104 +25,52 @@ CREATE MATERIALIZED VIEW rlarp.price_pool AS (
|
||||
,count(DISTINCT o.ordnum) ordcount
|
||||
FROM
|
||||
rlarp.osm_stack o
|
||||
INNER JOIN "CMS.CUSLG".itemm i ON
|
||||
i.item = o.part
|
||||
LEFT OUTER JOIN rlarp.molds m ON
|
||||
m.stlc = i.stlc
|
||||
LEFT OUTER JOIN _ds ON
|
||||
_ds.colgrp = o.colgrp
|
||||
AND _ds.brand = substring(i.branding,1,1)
|
||||
LEFT OUTER JOIN pricequote.market_setavgprice tp ON
|
||||
tp.season = (SELECT ssyr FROM rlarp.gld where drange @> current_date)
|
||||
AND tp.country = 'ALL'
|
||||
AND tp.geo = 'ALL'
|
||||
AND tp.region = 'ALL'
|
||||
AND tp.mold = o.stlc
|
||||
AND tp.mold = i.stlc
|
||||
AND tp.chan = 'DISTRIB DROP SHIP'
|
||||
AND tp.data_segment = o.dataseg
|
||||
AND tp.data_segment = 'v1:' || rtrim(COALESCE(i.coltier, ''))|| '.' || rtrim(substring(COALESCE(i.branding, ''), 1, 1))|| '.' || rtrim(COALESCE(i.uomp, ''))|| '.' || rtrim(COALESCE(i.suffix, ''))|| '.' || rtrim(COALESCE(i.accs_ps, ''))
|
||||
LEFT OUTER JOIN pricequote.market_setavgprice tq ON
|
||||
tq.season = (SELECT ssyr FROM rlarp.gld where drange @> current_date)
|
||||
AND tq.country = 'ALL'
|
||||
AND tq.geo = 'ALL'
|
||||
AND tq.region = 'ALL'
|
||||
AND tq.mold = o.stlc
|
||||
AND tq.mold = i.stlc
|
||||
AND tq.chan = 'DISTRIB DROP SHIP'
|
||||
AND tq.data_segment = o.v0dataseg
|
||||
AND tq.data_segment = _ds.dataseg
|
||||
LEFT OUTER JOIN rlarp.cust c ON
|
||||
c.code = CASE o.chan WHEN 'DIR' THEN o.bill_cust ELSE o.ship_cust END
|
||||
WHERE
|
||||
o.version IN ('Actual','Quotes')
|
||||
AND o.oseas >= 2015
|
||||
AND o.mingd <> 'B52'
|
||||
--AND o.part like 'SQL035%'
|
||||
--AND o.calc_status <> 'CANCELED'
|
||||
AND o.fs_line = '41010'
|
||||
--AND o.dsm <> 'PW'
|
||||
--AND i.coltierd <> 'C'
|
||||
GROUP BY
|
||||
o.customer
|
||||
,substring(o.version,1,1)
|
||||
,o.chanwide
|
||||
,o.nursery_region
|
||||
,o.greenhouse_region
|
||||
,o.partgroup
|
||||
,o.majgd
|
||||
,o.assc
|
||||
,o.coltierd
|
||||
,o.dataseg
|
||||
,o.v0dataseg
|
||||
,o.oseas
|
||||
HAVING
|
||||
round(sum(o.qty),0) > 0
|
||||
AND round(sum(o.sales_usd)/sum(o.qty),5) > 0
|
||||
UNION ALL
|
||||
SELECT
|
||||
o.customer
|
||||
,substring(o.version,1,1) version
|
||||
,o.chanwide
|
||||
,o.nursery_region
|
||||
,o.greenhouse_region
|
||||
,o.partgroup baseitem
|
||||
,o.majgd
|
||||
,o.assc
|
||||
,o.coltierd
|
||||
,o.dataseg v1dataseg
|
||||
,o.v0dataseg v0dataseg
|
||||
,0 oseas
|
||||
,round(sum(o.qty),0) units
|
||||
,round(sum(o.sales_usd),0) sales_usd
|
||||
,round(sum(COALESCE(tp.target_price,tq.target_price) * o.qty),2) target_price
|
||||
,count(DISTINCT o.ordnum) ordcount
|
||||
FROM
|
||||
rlarp.osm_stack o
|
||||
LEFT OUTER JOIN pricequote.market_setavgprice tp ON
|
||||
tp.season = (SELECT ssyr FROM rlarp.gld where drange @> current_date)
|
||||
AND tp.country = 'ALL'
|
||||
AND tp.geo = 'ALL'
|
||||
AND tp.region = 'ALL'
|
||||
AND tp.mold = o.stlc
|
||||
AND tp.chan = 'DISTRIB DROP SHIP'
|
||||
AND tp.data_segment = o.dataseg
|
||||
LEFT OUTER JOIN pricequote.market_setavgprice tq ON
|
||||
tq.season = (SELECT ssyr FROM rlarp.gld where drange @> current_date)
|
||||
AND tq.country = 'ALL'
|
||||
AND tq.geo = 'ALL'
|
||||
AND tq.region = 'ALL'
|
||||
AND tq.mold = o.stlc
|
||||
AND tq.chan = 'DISTRIB DROP SHIP'
|
||||
AND tq.data_segment = o.v0dataseg
|
||||
WHERE
|
||||
o.version IN ('Actual','Quotes')
|
||||
AND o.odate >= current_date - '4 months'::interval
|
||||
AND o.mingd <> 'B52'
|
||||
AND o.dsm <> 'PW'
|
||||
--AND o.part like 'SQL035%'
|
||||
--AND o.calc_status <> 'CANCELED'
|
||||
--AND o.fs_line = '41010'
|
||||
--AND o.dsm <> 'PW'
|
||||
--AND i.coltierd <> 'C'
|
||||
--AND i.coltier <> 'C'
|
||||
GROUP BY
|
||||
o.customer
|
||||
,substring(o.version,1,1)
|
||||
,o.chanwide
|
||||
,o.nursery_region
|
||||
,o.greenhouse_region
|
||||
,o.partgroup
|
||||
,o.majgd
|
||||
,o.assc
|
||||
,o.coltierd
|
||||
,o.dataseg
|
||||
,o.v0dataseg
|
||||
,c.greenhouse_region
|
||||
,m.part_group
|
||||
,m.majg
|
||||
,m.assc
|
||||
,i.coltier
|
||||
,'v1:' || rtrim(COALESCE(i.coltier, ''))|| '.' || rtrim(substring(COALESCE(i.branding, ''), 1, 1))|| '.' || rtrim(COALESCE(i.uomp, ''))|| '.' || rtrim(COALESCE(i.suffix, ''))|| '.' || rtrim(COALESCE(i.accs_ps, ''))
|
||||
,_ds.dataseg
|
||||
,o.oseas
|
||||
HAVING
|
||||
round(sum(o.qty),0) > 0
|
||||
@ -148,7 +96,7 @@ CREATE MATERIALIZED VIEW rlarp.price_pool AS (
|
||||
,sum(sales_usd) sales_usd
|
||||
,round(sum(sales_usd )/sum(units),5) price_usd
|
||||
,round(sum(target_price)/sum(units) FILTER (WHERE COALESCE(target_price,0) <> 0 ),5) target_price
|
||||
,jsonb_agg(DISTINCT coltierd) coltierd
|
||||
,jsonb_agg(DISTINCT coltier) coltier
|
||||
,count(DISTINCT customer) custcount
|
||||
,sum(ordcount) ordcount
|
||||
FROM
|
||||
@ -222,5 +170,5 @@ CREATE MATERIALIZED VIEW rlarp.price_pool AS (
|
||||
--LIMIT 1000
|
||||
) WITH DATA;
|
||||
|
||||
create index ppd_gset on rlarp.price_pool using gin (gset);
|
||||
--END;
|
||||
create index ppd_gset on rlarp.price_pool_dev using gin (gset);
|
||||
END;
|
@ -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, part, dataseg, version, part, qty, price, odate, ordnum, quoten,
|
||||
flag
|
||||
FROM #flagged
|
||||
CROSS APPLY (VALUES (f1), (f2), (f3), (f4), (f5), (f6)) AS f(flag)
|
||||
WHERE flag IS NOT NULL
|
||||
)
|
||||
--SELECT * FROM exploded_flags
|
||||
-- Step 3.2: Serialize each row into its JSON snippet
|
||||
-- Carry odate and version for deduplication in seg_pieces
|
||||
,serialized_flags AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
flag,
|
||||
odate,
|
||||
version,
|
||||
CONCAT(
|
||||
'"', flag, '":',
|
||||
JSON_QUERY((
|
||||
SELECT
|
||||
version,
|
||||
dataseg AS datasegment,
|
||||
part,
|
||||
qty,
|
||||
price,
|
||||
odate,
|
||||
ordnum,
|
||||
quoten,
|
||||
flag
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
))
|
||||
) AS json_piece
|
||||
FROM exploded_flags
|
||||
)
|
||||
--SELECT * FROM serialized_flags
|
||||
-- Step 3.3: Collect all global-level flags (mrs, mrq, lvs, lvq)
|
||||
,flag_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('mrs', 'mrq', 'lvs', 'lvq')
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM flag_json
|
||||
-- Step 3.4: Nest dss/dsq under each dataseg
|
||||
-- Only keep the most recent dss/dsq per dataseg/version (prevents duplicate keys)
|
||||
,seg_pieces AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
STRING_AGG(json_piece, ',') AS inner_json
|
||||
FROM (
|
||||
SELECT sf.*
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, flag
|
||||
ORDER BY odate DESC,
|
||||
CASE WHEN version = 'Actual' THEN 1 ELSE 0 END DESC
|
||||
) AS rn
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('dss', 'dsq')
|
||||
) sf
|
||||
WHERE sf.rn = 1
|
||||
) deduped
|
||||
GROUP BY customer, partgroup, dataseg
|
||||
)
|
||||
--SELECT * FROM seg_pieces
|
||||
-- Step 3.5: Wrap the inner_json under dataseg key
|
||||
,wrapped_segs AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
CONCAT(
|
||||
'"', dataseg, '": {', inner_json, '}'
|
||||
) AS json_piece
|
||||
FROM seg_pieces
|
||||
)
|
||||
-- Step 3.6: Aggregate all dataseg entries into one JSON block per customer/partgroup
|
||||
,seg_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
STRING_AGG(json_piece, ',') AS json_block
|
||||
FROM wrapped_segs
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
--SELECT * FROM seg_json
|
||||
--------------------------------------------------------------------------------
|
||||
-- Stage 4: Merge flags and segment blocks into a single JSON object
|
||||
-- Write final pricing history to pricing.lastpricedetail
|
||||
--------------------------------------------------------------------------------
|
||||
INSERT INTO
|
||||
pricing.lastpricedetail
|
||||
SELECT
|
||||
COALESCE(f.customer, s.customer) AS customer,
|
||||
COALESCE(f.partgroup, s.partgroup) AS partgroup,
|
||||
CONCAT(
|
||||
'{',
|
||||
COALESCE(f.json_block, ''),
|
||||
CASE
|
||||
WHEN f.json_block IS NOT NULL AND s.json_block IS NOT NULL THEN ','
|
||||
ELSE ''
|
||||
END,
|
||||
COALESCE(s.json_block, ''),
|
||||
'}'
|
||||
) AS part_stats
|
||||
FROM flag_json f
|
||||
FULL OUTER JOIN seg_json s
|
||||
ON f.customer = s.customer AND f.partgroup = s.partgroup;
|
@ -1,142 +0,0 @@
|
||||
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail;
|
||||
|
||||
--DROP MATERIALIZED VIEW pricequote.lastpricedetail
|
||||
|
||||
CREATE MATERIALIZED VIEW pricequote.lastpricedetail AS
|
||||
WITH base AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
part,
|
||||
dataseg,
|
||||
version,
|
||||
qtyord AS qty,
|
||||
ROUND(sales_usd/qty,5) AS price,
|
||||
odate,
|
||||
ordnum,
|
||||
quoten
|
||||
FROM rlarp.osm_stack
|
||||
WHERE
|
||||
version IN ('Actual','Quotes')
|
||||
AND customer IS NOT NULL
|
||||
AND fs_line = '41010'
|
||||
AND calc_status <> 'CANCELLED'
|
||||
AND qty <> 0
|
||||
AND partgroup <> ''
|
||||
AND version IN ('Actual', 'Quotes')
|
||||
),
|
||||
ranked AS (
|
||||
SELECT b.*,
|
||||
-- Most recent sale (Actuals first, newest date first)
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Actual') DESC,
|
||||
odate DESC NULLS LAST
|
||||
) AS rn_mrs,
|
||||
-- Most recent quote (Quotes first, newest date first)
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Quotes') DESC,
|
||||
odate DESC NULLS LAST
|
||||
) AS rn_mrq,
|
||||
-- Largest volume sale in last year (those inside window first)
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Actual' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
|
||||
qty DESC NULLS LAST
|
||||
) AS rn_lvs,
|
||||
-- Largest volume quote in last year (those inside window first)
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup
|
||||
ORDER BY (version = 'Quotes' AND odate >= CURRENT_DATE - INTERVAL '1 year') DESC,
|
||||
qty DESC NULLS LAST
|
||||
) AS rn_lvq,
|
||||
-- Per dataseg/version: most recent (version fixed in partition, so just date)
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, version
|
||||
ORDER BY odate DESC NULLS LAST
|
||||
) AS rn_dss,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer, partgroup, dataseg, version
|
||||
ORDER BY odate DESC NULLS LAST
|
||||
) AS rn_dsq
|
||||
FROM base b
|
||||
),
|
||||
flagged AS (
|
||||
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
|
||||
FROM ranked
|
||||
WHERE
|
||||
rn_mrs = 1 OR rn_mrq = 1 OR rn_lvs = 1 OR rn_lvq = 1 OR rn_dss = 1 OR rn_dsq = 1
|
||||
)
|
||||
--SELECT * FROM flagged WHERE customer = 'HYBELS' AND partgroup = 'HZP3E100'
|
||||
,exploded_flags AS (
|
||||
SELECT
|
||||
customer, partgroup, part, dataseg, version, qty, price, odate, ordnum, quoten,
|
||||
unnest(ARRAY[f1, f2, f3, f4, f5, f6]) AS flag
|
||||
FROM flagged
|
||||
),
|
||||
serialized_flags AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
flag,
|
||||
jsonb_build_object(
|
||||
'version', version,
|
||||
'datasegment', dataseg,
|
||||
'part', part,
|
||||
'qty', qty,
|
||||
'price', price,
|
||||
'odate', odate,
|
||||
'ordnum', ordnum,
|
||||
'quoten', quoten,
|
||||
'flag', flag
|
||||
) AS json_piece
|
||||
FROM exploded_flags
|
||||
WHERE flag IS NOT NULL
|
||||
),
|
||||
flag_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
jsonb_object_agg(flag, json_piece) AS global_flags
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('mrs', 'mrq', 'lvs', 'lvq')
|
||||
GROUP BY customer, partgroup
|
||||
),
|
||||
seg_pieces AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
dataseg,
|
||||
jsonb_object_agg(flag, json_piece) AS seg_flags
|
||||
FROM serialized_flags
|
||||
WHERE flag IN ('dss', 'dsq')
|
||||
GROUP BY customer, partgroup, dataseg
|
||||
),
|
||||
seg_json AS (
|
||||
SELECT
|
||||
customer,
|
||||
partgroup,
|
||||
jsonb_object_agg(dataseg, seg_flags) AS dataseg_block
|
||||
FROM seg_pieces
|
||||
GROUP BY customer, partgroup
|
||||
)
|
||||
SELECT
|
||||
COALESCE(f.customer, s.customer) AS customer,
|
||||
COALESCE(f.partgroup, s.partgroup) AS partgroup,
|
||||
(COALESCE(f.global_flags, '{}'::jsonb) || COALESCE(s.dataseg_block, '{}'::jsonb)) AS part_stats
|
||||
FROM flag_json f
|
||||
FULL OUTER JOIN seg_json s
|
||||
ON f.customer = s.customer AND f.partgroup = s.partgroup
|
||||
WITH DATA;
|
||||
|
||||
--SELECT * FROM pricequote.lastpricedetail;
|
||||
|
||||
CREATE INDEX lastpricedetail_idx ON pricequote.lastpricedetail(customer, partgroup);
|
@ -1,199 +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 ) ;
|
||||
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-----------------------------------------------------------traverse unit of measure graph-----------------------------------------------------------------------
|
||||
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
-------------setup table to hold target conversions---------------------
|
||||
|
||||
|
||||
SELECT DISTINCT
|
||||
jcpart partn
|
||||
,jcunit fu
|
||||
,'PC' tu
|
||||
,cast(null as numeric) factor
|
||||
INTO
|
||||
#anchor
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc
|
||||
WHERE
|
||||
1=1;
|
||||
|
||||
--SELECT * FROM #anchor
|
||||
|
||||
-------pre-build punit stacked on itself with the columns flipped so you can go either direction per join---------
|
||||
SELECT
|
||||
*
|
||||
INTO
|
||||
#g
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT1) IHUNT1,
|
||||
rtrim(IHUNT2) IHUNT2,
|
||||
IHCNV1 IHCNV1,
|
||||
IHCNV2 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
UNION
|
||||
SELECT
|
||||
IHPART IHPART,
|
||||
rtrim(IHUNT2) IHUNT1,
|
||||
rtrim(IHUNT1) IHUNT2,
|
||||
IHCNV2 IHCNV1,
|
||||
IHCNV1 IHCNV2
|
||||
FROM
|
||||
CMSInterfaceIN.LGDAT.PUNIT pu
|
||||
--only deal with parts in the anchor table or the &&global parts
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT partn FROM #anchor
|
||||
) items ON
|
||||
items.partn = pu.ihpart
|
||||
OR pu.ihpart = '&&GLOBAL'
|
||||
) x ;
|
||||
|
||||
CREATE INDEX g_idx on #g(ihpart,ihunt1);
|
||||
|
||||
WITH
|
||||
--------do the expansion on all paths until the target uom is matched----------------------------------------------
|
||||
--(complains about types not matching between anchor and recursion, explicitly just casting everything)
|
||||
uom (partn, partx, lvl, mastf, mastt, xf, xt, factor, xfactor, xnum, xden, id, uom_list) AS
|
||||
(
|
||||
SELECT
|
||||
cast(partn as varchar(20)) --partn
|
||||
,cast(partn as varchar(20)) --partx
|
||||
,cast(0 as int) --lvl
|
||||
,fu --mastf
|
||||
,tu --mastt
|
||||
,cast(fu as varchar(3)) --xf
|
||||
,cast(fu as varchar(3)) --xt
|
||||
,CAST(1 AS FLOAT) --factor
|
||||
,CAST(1 AS FLOAT) --xfactor
|
||||
,CAST(1 AS FLOAT) --xnum
|
||||
,CAST(1 AS FLOAT) --xden
|
||||
,format(row_number() over (ORDER BY partn),'000000')
|
||||
,cast(trim(fu) as varchar(max))
|
||||
FROM
|
||||
#anchor
|
||||
UNION ALL
|
||||
SELECT
|
||||
cast(uom.partn as varchar(20)) --partn
|
||||
,cast(ihpart as varchar(20)) --partx
|
||||
,CAST(uom.lvl + 1 AS INT) --lvl
|
||||
,uom.mastf --mastf
|
||||
,uom.mastt --mastt
|
||||
,cast(p.ihunt1 as varchar(3)) --xf
|
||||
,cast(p.ihunt2 as varchar(3)) --xt
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) --factor
|
||||
,CAST(p.ihcnv2/p.ihcnv1 AS FLOAT) * uom.xfactor --xfactor
|
||||
,p.ihcnv2 * uom.xnum --xnum
|
||||
,p.ihcnv1 * uom.xden --xden
|
||||
,uom.id + '.' + format(row_number() over (PARTITION BY uom.id ORDER BY partn),'00')
|
||||
,uom.uom_list + '.' + trim(p.ihunt2)
|
||||
FROM
|
||||
uom
|
||||
INNER JOIN #g p ON
|
||||
p.ihpart IN (uom.partn,'&&GLOBAL')
|
||||
AND p.ihunt1 = uom.xt
|
||||
WHERE
|
||||
1=1
|
||||
--AND p.ihunt2 not in ('BD','BG','BU','BX','CA','CS','PA','PL','SL','C','K','DOZ','PR')
|
||||
AND p.ihunt1 <> uom.mastt
|
||||
--prevent recursion: newest joined UOM can't be in the history
|
||||
AND charindex(p.ihunt2,uom.uom_list) = 0
|
||||
)
|
||||
--SELECT COUNT(*) FROM UOM
|
||||
--------------uom is going to have multiple rows per requested conversion, need to use row_number to pick the best row------------------------------
|
||||
,sorted AS (
|
||||
SELECT
|
||||
partn, mastf from_uom, xt to_uom, xfactor factor, lvl steps, row_number() OVER (PARTITION BY partn, mastf, mastt ORDER BY lvl ASC, factor ASC) rn
|
||||
FROM
|
||||
uom
|
||||
WHERE
|
||||
xt = mastt
|
||||
)
|
||||
SELECT * INTO #uom FROM sorted WHERE rn = 1;
|
||||
--so far so good
|
||||
|
||||
drop table #anchor;
|
||||
drop table #g;
|
||||
|
||||
TRUNCATE TABLE pricing.pricelist_ranged;
|
||||
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.factor,
|
||||
-- Normalize volume and price to PC
|
||||
p.jcvoll * u.factor AS vol_pc,
|
||||
p.jcpric / u.factor AS price_pc
|
||||
FROM
|
||||
cmsinterfacein.lgdat.iprcc p
|
||||
INNER JOIN #uom u
|
||||
ON u.partn = p.jcpart
|
||||
AND u.from_uom = p.jcunit
|
||||
AND u.to_uom = 'PC'
|
||||
),
|
||||
sorted AS (
|
||||
SELECT
|
||||
c.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY c.jcplcd, c.jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv c
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
INSERT INTO
|
||||
pricing.pricelist_ranged
|
||||
SELECT
|
||||
RTRIM(jcplcd) jcplcd,
|
||||
RTRIM(jcpart) jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price_pc AS price
|
||||
FROM
|
||||
ranged;
|
||||
|
||||
--CREATE INDEX pricelist_ranged_idx ON pricing.pricelist_ranged(jcpart, jcplcd, vb_from, vb_to);
|
@ -1,101 +0,0 @@
|
||||
DROP TABLE IF EXISTS uomc;
|
||||
|
||||
CREATE TEMP TABLE uomc AS (
|
||||
WITH
|
||||
uom AS (
|
||||
SELECT
|
||||
uom.p part
|
||||
,uom.f fu
|
||||
,uom.t tu
|
||||
,uom.nm/uom.dm conv
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
jsonb_agg(row_to_json(d)::jsonb) jdoc
|
||||
FROM
|
||||
(
|
||||
select distinct
|
||||
jcpart partn
|
||||
, jcunit fu
|
||||
, 'PC' tu
|
||||
from
|
||||
lgdat.iprcc
|
||||
WHERE
|
||||
jcpart <> ''
|
||||
) d
|
||||
) c
|
||||
JOIN LATERAL rlarp.uom_array(c.jdoc) uom ON TRUE
|
||||
)
|
||||
SELECT * FROM uom
|
||||
) WITH DATA;
|
||||
|
||||
CREATE INDEX uom_idx ON uomc (part, fu, tu);
|
||||
|
||||
|
||||
-- Clear the output table
|
||||
TRUNCATE TABLE pricequote.pricelist_ranged;
|
||||
|
||||
--DROP TABLE pricequote.pricelist_ranged;
|
||||
|
||||
-- Compute normalized volume/price and ranges
|
||||
--CREATE TABLE pricequote.pricelist_ranged AS (
|
||||
WITH
|
||||
conv AS (
|
||||
SELECT
|
||||
p.jcplcd,
|
||||
p.jcpart,
|
||||
p.jcunit,
|
||||
p.jcvoll,
|
||||
p.jcpric,
|
||||
u.conv,
|
||||
(p.jcvoll * u.conv) AS vol_pc,
|
||||
(p.jcpric / NULLIF(u.conv, 0)) AS price_pc
|
||||
FROM
|
||||
lgdat.iprcc p
|
||||
INNER JOIN uomc u
|
||||
ON u.part = p.jcpart
|
||||
AND u.fu = p.jcunit
|
||||
AND u.tu = 'PC'
|
||||
),
|
||||
--SELECT * FROM conv LIMIT 1000
|
||||
sorted AS (
|
||||
SELECT
|
||||
*,
|
||||
ROW_NUMBER() OVER (PARTITION BY jcplcd, jcpart ORDER BY vol_pc ASC) AS rn
|
||||
FROM conv
|
||||
),
|
||||
ranged AS (
|
||||
SELECT
|
||||
curr.jcplcd,
|
||||
curr.jcpart,
|
||||
curr.jcunit,
|
||||
curr.jcvoll,
|
||||
curr.jcpric,
|
||||
curr.vol_pc,
|
||||
curr.price_pc price,
|
||||
curr.vol_pc AS vb_from,
|
||||
COALESCE(next.vol_pc, 9999999.0) AS vb_to
|
||||
FROM
|
||||
sorted curr
|
||||
LEFT JOIN sorted next
|
||||
ON curr.jcplcd = next.jcplcd
|
||||
AND curr.jcpart = next.jcpart
|
||||
AND curr.rn + 1 = next.rn
|
||||
)
|
||||
--SELECT * FROM ranged
|
||||
INSERT INTO pricequote.pricelist_ranged (
|
||||
jcplcd, jcpart, jcunit, jcvoll, jcpric, vb_from, vb_to, price
|
||||
)
|
||||
SELECT
|
||||
jcplcd,
|
||||
jcpart,
|
||||
jcunit,
|
||||
jcvoll,
|
||||
jcpric,
|
||||
vb_from,
|
||||
vb_to,
|
||||
price
|
||||
FROM ranged;
|
||||
|
||||
|
||||
CREATE INDEX pricelist_ranged_idx ON pricequote.pricelist_ranged ( jcpart ASC , jcplcd ASC , vb_from ASC , vb_to ASC ) ;
|
@ -1,41 +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);
|
||||
|
||||
DELETE FROM pricing.target_prices;
|
||||
|
||||
INSERT INTO
|
||||
pricing.target_prices
|
||||
SELECT
|
||||
stlc,
|
||||
ds,
|
||||
chan,
|
||||
tier,
|
||||
vol,
|
||||
-- Extract lower bound: text between '[' and ','
|
||||
TRY_CAST(SUBSTRING(vol, 2, CHARINDEX(',', vol) - 2) AS INT) AS lower_bound,
|
||||
-- Extract upper bound: text between ',' and ')'
|
||||
CASE
|
||||
WHEN RIGHT(vol, 2) = ',)' THEN NULL
|
||||
ELSE TRY_CAST(SUBSTRING(vol, CHARINDEX(',', vol) + 1, LEN(vol) - CHARINDEX(',', vol) - 1) AS INT)
|
||||
END AS upper_bound,
|
||||
price,
|
||||
math
|
||||
FROM
|
||||
usmidsap02.ubm.pricequote.target_prices_view;
|
||||
|
||||
--SELECT COUNT(*) FROM pricing.target_prices
|
@ -1,14 +0,0 @@
|
||||
DROP TABLE pricequote.target_prices CASCADE;
|
||||
|
||||
CREATE TABLE pricequote.target_prices (
|
||||
stlc TEXT NOT NULL,
|
||||
ds TEXT NOT NULL,
|
||||
chan TEXT NOT NULL,
|
||||
tier TEXT NOT NULL,
|
||||
vol INT4RANGE NOT NULL,
|
||||
price NUMERIC,
|
||||
math TEXT[],
|
||||
PRIMARY KEY (stlc, ds, chan, tier, vol)
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON pricequote.target_prices TO PUBLIC;
|
@ -1,11 +0,0 @@
|
||||
CREATE or REPLACE VIEW pricequote.target_prices_view AS
|
||||
SELECT
|
||||
stlc
|
||||
,ds
|
||||
,chan
|
||||
,tier
|
||||
,vol::text vol
|
||||
,price
|
||||
,to_jsonb(math)::text AS math
|
||||
FROM
|
||||
pricequote.target_prices;
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user