Skip to content

Commit

Permalink
Merge pull request #3388 from DFE-Digital/CAPT-1975/payroll-timeout
Browse files Browse the repository at this point in the history
Move payroll run to background job
  • Loading branch information
rjlynch authored Nov 13, 2024
2 parents 064c940 + 68508e0 commit 358631d
Show file tree
Hide file tree
Showing 18 changed files with 349 additions and 229 deletions.
4 changes: 3 additions & 1 deletion app/controllers/admin/payroll_runs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def create
return
end

payroll_run = PayrollRun.create_with_claims!(claims, topups, created_by: admin_user)
payroll_run = PayrollRun.create!(created_by: admin_user)

PayrollRunJob.perform_later(payroll_run, claims.ids, topups.ids)

redirect_to [:admin, payroll_run], notice: "Payroll run created"
rescue ActiveRecord::RecordInvalid => e
Expand Down
24 changes: 24 additions & 0 deletions app/jobs/payroll_run_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class PayrollRunJob < ApplicationJob
def perform(payroll_run, claim_ids, topup_ids)
claims = Claim.where(id: claim_ids)
topups = Topup.where(id: topup_ids)

ActiveRecord::Base.transaction do
[claims, topups].reduce([], :concat).group_by(&:national_insurance_number).each_value do |grouped_items|
# associates the claim to the payment, for Topup that's its associated claim
grouped_claims = grouped_items.map { |i| i.is_a?(Topup) ? i.claim : i }

# associates the payment to the Topup, so we know it's payrolled
group_topups = grouped_items.select { |i| i.is_a?(Topup) }

award_amount = grouped_items.map(&:award_amount).compact.sum(0)
Payment.create!(payroll_run: payroll_run, claims: grouped_claims, topups: group_topups, award_amount: award_amount)
end

payroll_run.complete!
end
rescue => e
payroll_run.failed!
raise e
end
end
23 changes: 2 additions & 21 deletions app/models/payroll_run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class PayrollRun < ApplicationRecord
# backfill existing payroll runs and payments with a payment confirmation
belongs_to :confirmation_report_uploaded_by, class_name: "DfeSignIn::User", optional: true

enum :status, %w[pending complete failed].index_with(&:itself)

validate :ensure_no_payroll_run_this_month, on: :create

scope :this_month, -> { where(created_at: DateTime.now.all_month) }
Expand Down Expand Up @@ -41,27 +43,6 @@ def all_payments_confirmed?
@all_payments_confirmed = payment_confirmations.any? && total_confirmed_payments == payments_count
end

def self.create_with_claims!(claims, topups, attrs = {})
ActiveRecord::Base.transaction do
PayrollRun.create!(attrs).tap do |payroll_run|
[claims, topups].reduce([], :concat).group_by { |obj| group_by_field(obj) }.each_value do |grouped_items|
# associates the claim to the payment, for Topup that's its associated claim
grouped_claims = grouped_items.map { |i| i.is_a?(Topup) ? i.claim : i }

# associates the payment to the Topup, so we know it's payrolled
group_topups = grouped_items.select { |i| i.is_a?(Topup) }

award_amount = grouped_items.map(&:award_amount).compact.sum(0)
Payment.create!(payroll_run: payroll_run, claims: grouped_claims, topups: group_topups, award_amount: award_amount)
end
end
end
end

def self.group_by_field(obj)
obj.national_insurance_number
end

def download_triggered?
downloaded_at.present? && downloaded_by.present?
end
Expand Down
158 changes: 158 additions & 0 deletions app/views/admin/payroll_runs/_complete.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">
<%= @payroll_run.created_at.strftime("%B") %> payroll run
</h1>

<dl class="govuk-summary-list govuk-!-margin-bottom-9">
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Approved claims
</dt>

<dd class="govuk-summary-list__value">
<%= @payroll_run.number_of_claims_for_policy(:all, filter: :claims) %>
</dd>
</div>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Top ups
</dt>

<dd class="govuk-summary-list__value">
<%= @payroll_run.number_of_claims_for_policy(Policies::LevellingUpPremiumPayments, filter: :topups) %>
</dd>
</div>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Total award amount
</dt>

<dd class="govuk-summary-list__value">
<%= number_to_currency(@payroll_run.total_award_amount) %>
</dd>
</div>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Created by
</dt>

<dd class="govuk-summary-list__value">
<%= user_details(@payroll_run.created_by) %>
</dd>
</div>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Downloaded
</dt>

<dd class="govuk-summary-list__value">
<%= @payroll_run.download_triggered? ? l(@payroll_run.downloaded_at) : "No" %>
</dd>
</div>
<% if @payroll_run.download_triggered? %>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Downloaded by
</dt>

<dd class="govuk-summary-list__value">
<%= user_details(@payroll_run.downloaded_by) %>
</dd>
</div>
<% end %>
</dl>
</div>

<div class="govuk-grid-column-full">
<h2 class="govuk-heading-m">Summary of claim amounts by service</h2>

