Compare commits

...

6 Commits

4 changed files with 101 additions and 102 deletions

2
api.ts
View File

@ -52,7 +52,7 @@ router.get('/dseg_price/:billcode/:shipcode/:stlc/:dseg/:qty', async (ctx) => {
const { billcode, shipcode, stlc, dseg, qty } = ctx.params; const { billcode, shipcode, stlc, dseg, qty } = ctx.params;
const result = await client.queryObject({args: [billcode, shipcode, stlc, dseg, qty], text: query_dseg} ); const result = await client.queryObject({args: [billcode, shipcode, stlc, dseg, qty], text: query_dseg} );
const ag = apply_guidance(result.rows[0]["doc"]); const ag = apply_guidance(result.rows[0]["doc"]);
ctx.response.body = ag.guidance.FinalReason; ctx.response.body = ag.guidance.finalPrice.Snapped;
}); });
app.use(router.routes()); app.use(router.routes());

View File

@ -14,110 +14,109 @@ export function apply_guidance(doc: any) {
return match ? match.adj : null; return match ? match.adj : null;
} }
//let custPrice null; function lowestPrice(priceObject) {
//let custReason null; let Price = Infinity;
//let cvolPrice null; let Reason = '';
//let cvolReason null; let Source = '';
//let markPrice null; let Snapped = Infinity;
//let markReason null;
//let targPrice null;
//let targReason null;
//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 targetPrice = doc.pricing?.v1tp ?? doc.pricing?.v0tp;
const priceBand = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv; const priceBand = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv;
const earlyPrice = doc.hist?.cust?.early_price; const earlyCustPrice = doc.hist?.cust?.early_price;
const earlySeason = doc.hist?.cust?.early_season; const earlyCustSeason = doc.hist?.cust?.early_season;
const bridgePremium = doc.pricing?.bridgePremium ?? 1.00000; 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 altHist = doc.hist?.cust?.ds;
const iidx = doc.pricing?.iidx; const iidx = doc.pricing?.iidx;
const curr = doc.customer?.curr; const curr = doc.customer?.curr;
const fxrate = doc.customer?.fxrate ?? 1.0; const fxrate = doc.customer?.fxrate ?? 1.0;
const qty = doc.inputs?.qty; const qty = doc.inputs?.qty;
const pltq = doc.product?.pltq; 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 inflation = Math.max(...Object.keys(iidx).map(Number));
const inflationFactor = iidx[inflation] + 1; const inflationFactor = iidx[inflation];
const list = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null; const list = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null;
const listUSD = list ? list * fxrate :null;
//-------set basic customer pricing-------------- // ------------------calculate price adders------------------------------------------------------
custPrice = Number((earlyPrice * bridgePremium).toFixed(5)); let ltp = qty < pltq ? 0.15 : null;
anchorPrice = custPrice; let anchor_sd = priceBand ? ((bridgedPrice - targetPrice) / priceBand).toFixed(2) : 0
let anchor_sd = priceBand ? ((anchorPrice - targetPrice) / priceBand).toFixed(2) : 0
let optimization = getAdjValue(anchor_sd); 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}`;
// ------------------start building price options------------------------------------------------
// ------if there is not target price just exit--------------- let snap = .0005;
if (!targetPrice) {
anchorSource = "No target pricing setup"; let custPrice = r5(bridgedPrice * (1 + custAdder));
guidance.FinalReason = "No target pricing setup"; let custSeason = earlyCustSeason;
} else { let custReason = bridgePremium
// if there is no customer anchor price use target ? `${custSeason} (similar ${altHist} price ${pp(earlyCustPrice)} x ${bridgePremium} = ${pp(bridgedPrice)})${custAddReason}`
if (earlyPrice) { : `${custSeason} price ${pp(bridgedPrice)}${custAddReason}`;
// translate alternate product history to current product quoted let markPrice = r5(earlyMarkPrice * (1 + markAdder));
// --------if the price needs bridged, add the details to the description-------- let markReason = `${earlyMarkSeason} ASP ${pp(earlyMarkPrice)}${markAddReason}`;
if (bridgePremium === 1) { let targPrice = targetPrice ? r5(targetPrice * (1 + markAdder)) : null;
anchorSource = earlySeason + ' Price ' + earlyPrice; let targReason = `Target price ${pp(targetPrice)}${markAddReason}`;
custSource = anchorSource; let listPrice = listUSD;
} else { let listReason = fxrate === 1 ? "" : `list ${pp(list)} CAD ${pp(listUSD)} USD`
anchorSource = earlySeason + ' Similar (' + altHist + ') Price ' + earlyPrice + ' x ' + bridgePremium + ' = ' + anchorPrice;
custSource = anchorSource; let prices = {
cust: [custPrice, custReason, "cust", ceiling(custPrice,snap)],
mark: [markPrice, markReason, "mark", ceiling(markPrice,snap)],
targ: [targPrice, targReason, "targ", ceiling(targPrice,snap)],
list: [listPrice, listReason, "list", ceiling(listPrice,snap)]
} }
// --------after the early price is translated see if target is still less-------
if (targetPrice < anchorPrice) { let finalPrice = lowestPrice(prices);
anchorSource = `Target Price ${targetPrice}`; let guidance = {
anchorPrice = targetPrice; prices
,finalPrice
} }
} 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; doc.guidance = guidance;
return doc; return doc;
} }

View File

@ -110,7 +110,7 @@ FROM
,('customer v0ds other',7) ,('customer v0ds other',7)
,('customer v0ds vol' ,3) ,('customer v0ds vol' ,3)
,('customer v1ds other',6) ,('customer v1ds other',6)
,('customer v1ds vol' ,2) ,('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 exact' ,4)
,('market v0ds other' ,9) ,('market v0ds other' ,9)
,('market v0ds vol' ,5) ,('market v0ds vol' ,5)

View File

@ -104,7 +104,7 @@ FROM
,('customer v0ds other',7) ,('customer v0ds other',7)
,('customer v0ds vol' ,3) ,('customer v0ds vol' ,3)
,('customer v1ds other',6) ,('customer v1ds other',6)
,('customer v1ds vol' ,2) ,('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 exact' ,4)
,('market v0ds other' ,9) ,('market v0ds other' ,9)
,('market v0ds vol' ,5) ,('market v0ds vol' ,5)