Merge pull request #5858 from betagouv/dev

2021-01-27-01
This commit is contained in:
Paul Chavard 2021-01-27 14:13:39 +01:00 committed by GitHub
commit 0aa384c40b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 356 additions and 104 deletions

View file

@ -10,7 +10,7 @@ class Champs::SiretController < ApplicationController
return clear_siret_and_etablissement return clear_siret_and_etablissement
end end
if @siret.present? && @siret.length != 14 if !Siret.new(siret: @siret).valid?
return siret_error(:invalid) return siret_error(:invalid)
end end

View file

@ -19,6 +19,8 @@ module CreateAvisConcern
create_results = Avis.create( create_results = Avis.create(
expert_emails.flat_map do |email| expert_emails.flat_map do |email|
expert = User.create_or_promote_to_expert(email, SecureRandom.hex).expert
experts_procedure = ExpertsProcedure.find_or_create_by(procedure: dossier.procedure, expert: expert)
allowed_dossiers.map do |dossier| allowed_dossiers.map do |dossier|
{ {
email: email, email: email,
@ -26,7 +28,8 @@ module CreateAvisConcern
introduction_file: create_avis_params[:introduction_file], introduction_file: create_avis_params[:introduction_file],
claimant: current_instructeur, claimant: current_instructeur,
dossier: dossier, dossier: dossier,
confidentiel: confidentiel confidentiel: confidentiel,
experts_procedure: experts_procedure
} }
end end
end end

View file

@ -186,7 +186,7 @@ module NewAdministrateur
end end
def invited_expert_list def invited_expert_list
@invited_expert_emails = Avis.invited_expert_emails(@procedure) @invited_expert_emails = ExpertsProcedure.invited_expert_emails(@procedure)
end end
private private

View file

@ -803,13 +803,13 @@ type Entreprise {
effectif pour un mois donné effectif pour un mois donné
""" """
effectifMensuel: Effectif effectifMensuel: Effectif
formeJuridique: String! formeJuridique: String
formeJuridiqueCode: String! formeJuridiqueCode: String
inlineAdresse: String! inlineAdresse: String!
nom: String! nom: String
nomCommercial: String! nomCommercial: String!
numeroTvaIntracommunautaire: String! numeroTvaIntracommunautaire: String!
prenom: String! prenom: String
raisonSociale: String! raisonSociale: String!
siren: String! siren: String!
siretSiegeSocial: String! siretSiegeSocial: String!

View file

@ -9,8 +9,8 @@ module Types
field :siren, String, null: false field :siren, String, null: false
field :capital_social, GraphQL::Types::BigInt, null: false, description: "capital social de lentreprise. -1 si inconnu." field :capital_social, GraphQL::Types::BigInt, null: false, description: "capital social de lentreprise. -1 si inconnu."
field :numero_tva_intracommunautaire, String, null: false field :numero_tva_intracommunautaire, String, null: false
field :forme_juridique, String, null: false field :forme_juridique, String, null: true
field :forme_juridique_code, String, null: false field :forme_juridique_code, String, null: true
field :nom_commercial, String, null: false field :nom_commercial, String, null: false
field :raison_sociale, String, null: false field :raison_sociale, String, null: false
field :siret_siege_social, String, null: false field :siret_siege_social, String, null: false
@ -18,8 +18,8 @@ module Types
field :effectif_mensuel, EffectifType, null: true, description: "effectif pour un mois donné" field :effectif_mensuel, EffectifType, null: true, description: "effectif pour un mois donné"
field :effectif_annuel, EffectifType, null: true, description: "effectif moyen dune année" field :effectif_annuel, EffectifType, null: true, description: "effectif moyen dune année"
field :date_creation, GraphQL::Types::ISO8601Date, null: false field :date_creation, GraphQL::Types::ISO8601Date, null: false
field :nom, String, null: false field :nom, String, null: true
field :prenom, String, null: false field :prenom, String, null: true
field :inline_adresse, String, null: false field :inline_adresse, String, null: false
field :attestation_sociale_attachment, Types::File, null: true field :attestation_sociale_attachment, Types::File, null: true
field :attestation_fiscale_attachment, Types::File, null: true field :attestation_fiscale_attachment, Types::File, null: true

View file

@ -2,27 +2,30 @@
# #
# Table name: avis # Table name: avis
# #
# id :integer not null, primary key # id :integer not null, primary key
# answer :text # answer :text
# confidentiel :boolean default(FALSE), not null # confidentiel :boolean default(FALSE), not null
# email :string # email :string
# introduction :text # introduction :text
# revoked_at :datetime # revoked_at :datetime
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# claimant_id :integer not null # claimant_id :integer not null
# dossier_id :integer # dossier_id :integer
# instructeur_id :integer # experts_procedure_id :bigint
# instructeur_id :integer
# #
class Avis < ApplicationRecord class Avis < ApplicationRecord
include EmailSanitizableConcern include EmailSanitizableConcern
belongs_to :dossier, inverse_of: :avis, touch: true, optional: false belongs_to :dossier, inverse_of: :avis, touch: true, optional: false
belongs_to :instructeur, optional: true belongs_to :instructeur, optional: true
belongs_to :experts_procedure, optional: true
belongs_to :claimant, class_name: 'Instructeur', optional: false belongs_to :claimant, class_name: 'Instructeur', optional: false
has_one_attached :piece_justificative_file has_one_attached :piece_justificative_file
has_one_attached :introduction_file has_one_attached :introduction_file
has_one :expert, through: :experts_procedure
validates :piece_justificative_file, validates :piece_justificative_file,
content_type: AUTHORIZED_CONTENT_TYPES, content_type: AUTHORIZED_CONTENT_TYPES,
@ -64,16 +67,6 @@ class Avis < ApplicationRecord
Avis.find_by(id: avis_id)&.email == email Avis.find_by(id: avis_id)&.email == email
end end
def self.invited_expert_emails(procedure)
Avis
.joins(dossier: :revision)
.left_joins(instructeur: :user)
.where(procedure_revisions: { procedure_id: procedure })
.map(&:email_to_display)
.uniq
.sort
end
def spreadsheet_columns def spreadsheet_columns
[ [
['Dossier ID', dossier_id.to_s], ['Dossier ID', dossier_id.to_s],

View file

@ -7,8 +7,8 @@ class Entreprise < Hashie::Dash
property :siren property :siren
property :capital_social property :capital_social
property :numero_tva_intracommunautaire property :numero_tva_intracommunautaire
property :forme_juridique property :forme_juridique, default: nil
property :forme_juridique_code property :forme_juridique_code, default: nil
property :nom_commercial property :nom_commercial
property :raison_sociale property :raison_sociale
property :siret_siege_social property :siret_siege_social
@ -19,8 +19,8 @@ class Entreprise < Hashie::Dash
property :effectif_annuel property :effectif_annuel
property :effectif_annuel_annee property :effectif_annuel_annee
property :date_creation property :date_creation
property :nom property :nom, default: nil
property :prenom property :prenom, default: nil
property :inline_adresse property :inline_adresse
end end

View file

@ -48,6 +48,8 @@
# entreprise_id :integer # entreprise_id :integer
# #
class Etablissement < ApplicationRecord class Etablissement < ApplicationRecord
self.ignored_columns = [:entreprise_id]
belongs_to :dossier, optional: true belongs_to :dossier, optional: true
has_one :champ, class_name: 'Champs::SiretChamp' has_one :champ, class_name: 'Champs::SiretChamp'

15
app/models/expert.rb Normal file
View file

@ -0,0 +1,15 @@
# == Schema Information
#
# Table name: experts
#
# id :bigint not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
#
class Expert < ApplicationRecord
has_one :user
def email
user.email
end
end

View file

@ -0,0 +1,28 @@
# == Schema Information
#
# Table name: experts_procedures
#
# id :bigint not null, primary key
# allow_decision_access :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# expert_id :bigint not null
# procedure_id :bigint not null
#
class ExpertsProcedure < ApplicationRecord
belongs_to :expert
belongs_to :procedure
has_many :avis, dependent: :destroy
def email_to_display
expert&.email
end
def self.invited_expert_emails(procedure)
joins(:expert)
.where(procedure: procedure)
.map(&:email_to_display)
.sort
end
end

View file

@ -62,13 +62,16 @@ class Procedure < ApplicationRecord
belongs_to :published_revision, class_name: 'ProcedureRevision', optional: true belongs_to :published_revision, class_name: 'ProcedureRevision', optional: true
has_many :deleted_dossiers, dependent: :destroy has_many :deleted_dossiers, dependent: :destroy
has_many :published_types_de_champ, -> { ordered }, through: :published_revision, source: :types_de_champ has_many :published_types_de_champ, through: :published_revision, source: :types_de_champ
has_many :published_types_de_champ_private, -> { ordered }, through: :published_revision, source: :types_de_champ_private has_many :published_types_de_champ_private, through: :published_revision, source: :types_de_champ_private
has_many :draft_types_de_champ, -> { ordered }, through: :draft_revision, source: :types_de_champ has_many :draft_types_de_champ, through: :draft_revision, source: :types_de_champ
has_many :draft_types_de_champ_private, -> { ordered }, through: :draft_revision, source: :types_de_champ_private has_many :draft_types_de_champ_private, through: :draft_revision, source: :types_de_champ_private
has_many :all_types_de_champ, -> { joins(:procedure).where('types_de_champ.revision_id != procedures.draft_revision_id').ordered }, through: :revisions, source: :types_de_champ has_many :all_types_de_champ, -> { joins(:procedure).where('types_de_champ.revision_id != procedures.draft_revision_id') }, through: :revisions, source: :types_de_champ
has_many :all_types_de_champ_private, -> { joins(:procedure).where('types_de_champ.revision_id != procedures.draft_revision_id').ordered }, through: :revisions, source: :types_de_champ_private has_many :all_types_de_champ_private, -> { joins(:procedure).where('types_de_champ.revision_id != procedures.draft_revision_id') }, through: :revisions, source: :types_de_champ_private
has_many :experts_procedures, dependent: :destroy
has_many :experts, through: :experts_procedures
has_one :module_api_carto, dependent: :destroy has_one :module_api_carto, dependent: :destroy
has_one :attestation_template, dependent: :destroy has_one :attestation_template, dependent: :destroy

View file

@ -25,6 +25,7 @@
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# administrateur_id :bigint # administrateur_id :bigint
# expert_id :bigint
# instructeur_id :bigint # instructeur_id :bigint
# #
class User < ApplicationRecord class User < ApplicationRecord
@ -48,6 +49,7 @@ class User < ApplicationRecord
has_one :france_connect_information, dependent: :destroy has_one :france_connect_information, dependent: :destroy
belongs_to :instructeur, optional: true belongs_to :instructeur, optional: true
belongs_to :administrateur, optional: true belongs_to :administrateur, optional: true
belongs_to :expert, optional: true
accepts_nested_attributes_for :france_connect_information accepts_nested_attributes_for :france_connect_information
@ -128,6 +130,20 @@ class User < ApplicationRecord
user user
end end
def self.create_or_promote_to_expert(email, password)
user = User
.create_with(password: password, confirmed_at: Time.zone.now)
.find_or_create_by(email: email)
if user.valid?
if user.expert_id.nil?
user.create_expert!
end
end
user
end
def flipper_id def flipper_id
"User:#{id}" "User:#{id}"
end end

View file

@ -167,11 +167,9 @@
"required": [ "required": [
"identifiant_de_l_etablissement", "identifiant_de_l_etablissement",
"nom_etablissement", "nom_etablissement",
"statut_public_prive",
"type_contrat_prive", "type_contrat_prive",
"nom_commune", "nom_commune",
"code_commune", "code_commune",
"nombre_d_eleves",
"siren_siret", "siren_siret",
"libelle_academie", "libelle_academie",
"code_academie", "code_academie",

View file

@ -25,9 +25,11 @@
%tr %tr
%th.libelle Type de contrat privé : %th.libelle Type de contrat privé :
%td= champ.data['type_contrat_prive'] %td= champ.data['type_contrat_prive']
%tr
%th.libelle Nombre délèves : - if champ.data['nombre_d_eleves'].present?
%td= champ.data['nombre_d_eleves'] %tr
%th.libelle Nombre délèves :
%td= champ.data['nombre_d_eleves']
%tr %tr
%th.libelle Adresse : %th.libelle Adresse :

View file

@ -145,6 +145,6 @@
%td= try_format_date(etablissement.association_date_declaration) %td= try_format_date(etablissement.association_date_declaration)
%p %p
= link_to "➡ Autres informations sur lorganisme sur « entreprise.data.gouv.fr » (ex: fiche d'immatriculation RNCS)", = link_to "➡ Autres informations sur lorganisme sur « annuaire-entreprises.data.gouv.fr » (ex: fiche d'immatriculation RNCS)",
"https://entreprise.data.gouv.fr/etablissement/#{etablissement.siret}", "https://annuaire-entreprises.data.gouv.fr/entreprise/#{etablissement.siren}",
target: "_blank" target: "_blank"

View file

@ -30,6 +30,6 @@
- if procedure.api_entreprise_role?("bilans_bdf") - if procedure.api_entreprise_role?("bilans_bdf")
%p.etablissement-exercices Les 3 derniers bilans connus de votre entreprise par la Banque de France ont été joints à votre dossier. %p.etablissement-exercices Les 3 derniers bilans connus de votre entreprise par la Banque de France ont été joints à votre dossier.
%p %p
= link_to '➡ Autres informations sur lorganisme sur « entreprise.data.gouv.fr »', = link_to "➡ Autres informations sur lorganisme sur « annuaire-entreprises.data.gouv.fr »",
"https://entreprise.data.gouv.fr/etablissement/#{etablissement.siret}", "https://annuaire-entreprises.data.gouv.fr/entreprise/#{etablissement.siren}",
target: "_blank" target: "_blank"

View file

@ -12,8 +12,8 @@
%p.mb-4 %p.mb-4
Pour trouver votre numéro SIRET, utilisez Pour trouver votre numéro SIRET, utilisez
%a{ href: 'https://entreprise.data.gouv.fr/', target: '_blank', rel: 'noopener' } %a{ href: "https://annuaire-entreprises.data.gouv.fr" , target: '_blank', rel: 'noopener' }
entreprise.data.gouv.fr annuaire-entreprises.data.gouv.fr
ou renseignez-vous auprès de votre service comptable. ou renseignez-vous auprès de votre service comptable.
= f.submit "Valider", class: "button large primary expand mt-1", data: { disable_with: "Récupération des informations…" } = f.submit "Valider", class: "button large primary expand mt-1", data: { disable_with: "Récupération des informations…" }

View file

@ -0,0 +1,7 @@
class CreateExperts < ActiveRecord::Migration[6.0]
def change
create_table :experts do |t| # rubocop:disable Style/SymbolProc
t.timestamps
end
end
end

View file

@ -0,0 +1,6 @@
class LinkUserAndExpert < ActiveRecord::Migration[6.0]
def change
add_reference :users, :expert, index: true
add_foreign_key :users, :experts
end
end

View file

@ -0,0 +1,11 @@
class CreateExpertsProcedures < ActiveRecord::Migration[6.0]
def change
create_table :experts_procedures do |t|
t.references :expert, null: false, foreign_key: true
t.references :procedure, null: false, foreign_key: true
t.boolean :allow_decision_access, default: false, null: false
t.timestamps
end
end
end

View file

@ -0,0 +1,5 @@
class AddExpertsProcedureToAvis < ActiveRecord::Migration[6.0]
def change
add_reference :avis, :experts_procedure, foreign_key: true
end
end

View file

@ -0,0 +1,5 @@
class AddUniqueIndexToExpertsProcedures < ActiveRecord::Migration[6.0]
def change
add_index :experts_procedures, [:expert_id, :procedure_id], unique: true
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_01_14_224721) do ActiveRecord::Schema.define(version: 2021_01_21_134435) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -121,8 +121,10 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
t.integer "claimant_id", null: false t.integer "claimant_id", null: false
t.boolean "confidentiel", default: false, null: false t.boolean "confidentiel", default: false, null: false
t.datetime "revoked_at" t.datetime "revoked_at"
t.bigint "experts_procedure_id"
t.index ["claimant_id"], name: "index_avis_on_claimant_id" t.index ["claimant_id"], name: "index_avis_on_claimant_id"
t.index ["dossier_id"], name: "index_avis_on_dossier_id" t.index ["dossier_id"], name: "index_avis_on_dossier_id"
t.index ["experts_procedure_id"], name: "index_avis_on_experts_procedure_id"
t.index ["instructeur_id"], name: "index_avis_on_instructeur_id" t.index ["instructeur_id"], name: "index_avis_on_instructeur_id"
end end
@ -321,6 +323,22 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
t.datetime "updated_at" t.datetime "updated_at"
end end
create_table "experts", force: :cascade do |t|
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "experts_procedures", force: :cascade do |t|
t.bigint "expert_id", null: false
t.bigint "procedure_id", null: false
t.boolean "allow_decision_access", default: false, null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["expert_id", "procedure_id"], name: "index_experts_procedures_on_expert_id_and_procedure_id", unique: true
t.index ["expert_id"], name: "index_experts_procedures_on_expert_id"
t.index ["procedure_id"], name: "index_experts_procedures_on_procedure_id"
end
create_table "exports", force: :cascade do |t| create_table "exports", force: :cascade do |t|
t.string "format", null: false t.string "format", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
@ -676,9 +694,11 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
t.datetime "locked_at" t.datetime "locked_at"
t.bigint "instructeur_id" t.bigint "instructeur_id"
t.bigint "administrateur_id" t.bigint "administrateur_id"
t.bigint "expert_id"
t.index ["administrateur_id"], name: "index_users_on_administrateur_id" t.index ["administrateur_id"], name: "index_users_on_administrateur_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["expert_id"], name: "index_users_on_expert_id"
t.index ["instructeur_id"], name: "index_users_on_instructeur_id" t.index ["instructeur_id"], name: "index_users_on_instructeur_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
@ -706,6 +726,7 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
add_foreign_key "assign_tos", "groupe_instructeurs" add_foreign_key "assign_tos", "groupe_instructeurs"
add_foreign_key "attestation_templates", "procedures" add_foreign_key "attestation_templates", "procedures"
add_foreign_key "attestations", "dossiers" add_foreign_key "attestations", "dossiers"
add_foreign_key "avis", "experts_procedures"
add_foreign_key "avis", "instructeurs", column: "claimant_id" add_foreign_key "avis", "instructeurs", column: "claimant_id"
add_foreign_key "champs", "champs", column: "parent_id" add_foreign_key "champs", "champs", column: "parent_id"
add_foreign_key "closed_mails", "procedures" add_foreign_key "closed_mails", "procedures"
@ -715,6 +736,8 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
add_foreign_key "dossiers", "groupe_instructeurs" add_foreign_key "dossiers", "groupe_instructeurs"
add_foreign_key "dossiers", "procedure_revisions", column: "revision_id" add_foreign_key "dossiers", "procedure_revisions", column: "revision_id"
add_foreign_key "dossiers", "users" add_foreign_key "dossiers", "users"
add_foreign_key "experts_procedures", "experts"
add_foreign_key "experts_procedures", "procedures"
add_foreign_key "feedbacks", "users" add_foreign_key "feedbacks", "users"
add_foreign_key "geo_areas", "champs" add_foreign_key "geo_areas", "champs"
add_foreign_key "groupe_instructeurs", "procedures" add_foreign_key "groupe_instructeurs", "procedures"
@ -734,6 +757,7 @@ ActiveRecord::Schema.define(version: 2021_01_14_224721) do
add_foreign_key "types_de_champ", "procedure_revisions", column: "revision_id" add_foreign_key "types_de_champ", "procedure_revisions", column: "revision_id"
add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id" add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id"
add_foreign_key "users", "administrateurs" add_foreign_key "users", "administrateurs"
add_foreign_key "users", "experts"
add_foreign_key "users", "instructeurs" add_foreign_key "users", "instructeurs"
add_foreign_key "without_continuation_mails", "procedures" add_foreign_key "without_continuation_mails", "procedures"
end end

View file

@ -0,0 +1,28 @@
namespace :after_party do
desc 'Deployment task: backfill_expert_id_on_avis_table'
task backfill_experts_procedure_id_on_avis_table: :environment do
puts "Running deploy task 'backfill_expert_id_on_avis_table'"
# rubocop:disable DS/Unscoped
# rubocop:disable Rails/PluckInWhere
Instructeur.includes(:user)
.where(id: Avis.unscoped.pluck(:instructeur_id))
.where.not(users: { instructeur_id: nil })
.find_each do |instructeur|
user = instructeur.user
User.create_or_promote_to_expert(user.email, SecureRandom.hex)
user.reload
# rubocop:enable DS/Unscoped
# rubocop:enable Rails/PluckInWhere
instructeur.avis.each do |avis|
experts_procedure = ExpertsProcedure.find_or_create_by(expert: user.expert, procedure: avis.procedure)
avis.update_column(:experts_procedure_id, experts_procedure.id)
end
end
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end

14
spec/factories/expert.rb Normal file
View file

@ -0,0 +1,14 @@
FactoryBot.define do
sequence(:create_expert_email) { |n| "expert#{n}@expert.com" }
factory :expert do
transient do
email { generate(:expert_email) }
password { 'somethingverycomplated!' }
end
initialize_with do
User.create_or_promote_to_expert(email, password).expert
end
end
end

View file

@ -56,6 +56,23 @@ RSpec.describe Avis, type: :model do
end end
end end
describe "an avis is linked to an expert_procedure" do
let(:procedure) { create(:procedure) }
let(:expert) { create(:expert) }
let(:experts_procedure) { ExpertsProcedure.create(procedure: procedure, expert: expert) }
context 'an avis is linked to an experts_procedure' do
let!(:avis) { create(:avis, email: nil, experts_procedure: experts_procedure) }
before do
avis.reload
end
it { expect(avis.email).to be_nil }
it { expect(avis.experts_procedure).to eq(experts_procedure) }
end
end
describe '.avis_exists_and_email_belongs_to_avis?' do describe '.avis_exists_and_email_belongs_to_avis?' do
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier) }
let(:invited_email) { 'invited@avis.com' } let(:invited_email) { 'invited@avis.com' }
@ -200,46 +217,4 @@ RSpec.describe Avis, type: :model do
end end
end end
end end
describe '#invited_expert_emails' do
let!(:procedure) { create(:procedure, :published) }
subject { Avis.invited_expert_emails(procedure) }
context 'when there is one dossier' do
let!(:dossier) { create(:dossier, procedure: procedure) }
context 'when a procedure has one avis and unknown instructeur' do
let!(:avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') }
it { is_expected.to eq(['expert@expert.com']) }
end
context 'when a procedure has one avis and known instructeur' do
let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: 'expert@expert.com')) }
it { is_expected.to eq(['expert@expert.com']) }
end
context 'when a dossier has 2 avis from the same expert' do
let!(:avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') }
let!(:avis2) { create(:avis, dossier: dossier, email: 'expert@expert.com') }
it { is_expected.to eq(['expert@expert.com']) }
end
end
context 'when there are two dossiers' do
let!(:dossier) { create(:dossier, procedure: procedure) }
let!(:dossier2) { create(:dossier, procedure: procedure) }
context 'and each one has an avis from 3 different experts' do
let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '2_expert@expert.com')) }
let!(:unaffected_avis) { create(:avis, dossier: dossier2, email: '3_expert@expert.com') }
let!(:unaffected_avis2) { create(:avis, dossier: dossier2, email: '1_expert@expert.com') }
it { is_expected.to eq(['1_expert@expert.com', '2_expert@expert.com', '3_expert@expert.com']) }
end
end
end
end end

