###前提・実現したいこと
ruby(ruby on rails)で、ポイント振替機能みたいなものをつくっていているのですが、
2つの問題が起きてしまっています。
一つ目は残高以上の数値を入力しても取引が実行されてしまうので、
「モデルのvalidates(検証)で、支払ユーザー(現在ログインしているユーザー)が保有する口座残高以下の取引額のみ検証成功」にしたいと思っています。
ただ、残高を記録するモデルと取引額を記録するモデルは別々に分かれています。
図にするとこんな感じです。
例えば、AccountTransactionモデルにamount(取引額)を記録する前に、ログインユーザーの保有する残高が記録されたBasicIncomeAccountモデルのbalance(残高)を参照して、
「入力された値が残高以下なら検証成功として記録」
「入力された値が残高以上なら検証失敗として記録しない」
ようにしたいのですのですが、やり方がわからず困っています。
2つ目の問題は、入力フォームでマイナスの取引額を入力してしまうと、出金処理と入金処理が逆転してしまうので、「入力フォームでマイナス数値の入力を不可」にしたいと思っているのですが、フォーム側で制限ってできるのでしょうか?それとも、モデル側で制限した方がいいのでしょうか?
教えて頂ければ幸いです。
###現状のコード
入力フォームです。
ユーザーは、口座番号と送金額だけ入力しています。
app/views/account_transactions/new.html.erb
<%= form_for @account_transaction do |f|%> <div class="field"> <%= f.label :送金相手の口座番号 %> <%= f.text_field :deposit_account_id %> <%= f.label :送金額 %> <%= f.number_field %> <%= f.submit"送金確認" %> </div> <% end %>
account_transactions(取引)テーブル、
basic_income_accounts(口座)テーブル、
の内容です。
create_table "account_transactions", force: :cascade do |t| t.decimal "amount" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "withdrawal_account_id" t.integer "deposit_account_id" t.index ["deposit_account_id"], name: "index_account_transactions_on_deposit_account_id" t.index ["withdrawal_account_id"], name: "index_account_transactions_on_withdrawal_account_id" end create_table "basic_income_accounts", force: :cascade do |t| t.integer "user_id" t.string "account_number" t.decimal "balance" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["user_id"], name: "index_basic_income_accounts_on_user_id" end
また、BasicIncomeAccount(口座モデル)AccoutTransaction(取引モデル)は、
1:多の関連付けが施されています。
app/models/basic_income_account.rb
class BasicIncomeAccount < ApplicationRecord belongs_to :user validates :user_id, presence:true validates :account_number, presence: true, uniqueness: true validates :balance, presence: true, numericality: true #BasicIncomeAccountは、AccountTransactionを多数保有してる1:多の関係である has_many :withdrawal_account_transaction, class_name: 'AccountTransaction', :foreign_key => 'withdrawal_account_id' has_many :deposit_account_transaction, class_name: 'AccountTransaction', :foreign_key => 'deposit_account_id' has_many :deposits_and_withdrawals
今回の問題の本題となる取引モデルのコードです。
app/models/account_transaction.rb
class AccountTransaction < ApplicationRecord belongs_to :withdrawal, class_name: 'BasicIncomeAccount', :foreign_key => 'withdrawal_account_id' belongs_to :deposit, class_name: 'BasicIncomeAccount', :foreign_key => 'deposit_account_id' validates :withdrawal_account_id, presence:true validates :deposit_account_id, presence:true #amount(取引額)が入力され、入力された内容が数値が小数点で、自前の条件付きバリデーションに検証をクリアしたら検証成功という処理を記述したい validates :amount, presence: true, numericality: true, if: :balance_check has_many :deposits_and_withdrawals private ##おそらくカスタムなバリデーションをつくることになるが、その記述方法がわからない def balance_check if current_user.basic_income_acccount.balance < ?????????? errors.add(:amount, "残高以上の送金はできません") end end end
追記
account_transactionsのコントローラです。
ここで、AccountTransactionとDepositsAndWithdrawalにレコードを作成し、
BasicIncomeAccountの更新を同時かつトランザクション処理でできるようにしております。
(ポイント振替処理ができるようになっています)
/app/controllers/account_transactions_controller.rb
AccountTransaction.transaction do #取引モデルにレコードを作成保存 @account_transaction = AccountTransaction.new( withdrawal_account_id: current_user.basic_income_account.id, deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, amount: params[:account_transaction][:amount] ) @account_transaction.save! DepositsAndWithdrawal.transaction do #入出金モデルに出金側レコードを作成保存 @account_transaction.deposits_and_withdrawals.build( transaction_type: "出金", basic_income_account_id: current_user.basic_income_account.id, amount: -1 * params[:account_transaction][:amount].to_f ) @account_transaction.save! #入出金モデルに出金側レコードを作成保存 @account_transaction.deposits_and_withdrawals.build( transaction_type: "入金", basic_income_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, amount: params[:account_transaction][:amount].to_f ) @account_transaction.save! BasicIncomeAccount.transaction do #出金口座と入金口座の残高を更新する @withdrawal_basic_income_account = current_user.basic_income_account @deposit_basic_income_account = BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]) @withdrawal_amount = params[:account_transaction][:amount].to_f @deposit_amount = params[:account_transaction][:amount].to_f @withdrawal_basic_income_account.update(balance: @withdrawal_basic_income_account.balance - @withdrawal_amount) @deposit_basic_income_account.update(balance: @deposit_basic_income_account.balance + @deposit_amount) end end end #トランザクション処理が完全成功なら、root_path(ホーム画面)へ飛ぶ redirect_to root_path #トランザクション処理で例外発生(失敗)したら、以下の例外処理を実行する rescue => e render plain: e.message
###補足情報(言語/FW/ツール等のバージョンなど)
Rails 5.1.3
ruby 2.4.1
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/12/19 07:11