diff --git a/app/controllers/mm/users_controller.rb b/app/controllers/mm/users_controller.rb index 875ca724..793a9ff1 100644 --- a/app/controllers/mm/users_controller.rb +++ b/app/controllers/mm/users_controller.rb @@ -1,7 +1,7 @@ class Mm::UsersController < Base::HyaccController def index - @users = User.paginate page: params[:page], per_page: current_user.slips_per_page + @users = User.includes(:employee).paginate page: params[:page], per_page: current_user.slips_per_page end def show @@ -43,16 +43,50 @@ def update begin @user.transaction do @user.update!(user_params) - - flash[:notice] = 'ユーザを更新しました。' - render 'common/reload' end + + flash[:notice] = 'ユーザを更新しました。' + render 'common/reload' rescue => e handle(e) render :edit end end + def grant_admin + @user = User.find(params[:id]) + begin + @user.transaction do + @user.update!(admin: true) + end + + flash[:notice] = '管理権限を付与しました。' + redirect_after_admin_change + rescue => e + handle(e) + redirect_after_admin_change + end + end + + def revoke_admin + @user = User.find(params[:id]) + begin + @user.transaction do + @user.update!(admin: false) + end + + flash[:notice] = '管理権限を解除しました。' + if current_user.id == @user.id + redirect_to root_path + else + redirect_after_admin_change + end + rescue => e + handle(e) + redirect_after_admin_change + end + end + def destroy id = params[:id].to_i user = User.find(id) @@ -93,6 +127,7 @@ def user_params :zip_code, :address, :sex, :business_office_id, :birth, :my_number ] ] + permitted << :admin if action_name == 'create' ret = params.require(:user).permit(permitted) @@ -103,7 +138,7 @@ def user_params ret end - def employee_params + def employee_params return {} unless params.dig(:employee) permitted = [ branch_employees_attributes: [ @@ -111,4 +146,13 @@ def employee_params ] params.require(:employee).permit(permitted) end -end \ No newline at end of file + + def redirect_after_admin_change + if current_company.personal? + redirect_to action: 'index' + else + redirect_to mm_employees_path + end + end +end + diff --git a/app/models/employee.rb b/app/models/employee.rb index a111da94..2c535d0e 100644 --- a/app/models/employee.rb +++ b/app/models/employee.rb @@ -72,6 +72,10 @@ def disabled_name DISABLED_TYPES[disabled] end + def user_loginable? + user.present? && user.loginable?(self) + end + def fullname(separetor = ' ') "#{last_name}#{separetor}#{first_name}" end diff --git a/app/models/employee_finder.rb b/app/models/employee_finder.rb index aaaeed8c..fc69ca35 100644 --- a/app/models/employee_finder.rb +++ b/app/models/employee_finder.rb @@ -10,7 +10,9 @@ def disabled_types end def list - Employee.where(conditions).paginate(page: page, per_page: per_page) + Employee.where(conditions) + .includes(:user) + .paginate(page: page, per_page: per_page) end private diff --git a/app/models/user.rb b/app/models/user.rb index a0086c05..cfed8c8d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -24,14 +24,22 @@ class User < ApplicationRecord ) } + def loginable?(employee_record = nil) + emp = employee_record || employee + !deleted? && !emp.disabled? && !emp.deleted? + end + def active_admin? - admin? && !deleted? && !employee.disabled? && !employee.deleted? + admin? && loginable? end def would_remove_last_active_admin? - return false unless admin? - return false if deleted_in_database - return false if employee.deleted_in_database || employee.disabled_in_database + was_active = admin_in_database && !deleted_in_database && + !employee.deleted_in_database && !employee.disabled_in_database + return false unless was_active + + will_be_active = admin? && !deleted? && !employee.disabled? && !employee.deleted? + return false if will_be_active company_active_admins = self.class.active_admins.where(employees: { company_id: employee.company_id }) company_active_admins.where.not(id: id).none? diff --git a/app/models/validators/last_active_admin_validator.rb b/app/models/validators/last_active_admin_validator.rb index 59ab6faf..26225285 100644 --- a/app/models/validators/last_active_admin_validator.rb +++ b/app/models/validators/last_active_admin_validator.rb @@ -17,10 +17,17 @@ def validate(record) private def validate_user(user) - return unless user.will_save_change_to_deleted? && user.deleted? + becoming_inactive = (user.will_save_change_to_deleted? && user.deleted?) || + (user.will_save_change_to_admin? && !user.admin?) + return unless becoming_inactive return unless user.would_remove_last_active_admin? - user.errors.add(:base, HyaccErrors::ERR_LAST_ACTIVE_ADMIN_DELETE) + error = if user.will_save_change_to_admin? && !user.admin? + HyaccErrors::ERR_LAST_ACTIVE_ADMIN_REVOKE + else + HyaccErrors::ERR_LAST_ACTIVE_ADMIN_DELETE + end + user.errors.add(:base, error) end def validate_employee(employee) diff --git a/app/utils/hyacc_errors.rb b/app/utils/hyacc_errors.rb index 148415f7..7cd52a03 100644 --- a/app/utils/hyacc_errors.rb +++ b/app/utils/hyacc_errors.rb @@ -30,6 +30,7 @@ module HyaccErrors ERR_ILLEGAL_TAX_DETAIL = "消費税は税抜経理方式の場合のみ指定可能です。" ERR_LAST_ACTIVE_ADMIN_DELETE = "ログイン可能な管理権限を持つユーザーが0人になるため、削除できません。" ERR_LAST_ACTIVE_ADMIN_DISABLE = "ログイン可能な管理権限を持つユーザーが0人になるため、無効にできません。" + ERR_LAST_ACTIVE_ADMIN_REVOKE = "ログイン可能な管理権限を持つユーザーが0人になるため、管理権限を解除できません。" ERR_NO_CAPITATION_TARGET_BRANCH_EXISTS = "人頭割で配賦できる部門がありません。" ERR_NOT_JOURNALIZABLE_ACCOUNT = "仕訳が登録できない勘定科目が指定されています。" ERR_OVERRIDE_NEEDED = "サブクラスでの実装が必要です。" diff --git a/app/views/mm/employees/index.html.erb b/app/views/mm/employees/index.html.erb index b8d5b97c..0de6157c 100644 --- a/app/views/mm/employees/index.html.erb +++ b/app/views/mm/employees/index.html.erb @@ -11,6 +11,7 @@ <%= Employee.human_attribute_name :social_insurance_birthday %> 業務経歴 状態 + <%= User.human_attribute_name :admin %> @@ -30,6 +31,13 @@ <%= e.disabled_name %> + <% if e.user_loginable? %> + <%= e.user.admin? ? 'あり' : 'なし' %> + <% else %> + - + <% end %> + + <%= link_to '編集', edit_mm_employee_path(e), remote: true, class: 'btn btn-sm btn-light' %> <% if e.disabled? %> <%= link_to '削除', mm_employee_path(e), data: {confirm: '削除します。よろしいですか?'}, method: 'delete', class: 'btn btn-sm btn-danger' %> diff --git a/app/views/mm/employees/show.js.erb b/app/views/mm/employees/show.js.erb index 7e943a63..95ac2bda 100644 --- a/app/views/mm/employees/show.js.erb +++ b/app/views/mm/employees/show.js.erb @@ -15,4 +15,36 @@ options.buttons.push( } ); -hyacc.current_dialog(options).show('<%=j render 'show' %>'); \ No newline at end of file +<% if @e.user_loginable? %> + <% if @e.user.admin? %> + options.buttons.push({ + text: '管理権限を解除', + class: 'btn btn-light', + click: function() { + if (confirm('管理権限を解除します。よろしいですか?')) { + hyacc.current_dialog().close(); + var form = $('
', { method: 'post', action: '<%= revoke_admin_mm_user_path(@e.user) %>' }); + form.append($('', { type: 'hidden', name: 'authenticity_token', value: '<%= form_authenticity_token %>' })); + $('body').append(form); + form.submit(); + } + } + }); + <% else %> + options.buttons.push({ + text: '管理権限を付与', + class: 'btn btn-light', + click: function() { + if (confirm('管理権限を付与します。よろしいですか?')) { + hyacc.current_dialog().close(); + var form = $('', { method: 'post', action: '<%= grant_admin_mm_user_path(@e.user) %>' }); + form.append($('', { type: 'hidden', name: 'authenticity_token', value: '<%= form_authenticity_token %>' })); + $('body').append(form); + form.submit(); + } + } + }); + <% end %> +<% end %> + +hyacc.current_dialog(options).show('<%=j render 'show' %>'); diff --git a/app/views/mm/users/_form.html.erb b/app/views/mm/users/_form.html.erb index 79911462..754a106a 100644 --- a/app/views/mm/users/_form.html.erb +++ b/app/views/mm/users/_form.html.erb @@ -28,6 +28,10 @@ <%= f.label :password %> <%= f.password_field :password, autocomplete: "new-password", class: 'form-control form-control-sm' %> + + <%= f.label :admin %> + <%= f.check_box :admin, class: 'form-check-input' %> + <% end %> <%= f.fields_for "employee_attributes", @user.employee do |f| %> diff --git a/app/views/mm/users/index.html.erb b/app/views/mm/users/index.html.erb index e41f5eb1..b93cda8f 100644 --- a/app/views/mm/users/index.html.erb +++ b/app/views/mm/users/index.html.erb @@ -6,6 +6,7 @@ <%= User.human_attribute_name :login_id %> 氏名 + <%= User.human_attribute_name :admin %> 状態 @@ -15,6 +16,13 @@ <%= user.login_id %> <%= link_to user.employee.fullname, {action: 'show', id: user.id}, class: 'show', remote: true %> + + <% if user.loginable? %> + <%= user.admin? ? 'あり' : 'なし' %> + <% else %> + - + <% end %> + <%= HyaccConst::DELETED_TYPES[user.deleted] %> <%= link_to '編集', {action: 'edit', id: user.id}, remote: true, class: 'btn btn-sm btn-light' %> diff --git a/app/views/mm/users/show.js.erb b/app/views/mm/users/show.js.erb new file mode 100644 index 00000000..11fc9133 --- /dev/null +++ b/app/views/mm/users/show.js.erb @@ -0,0 +1,39 @@ +var options = { + title: '<%= title.present? ? title + ' 参照' : '参照' %>' +}; + +options.buttons = []; + +<% if @user.loginable? %> + <% if @user.admin? %> + options.buttons.push({ + text: '管理権限を解除', + class: 'btn btn-light', + click: function() { + if (confirm('管理権限を解除します。よろしいですか?')) { + hyacc.current_dialog().close(); + var form = $('', { method: 'post', action: '<%= revoke_admin_mm_user_path(@user) %>' }); + form.append($('', { type: 'hidden', name: 'authenticity_token', value: '<%= form_authenticity_token %>' })); + $('body').append(form); + form.submit(); + } + } + }); + <% else %> + options.buttons.push({ + text: '管理権限を付与', + class: 'btn btn-light', + click: function() { + if (confirm('管理権限を付与します。よろしいですか?')) { + hyacc.current_dialog().close(); + var form = $('', { method: 'post', action: '<%= grant_admin_mm_user_path(@user) %>' }); + form.append($('', { type: 'hidden', name: 'authenticity_token', value: '<%= form_authenticity_token %>' })); + $('body').append(form); + form.submit(); + } + } + }); + <% end %> +<% end %> + +hyacc.current_dialog(options).show('<%=j render 'show' %>'); diff --git a/config/locales/hyacc.ja.yml b/config/locales/hyacc.ja.yml index 479b031f..d59821ef 100644 --- a/config/locales/hyacc.ja.yml +++ b/config/locales/hyacc.ja.yml @@ -281,6 +281,7 @@ ja: user: account_count_of_frequencies: 勘定科目の優先表示数 + admin: 管理権限 code: "コード" password: パスワード first_name: "名前" diff --git a/config/routes/mm.rb b/config/routes/mm.rb index c2d4ff59..fd86f3ea 100644 --- a/config/routes/mm.rb +++ b/config/routes/mm.rb @@ -66,6 +66,10 @@ collection do get 'add_branch' end + member do + post 'grant_admin' + post 'revoke_admin' + end end end end diff --git a/test/controllers/mm/users_controller_test.rb b/test/controllers/mm/users_controller_test.rb index 2a2f5385..59e8d886 100644 --- a/test/controllers/mm/users_controller_test.rb +++ b/test/controllers/mm/users_controller_test.rb @@ -43,8 +43,9 @@ def test_create_with_valid_branch_employees_params assert_response :success - u = User.find_by_login_id('zero') + u = assigns(:user) assert_not_nil u + assert_not u.admin? assert_not_nil u.employee assert_equal 'test_create', u.employee.last_name assert_equal 'a', u.employee.first_name @@ -54,6 +55,60 @@ def test_create_with_valid_branch_employees_params assert_equal 2, u.employee.default_branch.id end + def test_createでadminを付与できる + sign_in admin + + params = user_params_with_valid_branch_employees + params[:user][:admin] = true + + post :create, xhr: true, params: params + + assert_response :success + assert assigns(:user).admin? + end + + def test_grant_admin + sign_in admin + + post :grant_admin, params: {id: user.id} + + assert_redirected_to mm_employees_path + assert user.reload.admin? + end + + def test_revoke_admin + other_admin = User.find(6) + other_admin.update!(admin: true) + + sign_in admin + post :revoke_admin, params: {id: other_admin.id} + + assert_redirected_to mm_employees_path + assert_not other_admin.reload.admin? + end + + def test_ログイン可能な管理権限を持つユーザーが1人のとき_adminを解除できない + sign_in admin + + post :revoke_admin, params: {id: admin.id} + + assert_redirected_to mm_employees_path + assert admin.reload.admin? + assert flash[:is_error_message] + assert_equal ERR_LAST_ACTIVE_ADMIN_REVOKE, flash[:notice] + end + + def test_ログイン可能な管理権限を持つユーザーが2人のとき_自分自身のadminを解除できる + other_admin = User.find(6) + other_admin.update!(admin: true) + + sign_in admin + post :revoke_admin, params: {id: admin.id} + + assert_redirected_to root_path + assert_not admin.reload.admin? + end + def test_create_with_invalid_branch_employees_params sign_in admin diff --git a/test/models/employee_test.rb b/test/models/employee_test.rb index 4bfaeda1..1793c1f9 100644 --- a/test/models/employee_test.rb +++ b/test/models/employee_test.rb @@ -24,4 +24,20 @@ def test_representative_or_family_type_name assert_nil Employee.find(1).representative_or_family_type_name end + def test_user_loginable_ユーザなしはfalse + e = Employee.new + assert_not e.user_loginable? + end + + def test_user_loginable_ログイン可能なユーザがいればtrue + e = user.employee + assert e.user_loginable? + assert e.user.loginable?(e) + end + + def test_user_loginable_無効化済み従業員はfalse + e = users(:disabled_employee_user).employee + assert_not e.user_loginable? + end + end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 36d75be3..9b829373 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -35,6 +35,20 @@ def test_削除済み従業員に紐づくユーザはログイン不可 assert_not users(:deleted_employee_user).active_for_authentication? end + def test_loginable_削除済みユーザはfalse + assert_not users(:deleted_user).loginable? + end + + def test_loginable_無効化済み従業員に紐づくユーザはfalse + assert_not users(:disabled_employee_user).loginable? + end + + def test_active_admin_無効化済みユーザでadminフラグがtrueでもfalse + u = users(:disabled_employee_user) + u.admin = true + assert_not u.active_admin? + end + def test_would_remove_last_active_admin_非adminはfalse assert_not user.admin? assert_not user.would_remove_last_active_admin? diff --git a/test/models/validators/last_active_admin_validator_test.rb b/test/models/validators/last_active_admin_validator_test.rb index 875a607b..d9ba54bc 100644 --- a/test/models/validators/last_active_admin_validator_test.rb +++ b/test/models/validators/last_active_admin_validator_test.rb @@ -79,4 +79,24 @@ def test_ログイン可能な管理権限を持つユーザーが2人のとき_ assert other_admin.employee.valid? end + def test_ログイン可能な管理権限を持つユーザーが1人のとき_adminを解除できない + assert admin.admin? + assert admin.active_admin? + + admin.admin = false + assert admin.invalid? + assert_equal ERR_LAST_ACTIVE_ADMIN_REVOKE, admin.errors[:base].first + end + + def test_ログイン可能な管理権限を持つユーザーが2人のとき_adminを解除できる + other_admin = User.find(6) + + other_admin.update!(admin: true) + assert admin.admin? + assert other_admin.admin? + + admin.admin = false + assert admin.valid? + end + end