VRFS-2821 - payment history screen added

This commit is contained in:
Seth Call 2015-04-12 13:45:26 -05:00
parent e42b926a5e
commit ab2925ef88
28 changed files with 665 additions and 41 deletions

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -31,6 +31,7 @@
*= require ./findSession
*= require ./session
*= require ./account
*= require ./accountPaymentHistory
*= require ./search
*= require ./ftue
*= require ./jamServer

View File

@ -330,3 +330,8 @@ $fair: #cc9900;
border-radius:8px;
}
.capitalize {
text-transform: capitalize
}

View File

@ -240,8 +240,4 @@
.jamtrack_buttons {
margin: 8px 4px 12px 4px;
}
.capitalize {
text-transform: capitalize
}

View File

@ -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

View File

@ -0,0 +1,7 @@
node :next_page do |page|
@next
end
node :entries do |page|
partial "api_sales/show", object: @sales
end

View File

@ -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
}

View File

@ -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

View File

@ -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">

View File

@ -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}}

View File

@ -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();

View File

@ -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}}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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