2015-08-10 11:05:06 +02:00
|
|
|
class Dossier < ActiveRecord::Base
|
2017-05-26 18:27:51 +02:00
|
|
|
enum state: {
|
|
|
|
draft: 'draft',
|
|
|
|
initiated: 'initiated',
|
|
|
|
replied: 'replied', # action utilisateur demandé
|
|
|
|
updated: 'updated', # etude par l'administration en cours
|
|
|
|
received: 'received',
|
|
|
|
closed: 'closed',
|
|
|
|
refused: 'refused',
|
|
|
|
without_continuation: 'without_continuation'
|
|
|
|
}
|
2015-09-22 18:30:20 +02:00
|
|
|
|
2017-05-26 18:22:31 +02:00
|
|
|
BROUILLON = %w(draft)
|
|
|
|
NOUVEAUX = %w(initiated)
|
|
|
|
OUVERT = %w(updated replied)
|
|
|
|
WAITING_FOR_GESTIONNAIRE = %w(updated)
|
|
|
|
WAITING_FOR_USER = %w(replied)
|
|
|
|
EN_CONSTRUCTION = %w(initiated updated replied)
|
|
|
|
EN_INSTRUCTION = %w(received)
|
2017-07-11 16:09:03 +02:00
|
|
|
EN_CONSTRUCTION_OU_INSTRUCTION = EN_CONSTRUCTION + EN_INSTRUCTION
|
2017-05-26 18:22:31 +02:00
|
|
|
A_INSTRUIRE = %w(received)
|
|
|
|
TERMINE = %w(closed refused without_continuation)
|
|
|
|
|
2015-09-24 11:45:00 +02:00
|
|
|
has_one :etablissement, dependent: :destroy
|
|
|
|
has_one :entreprise, dependent: :destroy
|
2016-08-30 11:18:43 +02:00
|
|
|
has_one :individual, dependent: :destroy
|
2017-06-02 14:30:26 +02:00
|
|
|
has_one :attestation
|
2016-03-16 15:34:35 +01:00
|
|
|
has_many :cerfa, dependent: :destroy
|
2016-01-18 16:20:51 +01:00
|
|
|
|
2015-09-24 11:45:00 +02:00
|
|
|
has_many :pieces_justificatives, dependent: :destroy
|
2016-08-08 12:52:30 +02:00
|
|
|
has_many :champs, class_name: 'ChampPublic', dependent: :destroy
|
|
|
|
has_many :champs_private, class_name: 'ChampPrivate', dependent: :destroy
|
2015-11-24 10:02:55 +01:00
|
|
|
has_many :quartier_prioritaires, dependent: :destroy
|
2016-01-18 12:03:18 +01:00
|
|
|
has_many :cadastres, dependent: :destroy
|
2016-01-18 16:20:51 +01:00
|
|
|
has_many :commentaires, dependent: :destroy
|
2016-02-08 18:16:18 +01:00
|
|
|
has_many :invites, dependent: :destroy
|
2016-09-14 16:36:01 +02:00
|
|
|
has_many :invites_user, class_name: 'InviteUser', dependent: :destroy
|
2017-04-14 18:20:14 +02:00
|
|
|
has_many :invites_gestionnaires, class_name: 'InviteGestionnaire', dependent: :destroy
|
2016-07-18 18:24:29 +02:00
|
|
|
has_many :follows
|
2016-12-21 17:26:31 +01:00
|
|
|
has_many :notifications, dependent: :destroy
|
2017-04-25 12:09:11 +02:00
|
|
|
has_many :avis, dependent: :destroy
|
2016-01-18 16:20:51 +01:00
|
|
|
|
2015-09-21 17:59:03 +02:00
|
|
|
belongs_to :procedure
|
2015-09-23 12:16:21 +02:00
|
|
|
belongs_to :user
|
2015-08-12 10:09:52 +02:00
|
|
|
|
2017-06-27 15:26:40 +02:00
|
|
|
default_scope { where(hidden_at: nil) }
|
2017-07-11 16:09:03 +02:00
|
|
|
scope :state_brouillon, -> { where(state: BROUILLON) }
|
|
|
|
scope :state_not_brouillon, -> { where.not(state: BROUILLON) }
|
|
|
|
scope :state_nouveaux, -> { where(state: NOUVEAUX) }
|
|
|
|
scope :state_ouvert, -> { where(state: OUVERT) }
|
|
|
|
scope :state_waiting_for_gestionnaire, -> { where(state: WAITING_FOR_GESTIONNAIRE) }
|
|
|
|
scope :state_waiting_for_user, -> { where(state: WAITING_FOR_USER) }
|
|
|
|
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_a_instruire, -> { where(state: A_INSTRUIRE) }
|
|
|
|
scope :state_termine, -> { where(state: TERMINE) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
2017-06-01 11:05:51 +02:00
|
|
|
scope :archived, -> { where(archived: true) }
|
|
|
|
scope :not_archived, -> { where(archived: false) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
|
|
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
|
|
|
|
2017-05-26 18:41:22 +02:00
|
|
|
scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) }
|
|
|
|
scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) }
|
2017-05-26 19:24:28 +02:00
|
|
|
scope :ouvert, -> { not_archived.state_ouvert.order_by_updated_at(:asc) }
|
2017-05-26 18:41:22 +02:00
|
|
|
scope :waiting_for_gestionnaire, -> { not_archived.state_waiting_for_gestionnaire.order_by_updated_at(:asc) }
|
|
|
|
scope :waiting_for_user, -> { not_archived.state_waiting_for_user.order_by_updated_at(:asc) }
|
|
|
|
scope :a_instruire, -> { not_archived.state_a_instruire.order_by_updated_at(:asc) }
|
2017-05-26 19:21:42 +02:00
|
|
|
scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) }
|
2017-05-26 18:45:08 +02:00
|
|
|
scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) }
|
2017-07-11 16:09:03 +02:00
|
|
|
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction.order_by_updated_at(:asc) }
|
|
|
|
scope :without_followers, -> { includes(:follows).where(follows: { id: nil }) }
|
2017-05-26 18:23:16 +02:00
|
|
|
|
2016-08-30 11:18:43 +02:00
|
|
|
accepts_nested_attributes_for :individual
|
|
|
|
|
2015-08-13 15:55:19 +02:00
|
|
|
delegate :siren, to: :entreprise
|
2015-12-03 12:00:22 +01:00
|
|
|
delegate :siret, to: :etablissement, allow_nil: true
|
2015-09-21 17:59:03 +02:00
|
|
|
delegate :types_de_piece_justificative, to: :procedure
|
2015-11-05 11:21:44 +01:00
|
|
|
delegate :types_de_champ, to: :procedure
|
2016-08-01 18:10:32 +02:00
|
|
|
delegate :france_connect_information, to: :user
|
2015-08-13 15:55:19 +02:00
|
|
|
|
2017-03-01 09:51:55 +01:00
|
|
|
before_validation :update_state_dates, if: -> { state_changed? }
|
|
|
|
|
2015-11-03 15:27:49 +01:00
|
|
|
after_save :build_default_champs, if: Proc.new { procedure_id_changed? }
|
2016-08-30 11:18:43 +02:00
|
|
|
after_save :build_default_individual, if: Proc.new { procedure.for_individual? }
|
2017-05-26 20:01:57 +02:00
|
|
|
after_save :send_notification_email
|
2015-08-24 15:23:07 +02:00
|
|
|
|
2015-09-24 11:17:17 +02:00
|
|
|
validates :user, presence: true
|
2015-08-21 11:37:13 +02:00
|
|
|
|
2017-01-02 16:45:03 +01:00
|
|
|
def unreaded_notifications
|
|
|
|
@unreaded_notif ||= notifications.where(already_read: false)
|
|
|
|
end
|
|
|
|
|
|
|
|
def first_unread_notification
|
|
|
|
unreaded_notifications.order("created_at ASC").first
|
|
|
|
end
|
|
|
|
|
2017-08-29 12:33:31 +02:00
|
|
|
def was_piece_justificative_uploaded_for_type_id?(type_id)
|
|
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0
|
|
|
|
end
|
|
|
|
|
2016-03-17 14:50:10 +01:00
|
|
|
def retrieve_last_piece_justificative_by_type(type)
|
2015-09-24 18:12:08 +02:00
|
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type).last
|
|
|
|
end
|
|
|
|
|
2016-03-17 14:50:10 +01:00
|
|
|
def retrieve_all_piece_justificative_by_type(type)
|
2016-03-22 17:36:36 +01:00
|
|
|
pieces_justificatives.where(type_de_piece_justificative_id: type).order(created_at: :DESC)
|
2015-09-24 18:12:08 +02:00
|
|
|
end
|
|
|
|
|
2015-11-03 15:27:49 +01:00
|
|
|
def build_default_champs
|
2017-08-29 12:20:58 +02:00
|
|
|
procedure.types_de_champ.all.each do |type_de_champ|
|
|
|
|
ChampPublic.create(type_de_champ: type_de_champ, dossier: self)
|
2016-08-08 12:52:30 +02:00
|
|
|
end
|
|
|
|
|
2017-08-29 12:20:58 +02:00
|
|
|
procedure.types_de_champ_private.all.each do |type_de_champ|
|
|
|
|
ChampPrivate.create(type_de_champ: type_de_champ, dossier: self)
|
2015-11-03 15:27:49 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-08-30 11:18:43 +02:00
|
|
|
def build_default_individual
|
2016-12-21 15:39:41 +01:00
|
|
|
if Individual.where(dossier_id: self.id).count == 0
|
|
|
|
Individual.create(dossier: self)
|
|
|
|
end
|
2016-08-30 11:18:43 +02:00
|
|
|
end
|
|
|
|
|
2015-11-04 11:14:07 +01:00
|
|
|
def ordered_champs
|
2017-08-29 16:30:34 +02:00
|
|
|
# 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')
|
2016-08-08 12:52:30 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def ordered_champs_private
|
2017-08-29 16:30:34 +02:00
|
|
|
# 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')
|
2015-11-04 11:14:07 +01:00
|
|
|
end
|
|
|
|
|
2016-10-07 15:16:03 +02:00
|
|
|
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
|
|
|
|
|
2017-06-13 11:50:06 +02:00
|
|
|
def next_step! role, action, motivation = nil
|
2017-02-23 17:54:11 +01:00
|
|
|
unless %w(initiate follow update comment receive refuse without_continuation close).include?(action)
|
2015-09-24 18:12:08 +02:00
|
|
|
fail 'action is not valid'
|
|
|
|
end
|
|
|
|
|
2015-10-05 16:42:29 +02:00
|
|
|
unless %w(user gestionnaire).include?(role)
|
2015-09-24 18:12:08 +02:00
|
|
|
fail 'role is not valid'
|
|
|
|
end
|
|
|
|
|
2017-04-05 10:22:37 +02:00
|
|
|
case role
|
|
|
|
when 'user'
|
2015-09-24 18:12:08 +02:00
|
|
|
case action
|
2017-04-05 10:22:37 +02:00
|
|
|
when 'initiate'
|
|
|
|
if draft?
|
|
|
|
initiated!
|
|
|
|
end
|
|
|
|
when 'update'
|
|
|
|
if replied?
|
|
|
|
updated!
|
|
|
|
end
|
|
|
|
when 'comment'
|
|
|
|
if replied?
|
|
|
|
updated!
|
|
|
|
end
|
2015-09-24 18:12:08 +02:00
|
|
|
end
|
2017-04-05 10:22:37 +02:00
|
|
|
when 'gestionnaire'
|
2015-09-24 18:12:08 +02:00
|
|
|
case action
|
2017-04-05 10:22:37 +02:00
|
|
|
when 'comment'
|
|
|
|
if updated?
|
|
|
|
replied!
|
|
|
|
elsif initiated?
|
|
|
|
replied!
|
|
|
|
end
|
|
|
|
when 'follow'
|
|
|
|
if initiated?
|
|
|
|
updated!
|
|
|
|
end
|
|
|
|
when 'close'
|
|
|
|
if received?
|
2017-06-08 14:04:47 +02:00
|
|
|
self.attestation = build_attestation
|
|
|
|
save
|
|
|
|
|
2017-04-05 10:22:37 +02:00
|
|
|
closed!
|
2017-06-13 11:50:06 +02:00
|
|
|
|
|
|
|
if motivation
|
|
|
|
self.motivation = motivation
|
|
|
|
save
|
|
|
|
end
|
2017-04-05 10:22:37 +02:00
|
|
|
end
|
|
|
|
when 'refuse'
|
|
|
|
if received?
|
|
|
|
refused!
|
2017-06-13 11:50:06 +02:00
|
|
|
|
|
|
|
if motivation
|
|
|
|
self.motivation = motivation
|
|
|
|
save
|
|
|
|
end
|
2017-04-05 10:22:37 +02:00
|
|
|
end
|
|
|
|
when 'without_continuation'
|
|
|
|
if received?
|
|
|
|
without_continuation!
|
2017-06-13 11:50:06 +02:00
|
|
|
|
|
|
|
if motivation
|
|
|
|
self.motivation = motivation
|
|
|
|
save
|
|
|
|
end
|
2017-04-05 10:22:37 +02:00
|
|
|
end
|
2015-09-24 18:12:08 +02:00
|
|
|
end
|
|
|
|
end
|
2017-04-05 10:22:37 +02:00
|
|
|
|
2015-09-24 18:12:08 +02:00
|
|
|
state
|
|
|
|
end
|
|
|
|
|
2017-04-12 10:28:22 +02:00
|
|
|
def brouillon?
|
|
|
|
BROUILLON.include?(state)
|
2016-10-05 16:45:51 +02:00
|
|
|
end
|
|
|
|
|
2017-07-11 15:43:20 +02:00
|
|
|
def en_construction?
|
|
|
|
EN_CONSTRUCTION.include?(state)
|
|
|
|
end
|
|
|
|
|
|
|
|
def en_instruction?
|
|
|
|
EN_INSTRUCTION.include?(state)
|
|
|
|
end
|
|
|
|
|
2016-02-02 18:37:38 +01:00
|
|
|
def cerfa_available?
|
2016-03-16 15:34:35 +01:00
|
|
|
procedure.cerfa_flag? && cerfa.size != 0
|
2016-02-02 18:37:38 +01:00
|
|
|
end
|
|
|
|
|
2016-11-14 17:43:34 +01:00
|
|
|
def convert_specific_hash_values_to_string(hash_to_convert)
|
2016-11-08 11:48:36 +01:00
|
|
|
hash = {}
|
|
|
|
hash_to_convert.each do |key, value|
|
2017-04-11 11:38:48 +02:00
|
|
|
value = serialize_value_for_export(value)
|
2016-11-14 17:25:17 +01:00
|
|
|
hash.store(key, value)
|
2016-11-08 11:48:36 +01:00
|
|
|
end
|
|
|
|
return hash
|
|
|
|
end
|
|
|
|
|
2017-04-05 15:10:58 +02:00
|
|
|
def full_data_strings_array
|
2017-04-05 15:11:44 +02:00
|
|
|
data_with_champs.map do |value|
|
2017-04-11 11:38:48 +02:00
|
|
|
serialize_value_for_export(value)
|
2016-11-14 17:43:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-09 15:36:18 +01:00
|
|
|
def export_entreprise_data
|
2016-10-07 15:34:10 +02:00
|
|
|
unless entreprise.nil?
|
2016-11-07 17:23:58 +01:00
|
|
|
etablissement_attr = EtablissementCsvSerializer.new(self.etablissement).attributes.map { |k, v| ["etablissement.#{k}".parameterize.underscore.to_sym, v] }.to_h
|
|
|
|
entreprise_attr = EntrepriseSerializer.new(self.entreprise).attributes.map { |k, v| ["entreprise.#{k}".parameterize.underscore.to_sym, v] }.to_h
|
|
|
|
else
|
|
|
|
etablissement_attr = EtablissementSerializer.new(Etablissement.new).attributes.map { |k, v| ["etablissement.#{k}".parameterize.underscore.to_sym, v] }.to_h
|
|
|
|
entreprise_attr = EntrepriseSerializer.new(Entreprise.new).attributes.map { |k, v| ["entreprise.#{k}".parameterize.underscore.to_sym, v] }.to_h
|
2016-10-07 15:34:10 +02:00
|
|
|
end
|
2016-11-14 17:43:34 +01:00
|
|
|
return convert_specific_hash_values_to_string(etablissement_attr.merge(entreprise_attr))
|
2016-11-09 15:36:18 +01:00
|
|
|
end
|
2016-10-07 15:34:10 +02:00
|
|
|
|
2016-11-14 10:41:56 +01:00
|
|
|
def data_with_champs
|
2017-04-13 15:05:55 +02:00
|
|
|
serialized_dossier = DossierTableExportSerializer.new(self)
|
2016-11-14 16:37:58 +01:00
|
|
|
data = serialized_dossier.attributes.values
|
2017-07-19 15:40:26 +02:00
|
|
|
data += self.ordered_champs.map(&:value)
|
|
|
|
data += self.ordered_champs_private.map(&:value)
|
2016-11-14 16:37:58 +01:00
|
|
|
data += self.export_entreprise_data.values
|
|
|
|
return data
|
|
|
|
end
|
|
|
|
|
|
|
|
def export_headers
|
2017-04-13 15:05:55 +02:00
|
|
|
serialized_dossier = DossierTableExportSerializer.new(self)
|
2016-11-14 16:37:58 +01:00
|
|
|
headers = serialized_dossier.attributes.keys
|
2017-07-19 15:40:26 +02:00
|
|
|
headers += self.procedure.types_de_champ.order(:order_place).map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
|
|
|
|
headers += self.procedure.types_de_champ_private.order(:order_place).map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
|
2016-11-14 16:37:58 +01:00
|
|
|
headers += self.export_entreprise_data.keys
|
|
|
|
return headers
|
2016-11-14 10:41:56 +01:00
|
|
|
end
|
|
|
|
|
2017-04-14 18:10:39 +02:00
|
|
|
def followers_gestionnaires
|
|
|
|
follows.includes(:gestionnaire).map(&:gestionnaire)
|
2017-01-26 17:54:04 +01:00
|
|
|
end
|
|
|
|
|
2016-06-20 13:57:57 +02:00
|
|
|
def reset!
|
2016-10-05 14:28:10 +02:00
|
|
|
etablissement.destroy
|
2016-10-05 15:01:31 +02:00
|
|
|
entreprise.destroy
|
2016-06-20 13:57:57 +02:00
|
|
|
|
|
|
|
update_attributes(autorisation_donnees: false)
|
|
|
|
end
|
2016-07-19 16:44:26 +02:00
|
|
|
|
|
|
|
def total_follow
|
|
|
|
follows.size
|
|
|
|
end
|
2016-07-22 15:06:30 +02:00
|
|
|
|
2016-09-13 12:17:56 +02:00
|
|
|
def read_only?
|
2017-02-21 18:05:48 +01:00
|
|
|
received? || closed? || refused? || without_continuation?
|
2016-09-13 12:17:56 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def owner? email
|
|
|
|
user.email == email
|
|
|
|
end
|
2016-09-14 16:36:01 +02:00
|
|
|
|
|
|
|
def invite_by_user? email
|
|
|
|
(invites_user.pluck :email).include? email
|
|
|
|
end
|
2017-03-01 09:51:55 +01:00
|
|
|
|
2017-03-06 18:17:28 +01:00
|
|
|
def can_be_initiated?
|
2017-07-13 15:09:25 +02:00
|
|
|
!(procedure.archivee? && draft?)
|
2017-03-06 18:17:28 +01:00
|
|
|
end
|
2017-03-06 17:54:45 +01:00
|
|
|
|
2017-04-18 17:31:01 +02:00
|
|
|
def text_summary
|
|
|
|
if brouillon?
|
|
|
|
parts = [
|
2017-04-26 15:08:15 +02:00
|
|
|
"Dossier en brouillon répondant à la procédure ",
|
2017-04-18 17:31:01 +02:00
|
|
|
procedure.libelle,
|
2017-05-02 09:48:25 +02:00
|
|
|
" gérée par l'organisme ",
|
2017-04-18 17:31:01 +02:00
|
|
|
procedure.organisation
|
|
|
|
]
|
|
|
|
else
|
|
|
|
parts = [
|
|
|
|
"Dossier déposé le ",
|
2017-05-11 12:46:42 +02:00
|
|
|
initiated_at.localtime.strftime("%d/%m/%Y"),
|
2017-04-26 15:08:15 +02:00
|
|
|
" sur la procédure ",
|
2017-04-18 17:31:01 +02:00
|
|
|
procedure.libelle,
|
2017-05-02 09:48:25 +02:00
|
|
|
" gérée par l'organisme ",
|
2017-04-18 17:31:01 +02:00
|
|
|
procedure.organisation
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
parts.join
|
|
|
|
end
|
|
|
|
|
2017-03-01 09:51:55 +01:00
|
|
|
private
|
|
|
|
|
2017-06-08 14:04:47 +02:00
|
|
|
def build_attestation
|
|
|
|
if procedure.attestation_template.present? && procedure.attestation_template.activated?
|
|
|
|
procedure.attestation_template.attestation_for(self)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-01 09:51:55 +01:00
|
|
|
def update_state_dates
|
|
|
|
if initiated? && !self.initiated_at
|
|
|
|
self.initiated_at = DateTime.now
|
|
|
|
elsif received? && !self.received_at
|
|
|
|
self.received_at = DateTime.now
|
|
|
|
elsif TERMINE.include?(state)
|
|
|
|
self.processed_at = DateTime.now
|
|
|
|
end
|
|
|
|
end
|
2017-03-06 17:54:45 +01:00
|
|
|
|
2017-04-11 11:38:48 +02:00
|
|
|
def serialize_value_for_export(value)
|
|
|
|
value.nil? || value.kind_of?(Time) ? value : value.to_s
|
|
|
|
end
|
2017-05-26 20:01:57 +02:00
|
|
|
|
|
|
|
def send_notification_email
|
|
|
|
if state_changed? && EN_INSTRUCTION.include?(state)
|
|
|
|
NotificationMailer.send_notification(self, procedure.received_mail_template).deliver_now!
|
|
|
|
end
|
|
|
|
end
|
2015-08-10 11:05:06 +02:00
|
|
|
end
|