Skip to content

Commit 1c39547

Browse files
authored
Merge pull request #972 from rubyforgood/issue-937-938_admin-school-years-stocks
Issue 937 938 admin school years stocks
2 parents c03d71d + 8878678 commit 1c39547

35 files changed

+1412
-89
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
module AdminV2
4+
class SchoolYearsController < BaseController
5+
before_action :set_school_year, only: %i[show edit update destroy]
6+
7+
def index
8+
@school_years = apply_sorting(SchoolYear.includes(:school, :year), default: "id")
9+
10+
@breadcrumbs = [
11+
{ label: "School Years" }
12+
]
13+
end
14+
15+
def show
16+
@breadcrumbs = [
17+
{ label: "School Years", path: admin_v2_school_years_path },
18+
{ label: @school_year.to_s }
19+
]
20+
end
21+
22+
def new
23+
@school_year = SchoolYear.new
24+
25+
@breadcrumbs = [
26+
{ label: "School Years", path: admin_v2_school_years_path },
27+
{ label: "New School Year" }
28+
]
29+
end
30+
31+
def edit
32+
@breadcrumbs = [
33+
{ label: "School Years", path: admin_v2_school_years_path },
34+
{ label: @school_year.to_s, path: admin_v2_school_year_path(@school_year) },
35+
{ label: "Edit" }
36+
]
37+
end
38+
39+
def create
40+
school = School.find(school_year_params[:school_id])
41+
year = Year.find(school_year_params[:year_id])
42+
43+
@school_year = SchoolYearCreationService.new(school: school, year: year).call
44+
45+
if @school_year.persisted?
46+
redirect_to admin_v2_school_year_path(@school_year), notice: t(".notice")
47+
else
48+
@breadcrumbs = [
49+
{ label: "School Years", path: admin_v2_school_years_path },
50+
{ label: "New School Year" }
51+
]
52+
render :new, status: :unprocessable_content
53+
end
54+
rescue ActiveRecord::RecordInvalid => e
55+
@school_year = e.record
56+
@breadcrumbs = [
57+
{ label: "School Years", path: admin_v2_school_years_path },
58+
{ label: "New School Year" }
59+
]
60+
render :new, status: :unprocessable_content
61+
end
62+
63+
def update
64+
if @school_year.update(school_year_params)
65+
redirect_to admin_v2_school_year_path(@school_year), notice: t(".notice")
66+
else
67+
@breadcrumbs = [
68+
{ label: "School Years", path: admin_v2_school_years_path },
69+
{ label: @school_year.to_s, path: admin_v2_school_year_path(@school_year) },
70+
{ label: "Edit" }
71+
]
72+
render :edit, status: :unprocessable_content
73+
end
74+
end
75+
76+
def destroy
77+
@school_year.destroy
78+
redirect_to admin_v2_school_years_path, notice: t(".notice")
79+
rescue ActiveRecord::DeleteRestrictionError
80+
redirect_to admin_v2_school_year_path(@school_year), alert: t(".delete_restricted")
81+
end
82+
83+
private
84+
85+
def set_school_year
86+
@school_year = SchoolYear.find(params.expect(:id))
87+
end
88+
89+
def school_year_params
90+
params.expect(school_year: %i[school_id year_id])
91+
end
92+
end
93+
end
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# frozen_string_literal: true
2+
3+
module AdminV2
4+
class StocksController < BaseController
5+
before_action :set_stock, only: %i[show edit update destroy]
6+
7+
def index
8+
@stocks = apply_sorting(Stock.all, default: "ticker")
9+
10+
@breadcrumbs = [
11+
{ label: "Stocks" }
12+
]
13+
end
14+
15+
def show
16+
@breadcrumbs = [
17+
{ label: "Stocks", path: admin_v2_stocks_path },
18+
{ label: @stock.ticker }
19+
]
20+
end
21+
22+
def new
23+
@stock = Stock.new
24+
25+
@breadcrumbs = [
26+
{ label: "Stocks", path: admin_v2_stocks_path },
27+
{ label: "New Stock" }
28+
]
29+
end
30+
31+
def edit
32+
@breadcrumbs = [
33+
{ label: "Stocks", path: admin_v2_stocks_path },
34+
{ label: @stock.ticker, path: admin_v2_stock_path(@stock) },
35+
{ label: "Edit" }
36+
]
37+
end
38+
39+
def create
40+
@stock = Stock.new(stock_params)
41+
42+
if @stock.save
43+
redirect_to admin_v2_stock_path(@stock), notice: t(".notice")
44+
else
45+
@breadcrumbs = [
46+
{ label: "Stocks", path: admin_v2_stocks_path },
47+
{ label: "New Stock" }
48+
]
49+
render :new, status: :unprocessable_content
50+
end
51+
end
52+
53+
def update
54+
if @stock.update(stock_params)
55+
redirect_to admin_v2_stock_path(@stock), notice: t(".notice")
56+
else
57+
@breadcrumbs = [
58+
{ label: "Stocks", path: admin_v2_stocks_path },
59+
{ label: @stock.ticker, path: admin_v2_stock_path(@stock) },
60+
{ label: "Edit" }
61+
]
62+
render :edit, status: :unprocessable_content
63+
end
64+
end
65+
66+
def destroy
67+
@stock.destroy
68+
redirect_to admin_v2_stocks_path, notice: t(".notice")
69+
rescue ActiveRecord::DeleteRestrictionError => e
70+
redirect_to admin_v2_stocks_path, alert: "Cannot delete stock: #{e.message}"
71+
end
72+
73+
private
74+
75+
def set_stock
76+
@stock = Stock.find(params.expect(:id))
77+
end
78+
79+
# rubocop:disable Metrics/MethodLength
80+
def stock_params
81+
params.expect(stock: %i[
82+
ticker
83+
company_name
84+
company_website
85+
stock_exchange
86+
description
87+
price_cents
88+
yesterday_price_cents
89+
employees
90+
industry
91+
cash_flow
92+
debt
93+
debt_to_equity
94+
profit_margin
95+
sales_growth
96+
industry_avg_debt_to_equity
97+
industry_avg_profit_margin
98+
industry_avg_sales_growth
99+
management
100+
competitor_names
101+
archived
102+
])
103+
end
104+
# rubocop:enable Metrics/MethodLength
105+
end
106+
end

