add basic invoices page

This commit is contained in:
Rimian Perkins 2019-10-28 14:05:58 +11:00
parent b7a3be9344
commit 87c83abcd3
15 changed files with 192 additions and 46 deletions

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module DiscoursePatrons
class InvoicesController < ::ApplicationController
include DiscoursePatrons::Stripe
requires_login
before_action :set_api_key
def index
begin
customer = find_customer
if viewing_own_invoices && customer.present?
invoices = ::Stripe::Invoice.list(customer: customer.customer_id)
render_json_dump invoices.data
else
render_json_dump []
end
rescue ::Stripe::InvalidRequestError => e
return render_json_error e.message
end
end
private
def viewing_own_invoices
current_user.id == params[:user_id].to_i
end
def find_customer
DiscoursePatrons::Customer.find_user(current_user)
end
end
end

View File

@ -0,0 +1,19 @@
import computed from "ember-addons/ember-computed-decorators";
import { ajax } from "discourse/lib/ajax";
const Invoice = Discourse.Model.extend({
@computed("created")
createdFormatted(created) {
return moment.unix(created).format();
}
});
Invoice.reopenClass({
findAll() {
return ajax("/patrons/invoices", { method: "get" }).then(result =>
result.map(invoice => Invoice.create(invoice))
);
}
});
export default Invoice;

View File

