VRFS-2821 - payment history screen added
This commit is contained in:
parent
e42b926a5e
commit
ab2925ef88
|
|
@ -2,6 +2,8 @@ module JamRuby
|
|||
class RecurlyTransactionWebHook < ActiveRecord::Base
|
||||
|
||||
belongs_to :user, class_name: 'JamRuby::User'
|
||||
belongs_to :sale_line_item, class_name: 'JamRuby::SaleLineItem', foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid', inverse_of: :recurly_transactions
|
||||
belongs_to :sale, class_name: 'JamRuby::Sale', foreign_key: 'invoice_id', primary_key: 'recurly_invoice_id', inverse_of: :recurly_transactions
|
||||
|
||||
validates :recurly_transaction_id, presence: true
|
||||
validates :action, presence: true
|
||||
|
|
@ -9,11 +11,24 @@ module JamRuby
|
|||
validates :amount_in_cents, numericality: {only_integer: true}
|
||||
validates :user, presence: true
|
||||
|
||||
|
||||
SUCCESSFUL_PAYMENT = 'payment'
|
||||
FAILED_PAYMENT = 'failed_payment'
|
||||
REFUND = 'refund'
|
||||
VOID = 'void'
|
||||
|
||||
def is_credit_type?
|
||||
transaction_type == REFUND || transaction_type == VOID
|
||||
end
|
||||
|
||||
def is_voided?
|
||||
transaction_type == VOID
|
||||
end
|
||||
|
||||
def is_refund?
|
||||
transaction_type == REFUND
|
||||
end
|
||||
|
||||
def self.is_transaction_web_hook?(document)
|
||||
|
||||
return false if document.root.nil?
|
||||
|
|
|
|||
|
|
@ -8,9 +8,66 @@ module JamRuby
|
|||
belongs_to :user, class_name: 'JamRuby::User'
|
||||
has_many :sale_line_items, class_name: 'JamRuby::SaleLineItem'
|
||||
|
||||
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale, foreign_key: 'invoice_id', primary_key: 'recurly_invoice_id'
|
||||
|
||||
validates :order_total, numericality: {only_integer: false}
|
||||
validates :user, presence: true
|
||||
|
||||
@@log = Logging.logger[Sale]
|
||||
|
||||
def self.index(user, params = {})
|
||||
|
||||
limit = params[:per_page]
|
||||
limit ||= 20
|
||||
limit = limit.to_i
|
||||
|
||||
query = Sale.limit(limit)
|
||||
.includes([:recurly_transactions, :sale_line_items])
|
||||
.where('sales.user_id' => user.id)
|
||||
.order('sales.created_at DESC')
|
||||
|
||||
current_page = params[:page].nil? ? 1 : params[:page].to_i
|
||||
next_page = current_page + 1
|
||||
|
||||
# will_paginate gem
|
||||
query = query.paginate(:page => current_page, :per_page => limit)
|
||||
|
||||
if query.length == 0 # no more results
|
||||
{ query: query, next_page: nil}
|
||||
elsif query.length < limit # no more results
|
||||
{ query: query, next_page: nil}
|
||||
else
|
||||
{ query: query, next_page: next_page }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def state
|
||||
original_total = self.recurly_total_in_cents
|
||||
|
||||
is_voided = false
|
||||
refund_total = 0
|
||||
|
||||
recurly_transactions.each do |transaction|
|
||||
if transaction.is_voided?
|
||||
is_voided = true
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
if transaction.is_refund?
|
||||
refund_total = refund_total + transaction.amount_in_cents
|
||||
end
|
||||
end
|
||||
|
||||
# if refund_total is > 0, then you have a refund.
|
||||
# if voided is true, then in theory the whole thing has been refunded
|
||||
{
|
||||
voided: is_voided,
|
||||
original_total: original_total,
|
||||
refund_total: refund_total
|
||||
}
|
||||
end
|
||||
|
||||
def self.preview_invoice(current_user, shopping_carts)
|
||||
|
||||
|
|
@ -102,10 +159,6 @@ module JamRuby
|
|||
sale.recurly_total_in_cents = invoice.total_in_cents
|
||||
sale.recurly_currency = invoice.currency
|
||||
|
||||
puts "Sale Line Items #{sale.sale_line_items.inspect}"
|
||||
|
||||
puts "----"
|
||||
puts "Invoice Line Items #{invoice.line_items.inspect}"
|
||||
# and resolve against sale_line_items
|
||||
sale.sale_line_items.each do |sale_line_item|
|
||||
found_line_item = false
|
||||
|
|
@ -120,7 +173,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
if !found_line_item
|
||||
@@loge.error("can't find line item #{sale_line_item.recurly_adjustment_uuid}")
|
||||
@@log.error("can't find line item #{sale_line_item.recurly_adjustment_uuid}")
|
||||
puts "CANT FIND LINE ITEM"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
module JamRuby
|
||||
class SaleLineItem < ActiveRecord::Base
|
||||
|
||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
||||
|
||||
JAMBLASTER = 'JamBlaster'
|
||||
JAMCLOUD = 'JamCloud'
|
||||
JAMTRACK = 'JamTrack'
|
||||
|
||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
||||
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
||||
|
||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]}
|
||||
validates :unit_price, numericality: {only_integer: false}
|
||||
validates :quantity, numericality: {only_integer: true}
|
||||
|
|
@ -19,10 +20,45 @@ module JamRuby
|
|||
validates :sale, presence:true
|
||||
|
||||
def product
|
||||
# TODO: beef up if there is more than one sort of sale
|
||||
JamTrack.find(product_id)
|
||||
if product_type == JAMTRACK
|
||||
JamTrack.find_by_id(product_id)
|
||||
else
|
||||
raise 'unsupported product type'
|
||||
end
|
||||
end
|
||||
|
||||
def product_info
|
||||
item = product
|
||||
{ name: product.name } if item
|
||||
end
|
||||
|
||||
def state
|
||||
voided = false
|
||||
refunded = false
|
||||
failed = false
|
||||
succeeded = false
|
||||
|
||||
recurly_transactions.each do |transaction|
|
||||
if transaction.transaction_type == RecurlyTransactionWebHook::VOID
|
||||
voided = true
|
||||
elsif transaction.transaction_type == RecurlyTransactionWebHook::REFUND
|
||||
refunded = true
|
||||
elsif transaction.transaction_type == RecurlyTransactionWebHook::FAILED_PAYMENT
|
||||
failed = true
|
||||
elsif transaction.transaction_type == RecurlyTransactionWebHook::SUCCESSFUL_PAYMENT
|
||||
succeeded = true
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
void: voided,
|
||||
refund: refunded,
|
||||
fail: failed,
|
||||
success: succeeded
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def self.create_from_shopping_cart(sale, shopping_cart, recurly_subscription_uuid, recurly_adjustment_uuid, recurly_adjustment_credit_uuid)
|
||||
product_info = shopping_cart.product_info
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,10 @@ module JamRuby
|
|||
self.purchased_jam_tracks.count
|
||||
end
|
||||
|
||||
def sales_count
|
||||
self.sales.count
|
||||
end
|
||||
|
||||
def joined_score
|
||||
return nil unless has_attribute?(:score)
|
||||
a = read_attribute(:score)
|
||||
|
|
|
|||
|
|
@ -61,12 +61,20 @@ module JamRuby
|
|||
account
|
||||
end
|
||||
|
||||
def payment_history(current_user)
|
||||
def payment_history(current_user, options ={})
|
||||
|
||||
limit = params[:limit]
|
||||
limit ||= 20
|
||||
limit = limit.to_i
|
||||
|
||||
cursor = options[:cursor]
|
||||
|
||||
payments = []
|
||||
account = get_account(current_user)
|
||||
if(account.present?)
|
||||
begin
|
||||
account.transactions.find_each do |transaction|
|
||||
|
||||
account.transaction.paginate(per_page:limit, cursor:cursor).each do |transaction|
|
||||
# XXX this isn't correct because we create 0 dollar transactions too (for free stuff)
|
||||
#if transaction.amount_in_cents > 0 # Account creation adds a transaction record
|
||||
payments << {
|
||||
|
|
@ -74,7 +82,8 @@ module JamRuby
|
|||
:amount_in_cents => transaction.amount_in_cents,
|
||||
:status => transaction.status,
|
||||
:payment_method => transaction.payment_method,
|
||||
:reference => transaction.reference
|
||||
:reference => transaction.reference,
|
||||
:plan_code => transaction.plan_code
|
||||
}
|
||||
#end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe SaleLineItem do
|
||||
|
||||
let(:user) {FactoryGirl.create(:user)}
|
||||
let(:user2) {FactoryGirl.create(:user)}
|
||||
let(:jam_track) {FactoryGirl.create(:jam_track)}
|
||||
|
||||
describe "associations" do
|
||||
|
||||
it "can find associated recurly transaction web hook" do
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid', nil, nil)
|
||||
transaction = FactoryGirl.create(:recurly_transaction_web_hook, subscription_id: 'some_recurly_uuid')
|
||||
|
||||
sale_line_item.reload
|
||||
sale_line_item.recurly_transactions.should eq([transaction])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "state" do
|
||||
|
||||
it "success" do
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid', nil, nil)
|
||||
transaction = FactoryGirl.create(:recurly_transaction_web_hook, subscription_id: 'some_recurly_uuid')
|
||||
|
||||
sale_line_item.reload
|
||||
sale_line_item.state.should eq({
|
||||
void: false,
|
||||
refund: false,
|
||||
fail: false,
|
||||
success: true
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,6 +2,46 @@ require 'spec_helper'
|
|||
|
||||
describe Sale do
|
||||
|
||||
let(:user) {FactoryGirl.create(:user)}
|
||||
let(:user2) {FactoryGirl.create(:user)}
|
||||
let(:jam_track) {FactoryGirl.create(:jam_track)}
|
||||
|
||||
describe "index" do
|
||||
it "empty" do
|
||||
result = Sale.index(user)
|
||||
result[:query].length.should eq(0)
|
||||
result[:next].should eq(nil)
|
||||
end
|
||||
|
||||
it "one" do
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
|
||||
|
||||
result = Sale.index(user)
|
||||
result[:query].length.should eq(1)
|
||||
result[:next].should eq(nil)
|
||||
end
|
||||
|
||||
it "user filtered correctly" do
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
|
||||
|
||||
result = Sale.index(user)
|
||||
result[:query].length.should eq(1)
|
||||
result[:next].should eq(nil)
|
||||
|
||||
sale2 = Sale.create_jam_track_sale(user2)
|
||||
shopping_cart = ShoppingCart.create(user2, jam_track)
|
||||
sale_line_item2 = SaleLineItem.create_from_shopping_cart(sale2, shopping_cart, nil, 'some_adjustment_uuid', nil)
|
||||
|
||||
result = Sale.index(user)
|
||||
result[:query].length.should eq(1)
|
||||
result[:next].should eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "place_order" do
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ describe ShoppingCart do
|
|||
it "should not add duplicate JamTrack to ShoppingCart" do
|
||||
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
|
||||
cart1.should_not be_nil
|
||||
cart1.errors.any?.should be_false
|
||||
user.reload
|
||||
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
|
||||
cart2.should be_nil
|
||||
cart2.errors.any?.should be_true
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@
|
|||
validProfiles : validProfiles,
|
||||
invalidProfiles : invalidProfiles,
|
||||
isNativeClient: gon.isNativeClient,
|
||||
musician: context.JK.currentUserMusician
|
||||
musician: context.JK.currentUserMusician,
|
||||
sales_count: userDetail.sales_count
|
||||
} , { variable: 'data' }));
|
||||
|
||||
$('#account-content-scroller').html($template);
|
||||
|
|
@ -113,7 +114,7 @@
|
|||
|
||||
// License dialog:
|
||||
$("#account-content-scroller").on('click', '#account-view-license-link', function(evt) {evt.stopPropagation(); app.layout.showDialog('jamtrack-license-dialog'); return false; } );
|
||||
$("#account-content-scroller").on('click', '#account-payment-history-link', function(evt) {evt.stopPropagation(); app.layout.showDialog('jamtrack-payment-history-dialog'); return false; } );
|
||||
$("#account-content-scroller").on('click', '#account-payment-history-link', function(evt) {evt.stopPropagation(); navToPaymentHistory(); return false; } );
|
||||
}
|
||||
|
||||
function renderAccount() {
|
||||
|
|
@ -157,6 +158,10 @@
|
|||
window.location = "/client#/account/audio"
|
||||
}
|
||||
|
||||
function navToPaymentHistory() {
|
||||
window.location = '/client#/account/paymentHistory'
|
||||
}
|
||||
|
||||
// handle update avatar event
|
||||
function updateAvatar(avatar_url) {
|
||||
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
context.JK ||= {}
|
||||
|
||||
context.JK.AccountPaymentHistoryScreen = class AccountPaymentHistoryScreen
|
||||
LIMIT = 20
|
||||
|
||||
constructor: (@app) ->
|
||||
@logger = context.JK.logger
|
||||
@rest = context.JK.Rest()
|
||||
@screen = null
|
||||
@scroller = null
|
||||
@genre = null
|
||||
@artist = null
|
||||
@instrument = null
|
||||
@availability = null
|
||||
@nextPager = null
|
||||
@noMoreSales = null
|
||||
@currentPage = 0
|
||||
@next = null
|
||||
@tbody = null
|
||||
@rowTemplate = null
|
||||
|
||||
beforeShow:(data) =>
|
||||
|
||||
|
||||
afterShow:(data) =>
|
||||
@refresh()
|
||||
|
||||
events:() =>
|
||||
@backBtn.on('click', @onBack)
|
||||
|
||||
onBack:() =>
|
||||
window.location = '/client#/account'
|
||||
return false
|
||||
|
||||
clearResults:() =>
|
||||
@currentPage = 0
|
||||
@tbody.empty()
|
||||
@noMoreSales.hide()
|
||||
@next = null
|
||||
|
||||
|
||||
|
||||
refresh:() =>
|
||||
@currentQuery = this.buildQuery()
|
||||
@rest.getSalesHistory(@currentQuery)
|
||||
.done(@salesHistoryDone)
|
||||
.fail(@salesHistoryFail)
|
||||
|
||||
|
||||
renderPayments:(response) =>
|
||||
if response.entries? && response.entries.length > 0
|
||||
for sale in response.entries
|
||||
amt = sale.recurly_total_in_cents
|
||||
amt = 0 if !amt?
|
||||
|
||||
original_total = sale.state.original_total
|
||||
refund_total = sale.state.refund_total
|
||||
|
||||
refund_state = null
|
||||
if original_total != 0 # the enclosed logic does not work for free purchases
|
||||
if refund_total == original_total
|
||||
refund_state = 'refunded'
|
||||
else if refund_total != 0 and refund_total < original_total
|
||||
refund_state = 'partial refund'
|
||||
|
||||
|
||||
displayAmount = (amt/100).toFixed(2)
|
||||
status = 'paid'
|
||||
|
||||
if sale.state.voided
|
||||
status = 'voided'
|
||||
displayAmount = (0).toFixed(2)
|
||||
else if refund_state?
|
||||
status = refund_state
|
||||
displayAmount = (amt/100).toFixed(2) + " (refunded: #{(refund_total/100).toFixed(2)})"
|
||||
|
||||
description = []
|
||||
for line_item in sale.line_items
|
||||
description.push(line_item.product_info?.name)
|
||||
|
||||
payment = {
|
||||
date: context.JK.formatDate(sale.created_at, true)
|
||||
amount: displayAmount
|
||||
status: status
|
||||
payment_method: 'Credit Card',
|
||||
description: description.join(', ')
|
||||
}
|
||||
|
||||
tr = $(context._.template(@rowTemplate, payment, { variable: 'data' }));
|
||||
@tbody.append(tr);
|
||||
else
|
||||
tr = "<tr><td class='center' colspan='5'>No payments found</td></tr>"
|
||||
@tbody.append(tr);
|
||||
|
||||
salesHistoryDone:(response) =>
|
||||
|
||||
# Turn in to HTML rows and append:
|
||||
#@tbody.html("")
|
||||
console.log("response.next", response)
|
||||
@next = response.next_page
|
||||
@renderPayments(response)
|
||||
if response.next_page == null
|
||||
# if we less results than asked for, end searching
|
||||
@scroller.infinitescroll 'pause'
|
||||
@logger.debug("end of history")
|
||||
if @currentPage > 0
|
||||
@noMoreSales.show()
|
||||
# there are bugs with infinitescroll not removing the 'loading'.
|
||||
# it's most noticeable at the end of the list, so whack all such entries
|
||||
$('.infinite-scroll-loader').remove()
|
||||
else
|
||||
@currentPage++
|
||||
this.buildQuery()
|
||||
this.registerInfiniteScroll()
|
||||
|
||||
|
||||
salesHistoryFail:(jqXHR)=>
|
||||
@noMoreSales.show()
|
||||
@app.notifyServerError jqXHR, 'Payment History Unavailable'
|
||||
|
||||
defaultQuery:() =>
|
||||
query =
|
||||
per_page: LIMIT
|
||||
page: @currentPage+1
|
||||
if @next
|
||||
query.since = @next
|
||||
query
|
||||
|
||||
buildQuery:() =>
|
||||
@currentQuery = this.defaultQuery()
|
||||
|
||||
|
||||
registerInfiniteScroll:() =>
|
||||
that = this
|
||||
@scroller.infinitescroll {
|
||||
behavior: 'local'
|
||||
navSelector: '#account-payment-history .btn-next-pager'
|
||||
nextSelector: '#account-payment-history .btn-next-pager'
|
||||
binder: @scroller
|
||||
dataType: 'json'
|
||||
appendCallback: false
|
||||
prefill: false
|
||||
bufferPx: 100
|
||||
loading:
|
||||
msg: $('<div class="infinite-scroll-loader">Loading ...</div>')
|
||||
img: '/assets/shared/spinner.gif'
|
||||
path: (page) =>
|
||||
'/api/sales?' + $.param(that.buildQuery())
|
||||
|
||||
}, (json, opts) =>
|
||||
this.salesHistoryDone(json)
|
||||
@scroller.infinitescroll 'resume'
|
||||
|
||||
initialize:() =>
|
||||
screenBindings =
|
||||
'beforeShow': this.beforeShow
|
||||
'afterShow': this.afterShow
|
||||
@app.bindScreen 'account/paymentHistory', screenBindings
|
||||
@screen = $('#account-payment-history')
|
||||
@scroller = @screen.find('.content-body-scroller')
|
||||
@nextPager = @screen.find('a.btn-next-pager')
|
||||
@noMoreSales = @screen.find('.end-of-payments-list')
|
||||
@tbody = @screen.find("table.payment-table tbody")
|
||||
@rowTemplate = $('#template-payment-history-row').html()
|
||||
@backBtn = @screen.find('.back')
|
||||
|
||||
if @screen.length == 0
|
||||
throw new Error('@screen must be specified')
|
||||
if @scroller.length == 0
|
||||
throw new Error('@scroller must be specified')
|
||||
if @tbody.length == 0
|
||||
throw new Error('@tbody must be specified')
|
||||
if @noMoreSales.length == 0
|
||||
throw new Error('@noMoreSales must be specified')
|
||||
|
||||
this.events()
|
||||
|
||||
|
||||
|
|
@ -1499,9 +1499,18 @@
|
|||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getBackingTracks(options) {
|
||||
function getSalesHistory(options) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/sales?' + $.param(options),
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
function getBackingTracks(options) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/backing_tracks?' + $.param(options),
|
||||
|
|
@ -1765,6 +1774,7 @@
|
|||
this.getJamtracks = getJamtracks;
|
||||
this.getPurchasedJamTracks = getPurchasedJamTracks;
|
||||
this.getPaymentHistory = getPaymentHistory;
|
||||
this.getSalesHistory = getSalesHistory;
|
||||
this.getJamTrackRight = getJamTrackRight;
|
||||
this.enqueueJamTrack = enqueueJamTrack;
|
||||
this.getBackingTracks = getBackingTracks;
|
||||
|
|
|
|||
|
|
@ -621,11 +621,12 @@
|
|||
}
|
||||
|
||||
// returns Fri May 20, 2013
|
||||
context.JK.formatDate = function (dateString) {
|
||||
context.JK.formatDate = function (dateString, suppressDay) {
|
||||
var date = new Date(dateString);
|
||||
return days[date.getDay()] + ' ' + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear();
|
||||
return (suppressDay ? '' : (days[date.getDay()] + ' ')) + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear();
|
||||
}
|
||||
|
||||
|
||||
context.JK.formatDateYYYYMMDD = function(dateString) {
|
||||
var date = new Date(dateString);
|
||||
return date.getFullYear() + '-' + context.JK.padString((date.getMonth() + 1).toString(), 2) + '-' + context.JK.padString(date.getDate(), 2);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
@import 'common.css.scss';
|
||||
|
||||
#account-payment-history {
|
||||
|
||||
.content-body-scroller {
|
||||
padding:20px;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
table td.loading {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.end-of-list {
|
||||
margin-top:20px;
|
||||
}
|
||||
td {
|
||||
|
||||
&.amount {
|
||||
}
|
||||
|
||||
&.voided {
|
||||
|
||||
text-decoration:line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.account-left {
|
||||
float: left;
|
||||
min-width: 165px;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.account-left h2 {
|
||||
color: #FFFFFF;
|
||||
font-size: 23px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.input-aligner {
|
||||
margin: 10px 14px 20px 0;
|
||||
text-align:right;
|
||||
|
||||
.back {
|
||||
margin-right:22px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
*= require ./findSession
|
||||
*= require ./session
|
||||
*= require ./account
|
||||
*= require ./accountPaymentHistory
|
||||
*= require ./search
|
||||
*= require ./ftue
|
||||
*= require ./jamServer
|
||||
|
|
|
|||
|
|
@ -330,3 +330,8 @@ $fair: #cc9900;
|
|||
border-radius:8px;
|
||||
}
|
||||
|
||||
|
||||
.capitalize {
|
||||
text-transform: capitalize
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -240,8 +240,4 @@
|
|||
|
||||
.jamtrack_buttons {
|
||||
margin: 8px 4px 12px 4px;
|
||||
}
|
||||
|
||||
.capitalize {
|
||||
text-transform: capitalize
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
class ApiSalesController < ApiController
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
data = Sale.index(current_user,
|
||||
page: params[:page],
|
||||
per_page: params[:per_page])
|
||||
|
||||
|
||||
@sales = data[:query]
|
||||
@next = data[:next_page]
|
||||
render "api_sales/index", :layout => nil
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
node :next_page do |page|
|
||||
@next
|
||||
end
|
||||
|
||||
node :entries do |page|
|
||||
partial "api_sales/show", object: @sales
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
object @sale
|
||||
|
||||
attributes :id, :recurly_invoice_id, :recurly_subtotal_in_cents, :recurly_tax_in_cents, :recurly_total_in_cents, :recurly_currency, :sale_type, :recurly_invoice_number, :state, :created_at
|
||||
|
||||
child(:recurly_transactions => :recurly_transactions) {
|
||||
attributes :transaction_type, :amount_in_cents
|
||||
}
|
||||
|
||||
child(:sale_line_items => :line_items) {
|
||||
attributes :id, :product_info
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
object @user
|
||||
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :reuse_card, :purchased_jamtracks_count
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count
|
||||
|
||||
if @user.musician?
|
||||
node :location do @user.location end
|
||||
|
|
@ -10,7 +10,7 @@ end
|
|||
|
||||
# give back more info if the user being fetched is yourself
|
||||
if @user == current_user
|
||||
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications
|
||||
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count
|
||||
|
||||
node :geoiplocation do |user|
|
||||
geoiplocation = current_user.geoiplocation
|
||||
|
|
|
|||
|
|
@ -118,7 +118,13 @@
|
|||
</div>
|
||||
|
||||
<div class="account-mid payments">
|
||||
<a id="account-payment-history-link" href="#">View Payment History</a>
|
||||
<div class="whitespace">
|
||||
{% if (data.sales_count == 0) { %}
|
||||
You have made no purchases.
|
||||
{% } else { %}
|
||||
You have made {{data.sales_count}} purchase{{data.sales_count == 1 ? '' : 's'}}.
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
.screen.secondary layout="screen" layout-id="account/paymentHistory" class="screen secondary" id="account-payment-history"
|
||||
|
||||
.content
|
||||
.content-head
|
||||
.content-icon=image_tag("content/icon_account.png", height:20, width:27 )
|
||||
h1 my account
|
||||
=render "screen_navigation"
|
||||
.content-body
|
||||
.content-body-scroller
|
||||
|
||||
.account-left
|
||||
h2 payment history:
|
||||
table.payment-table
|
||||
thead
|
||||
tr
|
||||
th DATE
|
||||
th METHOD
|
||||
th DESCRIPTION
|
||||
th STATUS
|
||||
th AMOUNT
|
||||
tbody
|
||||
a.btn-next-pager href="/api/sales?page=1" Next
|
||||
.end-of-payments-list.end-of-list="No more payment history"
|
||||
|
||||
|
||||
.input-aligner
|
||||
a.back href="" class="button-grey" BACK
|
||||
br clear="all"
|
||||
|
||||
|
||||
|
||||
|
||||
script#template-payment-history-row type="text/template"
|
||||
tr
|
||||
td
|
||||
| {{data.date}}
|
||||
td.capitalize
|
||||
| {{data.payment_method}}
|
||||
td
|
||||
| {{data.description}}
|
||||
td.capitalize
|
||||
| {{data.status}}
|
||||
td.amount class="{{data.status}}"
|
||||
| ${{data.amount}}
|
||||
|
|
@ -58,6 +58,7 @@
|
|||
<%= render "account_jamtracks" %>
|
||||
<%= render "account_session_detail" %>
|
||||
<%= render "account_session_properties" %>
|
||||
<%= render "account_payment_history" %>
|
||||
<%= render "inviteMusicians" %>
|
||||
<%= render "hoverBand" %>
|
||||
<%= render "hoverFan" %>
|
||||
|
|
@ -214,6 +215,9 @@
|
|||
var accountAudioProfile = new JK.AccountAudioProfile(JK.app);
|
||||
accountAudioProfile.initialize();
|
||||
|
||||
var accountPaymentHistoryScreen = new JK.AccountPaymentHistoryScreen(JK.app);
|
||||
accountPaymentHistoryScreen.initialize();
|
||||
|
||||
var searchResultScreen = new JK.SearchResultScreen(JK.app);
|
||||
searchResultScreen.initialize();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,3 @@
|
|||
.jamtrack_buttons
|
||||
.right
|
||||
a.button-orange class='btnCancel' layout-action='cancel' OK
|
||||
|
||||
script#template-payment-history-row type="text/template"
|
||||
tr
|
||||
td
|
||||
| {{data.date}}
|
||||
td
|
||||
| ${{data.amount}}
|
||||
td.capitalize
|
||||
| {{data.status}}
|
||||
td.capitalize
|
||||
| {{data.payment_method}}
|
||||
td
|
||||
| {{data.reference}}
|
||||
|
|
|
|||
|
|
@ -276,6 +276,9 @@ SampleApp::Application.routes.draw do
|
|||
match '/recurly/update_billing_info' => 'api_recurly#update_billing_info', :via => :put
|
||||
match '/recurly/place_order' => 'api_recurly#place_order', :via => :post
|
||||
|
||||
# sale info
|
||||
match '/sales' => 'api_sales#index', :via => :get
|
||||
|
||||
# login/logout
|
||||
match '/auth_session' => 'api_users#auth_session_create', :via => :post
|
||||
match '/auth_session' => 'api_users#auth_session_delete', :via => :delete
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ApiSalesController do
|
||||
render_views
|
||||
|
||||
let(:user) {FactoryGirl.create(:user)}
|
||||
let(:jam_track) {FactoryGirl.create(:jam_track)}
|
||||
|
||||
before(:each) do
|
||||
controller.current_user = user
|
||||
end
|
||||
|
||||
describe "index" do
|
||||
|
||||
it "empty" do
|
||||
get :index, { :format => 'json'}
|
||||
|
||||
response.should be_success
|
||||
body = JSON.parse(response.body)
|
||||
body['next_page'].should be_nil
|
||||
body['entries'].should eq([])
|
||||
end
|
||||
|
||||
it "one item" do
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
sale.recurly_invoice_id = SecureRandom.uuid
|
||||
sale.save!
|
||||
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
|
||||
|
||||
get :index, { :format => 'json'}
|
||||
response.should be_success
|
||||
body = JSON.parse(response.body)
|
||||
body['next_page'].should be_nil
|
||||
entries = body['entries']
|
||||
entries.should have(1).items
|
||||
sale_entry = entries[0]
|
||||
sale_entry["line_items"].should have(1).items
|
||||
sale_entry["recurly_transactions"].should have(0).items
|
||||
|
||||
|
||||
transaction = FactoryGirl.create(:recurly_transaction_web_hook, invoice_id: sale.recurly_invoice_id, transaction_type: RecurlyTransactionWebHook::VOID)
|
||||
|
||||
get :index, { :format => 'json'}
|
||||
response.should be_success
|
||||
body = JSON.parse(response.body)
|
||||
body['next_page'].should be_nil
|
||||
entries = body['entries']
|
||||
entries.should have(1).items
|
||||
sale_entry = entries[0]
|
||||
sale_entry["line_items"].should have(1).items
|
||||
sale_entry["recurly_transactions"].should have(1).items
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -756,4 +756,25 @@ FactoryGirl.define do
|
|||
bpm 120
|
||||
tap_in_count 3
|
||||
end
|
||||
|
||||
factory :recurly_transaction_web_hook, :class => JamRuby::RecurlyTransactionWebHook do
|
||||
|
||||
transaction_type JamRuby::RecurlyTransactionWebHook::SUCCESSFUL_PAYMENT
|
||||
sequence(:recurly_transaction_id ) { |n| "recurly-transaction-id-#{n}" }
|
||||
sequence(:subscription_id ) { |n| "subscription-id-#{n}" }
|
||||
sequence(:invoice_id ) { |n| "invoice-id-#{n}" }
|
||||
sequence(:invoice_number ) { |n| 1000 + n }
|
||||
invoice_number_prefix nil
|
||||
action 'purchase'
|
||||
status 'success'
|
||||
transaction_at Time.now
|
||||
amount_in_cents 199
|
||||
reference 100000
|
||||
message 'meh'
|
||||
association :user, factory: :user
|
||||
|
||||
factory :recurly_transaction_web_hook_failed do
|
||||
transaction_type JamRuby::RecurlyTransactionWebHook::FAILED_PAYMENT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
subject { page }
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:jam_track) {FactoryGirl.create(:jam_track)}
|
||||
|
||||
before(:each) do
|
||||
UserMailer.deliveries.clear
|
||||
|
|
@ -135,6 +136,26 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "payment history" do
|
||||
|
||||
it "show 1 sale" do
|
||||
|
||||
sale = Sale.create_jam_track_sale(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
|
||||
|
||||
visit "/client#/account"
|
||||
|
||||
find('.account-mid.payments', text: 'You have made 1 purchase.')
|
||||
|
||||
find("#account-payment-history-link").trigger(:click)
|
||||
find('h2', text: 'payment history:')
|
||||
find('table tr td', text: '$0.00') # 1st purchase is free
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue