fix bugs in create plans
This commit is contained in:
parent
a94287434d
commit
922dee581c
|
@ -48,6 +48,22 @@ module DiscoursePatrons
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
begin
|
||||||
|
plan = ::Stripe::Plan.update(
|
||||||
|
params[:id],
|
||||||
|
nickname: params[:nickname],
|
||||||
|
trial_period_days: params[:trial_period_days],
|
||||||
|
metadata: { group_name: params[:metadata][:group_name] }
|
||||||
|
)
|
||||||
|
|
||||||
|
render_json_dump plan
|
||||||
|
|
||||||
|
rescue ::Stripe::InvalidRequestError => e
|
||||||
|
return render_json_error e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
begin
|
begin
|
||||||
plan = ::Stripe::Plan.delete(params[:id])
|
plan = ::Stripe::Plan.delete(params[:id])
|
||||||
|
|
|
@ -19,9 +19,13 @@ module DiscoursePatrons
|
||||||
|
|
||||||
def create
|
def create
|
||||||
begin
|
begin
|
||||||
product = ::Stripe::Product.create(
|
create_params = product_params.merge!(type: 'service')
|
||||||
product_params.merge(type: 'service')
|
|
||||||
)
|
if params[:statement_descriptor].blank?
|
||||||
|
create_params.except!(:statement_descriptor)
|
||||||
|
end
|
||||||
|
|
||||||
|
product = ::Stripe::Product.create(create_params)
|
||||||
|
|
||||||
render_json_dump product
|
render_json_dump product
|
||||||
|
|
||||||
|
@ -71,8 +75,8 @@ module DiscoursePatrons
|
||||||
def product_params
|
def product_params
|
||||||
{
|
{
|
||||||
name: params[:name],
|
name: params[:name],
|
||||||
statement_descriptor: params[:statement_descriptor],
|
|
||||||
active: params[:active],
|
active: params[:active],
|
||||||
|
statement_descriptor: params[:statement_descriptor]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
|
@computed("model.plan.isNew")
|
||||||
|
planFieldDisabled(isNew) {
|
||||||
|
return !isNew;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("model.product.id")
|
||||||
|
productId(id) {
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
|
||||||
redirect(product_id) {
|
redirect(product_id) {
|
||||||
DiscourseURL.redirectTo(`/admin/plugins/discourse-patrons/products/${product_id}`);
|
DiscourseURL.redirectTo(`/admin/plugins/discourse-patrons/products/${product_id}`);
|
||||||
},
|
},
|
||||||
|
@ -11,8 +22,19 @@ export default Ember.Controller.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
createPlan() {
|
createPlan() {
|
||||||
const product_id = this.get('model.plan.product');
|
// TODO: set default group name beforehand
|
||||||
this.get('model.plan').save().then(() => this.redirect(product_id));
|
if (this.get("model.plan.metadata.group_name") === undefined) {
|
||||||
|
this.set(
|
||||||
|
"model.plan.metadata",
|
||||||
|
{ group_name: this.get("model.groups.firstObject.name") }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get('model.plan').save().then(() => this.redirect(this.productId));
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePlan() {
|
||||||
|
this.get('model.plan').update().then(() => this.redirect(this.productId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +1,26 @@
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
redirect() {
|
|
||||||
this.transitionToRoute("adminPlugins.discourse-patrons.products");
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
cancelProduct() {
|
cancelProduct() {
|
||||||
this.redirect();
|
this.transitionToRoute("adminPlugins.discourse-patrons.products");
|
||||||
},
|
},
|
||||||
|
|
||||||
createProduct() {
|
createProduct() {
|
||||||
// TODO: set default group name beforehand
|
|
||||||
if (this.get("model.product.metadata.group_name") === undefined) {
|
|
||||||
this.set(
|
|
||||||
"model.product.metadata",
|
|
||||||
{ group_name: this.get("model.groups.firstObject.name") }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get("model.product")
|
this.get("model.product")
|
||||||
.save()
|
.save()
|
||||||
.then(() => this.redirect())
|
.then(product => {
|
||||||
|
this.transitionToRoute("adminPlugins.discourse-patrons.products.show", product.id);
|
||||||
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateProduct() {
|
updateProduct() {
|
||||||
this.get("model.product")
|
this.get("model.product")
|
||||||
.update()
|
.update()
|
||||||
.then(() => this.redirect())
|
.then(() => {
|
||||||
|
this.transitionToRoute("adminPlugins.discourse-patrons.products");
|
||||||
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import computed from "ember-addons/ember-computed-decorators";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
const AdminPlan = Discourse.Model.extend({
|
const AdminPlan = Discourse.Model.extend({
|
||||||
|
isNew: false,
|
||||||
name: "",
|
name: "",
|
||||||
interval: "month",
|
interval: "month",
|
||||||
amount: 0,
|
amount: 0,
|
||||||
|
@ -13,6 +14,16 @@ const AdminPlan = Discourse.Model.extend({
|
||||||
return moment.unix(created).format();
|
return moment.unix(created).format();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("trial_period_days")
|
||||||
|
parseTrialPeriodDays(trial_period_days) {
|
||||||
|
if(trial_period_days) {
|
||||||
|
return parseInt(0 + trial_period_days);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
return ajax(`/patrons/admin/plans/${this.id}`, { method: "delete" });
|
return ajax(`/patrons/admin/plans/${this.id}`, { method: "delete" });
|
||||||
},
|
},
|
||||||
|
@ -22,12 +33,22 @@ const AdminPlan = Discourse.Model.extend({
|
||||||
nickname: this.nickname,
|
nickname: this.nickname,
|
||||||
interval: this.interval,
|
interval: this.interval,
|
||||||
amount: this.amount,
|
amount: this.amount,
|
||||||
trial_period_days: this.trial_period_days,
|
trial_period_days: this.parseTrialPeriodDays,
|
||||||
product: this.product,
|
product: this.product,
|
||||||
metadata: this.metadata,
|
metadata: this.metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ajax("/patrons/admin/plans", { method: "post", data });
|
return ajax("/patrons/admin/plans", { method: "post", data });
|
||||||
|
},
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = {
|
||||||
|
nickname: this.nickname,
|
||||||
|
trial_period_days: this.parseTrialPeriodDays,
|
||||||
|
metadata: this.metadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ajax(`/patrons/admin/plans/${this.id}`, { method: "patch", data });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@ const AdminProduct = Discourse.Model.extend({
|
||||||
active: this.active
|
active: this.active
|
||||||
};
|
};
|
||||||
|
|
||||||
return ajax("/patrons/admin/products", { method: "post", data });
|
return ajax("/patrons/admin/products", { method: "post", data }).then(product =>
|
||||||
|
AdminProduct.create(product)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default Discourse.Route.extend({
|
||||||
let plan;
|
let plan;
|
||||||
|
|
||||||
if(id === 'new') {
|
if(id === 'new') {
|
||||||
plan = AdminPlan.create({ product: product.get('id') });
|
plan = AdminPlan.create({ isNew: true, product: product.get('id') });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
plan = AdminPlan.find(id);
|
plan = AdminPlan.find(id);
|
||||||
|
|
|
@ -9,17 +9,15 @@
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-admin">
|
||||||
<thead>
|
<thead>
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.name'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.products.product.name'}}</th>
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.group'}}</th>
|
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.created_at'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.products.product.created_at'}}</th>
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.active'}}</th>
|
<th class="td-right">{{i18n 'discourse_patrons.admin.products.product.active'}}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</thead>
|
</thead>
|
||||||
{{#each model as |product|}}
|
{{#each model as |product|}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{product.name}}</td>
|
<td>{{product.name}}</td>
|
||||||
<td>{{product.metadata.group_name}}</td>
|
|
||||||
<td>{{format-date product.createdFormatted}}</td>
|
<td>{{format-date product.createdFormatted}}</td>
|
||||||
<td>{{product.active}}</td>
|
<td class="td-right">{{product.active}}</td>
|
||||||
<td class="td-right">
|
<td class="td-right">
|
||||||
{{#link-to "adminPlugins.discourse-patrons.products.show" product.id class="btn no-text btn-icon"}}
|
{{#link-to "adminPlugins.discourse-patrons.products.show" product.id class="btn no-text btn-icon"}}
|
||||||
{{d-icon "far-edit"}}
|
{{d-icon "far-edit"}}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="amount">{{i18n 'discourse_patrons.admin.plans.plan.amount'}}</label>
|
<label for="amount">{{i18n 'discourse_patrons.admin.plans.plan.amount'}}</label>
|
||||||
{{input type="text" name="name" value=model.plan.amount}}
|
{{input type="text" name="name" value=model.plan.amount disabled=planFieldDisabled}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="trial">
|
<label for="trial">
|
||||||
|
@ -51,7 +51,12 @@
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
{{d-button label="cancel" action=(action "cancelPlan" model.plan.product) icon="times"}}
|
{{d-button label="cancel" action=(action "cancelPlan" model.plan.product) icon="times"}}
|
||||||
{{d-button label="discourse_patrons.admin.plans.operations.create" action="createPlan" icon="plus" class="btn btn-primary"}}
|
|
||||||
|
{{#if model.plan.isNew}}
|
||||||
|
{{d-button label="discourse_patrons.admin.plans.operations.create" action="createPlan" icon="plus" class="btn btn-primary"}}
|
||||||
|
{{else}}
|
||||||
|
{{d-button label="discourse_patrons.admin.plans.operations.update" action="updatePlan" icon="check" class="btn btn-primary"}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -20,53 +20,54 @@
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h4>{{i18n 'discourse_patrons.admin.plans.title'}}</h4>
|
{{#unless model.product.isNew}}
|
||||||
|
<h4>{{i18n 'discourse_patrons.admin.plans.title'}}</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<table class="table discourse-patrons-admin">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname'}}</th>
|
||||||
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.interval'}}</th>
|
||||||
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.created_at'}}</th>
|
||||||
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.group'}}</th>
|
||||||
|
<th class="td-right">{{i18n 'discourse_patrons.admin.plans.plan.amount'}}</th>
|
||||||
|
<th class="td-right">
|
||||||
|
{{#link-to "adminPlugins.discourse-patrons.products.show.plans.show" model.product.id "new" class="btn"}}
|
||||||
|
{{i18n 'discourse_patrons.admin.plans.operations.add'}}
|
||||||
|
{{/link-to}}
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
{{#each model.plans as |plan|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{plan.nickname}}</td>
|
||||||
|
<td>{{plan.interval}}</td>
|
||||||
|
<td>{{format-date plan.createdFormatted}}</td>
|
||||||
|
<td>{{plan.metadata.group_name}}</td>
|
||||||
|
<td class="td-right">{{plan.amount}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
{{#link-to "adminPlugins.discourse-patrons.products.show.plans.show" model.product.id plan.id class="btn no-text btn-icon"}}
|
||||||
|
{{d-icon "far-edit"}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{d-button
|
||||||
|
action=(route-action "destroyPlan")
|
||||||
|
actionParam=plan
|
||||||
|
icon="trash-alt"
|
||||||
|
class="btn-danger btn no-text btn-icon"}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
<p>
|
|
||||||
<table class="table discourse-patrons-admin">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname'}}</th>
|
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.interval'}}</th>
|
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.created_at'}}</th>
|
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.group'}}</th>
|
|
||||||
<th class="td-right">{{i18n 'discourse_patrons.admin.plans.plan.amount'}}</th>
|
|
||||||
<th class="td-right">
|
|
||||||
{{#link-to "adminPlugins.discourse-patrons.products.show.plans.show" model.product.id "new" class="btn"}}
|
|
||||||
{{i18n 'discourse_patrons.admin.plans.operations.add'}}
|
|
||||||
{{/link-to}}
|
|
||||||
</th>
|
|
||||||
</thead>
|
|
||||||
{{#each model.plans as |plan|}}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{plan.nickname}}</td>
|
<td colspan="6">
|
||||||
<td>{{plan.interval}}</td>
|
{{#unless model.plans}}
|
||||||
<td>{{format-date plan.createdFormatted}}</td>
|
<hr>
|
||||||
<td>{{plan.metadata.group_name}}</td>
|
{{i18n 'discourse_patrons.admin.products.product.plan_help'}}
|
||||||
<td class="td-right">{{plan.amount}}</td>
|
{{/unless}}
|
||||||
<td class="td-right">
|
|
||||||
{{#link-to "adminPlugins.discourse-patrons.products.show.plans.show" model.product.id plan.id class="btn no-text btn-icon"}}
|
|
||||||
{{d-icon "far-edit"}}
|
|
||||||
{{/link-to}}
|
|
||||||
{{d-button
|
|
||||||
action=(route-action "destroyPlan")
|
|
||||||
actionParam=plan
|
|
||||||
icon="trash-alt"
|
|
||||||
class="btn-danger btn no-text btn-icon"}}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
</table>
|
||||||
|
</p>
|
||||||
<tr>
|
{{/unless}}
|
||||||
<td colspan="5">
|
|
||||||
{{#unless model.plans}}
|
|
||||||
<hr>
|
|
||||||
{{i18n 'discourse_patrons.admin.products.product.plan_help'}}
|
|
||||||
{{/unless}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
@ -73,8 +73,6 @@ en:
|
||||||
name: Product Name
|
name: Product Name
|
||||||
statement_descriptor: Statement Descriptor
|
statement_descriptor: Statement Descriptor
|
||||||
statement_descriptor_help: Extra information about a product which will appear on your customer’s credit card statement.
|
statement_descriptor_help: Extra information about a product which will appear on your customer’s credit card statement.
|
||||||
group: User Group
|
|
||||||
group_help: This is the discourse user group the customer gets added to when the subscription is created.
|
|
||||||
plan_help: Create a pricing plan to subscribe customers to this product
|
plan_help: Create a pricing plan to subscribe customers to this product
|
||||||
active: Active
|
active: Active
|
||||||
created_at: Created
|
created_at: Created
|
||||||
|
@ -83,13 +81,14 @@ en:
|
||||||
operations:
|
operations:
|
||||||
add: Add New Plan
|
add: Add New Plan
|
||||||
create: Create Plan
|
create: Create Plan
|
||||||
create_help: Once a pricing plan is created, only its nickname and trial period can be updated.
|
update: Update Plan
|
||||||
|
create_help: Once a pricing plan is created, only its nickname, trial period and user group can be updated.
|
||||||
new: New Plan
|
new: New Plan
|
||||||
destroy:
|
destroy:
|
||||||
confirm: Are you sure you want to destroy this plan?
|
confirm: Are you sure you want to destroy this plan?
|
||||||
plan:
|
plan:
|
||||||
nickname: Plan Nickname
|
nickname: Plan Nickname
|
||||||
nickname_help: This won't be visible to customers, but will help you find this plan later.
|
nickname_help: This won't be visible to customers, but will help you find this plan later
|
||||||
plan_id: Plan ID
|
plan_id: Plan ID
|
||||||
product: Product
|
product: Product
|
||||||
interval: Billing Interval
|
interval: Billing Interval
|
||||||
|
|
|
@ -46,9 +46,9 @@ module DiscoursePatrons
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete" do
|
describe "update" do
|
||||||
it "does not delete a plan" do
|
it "does not update a plan" do
|
||||||
::Stripe::Plan.expects(:delete).never
|
::Stripe::Plan.expects(:update).never
|
||||||
delete "/patrons/admin/plans/plan_12345.json"
|
delete "/patrons/admin/plans/plan_12345.json"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,6 +57,18 @@ module DiscoursePatrons
|
||||||
expect(response.status).to eq 403
|
expect(response.status).to eq 403
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "delete" do
|
||||||
|
it "does not delete a plan" do
|
||||||
|
::Stripe::Plan.expects(:delete).never
|
||||||
|
patch "/patrons/admin/plans/plan_12345.json"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not ok" do
|
||||||
|
patch "/patrons/admin/plans/plan_12345.json"
|
||||||
|
expect(response.status).to eq 403
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'authenticated' do
|
context 'authenticated' do
|
||||||
|
@ -114,6 +126,13 @@ module DiscoursePatrons
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "update" do
|
||||||
|
it "updates a plan" do
|
||||||
|
::Stripe::Plan.expects(:update)
|
||||||
|
patch "/patrons/admin/plans/plan_12345.json", params: { metadata: { group_name: 'discourse-user-group-name' } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "delete" do
|
describe "delete" do
|
||||||
it "deletes a plan" do
|
it "deletes a plan" do
|
||||||
::Stripe::Plan.expects(:delete).with('plan_12345')
|
::Stripe::Plan.expects(:delete).with('plan_12345')
|
||||||
|
|
|
@ -73,6 +73,11 @@ module DiscoursePatrons
|
||||||
::Stripe::Product.expects(:create).with(has_entry(statement_descriptor: 'Blessed are the cheesemakers'))
|
::Stripe::Product.expects(:create).with(has_entry(statement_descriptor: 'Blessed are the cheesemakers'))
|
||||||
post "/patrons/admin/products.json", params: { statement_descriptor: 'Blessed are the cheesemakers' }
|
post "/patrons/admin/products.json", params: { statement_descriptor: 'Blessed are the cheesemakers' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'has no statement descriptor if empty' do
|
||||||
|
::Stripe::Product.expects(:create).with(has_key(:statement_descriptor)).never
|
||||||
|
post "/patrons/admin/products.json", params: { statement_descriptor: '' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'show' do
|
describe 'show' do
|
||||||
|
|
Loading…
Reference in New Issue