@ -1,6 +1,8 @@
import Invoice from "discourse/plugins/discourse-patrons/discourse/models/invoice";
export default Discourse.Route.extend({
model() {
return {};
return Invoice.findAll();
},
setupController(controller, model) {

View File

@ -1,9 +1,9 @@
<h3>{{i18n 'discourse_patrons.admin.dashboard.title'}}</h3>
{{#load-more selector=".discourse-patrons-admin tr" action=(action "loadMore")}}
{{#load-more selector=".discourse-patrons-table tr" action=(action "loadMore")}}
{{#if model}}
<table class="table discourse-patrons-admin">
<table class="table discourse-patrons-table">
<thead>
<tr>
<th>{{i18n 'discourse_patrons.admin.dashboard.table.head.user'}}</th>

View File

@ -1,5 +1,5 @@
<table class="table discourse-patrons-admin">
<table class="table discourse-patrons-table">
<thead>
<th>{{i18n 'discourse_patrons.admin.plans.plan.plan_id'}}</th>
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname.title'}}</th>

View File

@ -7,7 +7,7 @@
</p>
{{#if model}}
<table class="table discourse-patrons-admin">
<table class="table discourse-patrons-table">
<thead>
<th>{{i18n 'discourse_patrons.admin.products.product.name'}}</th>
<th>{{i18n 'discourse_patrons.admin.products.product.created_at'}}</th>

View File

@ -24,7 +24,7 @@
<h4>{{i18n 'discourse_patrons.admin.plans.title'}}</h4>
<p>
<table class="table discourse-patrons-admin">
<table class="table discourse-patrons-table">
<thead>
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname'}}</th>
<th>{{i18n 'discourse_patrons.admin.plans.plan.interval'}}</th>

View File

@ -1,5 +1,5 @@
<table class="table discourse-patrons-admin">
<table class="table discourse-patrons-table">
<thead>
<tr>
<th>{{i18n 'discourse_patrons.admin.subscriptions.subscription.customer'}}</th>

View File

@ -1,3 +1,27 @@
<h3>{{i18n 'discourse_patrons.user.billing.title'}}</h3>
user/billing
{{#if model}}
<table class="topic-list">
<thead>
<th>{{i18n 'discourse_patrons.user.billing.invoices.amount'}}</th>
<th>{{i18n 'discourse_patrons.user.billing.invoices.number'}}</th>
<th>{{i18n 'discourse_patrons.user.billing.invoices.created_at'}}</th>
<th></th>
</thead>
{{#each model as |invoice|}}
<tr>
<td>{{invoice.amount_paid}}</td>
<td>{{invoice.number}}</td>
<td>{{format-date invoice.createdFormatted}}</td>
<td class="td-right">
<a href="{{invoice.invoice_pdf}}" class="btn btn-icon">
{{d-icon "download"}}
</a>
</td>
</tr>
{{/each}}
</table>
{{else}}
<p>{{i18n 'discourse_patrons.user.billing_help'}}</p>
{{/if}}

View File

@ -0,0 +1,33 @@
.discourse-patrons-section-columns {
display: flex;
justify-content: space-between;
@include breakpoint(medium) {
flex-direction: column;
}
.section-column {
min-width: calc(50% - 0.5em);
max-width: 100%;
&:last-child {
margin-left: 0.5em;
}
&:first-child {
margin-right: 0.5em;
}
@include breakpoint(medium) {
min-width: 100%;
&:last-child {
order: 2;
}
&:first-child {
order: 1;
}
}
}
}

View File

@ -10,47 +10,13 @@ textarea[readonly] {
border-color: #e9e9e9;
}
.discourse-patrons-section-columns {
display: flex;
justify-content: space-between;
@include breakpoint(medium) {
flex-direction: column;
}
.section-column {
min-width: calc(50% - 0.5em);
max-width: 100%;
&:last-child {
margin-left: 0.5em;
}
&:first-child {
margin-right: 0.5em;
}
@include breakpoint(medium) {
min-width: 100%;
&:last-child {
order: 2;
}
&:first-child {
order: 1;
}
}
}
}
#discourse-patrons-admin {
.btn-right {
text-align: right;
}
}
table.discourse-patrons-admin {
table.discourse-patrons-table {
.td-right {
text-align: right;
}

View File

@ -20,6 +20,14 @@ en:
navigation:
subscribe: Subscribe
billing: Billing
user:
billing_help: We couldn't find a customer identifier in our system.
billing:
title: Billing
invoices:
amount: Amount
number: Invoice Number
created_at: Created
subscribe:
title: Subscribe
card:

View File

@ -13,10 +13,11 @@ DiscoursePatrons::Engine.routes.draw do
end
resources :customers, only: [:create]
resources :subscriptions, only: [:create]
resources :invoices, only: [:index]
resources :patrons, only: [:index, :create]
resources :plans, only: [:index]
resources :products, only: [:index]
resources :patrons, only: [:index, :create]
resources :subscriptions, only: [:create]
get '/' => 'patrons#index'
end

View File

@ -11,6 +11,7 @@ enabled_site_setting :discourse_patrons_enabled
gem 'stripe', '5.7.1'
register_asset "stylesheets/common/discourse-patrons.scss"
register_asset "stylesheets/common/discourse-patrons-layout.scss"
register_asset "stylesheets/mobile/discourse-patrons.scss"
register_svg_icon "credit-card" if respond_to?(:register_svg_icon)
@ -50,6 +51,7 @@ after_initialize do
"../app/controllers/admin/products_controller",
"../app/controllers/admin/subscriptions_controller",
"../app/controllers/customers_controller",
"../app/controllers/invoices_controller",
"../app/controllers/patrons_controller",
"../app/controllers/plans_controller",
"../app/controllers/products_controller",

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'rails_helper'
module DiscoursePatrons
RSpec.describe InvoicesController do
describe "index" do
describe "not authenticated" do
it "does not list the invoices" do
::Stripe::Invoice.expects(:list).never
get "/patrons/invoices.json"
expect(response.status).to eq 403
end
end
describe "authenticated" do
let(:user) { Fabricate(:user) }
let(:stripe_customer) { { id: 'cus_id4567' } }
before do
sign_in(user)
end
describe "other user invoices" do
it "does not list the invoices" do
::Stripe::Invoice.expects(:list).never
get "/patrons/invoices.json", params: { user_id: 999999 }
end
end
describe "own invoices" do
context "stripe customer does not exist" do
it "lists empty" do
::Stripe::Invoice.expects(:list).never
get "/patrons/invoices.json", params: { user_id: user.id }
expect(response.body).to eq "[]"
end
end
context "stripe customer exists" do
before do
DiscoursePatrons::Customer.create_customer(user, stripe_customer)
end
it "lists the invoices" do
::Stripe::Invoice.expects(:list).with(customer: 'cus_id4567')
get "/patrons/invoices.json", params: { user_id: user.id }
end
end
end
end
end
end
end