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)
2022-11-30 12:39:47 +01:00
# archived_at :datetime
# archived_by :string
2020-08-06 16:35:45 +02:00
# 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
2023-01-11 11:17:34 +01:00
# migrated_champ_routage :boolean
2020-08-06 16:35:45 +02:00
# motivation :text
2023-01-03 14:46:10 +01:00
# prefill_token :string
# prefilled :boolean
2020-08-06 16:35:45 +02:00
# 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
2022-11-18 15:26:31 +01:00
# batch_operation_id :bigint
2021-09-07 10:36:09 +02:00
# dossier_transfer_id :bigint
2020-08-06 16:35:45 +02:00
# groupe_instructeur_id :bigint
2022-11-07 18:09:06 +01:00
# parent_dossier_id :bigint
2020-08-06 16:35:45 +02:00
# revision_id :bigint
# user_id :integer
#
2018-03-06 13:44:29 +01:00
class Dossier < ApplicationRecord
2019-02-27 12:03:53 +01:00
include DossierFilteringConcern
2022-12-01 09:37:00 +01:00
include DossierPrefillableConcern
2023-02-21 11:42:30 +01:00
include DossierRebaseConcern
include DossierSectionsConcern
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
2023-01-03 14:46:10 +01:00
has_secure_token :prefill_token
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
2022-11-29 11:30:06 +01:00
has_many :champs
2023-04-11 17:00:25 +02:00
# We have to remove champs in a particular order - champs with a reference to a parent have to be
# removed first, otherwise we get a foreign key constraint error.
has_many :champs_to_destroy , - > { order ( :parent_id ) } , class_name : 'Champ' , inverse_of : false , dependent : :destroy
has_many :champs_public , - > { root . public_ordered } , class_name : 'Champ' , inverse_of : false
has_many :champs_private , - > { root . private_ordered } , class_name : 'Champ' , inverse_of : false
2022-11-29 11:30:06 +01:00
has_many :champs_public_all , - > { public_only } , class_name : 'Champ' , inverse_of : false
has_many :champs_private_all , - > { private_only } , class_name : 'Champ' , inverse_of : false
has_many :prefilled_champs_public , - > { root . public_only . prefilled } , class_name : 'Champ' , inverse_of : false
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
2022-11-10 10:58:59 +01:00
belongs_to :parent_dossier , class_name : 'Dossier' , optional : true
2022-11-18 15:26:31 +01:00
belongs_to :batch_operation , optional : true
2023-02-07 10:27:51 +01:00
has_many :dossier_batch_operations , dependent : :destroy
2022-12-22 22:23:47 +01:00
has_many :batch_operations , through : :dossier_batch_operations
2021-05-12 19:04:31 +02:00
has_one :france_connect_information , through : :user
2015-08-12 10:09:52 +02:00
2020-08-27 19:55:10 +02:00
has_one :procedure , through : :revision
2022-11-09 12:09:24 +01:00
has_one :attestation_template , through : :procedure
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
2022-12-14 09:44:19 +01:00
has_many :cloned_dossiers , class_name : 'Dossier' , foreign_key : 'parent_dossier_id' , dependent : :nullify , inverse_of : :parent_dossier
2021-09-07 10:36:09 +02:00
2022-12-21 19:20:47 +01:00
accepts_nested_attributes_for :champs
2022-11-10 22:21:14 +01:00
accepts_nested_attributes_for :champs_public
2017-08-02 15:33:23 +02:00
accepts_nested_attributes_for :champs_private
2022-11-29 11:30:06 +01:00
accepts_nested_attributes_for :champs_public_all
accepts_nested_attributes_for :champs_private_all
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
2022-12-02 18:15:43 +01:00
transitions from : :en_construction , to : :en_instruction , guard : :can_passer_automatiquement_en_instruction?
2019-07-02 15:38:23 +02:00
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
2022-09-21 15:05:11 +02:00
transitions from : :en_instruction , to : :accepte , guard : :can_terminer?
2019-07-02 15:38:23 +02:00
end
event :accepter_automatiquement , after : :after_accepter_automatiquement do
2022-12-02 18:15:43 +01:00
transitions from : :en_construction , to : :accepte , guard : :can_accepter_automatiquement?
2019-07-02 15:38:23 +02:00
end
event :refuser , after : :after_refuser do
2022-09-21 15:05:11 +02:00
transitions from : :en_instruction , to : :refuse , guard : :can_terminer?
2019-07-02 15:38:23 +02:00
end
event :classer_sans_suite , after : :after_classer_sans_suite do
2022-09-21 15:05:11 +02:00
transitions from : :en_instruction , to : :sans_suite , guard : :can_terminer?
2019-07-02 15:38:23 +02:00
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 ) }
2023-02-23 13:13:01 +01:00
scope :state_accepte , - > { where ( state : states . fetch ( :accepte ) ) }
scope :state_refuse , - > { where ( state : states . fetch ( :refuse ) ) }
scope :state_sans_suite , - > { where ( state : states . fetch ( :sans_suite ) ) }
2017-05-26 18:23:16 +02:00
2023-01-03 14:46:10 +01:00
scope :archived , - > { where ( archived : true ) }
scope :not_archived , - > { where ( archived : false ) }
scope :prefilled , - > { where ( prefilled : true ) }
scope :hidden_by_user , - > { where . not ( hidden_by_user_at : nil ) }
scope :hidden_by_administration , - > { where . not ( hidden_by_administration_at : nil ) }
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 ) }
2023-03-21 13:29:51 +01:00
scope :hidden_for_administration , - > {
state_not_brouillon . hidden_by_administration . or ( state_en_construction . hidden_by_user )
}
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
2023-03-21 13:29:51 +01:00
scope :order_by_updated_at , - > ( order = :desc ) { order ( updated_at : order ) }
scope :order_by_created_at , - > ( order = :asc ) { order ( depose_at : order , created_at : order , id : order ) }
scope :updated_since , - > ( since ) { where ( 'dossiers.updated_at >= ?' , since ) }
scope :created_since , - > ( since ) { where ( 'dossiers.depose_at >= ?' , since ) }
2023-04-06 15:07:48 +02:00
scope :hidden_by_user_since , - > ( since ) { where ( 'dossiers.hidden_by_user_at IS NOT NULL AND dossiers.hidden_by_user_at >= ?' , since ) }
scope :hidden_by_administration_since , - > ( since ) { where ( 'dossiers.hidden_by_administration_at IS NOT NULL AND dossiers.hidden_by_administration_at >= ?' , since ) }
2023-03-21 13:29:51 +01:00
scope :hidden_since , - > ( since ) { hidden_by_user_since ( since ) . or ( hidden_by_administration_since ( 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-10-31 17:02:48 +01:00
scope :processed_by_month , - > ( all_groupe_instructeurs ) {
state_termine
. where ( groupe_instructeurs : all_groupe_instructeurs )
. group_by_period ( :month , :processed_at , reverse : true )
}
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
2023-04-19 11:01:27 +02:00
. where ( processed_at : date . all_month )
2021-04-29 17:29:47 +02:00
end
2022-10-10 10:55:31 +02:00
scope :ordered_for_export , - > {
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 }
2023-04-19 12:03:08 +02:00
scope :without_followers , - > { where . missing ( :follows ) }
2023-01-13 15:09:24 +01:00
scope :with_followers , - > { left_outer_joins ( :follows ) . where . not ( follows : { id : nil } ) }
2022-03-11 00:17:36 +01:00
scope :with_champs , - > {
2022-11-10 22:21:14 +01:00
includes ( champs_public : [
2022-03-11 00:17:36 +01:00
:type_de_champ ,
:geo_areas ,
2022-10-20 10:14:47 +02:00
piece_justificative_file_attachments : :blob ,
champs : [ :type_de_champ , piece_justificative_file_attachments : :blob ]
2022-03-11 00:17:36 +01:00
] )
}
scope :with_annotations , - > {
includes ( champs_private : [
:type_de_champ ,
:geo_areas ,
2022-10-20 10:14:47 +02:00
piece_justificative_file_attachments : :blob ,
champs : [ :type_de_champ , piece_justificative_file_attachments : :blob ]
2022-03-11 00:17:36 +01:00
] )
}
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 : [ ] ,
2022-12-06 09:45:59 +01:00
avis : { piece_justificative_file_attachment : :blob } ,
2022-03-11 00:17:36 +01:00
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-11-09 12:09:24 +01:00
scope :for_api_v2 , - > { includes ( :attestation_template , revision : [ 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
2022-11-25 15:43:00 +01:00
scope :not_having_batch_operation , - > { where ( batch_operation_id : nil ) }
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
2022-11-07 18:09:06 +01:00
before_save :build_default_champs_for_new_dossier , if : Proc . new { revision_id_was . nil? && parent_dossier_id . 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
2015-08-24 15:23:07 +02:00
2023-01-03 14:46:10 +01:00
validates :user , presence : true , if : - > { deleted_user_email_never_send . nil? } , unless : - > { prefilled }
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-12-01 12:00:21 +01:00
validates_associated :prefilled_champs_public , on : :prefilling
2022-11-30 11:23:04 +01:00
2022-07-07 19:14:27 +02:00
def types_de_champ_public
types_de_champ
end
2021-10-20 16:52:38 +02:00
def self . downloadable_sorted_batch
2022-10-10 10:55:31 +02:00
DossierPreloader . new ( includes (
:user ,
:individual ,
:followers_instructeurs ,
:traitement ,
:groupe_instructeur ,
:etablissement ,
procedure : [ :groupe_instructeurs ] ,
avis : [ :claimant , :expert ]
) . ordered_for_export ) . in_batches
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-06 00:49:59 +02:00
if termine?
traitement & . motivation || read_attribute ( :motivation )
end
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 ,
2022-11-10 22:21:14 +01:00
* champs_public . 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
2022-11-07 18:09:06 +01:00
def build_default_champs_for_new_dossier
2022-11-10 10:58:59 +01:00
revision . build_champs_public . each do | champ |
2022-11-10 22:21:14 +01:00
champs_public << 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
2023-02-02 09:59:03 +01:00
champs_public . filter { _1 . repetition? && _1 . mandatory? } . each do | champ |
champ . add_row ( revision )
end
champs_private . filter ( & :repetition? ) . each do | champ |
champ . add_row ( revision )
end
2015-11-03 15:27:49 +01:00
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-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
2022-09-21 15:05:11 +02:00
def can_terminer?
2023-01-18 18:31:37 +01:00
return false if any_etablissement_as_degraded_mode?
2022-09-21 15:05:11 +02:00
true
end
2022-12-02 18:15:43 +01:00
def can_accepter_automatiquement?
2022-12-14 18:15:57 +01:00
declarative_triggered_at . nil? && procedure . declarative_accepte? && can_terminer?
2022-12-02 18:15:43 +01:00
end
def can_passer_automatiquement_en_instruction?
2022-12-14 18:15:57 +01:00
( declarative_triggered_at . nil? && procedure . declarative_en_instruction? ) || procedure . auto_archive_on & . then { _1 < = Time . zone . today }
2022-12-02 18:15:43 +01: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
2023-01-18 18:31:37 +01:00
def any_etablissement_as_degraded_mode?
return true if etablissement & . as_degraded_mode?
return true if champs_public_all . any? { _1 . etablissement & . as_degraded_mode? }
false
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?
2023-03-20 18:14:59 +01:00
procedure . routing_enabled? && groupe_instructeur . present? && ( ! procedure . feature_enabled? ( :procedure_routage_api ) || ! defaut_groupe_instructeur? ) && ! procedure . feature_enabled? ( :routing_rules )
2021-03-03 18:21:00 +01:00
end
def show_groupe_instructeur_selector?
2023-03-20 18:14:59 +01:00
procedure . routing_enabled? && ! procedure . feature_enabled? ( :procedure_routage_api ) && ! procedure . feature_enabled? ( :routing_rules )
2021-03-03 18:21:00 +01:00
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
2023-01-11 18:49:39 +01:00
if update ( groupe_instructeur : , groupe_instructeur_updated_at : Time . zone . now )
if ! brouillon?
unfollow_stale_instructeurs
2020-02-26 12:30:52 +01:00
2023-01-11 18:49:39 +01:00
if author . present?
log_dossier_operation ( author , :changer_groupe_instructeur , self )
end
2020-02-26 12:30:52 +01:00
end
true
end
else
false
end
end
2022-11-30 12:39:47 +01:00
def archiver! ( instructeur )
update! ( archived : true , archived_at : Time . zone . now , archived_by : instructeur . email )
2020-09-09 15:04:58 +02:00
end
2022-11-30 12:39:47 +01:00
def desarchiver!
update! ( archived : false , archived_at : nil , archived_by : nil )
2020-09-09 15:04:58 +02:00
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
2023-01-03 14:46:10 +01:00
def orphan?
prefilled? && user . nil?
end
def owned_by? ( a_user )
return false if a_user . nil?
return false if orphan?
user == a_user
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
2022-11-24 15:49:15 +01:00
if attestation_template & . activated?
attestation_template . unspecified_champs_for_dossier ( self )
2018-04-06 13:09:37 +02:00
else
[ ]
end
end
2017-06-08 14:04:47 +02:00
def build_attestation
2022-11-24 15:49:15 +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 )
log_automatic_dossier_operation ( :supprimer , self )
2022-11-18 11:31:23 +01:00
dossier_operation_logs . purge_discarded
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
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!
2023-01-06 17:48:00 +01:00
procedure . compute_dossiers_count
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!
2022-12-06 18:11:56 +01:00
if ! disable_notification
2021-09-01 15:11:11 +02:00
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!
2022-12-06 18:11:56 +01:00
NotificationMailer . send_en_instruction_notification ( self ) . deliver_later
2019-05-02 16:22:16 +02:00
log_automatic_dossier_operation ( :passer_en_instruction )
2019-01-10 17:23:48 +01:00
end
2022-12-04 12:34:34 +01:00
def after_repasser_en_construction ( h )
instructeur = h [ :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!
2022-12-22 13:43:24 +01:00
rebase_later
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
2022-12-02 18:15:43 +01:00
def process_declarative!
if procedure . declarative_accepte? && may_accepter_automatiquement?
accepter_automatiquement!
elsif procedure . declarative_en_instruction? && may_passer_automatiquement_en_instruction?
passer_automatiquement_en_instruction!
end
end
2020-12-08 15:57:48 +01:00
def remove_titres_identite!
2022-11-10 22:21:14 +01:00
champs_public . filter ( & :titre_identite? ) . map ( & :piece_justificative_file ) . each ( & :purge_later )
2020-12-08 15:57:48 +01:00
end
2022-10-17 10:41:47 +02:00
def check_mandatory_and_visible_champs
2022-11-10 22:21:14 +01:00
( champs_public + champs_public . filter ( & :block? ) . filter ( & :visible? ) . flat_map ( & :champs ) )
2022-10-17 10:41:47 +02:00
. filter ( & :visible? )
2022-10-17 10:38:23 +02:00
. filter ( & :mandatory_blank? )
2019-01-30 16:14:15 +01:00
. map do | champ |
" Le champ #{ champ . libelle . truncate ( 200 ) } doit être rempli. "
end
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 ] ,
2022-07-18 13:21:50 +02:00
[ 'Entreprise état administratif' , etablissement & . entreprise_etat_administratif ] ,
2019-11-06 15:55:01 +01:00
[ '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
]
2022-11-04 10:10:57 +01:00
if procedure . routing_enabled?
2019-09-18 21:09:52 +02:00
columns << [ 'Groupe instructeur' , groupe_instructeur . label ]
end
2022-11-10 22:21:14 +01:00
columns + self . class . champs_for_export ( champs_public + 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-10-18 11:00:44 +02:00
champ = champs . find { | champ | champ . stable_id == type_de_champ . stable_id }
2022-10-13 18:08:52 +02:00
2022-10-18 11:00:44 +02:00
exported_values = if champ . nil? || ! champ . visible?
# some champs export multiple columns
# ex: commune.for_export => [commune, insee, departement]
# so we build a fake champ to have the right export
type_de_champ . champ . build . for_export
2022-10-13 18:08:52 +02:00
else
2022-10-18 11:00:44 +02:00
champ . for_export
2022-10-13 18:08:52 +02:00
end
2022-10-18 11:00:44 +02:00
# nil => [nil]
# text => [text]
# [commune, insee, departement] => [commune, insee, departement]
wrapped_exported_values = [ exported_values ] . flatten
wrapped_exported_values . 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 )
2022-11-10 22:21:14 +01:00
dossier_ids = champs_public . 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?
2023-01-26 14:14:40 +01:00
GeoArea . exists? ( champ_id : champs_public . ids + champs_private . ids )
2020-04-30 15:49:43 +02:00
end
def to_feature_collection
{
type : 'FeatureCollection' ,
id : id ,
bbox : bounding_box ,
features : geo_areas . map ( & :to_feature )
}
end
2022-11-16 11:50:19 +01:00
def self . to_feature_collection
{
type : 'FeatureCollection' ,
features : GeoArea . joins ( :champ ) . where ( champ : { dossier : ids } ) . 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-11-18 11:31:23 +01:00
dossier_operation_logs . purge_discarded
2022-02-08 10:47:43 +01:00
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-11-07 18:09:06 +01:00
def clone
2022-12-16 15:43:36 +01:00
dossier_attributes = [ :autorisation_donnees , :user_id , :revision_id , :groupe_instructeur_id ]
relationships = [ :individual , :etablissement ]
cloned_dossier = deep_clone ( only : dossier_attributes , include : relationships ) do | original , kopy |
PiecesJustificativesService . clone_attachments ( original , kopy )
2022-11-07 18:09:06 +01:00
if original . is_a? ( Dossier )
kopy . parent_dossier_id = original . id
kopy . state = Dossier . states . fetch ( :brouillon )
2022-12-16 15:43:36 +01:00
cloned_champs = original . champs
. index_by ( & :id )
. transform_values ( & :clone )
kopy . champs = cloned_champs . values . map do | champ |
champ . dossier = kopy
champ . parent = cloned_champs [ champ . parent_id ] if champ . child?
champ
2022-11-10 10:58:59 +01:00
end
2022-11-07 18:09:06 +01:00
end
end
2022-12-16 15:43:36 +01:00
transaction { cloned_dossier . save! }
cloned_dossier . reload
2022-11-07 18:09:06 +01:00
end
2022-12-01 11:17:48 +01:00
def find_champs_by_stable_ids ( stable_ids )
return [ ] if stable_ids . compact . empty?
2022-11-28 15:42:39 +01:00
2022-12-21 19:20:47 +01:00
champs . joins ( :type_de_champ ) . where ( types_de_champ : { stable_id : stable_ids } )
2022-11-28 15:42:39 +01:00
end
2022-12-21 17:18:19 +01:00
def skip_user_notification_email?
return true if brouillon? && procedure . declarative?
return true if for_procedure_preview?
return true if user_deleted?
false
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-11-10 22:21:14 +01:00
champs_public . flat_map ( & :geo_areas ) + champs_private . flat_map ( & :geo_areas )
2020-04-30 15:49:43 +02:00
end
def bounding_box
2023-02-14 18:38:01 +01:00
GeojsonService . bbox ( type : 'FeatureCollection' , features : geo_areas . map ( & :to_feature ) )
2020-04-30 15:49:43 +02:00
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
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
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
2022-10-06 15:17:54 +02:00
avis = avis_experts_procedures_ids
2021-04-09 09:55:36 +02:00
. uniq { | ( avis_id , experts_procedures_id ) | experts_procedures_id }
. map { | ( avis_id , _ ) | avis_id }
2022-10-06 15:17:54 +02:00
. then { | avis_ids | Avis . find ( avis_ids ) }
2021-04-09 09:55:36 +02:00
# rubocop:enable Lint/UnusedBlockArgument
2022-10-06 15:17:54 +02:00
avis . each { | a | ExpertMailer . send_dossier_decision_v2 ( a ) . deliver_later }
2021-04-09 09:55:36 +02:00
end
2015-08-10 11:05:06 +02:00
end