* wip
This commit is contained in:
parent
f29a9d7465
commit
04aa1a9b3c
|
|
@ -8,19 +8,20 @@ module JamRuby
|
|||
validates :cart_id, presence: true
|
||||
validates :cart_type, presence: true
|
||||
validates :cart_class_name, presence: true
|
||||
validates :marked_for_redeem, numericality: {only_integer: true}
|
||||
validates :marked_for_redeem, numericality: {only_integer: true}
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
def product_info
|
||||
product = self.cart_product
|
||||
{name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem:marked_for_redeem} unless product.nil?
|
||||
{name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem} unless product.nil?
|
||||
end
|
||||
|
||||
# multiply (quantity - redeemable) by price
|
||||
def total_price(product)
|
||||
(quantity - marked_for_redeem) * product.price
|
||||
end
|
||||
|
||||
def cart_product
|
||||
self.cart_class_name.classify.constantize.find_by_id self.cart_id unless self.cart_class_name.blank?
|
||||
end
|
||||
|
|
@ -57,7 +58,7 @@ module JamRuby
|
|||
mark_redeem = false
|
||||
if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
|
||||
mark_redeem = true # start out assuming we can redeem...
|
||||
shopping_carts.each do |shopping_cart|
|
||||
any_user.shopping_carts.each do |shopping_cart|
|
||||
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
|
||||
if shopping_cart.cart_type == product.class::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
|
||||
mark_redeem = false
|
||||
|
|
@ -67,5 +68,31 @@ module JamRuby
|
|||
end
|
||||
mark_redeem
|
||||
end
|
||||
|
||||
# adds a jam_track to cart, checking for promotions
|
||||
def self.add_jam_track_to_cart(any_user, jam_track)
|
||||
ShoppingCart.transaction do
|
||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||
@cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
|
||||
end
|
||||
end
|
||||
|
||||
# deletes a jam track from the shopping cart, updating redeem flag as necessary
|
||||
def self.remove_jam_track_from_cart(any_user, cart)
|
||||
ShoppingCart.transaction do
|
||||
cart.destroy
|
||||
# check if we should move the redemption
|
||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||
|
||||
carts = any_user.shopping_carts
|
||||
|
||||
# if we find any carts on the account, mark one redeemable
|
||||
if mark_redeem && carts.length > 0
|
||||
carts[0].redeem(mark_redeem)
|
||||
carts[0].save
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
var $backBtn = null;
|
||||
var $orderPrompt = null;
|
||||
var $emptyCartPrompt = null;
|
||||
var $noAccountInfoPrompt = null;
|
||||
|
||||
|
||||
function beforeShow() {
|
||||
|
|
@ -56,9 +57,10 @@
|
|||
function beforeShowOrder() {
|
||||
$orderPrompt.addClass('hidden')
|
||||
$emptyCartPrompt.addClass('hidden')
|
||||
$noAccountInfoPrompt.addClass('hidden')
|
||||
$orderPanel.removeClass("hidden")
|
||||
$thanksPanel.addClass("hidden")
|
||||
$orderContent.find(".place-order").addClass('disabled').off('click', placeOrder)
|
||||
$screen.find(".place-order").addClass('disabled').off('click', placeOrder)
|
||||
step = 3;
|
||||
renderNavigation();
|
||||
populateOrderPage();
|
||||
|
|
@ -77,6 +79,7 @@
|
|||
.fail(function(jqXHR) {
|
||||
if(jqXHR.status == 404) {
|
||||
// no account for this user
|
||||
$noAccountInfoPrompt.removeClass('hidden')
|
||||
app.notify({ title: "No account information",
|
||||
text: "Please restart the checkout process." },
|
||||
null,
|
||||
|
|
@ -97,9 +100,19 @@
|
|||
$.each(carts, function(index, cart) {
|
||||
sub_total += parseFloat(cart.product_info.total_price)
|
||||
});
|
||||
//data.grand_total = (sub_total + taxes).toFixed(2)
|
||||
data.sub_total = sub_total.toFixed(2)
|
||||
//data.taxes = taxes.toFixed(2)
|
||||
if(carts.length == 0) {
|
||||
data.grand_total = '-.--'
|
||||
data.sub_total = '-.--'
|
||||
data.taxes = '-.--'
|
||||
data.shipping_handling = '-.--'
|
||||
}
|
||||
else {
|
||||
data.grand_total = 'Calculating...'
|
||||
data.sub_total = '$' + sub_total.toFixed(2)
|
||||
data.taxes = 'Calculating...'
|
||||
data.shipping_handling = '$0.00'
|
||||
}
|
||||
|
||||
data.carts = carts
|
||||
data.billing_info = recurlyAccountInfo.billing_info
|
||||
data.shipping_info = recurlyAccountInfo.address
|
||||
|
|
@ -121,12 +134,14 @@
|
|||
if(carts.length == 0) {
|
||||
$orderPrompt.addClass('hidden')
|
||||
$emptyCartPrompt.removeClass('hidden')
|
||||
$noAccountInfoPrompt.addClass('hidden')
|
||||
$placeOrder.addClass('disabled')
|
||||
}
|
||||
else {
|
||||
logger.debug("cart has " + carts.length + " items in it")
|
||||
$orderPrompt.removeClass('hidden')
|
||||
$emptyCartPrompt.addClass('hidden')
|
||||
$noAccountInfoPrompt.addClass('hidden')
|
||||
$placeOrder.removeClass('disabled').on('click', placeOrder)
|
||||
|
||||
var planPricing = {}
|
||||
|
|
@ -184,7 +199,7 @@
|
|||
|
||||
if(allResolved) {
|
||||
$screen.find('.order-right-page .order-items-value.taxes').text('$' + totalTax.toFixed(2))
|
||||
$screen.find('.order-right-page .order-items-value.order-total').text('$' + totalPrice.toFixed(2))
|
||||
$screen.find('.order-right-page .order-items-value.grand-total').text('$' + totalPrice.toFixed(2))
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -349,8 +364,9 @@
|
|||
$purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header");
|
||||
$purchasedJamTracks = $purchasedJamTrack.find(".purchased-list")
|
||||
$backBtn = $screen.find('.back');
|
||||
$orderPrompt = $screen.find('.order-prompt');
|
||||
$emptyCartPrompt = $screen.find('.empty-cart-prompt');
|
||||
$orderPrompt = $screen.find('.order-prompt');
|
||||
$emptyCartPrompt = $screen.find('.empty-cart-prompt');
|
||||
$noAccountInfoPrompt = $screen.find('.no-account-info-prompt');
|
||||
$orderContent = $orderPanel.find(".order-content");
|
||||
|
||||
if ($screen.length == 0) throw "$screen must be specified";
|
||||
|
|
|
|||
|
|
@ -34,15 +34,13 @@
|
|||
}
|
||||
else {
|
||||
app.user().done(function(user) {
|
||||
if(user.reuse_card) {
|
||||
if(user.has_recurly_account && user.reuse_card) {
|
||||
window.location = '/client#/checkoutOrder';
|
||||
}
|
||||
else {
|
||||
window.location = '/client#/checkoutPayment';
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@
|
|||
background-color:#262626;
|
||||
@include border_box_sizing;
|
||||
|
||||
.order-total {
|
||||
.grand-total {
|
||||
color: #ed3618;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ class ApiShoppingCartsController < ApiController
|
|||
raise StateError, "Invalid JamTrack."
|
||||
end
|
||||
|
||||
ShoppingCart.transaction do
|
||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||
@cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
|
||||
end
|
||||
|
||||
@cart = ShoppingCart.add_jam_track_to_cart(any_user, jam_track)
|
||||
|
||||
if @cart.errors.any?
|
||||
response.status = :unprocessable_entity
|
||||
|
|
@ -50,20 +46,7 @@ class ApiShoppingCartsController < ApiController
|
|||
@cart = any_user.shopping_carts.find_by_id(params[:id])
|
||||
raise StateError, "Invalid Cart." if @cart.nil?
|
||||
|
||||
ShoppingCart.transaction do
|
||||
@cart.destroy
|
||||
|
||||
# check if we should move the redemption
|
||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||
|
||||
carts = any_user.shopping_carts
|
||||
|
||||
# if we find any carts on the account, mark one redeemable
|
||||
if mark_redeem && carts.length > 0
|
||||
carts[0].redeem(mark_redeem)
|
||||
carts[0].save
|
||||
end
|
||||
end
|
||||
ShoppingCart.remove_jam_track_from_cart(any_user, @cart)
|
||||
|
||||
respond_with responder: ApiResponder, :status => 204
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ div layout="screen" layout-id="checkoutOrder" id="checkoutOrderScreen" class="sc
|
|||
| You have nothing in your cart. You can go browse for JamTracks
|
||||
a href="/client#/jamtrack" here
|
||||
| .
|
||||
p.no-account-info-prompt.hidden
|
||||
| You have no billing info. Please go back to the
|
||||
a href="/client#/checkoutPayment" Payment
|
||||
| page to enter your billing info.
|
||||
.order-content
|
||||
|
||||
|
||||
|
|
@ -122,22 +126,22 @@ script type='text/template' id='template-order-content'
|
|||
.clearall
|
||||
|
||||
.billing-caption ORDER SUMMARY:
|
||||
.order-items-header Order items:
|
||||
.order-items-value ${{data.sub_total}}
|
||||
.order-items-header.order-total Order items:
|
||||
.order-items-value.order-total= "{{data.sub_total}}"
|
||||
.clearall
|
||||
.order-items-header Shipping & handling:
|
||||
.order-items-value $0.00
|
||||
.order-items-header.shipping-handling Shipping & handling:
|
||||
.order-items-value.shipping-handling= "{{data.shipping_handling}}"
|
||||
.clearall
|
||||
.line
|
||||
.order-items-header Total before tax:
|
||||
.order-items-value ${{data.sub_total}}
|
||||
.order-items-header.sub-total Total before tax:
|
||||
.order-items-value.sub-total= "{{data.sub_total}}"
|
||||
.clearall
|
||||
.order-items-header.taxes Taxes:
|
||||
.order-items-value.taxes Calculating...
|
||||
.order-items-value.taxes= "{{data.taxes}}"
|
||||
.clearall
|
||||
.line
|
||||
.order-items-header.order-total Order total:
|
||||
.order-items-value.order-total Calculating...
|
||||
.order-items-header.grand-total Order total:
|
||||
.order-items-value.grand-total= "{{data.grand_total}}"
|
||||
.clearall
|
||||
.order-help
|
||||
span By placing your order, you agree to JamKazam's
|
||||
|
|
|
|||
|
|
@ -3,12 +3,45 @@ require 'spec_helper'
|
|||
describe "Checkout", :js => true, :type => :feature, :capybara_feature => true do
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:jamtrack_acdc_backinblack) { FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack') }
|
||||
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
|
||||
let(:jamtrack_pearljam_evenflow) { @jamtrack_pearljam_evenflow }
|
||||
|
||||
let(:billing_info) {
|
||||
{
|
||||
first_name: 'Seth',
|
||||
last_name: 'Call',
|
||||
address1: '10704 Buckthorn Drive',
|
||||
city: 'Austin',
|
||||
state: 'Texas',
|
||||
country: 'US',
|
||||
zip: '78759',
|
||||
number: '4111111111111111',
|
||||
month: '08',
|
||||
year: '2017',
|
||||
verification_value: '012'
|
||||
}
|
||||
}
|
||||
|
||||
def create_account(user, billing_info)
|
||||
@recurlyClient.create_account(user, billing_info)
|
||||
@created_accounts << user
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.current_driver = Capybara.javascript_driver
|
||||
Capybara.default_wait_time = 30 # these tests are SLOOOOOW
|
||||
Capybara.default_wait_time = 60 # these tests are SLOOOOOW
|
||||
|
||||
@recurlyClient = RecurlyClient.new
|
||||
@created_accounts = []
|
||||
|
||||
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
|
||||
@jamtrack_pearljam_evenflow = FactoryGirl.create(:jam_track, name: 'Even Flow', original_artist: 'Pearl Jam', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-pearljam-evenflow')
|
||||
|
||||
# make sure plans are there
|
||||
@recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack)
|
||||
@recurlyClient.create_jam_track_plan(@jamtrack_pearljam_evenflow) unless @recurlyClient.find_jam_track_plan(@jamtrack_pearljam_evenflow)
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -22,13 +55,28 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
stub_const("APP_CONFIG", web_config)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
@created_accounts.each do |user|
|
||||
if user.recurly_code
|
||||
begin
|
||||
@account = Recurly::Account.find(user.recurly_code)
|
||||
if @account.present?
|
||||
@account.destroy
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
def verify_nav(selected)
|
||||
|
||||
3.times do |i|
|
||||
badge = i + 1
|
||||
|
||||
if i == selected
|
||||
if badge == selected
|
||||
find('.badge-number', text:badge)
|
||||
else
|
||||
find('.badge-number.disabled', text:badge)
|
||||
|
|
@ -222,14 +270,197 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
|
||||
it "payment shows saved card info correctly if user has billing info and reuse_card set to true" do
|
||||
|
||||
puts "jamtrack_acdc_backinblack #{jamtrack_acdc_backinblack}"
|
||||
ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack)
|
||||
|
||||
user.reuse_card = true
|
||||
user.recurly_code = user.id
|
||||
@recurlyClient.create_account(user, billing_info)
|
||||
user.has_redeemable_jamtrack = true
|
||||
user.save!
|
||||
|
||||
fast_signin(user, '/client#/checkoutPayment')
|
||||
|
||||
# ok, the user has a free jamtrack... verify that display confirms this
|
||||
find('p.payment-prompt.free-jamtrack')
|
||||
|
||||
# verify that all billing info looks disabled
|
||||
have_field('billing-first-name', disabled: true)
|
||||
have_field('billing-last-name', disabled: true)
|
||||
have_field('billing-address1', disabled: true)
|
||||
have_field('billing-address2', disabled: true)
|
||||
have_field('billing-city', disabled: true)
|
||||
have_field('billing-state', disabled: true)
|
||||
have_field('billing-zip', disabled: true)
|
||||
have_field('billing-country', disabled: true)
|
||||
have_field('card-number', disabled: true)
|
||||
have_field('card_expire-date_2i', disabled: true)
|
||||
have_field('card_expire-date_1i', disabled: true)
|
||||
have_field('card-number', disabled: true)
|
||||
have_field('card-verify', disabled: true)
|
||||
|
||||
# verify that the use current card checkbox is checked, and that the 'save card' checkbox is checking
|
||||
find('#reuse-existing-card').checked?.should be_true
|
||||
find('#save-card:checked').checked?.should be_true
|
||||
|
||||
# then uncheck 'reuse-existing-card', which should re-enable all the fields that were just disabled
|
||||
find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click)
|
||||
|
||||
# verify that all billing info looks enabled now, since 'reuse-existing-card' was unchecked
|
||||
have_field('billing-first-name', disabled: false)
|
||||
have_field('billing-last-name', disabled: false)
|
||||
have_field('billing-address1', disabled: false)
|
||||
have_field('billing-address2', disabled: false)
|
||||
have_field('billing-city', disabled: false)
|
||||
have_field('billing-state', disabled: false)
|
||||
have_field('billing-zip', disabled: false)
|
||||
have_field('billing-country', disabled: false)
|
||||
have_field('card-number', disabled: false)
|
||||
have_field('card_expire-date_2i', disabled: false)
|
||||
have_field('card_expire-date_1i', disabled: false)
|
||||
have_field('card-number', disabled: false)
|
||||
have_field('card-verify', disabled: false)
|
||||
|
||||
# ok, we want to fiddle some values, and later prove that they will be ignored once we set reuse-existing-card back to checked
|
||||
fill_in 'billing-first-name', with: 'Bobby'
|
||||
fill_in 'billing-last-name', with: 'Junk'
|
||||
fill_in 'billing-address1', with: '10702 Buckthorn Drive'
|
||||
|
||||
# flip it back to reuse existing
|
||||
find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click)
|
||||
|
||||
# hit next... we should move on to the payment screen
|
||||
|
||||
# try to submit, and see order page
|
||||
find('#payment-info-next').trigger(:click)
|
||||
|
||||
# find empty shopping cart prompt notice
|
||||
find('p.order-prompt')
|
||||
|
||||
account = @recurlyClient.get_account(user)
|
||||
account.billing_info.address1.should eq(billing_info[:address1])
|
||||
account.billing_info.first_name.should eq(billing_info[:first_name])
|
||||
account.billing_info.last_name.should eq(billing_info[:last_name])
|
||||
|
||||
find('.order-items-value.sub-total', text:'0.00')
|
||||
find('.order-items-value.taxes', text:'0.00')
|
||||
find('.order-items-value.order-total', text:'0.00')
|
||||
end
|
||||
|
||||
it "payment allows user to enter new billing info" do
|
||||
|
||||
ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack)
|
||||
|
||||
user.reuse_card = true
|
||||
@recurlyClient.create_account(user, billing_info)
|
||||
user.has_redeemable_jamtrack = true
|
||||
user.save!
|
||||
|
||||
fast_signin(user, '/client#/checkoutPayment')
|
||||
|
||||
# ok, the user has a free jamtrack... verify that display confirms this
|
||||
find('p.payment-prompt.free-jamtrack')
|
||||
|
||||
# verify that the use current card checkbox is checked, and that the 'save card' checkbox is checking
|
||||
find('#reuse-existing-card').checked?.should be_true
|
||||
find('#save-card:checked').checked?.should be_true
|
||||
|
||||
# then uncheck 'reuse-existing-card', which should re-enable all the fields that were just disabled
|
||||
find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click)
|
||||
|
||||
# ok, we want to fiddle some values, and later prove that they will be ignored once we set reuse-existing-card back to checked
|
||||
fill_in 'billing-first-name', with: 'Bobby'
|
||||
fill_in 'billing-last-name', with: 'Junk'
|
||||
fill_in 'billing-address1', with: '10702 Buckthorn Drive'
|
||||
fill_in 'card-number', with: '4111111111111111'
|
||||
fill_in 'card-verify', with: '013'
|
||||
|
||||
# hit next... we should move on to the payment screen
|
||||
|
||||
# try to submit, and see order page
|
||||
find('#payment-info-next').trigger(:click)
|
||||
|
||||
# find empty shopping cart prompt notice
|
||||
find('p.order-prompt')
|
||||
|
||||
account = @recurlyClient.get_account(user)
|
||||
account.billing_info.first_name.should eq('Bobby')
|
||||
account.billing_info.last_name.should eq('Junk')
|
||||
account.billing_info.address1.should eq('10702 Buckthorn Drive')
|
||||
|
||||
find('.order-items-value.sub-total', text:'0.00')
|
||||
find('.order-items-value.taxes', text:'0.00')
|
||||
find('.order-items-value.order-total', text:'0.00')
|
||||
end
|
||||
|
||||
|
||||
it "user with no redeemable jamtrack is not show the free-jamtrack prompt" do
|
||||
user.has_redeemable_jamtrack = false
|
||||
user.save!
|
||||
|
||||
fast_signin(user, '/client#/checkoutPayment')
|
||||
|
||||
# ok, the user has a free jamtrack... verify that display confirms this
|
||||
find('p.payment-prompt.no-free-jamtrack')
|
||||
end
|
||||
end
|
||||
|
||||
describe "Checkout Order" do
|
||||
it "shows empty cart notice" do
|
||||
|
||||
|
||||
fast_signin(user, '/client#/checkoutOrder')
|
||||
|
||||
# the user should be toldy they have a empty cart
|
||||
find('p.empty-cart-prompt')
|
||||
find('.order-items-value.order-total', text:'-.--')
|
||||
find('.order-items-value.sub-total', text:'-.--')
|
||||
find('.order-items-value.taxes', text:'-.--')
|
||||
find('.order-items-value.order-total', text:'-.--')
|
||||
|
||||
# verify that both Place Your Orders are disabled
|
||||
find('.order-content .place-order.disabled')
|
||||
find('.order-panel .place-order.disabled')
|
||||
end
|
||||
|
||||
it "shows one free item correctly" do
|
||||
|
||||
ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack)
|
||||
|
||||
@recurlyClient.create_account(user, billing_info)
|
||||
|
||||
user.reuse_card = true
|
||||
user.has_redeemable_jamtrack = true
|
||||
user.save!
|
||||
|
||||
fast_signin(user, '/client#/checkoutOrder')
|
||||
|
||||
find('p.order-prompt')
|
||||
find('.order-items-value.order-total', text:'$0.00')
|
||||
find('.order-items-value.shipping-handling', text:'$0.00')
|
||||
find('.order-items-value.sub-total', text:'$0.00')
|
||||
find('.order-items-value.taxes', text:'$0.00')
|
||||
find('.order-items-value.grand-total', text:'$0.00')
|
||||
end
|
||||
|
||||
it "shows one free, one not free item correctly" do
|
||||
|
||||
end
|
||||
|
||||
it "shows one non-free item correctly" do
|
||||
|
||||
end
|
||||
|
||||
it "shows two non-free items correctly" do
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe "Complete Checkout Flow" do
|
||||
it "for anonyous user" do
|
||||
|
||||
end
|
||||
|
||||
it "for existing user" do
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue