Skip to content

Commit

Permalink
jobs: draft the PreparePayments job
Browse files Browse the repository at this point in the history
  • Loading branch information
freesteph committed Feb 8, 2024
1 parent b17b896 commit e10e766
Show file tree
Hide file tree
Showing 27 changed files with 320 additions and 55 deletions.
23 changes: 23 additions & 0 deletions app/decorators/payment_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module PaymentDecorator
BADGE_STATE_MAPPING = {
ready: :success,
blocked: :error,
pending: :info
}.freeze

def summary
dsfr_badge(status: BADGE_STATE_MAPPING[current_state.to_sym]) do
status
end
end

def status
t("payments.state.#{current_state}")
end

def description
t("payments.description.#{current_state}")
end
end
16 changes: 16 additions & 0 deletions app/jobs/prepare_payments_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class PreparePaymentsJob < ApplicationJob
queue_as :default

def perform
Payment
.includes(:student)
.in_state(:pending)
.find_each do |payment|
payment.mark_ready!
rescue Statesman::GuardFailedError
payment.block!
end
end
end
38 changes: 38 additions & 0 deletions app/models/concerns/asp/student_checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module ASP
class StudentChecker
attr_reader :student

DATA_REQUIREMENTS = %i[rib birthplace_information address_information biological_sex].freeze

def initialize(student)
@student = student
end

def ready?
DATA_REQUIREMENTS.all? { |requirement| can_provide?(requirement) }
end

private

def attributes_present?(attributes)
attributes.all? { |attr| student.send(attr).present? }
end

def can_provide?(requirement)
case requirement
when :rib
student.rib.present?
when :birthplace_information
attributes_present? %i[birthplace_city_insee_code birthplace_country_insee_code]
when :address_information
attributes_present? %i[address_postal_code address_city_insee_code address_country_code]
when :biological_sex
student.male? || student.female?
else
raise ArgumentError, "don't know how to check for #{requirement}"
end
end
end
end
4 changes: 4 additions & 0 deletions app/models/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def state_machine
)
end

def block!
state_machine.transition_to!(:blocked)
end

def mark_ready!
state_machine.transition_to!(:ready)
end
Expand Down
7 changes: 7 additions & 0 deletions app/models/payment_state_machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ class PaymentStateMachine
include Statesman::Machine

state :pending, initial: true
state :blocked
state :ready
state :processing
state :successful
state :failed

transition from: :pending, to: :ready
transition from: :pending, to: :blocked
transition from: :blocked, to: :ready
transition from: :ready, to: :processing
transition from: :processing, to: :successful
transition from: :processing, to: :failed

guard_transition(to: :ready) do |payment|
ASP::StudentChecker.new(payment.student).ready?
end
end
16 changes: 0 additions & 16 deletions app/models/pfmp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,7 @@ def latest_payment
payments.last
end

def payable?
student.rib.present?
end

def payment_due?
student.allowance_left(mef).positive?
end

def breakdown
I18n.t("pfmps.breakdown", days: day_count, rate: wage.daily_rate, total: calculate_amount)
end

def payment_state
if payable?
latest_payment.state_machine.current_state
else
:blocked
end
end
end
8 changes: 7 additions & 1 deletion app/views/pfmps/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@
- if @pfmp.in_state?(:completed)
anticipé

%p= number_to_currency(@pfmp.calculate_amount)
- if @pfmp.payments.any?
- payment = @pfmp.payments.last

%strong= number_to_currency(payment.amount)

%p= payment.summary
%p= payment.description

%h2.fr-h5 Actions
.fr-btns-group.fr-btns-group--inline
Expand Down
4 changes: 2 additions & 2 deletions app/views/students/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
%li= link_to "Supprimer les coordonnées bancaires", confirm_deletion_class_student_rib_path(@classe, @student, @student.rib), class: 'fr-btn fr-btn--secondary'

%section.fr-mt-3w
%h2.fr-h4
%h2.fr-h4
PFMPs dans la classe
= @classe.label

= closed_schooling_information_tag(@schooling, class: "fr-mb-2w")

