2020-08-06 16:35:45 +02:00
|
|
|
|
# == Schema Information
|
|
|
|
|
#
|
|
|
|
|
# Table name: dossiers
|
|
|
|
|
#
|
|
|
|
|
# id :integer not null, primary key
|
2021-02-04 19:23:40 +01:00
|
|
|
|
# api_entreprise_job_exceptions :string is an Array
|
2020-08-06 16:35:45 +02:00
|
|
|
|
# archived :boolean default(FALSE)
|
|
|
|
|
# autorisation_donnees :boolean
|
|
|
|
|
# brouillon_close_to_expiration_notice_sent_at :datetime
|
2021-05-05 21:05:33 +02:00
|
|
|
|
# conservation_extension :interval default(0 seconds)
|
2021-06-04 12:03:31 +02:00
|
|
|
|
# declarative_triggered_at :datetime
|
2021-05-01 12:20:24 +02:00
|
|
|
|
# deleted_user_email_never_send :string
|
2021-11-24 12:27:32 +01:00
|
|
|
|
# depose_at :datetime
|
2020-08-06 16:35:45 +02:00
|
|
|
|
# en_construction_at :datetime
|
|
|
|
|
# en_construction_close_to_expiration_notice_sent_at :datetime
|
|
|
|
|
# en_instruction_at :datetime
|
2022-05-18 14:25:40 +02:00
|
|
|
|
# for_procedure_preview :boolean default(FALSE)
|
2020-08-06 16:35:45 +02:00
|
|
|
|
# groupe_instructeur_updated_at :datetime
|
|
|
|
|
# hidden_at :datetime
|
2021-12-21 12:44:57 +01:00
|
|
|
|
# hidden_by_administration_at :datetime
|
2022-02-08 10:47:43 +01:00
|
|
|
|
# hidden_by_reason :string
|
2021-11-10 17:36:24 +01:00
|
|
|
|
# hidden_by_user_at :datetime
|
2021-10-04 10:03:06 +02:00
|
|
|
|
# identity_updated_at :datetime
|
2020-08-06 16:35:45 +02:00
|
|
|
|
# last_avis_updated_at :datetime
|
|
|
|
|
# last_champ_private_updated_at :datetime
|
|
|
|
|
# last_champ_updated_at :datetime
|
|
|
|
|
# last_commentaire_updated_at :datetime
|
|
|
|
|
# motivation :text
|
|
|
|
|
# private_search_terms :text
|
|
|
|
|
# processed_at :datetime
|
|
|
|
|
# search_terms :text
|
|
|
|
|
# state :string
|
|
|
|
|
# termine_close_to_expiration_notice_sent_at :datetime
|
|
|
|
|
# created_at :datetime
|
|
|
|
|
# updated_at :datetime
|
2021-09-07 10:36:09 +02:00
|
|
|
|
# dossier_transfer_id :bigint
|
2020-08-06 16:35:45 +02:00
|
|
|
|
# groupe_instructeur_id :bigint
|
|
|
|
|
# revision_id :bigint
|
|
|
|
|
# user_id :integer
|
|
|
|
|
#
|
2018-03-06 13:44:29 +01:00
|
|
|
|
class Dossier < ApplicationRecord
|
2021-05-04 16:29:29 +02:00
|
|
|
|
self.ignored_columns = [:en_construction_conservation_extension]
|
2019-02-27 12:03:53 +01:00
|
|
|
|
include DossierFilteringConcern
|
2021-10-13 14:41:05 +02:00
|
|
|
|
include DossierRebaseConcern
|
2019-02-27 12:03:53 +01:00
|
|
|
|
|
2017-05-26 18:27:51 +02:00
|
|
|
|
enum state: {
|
2017-12-04 20:23:57 +01:00
|
|
|
|
brouillon: 'brouillon',
|
|
|
|
|
en_construction: 'en_construction',
|
|
|
|
|
en_instruction: 'en_instruction',
|
|
|
|
|
accepte: 'accepte',
|
|
|
|
|
refuse: 'refuse',
|
|
|
|
|
sans_suite: 'sans_suite'
|
2017-05-26 18:27:51 +02:00
|
|
|
|
}
|
2015-09-22 18:30:20 +02:00
|
|
|
|
|
2018-08-28 14:10:55 +02:00
|
|
|
|
EN_CONSTRUCTION_OU_INSTRUCTION = [states.fetch(:en_construction), states.fetch(:en_instruction)]
|
|
|
|
|
TERMINE = [states.fetch(:accepte), states.fetch(:refuse), states.fetch(:sans_suite)]
|
|
|
|
|
INSTRUCTION_COMMENCEE = TERMINE + [states.fetch(:en_instruction)]
|
2018-01-18 11:39:05 +01:00
|
|
|
|
SOUMIS = EN_CONSTRUCTION_OU_INSTRUCTION + TERMINE
|
2017-05-26 18:22:31 +02:00
|
|
|
|
|
2020-03-25 04:09:14 +01:00
|
|
|
|
REMAINING_DAYS_BEFORE_CLOSING = 2
|
2020-03-25 10:38:17 +01:00
|
|
|
|
INTERVAL_BEFORE_CLOSING = "#{REMAINING_DAYS_BEFORE_CLOSING} days"
|
2021-11-17 10:53:43 +01:00
|
|
|
|
REMAINING_WEEKS_BEFORE_EXPIRATION = 2
|
|
|
|
|
INTERVAL_BEFORE_EXPIRATION = "#{REMAINING_WEEKS_BEFORE_EXPIRATION} weeks"
|
|
|
|
|
MONTHS_AFTER_EXPIRATION = 1
|
|
|
|
|
DAYS_AFTER_EXPIRATION = 5
|
|
|
|
|
INTERVAL_EXPIRATION = "#{MONTHS_AFTER_EXPIRATION} month #{DAYS_AFTER_EXPIRATION} days"
|
2020-02-05 22:10:22 +01:00
|
|
|
|
|
2015-09-24 11:45:00 +02:00
|
|
|
|
has_one :etablissement, dependent: :destroy
|
2020-01-07 16:36:04 +01:00
|
|
|
|
has_one :individual, validate: false, dependent: :destroy
|
2018-11-28 16:13:58 +01:00
|
|
|
|
has_one :attestation, dependent: :destroy
|
2016-01-18 16:20:51 +01:00
|
|
|
|
|
2021-04-13 20:24:02 +02:00
|
|
|
|
# FIXME: some dossiers have more than one attestation
|
|
|
|
|
has_many :attestations, dependent: :destroy
|
|
|
|
|
|
2019-02-18 17:52:15 +01:00
|
|
|
|
has_one_attached :justificatif_motivation
|
|
|
|
|
|
2021-04-01 17:22:47 +02:00
|
|
|
|
has_many :champs, -> { root.public_ordered }, inverse_of: false, dependent: :destroy
|
|
|
|
|
has_many :champs_private, -> { root.private_ordered }, class_name: 'Champ', inverse_of: false, dependent: :destroy
|
2019-05-29 18:28:27 +02:00
|
|
|
|
has_many :commentaires, inverse_of: :dossier, dependent: :destroy
|
2016-02-08 18:16:18 +01:00
|
|
|
|
has_many :invites, dependent: :destroy
|
2019-06-12 19:10:53 +02:00
|
|
|
|
has_many :follows, -> { active }, inverse_of: :dossier
|
|
|
|
|
has_many :previous_follows, -> { inactive }, class_name: 'Follow', inverse_of: :dossier
|
2019-08-06 11:02:54 +02:00
|
|
|
|
has_many :followers_instructeurs, through: :follows, source: :instructeur
|
|
|
|
|
has_many :previous_followers_instructeurs, -> { distinct }, through: :previous_follows, source: :instructeur
|
2019-05-29 18:28:27 +02:00
|
|
|
|
has_many :avis, inverse_of: :dossier, dependent: :destroy
|
2021-02-25 10:10:24 +01:00
|
|
|
|
has_many :experts, through: :avis
|
2021-08-18 11:19:19 +02:00
|
|
|
|
has_many :traitements, -> { order(:processed_at) }, inverse_of: :dossier, dependent: :destroy do
|
2021-11-14 18:43:45 +01:00
|
|
|
|
def passer_en_construction(instructeur: nil, processed_at: Time.zone.now)
|
2021-11-04 19:05:04 +01:00
|
|
|
|
build(state: Dossier.states.fetch(:en_construction),
|
2021-11-14 18:43:45 +01:00
|
|
|
|
instructeur_email: instructeur&.email,
|
2021-11-04 19:05:04 +01:00
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
|
2021-11-14 18:43:45 +01:00
|
|
|
|
def passer_en_instruction(instructeur: nil, processed_at: Time.zone.now)
|
2021-11-04 19:05:04 +01:00
|
|
|
|
build(state: Dossier.states.fetch(:en_instruction),
|
2021-11-14 18:43:45 +01:00
|
|
|
|
instructeur_email: instructeur&.email,
|
2021-11-04 19:05:04 +01:00
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
|
2021-08-18 11:19:19 +02:00
|
|
|
|
def accepter_automatiquement(processed_at: Time.zone.now)
|
|
|
|
|
build(state: Dossier.states.fetch(:accepte),
|
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def accepter(motivation: nil, instructeur: nil, processed_at: Time.zone.now)
|
|
|
|
|
build(state: Dossier.states.fetch(:accepte),
|
|
|
|
|
instructeur_email: instructeur&.email,
|
|
|
|
|
motivation: motivation,
|
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def refuser(motivation: nil, instructeur: nil, processed_at: Time.zone.now)
|
|
|
|
|
build(state: Dossier.states.fetch(:refuse),
|
|
|
|
|
instructeur_email: instructeur&.email,
|
|
|
|
|
motivation: motivation,
|
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zone.now)
|
|
|
|
|
build(state: Dossier.states.fetch(:sans_suite),
|
|
|
|
|
instructeur_email: instructeur&.email,
|
|
|
|
|
motivation: motivation,
|
|
|
|
|
processed_at: processed_at)
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-11-04 19:05:04 +01:00
|
|
|
|
has_one :traitement, -> { order(processed_at: :desc) }, inverse_of: false
|
2016-01-18 16:20:51 +01:00
|
|
|
|
|
2020-11-17 13:46:13 +01:00
|
|
|
|
has_many :dossier_operation_logs, -> { order(:created_at) }, inverse_of: :dossier
|
2018-11-23 21:02:18 +01:00
|
|
|
|
|
2021-03-09 11:21:30 +01:00
|
|
|
|
belongs_to :groupe_instructeur, optional: true
|
2020-08-27 19:55:10 +02:00
|
|
|
|
belongs_to :revision, class_name: 'ProcedureRevision', optional: false
|
2021-05-11 17:49:29 +02:00
|
|
|
|
belongs_to :user, optional: true
|
2021-05-12 19:04:31 +02:00
|
|
|
|
has_one :france_connect_information, through: :user
|
2015-08-12 10:09:52 +02:00
|
|
|
|
|
2022-02-11 08:45:16 +01:00
|
|
|
|
has_one :attestation_template, through: :revision
|
2020-08-27 19:55:10 +02:00
|
|
|
|
has_one :procedure, through: :revision
|
2022-04-28 14:25:49 +02:00
|
|
|
|
has_many :types_de_champ, through: :revision, source: :types_de_champ_public
|
2020-08-27 19:55:10 +02:00
|
|
|
|
has_many :types_de_champ_private, through: :revision
|
|
|
|
|
|
2021-10-06 13:16:25 +02:00
|
|
|
|
belongs_to :transfer, class_name: 'DossierTransfer', foreign_key: 'dossier_transfer_id', optional: true, inverse_of: :dossiers
|
2021-09-07 10:36:09 +02:00
|
|
|
|
has_many :transfer_logs, class_name: 'DossierTransferLog', dependent: :destroy
|
|
|
|
|
|
2017-08-02 14:56:08 +02:00
|
|
|
|
accepts_nested_attributes_for :champs
|
2017-08-02 15:33:23 +02:00
|
|
|
|
accepts_nested_attributes_for :champs_private
|
2017-08-02 14:56:08 +02:00
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
include AASM
|
|
|
|
|
|
|
|
|
|
aasm whiny_persistence: true, column: :state, enum: true do
|
|
|
|
|
state :brouillon, initial: true
|
|
|
|
|
state :en_construction
|
|
|
|
|
state :en_instruction
|
|
|
|
|
state :accepte
|
|
|
|
|
state :refuse
|
|
|
|
|
state :sans_suite
|
|
|
|
|
|
|
|
|
|
event :passer_en_construction, after: :after_passer_en_construction do
|
|
|
|
|
transitions from: :brouillon, to: :en_construction
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :passer_en_instruction, after: :after_passer_en_instruction do
|
|
|
|
|
transitions from: :en_construction, to: :en_instruction
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :passer_automatiquement_en_instruction, after: :after_passer_automatiquement_en_instruction do
|
|
|
|
|
transitions from: :en_construction, to: :en_instruction
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :repasser_en_construction, after: :after_repasser_en_construction do
|
|
|
|
|
transitions from: :en_instruction, to: :en_construction
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :accepter, after: :after_accepter do
|
|
|
|
|
transitions from: :en_instruction, to: :accepte
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :accepter_automatiquement, after: :after_accepter_automatiquement do
|
|
|
|
|
transitions from: :en_construction, to: :accepte
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :refuser, after: :after_refuser do
|
|
|
|
|
transitions from: :en_instruction, to: :refuse
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :classer_sans_suite, after: :after_classer_sans_suite do
|
|
|
|
|
transitions from: :en_instruction, to: :sans_suite
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
event :repasser_en_instruction, after: :after_repasser_en_instruction do
|
2021-05-01 12:20:24 +02:00
|
|
|
|
transitions from: :refuse, to: :en_instruction, guard: :can_repasser_en_instruction?
|
|
|
|
|
transitions from: :sans_suite, to: :en_instruction, guard: :can_repasser_en_instruction?
|
|
|
|
|
transitions from: :accepte, to: :en_instruction, guard: :can_repasser_en_instruction?
|
2019-07-02 15:38:23 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-08-28 14:10:55 +02:00
|
|
|
|
scope :state_brouillon, -> { where(state: states.fetch(:brouillon)) }
|
|
|
|
|
scope :state_not_brouillon, -> { where.not(state: states.fetch(:brouillon)) }
|
|
|
|
|
scope :state_en_construction, -> { where(state: states.fetch(:en_construction)) }
|
2022-03-09 10:27:43 +01:00
|
|
|
|
scope :state_not_en_construction, -> { where.not(state: states.fetch(:en_construction)) }
|
2018-08-28 14:10:55 +02:00
|
|
|
|
scope :state_en_instruction, -> { where(state: states.fetch(:en_instruction)) }
|
2017-07-11 16:09:03 +02:00
|
|
|
|
scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) }
|
2018-06-13 17:32:50 +02:00
|
|
|
|
scope :state_instruction_commencee, -> { where(state: INSTRUCTION_COMMENCEE) }
|
2017-07-11 16:09:03 +02:00
|
|
|
|
scope :state_termine, -> { where(state: TERMINE) }
|
2021-11-10 17:36:24 +01:00
|
|
|
|
scope :state_not_termine, -> { where.not(state: TERMINE) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
|
2017-06-01 11:05:51 +02:00
|
|
|
|
scope :archived, -> { where(archived: true) }
|
|
|
|
|
scope :not_archived, -> { where(archived: false) }
|
2022-01-05 10:41:02 +01:00
|
|
|
|
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
|
2021-12-21 12:44:57 +01:00
|
|
|
|
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
|
2022-04-27 16:28:22 +02:00
|
|
|
|
scope :visible_by_user, -> { where(for_procedure_preview: false).or(where(for_procedure_preview: nil)).where(hidden_by_user_at: nil) }
|
2022-03-09 10:27:43 +01:00
|
|
|
|
scope :visible_by_administration, -> {
|
|
|
|
|
state_not_brouillon
|
|
|
|
|
.where(hidden_by_administration_at: nil)
|
|
|
|
|
.merge(visible_by_user.or(state_not_en_construction))
|
|
|
|
|
}
|
2022-03-30 17:24:08 +02:00
|
|
|
|
scope :visible_by_user_or_administration, -> { visible_by_user.or(visible_by_administration) }
|
2022-04-27 16:28:22 +02:00
|
|
|
|
scope :for_procedure_preview, -> { where(for_procedure_preview: true) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
|
|
|
|
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
2021-12-06 15:49:17 +01:00
|
|
|
|
scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
|
2019-09-26 14:57:58 +02:00
|
|
|
|
scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) }
|
2021-12-06 15:49:17 +01:00
|
|
|
|
scope :created_since, -> (since) { where('dossiers.depose_at >= ?', since) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
|
2020-10-30 15:01:13 +01:00
|
|
|
|
scope :with_type_de_champ, -> (stable_id) {
|
|
|
|
|
joins('INNER JOIN champs ON champs.dossier_id = dossiers.id INNER JOIN types_de_champ ON types_de_champ.id = champs.type_de_champ_id')
|
|
|
|
|
.where('types_de_champ.private = FALSE AND types_de_champ.stable_id = ?', stable_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scope :with_type_de_champ_private, -> (stable_id) {
|
|
|
|
|
joins('INNER JOIN champs ON champs.dossier_id = dossiers.id INNER JOIN types_de_champ ON types_de_champ.id = champs.type_de_champ_id')
|
|
|
|
|
.where('types_de_champ.private = TRUE AND types_de_champ.stable_id = ?', stable_id)
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-27 11:51:31 +02:00
|
|
|
|
scope :all_state, -> { not_archived.state_not_brouillon }
|
2017-12-05 16:14:02 +01:00
|
|
|
|
scope :en_construction, -> { not_archived.state_en_construction }
|
2017-09-27 11:51:31 +02:00
|
|
|
|
scope :en_instruction, -> { not_archived.state_en_instruction }
|
|
|
|
|
scope :termine, -> { not_archived.state_termine }
|
2021-11-26 15:06:32 +01:00
|
|
|
|
|
2022-04-01 18:20:12 +02:00
|
|
|
|
scope :processed_in_month, -> (date) do
|
2022-04-02 06:53:30 +02:00
|
|
|
|
date = date.to_datetime
|
2021-04-29 17:29:47 +02:00
|
|
|
|
state_termine
|
|
|
|
|
.joins(:traitements)
|
2022-04-01 18:20:12 +02:00
|
|
|
|
.where(traitements: { processed_at: date.beginning_of_month..date.end_of_month })
|
2021-04-29 17:29:47 +02:00
|
|
|
|
end
|
|
|
|
|
scope :downloadable_sorted, -> {
|
2019-07-30 15:39:20 +02:00
|
|
|
|
state_not_brouillon
|
2022-01-05 10:41:02 +01:00
|
|
|
|
.visible_by_administration
|
2019-07-30 15:39:20 +02:00
|
|
|
|
.includes(
|
|
|
|
|
:user,
|
|
|
|
|
:individual,
|
2019-08-06 11:02:54 +02:00
|
|
|
|
:followers_instructeurs,
|
2021-11-04 19:05:04 +01:00
|
|
|
|
:traitement,
|
2021-03-25 12:56:42 +01:00
|
|
|
|
:groupe_instructeur,
|
2022-06-13 22:54:57 +02:00
|
|
|
|
:etablissement,
|
2021-03-25 12:56:42 +01:00
|
|
|
|
procedure: [
|
|
|
|
|
:groupe_instructeurs,
|
|
|
|
|
:draft_types_de_champ,
|
|
|
|
|
:draft_types_de_champ_private,
|
|
|
|
|
:published_types_de_champ,
|
|
|
|
|
:published_types_de_champ_private
|
|
|
|
|
],
|
2022-06-13 22:54:57 +02:00
|
|
|
|
avis: [:claimant, :expert]
|
2021-12-06 15:49:17 +01:00
|
|
|
|
).order(depose_at: 'asc')
|
2019-07-30 15:39:20 +02:00
|
|
|
|
}
|
2017-09-27 11:51:31 +02:00
|
|
|
|
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
|
2017-10-10 18:35:00 +02:00
|
|
|
|
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
|
2022-03-11 00:17:36 +01:00
|
|
|
|
scope :with_champs, -> {
|
|
|
|
|
includes(champs: [
|
|
|
|
|
:type_de_champ,
|
|
|
|
|
:geo_areas,
|
|
|
|
|
piece_justificative_file_attachment: :blob,
|
|
|
|
|
champs: [:type_de_champ, piece_justificative_file_attachment: :blob]
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
scope :with_annotations, -> {
|
|
|
|
|
includes(champs_private: [
|
|
|
|
|
:type_de_champ,
|
|
|
|
|
:geo_areas,
|
|
|
|
|
piece_justificative_file_attachment: :blob,
|
|
|
|
|
champs: [:type_de_champ, piece_justificative_file_attachment: :blob]
|
|
|
|
|
])
|
|
|
|
|
}
|
2018-11-01 14:04:32 +01:00
|
|
|
|
scope :for_api, -> {
|
2022-03-11 00:17:36 +01:00
|
|
|
|
with_champs
|
|
|
|
|
.with_annotations
|
|
|
|
|
.includes(commentaires: { piece_jointe_attachment: :blob },
|
|
|
|
|
justificatif_motivation_attachment: :blob,
|
|
|
|
|
attestation: [],
|
|
|
|
|
avis: { piece_justificative_file_attachment: :blob },
|
|
|
|
|
traitement: [],
|
|
|
|
|
etablissement: [],
|
|
|
|
|
individual: [],
|
|
|
|
|
user: [])
|
2018-11-01 14:04:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 12:59:10 +01:00
|
|
|
|
scope :with_notifiable_procedure, -> (opts = { notify_on_closed: false }) do
|
|
|
|
|
states = opts[:notify_on_closed] ? [:publiee, :close, :depubliee] : [:publiee, :depubliee]
|
2020-03-24 12:50:59 +01:00
|
|
|
|
joins(:procedure)
|
2020-04-29 19:18:12 +02:00
|
|
|
|
.where(procedures: { aasm_state: states })
|
2021-05-01 12:20:24 +02:00
|
|
|
|
.where.not(user_id: nil)
|
2020-03-24 12:50:59 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-19 14:07:47 +01:00
|
|
|
|
scope :interval_brouillon_close_to_expiration, -> do
|
2022-03-30 17:24:08 +02:00
|
|
|
|
state_brouillon
|
|
|
|
|
.visible_by_user
|
2022-04-04 15:49:16 +02:00
|
|
|
|
.where("dossiers.created_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
|
2021-11-19 14:07:47 +01:00
|
|
|
|
end
|
|
|
|
|
scope :interval_en_construction_close_to_expiration, -> do
|
2022-03-30 17:24:08 +02:00
|
|
|
|
state_en_construction
|
|
|
|
|
.visible_by_user_or_administration
|
2022-04-04 15:49:16 +02:00
|
|
|
|
.where("dossiers.en_construction_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
|
2021-11-19 14:07:47 +01:00
|
|
|
|
end
|
2021-11-19 14:34:20 +01:00
|
|
|
|
scope :interval_termine_close_to_expiration, -> do
|
2021-11-30 14:47:19 +01:00
|
|
|
|
state_termine
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user_or_administration
|
2021-12-02 14:21:39 +01:00
|
|
|
|
.where(procedures: { procedure_expires_when_termine_enabled: true })
|
2022-04-04 15:49:16 +02:00
|
|
|
|
.where("dossiers.processed_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
|
2021-11-19 14:07:47 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-19 14:34:20 +01:00
|
|
|
|
scope :brouillon_close_to_expiration, -> do
|
|
|
|
|
joins(:procedure).interval_brouillon_close_to_expiration
|
|
|
|
|
end
|
|
|
|
|
scope :en_construction_close_to_expiration, -> do
|
|
|
|
|
joins(:procedure).interval_en_construction_close_to_expiration
|
|
|
|
|
end
|
2021-11-19 14:07:47 +01:00
|
|
|
|
scope :termine_close_to_expiration, -> do
|
2021-11-19 14:34:20 +01:00
|
|
|
|
joins(:procedure).interval_termine_close_to_expiration
|
2020-02-26 17:36:24 +01:00
|
|
|
|
end
|
2021-11-19 12:36:03 +01:00
|
|
|
|
|
2021-11-19 14:28:54 +01:00
|
|
|
|
scope :close_to_expiration, -> do
|
|
|
|
|
joins(:procedure).scoping do
|
2021-11-19 14:34:20 +01:00
|
|
|
|
interval_brouillon_close_to_expiration
|
|
|
|
|
.or(interval_en_construction_close_to_expiration)
|
|
|
|
|
.or(interval_termine_close_to_expiration)
|
2021-11-19 14:28:54 +01:00
|
|
|
|
end
|
2020-04-02 15:04:12 +02:00
|
|
|
|
end
|
2020-02-26 17:36:24 +01:00
|
|
|
|
|
2021-12-01 17:39:47 +01:00
|
|
|
|
scope :termine_or_en_construction_close_to_expiration, -> do
|
|
|
|
|
joins(:procedure).scoping do
|
|
|
|
|
interval_en_construction_close_to_expiration
|
|
|
|
|
.or(interval_termine_close_to_expiration)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-24 12:50:59 +01:00
|
|
|
|
scope :brouillon_expired, -> do
|
|
|
|
|
state_brouillon
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user
|
2020-03-25 10:38:17 +01:00
|
|
|
|
.where("brouillon_close_to_expiration_notice_sent_at + INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_EXPIRATION })
|
2020-03-24 12:50:59 +01:00
|
|
|
|
end
|
|
|
|
|
scope :en_construction_expired, -> do
|
|
|
|
|
state_en_construction
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user_or_administration
|
2020-03-25 10:38:17 +01:00
|
|
|
|
.where("en_construction_close_to_expiration_notice_sent_at + INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_EXPIRATION })
|
2020-03-24 12:50:59 +01:00
|
|
|
|
end
|
2020-04-02 15:04:12 +02:00
|
|
|
|
scope :termine_expired, -> do
|
|
|
|
|
state_termine
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user_or_administration
|
2020-04-02 15:04:12 +02:00
|
|
|
|
.where("termine_close_to_expiration_notice_sent_at + INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_EXPIRATION })
|
|
|
|
|
end
|
2020-02-26 17:36:24 +01:00
|
|
|
|
|
|
|
|
|
scope :without_brouillon_expiration_notice_sent, -> { where(brouillon_close_to_expiration_notice_sent_at: nil) }
|
|
|
|
|
scope :without_en_construction_expiration_notice_sent, -> { where(en_construction_close_to_expiration_notice_sent_at: nil) }
|
2020-04-02 15:04:12 +02:00
|
|
|
|
scope :without_termine_expiration_notice_sent, -> { where(termine_close_to_expiration_notice_sent_at: nil) }
|
2019-12-03 10:31:10 +01:00
|
|
|
|
|
2022-03-09 10:27:43 +01:00
|
|
|
|
scope :deleted_by_user_expired, -> { where('dossiers.hidden_by_user_at < ?', 1.week.ago) }
|
|
|
|
|
scope :deleted_by_administration_expired, -> { where('dossiers.hidden_by_administration_at < ?', 1.week.ago) }
|
|
|
|
|
scope :en_brouillon_expired_to_delete, -> { state_brouillon.deleted_by_user_expired }
|
|
|
|
|
scope :en_construction_expired_to_delete, -> { state_en_construction.deleted_by_user_expired }
|
|
|
|
|
scope :termine_expired_to_delete, -> { state_termine.deleted_by_user_expired.deleted_by_administration_expired }
|
2020-03-19 13:11:45 +01:00
|
|
|
|
|
2020-03-25 04:09:14 +01:00
|
|
|
|
scope :brouillon_near_procedure_closing_date, -> do
|
2020-02-05 22:10:22 +01:00
|
|
|
|
# select users who have submitted dossier for the given 'procedures.id'
|
|
|
|
|
users_who_submitted =
|
|
|
|
|
state_not_brouillon
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user
|
2021-03-09 11:21:25 +01:00
|
|
|
|
.joins(:revision)
|
|
|
|
|
.where("procedure_revisions.procedure_id = procedures.id")
|
2020-02-05 22:10:22 +01:00
|
|
|
|
.select(:user_id)
|
|
|
|
|
# select dossier in brouillon where procedure closes in two days and for which the user has not submitted a Dossier
|
2020-04-29 19:18:12 +02:00
|
|
|
|
state_brouillon
|
2022-03-30 17:24:08 +02:00
|
|
|
|
.visible_by_user
|
2020-04-29 19:18:12 +02:00
|
|
|
|
.with_notifiable_procedure
|
2020-03-25 10:38:17 +01:00
|
|
|
|
.where("procedures.auto_archive_on - INTERVAL :before_closing = :now", { now: Time.zone.today, before_closing: INTERVAL_BEFORE_CLOSING })
|
2020-02-05 22:10:22 +01:00
|
|
|
|
.where.not(user: users_who_submitted)
|
|
|
|
|
end
|
|
|
|
|
|
2022-06-11 12:41:47 +02:00
|
|
|
|
scope :for_api_v2, -> { includes(revision: [:attestation_template, procedure: [:administrateurs]], etablissement: [], individual: [], traitement: []) }
|
2019-09-18 13:07:30 +02:00
|
|
|
|
|
2020-09-18 15:40:26 +02:00
|
|
|
|
scope :with_notifications, -> do
|
2020-07-30 11:05:39 +02:00
|
|
|
|
joins(:follows)
|
|
|
|
|
.where('last_champ_updated_at > follows.demande_seen_at' \
|
2021-10-04 10:05:27 +02:00
|
|
|
|
' OR identity_updated_at > follows.demande_seen_at' \
|
2020-07-30 11:05:39 +02:00
|
|
|
|
' OR groupe_instructeur_updated_at > follows.demande_seen_at' \
|
|
|
|
|
' OR last_champ_private_updated_at > follows.annotations_privees_seen_at' \
|
|
|
|
|
' OR last_avis_updated_at > follows.avis_seen_at' \
|
|
|
|
|
' OR last_commentaire_updated_at > follows.messagerie_seen_at')
|
|
|
|
|
.distinct
|
2019-09-23 14:38:12 +02:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-31 16:33:14 +02:00
|
|
|
|
scope :by_statut, -> (instructeur, statut = 'tous') do
|
|
|
|
|
case statut
|
|
|
|
|
when 'a-suivre'
|
|
|
|
|
visible_by_administration
|
|
|
|
|
.without_followers
|
|
|
|
|
.en_cours
|
|
|
|
|
when 'suivis'
|
|
|
|
|
instructeur
|
|
|
|
|
.followed_dossiers
|
|
|
|
|
.en_cours
|
|
|
|
|
.merge(visible_by_administration)
|
|
|
|
|
when 'traites'
|
|
|
|
|
visible_by_administration.termine
|
|
|
|
|
when 'tous'
|
|
|
|
|
visible_by_administration.all_state
|
|
|
|
|
when 'supprimes_recemment'
|
|
|
|
|
hidden_by_administration.termine
|
|
|
|
|
when 'archives'
|
|
|
|
|
visible_by_administration.archived
|
|
|
|
|
when 'expirant'
|
|
|
|
|
visible_by_administration.termine_or_en_construction_close_to_expiration
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-08-30 11:18:43 +02:00
|
|
|
|
accepts_nested_attributes_for :individual
|
|
|
|
|
|
2018-04-23 11:57:38 +02:00
|
|
|
|
delegate :siret, :siren, to: :etablissement, allow_nil: true
|
2021-05-12 19:04:31 +02:00
|
|
|
|
delegate :france_connect_information, to: :user, allow_nil: true
|
2015-08-13 15:55:19 +02:00
|
|
|
|
|
2021-03-09 11:21:25 +01:00
|
|
|
|
before_save :build_default_champs, if: Proc.new { revision_id_was.nil? }
|
2018-07-25 19:34:06 +02:00
|
|
|
|
before_save :update_search_terms
|
2017-03-01 09:51:55 +01:00
|
|
|
|
|
2018-03-01 17:04:05 +01:00
|
|
|
|
after_save :send_web_hook
|
2020-09-28 17:38:49 +02:00
|
|
|
|
after_create_commit :send_draft_notification_email
|
2015-08-24 15:23:07 +02:00
|
|
|
|
|
2021-05-11 17:49:29 +02:00
|
|
|
|
validates :user, presence: true, if: -> { deleted_user_email_never_send.nil? }
|
2020-08-27 19:55:10 +02:00
|
|
|
|
validates :individual, presence: true, if: -> { revision.procedure.for_individual? }
|
2021-03-09 11:21:30 +01:00
|
|
|
|
validates :groupe_instructeur, presence: true, if: -> { !brouillon? }
|
2015-08-21 11:37:13 +02:00
|
|
|
|
|
2022-07-07 19:14:27 +02:00
|
|
|
|
def types_de_champ_public
|
|
|
|
|
types_de_champ
|
|
|
|
|
end
|
|
|
|
|
|
2022-06-13 22:54:57 +02:00
|
|
|
|
EXPORT_BATCH_SIZE = 2000
|
2021-10-20 16:52:38 +02:00
|
|
|
|
|
|
|
|
|
def self.downloadable_sorted_batch
|
2022-06-13 22:54:57 +02:00
|
|
|
|
ExportPreloader.new(self).in_batches
|
2021-10-20 16:52:38 +02:00
|
|
|
|
end
|
|
|
|
|
|
2022-06-13 22:54:57 +02:00
|
|
|
|
class ExportPreloader
|
|
|
|
|
def initialize(dossiers)
|
|
|
|
|
@dossiers = dossiers
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def in_batches
|
|
|
|
|
dossiers = @dossiers.downloadable_sorted.to_a
|
|
|
|
|
dossiers.each_slice(EXPORT_BATCH_SIZE) { |slice| load_dossiers(slice) }
|
|
|
|
|
dossiers
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
# returns: { revision_id : { type_de_champ_id : position } }
|
|
|
|
|
def positions
|
|
|
|
|
@positions ||= ProcedureRevisionTypeDeChamp
|
|
|
|
|
.where(revision_id: @dossiers.distinct.pluck(:revision_id))
|
|
|
|
|
.select(:revision_id, :type_de_champ_id, :position)
|
|
|
|
|
.group_by(&:revision_id)
|
|
|
|
|
.transform_values do |coordinates|
|
|
|
|
|
coordinates.index_by(&:type_de_champ_id).transform_values(&:position)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def load_dossiers(dossiers)
|
|
|
|
|
all_champs = Champ
|
|
|
|
|
.includes(:type_de_champ, piece_justificative_file_attachment: :blob)
|
|
|
|
|
.where(dossier_id: dossiers)
|
|
|
|
|
.to_a
|
|
|
|
|
|
|
|
|
|
load_etablissements(all_champs)
|
|
|
|
|
|
|
|
|
|
children_champs, root_champs = all_champs.partition(&:child?)
|
|
|
|
|
champs_by_dossier = root_champs.group_by(&:dossier_id)
|
|
|
|
|
champs_by_dossier_by_parent = children_champs
|
|
|
|
|
.group_by(&:dossier_id)
|
|
|
|
|
.transform_values do |champs|
|
|
|
|
|
champs.group_by(&:parent_id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
dossiers.each do |dossier|
|
2022-06-17 11:33:48 +02:00
|
|
|
|
load_dossier(dossier, champs_by_dossier[dossier.id], champs_by_dossier_by_parent[dossier.id] || {})
|
2022-06-13 22:54:57 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def load_etablissements(champs)
|
|
|
|
|
champs_siret = champs.filter(&:siret?)
|
|
|
|
|
etablissements_by_id = Etablissement.where(id: champs_siret.map(&:etablissement_id).compact).index_by(&:id)
|
|
|
|
|
champs_siret.each do |champ|
|
|
|
|
|
etablissement = etablissements_by_id[champ.etablissement_id]
|
|
|
|
|
champ.association(:etablissement).target = etablissement
|
|
|
|
|
if etablissement
|
|
|
|
|
etablissement.association(:champ).target = champ
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def load_dossier(dossier, champs, children_by_parent = {})
|
|
|
|
|
champs_public, champs_private = champs.partition(&:public?)
|
|
|
|
|
|
|
|
|
|
load_champs(dossier, :champs, champs_public, dossier)
|
|
|
|
|
load_champs(dossier, :champs_private, champs_private, dossier)
|
|
|
|
|
|
|
|
|
|
# Load repetition children champs
|
|
|
|
|
champs.filter(&:repetition?).each do |parent_champ|
|
|
|
|
|
champs = children_by_parent[parent_champ.id] || []
|
|
|
|
|
parent_champ.association(:dossier).target = dossier
|
|
|
|
|
|
|
|
|
|
load_champs(parent_champ, :champs, champs, dossier)
|
|
|
|
|
parent_champ.association(:champs).set_inverse_instance(parent_champ)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# We need to do this because of the check on `Etablissement#champ` in
|
|
|
|
|
# `Etablissement#libelle_for_export`. By assigning `nil` to `target` we mark association
|
|
|
|
|
# as loaded and so the check on `Etablissement#champ` will not trigger n+1 query.
|
|
|
|
|
if dossier.etablissement
|
|
|
|
|
dossier.etablissement.association(:champ).target = nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def load_champs(parent, name, champs, dossier)
|
|
|
|
|
champs.each do |champ|
|
|
|
|
|
champ.association(:dossier).target = dossier
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
parent.association(name).target = champs.sort_by do |champ|
|
|
|
|
|
positions[dossier.revision_id][champ.type_de_champ_id]
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-10-20 16:52:38 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-05-01 12:20:24 +02:00
|
|
|
|
def user_deleted?
|
2021-09-08 09:21:06 +02:00
|
|
|
|
persisted? && user_id.nil?
|
2021-05-01 12:20:24 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def user_email_for(use)
|
|
|
|
|
if user_deleted?
|
|
|
|
|
if use == :display
|
|
|
|
|
deleted_user_email_never_send
|
|
|
|
|
else
|
|
|
|
|
raise "Can not send email to discarded user"
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
user.email
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-02 11:02:50 +02:00
|
|
|
|
def motivation
|
2022-04-12 19:20:50 +02:00
|
|
|
|
return nil if !termine?
|
|
|
|
|
traitement&.motivation || read_attribute(:motivation)
|
2020-07-02 11:02:50 +02:00
|
|
|
|
end
|
|
|
|
|
|
2018-07-25 19:34:06 +02:00
|
|
|
|
def update_search_terms
|
|
|
|
|
self.search_terms = [
|
|
|
|
|
user&.email,
|
2018-08-22 17:48:25 +02:00
|
|
|
|
*champs.flat_map(&:search_terms),
|
2018-07-25 19:34:06 +02:00
|
|
|
|
*etablissement&.search_terms,
|
|
|
|
|
individual&.nom,
|
|
|
|
|
individual&.prenom
|
|
|
|
|
].compact.join(' ')
|
2018-08-22 17:48:25 +02:00
|
|
|
|
self.private_search_terms = champs_private.flat_map(&:search_terms).compact.join(' ')
|
2018-07-25 19:34:06 +02:00
|
|
|
|
end
|
|
|
|
|
|
2015-11-03 15:27:49 +01:00
|
|
|
|
def build_default_champs
|
2020-08-27 19:55:10 +02:00
|
|
|
|
revision.build_champs.each do |champ|
|
2019-01-30 16:14:15 +01:00
|
|
|
|
champs << champ
|
2018-08-22 17:16:06 +02:00
|
|
|
|
end
|
2020-08-27 19:55:10 +02:00
|
|
|
|
revision.build_champs_private.each do |champ|
|
2019-02-07 10:44:15 +01:00
|
|
|
|
champs_private << champ
|
2015-11-03 15:27:49 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-08-30 11:18:43 +02:00
|
|
|
|
def build_default_individual
|
2020-01-07 17:59:11 +01:00
|
|
|
|
if procedure.for_individual? && individual.blank?
|
|
|
|
|
self.individual = if france_connect_information.present?
|
|
|
|
|
Individual.from_france_connect(france_connect_information)
|
|
|
|
|
else
|
|
|
|
|
Individual.new
|
|
|
|
|
end
|
2016-12-21 15:39:41 +01:00
|
|
|
|
end
|
2016-08-30 11:18:43 +02:00
|
|
|
|
end
|
|
|
|
|
|
2017-07-11 15:58:31 +02:00
|
|
|
|
def en_construction_ou_instruction?
|
|
|
|
|
EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def termine?
|
|
|
|
|
TERMINE.include?(state)
|
|
|
|
|
end
|
|
|
|
|
|
2018-06-13 13:58:14 +02:00
|
|
|
|
def instruction_commencee?
|
|
|
|
|
INSTRUCTION_COMMENCEE.include?(state)
|
|
|
|
|
end
|
|
|
|
|
|
2016-06-20 13:57:57 +02:00
|
|
|
|
def reset!
|
2016-10-05 14:28:10 +02:00
|
|
|
|
etablissement.destroy
|
2016-06-20 13:57:57 +02:00
|
|
|
|
|
2018-02-08 17:13:15 +01:00
|
|
|
|
update_columns(autorisation_donnees: false)
|
2016-06-20 13:57:57 +02:00
|
|
|
|
end
|
2016-07-19 16:44:26 +02:00
|
|
|
|
|
2016-09-13 12:17:56 +02:00
|
|
|
|
def read_only?
|
2022-03-22 15:00:17 +01:00
|
|
|
|
en_instruction? || accepte? || refuse? || sans_suite? || procedure.discarded? || procedure.close? && brouillon?
|
2016-09-13 12:17:56 +02:00
|
|
|
|
end
|
|
|
|
|
|
2018-06-25 18:07:16 +02:00
|
|
|
|
def can_transition_to_en_construction?
|
2022-04-07 11:40:53 +02:00
|
|
|
|
brouillon? && procedure.dossier_can_transition_to_en_construction? && !for_procedure_preview?
|
2018-06-25 18:07:16 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-05-01 12:20:24 +02:00
|
|
|
|
def can_repasser_en_instruction?
|
|
|
|
|
termine? && !user_deleted?
|
|
|
|
|
end
|
|
|
|
|
|
2019-02-06 18:20:35 +01:00
|
|
|
|
def can_be_updated_by_user?
|
2018-06-08 15:51:46 +02:00
|
|
|
|
brouillon? || en_construction?
|
|
|
|
|
end
|
|
|
|
|
|
2019-02-06 19:11:55 +01:00
|
|
|
|
def can_be_deleted_by_user?
|
2021-12-07 15:38:37 +01:00
|
|
|
|
brouillon? || en_construction? || termine?
|
2019-02-06 19:11:55 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-09 10:27:43 +01:00
|
|
|
|
def can_be_deleted_by_administration?(reason)
|
|
|
|
|
termine? || reason == :procedure_removed
|
2022-01-05 10:41:02 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-03-12 15:41:47 +01:00
|
|
|
|
def messagerie_available?
|
2022-03-17 11:21:35 +01:00
|
|
|
|
visible_by_administration? && !hidden_by_user? && !user_deleted? && !archived
|
2019-03-12 15:41:47 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-22 14:51:52 +01:00
|
|
|
|
def expirable?
|
2021-11-30 14:47:19 +01:00
|
|
|
|
[
|
|
|
|
|
brouillon?,
|
|
|
|
|
en_construction?,
|
|
|
|
|
termine? && procedure.procedure_expires_when_termine_enabled
|
|
|
|
|
].any?
|
2021-11-22 14:51:52 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-08 13:16:08 +02:00
|
|
|
|
def expiration_date_reference
|
2021-11-17 10:53:43 +01:00
|
|
|
|
if brouillon?
|
|
|
|
|
created_at
|
|
|
|
|
elsif en_construction?
|
2021-11-18 18:15:57 +01:00
|
|
|
|
en_construction_at
|
2021-11-22 14:51:52 +01:00
|
|
|
|
elsif termine?
|
2021-11-17 10:53:43 +01:00
|
|
|
|
processed_at
|
2021-11-22 14:51:52 +01:00
|
|
|
|
else
|
2022-04-08 13:16:08 +02:00
|
|
|
|
fail "expiration_date_reference should not be called in state #{self.state}"
|
2021-11-22 14:51:52 +01:00
|
|
|
|
end
|
2021-05-04 16:29:29 +02:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-08 13:16:08 +02:00
|
|
|
|
def expiration_date_with_extention
|
2021-11-22 14:51:52 +01:00
|
|
|
|
[
|
2022-04-08 13:16:08 +02:00
|
|
|
|
expiration_date_reference,
|
2021-11-22 14:51:52 +01:00
|
|
|
|
conservation_extension,
|
|
|
|
|
procedure.duree_conservation_dossiers_dans_ds.months
|
2022-04-08 13:16:08 +02:00
|
|
|
|
].sum
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def expiration_notification_date
|
|
|
|
|
expiration_date_with_extention - REMAINING_WEEKS_BEFORE_EXPIRATION.weeks
|
2021-11-22 14:51:52 +01:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def close_to_expiration?
|
2021-12-07 13:32:27 +01:00
|
|
|
|
return false if en_instruction?
|
2022-04-08 13:16:08 +02:00
|
|
|
|
expiration_notification_date < Time.zone.now
|
2021-11-17 10:53:43 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-08 13:16:08 +02:00
|
|
|
|
def after_notification_expiration_date
|
2021-11-17 10:53:43 +01:00
|
|
|
|
if brouillon? && brouillon_close_to_expiration_notice_sent_at.present?
|
|
|
|
|
brouillon_close_to_expiration_notice_sent_at + duration_after_notice
|
|
|
|
|
elsif en_construction? && en_construction_close_to_expiration_notice_sent_at.present?
|
|
|
|
|
en_construction_close_to_expiration_notice_sent_at + duration_after_notice
|
|
|
|
|
elsif termine? && termine_close_to_expiration_notice_sent_at.present?
|
|
|
|
|
termine_close_to_expiration_notice_sent_at + duration_after_notice
|
|
|
|
|
end
|
2021-05-04 16:29:29 +02:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-08 13:16:08 +02:00
|
|
|
|
def expiration_date
|
|
|
|
|
after_notification_expiration_date.presence || expiration_date_with_extention
|
|
|
|
|
end
|
|
|
|
|
|
2021-11-22 14:51:52 +01:00
|
|
|
|
def duration_after_notice
|
|
|
|
|
MONTHS_AFTER_EXPIRATION.month + DAYS_AFTER_EXPIRATION.days
|
2021-11-17 10:53:43 +01:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def expiration_can_be_extended?
|
|
|
|
|
brouillon? || en_construction?
|
2020-03-19 14:47:09 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-08 13:16:08 +02:00
|
|
|
|
def extend_conservation(conservation_extension)
|
|
|
|
|
update(conservation_extension: self.conservation_extension + conservation_extension,
|
|
|
|
|
brouillon_close_to_expiration_notice_sent_at: nil,
|
|
|
|
|
en_construction_close_to_expiration_notice_sent_at: nil,
|
|
|
|
|
termine_close_to_expiration_notice_sent_at: nil)
|
|
|
|
|
end
|
|
|
|
|
|
2022-04-14 11:08:17 +02:00
|
|
|
|
def show_procedure_state_warning?
|
|
|
|
|
procedure.discarded? || (brouillon? && !procedure.dossier_can_transition_to_en_construction?)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-03 18:21:00 +01:00
|
|
|
|
def show_groupe_instructeur_details?
|
2021-03-09 11:21:30 +01:00
|
|
|
|
procedure.routee? && groupe_instructeur.present? && (!procedure.feature_enabled?(:procedure_routage_api) || !defaut_groupe_instructeur?)
|
2021-03-03 18:21:00 +01:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def show_groupe_instructeur_selector?
|
|
|
|
|
procedure.routee? && !procedure.feature_enabled?(:procedure_routage_api)
|
|
|
|
|
end
|
|
|
|
|
|
2020-02-26 12:30:52 +01:00
|
|
|
|
def assign_to_groupe_instructeur(groupe_instructeur, author = nil)
|
2021-03-09 11:21:25 +01:00
|
|
|
|
if (groupe_instructeur.nil? || groupe_instructeur.procedure == procedure) && self.groupe_instructeur != groupe_instructeur
|
2020-02-26 22:17:55 +01:00
|
|
|
|
if update(groupe_instructeur: groupe_instructeur, groupe_instructeur_updated_at: Time.zone.now)
|
2020-02-26 12:30:52 +01:00
|
|
|
|
unfollow_stale_instructeurs
|
|
|
|
|
|
|
|
|
|
if author.present?
|
|
|
|
|
log_dossier_operation(author, :changer_groupe_instructeur, self)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
true
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-09-09 15:04:58 +02:00
|
|
|
|
def archiver!(author)
|
|
|
|
|
update!(archived: true)
|
|
|
|
|
log_dossier_operation(author, :archiver)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def desarchiver!(author)
|
|
|
|
|
update!(archived: false)
|
|
|
|
|
log_dossier_operation(author, :desarchiver)
|
|
|
|
|
end
|
|
|
|
|
|
2017-04-18 17:31:01 +02:00
|
|
|
|
def text_summary
|
|
|
|
|
if brouillon?
|
|
|
|
|
parts = [
|
2018-09-05 14:48:42 +02:00
|
|
|
|
"Dossier en brouillon répondant à la démarche ",
|
2017-04-18 17:31:01 +02:00
|
|
|
|
procedure.libelle,
|
2017-05-02 09:48:25 +02:00
|
|
|
|
" gérée par l'organisme ",
|
2019-02-05 11:29:49 +01:00
|
|
|
|
procedure.organisation_name
|
2017-04-18 17:31:01 +02:00
|
|
|
|
]
|
|
|
|
|
else
|
|
|
|
|
parts = [
|
|
|
|
|
"Dossier déposé le ",
|
2021-12-06 15:49:17 +01:00
|
|
|
|
depose_at.strftime("%d/%m/%Y"),
|
2018-09-05 14:48:42 +02:00
|
|
|
|
" sur la démarche ",
|
2017-04-18 17:31:01 +02:00
|
|
|
|
procedure.libelle,
|
2017-05-02 09:48:25 +02:00
|
|
|
|
" gérée par l'organisme ",
|
2019-02-05 11:29:49 +01:00
|
|
|
|
procedure.organisation_name
|
2017-04-18 17:31:01 +02:00
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
parts.join
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-02 16:19:17 +01:00
|
|
|
|
def duree_totale_conservation_in_months
|
|
|
|
|
procedure.duree_conservation_dossiers_dans_ds + (conservation_extension / 1.month.to_i)
|
|
|
|
|
end
|
|
|
|
|
|
2021-02-25 10:10:24 +01:00
|
|
|
|
def avis_for_instructeur(instructeur)
|
2019-08-06 11:02:54 +02:00
|
|
|
|
if instructeur.dossiers.include?(self)
|
2017-09-08 11:52:20 +02:00
|
|
|
|
avis.order(created_at: :asc)
|
|
|
|
|
else
|
|
|
|
|
avis
|
|
|
|
|
.where(confidentiel: false)
|
2021-04-07 19:53:18 +02:00
|
|
|
|
.or(avis.where(claimant: instructeur))
|
2017-09-08 11:52:20 +02:00
|
|
|
|
.order(created_at: :asc)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-02-25 10:10:24 +01:00
|
|
|
|
def avis_for_expert(expert)
|
2021-05-19 15:41:12 +02:00
|
|
|
|
Avis
|
|
|
|
|
.where(dossier_id: id, confidentiel: false)
|
2021-05-20 16:15:59 +02:00
|
|
|
|
.or(Avis.where(id: expert.avis, dossier_id: id))
|
2021-05-19 15:41:12 +02:00
|
|
|
|
.order(created_at: :asc)
|
2021-02-25 10:10:24 +01:00
|
|
|
|
end
|
|
|
|
|
|
2017-11-17 23:40:51 +01:00
|
|
|
|
def owner_name
|
2018-04-23 11:57:38 +02:00
|
|
|
|
if etablissement.present?
|
|
|
|
|
etablissement.entreprise_raison_sociale
|
2017-11-17 23:40:51 +01:00
|
|
|
|
elsif individual.present?
|
|
|
|
|
"#{individual.nom} #{individual.prenom}"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-24 12:50:59 +01:00
|
|
|
|
def log_operations?
|
2021-10-05 16:03:56 +02:00
|
|
|
|
!procedure.brouillon? && !brouillon?
|
2020-03-24 12:50:59 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-12-16 11:25:44 +01:00
|
|
|
|
def hidden_by_user?
|
|
|
|
|
hidden_by_user_at.present?
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-21 12:44:57 +01:00
|
|
|
|
def hidden_by_administration?
|
|
|
|
|
hidden_by_administration_at.present?
|
2021-12-16 11:25:44 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-09 10:27:43 +01:00
|
|
|
|
def hidden_for_administration?
|
|
|
|
|
hidden_by_administration? || (hidden_by_user? && en_construction?) || brouillon?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def visible_by_administration?
|
|
|
|
|
!hidden_for_administration?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def hidden_for_administration_and_user?
|
|
|
|
|
hidden_for_administration? && hidden_by_user?
|
2021-12-16 11:25:44 +01:00
|
|
|
|
end
|
|
|
|
|
|
2018-10-31 13:28:39 +01:00
|
|
|
|
def expose_legacy_carto_api?
|
|
|
|
|
procedure.expose_legacy_carto_api?
|
|
|
|
|
end
|
|
|
|
|
|
2018-10-10 19:58:51 +02:00
|
|
|
|
def geo_position
|
|
|
|
|
if etablissement.present?
|
2020-01-14 19:00:17 +01:00
|
|
|
|
point = Geocoder.search(etablissement.geo_adresse).first
|
2018-10-10 19:58:51 +02:00
|
|
|
|
end
|
|
|
|
|
|
2020-04-16 10:22:07 +02:00
|
|
|
|
lon = Champs::CarteChamp::DEFAULT_LON.to_s
|
|
|
|
|
lat = Champs::CarteChamp::DEFAULT_LAT.to_s
|
2018-10-10 19:58:51 +02:00
|
|
|
|
zoom = "13"
|
|
|
|
|
|
|
|
|
|
if point.present?
|
2020-01-14 19:00:17 +01:00
|
|
|
|
lat, lon = point.coordinates.map(&:to_s)
|
2018-10-10 19:58:51 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
{ lon: lon, lat: lat, zoom: zoom }
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-02 16:10:29 +01:00
|
|
|
|
def unspecified_attestation_champs
|
2018-04-06 16:21:48 +02:00
|
|
|
|
if attestation_template&.activated?
|
2018-04-06 13:09:37 +02:00
|
|
|
|
attestation_template.unspecified_champs_for_dossier(self)
|
|
|
|
|
else
|
|
|
|
|
[]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-06-08 14:04:47 +02:00
|
|
|
|
def build_attestation
|
2022-02-02 16:10:29 +01:00
|
|
|
|
if attestation_template&.activated?
|
|
|
|
|
attestation_template.attestation_for(self)
|
2017-06-08 14:04:47 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-10-27 10:02:56 +02:00
|
|
|
|
def expired_keep_track_and_destroy!
|
|
|
|
|
transaction do
|
2022-03-09 10:27:43 +01:00
|
|
|
|
DeletedDossier.create_from_dossier(self, :expired)
|
|
|
|
|
dossier_operation_logs.destroy_all
|
|
|
|
|
log_automatic_dossier_operation(:supprimer, self)
|
2021-10-27 10:02:56 +02:00
|
|
|
|
destroy!
|
2020-03-24 12:50:59 +01:00
|
|
|
|
end
|
2021-10-27 10:02:56 +02:00
|
|
|
|
true
|
|
|
|
|
rescue
|
|
|
|
|
false
|
2020-03-19 11:53:25 +01:00
|
|
|
|
end
|
2018-07-23 15:06:06 +02:00
|
|
|
|
|
2022-01-05 10:41:02 +01:00
|
|
|
|
def author_is_user(author)
|
|
|
|
|
author.is_a?(User)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def author_is_administration(author)
|
|
|
|
|
author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin)
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-09 10:27:43 +01:00
|
|
|
|
def hide_and_keep_track!(author, reason)
|
2021-10-27 10:02:56 +02:00
|
|
|
|
transaction do
|
2022-03-09 10:27:43 +01:00
|
|
|
|
if author_is_administration(author) && can_be_deleted_by_administration?(reason)
|
|
|
|
|
update(hidden_by_administration_at: Time.zone.now, hidden_by_reason: reason)
|
|
|
|
|
elsif author_is_user(author) && can_be_deleted_by_user?
|
|
|
|
|
update(hidden_by_user_at: Time.zone.now, dossier_transfer_id: nil, hidden_by_reason: reason)
|
|
|
|
|
else
|
|
|
|
|
raise "Unauthorized dossier hide attempt Dossier##{id} by #{author} for reason #{reason}"
|
2021-12-10 17:13:26 +01:00
|
|
|
|
end
|
2022-03-09 10:27:43 +01:00
|
|
|
|
|
|
|
|
|
log_dossier_operation(author, :supprimer, self)
|
2021-10-27 10:02:56 +02:00
|
|
|
|
end
|
2020-11-17 13:25:35 +01:00
|
|
|
|
|
2022-03-09 10:27:43 +01:00
|
|
|
|
if en_construction? && !hidden_by_administration?
|
2022-02-08 10:47:43 +01:00
|
|
|
|
administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email)
|
|
|
|
|
administration_emails.each do |email|
|
|
|
|
|
DossierMailer.notify_en_construction_deletion_to_administration(self, email).deliver_later
|
2021-10-27 10:02:56 +02:00
|
|
|
|
end
|
2018-07-23 15:06:06 +02:00
|
|
|
|
end
|
2018-05-30 11:36:48 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-10-27 10:02:56 +02:00
|
|
|
|
def restore(author)
|
2022-03-09 10:27:43 +01:00
|
|
|
|
transaction do
|
|
|
|
|
if author_is_administration(author)
|
|
|
|
|
update(hidden_by_administration_at: nil)
|
|
|
|
|
elsif author_is_user(author)
|
2022-01-05 10:41:02 +01:00
|
|
|
|
update(hidden_by_user_at: nil)
|
2020-03-26 17:35:50 +01:00
|
|
|
|
end
|
2022-03-09 10:27:43 +01:00
|
|
|
|
|
|
|
|
|
if !hidden_by_user? && !hidden_by_administration?
|
|
|
|
|
update(hidden_by_reason: nil)
|
2022-01-27 17:01:27 +01:00
|
|
|
|
end
|
2022-03-09 10:27:43 +01:00
|
|
|
|
|
|
|
|
|
log_dossier_operation(author, :restaurer, self)
|
2020-03-26 17:35:50 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-08 11:23:03 +01:00
|
|
|
|
def attestation_activated?
|
2022-02-11 08:45:16 +01:00
|
|
|
|
termine? && attestation_template&.activated?
|
2021-12-08 11:23:03 +01:00
|
|
|
|
end
|
|
|
|
|
|
2020-07-01 17:31:08 +02:00
|
|
|
|
def after_passer_en_construction
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.conservation_extension = 0.days
|
2021-11-24 12:27:32 +01:00
|
|
|
|
self.depose_at = self.en_construction_at = self.traitements
|
2021-11-04 19:05:04 +01:00
|
|
|
|
.passer_en_construction
|
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2020-07-01 17:31:08 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-26 12:19:40 +01:00
|
|
|
|
def after_passer_en_instruction(h)
|
|
|
|
|
instructeur = h[:instructeur]
|
|
|
|
|
disable_notification = h.fetch(:disable_notification, false)
|
|
|
|
|
|
2019-08-06 11:02:54 +02:00
|
|
|
|
instructeur.follow(self)
|
2018-11-26 21:29:06 +01:00
|
|
|
|
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.en_construction_close_to_expiration_notice_sent_at = nil
|
|
|
|
|
self.conservation_extension = 0.days
|
|
|
|
|
self.en_instruction_at = self.traitements
|
2021-11-14 18:43:45 +01:00
|
|
|
|
.passer_en_instruction(instructeur: instructeur)
|
2021-11-04 19:05:04 +01:00
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
|
|
|
|
|
2021-09-01 15:11:11 +02:00
|
|
|
|
if !procedure.declarative_accepte? && !disable_notification
|
|
|
|
|
NotificationMailer.send_en_instruction_notification(self).deliver_later
|
|
|
|
|
end
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :passer_en_instruction)
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
def after_passer_automatiquement_en_instruction
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.en_construction_close_to_expiration_notice_sent_at = nil
|
|
|
|
|
self.conservation_extension = 0.days
|
|
|
|
|
self.en_instruction_at = self.declarative_triggered_at = self.traitements
|
|
|
|
|
.passer_en_instruction
|
|
|
|
|
.processed_at
|
2021-06-04 12:03:31 +02:00
|
|
|
|
save!
|
2019-05-02 16:22:16 +02:00
|
|
|
|
log_automatic_dossier_operation(:passer_en_instruction)
|
2019-01-10 17:23:48 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-08-06 11:02:54 +02:00
|
|
|
|
def after_repasser_en_construction(instructeur)
|
2021-11-24 11:54:18 +01:00
|
|
|
|
create_missing_traitemets
|
|
|
|
|
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.en_construction_close_to_expiration_notice_sent_at = nil
|
|
|
|
|
self.conservation_extension = 0.days
|
|
|
|
|
self.en_construction_at = self.traitements
|
2021-11-14 18:43:45 +01:00
|
|
|
|
.passer_en_construction(instructeur: instructeur)
|
2021-11-04 19:05:04 +01:00
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :repasser_en_construction)
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-26 12:19:40 +01:00
|
|
|
|
def after_repasser_en_instruction(h)
|
|
|
|
|
instructeur = h[:instructeur]
|
|
|
|
|
disable_notification = h.fetch(:disable_notification, false)
|
|
|
|
|
|
2021-11-24 11:54:18 +01:00
|
|
|
|
create_missing_traitemets
|
|
|
|
|
|
2021-12-16 11:25:44 +01:00
|
|
|
|
self.hidden_by_user_at = nil
|
2020-05-12 19:01:25 +02:00
|
|
|
|
self.archived = false
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.termine_close_to_expiration_notice_sent_at = nil
|
|
|
|
|
self.conservation_extension = 0.days
|
|
|
|
|
self.en_instruction_at = self.traitements
|
2021-11-14 18:43:45 +01:00
|
|
|
|
.passer_en_instruction(instructeur: instructeur)
|
2021-11-04 19:05:04 +01:00
|
|
|
|
.processed_at
|
2019-07-01 17:45:03 +02:00
|
|
|
|
attestation&.destroy
|
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
save!
|
2021-09-01 15:11:11 +02:00
|
|
|
|
if !disable_notification
|
|
|
|
|
DossierMailer.notify_revert_to_instruction(self).deliver_later
|
|
|
|
|
end
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :repasser_en_instruction)
|
2019-07-01 17:45:03 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-26 12:19:40 +01:00
|
|
|
|
def after_accepter(h)
|
|
|
|
|
instructeur = h[:instructeur]
|
|
|
|
|
motivation = h[:motivation]
|
|
|
|
|
justificatif = h[:justificatif]
|
|
|
|
|
disable_notification = h.fetch(:disable_notification, false)
|
|
|
|
|
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.processed_at = self.traitements
|
|
|
|
|
.accepter(motivation: motivation, instructeur: instructeur)
|
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2019-07-02 15:38:23 +02:00
|
|
|
|
|
2019-02-18 17:52:15 +01:00
|
|
|
|
if justificatif
|
|
|
|
|
self.justificatif_motivation.attach(justificatif)
|
|
|
|
|
end
|
2018-11-26 19:12:56 +01:00
|
|
|
|
|
|
|
|
|
if attestation.nil?
|
2019-07-02 15:38:23 +02:00
|
|
|
|
self.attestation = build_attestation
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
save!
|
2020-12-08 15:57:48 +01:00
|
|
|
|
remove_titres_identite!
|
2021-09-01 15:11:11 +02:00
|
|
|
|
if !disable_notification
|
|
|
|
|
NotificationMailer.send_accepte_notification(self).deliver_later
|
|
|
|
|
end
|
2021-04-09 09:55:36 +02:00
|
|
|
|
send_dossier_decision_to_experts(self)
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :accepter, self)
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
def after_accepter_automatiquement
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.processed_at = self.en_instruction_at = self.declarative_triggered_at = self.traitements
|
|
|
|
|
.accepter_automatiquement
|
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2019-01-16 11:00:25 +01:00
|
|
|
|
|
|
|
|
|
if attestation.nil?
|
2019-07-02 15:38:23 +02:00
|
|
|
|
self.attestation = build_attestation
|
2019-01-16 11:00:25 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
save!
|
2020-12-08 15:57:48 +01:00
|
|
|
|
remove_titres_identite!
|
2021-04-29 19:10:22 +02:00
|
|
|
|
NotificationMailer.send_accepte_notification(self).deliver_later
|
2019-05-02 16:22:16 +02:00
|
|
|
|
log_automatic_dossier_operation(:accepter, self)
|
2019-01-16 11:00:25 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-26 12:19:40 +01:00
|
|
|
|
def after_refuser(h)
|
|
|
|
|
instructeur = h[:instructeur]
|
|
|
|
|
motivation = h[:motivation]
|
|
|
|
|
justificatif = h[:justificatif]
|
|
|
|
|
disable_notification = h.fetch(:disable_notification, false)
|
|
|
|
|
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.processed_at = self.traitements
|
|
|
|
|
.refuser(motivation: motivation, instructeur: instructeur)
|
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2019-07-02 15:38:23 +02:00
|
|
|
|
|
2019-02-18 17:52:15 +01:00
|
|
|
|
if justificatif
|
|
|
|
|
self.justificatif_motivation.attach(justificatif)
|
|
|
|
|
end
|
2018-11-26 19:12:56 +01:00
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
save!
|
2020-12-08 15:57:48 +01:00
|
|
|
|
remove_titres_identite!
|
2021-09-01 15:11:11 +02:00
|
|
|
|
if !disable_notification
|
|
|
|
|
NotificationMailer.send_refuse_notification(self).deliver_later
|
|
|
|
|
end
|
2021-04-09 09:55:36 +02:00
|
|
|
|
send_dossier_decision_to_experts(self)
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :refuser, self)
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-11-26 12:19:40 +01:00
|
|
|
|
def after_classer_sans_suite(h)
|
|
|
|
|
instructeur = h[:instructeur]
|
|
|
|
|
motivation = h[:motivation]
|
|
|
|
|
justificatif = h[:justificatif]
|
|
|
|
|
disable_notification = h.fetch(:disable_notification, false)
|
|
|
|
|
|
2021-11-04 19:05:04 +01:00
|
|
|
|
self.processed_at = self.traitements
|
|
|
|
|
.classer_sans_suite(motivation: motivation, instructeur: instructeur)
|
|
|
|
|
.processed_at
|
|
|
|
|
save!
|
2019-07-02 15:38:23 +02:00
|
|
|
|
|
2019-02-18 17:52:15 +01:00
|
|
|
|
if justificatif
|
|
|
|
|
self.justificatif_motivation.attach(justificatif)
|
|
|
|
|
end
|
2018-11-26 19:12:56 +01:00
|
|
|
|
|
2019-07-02 15:38:23 +02:00
|
|
|
|
save!
|
2020-12-08 15:57:48 +01:00
|
|
|
|
remove_titres_identite!
|
2021-09-01 15:11:11 +02:00
|
|
|
|
if !disable_notification
|
|
|
|
|
NotificationMailer.send_sans_suite_notification(self).deliver_later
|
|
|
|
|
end
|
2021-04-09 09:55:36 +02:00
|
|
|
|
send_dossier_decision_to_experts(self)
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :classer_sans_suite, self)
|
2018-11-26 19:12:56 +01:00
|
|
|
|
end
|
|
|
|
|
|
2020-12-08 15:57:48 +01:00
|
|
|
|
def remove_titres_identite!
|
|
|
|
|
champs.filter(&:titre_identite?).map(&:piece_justificative_file).each(&:purge_later)
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-30 16:14:15 +01:00
|
|
|
|
def check_mandatory_champs
|
2019-09-12 11:26:22 +02:00
|
|
|
|
(champs + champs.filter(&:repetition?).flat_map(&:champs))
|
|
|
|
|
.filter(&:mandatory_and_blank?)
|
2019-01-30 16:14:15 +01:00
|
|
|
|
.map do |champ|
|
|
|
|
|
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-28 14:22:56 +02:00
|
|
|
|
def log_modifier_annotations!(instructeur)
|
2019-09-12 11:26:22 +02:00
|
|
|
|
champs_private.filter(&:value_previously_changed?).each do |champ|
|
2019-08-06 11:02:54 +02:00
|
|
|
|
log_dossier_operation(instructeur, :modifier_annotation, champ)
|
2019-05-02 16:23:47 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2019-05-02 16:24:24 +02:00
|
|
|
|
|
2020-07-28 18:16:03 +02:00
|
|
|
|
def log_modifier_annotation!(champ, instructeur)
|
|
|
|
|
log_dossier_operation(instructeur, :modifier_annotation, champ)
|
|
|
|
|
end
|
|
|
|
|
|
2019-05-02 16:24:24 +02:00
|
|
|
|
def demander_un_avis!(avis)
|
|
|
|
|
log_dossier_operation(avis.claimant, :demander_un_avis, avis)
|
|
|
|
|
end
|
|
|
|
|
|
2021-06-22 10:17:10 +02:00
|
|
|
|
def spreadsheet_columns_csv(types_de_champ:)
|
|
|
|
|
spreadsheet_columns(with_etablissement: true, types_de_champ: types_de_champ)
|
2019-11-06 15:55:01 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-06-22 10:17:10 +02:00
|
|
|
|
def spreadsheet_columns_xlsx(types_de_champ:)
|
|
|
|
|
spreadsheet_columns(types_de_champ: types_de_champ)
|
2019-11-06 15:55:01 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-06-22 10:17:10 +02:00
|
|
|
|
def spreadsheet_columns_ods(types_de_champ:)
|
|
|
|
|
spreadsheet_columns(types_de_champ: types_de_champ)
|
2019-11-06 15:55:01 +01:00
|
|
|
|
end
|
|
|
|
|
|
2021-06-22 10:17:10 +02:00
|
|
|
|
def spreadsheet_columns(with_etablissement: false, types_de_champ:)
|
2019-09-18 21:09:52 +02:00
|
|
|
|
columns = [
|
2019-04-03 14:29:30 +02:00
|
|
|
|
['ID', id.to_s],
|
2021-05-01 12:20:24 +02:00
|
|
|
|
['Email', user_email_for(:display)]
|
2019-10-31 14:14:14 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if procedure.for_individual?
|
|
|
|
|
columns += [
|
|
|
|
|
['Civilité', individual&.gender],
|
|
|
|
|
['Nom', individual&.nom],
|
2020-05-13 15:57:56 +02:00
|
|
|
|
['Prénom', individual&.prenom]
|
2019-10-31 14:14:14 +01:00
|
|
|
|
]
|
2020-05-13 15:57:56 +02:00
|
|
|
|
if procedure.ask_birthday
|
|
|
|
|
columns += [['Date de naissance', individual&.birthdate]]
|
|
|
|
|
end
|
2019-11-06 15:55:01 +01:00
|
|
|
|
elsif with_etablissement
|
|
|
|
|
columns += [
|
|
|
|
|
['Établissement SIRET', etablissement&.siret],
|
|
|
|
|
['Établissement siège social', etablissement&.siege_social],
|
|
|
|
|
['Établissement NAF', etablissement&.naf],
|
|
|
|
|
['Établissement libellé NAF', etablissement&.libelle_naf],
|
|
|
|
|
['Établissement Adresse', etablissement&.adresse],
|
|
|
|
|
['Établissement numero voie', etablissement&.numero_voie],
|
|
|
|
|
['Établissement type voie', etablissement&.type_voie],
|
|
|
|
|
['Établissement nom voie', etablissement&.nom_voie],
|
|
|
|
|
['Établissement complément adresse', etablissement&.complement_adresse],
|
|
|
|
|
['Établissement code postal', etablissement&.code_postal],
|
|
|
|
|
['Établissement localité', etablissement&.localite],
|
|
|
|
|
['Établissement code INSEE localité', etablissement&.code_insee_localite],
|
|
|
|
|
['Entreprise SIREN', etablissement&.entreprise_siren],
|
|
|
|
|
['Entreprise capital social', etablissement&.entreprise_capital_social],
|
|
|
|
|
['Entreprise numero TVA intracommunautaire', etablissement&.entreprise_numero_tva_intracommunautaire],
|
|
|
|
|
['Entreprise forme juridique', etablissement&.entreprise_forme_juridique],
|
|
|
|
|
['Entreprise forme juridique code', etablissement&.entreprise_forme_juridique_code],
|
|
|
|
|
['Entreprise nom commercial', etablissement&.entreprise_nom_commercial],
|
|
|
|
|
['Entreprise raison sociale', etablissement&.entreprise_raison_sociale],
|
|
|
|
|
['Entreprise SIRET siège social', etablissement&.entreprise_siret_siege_social],
|
|
|
|
|
['Entreprise code effectif entreprise', etablissement&.entreprise_code_effectif_entreprise],
|
|
|
|
|
['Entreprise date de création', etablissement&.entreprise_date_creation],
|
|
|
|
|
['Entreprise nom', etablissement&.entreprise_nom],
|
|
|
|
|
['Entreprise prénom', etablissement&.entreprise_prenom],
|
|
|
|
|
['Association RNA', etablissement&.association_rna],
|
|
|
|
|
['Association titre', etablissement&.association_titre],
|
|
|
|
|
['Association objet', etablissement&.association_objet],
|
|
|
|
|
['Association date de création', etablissement&.association_date_creation],
|
|
|
|
|
['Association date de déclaration', etablissement&.association_date_declaration],
|
|
|
|
|
['Association date de publication', etablissement&.association_date_publication]
|
|
|
|
|
]
|
2019-10-31 14:14:14 +01:00
|
|
|
|
else
|
|
|
|
|
columns << ['Entreprise raison sociale', etablissement&.entreprise_raison_sociale]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
columns += [
|
2019-04-03 14:29:30 +02:00
|
|
|
|
['Archivé', :archived],
|
2021-05-10 11:00:57 +02:00
|
|
|
|
['État du dossier', Dossier.human_attribute_name("state.#{state}")],
|
2019-04-03 14:29:30 +02:00
|
|
|
|
['Dernière mise à jour le', :updated_at],
|
2021-12-06 15:49:17 +01:00
|
|
|
|
['Déposé le', :depose_at],
|
2019-07-04 15:02:25 +02:00
|
|
|
|
['Passé en instruction le', :en_instruction_at],
|
2019-04-03 14:29:30 +02:00
|
|
|
|
['Traité le', :processed_at],
|
|
|
|
|
['Motivation de la décision', :motivation],
|
2019-08-06 11:02:54 +02:00
|
|
|
|
['Instructeurs', followers_instructeurs.map(&:email).join(' ')]
|
2019-09-18 21:09:52 +02:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if procedure.routee?
|
|
|
|
|
columns << ['Groupe instructeur', groupe_instructeur.label]
|
|
|
|
|
end
|
2021-10-22 12:18:43 +02:00
|
|
|
|
columns + self.class.champs_for_export(champs + champs_private, types_de_champ)
|
2019-04-03 14:29:30 +02:00
|
|
|
|
end
|
|
|
|
|
|
2022-05-05 17:55:07 +02:00
|
|
|
|
# Get all the champs values for the types de champ in the final list.
|
|
|
|
|
# Dossier might not have corresponding champ – display nil.
|
|
|
|
|
# To do so, we build a virtual champ when there is no value so we can call for_export with all indexes
|
2021-10-22 12:18:43 +02:00
|
|
|
|
def self.champs_for_export(champs, types_de_champ)
|
2021-08-18 14:02:40 +02:00
|
|
|
|
types_de_champ.flat_map do |type_de_champ|
|
2022-05-05 17:55:07 +02:00
|
|
|
|
champ_or_new = champs.find { |champ| champ.stable_id == type_de_champ.stable_id }
|
|
|
|
|
champ_or_new ||= type_de_champ.champ.build
|
|
|
|
|
Array.wrap(champ_or_new.for_export || [nil]).map.with_index do |champ_value, index|
|
2021-08-18 14:13:10 +02:00
|
|
|
|
[type_de_champ.libelle_for_export(index), champ_value]
|
2021-08-18 14:02:40 +02:00
|
|
|
|
end
|
2019-04-03 14:29:30 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-02-25 10:10:24 +01:00
|
|
|
|
def linked_dossiers_for(instructeur_or_expert)
|
2021-06-10 15:24:15 +02:00
|
|
|
|
dossier_ids = champs.filter(&:dossier_link?).filter_map(&:value)
|
2021-02-25 10:10:24 +01:00
|
|
|
|
instructeur_or_expert.dossiers.where(id: dossier_ids)
|
2020-02-05 17:38:24 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-11-28 18:03:23 +01:00
|
|
|
|
def hash_for_deletion_mail
|
|
|
|
|
{ id: self.id, procedure_libelle: self.procedure.libelle }
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-30 15:49:43 +02:00
|
|
|
|
def geo_data?
|
|
|
|
|
geo_areas.present?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def to_feature_collection
|
|
|
|
|
{
|
|
|
|
|
type: 'FeatureCollection',
|
|
|
|
|
id: id,
|
|
|
|
|
bbox: bounding_box,
|
|
|
|
|
features: geo_areas.map(&:to_feature)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
2021-02-04 19:24:52 +01:00
|
|
|
|
def log_api_entreprise_job_exception(exception)
|
|
|
|
|
exceptions = self.api_entreprise_job_exceptions ||= []
|
|
|
|
|
exceptions << exception.inspect
|
|
|
|
|
update_column(:api_entreprise_job_exceptions, exceptions)
|
|
|
|
|
end
|
|
|
|
|
|
2021-09-01 18:17:26 +02:00
|
|
|
|
def user_locale
|
|
|
|
|
user&.locale || I18n.default_locale
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-08 10:47:43 +01:00
|
|
|
|
def purge_discarded
|
2021-10-21 13:29:47 +02:00
|
|
|
|
transaction do
|
2022-03-09 10:27:43 +01:00
|
|
|
|
DeletedDossier.create_from_dossier(self, hidden_by_reason)
|
2022-02-08 10:47:43 +01:00
|
|
|
|
dossier_operation_logs.not_deletion.destroy_all
|
|
|
|
|
destroy
|
2021-10-21 13:29:47 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-08 10:47:43 +01:00
|
|
|
|
def self.purge_discarded
|
2022-03-09 10:27:43 +01:00
|
|
|
|
en_brouillon_expired_to_delete.find_each(&:purge_discarded)
|
|
|
|
|
en_construction_expired_to_delete.find_each(&:purge_discarded)
|
|
|
|
|
termine_expired_to_delete.find_each(&:purge_discarded)
|
2022-02-08 10:47:43 +01:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-10 19:22:07 +01:00
|
|
|
|
def sections_for(champ)
|
|
|
|
|
@sections = Hash.new do |hash, parent|
|
|
|
|
|
case parent
|
|
|
|
|
when :public
|
|
|
|
|
hash[parent] = champs.filter(&:header_section?)
|
|
|
|
|
when :private
|
|
|
|
|
hash[parent] = champs_private.filter(&:header_section?)
|
|
|
|
|
else
|
|
|
|
|
hash[parent] = parent.champs.filter(&:header_section?)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
@sections[champ.parent || (champ.public? ? :public : :private)]
|
|
|
|
|
end
|
|
|
|
|
|
2017-12-05 17:43:32 +01:00
|
|
|
|
private
|
|
|
|
|
|
2021-11-24 11:54:18 +01:00
|
|
|
|
def create_missing_traitemets
|
|
|
|
|
if en_construction_at.present? && traitements.en_construction.empty?
|
|
|
|
|
self.traitements.passer_en_construction(processed_at: en_construction_at)
|
2021-11-24 12:27:32 +01:00
|
|
|
|
self.depose_at ||= en_construction_at
|
2021-11-24 11:54:18 +01:00
|
|
|
|
end
|
|
|
|
|
if en_instruction_at.present? && traitements.en_instruction.empty?
|
|
|
|
|
self.traitements.passer_en_instruction(processed_at: en_instruction_at)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-10-27 10:02:56 +02:00
|
|
|
|
def deleted_dossier
|
|
|
|
|
@deleted_dossier ||= DeletedDossier.find_by(dossier_id: id)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-03 18:21:00 +01:00
|
|
|
|
def defaut_groupe_instructeur?
|
|
|
|
|
groupe_instructeur == procedure.defaut_groupe_instructeur
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-30 15:49:43 +02:00
|
|
|
|
def geo_areas
|
2022-03-11 00:17:36 +01:00
|
|
|
|
champs.flat_map(&:geo_areas) + champs_private.flat_map(&:geo_areas)
|
2020-04-30 15:49:43 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def bounding_box
|
|
|
|
|
factory = RGeo::Geographic.simple_mercator_factory
|
|
|
|
|
bounding_box = RGeo::Cartesian::BoundingBox.new(factory)
|
|
|
|
|
|
2021-06-10 15:24:15 +02:00
|
|
|
|
geo_areas.filter_map(&:rgeo_geometry).each do |geometry|
|
2020-05-05 10:26:13 +02:00
|
|
|
|
bounding_box.add(geometry)
|
2020-04-30 15:49:43 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
[bounding_box.max_point, bounding_box.min_point].compact.flat_map(&:coordinates)
|
|
|
|
|
end
|
|
|
|
|
|
2019-05-02 16:22:16 +02:00
|
|
|
|
def log_dossier_operation(author, operation, subject = nil)
|
2020-03-24 12:50:59 +01:00
|
|
|
|
if log_operations?
|
|
|
|
|
DossierOperationLog.create_and_serialize(
|
|
|
|
|
dossier: self,
|
|
|
|
|
operation: DossierOperationLog.operations.fetch(operation),
|
|
|
|
|
author: author,
|
|
|
|
|
subject: subject
|
|
|
|
|
)
|
|
|
|
|
end
|
2018-11-26 21:29:06 +01:00
|
|
|
|
end
|
|
|
|
|
|
2019-05-02 16:22:16 +02:00
|
|
|
|
def log_automatic_dossier_operation(operation, subject = nil)
|
2020-03-24 12:50:59 +01:00
|
|
|
|
if log_operations?
|
|
|
|
|
DossierOperationLog.create_and_serialize(
|
|
|
|
|
dossier: self,
|
|
|
|
|
operation: DossierOperationLog.operations.fetch(operation),
|
|
|
|
|
automatic_operation: true,
|
|
|
|
|
subject: subject
|
|
|
|
|
)
|
|
|
|
|
end
|
2019-02-13 16:13:37 +01:00
|
|
|
|
end
|
|
|
|
|
|
2017-10-11 15:36:40 +02:00
|
|
|
|
def send_draft_notification_email
|
2022-04-07 11:40:53 +02:00
|
|
|
|
if brouillon? && !procedure.declarative? && !for_procedure_preview?
|
2018-11-20 11:50:25 +01:00
|
|
|
|
DossierMailer.notify_new_draft(self).deliver_later
|
2017-10-11 15:36:40 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-03-01 17:04:05 +01:00
|
|
|
|
|
|
|
|
|
def send_web_hook
|
2020-09-22 17:03:19 +02:00
|
|
|
|
if saved_change_to_state? && !brouillon? && procedure.web_hook_url.present?
|
2018-03-01 17:04:05 +01:00
|
|
|
|
WebHookJob.perform_later(
|
2022-06-01 15:03:31 +02:00
|
|
|
|
procedure.id,
|
|
|
|
|
self.id,
|
|
|
|
|
self.state,
|
|
|
|
|
self.updated_at
|
2018-03-01 17:04:05 +01:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-12-03 10:31:10 +01:00
|
|
|
|
|
2020-02-25 15:51:30 +01:00
|
|
|
|
def unfollow_stale_instructeurs
|
2020-02-26 12:30:52 +01:00
|
|
|
|
followers_instructeurs.each do |instructeur|
|
|
|
|
|
if instructeur.groupe_instructeurs.exclude?(groupe_instructeur)
|
|
|
|
|
instructeur.unfollow(self)
|
2022-03-09 10:27:43 +01:00
|
|
|
|
if visible_by_administration?
|
2020-04-01 11:10:25 +02:00
|
|
|
|
DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later
|
|
|
|
|
end
|
2020-02-25 15:51:30 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-05 22:10:22 +01:00
|
|
|
|
|
|
|
|
|
def self.notify_draft_not_submitted
|
2020-03-25 04:09:14 +01:00
|
|
|
|
brouillon_near_procedure_closing_date
|
|
|
|
|
.includes(:user)
|
2020-02-05 22:10:22 +01:00
|
|
|
|
.find_each do |dossier|
|
2020-03-19 03:46:12 +01:00
|
|
|
|
DossierMailer.notify_brouillon_not_submitted(dossier).deliver_later
|
2020-02-05 22:10:22 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-04-09 09:55:36 +02:00
|
|
|
|
|
|
|
|
|
def send_dossier_decision_to_experts(dossier)
|
|
|
|
|
avis_experts_procedures_ids = Avis
|
|
|
|
|
.joins(:experts_procedure)
|
|
|
|
|
.where(dossier: dossier, experts_procedures: { allow_decision_access: true })
|
|
|
|
|
.with_answer
|
|
|
|
|
.distinct
|
|
|
|
|
.pluck('avis.id, experts_procedures.id')
|
|
|
|
|
|
|
|
|
|
# rubocop:disable Lint/UnusedBlockArgument
|
|
|
|
|
avis_ids = avis_experts_procedures_ids
|
|
|
|
|
.uniq { |(avis_id, experts_procedures_id)| experts_procedures_id }
|
|
|
|
|
.map { |(avis_id, _)| avis_id }
|
|
|
|
|
# rubocop:enable Lint/UnusedBlockArgument
|
|
|
|
|
|
|
|
|
|
avis_ids.each { |avis_id| ExpertMailer.send_dossier_decision(avis_id).deliver_later }
|
|
|
|
|
end
|
2015-08-10 11:05:06 +02:00
|
|
|
|
end
|