Skip to content

Commit b932d80

Browse files
committed
Merge branch 'main' of github.com:cs169/snapcon
2 parents f19a779 + 23cbde8 commit b932d80

19 files changed

+166
-141
lines changed

app/assets/javascripts/osem-tickets.js

+13-50
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,34 @@
1-
2-
function update_currency_rates(data) {
3-
data.forEach(function(conversion) {
4-
var key = conversion.from_currency + "_to_" + conversion.to_currency;
5-
window.currencyRates[key] = { rate: conversion.rate, symbol: conversion.symbol };
6-
});
7-
$('.quantity').each(function() {
8-
update_price($(this));
9-
});
10-
}
11-
12-
function fetch_currency_rates(){
13-
//todo get conference dynamically
14-
var conferenceIdentifier = $('meta[name="conference-short-title"]').data('short-title');
15-
var requestUrl = "/admin/conferences/" + conferenceIdentifier + "/currency_conversions";
16-
$.ajax({
17-
url: requestUrl,
18-
type: "GET",
19-
dataType: "json",
20-
success: function(data) {
21-
update_currency_rates(data);
22-
},
23-
error: function(xhr, status, error) {
24-
console.error("Failed to fetch currency rates: " + error);
25-
}
26-
});
27-
28-
}
29-
301
function update_price($this){
31-
322
var id = $this.data('id');
33-
343
var selectedCurrency = $('#currency_selector').val();
35-
var baseCurrency = "USD"; // todo fetch from controller
36-
var conversionKey = baseCurrency + "_to_" + selectedCurrency;
37-
var conversionData = window.currencyRates[conversionKey] || { rate: 1, symbol: '$' };
4+
var conversionData = window.currencyRates[selectedCurrency] || { rate: 1, symbol: '$' };
385
var conversionRate = conversionData.rate;
396

407
var originalPrice = parseFloat($('#price_'+id).data('original-price'));
418
var convertedPrice = originalPrice*conversionRate;
429

43-
4410
// Calculate price for row
45-
var value = $this.val();
46-
$('#price_' + id).text(conversionData.symbol + " " + convertedPrice.toFixed(2));
47-
48-
49-
$('#total_row_' + id).text(conversionData.symbol + " " + (value * convertedPrice).toFixed(2));
11+
$('#currency_symbol_' + id).text(conversionData.symbol);
12+
$('#total_currency_symbol_' + id).text(conversionData.symbol);
13+
$('#price_' + id).text(convertedPrice.toFixed(2));
14+
$('#total_row_' + id).text((convertedPrice * $this.val()).toFixed(2));
5015

5116
// Calculate total price
5217
var total = 0;
53-
$('.total_row').each(function( index ) {
54-
//a bit hacky, look into setting symbol as an attribute by itself instead of concatenating
55-
total += parseFloat($(this).text().replace(conversionData.symbol, '').trim());
18+
$('span[id^="total_row_"]').each(function() {
19+
total += parseFloat($(this).text());
5620
});
57-
$('#total_price').text(conversionData.symbol + total.toFixed(2));
21+
$('#total-currency-symbol').text(conversionData.symbol);
22+
$('#total-price').text(total.toFixed(2));
5823
}
5924

6025
$( document ).ready(function() {
6126

62-
// Initialize currency conversion rates
6327
window.currencyRates = {};
64-
65-
if($('#currency_selector').length > 0) {
66-
fetch_currency_rates();
28+
if (window.currencyMeta) {
29+
window.currencyMeta.forEach(function(currencyInfo) {
30+
window.currencyRates[currencyInfo.currency] = { rate: currencyInfo.rate, symbol: currencyInfo.symbol };
31+
});
6732
}
6833

6934
$('.quantity').each(function() {
@@ -77,8 +42,6 @@ $( document ).ready(function() {
7742
$('[data-toggle="tooltip"]').tooltip()
7843
});
7944

80-
81-
// Add change event listener to currency selector
8245
$('#currency_selector').change(function() {
8346
$('.quantity').each(function() {
8447
update_price($(this));

app/controllers/admin/currency_conversions_controller.rb

+1-16
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,7 @@ class CurrencyConversionsController < Admin::BaseController
66
before_action :set_currency_options, only: [:new, :create, :edit]
77

88
# GET /currency_conversions
9-
def index
10-
#todo: rescue currency that doesnt exist
11-
currency_conversions_with_symbols = @conference.currency_conversions.map do |conversion|
12-
{
13-
from_currency: conversion.from_currency,
14-
to_currency: conversion.to_currency,
15-
rate: conversion.rate,
16-
symbol: Money::Currency.new(conversion.to_currency).symbol
17-
}
18-
end
19-
20-
respond_to do |format|
21-
format.html
22-
format.json { render json: currency_conversions_with_symbols }
23-
end
24-
end
9+
def index; end
2510

2611
# GET /currency_conversions/1
2712
def show; end

app/controllers/admin/tickets_controller.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def ticket_params
9797

9898
def gift_ticket_params
9999
response = params.require(:ticket_purchase).permit(:user_id)
100-
response.merge(paid: true, amount_paid: 0, conference: @conference)
100+
response.merge(paid: true, amount_paid: 0, conference: @conference, currency: @conference.tickets.first.price_currency)
101101
end
102102
end
103103
end

app/controllers/payments_controller.rb

+9-33
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,33 @@ class PaymentsController < ApplicationController
55
load_and_authorize_resource
66
load_resource :conference, find_by: :short_title
77
authorize_resource :conference_registrations, class: Registration
8-
before_action :load_currency_conversions, only: [:new, :create]
98

109
def index
1110
@payments = current_user.payments
1211
end
1312

1413
def new
15-
# todo: use "base currency"
14+
# TODO: use "base currency"
1615
session[:selected_currency] = params[:currency] if params[:currency].present?
17-
selected_currency = session[:selected_currency] || 'USD'
18-
from_currency = "USD"
16+
selected_currency = session[:selected_currency] || @conference.tickets.first.price_currency
17+
from_currency = @conference.tickets.first.price_currency
1918

20-
@total_amount_to_pay = convert_currency(Ticket.total_price(@conference, current_user, paid: false).amount, from_currency, selected_currency)
19+
@total_amount_to_pay = CurrencyConversion.convert_currency(@conference, Ticket.total_price(@conference, current_user, paid: false), from_currency, selected_currency)
2120
raise CanCan::AccessDenied.new('Nothing to pay for!', :new, Payment) if @total_amount_to_pay.zero?
21+
raise CanCan::AccessDenied.new('Selected currency is invalid!', :new, Payment) if @total_amount_to_pay.negative?
2222

2323
@has_registration_ticket = params[:has_registration_ticket]
2424
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
25-
#a way to display the currency values in the view, but there might be a better way to do this.
25+
2626
@converted_prices = {}
2727
@unpaid_ticket_purchases.each do |ticket_purchase|
28-
#hardcoded
29-
@converted_prices[ticket_purchase.id] = convert_currency(ticket_purchase.price.amount, 'USD', selected_currency)
28+
@converted_prices[ticket_purchase.id] = ticket_purchase.amount_paid
3029
end
3130
@currency = selected_currency
3231
end
3332

3433
def create
35-
@payment = Payment.new (payment_params)
34+
@payment = Payment.new payment_params
3635

3736
if @payment.purchase && @payment.save
3837
update_purchased_ticket_purchases
@@ -52,7 +51,7 @@ def create
5251
notice: 'Thanks! Your ticket is booked successfully.'
5352
end
5453
else
55-
@total_amount_to_pay = convert_currency(Ticket.total_price(@conference, current_user, paid: false).amount, from_currency, selected_currency)
54+
@total_amount_to_pay = convert_currency(@conference, Ticket.total_price(@conference, current_user, paid: false), from_currency, selected_currency)
5655
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
5756
flash.now[:error] = @payment.errors.full_messages.to_sentence + ' Please try again with correct credentials.'
5857
render :new
@@ -73,27 +72,4 @@ def update_purchased_ticket_purchases
7372
ticket_purchase.pay(@payment)
7473
end
7574
end
76-
77-
def load_currency_conversions
78-
@currency_conversions = @conference.currency_conversions
79-
end
80-
81-
def convert_currency(amount, from_currency, to_currency)
82-
update_rate(from_currency, to_currency)
83-
84-
money_amount = Money.from_amount(amount, from_currency)
85-
converted_amount = money_amount.exchange_to(to_currency)
86-
converted_amount
87-
end
88-
89-
def update_rate(from_currency, to_currency)
90-
conversion = @currency_conversions.find_by(from_currency: from_currency, to_currency: to_currency)
91-
if conversion
92-
Money.add_rate(from_currency, to_currency, conversion.rate)
93-
else
94-
#If no conversion is found. Typically only possible if base to base. Maybe make this error out.
95-
Money.add_rate(from_currency, to_currency, 1) unless from_currency == to_currency
96-
end
97-
end
98-
9975
end

app/controllers/ticket_purchases_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class TicketPurchasesController < ApplicationController
44
before_action :authenticate_user!
55
load_resource :conference, find_by: :short_title
66
authorize_resource :conference_registrations, class: Registration
7+
authorize_resource
78

89
def create
910
current_user.ticket_purchases.by_conference(@conference).unpaid.destroy_all

app/controllers/tickets_controller.rb

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ class TicketsController < ApplicationController
44
before_action :authenticate_user!
55
load_resource :conference, find_by: :short_title
66
before_action :load_tickets
7-
87
before_action :load_currency_conversions, only: :index
9-
108
authorize_resource :ticket, through: :conference
119
authorize_resource :conference_registrations, class: Registration
1210
before_action :check_load_resource, only: :index
@@ -26,6 +24,12 @@ def load_tickets
2624

2725
def load_currency_conversions
2826
@currency_conversions = @conference.currency_conversions
29-
@currencies = @currency_conversions.map(&:to_currency).uniq
27+
@currencies = [@conference.tickets.first.price_currency] | @currency_conversions.map(&:to_currency).uniq
28+
@currency_meta = @currencies.map do |currency|
29+
currency_conversion = @currency_conversions.find { |c| c.to_currency == currency }
30+
rate = currency_conversion ? currency_conversion.rate : 1
31+
symbol = Money::Currency.new(currency).symbol
32+
{ currency: currency, rate: rate, symbol: symbol }
33+
end
3034
end
3135
end

app/models/currency_conversion.rb

+12
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,16 @@ class CurrencyConversion < ApplicationRecord
2525
belongs_to :conference
2626
validates :rate, numericality: { greater_than: 0 }
2727
validates :from_currency, uniqueness: { scope: :to_currency }, on: :create
28+
29+
def self.convert_currency(conference, amount, from_currency, to_currency)
30+
conversion = conference.currency_conversions.find_by(from_currency: from_currency, to_currency: to_currency)
31+
if conversion
32+
Money.add_rate(from_currency, to_currency, conversion.rate)
33+
amount.exchange_to(to_currency)
34+
elsif from_currency == to_currency
35+
amount
36+
else
37+
Money.from_amount(-1, 'USD')
38+
end
39+
end
2840
end

app/models/payment.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Payment < ApplicationRecord
3434
}
3535

3636
def amount_to_pay
37-
Ticket.total_price(conference, user, paid: false).cents
37+
CurrencyConversion.convert_currency(conference, Ticket.total_price(conference, user, paid: false), conference.tickets.first.price_currency, currency).cents
3838
end
3939

4040
def stripe_description

app/models/ticket_purchase.rb

+9-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class TicketPurchase < ApplicationRecord
2323
belongs_to :conference
2424
belongs_to :payment
2525

26-
validates :ticket_id, :user_id, :conference_id, :quantity, presence: true
26+
validates :ticket_id, :user_id, :conference_id, :quantity, :currency, presence: true
2727
validate :one_registration_ticket_per_user
2828
validate :registration_ticket_already_purchased, on: :create
2929
validates :quantity, numericality: { greater_than: 0 }
@@ -65,14 +65,19 @@ def self.purchase(conference, user, purchases, currency)
6565
end
6666

6767
def self.purchase_ticket(conference, quantity, ticket, user, currency)
68+
converted_amount = CurrencyConversion.convert_currency(conference, ticket.price, ticket.price_currency, currency)
69+
if converted_amount < 0
70+
errors.push('Currency is invalid')
71+
purchase.pay(nil)
72+
end
6873
if quantity > 0
6974
purchase = new(ticket_id: ticket.id,
7075
conference_id: conference.id,
7176
user_id: user.id,
7277
quantity: quantity,
73-
amount_paid: ticket.price,
74-
currency: currency)
75-
purchase.pay(nil) if ticket.price_cents.zero?
78+
amount_paid: converted_amount,
79+
currency: currency)
80+
purchase.pay(nil) if converted_amount.zero?
7681
end
7782
purchase
7883
end

app/views/payments/_payment.html.haml

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
= humanized_money_with_symbol @converted_prices[ticket.id]
1919
%td
2020
= humanized_money_with_symbol (@converted_prices[ticket.id] * ticket.quantity)
21-
2221
= form_tag conference_payments_path(@conference.short_title, :has_registration_ticket => @has_registration_ticket) do
2322
%script.stripe-button{ src: "https://checkout.stripe.com/checkout.js",
2423
data: { amount: @total_amount_to_pay.cents,

app/views/tickets/_ticket.html.haml

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
You currently have a registraction ticket.
2424

2525
%td.col-sm-1.col-md-1.text-center
26+
%span.currency-symbol{id: "currency_symbol_#{ticket.id}"}
27+
= ticket.price.currency.symbol
2628
%span{id: "price_#{ticket.id}", 'data-original-price': humanized_money(ticket.price).to_s}
2729
= humanized_money ticket.price
2830
%td.col-sm-1.col-md-1.text-center
2931
%strong
30-
%span.total_row{id: "total_row_#{ticket.id}"}
32+
%span.total-currency-symbol{id: "total_currency_symbol_#{ticket.id}"}
33+
= ticket.price.currency.symbol
34+
%span.total-row{id: "total_row_#{ticket.id}"}
3135
0

app/views/tickets/index.html.haml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
%meta{name: 'conference-short-title', data: { short_title: @conference.short_title }}
2+
:javascript
3+
window.currencyMeta = #{@currency_meta.to_json.html_safe};
24
.container
35
.row
46
.col-md-8
@@ -38,8 +40,8 @@
3840
%td.col-sm-1.col-md-1.text-center
3941
%h4
4042
%strong
41-
%span{ id: 'total_price' }
42-
0
43+
%span#total-currency-symbol $
44+
%span#total-price 0
4345
.pull-right
4446
.btn-group-vertical
4547
= button_tag(type: 'submit', class: 'btn btn-success btn-lg') do

spec/factories/payments.rb

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
user
2121
conference
2222
status { 'unpaid' }
23+
currency { 'USD' }
2324
end
2425
end

spec/factories/ticket_purchases.rb

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
conference
2323
ticket
2424
quantity { 10 }
25+
currency { 'USD' }
2526
factory :paid_ticket_purchase do
2627
after(:build) do |ticket_purchase|
2728
payment = create(:payment)

spec/features/currency_conversions_spec.rb

-21
Original file line numberDiff line numberDiff line change
@@ -106,26 +106,5 @@
106106
end
107107
end
108108
end
109-
110-
it 'buys a ticket in EUR', feature: true, js: true do
111-
create(:ticket, conference: conference, price_cents: 10_000)
112-
conference.currency_conversions << create(:currency_conversion)
113-
visit conference_tickets_path(conference.short_title)
114-
select 'EUR', from: 'currency_selector'
115-
find('.quantity', match: :first).set(1)
116-
expect(page).to have_content('89.00') # 100 USD to EUR at a rate of 0.89
117-
end
118-
119-
it 'switches between EUR and GBP', feature: true, js: true do
120-
create(:ticket, conference: conference, price_cents: 10_000)
121-
conference.currency_conversions << create(:currency_conversion)
122-
create(:currency_conversion, from_currency: 'USD', to_currency: 'GBP', rate: 0.75, conference: conference)
123-
visit conference_tickets_path(conference.short_title)
124-
select 'EUR', from: 'currency_selector'
125-
find('.quantity', match: :first).set(1)
126-
expect(page).to have_content('89.00')
127-
select 'GBP', from: 'currency_selector'
128-
expect(page).to have_content('75.00')
129-
end
130109
end
131110
end

0 commit comments

Comments
 (0)