commit
75df51a783
38 changed files with 704 additions and 67 deletions
1
Gemfile
1
Gemfile
|
@ -47,6 +47,7 @@ gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
|||
gem 'openid_connect'
|
||||
gem 'openstack'
|
||||
gem 'pg'
|
||||
gem 'phonelib'
|
||||
gem 'prawn' # PDF Generation
|
||||
gem 'prawn_rails'
|
||||
gem 'premailer-rails'
|
||||
|
|
|
@ -241,7 +241,7 @@ GEM
|
|||
graphiql-rails (1.7.0)
|
||||
railties
|
||||
sprockets-rails
|
||||
graphql (1.9.10)
|
||||
graphql (1.9.15)
|
||||
graphql-batch (0.4.1)
|
||||
graphql (>= 1.3, < 2)
|
||||
promise.rb (~> 0.7.2)
|
||||
|
@ -420,6 +420,7 @@ GEM
|
|||
ast (~> 2.4.0)
|
||||
pdf-core (0.7.0)
|
||||
pg (1.1.3)
|
||||
phonelib (0.6.39)
|
||||
powerpack (0.1.2)
|
||||
prawn (2.2.2)
|
||||
pdf-core (~> 0.7.0)
|
||||
|
@ -767,6 +768,7 @@ DEPENDENCIES
|
|||
openid_connect
|
||||
openstack
|
||||
pg
|
||||
phonelib
|
||||
prawn
|
||||
prawn_rails
|
||||
premailer-rails
|
||||
|
|
|
@ -77,6 +77,7 @@ En local, un utilisateur de test est créé automatiquement, avec les identifian
|
|||
InstructeurEmailNotificationJob.set(cron: "0 10 * * 1,2,3,4,5,6").perform_later
|
||||
PurgeUnattachedBlobsJob.set(cron: "0 0 * * *").perform_later
|
||||
OperationsSignatureJob.set(cron: "0 6 * * *").perform_later
|
||||
SeekAndDestroyExpiredDossiersJob.set(cron: "0 7 * * *").perform_later
|
||||
|
||||
### Voir les emails envoyés en local
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ module NewAdministrateur
|
|||
end
|
||||
|
||||
def procedure_params
|
||||
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :euro_flag, :logo, :auto_archive_on, :monavis_embed]
|
||||
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :declarative_with_state, :euro_flag, :logo, :auto_archive_on, :monavis_embed]
|
||||
permited_params = if @procedure&.locked?
|
||||
params.require(:procedure).permit(*editable_params)
|
||||
else
|
||||
|
|
|
@ -26,6 +26,10 @@ class Api::V2::Schema < GraphQL::Schema
|
|||
Types::MessageType
|
||||
when Instructeur, User
|
||||
Types::ProfileType
|
||||
when Individual
|
||||
Types::PersonnePhysiqueType
|
||||
when Etablissement
|
||||
Types::PersonneMoraleType
|
||||
else
|
||||
raise GraphQL::ExecutionError.new("Unexpected object: #{obj}")
|
||||
end
|
||||
|
@ -33,6 +37,7 @@ class Api::V2::Schema < GraphQL::Schema
|
|||
|
||||
orphan_types Types::Champs::CarteChampType,
|
||||
Types::Champs::CheckboxChampType,
|
||||
Types::Champs::CiviliteChampType,
|
||||
Types::Champs::DateChampType,
|
||||
Types::Champs::DecimalNumberChampType,
|
||||
Types::Champs::DossierLinkChampType,
|
||||
|
@ -45,7 +50,9 @@ class Api::V2::Schema < GraphQL::Schema
|
|||
Types::Champs::TextChampType,
|
||||
Types::GeoAreas::ParcelleCadastraleType,
|
||||
Types::GeoAreas::QuartierPrioritaireType,
|
||||
Types::GeoAreas::SelectionUtilisateurType
|
||||
Types::GeoAreas::SelectionUtilisateurType,
|
||||
Types::PersonneMoraleType,
|
||||
Types::PersonnePhysiqueType
|
||||
|
||||
def self.unauthorized_object(error)
|
||||
# Add a top-level error to the response instead of returning nil:
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
type Association {
|
||||
dateCreation: ISO8601Date!
|
||||
dateDeclaration: ISO8601Date!
|
||||
datePublication: ISO8601Date!
|
||||
objet: String!
|
||||
rna: String!
|
||||
titre: String!
|
||||
}
|
||||
|
||||
type Avis {
|
||||
attachmentUrl: URL
|
||||
dateQuestion: ISO8601DateTime!
|
||||
|
@ -76,6 +85,33 @@ type CheckboxChamp implements Champ {
|
|||
value: Boolean!
|
||||
}
|
||||
|
||||
enum Civilite {
|
||||
"""
|
||||
Monsieur
|
||||
"""
|
||||
M
|
||||
|
||||
"""
|
||||
Madame
|
||||
"""
|
||||
Mme
|
||||
}
|
||||
|
||||
type CiviliteChamp implements Champ {
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
Libellé du champ.
|
||||
"""
|
||||
label: String!
|
||||
|
||||
"""
|
||||
La valeur du champ sous forme texte.
|
||||
"""
|
||||
stringValue: String
|
||||
value: Civilite
|
||||
}
|
||||
|
||||
"""
|
||||
GeoJSON coordinates
|
||||
"""
|
||||
|
@ -157,6 +193,10 @@ type DecimalNumberChamp implements Champ {
|
|||
value: Float
|
||||
}
|
||||
|
||||
interface Demandeur {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
"""
|
||||
Une demarche
|
||||
"""
|
||||
|
@ -184,6 +224,11 @@ type Demarche {
|
|||
"""
|
||||
datePublication: ISO8601DateTime!
|
||||
|
||||
"""
|
||||
L'état de dossier pour une démarche déclarative
|
||||
"""
|
||||
declarative: DossierDeclarativeState
|
||||
|
||||
"""
|
||||
Description de la démarche.
|
||||
"""
|
||||
|
@ -240,6 +285,7 @@ type Demarche {
|
|||
Le numero de la démarche.
|
||||
"""
|
||||
number: Int!
|
||||
service: Service!
|
||||
|
||||
"""
|
||||
L'état de la démarche.
|
||||
|
@ -322,6 +368,7 @@ type Dossier {
|
|||
Date de traitement.
|
||||
"""
|
||||
dateTraitement: ISO8601DateTime
|
||||
demandeur: Demandeur!
|
||||
id: ID!
|
||||
instructeurs: [Profile!]!
|
||||
messages: [Message!]!
|
||||
|
@ -428,6 +475,18 @@ type DossierConnection {
|
|||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
enum DossierDeclarativeState {
|
||||
"""
|
||||
Accepté
|
||||
"""
|
||||
accepte
|
||||
|
||||
"""
|
||||
En instruction
|
||||
"""
|
||||
en_instruction
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
|
@ -578,6 +637,22 @@ enum DossierState {
|
|||
sans_suite
|
||||
}
|
||||
|
||||
type Entreprise {
|
||||
capitalSocial: Int!
|
||||
codeEffectifEntreprise: String!
|
||||
dateCreation: ISO8601Date!
|
||||
formeJuridique: String!
|
||||
formeJuridiqueCode: String!
|
||||
inlineAdresse: String!
|
||||
nom: String!
|
||||
nomCommercial: String!
|
||||
numeroTvaIntracommunautaire: String!
|
||||
prenom: String!
|
||||
raisonSociale: String!
|
||||
siren: String!
|
||||
siretSiegeSocial: String!
|
||||
}
|
||||
|
||||
interface GeoArea {
|
||||
geometry: GeoJSON!
|
||||
id: ID!
|
||||
|
@ -615,6 +690,11 @@ type GroupeInstructeur {
|
|||
label: String!
|
||||
}
|
||||
|
||||
"""
|
||||
An ISO 8601-encoded date
|
||||
"""
|
||||
scalar ISO8601Date
|
||||
|
||||
"""
|
||||
An ISO 8601-encoded datetime
|
||||
"""
|
||||
|
@ -758,21 +838,32 @@ type ParcelleCadastrale implements GeoArea {
|
|||
surfaceParcelle: Float!
|
||||
}
|
||||
|
||||
type PersonneMorale {
|
||||
type PersonneMorale implements Demandeur {
|
||||
adresse: String!
|
||||
association: Association
|
||||
codeInseeLocalite: String!
|
||||
codePostal: String!
|
||||
complementAdresse: String!
|
||||
entreprise: Entreprise
|
||||
id: ID!
|
||||
libelleNaf: String!
|
||||
localite: String!
|
||||
naf: String!
|
||||
nomVoie: String!
|
||||
numeroVoie: String!
|
||||
siegeSocial: String!
|
||||
siegeSocial: Boolean!
|
||||
siret: String!
|
||||
typeVoie: String!
|
||||
}
|
||||
|
||||
type PersonnePhysique implements Demandeur {
|
||||
civilite: Civilite
|
||||
dateDeNaissance: ISO8601Date
|
||||
id: ID!
|
||||
nom: String!
|
||||
prenom: String!
|
||||
}
|
||||
|
||||
type PieceJustificativeChamp implements Champ {
|
||||
id: ID!
|
||||
|
||||
|
@ -845,6 +936,13 @@ type SelectionUtilisateur implements GeoArea {
|
|||
source: GeoAreaSource!
|
||||
}
|
||||
|
||||
type Service {
|
||||
id: ID!
|
||||
nom: String!
|
||||
organisme: String!
|
||||
typeOrganisme: TypeOrganisme!
|
||||
}
|
||||
|
||||
type SiretChamp implements Champ {
|
||||
etablissement: PersonneMorale
|
||||
id: ID!
|
||||
|
@ -1012,6 +1110,43 @@ enum TypeDeChamp {
|
|||
yes_no
|
||||
}
|
||||
|
||||
enum TypeOrganisme {
|
||||
"""
|
||||
Administration centrale
|
||||
"""
|
||||
administration_centrale
|
||||
|
||||
"""
|
||||
Association
|
||||
"""
|
||||
association
|
||||
|
||||
"""
|
||||
Autre
|
||||
"""
|
||||
autre
|
||||
|
||||
"""
|
||||
Collectivité territoriale
|
||||
"""
|
||||
collectivite_territoriale
|
||||
|
||||
"""
|
||||
Établissement d’enseignement
|
||||
"""
|
||||
etablissement_enseignement
|
||||
|
||||
"""
|
||||
Opérateur d'État
|
||||
"""
|
||||
operateur_d_etat
|
||||
|
||||
"""
|
||||
Service déconcentré de l'État
|
||||
"""
|
||||
service_deconcentre_de_l_etat
|
||||
}
|
||||
|
||||
"""
|
||||
A valid URL, transported as a string
|
||||
"""
|
||||
|
|
|
@ -31,6 +31,8 @@ module Types
|
|||
Types::Champs::MultipleDropDownListChampType
|
||||
when ::Champs::LinkedDropDownListChamp
|
||||
Types::Champs::LinkedDropDownListChampType
|
||||
when ::Champs::CiviliteChamp
|
||||
Types::Champs::CiviliteChampType
|
||||
else
|
||||
Types::Champs::TextChampType
|
||||
end
|
||||
|
|
7
app/graphql/types/champs/civilite_champ_type.rb
Normal file
7
app/graphql/types/champs/civilite_champ_type.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Types::Champs
|
||||
class CiviliteChampType < Types::BaseObject
|
||||
implements Types::ChampType
|
||||
|
||||
field :value, Types::Civilite, null: true
|
||||
end
|
||||
end
|
6
app/graphql/types/civilite.rb
Normal file
6
app/graphql/types/civilite.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
module Types
|
||||
class Civilite < Types::BaseEnum
|
||||
value("M", "Monsieur", value: Individual::GENDER_MALE)
|
||||
value("Mme", "Madame", value: Individual::GENDER_FEMALE)
|
||||
end
|
||||
end
|
18
app/graphql/types/demandeur_type.rb
Normal file
18
app/graphql/types/demandeur_type.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Types
|
||||
module DemandeurType
|
||||
include Types::BaseInterface
|
||||
|
||||
global_id_field :id
|
||||
|
||||
definition_methods do
|
||||
def resolve_type(object, context)
|
||||
case object
|
||||
when Individual
|
||||
Types::PersonnePhysiqueType
|
||||
when Etablissement
|
||||
Types::PersonneMoraleType
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,6 +6,14 @@ module Types
|
|||
end
|
||||
end
|
||||
|
||||
class DossierDeclarativeState < Types::BaseEnum
|
||||
Procedure.declarative_with_states.each do |symbol_name, string_name|
|
||||
value(string_name,
|
||||
I18n.t("declarative_with_state/#{string_name}", scope: [:activerecord, :attributes, :procedure]),
|
||||
value: symbol_name)
|
||||
end
|
||||
end
|
||||
|
||||
description "Une demarche"
|
||||
|
||||
global_id_field :id
|
||||
|
@ -13,6 +21,7 @@ module Types
|
|||
field :title, String, "Le titre de la démarche.", null: false, method: :libelle
|
||||
field :description, String, "Description de la démarche.", null: false
|
||||
field :state, DemarcheState, "L'état de la démarche.", null: false
|
||||
field :declarative, DossierDeclarativeState, "L'état de dossier pour une démarche déclarative", null: true, method: :declarative_with_state
|
||||
|
||||
field :date_creation, GraphQL::Types::ISO8601DateTime, "Date de la création.", null: false, method: :created_at
|
||||
field :date_publication, GraphQL::Types::ISO8601DateTime, "Date de la publication.", null: false, method: :published_at
|
||||
|
@ -20,6 +29,7 @@ module Types
|
|||
field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true, method: :closed_at
|
||||
|
||||
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
|
||||
field :service, Types::ServiceType, null: false
|
||||
|
||||
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d'une démarche.", null: false do
|
||||
argument :order, Types::Order, default_value: :asc, required: false, description: "L'ordre des dossiers."
|
||||
|
@ -39,6 +49,10 @@ module Types
|
|||
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
|
||||
end
|
||||
|
||||
def service
|
||||
Loaders::Record.for(Service).load(object.service_id)
|
||||
end
|
||||
|
||||
def dossiers(updated_since: nil, created_since: nil, state: nil, order:)
|
||||
dossiers = object.dossiers.state_not_brouillon.for_api_v2
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ module Types
|
|||
{ Extensions::Attachment => { attachment: :justificatif_motivation } }
|
||||
]
|
||||
|
||||
field :demandeur, Types::DemandeurType, null: false
|
||||
|
||||
field :usager, Types::ProfileType, null: false
|
||||
field :instructeurs, [Types::ProfileType], null: false
|
||||
|
||||
|
@ -61,6 +63,14 @@ module Types
|
|||
Loaders::Association.for(object.class, :champs_private).load(object)
|
||||
end
|
||||
|
||||
def demandeur
|
||||
if object.procedure.for_individual
|
||||
Loaders::Association.for(object.class, :individual).load(object)
|
||||
else
|
||||
Loaders::Association.for(object.class, :etablissement).load(object)
|
||||
end
|
||||
end
|
||||
|
||||
def self.authorized?(object, context)
|
||||
authorized_demarche?(object.procedure, context)
|
||||
end
|
||||
|
|
|
@ -1,7 +1,34 @@
|
|||
module Types
|
||||
class PersonneMoraleType < Types::BaseObject
|
||||
class EntrepriseType < Types::BaseObject
|
||||
field :siren, String, null: false
|
||||
field :capital_social, Int, null: false
|
||||
field :numero_tva_intracommunautaire, String, null: false
|
||||
field :forme_juridique, String, null: false
|
||||
field :forme_juridique_code, String, null: false
|
||||
field :nom_commercial, String, null: false
|
||||
field :raison_sociale, String, null: false
|
||||
field :siret_siege_social, String, null: false
|
||||
field :code_effectif_entreprise, String, null: false
|
||||
field :date_creation, GraphQL::Types::ISO8601Date, null: false
|
||||
field :nom, String, null: false
|
||||
field :prenom, String, null: false
|
||||
field :inline_adresse, String, null: false
|
||||
end
|
||||
|
||||
class AssociationType < Types::BaseObject
|
||||
field :rna, String, null: false
|
||||
field :titre, String, null: false
|
||||
field :objet, String, null: false
|
||||
field :date_creation, GraphQL::Types::ISO8601Date, null: false
|
||||
field :date_declaration, GraphQL::Types::ISO8601Date, null: false
|
||||
field :date_publication, GraphQL::Types::ISO8601Date, null: false
|
||||
end
|
||||
|
||||
implements Types::DemandeurType
|
||||
|
||||
field :siret, String, null: false
|
||||
field :siege_social, String, null: false
|
||||
field :siege_social, Boolean, null: false
|
||||
field :naf, String, null: false
|
||||
field :libelle_naf, String, null: false
|
||||
field :adresse, String, null: false
|
||||
|
@ -12,5 +39,26 @@ module Types
|
|||
field :code_postal, String, null: false
|
||||
field :localite, String, null: false
|
||||
field :code_insee_localite, String, null: false
|
||||
field :entreprise, EntrepriseType, null: true
|
||||
field :association, AssociationType, null: true
|
||||
|
||||
def entreprise
|
||||
if object.entreprise_siren.present?
|
||||
object.entreprise
|
||||
end
|
||||
end
|
||||
|
||||
def association
|
||||
if object.association?
|
||||
{
|
||||
rna: object.association_rna,
|
||||
titre: object.association_titre,
|
||||
objet: object.association_objet,
|
||||
date_creation: object.association_date_creation,
|
||||
date_declaration: object.association_date_declaration,
|
||||
date_publication: object.association_date_publication
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
10
app/graphql/types/personne_physique_type.rb
Normal file
10
app/graphql/types/personne_physique_type.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
module Types
|
||||
class PersonnePhysiqueType < Types::BaseObject
|
||||
implements Types::DemandeurType
|
||||
|
||||
field :nom, String, null: false
|
||||
field :prenom, String, null: false
|
||||
field :civilite, Types::Civilite, null: true, method: :gender
|
||||
field :date_de_naissance, GraphQL::Types::ISO8601Date, null: true, method: :birthdate
|
||||
end
|
||||
end
|
15
app/graphql/types/service_type.rb
Normal file
15
app/graphql/types/service_type.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Types
|
||||
class ServiceType < Types::BaseObject
|
||||
class TypeOrganisme < Types::BaseEnum
|
||||
Service.type_organismes.each do |symbol_name, string_name|
|
||||
value(string_name, I18n.t(symbol_name, scope: [:type_organisme]), value: symbol_name)
|
||||
end
|
||||
end
|
||||
|
||||
global_id_field :id
|
||||
|
||||
field :nom, String, null: false
|
||||
field :type_organisme, TypeOrganisme, null: false
|
||||
field :organisme, String, null: false
|
||||
end
|
||||
end
|
8
app/jobs/seek_and_destroy_expired_dossiers_job.rb
Normal file
8
app/jobs/seek_and_destroy_expired_dossiers_job.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class SeekAndDestroyExpiredDossiersJob < ApplicationJob
|
||||
queue_as :cron
|
||||
|
||||
def perform(*args)
|
||||
Dossier.send_brouillon_expiration_notices
|
||||
Dossier.destroy_brouillons_and_notify
|
||||
end
|
||||
end
|
|
@ -69,4 +69,18 @@ class DossierMailer < ApplicationMailer
|
|||
format.html { render layout: 'mailers/notifications_layout' }
|
||||
end
|
||||
end
|
||||
|
||||
def notify_brouillon_near_deletion(user, dossiers)
|
||||
@subject = default_i18n_subject(count: dossiers.count)
|
||||
@dossiers = dossiers
|
||||
|
||||
mail(to: user.email, subject: @subject)
|
||||
end
|
||||
|
||||
def notify_brouillon_deletion(user, dossier_hashes)
|
||||
@subject = default_i18n_subject(count: dossier_hashes.count)
|
||||
@dossier_hashes = dossier_hashes
|
||||
|
||||
mail(to: user.email, subject: @subject)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,8 @@ class Dossier < ApplicationRecord
|
|||
|
||||
TAILLE_MAX_ZIP = 50.megabytes
|
||||
|
||||
DRAFT_EXPIRATION = 1.month + 5.days
|
||||
|
||||
has_one :etablissement, dependent: :destroy
|
||||
has_one :individual, dependent: :destroy
|
||||
has_one :attestation, dependent: :destroy
|
||||
|
@ -162,6 +164,14 @@ class Dossier < ApplicationRecord
|
|||
user: [])
|
||||
}
|
||||
|
||||
scope :brouillon_close_to_expiration, -> do
|
||||
brouillon
|
||||
.joins(:procedure)
|
||||
.where("dossiers.created_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - (1 * interval '1 month') <= now()")
|
||||
end
|
||||
scope :expired_brouillon, -> { brouillon.where("brouillon_close_to_expiration_notice_sent_at < ?", (Time.zone.now - (DRAFT_EXPIRATION))) }
|
||||
scope :without_notice_sent, -> { where(brouillon_close_to_expiration_notice_sent_at: nil) }
|
||||
|
||||
scope :for_procedure, -> (procedure) { includes(:user, :groupe_instructeur).where(groupe_instructeurs: { procedure: procedure }) }
|
||||
scope :for_api_v2, -> { includes(procedure: [:administrateurs], etablissement: [], individual: []) }
|
||||
|
||||
|
@ -577,6 +587,10 @@ class Dossier < ApplicationRecord
|
|||
Dossier.where(id: champs.filter(&:dossier_link?).map(&:value).compact)
|
||||
end
|
||||
|
||||
def hash_for_deletion_mail
|
||||
{ id: self.id, procedure_libelle: self.procedure.libelle }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_dossier_operation(author, operation, subject = nil)
|
||||
|
@ -627,4 +641,37 @@ class Dossier < ApplicationRecord
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def self.send_brouillon_expiration_notices
|
||||
brouillons = Dossier
|
||||
.brouillon_close_to_expiration
|
||||
.without_notice_sent
|
||||
|
||||
brouillons
|
||||
.includes(:user)
|
||||
.group_by(&:user)
|
||||
.each do |(user, dossiers)|
|
||||
DossierMailer.notify_brouillon_near_deletion(user, dossiers).deliver_later
|
||||
end
|
||||
|
||||
brouillons.update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now)
|
||||
end
|
||||
|
||||
def self.destroy_brouillons_and_notify
|
||||
expired_brouillons = Dossier.expired_brouillon
|
||||
|
||||
expired_brouillons
|
||||
.includes(:procedure, :user)
|
||||
.group_by(&:user)
|
||||
.each do |(user, dossiers)|
|
||||
|
||||
dossier_hashes = dossiers.map(&:hash_for_deletion_mail)
|
||||
DossierMailer.notify_brouillon_deletion(user, dossier_hashes).deliver_later
|
||||
|
||||
dossiers.each do |dossier|
|
||||
DeletedDossier.create_from_dossier(dossier)
|
||||
dossier.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -305,6 +305,12 @@ class Procedure < ApplicationRecord
|
|||
declarative_with_state == Procedure.declarative_with_states.fetch(:accepte)
|
||||
end
|
||||
|
||||
def self.declarative_attributes_for_select
|
||||
declarative_with_states.map do |state, _|
|
||||
[I18n.t("activerecord.attributes.#{model_name.i18n_key}.declarative_with_state/#{state}"), state]
|
||||
end
|
||||
end
|
||||
|
||||
# Warning: dossier after_save build_default_champs must be removed
|
||||
# to save a dossier created from this method
|
||||
def new_dossier
|
||||
|
|
|
@ -20,7 +20,7 @@ class Service < ApplicationRecord
|
|||
validates :organisme, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||
validates :type_organisme, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||
validates :email, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||
validates :telephone, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||
validates :telephone, phone: { possible: true, allow_blank: true }
|
||||
validates :horaires, presence: { message: 'doivent être renseignés' }, allow_nil: false
|
||||
validates :adresse, presence: { message: 'doit être renseignée' }, allow_nil: false
|
||||
validates :administrateur, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||
|
|
12
app/views/dossier_mailer/notify_brouillon_deletion.html.haml
Normal file
12
app/views/dossier_mailer/notify_brouillon_deletion.html.haml
Normal file
|
@ -0,0 +1,12 @@
|
|||
- content_for(:title, "#{@subject}")
|
||||
|
||||
%p
|
||||
Bonjour,
|
||||
|
||||
%p= t('.automatic_dossier_deletion', count: @dossier_hashes.count)
|
||||
|
||||
%ul
|
||||
- @dossier_hashes.each do |d|
|
||||
%li n° #{d[:id]} (#{d[:procedure_libelle]})
|
||||
|
||||
= render partial: "layouts/mailers/signature"
|
|
@ -0,0 +1,16 @@
|
|||
- content_for(:title, "#{@subject}")
|
||||
|
||||
%p
|
||||
Bonjour,
|
||||
|
||||
%p
|
||||
Afin de limiter la conservation de vos données personnelles,
|
||||
= t('.automatic_dossier_deletion', count: @dossiers.count)
|
||||
%ul
|
||||
- @dossiers.each do |d|
|
||||
%li= link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))
|
||||
|
||||
%p
|
||||
#{sanitize(t('.send_your_draft', count: @dossiers.count))}. Et sinon, vous n'avez rien à faire.
|
||||
|
||||
= render partial: "layouts/mailers/signature"
|
|
@ -113,3 +113,13 @@
|
|||
%p.explication
|
||||
La clôture automatique suspend la publication de la démarche et entraîne le passage de tous les dossiers "en construction"
|
||||
(c'est à dire ceux qui ont été déposés), au statut "en instruction", ce qui ne permet plus aux usagers de les modifier.
|
||||
|
||||
= f.label :declarative_with_state do
|
||||
Démarche déclarative
|
||||
= f.select :declarative_with_state, Procedure.declarative_attributes_for_select, { include_blank: true }, class: 'form-control'
|
||||
|
||||
%p.explication
|
||||
Par défaut, une démarche n'est pas déclarative; à son dépot, un dossier est «en construction». Vous pouvez choisir de la rendre déclarative, afin que tous les dossiers déposés soient immédiatement au statut "en instruction" en "accepté".
|
||||
%br
|
||||
%br
|
||||
Dans le cadre d'une démarche déclarative, au dépot, seul l'email associé à l'état choisi est envoyé. (ex: démarche déclarative «accepté»: envoi uniquement de l'email d'acceptation)
|
||||
|
|
|
@ -18,16 +18,23 @@
|
|||
|
||||
%h2.header-section Informations de contact
|
||||
|
||||
%p.explication Ces informations seront visibles par les utilisateurs du formulaire.
|
||||
%p.explication
|
||||
Votre démarche sera hébergée par demarche-simplifiees.fr – mais nous ne pouvons pas assurer le support des démarches. Et malgré la dématérialisation, les usagers se poseront parfois des questions légitimes sur le processus administratif.
|
||||
%br
|
||||
Il est donc important que les usagers puissent vous contacter s'ils ont des questions sur votre démarche.
|
||||
%br
|
||||
Ces informations seront visibles par les utilisateurs de la démarche, affichées dans le menu "Aide".
|
||||
|
||||
%p.explication Indiquez une adresse mail en capacité de répondre à l'usager.
|
||||
= f.label :email do
|
||||
Adresse email
|
||||
%span.mandatory *
|
||||
= f.email_field :email, placeholder: 'contact@mon-service.fr', required: true
|
||||
|
||||
%p.explication Indiquez le numéro de téléphone du service le plus à même de fournir des réponses pertinentes à vos usagers.
|
||||
= f.label :telephone do
|
||||
Numéro de téléphone
|
||||
%span.mandatory *
|
||||
Numéro de téléphone
|
||||
= f.telephone_field :telephone, placeholder: '04 12 24 42 37', required: true
|
||||
|
||||
= f.label :horaires do
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
.editable-champ{ class: "editable-champ-#{champ.type_champ}" }
|
||||
- if champ.repetition?
|
||||
= render partial: 'shared/dossiers/editable_champs/header_section', locals: { champ: champ }
|
||||
|
||||
- if champ.description.present?
|
||||
%p.notice= string_to_html(champ.description, false)
|
||||
- elsif has_label?(champ)
|
||||
= render partial: 'shared/dossiers/editable_champs/champ_label', locals: { form: form, champ: champ, seen_at: defined?(seen_at) ? seen_at : nil }
|
||||
|
||||
|
|
|
@ -24,13 +24,14 @@
|
|||
Par email :
|
||||
= link_to service.email, "mailto:#{service.email}"
|
||||
|
||||
%li
|
||||
Par téléphone :
|
||||
= link_to service.telephone, service.telephone_url
|
||||
- if service.telephone.present?
|
||||
%li
|
||||
Par téléphone :
|
||||
= link_to service.telephone, service.telephone_url
|
||||
|
||||
%li
|
||||
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
||||
= simple_format(horaires, {}, wrapper_tag: 'span')
|
||||
%li
|
||||
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
||||
= simple_format(horaires, {}, wrapper_tag: 'span')
|
||||
|
||||
|
||||
- politiques = politiques_conservation_de_donnees(procedure)
|
||||
|
|
2
config/initializers/phonelib.rb
Normal file
2
config/initializers/phonelib.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
Phonelib.default_country = "FR"
|
||||
Phonelib.parse_special = true
|
|
@ -14,3 +14,5 @@ fr:
|
|||
aasm_state/publiee: Publiée
|
||||
aasm_state/close: Close
|
||||
aasm_state/hidden: Suprimée
|
||||
declarative_with_state/en_instruction: En instruction
|
||||
declarative_with_state/accepte: Accepté
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
fr:
|
||||
dossier_mailer:
|
||||
notify_brouillon_deletion:
|
||||
subject:
|
||||
one: "Un dossier en brouillon a été supprimé automatiquement"
|
||||
other: "Des dossiers en brouillon ont été supprimés automatiquement"
|
||||
automatic_dossier_deletion:
|
||||
one: "Le délai maximum de conservation du dossier en brouillon suivant a été atteint, il a donc été supprimé :"
|
||||
other: "Le délai maximum de conservation des dossiers en brouillon suivants a été atteint, ils ont donc été supprimés :"
|
|
@ -0,0 +1,12 @@
|
|||
fr:
|
||||
dossier_mailer:
|
||||
notify_brouillon_near_deletion:
|
||||
subject:
|
||||
one: Un dossier en brouillon va bientôt être supprimé
|
||||
other: Des dossiers en brouillon vont bientôt être supprimés
|
||||
automatic_dossier_deletion:
|
||||
one: "le dossier en brouillon suivant sera bientôt automatiquement supprimé :"
|
||||
other: "les dossiers en brouillon suivant seront bientôt automatiquement supprimés :"
|
||||
send_your_draft:
|
||||
one: "Si vous souhaitez toujours déposer ce dossier, vous pouvez retrouver votre brouillon pendant encore <b>un mois</b>"
|
||||
other: "Si vous souhaitez toujours déposer ces dossiers, vous pouvez retrouver vos brouillons pendant encore <b>un mois</b>"
|
|
@ -0,0 +1,5 @@
|
|||
class AddNearDeletionNoticeSendToDossier < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :dossiers, :brouillon_close_to_expiration_notice_sent_at, :datetime
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2019_11_14_113700) do
|
||||
ActiveRecord::Schema.define(version: 2019_11_28_081324) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -252,6 +252,7 @@ ActiveRecord::Schema.define(version: 2019_11_14_113700) do
|
|||
t.text "search_terms"
|
||||
t.text "private_search_terms"
|
||||
t.bigint "groupe_instructeur_id"
|
||||
t.datetime "brouillon_close_to_expiration_notice_sent_at"
|
||||
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
|
||||
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
|
||||
t.index ["archived"], name: "index_dossiers_on_archived"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: enable_seek_and_destroy_job'
|
||||
task enable_seek_and_destroy_job: :environment do
|
||||
SeekAndDestroyExpiredDossiersJob.set(cron: "0 7 * * *").perform_later
|
||||
AfterParty::TaskRecord.create version: '20191203142402'
|
||||
end
|
||||
end
|
|
@ -3,18 +3,19 @@ require 'spec_helper'
|
|||
describe API::V2::GraphqlController do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:token) { admin.renew_api_token }
|
||||
let(:procedure) { create(:procedure, :with_all_champs, administrateurs: [admin]) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, administrateurs: [admin]) }
|
||||
let(:dossier) do
|
||||
dossier = create(:dossier,
|
||||
:en_construction,
|
||||
:with_all_champs,
|
||||
:for_individual,
|
||||
procedure: procedure)
|
||||
create(:commentaire, dossier: dossier, email: 'test@test.com')
|
||||
dossier
|
||||
end
|
||||
let(:dossier1) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 1.day.ago) }
|
||||
let(:dossier2) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 3.days.ago) }
|
||||
let!(:dossier_brouillon) { create(:dossier, procedure: procedure) }
|
||||
let(:dossier1) { create(:dossier, :en_construction, :for_individual, procedure: procedure, en_construction_at: 1.day.ago) }
|
||||
let(:dossier2) { create(:dossier, :en_construction, :for_individual, procedure: procedure, en_construction_at: 3.days.ago) }
|
||||
let!(:dossier_brouillon) { create(:dossier, :for_individual, procedure: procedure) }
|
||||
let(:dossiers) { [dossier2, dossier1, dossier] }
|
||||
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
|
||||
|
||||
|
@ -39,6 +40,11 @@ describe API::V2::GraphqlController do
|
|||
email
|
||||
}
|
||||
}
|
||||
service {
|
||||
nom
|
||||
typeOrganisme
|
||||
organisme
|
||||
}
|
||||
champDescriptors {
|
||||
id
|
||||
type
|
||||
|
@ -79,7 +85,7 @@ describe API::V2::GraphqlController do
|
|||
number: procedure.id,
|
||||
title: procedure.libelle,
|
||||
description: procedure.description,
|
||||
state: 'brouillon',
|
||||
state: 'publiee',
|
||||
dateFermeture: nil,
|
||||
dateCreation: procedure.created_at.iso8601,
|
||||
dateDerniereModification: procedure.updated_at.iso8601,
|
||||
|
@ -89,6 +95,11 @@ describe API::V2::GraphqlController do
|
|||
label: "défaut"
|
||||
}
|
||||
],
|
||||
service: {
|
||||
nom: procedure.service.nom,
|
||||
typeOrganisme: procedure.service.type_organisme,
|
||||
organisme: procedure.service.organisme
|
||||
},
|
||||
champDescriptors: procedure.types_de_champ.map do |tdc|
|
||||
{
|
||||
id: tdc.to_typed_id,
|
||||
|
@ -149,6 +160,15 @@ describe API::V2::GraphqlController do
|
|||
id
|
||||
email
|
||||
}
|
||||
demandeur {
|
||||
id
|
||||
... on PersonnePhysique {
|
||||
nom
|
||||
prenom
|
||||
civilite
|
||||
dateDeNaissance
|
||||
}
|
||||
}
|
||||
instructeurs {
|
||||
id
|
||||
email
|
||||
|
@ -177,45 +197,104 @@ describe API::V2::GraphqlController do
|
|||
}"
|
||||
end
|
||||
|
||||
it "should be returned" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
expect(gql_data).to eq(dossier: {
|
||||
id: dossier.to_typed_id,
|
||||
number: dossier.id,
|
||||
state: 'en_construction',
|
||||
dateDerniereModification: dossier.updated_at.iso8601,
|
||||
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
||||
datePassageEnInstruction: nil,
|
||||
dateTraitement: nil,
|
||||
motivation: nil,
|
||||
motivationAttachmentUrl: nil,
|
||||
usager: {
|
||||
id: dossier.user.to_typed_id,
|
||||
email: dossier.user.email
|
||||
},
|
||||
instructeurs: [
|
||||
{
|
||||
id: instructeur.to_typed_id,
|
||||
email: instructeur.email
|
||||
context "with individual" do
|
||||
it "should be returned" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
expect(gql_data).to eq(dossier: {
|
||||
id: dossier.to_typed_id,
|
||||
number: dossier.id,
|
||||
state: 'en_construction',
|
||||
dateDerniereModification: dossier.updated_at.iso8601,
|
||||
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
||||
datePassageEnInstruction: nil,
|
||||
dateTraitement: nil,
|
||||
motivation: nil,
|
||||
motivationAttachmentUrl: nil,
|
||||
usager: {
|
||||
id: dossier.user.to_typed_id,
|
||||
email: dossier.user.email
|
||||
},
|
||||
instructeurs: [
|
||||
{
|
||||
id: instructeur.to_typed_id,
|
||||
email: instructeur.email
|
||||
}
|
||||
],
|
||||
demandeur: {
|
||||
id: dossier.individual.to_typed_id,
|
||||
nom: dossier.individual.nom,
|
||||
prenom: dossier.individual.prenom,
|
||||
civilite: 'M',
|
||||
dateDeNaissance: '1991-11-01'
|
||||
},
|
||||
messages: dossier.commentaires.map do |commentaire|
|
||||
{
|
||||
body: commentaire.body,
|
||||
attachmentUrl: nil,
|
||||
email: commentaire.email
|
||||
}
|
||||
end,
|
||||
avis: [],
|
||||
champs: dossier.champs.map do |champ|
|
||||
{
|
||||
id: champ.to_typed_id,
|
||||
label: champ.libelle,
|
||||
stringValue: champ.for_api_v2
|
||||
}
|
||||
end
|
||||
})
|
||||
expect(gql_data[:dossier][:champs][0][:id]).to eq(dossier.champs[0].type_de_champ.to_typed_id)
|
||||
end
|
||||
end
|
||||
|
||||
context "with entreprise" do
|
||||
let(:procedure) { create(:procedure, :published, administrateurs: [admin]) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, procedure: procedure) }
|
||||
|
||||
let(:query) do
|
||||
"{
|
||||
dossier(number: #{dossier.id}) {
|
||||
id
|
||||
number
|
||||
usager {
|
||||
id
|
||||
email
|
||||
}
|
||||
demandeur {
|
||||
id
|
||||
... on PersonneMorale {
|
||||
siret
|
||||
siegeSocial
|
||||
entreprise {
|
||||
siren
|
||||
dateCreation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
messages: dossier.commentaires.map do |commentaire|
|
||||
{
|
||||
body: commentaire.body,
|
||||
attachmentUrl: nil,
|
||||
email: commentaire.email
|
||||
}"
|
||||
end
|
||||
|
||||
it "should be returned" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
expect(gql_data).to eq(dossier: {
|
||||
id: dossier.to_typed_id,
|
||||
number: dossier.id,
|
||||
usager: {
|
||||
id: dossier.user.to_typed_id,
|
||||
email: dossier.user.email
|
||||
},
|
||||
demandeur: {
|
||||
id: dossier.etablissement.to_typed_id,
|
||||
siret: dossier.etablissement.siret,
|
||||
siegeSocial: dossier.etablissement.siege_social,
|
||||
entreprise: {
|
||||
siren: dossier.etablissement.entreprise_siren,
|
||||
dateCreation: dossier.etablissement.entreprise_date_creation.iso8601
|
||||
}
|
||||
}
|
||||
end,
|
||||
avis: [],
|
||||
champs: dossier.champs.map do |champ|
|
||||
{
|
||||
id: champ.to_typed_id,
|
||||
label: champ.libelle,
|
||||
stringValue: champ.for_api_v2
|
||||
}
|
||||
end
|
||||
})
|
||||
expect(gql_data[:dossier][:champs][0][:id]).to eq(dossier.champs[0].type_de_champ.to_typed_id)
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -296,7 +375,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
describe 'dossierPasserEnInstruction' do
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :for_individual, procedure: procedure) }
|
||||
let(:query) do
|
||||
"mutation {
|
||||
dossierPasserEnInstruction(input: {
|
||||
|
@ -331,7 +410,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
context 'validation error' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, procedure: procedure) }
|
||||
|
||||
it "should fail" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
|
@ -344,7 +423,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
describe 'dossierClasserSansSuite' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, procedure: procedure) }
|
||||
let(:query) do
|
||||
"mutation {
|
||||
dossierClasserSansSuite(input: {
|
||||
|
@ -380,7 +459,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
context 'validation error' do
|
||||
let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :accepte, :for_individual, procedure: procedure) }
|
||||
|
||||
it "should fail" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
|
@ -393,7 +472,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
describe 'dossierRefuser' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, procedure: procedure) }
|
||||
let(:query) do
|
||||
"mutation {
|
||||
dossierRefuser(input: {
|
||||
|
@ -429,7 +508,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
context 'validation error' do
|
||||
let(:dossier) { create(:dossier, :sans_suite, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :sans_suite, :for_individual, procedure: procedure) }
|
||||
|
||||
it "should fail" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
|
@ -442,7 +521,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
describe 'dossierAccepter' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, procedure: procedure) }
|
||||
let(:query) do
|
||||
"mutation {
|
||||
dossierAccepter(input: {
|
||||
|
@ -511,7 +590,7 @@ describe API::V2::GraphqlController do
|
|||
end
|
||||
|
||||
context 'validation error' do
|
||||
let(:dossier) { create(:dossier, :refuse, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :refuse, :for_individual, procedure: procedure) }
|
||||
|
||||
it "should fail" do
|
||||
expect(gql_errors).to eq(nil)
|
||||
|
|
|
@ -83,4 +83,27 @@ RSpec.describe DossierMailer, type: :mailer do
|
|||
|
||||
it_behaves_like 'a dossier notification'
|
||||
end
|
||||
|
||||
describe '.notify_brouillon_near_deletion' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
|
||||
before do
|
||||
duree = dossier.procedure.duree_conservation_dossiers_dans_ds
|
||||
@date_suppression = dossier.created_at + duree.months
|
||||
end
|
||||
|
||||
subject { described_class.notify_brouillon_near_deletion(dossier.user, [dossier]) }
|
||||
|
||||
it { expect(subject.body).to include("n° #{dossier.id} ") }
|
||||
it { expect(subject.body).to include(dossier.procedure.libelle) }
|
||||
end
|
||||
|
||||
describe '.notify_brouillon_deletion' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
|
||||
subject { described_class.notify_brouillon_deletion(dossier.user, [dossier.hash_for_deletion_mail]) }
|
||||
|
||||
it { expect(subject.subject).to eq("Un dossier en brouillon a été supprimé automatiquement") }
|
||||
it { expect(subject.body).to include("n° #{dossier.id} (#{dossier.procedure.libelle})") }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,23 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
DossierMailer.notify_revert_to_instruction(dossier)
|
||||
end
|
||||
|
||||
def notify_brouillon_near_deletion
|
||||
DossierMailer.notify_brouillon_near_deletion(User.new(email: "usager@example.com"), [dossier])
|
||||
end
|
||||
|
||||
def notify_brouillons_near_deletion
|
||||
DossierMailer.notify_brouillon_near_deletion(User.new(email: "usager@example.com"), [dossier, dossier])
|
||||
end
|
||||
|
||||
def notify_brouillon_deletion
|
||||
DossierMailer.notify_brouillon_deletion(User.new(email: "usager@example.com"), [dossier.hash_for_deletion_mail])
|
||||
end
|
||||
|
||||
def notify_brouillons_deletion
|
||||
dossier_hashes = [dossier, dossier].map(&:hash_for_deletion_mail)
|
||||
DossierMailer.notify_brouillon_deletion(User.new(email: "usager@example.com"), dossier_hashes)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deleted_dossier
|
||||
|
|
|
@ -1047,4 +1047,57 @@ describe Dossier do
|
|||
it { expect(Dossier.for_procedure(procedure_1)).to contain_exactly(dossier_1_1, dossier_1_2) }
|
||||
it { expect(Dossier.for_procedure(procedure_2)).to contain_exactly(dossier_2_1) }
|
||||
end
|
||||
|
||||
describe '#send_brouillon_expiration_notices' do
|
||||
let!(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
|
||||
let!(:date_close_to_expiration) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months + 1.month }
|
||||
let!(:date_expired) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months - 6.days }
|
||||
let!(:date_not_expired) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months + 2.months }
|
||||
|
||||
before { Timecop.freeze(Time.zone.parse('12/12/2012').beginning_of_day) }
|
||||
after { Timecop.return }
|
||||
|
||||
context "Envoi de message pour les dossiers expirant dans - d'un mois" do
|
||||
let!(:expired_brouillon) { create(:dossier, procedure: procedure, created_at: date_expired) }
|
||||
let!(:brouillon_close_to_expiration) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration) }
|
||||
let!(:brouillon_close_but_with_notice_sent) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration, brouillon_close_to_expiration_notice_sent_at: Time.zone.now) }
|
||||
let!(:valid_brouillon) { create(:dossier, procedure: procedure, created_at: date_not_expired) }
|
||||
|
||||
before do
|
||||
allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_return(double(deliver_later: nil))
|
||||
Dossier.send_brouillon_expiration_notices
|
||||
end
|
||||
|
||||
it 'verification de la creation de mail' do
|
||||
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with(brouillon_close_to_expiration.user, [brouillon_close_to_expiration])
|
||||
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with(expired_brouillon.user, [expired_brouillon])
|
||||
end
|
||||
|
||||
it 'Verification du changement d etat du champ' do
|
||||
expect(brouillon_close_to_expiration.reload.brouillon_close_to_expiration_notice_sent_at).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy_brouillons_and_notify' do
|
||||
let!(:today) { Time.zone.now.at_midnight }
|
||||
|
||||
let!(:expired_brouillon) { create(:dossier, brouillon_close_to_expiration_notice_sent_at: today - (Dossier::DRAFT_EXPIRATION + 1.day)) }
|
||||
let!(:other_brouillon) { create(:dossier, brouillon_close_to_expiration_notice_sent_at: today - (Dossier::DRAFT_EXPIRATION - 1.day)) }
|
||||
|
||||
before do
|
||||
allow(DossierMailer).to receive(:notify_brouillon_deletion).and_return(double(deliver_later: nil))
|
||||
Dossier.destroy_brouillons_and_notify
|
||||
end
|
||||
|
||||
it 'notifies deletion' do
|
||||
expect(DossierMailer).to have_received(:notify_brouillon_deletion).once
|
||||
expect(DossierMailer).to have_received(:notify_brouillon_deletion).with(expired_brouillon.user, [expired_brouillon.hash_for_deletion_mail])
|
||||
end
|
||||
|
||||
it 'deletes the expired brouillon' do
|
||||
expect(DeletedDossier.find_by(dossier_id: expired_brouillon.id)).to be_present
|
||||
expect { expired_brouillon.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ describe Service, type: :model do
|
|||
organisme: 'mairie des iles',
|
||||
type_organisme: Service.type_organismes.fetch(:association),
|
||||
email: 'super@email.com',
|
||||
telephone: '1212202',
|
||||
telephone: '012345678',
|
||||
horaires: 'du lundi au vendredi',
|
||||
adresse: '12 rue des schtroumpfs',
|
||||
administrateur_id: administrateur.id
|
||||
|
@ -16,6 +16,33 @@ describe Service, type: :model do
|
|||
|
||||
it { expect(Service.new(params).valid?).to be_truthy }
|
||||
|
||||
it 'should forbid invalid phone numbers' do
|
||||
service = Service.create(params)
|
||||
invalid_phone_numbers = ["1", "Néant", "01 60 50 40 30 20"]
|
||||
|
||||
invalid_phone_numbers.each do |tel|
|
||||
service.telephone = tel
|
||||
expect(service.valid?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
it 'should accept no phone numbers' do
|
||||
service = Service.create(params)
|
||||
service.telephone = nil
|
||||
|
||||
expect(service.valid?).to be_truthy
|
||||
end
|
||||
|
||||
it 'should accept valid phone numbers' do
|
||||
service = Service.create(params)
|
||||
valid_phone_numbers = ["3646", "273115", "0160376983", "01 60 50 40 30 ", "+33160504030"]
|
||||
|
||||
valid_phone_numbers.each do |tel|
|
||||
service.telephone = tel
|
||||
expect(service.valid?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a first service exists' do
|
||||
before { Service.create(params) }
|
||||
|
||||
|
|
Loading…
Reference in a new issue