diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ef2de47..82eacbf1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ - [TODO DEPLOY] `rake fablab:setup:chain_invoices_items_records` - [TODO DEPLOY] `rake fablab:setup:chain_invoices_records` - [TODO DEPLOY] `rake fablab:setup:chain_history_values_records` -- [TODO DEPLOY] -> (only dev) yarn install +- [TODO DEPLOY] -> (only dev) yarn install ## v3.1.1 2019 April 8 diff --git a/app/controllers/api/wallet_controller.rb b/app/controllers/api/wallet_controller.rb index ff904166b..d573497bb 100644 --- a/app/controllers/api/wallet_controller.rb +++ b/app/controllers/api/wallet_controller.rb @@ -5,7 +5,8 @@ class API::WalletController < API::ApiController before_action :authenticate_user! def by_user - @wallet = Wallet.find_by(user_id: params[:user_id]) + invoicing_profile = InvoicingProfile.find_by(user_id: params[:user_id]) + @wallet = Wallet.find_by(invoicing_profile_id: invoicing_profile.id) authorize @wallet render :show end @@ -13,7 +14,7 @@ class API::WalletController < API::ApiController def transactions @wallet = Wallet.find(params[:id]) authorize @wallet - @wallet_transactions = @wallet.wallet_transactions.includes(:invoice, user: [:profile]).order(created_at: :desc) + @wallet_transactions = @wallet.wallet_transactions.includes(:invoice, :invoicing_profile).order(created_at: :desc) end def credit diff --git a/app/models/invoicing_profile.rb b/app/models/invoicing_profile.rb index 9fa546f11..e574af6cd 100644 --- a/app/models/invoicing_profile.rb +++ b/app/models/invoicing_profile.rb @@ -6,9 +6,20 @@ class InvoicingProfile < ActiveRecord::Base accepts_nested_attributes_for :organization, allow_destroy: false has_many :invoices, dependent: :destroy + has_one :wallet, dependent: :destroy + has_many :wallet_transactions, dependent: :destroy + + after_create :create_a_wallet + def full_name # if first_name or last_name is nil, the empty string will be used as a temporary replacement (first_name || '').humanize.titleize + ' ' + (last_name || '').humanize.titleize end + + private + + def create_a_wallet + create_wallet + end end diff --git a/app/models/user.rb b/app/models/user.rb index 3038c7be4..27330b046 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -52,9 +52,6 @@ class User < ActiveRecord::Base has_many :tags, through: :user_tags accepts_nested_attributes_for :tags, allow_destroy: true - has_one :wallet, dependent: :destroy - has_many :wallet_transactions, dependent: :destroy - has_many :exports, dependent: :destroy has_many :history_values, dependent: :nullify @@ -65,7 +62,6 @@ class User < ActiveRecord::Base 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_group_changed, if: :group_id_changed? @@ -138,6 +134,14 @@ class User < ActiveRecord::Base invoicing_profile.invoices end + def wallet + invoicing_profile.wallet + end + + def wallet_transactions + invoicing_profile.wallet_transactions + end + def generate_subscription_invoice(operator_id) return unless subscription @@ -328,10 +332,6 @@ class User < ActiveRecord::Base StripeWorker.perform_async(:create_stripe_customer, id) end - def create_a_wallet - create_wallet - end - def send_devise_notification(notification, *args) devise_mailer.send(notification, self, *args).deliver_later end diff --git a/app/models/wallet.rb b/app/models/wallet.rb index da8128eb7..fdc419416 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -1,13 +1,18 @@ +# frozen_string_literal: true + +# user's virtual wallet which can be credited by an admin +# all subsequent user's transactions will charge the wallet, as the default payment mean, if the wallet amount > 0 +# if the wallet amount is not sufficient, a secondary payment mean will be requested (card or cash, depending on the login context) class Wallet < ActiveRecord::Base include AmountConcern - belongs_to :user + belongs_to :invoicing_profile has_many :wallet_transactions, dependent: :destroy - validates :user, presence: true + validates :invoicing_profile, presence: true def credit(amount) - if amount.is_a?(Numeric) and amount >= 0 + if amount.is_a?(Numeric) && amount >= 0 self.amount += amount return save end @@ -15,10 +20,14 @@ class Wallet < ActiveRecord::Base end def debit(amount) - if amount.is_a?(Numeric) and amount >= 0 + if amount.is_a?(Numeric) && amount >= 0 self.amount -= amount return save end false end + + def user + invoicing_profile.user + end end diff --git a/app/models/wallet_transaction.rb b/app/models/wallet_transaction.rb index 0de5feb3b..2fef76657 100644 --- a/app/models/wallet_transaction.rb +++ b/app/models/wallet_transaction.rb @@ -1,12 +1,19 @@ +# frozen_string_literal: true + +# track of all transactions payed using the given wallet class WalletTransaction < ActiveRecord::Base include AmountConcern - belongs_to :user + belongs_to :invoicing_profile belongs_to :wallet belongs_to :reservation belongs_to :transactable, polymorphic: true has_one :invoice - validates_inclusion_of :transaction_type, in: %w( credit debit ) - validates :user, :wallet, presence: true + validates_inclusion_of :transaction_type, in: %w[credit debit] + validates :invoicing_profile, :wallet, presence: true + + def user + invoicing_profile.user + end end diff --git a/app/services/wallet_service.rb b/app/services/wallet_service.rb index a60b98d19..12d0a74e6 100644 --- a/app/services/wallet_service.rb +++ b/app/services/wallet_service.rb @@ -8,7 +8,7 @@ class WalletService def credit(amount) ActiveRecord::Base.transaction do if @wallet.credit(amount) - transaction = WalletTransaction.new(user: @user, wallet: @wallet, transaction_type: 'credit', amount: amount) + transaction = WalletTransaction.new(invoicing_profile: @user.invoicing_profile, wallet: @wallet, transaction_type: 'credit', amount: amount) if transaction.save NotificationCenter.call type: 'notify_user_wallet_is_credited', receiver: @wallet.user, @@ -28,10 +28,15 @@ class WalletService def debit(amount, transactable) ActiveRecord::Base.transaction do if @wallet.debit(amount) - transaction = WalletTransaction.new(user: @user, wallet: @wallet, transaction_type: 'debit', amount: amount, transactable: transactable) - if transaction.save - return transaction - end + transaction = WalletTransaction.new( + invoicing_profile: @user&.invoicing_profile, + wallet: @wallet, + transaction_type: 'debit', + amount: amount, + transactable: transactable + ) + + return transaction if transaction.save end raise ActiveRecord::Rollback end diff --git a/app/views/api/wallet/show.json.jbuilder b/app/views/api/wallet/show.json.jbuilder index ef0b12454..ee8f2bd56 100644 --- a/app/views/api/wallet/show.json.jbuilder +++ b/app/views/api/wallet/show.json.jbuilder @@ -1 +1,2 @@ -json.extract! @wallet, :id, :user_id, :amount +json.extract! @wallet, :id, :invoicing_profile_id, :amount +json.user_id @wallet.invoicing_profile.user_id diff --git a/app/views/api/wallet/transactions.json.jbuilder b/app/views/api/wallet/transactions.json.jbuilder index 14a484915..eaa955dd2 100644 --- a/app/views/api/wallet/transactions.json.jbuilder +++ b/app/views/api/wallet/transactions.json.jbuilder @@ -1,11 +1,13 @@ json.array!(@wallet_transactions) do |t| 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 + json.id t.invoicing_profile.user_id + json.full_name t.invoicing_profile.full_name + end + if t.invoice + json.invoice do + json.id t.invoice.id + json.reference t.invoice.reference + end end - json.invoice do - json.id t.invoice.id - json.reference t.invoice.reference - end if t.invoice end diff --git a/db/migrate/20190603123754_add_invoicing_profile_to_wallet.rb b/db/migrate/20190603123754_add_invoicing_profile_to_wallet.rb new file mode 100644 index 000000000..5354ebf32 --- /dev/null +++ b/db/migrate/20190603123754_add_invoicing_profile_to_wallet.rb @@ -0,0 +1,6 @@ +class AddInvoicingProfileToWallet < ActiveRecord::Migration + def change + add_reference :wallets, :invoicing_profile, index: true, foreign_key: true + add_reference :wallet_transactions, :invoicing_profile, index: true, foreign_key: true + end +end diff --git a/db/migrate/20190603123916_migrate_wallet_to_invoicing_profile.rb b/db/migrate/20190603123916_migrate_wallet_to_invoicing_profile.rb new file mode 100644 index 000000000..37450dcc3 --- /dev/null +++ b/db/migrate/20190603123916_migrate_wallet_to_invoicing_profile.rb @@ -0,0 +1,31 @@ +class MigrateWalletToInvoicingProfile < ActiveRecord::Migration + def up + Wallet.all.each do |w| + user = User.find(w.user_id) + w.update_attributes( + invoicing_profile_id: user.invoicing_profile.id + ) + end + WalletTransaction.all.each do |wt| + user = User.find(wt.user_id) + wt.update_attributes( + invoicing_profile_id: user.invoicing_profile.id + ) + end + end + + def down + Wallet.all.each do |w| + invoicing_profile = User.find(w.invoicing_profile_id) + w.update_attributes( + user_id: invoicing_profile.user_id + ) + end + WalletTransaction.all.each do |wt| + invoicing_profile = User.find(wt.invoicing_profile_id) + wt.update_attributes( + user_id: invoicing_profile.user_id + ) + end + end +end diff --git a/db/migrate/20190603130038_remove_user_id_from_wallet.rb b/db/migrate/20190603130038_remove_user_id_from_wallet.rb new file mode 100644 index 000000000..0f8d60e4e --- /dev/null +++ b/db/migrate/20190603130038_remove_user_id_from_wallet.rb @@ -0,0 +1,6 @@ +class RemoveUserIdFromWallet < ActiveRecord::Migration + def change + remove_reference :wallets, :user, index: true, foreign_key: true + remove_reference :wallet_transactions, :user, index: true, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 08f2bbda9..c066a8056 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: 20190529120814) do +ActiveRecord::Schema.define(version: 20190603130038) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -860,28 +860,28 @@ ActiveRecord::Schema.define(version: 20190529120814) 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 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "invoicing_profile_id" end + add_index "wallet_transactions", ["invoicing_profile_id"], name: "index_wallet_transactions_on_invoicing_profile_id", using: :btree 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 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "amount", default: 0 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "invoicing_profile_id" end - add_index "wallets", ["user_id"], name: "index_wallets_on_user_id", using: :btree + add_index "wallets", ["invoicing_profile_id"], name: "index_wallets_on_invoicing_profile_id", using: :btree add_foreign_key "accounting_periods", "users", column: "closed_by" add_foreign_key "availability_tags", "availabilities" @@ -915,7 +915,7 @@ ActiveRecord::Schema.define(version: 20190529120814) do add_foreign_key "tickets", "reservations" add_foreign_key "user_tags", "tags" add_foreign_key "user_tags", "users" - add_foreign_key "wallet_transactions", "users" + add_foreign_key "wallet_transactions", "invoicing_profiles" add_foreign_key "wallet_transactions", "wallets" - add_foreign_key "wallets", "users" + add_foreign_key "wallets", "invoicing_profiles" end diff --git a/test/fixtures/wallet_transactions.yml b/test/fixtures/wallet_transactions.yml index 012604359..1f6eba863 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: 5 + invoicing_profile_id: 5 wallet: wallet_5 transaction_type: credit amount: 1000 diff --git a/test/fixtures/wallets.yml b/test/fixtures/wallets.yml index 696573c94..84521bdbd 100644 --- a/test/fixtures/wallets.yml +++ b/test/fixtures/wallets.yml @@ -1,27 +1,27 @@ wallet_2: - user_id: 2 + invoicing_profile_id: 2 amount: 0 wallet_4: - user_id: 4 + invoicing_profile_id: 4 amount: 0 wallet_6: - user_id: 6 + invoicing_profile_id: 6 amount: 0 wallet_5: - user_id: 5 + invoicing_profile_id: 5 amount: 1000 wallet_3: - user_id: 3 + invoicing_profile_id: 3 amount: 0 wallet_1: - user_id: 1 + invoicing_profile_id: 1 amount: 0 wallet_7: - user_id: 7 + invoicing_profile_id: 7 amount: 0 diff --git a/test/integration/members/as_admin_test.rb b/test/integration/members/as_admin_test.rb index 3276c854f..01ac9f914 100644 --- a/test/integration/members/as_admin_test.rb +++ b/test/integration/members/as_admin_test.rb @@ -25,6 +25,11 @@ class MembersTest < ActionDispatch::IntegrationTest first_name: 'Robert', birthday: '2018-02-08', phone: '0485232145' + }, + invoicing_profile_attributes: { + address_attributes: { + address: '21 grand rue, 73110 Bourget-en-Huile' + } } } }.to_json, default_headers end diff --git a/test/integration/wallets_test.rb b/test/integration/wallets_test.rb index d279edc28..7e0e3791b 100644 --- a/test/integration/wallets_test.rb +++ b/test/integration/wallets_test.rb @@ -19,7 +19,7 @@ class WalletsTest < ActionDispatch::IntegrationTest assert_equal 200, response.status assert_equal Mime::JSON, response.content_type wallet = json_response(response.body) - assert_equal @vlonchamp.wallet.user_id, wallet[:user_id] + assert_equal @vlonchamp.wallet.invoicing_profile_id, wallet[:invoicing_profile_id] assert_equal @vlonchamp.wallet.amount, wallet[:amount] end @@ -31,7 +31,7 @@ class WalletsTest < ActionDispatch::IntegrationTest 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.invoicing_profile_id, wallet[:invoicing_profile_id] assert_equal @user1.wallet.amount, wallet[:amount] end diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb index 7eb1fbb01..16dbb76bf 100644 --- a/test/models/wallet_test.rb +++ b/test/models/wallet_test.rb @@ -1,14 +1,14 @@ require 'test_helper' class WalletTest < ActiveSupport::TestCase - test "default amount must be zero" do + test 'default amount must be zero' do w = Wallet.new - assert w.amount == 0 + assert w.amount.zero? end - test 'should user present' do + test 'should invoicing_profile present' do w = Wallet.create - assert w.errors[:user].present? + assert w.errors[:invoicing_profile].present? end test 'can credit amount' do diff --git a/test/models/wallet_transaction_test.rb b/test/models/wallet_transaction_test.rb index f51de148a..ec79d65ed 100644 --- a/test/models/wallet_transaction_test.rb +++ b/test/models/wallet_transaction_test.rb @@ -4,7 +4,7 @@ class WalletTransactionTest < ActiveSupport::TestCase test 'transaction type must be credit or debit' do @jdupond = User.find_by(username: 'jdupond') @jdupond_wallet = @jdupond.wallet - transaction = WalletTransaction.new amount: 5, user: @jdupond, wallet: @jdupond_wallet + transaction = WalletTransaction.new amount: 5, invoicing_profile: @jdupond.invoicing_profile, wallet: @jdupond_wallet transaction.transaction_type = 'credit' assert transaction.valid? transaction.transaction_type = 'debit'