diff --git a/app/controllers/allocations_controller.rb b/app/controllers/allocations_controller.rb index 415dc81..de66709 100644 --- a/app/controllers/allocations_controller.rb +++ b/app/controllers/allocations_controller.rb @@ -25,30 +25,13 @@ def upload_review api :POST, '/allocations?membership_id&amount' def create group = Group.find(allocation_params[:group_id]) - user = User.find(allocation_params[:user_id]) - amount = allocation_params[:amount] notify = allocation_params[:notify] params[:allocation].delete(:notify) from_group_account = allocation_params[:from_group_account] params[:allocation].delete(:from_group_account) render status: 403, nothing: true and return unless current_user.is_admin_for?(group) - allocation = Allocation.new(allocation_params) - if allocation.save - if from_group_account - from_account_id = group.status_account_id - end - m = Membership.find_by(group_id: allocation_params[:group_id], member_id: allocation_params[:user_id]) - Transaction.create!({ - datetime: allocation.created_at, - from_account_id: from_account_id || m.incoming_account_id, - to_account_id: m.status_account_id, - user_id: current_user.id, - amount: amount - }) - if notify && (amount > 0) - UserMailer.notify_member_that_they_received_allocation(admin: current_user, member: user, group: group, amount: amount).deliver_later - end + if (allocation = AllocationService.create_allocation(allocation_params, notify, current_user)) render json: [allocation], status: 201 else render status: 400, nothing: true diff --git a/app/extras/cobudget_cleanup.rb b/app/extras/cobudget_cleanup.rb new file mode 100644 index 0000000..a2e2cb1 --- /dev/null +++ b/app/extras/cobudget_cleanup.rb @@ -0,0 +1,9 @@ +class CobudgetCleanup + def self.archived_members_with_funds! + if DateTime.now.utc.hour == 7 + Group.find_each do |g| + g.cleanup_archived_members_with_funds(user) + end + end + end +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index b552258..1d94fba 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -106,9 +106,20 @@ def notify_admins_funds_are_returned_to_group_account(admin:, bucket:, done_by:, subject: "Funds from cancelled bucket have been returned to group account") end + def notify_admins_archived_member_funds(admin: , group: ,memberlist: ) + @memberlist = memberlist + @group = group + @group_user = group.ensure_group_user_exist() + mail(to: admin, + from: "Cobudget Updates ", + subject: "Funds from archived members is returned to group account") + end + def check_transactions_email - mail(to: "devops@greaterthan.finance", - from: "Cobudget Updates ", - subject: "DB transactions consistency check") + if Rails.configuration.respond_to?('devops_user') + mail(to: Rails.configuration.devops_user, + from: "Cobudget Updates ", + subject: "DB transactions consistency check") + end end end diff --git a/app/models/group.rb b/app/models/group.rb index 12cfae2..571b1d0 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -49,12 +49,75 @@ def extend_trial self.save end + def find_archived_members_with_funds() + l = [] + Membership.where(group_id: id).where.not(archived_at: nil).find_each do |membership| + if membership.raw_balance != 0 + l.push({ + membership_id: membership.id, + user_name: User.find(membership.member_id).name, + archived_at: membership.archived_at, + balance: membership.raw_balance + }) + end + end + l + end + + def transfer_balance_from_member_to_group_account(member_id, current_user) + m = Membership.find(member_id) + amount = m.raw_balance + ActiveRecord::Base.transaction do + a = Allocation.create(user_id: member_id, group_id: id, amount: -amount) + Transaction.create!({ + datetime: a.created_at, + from_account_id: m.status_account_id, + to_account_id: status_account_id, + user_id: current_user.id, + amount: amount + }) + end + end + + def transfer_memberships_to_group_account(transfer_from_list, current_user) + group_membership = ensure_group_account_exist + transfer_from_list.each do |e| + transfer_balance_from_member_to_group_account(e[:membership_id], current_user) + end + + mail_admins_about_members(transfer_from_list) + end + def for_each_admin Membership.where(group_id: id, is_admin: :true, archived_at: nil).find_each do |admin| yield admin end end + def mail_admins_about_members(memberlist) + if memberlist.length > 0 + l = memberlist.map { |e| + e[:archived_at] = e[:archived_at].strftime("%B %d, %Y") + e[:balance] = Money.new(e[:balance] * 100, currency_code).format + } + for_each_admin do |admin| + UserMailer.notify_admins_archived_member_funds(admin: admin.member.name_and_email, + group: self, memberlist: memberlist).deliver_later + end + if Rails.configuration.respond_to?('devops_user') + UserMailer.notify_admins_archived_member_funds(admin: Rails.configuration.devops_user, + group: self, memberlist: memberlist).deliver_later + end + end + end + + def cleanup_archived_members_with_funds(current_user) + l = find_archived_members_with_funds() + if l.length > 0 + transfer_memberships_to_group_account(l, current_user) + end + end + def add_member(user) memberships.create!(member: user, is_admin: false) end diff --git a/app/services/allocation_service.rb b/app/services/allocation_service.rb index 3c3358f..6e284b7 100644 --- a/app/services/allocation_service.rb +++ b/app/services/allocation_service.rb @@ -25,6 +25,27 @@ def self.check_csv_for_errors(csv:, group:) errors if errors.any? end + def self.create_allocation(allocation_params, notify, current_user) + allocation = Allocation.new(allocation_params) + amount = allocation_params[:amount] + if allocation.save + m = Membership.find_by(group_id: allocation_params[:group_id], member_id: allocation_params[:user_id]) + Transaction.create!({ + datetime: allocation.created_at, + from_account_id: m.incoming_account_id, + to_account_id: m.status_account_id, + user_id: current_user.id, + amount: amount + }) + if notify && (amount > 0) + UserMailer.notify_member_that_they_received_allocation(admin: current_user, member: user, group: group, amount: amount).deliver_later + end + allocation + else + nil + end + end + def self.generate_csv_upload_preview(csv:, group:) csv.group_by { |row| row[0].downcase }.map do |email, rows| allocation_amount = rows.sum { |row| row[1].to_f } diff --git a/app/views/user_mailer/notify_admins_archived_member_funds.erb b/app/views/user_mailer/notify_admins_archived_member_funds.erb new file mode 100644 index 0000000..2a09226 --- /dev/null +++ b/app/views/user_mailer/notify_admins_archived_member_funds.erb @@ -0,0 +1,23 @@ +

