350 lines
12 KiB
Ruby
350 lines
12 KiB
Ruby
class Dossier < ApplicationRecord
|
|
enum state: {
|
|
brouillon: 'brouillon',
|
|
en_construction: 'en_construction',
|
|
en_instruction: 'en_instruction',
|
|
accepte: 'accepte',
|
|
refuse: 'refuse',
|
|
sans_suite: 'sans_suite'
|
|
}
|
|
|
|
EN_CONSTRUCTION_OU_INSTRUCTION = %w(en_construction en_instruction)
|
|
TERMINE = %w(accepte refuse sans_suite)
|
|
INSTRUCTION_COMMENCEE = TERMINE + %w(en_instruction)
|
|
SOUMIS = EN_CONSTRUCTION_OU_INSTRUCTION + TERMINE
|
|
|
|
has_one :etablissement, dependent: :destroy
|
|
has_one :individual, dependent: :destroy
|
|
has_one :attestation
|
|
has_many :cerfa, dependent: :destroy
|
|
|
|
has_many :pieces_justificatives, dependent: :destroy
|
|
has_many :champs, -> { public_only }, dependent: :destroy
|
|
has_many :champs_private, -> { private_only }, class_name: 'Champ', dependent: :destroy
|
|
has_many :quartier_prioritaires, dependent: :destroy
|
|
has_many :cadastres, dependent: :destroy
|
|
has_many :commentaires, dependent: :destroy
|
|
has_many :invites, dependent: :destroy
|
|
has_many :invites_user, class_name: 'InviteUser', dependent: :destroy
|
|
has_many :invites_gestionnaires, class_name: 'InviteGestionnaire', dependent: :destroy
|
|
has_many :follows
|
|
has_many :avis, dependent: :destroy
|
|
|
|
belongs_to :procedure
|
|
belongs_to :user
|
|
|
|
accepts_nested_attributes_for :champs
|
|
accepts_nested_attributes_for :champs_private
|
|
|
|
validates :autorisation_donnees, acceptance: { message: 'doit être coché' }, allow_nil: false, on: :update
|
|
|
|
default_scope { where(hidden_at: nil) }
|
|
scope :state_brouillon, -> { where(state: 'brouillon') }
|
|
scope :state_not_brouillon, -> { where.not(state: 'brouillon') }
|
|
scope :state_en_construction, -> { where(state: 'en_construction') }
|
|
scope :state_en_instruction, -> { where(state: 'en_instruction') }
|
|
scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) }
|
|
scope :state_instruction_commencee, -> { where(state: INSTRUCTION_COMMENCEE) }
|
|
scope :state_termine, -> { where(state: TERMINE) }
|
|
|
|
scope :archived, -> { where(archived: true) }
|
|
scope :not_archived, -> { where(archived: false) }
|
|
|
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
|
|
|
scope :all_state, -> { not_archived.state_not_brouillon }
|
|
scope :en_construction, -> { not_archived.state_en_construction }
|
|
scope :en_instruction, -> { not_archived.state_en_instruction }
|
|
scope :termine, -> { not_archived.state_termine }
|
|
scope :downloadable_sorted, -> { state_not_brouillon.includes(:etablissement, :champs, :champs_private).order(en_construction_at: 'asc') }
|
|
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
|
|
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
|
|
scope :followed_by, -> (gestionnaire) { joins(:follows).where(follows: { gestionnaire: gestionnaire }) }
|
|
scope :with_ordered_champs, -> { includes(champs: :type_de_champ).order('types_de_champ.order_place') }
|
|
|
|
accepts_nested_attributes_for :individual
|
|
|
|
delegate :siret, :siren, to: :etablissement, allow_nil: true
|
|
delegate :types_de_piece_justificative, to: :procedure
|
|
delegate :types_de_champ, to: :procedure
|
|
delegate :france_connect_information, to: :user
|
|
|
|
before_validation :update_state_dates, if: -> { state_changed? }
|
|
|
|
after_save :build_default_champs, if: Proc.new { saved_change_to_procedure_id? }
|
|
after_save :build_default_individual, if: Proc.new { procedure.for_individual? }
|
|
after_save :send_dossier_received
|
|
after_save :send_web_hook
|
|
after_create :send_draft_notification_email
|
|
|
|
validates :user, presence: true
|
|
|
|
def was_piece_justificative_uploaded_for_type_id?(type_id)
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0
|
|
end
|
|
|
|
def retrieve_last_piece_justificative_by_type(type)
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type).last
|
|
end
|
|
|
|
def retrieve_all_piece_justificative_by_type(type)
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type).order(created_at: :DESC)
|
|
end
|
|
|
|
def build_default_champs
|
|
procedure.all_types_de_champ.each do |type_de_champ|
|
|
type_de_champ.champ.create(dossier: self)
|
|
end
|
|
end
|
|
|
|
def build_default_individual
|
|
if Individual.where(dossier_id: self.id).count == 0
|
|
Individual.create(dossier: self)
|
|
end
|
|
end
|
|
|
|
def ordered_champs
|
|
# TODO: use the line below when the procedure preview does not leak champ with dossier_id == 0
|
|
# champs.joins(:type_de_champ).order('types_de_champ.order_place')
|
|
champs.joins(', types_de_champ').where("champs.type_de_champ_id = types_de_champ.id AND types_de_champ.procedure_id = #{procedure.id}").order('order_place')
|
|
end
|
|
|
|
def ordered_champs_private
|
|
# TODO: use the line below when the procedure preview does not leak champ with dossier_id == 0
|
|
# champs_private.includes(:type_de_champ).order('types_de_champ.order_place')
|
|
champs_private.joins(', types_de_champ').where("champs.type_de_champ_id = types_de_champ.id AND types_de_champ.procedure_id = #{procedure.id}").order('order_place')
|
|
end
|
|
|
|
def ordered_pieces_justificatives
|
|
champs.joins(', types_de_piece_justificative').where("pieces_justificatives.type_de_piece_justificative_id = types_de_piece_justificative.id AND types_de_piece_justificative.procedure_id = #{procedure.id}").order('order_place ASC')
|
|
end
|
|
|
|
def en_construction_ou_instruction?
|
|
EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
|
|
end
|
|
|
|
def termine?
|
|
TERMINE.include?(state)
|
|
end
|
|
|
|
def instruction_commencee?
|
|
INSTRUCTION_COMMENCEE.include?(state)
|
|
end
|
|
|
|
def cerfa_available?
|
|
procedure.cerfa_flag? && cerfa.size != 0
|
|
end
|
|
|
|
def export_headers
|
|
serialized_dossier = DossierTableExportSerializer.new(self)
|
|
headers = serialized_dossier.attributes.keys
|
|
headers += procedure.types_de_champ.order(:order_place).map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
|
|
headers += procedure.types_de_champ_private.order(:order_place).map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
|
|
headers += export_etablissement_data.keys
|
|
headers
|
|
end
|
|
|
|
def export_values
|
|
sorted_values.map do |value|
|
|
serialize_value_for_export(value)
|
|
end
|
|
end
|
|
|
|
def followers_gestionnaires
|
|
follows.includes(:gestionnaire).map(&:gestionnaire)
|
|
end
|
|
|
|
def reset!
|
|
etablissement.destroy
|
|
|
|
update_columns(autorisation_donnees: false)
|
|
end
|
|
|
|
def total_follow
|
|
follows.size
|
|
end
|
|
|
|
def read_only?
|
|
en_instruction? || accepte? || refuse? || sans_suite?
|
|
end
|
|
|
|
def invite_for_user(user)
|
|
invites_user.find_by(user_id: user.id)
|
|
end
|
|
|
|
def can_be_en_construction?
|
|
!(procedure.archivee? && brouillon?)
|
|
end
|
|
|
|
def can_be_updated_by_the_user?
|
|
brouillon? || en_construction?
|
|
end
|
|
|
|
def text_summary
|
|
if brouillon?
|
|
parts = [
|
|
"Dossier en brouillon répondant à la procédure ",
|
|
procedure.libelle,
|
|
" gérée par l'organisme ",
|
|
procedure.organisation
|
|
]
|
|
else
|
|
parts = [
|
|
"Dossier déposé le ",
|
|
en_construction_at.localtime.strftime("%d/%m/%Y"),
|
|
" sur la procédure ",
|
|
procedure.libelle,
|
|
" gérée par l'organisme ",
|
|
procedure.organisation
|
|
]
|
|
end
|
|
|
|
parts.join
|
|
end
|
|
|
|
def avis_for(gestionnaire)
|
|
if gestionnaire.dossiers.include?(self)
|
|
avis.order(created_at: :asc)
|
|
else
|
|
avis
|
|
.where(confidentiel: false)
|
|
.or(avis.where(claimant: gestionnaire))
|
|
.or(avis.where(gestionnaire: gestionnaire))
|
|
.order(created_at: :asc)
|
|
end
|
|
end
|
|
|
|
def get_value(table, column)
|
|
case table
|
|
when 'self'
|
|
self.send(column)
|
|
when 'user'
|
|
self.user.send(column)
|
|
when 'france_connect_information'
|
|
self.user.france_connect_information&.send(column)
|
|
when 'entreprise'
|
|
self.etablissement&.send(:"entreprise_#{column}")
|
|
when 'etablissement'
|
|
self.etablissement&.send(column)
|
|
when 'type_de_champ'
|
|
self.champs.find { |c| c.type_de_champ_id == column.to_i }.value
|
|
when 'type_de_champ_private'
|
|
self.champs_private.find { |c| c.type_de_champ_id == column.to_i }.value
|
|
end
|
|
end
|
|
|
|
def self.sanitize_for_order(order)
|
|
sanitize_sql_for_order(order)
|
|
end
|
|
|
|
def owner_name
|
|
if etablissement.present?
|
|
etablissement.entreprise_raison_sociale
|
|
elsif individual.present?
|
|
"#{individual.nom} #{individual.prenom}"
|
|
end
|
|
end
|
|
|
|
def statut
|
|
if accepte?
|
|
'accepté'
|
|
elsif sans_suite?
|
|
'classé sans suite'
|
|
elsif refuse?
|
|
'refusé'
|
|
end
|
|
end
|
|
|
|
def user_geometry
|
|
if json_latlngs.present?
|
|
UserGeometry.new(json_latlngs)
|
|
end
|
|
end
|
|
|
|
def unspecified_attestation_champs
|
|
attestation_template = procedure.attestation_template
|
|
|
|
if attestation_template&.activated?
|
|
attestation_template.unspecified_champs_for_dossier(self)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def build_attestation
|
|
if procedure.attestation_template&.activated?
|
|
procedure.attestation_template.attestation_for(self)
|
|
end
|
|
end
|
|
|
|
def delete_and_keep_track
|
|
now = Time.now
|
|
deleted_dossier = DeletedDossier.create!(dossier_id: id, procedure: procedure, state: state, deleted_at: now)
|
|
update(hidden_at: now)
|
|
DossierMailer.notify_deletion_to_user(deleted_dossier, user.email).deliver_later
|
|
DossierMailer.notify_deletion_to_administration(deleted_dossier, procedure.administrateur.email).deliver_later
|
|
end
|
|
|
|
private
|
|
|
|
def update_state_dates
|
|
if en_construction? && !self.en_construction_at
|
|
self.en_construction_at = DateTime.now
|
|
elsif en_instruction? && !self.en_instruction_at
|
|
self.en_instruction_at = DateTime.now
|
|
elsif TERMINE.include?(state)
|
|
self.processed_at = DateTime.now
|
|
end
|
|
end
|
|
|
|
def serialize_value_for_export(value)
|
|
value.nil? || value.kind_of?(Time) ? value : value.to_s
|
|
end
|
|
|
|
def convert_specific_hash_values_to_string(hash_to_convert)
|
|
hash_to_convert.transform_values do |value|
|
|
serialize_value_for_export(value)
|
|
end
|
|
end
|
|
|
|
def export_etablissement_data
|
|
if etablissement.present?
|
|
etablissement_attr = EtablissementCsvSerializer.new(etablissement).attributes.transform_keys { |k| "etablissement.#{k}".parameterize.underscore.to_sym }
|
|
entreprise_attr = EntrepriseSerializer.new(etablissement.entreprise).attributes.transform_keys { |k| "entreprise.#{k}".parameterize.underscore.to_sym }
|
|
else
|
|
etablissement_attr = EtablissementSerializer.new(Etablissement.new).attributes.transform_keys { |k| "etablissement.#{k}".parameterize.underscore.to_sym }
|
|
entreprise_attr = EntrepriseSerializer.new(Entreprise.new).attributes.transform_keys { |k| "entreprise.#{k}".parameterize.underscore.to_sym }
|
|
end
|
|
convert_specific_hash_values_to_string(etablissement_attr.merge(entreprise_attr))
|
|
end
|
|
|
|
def sorted_values
|
|
serialized_dossier = DossierTableExportSerializer.new(self)
|
|
values = serialized_dossier.attributes.values
|
|
values += ordered_champs.map(&:for_export)
|
|
values += ordered_champs_private.map(&:for_export)
|
|
values += export_etablissement_data.values
|
|
values
|
|
end
|
|
|
|
def send_dossier_received
|
|
if saved_change_to_state? && en_instruction?
|
|
NotificationMailer.send_dossier_received(self).deliver_later
|
|
end
|
|
end
|
|
|
|
def send_draft_notification_email
|
|
if brouillon?
|
|
NotificationMailer.send_draft_notification(self).deliver_later
|
|
end
|
|
end
|
|
|
|
def send_web_hook
|
|
if saved_change_to_state? && !brouillon? && procedure.web_hook_url
|
|
WebHookJob.perform_later(
|
|
procedure,
|
|
self
|
|
)
|
|
end
|
|
end
|
|
end
|