diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 6a88628204..3562cd1345 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1292,6 +1292,25 @@ def merchant_memo_check end end + def receipts + @user = User.find_by(email: params[:user_email]) if params[:user_email].present? + if @user + deprecated = @user.transactions_missing_receipt + current = @user.transactions_missing_receipt_v2 + difference = deprecated - current + + @results = { + counts: { + deprecated: deprecated.count, + current: current.count, + }, + difference: + } + else + @results = "Select a user" + end + end + def employees @page = params[:page] || 1 @per = params[:per] || 20 diff --git a/app/jobs/task/nightly_job.rb b/app/jobs/task/nightly_job.rb new file mode 100644 index 0000000000..fecbb5cdf5 --- /dev/null +++ b/app/jobs/task/nightly_job.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Task + class NightlyJob < ApplicationJob + queue_as :low + def perform + ::TaskService::Nightly.new.run + end + + end + +end diff --git a/app/models/concerns/receiptable.rb b/app/models/concerns/receiptable.rb index c5df65fb07..8fcd1fdea8 100644 --- a/app/models/concerns/receiptable.rb +++ b/app/models/concerns/receiptable.rb @@ -41,11 +41,17 @@ def no_or_lost_receipt! raise e end - after_create_commit do + after_create_commit :create_task! + + def ensure_task_exists! + create_task! unless tasks.any? || !receipt_required? + end + + def create_task! safely do assignee = try(:author) || try(:user) || try(:event) - if missing_receipt? && assignee - Task::Receiptable::Upload.create!(taskable: self, assignee:) + if assignee + Task::Receiptable::Upload.create!(taskable: self, assignee:, complete: !missing_receipt?) end end end diff --git a/app/models/hcb_code.rb b/app/models/hcb_code.rb index 63a3153625..801bfa3b8c 100644 --- a/app/models/hcb_code.rb +++ b/app/models/hcb_code.rb @@ -458,6 +458,10 @@ def ct2 # HCB-600: Stripe card charges (always required) # @sampoder + scope :of_type, ->(code) { + where("hcb_code LIKE 'HCB-#{code}%'") + } + scope :receipt_required, -> { joins("LEFT JOIN canonical_pending_transactions ON canonical_pending_transactions.hcb_code = hcb_codes.hcb_code") .joins("LEFT JOIN canonical_pending_declined_mappings ON canonical_pending_declined_mappings.canonical_pending_transaction_id = canonical_pending_transactions.id") @@ -470,8 +474,18 @@ def ct2 ") } + scope :without_receipt, -> { + left_outer_joins(:receipts).where(receipts: { id: nil }) + } + + scope :with_receipt, -> { + left_outer_joins(:receipts).where.not(receipts: { id: nil }) + } + + def receipt_required? return false if pt&.declined? + return false if event.plan.type == Event::Plan::SalaryAccount.name (type == :card_charge) || # starting from Feb. 2024, receipts have been required for ACHs & checks diff --git a/app/models/task/receiptable/upload.rb b/app/models/task/receiptable/upload.rb index 77609c969c..17285fff4a 100644 --- a/app/models/task/receiptable/upload.rb +++ b/app/models/task/receiptable/upload.rb @@ -23,8 +23,10 @@ class Task module Receiptable class Upload < Task + scope :for_hcb_code, -> { where(taskable_type: "HcbCode") } + def update_complete! - update(complete: !taskable.missing_receipt?) + update(complete: taskable.nil? || !taskable.missing_receipt? || !taskable.receipt_required?) end def text diff --git a/app/models/user.rb b/app/models/user.rb index b7c4991cdd..7ac9b17ebc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -321,6 +321,10 @@ def transactions_missing_receipt end end + def transactions_missing_receipt_v2 + HcbCode.where(id: tasks.where(type: "Task::Receiptable::Upload", taskable_type: "HcbCode").incomplete.select(:taskable_id)).of_type(600) + end + def transactions_missing_receipt_count @transactions_missing_receipt_count ||= begin transactions_missing_receipt.size diff --git a/app/services/task_service/nightly.rb b/app/services/task_service/nightly.rb new file mode 100644 index 0000000000..411f49e351 --- /dev/null +++ b/app/services/task_service/nightly.rb @@ -0,0 +1,57 @@ +module TaskService + class Nightly + def initialize + @tasks = Task::Receiptable::Upload.incomplete + @hcb_codes_without_task = HcbCode.receipt_required.where.not(id: Task::Receiptable::Upload.select(:taskable_id)) + end + + def run + puts "starting nightly task service" + + ensure_tasks_exist + update_task_completion + check_for_parity + end + + def ensure_tasks_exist + count = @hcb_codes_without_task.count + i = 0 + + @hcb_codes_without_task.find_each(batch_size: 100) do |hcb_code| + hcb_code.ensure_task_exists! + i += 1 + puts "Processed HCB Code #{i} of #{count}" if i % 100 == 0 + end + end + + def update_task_completion + count = @tasks.count + i = 0 + + @tasks.find_each(batch_size: 100) do |task| + task.update_complete! + i += 1 + puts "Updated Task #{i} of #{count}" if i % 100 == 0 + end + end + + def check_for_parity + count = User.count + i = 0 + + User.find_each(batch_size: 100) do |user| + old = user.transactions_missing_receipt + current = user.transactions_missing_receipt_v2 + + if old.count != current.count + Airbrake.notify("User #{user.id} has #{old.count} old, but #{current.count} new") + end + + i += 1 + + puts "Checked User #{i} of #{count}" if i % 100 == 0 + end + end + + end +end diff --git a/app/views/admin/receipts.html.erb b/app/views/admin/receipts.html.erb new file mode 100644 index 0000000000..09dfaa6f9f --- /dev/null +++ b/app/views/admin/receipts.html.erb @@ -0,0 +1,11 @@ +<%= form_with url: receipts_admin_index_path, method: :get, local: true do |form| %> + <%= form.text_field :user_email, value: params[:user_email], placeholder: 'Enter user email' %> + <%= form.submit 'Search' %> +<% end %> + +<% if @user %> +