From db8a6e8a3236e3ad4a03c13268acd4a842041c1d Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 18 Jul 2016 18:16:54 +0200 Subject: [PATCH 01/30] application can show user wallet --- .../controllers/admin/members.coffee.erb | 8 +++- .../javascripts/controllers/wallet.coffee | 9 ++++ app/assets/javascripts/router.coffee.erb | 18 +++++++- app/assets/javascripts/services/wallet.coffee | 14 ++++++ .../templates/admin/members/edit.html.erb | 6 +++ app/assets/templates/dashboard/nav.html.erb | 19 ++++---- .../templates/dashboard/wallet.html.erb | 16 +++++++ app/assets/templates/shared/header.html.erb | 1 + app/assets/templates/wallet/show.html.erb | 7 +++ app/controllers/api/wallet_controller.rb | 14 ++++++ app/helpers/application_helper.rb | 4 ++ app/models/user.rb | 7 +++ app/models/wallet.rb | 4 ++ app/policies/wallet_policy.rb | 5 +++ app/views/api/wallet/show.json.jbuilder | 2 + config/locales/app.public.en.yml | 1 + config/locales/app.public.fr.yml | 1 + config/locales/app.shared.en.yml | 5 +++ config/locales/app.shared.fr.yml | 5 +++ config/routes.rb | 4 ++ db/migrate/20160704095606_create_wallets.rb | 18 ++++++++ db/schema.rb | 10 +++++ test/fixtures/wallets.yml | 27 ++++++++++++ test/integration/wallets_test.rb | 43 +++++++++++++++++++ test/models/user_test.rb | 9 ++++ test/models/wallet_test.rb | 13 ++++++ 26 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/controllers/wallet.coffee create mode 100644 app/assets/javascripts/services/wallet.coffee create mode 100644 app/assets/templates/dashboard/wallet.html.erb create mode 100644 app/assets/templates/wallet/show.html.erb create mode 100644 app/controllers/api/wallet_controller.rb create mode 100644 app/models/wallet.rb create mode 100644 app/policies/wallet_policy.rb create mode 100644 app/views/api/wallet/show.json.jbuilder create mode 100644 db/migrate/20160704095606_create_wallets.rb create mode 100644 test/fixtures/wallets.yml create mode 100644 test/integration/wallets_test.rb create mode 100644 test/models/user_test.rb create mode 100644 test/models/wallet_test.rb diff --git a/app/assets/javascripts/controllers/admin/members.coffee.erb b/app/assets/javascripts/controllers/admin/members.coffee.erb index 06516d796..0e8058d2c 100644 --- a/app/assets/javascripts/controllers/admin/members.coffee.erb +++ b/app/assets/javascripts/controllers/admin/members.coffee.erb @@ -260,8 +260,8 @@ Application.Controllers.controller "AdminMembersController", ["$scope", 'members ## # Controller used in the member edition page ## -Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t' -, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t) -> +Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise' +, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise) -> @@ -300,6 +300,10 @@ Application.Controllers.controller "EditMemberController", ["$scope", "$state", ## Profiles types (student/standard/...) $scope.groups = [] + ## the user wallet + $scope.wallet = walletPromise + + $scope.view = 'member_edit' ## diff --git a/app/assets/javascripts/controllers/wallet.coffee b/app/assets/javascripts/controllers/wallet.coffee new file mode 100644 index 000000000..51764efa1 --- /dev/null +++ b/app/assets/javascripts/controllers/wallet.coffee @@ -0,0 +1,9 @@ +'use strict' + +Application.Controllers.controller "WalletController", ['$scope', 'walletPromise', ($scope, walletPromise)-> + + ### PUBLIC SCOPE ### + + ## current user wallet + $scope.wallet = walletPromise +] diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 2342fa126..5c237854f 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -197,6 +197,19 @@ angular.module('application.router', ['ui.router']). translations: [ 'Translations', (Translations) -> Translations.query('app.logged.dashboard.invoices').$promise ] + .state 'app.logged.dashboard.wallet', + url: '/wallet' + views: + 'main@': + templateUrl: '<%= asset_path "dashboard/wallet.html" %>' + controller: 'WalletController' + resolve: + walletPromise: ['Wallet', (Wallet)-> + Wallet.my().$promise + ] + translations: [ 'Translations', (Translations) -> + Translations.query(['app.shared.wallet']).$promise + ] # members @@ -859,11 +872,14 @@ angular.module('application.router', ['ui.router']). memberPromise: ['Member', '$stateParams', (Member, $stateParams)-> Member.get(id: $stateParams.id).$promise ] + walletPromise: ['Wallet', '$stateParams', (Wallet, $stateParams)-> + Wallet.getWalletByUser(user_id: $stateParams.id).$promise + ] tagsPromise: ['Tag', (Tag)-> Tag.query().$promise ] translations: [ 'Translations', (Translations) -> - Translations.query(['app.admin.members_edit', 'app.shared.user', 'app.shared.user_admin']).$promise + Translations.query(['app.admin.members_edit', 'app.shared.user', 'app.shared.user_admin', 'app.shared.wallet']).$promise ] .state 'app.admin.admins_new', url: '/admin/admins/new' diff --git a/app/assets/javascripts/services/wallet.coffee b/app/assets/javascripts/services/wallet.coffee new file mode 100644 index 000000000..0e8429d15 --- /dev/null +++ b/app/assets/javascripts/services/wallet.coffee @@ -0,0 +1,14 @@ +'use strict' + +Application.Services.factory 'Wallet', ["$resource", ($resource)-> + $resource "/api/wallet", + {}, + my: + method: 'GET' + url: '/api/wallet/my' + isArray: false + getWalletByUser: + method: 'GET' + url: '/api/wallet/by_user/:user_id' + isArray: false +] diff --git a/app/assets/templates/admin/members/edit.html.erb b/app/assets/templates/admin/members/edit.html.erb index e8b9eaf94..b9cb70da2 100644 --- a/app/assets/templates/admin/members/edit.html.erb +++ b/app/assets/templates/admin/members/edit.html.erb @@ -215,6 +215,12 @@ + + +
+ +
+
diff --git a/app/assets/templates/dashboard/nav.html.erb b/app/assets/templates/dashboard/nav.html.erb index 954fcd515..b81d54246 100644 --- a/app/assets/templates/dashboard/nav.html.erb +++ b/app/assets/templates/dashboard/nav.html.erb @@ -8,15 +8,16 @@
-

{{ 'dashboard' }}

- +

{{ 'dashboard' }}