View file

@ -0,0 +1,15 @@
RSpec.describe Expert, type: :model do
describe 'an expert could be add to a procedure' do
let(:procedure) { create(:procedure) }
let(:expert) { create(:expert) }
before do
procedure.experts << expert
procedure.reload
end
it { expect(procedure.experts).to eq([expert]) }
it { expect(ExpertsProcedure.where(expert: expert, procedure: procedure).count).to eq(1) }
it { expect(ExpertsProcedure.where(expert: expert, procedure: procedure).first.allow_decision_access).to be_falsy }
end
end

View file

@ -0,0 +1,42 @@
RSpec.describe ExpertsProcedure, type: :model do
describe '#invited_expert_emails' do
let!(:procedure) { create(:procedure, :published) }
let(:expert) { create(:expert) }
let(:expert2) { create(:expert) }
let(:expert3) { create(:expert) }
let(:experts_procedure) { ExpertsProcedure.create(expert: expert, procedure: procedure) }
let(:experts_procedure2) { ExpertsProcedure.create(expert: expert2, procedure: procedure) }
let(:experts_procedure3) { ExpertsProcedure.create(expert: expert3, procedure: procedure) }
subject { ExpertsProcedure.invited_expert_emails(procedure) }
context 'when there is one dossier' do
let!(:dossier) { create(:dossier, procedure: procedure) }
context 'when a procedure has one avis and known instructeur' do
let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: expert.email), experts_procedure: experts_procedure) }
it { is_expected.to eq([expert.email]) }
end
context 'when a dossier has 2 avis from the same expert' do
let!(:avis) { create(:avis, dossier: dossier, experts_procedure: experts_procedure) }
let!(:avis2) { create(:avis, dossier: dossier, experts_procedure: experts_procedure) }
it { is_expected.to eq([expert.email]) }
end
end
context 'when there are two dossiers' do
let!(:dossier) { create(:dossier, procedure: procedure) }
let!(:dossier2) { create(:dossier, procedure: procedure) }
context 'and each one has an avis from 3 different experts' do
let!(:avis) { create(:avis, dossier: dossier, experts_procedure: experts_procedure) }
let!(:avis2) { create(:avis, dossier: dossier2, experts_procedure: experts_procedure2) }
let!(:avis3) { create(:avis, dossier: dossier2, experts_procedure: experts_procedure3) }
it { is_expected.to eq([expert.email, expert2.email, expert3.email].sort) }
end
end
end
end

View file

@ -51,16 +51,22 @@ describe ProcedureRevision do
it 'move down' do it 'move down' do
expect(revision.types_de_champ.index(type_de_champ)).to eq(0) expect(revision.types_de_champ.index(type_de_champ)).to eq(0)
type_de_champ.update(order_place: nil)
revision.move_type_de_champ(type_de_champ.stable_id, 2) revision.move_type_de_champ(type_de_champ.stable_id, 2)
revision.reload revision.reload
expect(revision.types_de_champ.index(type_de_champ)).to eq(2) expect(revision.types_de_champ.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ_for_export.index(type_de_champ)).to eq(2)
end end
it 'move up' do it 'move up' do
expect(revision.types_de_champ.index(last_type_de_champ)).to eq(3) expect(revision.types_de_champ.index(last_type_de_champ)).to eq(3)
last_type_de_champ.update(order_place: nil)
revision.move_type_de_champ(last_type_de_champ.stable_id, 0) revision.move_type_de_champ(last_type_de_champ.stable_id, 0)
revision.reload revision.reload
expect(revision.types_de_champ.index(last_type_de_champ)).to eq(0) expect(revision.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ_for_export.index(last_type_de_champ)).to eq(0)
end end
context 'repetition' do context 'repetition' do

View file

@ -163,6 +163,57 @@ describe User, type: :model do
end end
end end
describe '.create_or_promote_to_expert' do
let(:email) { 'exp1@gmail.com' }
let(:password) { 'un super expert !' }
subject { User.create_or_promote_to_expert(email, password) }
context 'with an invalid email' do
let(:email) { 'invalid' }
it 'does not build an expert' do
user = subject
expect(user.valid?).to be false
expect(user.expert).to be_nil
end
end
context 'without an existing user' do
it do
user = subject
expect(user.valid_password?(password)).to be true
expect(user.confirmed_at).to be_present
expect(user.expert).to be_present
end
end
context 'with an existing user' do
before { create(:user, email: email, password: 'my-s3cure-p4ssword') }
it 'keeps the previous password' do
user = subject
expect(user.valid_password?('my-s3cure-p4ssword')).to be true
expect(user.expert).to be_present
end
context 'with an existing expert' do
let!(:expert) { Expert.create }
before do
User
.find_by(email: email)
.update!(expert: expert)
end
it 'keeps the existing experts' do
user = subject
expect(user.expert).to eq(expert)
end
end
end
end
describe 'invite_administrateur!' do describe 'invite_administrateur!' do
let(:super_admin) { create(:super_admin) } let(:super_admin) { create(:super_admin) }
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }

