Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/controllers/admin/schools_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ def reopen
redirect_to admin_school_path(requested_resource)
end

def archive
if requested_resource.archive
flash[:notice] = t('administrate.controller.archive_school.success')
else
flash[:error] = requested_resource.errors.full_messages.to_sentence.presence || t('administrate.controller.archive_school.error')
end

redirect_to admin_school_path(requested_resource)
end

def default_sorting_attribute
:created_at
end
Expand Down
4 changes: 4 additions & 0 deletions app/dashboards/school_dashboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ class SchoolDashboard < Administrate::BaseDashboard
lessons: Field::HasMany,
projects: Field::HasMany,
roles: SchoolRolesField,
student_count: Field::Number,
reference: Field::String,
district_name: Field::String,
district_nces_id: Field::String,
school_roll_number: Field::String,
verified_at: Field::DateTime,
rejected_at: Field::DateTime,
archived_at: Field::DateTime,
created_at: Field::DateTime,
updated_at: Field::DateTime,
user_origin: EnumField
Expand Down Expand Up @@ -64,6 +66,7 @@ class SchoolDashboard < Administrate::BaseDashboard
user_origin
creator
roles
student_count
creator_role
creator_department
reference
Expand All @@ -84,6 +87,7 @@ class SchoolDashboard < Administrate::BaseDashboard
updated_at
verified_at
rejected_at
archived_at
].freeze

# FORM_ATTRIBUTES
Expand Down
6 changes: 6 additions & 0 deletions app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class Role < ApplicationRecord
validate :students_cannot_have_additional_roles
validate :users_can_only_have_roles_in_one_school

default_scope { where(archived_at: nil) }

has_paper_trail(
meta: {
meta_school_id: ->(cm) { cm.school&.id }
Expand All @@ -18,6 +20,10 @@ class Role < ApplicationRecord

after_commit :do_salesforce_sync, on: %i[create update], if: -> { FeatureFlags.salesforce_sync? && !student? }

def archive!
update!(archived_at: Time.zone.now)
end

private

def students_cannot_have_additional_roles
Expand Down
47 changes: 40 additions & 7 deletions app/models/school.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class School < ApplicationRecord

enum :user_origin, { for_education: 0, experience_cs: 1 }, default: :for_education, validate: true

scope :active, -> { where(rejected_at: nil, archived_at: nil) }

validates :name, presence: true
validates :website, presence: true, format: { with: VALID_URL_REGEX, message: I18n.t('validations.school.website') }
validates :address_line_1, presence: true
Expand All @@ -20,22 +22,22 @@ class School < ApplicationRecord
validates :postal_code, presence: true
validates :country_code, presence: true, inclusion: { in: ISO3166::Country.codes }
validates :reference,
uniqueness: { conditions: -> { where(rejected_at: nil) }, case_sensitive: false, allow_blank: true, message: I18n.t('validations.school.reference_urn_exists') },
uniqueness: { conditions: -> { active }, case_sensitive: false, allow_blank: true, message: I18n.t('validations.school.reference_urn_exists') },
format: { with: /\A\d{5,6}\z/, allow_nil: true, message: I18n.t('validations.school.reference') },
if: :united_kingdom?, on: :create, unless: :rejected?
if: :united_kingdom?, on: :create, unless: :hidden?
validates :district_nces_id,
format: { with: /\A\d{7}\z/, allow_nil: true, message: I18n.t('validations.school.district_nces_id') },
if: :united_states?, on: :create
validates :district_name, presence: true, if: :united_states?
validates :school_roll_number,
uniqueness: { conditions: -> { where(rejected_at: nil) }, case_sensitive: false, allow_blank: true, message: I18n.t('validations.school.school_roll_number_exists') },
uniqueness: { conditions: -> { active }, case_sensitive: false, allow_blank: true, message: I18n.t('validations.school.school_roll_number_exists') },
format: { with: /\A[0-9]+[A-Z]+\z/, allow_nil: true, message: I18n.t('validations.school.school_roll_number') },
presence: true, on: :create, if: :ireland?, unless: :rejected?
presence: true, on: :create, if: :ireland?, unless: :hidden?
validates :creator_id,
presence: true,
uniqueness: {
conditions: -> { where(rejected_at: nil) }
}, unless: :rejected?
conditions: -> { active }
}, unless: :hidden?
validates :creator_agree_authority, presence: true, acceptance: true
validates :creator_agree_terms_and_conditions, presence: true, acceptance: true
validates :creator_agree_responsible_safeguarding, presence: true, acceptance: true
Expand All @@ -44,6 +46,7 @@ class School < ApplicationRecord
validates :code,
uniqueness: { allow_nil: true },
format: { with: /\d\d-\d\d-\d\d/, allow_nil: true }
validate :cannot_archive_with_students, on: :archive
validate :verified_at_cannot_be_changed
validate :code_cannot_be_changed

Expand All @@ -59,7 +62,7 @@ class School < ApplicationRecord
after_commit -> { do_salesforce_sync(is_create: false) }, on: :update, if: -> { FeatureFlags.salesforce_sync? }

def self.find_for_user!(user)
school = Role.find_by(user_id: user.id)&.school || find_by(creator_id: user.id, rejected_at: nil)
school = Role.find_by(user_id: user.id)&.school || active.find_by(creator_id: user.id)
raise ActiveRecord::RecordNotFound unless school

school
Expand All @@ -82,6 +85,10 @@ def verify!
update!(verified_at: Time.zone.now)
end

def hidden?
rejected? || archived?
end

def generate_code!
return code if code.present?

Expand All @@ -103,6 +110,28 @@ def reopen
update(rejected_at: nil)
end

def archive
return true if archived?

transaction do
self.archived_at = Time.zone.now
save!(context: :archive)
roles.where(role: %i[owner teacher]).find_each(&:archive!)
end

true
rescue ActiveRecord::RecordInvalid
false
end

def archived?
archived_at.present?
end

def student_count
roles.student.count
end

def postal_code=(str)
super(str.to_s.upcase)
end
Expand Down Expand Up @@ -155,6 +184,10 @@ def normalize_school_roll_number
self.school_roll_number = (school_roll_number.presence&.upcase)
end

def cannot_archive_with_students
errors.add(:base, 'Cannot archive a school with students') if roles.student.exists?
end

def verified_at_cannot_be_changed
errors.add(:verified_at, 'cannot be changed after verification') if verified_at_was.present? && verified_at_changed?
end
Expand Down
11 changes: 11 additions & 0 deletions app/views/admin/schools/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ as well as a link to its edit page.
style: "background-color: darkorange; margin: 5px;",
data: { confirm: t("administrate.actions.confirm_reopen_school") }
) unless page.resource.verified? || !page.resource.rejected? %>