+
diff --git a/app/assets/templates/dashboard/wallet.html.erb b/app/assets/templates/dashboard/wallet.html.erb new file mode 100644 index 000000000..0fef50380 --- /dev/null +++ b/app/assets/templates/dashboard/wallet.html.erb @@ -0,0 +1,16 @@ +
+ +
+
+ +
+ +
+ + +
+
+ +
+
+
diff --git a/app/assets/templates/shared/header.html.erb b/app/assets/templates/shared/header.html.erb index 263be0ab2..2fae7886f 100644 --- a/app/assets/templates/shared/header.html.erb +++ b/app/assets/templates/shared/header.html.erb @@ -40,6 +40,7 @@
  • {{ 'my_trainings' }}
  • {{ 'my_events' }}
  • {{ 'my_invoices' }}
  • +
  • {{ 'my_wallet' }}
  • {{ 'sign_out' | translate }}
  • diff --git a/app/assets/templates/wallet/show.html.erb b/app/assets/templates/wallet/show.html.erb new file mode 100644 index 000000000..307d74e3c --- /dev/null +++ b/app/assets/templates/wallet/show.html.erb @@ -0,0 +1,7 @@ +
    +

    {{'your_wallet_amount'}}

    +

    {{'wallet_amount'}}

    +
    +
    {{wallet.amount | currency}}
    +
    +
    diff --git a/app/controllers/api/wallet_controller.rb b/app/controllers/api/wallet_controller.rb new file mode 100644 index 000000000..1378b3c12 --- /dev/null +++ b/app/controllers/api/wallet_controller.rb @@ -0,0 +1,14 @@ +class API::WalletController < API::ApiController + before_action :authenticate_user! + + def my + @wallet = current_user.wallet + render :show + end + + def by_user + authorize Wallet + @wallet = Wallet.find_by(user_id: params[:user_id]) + render :show + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c504024bc..c575c3a6f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -52,6 +52,10 @@ module ApplicationHelper if (bool) then return :true else return :false end end + def amount_to_f(amount) + amount / 100.00 + end + private ## inspired by gems/actionview-4.2.5/lib/action_view/helpers/translation_helper.rb diff --git a/app/models/user.rb b/app/models/user.rb index c3522a747..bf06305cf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -45,12 +45,15 @@ class User < ActiveRecord::Base has_many :tags, through: :user_tags accepts_nested_attributes_for :tags, allow_destroy: true + has_one :wallet, dependent: :destroy + # fix for create admin user before_save do self.email.downcase! if self.email end before_create :assign_default_role + after_create :create_a_wallet after_commit :create_stripe_customer, on: [:create] after_commit :notify_admin_when_user_is_created, on: :create after_update :notify_admin_invoicing_changed, if: :invoicing_disabled_changed? @@ -333,6 +336,10 @@ class User < ActiveRecord::Base StripeWorker.perform_async(:create_stripe_customer, id) end + def create_a_wallet + self.create_wallet + end + def notify_admin_when_user_is_created if need_completion? and not provider.nil? NotificationCenter.call type: 'notify_admin_when_user_is_imported', diff --git a/app/models/wallet.rb b/app/models/wallet.rb new file mode 100644 index 000000000..c78dcdce5 --- /dev/null +++ b/app/models/wallet.rb @@ -0,0 +1,4 @@ +class Wallet < ActiveRecord::Base + belongs_to :user + validates :user, presence: true +end diff --git a/app/policies/wallet_policy.rb b/app/policies/wallet_policy.rb new file mode 100644 index 000000000..24d7cdd30 --- /dev/null +++ b/app/policies/wallet_policy.rb @@ -0,0 +1,5 @@ +class WalletPolicy < ApplicationPolicy + def by_user? + user.is_admin? + end +end diff --git a/app/views/api/wallet/show.json.jbuilder b/app/views/api/wallet/show.json.jbuilder new file mode 100644 index 000000000..a96e1213b --- /dev/null +++ b/app/views/api/wallet/show.json.jbuilder @@ -0,0 +1,2 @@ +json.user_id @wallet.user_id +json.amount amount_to_f(@wallet.amount) diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index 908f8b33d..c4af3384a 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -14,6 +14,7 @@ en: my_trainings: "My Trainings" my_events: "My Events" my_invoices: "My Invoices" + my_wallet: "My Wallet" # login/logout sign_out: "Sign Out" diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index ba37e3600..b7c764b5a 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -14,6 +14,7 @@ fr: my_trainings: "Mes formations" my_events: "Mes évènements" my_invoices: "Mes factures" + my_wallet: "Mon porte-monnaie" # connexion / déconnexion sign_out: "Se déconnecter" diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index b3fa419fe..d0b293f84 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -295,3 +295,8 @@ en: author: "Author" collaborator: "Collaborator" private_profile: "Private profile" + + wallet: + wallet: 'Wallet' + your_wallet_amount: 'Your amount available' + wallet_amount: 'Amount available' diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml index 74b1dfe45..d0a46fd7f 100644 --- a/config/locales/app.shared.fr.yml +++ b/config/locales/app.shared.fr.yml @@ -295,3 +295,8 @@ fr: author: "Auteur" collaborator: "Collaborateur" private_profile: "Profil privé" + + wallet: + wallet: 'Porte-monnaie' + your_wallet_amount: 'Votre montant disponible' + wallet_amount: 'Montant disponible' diff --git a/config/routes.rb b/config/routes.rb index 75c9a0331..94258cf40 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,6 +48,10 @@ Rails.application.routes.draw do resources :notifications, only: [:index, :show, :update] do match :update_all, path: '/', via: [:put, :patch], on: :collection end + resources :wallet do + get :my, on: :collection + get '/by_user/:user_id', action: 'by_user', on: :collection + end # for homepage get '/last_subscribed/:last' => "members#last_subscribed" diff --git a/db/migrate/20160704095606_create_wallets.rb b/db/migrate/20160704095606_create_wallets.rb new file mode 100644 index 000000000..4bad09e11 --- /dev/null +++ b/db/migrate/20160704095606_create_wallets.rb @@ -0,0 +1,18 @@ +class CreateWallets < ActiveRecord::Migration + def up + create_table :wallets do |t| + t.belongs_to :user, index: true, foreign_key: true + t.integer :amount, default: 0 + + t.timestamps null: false + end + + User.all.each do |u| + Wallet.create(user: u) + end + end + + def down + drop_table :wallets + end +end diff --git a/db/schema.rb b/db/schema.rb index 950914a6d..b6fa65dc1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -708,6 +708,15 @@ ActiveRecord::Schema.define(version: 20160714095018) do add_index "users_roles", ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id", using: :btree + create_table "wallets", force: :cascade do |t| + t.integer "user_id" + t.integer "amount", default: 0 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "wallets", ["user_id"], name: "index_wallets_on_user_id", using: :btree + add_foreign_key "availability_tags", "availabilities" add_foreign_key "availability_tags", "tags" add_foreign_key "events_event_themes", "event_themes" @@ -718,4 +727,5 @@ ActiveRecord::Schema.define(version: 20160714095018) do add_foreign_key "prices", "plans" add_foreign_key "user_tags", "tags" add_foreign_key "user_tags", "users" + add_foreign_key "wallets", "users" end diff --git a/test/fixtures/wallets.yml b/test/fixtures/wallets.yml new file mode 100644 index 000000000..a5dbec1d4 --- /dev/null +++ b/test/fixtures/wallets.yml @@ -0,0 +1,27 @@ +wallet_2: + user_id: 2 + amount: 0 + +wallet_4: + user_id: 4 + amount: 0 + +wallet_6: + user_id: 6 + amount: 0 + +wallet_5: + user_id: 5 + amount: 0 + +wallet_3: + user_id: 3 + amount: 0 + +wallet_1: + user_id: 1 + amount: 0 + +wallet_7: + user_id: 7 + amount: 0 diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb new file mode 100644 index 000000000..fb9ce9d25 --- /dev/null +++ b/test/integration/wallets_test.rb @@ -0,0 +1,43 @@ +class WalletsTest < ActionDispatch::IntegrationTest + + # Called before every test method runs. Can be used + # to set up fixture information. + def setup + @jdupond = User.find_by_username('jdupond') + login_as(@jdupond, scope: :user) + end + + # Called after every test method runs. Can be used to tear + # down fixture information. + + def teardown + # Do nothing + end + + test 'get my wallet' do + get '/api/wallet/my' + assert_equal 200, response.status + assert_equal Mime::JSON, response.content_type + wallet = json_response(response.body) + assert_equal @jdupond.wallet.user_id, wallet[:user_id] + assert_equal @jdupond.wallet.amount, wallet[:amount] + end + + test 'admin can get wallet by user id' do + @admin = User.find_by_username('admin') + login_as(@admin, scope: :user) + @user1 = User.first + get "/api/wallet/by_user/#{@user1.id}" + assert_equal 200, response.status + assert_equal Mime::JSON, response.content_type + wallet = json_response(response.body) + assert_equal @user1.wallet.user_id, wallet[:user_id] + assert_equal @user1.wallet.amount, wallet[:amount] + end + + test 'cant get wallet of user if not admin' do + @user1 = User.first + get "/api/wallet/by_user/#{@user1.id}" + assert_equal 403, response.status + end +end diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 000000000..7426e4f3f --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + test "must create a wallet after create user" do + u = User.create(username: 'user', email: 'userwallet@fabmanager.com', password: 'testpassword', password_confirmation: 'testpassword', + profile_attributes: {first_name: 'user', last_name: 'wallet', gender: true, birthday: 18.years.ago, phone: '0123456789'} ) + assert u.wallet.present? + end +end diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb new file mode 100644 index 000000000..d878f1800 --- /dev/null +++ b/test/models/wallet_test.rb @@ -0,0 +1,13 @@ +require 'test_helper' + +class WalletTest < ActiveSupport::TestCase + test "default amount must be zero" do + w = Wallet.new + assert w.amount == 0 + end + + test 'should user present' do + w = Wallet.create + assert w.errors[:user].present? + end +end From 363fd73bc46e9e34949e12f01d315008ecd64b48 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 4 Jul 2016 19:20:10 +0200 Subject: [PATCH 02/30] add model WalletTransaction --- app/models/wallet.rb | 12 ++++++++++++ app/models/wallet_transaction.rb | 6 ++++++ ...20160704165139_create_wallet_transactions.rb | 13 +++++++++++++ db/schema.rb | 17 +++++++++++++++++ test/fixtures/wallet_transactions.yml | 13 +++++++++++++ test/models/wallet_test.rb | 15 +++++++++++++++ test/models/wallet_transaction_test.rb | 7 +++++++ 7 files changed, 83 insertions(+) create mode 100644 app/models/wallet_transaction.rb create mode 100644 db/migrate/20160704165139_create_wallet_transactions.rb create mode 100644 test/fixtures/wallet_transactions.yml create mode 100644 test/models/wallet_transaction_test.rb diff --git a/app/models/wallet.rb b/app/models/wallet.rb index c78dcdce5..8c2374e64 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -1,4 +1,16 @@ class Wallet < ActiveRecord::Base belongs_to :user + validates :user, presence: true + validates_numericality_of :amount, greater_than_or_equal_to: 0 + + def credit(amount) + self.amount += amount + save + end + + def debit(amount) + self.amount -= amount + save + end end diff --git a/app/models/wallet_transaction.rb b/app/models/wallet_transaction.rb new file mode 100644 index 000000000..040c4d4d3 --- /dev/null +++ b/app/models/wallet_transaction.rb @@ -0,0 +1,6 @@ +class WalletTransaction < ActiveRecord::Base + belongs_to :user + belongs_to :wallet + belongs_to :reservation + belongs_to :transactable, polymorphic: true +end diff --git a/db/migrate/20160704165139_create_wallet_transactions.rb b/db/migrate/20160704165139_create_wallet_transactions.rb new file mode 100644 index 000000000..a74b878ab --- /dev/null +++ b/db/migrate/20160704165139_create_wallet_transactions.rb @@ -0,0 +1,13 @@ +class CreateWalletTransactions < ActiveRecord::Migration + def change + create_table :wallet_transactions do |t| + t.belongs_to :user, index: true, foreign_key: true + t.belongs_to :wallet, index: true, foreign_key: true + t.references :transactable, polymorphic: true, index: {name: 'index_wallet_transactions_on_transactable'} + t.string :transaction_type + t.integer :amount + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b6fa65dc1..0a50f1196 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -708,6 +708,21 @@ ActiveRecord::Schema.define(version: 20160714095018) do add_index "users_roles", ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id", using: :btree + create_table "wallet_transactions", force: :cascade do |t| + t.integer "user_id" + t.integer "wallet_id" + t.integer "transactable_id" + t.string "transactable_type" + t.string "transaction_type" + t.integer "amount" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "wallet_transactions", ["transactable_type", "transactable_id"], name: "index_wallet_transactions_on_transactable", using: :btree + add_index "wallet_transactions", ["user_id"], name: "index_wallet_transactions_on_user_id", using: :btree + add_index "wallet_transactions", ["wallet_id"], name: "index_wallet_transactions_on_wallet_id", using: :btree + create_table "wallets", force: :cascade do |t| t.integer "user_id" t.integer "amount", default: 0 @@ -727,5 +742,7 @@ ActiveRecord::Schema.define(version: 20160714095018) do add_foreign_key "prices", "plans" add_foreign_key "user_tags", "tags" add_foreign_key "user_tags", "users" + add_foreign_key "wallet_transactions", "users" + add_foreign_key "wallet_transactions", "wallets" add_foreign_key "wallets", "users" end diff --git a/test/fixtures/wallet_transactions.yml b/test/fixtures/wallet_transactions.yml new file mode 100644 index 000000000..8fef445f6 --- /dev/null +++ b/test/fixtures/wallet_transactions.yml @@ -0,0 +1,13 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user_id: + wallet_id: + transaction_type: MyString + amount: 1 + +two: + user_id: + wallet_id: + transaction_type: MyString + amount: 1 diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb index d878f1800..ef4d6b52b 100644 --- a/test/models/wallet_test.rb +++ b/test/models/wallet_test.rb @@ -10,4 +10,19 @@ class WalletTest < ActiveSupport::TestCase w = Wallet.create assert w.errors[:user].present? end + + test 'can credit amount' do + w = Wallet.first + expected_amount = w.amount + 5 + assert w.credit(5) + assert_equal w.amount, expected_amount + end + + test 'can debit amount' do + w = Wallet.first + w.credit(5) + expected_amount = 0 + assert w.debit(5) + assert_equal w.amount, expected_amount + end end diff --git a/test/models/wallet_transaction_test.rb b/test/models/wallet_transaction_test.rb new file mode 100644 index 000000000..01796026f --- /dev/null +++ b/test/models/wallet_transaction_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class WalletTransactionTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From cac9e16c1704d4661fd3a8d4bafc9f1247b90886 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 10:38:39 +0200 Subject: [PATCH 03/30] cant debit/credit a negative --- app/models/wallet.rb | 14 ++++++++++---- test/models/wallet_test.rb | 11 +++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/models/wallet.rb b/app/models/wallet.rb index 8c2374e64..17483c6b4 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -5,12 +5,18 @@ class Wallet < ActiveRecord::Base validates_numericality_of :amount, greater_than_or_equal_to: 0 def credit(amount) - self.amount += amount - save + if amount.is_a?(Numeric) and amount >= 0 + self.amount += amount + return save + end + false end def debit(amount) - self.amount -= amount - save + if amount.is_a?(Numeric) and amount >= 0 + self.amount -= amount + return save + end + false end end diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb index ef4d6b52b..c557d58a7 100644 --- a/test/models/wallet_test.rb +++ b/test/models/wallet_test.rb @@ -25,4 +25,15 @@ class WalletTest < ActiveSupport::TestCase assert w.debit(5) assert_equal w.amount, expected_amount end + + test 'cant debit/credit a negative' do + w = Wallet.new + assert_not w.credit(-5) + assert_not w.debit(-5) + end + + test 'wallet amount cant < 0 after debit' do + w = Wallet.new + assert_not w.debit(5) + end end From b22bae1d8f97c4da318a51c3841a0eb446cd4f48 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 11:32:06 +0200 Subject: [PATCH 04/30] wallet amount convert auto to float --- app/models/wallet.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/wallet.rb b/app/models/wallet.rb index 17483c6b4..eca7adf59 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -4,6 +4,14 @@ class Wallet < ActiveRecord::Base validates :user, presence: true validates_numericality_of :amount, greater_than_or_equal_to: 0 + def amount=(amount) + write_attribute(:amount, amount.to_i * 100) + end + + def amount + read_attribute(:amount) / 100.0 + end + def credit(amount) if amount.is_a?(Numeric) and amount >= 0 self.amount += amount From 9116e8c04a14e06ed16c18b0d2e2a67592b1e187 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 12:15:26 +0200 Subject: [PATCH 05/30] wallet transaction type must be credit/debit --- app/models/wallet_transaction.rb | 2 ++ test/models/wallet_transaction_test.rb | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/models/wallet_transaction.rb b/app/models/wallet_transaction.rb index 040c4d4d3..df5868839 100644 --- a/app/models/wallet_transaction.rb +++ b/app/models/wallet_transaction.rb @@ -3,4 +3,6 @@ class WalletTransaction < ActiveRecord::Base belongs_to :wallet belongs_to :reservation belongs_to :transactable, polymorphic: true + + validates_inclusion_of :transaction_type, in: %w( credit debit ) end diff --git a/test/models/wallet_transaction_test.rb b/test/models/wallet_transaction_test.rb index 01796026f..3763f85bd 100644 --- a/test/models/wallet_transaction_test.rb +++ b/test/models/wallet_transaction_test.rb @@ -1,7 +1,13 @@ require 'test_helper' class WalletTransactionTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + test 'transaction type must be credit or debit' do + transaction = WalletTransaction.new + transaction.transaction_type = 'credit' + assert transaction.valid? + transaction.transaction_type = 'debit' + assert transaction.valid? + transaction.transaction_type = 'other' + assert_not transaction.valid? + end end From 1dab903054605bab410bf7527dc8f844859e7a00 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 12:30:52 +0200 Subject: [PATCH 06/30] refactoring wallet amount to concern --- app/models/concerns/amount_concern.rb | 23 +++++++++++++++++++++++ app/models/wallet.rb | 12 +++--------- app/models/wallet_transaction.rb | 2 ++ test/models/wallet_transaction_test.rb | 2 +- 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 app/models/concerns/amount_concern.rb diff --git a/app/models/concerns/amount_concern.rb b/app/models/concerns/amount_concern.rb new file mode 100644 index 000000000..86b01b443 --- /dev/null +++ b/app/models/concerns/amount_concern.rb @@ -0,0 +1,23 @@ +module AmountConcern + extend ActiveSupport::Concern + + included do + validates_numericality_of :amount, greater_than_or_equal_to: 0 + + def amount=(amount) + if amount.nil? + write_attribute(:amount, amount) + else + write_attribute(:amount, amount.to_i * 100) + end + end + + def amount + if read_attribute(:amount).blank? + read_attribute(:amount) + else + read_attribute(:amount) / 100.0 + end + end + end +end diff --git a/app/models/wallet.rb b/app/models/wallet.rb index eca7adf59..da8128eb7 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -1,16 +1,10 @@ class Wallet < ActiveRecord::Base + include AmountConcern + belongs_to :user + has_many :wallet_transactions, dependent: :destroy validates :user, presence: true - validates_numericality_of :amount, greater_than_or_equal_to: 0 - - def amount=(amount) - write_attribute(:amount, amount.to_i * 100) - end - - def amount - read_attribute(:amount) / 100.0 - end def credit(amount) if amount.is_a?(Numeric) and amount >= 0 diff --git a/app/models/wallet_transaction.rb b/app/models/wallet_transaction.rb index df5868839..4f88f1c10 100644 --- a/app/models/wallet_transaction.rb +++ b/app/models/wallet_transaction.rb @@ -1,4 +1,6 @@ class WalletTransaction < ActiveRecord::Base + include AmountConcern + belongs_to :user belongs_to :wallet belongs_to :reservation diff --git a/test/models/wallet_transaction_test.rb b/test/models/wallet_transaction_test.rb index 3763f85bd..7eac9a4d2 100644 --- a/test/models/wallet_transaction_test.rb +++ b/test/models/wallet_transaction_test.rb @@ -2,7 +2,7 @@ require 'test_helper' class WalletTransactionTest < ActiveSupport::TestCase test 'transaction type must be credit or debit' do - transaction = WalletTransaction.new + transaction = WalletTransaction.new amount: 5 transaction.transaction_type = 'credit' assert transaction.valid? transaction.transaction_type = 'debit' From d0956bb0aa9aa9ef560afba782acad81338be188 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 13:20:25 +0200 Subject: [PATCH 07/30] show wallet transactions of user --- .../controllers/admin/members.coffee.erb | 7 +++-- .../javascripts/controllers/wallet.coffee | 5 +++- app/assets/javascripts/router.coffee.erb | 6 ++++ app/assets/javascripts/services/wallet.coffee | 4 +++ app/assets/stylesheets/app.colors.scss | 1 + app/assets/stylesheets/app.components.scss | 25 ++++++++++++++++ .../templates/admin/members/edit.html.erb | 4 +++ .../templates/dashboard/wallet.html.erb | 5 ++++ .../templates/wallet/transactions.html.erb | 27 +++++++++++++++++ app/controllers/api/wallet_controller.rb | 6 ++++ app/policies/wallet_policy.rb | 4 +++ app/services/wallet_service.rb | 13 ++++++++ app/views/api/wallet/show.json.jbuilder | 3 +- .../api/wallet/transactions.json.jbuilder | 7 +++++ config/locales/app.shared.en.yml | 5 ++++ config/locales/app.shared.fr.yml | 5 ++++ config/routes.rb | 1 + test/fixtures/wallet_transactions.yml | 16 ++++------ test/fixtures/wallets.yml | 2 +- test/integration/wallets_test.rb | 30 ++++++++++++++----- test/models/wallet_test.rb | 2 +- test/services/wallet_service_test.rb | 27 +++++++++++++++++ 22 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 app/assets/templates/wallet/transactions.html.erb create mode 100644 app/services/wallet_service.rb create mode 100644 app/views/api/wallet/transactions.json.jbuilder create mode 100644 test/services/wallet_service_test.rb diff --git a/app/assets/javascripts/controllers/admin/members.coffee.erb b/app/assets/javascripts/controllers/admin/members.coffee.erb index 0e8058d2c..008cc7ca8 100644 --- a/app/assets/javascripts/controllers/admin/members.coffee.erb +++ b/app/assets/javascripts/controllers/admin/members.coffee.erb @@ -260,8 +260,8 @@ Application.Controllers.controller "AdminMembersController", ["$scope", 'members ## # Controller used in the member edition page ## -Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise' -, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise) -> +Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise' +, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise) -> @@ -303,6 +303,9 @@ Application.Controllers.controller "EditMemberController", ["$scope", "$state", ## the user wallet $scope.wallet = walletPromise + ## user wallet transactions + $scope.transactions = transactionsPromise + $scope.view = 'member_edit' diff --git a/app/assets/javascripts/controllers/wallet.coffee b/app/assets/javascripts/controllers/wallet.coffee index 51764efa1..9ab033672 100644 --- a/app/assets/javascripts/controllers/wallet.coffee +++ b/app/assets/javascripts/controllers/wallet.coffee @@ -1,9 +1,12 @@ 'use strict' -Application.Controllers.controller "WalletController", ['$scope', 'walletPromise', ($scope, walletPromise)-> +Application.Controllers.controller "WalletController", ['$scope', 'walletPromise', 'transactionsPromise', ($scope, walletPromise, transactionsPromise)-> ### PUBLIC SCOPE ### ## current user wallet $scope.wallet = walletPromise + + ## current wallet transactions + $scope.transactions = transactionsPromise ] diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 5c237854f..347c49b68 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -207,6 +207,9 @@ angular.module('application.router', ['ui.router']). walletPromise: ['Wallet', (Wallet)-> Wallet.my().$promise ] + transactionsPromise: ['Wallet', 'walletPromise', (Wallet, walletPromise)-> + Wallet.transactions(id: walletPromise.id).$promise + ] translations: [ 'Translations', (Translations) -> Translations.query(['app.shared.wallet']).$promise ] @@ -875,6 +878,9 @@ angular.module('application.router', ['ui.router']). walletPromise: ['Wallet', '$stateParams', (Wallet, $stateParams)-> Wallet.getWalletByUser(user_id: $stateParams.id).$promise ] + transactionsPromise: ['Wallet', 'walletPromise', (Wallet, walletPromise)-> + Wallet.transactions(id: walletPromise.id).$promise + ] tagsPromise: ['Tag', (Tag)-> Tag.query().$promise ] diff --git a/app/assets/javascripts/services/wallet.coffee b/app/assets/javascripts/services/wallet.coffee index 0e8429d15..e3d5c7380 100644 --- a/app/assets/javascripts/services/wallet.coffee +++ b/app/assets/javascripts/services/wallet.coffee @@ -11,4 +11,8 @@ Application.Services.factory 'Wallet', ["$resource", ($resource)-> method: 'GET' url: '/api/wallet/by_user/:user_id' isArray: false + transactions: + method: 'GET' + url: '/api/wallet/:id/transactions' + isArray: true ] diff --git a/app/assets/stylesheets/app.colors.scss b/app/assets/stylesheets/app.colors.scss index 0b49e3b72..e0c6d7d63 100644 --- a/app/assets/stylesheets/app.colors.scss +++ b/app/assets/stylesheets/app.colors.scss @@ -39,3 +39,4 @@ .text-purple { color: $violet !important; } .text-japonica { color: $japonica !important; } .text-beige { color: $beige !important; } +.text-green, .green { color: #79C84A !important; } diff --git a/app/assets/stylesheets/app.components.scss b/app/assets/stylesheets/app.components.scss index 317ec00fc..e668b9e51 100644 --- a/app/assets/stylesheets/app.components.scss +++ b/app/assets/stylesheets/app.components.scss @@ -514,3 +514,28 @@ padding: 10px; border-radius: 3px; } } + +.wallet-amount-container { + padding: 20px 0; + border-top: 2px dotted $border-color; + border-bottom: 2px dotted $border-color; + margin-bottom: 20px; + text-align: center; + + .wallet-amount { + font-size: rem-calc(40); + font-weight: 700; + font-style: italic; + color: #616161; + + span { + font-weight: 500; + font-size: .7em; + } + + &.cr-green { + color: $green; + } + } +} + diff --git a/app/assets/templates/admin/members/edit.html.erb b/app/assets/templates/admin/members/edit.html.erb index b9cb70da2..ed5368a2e 100644 --- a/app/assets/templates/admin/members/edit.html.erb +++ b/app/assets/templates/admin/members/edit.html.erb @@ -220,6 +220,10 @@
    + +
    + +
    diff --git a/app/assets/templates/dashboard/wallet.html.erb b/app/assets/templates/dashboard/wallet.html.erb index 0fef50380..175504c64 100644 --- a/app/assets/templates/dashboard/wallet.html.erb +++ b/app/assets/templates/dashboard/wallet.html.erb @@ -12,5 +12,10 @@
    + +
    + +
    + diff --git a/app/assets/templates/wallet/transactions.html.erb b/app/assets/templates/wallet/transactions.html.erb new file mode 100644 index 000000000..17130b013 --- /dev/null +++ b/app/assets/templates/wallet/transactions.html.erb @@ -0,0 +1,27 @@ +
    + + + + + + + + + + + + + + + + + +
    {{ 'date' }}{{ 'operation' }}{{ 'operator' }}{{ 'amount' }}
    {{ t.created_at | amDateFormat:'L' }} + {{ 'credit' }} + {{ t.user.full_name }} + + + - + {{ t.amount | currency }} +
    +

    {{ 'no_transactions_for_now' }}

    +
    diff --git a/app/controllers/api/wallet_controller.rb b/app/controllers/api/wallet_controller.rb index 1378b3c12..697279bd8 100644 --- a/app/controllers/api/wallet_controller.rb +++ b/app/controllers/api/wallet_controller.rb @@ -11,4 +11,10 @@ class API::WalletController < API::ApiController @wallet = Wallet.find_by(user_id: params[:user_id]) render :show end + + def transactions + @wallet = Wallet.find(params[:id]) + authorize @wallet + @wallet_transactions = @wallet.wallet_transactions.includes(:transactable, user: [:profile]).order(created_at: :desc) + end end diff --git a/app/policies/wallet_policy.rb b/app/policies/wallet_policy.rb index 24d7cdd30..b3f849b11 100644 --- a/app/policies/wallet_policy.rb +++ b/app/policies/wallet_policy.rb @@ -2,4 +2,8 @@ class WalletPolicy < ApplicationPolicy def by_user? user.is_admin? end + + def transactions? + user.is_admin? or user == record.user + end end diff --git a/app/services/wallet_service.rb b/app/services/wallet_service.rb new file mode 100644 index 000000000..62e657c98 --- /dev/null +++ b/app/services/wallet_service.rb @@ -0,0 +1,13 @@ +class WalletService + def initialize(user: nil, wallet: nil) + @user = user + @wallet = wallet + end + + def credit(amount) + if @wallet.credit(amount) + WalletTransaction.create(user: @user, wallet: @wallet, transaction_type: 'credit', amount: amount) + return true + end + end +end diff --git a/app/views/api/wallet/show.json.jbuilder b/app/views/api/wallet/show.json.jbuilder index a96e1213b..ef0b12454 100644 --- a/app/views/api/wallet/show.json.jbuilder +++ b/app/views/api/wallet/show.json.jbuilder @@ -1,2 +1 @@ -json.user_id @wallet.user_id -json.amount amount_to_f(@wallet.amount) +json.extract! @wallet, :id, :user_id, :amount diff --git a/app/views/api/wallet/transactions.json.jbuilder b/app/views/api/wallet/transactions.json.jbuilder new file mode 100644 index 000000000..d588705c4 --- /dev/null +++ b/app/views/api/wallet/transactions.json.jbuilder @@ -0,0 +1,7 @@ +json.array!(@wallet_transactions) do |t| + json.extract! t, :id, :transaction_type, :created_at, :amount + json.user do + json.id t.user.id + json.full_name t.user.profile.full_name + end +end diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index d0b293f84..7c655ba36 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -300,3 +300,8 @@ en: wallet: 'Wallet' your_wallet_amount: 'Your amount available' wallet_amount: 'Amount available' + no_transactions_for_now: 'No transactions for now' + operation: 'Operation' + operator: 'Operator' + amount: 'Amount' + credit: 'Crédit' diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml index d0a46fd7f..7215c8599 100644 --- a/config/locales/app.shared.fr.yml +++ b/config/locales/app.shared.fr.yml @@ -300,3 +300,8 @@ fr: wallet: 'Porte-monnaie' your_wallet_amount: 'Votre montant disponible' wallet_amount: 'Montant disponible' + no_transactions_for_now: 'Aucune transaction pour le moment' + operation: 'Opération' + operator: 'Opérateur' + amount: 'Montant' + credit: 'Crédit' diff --git a/config/routes.rb b/config/routes.rb index 94258cf40..02d730d0a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,6 +51,7 @@ Rails.application.routes.draw do resources :wallet do get :my, on: :collection get '/by_user/:user_id', action: 'by_user', on: :collection + get :transactions, on: :member end # for homepage diff --git a/test/fixtures/wallet_transactions.yml b/test/fixtures/wallet_transactions.yml index 8fef445f6..9edb192b1 100644 --- a/test/fixtures/wallet_transactions.yml +++ b/test/fixtures/wallet_transactions.yml @@ -1,13 +1,7 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - user_id: - wallet_id: - transaction_type: MyString - amount: 1 - -two: - user_id: - wallet_id: - transaction_type: MyString - amount: 1 +transaction1: + user_id: 4 + wallet: wallet_4 + transaction_type: credit + amount: 1000 diff --git a/test/fixtures/wallets.yml b/test/fixtures/wallets.yml index a5dbec1d4..e86e3a29f 100644 --- a/test/fixtures/wallets.yml +++ b/test/fixtures/wallets.yml @@ -4,7 +4,7 @@ wallet_2: wallet_4: user_id: 4 - amount: 0 + amount: 1000 wallet_6: user_id: 6 diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index fb9ce9d25..be1035b96 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -3,8 +3,8 @@ class WalletsTest < ActionDispatch::IntegrationTest # Called before every test method runs. Can be used # to set up fixture information. def setup - @jdupond = User.find_by_username('jdupond') - login_as(@jdupond, scope: :user) + @kdumas = User.find_by(username: 'kdumas') + login_as(@kdumas, scope: :user) end # Called after every test method runs. Can be used to tear @@ -19,8 +19,8 @@ class WalletsTest < ActionDispatch::IntegrationTest assert_equal 200, response.status assert_equal Mime::JSON, response.content_type wallet = json_response(response.body) - assert_equal @jdupond.wallet.user_id, wallet[:user_id] - assert_equal @jdupond.wallet.amount, wallet[:amount] + assert_equal @kdumas.wallet.user_id, wallet[:user_id] + assert_equal @kdumas.wallet.amount, wallet[:amount] end test 'admin can get wallet by user id' do @@ -35,9 +35,25 @@ class WalletsTest < ActionDispatch::IntegrationTest assert_equal @user1.wallet.amount, wallet[:amount] end - test 'cant get wallet of user if not admin' do - @user1 = User.first - get "/api/wallet/by_user/#{@user1.id}" + test 'cant get wallet of an user if not admin' do + user5 = users(:user_5) + get "/api/wallet/by_user/#{user5.id}" + assert_equal 403, response.status + end + + test 'get all transactions of wallet' do + w = @kdumas.wallet + get "/api/wallet/#{w.id}/transactions" + assert_equal 200, response.status + assert_equal Mime::JSON, response.content_type + transactions = json_response(response.body) + assert_equal w.wallet_transactions.count, transactions.size + assert_equal wallet_transactions(:transaction1).id, transactions.first[:id] + end + + test 'only admin and wallet owner can show their transactions' do + user5 = users(:user_5) + get "/api/wallet/#{user5.wallet.id}/transactions" assert_equal 403, response.status end end diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb index c557d58a7..537ae6bdf 100644 --- a/test/models/wallet_test.rb +++ b/test/models/wallet_test.rb @@ -21,7 +21,7 @@ class WalletTest < ActiveSupport::TestCase test 'can debit amount' do w = Wallet.first w.credit(5) - expected_amount = 0 + expected_amount = w.amount - 5 assert w.debit(5) assert_equal w.amount, expected_amount end diff --git a/test/services/wallet_service_test.rb b/test/services/wallet_service_test.rb new file mode 100644 index 000000000..46536423f --- /dev/null +++ b/test/services/wallet_service_test.rb @@ -0,0 +1,27 @@ +require 'test_helper' + +class WalletServiceTest < ActiveSupport::TestCase + setup do + @admin = User.find_by(username: 'admin') + @user = User.find_by(username: 'jdupond') + @wallet = @user.wallet + end + + test 'admin can credit a wallet' do + service = WalletService.new(user: @admin, wallet: @wallet) + expected_amount = @wallet.amount + 5 + assert service.credit(5) + assert_equal @wallet.amount, expected_amount + end + + test 'create a credit transaction after credit amount to wallet' do + service = WalletService.new(user: @admin, wallet: @wallet) + assert_equal 0, @wallet.wallet_transactions.count + assert service.credit(10) + transaction = @wallet.wallet_transactions.first + assert_equal transaction.transaction_type, 'credit' + assert_equal transaction.amount, 10 + assert_equal transaction.user, @admin + assert_equal transaction.wallet, @wallet + end +end From fc7eaaab940cb295150bc0ef4ce5bdab2cab0cf4 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 5 Jul 2016 19:07:50 +0200 Subject: [PATCH 08/30] add notification and alert when user wallet credit --- .../controllers/admin/members.coffee.erb | 37 ++++++++++++++++++- app/assets/javascripts/services/wallet.coffee | 4 ++ app/assets/stylesheets/app.components.scss | 20 ++++++++++ .../templates/admin/members/edit.html.erb | 6 +++ .../templates/wallet/credit_modal.html.erb | 20 ++++++++++ app/controllers/api/wallet_controller.rb | 11 ++++++ app/models/notification_type.rb | 2 + app/policies/wallet_policy.rb | 4 ++ app/services/wallet_service.rb | 9 ++++- ...dmin_user_wallet_is_credited.json.jbuilder | 7 ++++ ...tify_user_wallet_is_credited.json.jbuilder | 5 +++ ...ify_admin_user_wallet_is_credited.html.erb | 9 +++++ .../notify_user_wallet_is_credited.html.erb | 2 + config/locales/app.shared.en.yml | 5 ++- config/locales/app.shared.fr.yml | 5 +++ config/locales/en.yml | 4 ++ config/locales/fr.yml | 4 ++ config/locales/mails.en.yml | 10 +++++ config/locales/mails.fr.yml | 10 +++++ config/routes.rb | 1 + test/integration/wallets_test.rb | 19 ++++++++++ 21 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 app/assets/templates/wallet/credit_modal.html.erb create mode 100644 app/views/api/notifications/_notify_admin_user_wallet_is_credited.json.jbuilder create mode 100644 app/views/api/notifications/_notify_user_wallet_is_credited.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_admin_user_wallet_is_credited.html.erb create mode 100644 app/views/notifications_mailer/notify_user_wallet_is_credited.html.erb diff --git a/app/assets/javascripts/controllers/admin/members.coffee.erb b/app/assets/javascripts/controllers/admin/members.coffee.erb index 008cc7ca8..2c243e738 100644 --- a/app/assets/javascripts/controllers/admin/members.coffee.erb +++ b/app/assets/javascripts/controllers/admin/members.coffee.erb @@ -260,8 +260,8 @@ Application.Controllers.controller "AdminMembersController", ["$scope", 'members ## # Controller used in the member edition page ## -Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise' -, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise) -> +Application.Controllers.controller "EditMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise', 'Wallet' +, ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise, Wallet) -> @@ -403,6 +403,39 @@ Application.Controllers.controller "EditMemberController", ["$scope", "$state", $scope.subscription = subscription + $scope.createWalletCreditModal = (user, wallet)-> + modalInstance = $uibModal.open + animation: true, + templateUrl: '<%= asset_path "wallet/credit_modal.html" %>' + controller: ['$scope', '$uibModalInstance', 'Wallet', '$locale', ($scope, $uibModalInstance, Wallet, $locale) -> + + ## currency symbol for the current locale (cf. angular-i18n) + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + + ## + # Modal dialog validation callback + ## + $scope.ok = -> + Wallet.credit { id: wallet.id }, { amount: $scope.amount }, (_wallet)-> + + growl.success(_t('wallet_credit_successfully')) + $uibModalInstance.close(_wallet) + , (error)-> + growl.error(_t('a_problem_occurred_for_wallet_credit')) + + ## + # Modal dialog cancellation callback + ## + $scope.cancel = -> + $uibModalInstance.dismiss('cancel') + ] + # once the form was validated succesfully ... + modalInstance.result.then (wallet) -> + $scope.wallet = wallet + Wallet.transactions {id: wallet.id}, (transactions) -> + $scope.transactions = transactions + + ### PRIVATE SCOPE ### diff --git a/app/assets/javascripts/services/wallet.coffee b/app/assets/javascripts/services/wallet.coffee index e3d5c7380..a44326683 100644 --- a/app/assets/javascripts/services/wallet.coffee +++ b/app/assets/javascripts/services/wallet.coffee @@ -15,4 +15,8 @@ Application.Services.factory 'Wallet', ["$resource", ($resource)-> method: 'GET' url: '/api/wallet/:id/transactions' isArray: true + credit: + method: 'PUT' + url: '/api/wallet/:id/credit' + isArray: false ] diff --git a/app/assets/stylesheets/app.components.scss b/app/assets/stylesheets/app.components.scss index e668b9e51..babf2b8bb 100644 --- a/app/assets/stylesheets/app.components.scss +++ b/app/assets/stylesheets/app.components.scss @@ -539,3 +539,23 @@ padding: 10px; } } +.amountGroup { + input { + display: inline-block; + width: 100px; + margin-left: 5px; + padding-right: 6px; + font-weight: bold; + color: $green; + font-size: 1.2em; + line-height: 0; + } + .afterAmount { + margin-left: -35px; + font-weight: bold; + color: $green; + font-size: 1.2em; + line-height: 0; + } +} + diff --git a/app/assets/templates/admin/members/edit.html.erb b/app/assets/templates/admin/members/edit.html.erb index ed5368a2e..7b706e0ec 100644 --- a/app/assets/templates/admin/members/edit.html.erb +++ b/app/assets/templates/admin/members/edit.html.erb @@ -219,6 +219,12 @@
    + +
    +
    + +
    +
    diff --git a/app/assets/templates/wallet/credit_modal.html.erb b/app/assets/templates/wallet/credit_modal.html.erb new file mode 100644 index 000000000..ac98cdb0d --- /dev/null +++ b/app/assets/templates/wallet/credit_modal.html.erb @@ -0,0 +1,20 @@ + + + diff --git a/app/controllers/api/wallet_controller.rb b/app/controllers/api/wallet_controller.rb index 697279bd8..c62672b5a 100644 --- a/app/controllers/api/wallet_controller.rb +++ b/app/controllers/api/wallet_controller.rb @@ -17,4 +17,15 @@ class API::WalletController < API::ApiController authorize @wallet @wallet_transactions = @wallet.wallet_transactions.includes(:transactable, user: [:profile]).order(created_at: :desc) end + + def credit + @wallet = Wallet.find(params[:id]) + authorize @wallet + service = WalletService.new(user: current_user, wallet: @wallet) + if service.credit(params[:amount].to_f) + render :show + else + head 422 + end + end end diff --git a/app/models/notification_type.rb b/app/models/notification_type.rb index 38e8376f8..bbdf3e484 100644 --- a/app/models/notification_type.rb +++ b/app/models/notification_type.rb @@ -37,5 +37,7 @@ class NotificationType notify_admin_profile_complete notify_admin_abuse_reported notify_admin_invoicing_changed + notify_user_wallet_is_credited + notify_admin_user_wallet_is_credited ) end diff --git a/app/policies/wallet_policy.rb b/app/policies/wallet_policy.rb index b3f849b11..edc737386 100644 --- a/app/policies/wallet_policy.rb +++ b/app/policies/wallet_policy.rb @@ -6,4 +6,8 @@ class WalletPolicy < ApplicationPolicy def transactions? user.is_admin? or user == record.user end + + def credit? + user.is_admin? + end end diff --git a/app/services/wallet_service.rb b/app/services/wallet_service.rb index 62e657c98..237804928 100644 --- a/app/services/wallet_service.rb +++ b/app/services/wallet_service.rb @@ -6,7 +6,14 @@ class WalletService def credit(amount) if @wallet.credit(amount) - WalletTransaction.create(user: @user, wallet: @wallet, transaction_type: 'credit', amount: amount) + transaction = WalletTransaction.create(user: @user, wallet: @wallet, transaction_type: 'credit', amount: amount) + + NotificationCenter.call type: 'notify_user_wallet_is_credited', + receiver: @wallet.user, + attached_object: transaction + NotificationCenter.call type: 'notify_admin_user_wallet_is_credited', + receiver: User.admins, + attached_object: transaction return true end end diff --git a/app/views/api/notifications/_notify_admin_user_wallet_is_credited.json.jbuilder b/app/views/api/notifications/_notify_admin_user_wallet_is_credited.json.jbuilder new file mode 100644 index 000000000..6b692d542 --- /dev/null +++ b/app/views/api/notifications/_notify_admin_user_wallet_is_credited.json.jbuilder @@ -0,0 +1,7 @@ +json.title notification.notification_type +amount = notification.attached_object.amount +json.description t('.wallet_is_credited', + AMOUNT: number_to_currency(amount), + USER: notification.attached_object.wallet.user.profile.full_name, + ADMIN: notification.attached_object.user.profile.full_name) +json.url notification_url(notification, format: :json) diff --git a/app/views/api/notifications/_notify_user_wallet_is_credited.json.jbuilder b/app/views/api/notifications/_notify_user_wallet_is_credited.json.jbuilder new file mode 100644 index 000000000..836492cf5 --- /dev/null +++ b/app/views/api/notifications/_notify_user_wallet_is_credited.json.jbuilder @@ -0,0 +1,5 @@ +json.title notification.notification_type +amount = notification.attached_object.amount +json.description t('.your_wallet_is_credited', + AMOUNT: number_to_currency(amount)) +json.url notification_url(notification, format: :json) diff --git a/app/views/notifications_mailer/notify_admin_user_wallet_is_credited.html.erb b/app/views/notifications_mailer/notify_admin_user_wallet_is_credited.html.erb new file mode 100644 index 000000000..8f758a2c8 --- /dev/null +++ b/app/views/notifications_mailer/notify_admin_user_wallet_is_credited.html.erb @@ -0,0 +1,9 @@ +<%# this is a mail template of notifcation notify_admin_user_wallet_is_credited %> +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> +

    + <%= t('.body.wallet_credit_html', + AMOUNT: number_to_currency(@attached_object.amount), + USER: @attached_object.wallet.user.profile.full_name, + ADMIN: @attached_object.user.profile.full_name) + %> +

    diff --git a/app/views/notifications_mailer/notify_user_wallet_is_credited.html.erb b/app/views/notifications_mailer/notify_user_wallet_is_credited.html.erb new file mode 100644 index 000000000..a2894c454 --- /dev/null +++ b/app/views/notifications_mailer/notify_user_wallet_is_credited.html.erb @@ -0,0 +1,2 @@ +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> +

    <%= t('.body.wallet_credit_html', AMOUNT: number_to_currency(@attached_object.amount)) %>

    diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index 7c655ba36..190386259 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -304,4 +304,7 @@ en: operation: 'Operation' operator: 'Operator' amount: 'Amount' - credit: 'Crédit' + credit: 'Credit' + to_credit: 'Credit' + wallet_credit_successfully: "Wallet of user is credited successfully." + a_problem_occurred_for_wallet_credit: "A problem is occurred while taking the credit of wallet" diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml index 7215c8599..6eee7821a 100644 --- a/config/locales/app.shared.fr.yml +++ b/config/locales/app.shared.fr.yml @@ -305,3 +305,8 @@ fr: operator: 'Opérateur' amount: 'Montant' credit: 'Crédit' + to_credit: 'Créditer' + wallet_credit_successfully: "Le porte-monnaie d'utilisateur a été chargé avec succès." + a_problem_occurred_for_wallet_credit: "Il y a eu un problème lors de chargement au porte-monnaie d'utilisateur." + amount_is_required: "Le montant est obligatoire" + amount_minimum_1: "Le montant minimum est d'1" diff --git a/config/locales/en.yml b/config/locales/en.yml index 32afecb9f..0e50e0059 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -229,6 +229,10 @@ en: undefined_notification: unknown_notification: "Unknown notification" notification_ID_wrong_type_TYPE_unknown: "Notification %{ID} wrong (type %{TYPE} unknown)" + notify_user_wallet_is_credited: + your_wallet_is_credited: "Your wallet is credited" + notify_admin_user_wallet_is_credited: + wallet_is_credited: "The wallet of %{USER} is credited %{AMOUNT}" statistics: # statistics tools for admins diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6f93733f6..607c419fd 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -229,6 +229,10 @@ fr: undefined_notification: unknown_notification: "Notification inconnue" notification_ID_wrong_type_TYPE_unknown: "Notification {ID} erronée (type {TYPE} inconnu)." + notify_user_wallet_is_credited: + your_wallet_is_credited: "Votre porte-monnaie est chargé %{AMOUNT}" + notify_admin_user_wallet_is_credited: + wallet_is_credited: "Le porte-monnaie de %{USER} est chargé %{AMOUNT}" statistics: # outil de statistiques pour les administrateurs diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index 8ec076374..ba37e145f 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -240,5 +240,15 @@ en: disabled: "From now on, no invoice will be issued when the user pays at the reception." enabled: "From now on, all payments made by this user at the reception will lead to invoicing issuing. " + notify_user_wallet_is_credited: + subject: "Your wallet is credited" + body: + wallet_credit_html: "Your wallet is credited %{AMOUNT}." + + notify_admin_user_wallet_is_credited: + subject: "The wallet of an user is credited" + body: + wallet_credit_html: "The wallet of %{USER} is credited %{AMOUNT} by %{ADMIN}." + shared: hello: "Hello %{user_name}" diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index 2f18c1ac3..63fb6fe4c 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -240,5 +240,15 @@ fr: disabled: "Désormais, aucune facture ne sera générée pour les paiement de cet utilisateur effectués à l'accueil." enabled: "Désormais, tous les paiement de cet utilisateur effectués à l'accueil, donneront lieu à la génération d'une facture." + notify_user_wallet_is_credited: + subject: "Votre porte-monnaie est chargé" + body: + wallet_credit_html: "Votre porte-monnaie est chargé %{AMOUNT}." + + notify_admin_user_wallet_is_credited: + subject: "Le porte-monnaie d'un utilisateur est chargé" + body: + wallet_credit_html: "Le porte-monnaie de %{USER} est chargé %{AMOUNT} par %{ADMIN}." + shared: hello: "Bonjour %{user_name}" diff --git a/config/routes.rb b/config/routes.rb index 02d730d0a..0d90fc87e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,7 @@ Rails.application.routes.draw do get :my, on: :collection get '/by_user/:user_id', action: 'by_user', on: :collection get :transactions, on: :member + put :credit, on: :member end # for homepage diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index be1035b96..05c56fd39 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -56,4 +56,23 @@ class WalletsTest < ActionDispatch::IntegrationTest get "/api/wallet/#{user5.wallet.id}/transactions" assert_equal 403, response.status end + + test 'admin can credit amount to a wallet' do + admin = users(:user_1) + login_as(admin, scope: :user) + w = @kdumas.wallet + amount = 10 + expected_amount = w.amount + amount + put "/api/wallet/#{w.id}/credit", + { + amount: amount + } + + assert_equal 200, response.status + assert_equal Mime::JSON, response.content_type + wallet = json_response(response.body) + w.reload + assert_equal w.amount, expected_amount + assert_equal w.amount, wallet[:amount] + end end From 31d5c6d3b33417a649058860588874ced60cb289 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Thu, 7 Jul 2016 15:57:06 +0200 Subject: [PATCH 09/30] pay totalement/partialement reservation by wallet --- .../javascripts/controllers/events.coffee.erb | 41 +++++++++++----- .../controllers/machines.coffee.erb | 47 +++++++++++++----- .../controllers/trainings.coffee.erb | 48 ++++++++++++++----- app/assets/javascripts/router.coffee.erb | 10 ++-- .../javascripts/services/helpers.coffee | 6 +++ app/assets/javascripts/services/wallet.coffee | 4 -- .../shared/valid_reservation_modal.html.erb | 4 ++ .../templates/stripe/payment_modal.html.erb | 3 ++ app/controllers/api/wallet_controller.rb | 7 +-- app/models/reservation.rb | 42 +++++++++++----- app/policies/wallet_policy.rb | 2 +- app/services/wallet_service.rb | 9 ++++ config/routes.rb | 3 +- test/integration/wallets_test.rb | 2 +- 14 files changed, 161 insertions(+), 67 deletions(-) create mode 100644 app/assets/javascripts/services/helpers.coffee diff --git a/app/assets/javascripts/controllers/events.coffee.erb b/app/assets/javascripts/controllers/events.coffee.erb index b3d84f317..7e2adb3da 100644 --- a/app/assets/javascripts/controllers/events.coffee.erb +++ b/app/assets/javascripts/controllers/events.coffee.erb @@ -132,8 +132,8 @@ Application.Controllers.controller "EventsController", ["$scope", "$state", 'Eve -Application.Controllers.controller "ShowEventController", ["$scope", "$state", "$stateParams", "Event", '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'reducedAmountAlert', 'growl', '_t' -($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, reducedAmountAlert, growl, _t) -> +Application.Controllers.controller "ShowEventController", ["$scope", "$state", "$stateParams", "Event", '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'reducedAmountAlert', 'growl', '_t', 'Wallet', 'helpers', +($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, reducedAmountAlert, growl, _t, Wallet, helpers) -> @@ -242,11 +242,13 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " if Object.keys($scope.ctrl.member).length > 0 reservation = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) - if $scope.currentUser.role isnt 'admin' and $scope.reserve.amountTotal > 0 - payByStripe(reservation) - else - if $scope.currentUser.role is 'admin' or $scope.reserve.amountTotal is 0 - payOnSite(reservation) + Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) -> + amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount) + if $scope.currentUser.role isnt 'admin' and amountToPay > 0 + payByStripe(reservation) + else + if $scope.currentUser.role is 'admin' or amountToPay is 0 + payOnSite(reservation) else # otherwise we alert, this error musn't occur when the current user is not admin growl.error(_t('please_select_a_member_first')) @@ -461,15 +463,20 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " reservation price: -> Price.compute({reservation: reservation}).$promise + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise objectToPay: -> eventToReserve: $scope.event reserve: $scope.reserve member: $scope.ctrl.member - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $locale) -> + # user wallet amount + $scope.walletAmount = wallet.amount + # Price - $scope.amount = price.price + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # CGV $scope.cgv = cgv.custom_asset @@ -477,6 +484,8 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + # Callback for the stripe payment authorization $scope.payment = (status, response) -> if response.error @@ -511,13 +520,23 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " reservation price: -> Price.compute({reservation: reservation}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation) -> + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', '$locale', 'helpers', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, $locale, helpers) -> + # user wallet amount + $scope.walletAmount = wallet.amount + # Price - $scope.amount = price.price + $scope.price = price.price + + # price to pay + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + # Button label if $scope.amount > 0 $scope.validButtonName = _t('confirm_(payment_on_site)') diff --git a/app/assets/javascripts/controllers/machines.coffee.erb b/app/assets/javascripts/controllers/machines.coffee.erb index 79f299d62..e38e0824c 100644 --- a/app/assets/javascripts/controllers/machines.coffee.erb +++ b/app/assets/javascripts/controllers/machines.coffee.erb @@ -268,8 +268,8 @@ Application.Controllers.controller "ShowMachineController", ['$scope', '$state', # This controller workflow is pretty similar to the trainings reservation controller. ## -Application.Controllers.controller "ReserveMachineController", ["$scope", "$state", '$stateParams', "$uibModal", '_t', "moment", 'Machine', 'Auth', 'dialogs', '$timeout', 'Price', 'Member', 'Availability', 'Slot', 'Setting', 'CustomAsset', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'uiCalendarConfig', 'CalendarConfig' -($scope, $state, $stateParams, $uibModal, _t, moment, Machine, Auth, dialogs, $timeout, Price, Member, Availability, Slot, Setting, CustomAsset, plansPromise, groupsPromise, growl, settingsPromise, uiCalendarConfig, CalendarConfig) -> +Application.Controllers.controller "ReserveMachineController", ["$scope", "$state", '$stateParams', "$uibModal", '_t', "moment", 'Machine', 'Auth', 'dialogs', '$timeout', 'Price', 'Member', 'Availability', 'Slot', 'Setting', 'CustomAsset', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'Wallet', 'helpers', 'uiCalendarConfig', 'CalendarConfig', +($scope, $state, $stateParams, $uibModal, _t, moment, Machine, Auth, dialogs, $timeout, Price, Member, Availability, Slot, Setting, CustomAsset, plansPromise, groupsPromise, growl, settingsPromise, Wallet, helpers, uiCalendarConfig, CalendarConfig) -> @@ -529,11 +529,13 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat if Object.keys($scope.ctrl.member).length > 0 reservation = mkReservation($scope.ctrl.member, $scope.eventsReserved, $scope.selectedPlan) - if $scope.currentUser.role isnt 'admin' and $scope.amountTotal > 0 - payByStripe(reservation) - else - if $scope.currentUser.role is 'admin' or $scope.amountTotal is 0 - payOnSite(reservation) + Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) -> + amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount) + if $scope.currentUser.role isnt 'admin' and amountToPay > 0 + payByStripe(reservation) + else + if $scope.currentUser.role is 'admin' or amountToPay is 0 + payOnSite(reservation) else # otherwise we alert, this error musn't occur when the current user is not admin growl.error(_t('please_select_a_member_first')) @@ -752,11 +754,16 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat reservation price: -> Price.compute({reservation: reservation}).$promise + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $locale) -> + # user wallet amount + $scope.walletAmount = wallet.amount + # Price - $scope.amount = price.price + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # CGV $scope.cgv = cgv.custom_asset @@ -764,6 +771,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + ## # Callback to process the payment with Stripe, triggered on button click ## @@ -800,19 +809,31 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat reservation price: -> Price.compute({reservation: reservation}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation) -> + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', '$locale', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, $locale) -> + # user wallet amount + $scope.walletAmount = wallet.amount # Price - $scope.amount = price.price + $scope.price = price.price + + # price to pay + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + # Button label if $scope.amount > 0 - $scope.validButtonName = _t('confirm_(payment_on_site)') + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.amount)}" else - $scope.validButtonName = _t('confirm') + if price.price > 0 + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')(price.price)}" + else + $scope.validButtonName = _t('confirm') ## # Callback to process the local payment, triggered on button click diff --git a/app/assets/javascripts/controllers/trainings.coffee.erb b/app/assets/javascripts/controllers/trainings.coffee.erb index f7c8589fc..c525f0baa 100644 --- a/app/assets/javascripts/controllers/trainings.coffee.erb +++ b/app/assets/javascripts/controllers/trainings.coffee.erb @@ -51,8 +51,8 @@ Application.Controllers.controller "ShowTrainingController", ['$scope', '$state' # training can be reserved during the reservation process (the shopping cart may contains only one training and a subscription). ## -Application.Controllers.controller "ReserveTrainingController", ["$scope", "$state", '$stateParams', '$filter', '$compile', "$uibModal", 'Auth', 'dialogs', '$timeout', 'Price', 'Availability', 'Slot', 'Member', 'Setting', 'CustomAsset', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'trainingPromise', '_t', 'uiCalendarConfig', 'CalendarConfig' -($scope, $state, $stateParams, $filter, $compile, $uibModal, Auth, dialogs, $timeout, Price, Availability, Slot, Member, Setting, CustomAsset, availabilityTrainingsPromise, plansPromise, groupsPromise, growl, settingsPromise, trainingPromise, _t, uiCalendarConfig, CalendarConfig) -> +Application.Controllers.controller "ReserveTrainingController", ["$scope", "$state", '$stateParams', '$filter', '$compile', "$uibModal", 'Auth', 'dialogs', '$timeout', 'Price', 'Availability', 'Slot', 'Member', 'Setting', 'CustomAsset', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'trainingPromise', '_t', 'Wallet', 'helpers', 'uiCalendarConfig', 'CalendarConfig' +($scope, $state, $stateParams, $filter, $compile, $uibModal, Auth, dialogs, $timeout, Price, Availability, Slot, Member, Setting, CustomAsset, availabilityTrainingsPromise, plansPromise, groupsPromise, growl, settingsPromise, trainingPromise, _t, Wallet, helpers, uiCalendarConfig, CalendarConfig) -> @@ -188,11 +188,13 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta if Object.keys($scope.ctrl.member).length > 0 reservation = mkReservation($scope.ctrl.member, $scope.selectedTraining, $scope.selectedPlan) - if $scope.currentUser.role isnt 'admin' and $scope.amountTotal > 0 - payByStripe(reservation) - else - if $scope.currentUser.role is 'admin' or $scope.amountTotal is 0 - payOnSite(reservation) + Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) -> + amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount) + if $scope.currentUser.role isnt 'admin' and amountToPay > 0 + payByStripe(reservation) + else + if $scope.currentUser.role is 'admin' or amountToPay is 0 + payOnSite(reservation) else # otherwise we alert, this error musn't occur when the current user is not admin growl.error(_t('please_select_a_member_first')) @@ -474,11 +476,16 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta reservation price: -> Price.compute({reservation: reservation}).$promise + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'wallet', 'cgv', 'Auth', 'Reservation', '$locale', 'helpers', ($scope, $uibModalInstance, $state, reservation, price, wallet, cgv, Auth, Reservation, $locale, helpers) -> + # user wallet amount + $scope.walletAmount = wallet.amount + # Price - $scope.amount = price.price + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # CGV $scope.cgv = cgv.custom_asset @@ -486,6 +493,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + ## # Callback to process the payment with Stripe, triggered on button click ## @@ -525,18 +534,31 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta reservation price: -> Price.compute({reservation: reservation}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation) -> + wallet: -> + Wallet.getWalletByUser({user_id: reservation.user_id}).$promise + controller: ['$scope', '$uibModalInstance', '$state', '$filter', 'reservation', 'price', 'wallet', 'Auth', 'Reservation', '$locale', 'helpers', ($scope, $uibModalInstance, $state, $filter, reservation, price, wallet, Auth, Reservation, $locale, helpers) -> + # user wallet amount + $scope.walletAmount = wallet.amount + # Price - $scope.amount = price.price + $scope.price = price.price + + # price to pay + $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # Reservation $scope.reservation = reservation + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + # Button label if $scope.amount > 0 - $scope.validButtonName = _t('confirm_(payment_on_site)') + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.amount)}" else - $scope.validButtonName = _t('confirm') + if price.price > 0 + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')(price.price)}" + else + $scope.validButtonName = _t('confirm') ## # Callback to process the local payment, triggered on button click diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 347c49b68..591b790aa 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -204,8 +204,8 @@ angular.module('application.router', ['ui.router']). templateUrl: '<%= asset_path "dashboard/wallet.html" %>' controller: 'WalletController' resolve: - walletPromise: ['Wallet', (Wallet)-> - Wallet.my().$promise + walletPromise: ['Wallet', 'currentUser', (Wallet, currentUser)-> + Wallet.getWalletByUser(user_id: currentUser.id).$promise ] transactionsPromise: ['Wallet', 'walletPromise', (Wallet, walletPromise)-> Wallet.transactions(id: walletPromise.id).$promise @@ -363,7 +363,7 @@ angular.module('application.router', ['ui.router']). ] translations: [ 'Translations', (Translations) -> Translations.query(['app.logged.machines_reserve', 'app.shared.plan_subscribe', 'app.shared.member_select', - 'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal']).$promise + 'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal', 'app.shared.wallet']).$promise ] .state 'app.admin.machines_edit', url: '/machines/:id/edit' @@ -440,7 +440,7 @@ angular.module('application.router', ['ui.router']). ] translations: [ 'Translations', (Translations) -> Translations.query(['app.logged.trainings_reserve', 'app.shared.plan_subscribe', 'app.shared.member_select', - 'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal']).$promise + 'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal', 'app.shared.wallet']).$promise ] # notifications .state 'app.logged.notifications', @@ -510,7 +510,7 @@ angular.module('application.router', ['ui.router']). Setting.get(name: 'event_reduced_amount_alert').$promise ] translations: [ 'Translations', (Translations) -> - Translations.query(['app.public.events_show', 'app.shared.member_select', 'app.shared.stripe', 'app.shared.valid_reservation_modal']).$promise + Translations.query(['app.public.events_show', 'app.shared.member_select', 'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.wallet']).$promise ] # calendar global (trainings, machines and events) diff --git a/app/assets/javascripts/services/helpers.coffee b/app/assets/javascripts/services/helpers.coffee new file mode 100644 index 000000000..488f12852 --- /dev/null +++ b/app/assets/javascripts/services/helpers.coffee @@ -0,0 +1,6 @@ +'use strict' + +Application.Services.factory 'helpers', [()-> + getAmountToPay: (price, walletAmount)-> + if walletAmount > price then 0 else price - walletAmount + ] diff --git a/app/assets/javascripts/services/wallet.coffee b/app/assets/javascripts/services/wallet.coffee index a44326683..44c414fdb 100644 --- a/app/assets/javascripts/services/wallet.coffee +++ b/app/assets/javascripts/services/wallet.coffee @@ -3,10 +3,6 @@ Application.Services.factory 'Wallet', ["$resource", ($resource)-> $resource "/api/wallet", {}, - my: - method: 'GET' - url: '/api/wallet/my' - isArray: false getWalletByUser: method: 'GET' url: '/api/wallet/by_user/:user_id' diff --git a/app/assets/templates/shared/valid_reservation_modal.html.erb b/app/assets/templates/shared/valid_reservation_modal.html.erb index 1c2483cd6..39e25a1b8 100644 --- a/app/assets/templates/shared/valid_reservation_modal.html.erb +++ b/app/assets/templates/shared/valid_reservation_modal.html.erb @@ -3,6 +3,10 @@

    {{ 'booking_confirmation' }}

    +

    {{ 'you_have_amount_in_wallet' | translate:{ amount: walletAmount, currency: currencySymbol } }}

    +

    {{'credit_amount_for_pay_reservation' | translate:{ amount: amount, currency: currencySymbol } }}

    +
    diff --git a/app/controllers/api/wallet_controller.rb b/app/controllers/api/wallet_controller.rb index c62672b5a..9753bf63d 100644 --- a/app/controllers/api/wallet_controller.rb +++ b/app/controllers/api/wallet_controller.rb @@ -1,14 +1,9 @@ class API::WalletController < API::ApiController before_action :authenticate_user! - def my - @wallet = current_user.wallet - render :show - end - def by_user - authorize Wallet @wallet = Wallet.find_by(user_id: params[:user_id]) + authorize @wallet render :show end diff --git a/app/models/reservation.rb b/app/models/reservation.rb index ab9f353d4..d1046f467 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -17,6 +17,7 @@ class Reservation < ActiveRecord::Base after_commit :notify_member_create_reservation, on: :create after_commit :notify_admin_member_create_reservation, on: :create after_save :update_event_nb_free_places, if: Proc.new { |reservation| reservation.reservable_type == 'Event' } + after_save :debit_user_wallet # # Generate an array of {Stripe::InvoiceItem} with the elements in the current reservation, price included. @@ -124,6 +125,18 @@ class Reservation < ActiveRecord::Base end + total = invoice_items.map(&:amount).map(&:to_i).reduce(:+) || 0 + wallet_amount = (user.wallet.amount * 100).to_i + @wallet_amount_debit = wallet_amount >= total ? total : wallet_amount + if @wallet_amount_debit != 0 + invoice_items << Stripe::InvoiceItem.create( + customer: user.stp_customer_id, + amount: -@wallet_amount_debit, + currency: Rails.application.secrets.stripe_currency, + description: "wallet -#{@wallet_amount_debit / 100.0}" + ) + end + # let's return the resulting array of items invoice_items end @@ -170,49 +183,49 @@ class Reservation < ActiveRecord::Base # # IMPORTANT NOTE: here, we have to create an invoice manually and pay it to pay all waiting stripe invoice items # - invoice = Stripe::Invoice.create( + stp_invoice = Stripe::Invoice.create( customer: user.stp_customer_id, ) - invoice.pay + stp_invoice.pay card.delete if card - self.stp_invoice_id = invoice.id - self.invoice.stp_invoice_id = invoice.id - self.invoice.total = invoice.total + self.stp_invoice_id = stp_invoice.id + self.invoice.stp_invoice_id = stp_invoice.id + self.invoice.total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+) save! rescue Stripe::CardError => card_error - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.info card_error errors[:card] << card_error.message return false rescue Stripe::InvalidRequestError => e # Invalid parameters were supplied to Stripe's API - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.error e errors[:payment] << e.message return false rescue Stripe::AuthenticationError => e # Authentication with Stripe's API failed # (maybe you changed API keys recently) - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.error e errors[:payment] << e.message return false rescue Stripe::APIConnectionError => e # Network communication with Stripe failed - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.error e errors[:payment] << e.message return false rescue Stripe::StripeError => e # Display a very generic error to the user, and maybe send # yourself an email - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.error e errors[:payment] << e.message return false rescue => e # Something else happened, completely unrelated to Stripe - clear_payment_info(card, invoice, invoice_items) + clear_payment_info(card, stp_invoice, invoice_items) logger.error e errors[:payment] << e.message return false @@ -332,4 +345,11 @@ class Reservation < ActiveRecord::Base end reservable.update_columns(nb_free_places: nb_free_places) end + + def debit_user_wallet + if @wallet_amount_debit.present? and @wallet_amount_debit != 0 + amount = @wallet_amount_debit / 100.0 + WalletService.new(user: user, wallet: user.wallet).debit(amount, self) + end + end end diff --git a/app/policies/wallet_policy.rb b/app/policies/wallet_policy.rb index edc737386..028fb54f9 100644 --- a/app/policies/wallet_policy.rb +++ b/app/policies/wallet_policy.rb @@ -1,6 +1,6 @@ class WalletPolicy < ApplicationPolicy def by_user? - user.is_admin? + user.is_admin? or user == record.user end def transactions? diff --git a/app/services/wallet_service.rb b/app/services/wallet_service.rb index 237804928..53381439b 100644 --- a/app/services/wallet_service.rb +++ b/app/services/wallet_service.rb @@ -16,5 +16,14 @@ class WalletService attached_object: transaction return true end + return false + end + + def debit(amount, transactable) + if @wallet.debit(amount) + WalletTransaction.create(user: @user, wallet: @wallet, transaction_type: 'debit', amount: amount, transactable: transactable) + return true + end + return false end end diff --git a/config/routes.rb b/config/routes.rb index 0d90fc87e..eb95d7ac5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,8 +48,7 @@ Rails.application.routes.draw do resources :notifications, only: [:index, :show, :update] do match :update_all, path: '/', via: [:put, :patch], on: :collection end - resources :wallet do - get :my, on: :collection + resources :wallet, only: [] do get '/by_user/:user_id', action: 'by_user', on: :collection get :transactions, on: :member put :credit, on: :member diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index 05c56fd39..25c84e1c4 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -15,7 +15,7 @@ class WalletsTest < ActionDispatch::IntegrationTest end test 'get my wallet' do - get '/api/wallet/my' + get "/api/wallet/by_user/#{@kdumas.id}" assert_equal 200, response.status assert_equal Mime::JSON, response.content_type wallet = json_response(response.body) From 2dbc026db1503708489ba5d7607a2e57c902c6d5 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Fri, 8 Jul 2016 17:24:51 +0200 Subject: [PATCH 10/30] pay subscription by wallet --- .../javascripts/controllers/plans.coffee.erb | 51 +++++++++++++++---- app/assets/javascripts/router.coffee.erb | 2 +- .../templates/plans/payment_modal.html.erb | 6 ++- .../api/subscriptions_controller.rb | 4 +- app/models/reservation.rb | 25 +++++++-- app/models/subscription.rb | 30 ++++++++++- config/locales/app.shared.fr.yml | 3 ++ 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/controllers/plans.coffee.erb b/app/assets/javascripts/controllers/plans.coffee.erb index 529f2ba25..dd7f98d99 100644 --- a/app/assets/javascripts/controllers/plans.coffee.erb +++ b/app/assets/javascripts/controllers/plans.coffee.erb @@ -1,7 +1,7 @@ 'use strict' -Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScope", "$state", '$uibModal', 'Auth', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t' -, ($scope, $rootScope, $state, $uibModal, Auth, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t) -> +Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScope", "$state", '$uibModal', 'Auth', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t', 'Wallet', 'helpers' +, ($scope, $rootScope, $state, $uibModal, Auth, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) -> @@ -72,10 +72,13 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop # Callback to trigger the payment process of the subscription ## $scope.openSubscribePlanModal = -> - if $scope.currentUser.role isnt 'admin' - payByStripe() - else - payOnSite() + Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) -> + amountToPay = helpers.getAmountToPay($scope.selectedPlan.amount, wallet.amount) + if $scope.currentUser.role isnt 'admin' and amountToPay > 0 + payByStripe() + else + if $scope.currentUser.role is 'admin' or amountToPay is 0 + payOnSite() @@ -162,9 +165,17 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop resolve: selectedPlan: -> $scope.selectedPlan member: -> $scope.ctrl.member - controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', 'CustomAsset', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription, CustomAsset) -> - $scope.amount = selectedPlan.amount + wallet: -> + Wallet.getWalletByUser({user_id: $scope.ctrl.member.id}).$promise + controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', 'CustomAsset', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription, CustomAsset, wallet, helpers, $locale) -> + # user wallet amount + $scope.walletAmount = wallet.amount + + $scope.amount = helpers.getAmountToPay(selectedPlan.amount, wallet.amount) + $scope.selectedPlan = selectedPlan + + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM # retrieve the CGV CustomAsset.get {name: 'cgv-file'}, (cgv) -> $scope.cgv = cgv.custom_asset @@ -203,9 +214,31 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop resolve: selectedPlan: -> $scope.selectedPlan member: -> $scope.ctrl.member - controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription) -> + wallet: -> + Wallet.getWalletByUser({user_id: $scope.ctrl.member.id}).$promise + controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', 'wallet', 'helpers', '$locale', '$filter', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription, wallet, helpers, $locale, $filter) -> + # user wallet amount + $scope.walletAmount = wallet.amount + + $scope.price = selectedPlan.amount + + # price to pay + $scope.amount = helpers.getAmountToPay($scope.price, wallet.amount) + + $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.plan = selectedPlan $scope.member = member + + # Button label + if $scope.amount > 0 + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.amount)}" + else + if $scope.price > 0 + $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.price)}" + else + $scope.validButtonName = _t('confirm') + $scope.ok = -> $scope.attempting = true Subscription.save diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 591b790aa..1c1d92d42 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -473,7 +473,7 @@ angular.module('application.router', ['ui.router']). Group.query().$promise ] translations: [ 'Translations', (Translations) -> - Translations.query(['app.public.plans', 'app.shared.member_select', 'app.shared.stripe']).$promise + Translations.query(['app.public.plans', 'app.shared.member_select', 'app.shared.stripe', 'app.shared.wallet']).$promise ] # events diff --git a/app/assets/templates/plans/payment_modal.html.erb b/app/assets/templates/plans/payment_modal.html.erb index 42b33239f..faa472c24 100644 --- a/app/assets/templates/plans/payment_modal.html.erb +++ b/app/assets/templates/plans/payment_modal.html.erb @@ -3,10 +3,14 @@

    {{ 'subscription_confirmation' }}

    diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb index 20bcdc709..efcef23b1 100644 --- a/app/controllers/api/subscriptions_controller.rb +++ b/app/controllers/api/subscriptions_controller.rb @@ -18,8 +18,10 @@ class API::SubscriptionsController < API::ApiController @subscription.attributes = subscription_params is_subscribe = @subscription.save_with_local_payment(!User.find(subscription_params[:user_id]).invoicing_disabled?) else + member = User.find(subscription_params[:user_id]) + plan = Plan.find(subscription_params[:plan_id]) @subscription = Subscription.find_or_initialize_by(user_id: current_user.id) - if valid_card_token?(subscription_params[:card_token]) + if valid_card_token?(subscription_params[:card_token]) or (member.wallet.amount >= plan.amount / 100.0) @subscription.update_column(:expired_at, nil) unless @subscription.new_record? # very important @subscription.attributes = subscription_params.merge(user_id: current_user.id) is_subscribe = @subscription.save_with_payment diff --git a/app/models/reservation.rb b/app/models/reservation.rb index d1046f467..4d60e53e5 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -17,7 +17,7 @@ class Reservation < ActiveRecord::Base after_commit :notify_member_create_reservation, on: :create after_commit :notify_admin_member_create_reservation, on: :create after_save :update_event_nb_free_places, if: Proc.new { |reservation| reservation.reservable_type == 'Event' } - after_save :debit_user_wallet + after_create :debit_user_wallet # # Generate an array of {Stripe::InvoiceItem} with the elements in the current reservation, price included. @@ -125,10 +125,8 @@ class Reservation < ActiveRecord::Base end - total = invoice_items.map(&:amount).map(&:to_i).reduce(:+) || 0 - wallet_amount = (user.wallet.amount * 100).to_i - @wallet_amount_debit = wallet_amount >= total ? total : wallet_amount - if @wallet_amount_debit != 0 + @wallet_amount_debit = get_wallet_amount_debit + if @wallet_amount_debit != 0 and !on_site invoice_items << Stripe::InvoiceItem.create( customer: user.stp_customer_id, amount: -@wallet_amount_debit, @@ -264,6 +262,14 @@ class Reservation < ActiveRecord::Base def save_with_local_payment if user.invoicing_disabled? if valid? + + ### generate invoice only for calcul price, to refactoring!! + build_invoice(user: user) + generate_invoice_items(true) + @wallet_amount_debit = get_wallet_amount_debit + self.invoice = nil + ### + save! UsersCredits::Manager.new(reservation: self).update_credits return true @@ -346,6 +352,15 @@ class Reservation < ActiveRecord::Base reservable.update_columns(nb_free_places: nb_free_places) end + def get_wallet_amount_debit + total = self.invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+) or 0 + if plan_id.present? + total += plan.amount + end + wallet_amount = (user.wallet.amount * 100).to_i + return wallet_amount >= total ? total : wallet_amount + end + def debit_user_wallet if @wallet_amount_debit.present? and @wallet_amount_debit != 0 amount = @wallet_amount_debit / 100.0 diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 04ecc8db3..73445a947 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -16,13 +16,27 @@ class Subscription < ActiveRecord::Base after_save :notify_member_subscribed_plan, if: :is_new? after_save :notify_admin_subscribed_plan, if: :is_new? after_save :notify_partner_subscribed_plan, if: :of_partner_plan? + after_save :debit_user_wallet # Stripe subscription payment def save_with_payment(invoice = true) if valid? customer = Stripe::Customer.retrieve(user.stp_customer_id) begin - new_subscription = customer.subscriptions.create(plan: plan.stp_plan_id, card: card_token) + # dont add a wallet invoice item if pay subscription by reservation + if invoice + @wallet_amount_debit = get_wallet_amount_debit + if @wallet_amount_debit != 0 + Stripe::InvoiceItem.create( + customer: user.stp_customer_id, + amount: -@wallet_amount_debit, + currency: Rails.application.secrets.stripe_currency, + description: "wallet -#{@wallet_amount_debit / 100.0}" + ) + end + end + + new_subscription = customer.subscriptions.create(plan: plan.stp_plan_id, source: card_token) self.stp_subscription_id = new_subscription.id self.canceled_at = nil self.expired_at = Time.at(new_subscription.current_period_end) @@ -73,6 +87,8 @@ class Subscription < ActiveRecord::Base def save_with_local_payment(invoice = true) if valid? + @wallet_amount_debit = get_wallet_amount_debit if invoice + self.stp_subscription_id = nil self.canceled_at = nil set_expired_at @@ -209,4 +225,16 @@ class Subscription < ActiveRecord::Base plan.is_a?(PartnerPlan) end + def get_wallet_amount_debit + total = plan.amount + wallet_amount = (user.wallet.amount * 100).to_i + return wallet_amount >= total ? total : wallet_amount + end + + def debit_user_wallet + if @wallet_amount_debit.present? and @wallet_amount_debit != 0 + amount = @wallet_amount_debit / 100.0 + WalletService.new(user: user, wallet: user.wallet).debit(amount, self) + end + end end diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml index 6eee7821a..049662dc8 100644 --- a/config/locales/app.shared.fr.yml +++ b/config/locales/app.shared.fr.yml @@ -160,6 +160,7 @@ fr: _the_general_terms_and_conditions: "les conditions générales de vente." enter_your_card_number: "Saisissez votre numéro de carte" confirm_my_payment_of_: "Valider mon paiement de" # contexte : valider mon paiement de 20,00 € + credit_amount_for_pay_reservation: "Vous devez créditer {{amount}} {{currency}} pour valider votre réservation" valid_reservation_modal: # fenêtre de paiement sur place d'une réservation @@ -310,3 +311,5 @@ fr: a_problem_occurred_for_wallet_credit: "Il y a eu un problème lors de chargement au porte-monnaie d'utilisateur." amount_is_required: "Le montant est obligatoire" amount_minimum_1: "Le montant minimum est d'1" + you_have_amount_in_wallet: "Vous avez {{amount}} {{currency}} sur votre porte-monnaie" + wallet_pay_reservation: "Vous pouvez effectuer directement votre paiement de réservation" From 61273bd66fa971e70b4670bc113324d5f94bb0d8 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 11 Jul 2016 11:40:20 +0200 Subject: [PATCH 11/30] fix test --- test/fixtures/wallet_transactions.yml | 4 ++-- test/fixtures/wallets.yml | 4 ++-- test/integration/events_test.rb | 4 ++-- test/integration/wallets_test.rb | 18 ++++++++-------- test/services/wallet_service_test.rb | 30 +++++++++++++++++++-------- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/test/fixtures/wallet_transactions.yml b/test/fixtures/wallet_transactions.yml index 9edb192b1..012604359 100644 --- a/test/fixtures/wallet_transactions.yml +++ b/test/fixtures/wallet_transactions.yml @@ -1,7 +1,7 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html transaction1: - user_id: 4 - wallet: wallet_4 + user_id: 5 + wallet: wallet_5 transaction_type: credit amount: 1000 diff --git a/test/fixtures/wallets.yml b/test/fixtures/wallets.yml index e86e3a29f..696573c94 100644 --- a/test/fixtures/wallets.yml +++ b/test/fixtures/wallets.yml @@ -4,7 +4,7 @@ wallet_2: wallet_4: user_id: 4 - amount: 1000 + amount: 0 wallet_6: user_id: 6 @@ -12,7 +12,7 @@ wallet_6: wallet_5: user_id: 5 - amount: 0 + amount: 1000 wallet_3: user_id: 3 diff --git a/test/integration/events_test.rb b/test/integration/events_test.rb index ca821ad45..98701c69d 100644 --- a/test/integration/events_test.rb +++ b/test/integration/events_test.rb @@ -69,7 +69,7 @@ class EventsTest < ActionDispatch::IntegrationTest reservable_type: 'Event', nb_reserve_places: 2, nb_reserve_reduced_places: 0, - slot_attributes: [ + slots_attributes: [ { start_at: e.availability.start_at, end_at: e.availability.end_at, @@ -114,4 +114,4 @@ class EventsTest < ActionDispatch::IntegrationTest assert_equal 20, e.nb_total_places, 'Total number of places was not updated' assert_equal 18, e.nb_free_places, 'Number of free places was not updated' end -end \ No newline at end of file +end diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index 25c84e1c4..5ace5e4af 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -3,8 +3,8 @@ class WalletsTest < ActionDispatch::IntegrationTest # Called before every test method runs. Can be used # to set up fixture information. def setup - @kdumas = User.find_by(username: 'kdumas') - login_as(@kdumas, scope: :user) + @vlonchamp = User.find_by(username: 'vlonchamp') + login_as(@vlonchamp, scope: :user) end # Called after every test method runs. Can be used to tear @@ -15,12 +15,12 @@ class WalletsTest < ActionDispatch::IntegrationTest end test 'get my wallet' do - get "/api/wallet/by_user/#{@kdumas.id}" + get "/api/wallet/by_user/#{@vlonchamp.id}" assert_equal 200, response.status assert_equal Mime::JSON, response.content_type wallet = json_response(response.body) - assert_equal @kdumas.wallet.user_id, wallet[:user_id] - assert_equal @kdumas.wallet.amount, wallet[:amount] + assert_equal @vlonchamp.wallet.user_id, wallet[:user_id] + assert_equal @vlonchamp.wallet.amount, wallet[:amount] end test 'admin can get wallet by user id' do @@ -36,13 +36,13 @@ class WalletsTest < ActionDispatch::IntegrationTest end test 'cant get wallet of an user if not admin' do - user5 = users(:user_5) + user5 = users(:user_4) get "/api/wallet/by_user/#{user5.id}" assert_equal 403, response.status end test 'get all transactions of wallet' do - w = @kdumas.wallet + w = @vlonchamp.wallet get "/api/wallet/#{w.id}/transactions" assert_equal 200, response.status assert_equal Mime::JSON, response.content_type @@ -52,7 +52,7 @@ class WalletsTest < ActionDispatch::IntegrationTest end test 'only admin and wallet owner can show their transactions' do - user5 = users(:user_5) + user5 = users(:user_4) get "/api/wallet/#{user5.wallet.id}/transactions" assert_equal 403, response.status end @@ -60,7 +60,7 @@ class WalletsTest < ActionDispatch::IntegrationTest test 'admin can credit amount to a wallet' do admin = users(:user_1) login_as(admin, scope: :user) - w = @kdumas.wallet + w = @vlonchamp.wallet amount = 10 expected_amount = w.amount + amount put "/api/wallet/#{w.id}/credit", diff --git a/test/services/wallet_service_test.rb b/test/services/wallet_service_test.rb index 46536423f..fc33ddcea 100644 --- a/test/services/wallet_service_test.rb +++ b/test/services/wallet_service_test.rb @@ -3,25 +3,37 @@ require 'test_helper' class WalletServiceTest < ActiveSupport::TestCase setup do @admin = User.find_by(username: 'admin') - @user = User.find_by(username: 'jdupond') - @wallet = @user.wallet + @jdupond = User.find_by(username: 'jdupond') + @jdupond_wallet = @jdupond.wallet + @vlonchamp = User.find_by(username: 'vlonchamp') + @vlonchamp_wallet = @vlonchamp.wallet end test 'admin can credit a wallet' do - service = WalletService.new(user: @admin, wallet: @wallet) - expected_amount = @wallet.amount + 5 + service = WalletService.new(user: @admin, wallet: @jdupond_wallet) + expected_amount = @jdupond_wallet.amount + 5 assert service.credit(5) - assert_equal @wallet.amount, expected_amount + assert_equal @jdupond_wallet.amount, expected_amount end test 'create a credit transaction after credit amount to wallet' do - service = WalletService.new(user: @admin, wallet: @wallet) - assert_equal 0, @wallet.wallet_transactions.count + service = WalletService.new(user: @admin, wallet: @jdupond_wallet) + assert_equal 0, @jdupond_wallet.wallet_transactions.count assert service.credit(10) - transaction = @wallet.wallet_transactions.first + transaction = @jdupond_wallet.wallet_transactions.first assert_equal transaction.transaction_type, 'credit' assert_equal transaction.amount, 10 assert_equal transaction.user, @admin - assert_equal transaction.wallet, @wallet + assert_equal transaction.wallet, @jdupond_wallet + end + + test 'create a debit transaction after debit amoutn to wallet' do + service = WalletService.new(user: @vlonchamp, wallet: @vlonchamp_wallet) + assert service.debit(5, nil) + transaction = @vlonchamp_wallet.wallet_transactions.last + assert_equal transaction.transaction_type, 'debit' + assert_equal transaction.amount, 5 + assert_equal transaction.user, @vlonchamp + assert_equal transaction.wallet, @vlonchamp_wallet end end From 220676b4dcfa08ad2c0806db0f656a7876aace26 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 11 Jul 2016 15:32:42 +0200 Subject: [PATCH 12/30] test user reservation/subscription with wallet --- .../reservations/create_as_admin_test.rb | 103 ++ test/integration/reservations/create_test.rb | 70 ++ .../subscriptions/create_as_user_test.rb | 60 +- ...ate_for_machine_and_pay_wallet_success.yml | 1063 +++++++++++++++++ ...for_machine_without_subscription_error.yml | 169 +-- ...ptions_user_create_success_with_wallet.yml | 821 +++++++++++++ 6 files changed, 2167 insertions(+), 119 deletions(-) create mode 100644 test/vcr_cassettes/reservations_create_for_machine_and_pay_wallet_success.yml create mode 100644 test/vcr_cassettes/subscriptions_user_create_success_with_wallet.yml diff --git a/test/integration/reservations/create_as_admin_test.rb b/test/integration/reservations/create_as_admin_test.rb index 4b569735b..046d166bb 100644 --- a/test/integration/reservations/create_as_admin_test.rb +++ b/test/integration/reservations/create_as_admin_test.rb @@ -220,5 +220,108 @@ module Reservations # notification assert_not_empty Notification.where(attached_object: reservation) end + + test "user without subscription reserves a machine and pay by wallet with success" do + @vlonchamp = User.find_by(username: 'vlonchamp') + machine = Machine.find(6) + availability = machine.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + users_credit_count = UsersCredit.count + + post reservations_path, { reservation: { + user_id: @vlonchamp.id, + 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, default_headers + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count + 1, Invoice.count + assert_equal invoice_items_count + 1, InvoiceItem.count + assert_equal users_credit_count, UsersCredit.count + + # reservation assertions + reservation = Reservation.last + + assert reservation.invoice + assert reservation.stp_invoice_id.blank? + assert_equal 1, reservation.invoice.invoice_items.count + + # invoice assertions + invoice = reservation.invoice + + assert invoice.stp_invoice_id.blank? + refute invoice.total.blank? + + # invoice_items assertions + invoice_item = InvoiceItem.last + + refute invoice_item.stp_invoice_item_id + assert_equal invoice_item.amount, machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert_invoice_pdf invoice + + # notification + assert_not_empty Notification.where(attached_object: reservation) + + # wallet + assert_equal @vlonchamp.wallet.amount, 0 + assert_equal @vlonchamp.wallet.wallet_transactions.count, 2 + transaction = @vlonchamp.wallet.wallet_transactions.last + assert_equal transaction.transaction_type, 'debit' + assert_equal transaction.amount, 10 + end + + test "user without subscription and with invoicing disabled reserves a machine and pay wallet with success" do + @vlonchamp = User.find_by(username: 'vlonchamp') + @vlonchamp.update!(invoicing_disabled: true) + machine = Machine.find(6) + availability = machine.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + users_credit_count = UsersCredit.count + + post reservations_path, { reservation: { + user_id: @vlonchamp.id, + 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, default_headers + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count, Invoice.count + assert_equal invoice_items_count, InvoiceItem.count + assert_equal users_credit_count, UsersCredit.count + + # reservation assertions + reservation = Reservation.last + + refute reservation.invoice + assert reservation.stp_invoice_id.blank? + + # notification + assert_not_empty Notification.where(attached_object: reservation) + end end end diff --git a/test/integration/reservations/create_test.rb b/test/integration/reservations/create_test.rb index 296162816..6648a7cd6 100644 --- a/test/integration/reservations/create_test.rb +++ b/test/integration/reservations/create_test.rb @@ -292,5 +292,75 @@ module Reservations # check that user subscription were extended assert_equal reservation.slots.first.start_at + plan.duration, @user_with_subscription.subscription.expired_at end + + test "user reserves a machine and pay by wallet with success" do + @vlonchamp = User.find_by(username: 'vlonchamp') + login_as(@vlonchamp, scope: :user) + + machine = Machine.find(6) + availability = machine.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + users_credit_count = UsersCredit.count + wallet_transactions_count = WalletTransaction.count + + VCR.use_cassette("reservations_create_for_machine_and_pay_wallet_success") do + post reservations_path, { reservation: { + user_id: @vlonchamp.id, + reservable_id: machine.id, + reservable_type: machine.class.name, + card_token: stripe_card_token, + 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, default_headers + end + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count + 1, Invoice.count + assert_equal invoice_items_count + 1, InvoiceItem.count + assert_equal users_credit_count, UsersCredit.count + assert_equal wallet_transactions_count + 1, WalletTransaction.count + + # reservation assertions + reservation = Reservation.last + + assert reservation.invoice + refute reservation.stp_invoice_id.blank? + assert_equal 1, reservation.invoice.invoice_items.count + + # invoice assertions + invoice = reservation.invoice + + refute invoice.stp_invoice_id.blank? + refute invoice.total.blank? + + # invoice_items assertions + invoice_item = InvoiceItem.last + + assert invoice_item.stp_invoice_item_id + assert_equal invoice_item.amount, machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert_invoice_pdf invoice + + # notification + assert_not_empty Notification.where(attached_object: reservation) + + # wallet + assert_equal @vlonchamp.wallet.amount, 0 + assert_equal @vlonchamp.wallet.wallet_transactions.count, 2 + transaction = @vlonchamp.wallet.wallet_transactions.last + assert_equal transaction.transaction_type, 'debit' + assert_equal transaction.amount, 10 + end end end diff --git a/test/integration/subscriptions/create_as_user_test.rb b/test/integration/subscriptions/create_as_user_test.rb index fd1882bca..0397dd85a 100644 --- a/test/integration/subscriptions/create_as_user_test.rb +++ b/test/integration/subscriptions/create_as_user_test.rb @@ -83,4 +83,62 @@ class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest assert_nil @user.subscription, "user's subscription was found" end -end \ No newline at end of file + + test 'user successfully takes a subscription with wallet' do + @vlonchamp = User.find_by(username: 'vlonchamp') + login_as(@vlonchamp, scope: :user) + plan = Plan.find_by(group_id: @vlonchamp.group.id, type: 'Plan', base_name: 'Mensuel tarif réduit') + + VCR.use_cassette("subscriptions_user_create_success_with_wallet") do + post '/api/subscriptions', + { + subscription: { + plan_id: plan.id, + user_id: @vlonchamp.id, + card_token: stripe_card_token + } + }.to_json, default_headers + end + + # Check response format & status + assert_equal 201, response.status, response.body + assert_equal Mime::JSON, response.content_type + + # Check the correct plan was subscribed + subscription = json_response(response.body) + assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match' + + # Check that the user has the correct subscription + assert_not_nil @vlonchamp.subscription, "user's subscription was not found" + assert_not_nil @vlonchamp.subscription.plan, "user's subscribed plan was not found" + assert_equal plan.id, @vlonchamp.subscription.plan_id, "user's plan does not match" + + # Check that the training credits were set correctly + assert_empty @vlonchamp.training_credits, 'training credits were not reset' + assert_equal @vlonchamp.subscription.plan.training_credit_nb, plan.training_credit_nb, 'trainings credits were not allocated' + + # Check that the user benefit from prices of his plan + printer = Machine.find_by_slug('imprimante-3d') + assert_equal 10, (printer.prices.find_by(group_id: @vlonchamp.group_id, plan_id: @vlonchamp.subscription.plan_id).amount / 100), 'machine hourly price does not match' + + # Check notifications were sent for every admins + notifications = Notification.where(notification_type_id: NotificationType.find_by_name('notify_admin_subscribed_plan'), attached_object_type: 'Subscription', attached_object_id: subscription[:id]) + assert_not_empty notifications, 'no notifications were created' + notified_users_ids = notifications.map {|n| n.receiver_id } + User.admins.each do |adm| + assert_includes notified_users_ids, adm.id, "Admin #{adm.id} was not notified" + end + + # Check generated invoice + invoice = Invoice.find_by(invoiced_type: 'Subscription', invoiced_id: subscription[:id]) + assert_invoice_pdf invoice + assert_equal plan.amount, invoice.total, 'Invoice total price does not match the bought subscription' + + # wallet + assert_equal @vlonchamp.wallet.amount, 0 + assert_equal @vlonchamp.wallet.wallet_transactions.count, 2 + transaction = @vlonchamp.wallet.wallet_transactions.last + assert_equal transaction.transaction_type, 'debit' + assert_equal transaction.amount, 10 + end +end diff --git a/test/vcr_cassettes/reservations_create_for_machine_and_pay_wallet_success.yml b/test/vcr_cassettes/reservations_create_for_machine_and_pay_wallet_success.yml new file mode 100644 index 000000000..ea837fd19 --- /dev/null +++ b/test/vcr_cassettes/reservations_create_for_machine_and_pay_wallet_success.yml @@ -0,0 +1,1063 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/tokens + body: + encoding: UTF-8 + string: card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2017&card[cvc]=314 + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '81' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:02:58 GMT + Content-Type: + - application/json + Content-Length: + - '778' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigzZOUKbFLP8 + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "tok_17xHFG2sOmf47Nz9pZ4CafpU", + "object": "token", + "card": { + "id": "card_17xHFG2sOmf47Nz95yErDQbL", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "cvc_check": "unchecked", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + }, + "client_ip": "86.76.5.109", + "created": 1459951378, + "livemode": false, + "type": "card", + "used": false + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:02:58 GMT +- request: + method: post + uri: https://api.stripe.com/v1/invoiceitems + body: + encoding: UTF-8 + string: customer=cus_8CzNtM08NVlSGN&amount=3200¤cy=usd&description=FORM1%2B+imprimante+3D+April+11%2C+2016+14%3A00+-+03%3A00+PM + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '125' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:02:58 GMT + Content-Type: + - application/json + Content-Length: + - '469' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigTaKJ04PVMc + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "ii_17xHFG2sOmf47Nz9hhIaJZtF", + "object": "invoiceitem", + "amount": 3200, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951378, + "description": "FORM1+ imprimante 3D April 11, 2016 14:00 - 03:00 PM", + "discountable": true, + "invoice": null, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:02:58 GMT +- request: + method: post + uri: https://api.stripe.com/v1/invoiceitems + body: + encoding: UTF-8 + string: customer=cus_8CzNtM08NVlSGN&amount=-1000¤cy=usd&description=Wallet+-1000 + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '125' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:02:58 GMT + Content-Type: + - application/json + Content-Length: + - '469' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigTaKJ04PVMc + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "ii_17xHFG2sOmf47N59hh8aJSt6", + "object": "invoiceitem", + "amount": -1000, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951378, + "description": "Wallet -1000", + "discountable": true, + "invoice": null, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:02:58 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:02:59 GMT + Content-Type: + - application/json + Content-Length: + - '3462' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8Digc2V3aKSGrn + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "cus_8CzNtM08NVlSGN", + "object": "customer", + "account_balance": 0, + "created": 1459948888, + "currency": "usd", + "default_source": "card_17xGjJ2sOmf47Nz9UrQOP8Cl", + "delinquent": false, + "description": "Jean Dupond", + "discount": null, + "email": "jean.dupond@gmail.com", + "livemode": false, + "metadata": {}, + "shipping": null, + "sources": { + "object": "list", + "data": [ + { + "id": "card_17xGjJ2sOmf47Nz9UrQOP8Cl", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_8CzNtM08NVlSGN", + "cvc_check": "pass", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/sources" + }, + "subscriptions": { + "object": "list", + "data": [ + { + "id": "sub_8Di9gqPLwt5IIC", + "object": "subscription", + "application_fee_percent": null, + "cancel_at_period_end": true, + "canceled_at": 1459949404, + "current_period_end": 1462541399, + "current_period_start": 1459949399, + "customer": "cus_8CzNtM08NVlSGN", + "discount": null, + "ended_at": null, + "metadata": {}, + "plan": { + "id": "mensuel-standard-month-20160404171519", + "object": "plan", + "amount": 3000, + "created": 1459782921, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": {}, + "name": "Mensuel - standard, association - month", + "statement_descriptor": null, + "trial_period_days": null + }, + "quantity": 1, + "start": 1459949399, + "status": "active", + "tax_percent": null, + "trial_end": null, + "trial_start": null + }, + { + "id": "sub_8Di2VadRvr7A99", + "object": "subscription", + "application_fee_percent": null, + "cancel_at_period_end": true, + "canceled_at": 1459948972, + "current_period_end": 1462540968, + "current_period_start": 1459948968, + "customer": "cus_8CzNtM08NVlSGN", + "discount": null, + "ended_at": null, + "metadata": {}, + "plan": { + "id": "mensuel-standard-month-20160404171519", + "object": "plan", + "amount": 3000, + "created": 1459782921, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": {}, + "name": "Mensuel - standard, association - month", + "statement_descriptor": null, + "trial_period_days": null + }, + "quantity": 1, + "start": 1459948968, + "status": "active", + "tax_percent": null, + "trial_end": null, + "trial_start": null + } + ], + "has_more": false, + "total_count": 2, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/subscriptions" + } + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:02:59 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN/sources + body: + encoding: UTF-8 + string: card=tok_17xHFG2sOmf47Nz9pZ4CafpU + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '33' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:03:00 GMT + Content-Type: + - application/json + Content-Length: + - '577' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigAxuQClwx3A + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "card_17xHFG2sOmf47Nz95yErDQbL", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_8CzNtM08NVlSGN", + "cvc_check": "pass", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:03:00 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN + body: + encoding: UTF-8 + string: default_source=card_17xHFG2sOmf47Nz95yErDQbL + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '44' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:03:01 GMT + Content-Type: + - application/json + Content-Length: + - '4190' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8Dig1Js3cBEeqQ + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "cus_8CzNtM08NVlSGN", + "object": "customer", + "account_balance": 0, + "created": 1459948888, + "currency": "usd", + "default_source": "card_17xHFG2sOmf47Nz95yErDQbL", + "delinquent": false, + "description": "Jean Dupond", + "discount": null, + "email": "jean.dupond@gmail.com", + "livemode": false, + "metadata": {}, + "shipping": null, + "sources": { + "object": "list", + "data": [ + { + "id": "card_17xHFG2sOmf47Nz95yErDQbL", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_8CzNtM08NVlSGN", + "cvc_check": "pass", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + }, + { + "id": "card_17xGjJ2sOmf47Nz9UrQOP8Cl", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_8CzNtM08NVlSGN", + "cvc_check": "pass", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + } + ], + "has_more": false, + "total_count": 2, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/sources" + }, + "subscriptions": { + "object": "list", + "data": [ + { + "id": "sub_8Di9gqPLwt5IIC", + "object": "subscription", + "application_fee_percent": null, + "cancel_at_period_end": true, + "canceled_at": 1459949404, + "current_period_end": 1462541399, + "current_period_start": 1459949399, + "customer": "cus_8CzNtM08NVlSGN", + "discount": null, + "ended_at": null, + "metadata": {}, + "plan": { + "id": "mensuel-standard-month-20160404171519", + "object": "plan", + "amount": 3000, + "created": 1459782921, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": {}, + "name": "Mensuel - standard, association - month", + "statement_descriptor": null, + "trial_period_days": null + }, + "quantity": 1, + "start": 1459949399, + "status": "active", + "tax_percent": null, + "trial_end": null, + "trial_start": null + }, + { + "id": "sub_8Di2VadRvr7A99", + "object": "subscription", + "application_fee_percent": null, + "cancel_at_period_end": true, + "canceled_at": 1459948972, + "current_period_end": 1462540968, + "current_period_start": 1459948968, + "customer": "cus_8CzNtM08NVlSGN", + "discount": null, + "ended_at": null, + "metadata": {}, + "plan": { + "id": "mensuel-standard-month-20160404171519", + "object": "plan", + "amount": 3000, + "created": 1459782921, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": {}, + "name": "Mensuel - standard, association - month", + "statement_descriptor": null, + "trial_period_days": null + }, + "quantity": 1, + "start": 1459948968, + "status": "active", + "tax_percent": null, + "trial_end": null, + "trial_start": null + } + ], + "has_more": false, + "total_count": 2, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/subscriptions" + } + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:03:01 GMT +- request: + method: post + uri: https://api.stripe.com/v1/invoices + body: + encoding: UTF-8 + string: customer=cus_8CzNtM08NVlSGN + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '27' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:03:02 GMT + Content-Type: + - application/json + Content-Length: + - '1426' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigRXqOIStdA0 + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "in_17xHFK2sOmf47Nz9jegPFlNt", + "object": "invoice", + "amount_due": 2200, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "charge": null, + "closed": false, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951382, + "description": null, + "discount": null, + "ending_balance": null, + "forgiven": false, + "lines": { + "object": "list", + "data": [ + { + "id": "ii_17xHFG2sOmf47Nz9hhIaJZtF", + "object": "line_item", + "amount": 3200, + "currency": "usd", + "description": "FORM1+ imprimante 3D April 11, 2016 14:00 - 03:00 PM", + "discountable": true, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null, + "type": "invoiceitem" + }, + { + "id": "ii_17xHFG2sOmf47N59hh8aJSt6", + "object": "invoiceitem", + "amount": -1000, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951378, + "description": "Wallet -1000", + "discountable": true, + "invoice": null, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_17xHFK2sOmf47Nz9jegPFlNt/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": 1459954982, + "paid": false, + "period_end": 1459951382, + "period_start": 1459948968, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "subscription": null, + "subtotal": 2200, + "tax": null, + "tax_percent": null, + "total": 2200, + "webhooks_delivered_at": null + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:03:02 GMT +- request: + method: post + uri: https://api.stripe.com/v1/invoices/in_17xHFK2sOmf47Nz9jegPFlNt/pay + body: + encoding: ASCII-8BIT + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + Content-Length: + - '0' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:03:03 GMT + Content-Type: + - application/json + Content-Length: + - '1445' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8DigckzVuj8MLI + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "in_17xHFK2sOmf47Nz9jegPFlNt", + "object": "invoice", + "amount_due": 2200, + "application_fee": null, + "attempt_count": 1, + "attempted": true, + "charge": "ch_17xHFL2sOmf47Nz9FCQ0BJKc", + "closed": true, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951382, + "description": null, + "discount": null, + "ending_balance": 0, + "forgiven": false, + "lines": { + "object": "list", + "data": [ + { + "id": "ii_17xHFG2sOmf47Nz9hhIaJZtF", + "object": "line_item", + "amount": 3200, + "currency": "usd", + "description": "FORM1+ imprimante 3D April 11, 2016 14:00 - 03:00 PM", + "discountable": true, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null, + "type": "invoiceitem" + }, + { + "id": "ii_17xHFG2sOmf47N59hh8aJSt6", + "object": "invoiceitem", + "amount": -1000, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1459951378, + "description": "Wallet -1000", + "discountable": true, + "invoice": null, + "livemode": false, + "metadata": {}, + "period": { + "start": 1459951378, + "end": 1459951378 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_17xHFK2sOmf47Nz9jegPFlNt/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "paid": true, + "period_end": 1459951382, + "period_start": 1459948968, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "subscription": null, + "subtotal": 2200, + "tax": null, + "tax_percent": null, + "total": 2200, + "webhooks_delivered_at": 1459951382 + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:03:03 GMT +- request: + method: delete + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN/sources/card_17xHFG2sOmf47Nz95yErDQbL + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin + MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 + PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Wed, 06 Apr 2016 14:03:04 GMT + Content-Type: + - application/json + Content-Length: + - '63' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8Dig3VHawFrxab + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "deleted": true, + "id": "card_17xHFG2sOmf47Nz95yErDQbL" + } + http_version: + recorded_at: Wed, 06 Apr 2016 14:03:04 GMT +recorded_with: VCR 3.0.1 diff --git a/test/vcr_cassettes/reservations_create_for_machine_without_subscription_error.yml b/test/vcr_cassettes/reservations_create_for_machine_without_subscription_error.yml index 51b5dc8b1..65d6aa1af 100644 --- a/test/vcr_cassettes/reservations_create_for_machine_without_subscription_error.yml +++ b/test/vcr_cassettes/reservations_create_for_machine_without_subscription_error.yml @@ -18,9 +18,9 @@ http_interactions: Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-User-Agent: - - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin - MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 - PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' Content-Length: - '81' response: @@ -31,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Wed, 06 Apr 2016 14:31:05 GMT + - Mon, 11 Jul 2016 13:26:13 GMT Content-Type: - application/json Content-Length: - - '778' + - '780' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -49,7 +49,7 @@ http_interactions: Cache-Control: - no-cache, no-store Request-Id: - - req_8Dj8hOci59D4xC + - req_8nflaXEiTf6O0X Stripe-Version: - '2015-10-16' Strict-Transport-Security: @@ -58,10 +58,10 @@ http_interactions: encoding: UTF-8 string: | { - "id": "tok_17xHgT2sOmf47Nz962TaLtD7", + "id": "tok_18W4QL2sOmf47Nz9GM695H9O", "object": "token", "card": { - "id": "card_17xHgT2sOmf47Nz98eSsgCfX", + "id": "card_18W4QL2sOmf47Nz9NuqbiJMr", "object": "card", "address_city": null, "address_country": null, @@ -84,20 +84,20 @@ http_interactions: "name": null, "tokenization_method": null }, - "client_ip": "86.76.5.109", - "created": 1459953065, + "client_ip": "82.122.118.54", + "created": 1468243573, "livemode": false, "type": "card", "used": false } http_version: - recorded_at: Wed, 06 Apr 2016 14:31:05 GMT + recorded_at: Mon, 11 Jul 2016 13:26:13 GMT - request: method: post uri: https://api.stripe.com/v1/invoiceitems body: encoding: UTF-8 - string: customer=cus_8Di1wjdVktv5kt&amount=3200¤cy=usd&description=FORM1%2B+imprimante+3D+April+11%2C+2016+14%3A00+-+03%3A00+PM + string: customer=cus_8Di1wjdVktv5kt&amount=3200¤cy=usd&description=FORM1%2B+imprimante+3D+July+10%2C+2016+14%3A00+-+03%3A00+PM headers: Accept: - "*/*; q=0.5, application/xml" @@ -110,11 +110,11 @@ http_interactions: Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-User-Agent: - - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin - MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 - PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' Content-Length: - - '125' + - '124' response: status: code: 200 @@ -123,11 +123,11 @@ http_interactions: Server: - nginx Date: - - Wed, 06 Apr 2016 14:31:06 GMT + - Mon, 11 Jul 2016 13:26:14 GMT Content-Type: - application/json Content-Length: - - '469' + - '468' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -141,7 +141,7 @@ http_interactions: Cache-Control: - no-cache, no-store Request-Id: - - req_8Dj87wp0RJ3Z71 + - req_8nflWbFKH0Wr7j Stripe-Version: - '2015-10-16' Strict-Transport-Security: @@ -150,20 +150,20 @@ http_interactions: encoding: UTF-8 string: | { - "id": "ii_17xHgU2sOmf47Nz9Pp3l4ZAA", + "id": "ii_18W4QM2sOmf47Nz9g7EdrbZV", "object": "invoiceitem", "amount": 3200, "currency": "usd", "customer": "cus_8Di1wjdVktv5kt", - "date": 1459953066, - "description": "FORM1+ imprimante 3D April 11, 2016 14:00 - 03:00 PM", + "date": 1468243574, + "description": "FORM1+ imprimante 3D July 10, 2016 14:00 - 03:00 PM", "discountable": true, "invoice": null, "livemode": false, "metadata": {}, "period": { - "start": 1459953066, - "end": 1459953066 + "start": 1468243574, + "end": 1468243574 }, "plan": null, "proration": false, @@ -171,7 +171,7 @@ http_interactions: "subscription": null } http_version: - recorded_at: Wed, 06 Apr 2016 14:31:06 GMT + recorded_at: Mon, 11 Jul 2016 13:26:14 GMT - request: method: get uri: https://api.stripe.com/v1/customers/cus_8Di1wjdVktv5kt @@ -190,9 +190,9 @@ http_interactions: Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-User-Agent: - - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin - MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 - PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' response: status: code: 200 @@ -201,11 +201,11 @@ http_interactions: Server: - nginx Date: - - Wed, 06 Apr 2016 14:31:07 GMT + - Mon, 11 Jul 2016 13:26:15 GMT Content-Type: - application/json Content-Length: - - '3462' + - '1408' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -219,7 +219,7 @@ http_interactions: Cache-Control: - no-cache, no-store Request-Id: - - req_8Dj8aOsnrVp2zU + - req_8nflczxuJx3Fit Stripe-Version: - '2015-10-16' Strict-Transport-Security: @@ -233,7 +233,7 @@ http_interactions: "account_balance": 0, "created": 1459948888, "currency": "usd", - "default_source": "card_17xGjJ2sOmf47Nz9UrQOP8Cl", + "default_source": "card_17z7CT2sOmf47Nz9wtWkhGor", "delinquent": false, "description": "Jean Dupond", "discount": null, @@ -245,7 +245,7 @@ http_interactions: "object": "list", "data": [ { - "id": "card_17xGjJ2sOmf47Nz9UrQOP8Cl", + "id": "card_17z7CT2sOmf47Nz9wtWkhGor", "object": "card", "address_city": null, "address_country": null, @@ -276,87 +276,20 @@ http_interactions: }, "subscriptions": { "object": "list", - "data": [ - { - "id": "sub_8Di9gqPLwt5IIC", - "object": "subscription", - "application_fee_percent": null, - "cancel_at_period_end": true, - "canceled_at": 1459949404, - "current_period_end": 1462541399, - "current_period_start": 1459949399, - "customer": "cus_8Di1wjdVktv5kt", - "discount": null, - "ended_at": null, - "metadata": {}, - "plan": { - "id": "mensuel-standard-month-20160404171519", - "object": "plan", - "amount": 3000, - "created": 1459782921, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "name": "Mensuel - standard, association - month", - "statement_descriptor": null, - "trial_period_days": null - }, - "quantity": 1, - "start": 1459949399, - "status": "active", - "tax_percent": null, - "trial_end": null, - "trial_start": null - }, - { - "id": "sub_8Di2VadRvr7A99", - "object": "subscription", - "application_fee_percent": null, - "cancel_at_period_end": true, - "canceled_at": 1459948972, - "current_period_end": 1462540968, - "current_period_start": 1459948968, - "customer": "cus_8Di1wjdVktv5kt", - "discount": null, - "ended_at": null, - "metadata": {}, - "plan": { - "id": "mensuel-standard-month-20160404171519", - "object": "plan", - "amount": 3000, - "created": 1459782921, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "name": "Mensuel - standard, association - month", - "statement_descriptor": null, - "trial_period_days": null - }, - "quantity": 1, - "start": 1459948968, - "status": "active", - "tax_percent": null, - "trial_end": null, - "trial_start": null - } - ], + "data": [], "has_more": false, - "total_count": 2, + "total_count": 0, "url": "/v1/customers/cus_8Di1wjdVktv5kt/subscriptions" } } http_version: - recorded_at: Wed, 06 Apr 2016 14:31:07 GMT + recorded_at: Mon, 11 Jul 2016 13:26:15 GMT - request: method: post uri: https://api.stripe.com/v1/customers/cus_8Di1wjdVktv5kt/sources body: encoding: UTF-8 - string: card=tok_17xHgT2sOmf47Nz962TaLtD7 + string: card=tok_18W4QL2sOmf47Nz9GM695H9O headers: Accept: - "*/*; q=0.5, application/xml" @@ -369,9 +302,9 @@ http_interactions: Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-User-Agent: - - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin - MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 - PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' Content-Length: - '33' response: @@ -382,7 +315,7 @@ http_interactions: Server: - nginx Date: - - Wed, 06 Apr 2016 14:31:09 GMT + - Mon, 11 Jul 2016 13:26:16 GMT Content-Type: - application/json Content-Length: @@ -400,7 +333,7 @@ http_interactions: Cache-Control: - no-cache, no-store Request-Id: - - req_8Dj8Q0yiZoox2F + - req_8nflIAexuhWlA2 Stripe-Version: - '2015-10-16' body: @@ -415,10 +348,10 @@ http_interactions: } } http_version: - recorded_at: Wed, 06 Apr 2016 14:31:09 GMT + recorded_at: Mon, 11 Jul 2016 13:26:16 GMT - request: method: delete - uri: https://api.stripe.com/v1/invoiceitems/ii_17xHgU2sOmf47Nz9Pp3l4ZAA + uri: https://api.stripe.com/v1/invoiceitems/ii_18W4QM2sOmf47Nz9g7EdrbZV body: encoding: US-ASCII string: '' @@ -434,9 +367,9 @@ http_interactions: Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-User-Agent: - - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin14","engine":"ruby","publisher":"stripe","uname":"Darwin - MBP-sleede-Nicolas.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 - PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64","hostname":"MBP-sleede-Nicolas.local"}' + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' response: status: code: 200 @@ -445,7 +378,7 @@ http_interactions: Server: - nginx Date: - - Wed, 06 Apr 2016 14:31:09 GMT + - Mon, 11 Jul 2016 13:26:17 GMT Content-Type: - application/json Content-Length: @@ -463,7 +396,7 @@ http_interactions: Cache-Control: - no-cache, no-store Request-Id: - - req_8Dj86hW0UVRjBL + - req_8nflVBopesOpHn Stripe-Version: - '2015-10-16' Strict-Transport-Security: @@ -473,8 +406,8 @@ http_interactions: string: | { "deleted": true, - "id": "ii_17xHgU2sOmf47Nz9Pp3l4ZAA" + "id": "ii_18W4QM2sOmf47Nz9g7EdrbZV" } http_version: - recorded_at: Wed, 06 Apr 2016 14:31:09 GMT + recorded_at: Mon, 11 Jul 2016 13:26:17 GMT recorded_with: VCR 3.0.1 diff --git a/test/vcr_cassettes/subscriptions_user_create_success_with_wallet.yml b/test/vcr_cassettes/subscriptions_user_create_success_with_wallet.yml new file mode 100644 index 000000000..8407e0a69 --- /dev/null +++ b/test/vcr_cassettes/subscriptions_user_create_success_with_wallet.yml @@ -0,0 +1,821 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/tokens + body: + encoding: UTF-8 + string: card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2017&card[cvc]=314 + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + Content-Length: + - '81' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:52 GMT + Content-Type: + - application/json + Content-Length: + - '780' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbr26ZMi1nMx + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "tok_18W4GK2sOmf47Nz9I7fm3Y18", + "object": "token", + "card": { + "id": "card_18W4GK2sOmf47Nz9K6dfSmZl", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "cvc_check": "unchecked", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + }, + "client_ip": "82.122.118.54", + "created": 1468242952, + "livemode": false, + "type": "card", + "used": false + } + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:52 GMT +- request: + method: get + uri: https://api.stripe.com/v1/tokens/tok_18W4GK2sOmf47Nz9I7fm3Y18 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:53 GMT + Content-Type: + - application/json + Content-Length: + - '780' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbbQfs4YR066 + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "tok_18W4GK2sOmf47Nz9I7fm3Y18", + "object": "token", + "card": { + "id": "card_18W4GK2sOmf47Nz9K6dfSmZl", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "cvc_check": "unchecked", + "dynamic_last4": null, + "exp_month": 4, + "exp_year": 2017, + "fingerprint": "o52jybR7bnmNn6AT", + "funding": "credit", + "last4": "4242", + "metadata": {}, + "name": null, + "tokenization_method": null + }, + "client_ip": "82.122.118.54", + "created": 1468242952, + "livemode": false, + "type": "card", + "used": false + } + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:53 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:54 GMT + Content-Type: + - application/json + Content-Length: + - '655' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfb0Ez5UgkL6i + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "cus_8CzNtM08NVlSGN", + "object": "customer", + "account_balance": 0, + "created": 1459782849, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "Vanessa Lonchamp", + "discount": null, + "email": "vanessa.lonchamp@sfr.fr", + "livemode": false, + "metadata": {}, + "shipping": null, + "sources": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/sources" + }, + "subscriptions": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_8CzNtM08NVlSGN/subscriptions" + } + } + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:54 GMT +- request: + method: post + uri: https://api.stripe.com/v1/invoiceitems + body: + encoding: UTF-8 + string: customer=cus_8CzNtM08NVlSGN&amount=-1000¤cy=usd&description=wallet+-10.0 + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + Content-Length: + - '78' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:55 GMT + Content-Type: + - application/json + Content-Length: + - '431' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbS1DU8keH5x + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: UTF-8 + string: | + { + "id": "ii_18W4GN2sOmf47Nz9K1dTfTyA", + "object": "invoiceitem", + "amount": -1000, + "currency": "usd", + "customer": "cus_8CzNtM08NVlSGN", + "date": 1468242955, + "description": "wallet -10.0", + "discountable": false, + "invoice": null, + "livemode": false, + "metadata": {}, + "period": { + "start": 1468242955, + "end": 1468242955 + }, + "plan": null, + "proration": false, + "quantity": null, + "subscription": null + } + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:55 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN/subscriptions + body: + encoding: UTF-8 + string: plan=mensuel-tarif-reduit-student-month-20160404171827&source=tok_18W4GK2sOmf47Nz9I7fm3Y18 + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + Content-Length: + - '90' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:57 GMT + Content-Type: + - application/json + Content-Length: + - '926' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbYSDVSvjiep + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJpZCI6ICJzdWJfOG5mYlRHR0lRUlF6eDEiLAogICJvYmplY3QiOiAi + c3Vic2NyaXB0aW9uIiwKICAiYXBwbGljYXRpb25fZmVlX3BlcmNlbnQiOiBu + dWxsLAogICJjYW5jZWxfYXRfcGVyaW9kX2VuZCI6IGZhbHNlLAogICJjYW5j + ZWxlZF9hdCI6IG51bGwsCiAgImNyZWF0ZWQiOiAxNDY4MjQyOTU2LAogICJj + dXJyZW50X3BlcmlvZF9lbmQiOiAxNDcwOTIxMzU2LAogICJjdXJyZW50X3Bl + cmlvZF9zdGFydCI6IDE0NjgyNDI5NTYsCiAgImN1c3RvbWVyIjogImN1c184 + Q3pOdE0wOE5WbFNHTiIsCiAgImRpc2NvdW50IjogbnVsbCwKICAiZW5kZWRf + YXQiOiBudWxsLAogICJsaXZlbW9kZSI6IGZhbHNlLAogICJtZXRhZGF0YSI6 + IHt9LAogICJwbGFuIjogewogICAgImlkIjogIm1lbnN1ZWwtdGFyaWYtcmVk + dWl0LXN0dWRlbnQtbW9udGgtMjAxNjA0MDQxNzE4MjciLAogICAgIm9iamVj + dCI6ICJwbGFuIiwKICAgICJhbW91bnQiOiAyMDAwLAogICAgImNyZWF0ZWQi + OiAxNDU5NzgzMTA4LAogICAgImN1cnJlbmN5IjogInVzZCIsCiAgICAiaW50 + ZXJ2YWwiOiAibW9udGgiLAogICAgImludGVydmFsX2NvdW50IjogMSwKICAg + ICJsaXZlbW9kZSI6IGZhbHNlLAogICAgIm1ldGFkYXRhIjoge30sCiAgICAi + bmFtZSI6ICJNZW5zdWVsIHRhcmlmIHLDqWR1aXQgLSDDqXR1ZGlhbnQsIC0g + ZGUgMjUgYW5zLCBlbnNlaWduYW50LCBkZW1hbmRldXIgZCdlbXBsb2kgLSBt + b250aCIsCiAgICAic3RhdGVtZW50X2Rlc2NyaXB0b3IiOiBudWxsLAogICAg + InRyaWFsX3BlcmlvZF9kYXlzIjogbnVsbAogIH0sCiAgInF1YW50aXR5Ijog + MSwKICAic3RhcnQiOiAxNDY4MjQyOTU2LAogICJzdGF0dXMiOiAiYWN0aXZl + IiwKICAidGF4X3BlcmNlbnQiOiBudWxsLAogICJ0cmlhbF9lbmQiOiBudWxs + LAogICJ0cmlhbF9zdGFydCI6IG51bGwKfQo= + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:57 GMT +- request: + method: get + uri: https://api.stripe.com/v1/invoices?customer=cus_8CzNtM08NVlSGN&limit=1 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:58 GMT + Content-Type: + - application/json + Content-Length: + - '2835' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbi8eORMWkgD + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJvYmplY3QiOiAibGlzdCIsCiAgImRhdGEiOiBbCiAgICB7CiAgICAg + ICJpZCI6ICJpbl8xOFc0R08yc09tZjQ3Tno5b291RFEzaGUiLAogICAgICAi + b2JqZWN0IjogImludm9pY2UiLAogICAgICAiYW1vdW50X2R1ZSI6IDEwMDAs + CiAgICAgICJhcHBsaWNhdGlvbl9mZWUiOiBudWxsLAogICAgICAiYXR0ZW1w + dF9jb3VudCI6IDEsCiAgICAgICJhdHRlbXB0ZWQiOiB0cnVlLAogICAgICAi + Y2hhcmdlIjogImNoXzE4VzRHTzJzT21mNDdOejlnb1k2T3lEbCIsCiAgICAg + ICJjbG9zZWQiOiB0cnVlLAogICAgICAiY3VycmVuY3kiOiAidXNkIiwKICAg + ICAgImN1c3RvbWVyIjogImN1c184Q3pOdE0wOE5WbFNHTiIsCiAgICAgICJk + YXRlIjogMTQ2ODI0Mjk1NiwKICAgICAgImRlc2NyaXB0aW9uIjogbnVsbCwK + ICAgICAgImRpc2NvdW50IjogbnVsbCwKICAgICAgImVuZGluZ19iYWxhbmNl + IjogMCwKICAgICAgImZvcmdpdmVuIjogZmFsc2UsCiAgICAgICJsaW5lcyI6 + IHsKICAgICAgICAib2JqZWN0IjogImxpc3QiLAogICAgICAgICJkYXRhIjog + WwogICAgICAgICAgewogICAgICAgICAgICAiaWQiOiAiaWlfMThXNEdOMnNP + bWY0N056OUsxZFRmVHlBIiwKICAgICAgICAgICAgIm9iamVjdCI6ICJsaW5l + X2l0ZW0iLAogICAgICAgICAgICAiYW1vdW50IjogLTEwMDAsCiAgICAgICAg + ICAgICJjdXJyZW5jeSI6ICJ1c2QiLAogICAgICAgICAgICAiZGVzY3JpcHRp + b24iOiAid2FsbGV0IC0xMC4wIiwKICAgICAgICAgICAgImRpc2NvdW50YWJs + ZSI6IGZhbHNlLAogICAgICAgICAgICAibGl2ZW1vZGUiOiBmYWxzZSwKICAg + ICAgICAgICAgIm1ldGFkYXRhIjoge30sCiAgICAgICAgICAgICJwZXJpb2Qi + OiB7CiAgICAgICAgICAgICAgInN0YXJ0IjogMTQ2ODI0Mjk1NSwKICAgICAg + ICAgICAgICAiZW5kIjogMTQ2ODI0Mjk1NQogICAgICAgICAgICB9LAogICAg + ICAgICAgICAicGxhbiI6IG51bGwsCiAgICAgICAgICAgICJwcm9yYXRpb24i + OiBmYWxzZSwKICAgICAgICAgICAgInF1YW50aXR5IjogbnVsbCwKICAgICAg + ICAgICAgInN1YnNjcmlwdGlvbiI6IG51bGwsCiAgICAgICAgICAgICJ0eXBl + IjogImludm9pY2VpdGVtIgogICAgICAgICAgfSwKICAgICAgICAgIHsKICAg + ICAgICAgICAgImlkIjogInN1Yl84bmZiVEdHSVFSUXp4MSIsCiAgICAgICAg + ICAgICJvYmplY3QiOiAibGluZV9pdGVtIiwKICAgICAgICAgICAgImFtb3Vu + dCI6IDIwMDAsCiAgICAgICAgICAgICJjdXJyZW5jeSI6ICJ1c2QiLAogICAg + ICAgICAgICAiZGVzY3JpcHRpb24iOiBudWxsLAogICAgICAgICAgICAiZGlz + Y291bnRhYmxlIjogdHJ1ZSwKICAgICAgICAgICAgImxpdmVtb2RlIjogZmFs + c2UsCiAgICAgICAgICAgICJtZXRhZGF0YSI6IHt9LAogICAgICAgICAgICAi + cGVyaW9kIjogewogICAgICAgICAgICAgICJzdGFydCI6IDE0NjgyNDI5NTYs + CiAgICAgICAgICAgICAgImVuZCI6IDE0NzA5MjEzNTYKICAgICAgICAgICAg + fSwKICAgICAgICAgICAgInBsYW4iOiB7CiAgICAgICAgICAgICAgImlkIjog + Im1lbnN1ZWwtdGFyaWYtcmVkdWl0LXN0dWRlbnQtbW9udGgtMjAxNjA0MDQx + NzE4MjciLAogICAgICAgICAgICAgICJvYmplY3QiOiAicGxhbiIsCiAgICAg + ICAgICAgICAgImFtb3VudCI6IDIwMDAsCiAgICAgICAgICAgICAgImNyZWF0 + ZWQiOiAxNDU5NzgzMTA4LAogICAgICAgICAgICAgICJjdXJyZW5jeSI6ICJ1 + c2QiLAogICAgICAgICAgICAgICJpbnRlcnZhbCI6ICJtb250aCIsCiAgICAg + ICAgICAgICAgImludGVydmFsX2NvdW50IjogMSwKICAgICAgICAgICAgICAi + bGl2ZW1vZGUiOiBmYWxzZSwKICAgICAgICAgICAgICAibWV0YWRhdGEiOiB7 + fSwKICAgICAgICAgICAgICAibmFtZSI6ICJNZW5zdWVsIHRhcmlmIHLDqWR1 + aXQgLSDDqXR1ZGlhbnQsIC0gZGUgMjUgYW5zLCBlbnNlaWduYW50LCBkZW1h + bmRldXIgZCdlbXBsb2kgLSBtb250aCIsCiAgICAgICAgICAgICAgInN0YXRl + bWVudF9kZXNjcmlwdG9yIjogbnVsbCwKICAgICAgICAgICAgICAidHJpYWxf + cGVyaW9kX2RheXMiOiBudWxsCiAgICAgICAgICAgIH0sCiAgICAgICAgICAg + ICJwcm9yYXRpb24iOiBmYWxzZSwKICAgICAgICAgICAgInF1YW50aXR5Ijog + MSwKICAgICAgICAgICAgInN1YnNjcmlwdGlvbiI6IG51bGwsCiAgICAgICAg + ICAgICJ0eXBlIjogInN1YnNjcmlwdGlvbiIKICAgICAgICAgIH0KICAgICAg + ICBdLAogICAgICAgICJoYXNfbW9yZSI6IGZhbHNlLAogICAgICAgICJ0b3Rh + bF9jb3VudCI6IDIsCiAgICAgICAgInVybCI6ICIvdjEvaW52b2ljZXMvaW5f + MThXNEdPMnNPbWY0N056OW9vdURRM2hlL2xpbmVzIgogICAgICB9LAogICAg + ICAibGl2ZW1vZGUiOiBmYWxzZSwKICAgICAgIm1ldGFkYXRhIjoge30sCiAg + ICAgICJuZXh0X3BheW1lbnRfYXR0ZW1wdCI6IG51bGwsCiAgICAgICJwYWlk + IjogdHJ1ZSwKICAgICAgInBlcmlvZF9lbmQiOiAxNDY4MjQyOTU2LAogICAg + ICAicGVyaW9kX3N0YXJ0IjogMTQ2ODI0Mjk1NiwKICAgICAgInJlY2VpcHRf + bnVtYmVyIjogbnVsbCwKICAgICAgInN0YXJ0aW5nX2JhbGFuY2UiOiAwLAog + ICAgICAic3RhdGVtZW50X2Rlc2NyaXB0b3IiOiBudWxsLAogICAgICAic3Vi + c2NyaXB0aW9uIjogInN1Yl84bmZiVEdHSVFSUXp4MSIsCiAgICAgICJzdWJ0 + b3RhbCI6IDEwMDAsCiAgICAgICJ0YXgiOiBudWxsLAogICAgICAidGF4X3Bl + cmNlbnQiOiBudWxsLAogICAgICAidG90YWwiOiAxMDAwLAogICAgICAid2Vi + aG9va3NfZGVsaXZlcmVkX2F0IjogMTQ2ODI0Mjk1NgogICAgfQogIF0sCiAg + Imhhc19tb3JlIjogZmFsc2UsCiAgInVybCI6ICIvdjEvaW52b2ljZXMiCn0K + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:58 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:59 GMT + Content-Type: + - application/json + Content-Length: + - '2556' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbxXXi48kJ9H + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJpZCI6ICJjdXNfOEN6TnRNMDhOVmxTR04iLAogICJvYmplY3QiOiAi + Y3VzdG9tZXIiLAogICJhY2NvdW50X2JhbGFuY2UiOiAwLAogICJjcmVhdGVk + IjogMTQ1OTc4Mjg0OSwKICAiY3VycmVuY3kiOiAidXNkIiwKICAiZGVmYXVs + dF9zb3VyY2UiOiAiY2FyZF8xOFc0R0syc09tZjQ3Tno5SzZkZlNtWmwiLAog + ICJkZWxpbnF1ZW50IjogZmFsc2UsCiAgImRlc2NyaXB0aW9uIjogIlZhbmVz + c2EgTG9uY2hhbXAiLAogICJkaXNjb3VudCI6IG51bGwsCiAgImVtYWlsIjog + InZhbmVzc2EubG9uY2hhbXBAc2ZyLmZyIiwKICAibGl2ZW1vZGUiOiBmYWxz + ZSwKICAibWV0YWRhdGEiOiB7fSwKICAic2hpcHBpbmciOiBudWxsLAogICJz + b3VyY2VzIjogewogICAgIm9iamVjdCI6ICJsaXN0IiwKICAgICJkYXRhIjog + WwogICAgICB7CiAgICAgICAgImlkIjogImNhcmRfMThXNEdLMnNPbWY0N056 + OUs2ZGZTbVpsIiwKICAgICAgICAib2JqZWN0IjogImNhcmQiLAogICAgICAg + ICJhZGRyZXNzX2NpdHkiOiBudWxsLAogICAgICAgICJhZGRyZXNzX2NvdW50 + cnkiOiBudWxsLAogICAgICAgICJhZGRyZXNzX2xpbmUxIjogbnVsbCwKICAg + ICAgICAiYWRkcmVzc19saW5lMV9jaGVjayI6IG51bGwsCiAgICAgICAgImFk + ZHJlc3NfbGluZTIiOiBudWxsLAogICAgICAgICJhZGRyZXNzX3N0YXRlIjog + bnVsbCwKICAgICAgICAiYWRkcmVzc196aXAiOiBudWxsLAogICAgICAgICJh + ZGRyZXNzX3ppcF9jaGVjayI6IG51bGwsCiAgICAgICAgImJyYW5kIjogIlZp + c2EiLAogICAgICAgICJjb3VudHJ5IjogIlVTIiwKICAgICAgICAiY3VzdG9t + ZXIiOiAiY3VzXzhDek50TTA4TlZsU0dOIiwKICAgICAgICAiY3ZjX2NoZWNr + IjogInBhc3MiLAogICAgICAgICJkeW5hbWljX2xhc3Q0IjogbnVsbCwKICAg + ICAgICAiZXhwX21vbnRoIjogNCwKICAgICAgICAiZXhwX3llYXIiOiAyMDE3 + LAogICAgICAgICJmaW5nZXJwcmludCI6ICJvNTJqeWJSN2JubU5uNkFUIiwK + ICAgICAgICAiZnVuZGluZyI6ICJjcmVkaXQiLAogICAgICAgICJsYXN0NCI6 + ICI0MjQyIiwKICAgICAgICAibWV0YWRhdGEiOiB7fSwKICAgICAgICAibmFt + ZSI6IG51bGwsCiAgICAgICAgInRva2VuaXphdGlvbl9tZXRob2QiOiBudWxs + CiAgICAgIH0KICAgIF0sCiAgICAiaGFzX21vcmUiOiBmYWxzZSwKICAgICJ0 + b3RhbF9jb3VudCI6IDEsCiAgICAidXJsIjogIi92MS9jdXN0b21lcnMvY3Vz + XzhDek50TTA4TlZsU0dOL3NvdXJjZXMiCiAgfSwKICAic3Vic2NyaXB0aW9u + cyI6IHsKICAgICJvYmplY3QiOiAibGlzdCIsCiAgICAiZGF0YSI6IFsKICAg + ICAgewogICAgICAgICJpZCI6ICJzdWJfOG5mYlRHR0lRUlF6eDEiLAogICAg + ICAgICJvYmplY3QiOiAic3Vic2NyaXB0aW9uIiwKICAgICAgICAiYXBwbGlj + YXRpb25fZmVlX3BlcmNlbnQiOiBudWxsLAogICAgICAgICJjYW5jZWxfYXRf + cGVyaW9kX2VuZCI6IGZhbHNlLAogICAgICAgICJjYW5jZWxlZF9hdCI6IG51 + bGwsCiAgICAgICAgImNyZWF0ZWQiOiAxNDY4MjQyOTU2LAogICAgICAgICJj + dXJyZW50X3BlcmlvZF9lbmQiOiAxNDcwOTIxMzU2LAogICAgICAgICJjdXJy + ZW50X3BlcmlvZF9zdGFydCI6IDE0NjgyNDI5NTYsCiAgICAgICAgImN1c3Rv + bWVyIjogImN1c184Q3pOdE0wOE5WbFNHTiIsCiAgICAgICAgImRpc2NvdW50 + IjogbnVsbCwKICAgICAgICAiZW5kZWRfYXQiOiBudWxsLAogICAgICAgICJs + aXZlbW9kZSI6IGZhbHNlLAogICAgICAgICJtZXRhZGF0YSI6IHt9LAogICAg + ICAgICJwbGFuIjogewogICAgICAgICAgImlkIjogIm1lbnN1ZWwtdGFyaWYt + cmVkdWl0LXN0dWRlbnQtbW9udGgtMjAxNjA0MDQxNzE4MjciLAogICAgICAg + ICAgIm9iamVjdCI6ICJwbGFuIiwKICAgICAgICAgICJhbW91bnQiOiAyMDAw + LAogICAgICAgICAgImNyZWF0ZWQiOiAxNDU5NzgzMTA4LAogICAgICAgICAg + ImN1cnJlbmN5IjogInVzZCIsCiAgICAgICAgICAiaW50ZXJ2YWwiOiAibW9u + dGgiLAogICAgICAgICAgImludGVydmFsX2NvdW50IjogMSwKICAgICAgICAg + ICJsaXZlbW9kZSI6IGZhbHNlLAogICAgICAgICAgIm1ldGFkYXRhIjoge30s + CiAgICAgICAgICAibmFtZSI6ICJNZW5zdWVsIHRhcmlmIHLDqWR1aXQgLSDD + qXR1ZGlhbnQsIC0gZGUgMjUgYW5zLCBlbnNlaWduYW50LCBkZW1hbmRldXIg + ZCdlbXBsb2kgLSBtb250aCIsCiAgICAgICAgICAic3RhdGVtZW50X2Rlc2Ny + aXB0b3IiOiBudWxsLAogICAgICAgICAgInRyaWFsX3BlcmlvZF9kYXlzIjog + bnVsbAogICAgICAgIH0sCiAgICAgICAgInF1YW50aXR5IjogMSwKICAgICAg + ICAic3RhcnQiOiAxNDY4MjQyOTU2LAogICAgICAgICJzdGF0dXMiOiAiYWN0 + aXZlIiwKICAgICAgICAidGF4X3BlcmNlbnQiOiBudWxsLAogICAgICAgICJ0 + cmlhbF9lbmQiOiBudWxsLAogICAgICAgICJ0cmlhbF9zdGFydCI6IG51bGwK + ICAgICAgfQogICAgXSwKICAgICJoYXNfbW9yZSI6IGZhbHNlLAogICAgInRv + dGFsX2NvdW50IjogMSwKICAgICJ1cmwiOiAiL3YxL2N1c3RvbWVycy9jdXNf + OEN6TnRNMDhOVmxTR04vc3Vic2NyaXB0aW9ucyIKICB9Cn0K + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:58 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN/subscriptions/sub_8nfbTGGIQRQzx1 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:15:59 GMT + Content-Type: + - application/json + Content-Length: + - '926' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbwT9nBtTlrw + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJpZCI6ICJzdWJfOG5mYlRHR0lRUlF6eDEiLAogICJvYmplY3QiOiAi + c3Vic2NyaXB0aW9uIiwKICAiYXBwbGljYXRpb25fZmVlX3BlcmNlbnQiOiBu + dWxsLAogICJjYW5jZWxfYXRfcGVyaW9kX2VuZCI6IGZhbHNlLAogICJjYW5j + ZWxlZF9hdCI6IG51bGwsCiAgImNyZWF0ZWQiOiAxNDY4MjQyOTU2LAogICJj + dXJyZW50X3BlcmlvZF9lbmQiOiAxNDcwOTIxMzU2LAogICJjdXJyZW50X3Bl + cmlvZF9zdGFydCI6IDE0NjgyNDI5NTYsCiAgImN1c3RvbWVyIjogImN1c184 + Q3pOdE0wOE5WbFNHTiIsCiAgImRpc2NvdW50IjogbnVsbCwKICAiZW5kZWRf + YXQiOiBudWxsLAogICJsaXZlbW9kZSI6IGZhbHNlLAogICJtZXRhZGF0YSI6 + IHt9LAogICJwbGFuIjogewogICAgImlkIjogIm1lbnN1ZWwtdGFyaWYtcmVk + dWl0LXN0dWRlbnQtbW9udGgtMjAxNjA0MDQxNzE4MjciLAogICAgIm9iamVj + dCI6ICJwbGFuIiwKICAgICJhbW91bnQiOiAyMDAwLAogICAgImNyZWF0ZWQi + OiAxNDU5NzgzMTA4LAogICAgImN1cnJlbmN5IjogInVzZCIsCiAgICAiaW50 + ZXJ2YWwiOiAibW9udGgiLAogICAgImludGVydmFsX2NvdW50IjogMSwKICAg + ICJsaXZlbW9kZSI6IGZhbHNlLAogICAgIm1ldGFkYXRhIjoge30sCiAgICAi + bmFtZSI6ICJNZW5zdWVsIHRhcmlmIHLDqWR1aXQgLSDDqXR1ZGlhbnQsIC0g + ZGUgMjUgYW5zLCBlbnNlaWduYW50LCBkZW1hbmRldXIgZCdlbXBsb2kgLSBt + b250aCIsCiAgICAic3RhdGVtZW50X2Rlc2NyaXB0b3IiOiBudWxsLAogICAg + InRyaWFsX3BlcmlvZF9kYXlzIjogbnVsbAogIH0sCiAgInF1YW50aXR5Ijog + MSwKICAic3RhcnQiOiAxNDY4MjQyOTU2LAogICJzdGF0dXMiOiAiYWN0aXZl + IiwKICAidGF4X3BlcmNlbnQiOiBudWxsLAogICJ0cmlhbF9lbmQiOiBudWxs + LAogICJ0cmlhbF9zdGFydCI6IG51bGwKfQo= + http_version: + recorded_at: Mon, 11 Jul 2016 13:15:59 GMT +- request: + method: delete + uri: https://api.stripe.com/v1/customers/cus_8CzNtM08NVlSGN/subscriptions/sub_8nfbTGGIQRQzx1?at_period_end=true + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - "*/*; q=0.5, application/xml" + Accept-Encoding: + - gzip, deflate + User-Agent: + - Stripe/v1 RubyBindings/1.30.2 + Authorization: + - Bearer sk_test_testfaketestfaketestfake + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-User-Agent: + - '{"bindings_version":"1.30.2","lang":"ruby","lang_version":"2.3.0 p0 (2015-12-25)","platform":"x86_64-darwin15","engine":"ruby","publisher":"stripe","uname":"Darwin + mbp-sleede-peng.home 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 + PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64","hostname":"mbp-sleede-peng.home"}' + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 11 Jul 2016 13:16:00 GMT + Content-Type: + - application/json + Content-Length: + - '931' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, POST, HEAD, OPTIONS, DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Request-Id: + - req_8nfbwqRs1dLP5C + Stripe-Version: + - '2015-10-16' + Strict-Transport-Security: + - max-age=31556926; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJpZCI6ICJzdWJfOG5mYlRHR0lRUlF6eDEiLAogICJvYmplY3QiOiAi + c3Vic2NyaXB0aW9uIiwKICAiYXBwbGljYXRpb25fZmVlX3BlcmNlbnQiOiBu + dWxsLAogICJjYW5jZWxfYXRfcGVyaW9kX2VuZCI6IHRydWUsCiAgImNhbmNl + bGVkX2F0IjogMTQ2ODI0Mjk2MCwKICAiY3JlYXRlZCI6IDE0NjgyNDI5NTYs + CiAgImN1cnJlbnRfcGVyaW9kX2VuZCI6IDE0NzA5MjEzNTYsCiAgImN1cnJl + bnRfcGVyaW9kX3N0YXJ0IjogMTQ2ODI0Mjk1NiwKICAiY3VzdG9tZXIiOiAi + Y3VzXzhDek50TTA4TlZsU0dOIiwKICAiZGlzY291bnQiOiBudWxsLAogICJl + bmRlZF9hdCI6IG51bGwsCiAgImxpdmVtb2RlIjogZmFsc2UsCiAgIm1ldGFk + YXRhIjoge30sCiAgInBsYW4iOiB7CiAgICAiaWQiOiAibWVuc3VlbC10YXJp + Zi1yZWR1aXQtc3R1ZGVudC1tb250aC0yMDE2MDQwNDE3MTgyNyIsCiAgICAi + b2JqZWN0IjogInBsYW4iLAogICAgImFtb3VudCI6IDIwMDAsCiAgICAiY3Jl + YXRlZCI6IDE0NTk3ODMxMDgsCiAgICAiY3VycmVuY3kiOiAidXNkIiwKICAg + ICJpbnRlcnZhbCI6ICJtb250aCIsCiAgICAiaW50ZXJ2YWxfY291bnQiOiAx + LAogICAgImxpdmVtb2RlIjogZmFsc2UsCiAgICAibWV0YWRhdGEiOiB7fSwK + ICAgICJuYW1lIjogIk1lbnN1ZWwgdGFyaWYgcsOpZHVpdCAtIMOpdHVkaWFu + dCwgLSBkZSAyNSBhbnMsIGVuc2VpZ25hbnQsIGRlbWFuZGV1ciBkJ2VtcGxv + aSAtIG1vbnRoIiwKICAgICJzdGF0ZW1lbnRfZGVzY3JpcHRvciI6IG51bGws + CiAgICAidHJpYWxfcGVyaW9kX2RheXMiOiBudWxsCiAgfSwKICAicXVhbnRp + dHkiOiAxLAogICJzdGFydCI6IDE0NjgyNDI5NTYsCiAgInN0YXR1cyI6ICJh + Y3RpdmUiLAogICJ0YXhfcGVyY2VudCI6IG51bGwsCiAgInRyaWFsX2VuZCI6 + IG51bGwsCiAgInRyaWFsX3N0YXJ0IjogbnVsbAp9Cg== + http_version: + recorded_at: Mon, 11 Jul 2016 13:16:00 GMT +recorded_with: VCR 3.0.1 From 38fafb7a7088b1aebb938efe97a8e6870b027e1e Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 11 Jul 2016 16:04:15 +0200 Subject: [PATCH 13/30] wallet credit amount can be a float --- app/models/concerns/amount_concern.rb | 2 +- test/integration/wallets_test.rb | 2 +- test/models/wallet_test.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/amount_concern.rb b/app/models/concerns/amount_concern.rb index 86b01b443..b2d07d050 100644 --- a/app/models/concerns/amount_concern.rb +++ b/app/models/concerns/amount_concern.rb @@ -8,7 +8,7 @@ module AmountConcern if amount.nil? write_attribute(:amount, amount) else - write_attribute(:amount, amount.to_i * 100) + write_attribute(:amount, (amount * 100).to_i) end end diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index 5ace5e4af..bc9c89aac 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -61,7 +61,7 @@ class WalletsTest < ActionDispatch::IntegrationTest admin = users(:user_1) login_as(admin, scope: :user) w = @vlonchamp.wallet - amount = 10 + amount = 10.5 expected_amount = w.amount + amount put "/api/wallet/#{w.id}/credit", { diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb index 537ae6bdf..7eb1fbb01 100644 --- a/test/models/wallet_test.rb +++ b/test/models/wallet_test.rb @@ -13,8 +13,8 @@ class WalletTest < ActiveSupport::TestCase test 'can credit amount' do w = Wallet.first - expected_amount = w.amount + 5 - assert w.credit(5) + expected_amount = w.amount + 5.5 + assert w.credit(5.5) assert_equal w.amount, expected_amount end From 4f42253d3d8c4a3d329ae1c46a4fd4597f46a0dc Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 11 Jul 2016 16:58:17 +0200 Subject: [PATCH 14/30] wallet transaction list operation locale --- app/assets/templates/wallet/transactions.html.erb | 4 ++++ app/views/api/wallet/transactions.json.jbuilder | 7 ++++++- config/locales/app.shared.en.yml | 4 ++++ config/locales/app.shared.fr.yml | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/assets/templates/wallet/transactions.html.erb b/app/assets/templates/wallet/transactions.html.erb index 17130b013..d70aa110a 100644 --- a/app/assets/templates/wallet/transactions.html.erb +++ b/app/assets/templates/wallet/transactions.html.erb @@ -13,6 +13,10 @@ {{ t.created_at | amDateFormat:'L' }} {{ 'credit' }} + {{ 'debit_subscription' }} + {{ 'debit_reservation_training' }} + {{ 'debit_reservation_machine' }} + {{ 'debit_reservation_event' }} {{ t.user.full_name }} diff --git a/app/views/api/wallet/transactions.json.jbuilder b/app/views/api/wallet/transactions.json.jbuilder index d588705c4..050cb137f 100644 --- a/app/views/api/wallet/transactions.json.jbuilder +++ b/app/views/api/wallet/transactions.json.jbuilder @@ -1,7 +1,12 @@ json.array!(@wallet_transactions) do |t| - json.extract! t, :id, :transaction_type, :created_at, :amount + json.extract! t, :id, :transaction_type, :created_at, :amount, :transactable_type json.user do json.id t.user.id json.full_name t.user.profile.full_name end + json.transactable do + if t.transactable_type == 'Reservation' + json.reservable_type t.transactable.reservable_type + end + end if t.transactable end diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index 190386259..2461264bf 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -308,3 +308,7 @@ en: to_credit: 'Credit' wallet_credit_successfully: "Wallet of user is credited successfully." a_problem_occurred_for_wallet_credit: "A problem is occurred while taking the credit of wallet" + debit_subscription: "Debit by subscription" + debit_reservation_training: "Debit by reservation of training" + debit_reservation_machine: "Debit by reservation of machine" + debit_reservation_event: "Debit by reservation of event" diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml index 049662dc8..3544febdc 100644 --- a/config/locales/app.shared.fr.yml +++ b/config/locales/app.shared.fr.yml @@ -313,3 +313,7 @@ fr: amount_minimum_1: "Le montant minimum est d'1" you_have_amount_in_wallet: "Vous avez {{amount}} {{currency}} sur votre porte-monnaie" wallet_pay_reservation: "Vous pouvez effectuer directement votre paiement de réservation" + debit_subscription: "Payer un abonnement" + debit_reservation_training: "Payer un reservation de formation" + debit_reservation_machine: "Payer un reservation de machine" + debit_reservation_event: "Payer un reservation d'évenement" From 2c49285740a33f4d635e8095f281d45c20bd5baf Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 12 Jul 2016 11:39:18 +0200 Subject: [PATCH 15/30] avoir by wallet --- app/assets/javascripts/controllers/admin/invoices.coffee.erb | 1 + app/models/avoir.rb | 2 +- app/pdfs/pdf/invoice.rb | 2 ++ config/locales/app.admin.en.yml | 1 + config/locales/app.admin.fr.yml | 1 + config/locales/en.yml | 1 + config/locales/fr.yml | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/controllers/admin/invoices.coffee.erb b/app/assets/javascripts/controllers/admin/invoices.coffee.erb index daa410c94..1a1230cb0 100644 --- a/app/assets/javascripts/controllers/admin/invoices.coffee.erb +++ b/app/assets/javascripts/controllers/admin/invoices.coffee.erb @@ -494,6 +494,7 @@ Application.Controllers.controller 'AvoirModalController', ["$scope", "$uibModal {name: _t('by_cash'), value: 'cash'} {name: _t('by_cheque'), value: 'cheque'} {name: _t('by_transfer'), value: 'transfer'} + {name: _t('by_wallet'), value: 'wallet'} ] ## If a subscription was took with the current invoice, should it be canceled or not diff --git a/app/models/avoir.rb b/app/models/avoir.rb index a24564718..212c0627e 100644 --- a/app/models/avoir.rb +++ b/app/models/avoir.rb @@ -2,7 +2,7 @@ class Avoir < Invoice belongs_to :invoice after_create :expire_subscription, if: :subscription_to_expire - validates :avoir_mode, :inclusion => {:in => %w(stripe cheque transfer none cash)} + validates :avoir_mode, :inclusion => {:in => %w(stripe cheque transfer none cash wallet)} attr_accessor :invoice_items_ids diff --git a/app/pdfs/pdf/invoice.rb b/app/pdfs/pdf/invoice.rb index d9ce100f5..9c36f18df 100644 --- a/app/pdfs/pdf/invoice.rb +++ b/app/pdfs/pdf/invoice.rb @@ -196,6 +196,8 @@ module PDF payment_verbose += I18n.t('invoices.by_transfer') when 'cash' payment_verbose += I18n.t('invoices.by_cash') + when 'wallet' + payment_verbose += I18n.t('invoices.by_wallet') when 'none' payment_verbose = I18n.t('invoices.no_refund') else diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 9d70c3e59..d20f9ecdf 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -246,6 +246,7 @@ en: by_cash: "By cash" by_cheque: "By cheque" by_transfer: "By transfer" + by_wallet: "By wallet" you_must_select_at_least_one_element_to_create_a_refund: "You must select at least one element, to create a refund." unable_to_create_the_refund: "Unable to create the refund" invoice_reference_successfully_saved: "Invoice reference successfully saved." diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 87e4e04e9..8a8eb9c67 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -246,6 +246,7 @@ fr: by_cash: "En espèces" by_cheque: "Par chèque" by_transfer: "Par virement" + by_wallet: "Par porte-monnaie" you_must_select_at_least_one_element_to_create_a_refund: "Vous devez sélectionner au moins un élément sur lequel créer un avoir." unable_to_create_the_refund: "Impossible de créer l'avoir" invoice_reference_successfully_saved: "La référence facture a bien été enregistrée." diff --git a/config/locales/en.yml b/config/locales/en.yml index 0e50e0059..c1096022d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -97,6 +97,7 @@ en: by_transfer: "by transfer" by_cash: "by cash" no_refund: "No refund" + by_wallet: "by wallet" settlement_by_debit_card: "Settlement by debit card" settlement_done_at_the_reception: "Settlement done at the reception" on_DATE_at_TIME: "on %{DATE} at %{TIME}," diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 607c419fd..d053581bc 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -96,6 +96,7 @@ fr: by_cheque: "par chèque" by_transfer: "par virement" by_cash: "en espèces" + by_wallet: "par porte-monnaie" no_refund: "Pas de remboursement" settlement_by_debit_card: "Règlement effectué par carte bancaire" settlement_done_at_the_reception: "Règlement effectué à l'accueil" From 773c1adf4aa70eb33dfc319f24e9e79ab88e3351 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 12 Jul 2016 12:29:51 +0200 Subject: [PATCH 16/30] edit invoice reference --- .../javascripts/controllers/admin/invoices.coffee.erb | 2 ++ app/assets/templates/admin/invoices/index.html.erb | 9 ++++++++- app/models/invoice.rb | 3 +++ config/locales/app.admin.en.yml | 3 +++ config/locales/app.admin.fr.yml | 3 +++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/controllers/admin/invoices.coffee.erb b/app/assets/javascripts/controllers/admin/invoices.coffee.erb index 1a1230cb0..044e8fd05 100644 --- a/app/assets/javascripts/controllers/admin/invoices.coffee.erb +++ b/app/assets/javascripts/controllers/admin/invoices.coffee.erb @@ -133,6 +133,8 @@ Application.Controllers.controller "InvoicesController", ["$scope", "$state", 'I sample = sample.replace(/X\[([^\]]+)\]/g, (match, p1, offset, string) -> p1 ) + # information about wallet (W[text]) - does not apply here + sample = sample.replace(/W\[([^\]]+)\]/g, "") # information about refunds (R[text]) - does not apply here sample = sample.replace(/R\[([^\]]+)\]/g, "") sample diff --git a/app/assets/templates/admin/invoices/index.html.erb b/app/assets/templates/admin/invoices/index.html.erb index 730c0c01d..6ba4bda8a 100644 --- a/app/assets/templates/admin/invoices/index.html.erb +++ b/app/assets/templates/admin/invoices/index.html.erb @@ -210,6 +210,7 @@
  • {{ 'day' | translate }}
  • {{ '#_of_invoice' | translate }}
  • {{ 'online_sales' | translate }}
  • +
  • {{ 'wallet' | translate }}
  • {{ 'refund' | translate }}
  • @@ -279,6 +280,12 @@ + + \ No newline at end of file + diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 787eec524..301ac0cba 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -66,6 +66,9 @@ class Invoice < ActiveRecord::Base reference.gsub!(/X\[([^\]]+)\]/, ''.to_s) end + # information about wallet (W[text]) + reference.gsub!(/W\[([^\]]+)\]/, ''.to_s) + # remove information about refunds (R[text]) reference.gsub!(/R\[([^\]]+)\]/, ''.to_s) diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index d20f9ecdf..c416576d6 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -201,6 +201,7 @@ en: day: "Day" "#_of_invoice": "# of invoice" online_sales: "Online sales" + wallet: "Wallet" refund: "Refund" documentation: "Documentation" 2_digits_year_(eg_70): "2 digits year (eg. 70)" @@ -224,6 +225,8 @@ en: add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned: "Add a notice regarding refunds, only if the invoice is concerned." this_will_never_be_added_when_an_online_sales_notice_is_present: "This will never be added when an online sales notice is present." (eg_R[/A]_will_add_/A_to_the_refund_invoices): '(ed. R[/A] will add "/A" to the refund invoices)' + add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned: "Add a notice regarding the wallet, only if the invoice is concerned." + (eg_W[/PM]_will_add_/PM_to_the_invoices_settled_with_wallet): '(eg. W[/PM] will add "/PM" to the invoices settled with wallet)' code: "Code" enable_the_code: "Enable the code" enabled: "Enabled" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 8a8eb9c67..37e23e7be 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -201,6 +201,7 @@ fr: day: "Jour" "#_of_invoice": "N° de facture" online_sales: "Vente en ligne" + wallet: "Porte-monnaie" refund: "Remboursement" documentation: "Documentation" 2_digits_year_(eg_70): "Année sur 2 chiffres (ex. 70)" @@ -224,6 +225,8 @@ fr: add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned: "Ajoute une information relative aux remboursements, uniquement si cela concerne la facture. " this_will_never_be_added_when_an_online_sales_notice_is_present: "Ceci ne sera jamais cumulé avec une information de vente en ligne." (eg_R[/A]_will_add_/A_to_the_refund_invoices): '(ex. R[/A] ajoutera "/A" aux factures de remboursement)' + add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned: "Ajoute une information relative à le porte-monnaie, uniquement si cela concerne la facture." + (eg_W[/PM]_will_add_/PM_to_the_invoices_settled_with_wallet): '(ex. W[/PM] ajoutera "/PM" aux factures réglées avec porte-monnaie)' code: "Code" enable_the_code: "Activer le code" enabled: "Activé" From f2010e752d51a00ce5c514faa971d95f17d2aac3 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 18 Jul 2016 19:29:21 +0200 Subject: [PATCH 17/30] show pay by wallet in invoice --- app/models/reservation.rb | 1 + app/models/subscription.rb | 1 + app/pdfs/pdf/invoice.rb | 16 +++++++++++++++- config/locales/en.yml | 2 ++ config/locales/fr.yml | 2 ++ ...0160718165434_add_wallet_amount_to_invoice.rb | 5 +++++ db/schema.rb | 3 ++- 7 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20160718165434_add_wallet_amount_to_invoice.rb diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 4d60e53e5..addc6202b 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -365,6 +365,7 @@ class Reservation < ActiveRecord::Base if @wallet_amount_debit.present? and @wallet_amount_debit != 0 amount = @wallet_amount_debit / 100.0 WalletService.new(user: user, wallet: user.wallet).debit(amount, self) + self.invoice.update_columns(wallet_amount: @wallet_amount_debit) end end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 73445a947..47c031af0 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -235,6 +235,7 @@ class Subscription < ActiveRecord::Base if @wallet_amount_debit.present? and @wallet_amount_debit != 0 amount = @wallet_amount_debit / 100.0 WalletService.new(user: user, wallet: user.wallet).debit(amount, self) + invoices.order(created_at: :ASC).last.update_columns(wallet_amount: @wallet_amount_debit) end end end diff --git a/app/pdfs/pdf/invoice.rb b/app/pdfs/pdf/invoice.rb index 9c36f18df..4a2ec3d4f 100644 --- a/app/pdfs/pdf/invoice.rb +++ b/app/pdfs/pdf/invoice.rb @@ -185,6 +185,10 @@ module PDF # payment details move_down 20 + if invoice.wallet_amount + wallet_amount = invoice.wallet_amount / 100.0 + total = total - wallet_amount + end if invoice.is_a?(Avoir) payment_verbose = I18n.t('invoices.refund_on_DATE', DATE:I18n.l(invoice.avoir_date.to_date))+' ' case invoice.avoir_mode @@ -209,12 +213,22 @@ module PDF else payment_verbose = I18n.t('invoices.settlement_done_at_the_reception') end + if total == 0 and wallet_amount + payment_verbose = I18n.t('invoices.settlement_by_wallet') + end end unless invoice.is_a?(Avoir) payment_verbose += ' '+I18n.t('invoices.on_DATE_at_TIME', DATE: I18n.l(invoice.created_at.to_date), TIME:I18n.l(invoice.created_at, format: :hour_minute)) end unless invoice.is_a?(Avoir) and invoice.avoir_mode == 'none' - payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total)) if invoice.avoir_mode != 'none' + payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total)) if invoice.avoir_mode != 'none' if total > 0 + end + if invoice.wallet_amount + if total > 0 + payment_verbose += ' '+I18n.t('invoices.and') + ' ' + I18n.t('invoices.by_wallet') + ' ' + I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) + else + payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) + end end text payment_verbose diff --git a/config/locales/en.yml b/config/locales/en.yml index c1096022d..b9efe06a6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -100,12 +100,14 @@ en: by_wallet: "by wallet" settlement_by_debit_card: "Settlement by debit card" settlement_done_at_the_reception: "Settlement done at the reception" + settlement_by_wallet: "Settlement by wallet" on_DATE_at_TIME: "on %{DATE} at %{TIME}," for_an_amount_of_AMOUNT: "for an amount of %{AMOUNT}" on_DATE_from_START_to_END: "On %{DATE} from %{START} to %{END}" # eg: on feb. 7 from 7AM to 9AM from_STARTDATE_to_ENDDATE_from_STARTTIME_to_ENDTIME: "From %{STARTDATE} to %{ENDDATE}, from %{STARTTIME} to %{ENDTIME}" # eg: from feb. 7 to feb. 10, from 6PM to 10PM subscription_of_NAME_for_DURATION_starting_from_DATE: "Subscription of %{NAME} for %{DURATION} starting from %{DATE}" subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE: "Subscription of %{NAME} extended (Free days) starting from %{STARTDATE} until %{ENDDATE}" + and: 'and' trainings: # training availabilities diff --git a/config/locales/fr.yml b/config/locales/fr.yml index d053581bc..4114f89e0 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -100,12 +100,14 @@ fr: no_refund: "Pas de remboursement" settlement_by_debit_card: "Règlement effectué par carte bancaire" settlement_done_at_the_reception: "Règlement effectué à l'accueil" + settlement_by_wallet: "Règlement effectué par porte-monnaie" on_DATE_at_TIME: "le %{DATE} à %{TIME}," for_an_amount_of_AMOUNT: "pour un montant de %{AMOUNT}" on_DATE_from_START_to_END: "Le %{DATE} de %{START} à %{END}" # eg: on feb. 7 from 7AM to 9AM from_STARTDATE_to_ENDDATE_from_STARTTIME_to_ENDTIME: "Du %{STARTDATE} au %{ENDDATE}, de %{STARTTIME} à %{ENDTIME}" # eg: from feb. 7 to feb. 10, from 6PM to 10PM subscription_of_NAME_for_DURATION_starting_from_DATE: "Abonnement de %{NAME} pour %{DURATION} à compter du %{DATE}" subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE: "Prolongement Abonnement (Jours gratuits) de %{NAME} à compter du %{STARTDATE} jusqu'au %{ENDDATE}" + and: 'et' trainings: # disponibilités formations diff --git a/db/migrate/20160718165434_add_wallet_amount_to_invoice.rb b/db/migrate/20160718165434_add_wallet_amount_to_invoice.rb new file mode 100644 index 000000000..9900b3a5f --- /dev/null +++ b/db/migrate/20160718165434_add_wallet_amount_to_invoice.rb @@ -0,0 +1,5 @@ +class AddWalletAmountToInvoice < ActiveRecord::Migration + def change + add_column :invoices, :wallet_amount, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 0a50f1196..63222c2c7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160714095018) do +ActiveRecord::Schema.define(version: 20160718165434) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -220,6 +220,7 @@ ActiveRecord::Schema.define(version: 20160714095018) do t.string "type", limit: 255 t.boolean "subscription_to_expire" t.text "description" + t.integer "wallet_amount" end add_index "invoices", ["invoice_id"], name: "index_invoices_on_invoice_id", using: :btree From 7be60c2d00acb5330629d2a49b2bd42e39fb1fed Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 19 Jul 2016 11:33:41 +0200 Subject: [PATCH 18/30] fix locale key no found error --- app/assets/templates/dashboard/nav.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/templates/dashboard/nav.html.erb b/app/assets/templates/dashboard/nav.html.erb index b81d54246..9d609a588 100644 --- a/app/assets/templates/dashboard/nav.html.erb +++ b/app/assets/templates/dashboard/nav.html.erb @@ -14,7 +14,7 @@
  • {{ 'my_settings' }}
  • {{ 'my_projects' }}
  • {{ 'my_trainings' }}
  • -
  • {{ 'my_courses_and_workshops' }}
  • +
  • {{ 'my_events' }}
  • {{ 'my_invoices' }}
  • {{ 'my_wallet' }}
  • From 50ba15b5bffcbc6b988d3cf54d2e9c79c8730c88 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 19 Jul 2016 11:37:01 +0200 Subject: [PATCH 19/30] fix bug: dont update wallet amount if invoice disable, cant update wallet amount when user only subscribe subscription --- app/models/reservation.rb | 2 +- app/models/subscription.rb | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/reservation.rb b/app/models/reservation.rb index addc6202b..bcadc12fd 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -365,7 +365,7 @@ class Reservation < ActiveRecord::Base if @wallet_amount_debit.present? and @wallet_amount_debit != 0 amount = @wallet_amount_debit / 100.0 WalletService.new(user: user, wallet: user.wallet).debit(amount, self) - self.invoice.update_columns(wallet_amount: @wallet_amount_debit) + self.invoice.update_columns(wallet_amount: @wallet_amount_debit) if !user.invoicing_disabled? end end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 47c031af0..0a25d5a21 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -16,7 +16,6 @@ class Subscription < ActiveRecord::Base after_save :notify_member_subscribed_plan, if: :is_new? after_save :notify_admin_subscribed_plan, if: :is_new? after_save :notify_partner_subscribed_plan, if: :of_partner_plan? - after_save :debit_user_wallet # Stripe subscription payment def save_with_payment(invoice = true) @@ -46,7 +45,12 @@ class Subscription < ActiveRecord::Base # generate invoice stp_invoice = Stripe::Invoice.all(customer: user.stp_customer_id, limit: 1).data.first - generate_invoice(stp_invoice.id).save if invoice + if invoice + invoc = generate_invoice(stp_invoice.id) + # debit wallet + invoc.wallet_amount = @wallet_amount_debit if debit_user_wallet + invoc.save + end # cancel subscription after create cancel return true @@ -94,7 +98,12 @@ class Subscription < ActiveRecord::Base set_expired_at save! UsersCredits::Manager.new(user: self.user).reset_credits if expired_date_changed - generate_invoice.save if invoice + if invoice + invoc = generate_invoice + # debit wallet + invoc.wallet_amount = @wallet_amount_debit if debit_user_wallet + invoc.save + end return true else return false @@ -235,7 +244,6 @@ class Subscription < ActiveRecord::Base if @wallet_amount_debit.present? and @wallet_amount_debit != 0 amount = @wallet_amount_debit / 100.0 WalletService.new(user: user, wallet: user.wallet).debit(amount, self) - invoices.order(created_at: :ASC).last.update_columns(wallet_amount: @wallet_amount_debit) end end end From 85153c201ecb2fdb8802fc783bdc7021563ffc28 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 19 Jul 2016 11:37:12 +0200 Subject: [PATCH 20/30] test invoice wallet amount --- test/integration/reservations/create_as_admin_test.rb | 1 + test/integration/reservations/create_test.rb | 1 + test/integration/subscriptions/create_as_user_test.rb | 1 + 3 files changed, 3 insertions(+) diff --git a/test/integration/reservations/create_as_admin_test.rb b/test/integration/reservations/create_as_admin_test.rb index 046d166bb..af3e59243 100644 --- a/test/integration/reservations/create_as_admin_test.rb +++ b/test/integration/reservations/create_as_admin_test.rb @@ -282,6 +282,7 @@ module Reservations transaction = @vlonchamp.wallet.wallet_transactions.last assert_equal transaction.transaction_type, 'debit' assert_equal transaction.amount, 10 + assert_equal transaction.amount, invoice.wallet_amount / 100.0 end test "user without subscription and with invoicing disabled reserves a machine and pay wallet with success" do diff --git a/test/integration/reservations/create_test.rb b/test/integration/reservations/create_test.rb index 6648a7cd6..c55f528df 100644 --- a/test/integration/reservations/create_test.rb +++ b/test/integration/reservations/create_test.rb @@ -361,6 +361,7 @@ module Reservations transaction = @vlonchamp.wallet.wallet_transactions.last assert_equal transaction.transaction_type, 'debit' assert_equal transaction.amount, 10 + assert_equal transaction.amount, invoice.wallet_amount / 100.0 end end end diff --git a/test/integration/subscriptions/create_as_user_test.rb b/test/integration/subscriptions/create_as_user_test.rb index 0397dd85a..1d3907438 100644 --- a/test/integration/subscriptions/create_as_user_test.rb +++ b/test/integration/subscriptions/create_as_user_test.rb @@ -140,5 +140,6 @@ class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest transaction = @vlonchamp.wallet.wallet_transactions.last assert_equal transaction.transaction_type, 'debit' assert_equal transaction.amount, 10 + assert_equal transaction.amount, invoice.wallet_amount / 100.0 end end From 9befe091b84000dd8e3710d29cc37222a186a3c8 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 19 Jul 2016 13:16:49 +0200 Subject: [PATCH 21/30] refactoring generate invoice settlement info --- app/pdfs/pdf/invoice.rb | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/app/pdfs/pdf/invoice.rb b/app/pdfs/pdf/invoice.rb index 4a2ec3d4f..f35a9c07e 100644 --- a/app/pdfs/pdf/invoice.rb +++ b/app/pdfs/pdf/invoice.rb @@ -185,10 +185,6 @@ module PDF # payment details move_down 20 - if invoice.wallet_amount - wallet_amount = invoice.wallet_amount / 100.0 - total = total - wallet_amount - end if invoice.is_a?(Avoir) payment_verbose = I18n.t('invoices.refund_on_DATE', DATE:I18n.l(invoice.avoir_date.to_date))+' ' case invoice.avoir_mode @@ -207,7 +203,14 @@ module PDF else puts "ERROR : specified refunding method (#{payment_verbose}) is unknown" end + payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total)) + else + if invoice.wallet_amount + wallet_amount = invoice.wallet_amount / 100.0 + total = total - wallet_amount + end + if invoice.stp_invoice_id payment_verbose = I18n.t('invoices.settlement_by_debit_card') else @@ -216,18 +219,17 @@ module PDF if total == 0 and wallet_amount payment_verbose = I18n.t('invoices.settlement_by_wallet') end - end - unless invoice.is_a?(Avoir) + payment_verbose += ' '+I18n.t('invoices.on_DATE_at_TIME', DATE: I18n.l(invoice.created_at.to_date), TIME:I18n.l(invoice.created_at, format: :hour_minute)) - end - unless invoice.is_a?(Avoir) and invoice.avoir_mode == 'none' - payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total)) if invoice.avoir_mode != 'none' if total > 0 - end - if invoice.wallet_amount - if total > 0 - payment_verbose += ' '+I18n.t('invoices.and') + ' ' + I18n.t('invoices.by_wallet') + ' ' + I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) - else - payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) + if total > 0 or !invoice.wallet_amount + payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total)) + end + if invoice.wallet_amount + if total > 0 + payment_verbose += ' '+I18n.t('invoices.and') + ' ' + I18n.t('invoices.by_wallet') + ' ' + I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) + else + payment_verbose += ' '+I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount)) + end end end text payment_verbose From 5d84dcb666d38b7c6d4eeb4ba039c44d7a6fd362 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 19 Jul 2016 16:24:50 +0200 Subject: [PATCH 22/30] update locale --- .../javascripts/controllers/events.coffee.erb | 8 +++++-- .../controllers/machines.coffee.erb | 6 ++++- .../javascripts/controllers/plans.coffee.erb | 7 +++++- .../controllers/trainings.coffee.erb | 6 ++++- .../templates/plans/payment_modal.html.erb | 4 ++-- .../shared/valid_reservation_modal.html.erb | 4 ++-- .../templates/stripe/payment_modal.html.erb | 22 +++++++++---------- .../templates/wallet/credit_modal.html.erb | 4 ++-- config/locales/app.admin.fr.yml | 2 +- config/locales/app.shared.en.yml | 4 ++++ config/locales/app.shared.fr.yml | 5 ++++- config/locales/en.yml | 4 ++-- config/locales/fr.yml | 4 ++-- config/locales/mails.en.yml | 8 +++---- config/locales/mails.fr.yml | 8 +++---- 15 files changed, 60 insertions(+), 36 deletions(-) diff --git a/app/assets/javascripts/controllers/events.coffee.erb b/app/assets/javascripts/controllers/events.coffee.erb index 7e2adb3da..2813ee2c7 100644 --- a/app/assets/javascripts/controllers/events.coffee.erb +++ b/app/assets/javascripts/controllers/events.coffee.erb @@ -471,7 +471,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " eventToReserve: $scope.event reserve: $scope.reserve member: $scope.ctrl.member - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $locale) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$locale', '$filter', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $locale, $filter) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -486,6 +486,8 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + # Callback for the stripe payment authorization $scope.payment = (status, response) -> if response.error @@ -522,7 +524,7 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " Price.compute({reservation: reservation}).$promise wallet: -> Wallet.getWalletByUser({user_id: reservation.user_id}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', '$locale', 'helpers', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, $locale, helpers) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', '$locale', 'helpers', '$filter', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, $locale, helpers, $filter) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -537,6 +539,8 @@ Application.Controllers.controller "ShowEventController", ["$scope", "$state", " $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + # Button label if $scope.amount > 0 $scope.validButtonName = _t('confirm_(payment_on_site)') diff --git a/app/assets/javascripts/controllers/machines.coffee.erb b/app/assets/javascripts/controllers/machines.coffee.erb index e38e0824c..b3e665921 100644 --- a/app/assets/javascripts/controllers/machines.coffee.erb +++ b/app/assets/javascripts/controllers/machines.coffee.erb @@ -758,7 +758,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $locale) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$locale', '$filter', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $locale, $filter) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -773,6 +773,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + ## # Callback to process the payment with Stripe, triggered on button click ## @@ -826,6 +828,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + # Button label if $scope.amount > 0 $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.amount)}" diff --git a/app/assets/javascripts/controllers/plans.coffee.erb b/app/assets/javascripts/controllers/plans.coffee.erb index dd7f98d99..e5c5379e0 100644 --- a/app/assets/javascripts/controllers/plans.coffee.erb +++ b/app/assets/javascripts/controllers/plans.coffee.erb @@ -167,7 +167,7 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop member: -> $scope.ctrl.member wallet: -> Wallet.getWalletByUser({user_id: $scope.ctrl.member.id}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', 'CustomAsset', 'wallet', 'helpers', '$locale', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription, CustomAsset, wallet, helpers, $locale) -> + controller: ['$scope', '$uibModalInstance', '$state', 'selectedPlan', 'member', 'Subscription', 'CustomAsset', 'wallet', 'helpers', '$locale', '$filter', ($scope, $uibModalInstance, $state, selectedPlan, member, Subscription, CustomAsset, wallet, helpers, $locale, $filter) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -176,6 +176,9 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop $scope.selectedPlan = selectedPlan $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + + $scope.numberFilter = $filter('number') + # retrieve the CGV CustomAsset.get {name: 'cgv-file'}, (cgv) -> $scope.cgv = cgv.custom_asset @@ -227,6 +230,8 @@ Application.Controllers.controller "PlansIndexController", ["$scope", "$rootScop $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + $scope.plan = selectedPlan $scope.member = member diff --git a/app/assets/javascripts/controllers/trainings.coffee.erb b/app/assets/javascripts/controllers/trainings.coffee.erb index c525f0baa..7fd8825be 100644 --- a/app/assets/javascripts/controllers/trainings.coffee.erb +++ b/app/assets/javascripts/controllers/trainings.coffee.erb @@ -480,7 +480,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise - controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'wallet', 'cgv', 'Auth', 'Reservation', '$locale', 'helpers', ($scope, $uibModalInstance, $state, reservation, price, wallet, cgv, Auth, Reservation, $locale, helpers) -> + controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'wallet', 'cgv', 'Auth', 'Reservation', '$locale', 'helpers', '$filter', ($scope, $uibModalInstance, $state, reservation, price, wallet, cgv, Auth, Reservation, $locale, helpers, $filter) -> # user wallet amount $scope.walletAmount = wallet.amount @@ -495,6 +495,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + ## # Callback to process the payment with Stripe, triggered on button click ## @@ -551,6 +553,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM + $scope.numberFilter = $filter('number') + # Button label if $scope.amount > 0 $scope.validButtonName = "#{_t('confirm_my_payment_of_')} #{$filter('currency')($scope.amount)}" diff --git a/app/assets/templates/plans/payment_modal.html.erb b/app/assets/templates/plans/payment_modal.html.erb index faa472c24..a0103a77c 100644 --- a/app/assets/templates/plans/payment_modal.html.erb +++ b/app/assets/templates/plans/payment_modal.html.erb @@ -3,9 +3,9 @@

    {{ 'subscription_confirmation' }}