- if @pfmps.none?
Expand Down
12 changes: 8 additions & 4 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,15 @@ fr:
d'établissement sans quoi le paiement ne peut pas être déclenché.
validated: |
La PFMP a été validée. La demande de paiement sera envoyée à l'ASP à partir de janvier 2024.
payments:
blocked: "Informations de paiement manquantes"
payments:
description:
pending: Cette demande de paiement sera traitée bientôt.
ready: Ce paiement est prêt à l'envoi
blocked: Il manque des informations pour envoyer le paiement
state:
pending: "Planifié"
breakdown: "%{days} jours × %{rate}€ par jour = %{total}€"
ready: "Prêt"
blocked: "Bloqué"
ministries:
masa: Ministère de l'Agriculture et de la Souveraineté Alimentaire (MASA)
menj: Ministère de l'Éducation Nationale et de la Jeunesse (MENJ)
Expand Down
39 changes: 39 additions & 0 deletions features/paiements.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# language: fr

Fonctionnalité: Gestion des paiements
Contexte:
Sachant que je suis un personnel MENJ directeur de l'établissement "DINUM"
Et que mon établissement propose une formation "Art" rémunérée à 10 euros par jour et plafonnée à 100 euros par an
Et que l'API SYGNE renvoie 10 élèves dans la classe de "A1" formation "Art" dont "Marie Curie", INE "MC" pour l'établissement "DINUM"
Et que je me connecte en tant que personnel MENJ
Et que toutes les tâches de fond sont terminées
Et que je passe l'écran d'accueil
Et que je consulte la liste des classes
Et que je consulte le profil de "Marie Curie" dans la classe de "A1"

Scénario: Le personnel de direction peut voir un paiement planifié
Sachant que je renseigne et valide une PFMP de 3 jours
Quand je consulte le profil de "Marie Curie" dans la classe de "A1"
Et que je consulte la dernière PFMP
Alors je peux voir un paiement "Planifié" de 30 euros

Scénario: Le personnel de direction peut voir un paiement bloqué
Sachant que je renseigne et valide une PFMP de 3 jours
Et que la tâche de préparation des paiements démarre
Et que toutes les tâches de fond sont terminées
Quand je consulte le profil de "Marie Curie" dans la classe de "A1"
Et que je consulte la dernière PFMP
Alors je peux voir un paiement "Bloqué" de 30 euros
Et la page contient "Il manque des informations pour envoyer le paiement"

Scénario: Le personnel de direction peut voir un paiment prêt pour l'ASP
Sachant que l'API SYGNE peut fournir les informations complètes des étudiants
Et que les informations personnelles ont été récupérées pour l'élève avec l'INE "MC"
Et que je renseigne et valide une PFMP de 3 jours
Et que l'élève "Marie Curie" a déjà des coordonnées bancaires
Et que la tâche de préparation des paiements démarre
Et que toutes les tâches de fond sont terminées
Quand je consulte le profil de "Marie Curie" dans la classe de "A1"
Et que je consulte la dernière PFMP
Alors je peux voir un paiement "Prêt" de 30 euros
Et la page contient "Ce paiement est prêt à l'envoi"
43 changes: 37 additions & 6 deletions features/step_definitions/api_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,46 @@
) do |count, classe, ine, uai|
@sygne_results = []

mock_sygne_token_with
mock_sygne_token

payload = FactoryBot.build_list(:sygne_student, count, classe: classe).tap do |students|
students.last["ine"] = ine
end

@sygne_results << payload

mock_sygne_students_endpoint_with(uai, payload)
mock_sygne_students_endpoint(uai, payload)
end

Sachantque(
"l'API SYGNE renvoie {int} élèves " \
"dans la classe de {string} formation {string} " \
"dont {string}, INE {string} pour l'établissement {string}"
) do |count, classe, mef, name, ine, uai| # rubocop:disable Metrics/ParameterLists
mock_sygne_token

@sygne_results = []

first_name, last_name = name.split

mef = Mef.find_by!(label: mef)

payload = FactoryBot.build_list(:sygne_student, count, classe: classe, mef: mef.code.concat("0")).tap do |students|
students.last["ine"] = ine
students.last["prenom"] = first_name
students.last["nom"] = last_name
end

@sygne_results << payload

mock_sygne_students_endpoint(uai, payload)
end

