add basic invoices page
This commit is contained in:
parent
b7a3be9344
commit
87c83abcd3
|
@ -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
|
|
@ -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;
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue