Massive AI Overhaul
This commit is contained in:
125
quote-pricing.js
Normal file
125
quote-pricing.js
Normal file
@@ -0,0 +1,125 @@
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
const DEFAULTS = {
|
||||
RATE_M365: 130,
|
||||
RATE_M365_M2M: 140,
|
||||
RATE_BYOL: 110,
|
||||
M365_RETAIL_MONTHLY: 35,
|
||||
M365_RETAIL_ANNUAL: 30,
|
||||
ADDON_EXT_HOURS: 25,
|
||||
ADDON_1PASSWORD: 9,
|
||||
ADDON_INKY: 8,
|
||||
ADDON_ZERO_TRUST_USER: 55,
|
||||
RATE_ENDPOINT: 35,
|
||||
RATE_SERVER: 120,
|
||||
ADDON_USB_BLOCKING: 4,
|
||||
ADDON_BARE_METAL_BACKUP: 25,
|
||||
ZT_SEAT_RATE: 25,
|
||||
ZT_ROUTER_RATE: 100,
|
||||
ADMIN_FEE_FLOOR: 150,
|
||||
ADMIN_FEE_MINIMUM: 650,
|
||||
ADMIN_FEE_ZT: 250,
|
||||
ADMIN_1PWM_PCT: 0.10,
|
||||
VOIP_RATE_BASIC: 28,
|
||||
VOIP_RATE_STANDARD: 35,
|
||||
VOIP_RATE_PREMIUM: 45,
|
||||
VOIP_PHONE_RATE: 15,
|
||||
VOIP_FAX_RATE: 10,
|
||||
TOOL_COST_PER_USER: 42,
|
||||
TOOL_COST_PER_ENDPOINT: 23,
|
||||
TOOL_COST_MIN: 650,
|
||||
IT_SALARY_1: 85000,
|
||||
IT_SALARY_5: 420000,
|
||||
DISCOUNT_M2M: 0,
|
||||
DISCOUNT_12MO: 0.03,
|
||||
DISCOUNT_24MO: 0.05,
|
||||
HST_RATE: 0.13
|
||||
};
|
||||
|
||||
const KNOWN_KEYS = Object.keys(DEFAULTS);
|
||||
let pricingFallbackShown = false;
|
||||
|
||||
function applyValues(values) {
|
||||
KNOWN_KEYS.forEach(key => {
|
||||
global[key] = values[key];
|
||||
});
|
||||
}
|
||||
|
||||
function getSnapshot() {
|
||||
return KNOWN_KEYS.reduce((snapshot, key) => {
|
||||
snapshot[key] = global[key];
|
||||
return snapshot;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function showPricingStatus(message) {
|
||||
const host = document.querySelector('.top-bar-right');
|
||||
if (!host) return;
|
||||
let el = document.getElementById('pricingStatus');
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = 'pricingStatus';
|
||||
el.style.marginTop = '6px';
|
||||
el.style.fontSize = '11px';
|
||||
el.style.letterSpacing = '0.02em';
|
||||
el.style.color = 'var(--amber)';
|
||||
host.appendChild(el);
|
||||
}
|
||||
el.textContent = message;
|
||||
}
|
||||
|
||||
function reportPricingFallback(reason) {
|
||||
if (pricingFallbackShown) return;
|
||||
pricingFallbackShown = true;
|
||||
console.warn(`[SVS Quote] ${reason} Using built-in pricing defaults.`);
|
||||
showPricingStatus('Pricing file unavailable - using built-in defaults');
|
||||
}
|
||||
|
||||
function applyPricingData(data) {
|
||||
let applied = 0;
|
||||
Object.keys(data).forEach(category => {
|
||||
const group = data[category];
|
||||
if (typeof group !== 'object' || group === null) return;
|
||||
Object.keys(group).forEach(key => {
|
||||
const entry = group[key];
|
||||
if (typeof entry !== 'object' || entry === null) return;
|
||||
const val = parseFloat(entry.value);
|
||||
if (!KNOWN_KEYS.includes(key) || Number.isNaN(val)) return;
|
||||
global[key] = val;
|
||||
applied++;
|
||||
});
|
||||
});
|
||||
return applied;
|
||||
}
|
||||
|
||||
async function loadPricing() {
|
||||
pricingFallbackShown = false;
|
||||
applyValues(DEFAULTS);
|
||||
|
||||
// Pricing loaded via <script src="package-prices-data.js"> which sets window.SVS_PRICING_DATA
|
||||
if (global.SVS_PRICING_DATA && typeof global.SVS_PRICING_DATA === 'object') {
|
||||
const applied = applyPricingData(global.SVS_PRICING_DATA);
|
||||
if (applied > 0) return true;
|
||||
}
|
||||
|
||||
reportPricingFallback('package-prices-data.js not loaded.');
|
||||
return false;
|
||||
}
|
||||
|
||||
applyValues(DEFAULTS);
|
||||
|
||||
global.SVSQuotePricing = {
|
||||
DEFAULTS: Object.freeze({ ...DEFAULTS }),
|
||||
KNOWN_KEYS: Object.freeze([...KNOWN_KEYS]),
|
||||
getSnapshot,
|
||||
loadPricing,
|
||||
reportPricingFallback,
|
||||
showPricingStatus
|
||||
};
|
||||
|
||||
// Backward-compatible globals for the current calculator runtime.
|
||||
global.loadPricing = loadPricing;
|
||||
global.reportPricingFallback = reportPricingFallback;
|
||||
global.showPricingStatus = showPricingStatus;
|
||||
})(window);
|
||||
Reference in New Issue
Block a user