2024-04-29 00:17:15 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-08-06 11:02:54 +02:00
|
|
|
class Instructeur < ApplicationRecord
|
2024-03-19 14:57:19 +01:00
|
|
|
self.ignored_columns += [:agent_connect_id]
|
|
|
|
|
2023-10-06 17:12:00 +02:00
|
|
|
include UserFindByConcern
|
2022-03-02 11:26:53 +01:00
|
|
|
has_and_belongs_to_many :administrateurs
|
2015-11-16 18:08:19 +01:00
|
|
|
|
2024-03-19 14:57:19 +01:00
|
|
|
has_many :agent_connect_information, dependent: :destroy
|
2023-12-12 14:23:42 +01:00
|
|
|
|
2016-06-20 17:37:04 +02:00
|
|
|
has_many :assign_to, dependent: :destroy
|
2022-11-08 12:42:16 +01:00
|
|
|
has_many :groupe_instructeurs, -> { order(:label) }, through: :assign_to
|
|
|
|
has_many :unordered_groupe_instructeurs, through: :assign_to, source: :groupe_instructeur
|
|
|
|
has_many :procedures, -> { distinct }, through: :unordered_groupe_instructeurs
|
2023-10-17 14:25:14 +02:00
|
|
|
has_many :deleted_dossiers, through: :procedures
|
2022-11-18 15:26:31 +01:00
|
|
|
has_many :batch_operations, dependent: :nullify
|
2019-08-06 11:02:54 +02:00
|
|
|
has_many :assign_to_with_email_notifications, -> { with_email_notifications }, class_name: 'AssignTo', inverse_of: :instructeur
|
2019-08-22 17:58:31 +02:00
|
|
|
has_many :groupe_instructeur_with_email_notifications, through: :assign_to_with_email_notifications, source: :groupe_instructeur
|
2024-03-02 22:13:09 +01:00
|
|
|
has_many :export_templates, through: :groupe_instructeurs
|
2019-03-18 14:43:05 +01:00
|
|
|
|
2022-10-19 11:53:10 +02:00
|
|
|
has_many :commentaires, inverse_of: :instructeur, dependent: :nullify
|
2022-11-08 12:42:16 +01:00
|
|
|
has_many :dossiers, -> { state_not_brouillon }, through: :unordered_groupe_instructeurs
|
2019-08-06 11:02:54 +02:00
|
|
|
has_many :follows, -> { active }, inverse_of: :instructeur
|
|
|
|
has_many :previous_follows, -> { inactive }, class_name: 'Follow', inverse_of: :instructeur
|
2018-01-23 18:23:07 +01:00
|
|
|
has_many :followed_dossiers, through: :follows, source: :dossier
|
2019-06-07 14:59:49 +02:00
|
|
|
has_many :previously_followed_dossiers, -> { distinct }, through: :previous_follows, source: :dossier
|
2020-01-30 10:48:28 +01:00
|
|
|
has_many :trusted_device_tokens, dependent: :destroy
|
2021-07-22 14:30:48 +02:00
|
|
|
has_many :bulk_messages, dependent: :destroy
|
2024-02-07 17:19:50 +01:00
|
|
|
has_many :exports, as: :user_profile
|
|
|
|
has_many :archives, as: :user_profile
|
2016-03-14 16:49:12 +01:00
|
|
|
|
2022-03-15 17:28:22 +01:00
|
|
|
belongs_to :user
|
2019-08-08 16:49:40 +02:00
|
|
|
|
2020-04-02 10:18:23 +02:00
|
|
|
scope :with_instant_email_message_notifications, -> {
|
|
|
|
includes(:assign_to).where(assign_tos: { instant_email_message_notifications_enabled: true })
|
|
|
|
}
|
|
|
|
|
2020-04-09 11:43:23 +02:00
|
|
|
scope :with_instant_email_dossier_notifications, -> {
|
|
|
|
includes(:assign_to).where(assign_tos: { instant_email_dossier_notifications_enabled: true })
|
|
|
|
}
|
|
|
|
|
2022-08-30 21:41:36 +02:00
|
|
|
scope :with_instant_expert_avis_email_notifications_enabled, -> {
|
|
|
|
includes(:assign_to).where(assign_tos: { instant_expert_avis_email_notifications_enabled: true })
|
|
|
|
}
|
|
|
|
|
2019-10-31 11:49:22 +01:00
|
|
|
default_scope { eager_load(:user) }
|
|
|
|
|
2019-10-15 18:32:18 +02:00
|
|
|
def email
|
|
|
|
user.email
|
|
|
|
end
|
|
|
|
|
2017-07-17 11:28:14 +02:00
|
|
|
def follow(dossier)
|
2019-04-09 11:00:13 +02:00
|
|
|
begin
|
|
|
|
followed_dossiers << dossier
|
2019-06-07 14:59:14 +02:00
|
|
|
# If the user tries to follow a dossier she already follows,
|
|
|
|
# we just fail silently: it means the goal is already reached.
|
2019-04-09 11:00:13 +02:00
|
|
|
rescue ActiveRecord::RecordNotUnique
|
2019-06-07 14:59:14 +02:00
|
|
|
# Database uniqueness constraint
|
|
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
|
|
# ActiveRecord validation
|
2019-08-06 11:02:54 +02:00
|
|
|
raise unless e.record.errors.details.dig(:instructeur_id, 0, :error) == :taken
|
2019-04-09 11:00:13 +02:00
|
|
|
end
|
2017-07-17 11:28:14 +02:00
|
|
|
end
|
|
|
|
|
2017-10-05 14:10:49 +02:00
|
|
|
def unfollow(dossier)
|
2019-06-07 14:59:49 +02:00
|
|
|
f = follows.find_by(dossier: dossier)
|
|
|
|
if f.present?
|
|
|
|
f.update(unfollowed_at: Time.zone.now)
|
|
|
|
end
|
2017-10-05 14:10:49 +02:00
|
|
|
end
|
|
|
|
|
2017-07-17 13:13:20 +02:00
|
|
|
def follow?(dossier)
|
|
|
|
followed_dossiers.include?(dossier)
|
2016-07-18 18:24:29 +02:00
|
|
|
end
|
2016-08-02 14:49:41 +02:00
|
|
|
|
2018-02-19 11:20:57 +01:00
|
|
|
def assign_to_procedure(procedure)
|
2021-09-14 19:48:38 +02:00
|
|
|
if !procedure.defaut_groupe_instructeur.in?(groupe_instructeurs)
|
|
|
|
groupe_instructeurs << procedure.defaut_groupe_instructeur
|
2018-02-20 11:24:32 +01:00
|
|
|
end
|
2018-02-19 11:20:57 +01:00
|
|
|
end
|
|
|
|
|
2022-08-30 21:41:36 +02:00
|
|
|
NOTIFICATION_SETTINGS = [:daily_email_notifications_enabled, :instant_email_dossier_notifications_enabled, :instant_email_message_notifications_enabled, :weekly_email_notifications_enabled, :instant_expert_avis_email_notifications_enabled]
|
2021-12-21 19:06:38 +01:00
|
|
|
|
|
|
|
def notification_settings(procedure_id)
|
|
|
|
assign_to
|
|
|
|
.joins(:groupe_instructeur)
|
|
|
|
.find_by(groupe_instructeurs: { procedure_id: procedure_id })
|
|
|
|
&.slice(*NOTIFICATION_SETTINGS) || {}
|
2018-02-19 11:20:57 +01:00
|
|
|
end
|
|
|
|
|
2017-05-12 16:47:18 +02:00
|
|
|
def last_week_overview
|
2018-10-25 15:07:15 +02:00
|
|
|
start_date = Time.zone.now.beginning_of_week
|
2017-05-12 16:47:18 +02:00
|
|
|
|
|
|
|
active_procedure_overviews = procedures
|
2020-02-11 15:51:15 +01:00
|
|
|
.where(assign_tos: { weekly_email_notifications_enabled: true })
|
2018-01-15 19:34:08 +01:00
|
|
|
.publiees
|
2020-01-15 14:57:40 +01:00
|
|
|
.map { |procedure| procedure.procedure_overview(start_date, groupe_instructeurs) }
|
2019-09-12 11:26:22 +02:00
|
|
|
.filter(&:had_some_activities?)
|
2017-05-12 16:47:18 +02:00
|
|
|
|
2020-02-11 15:51:15 +01:00
|
|
|
if active_procedure_overviews.empty?
|
2017-05-12 16:47:18 +02:00
|
|
|
nil
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start_date: start_date,
|
2018-10-01 14:06:08 +02:00
|
|
|
procedure_overviews: active_procedure_overviews
|
2017-05-12 16:47:18 +02:00
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-23 11:39:54 +02:00
|
|
|
def procedure_presentation_and_errors_for_procedure_id(procedure_id)
|
2022-03-31 16:32:45 +02:00
|
|
|
assign_to
|
|
|
|
.joins(:groupe_instructeur)
|
|
|
|
.includes(:instructeur, :procedure)
|
|
|
|
.find_by(groupe_instructeurs: { procedure_id: procedure_id })
|
|
|
|
.procedure_presentation_or_default_and_errors
|
2017-10-02 17:03:38 +02:00
|
|
|
end
|
|
|
|
|
2017-10-05 16:10:00 +02:00
|
|
|
def notifications_for_dossier(dossier)
|
2023-11-28 17:28:03 +01:00
|
|
|
follow = Follow.find_by(instructeur: self, dossier:)
|
2017-10-05 16:10:00 +02:00
|
|
|
|
|
|
|
if follow.present?
|
2023-11-28 17:28:03 +01:00
|
|
|
demande = dossier.last_champ_updated_at&.>(follow.demande_seen_at) ||
|
|
|
|
dossier.groupe_instructeur_updated_at&.>(follow.demande_seen_at) ||
|
2021-10-04 10:07:04 +02:00
|
|
|
dossier.identity_updated_at&.>(follow.demande_seen_at) ||
|
|
|
|
false
|
2017-10-05 16:10:00 +02:00
|
|
|
|
2023-11-28 17:28:03 +01:00
|
|
|
annotations_privees = dossier.last_champ_private_updated_at&.>(follow.annotations_privees_seen_at) || false
|
|
|
|
avis_notif = dossier.last_avis_updated_at&.>(follow.avis_seen_at) || false
|
|
|
|
messagerie = dossier.last_commentaire_updated_at&.>(follow.messagerie_seen_at) || false
|
2017-10-05 16:10:00 +02:00
|
|
|
|
|
|
|
annotations_hash(demande, annotations_privees, avis_notif, messagerie)
|
|
|
|
else
|
|
|
|
annotations_hash(false, false, false, false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-07 18:03:03 +02:00
|
|
|
def notifications_for_groupe_instructeurs(groupe_instructeurs)
|
2024-01-11 18:35:19 +01:00
|
|
|
notifications_for(groupe_instructeur: groupe_instructeurs)
|
2021-04-07 18:03:03 +02:00
|
|
|
.pluck(:state, :id)
|
|
|
|
.reduce({ termines: [], en_cours: [] }) do |acc, e|
|
|
|
|
if Dossier::TERMINE.include?(e[0])
|
|
|
|
acc[:termines] << e[1]
|
|
|
|
elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(e[0])
|
|
|
|
acc[:en_cours] << e[1]
|
|
|
|
end
|
|
|
|
acc
|
|
|
|
end
|
2017-10-05 16:10:00 +02:00
|
|
|
end
|
|
|
|
|
2024-01-11 18:35:19 +01:00
|
|
|
def notifications_for_dossiers(dossier_ids)
|
|
|
|
notifications_for(id: dossier_ids)
|
|
|
|
.pluck(:id)
|
|
|
|
end
|
|
|
|
|
2020-09-10 18:16:58 +02:00
|
|
|
def procedure_ids_with_notifications(scope)
|
|
|
|
groupe_instructeur_ids = Dossier
|
2019-09-20 12:42:10 +02:00
|
|
|
.send(scope) # :en_cours or :termine (or any other Dossier scope)
|
2019-09-23 14:38:12 +02:00
|
|
|
.merge(followed_dossiers)
|
2022-03-30 11:48:19 +02:00
|
|
|
.visible_by_administration
|
2020-09-18 15:40:26 +02:00
|
|
|
.with_notifications
|
2020-09-10 18:16:58 +02:00
|
|
|
.select(:groupe_instructeur_id)
|
2017-10-05 16:10:00 +02:00
|
|
|
|
2020-09-10 18:16:58 +02:00
|
|
|
GroupeInstructeur.where(id: groupe_instructeur_ids).pluck(:procedure_id)
|
2017-10-05 16:10:00 +02:00
|
|
|
end
|
|
|
|
|
2018-10-23 18:46:45 +02:00
|
|
|
def mark_tab_as_seen(dossier, tab)
|
|
|
|
attributes = {}
|
2018-10-25 15:07:15 +02:00
|
|
|
attributes["#{tab}_seen_at"] = Time.zone.now
|
2019-08-06 11:02:54 +02:00
|
|
|
Follow.where(instructeur: self, dossier: dossier).update_all(attributes)
|
2018-10-23 18:46:45 +02:00
|
|
|
end
|
|
|
|
|
2019-03-13 17:59:33 +01:00
|
|
|
def email_notification_data
|
2019-08-22 17:58:31 +02:00
|
|
|
groupe_instructeur_with_email_notifications
|
|
|
|
.reduce([]) do |acc, groupe|
|
|
|
|
procedure = groupe.procedure
|
2019-03-13 17:59:33 +01:00
|
|
|
|
2021-04-07 18:03:03 +02:00
|
|
|
notifications = notifications_for_groupe_instructeurs([groupe.id])
|
|
|
|
nb_notification = notifications[:en_cours].count + notifications[:termines].count
|
|
|
|
|
2019-03-13 17:59:33 +01:00
|
|
|
h = {
|
2022-03-11 13:57:53 +01:00
|
|
|
nb_en_construction: groupe.dossiers.visible_by_administration.en_construction.count,
|
|
|
|
nb_en_instruction: groupe.dossiers.visible_by_administration.en_instruction.count,
|
2023-04-19 11:01:27 +02:00
|
|
|
nb_accepted: Traitement.where(dossier: groupe.dossiers.accepte, processed_at: Time.zone.yesterday.all_day).count,
|
2021-04-07 18:03:03 +02:00
|
|
|
nb_notification: nb_notification
|
2019-03-13 17:59:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if h[:nb_en_construction] > 0 || h[:nb_notification] > 0
|
|
|
|
h[:procedure_id] = procedure.id
|
|
|
|
h[:procedure_libelle] = procedure.libelle
|
|
|
|
acc << h
|
|
|
|
end
|
|
|
|
|
2020-03-20 10:53:42 +01:00
|
|
|
if h[:nb_en_instruction] > 0 || h[:nb_accepted] > 0
|
|
|
|
[["en_instruction", h[:nb_en_instruction]], ["accepte", h[:nb_accepted]]].each do |state, count|
|
|
|
|
if procedure&.declarative_with_state == state && count > 0
|
|
|
|
h[:procedure_id] = procedure.id
|
|
|
|
h[:procedure_libelle] = procedure.libelle
|
|
|
|
acc << h
|
|
|
|
end
|
2020-03-13 10:24:25 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-13 17:59:33 +01:00
|
|
|
acc
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-20 10:41:47 +02:00
|
|
|
def create_trusted_device_token
|
|
|
|
trusted_device_token = trusted_device_tokens.create
|
|
|
|
trusted_device_token.token
|
|
|
|
end
|
|
|
|
|
|
|
|
def young_login_token?
|
|
|
|
trusted_device_token = trusted_device_tokens.order(created_at: :desc).first
|
|
|
|
trusted_device_token&.token_young?
|
|
|
|
end
|
|
|
|
|
2024-09-11 18:42:29 +02:00
|
|
|
def should_receive_email_activation?
|
|
|
|
# if was recently created or received an activation email more than 7 days ago
|
|
|
|
previously_new_record? || user.reset_password_sent_at.nil? || user.reset_password_sent_at < Devise.reset_password_within.ago
|
|
|
|
end
|
|
|
|
|
2020-01-30 10:48:28 +01:00
|
|
|
def can_be_deleted?
|
|
|
|
user.administrateur.nil? && procedures.all? { |p| p.defaut_groupe_instructeur.instructeurs.count > 1 }
|
|
|
|
end
|
|
|
|
|
2020-03-26 16:17:07 +01:00
|
|
|
# required to display feature flags field in manager
|
|
|
|
def features
|
|
|
|
end
|
|
|
|
|
2020-09-02 17:44:51 +02:00
|
|
|
def flipper_id
|
|
|
|
"Instructeur:#{id}"
|
|
|
|
end
|
|
|
|
|
2021-04-09 09:32:10 +02:00
|
|
|
def dossiers_count_summary(groupe_instructeur_ids)
|
|
|
|
query = <<~EOF
|
|
|
|
SELECT
|
2024-07-17 16:03:21 +02:00
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.id IS NULL) AS a_suivre,
|
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
|
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
|
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND not archived) AS tous,
|
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND archived) AS archives,
|
2024-07-18 14:15:29 +02:00
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NOT NULL AND not archived OR dossiers.hidden_by_expired_at IS NOT NULL) AS supprimes,
|
2024-07-17 16:03:21 +02:00
|
|
|
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_by_expired_at IS NULL AND procedures.procedure_expires_when_termine_enabled
|
2021-12-01 14:39:03 +01:00
|
|
|
AND (
|
|
|
|
dossiers.state in ('accepte', 'refuse', 'sans_suite')
|
|
|
|
AND dossiers.processed_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
|
|
|
|
) OR (
|
2024-07-17 16:03:21 +02:00
|
|
|
dossiers.state in ('en_construction') AND dossiers.hidden_by_expired_at IS NULL
|
2021-12-01 14:39:03 +01:00
|
|
|
AND dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
|
|
|
|
)
|
|
|
|
) AS expirant
|
2022-03-09 10:25:48 +01:00
|
|
|
FROM dossiers
|
|
|
|
INNER JOIN procedure_revisions
|
|
|
|
ON procedure_revisions.id = dossiers.revision_id
|
|
|
|
INNER JOIN procedures
|
|
|
|
ON procedures.id = procedure_revisions.procedure_id
|
2021-04-09 09:32:10 +02:00
|
|
|
LEFT OUTER JOIN follows
|
|
|
|
ON follows.dossier_id = dossiers.id
|
|
|
|
AND follows.unfollowed_at IS NULL
|
2022-03-09 10:25:48 +01:00
|
|
|
WHERE dossiers.state != 'brouillon'
|
|
|
|
AND dossiers.groupe_instructeur_id in (:groupe_instructeur_ids)
|
2024-07-17 16:03:21 +02:00
|
|
|
AND (dossiers.hidden_by_user_at IS NULL OR dossiers.state != 'en_construction')
|
2021-04-09 09:32:10 +02:00
|
|
|
EOF
|
|
|
|
|
|
|
|
sanitized_query = ActiveRecord::Base.sanitize_sql([
|
|
|
|
query,
|
|
|
|
instructeur_id: id,
|
2021-12-01 14:39:03 +01:00
|
|
|
groupe_instructeur_ids: groupe_instructeur_ids,
|
|
|
|
now: Time.zone.now,
|
|
|
|
expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION
|
2021-04-09 09:32:10 +02:00
|
|
|
])
|
|
|
|
|
|
|
|
Dossier.connection.select_all(sanitized_query).first
|
|
|
|
end
|
|
|
|
|
2021-10-04 15:26:01 +02:00
|
|
|
def merge(old_instructeur)
|
2021-10-12 11:04:06 +02:00
|
|
|
return if old_instructeur.nil?
|
|
|
|
|
2021-10-07 14:34:01 +02:00
|
|
|
old_instructeur
|
|
|
|
.assign_to
|
|
|
|
.where.not(groupe_instructeur_id: assign_to.pluck(:groupe_instructeur_id))
|
|
|
|
.update_all(instructeur_id: id)
|
|
|
|
|
|
|
|
old_instructeur
|
|
|
|
.follows
|
|
|
|
.where.not(dossier_id: follows.pluck(:dossier_id))
|
|
|
|
.update_all(instructeur_id: id)
|
|
|
|
|
|
|
|
admin_with_new_instructeur, admin_without_new_instructeur = old_instructeur
|
|
|
|
.administrateurs
|
|
|
|
.partition { |admin| admin.instructeurs.exists?(id) }
|
|
|
|
|
|
|
|
admin_without_new_instructeur.each do |admin|
|
|
|
|
admin.instructeurs << self
|
|
|
|
admin.instructeurs.delete(old_instructeur)
|
|
|
|
end
|
|
|
|
|
|
|
|
admin_with_new_instructeur.each do |admin|
|
|
|
|
admin.instructeurs.delete(old_instructeur)
|
|
|
|
end
|
2021-10-04 15:26:01 +02:00
|
|
|
old_instructeur.commentaires.update_all(instructeur_id: id)
|
|
|
|
old_instructeur.bulk_messages.update_all(instructeur_id: id)
|
2022-05-17 15:21:35 +02:00
|
|
|
|
|
|
|
Avis
|
|
|
|
.where(claimant_id: old_instructeur.id, claimant_type: Instructeur.name)
|
|
|
|
.update_all(claimant_id: id)
|
2021-10-04 15:26:01 +02:00
|
|
|
end
|
|
|
|
|
2024-03-19 15:24:53 +01:00
|
|
|
def last_agent_connect_information
|
|
|
|
agent_connect_information.order(updated_at: :desc).first
|
|
|
|
end
|
|
|
|
|
2024-03-13 13:51:22 +01:00
|
|
|
def export_templates_for(procedure)
|
|
|
|
procedure.export_templates.where(groupe_instructeur: groupe_instructeurs).order(:name)
|
|
|
|
end
|
|
|
|
|
2018-10-23 18:46:45 +02:00
|
|
|
private
|
|
|
|
|
|
|
|
def annotations_hash(demande, annotations_privees, avis, messagerie)
|
|
|
|
{
|
|
|
|
demande: demande,
|
|
|
|
annotations_privees: annotations_privees,
|
|
|
|
avis: avis,
|
|
|
|
messagerie: messagerie
|
|
|
|
}
|
|
|
|
end
|
2024-01-11 18:35:19 +01:00
|
|
|
|
|
|
|
def notifications_for(condition)
|
|
|
|
Dossier
|
|
|
|
.visible_by_administration
|
|
|
|
.not_archived
|
|
|
|
.where(condition)
|
|
|
|
.merge(followed_dossiers)
|
|
|
|
.with_notifications
|
|
|
|
end
|
2015-09-22 10:15:12 +02:00
|
|
|
end
|