Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Send claims to ESFA" flow #1258

Merged
merged 3 commits into from
Dec 23, 2024
Merged
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
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CLAIMS_HOST=claims.localhost
CLAIMS_HOSTS=claims.localhost
CLAIMS_DFE_SIGN_IN_CLIENT_ID=123
CLAIMS_DFE_SIGN_IN_SECRET=secret
CLAIMS_ESFA_EMAIL_ADDRESSES=esfa@example.com

PLACEMENTS_HOST=placements.localhost
PLACEMENTS_HOSTS=placements.localhost
Expand Down
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
require:
- rubocop-rspec

inherit_gem:
rubocop-govuk:
- config/default.yml
- config/rails.yml
- config/rspec.yml
pundit:
- config/rubocop-rspec.yml

inherit_mode:
merge:
Expand Down
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ group :development do
gem "annotate", require: false
gem "prettier_print", require: false
gem "rladr"
gem "rubocop-govuk", require: false
gem "syntax_tree", require: false
gem "syntax_tree-haml", require: false
gem "syntax_tree-rbs", require: false
Expand Down Expand Up @@ -159,6 +158,9 @@ group :test do
gem "timecop"
gem "undercover", "~> 0.5.0"
gem "webmock"

gem "rubocop-govuk", require: false
gem "rubocop-rspec", require: false
end

group :test, :development do
Expand Down
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ DEPENDENCIES
rspec
rspec-rails
rubocop-govuk
rubocop-rspec
ruby-lsp-rspec
selenium-webdriver
sentry-rails
Expand Down
5 changes: 1 addition & 4 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base
include RoutesHelper
include Pagy::Backend
include Pundit::Authorization
include PunditNamespaces

before_action :authenticate_user!

Expand Down Expand Up @@ -74,8 +75,4 @@ def user_not_authorized
redirect_back(fallback_location: root_path)
end
end

def unwrap_pundit_scope(scope)
scope.is_a?(Array) ? scope : [scope]
end
end
14 changes: 2 additions & 12 deletions app/controllers/claims/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
class Claims::ApplicationController < ApplicationController
after_action :verify_authorized

def authorize(record, query = nil, policy_class: nil)
super([:claims, *unwrap_pundit_scope(record)], query, policy_class:)
end
append_pundit_namespace :claims

def policy(record)
super([:claims, *unwrap_pundit_scope(record)])
end
after_action :verify_authorized

private

def pundit_policy_scope(scope)
super([:claims, *unwrap_pundit_scope(scope)])
end

def has_school_accepted_grant_conditions?
return true if current_user.support_user?

Expand Down
29 changes: 29 additions & 0 deletions app/controllers/claims/payments/claims_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Claims::Payments::ClaimsController < Claims::ApplicationController
skip_before_action :authenticate_user!

before_action :skip_authorization
before_action :validate_token
before_action :set_payment

def download
send_data @payment.csv_file.download, filename: "payments-claims-#{Time.current.iso8601}.csv"
end

private

def validate_token
@payment_id = Rails.application.message_verifier(:payments).verify(token_param)
rescue ActiveSupport::MessageVerifier::InvalidSignature
render "error"
end

def set_payment
@payment = Claims::Payment.find(@payment_id)
rescue ActiveRecord::RecordNotFound
render "error"
end

def token_param
params.fetch(:token, nil)
end
end
14 changes: 2 additions & 12 deletions app/controllers/claims/support/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
class Claims::Support::ApplicationController < Claims::ApplicationController
before_action :authorize_user!

def authorize(record, query = nil, policy_class: nil)
super([:support, *unwrap_pundit_scope(record)], query, policy_class:)
end
append_pundit_namespace :support

def policy(record)
super([:support, *unwrap_pundit_scope(record)])
end
before_action :authorize_user!

private

def pundit_policy_scope(scope)
super([:support, *unwrap_pundit_scope(scope)])
end

def authorize_user!
return if current_user.support_user?

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Claims::Support::Claims::Payments::ClaimsController < Claims::Support::ApplicationController
append_pundit_namespace :claims, :payments

before_action :set_claim, only: %i[show]
before_action :authorize_claim

Expand All @@ -11,6 +13,6 @@ def set_claim
end

def authorize_claim
authorize [:payments, @claim || Claims::Claim]
authorize @claim || Claims::Claim
end
end
18 changes: 17 additions & 1 deletion app/controllers/claims/support/claims/payments_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Claims::Support::Claims::PaymentsController < Claims::Support::ApplicationController
append_pundit_namespace :claims

before_action :authorize_claims

helper_method :filter_form
Expand All @@ -7,6 +9,20 @@ def index
@pagy, @claims = pagy(filtered_claims)
end

def new
@submitted_claims = Claims::Claim.submitted

unless policy(Claims::Payment).create?
render "new_not_permitted"
end
end

def create
Claims::Payment::CreateAndDeliver.call(current_user:)

redirect_to claims_support_claims_payments_path, flash: { success: true, heading: "Claims sent to ESFA" }
end

private

def filtered_claims
Expand Down Expand Up @@ -40,6 +56,6 @@ def filter_params
end

def authorize_claims
authorize filtered_claims
authorize [:payments, filtered_claims]
end
end
27 changes: 27 additions & 0 deletions app/controllers/concerns/pundit_namespaces.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module PunditNamespaces
extend ActiveSupport::Concern

