2018-03-06 13:44:29 +01:00
class Dossier < ApplicationRecord
2024-04-03 10:21:50 +02:00
self . ignored_columns += [ :re_instructed_at , :search_terms , :private_search_terms ]
2023-11-14 16:49:50 +01:00
2023-04-03 17:05:54 +02:00
include DossierCloneConcern
include DossierCorrectableConcern
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
2023-04-22 19:54:41 +02:00
include DossierSearchableConcern
2023-02-21 11:42:30 +01:00
include DossierSectionsConcern
2024-04-01 18:55:08 +02:00
include DossierStateConcern
2024-04-18 10:09:42 +02:00
include DossierChampsConcern
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 "
2023-11-07 07:47:40 +01:00
INTERVAL_BEFORE_EXPIRATION = " #{ Expired :: REMAINING_WEEKS_BEFORE_EXPIRATION } weeks "
2021-11-17 10:53:43 +01:00
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
2024-02-12 17:05:25 +01:00
has_many :champs_public , - > { root . public_only } , class_name : 'Champ' , inverse_of : false
has_many :champs_private , - > { root . private_only } , class_name : 'Champ' , inverse_of : false
2022-11-29 11:30:06 +01:00
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
2024-02-13 08:04:07 +01:00
has_many :preloaded_commentaires , - > { includes ( :dossier_correction , piece_jointe_attachments : :blob ) } , class_name : 'Commentaire' , inverse_of : :dossier
2023-06-02 15:46:58 +02:00
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
2024-06-05 14:21:58 +02:00
has_many :avis , - > { order ( :created_at ) } , 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 ,
2023-10-17 12:00:16 +02:00
processed_at : ,
browser : Current . browser )
2021-11-04 19:05:04 +01:00
end
2023-07-24 11:06:52 +02:00
def submit_en_construction ( processed_at : Time . zone . now )
2023-10-17 12:00:16 +02:00
build ( state : Dossier . states . fetch ( :en_construction ) ,
processed_at : ,
browser : Current . browser )
2023-07-24 11:06:52 +02:00
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 ,
2023-10-17 12:00:16 +02:00
processed_at : ,
browser : Current . browser )
2021-11-04 19:05:04 +01:00
end
2021-08-18 11:19:19 +02:00
def accepter_automatiquement ( processed_at : Time . zone . now )
build ( state : Dossier . states . fetch ( :accepte ) ,
2023-10-17 12:00:16 +02:00
processed_at : )
2021-08-18 11:19:19 +02:00
end
def accepter ( motivation : nil , instructeur : nil , processed_at : Time . zone . now )
build ( state : Dossier . states . fetch ( :accepte ) ,
instructeur_email : instructeur & . email ,
2023-10-17 12:00:16 +02:00
motivation : ,
processed_at : ,
browser : Current . browser )
2021-08-18 11:19:19 +02:00
end
def refuser ( motivation : nil , instructeur : nil , processed_at : Time . zone . now )
build ( state : Dossier . states . fetch ( :refuse ) ,
instructeur_email : instructeur & . email ,
2023-10-17 12:00:16 +02:00
motivation : ,
processed_at : ,
browser : Current . browser )
2021-08-18 11:19:19 +02:00
end
2023-07-31 13:50:22 +02:00
def refuser_automatiquement ( processed_at : Time . zone . now , motivation : )
build ( state : Dossier . states . fetch ( :refuse ) ,
2023-10-17 12:00:16 +02:00
motivation : ,
processed_at : )
2023-07-31 13:50:22 +02:00
end
2021-08-18 11:19:19 +02:00
def classer_sans_suite ( motivation : nil , instructeur : nil , processed_at : Time . zone . now )
build ( state : Dossier . states . fetch ( :sans_suite ) ,
instructeur_email : instructeur & . email ,
2023-10-17 12:00:16 +02:00
motivation : ,
processed_at : ,
browser : Current . browser )
2021-08-18 11:19:19 +02:00
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
2023-06-30 11:26:23 +02:00
has_many :dossier_assignments , - > { order ( :assigned_at ) } , inverse_of : :dossier , dependent : :destroy
has_one :dossier_assignment , - > { order ( assigned_at : :desc ) } , inverse_of : false
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-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
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
2023-06-21 16:42:41 +02:00
after_destroy_commit :log_destroy
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
2023-09-07 10:03:17 +02:00
accepts_nested_attributes_for :individual
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
2023-07-18 16:44:03 +02:00
state :en_construction
2019-07-02 15:38:23 +02:00
state :en_instruction
state :accepte
state :refuse
state :sans_suite
2024-04-01 18:56:21 +02:00
event :passer_en_construction , after : :after_passer_en_construction , after_commit : :after_commit_passer_en_construction do
2024-06-05 17:34:17 +02:00
transitions from : :brouillon , to : :en_construction , guard : :can_passer_en_construction?
2019-07-02 15:38:23 +02:00
end
2024-04-01 18:56:21 +02:00
event :passer_en_instruction , after : :after_passer_en_instruction , after_commit : :after_commit_passer_en_instruction do
2023-12-07 17:32:39 +01:00
transitions from : :en_construction , to : :en_instruction , guard : :can_passer_en_instruction?
2019-07-02 15:38:23 +02:00
end
2024-04-01 18:56:21 +02:00
event :passer_automatiquement_en_instruction , after : :after_passer_automatiquement_en_instruction , after_commit : :after_commit_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
2024-04-01 18:56:21 +02:00
event :repasser_en_construction , after : :after_repasser_en_construction , after_commit : :after_commit_repasser_en_construction do
2023-06-06 14:41:47 +02:00
transitions from : :en_instruction , to : :en_construction , guard : :can_repasser_en_construction?
end
2024-04-01 18:56:21 +02:00
event :repasser_en_construction_with_pending_correction , after : :after_repasser_en_construction , after_commit : :after_commit_repasser_en_construction do
2019-07-02 15:38:23 +02:00
transitions from : :en_instruction , to : :en_construction
end
2024-04-01 18:56:21 +02:00
event :accepter , after : :after_accepter , after_commit : :after_commit_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
2024-04-01 18:56:21 +02:00
event :accepter_automatiquement , after : :after_accepter_automatiquement , after_commit : :after_commit_accepter_automatiquement do
2022-12-02 18:15:43 +01:00
transitions from : :en_construction , to : :accepte , guard : :can_accepter_automatiquement?
2023-06-04 23:50:34 +02:00
transitions from : :en_instruction , to : :accepte , guard : :can_accepter_automatiquement?
2019-07-02 15:38:23 +02:00
end
2024-04-01 18:56:21 +02:00
event :refuser , after : :after_refuser , after_commit : :after_commit_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
2024-04-01 18:56:21 +02:00
event :refuser_automatiquement , after : :after_refuser_automatiquement , after_commit : :after_commit_refuser_automatiquement do
2023-07-31 13:50:22 +02:00
transitions from : :en_instruction , to : :refuse , guard : :can_refuser_automatiquement?
end
2024-04-01 18:56:21 +02:00
event :classer_sans_suite , after : :after_classer_sans_suite , after_commit : :after_commit_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
2024-04-01 18:56:21 +02:00
event :repasser_en_instruction , after : :after_repasser_en_instruction , after_commit : :after_commit_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 ) }
2024-06-12 16:32:42 +02:00
scope :hidden_by_expired , - > { where ( hidden_by_reason : 'expired' ) }
scope :visible_by_user , - > { where ( for_procedure_preview : false ) . where ( hidden_by_user_at : nil , editing_fork_origin_id : 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 ) }
2023-03-21 15:59:03 +01:00
scope :for_editing_fork , - > { where . not ( editing_fork_origin_id : nil ) }
2023-07-13 17:58:11 +02:00
scope :for_groupe_instructeur , - > ( groupe_instructeurs ) { where ( groupe_instructeur : groupe_instructeurs ) }
2023-07-21 16:20:31 +02:00
scope :order_by_updated_at , - > ( order = :desc ) { order ( updated_at : order , id : order ) }
scope :order_by_created_at , - > ( order = :asc ) { order ( depose_at : order , id : order ) }
2023-03-21 13:29:51 +01:00
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
2024-07-01 15:31:32 +02:00
scope :with_type_de_champ , - > ( stable_id ) { joins ( :champs ) . where ( champs : { stream : 'main' , stable_id : } ) }
2020-10-30 15:01:13 +01:00
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
:geo_areas ,
2022-10-20 10:14:47 +02:00
piece_justificative_file_attachments : :blob ,
2024-07-01 15:31:32 +02:00
champs : [ piece_justificative_file_attachments : :blob ]
2022-03-11 00:17:36 +01:00
] )
}
2023-04-18 12:03:10 +02:00
2023-04-25 10:56:59 +02:00
scope :brouillons_recently_updated , - > { updated_since ( 2 . days . ago ) . state_brouillon . order_by_updated_at }
2022-03-11 00:17:36 +01:00
scope :with_annotations , - > {
includes ( champs_private : [
:geo_areas ,
2022-10-20 10:14:47 +02:00
piece_justificative_file_attachments : :blob ,
2024-07-01 15:31:32 +02:00
champs : [ 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
2024-02-13 08:04:07 +01:00
. includes ( commentaires : { piece_jointe_attachments : :blob } ,
2022-03-11 00:17:36 +01:00
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 ) }
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
2023-05-09 13:39:46 +02:00
scope :by_statut , - > ( statut , instructeur = nil ) do
2022-03-31 16:33:14 +02:00
case statut
when 'a-suivre'
visible_by_administration
. without_followers
. en_cours
when 'suivis'
instructeur
. followed_dossiers
. merge ( visible_by_administration )
2023-05-04 18:24:18 +02:00
. en_cours
2022-03-31 16:33:14 +02:00
when 'traites'
visible_by_administration . termine
when 'tous'
visible_by_administration . all_state
when 'supprimes_recemment'
2024-06-12 16:32:42 +02:00
hidden_by_administration
2022-03-31 16:33:14 +02:00
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
2018-04-23 11:57:38 +02:00
delegate :siret , :siren , to : :etablissement , allow_nil : true
2024-03-05 14:00:54 +01:00
delegate :france_connected_with_one_identity? , to : :user , allow_nil : true
2023-03-21 15:59:03 +01:00
before_save :build_default_champs_for_new_dossier , if : Proc . new { revision_id_was . nil? && parent_dossier_id . nil? && editing_fork_origin_id . nil? }
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? }
2023-11-22 18:16:41 +01:00
validates :mandataire_first_name , presence : true , if : :for_tiers?
validates :mandataire_last_name , presence : true , if : :for_tiers?
validates :for_tiers , inclusion : { in : [ true , false ] } , if : - > { revision & . procedure & . for_individual? }
2015-08-21 11:37:13 +02:00
2023-10-26 16:02:23 +02:00
validates_associated :prefilled_champs_public , on : :champs_public_value
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 ,
2023-06-28 18:57:32 +02:00
:pending_corrections ,
2022-10-10 10:55:31 +02:00
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
2023-12-05 17:49:19 +01:00
def expiration_started?
[
brouillon_close_to_expiration_notice_sent_at ,
en_construction_close_to_expiration_notice_sent_at ,
termine_close_to_expiration_notice_sent_at
] . any? ( & :present? )
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
2022-11-07 18:09:06 +01:00
def build_default_champs_for_new_dossier
2024-07-01 15:31:32 +02:00
revision . build_champs_public ( self ) . each do | champ |
2022-11-10 22:21:14 +01:00
champs_public << champ
2018-08-22 17:16:06 +02:00
end
2024-07-01 15:31:32 +02:00
revision . build_champs_private ( self ) . 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?
2024-03-05 14:00:54 +01:00
self . individual = if france_connected_with_one_identity?
2024-03-21 17:57:57 +01:00
Individual . from_france_connect ( user . france_connect_informations . first )
2020-01-07 17:59:11 +01:00
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?
2023-03-21 15:59:03 +01:00
brouillon? && procedure . dossier_can_transition_to_en_construction? && ! for_procedure_preview? && ! editing_fork?
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?
2023-06-04 23:50:34 +02:00
return false unless can_terminer?
return true if declarative_triggered_at . nil? && procedure . declarative_accepte? && en_construction?
2023-07-31 13:50:22 +02:00
return true if procedure . sva? && can_terminer_automatiquement_by_sva_svr?
false
end
def can_refuser_automatiquement?
return false unless can_terminer?
return true if procedure . svr? && can_terminer_automatiquement_by_sva_svr?
2023-06-04 23:50:34 +02:00
false
2022-12-02 18:15:43 +01:00
end
2024-06-04 17:40:48 +02:00
def blocked_with_pending_correction?
procedure . feature_enabled? ( :blocking_pending_correction ) && pending_correction?
end
2024-06-05 17:34:17 +02:00
def can_passer_en_construction?
2024-06-20 15:16:53 +02:00
return true if ! revision . ineligibilite_enabled || ! revision . ineligibilite_rules
2024-06-05 17:34:17 +02:00
! revision . ineligibilite_rules . compute ( champs_for_revision ( scope : :public ) )
end
2023-12-07 17:32:39 +01:00
def can_passer_en_instruction?
2024-06-04 17:40:48 +02:00
return false if blocked_with_pending_correction?
2023-12-07 17:32:39 +01:00
true
end
2022-12-02 18:15:43 +01:00
def can_passer_automatiquement_en_instruction?
2023-12-12 12:07:12 +01:00
# Auto archive always passe en instruction, even if there is a pending correction
return true if procedure . auto_archive_on? && ! procedure . auto_archive_on . future?
2023-12-07 17:32:39 +01:00
return false if ! can_passer_en_instruction?
2023-06-02 17:51:02 +02:00
return true if declarative_triggered_at . nil? && procedure . declarative_en_instruction?
2023-12-18 12:32:27 +01:00
return true if procedure . sva_svr_enabled? && sva_svr_decision_triggered_at . nil? && ! pending_correction?
2023-06-02 17:51:02 +02:00
false
2022-12-02 18:15:43 +01:00
end
2023-06-06 14:41:47 +02:00
def can_repasser_en_construction?
! procedure . sva_svr_enabled?
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
2024-06-04 17:52:01 +02:00
def can_be_deleted_by_automatic? ( reason )
2024-06-12 16:32:42 +02:00
brouillon? || en_construction? || termine? || reason == :expired
2024-06-04 17:52:01 +02:00
end
2023-07-31 13:50:22 +02:00
def can_terminer_automatiquement_by_sva_svr?
sva_svr_decision_triggered_at . nil? && ! pending_correction? && ( sva_svr_decision_on . today? || sva_svr_decision_on . past? )
end
2023-01-18 18:31:37 +01:00
def any_etablissement_as_degraded_mode?
return true if etablissement & . as_degraded_mode?
2024-02-08 18:28:15 +01:00
return true if champs_for_revision ( scope : :public ) . any? { _1 . etablissement & . as_degraded_mode? }
2023-01-18 18:31:37 +01:00
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
2023-11-10 13:24:37 +01:00
def expiration_date_with_extension
2023-11-10 13:23:35 +01:00
expiration_date_reference + conservation_extension + procedure . duree_conservation_dossiers_dans_ds . months
2022-04-08 13:16:08 +02:00
end
def expiration_notification_date
2023-11-07 07:47:40 +01:00
expiration_date_with_extension - Expired :: 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?
2024-06-04 17:52:01 +02:00
expiration_notification_date < Time . zone . now && expiration_notification_date > Expired :: REMAINING_WEEKS_BEFORE_EXPIRATION . weeks . ago
end
def has_expired?
return false if en_instruction?
expiration_notification_date < Time . zone . now && expiration_notification_date < Expired :: REMAINING_WEEKS_BEFORE_EXPIRATION . weeks . ago
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
2023-11-10 13:24:37 +01:00
after_notification_expiration_date . presence || expiration_date_with_extension
2022-04-08 13:16:08 +02:00
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 )
2024-06-11 15:54:54 +02:00
2024-06-12 16:32:42 +02:00
if hidden_by_reason == 'expired'
update ( hidden_by_administration_at : nil , hidden_by_user_at : nil , hidden_by_reason : nil )
2024-06-11 15:54:54 +02:00
end
2022-04-08 13:16:08 +02:00
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
2023-07-05 17:48:18 +02:00
def assign_to_groupe_instructeur ( groupe_instructeur , mode , author = nil )
2023-04-14 10:38:45 +02:00
return if groupe_instructeur . present? && groupe_instructeur . procedure != procedure
return if self . groupe_instructeur == groupe_instructeur
2020-02-26 12:30:52 +01:00
2023-07-05 17:48:18 +02:00
previous_groupe_instructeur = self . groupe_instructeur
2023-09-06 14:41:26 +02:00
track_assigned_dossier_without_groupe_instructeur if groupe_instructeur . nil?
2023-04-14 10:38:45 +02:00
update! ( groupe_instructeur : , groupe_instructeur_updated_at : Time . zone . now )
2023-07-06 11:06:10 +02:00
update! ( forced_groupe_instructeur : true ) if mode == DossierAssignment . modes . fetch ( :manual )
2023-09-06 14:43:57 +02:00
create_assignment ( mode , previous_groupe_instructeur , groupe_instructeur , author & . email )
2023-04-14 10:38:45 +02:00
if ! brouillon?
unfollow_stale_instructeurs
if author . present?
log_dossier_operation ( author , :changer_groupe_instructeur , self )
2020-02-26 12:30:52 +01:00
end
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_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
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
2024-06-04 17:52:01 +02:00
def author_is_automatic ( author )
author == :automatic
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 )
2024-06-04 17:52:01 +02:00
elsif author_is_automatic ( author ) && can_be_deleted_by_automatic? ( reason )
2024-06-12 16:32:42 +02:00
update ( hidden_by_administration_at : Time . zone . now , hidden_by_user_at : Time . zone . now , hidden_by_reason : reason )
2022-03-09 10:27:43 +01:00
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
2023-11-29 12:59:55 +01:00
def email_template_for ( state )
procedure . email_template_for ( state )
2023-07-07 14:06:10 +02:00
end
2023-11-22 12:06:21 +01:00
def submit_en_construction!
2023-07-24 11:06:52 +02:00
self . traitements . submit_en_construction
save!
RoutingEngine . compute ( self )
2023-11-22 12:06:21 +01:00
resolve_pending_correction!
process_sva_svr!
2024-07-15 09:34:54 +02:00
remove_piece_justificative_file_not_visible!
2023-07-24 11:06:52 +02: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
2023-06-02 17:51:02 +02:00
def process_sva_svr!
return unless procedure . sva_svr_enabled?
return if sva_svr_decision_triggered_at . present?
2023-06-28 12:18:26 +02:00
# set or recompute sva date, except for dossiers submitted before sva was enabled
if depose_at . today? || sva_svr_decision_on . present?
self . sva_svr_decision_on = SVASVRDecisionDateCalculatorService . new ( self , procedure ) . decision_date
end
return if sva_svr_decision_on . nil?
2023-06-04 23:50:34 +02:00
if en_construction? && may_passer_automatiquement_en_instruction?
passer_automatiquement_en_instruction!
elsif en_instruction? && procedure . sva? && may_accepter_automatiquement?
accepter_automatiquement!
2023-07-31 13:50:22 +02:00
elsif en_instruction? && procedure . svr? && may_refuser_automatiquement?
refuser_automatiquement!
2023-06-05 18:22:33 +02:00
elsif will_save_change_to_sva_svr_decision_on?
save! # we always want the most up to date decision when there is a pending correction
2023-06-04 23:50:34 +02:00
end
2023-06-02 17:51:02 +02:00
end
2023-09-04 15:14:50 +02:00
def previously_termine?
traitements . termine . exists?
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
2024-07-15 09:34:54 +02:00
def remove_piece_justificative_file_not_visible!
2024-07-16 09:03:56 +02:00
champs . each do | champ |
next unless champ . piece_justificative_file . attached?
next if champ . visible?
champ . piece_justificative_file . purge_later
end
2024-07-15 09:34:54 +02:00
end
2022-10-17 10:41:47 +02:00
def check_mandatory_and_visible_champs
2024-02-08 18:28:15 +01:00
champs_for_revision ( scope : :public )
2023-11-28 17:33:29 +01:00
. filter { _1 . child? ? _1 . parent . visible? : true }
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 |
2024-06-05 19:16:41 +02:00
champ . errors . add ( :value , :missing )
2019-01-30 16:14:15 +01:00
end
2024-06-05 19:16:41 +02:00
. each { errors . import ( _1 ) }
2024-06-05 17:33:03 +02:00
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 ] ,
2024-02-19 16:59:09 +01:00
[ 'Email' , user_email_for ( :display ) ] ,
2024-02-19 17:33:26 +01:00
[ 'FranceConnect ?' , user_from_france_connect? ]
2019-10-31 14:14:14 +01:00
]
if procedure . for_individual?
columns += [
[ 'Civilité' , individual & . gender ] ,
[ 'Nom' , individual & . nom ] ,
2024-02-19 16:52:31 +01:00
[ 'Prénom' , individual & . prenom ] ,
2024-02-22 14:01:41 +01:00
[ 'Dépôt pour un tiers' , :for_tiers ] ,
2024-02-19 16:52:31 +01:00
[ 'Nom du mandataire' , :mandataire_last_name ] ,
[ 'Prénom du mandataire' , :mandataire_first_name ]
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
2023-11-22 17:15:51 +01:00
if procedure . chorusable? && procedure . chorus_configuration . complete?
2023-11-02 16:00:28 +01:00
columns += [
2023-11-22 17:15:51 +01:00
[ 'Domaine Fonctionnel' , procedure . chorus_configuration . domaine_fonctionnel & . fetch ( " code " ) { '' } ] ,
[ 'Référentiel De Programmation' , procedure . chorus_configuration . referentiel_de_programmation & . fetch ( " code " ) { '' } ] ,
2023-12-07 15:00:09 +01:00
[ 'Centre De Coût' , procedure . chorus_configuration . centre_de_cout & . fetch ( " code " ) { '' } ]
2023-11-02 16:00:28 +01:00
]
end
2019-10-31 14:14:14 +01:00
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 ] ,
2024-03-18 12:54:05 +01:00
[ 'Dernière mise à jour du dossier le' , :last_champ_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 ] ,
2023-08-31 15:25:29 +02:00
procedure . sva_svr_enabled? ? [ " Date décision #{ procedure . sva_svr_configuration . human_decision } " , :sva_svr_decision_on ] : nil ,
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 ( ' ' ) ]
2023-06-22 12:06:38 +02:00
] . compact
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
2024-03-15 14:10:12 +01:00
columns + champs_for_export ( types_de_champ )
2019-04-03 14:29:30 +02:00
end
2021-02-25 10:10:24 +01:00
def linked_dossiers_for ( instructeur_or_expert )
2023-11-28 17:33:29 +01:00
dossier_ids = champs_for_revision . 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-11-28 17:33:29 +01:00
GeoArea . exists? ( champ_id : champs_for_revision )
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
2024-01-17 13:15:42 +01:00
rescue = > e
Sentry . capture_exception ( e , extra : { dossier : id } )
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-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
2023-06-22 14:18:37 +02:00
def sva_svr_decision_in_days
( sva_svr_decision_on - Date . current ) . to_i
end
2023-07-04 23:16:19 +02:00
def create_assignment ( mode , previous_groupe_instructeur , groupe_instructeur , instructeur_email = nil )
DossierAssignment . create! (
dossier_id : self . id ,
mode : mode ,
previous_groupe_instructeur_id : previous_groupe_instructeur & . id ,
groupe_instructeur_id : groupe_instructeur . id ,
previous_groupe_instructeur_label : previous_groupe_instructeur & . label ,
groupe_instructeur_label : groupe_instructeur . label ,
assigned_at : Time . zone . now ,
assigned_by : instructeur_email
)
end
2023-08-14 17:43:35 +02:00
def service
groupe_instructeur & . contact_information || procedure . service
end
2023-11-22 18:16:41 +01:00
def mandataire_full_name
" #{ mandataire_first_name } #{ mandataire_last_name } "
end
2024-02-19 17:33:26 +01:00
def user_from_france_connect?
2024-02-29 11:21:32 +01:00
return false if user_deleted?
2024-03-05 14:00:54 +01:00
user . france_connected_with_one_identity?
2024-02-19 17:33:26 +01:00
end
2024-02-08 19:20:38 +01:00
def has_annotations?
revision . revision_types_de_champ_private . present?
end
2024-04-03 11:07:40 +02:00
def hide_info_with_accuse_lecture?
2024-03-26 14:50:49 +01:00
procedure . accuse_lecture? && termine? && accuse_lecture_agreement_at . blank?
end
2024-04-15 10:37:47 +02:00
def termine_and_accuse_lecture?
procedure . accuse_lecture? && termine?
end
2024-06-17 16:41:51 +02:00
def track_can_passer_en_construction
if ! revision . ineligibilite_enabled
yield
[ true , true ] # without eligibilite rules, we never reach dossier.champs.visible?, don't cache anything
else
from = can_passer_en_construction? # with eligibilite rules, self.champ[x].visible is cached by passing thru conditions checks
yield
champs . map ( & :reset_visible ) # we must reset self.champs[x].visible?, because an update occurred and we should re-evaluate champs[x] visibility
[ from , can_passer_en_construction? ]
end
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
2023-11-28 17:33:29 +01:00
champs_for_revision . 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
2023-06-21 16:42:41 +02:00
def log_destroy
app_traces = caller . reject { _1 . match? ( %r{ /ruby/.+/gems/ } ) } . map { _1 . sub ( Rails . root . to_s , " " ) }
payload = {
message : " Dossier destroyed " ,
dossier_id : id ,
procedure_id : procedure . id ,
request_id : Current . request_id ,
user_id : Current . user & . id ,
controller : app_traces . find { _1 . match? ( %r{ /controllers/|/jobs/ } ) } ,
2023-06-21 16:42:41 +02:00
caller : app_traces . first
2023-06-21 16:42:41 +02:00
}
logger = Lograge . logger || Rails . logger
logger . info payload . to_json
end
2023-09-06 14:41:26 +02:00
def track_assigned_dossier_without_groupe_instructeur
Sentry . capture_message (
" Assigned dossier without groupe_instructeur " ,
extra : {
dossier_id : self . id
}
)
end
2015-08-10 11:05:06 +02:00
end