app/form_builders/admin_v2/form_builder.rb

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def number_field(attribute, options = {})
4747
end
4848
end
4949

50-
# Currency field with $ prefix and proper formatting
50+
# Currency field with proper formatting
5151
# Usage: f.currency_field :price_cents, multiplier: 0.01, decimals: 2
5252
def currency_field(attribute, options = {})
5353
multiplier = options.delete(:multiplier) || 0.01
@@ -57,18 +57,13 @@ def currency_field(attribute, options = {})
5757
value = object.public_send(attribute)
5858
display_value = value ? (value * multiplier).round(decimals) : nil
5959

60-
@template.content_tag(:div, class: "relative rounded-md shadow-sm") do
61-
@template.content_tag(:div, class: "pointer-events-none absolute inset-y-0 left-0 flex items-center pl-5") do
62-
@template.content_tag(:span, "$", class: "text-gray-500 sm:text-sm")
63-
end +
64-
number_field(attribute,
65-
options.merge(
66-
value: display_value,
67-
step: (1.0 / (10**decimals)),
68-
class: "#{input_class(attribute)} pl-12",
69-
data: { currency_multiplier: (1.0 / multiplier).to_i }
70-
))
71-
end
60+
number_field(attribute,
61+
options.merge(
62+
value: display_value,
63+
step: (1.0 / (10**decimals)),
64+
class: input_class(attribute),
65+
data: { currency_multiplier: (1.0 / multiplier).to_i }
66+
))
7267
end
7368
end
7469

app/models/classroom.rb

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class Classroom < ApplicationRecord
99
has_one :school, through: :school_year
1010
has_one :year, through: :school_year
1111

12+
delegate :name, to: :school, prefix: :school
13+
delegate :name, to: :year, prefix: :year
14+
1215
has_many :users, dependent: :nullify
1316
has_many :teacher_classrooms, dependent: :destroy
1417
has_many :teachers, through: :teacher_classrooms
@@ -44,15 +47,9 @@ def continuous?(values)
4447
values.each_cons(2).all? { |a, b| b == a + 1 }
4548
end
4649

47-
def to_s
48-
if grades_display.present?
49-
"#{name} (#{grades_display})"
50-
else
51-
name
52-
end
53-
end
54-
end
50+
private
5551

56-
def grade_level
57-
errors.add(:grades, "must have at least one grade") if grades.empty?
52+
def grade_level
53+
errors.add(:grades, "must have at least one grade") if grades.empty?
54+
end
5855
end

app/models/grade_entry.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class GradeEntry < ApplicationRecord
44
belongs_to :grade_book
55
belongs_to :user
66

7+
delegate :username, to: :user
8+
79
EARNINGS_PER_DAY_ATTENDANCE = 20
810
EARNINGS_FOR_A_GRADE = 3_00
911
EARNINGS_FOR_B_GRADE = 2_00

app/models/portfolio.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class Portfolio < ApplicationRecord
66
belongs_to :user
77
validate :user_must_be_student
88

9+
delegate :username, :student?, :school_name, :trading_enabled?, to: :user
10+
911
has_many :portfolio_transactions, dependent: :destroy
1012
has_many :portfolio_stocks, dependent: :destroy
1113
has_many :stocks, through: :portfolio_stocks

app/models/school_year.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ class SchoolYear < ApplicationRecord
66
has_many :classrooms, dependent: :restrict_with_error
77
has_many :quarters, dependent: :restrict_with_error
88

9-
def to_s
10-
"#{school.name} (#{year.name})"
11-
end
9+
delegate :name, to: :school, prefix: :school
10+
delegate :name, to: :year, prefix: :year
1211
end

app/models/user.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def really_destroy!
2222
# Allow calling `user.school` (used in portfolio view) via the classroom's associated school.
2323
# This prevents undefined method errors for Student records without directly adding a belongs_to.
2424
delegate :school, to: :classroom, allow_nil: true
25+
delegate :name, to: :school, prefix: :school, allow_nil: true
26+
delegate :trading_enabled?, to: :classroom, allow_nil: true
2527

2628
has_one :portfolio, dependent: :destroy
2729
accepts_nested_attributes_for :portfolio

app/models/year.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class Year < ApplicationRecord
77

88
validates :name, presence: true, uniqueness: true
99

10+
scope :ordered_by_start_year, -> { order(Arel.sql("CAST(SUBSTRING(name FROM 1 FOR 4) AS INTEGER) DESC")) }
11+
1012
def previous_year
1113
@previous_year || Year.find_by(name: "#{start_year_value - 1} - #{start_year_value}")
1214
end

app/presenters/base_presenter.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
class BasePresenter
4+
attr_reader :object
5+
6+
def initialize(object)
7+
@object = object
8+
end
9+
10+
def method_missing(method_name, *, &)
11+
if object.respond_to?(method_name)
12+
object.public_send(method_name, *, &)
13+
else
14+
super
15+
end
16+
end
17+
18+
def respond_to_missing?(method_name, include_private = false)
19+
object.respond_to?(method_name) || super
20+
end
21+
end

0 commit comments

Comments
 (0)