# ce step permet de mocker l'API SYGNE sans inclure la logique des
# autres steps qui construisent les données passées en paramètre.
Sachantque("l'API SYGNE peut renvoyer des élèves pour l'établissement {string}") do |uai|
mock_sygne_token
mock_sygne_students_endpoint(uai, [])
end

Sachantque("l'API FREGATA renvoie une liste d'élèves pour l'établissement {string}") do |uai|
Expand All @@ -35,13 +66,13 @@
Sachantque("l'API SYGNE renvoie un élève avec l'INE {string} qui a quitté l'établissement {string}") do |ine, uai|
payload_without_student = @sygne_results.last.dup.reject { |student| student["ine"] == ine }

mock_sygne_token_with
mock_sygne_students_endpoint_with(uai, payload_without_student)
mock_sygne_token
mock_sygne_students_endpoint(uai, payload_without_student)
end

Sachantque("l'API SYGNE renvoie {int} nouvel/nouveaux élève(s) pour l'établissement {string}") do |count, uai|
payload = FactoryBot.build_list(:sygne_student, count)

mock_sygne_token_with
mock_sygne_students_endpoint_with(uai, payload)
mock_sygne_token
mock_sygne_students_endpoint(uai, payload)
end
14 changes: 14 additions & 0 deletions features/step_definitions/paiement_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

World(ActionView::Helpers::NumberHelper)

Quand("la tâche de préparation des paiements démarre") do
PreparePaymentsJob.perform_later
end

Alors("je peux voir un paiement {string} de {int} euros") do |state, amount|
steps %(
Alors la page contient "#{state}"
Et la page contient "#{number_to_currency(amount)}"
)
end
8 changes: 7 additions & 1 deletion features/step_definitions/perdir_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,16 @@ def find_student_by_full_name(name)
)
end

Quand("je consulte la dernière PFMP") do
steps %(
Et que je clique sur "Voir la PFMP" dans la dernière rangée
)
end

Quand("je renseigne et valide une PFMP de {int} jours") do |days|
steps %(
Quand je renseigne une PFMP de #{days} jours
Et que je clique sur "Voir la PFMP" dans la dernière rangée
Et que je consulte la dernière PFMP
Et que je clique sur "Valider"
)
end
Expand Down
6 changes: 6 additions & 0 deletions features/step_definitions/student_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
)
end

Sachantque("les informations personnelles ont été récupérées pour l'élève avec l'INE {string}") do |ine|
student = Student.find_by(ine: ine)

FetchStudentInformationJob.perform_now(student.current_schooling)
end

Quand("l'élève avec l'INE {string} s'appelle {string} {string}") do |ine, first_name, last_name|
Student.find_by(ine:).update(first_name:, last_name:)
end
Expand Down
2 changes: 1 addition & 1 deletion mock
Submodule mock updated 2 files
+0 −0 asp/depot/.keep
+0 −0 asp/retour/.keep
4 changes: 2 additions & 2 deletions spec/api/student_api/sygne_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
let(:student_data) { Rails.root.join("mock/data/sygne-student.json").read }

before do
mock_sygne_token_with
mock_sygne_students_endpoint_with(establishment.uai, data)
mock_sygne_token
mock_sygne_students_endpoint(establishment.uai, data)
end

it "grabs an access token before calling the API" do
Expand Down
5 changes: 5 additions & 0 deletions spec/factories/students.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
biological_sex { [1, 2].sample }
end

trait :with_all_asp_info do
with_rib
with_extra_info
end

trait :with_address_info do
with_address
end
Expand Down
2 changes: 1 addition & 1 deletion spec/jobs/fetch_student_information_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
let(:sygne_api) { instance_double(StudentApi::Sygne) }

before do
WebmockHelpers.mock_sygne_token_with(token)
WebmockHelpers.mock_sygne_token(token)
WebmockHelpers.mock_sygne_student_endpoint_with(student.ine, payload)
end

Expand Down
2 changes: 1 addition & 1 deletion spec/jobs/generate_attributive_decision_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
before do
ActiveJob::Base.queue_adapter = :test

WebmockHelpers.mock_sygne_token_with
WebmockHelpers.mock_sygne_token
WebmockHelpers.mock_sygne_student_endpoint_with(student.ine, build(:sygne_student, ine_value: student.ine).to_json)
end

Expand Down
Loading

0 comments on commit e10e766

Please sign in to comment.