included do
def unwrap_pundit_scope(scope)
scope.is_a?(Array) ? scope : [scope]
end
end

class_methods do
def append_pundit_namespace(*namespaces)
define_method :authorize do |record, query = nil, policy_class: nil|
super([*namespaces, *unwrap_pundit_scope(record)], query, policy_class:)
end

define_method :policy do |record|
super([*namespaces, *unwrap_pundit_scope(record)])
end

define_method :pundit_policy_scope do |scope|
super([*namespaces, *unwrap_pundit_scope(scope)])
end

private :pundit_policy_scope
end
end
end
14 changes: 2 additions & 12 deletions app/controllers/placements/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
class Placements::ApplicationController < ApplicationController
append_pundit_namespace :placements

after_action :verify_policy_scoped, if: ->(c) { c.action_name == "index" }
before_action :authorize_support_user!

def authorize(record, query = nil, policy_class: nil)
super([:placements, *unwrap_pundit_scope(record)], query, policy_class:)
end

def policy(record)
super([:placements, *unwrap_pundit_scope(record)])
end

private

def pundit_policy_scope(scope)
super([:placements, *unwrap_pundit_scope(scope)])
end

def set_school
@school = policy_scope(Placements::School).find(params.require(:school_id))
end
Expand Down
24 changes: 24 additions & 0 deletions app/mailers/claims/payment_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Claims::PaymentMailer < Claims::ApplicationMailer
def payment_created_notification(payment)
@payment = payment

notify_email to: esfa_email_addresses,
from: support_email,
subject: t(".subject"),
body: t(
".body",
download_page_url: claims_payments_claims_url(token:),
support_email:,
)
end

private

def esfa_email_addresses
ENV.fetch("CLAIMS_ESFA_EMAIL_ADDRESSES").split(",")
end

def token
Rails.application.message_verifier(:payments).generate(@payment.id, expires_in: 7.days)
end
end
4 changes: 3 additions & 1 deletion app/models/claims/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ class Claims::Payment < ApplicationRecord
belongs_to :sent_by, class_name: "Claims::SupportUser"

has_many :payment_claims, dependent: :destroy
has_many :claims, through: :payment_claims
has_many :claims, through: :payment_claims, class_name: "Claims::Claim"

has_one_attached :csv_file
end
9 changes: 9 additions & 0 deletions app/policies/claims/support/claims/payment_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Claims::Support::Claims::PaymentPolicy < Claims::ApplicationPolicy
def new?
true
end

def create?
Claims::Claim.submitted.any?
end
end
5 changes: 5 additions & 0 deletions app/policies/claims/support/claims/payments/claim_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Claims::Support::Claims::Payments::ClaimPolicy < Claims::ApplicationPolicy
def update?
false
end
end
2 changes: 0 additions & 2 deletions app/policies/claims/support/payments/claim_policy.rb

This file was deleted.

33 changes: 33 additions & 0 deletions app/services/claims/payment/create_and_deliver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Claims::Payment::CreateAndDeliver < ApplicationService
def initialize(current_user:)
@current_user = current_user
end

def call
return if submitted_claims.none?

ActiveRecord::Base.transaction do |transaction|
csv_file = Claims::Payments::Claim::GenerateCSVFile.call(claims: submitted_claims)

payment = Claims::Payment.create!(sent_by: current_user, csv_file: File.open(csv_file.to_io), claims: submitted_claims)

submitted_claims.find_each do |claim|
claim.update!(status: :payment_in_progress, payment_in_progress_at: Time.current)
end

Claims::ClaimActivity.create!(action: :payment_delivered, user: current_user, record: payment)

transaction.after_commit do
Claims::PaymentMailer.payment_created_notification(payment).deliver_later
end
end
end

private

attr_reader :current_user

def submitted_claims
@submitted_claims ||= Claims::Claim.submitted
end
end
50 changes: 50 additions & 0 deletions app/services/claims/payments/claim/generate_csv_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require "csv"

class Claims::Payments::Claim::GenerateCSVFile < ApplicationService
HEADERS = %w[
claim_reference
school_urn
school_name
school_local_authority
claim_amount
school_type_of_establishment
school_group
claim_submission_date
claim_status
claim_unpaid_reason
].freeze

def initialize(claims:)
@claims = claims
end

def call
CSV.open(file_name, "w", headers: true) do |csv|
csv << HEADERS

claims.each do |claim|
csv << [
claim.reference,
claim.school.urn,
claim.school_name,
claim.school.local_authority_name,
claim.amount.format(symbol: false, decimal_mark: ".", no_cents: false),
claim.school.type_of_establishment,
claim.school.group,
claim.submitted_at&.iso8601,
claim.status,
]
end

csv
end
end

private

attr_reader :claims

def file_name
Rails.root.join("tmp/payment-claims-#{Time.current}.csv")
end
end
11 changes: 11 additions & 0 deletions app/views/claims/payments/claims/error.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="govuk-width-container">
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l"><%= t(".page_title") %></h1>

<p class="govuk-body"><%= t(".description") %></p>

<p class="govuk-body"><%= t(".support_html", mail_to: govuk_mail_to(t("claims.support_email"), t("claims.support_email_html"))) %></p>
</div>
</div>
</div>
Loading
Loading