diff --git a/app/controllers/admin/students_controller.rb b/app/controllers/admin/students_controller.rb index 6d6caa9..42c3d28 100644 --- a/app/controllers/admin/students_controller.rb +++ b/app/controllers/admin/students_controller.rb @@ -2,11 +2,12 @@ module Admin class StudentsController < Admin::ApplicationController # Overwrite any of the RESTful controller actions to implement custom behavior # For example, you may want to send an email after a foo is updated. - # - # def update - # super - # send_foo_updated_email(requested_resource) - # end + + def update + super + + create_portfolio_transaction! + end # Override this method to specify custom lookup behavior. # This will be used to set the resource for the `show`, `edit`, and `update` @@ -42,5 +43,17 @@ class StudentsController < Admin::ApplicationController # See https://administrate-demo.herokuapp.com/customizing_controller_actions # for more information + # + private + + def create_portfolio_transaction! + return unless fund_amount + + PortfolioTransaction.create!(portfolio: requested_resource.portfolio, amount: fund_amount) if fund_amount + end + + def fund_amount + @fund_amount ||= params['student']['add_fund_amount'] + end end end diff --git a/app/dashboards/portfolio_transaction_dashboard.rb b/app/dashboards/portfolio_transaction_dashboard.rb index 0f943da..8d67f86 100644 --- a/app/dashboards/portfolio_transaction_dashboard.rb +++ b/app/dashboards/portfolio_transaction_dashboard.rb @@ -1,4 +1,4 @@ -require "administrate/base_dashboard" +require 'administrate/base_dashboard' class PortfolioTransactionDashboard < Administrate::BaseDashboard # ATTRIBUTE_TYPES @@ -9,11 +9,13 @@ class PortfolioTransactionDashboard < Administrate::BaseDashboard # on pages throughout the dashboard. ATTRIBUTE_TYPES = { id: Field::Number, - actor: Field::BelongsTo, portfolio: Field::BelongsTo, - transaction_type: Field::Select.with_options(searchable: false, collection: ->(field) { field.resource.class.send(field.attribute.to_s.pluralize).keys }), + transaction_type: Field::Select.with_options(searchable: false, collection: lambda { |field| + field.resource.class.send(field.attribute.to_s.pluralize).keys + }), + amount: Field::Number.with_options(decimals: 2), created_at: Field::DateTime, - updated_at: Field::DateTime, + updated_at: Field::DateTime }.freeze # COLLECTION_ATTRIBUTES @@ -23,7 +25,6 @@ class PortfolioTransactionDashboard < Administrate::BaseDashboard # Feel free to add, remove, or rearrange items. COLLECTION_ATTRIBUTES = %i[ id - actor portfolio transaction_type ].freeze @@ -32,7 +33,6 @@ class PortfolioTransactionDashboard < Administrate::BaseDashboard # an array of attributes that will be displayed on the model's show page. SHOW_PAGE_ATTRIBUTES = %i[ id - actor portfolio transaction_type created_at @@ -43,9 +43,9 @@ class PortfolioTransactionDashboard < Administrate::BaseDashboard # an array of attributes that will be displayed # on the model's form (`new` and `edit`) pages. FORM_ATTRIBUTES = %i[ - actor portfolio transaction_type + amount ].freeze # COLLECTION_FILTERS diff --git a/app/models/portfolio.rb b/app/models/portfolio.rb index da52130..198a859 100644 --- a/app/models/portfolio.rb +++ b/app/models/portfolio.rb @@ -3,6 +3,10 @@ class Portfolio < ApplicationRecord has_many :portfolio_transactions has_many :portfolio_stocks + def cash_balance + portfolio_transactions.sum(:amount) + end + # a User (specifically student) can buy a certain amount of stock def buy_stock(ticker, shares) stock = Stock.find_by(ticker:) diff --git a/app/models/student.rb b/app/models/student.rb index 71dfb88..43e6df0 100644 --- a/app/models/student.rb +++ b/app/models/student.rb @@ -1,4 +1,2 @@ class Student < User - belongs_to :classroom - has_one :portfolio, foreign_key: 'user_id' -end \ No newline at end of file +end diff --git a/app/models/user.rb b/app/models/user.rb index 16a0cc1..0afdf52 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,7 @@ class User < ApplicationRecord has_one :portfolio has_many :orders + belongs_to :classroom # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable diff --git a/app/views/admin/students/_form.html.erb b/app/views/admin/students/_form.html.erb new file mode 100644 index 0000000..25fb1d1 --- /dev/null +++ b/app/views/admin/students/_form.html.erb @@ -0,0 +1,74 @@ +<%# +# Form Partial + +This partial is rendered on a resource's `new` and `edit` pages, +and renders all form fields for a resource's editable attributes. + +## Local variables: + +- `page`: + An instance of [Administrate::Page::Form][1]. + Contains helper methods to display a form, + and knows which attributes should be displayed in the resource's form. + +[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Form +%> + +<%= form_for([namespace, page.resource], html: { class: "form" }) do |f| %> + <% if page.resource.errors.any? %> +
+

+ <%= t( + "administrate.form.errors", + pluralized_errors: pluralize(page.resource.errors.count, t("administrate.form.error")), + resource_name: display_resource_name(page.resource_name, singular: true) + ) %> +

