Procházet zdrojové kódy

Fix group plan localized pricing and force 2 decimal number for non-integer currency value (#8576)

GitOrigin-RevId: d6dedf7af04c79cef6b9cd39f5496610ffc52938
M Fahru před 4 roky
rodič
revize
e97c56b105

+ 10 - 11
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'

+ 48 - 0
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}`,
+      }
+    }
+  }
+}

+ 34 - 51
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 {
-    priceInUnit: priceInUnitProfessional,
-    perUserPrice: perUserPriceProfessional,
-  } = calculatePrice('professional')
-  let displayPriceProfessional = `${currencySymbol}${priceInUnitProfessional}`
-  let displayPerUserPriceProfessional = `${currencySymbol}${perUserPriceProfessional}`
+    localizedPrice: localizedPriceProfessional,
+    localizedPerUserPrice: localizedPerUserPriceProfessional,
+  } = createLocalizedGroupPlanPrice({
+    plan: 'professional',
+    licenseSize,
+    currency,
+    usage,
+  })
 
   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
+  ).innerText = localizedPriceProfessional
+
   document.querySelector(
-    '[data-ol-plans-v2-group-total-price="collaborator"]'
-  ).innerText = displayPriceCollaborator
+    '[data-ol-plans-v2-group-price-per-user="professional"]'
+  ).innerText = localizedPerUserPriceProfessional
 
   document.querySelector(
-    '[data-ol-plans-v2-group-price-per-user="collaborator"]'
-  ).innerText = displayPerUserPriceCollaborator
+    '[data-ol-plans-v2-group-total-price="collaborator"]'
+  ).innerText = localizedPriceCollaborator
 
   document.querySelector(
-    '[data-ol-plans-v2-group-price-per-user="professional"]'
-  ).innerText = displayPerUserPriceProfessional
+    '[data-ol-plans-v2-group-price-per-user="collaborator"]'
+  ).innerText = localizedPerUserPriceCollaborator
 
-  // educational discount can only be activated if numberOfLicenses is >= 10
-  const notEligibleForDiscount =
-    numberOfLicenses < MINIMUM_NUMBER_OF_LICENSES_EDUCATIONAL_DISCOUNT
+  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]'

+ 86 - 0
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',
+        })
+      })
+    })
+  })
+})