###前提・実現したいこと
ruby(ruby on rails)で、単一ポイントの取引機能(振込機能)があり、
支払ユーザーが保有する口座の残高を超える取引額を不可にしたい(支払ユーザーの口座残高未満の取引額ならモデルに保存・更新したい)と考えています。
ただし、残高を記録しているモデルと取引額を記録しているモデルは別々になっています。
いわゆる複数のモデルが絡むバリデーションになると思うのですが、そうゆう場合は、フォームオブジェクトで設定した方がいいと聞き、
フォームオブジェクトでバリデーションを作成してみたのですが、
よくわからず、うまくいかず困っています。
現状では、入力フォームで「支払先の口座番号」と「取引額」を入力すると、
3つのモデルが同時に動くようになっています。
図にするとこんな感じです。
データのやりとりはこんな感じです。
例えば、支払ユーザーの口座残高が10000(1万)なら、1万1以上の数値の入力を
AmmountTransaction(取引)モデルのamount(取引額)、
DepositsAndwithdrawal(入出金)モデルのamount(取引額)、
BasicIncomeAccount(口座)モデルのbalance(残高)を更新する際に使う増減額、
で禁止(バリデーション)できるようにしたいのですが、
その場合、formオブジェクトやコントローラ等のファイルには、どんな記述をすれば
いいのでしょうか?
教えて頂ければ幸いです。
###試しに書いたソースコード
フォームオブジェクトのコードになります。
app/models/form/account_transaction_form.rb
ruby
1class AccountTransactionForm 2 3 include ActiveModel::Model 4 5 attr_accessor :erroes, :amount, :deposit_account_id 6 7 def initialize(amount, withdrawal_account_id, deposit_account_id, basic_income_account) 8 @amount = amount 9 @withdrawal_account_id = withdrawal_account_id 10 @deposit_account_id = deposit_account_id 11 @balance = basic_income_acccount.balance 12 @errors = ActiveModel::Errors.new(self) 13 end 14 15 #検証に関する記述 16 def valid? 17 errors.clear 18 if @balance < @amount 19 errors.add(:amount, "残高以上の送金はできません") 20 end 21 errors.empty? 22 end 23end 24
コントローラのコードです。3つのモデルを動かす取引処理の記述が書かれています。
取引処理は複数のモデルが絡む固有の処理(ビジネスロジックというもの?)なので、
この固有の処理もフォームオブジェクトやサービスクラスに
フォームオブジェクトに移動させた方がいいのですが、質問の本題から離れるのでひとまずおいておきます。
コントローラには、フォームオブジェクトを呼び出すコードを記述をすると思うのですが、
仮に複数のモデルが絡むバリデーションだけを行うフォームオブジェクトの場合、
どこにどんな記述をすればいいのでしょうか?
ruby
1class AccountTransactionsController < ApplicationController 2 3 #取引を作成する 4 def new 5 @account_transaction = AccountTransaction.new 6 end 7 8 #取引処理をする為に3つのモデルを同時に保存・更新している 9 def create 10 #取引モデルに1件のレコードを新規作成 11 AccountTransaction.transaction do 12 @account_transaction = AccountTransaction.new( 13 withdrawal_account_id: current_user.basic_income_account.id, 14 deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, 15 amount: params[:account_transaction][:amount] 16 ) 17 @account_transaction.save! 18 #入出金モデルに2件のレコードを新規作成 19 DepositsAndWithdrawal.transaction do 20 #出金側レコードを作成保存 21 @account_transaction.deposits_and_withdrawals.build( 22 transaction_type: "出金", 23 basic_income_account_id: current_user.basic_income_account.id, 24 amount: -1 * params[:account_transaction][:amount].to_f 25 ) 26 @account_transaction.save! 27 #出金側レコードを作成保存 28 @account_transaction.deposits_and_withdrawals.build( 29 transaction_type: "入金", 30 basic_income_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, 31 amount: params[:account_transaction][:amount].to_f 32 ) 33 @account_transaction.save! 34 #口座モデルを更新(残高の増減) 35 BasicIncomeAccount.transaction do 36 @withdrawal_basic_income_account = current_user.basic_income_account 37 @deposit_basic_income_account = BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]) 38 39 @withdrawal_amount = params[:account_transaction][:amount].to_f 40 @deposit_amount = params[:account_transaction][:amount].to_f 41 42 @withdrawal_basic_income_account.update(balance: @withdrawal_basic_income_account.balance - @withdrawal_amount) 43 @deposit_basic_income_account.update(balance: @deposit_basic_income_account.balance + @deposit_amount) 44 end 45 end 46 end 47 redirect_to root_path 48 rescue => e 49 render plain: e.message 50 51 end 52 53 54 private 55 def account_transaction_params 56 params.require(:account_transaction).permit(:deposit_account_id, :amount) 57 end 58end
入力フォーム画面のコードです。
<%= form_for @account_transaction do |f|%> <% if @account_transaction.errors.any? %> <h3>入力内容にエラーが<%= @account_transaction.errors.count %>件あります</h3> <ul> <% @account_transaction.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> <% end %> <div class="field"> <%= f.label :送金相手の口座番号 %> <%= f.text_field :deposit_account_id %> <%= f.label :送金額 %> <%= f.number_field :amount,min:1,max:999999999999999 %> <%= f.submit"送金確認" %> </div> <% end %>
3つのモデルの状態です。
ruby
1 create_table "account_transactions", force: :cascade do |t| 2 t.decimal "amount" 3 t.datetime "created_at", null: false 4 t.datetime "updated_at", null: false 5 t.integer "withdrawal_account_id" 6 t.integer "deposit_account_id" 7 t.index ["deposit_account_id"], name: "index_account_transactions_on_deposit_account_id" 8 t.index ["withdrawal_account_id"], name: "index_account_transactions_on_withdrawal_account_id" 9 end 10 11 create_table "basic_income_accounts", force: :cascade do |t| 12 t.integer "user_id" 13 t.string "account_number" 14 t.decimal "balance" 15 t.datetime "created_at", null: false 16 t.datetime "updated_at", null: false 17 t.index ["user_id"], name: "index_basic_income_accounts_on_user_id" 18 end 19 20 create_table "deposits_and_withdrawals", force: :cascade do |t| 21 t.integer "account_transaction_id" 22 t.string "transaction_type" 23 t.decimal "amount" 24 t.datetime "created_at", null: false 25 t.datetime "updated_at", null: false 26 t.integer "basic_income_account_id" 27 t.index ["account_transaction_id"], name: "index_deposits_and_withdrawals_on_account_transaction_id" 28 t.index ["basic_income_account_id"], name: "index_deposits_and_withdrawals_on_basic_income_account_id" 29 end
###補足情報(言語/FW/ツール等のバージョンなど)
Rails 5.1.3
ruby 2.4.1
devise (4.3.0)
あなたの回答
tips
プレビュー