mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-26 20:54:21 +01:00
improved testing of payment schedules + some fixes
TODO: fix the test rails test test/integration/reservations/create_test.rb Reservations::CreateTest#test_user_reserves_a_machine_and_renew_a_subscription_with_payment_schedule_and_coupon_and_wallet [test/integration/reservations/create_test.rb:841] Minitest::Assertion: Expected: "stripe" Actual: nil
This commit is contained in:
parent
d891690ab8
commit
728ae4310c
2
Procfile
2
Procfile
@ -1,4 +1,4 @@
|
||||
#web: bundle exec rails server puma -p $PORT
|
||||
web: bundle exec rails server puma -p $PORT
|
||||
worker: bundle exec sidekiq -C ./config/sidekiq.yml
|
||||
wp-client: bin/webpack-dev-server
|
||||
wp-server: SERVER_BUNDLE_ONLY=yes bin/webpack --watch
|
||||
|
@ -938,7 +938,7 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
|
||||
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
|
||||
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
|
||||
if ((AuthService.isAuthorized(['member']) && amountToPay > 0) ||
|
||||
if ((AuthService.isAuthorized(['member']) && (amountToPay > 0 || (amountToPay === 0 && hasOtherDeadlines()))) ||
|
||||
(AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
|
||||
return payByStripe(reservation);
|
||||
} else {
|
||||
|
@ -17,6 +17,8 @@
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
animation: 0.15s linear fadeIn;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.fab-modal-sm { width: 340px; }
|
||||
|
@ -79,6 +79,8 @@
|
||||
</select>
|
||||
<span class="help-block error" ng-show="couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$error.required" translate>{{ 'app.shared.coupon.validity_per_user_is_required' }}</span>
|
||||
</div>
|
||||
<p class="alert alert-warning" ng-show="coupon.validity_per_user == 'once'" translate>{{ 'app.shared.coupon.warn_validity_once' }}</p>
|
||||
<p class="alert alert-warning" ng-show="coupon.validity_per_user == 'forever'" translate>{{ 'app.shared.coupon.warn_validity_forever' }}</p>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': errors['valid_until']}">
|
||||
<label for="coupon[valid_until]" translate>{{ 'app.shared.coupon.valid_until' }}</label>
|
||||
|
@ -17,6 +17,9 @@
|
||||
<a ng-href="api/invoices/{{t.invoice.id}}/download" target="_blank" ng-if="t.invoice.id">
|
||||
{{::t.invoice.reference}}
|
||||
</a>
|
||||
<a ng-href="api/payment_schedules/{{t.payment_schedule.id}}/download" target="_blank" ng-if="t.payment_schedule.id">
|
||||
{{::t.payment_schedule.reference}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ ::t.user.full_name }}</td>
|
||||
<td ng-class="{'green':t.transaction_type == 'credit', 'red':t.transaction_type == 'debit'}">
|
||||
|
@ -32,13 +32,17 @@ class Coupon < ApplicationRecord
|
||||
}
|
||||
|
||||
def safe_destroy
|
||||
if invoices.size.zero?
|
||||
if usages.zero?
|
||||
destroy
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def usages
|
||||
invoices.count + payment_schedule.count
|
||||
end
|
||||
|
||||
##
|
||||
# Check the status of the current coupon. The coupon:
|
||||
# - may have been disabled by an admin,
|
||||
|
@ -38,6 +38,7 @@ class Subscription < ApplicationRecord
|
||||
end
|
||||
|
||||
def cancel
|
||||
# TODO, currently unused, refactor to use with PaymentSchedule
|
||||
update_columns(canceled_at: DateTime.current)
|
||||
end
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
json.extract! coupon, :id, :name, :code, :type, :percent_off, :valid_until, :validity_per_user, :max_usages, :active, :created_at
|
||||
json.amount_off (coupon.amount_off / 100.00) unless coupon.amount_off.nil?
|
||||
json.usages coupon.invoices.count
|
||||
json.usages coupon.usages
|
||||
json.status coupon.status
|
@ -10,4 +10,10 @@ json.array!(@wallet_transactions) do |t|
|
||||
json.reference t.invoice.reference
|
||||
end
|
||||
end
|
||||
if t.payment_schedule
|
||||
json.payment_schedule do
|
||||
json.id t.payment_schedule.id
|
||||
json.reference t.payment_schedule.reference
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -49,5 +49,19 @@ class StripeWorker
|
||||
object.update_attributes(stp_product_id: product.id)
|
||||
puts "Stripe product was created for the #{class_name} \##{id}"
|
||||
end
|
||||
|
||||
rescue Stripe::InvalidRequestError
|
||||
STDERR.puts "WARNING: saved stp_product_id (#{object.stp_product_id}) does not match on Stripe, recreating..."
|
||||
product = Stripe::Product.create(
|
||||
{
|
||||
name: object.name,
|
||||
metadata: {
|
||||
id: object.id,
|
||||
type: class_name
|
||||
}
|
||||
}, { api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
object.update_attributes(stp_product_id: product.id)
|
||||
puts "Stripe product was created for the #{class_name} \##{id}"
|
||||
end
|
||||
end
|
||||
|
@ -42,11 +42,6 @@ module Fablab
|
||||
|
||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||
#
|
||||
# /!\ ALL locales SHOULD be configured accordingly with this locale. /!\
|
||||
#
|
||||
config.i18n.default_locale = Rails.application.secrets.rails_locale
|
||||
config.i18n.fallbacks = [Rails.application.secrets.app_locale, :en]
|
||||
|
||||
config.to_prepare do
|
||||
Devise::Mailer.layout 'notifications_mailer'
|
||||
|
@ -5,3 +5,10 @@ I18n.config.available_locales += %i[en en-AU-CA en-GB en-IE en-IN en-NZ en-US en
|
||||
es-AR es-CL es-CO es-CR es-DO es-EC es-ES es-MX es-PA es-PE es-US es-VE pt pt-BR zu]
|
||||
# we allow the Zulu locale (zu) as it is used for In-Context translation
|
||||
# @see https://support.crowdin.com/in-context-localization/
|
||||
|
||||
|
||||
#
|
||||
# /!\ ALL locales SHOULD be configured accordingly with the default_locale. /!\
|
||||
#
|
||||
I18n.config.default_locale = Rails.application.secrets.rails_locale
|
||||
I18n.config.locale = Rails.application.secrets.rails_locale
|
||||
|
@ -346,7 +346,7 @@ en:
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_delete_this_coupon: "Do you really want to delete this coupon?"
|
||||
coupon_was_successfully_deleted: "Coupon was successfully deleted."
|
||||
unable_to_delete_the_specified_coupon_already_in_use: "Unable to delete the specified coupon: it is already used with some invoices."
|
||||
unable_to_delete_the_specified_coupon_already_in_use: "Unable to delete the specified coupon: it is already used with some invoices and/or some payment schedules."
|
||||
unable_to_delete_the_specified_coupon_an_unexpected_error_occurred: "Unable to delete the specified coupon: an unexpected error occurred."
|
||||
send_a_coupon: "Send a coupon"
|
||||
coupon: "Coupon"
|
||||
|
@ -346,7 +346,7 @@ fr:
|
||||
confirmation_required: "Confirmation requise"
|
||||
do_you_really_want_to_delete_this_coupon: "Êtes-vous sûr(e) de vouloir supprimer ce code promotionnel ?"
|
||||
coupon_was_successfully_deleted: "Le code promotionnel a bien été supprimé."
|
||||
unable_to_delete_the_specified_coupon_already_in_use: "Impossible de supprimer le code promotionnel : il est utilisé dans des factures."
|
||||
unable_to_delete_the_specified_coupon_already_in_use: "Impossible de supprimer le code promotionnel : il est utilisé dans des factures et/ou des échéanciers."
|
||||
unable_to_delete_the_specified_coupon_an_unexpected_error_occurred: "Impossible de supprimer le code promotionnel : une erreur inattendue s'est produite."
|
||||
send_a_coupon: "Envoyer un code promo"
|
||||
coupon: "Code promo"
|
||||
|
@ -111,6 +111,10 @@ Rails.application.routes.draw do
|
||||
get 'first', action: 'first', on: :collection
|
||||
end
|
||||
|
||||
resources :payment_schedules, only: %i[index show] do
|
||||
get 'download', on: :member
|
||||
end
|
||||
|
||||
resources :i_calendar, only: %i[index create destroy] do
|
||||
get 'events', on: :member
|
||||
post 'sync', on: :member
|
||||
|
@ -4,15 +4,6 @@
|
||||
namespace :fablab do
|
||||
namespace :stripe do
|
||||
|
||||
desc 'Cancel stripe subscriptions'
|
||||
task cancel_subscriptions: :environment do
|
||||
Subscription.where('expiration_date >= ?', DateTime.current.at_beginning_of_day).each do |s|
|
||||
puts "-> Start cancel subscription of #{s.user.email}"
|
||||
s.cancel
|
||||
puts '-> Done'
|
||||
end
|
||||
end
|
||||
|
||||
desc 'find any invoices with incoherent total between stripe and DB'
|
||||
task :find_incoherent_invoices, [:start_date] => :environment do |_task, args|
|
||||
puts 'DEPRECATION WARNING: Will not work for invoices created from version 4.1.0 and above'
|
||||
|
4
test/fixtures/coupons.yml
vendored
4
test/fixtures/coupons.yml
vendored
@ -5,7 +5,7 @@ one:
|
||||
code: XMAS10
|
||||
percent_off: 10
|
||||
valid_until: 2015-12-31 23:59:59
|
||||
max_usages: nil
|
||||
max_usages:
|
||||
active: true
|
||||
validity_per_user: once
|
||||
|
||||
@ -32,6 +32,6 @@ cash2:
|
||||
code: GIME3EUR
|
||||
amount_off: 300
|
||||
valid_until: <%= 1.year.from_now.utc.strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
|
||||
max_usages: nil
|
||||
max_usages:
|
||||
active: true
|
||||
validity_per_user: once
|
||||
|
60
test/fixtures/prices.yml
vendored
60
test/fixtures/prices.yml
vendored
@ -298,3 +298,63 @@ price_42:
|
||||
amount: 1000
|
||||
created_at: 2016-04-04 15:18:28.860220000 Z
|
||||
updated_at: 2016-04-04 15:18:50.517702000 Z
|
||||
|
||||
price_43:
|
||||
id: 43
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 1
|
||||
priceable_type: Machine
|
||||
amount: 1000
|
||||
created_at: 2016-04-04 15:18:28.836899000 Z
|
||||
updated_at: 2016-04-04 15:18:50.507019000 Z
|
||||
|
||||
price_44:
|
||||
id: 44
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 2
|
||||
priceable_type: Machine
|
||||
amount: 1000
|
||||
created_at: 2016-04-04 15:18:28.842674000 Z
|
||||
updated_at: 2016-04-04 15:18:50.508799000 Z
|
||||
|
||||
price_45:
|
||||
id: 45
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 3
|
||||
priceable_type: Machine
|
||||
amount: 1500
|
||||
created_at: 2016-04-04 15:18:28.847736000 Z
|
||||
updated_at: 2016-04-04 15:18:50.510437000 Z
|
||||
|
||||
price_46:
|
||||
id: 46
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 4
|
||||
priceable_type: Machine
|
||||
amount: 1000
|
||||
created_at: 2016-04-04 15:18:28.852783000 Z
|
||||
updated_at: 2016-04-04 15:18:50.512239000 Z
|
||||
|
||||
price_47:
|
||||
id: 47
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 5
|
||||
priceable_type: Machine
|
||||
amount: 800
|
||||
created_at: 2016-04-04 15:18:28.856602000 Z
|
||||
updated_at: 2016-04-04 15:18:50.514062000 Z
|
||||
|
||||
price_48:
|
||||
id: 48
|
||||
group_id: 1
|
||||
plan_id: 4
|
||||
priceable_id: 6
|
||||
priceable_type: Machine
|
||||
amount: 1000
|
||||
created_at: 2016-04-04 15:18:28.860220000 Z
|
||||
updated_at: 2016-04-04 15:18:50.517702000 Z
|
||||
|
8
test/fixtures/wallet_transactions.yml
vendored
8
test/fixtures/wallet_transactions.yml
vendored
@ -1,7 +1,13 @@
|
||||
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
transaction1:
|
||||
invoicing_profile_id: 5
|
||||
invoicing_profile_id: 1
|
||||
wallet: wallet_5
|
||||
transaction_type: credit
|
||||
amount: 1000
|
||||
|
||||
transaction2:
|
||||
invoicing_profile_id: 1
|
||||
wallet: wallet_7
|
||||
transaction_type: credit
|
||||
amount: 25500
|
||||
|
2
test/fixtures/wallets.yml
vendored
2
test/fixtures/wallets.yml
vendored
@ -24,4 +24,4 @@ wallet_1:
|
||||
|
||||
wallet_7:
|
||||
invoicing_profile_id: 7
|
||||
amount: 0
|
||||
amount: 25500
|
||||
|
@ -742,4 +742,115 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
|
||||
reservation = json_response(response.body)
|
||||
assert_equal plan.id, reservation[:user][:subscribed_plan][:id], 'subscribed plan does not match'
|
||||
end
|
||||
|
||||
test 'user reserves a machine and renew a subscription with payment schedule and coupon and wallet' do
|
||||
user = User.find_by(username: 'lseguin')
|
||||
login_as(user, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
subscriptions_count = Subscription.count
|
||||
user_subscriptions_count = user.subscriptions.count
|
||||
payment_schedule_count = PaymentSchedule.count
|
||||
payment_schedule_items_count = PaymentScheduleItem.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
machine = Machine.find(1)
|
||||
availability = machine.availabilities.last
|
||||
plan = Plan.find_by(group_id: user.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
|
||||
|
||||
VCR.use_cassette('reservations_machine_subscription_with_payment_schedule_coupon_wallet') do
|
||||
get "/api/payments/setup_intent/#{user.id}"
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
# Check the response
|
||||
setup_intent = json_response(response.body)
|
||||
assert_not_nil setup_intent[:client_secret]
|
||||
assert_not_nil setup_intent[:id]
|
||||
assert_match /^#{setup_intent[:id]}_secret_/, setup_intent[:client_secret]
|
||||
|
||||
# Confirm the intent (normally, this is done on browser-side)
|
||||
stripe_res = Stripe::SetupIntent.confirm(
|
||||
setup_intent[:id],
|
||||
{ payment_method: stripe_payment_method },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
|
||||
# check the confirmation
|
||||
assert_equal setup_intent[:id], stripe_res.id
|
||||
assert_equal 'succeeded', stripe_res.status
|
||||
assert_equal 'off_session', stripe_res.usage
|
||||
|
||||
|
||||
post '/api/payments/confirm_payment_schedule',
|
||||
params: {
|
||||
setup_intent_id: setup_intent[:id],
|
||||
cart_items: {
|
||||
coupon_code: 'GIME3EUR',
|
||||
reservation: {
|
||||
plan_id: plan.id,
|
||||
payment_schedule: true,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
assert_equal reservations_count + 1, Reservation.count, 'missing the reservation'
|
||||
assert_equal invoice_count, Invoice.count, "an invoice was generated but it shouldn't"
|
||||
assert_equal invoice_items_count, InvoiceItem.count, "some invoice items were generated but they shouldn't"
|
||||
assert_equal 0, UsersCredit.count, "user's credits were not reset"
|
||||
assert_equal subscriptions_count + 1, Subscription.count, 'missing the subscription'
|
||||
assert_equal payment_schedule_count + 1, PaymentSchedule.count, 'missing the payment schedule'
|
||||
assert_equal payment_schedule_items_count + 12, PaymentScheduleItem.count, 'missing some payment schedule items'
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count, 'missing the wallet transaction'
|
||||
|
||||
# get the objects
|
||||
reservation = Reservation.last
|
||||
subscription = Subscription.last
|
||||
payment_schedule = PaymentSchedule.last
|
||||
|
||||
# subscription assertions
|
||||
assert_equal user_subscriptions_count + 1, user.subscriptions.count
|
||||
assert_equal user, subscription.user
|
||||
assert_not_nil user.subscribed_plan, "user's subscribed plan was not found"
|
||||
assert_not_nil user.subscription, "user's subscription was not found"
|
||||
assert_equal plan.id, user.subscribed_plan.id, "user's plan does not match"
|
||||
|
||||
# reservation assertions
|
||||
assert reservation.payment_schedule
|
||||
assert_equal payment_schedule.scheduled, reservation
|
||||
|
||||
# payment schedule assertions
|
||||
assert_not_nil payment_schedule.reference
|
||||
assert_equal 'stripe', payment_schedule.payment_method
|
||||
assert_not_nil payment_schedule.stp_subscription_id
|
||||
assert_not_nil payment_schedule.stp_setup_intent_id
|
||||
assert_not_nil payment_schedule.wallet_transaction
|
||||
assert_equal payment_schedule.ordered_items.first.amount, payment_schedule.wallet_amount
|
||||
assert_equal Coupon.find_by(code: 'GIME3EUR').id, payment_schedule.coupon_id
|
||||
assert_equal 'test', payment_schedule.environment
|
||||
assert payment_schedule.check_footprint
|
||||
assert_equal user.invoicing_profile.id, payment_schedule.invoicing_profile_id
|
||||
assert_equal payment_schedule.invoicing_profile_id, payment_schedule.operator_profile_id
|
||||
|
||||
# Check the answer
|
||||
reservation = json_response(response.body)
|
||||
assert_equal plan.id, reservation[:user][:subscribed_plan][:id], 'subscribed plan does not match'
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user