mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-03 22:29:01 +02:00
Merge pull request #23395 from overleaf/ls-display-discount-in-cost-summary
Display discount information in cost summary GitOrigin-RevId: 95ff56b21b15e55860968e8ce4519c897b85ebba
This commit is contained in:
@@ -366,6 +366,8 @@ function computeImmediateCharge(subscriptionChange) {
|
||||
subscriptionChange.invoiceCollection?.chargeInvoice?.subtotal ?? 0
|
||||
let tax = subscriptionChange.invoiceCollection?.chargeInvoice?.tax ?? 0
|
||||
let total = subscriptionChange.invoiceCollection?.chargeInvoice?.total ?? 0
|
||||
let discount =
|
||||
subscriptionChange.invoiceCollection?.chargeInvoice?.discount ?? 0
|
||||
for (const creditInvoice of subscriptionChange.invoiceCollection
|
||||
?.creditInvoices ?? []) {
|
||||
// The credit invoice numbers are already negative
|
||||
@@ -373,12 +375,13 @@ function computeImmediateCharge(subscriptionChange) {
|
||||
total = roundToTwoDecimal(total + (creditInvoice.total ?? 0))
|
||||
// Tax rate can be different in credit invoice if a user relocates
|
||||
tax = roundToTwoDecimal(tax + (creditInvoice.tax ?? 0))
|
||||
discount = roundToTwoDecimal(discount + (creditInvoice.discount ?? 0))
|
||||
}
|
||||
|
||||
return new RecurlyImmediateCharge({
|
||||
subtotal,
|
||||
total,
|
||||
tax,
|
||||
discount,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ class RecurlySubscriptionChange {
|
||||
this.nextAddOns = props.nextAddOns
|
||||
this.immediateCharge =
|
||||
props.immediateCharge ??
|
||||
new RecurlyImmediateCharge({ subtotal: 0, tax: 0, total: 0 })
|
||||
new RecurlyImmediateCharge({ subtotal: 0, tax: 0, total: 0, discount: 0 })
|
||||
|
||||
this.subtotal = this.nextPlanPrice
|
||||
for (const addOn of this.nextAddOns) {
|
||||
@@ -386,11 +386,13 @@ class RecurlyImmediateCharge {
|
||||
* @param {number} props.subtotal
|
||||
* @param {number} props.tax
|
||||
* @param {number} props.total
|
||||
* @param {number} props.discount
|
||||
*/
|
||||
constructor(props) {
|
||||
this.subtotal = props.subtotal
|
||||
this.tax = props.tax
|
||||
this.total = props.total
|
||||
this.discount = props.discount
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -387,6 +387,7 @@
|
||||
"disable_stop_on_first_error": "",
|
||||
"disabling": "",
|
||||
"disconnected": "",
|
||||
"discount": "",
|
||||
"discount_of": "",
|
||||
"dismiss_error_popup": "",
|
||||
"display_deleted_user": "",
|
||||
|
||||
+15
@@ -74,6 +74,19 @@ function CostSummary({ subscriptionChange, totalLicenses }: CostSummaryProps) {
|
||||
)}
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
{subscriptionChange.immediateCharge.discount !== 0 && (
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<span className="me-auto">{t('discount')}</span>
|
||||
<span data-testid="discount">
|
||||
(
|
||||
{formatCurrency(
|
||||
subscriptionChange.immediateCharge.discount,
|
||||
subscriptionChange.currency
|
||||
)}
|
||||
)
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
)}
|
||||
<ListGroup.Item
|
||||
className="bg-transparent border-0 px-0 gap-3 card-description-secondary"
|
||||
data-testid="tax"
|
||||
@@ -134,6 +147,8 @@ function CostSummary({ subscriptionChange, totalLicenses }: CostSummaryProps) {
|
||||
),
|
||||
}
|
||||
)}
|
||||
{subscriptionChange.immediateCharge.discount !== 0 &&
|
||||
` ${t('coupons_not_included')}.`}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
+15
@@ -45,6 +45,19 @@ function UpgradeSummary({ subscriptionChange }: UpgradeSummaryProps) {
|
||||
)}
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
{subscriptionChange.immediateCharge.discount !== 0 && (
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<span className="me-auto">{t('discount')}</span>
|
||||
<span data-testid="discount">
|
||||
(
|
||||
{formatCurrency(
|
||||
subscriptionChange.immediateCharge.discount,
|
||||
subscriptionChange.currency
|
||||
)}
|
||||
)
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
)}
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<span className="me-auto">{t('sales_tax')}</span>
|
||||
<span data-testid="tax">
|
||||
@@ -90,6 +103,8 @@ function UpgradeSummary({ subscriptionChange }: UpgradeSummaryProps) {
|
||||
date: formatTime(subscriptionChange.nextInvoice.date, 'MMMM D'),
|
||||
}
|
||||
)}
|
||||
{subscriptionChange.immediateCharge.discount !== 0 &&
|
||||
` ${t('coupons_not_included')}.`}
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
||||
@@ -508,6 +508,7 @@
|
||||
"disable_stop_on_first_error": "Disable “Stop on first error”",
|
||||
"disabling": "Disabling",
|
||||
"disconnected": "Disconnected",
|
||||
"discount": "Discount",
|
||||
"discount_of": "Discount of __amount__",
|
||||
"discover_latex_templates_and_examples": "Discover LaTeX templates and examples to help with everything from writing a journal article to using a specific LaTeX package.",
|
||||
"discover_why_people_worldwide_trust_overleaf": "Discover why __count__ million people worldwide trust Overleaf with their work.",
|
||||
|
||||
+30
-17
@@ -2,7 +2,6 @@ import '../../../helpers/bootstrap-5'
|
||||
import AddSeats, {
|
||||
MAX_NUMBER_OF_USERS,
|
||||
} from '@/features/group-management/components/add-seats/add-seats'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
describe('<AddSeats />', function () {
|
||||
beforeEach(function () {
|
||||
@@ -15,11 +14,7 @@ describe('<AddSeats />', function () {
|
||||
win.metaAttributesCache.set('ol-isProfessional', false)
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<AddSeats />
|
||||
</SplitTestProvider>
|
||||
)
|
||||
cy.mount(<AddSeats />)
|
||||
|
||||
cy.findByRole('button', { name: /add users/i })
|
||||
cy.findByTestId('add-more-users-group-form')
|
||||
@@ -88,11 +83,7 @@ describe('<AddSeats />', function () {
|
||||
win.metaAttributesCache.set('ol-isProfessional', true)
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<AddSeats />
|
||||
</SplitTestProvider>
|
||||
)
|
||||
cy.mount(<AddSeats />)
|
||||
|
||||
cy.findByRole('link', { name: /upgrade my plan/i }).should('not.exist')
|
||||
})
|
||||
@@ -216,12 +207,6 @@ describe('<AddSeats />', function () {
|
||||
describe('entered less than the maximum allowed number of users', function () {
|
||||
beforeEach(function () {
|
||||
this.adding = 1
|
||||
|
||||
cy.findByRole('button', { name: /add users/i }).as('addUsersBtn')
|
||||
cy.findByRole('button', { name: /send request/i }).should('not.exist')
|
||||
})
|
||||
|
||||
it('renders the preview data', function () {
|
||||
this.body = {
|
||||
change: {
|
||||
type: 'add-on-update',
|
||||
@@ -236,6 +221,7 @@ describe('<AddSeats />', function () {
|
||||
subtotal: 100,
|
||||
tax: 20,
|
||||
total: 120,
|
||||
discount: 0,
|
||||
},
|
||||
nextInvoice: {
|
||||
date: '2025-12-01T00:00:00.000Z',
|
||||
@@ -252,6 +238,11 @@ describe('<AddSeats />', function () {
|
||||
},
|
||||
}
|
||||
|
||||
cy.findByRole('button', { name: /add users/i }).as('addUsersBtn')
|
||||
cy.findByRole('button', { name: /send request/i }).should('not.exist')
|
||||
})
|
||||
|
||||
it('renders the preview data', function () {
|
||||
cy.intercept('POST', '/user/subscription/group/add-users/preview', {
|
||||
statusCode: 200,
|
||||
body: this.body,
|
||||
@@ -289,6 +280,8 @@ describe('<AddSeats />', function () {
|
||||
)
|
||||
})
|
||||
|
||||
cy.findByTestId('discount').should('not.exist')
|
||||
|
||||
cy.findByTestId('total').within(() => {
|
||||
cy.findByText(/total due today/i)
|
||||
cy.findByTestId('price').should(
|
||||
@@ -306,6 +299,26 @@ describe('<AddSeats />', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('renders the preview data with discount', function () {
|
||||
this.body.immediateCharge.discount = 50
|
||||
|
||||
cy.intercept('POST', '/user/subscription/group/add-users/preview', {
|
||||
statusCode: 200,
|
||||
body: this.body,
|
||||
}).as('addUsersRequest')
|
||||
cy.get('@input').type(this.adding.toString())
|
||||
|
||||
cy.findByTestId('cost-summary').within(() => {
|
||||
cy.findByTestId('discount').within(() => {
|
||||
cy.findByText(`($${this.body.immediateCharge.discount}.00)`)
|
||||
})
|
||||
|
||||
cy.findByText(
|
||||
/This does not include your current discounts, which will be applied automatically before your next payment/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('request', function () {
|
||||
afterEach(function () {
|
||||
cy.findByRole('button', { name: /go to subscriptions/i }).should(
|
||||
|
||||
+40
-9
@@ -1,8 +1,15 @@
|
||||
import '../../../helpers/bootstrap-5'
|
||||
import UpgradeSubscription from '@/features/group-management/components/upgrade-subscription/upgrade-subscription'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
import { SubscriptionChangePreview } from '../../../../../types/subscription/subscription-change-preview'
|
||||
|
||||
describe('<UpgradeSubscription />', function () {
|
||||
const resetPreviewAndRemount = (preview: SubscriptionChangePreview) => {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-subscriptionChangePreview', preview)
|
||||
})
|
||||
|
||||
cy.mount(<UpgradeSubscription />)
|
||||
}
|
||||
beforeEach(function () {
|
||||
this.totalLicenses = 2
|
||||
this.preview = {
|
||||
@@ -11,7 +18,12 @@ describe('<UpgradeSubscription />', function () {
|
||||
prevPlan: { name: 'Overleaf Standard Group' },
|
||||
},
|
||||
currency: 'USD',
|
||||
immediateCharge: { subtotal: 353.99, tax: 70.8, total: 424.79 },
|
||||
immediateCharge: {
|
||||
subtotal: 353.99,
|
||||
tax: 70.8,
|
||||
total: 424.79,
|
||||
discount: 0,
|
||||
},
|
||||
paymentMethod: 'Visa **** 1111',
|
||||
nextPlan: { annual: true },
|
||||
nextInvoice: {
|
||||
@@ -35,14 +47,8 @@ describe('<UpgradeSubscription />', function () {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
|
||||
win.metaAttributesCache.set('ol-totalLicenses', this.totalLicenses)
|
||||
win.metaAttributesCache.set('ol-subscriptionChangePreview', this.preview)
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<UpgradeSubscription />
|
||||
</SplitTestProvider>
|
||||
)
|
||||
resetPreviewAndRemount(this.preview)
|
||||
})
|
||||
|
||||
it('shows the group name', function () {
|
||||
@@ -93,6 +99,31 @@ describe('<UpgradeSubscription />', function () {
|
||||
cy.findByTestId('total').within(() => {
|
||||
cy.findByText('$424.79')
|
||||
})
|
||||
cy.findByTestId('discount').should('not.exist')
|
||||
})
|
||||
|
||||
it('shows subtotal, discount, tax and total price', function () {
|
||||
resetPreviewAndRemount({
|
||||
...this.preview,
|
||||
immediateCharge: {
|
||||
subtotal: 353.99,
|
||||
tax: 70.8,
|
||||
total: 424.79,
|
||||
discount: 50,
|
||||
},
|
||||
})
|
||||
cy.findByTestId('subtotal').within(() => {
|
||||
cy.findByText('$353.99')
|
||||
})
|
||||
cy.findByTestId('tax').within(() => {
|
||||
cy.findByText('$70.80')
|
||||
})
|
||||
cy.findByTestId('total').within(() => {
|
||||
cy.findByText('$424.79')
|
||||
})
|
||||
cy.findByTestId('discount').within(() => {
|
||||
cy.findByText('($50.00)')
|
||||
})
|
||||
})
|
||||
|
||||
it('shows total users', function () {
|
||||
|
||||
@@ -9,6 +9,7 @@ export type SubscriptionChangePreview = {
|
||||
subtotal: number
|
||||
tax: number
|
||||
total: number
|
||||
discount: number
|
||||
}
|
||||
nextInvoice: {
|
||||
date: string
|
||||
|
||||
Reference in New Issue
Block a user