commit
0aa384c40b
31 changed files with 356 additions and 104 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!
|
||||||
|
|
|
@ -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 l’entreprise. -1 si inconnu."
|
field :capital_social, GraphQL::Types::BigInt, null: false, description: "capital social de l’entreprise. -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 d’une année"
|
field :effectif_annuel, EffectifType, null: true, description: "effectif moyen d’une 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
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
15
app/models/expert.rb
Normal 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
|
28
app/models/experts_procedure.rb
Normal file
28
app/models/experts_procedure.rb
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 :
|
||||||
|
|
|
@ -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 l’organisme sur « entreprise.data.gouv.fr » (ex: fiche d'immatriculation RNCS)",
|
= link_to "➡ Autres informations sur l’organisme 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"
|
||||||
|
|
|
@ -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 l’organisme sur « entreprise.data.gouv.fr »',
|
= link_to "➡ Autres informations sur l’organisme 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"
|
||||||
|
|
|
@ -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…" }
|
||||||
|
|
7
db/migrate/20210107143316_create_experts.rb
Normal file
7
db/migrate/20210107143316_create_experts.rb
Normal 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
|
6
db/migrate/20210107143938_link_user_and_expert.rb
Normal file
6
db/migrate/20210107143938_link_user_and_expert.rb
Normal 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
|
11
db/migrate/20210112120658_create_experts_procedures.rb
Normal file
11
db/migrate/20210112120658_create_experts_procedures.rb
Normal 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
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddExpertsProcedureToAvis < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_reference :avis, :experts_procedure, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddUniqueIndexToExpertsProcedures < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_index :experts_procedures, [:expert_id, :procedure_id], unique: true
|
||||||
|
end
|
||||||
|
end
|
26
db/schema.rb
26
db/schema.rb
|
@ -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
|
||||||
|
|
|
@ -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
14
spec/factories/expert.rb
Normal 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
|
|
@ -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
|
||||||
|
|
15
spec/models/expert_spec.rb
Normal file
15
spec/models/expert_spec.rb
Normal 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
|
42
spec/models/experts_procedure_spec.rb
Normal file
42
spec/models/experts_procedure_spec.rb
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue