Avant Pricing Calculator

Choose Your Organization Type

Education Discounts are given to private and public K-12 and Higher Education institutions. Testing for teacher certification or candidate skill verification does not qualify for education discounts.

Test Type
Qty
Unit Price
Subtotal
Non-Discounted Total: $0.00
You Save: $0.00
Total: $0.00
Demander un devis

Date: | Org Type: This calculator is for pricing estimates only. Prices are subject to change without notice.

$0.00
$0.00
`; section.appendChild(row); }); rowsWrap.appendChild(section); }); } const PRODUCT_LOOKUP = {}; PRODUCT_GROUPS.forEach(group => { group.items.forEach(item => { PRODUCT_LOOKUP[item.id] = item; }); }); const markersWrap = calc.querySelector(".pc-progress-markers"); if (markersWrap) { markersWrap.innerHTML = ""; tierBreaks.forEach(t => { const line = document.createElement("div"); line.className = "pc-tier-marker"; markersWrap.appendChild(line); const label = document.createElement("div"); label.className = "pc-tier-marker-label"; label.textContent = t.label; if (tierColors[t.min]) { label.style.setProperty("--pc-tier-color", tierColors[t.min]); } markersWrap.appendChild(label); }); } renderProducts(); let rows = document.querySelectorAll(".pc-row"); const quoteBtn = document.getElementById("requestQuoteDynamic"); function formatMoney(amount) { const [whole, decimal] = amount .toLocaleString("en-US", { minimumFractionDigits: 2 }) .split("."); return `$${whole}.${decimal}`; } function formatStrike(b,u){ return b===u?formatMoney(u): `${formatMoney(b)} ${formatMoney(u)}`; } function updateDiscountState(){ calc.classList.toggle("discountAllowed",discountsEnabled); calc.classList.toggle("discountNone",!discountsEnabled); } function syncHiddenQty() { rows.forEach(row => { const input = row.querySelector(".pc-qty input"); if (!input) return; const hidden = getComputedStyle(row).pointerEvents === "none"; // uses your CSS state const current = +input.value || 0; if (hidden) { // save qty once, then clear it so totals drop immediately if (current > 0) row.dataset.savedQty = String(current); input.value = ""; } else { // if coming back visible, restore previous qty (if any) const saved = +row.dataset.savedQty || 0; if (saved > 0 && current === 0) { input.value = saved; } } }); } // Make 0 blank in number inputs when using arrow buttons function normalizeQtyInput(input) { const value = +input.value || 0; if (value <= 0) { input.value = ""; } else { input.value = value; // removes accidental decimals like 1.000 } } function updateTotals(){ let totalQty=0,grand=0,baseline=0; rows.forEach(r => { if (getComputedStyle(r).pointerEvents === "none") return; totalQty += +r.querySelector(".pc-qty input").value || 0; }); // ✅ Update total test count in footer const totalEl = document.getElementById("pc-footer-total"); if (totalEl) { totalEl.textContent = totalQty; } const scaleMax = Math.max(500, totalQty || 1); calc.style.setProperty("--pc-scale-max", scaleMax); const lines = calc.querySelectorAll(".pc-tier-marker"); const labels = calc.querySelectorAll(".pc-tier-marker-label"); tierBreaks.forEach((t, i) => { const pct = Math.min((t.min / scaleMax) * 100, 100); if (lines[i]) lines[i].style.left = pct + "%"; if (labels[i]) labels[i].style.left = pct + "%"; }); let tier = discountsEnabled ? tiers.find(t=>totalQty>=t.min)||tiers.at(-1) : {key:1,label:"Standard"}; /* --- Progress bar percentage --- */ let progress = 0; if (discountsEnabled && totalQty > 0) { const nextTier = tiers.find(t => t.min > totalQty); progress = nextTier ? (totalQty / nextTier.min) * 100 : 100; } progress = Math.min(100, progress); calc.style.setProperty("--pc-progress", progress + "%"); calc.style.setProperty("--pc-tier-color", discountsEnabled?tierColors[tier.key]:"#999"); rows.forEach(row => { if (getComputedStyle(row).pointerEvents === "none") return; const id = row.dataset.test; const qty = +row.querySelector(".pc-qty input").value || 0; const item = PRODUCT_LOOKUP[id]; if (!item || !item.pricing) return; if (qty > 0) { row.classList.add("selectedRows"); } else { row.classList.remove("selectedRows"); } // Resolve pricing tiers (preset or explicit map) const tierMap = typeof item.pricing === "string" ? PRICING_PRESETS[item.pricing] : item.pricing; if (!tierMap) return; // Base price is always tier 1 const base = tierMap[1]; // Determine active tier key let activeTierKey = 1; if (discountsEnabled) { Object.keys(tierMap) .map(Number) .sort((a, b) => a - b) .forEach(key => { if (totalQty >= key) activeTierKey = key; }); } // Discounted unit price const unit = tierMap[activeTierKey] ?? base; row.querySelector(".pc-unit").innerHTML=formatStrike(base,unit); row.querySelector(".pc-subtotal").innerHTML=formatStrike(base*qty,unit*qty); grand+=unit*qty; baseline+=base*qty; }); document.querySelectorAll("#pc-footer-grand") .forEach(e=>e.textContent=formatMoney(grand)); document.querySelectorAll(".pc-savings") .forEach(e=>e.textContent=formatMoney(baseline-grand)); document.getElementById("pc-undiscountedTotal").textContent=formatMoney(baseline); updateQuoteLink(); } function updateQuoteLink() { const products = new Set(); const tests = []; rows.forEach(r => { const q = +r.querySelector(".pc-qty input").value || 0; if (!q) return; const id = r.dataset.test; const item = PRODUCT_LOOKUP[id]; if (!item) return; // Collect product param if (item.productParam) { products.add(item.productParam); } // Collect test param (no qty anymore) if (item.testParam) { tests.push(item.testParam); } }); // Nothing selected if (!products.size && !tests.length) { quoteBtn.href = "/request-quote"; quoteBtn.classList.add("disabled"); return; } quoteBtn.classList.remove("disabled"); // Build query params const params = new URLSearchParams(); if (products.size) { params.set("product", [...products].join(",")); } if (tests.length) { params.set("test", tests.join(",")); } if (selectedAudience) { params.set("audience", selectedAudience); } quoteBtn.href = `/request-quote?${params.toString()}`; } document.querySelectorAll('input[name="pc-audience"]').forEach(r=>{ r.addEventListener("change",e=>{ selectedAudience = e.target.value; const orgLabel = document.querySelector(".currentOrgType"); if (orgLabel) orgLabel.textContent = selectedAudience; // ✅ Enable CSS filtering rules (reuses Request Quote CSS) calc.setAttribute("orgtype", selectedAudience); discountsEnabled = DISCOUNT_AUDIENCES.includes(selectedAudience); calc.classList.remove("needsAudience"); syncHiddenQty(); updateDiscountState(); updateTotals(); }); }); rows.forEach(r => { const input = r.querySelector(".pc-qty input"); input.addEventListener("input", e => { normalizeQtyInput(e.target); updateTotals(); }); input.addEventListener("change", e => { normalizeQtyInput(e.target); }); }); updateDiscountState(); updateTotals();
Mise à jour :
Janvier