+ + +
+ <% end %> + + <% page.attributes(controller.action_name).each do |title, attributes| -%> +
"> + <% if title.present? %> + <%= t "helpers.label.#{f.object_name}.#{title}", default: title %> + <% end %> + + <% attributes.each do |attribute| %> +
+ <%= render_field attribute, f: f %> + + <% hint_key = "administrate.field_hints.#{page.resource_name}.#{attribute.name}" %> + <% if I18n.exists?(hint_key) -%> +
+ <%= I18n.t(hint_key) %> +
+ <% end -%> +
+ <% end %> +
+ <% if action_name == 'edit' %> +
+
+
+ <%= sprintf('%.2f', page.resource&.portfolio&.cash_balance || 0)%> +
+
+
+
+
+ +
+
+ <% end %> + <% end -%> + +
+ <%= f.submit %> +
+<% end %> diff --git a/config/environments/test.rb b/config/environments/test.rb index adbb4a6..b8825b2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -61,4 +61,7 @@ # Raise error when a before_action's only/except options reference missing actions config.action_controller.raise_on_missing_callback_actions = true + + # TODO: revisit + config.assets.css_compressor = nil end diff --git a/config/routes.rb b/config/routes.rb index 577bea4..a836d68 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,7 +7,7 @@ # resources :portfolios # resources :portfolio_stocks - # resources :portfolio_transactions + resources :portfolio_transactions, except: [:index] resources :students resources :teachers resources :users diff --git a/db/migrate/20240531145714_add_amount_to_portfolio_transactions.rb b/db/migrate/20240531145714_add_amount_to_portfolio_transactions.rb new file mode 100644 index 0000000..508d88d --- /dev/null +++ b/db/migrate/20240531145714_add_amount_to_portfolio_transactions.rb @@ -0,0 +1,5 @@ +class AddAmountToPortfolioTransactions < ActiveRecord::Migration[7.1] + def change + add_column :portfolio_transactions, :amount, :decimal, precision: 8, scale: 2, null: false + end +end diff --git a/db/migrate/20240531151002_remove_cash_balance_from_portfolios.rb b/db/migrate/20240531151002_remove_cash_balance_from_portfolios.rb new file mode 100644 index 0000000..67cffd3 --- /dev/null +++ b/db/migrate/20240531151002_remove_cash_balance_from_portfolios.rb @@ -0,0 +1,5 @@ +class RemoveCashBalanceFromPortfolios < ActiveRecord::Migration[7.1] + def change + remove_column :portfolios, :cash_balance + end +end diff --git a/db/schema.rb b/db/schema.rb index b3274e4..8b3f071 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -53,12 +53,12 @@ t.integer "transaction_type" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.decimal "amount", precision: 8, scale: 2, null: false t.index ["portfolio_id"], name: "index_portfolio_transactions_on_portfolio_id" end create_table "portfolios", force: :cascade do |t| t.bigint "user_id", null: false - t.float "cash_balance" t.float "current_position" t.json "transactions" t.datetime "created_at", null: false diff --git a/test/controllers/admin/students_controller_test.rb b/test/controllers/admin/students_controller_test.rb new file mode 100644 index 0000000..759283f --- /dev/null +++ b/test/controllers/admin/students_controller_test.rb @@ -0,0 +1,29 @@ +require 'test_helper' + +class Admin::StudentsControllerTest < ActionDispatch::IntegrationTest + setup do + @student = users(:one) + end + + test 'should update student email' do + @student.update_attribute(:email, 'nottest@nottest.com') + + patch admin_student_url(@student), params: { student: { email: 'test@test.com' } } + @student.reload + assert_equal 'test@test.com', @student.email + assert_redirected_to admin_student_url(@student) + end + + test 'given a add_fund_amount, creates a transaction' do + assert_difference('PortfolioTransaction.count', 1) do + patch admin_student_url(@student), params: { student: { add_fund_amount: '10.50' } } + end + + transaction = PortfolioTransaction.last + + assert_equal 10.50, transaction.amount + assert_equal @student.reload.portfolio.portfolio_transactions.last, transaction + + assert_redirected_to admin_student_url(@student) + end +end diff --git a/test/fixtures/portfolio_transactions.yml b/test/fixtures/portfolio_transactions.yml index 8e51926..8dc242f 100644 --- a/test/fixtures/portfolio_transactions.yml +++ b/test/fixtures/portfolio_transactions.yml @@ -2,8 +2,10 @@ one: portfolio: one + amount: 10.00 transaction_type: 1 two: - portfolio: two + portfolio: one + amount: 15.50 transaction_type: 1 diff --git a/test/fixtures/portfolios.yml b/test/fixtures/portfolios.yml index 156f883..f1c9c49 100644 --- a/test/fixtures/portfolios.yml +++ b/test/fixtures/portfolios.yml @@ -2,12 +2,10 @@ one: user: one - cash_balance: 1.5 current_position: 1.5 - transactions: + transactions: two: user: two - cash_balance: 1.5 current_position: 1.5 - transactions: + transactions: diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 82fa2a2..dc3b46e 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -4,10 +4,12 @@ # model remove the "{}" from the fixture names and add the columns immediately # below each fixture, per the syntax in the comments below # -one: - username: "lkfjd" +one: + username: abc123 + classroom: one # column: value # two: - username: "dglk" + username: def123 + classroom: two # column: value diff --git a/test/fixtures/years.yml b/test/fixtures/years.yml index 338db83..e066652 100644 --- a/test/fixtures/years.yml +++ b/test/fixtures/years.yml @@ -1,7 +1,7 @@ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html one: - year: 1 + year: 2023 two: - year: 2 + year: 2024 diff --git a/test/models/portfolio_test.rb b/test/models/portfolio_test.rb index 1c329a7..692d1f2 100644 --- a/test/models/portfolio_test.rb +++ b/test/models/portfolio_test.rb @@ -1,8 +1,9 @@ -require "test_helper" +require 'test_helper' class PortfolioTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end - #TODO: Add tests for the Portfolio model + fixtures :portfolios + test '#cash_balance' do + portfolio = portfolios(:one) + assert_equal BigDecimal('25.50'), portfolio.cash_balance + end end