View file

@ -12,7 +12,7 @@ describe 'new_administrateur/procedures/invited_expert_list.html.haml', type: :v
context 'when the procedure has 0 avis' do context 'when the procedure has 0 avis' do
let!(:dossier) { create(:dossier, procedure: procedure) } let!(:dossier) { create(:dossier, procedure: procedure) }
before do before do
@invited_expert_emails = Avis.invited_expert_emails(procedure) @invited_expert_emails = ExpertsProcedure.invited_expert_emails(procedure)
subject subject
end end
@ -24,18 +24,21 @@ describe 'new_administrateur/procedures/invited_expert_list.html.haml', type: :v
context 'when the procedure has 3 avis from 2 experts and 1 unasigned' do context 'when the procedure has 3 avis from 2 experts and 1 unasigned' do
let!(:dossier) { create(:dossier, procedure: procedure) } let!(:dossier) { create(:dossier, procedure: procedure) }
let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '1_expert@expert.com')) } let(:expert) { create(:expert) }
let!(:avis2) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '2_expert@expert.com')) } let(:expert2) { create(:expert) }
let!(:unasigned_avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') } let(:experts_procedure) { ExpertsProcedure.create(procedure: procedure, expert: expert) }
let(:experts_procedure2) { ExpertsProcedure.create(procedure: procedure, expert: expert2) }
let!(:avis) { create(:avis, dossier: dossier, experts_procedure: experts_procedure) }
let!(:avis2) { create(:avis, dossier: dossier, experts_procedure: experts_procedure2) }
before do before do
@invited_expert_emails = Avis.invited_expert_emails(procedure) @invited_expert_emails = ExpertsProcedure.invited_expert_emails(procedure)
subject subject
end end
it 'has 3 experts and match array' do it 'has 2 experts and match array' do
expect(@invited_expert_emails.count).to eq(3) expect(@invited_expert_emails.count).to eq(2)
expect(@invited_expert_emails).to eq(['1_expert@expert.com', '2_expert@expert.com', 'expert@expert.com']) expect(@invited_expert_emails).to eq([expert.email, expert2.email])
end end
end end
end end