demarches-normaliennes/app/models/dossier.rb
2019-02-04 16:19:07 +01:00

391 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 = [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)]
SOUMIS = EN_CONSTRUCTION_OU_INSTRUCTION + TERMINE
has_one :etablissement, dependent: :destroy
has_one :individual, dependent: :destroy
has_one :attestation, dependent: :destroy
has_many :pieces_justificatives, dependent: :destroy
has_many :champs, -> { root.public_only.ordered }, dependent: :destroy
has_many :champs_private, -> { root.private_only.ordered }, class_name: 'Champ', dependent: :destroy
has_many :commentaires, dependent: :destroy
has_many :invites, dependent: :destroy
has_many :follows
has_many :followers_gestionnaires, through: :follows, source: :gestionnaire
has_many :avis, dependent: :destroy
has_many :dossier_operation_logs, dependent: :destroy
belongs_to :procedure
belongs_to :user
accepts_nested_attributes_for :champs
accepts_nested_attributes_for :champs_private
default_scope { where(hidden_at: nil) }
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)) }
scope :state_en_instruction, -> { where(state: states.fetch(: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, :user, :individual, :followers_gestionnaires, champs: { etablissement: [], type_de_champ: :drop_down_list }, champs_private: { etablissement: [], type_de_champ: :drop_down_list }).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_champs, -> { includes(champs: :type_de_champ) }
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
scope :since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
scope :for_api, -> {
includes(commentaires: [],
champs: [
:geo_areas,
:etablissement,
piece_justificative_file_attachment: :blob
],
champs_private: [
:geo_areas,
:etablissement,
piece_justificative_file_attachment: :blob
],
pieces_justificatives: [],
etablissement: [],
individual: [],
user: [])
}
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? }
before_save :build_default_champs, if: Proc.new { procedure_id_changed? }
before_save :build_default_individual, if: Proc.new { procedure.for_individual? }
before_save :update_search_terms
after_save :send_dossier_received
after_save :send_web_hook
after_create :send_draft_notification_email
validates :user, presence: true
def update_search_terms
self.search_terms = [
user&.email,
*champs.flat_map(&:search_terms),
*etablissement&.search_terms,
individual&.nom,
individual&.prenom
].compact.join(' ')
self.private_search_terms = champs_private.flat_map(&:search_terms).compact.join(' ')
end
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.types_de_champ.each do |type_de_champ|
champ = type_de_champ.champ.build
if type_de_champ.repetition?
champ.add_row
end
champs << champ
end
procedure.types_de_champ_private.each do |type_de_champ|
champs_private << type_de_champ.champ.build
end
end
def build_default_individual
if Individual.where(dossier_id: self.id).count == 0
build_individual
end
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 reset!
etablissement.destroy
update_columns(autorisation_donnees: false)
end
def read_only?
en_instruction? || accepte? || refuse? || sans_suite?
end
def can_transition_to_en_construction?
!procedure.archivee? && brouillon?
end
def can_be_updated_by_the_user?
brouillon? || en_construction?
end
def retention_end_date
if instruction_commencee?
en_instruction_at + procedure.duree_conservation_dossiers_dans_ds.months
end
end
def retention_expired?
instruction_commencee? && retention_end_date <= Time.zone.now
end
def text_summary
if brouillon?
parts = [
"Dossier en brouillon répondant à la démarche ",
procedure.libelle,
" gérée par l'organisme ",
procedure.organisation
]
else
parts = [
"Dossier déposé le ",
en_construction_at.strftime("%d/%m/%Y"),
" sur la démarche ",
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 owner_name
if etablissement.present?
etablissement.entreprise_raison_sociale
elsif individual.present?
"#{individual.nom} #{individual.prenom}"
end
end
def expose_legacy_carto_api?
procedure.expose_legacy_carto_api?
end
def geo_position
if etablissement.present?
point = ApiAdresse::PointAdapter.new(etablissement.geo_adresse).geocode
end
lon = "2.428462"
lat = "46.538192"
zoom = "13"
if point.present?
lon = point.x.to_s
lat = point.y.to_s
end
{ lon: lon, lat: lat, zoom: zoom }
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.zone.now
deleted_dossier = DeletedDossier.create!(dossier_id: id, procedure: procedure, state: state, deleted_at: now)
update(hidden_at: now)
if en_construction?
administration_emails = followers_gestionnaires.present? ? followers_gestionnaires.pluck(:email) : [procedure.administrateur.email]
administration_emails.each do |email|
DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later
end
end
DossierMailer.notify_deletion_to_user(deleted_dossier, user.email).deliver_later
end
def passer_en_instruction!(gestionnaire)
en_instruction!
gestionnaire.follow(self)
log_dossier_operation(gestionnaire, :passer_en_instruction)
end
def passer_automatiquement_en_instruction!
en_instruction!
log_dossier_operation(nil, :passer_en_instruction, automatic_operation: true)
end
def repasser_en_construction!(gestionnaire)
self.en_instruction_at = nil
en_construction!
log_dossier_operation(gestionnaire, :repasser_en_construction)
end
def accepter!(gestionnaire, motivation)
self.motivation = motivation
self.en_instruction_at ||= Time.zone.now
accepte!
if attestation.nil?
update(attestation: build_attestation)
end
NotificationMailer.send_closed_notification(self).deliver_later
log_dossier_operation(gestionnaire, :accepter)
end
def accepter_automatiquement!
self.en_instruction_at ||= Time.zone.now
accepte!
if attestation.nil?
update(attestation: build_attestation)
end
NotificationMailer.send_closed_notification(self).deliver_later
log_dossier_operation(nil, :accepter, automatic_operation: true)
end
def refuser!(gestionnaire, motivation)
self.motivation = motivation
self.en_instruction_at ||= Time.zone.now
refuse!
NotificationMailer.send_refused_notification(self).deliver_later
log_dossier_operation(gestionnaire, :refuser)
end
def classer_sans_suite!(gestionnaire, motivation)
self.motivation = motivation
self.en_instruction_at ||= Time.zone.now
sans_suite!
NotificationMailer.send_without_continuation_notification(self).deliver_later
log_dossier_operation(gestionnaire, :classer_sans_suite)
end
def check_mandatory_champs
(champs + champs.select(&:repetition?).flat_map(&:champs))
.select(&:mandatory_and_blank?)
.map do |champ|
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
end
end
private
def log_dossier_operation(gestionnaire, operation, automatic_operation: false)
dossier_operation_logs.create(
gestionnaire: gestionnaire,
operation: DossierOperationLog.operations.fetch(operation),
automatic_operation: automatic_operation
)
end
def update_state_dates
if en_construction? && !self.en_construction_at
self.en_construction_at = Time.zone.now
elsif en_instruction? && !self.en_instruction_at
self.en_instruction_at = Time.zone.now
elsif TERMINE.include?(state)
self.processed_at = Time.zone.now
end
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?
DossierMailer.notify_new_draft(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