+ Archived members with funds has been found in <%= @group.name %> +

+ +

+ The funds has been transferred to the group account <%= @group_user.name %> which you will find in the list of funders. +

+ + + + + + + + + <% @memberlist.each do |m| %> + + + + + + <% end %> +
Archived members with funds
UserArchivedAmount
<%= m[:user_name] %><%= m[:archived_at] %><%= m[:balance] %>
diff --git a/config/environments/development.rb b/config/environments/development.rb index abc4f21..c681cee 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -36,4 +36,7 @@ config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 } config.action_mailer.default_url_options = { host: 'localhost:9000' } config.action_mailer.raise_delivery_errors = true + + # Devops user. Mails will be sent to the user when it's sent to group admins + config.devops_user = "devops@yourdomain.org" end diff --git a/config/environments/production.rb b/config/environments/production.rb index 72c05bb..823107b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -92,6 +92,9 @@ :password => ENV['SMTP_PASSWORD'], :domain => ENV['SMTP_DOMAIN'], :enable_starttls_auto => true + + config.devops_user = ENV['DEVOPS_MAIL'] + } end diff --git a/lib/tasks/cobudget.rake b/lib/tasks/cobudget.rake index cb6a81f..fad214d 100644 --- a/lib/tasks/cobudget.rake +++ b/lib/tasks/cobudget.rake @@ -5,5 +5,6 @@ namespace :cobudget do DeliverRecentActivityEmails.to_daily_digest_subscribers! DeliverRecentActivityEmails.to_weekly_digest_subscribers! DeliverCheckTransactionsEmail.check_transactions! + CobudgetCleanup.archived_members_with_funds! end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 03cacad..61d11d1 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe Group, :type => :model do + after { ActionMailer::Base.deliveries.clear } describe "#add_admin(user)" do context "user already member of group" do it "makes the user an admin of group" do @@ -65,4 +66,53 @@ end end end + + describe "membership cleanup" do + context "by moving funds from archived member to group account" do + before do + admins = create_list(:user, 2) + admins.each { |a| group.add_admin(a) } + normal_users = create_list(:user, 3) + @normal_memberships = normal_users.map { |u| group.add_member(u) } + normal_users.each { |u| create(:allocation, user: u, group: group, amount: 10) } + @normal_memberships.each { |m| create(:transaction, from_account_id: m.incoming_account_id, + to_account_id: m.status_account_id, user_id: user.id, amount: 10) } + archived_users = create_list(:user, 3) + @archived_memberships = archived_users.map { |u| group.add_member(u) } + archived_users.each { |u| create(:allocation, user: u, group: group, amount: 7) } + @archived_memberships.each { |m| create(:transaction, from_account_id: m.incoming_account_id, + to_account_id: m.status_account_id, user_id: user.id, amount: 7) } + @archived_memberships.each { |m| m.archive! } + group.cleanup_archived_members_with_funds(group.ensure_group_user_exist()) + @group_membership = group.ensure_group_account_exist() + end + + it "has moved funds from archived users" do + @archived_memberships.each do |m| + expect(m.raw_balance).to eq(0) + expect(Account.find(m.status_account_id).balance).to eq(0) + end + end + + it "has not touched funds in non-archived users" do + @normal_memberships.each do |m| + expect(m.raw_balance).to eq(10) + expect(Account.find(m.status_account_id).balance).to eq(10) + end + end + + it "has moved the funds from echived users to group account" do + expect(@group_membership.raw_balance).to eq(21) + expect(Account.find(@group_membership.status_account_id).balance).to eq(21) + end + + it "sends mails to admins" do + sent_emails = ActionMailer::Base.deliveries + recipients = sent_emails.map { |email| email.to.first } + admin_emails = Membership.where(group_id: group.id, is_admin: :true).map { |admin| admin.member.email } + expect(recipients).to match_array(admin_emails) + expect(sent_emails.first.body).to include("transferred") + end + end + end end