Commit baa820af authored by Oswaldo Ferreira's avatar Oswaldo Ferreira Committed by Douglas Barbosa Alexandre

Add an Upgrade button to User's billings page

It adds an upgrade button to the user billings
page that redirects to the customers portal
upgrade checkout page.

If the user is not logged in at the portal he/she
will be automatically redirected after the login
process.

If the user is at the latest tier, the button
won't be presented and it'll fallback to the
existing plans table.
parent daa7e56f
.billing-plan-header {
border-bottom: 0;
padding-bottom: 0;
.billing-plan-logo svg {
height: 100px;
......@@ -18,10 +19,6 @@
}
}
.billing-plans-alert {
margin: 0;
}
.billing-plans {
display: flex;
flex-direction: row;
......
......@@ -13,10 +13,6 @@ module BillingPlansHelper
plan.purchase_link&.action == 'current_plan'
end
def has_plan_purchase_link?(plans_data)
plans_data.any? { |plan| plan.purchase_link&.href }
end
def plan_purchase_link(href, link_text)
if href
link_to link_text, href, class: 'btn btn-primary btn-inverted'
......
......@@ -69,7 +69,7 @@ module EE
validate :validate_plan_name
validate :validate_shared_runner_minutes_support
delegate :trial?, :trial_ends_on, to: :gitlab_subscription, allow_nil: true
delegate :trial?, :trial_ends_on, :upgradable?, to: :gitlab_subscription, allow_nil: true
before_create :sync_membership_lock_with_parent
......
......@@ -16,7 +16,7 @@
%h4
- plan_link = plan.about_page_href ? link_to(plan.code.titleize, plan.about_page_href) : plan.name
- if namespace == current_user.namespace
= s_("BillingPlans|@%{user_name} you are currently on the %{plan_link} plan.").html_safe % { user_name: current_user.username, plan_link: plan_link }
= s_("BillingPlans|You are currently using the %{plan_link} plan.").html_safe % { plan_link: plan_link }
- else
= s_("BillingPlans|%{group_name} is currently using the %{plan_link} plan.").html_safe % { group_name: namespace.full_name, plan_link: plan_link }
......
- current_plan = subscription_plan_info(plans_data, namespace.actual_plan_name)
- support_link = link_to s_("BillingPlans|Contact Support"), "https://support.gitlab.com"
- if current_plan
= render 'shared/billings/billing_plan_header', namespace: namespace, plan: current_plan
- unless has_plan_purchase_link?(plans_data)
.billing-plans-alert.card.prepend-top-10
.card-header.bg-warning.text-white
= s_("BillingPlans|Automatic downgrade and upgrade to some plans is currently not available.")
- customer_support_url = 'https://about.gitlab.com/sales/';
- customer_support_link = link_to s_("BillingPlans|Customer Support"), customer_support_url
= s_("BillingPlans|Please contact %{customer_support_link} in that case.").html_safe % { customer_support_link: customer_support_link }
- if namespace.upgradable?
.gl-p-4.center
= link_to s_("BillingPlan|Upgrade plan"), plan_upgrade_url(namespace, current_plan), class: "btn btn-success"
.billing-plans
- plans_data.each do |plan|
= render 'shared/billings/billing_plan', namespace: namespace, plan: plan
- else
.billing-plans
- plans_data.each do |plan|
= render 'shared/billings/billing_plan', namespace: namespace, plan: plan
- if namespace.actual_plan
.center
= s_("BillingPlans|If you would like to downgrade your plan please %{support_link}.").html_safe % { support_link: support_link }
---
title: Add Upgrade button to the User Billing page
merge_request: 15075
author:
type: added
require 'spec_helper'
describe 'Billing plan pages', :feature do
include StubRequests
let(:user) { create(:user) }
let(:namespace) { user.namespace }
let(:free_plan) { create(:free_plan) }
let(:bronze_plan) { create(:bronze_plan) }
let(:gold_plan) { create(:gold_plan) }
let(:plans_data) do
[
{
name: "Free",
price_per_month: 0,
free: true,
code: "free",
price_per_year: 0,
purchase_link: {
action: "downgrade",
href: nil
},
features: []
},
{
name: "Bronze",
price_per_month: 4,
free: false,
code: "bronze",
price_per_year: 48,
purchase_link: {
action: "current_plan",
href: nil
},
features: []
},
{
name: "Silver",
price_per_month: 19,
free: false,
code: "silver",
price_per_year: 228,
purchase_link: {
action: "upgrade",
href: nil
},
features: []
},
{
name: "Gold",
price_per_month: 99,
free: false,
code: "gold",
price_per_year: 1188,
purchase_link: {
action: "upgrade",
href: nil
},
features: []
}
]
end
shared_examples 'displays all plans and correct actions' do
it 'displays all plans' do
page.within('.billing-plans') do
panels = page.all('.card')
expect(panels.length).to eq(plans_data.length)
plans_data.each.with_index do |data, index|
expect(panels[index].find('.card-header')).to have_content(data[:name])
end
end
end
it 'displays correct plan actions' do
expected_actions = plans_data.map { |data| data.fetch(:purchase_link).fetch(:action) }
plan_actions = page.all('.billing-plans .card .plan-action')
expect(plan_actions.length).to eq(expected_actions.length)
expected_actions.each_with_index do |expected_action, index|
action = plan_actions[index]
case expected_action
when 'downgrade'
expect(action).to have_content('Downgrade')
expect(action).to have_css('.disabled')
when 'current_plan'
expect(action).to have_content('Current plan')
expect(action).to have_css('.disabled')
when 'upgrade'
expect(action).to have_content('Upgrade')
expect(action).to have_css('.disabled')
end
end
JSON.parse(File.read(Rails.root.join('ee/spec/fixtures/gitlab_com_plans.json'))).map do |data|
data.deep_symbolize_keys
end
end
before do
expect(Gitlab::HTTP).to receive(:get).and_return(double(body: plans_data.to_json))
stub_full_request("https://customers.gitlab.com/gitlab_plans?plan=#{plan}")
.to_return(status: 200, body: plans_data.to_json)
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true }
gitlab_sign_in(user)
end
def external_upgrade_url(namespace, plan)
if Plan::PAID_HOSTED_PLANS.include?(plan.name)
"#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{namespace.id}/upgrade/#{plan.name}-external-id"
end
end
context 'users profile billing page' do
let(:page_path) { profile_billings_path }
it_behaves_like 'billings gold trial callout'
context 'on bronze' do
context 'on free' do
let(:plan) { free_plan.name }
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: nil, seats: 15)
end
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(bronze_plan)
visit page_path
end
it 'displays all plans' do
page.within('.billing-plans') do
panels = page.all('.card')
expect(panels.length).to eq(plans_data.length)
plans_data.each.with_index do |data, index|
expect(panels[index].find('.card-header')).to have_content(data[:name])
end
end
end
it 'displays correct plan actions' do
expected_actions = plans_data.map { |data| data.fetch(:purchase_link).fetch(:action) }
plan_actions = page.all('.billing-plans .card .plan-action')
expect(plan_actions.length).to eq(expected_actions.length)
expected_actions.each_with_index do |expected_action, index|
action = plan_actions[index]
case expected_action
when 'downgrade'
expect(action).to have_content('Downgrade')
expect(action).to have_css('.disabled')
when 'current_plan'
expect(action).to have_content('Current plan')
expect(action).to have_css('.disabled')
when 'upgrade'
expect(action).to have_content('Upgrade')
expect(action).not_to have_css('.disabled')
end
end
end
end
context 'on bronze' do
let(:plan) { bronze_plan.name }
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: bronze_plan, seats: 15)
end
before do
visit page_path
end
include_examples 'displays all plans and correct actions'
it 'displays header and actions' do
page.within('.billing-plan-header') do
expect(page).to have_content('You are currently using the Bronze plan.')
it 'displays plan header' do
expect(page).to have_css('.billing-plan-logo img')
end
page.within('.content') do
expect(page).to have_link('Upgrade plan', href: external_upgrade_url(namespace, bronze_plan))
expect(page).to have_content('downgrade your plan')
expect(page).to have_link('Contact Support', href: 'https://support.gitlab.com')
end
end
end
context 'on gold' do
let(:plan) { gold_plan.name }
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: gold_plan, seats: 15)
end
before do
visit page_path
end
it 'displays header and actions' do
page.within('.billing-plan-header') do
expect(page).to have_content("@#{user.username} you are currently on the Bronze")
expect(page).to have_content('You are currently using the Gold plan.')
expect(page).to have_css('.billing-plan-logo img')
end
page.within('.content') do
expect(page).not_to have_link('Upgrade plan')
expect(page).to have_content('downgrade your plan')
expect(page).to have_link('Contact Support', href: 'https://support.gitlab.com')
end
end
end
end
context 'group billing page' do
let(:group) { create(:group) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let(:namespace) { create(:group) }
let!(:group_member) { create(:group_member, :owner, group: namespace, user: user) }
context 'top-most group' do
let(:page_path) { group_billings_path(group) }
let(:page_path) { group_billings_path(namespace) }
it_behaves_like 'billings gold trial callout'
context 'on bronze' do
before do
expect_any_instance_of(EE::Group).to receive(:plan).at_least(:once).and_return(bronze_plan)
let(:plan) { bronze_plan.name }
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: bronze_plan, seats: 15)
end
before do
visit page_path
end
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("#{group.name} is currently using the Bronze plan")
expect(page).to have_content("#{namespace.name} is currently using the Bronze plan")
expect(page).to have_css('.billing-plan-logo .identicon')
end
end
it 'does not display the billing plans table' do
expect(page).not_to have_css('.billing-plans')
end
it 'displays subscription table', :js do
expect(page).to have_selector('.js-subscription-table')
end
......@@ -168,10 +177,17 @@ describe 'Billing plan pages', :feature do
let(:subgroup2) { create(:group, parent: subgroup1) }
let!(:subgroup2_member) { create(:group_member, :owner, group: subgroup2, user: user3) }
let(:page_path) { group_billings_path(subgroup2) }
let(:namespace) { group }
it_behaves_like 'billings gold trial callout'
context 'on bronze' do
let(:plan) { bronze_plan.name }
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: bronze_plan, seats: 15)
end
before do
visit page_path
end
......@@ -182,13 +198,13 @@ describe 'Billing plan pages', :feature do
expect(page).to have_css('.billing-plan-logo .identicon')
expect(page.find('.btn-success')).to have_content('Manage plan')
end
expect(page).not_to have_css('.billing-plans')
end
end
end
context 'with unexpected JSON' do
let(:plan) { 'free' }
let(:plans_data) do
[
{
......@@ -214,15 +230,5 @@ describe 'Billing plan pages', :feature do
it 'renders no header for missing plan' do
expect(page).not_to have_css('.billing-plan-header')
end
it 'displays all plans' do
page.within('.billing-plans') do
panels = page.all('.card')
expect(panels.length).to eq(plans_data.length)
plans_data.each_with_index do |data, index|
expect(panels[index].find('.card-header')).to have_content(data[:name])
end
end
end
end
end
{
"free": {
"rows": [
{
"header": { "icon": "monitor", "title": "Usage" },
"columns": [
{
"id": "seatsInUse",
"label": "Seats currently in use",
"value": null,
"colClass": "number",
"popover": {
"content": "This is the number of seats you will be required to purchase if you update to a paid plan."
}
},
{
"id": "subscriptionStartDate",
"label": "Subscription start date",
"value": null,
"isDate": true
}
]
}
]
},
"trial": {
"rows": [
{
"header": { "icon": "monitor", "title": "Usage" },
"columns": [
{
"id": "seatsInUse",
"label": "Seats currently in use",
"value": null,
"colClass": "number",
"popover": { "content": "Usage count is performed once a day at 12:00 PM." }
},
{
"id": "subscriptionStartDate",
"label": "Trial start date",
"value": null,
"isDate": true
},
{ "id": "subscriptionEndDate", "label": "Trial end date", "value": null, "isDate": true }
]
}
]
},
"default": {
"rows": [
{
"header": { "icon": "monitor", "title": "Usage" },
"columns": [
{
"id": "seatsInSubscription",
"label": "Seats in subscription",
"value": 0,
"colClass": "number"
},
{
"id": "seatsInUse",
"label": "Seats currently in use",
"value": 1,
"colClass": "number",
"popover": { "content": "Usage count is performed once a day at 12:00 PM." }
},
{
"id": "maxSeatsUsed",
"label": "Max seats used",
"value": 0,
"colClass": "number",
"popover": {
"content": "This is the maximum number of users that have existed at the same time since this subscription started."
}
},
{
"id": "seatsOwed",
"label": "Seats owed",
"value": 0,
"colClass": "number",
"popover": {
"content": "GitLab allows you to continue using your subscription even if you exceed the number of seats you purchased. You will be required to pay for these seats upon renewal."
}
}
]
},
{
"header": { "icon": "calendar", "title": "Billing" },
"columns": [
{
"id": "subscriptionStartDate",
"label": "Subscription start date",
"value": "2019-07-31",
"isDate": true
},
{
"id": "subscriptionEndDate",
"label": "Subscription end date",
"value": null,
"isDate": true
},
{
"id": "lastInvoice",
"label": "Last invoice",
"value": null,
"isDate": true,
"popover": {
"content": "This is the last time the GitLab.com team was in contact with you to settle any outstanding balances."
},
"hideContent": true
},
{
"id": "nextInvoice",
"label": "Next invoice",
"value": null,
"isDate": true,
"popover": {
"content": "This is the next date when the GitLab.com team is scheduled to get in contact with you to settle any outstanding balances."
},
"hideContent": true
}
]
}
]
}
}
......@@ -75,10 +75,6 @@ describe('EE billings subscription module mutations', () => {
expect(getStateTableValues(tableKey)).toMatchSnapshot();
});
});
it('sets table values', () => {
expect(getStateTableValues('free')).toMatchSnapshot();
});
});
describe(types.RECEIVE_SUBSCRIPTION_ERROR, () => {
......
......@@ -60,11 +60,13 @@ end
shared_examples 'billings gold trial callout' do
context 'on a free plan' do
let(:plan) { nil }
let(:plan) { 'free' }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: nil, seats: 15)
end
before do
visit page_path
end
......@@ -80,11 +82,13 @@ shared_examples 'billings gold trial callout' do
where(case_names: ->(plan_type) {"like #{plan_type}"}, plan_type: [:bronze, :silver])
with_them do
let(:plan) { plans[plan_type] }
let(:plan) { plan_type }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: plans[plan_type], seats: 15)
end
before do
visit page_path
end
......@@ -99,11 +103,13 @@ shared_examples 'billings gold trial callout' do
end
context 'on a gold plan' do
set(:plan) { create(:gold_plan) }
let(:plan) { gold_plan.name }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: gold_plan, seats: 15)
end
before do
visit page_path
end
......
......@@ -2224,10 +2224,7 @@ msgstr ""
msgid "BillingPlans|%{group_name} is currently using the %{plan_link} plan."
msgstr ""
msgid "BillingPlans|@%{user_name} you are currently on the %{plan_link} plan."
msgstr ""
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
msgid "BillingPlans|Contact Support"
msgstr ""
msgid "BillingPlans|Contact Support"
......@@ -2236,10 +2233,10 @@ msgstr ""
msgid "BillingPlans|Current plan"
msgstr ""
msgid "BillingPlans|Customer Support"
msgid "BillingPlans|Downgrade"
msgstr ""
msgid "BillingPlans|Downgrade"
msgid "BillingPlans|If you would like to downgrade your plan please %{support_link}."
msgstr ""
msgid "BillingPlans|If you would like to downgrade your plan please %{support_link}."
......@@ -2254,7 +2251,7 @@ msgstr ""
msgid "BillingPlans|Manage plan"
msgstr ""
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgid "BillingPlans|Pricing page"
msgstr ""
msgid "BillingPlans|Pricing page"
......@@ -2272,6 +2269,9 @@ msgstr ""
msgid "BillingPlans|Upgrade"
msgstr ""
msgid "BillingPlans|You are currently using the %{plan_link} plan."
msgstr ""
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
msgstr ""
......@@ -2293,6 +2293,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
msgid "BillingPlan|Upgrade plan"
msgstr ""
msgid "Bitbucket Server Import"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment