diff --git a/services/web/frontend/js/features/plans/group-plan-modal/index.js b/services/web/frontend/js/features/plans/group-plan-modal/index.js index ec4cea4f50..5cb85de394 100644 --- a/services/web/frontend/js/features/plans/group-plan-modal/index.js +++ b/services/web/frontend/js/features/plans/group-plan-modal/index.js @@ -1,6 +1,7 @@ import getMeta from '../../../utils/meta' import { swapModal } from '../../utils/swapModal' import * as eventTracking from '../../../infrastructure/event-tracking' +import { createLocalizedGroupPlanPrice } from '../utils/group-plan-pricing' function getFormValues() { const modalEl = document.querySelector('[data-ol-group-plan-modal]') @@ -16,18 +17,16 @@ function getFormValues() { } export function updateGroupModalPlanPricing() { - const groupPlans = getMeta('ol-groupPlans') - const currencySymbols = getMeta('ol-currencySymbols') - const modalEl = document.querySelector('[data-ol-group-plan-modal]') const { planCode, size, currency, usage } = getFormValues() - const priceInCents = - groupPlans[usage][planCode][currency][size].price_in_cents - const priceInUnit = (priceInCents / 100).toFixed() - const currencySymbol = currencySymbols[currency] - const displayPrice = `${currencySymbol}${priceInUnit}` - const perUserPrice = parseFloat((priceInCents / 100 / size).toFixed(2)) + const { localizedPrice, localizedPerUserPrice } = + createLocalizedGroupPlanPrice({ + plan: planCode, + licenseSize: size, + currency, + usage, + }) modalEl.querySelectorAll('[data-ol-group-plan-plan-code]').forEach(el => { el.hidden = el.getAttribute('data-ol-group-plan-plan-code') !== planCode @@ -36,10 +35,10 @@ export function updateGroupModalPlanPricing() { el.hidden = el.getAttribute('data-ol-group-plan-usage') !== usage }) modalEl.querySelector('[data-ol-group-plan-display-price]').innerText = - displayPrice + localizedPrice modalEl.querySelector( '[data-ol-group-plan-price-per-user]' - ).innerText = `${currencySymbol}${perUserPrice} per user` + ).innerText = `${localizedPerUserPrice} per user` modalEl.querySelector('[data-ol-group-plan-educational-discount]').hidden = usage !== 'educational' diff --git a/services/web/frontend/js/features/plans/utils/group-plan-pricing.js b/services/web/frontend/js/features/plans/utils/group-plan-pricing.js new file mode 100644 index 0000000000..a78b775a11 --- /dev/null +++ b/services/web/frontend/js/features/plans/utils/group-plan-pricing.js @@ -0,0 +1,48 @@ +import getMeta from '../../../utils/meta' + +// plan: 'collaborator' or 'professional' +// the rest of available arguments can be seen in the groupPlans value +export function createLocalizedGroupPlanPrice({ + plan, + licenseSize, + currency, + usage, +}) { + const groupPlans = getMeta('ol-groupPlans') + const currencySymbols = getMeta('ol-currencySymbols') + const priceInCents = + groupPlans[usage][plan][currency][licenseSize].price_in_cents + + const price = priceInCents / 100 + const perUserPrice = price / parseInt(licenseSize) + + const strPrice = price.toFixed() + let strPerUserPrice = '' + + if (Number.isInteger(perUserPrice)) { + strPerUserPrice = String(perUserPrice) + } else { + strPerUserPrice = perUserPrice.toFixed(2) + } + + const currencySymbol = currencySymbols[currency] + + switch (currencySymbol) { + case 'Fr': + return { + localizedPrice: `${currencySymbol} ${strPrice}`, + localizedPerUserPrice: `${currencySymbol} ${strPerUserPrice}`, + } + case 'kr': + return { + localizedPrice: `${strPrice} ${currencySymbol}`, + localizedPerUserPrice: `${strPerUserPrice} ${currencySymbol}`, + } + default: { + return { + localizedPrice: `${currencySymbol}${strPrice}`, + localizedPerUserPrice: `${currencySymbol}${strPerUserPrice}`, + } + } + } +} diff --git a/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-group-plan.js b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-group-plan.js index c836eaaa67..54b899034e 100644 --- a/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-group-plan.js +++ b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-group-plan.js @@ -1,93 +1,76 @@ import { updateGroupModalPlanPricing } from '../../../../features/plans/group-plan-modal' import '../../../../features/plans/plans-v2-group-plan-modal' +import { createLocalizedGroupPlanPrice } from '../../../../features/plans/utils/group-plan-pricing' import getMeta from '../../../../utils/meta' -const MINIMUM_NUMBER_OF_LICENSES_EDUCATIONAL_DISCOUNT = 10 +const MINIMUM_LICENSE_SIZE_EDUCATIONAL_DISCOUNT = 10 export function updateMainGroupPlanPricing() { - const groupPlans = getMeta('ol-groupPlans') - const currencySymbols = getMeta('ol-currencySymbols') - const currentCurrencyCode = getMeta('ol-recommendedCurrency') + const currency = getMeta('ol-recommendedCurrency') const formEl = document.querySelector( '[data-ol-plans-v2-license-picker-form]' ) - const numberOfLicenses = formEl.querySelector( + const licenseSize = formEl.querySelector( '[data-ol-plans-v2-license-picker-select]' ).value - const currency = currentCurrencyCode - const currencySymbol = currencySymbols[currency] + const usage = formEl.querySelector( '[data-ol-plans-v2-license-picker-educational-discount-input]' ).checked ? 'educational' : 'enterprise' - function calculatePrice(plan) { - const priceInCents = - groupPlans[usage][plan][currency][numberOfLicenses].price_in_cents - const priceInUnit = (priceInCents / 100).toFixed() - const perUserPrice = (priceInCents / 100 / numberOfLicenses).toFixed(2) - - return { priceInUnit, perUserPrice } - } + const { + localizedPrice: localizedPriceProfessional, + localizedPerUserPrice: localizedPerUserPriceProfessional, + } = createLocalizedGroupPlanPrice({ + plan: 'professional', + licenseSize, + currency, + usage, + }) const { - priceInUnit: priceInUnitProfessional, - perUserPrice: perUserPriceProfessional, - } = calculatePrice('professional') - let displayPriceProfessional = `${currencySymbol}${priceInUnitProfessional}` - let displayPerUserPriceProfessional = `${currencySymbol}${perUserPriceProfessional}` - - const { - priceInUnit: priceInUnitCollaborator, - perUserPrice: perUserPriceCollaborator, - } = calculatePrice('collaborator') - let displayPriceCollaborator = `${currencySymbol}${priceInUnitCollaborator}` - let displayPerUserPriceCollaborator = `${currencySymbol}${perUserPriceCollaborator}` - - if (currencySymbol === 'kr') { - displayPriceProfessional = `${priceInUnitProfessional} ${currencySymbol}` - displayPerUserPriceProfessional = `${perUserPriceProfessional} ${currencySymbol}` - displayPriceCollaborator = `${priceInUnitCollaborator} ${currencySymbol}` - displayPerUserPriceCollaborator = `${perUserPriceCollaborator} ${currencySymbol}` - } else if (currencySymbol === 'Fr') { - displayPriceProfessional = `${currencySymbol} ${priceInUnitProfessional}` - displayPerUserPriceProfessional = `${currencySymbol} ${perUserPriceProfessional}` - displayPriceCollaborator = `${currencySymbol} ${priceInUnitCollaborator}` - displayPerUserPriceCollaborator = `${currencySymbol} ${perUserPriceCollaborator}` - } + localizedPrice: localizedPriceCollaborator, + localizedPerUserPrice: localizedPerUserPriceCollaborator, + } = createLocalizedGroupPlanPrice({ + plan: 'collaborator', + licenseSize, + currency, + usage, + }) document.querySelector( '[data-ol-plans-v2-group-total-price="professional"]' - ).innerText = displayPriceProfessional - document.querySelector( - '[data-ol-plans-v2-group-total-price="collaborator"]' - ).innerText = displayPriceCollaborator - - document.querySelector( - '[data-ol-plans-v2-group-price-per-user="collaborator"]' - ).innerText = displayPerUserPriceCollaborator + ).innerText = localizedPriceProfessional document.querySelector( '[data-ol-plans-v2-group-price-per-user="professional"]' - ).innerText = displayPerUserPriceProfessional + ).innerText = localizedPerUserPriceProfessional - // educational discount can only be activated if numberOfLicenses is >= 10 - const notEligibleForDiscount = - numberOfLicenses < MINIMUM_NUMBER_OF_LICENSES_EDUCATIONAL_DISCOUNT + document.querySelector( + '[data-ol-plans-v2-group-total-price="collaborator"]' + ).innerText = localizedPriceCollaborator + + document.querySelector( + '[data-ol-plans-v2-group-price-per-user="collaborator"]' + ).innerText = localizedPerUserPriceCollaborator + + const notEligibleForEducationalDiscount = + licenseSize < MINIMUM_LICENSE_SIZE_EDUCATIONAL_DISCOUNT formEl .querySelector( '[data-ol-plans-v2-license-picker-educational-discount-label]' ) - .classList.toggle('disabled', notEligibleForDiscount) + .classList.toggle('disabled', notEligibleForEducationalDiscount) formEl.querySelector( '[data-ol-plans-v2-license-picker-educational-discount-input]' - ).disabled = notEligibleForDiscount + ).disabled = notEligibleForEducationalDiscount - if (notEligibleForDiscount) { + if (notEligibleForEducationalDiscount) { // force disable educational discount checkbox formEl.querySelector( '[data-ol-plans-v2-license-picker-educational-discount-input]' diff --git a/services/web/test/frontend/shared/utils/group-plan-pricing.test.js b/services/web/test/frontend/shared/utils/group-plan-pricing.test.js new file mode 100644 index 0000000000..4d00006b63 --- /dev/null +++ b/services/web/test/frontend/shared/utils/group-plan-pricing.test.js @@ -0,0 +1,86 @@ +import { expect } from 'chai' +import { createLocalizedGroupPlanPrice } from '../../../../frontend/js/features/plans/utils/group-plan-pricing' + +describe('group-plan-pricing', function () { + beforeEach(function () { + window.metaAttributesCache = window.metaAttributesCache || new Map() + window.metaAttributesCache.set('ol-groupPlans', { + enterprise: { + professional: { + CHF: { + 2: { + price_in_cents: 10000, + }, + }, + DKK: { + 2: { + price_in_cents: 20000, + }, + }, + USD: { + 2: { + price_in_cents: 30000, + }, + }, + }, + }, + }) + window.metaAttributesCache.set('ol-currencySymbols', { + CHF: 'Fr', + DKK: 'kr', + USD: '$', + }) + }) + + afterEach(function () { + window.metaAttributesCache = new Map() + }) + + describe('createLocalizedGroupPlanPrice', function () { + describe('CHF currency', function () { + it('should return the correct localized price', function () { + const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({ + plan: 'professional', + currency: 'CHF', + licenseSize: '2', + usage: 'enterprise', + }) + + expect(localizedGroupPlanPrice).to.deep.equal({ + localizedPrice: 'Fr 100', + localizedPerUserPrice: 'Fr 50', + }) + }) + }) + describe('DKK currency', function () { + it('should return the correct localized price', function () { + const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({ + plan: 'professional', + currency: 'DKK', + licenseSize: '2', + usage: 'enterprise', + }) + + expect(localizedGroupPlanPrice).to.deep.equal({ + localizedPrice: '200 kr', + localizedPerUserPrice: '100 kr', + }) + }) + }) + describe('other supported currencies', function () { + it('should return the correct localized price', function () { + const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({ + plan: 'professional', + currency: 'USD', + licenseSize: '2', + usage: 'enterprise', + }) + + expect(localizedGroupPlanPrice).to.deep.equal({ + localizedPrice: '$300', + localizedPerUserPrice: '$150', + }) + }) + }) + }) +})