<%= button_to(
t("administrate.actions.archive_school"),
archive_admin_school_path(page.resource),
class: "button button--archive",
method: :patch,
style: "background-color: firebrick; margin: 5px;",
form: {
onsubmit: "return confirm('#{j(t("administrate.actions.confirm_archive_school"))}')"
}
) unless page.resource.archived? %>
</div>
</header>

Expand Down
5 changes: 5 additions & 0 deletions config/locales/admin/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ en:
confirm_verify_school: Are you sure you want to verify this school?
reopen_school: Reopen School
confirm_reopen_school: Are you sure you want to reopen this school?
archive_school: Archive School
confirm_archive_school: Are you sure you want to archive this school?
controller:
verify_school:
success: "Successfully verified."
error: "There was an error verifying the school"
reopen_school:
success: "Successfully reopened."
error: "There was an error reopening the school"
archive_school:
success: "Successfully archived."
error: "There was an error archiving the school"
activerecord:
attributes:
school:
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
member do
post :verify
patch :reopen
patch :archive
end
end

Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20260703145125_add_archived_at_to_schools.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddArchivedAtToSchools < ActiveRecord::Migration[8.1]
def change
add_column :schools, :archived_at, :datetime
end
end
12 changes: 12 additions & 0 deletions db/migrate/20260703150521_update_school_index_conditions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class UpdateSchoolIndexConditions < ActiveRecord::Migration[8.1]
def change
remove_index :schools, :creator_id
add_index :schools, :creator_id, unique: true, where: "rejected_at IS NULL AND archived_at IS NULL"

remove_index :schools, :reference
add_index :schools, :reference, unique: true, where: "rejected_at IS NULL AND archived_at IS NULL"

remove_index :schools, :school_roll_number
add_index :schools, :school_roll_number, unique: true, where: "rejected_at IS NULL AND archived_at IS NULL"
end
end
5 changes: 5 additions & 0 deletions db/migrate/20260703151018_add_archived_at_to_roles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddArchivedAtToRoles < ActiveRecord::Migration[8.1]
def change
add_column :roles, :archived_at, :datetime
end
end
10 changes: 6 additions & 4 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions spec/features/admin/schools_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
expect(response.body).to include(I18n.t('administrate.actions.verify_school'))
end

it 'includes link to archive school' do
expect(response.body).to include(I18n.t('administrate.actions.archive_school'))
end

it 'does not include a link to search for this school by its ZIP code in the NCES public schools database' do
expect(response.body).not_to include('Search for this school in the NCES database')
end
Expand Down Expand Up @@ -201,6 +205,34 @@
end
end

describe 'PUT #archive' do
let(:creator) { create(:user) }
let(:school) { create(:school, creator_id: creator.id) }

before do
stub_user_info_api_for(creator)
end

it 'marks the school as archived' do
patch archive_admin_school_path(school)
expect(school.reload).to be_archived
end

it 'displays the validation error when the school has students' do
create(:student_role, school:)

patch archive_admin_school_path(school)
follow_redirect!

expect(response.body).to include('Cannot archive a school with students')
end

it 'redirects to school path' do
patch archive_admin_school_path(school)
expect(response).to redirect_to(admin_school_path(school))
end
end

private

def sign_in_as(user)
Expand Down
Loading
Loading