<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header">Service</th>
<th scope="col" class="govuk-table__header">Number of claims</th>
<th scope="col" class="govuk-table__header">Total claimed amount</th>
</tr>
<% Policies.all.each do |policy| %>
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header"><%= policy.short_name %></th>
<td class="govuk-table__cell"><%= @payroll_run.number_of_claims_for_policy(policy, filter: :claims) %></td>
<td class="govuk-table__cell"><%= number_to_currency(@payroll_run.total_claim_amount_for_policy(policy, filter: :claims)) %></td>
</tr>
<% end %>
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header"><%= I18n.t("levelling_up_premium_payments.policy_short_name") %> Top Ups</th>
<td class="govuk-table__cell"><%= @payroll_run.number_of_claims_for_policy(Policies::LevellingUpPremiumPayments, filter: :topups) %></td>
<td class="govuk-table__cell"><%= number_to_currency(@payroll_run.total_claim_amount_for_policy(Policies::LevellingUpPremiumPayments, filter: :topups)) %></td>
</tr>
</thead>
<tbody class="govuk-table__body">
</tbody>
</table>
</div>

<% unless @payroll_run.all_payments_confirmed? %>
<div class="govuk-grid-column-full">
<div class="govuk-form-group">
<label class="govuk-label" for="payroll_run_download_link">
You can now send this link to DfE Payroll for processing.
</label>
<%= text_field_tag "payroll_run_download_link", new_admin_payroll_run_download_url(@payroll_run), data: {"copy-to-clipboard": :true}, readonly: true, class: ["govuk-input"] %>
</div>
</div>
<% end %>

<div class="govuk-grid-column-full">
<h2 class="govuk-heading-l">Payments</h2>

<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header">Payment ID</th>
<th scope="col" class="govuk-table__header">Payee Name</th>
<th scope="col" class="govuk-table__header">Claim Reference</th>
<th scope="col" class="govuk-table__header">Service</th>
<th scope="col" class="govuk-table__header">Claim Amount</th>
<th scope="col" class="govuk-table__header">Payment Amount</th>
<% unless @payroll_run.all_payments_confirmed? %>
<th scope="col" class="govuk-table__header"><span class="govuk-visually-hidden">Actions</span></th>
<% end %>
</tr>
</thead>
<tbody class="govuk-table__body">
<% @payments.each do |payment| %>
<% payment.claims.each_with_index do |claim, index| %>
<% number_of_claims = payment.claims.size %>
<% topup_claim_ids = payment.topups.pluck(:claim_id) %>
<% line_item = topup_claim_ids.include?(claim.id) ? payment.topups.select { |t| t.claim_id == claim.id }.first : claim %>

<tr class="govuk-table__row">
<% if index == 0 %>
<th scope="row" rowspan="<%= number_of_claims %>" class="govuk-table__header"><%= payment.id %></th>
<td class="govuk-table__cell" rowspan="<%= number_of_claims %>"><%= payment.banking_name %></td>
<% end %>
<td class="govuk-table__cell"><%= link_to claim.reference, admin_claim_path(claim), class: "govuk-link" %></td>
<td class="govuk-table__cell"><%= line_item.is_a?(Topup) ? "#{I18n.t("levelling_up_premium_payments.policy_short_name")} (top up)" : claim.policy.short_name %></td>
<td class="govuk-table__cell"><%= number_to_currency(line_item.award_amount) %></td>
<% if index == 0 %>
<td class="govuk-table__cell" rowspan="<%= number_of_claims %>"><%= number_to_currency(payment.award_amount) %></td>
<% unless @payroll_run.all_payments_confirmed? %>
<td class="govuk-table__cell" rowspan="<%= number_of_claims %>">
<% unless payment.confirmation.present? %>
<%= link_to remove_admin_payroll_run_payment_path(id: payment.id, payroll_run_id: payment.payroll_run.id), class: "govuk-link" do %>
Remove <span class="govuk-visually-hidden">payment row</span>
<% end %>
<% end %>
</td>
<% end %>
<% end %>
</tr>
<% end %>
<% end %>
</tbody>
</table>

<%== render partial: 'pagination', locals: { pagy: @pagy } %>
</div>
</div>
15 changes: 15 additions & 0 deletions app/views/admin/payroll_runs/_failed.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">
<%= @payroll_run.created_at.strftime("%B") %> payroll run
</h1>

<div class="govuk-warning-text">
<span class="govuk-warning-text__icon" aria-hidden="true">!</span>
<strong class="govuk-warning-text__text">
<span class="govuk-visually-hidden">Warning</span>
This payroll run errored, please contact tech support
</strong>
</div>
</div>
</div>
14 changes: 14 additions & 0 deletions app/views/admin/payroll_runs/_pending.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">
<%= @payroll_run.created_at.strftime("%B") %> payroll run
</h1>

<p class="govuk-body">
This payroll run is in progress
</p>

<%= govuk_button_link_to "Refresh", admin_payroll_run_path(@payroll_run) %>
</div>
</div>

Loading

0 comments on commit 358631d

Please sign in to comment.