Merge branch 'dev'

This commit is contained in:
gregoirenovel 2018-11-08 16:54:24 +01:00
commit 26fd99e942
60 changed files with 692 additions and 417 deletions

View file

@ -145,6 +145,8 @@ group :test do
gem 'capybara-selenium' gem 'capybara-selenium'
# Save a dump of the page when an integration test fails # Save a dump of the page when an integration test fails
gem 'capybara-screenshot' gem 'capybara-screenshot'
# Access emails during integration tests
gem 'capybara-email'
end end
group :development do group :development do

View file

@ -120,6 +120,9 @@ GEM
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
xpath (>= 2.0, < 4.0) xpath (>= 2.0, < 4.0)
capybara-email (3.0.1)
capybara (>= 2.4, < 4.0)
mail
capybara-screenshot (1.0.21) capybara-screenshot (1.0.21)
capybara (>= 1.0, < 4) capybara (>= 1.0, < 4)
launchy launchy
@ -812,6 +815,7 @@ DEPENDENCIES
browser browser
byebug byebug
capybara capybara
capybara-email
capybara-screenshot capybara-screenshot
capybara-selenium capybara-selenium
carrierwave carrierwave

View file

@ -0,0 +1,27 @@
$default-space: 15px;
$new-p-margin-bottom: 3 * $default-space;
.suivi {
width: 1040px;
margin: 0 auto;
padding-top: $default-space * 2;
padding-bottom: $default-space * 2;
ul {
list-style-type: disc;
margin-top: -($default-space * 2);
}
iframe {
border: none;
width: 100%;
}
.new-h2 {
text-align: left;
}
}
.new-p {
margin-bottom: $new-p-margin-bottom;
}

View file

@ -1,19 +0,0 @@
class Admin::ChangeDossierStateController < AdminController
def index
@dossier = Dossier.new
end
def change
@dossier = Dossier.find(params[:dossier][:id])
@dossier.update state: params[:next_state]
end
def check
@dossier = Dossier.find(params[:dossier][:id])
if @dossier.procedure.administrateur.email != current_administrateur.email
flash.alert = 'Dossier introuvable'
return redirect_to admin_change_dossier_state_path
end
end
end

View file

@ -5,30 +5,30 @@ class Admin::TypesDeChampController < AdminController
def destroy def destroy
@procedure.types_de_champ.destroy(params[:id]) @procedure.types_de_champ.destroy(params[:id])
create_facade setup_type_de_champ_service
render 'show', format: :js render 'show', format: :js
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render json: { message: 'Champ not found' }, status: 404 render json: { message: 'Champ not found' }, status: 404
end end
def show def show
create_facade setup_type_de_champ_service
end end
def update def update
if @procedure.update(TypesDeChampService.create_update_procedure_params params) setup_type_de_champ_service
if @procedure.update(@type_de_champ_service.create_update_procedure_params(params))
flash.now.notice = 'Modifications sauvegardées' flash.now.notice = 'Modifications sauvegardées'
else else
flash.now.alert = @procedure.errors.full_messages.join(', ') flash.now.alert = @procedure.errors.full_messages.join(', ')
end end
create_facade
render 'show', format: :js render 'show', format: :js
end end
def move_up def move_up
index = params[:index].to_i - 1 index = params[:index].to_i - 1
if @procedure.switch_types_de_champ index if @procedure.switch_types_de_champ(index)
create_facade setup_type_de_champ_service
render 'show', format: :js render 'show', format: :js
else else
render json: {}, status: 400 render json: {}, status: 400
@ -36,8 +36,8 @@ class Admin::TypesDeChampController < AdminController
end end
def move_down def move_down
if @procedure.switch_types_de_champ params[:index].to_i if @procedure.switch_types_de_champ(params[:index].to_i)
create_facade setup_type_de_champ_service
render 'show', format: :js render 'show', format: :js
else else
render json: {}, status: 400 render json: {}, status: 400
@ -46,7 +46,7 @@ class Admin::TypesDeChampController < AdminController
private private
def create_facade def setup_type_de_champ_service
@types_de_champ_facade = AdminTypesDeChampFacades.new false, @procedure @type_de_champ_service = TypesDeChampService.new(@procedure)
end end
end end

View file

@ -5,28 +5,31 @@ class Admin::TypesDeChampPrivateController < AdminController
def destroy def destroy
@procedure.types_de_champ_private.destroy(params[:id]) @procedure.types_de_champ_private.destroy(params[:id])
create_facade setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js render 'admin/types_de_champ/show', format: :js
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render json: { message: 'Champ not found' }, status: 404 render json: { message: 'Champ not found' }, status: 404
end end
def show def show
create_facade setup_type_de_champ_service
render 'admin/types_de_champ/show' render 'admin/types_de_champ/show'
end end
def update def update
@procedure.update(TypesDeChampService.create_update_procedure_params params, true) setup_type_de_champ_service
create_facade if @procedure.update(@type_de_champ_service.create_update_procedure_params(params))
flash.now.notice = 'Modifications sauvegardées' flash.now.notice = 'Modifications sauvegardées'
else
flash.now.alert = @procedure.errors.full_messages.join(', ')
end
render 'admin/types_de_champ/show', format: :js render 'admin/types_de_champ/show', format: :js
end end
def move_up def move_up
index = params[:index].to_i - 1 index = params[:index].to_i - 1
if @procedure.switch_types_de_champ_private index if @procedure.switch_types_de_champ_private(index)
create_facade setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js render 'admin/types_de_champ/show', format: :js
else else
render json: {}, status: 400 render json: {}, status: 400
@ -34,8 +37,8 @@ class Admin::TypesDeChampPrivateController < AdminController
end end
def move_down def move_down
if @procedure.switch_types_de_champ_private params[:index].to_i if @procedure.switch_types_de_champ_private(params[:index].to_i)
create_facade setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js render 'admin/types_de_champ/show', format: :js
else else
render json: {}, status: 400 render json: {}, status: 400
@ -44,7 +47,7 @@ class Admin::TypesDeChampPrivateController < AdminController
private private
def create_facade def setup_type_de_champ_service
@types_de_champ_facade = AdminTypesDeChampFacades.new true, @procedure @type_de_champ_service = TypesDeChampService.new(@procedure, true)
end end
end end

View file

@ -4,7 +4,7 @@ class API::V1::DossiersController < APIController
DEFAULT_PAGE_SIZE = 100 DEFAULT_PAGE_SIZE = 100
def index def index
dossiers = @procedure.dossiers.state_not_brouillon.page(params[:page]).per(per_page) dossiers = @dossiers.page(params[:page]).per(per_page)
render json: { dossiers: dossiers.map{ |dossier| DossiersSerializer.new(dossier) }, pagination: pagination(dossiers) }, status: 200 render json: { dossiers: dossiers.map{ |dossier| DossiersSerializer.new(dossier) }, pagination: pagination(dossiers) }, status: 200
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
@ -12,7 +12,7 @@ class API::V1::DossiersController < APIController
end end
def show def show
dossier = @procedure.dossiers.find(params[:id]) dossier = @dossiers.for_api.find(params[:id])
respond_to do |format| respond_to do |format|
format.json { render json: { dossier: DossierSerializer.new(dossier).as_json }, status: 200 } format.json { render json: { dossier: DossierSerializer.new(dossier).as_json }, status: 200 }
@ -36,12 +36,14 @@ class API::V1::DossiersController < APIController
end end
def fetch_procedure_and_check_token def fetch_procedure_and_check_token
@procedure = Procedure.includes(:administrateur).find(params[:procedure_id]) @procedure = Procedure.for_api.find(params[:procedure_id])
if !valid_token_for_administrateur?(@procedure.administrateur) if !valid_token_for_administrateur?(@procedure.administrateur)
render json: {}, status: :unauthorized render json: {}, status: :unauthorized
end end
@dossiers = @procedure.dossiers.state_not_brouillon
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render json: {}, status: :not_found render json: {}, status: :not_found
end end

View file

@ -8,7 +8,7 @@ class API::V1::ProceduresController < APIController
private private
def fetch_procedure_and_check_token def fetch_procedure_and_check_token
@procedure = Procedure.includes(:administrateur).find(params[:id]) @procedure = Procedure.for_api.find(params[:id])
if !valid_token_for_administrateur?(@procedure.administrateur) if !valid_token_for_administrateur?(@procedure.administrateur)
render json: {}, status: :unauthorized render json: {}, status: :unauthorized

View file

@ -4,8 +4,10 @@ module Manager
before_action :default_params before_action :default_params
def default_params def default_params
params[:order] ||= "created_at" params[resource_name] ||= {
params[:direction] ||= "desc" order: "created_at",
direction: "desc"
}
end end
protected protected

View file

@ -55,4 +55,7 @@ class RootController < ApplicationController
@dossier = Dossier.new(champs: all_champs) @dossier = Dossier.new(champs: all_champs)
end end
def suivi
end
end end

View file

@ -25,8 +25,24 @@ class Users::ConfirmationsController < Devise::ConfirmationsController
# super(resource_name) # super(resource_name)
# end # end
# The path used after confirmation. # If the user clicks the confirmation link before the maximum delay,
# def after_confirmation_path_for(resource_name, resource) # they will be signed in directly.
# super(resource_name, resource) def sign_in_after_confirmation?(resource)
# end # Avoid keeping auto-sign-in links in users inboxes for too long.
# 95% of users confirm their account within two hours.
auto_sign_in_timeout = 2.hours
resource.confirmation_sent_at + auto_sign_in_timeout > DateTime.current
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
if sign_in_after_confirmation?(resource)
resource.remember_me = true
sign_in(resource)
resource.force_sync_credentials
after_sign_in_path_for(resource_name)
else
super(resource_name, resource)
end
end
end end

View file

@ -1,6 +1,4 @@
class DossierDecorator < Draper::Decorator class DossierDecorator < Draper::Decorator
include Rails.application.routes.url_helpers
delegate :current_page, :limit_value, :total_pages delegate :current_page, :limit_value, :total_pages
delegate_all delegate_all
@ -11,12 +9,4 @@ class DossierDecorator < Draper::Decorator
def last_update def last_update
updated_at.strftime('%d/%m/%Y %H:%M') updated_at.strftime('%d/%m/%Y %H:%M')
end end
def display_state
DossierDecorator.case_state_fr state
end
def self.case_state_fr(state = self.state)
h.t("activerecord.attributes.dossier.state.#{state}")
end
end end

View file

@ -1,48 +0,0 @@
class AdminTypesDeChampFacades
include Rails.application.routes.url_helpers
def initialize(private, procedure)
@private = private
@procedure = procedure
end
def private
@private
end
def active
@private ? 'Annotations privées' : 'Champs'
end
def url
@private ? admin_procedure_types_de_champ_private_path(@procedure) : admin_procedure_types_de_champ_path(@procedure)
end
def types_de_champ
@private ? @procedure.types_de_champ_private_ordered.decorate : @procedure.types_de_champ_ordered.decorate
end
def new_type_de_champ
TypeDeChamp.new(private: @private).decorate
end
def fields_for_var
@private ? :types_de_champ_private : :types_de_champ
end
def move_up_url(ff)
@private ? move_up_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_up_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def move_down_url(ff)
@private ? move_down_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_down_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def delete_url(ff)
@private ? admin_procedure_type_de_champ_private_path(@procedure, ff.object.id) : admin_procedure_type_de_champ_path(@procedure, ff.object.id)
end
def add_button_id
@private ? :add_type_de_champ_private : :add_type_de_champ
end
end

View file

@ -1,55 +0,0 @@
class DossierFacades
# TODO rechercher en fonction de la personne/email
def initialize(dossier_id, email, champ_id = nil)
@dossier = Dossier.find(dossier_id)
@champ_id = champ_id
end
def dossier
@dossier.decorate
end
def champs
@dossier.champs
end
def etablissement
@dossier.etablissement
end
def types_de_pieces_justificatives
@dossier.types_de_piece_justificative.order('order_place ASC')
end
def champ_id
@champ_id
end
def commentaires
@dossier.commentaires.where(champ_id: @champ_id).decorate
end
def procedure
@dossier.procedure
end
def invites
@dossier.invites
end
def champs_private
@dossier.champs_private
end
def individual
@dossier.individual
end
def commentaires_files
PieceJustificative.where(dossier_id: @dossier.id, type_de_piece_justificative_id: nil)
end
def followers
@dossier.followers_gestionnaires
end
end

View file

@ -26,4 +26,26 @@ module DossierHelper
def dossier_submission_is_closed?(dossier) def dossier_submission_is_closed?(dossier)
dossier.brouillon? && dossier.procedure.archivee? dossier.brouillon? && dossier.procedure.archivee?
end end
def dossier_display_state(dossier, lower: false)
state = I18n.t(dossier.state, scope: [:activerecord, :attributes, :dossier, :state])
lower ? state.downcase : state
end
def dossier_legacy_state(dossier)
case dossier.state
when Dossier.states.fetch(:en_construction)
'initiated'
when Dossier.states.fetch(:en_instruction)
'received'
when Dossier.states.fetch(:accepte)
'closed'
when Dossier.states.fetch(:refuse)
'refused'
when Dossier.states.fetch(:sans_suite)
'without_continuation'
else
dossier.state
end
end
end end

View file

@ -1,19 +0,0 @@
module TypeDeChampHelper
TOGGLES = {
TypeDeChamp.type_champs.fetch(:piece_justificative) => :champ_pj?,
TypeDeChamp.type_champs.fetch(:siret) => :champ_siret?,
TypeDeChamp.type_champs.fetch(:linked_drop_down_list) => :champ_linked_dropdown?,
TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number?
}
def tdc_options
tdcs = TypeDeChamp.type_de_champs_list_fr
tdcs.select! do |tdc|
toggle = TOGGLES[tdc.last]
toggle.blank? || Flipflop.send(toggle)
end
tdcs
end
end

View file

@ -5,6 +5,11 @@ class Champ < ApplicationRecord
has_one_attached :piece_justificative_file has_one_attached :piece_justificative_file
has_one :virus_scan has_one :virus_scan
# We declare champ specific relationships (Champs::CarteChamp and Champs::SiretChamp)
# here because otherwise we can't easily use includes in our queries.
has_many :geo_areas, dependent: :destroy
belongs_to :etablissement, dependent: :destroy
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) } scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }

View file

@ -1,6 +1,4 @@
class Champs::CarteChamp < Champ class Champs::CarteChamp < Champ
has_many :geo_areas, foreign_key: :champ_id, dependent: :destroy
# We are not using scopes here as we want to access # We are not using scopes here as we want to access
# the following collections on unsaved records. # the following collections on unsaved records.
def cadastres def cadastres

View file

@ -37,7 +37,6 @@ class Champs::SiretChamp < Champ
] ]
] ]
belongs_to :etablissement, dependent: :destroy
accepts_nested_attributes_for :etablissement, allow_destroy: true, update_only: true accepts_nested_attributes_for :etablissement, allow_destroy: true, update_only: true
def search_terms def search_terms

View file

@ -59,6 +59,26 @@ class Dossier < ApplicationRecord
scope :with_champs, -> { includes(champs: :type_de_champ) } 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 :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 :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: [],
quartier_prioritaires: [],
cadastres: [],
etablissement: [],
individual: [],
user: [])
}
accepts_nested_attributes_for :individual accepts_nested_attributes_for :individual
delegate :siret, :siren, to: :etablissement, allow_nil: true delegate :siret, :siren, to: :etablissement, allow_nil: true
@ -225,16 +245,6 @@ class Dossier < ApplicationRecord
end end
end end
def statut
if accepte?
'accepté'
elsif sans_suite?
'classé sans suite'
elsif refuse?
'refusé'
end
end
def user_geometry def user_geometry
if json_latlngs.present? if json_latlngs.present?
UserGeometry.new(json_latlngs) UserGeometry.new(json_latlngs)
@ -288,23 +298,6 @@ class Dossier < ApplicationRecord
DossierMailer.notify_deletion_to_user(deleted_dossier, user.email).deliver_later DossierMailer.notify_deletion_to_user(deleted_dossier, user.email).deliver_later
end end
def old_state_value
case state
when Dossier.states.fetch(:en_construction)
'initiated'
when Dossier.states.fetch(:en_instruction)
'received'
when Dossier.states.fetch(:accepte)
'closed'
when Dossier.states.fetch(:refuse)
'refused'
when Dossier.states.fetch(:sans_suite)
'without_continuation'
else
state
end
end
private private
def update_state_dates def update_state_dates

View file

@ -47,6 +47,16 @@ class Procedure < ApplicationRecord
scope :cloned_from_library, -> { where(cloned_from_library: true) } scope :cloned_from_library, -> { where(cloned_from_library: true) }
scope :avec_lien, -> { where.not(path: nil) } scope :avec_lien, -> { where.not(path: nil) }
scope :for_api, -> {
includes(
:administrateur,
:types_de_champ_private,
:types_de_champ,
:types_de_piece_justificative,
:module_api_carto
)
}
validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :libelle, presence: true, allow_blank: false, allow_nil: false
validates :description, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false
validate :check_juridique validate :check_juridique

View file

@ -1,4 +1,6 @@
class DossierSerializer < ActiveModel::Serializer class DossierSerializer < ActiveModel::Serializer
include DossierHelper
attributes :id, attributes :id,
:created_at, :created_at,
:updated_at, :updated_at,
@ -42,11 +44,11 @@ class DossierSerializer < ActiveModel::Serializer
end end
def state def state
object.old_state_value dossier_legacy_state(object)
end end
def simplified_state def simplified_state
object.decorate.display_state dossier_display_state(object)
end end
def initiated_at def initiated_at

View file

@ -1,4 +1,6 @@
class DossierTableExportSerializer < ActiveModel::Serializer class DossierTableExportSerializer < ActiveModel::Serializer
include DossierHelper
attributes :id, attributes :id,
:created_at, :created_at,
:updated_at, :updated_at,
@ -22,20 +24,7 @@ class DossierTableExportSerializer < ActiveModel::Serializer
end end
def state def state
case object.state dossier_legacy_state(object)
when Dossier.states.fetch(:en_construction)
'initiated'
when Dossier.states.fetch(:en_instruction)
'received'
when Dossier.states.fetch(:accepte)
'closed'
when Dossier.states.fetch(:refuse)
'refused'
when Dossier.states.fetch(:sans_suite)
'without_continuation'
else
object.state
end
end end
def initiated_at def initiated_at

View file

@ -1,4 +1,6 @@
class DossiersSerializer < ActiveModel::Serializer class DossiersSerializer < ActiveModel::Serializer
include DossierHelper
attributes :id, attributes :id,
:updated_at, :updated_at,
:initiated_at, :initiated_at,
@ -13,6 +15,6 @@ class DossiersSerializer < ActiveModel::Serializer
end end
def state def state
object.old_state_value dossier_legacy_state(object)
end end
end end

View file

@ -1,7 +1,71 @@
class TypesDeChampService class TypesDeChampService
def self.create_update_procedure_params(params, private = false) include Rails.application.routes.url_helpers
attributes = (private ? 'types_de_champ_private_attributes' : 'types_de_champ_attributes')
TOGGLES = {
TypeDeChamp.type_champs.fetch(:piece_justificative) => :champ_pj?,
TypeDeChamp.type_champs.fetch(:siret) => :champ_siret?,
TypeDeChamp.type_champs.fetch(:linked_drop_down_list) => :champ_linked_dropdown?,
TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number?
}
def options
types_de_champ = TypeDeChamp.type_de_champs_list_fr
types_de_champ.select! do |tdc|
toggle = TOGGLES[tdc.last]
toggle.blank? || Flipflop.send(toggle)
end
types_de_champ
end
def initialize(procedure, private_type_de_champ = false)
@procedure = procedure
@private_type_de_champ = private_type_de_champ
end
def private?
@private_type_de_champ
end
def active
private? ? 'Annotations privées' : 'Champs'
end
def url
private? ? admin_procedure_types_de_champ_private_path(@procedure) : admin_procedure_types_de_champ_path(@procedure)
end
def types_de_champ
private? ? @procedure.types_de_champ_private_ordered.decorate : @procedure.types_de_champ_ordered.decorate
end
def new_type_de_champ
TypeDeChamp.new(private: private?).decorate
end
def fields_for_var
private? ? :types_de_champ_private : :types_de_champ
end
def move_up_url(ff)
private? ? move_up_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_up_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def move_down_url(ff)
private? ? move_down_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_down_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def delete_url(ff)
private? ? admin_procedure_type_de_champ_private_path(@procedure, ff.object.id) : admin_procedure_type_de_champ_path(@procedure, ff.object.id)
end
def add_button_id
private? ? :add_type_de_champ_private : :add_type_de_champ
end
def create_update_procedure_params(params)
attributes = "#{fields_for_var}_attributes"
params_with_ordered_champs = order_champs(params, attributes) params_with_ordered_champs = order_champs(params, attributes)
parameters = params_with_ordered_champs parameters = params_with_ordered_champs
@ -21,13 +85,13 @@ class TypesDeChampService
]) ])
parameters[attributes].each do |index, param| parameters[attributes].each do |index, param|
param[:private] = private param[:private] = private?
if param[:libelle].empty? if param[:libelle].empty?
parameters[attributes].delete(index.to_s) parameters[attributes].delete(index.to_s)
end end
if param['drop_down_list_attributes'] && param['drop_down_list_attributes']['value'] if param['drop_down_list_attributes'] && param['drop_down_list_attributes']['value']
param['drop_down_list_attributes']['value'] = self.clean_value (param['drop_down_list_attributes']['value']) param['drop_down_list_attributes']['value'] = clean_value(param['drop_down_list_attributes']['value'])
end end
end end
@ -36,7 +100,7 @@ class TypesDeChampService
private private
def self.order_champs(params, attributes) def order_champs(params, attributes)
# It's OK to use an unsafe hash here because the params will then go through # It's OK to use an unsafe hash here because the params will then go through
# require / permit methods in #create_update_procedure_params # require / permit methods in #create_update_procedure_params
tdcas = params[:procedure][attributes].to_unsafe_hash.to_a tdcas = params[:procedure][attributes].to_unsafe_hash.to_a
@ -64,15 +128,15 @@ class TypesDeChampService
params params
end end
def self.is_number?(value) def is_number?(value)
(value =~ /^[0-9]+$/) == 0 (value =~ /^[0-9]+$/) == 0
end end
def self.tdca_order_changed?(tdca) def tdca_order_changed?(tdca)
(tdca[:order_place].to_i + 1) != tdca[:custom_order_place].to_i (tdca[:order_place].to_i + 1) != tdca[:custom_order_place].to_i
end end
def self.clean_value(value) def clean_value(value)
value.split("\r\n").map(&:strip).join("\r\n") value.split("\r\n").map(&:strip).join("\r\n")
end end
end end

View file

@ -1,16 +0,0 @@
.center
%h2 Outil de changement d'état d'un dossier
%h4.text-success
Changement effectué
= form_for @dossier, url: 'change_dossier_state', action: :put do |f|
Dossier ID :
= @dossier.id
%br
État :
= @dossier.decorate.display_state
%br
%br
= link_to 'Réaliser un autre dossier', 'change_dossier_state'

View file

@ -1,17 +0,0 @@
.center
%h2 Outil de changement d'état d'un dossier
= form_for @dossier, url: 'change_dossier_state', action: :put do |f|
Dossier ID :
= @dossier.id
= f.hidden_field :id
%br
État :
= @dossier.decorate.display_state
%br
État souhaité :
%select{ id: :next_state, name: :next_state }
- Dossier.states.each do |state|
%option{ value: state[0] }
= DossierDecorator.case_state_fr state[1]
= f.submit 'Valider'

View file

@ -1,7 +0,0 @@
.center
%h2 Outil de changement d'état d'un dossier
= form_for @dossier, url: 'change_dossier_state', action: :post do |f|
Dossier ID
= f.text_field :id
= f.submit 'Vérifier état'

View file

@ -1,4 +1,4 @@
= f.fields_for @types_de_champ_facade.fields_for_var, types_de_champ, remote: true do |ff| = f.fields_for type_de_champ_service.fields_for_var, types_de_champ, remote: true do |ff|
- type_champ = ff.object.object.type_champ - type_champ = ff.object.object.type_champ
.form-inline{ class: (type_champ == TypeDeChamp.type_champs.fetch(:header_section) ? 'header-section' : nil) } .form-inline{ class: (type_champ == TypeDeChamp.type_champs.fetch(:header_section) ? 'header-section' : nil) }
@ -8,7 +8,7 @@
.form-group.type .form-group.type
%h4 Type %h4 Type
= ff.select :type_champ, tdc_options, {}, { class: 'form-control type-champ' } = ff.select :type_champ, type_de_champ_service.options, {}, { class: 'form-control type-champ' }
.form-group.description .form-group.description
%h4 Description %h4 Description
@ -65,8 +65,8 @@
- if ff.object.id.present? - if ff.object.id.present?
.form-group .form-group
%br &nbsp; %br &nbsp;
= ff.object.button_up(index: ff.index, url: @types_de_champ_facade.move_up_url(ff), private: @types_de_champ_facade.private) = ff.object.button_up(index: ff.index, url: type_de_champ_service.move_up_url(ff), private: type_de_champ_service.private?)
= ff.object.button_down(index: ff.index, url: @types_de_champ_facade.move_down_url(ff), private: @types_de_champ_facade.private) = ff.object.button_down(index: ff.index, url: type_de_champ_service.move_down_url(ff), private: type_de_champ_service.private?)
.form-group .form-group
%h4 position %h4 position
@ -76,10 +76,10 @@
%br &nbsp; %br &nbsp;
- if ff.object.id.nil? - if ff.object.id.nil?
= f.button 'Ajouter le champ', = f.button 'Ajouter le champ',
id: @types_de_champ_facade.add_button_id, id: type_de_champ_service.add_button_id,
class: 'btn btn-success', class: 'btn btn-success',
data: { disable: true } data: { disable: true }
- else - else
= link_to("", @types_de_champ_facade.delete_url(ff), method: :delete, remote: true, id: "delete_type_de_champ_#{ff.object.id}", class: %w(form-control btn btn-danger fa fa-trash-o) ) = link_to("", type_de_champ_service.delete_url(ff), method: :delete, remote: true, id: "delete_type_de_champ_#{ff.object.id}", class: %w(form-control btn btn-danger fa fa-trash-o) )
%div{ style: 'background-color: rgb(204, 204, 204); height: 1px; margin: 30px auto;' } %div{ style: 'background-color: rgb(204, 204, 204); height: 1px; margin: 30px auto;' }

View file

@ -1,5 +1,5 @@
= form_for [:admin, @procedure], url: @types_de_champ_facade.url, remote: true do |f| = form_for [:admin, procedure], url: type_de_champ_service.url, remote: true do |f|
= render partial: 'admin/types_de_champ/fields', locals: { types_de_champ: @types_de_champ_facade.types_de_champ, f: f } = render partial: 'admin/types_de_champ/fields', locals: { type_de_champ_service: type_de_champ_service, types_de_champ: type_de_champ_service.types_de_champ, f: f }
= f.button 'Enregistrer', = f.button 'Enregistrer',
id: :save, id: :save,
@ -8,4 +8,4 @@
%hr %hr
#new_type_de_champ #new_type_de_champ
= render partial: 'admin/types_de_champ/fields', locals: { types_de_champ: @types_de_champ_facade.new_type_de_champ, f: f } = render partial: 'admin/types_de_champ/fields', locals: { type_de_champ_service: type_de_champ_service, types_de_champ: type_de_champ_service.new_type_de_champ, f: f }

View file

@ -1,3 +1,3 @@
.row.white-back .row.white-back
#liste-champ #liste-champ
= render partial: 'admin/types_de_champ/form' = render partial: 'admin/types_de_champ/form', locals: { procedure: @procedure, type_de_champ_service: @type_de_champ_service }

View file

@ -1,2 +1,2 @@
<%= render_flash(timeout: 3000, sticky: true) %> <%= render_flash(timeout: 3000, sticky: true) %>
<%= render_to_element('#liste-champ', partial: 'admin/types_de_champ/form', locals: { procedure: @procedure, types_de_champ: @types_de_champ }) %> <%= render_to_element('#liste-champ', partial: 'admin/types_de_champ/form', locals: { procedure: @procedure, type_de_champ_service: @type_de_champ_service }) %>

View file

@ -0,0 +1,15 @@
:javascript
var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.www.demarches-simplifiees.fr"]);
_paq.push(["setDomains", ["*.www.demarches-simplifiees.fr"]]);
_paq.push(["setDoNotTrack", true]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//stats.data.gouv.fr/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '73']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();

View file

@ -16,10 +16,13 @@
= javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
= csrf_meta_tags = csrf_meta_tags
= render partial: "layouts/matomo"
:javascript :javascript
DATA = [{ DATA = [{
sentry: #{raw(sentry_config)} sentry: #{raw(sentry_config)}
}]; }];
%body{ class: browser.platform.ios? ? 'ios' : nil } %body{ class: browser.platform.ios? ? 'ios' : nil }
= render partial: 'layouts/outdated_browser_banner' = render partial: 'layouts/outdated_browser_banner'
= render partial: 'layouts/pre_maintenance' = render partial: 'layouts/pre_maintenance'

View file

@ -1 +1 @@
= render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: @types_de_champ_facade.active } = render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: @type_de_champ_service.active }

View file

@ -21,10 +21,14 @@
- if Rails.env.development? - if Rails.env.development?
= stylesheet_link_tag :xray = stylesheet_link_tag :xray
- if !current_user
= render partial: "layouts/matomo"
:javascript :javascript
DATA = [{ DATA = [{
sentry: #{raw(sentry_config)} sentry: #{raw(sentry_config)}
}]; }];
%body{ class: browser.platform.ios? ? 'ios' : nil } %body{ class: browser.platform.ios? ? 'ios' : nil }
.page-wrapper .page-wrapper
= render partial: "layouts/outdated_browser_banner" = render partial: "layouts/outdated_browser_banner"

View file

@ -46,4 +46,4 @@
.print-header .print-header
= dossier.procedure.libelle.truncate_words(10) = dossier.procedure.libelle.truncate_words(10)
> >
= "Dossier nº #{dossier.id} (#{dossier.statut})" = "Dossier nº #{dossier.id} (#{dossier_display_state(dossier, lower: true)})"

View file

@ -1,7 +1,7 @@
- if dossier.en_construction? || dossier.en_instruction? - if dossier.en_construction? || dossier.en_instruction?
%span.dropdown %span.dropdown
%button.button.primary.dropdown-button %button.button.primary.dropdown-button
= dossier.decorate.display_state = dossier_display_state dossier
.dropdown-content.fade-in-down .dropdown-content.fade-in-down
%ul.dropdown-items %ul.dropdown-items
- if dossier.en_construction? - if dossier.en_construction?
@ -55,7 +55,7 @@
- if dossier.motivation.present? || dossier.attestation.present? - if dossier.motivation.present? || dossier.attestation.present?
%span.dropdown %span.dropdown
%button.button.dropdown-button{ class: button_or_label_class(dossier) } %button.button.dropdown-button{ class: button_or_label_class(dossier) }
= dossier.statut = dossier_display_state(dossier, lower: true)
.dropdown-content.fade-in-down.terminated .dropdown-content.fade-in-down.terminated
- if dossier.motivation.present? - if dossier.motivation.present?
%h4 Motivation %h4 Motivation
@ -67,4 +67,4 @@
= link_to "Voir l'attestation", attestation_gestionnaire_dossier_path(dossier.procedure, dossier), target: '_blank', class: 'button' = link_to "Voir l'attestation", attestation_gestionnaire_dossier_path(dossier.procedure, dossier), target: '_blank', class: 'button'
- else - else
%span.label{ class: button_or_label_class(dossier) } %span.label{ class: button_or_label_class(dossier) }
= dossier.statut = dossier_display_state(dossier, lower: true)

View file

@ -26,6 +26,8 @@
= link_to "CGU", CGU_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer" = link_to "CGU", CGU_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
%li.footer-link %li.footer-link
= link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer" = link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
%li.footer-link
= link_to "Suivi d'audience et vie privée", suivi_path, :class => "footer-link"
%li.footer-column %li.footer-column
%ul.footer-links %ul.footer-links

View file

@ -0,0 +1,23 @@
- content_for(:title, 'Suivi')
.suivi
%h1.new-h1 Cookies déposés et configuration du suivi
%p.new-p
Ce site dépose un petit fichier texte (un « cookie ») sur votre ordinateur lorsque vous le consultez. Cela nous permet de mesurer le nombre de visites et de comprendre quelles sont les pages les plus consultées.
%iframe{ :src => MATOMO_IFRAME_URL }
%h2.new-h2 Ce site naffiche pas de bannière de consentement aux cookies, pourquoi ?
%p.new-p
Cest vrai, vous navez pas eu à cliquer sur un bloc qui recouvre la moitié de la page pour dire que vous êtes daccord avec le dépôt de cookies.
%br
%br
Rien d'exceptionnel, pas de passe-droit. Nous respectons simplement la loi, qui dit que certains outils de suivi daudience, correctement configurés pour respecter la vie privée, sont exemptés dautorisation préalable.
%br
%br
Nous utilisons pour cela <a href="https://matomo.org/" target="_blank" >Matomo</a>, un outil <a href="https://matomo.org/free-software/" target="_blank">libre</a>, paramétré pour être en conformité avec la <a href="https://www.cnil.fr/fr/solutions-pour-la-mesure-daudience">recommandation « Cookies » </a>de la CNIL. Cela signifie que votre adresse IP, par exemple, est anonymisée avant dêtre enregistrée. Il est donc impossible dassocier vos visites sur ce site à votre personne.
%h2.new-h2 Je contribue à enrichir vos données, puis-je y accéder ?
%p.new-p
Bien sûr ! Les statistiques dusage sont en accès libre sur <a href="https://stats.data.gouv.fr/index.php?module=MultiSites&action=index&idSite=1&period=range&date=previous30&updated=1#?idSite=1&period=range&date=previous30&category=Dashboard_Dashboard&subcategory=1&module=MultiSites&action=index" target="_blank">stats.data.gouv.fr</a>.

View file

@ -39,7 +39,6 @@ Rails.application.configure do
# Print deprecation notices to the stderr. # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr config.active_support.deprecation = :stderr
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { :host => 'localhost:3000' } config.action_mailer.default_url_options = { :host => 'localhost:3000' }
Rails.application.routes.default_url_options = { Rails.application.routes.default_url_options = {
host: 'localhost:3000', host: 'localhost:3000',

View file

@ -22,3 +22,4 @@ MENTIONS_LEGALES_URL = [CGU_URL, "4-mentions-legales"].join("#")
API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "api"].join("/") API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "api"].join("/")
WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/") WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/")
FAQ_URL = "https://faq.demarches-simplifiees.fr" FAQ_URL = "https://faq.demarches-simplifiees.fr"
MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli"

View file

@ -128,6 +128,7 @@ Rails.application.routes.draw do
end end
get "patron" => "root#patron" get "patron" => "root#patron"
get "suivi" => "root#suivi"
get "contact", to: "support#index" get "contact", to: "support#index"
post "contact", to: "support#create" post "contact", to: "support#create"
@ -172,10 +173,6 @@ Rails.application.routes.draw do
get 'procedures/path_list' => 'procedures#path_list' get 'procedures/path_list' => 'procedures#path_list'
get 'procedures/available' => 'procedures#check_availability' get 'procedures/available' => 'procedures#check_availability'
get 'change_dossier_state' => 'change_dossier_state#index'
post 'change_dossier_state' => 'change_dossier_state#check'
patch 'change_dossier_state' => 'change_dossier_state#change'
resources :procedures do resources :procedures do
collection do collection do
get 'new_from_existing' => 'procedures#new_from_existing', as: :new_from_existing get 'new_from_existing' => 'procedures#new_from_existing', as: :new_from_existing

View file

@ -67,6 +67,9 @@ namespace :'2018_07_31_nutriscore' do
libelle: 'Numéro SIRET' libelle: 'Numéro SIRET'
) )
) do |d, target_tdc| ) do |d, target_tdc|
if d.etablissement.present?
d.etablissement.signature = d.etablissement.sign
end
target_tdc.champ.create( target_tdc.champ.create(
value: d.etablissement&.siret, value: d.etablissement&.siret,
etablissement: d.etablissement, etablissement: d.etablissement,

View file

@ -0,0 +1,128 @@
require Rails.root.join("lib", "tasks", "task_helper")
namespace :after_party do
desc 'Deployment task: approve / reject dossiers for procedure 8670'
task mass_process_procedure_8670_dossiers: :environment do
class MassProcessProcedure8670
DOSSIER_IDS_TO_ACCEPT = [
194722, 172178, 170186, 177122, 171783, 173930, 176042, 183202, 192081, 170387,
171136, 171765, 173743, 173784, 173802, 173904, 173915, 171744, 173945, 170839,
173954, 173967, 173990, 174003, 172217, 170449, 176603, 175921, 172634, 179048,
180970, 181508, 182035, 181526, 178543, 170822, 170782, 186579, 187928, 188183,
188399, 176860, 170614, 194011, 194100, 196307, 200282, 171544, 201953, 176316,
205882, 205907, 205962, 178940, 181650, 202114, 180333, 174687, 192439, 169826,
170121, 169894, 170635, 170626, 171896, 171993, 171935, 172028, 172087, 172094,
172138, 172206, 172456, 172468, 172533, 172627, 172758, 172834, 172845, 172879,
172884, 173411, 173662, 172256, 173909, 191259, 197681, 200041, 177306, 202624,
203230, 204556, 205785, 198252, 170227, 173513, 172296, 174292, 174483, 174492,
175076, 176540, 177177, 177322, 183210, 183498, 184353, 195189, 195967, 186147,
170799, 178152, 177440, 184132, 169967, 175335, 177364, 179365, 197527, 172820,
187060, 200326, 169921, 183622, 174745, 175484, 174512, 180860, 189163, 170054,
170106, 206667, 170263, 173759, 169879, 170632, 190310, 170325, 170336, 170650,
171520, 171050, 170414, 173804, 173911, 173947, 178986, 172030, 177428, 182875,
198458, 199080, 172489, 200406, 204297, 171184, 171265, 171338, 171347, 172620,
173162, 171939, 171597, 173878, 173758, 175861, 175923, 176851, 176957, 172479,
183279, 177429, 185382, 185586, 188898, 172840, 180340, 195351, 171135, 170583,
171680, 174150, 175066, 177164, 172951, 170623, 172863, 178732, 178268, 179848,
179896, 179923, 179283, 180083, 185764, 192455, 190329, 197121, 169897, 170005,
170023, 170127, 170399, 170371, 170351, 170519, 170654, 170680, 170774, 170781,
171892, 169828, 171989, 172070, 171952, 171923, 172184, 174859, 175560, 175865,
172922, 171889, 173550, 181501, 179897, 185241, 190364, 193743, 178551, 199361,
173739, 169885, 169893, 171777, 179338, 179818, 170339, 178090, 187012, 191063,
179911, 195101, 177916, 170242, 173537, 173895, 173700, 174642, 174749, 174880,
174818, 175011, 174863, 175422, 175644, 177797, 177829, 174276, 200208, 204312,
204356, 179106, 177928, 180376, 181086, 180048, 192202, 194193, 204479, 204979,
183388, 185549
]
DOSSIER_IDS_TO_REJECT = [
172516, 177423, 177002, 179031, 176856, 179193, 179237, 179333, 179912, 179949,
181001, 185704, 185710, 177001, 186898, 175420, 175412, 195668, 174463, 175347,
174606, 176668, 176749, 177007, 177037, 174306, 177373, 174496, 174583, 205297,
191646, 178553, 184288, 174296, 199563, 202567, 180596, 194441, 196523, 183504,
190011, 184563, 175047, 177243, 174108, 174423, 170552, 171931, 170955, 170415,
170652, 170145, 170044, 169841, 171280, 177569, 174711, 180357, 180554, 175594,
181370, 180370, 180279, 182877, 188432, 183516, 191845, 184965, 198962, 199250,
202324, 205887, 172006, 196073, 197861, 198389, 188855, 198639, 203881, 205520,
205626, 206468, 196904, 206619, 206730, 175088, 191405, 173038, 195082, 185849,
188454, 188501, 188713, 171057, 177541, 177882, 178185, 178951, 178962, 178997,
179090, 179234, 173959, 177621, 174022, 181414, 181895, 183081, 175935, 175951,
176156, 176200, 176506, 176567, 173898, 173906, 173905, 173932, 173810, 173949,
173961, 174033, 172939, 174227, 172362, 173008, 174979, 173396, 173196, 172143,
173790, 173745, 173779, 172151, 170332, 171424, 171434, 170459, 171635, 171689,
170409, 171429, 171940, 170266, 172632, 172742, 170689, 206612, 169877, 170402,
170563, 170605, 170658, 170653, 170699, 170511, 170835, 183559, 187911, 188163,
188685, 188702, 170678, 183994, 173899, 194530, 194873, 194433, 173971, 174004,
174239, 174430, 175849, 175850, 176265, 176630, 176789, 175946, 172407, 177398,
170027, 170002, 170404, 173678, 170655, 170328, 170405, 170686, 171106, 171763,
172317, 172763, 172880, 173250, 174938, 170714, 175798, 175899, 176015, 176041,
176258, 176341, 176909, 176944, 174031, 180109, 170316, 174100, 174540, 175910,
177872, 178117, 179092, 183923, 175005, 185795, 186580, 181383, 189186, 194998,
177475, 174446, 180508, 181216, 181290, 181905, 191344, 187745, 192016, 193188,
170201, 170288, 170568
]
def run
rake_puts "Running deploy task 'mass_process_procedure_8670_dossiers'\n"
reject_dossiers
accept_dossiers
AfterParty::TaskRecord.create version: '20181106170434'
end
def reject_dossiers
rake_puts "Rejecting dossiers\n"
dossiers_for_traitement.where(id: DOSSIER_IDS_TO_REJECT).find_each do |dossier|
if skip_dossier?(dossier)
next
end
dossier.update(
state: Dossier.states.fetch(:refuse),
motivation: "Malheureusement, votre dossier n'a pas été tiré au sort",
processed_at: Time.zone.now
)
NotificationMailer.send_refused_notification(dossier).deliver_later
end
end
def accept_dossiers
rake_puts "Accepting dossiers\n"
dossiers_for_traitement.where(id: DOSSIER_IDS_TO_ACCEPT).find_each do |dossier|
if skip_dossier?(dossier)
next
end
dossier.update(
state: Dossier.states.fetch(:accepte),
processed_at: Time.zone.now
)
dossier.attestation = dossier.build_attestation
dossier.save
NotificationMailer.send_closed_notification(dossier).deliver_later
end
end
def dossiers_for_traitement
Dossier.includes(:procedure, :user, :etablissement, :champs, :champs_private)
end
def skip_dossier?(dossier)
if dossier.procedure_id != 8670
rake_puts "Skipping dossier #{dossier.id} (wrong procedure)\n"
return true
end
if !dossier.en_instruction?
rake_puts "Skipping dossier #{dossier.id} (not en instruction)\n"
return true
end
return false
end
end
MassProcessProcedure8670.new.run
end
end

View file

@ -136,7 +136,7 @@ describe API::V1::DossiersController do
context 'when dossier exists and belongs to procedure' do context 'when dossier exists and belongs to procedure' do
let(:procedure_id) { procedure.id } let(:procedure_id) { procedure.id }
let(:date_creation) { Time.zone.local(2008, 9, 1, 10, 5, 0) } let(:date_creation) { Time.zone.local(2008, 9, 1, 10, 5, 0) }
let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, procedure: procedure, motivation: "Motivation") } } let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :en_construction, procedure: procedure, motivation: "Motivation") } }
let(:dossier_id) { dossier.id } let(:dossier_id) { dossier.id }
let(:body) { JSON.parse(retour.body, symbolize_names: true) } let(:body) { JSON.parse(retour.body, symbolize_names: true) }
let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs] } let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs] }
@ -147,7 +147,7 @@ describe API::V1::DossiersController do
end end
it { expect(subject[:id]).to eq(dossier.id) } it { expect(subject[:id]).to eq(dossier.id) }
it { expect(subject[:state]).to eq(dossier.state) } it { expect(subject[:state]).to eq('initiated') }
it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') }
it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') }
it { expect(subject[:archived]).to eq(dossier.archived) } it { expect(subject[:archived]).to eq(dossier.archived) }

View file

@ -0,0 +1,57 @@
require 'spec_helper'
describe Users::ConfirmationsController, type: :controller do
let!(:user) { create(:user, :unconfirmed) }
let(:confirmation_token) { user.confirmation_token }
before do
@request.env["devise.mapping"] = Devise.mappings[:user]
end
describe '#show' do
context 'when confirming within the auto-sign-in delay' do
before do
Timecop.travel(1.hour.from_now) {
get :show, params: { confirmation_token: confirmation_token }
}
end
it 'confirms the user' do
expect(user.reload).to be_confirmed
end
it 'signs in the user after confirming its token' do
expect(controller.current_user).to eq(user)
expect(controller.current_gestionnaire).to be(nil)
expect(controller.current_administrateur).to be(nil)
end
it 'redirects the user to the root page' do
# NB: the root page may redirect the user again to the stored procedure path
expect(controller).to redirect_to(root_path)
end
end
context 'when the auto-sign-in delay has expired' do
before do
Timecop.travel(3.hours.from_now) {
get :show, params: { confirmation_token: confirmation_token }
}
end
it 'confirms the user' do
expect(user.reload).to be_confirmed
end
it 'doesnt sign in the user' do
expect(subject.current_user).to be(nil)
expect(subject.current_gestionnaire).to be(nil)
expect(subject.current_administrateur).to be(nil)
end
it 'redirects the user to the sign-in path' do
expect(subject).to redirect_to(new_user_session_path)
end
end
end
end

View file

@ -18,38 +18,4 @@ describe DossierDecorator do
subject { super().last_update } subject { super().last_update }
it { is_expected.to eq('24/12/2015 14:10') } it { is_expected.to eq('24/12/2015 14:10') }
end end
describe 'state_fr' do
subject{ super().display_state }
it 'brouillon is brouillon' do
dossier.brouillon!
expect(subject).to eq('Brouillon')
end
it 'en_construction is En construction' do
dossier.en_construction!
expect(subject).to eq('En construction')
end
it 'accepte is traité' do
dossier.accepte!
expect(subject).to eq('Accepté')
end
it 'en_instruction is reçu' do
dossier.en_instruction!
expect(subject).to eq('En instruction')
end
it 'sans_suite is traité' do
dossier.sans_suite!
expect(subject).to eq('Sans suite')
end
it 'refuse is traité' do
dossier.refuse!
expect(subject).to eq('Refusé')
end
end
end end

View file

@ -4,5 +4,9 @@ FactoryBot.define do
email { generate(:user_email) } email { generate(:user_email) }
password { 'password' } password { 'password' }
confirmed_at { Time.zone.now } confirmed_at { Time.zone.now }
trait :unconfirmed do
confirmed_at { nil }
end
end end
end end

View file

@ -73,9 +73,6 @@ feature 'The gestionnaire part' do
end end
scenario 'A gestionnaire can use avis' do scenario 'A gestionnaire can use avis' do
ActionMailer::Base.deliveries = []
ActiveJob::Base.queue_adapter = :test
log_in(gestionnaire.email, password) log_in(gestionnaire.email, password)
click_on procedure.libelle click_on procedure.libelle

View file

@ -32,21 +32,17 @@ feature 'Invitations' do
scenario 'an invited user can register using the registration link sent in the invitation email' do scenario 'an invited user can register using the registration link sent in the invitation email' do
# Click the invitation link # Click the invitation link
visit users_dossiers_invite_path(invite.id, params: { email: invite.email }) visit users_dossiers_invite_path(invite.id, params: { email: invite.email })
# Create the account
expect(page).to have_current_path(new_user_registration_path, ignore_query: true) expect(page).to have_current_path(new_user_registration_path, ignore_query: true)
expect(page).to have_field('user_email', with: invite.email) expect(page).to have_field('user_email', with: invite.email)
fill_in 'user_password', with: user_password
click_on 'Créer un compte'
# Create the account
sign_up_with invite.email, user_password
expect(page).to have_content("lien d'activation") expect(page).to have_content("lien d'activation")
# Confirm the email # Confirm the account
user = User.find_by(email: invite.email) # (The user should be redirected to the dossier they was invited on)
visit Rails.application.routes.url_helpers.user_confirmation_path(confirmation_token: user.confirmation_token) click_confirmation_link_for invite.email
submit_login_form(user.email, user_password) expect(page).to have_content('Votre compte a été activé')
# The user should be redirected to the dossier they was invited on
expect(page).to have_current_path(brouillon_dossier_path(dossier)) expect(page).to have_current_path(brouillon_dossier_path(dossier))
end end
end end
@ -102,16 +98,10 @@ feature 'Invitations' do
def log_in(user) def log_in(user)
visit '/' visit '/'
click_on 'Connexion' click_on 'Connexion'
submit_login_form(user.email, user.password) sign_in_with(user.email, user.password)
expect(page).to have_current_path(dossiers_path) expect(page).to have_current_path(dossiers_path)
end end
def submit_login_form(email, password)
fill_in 'user_email', with: email
fill_in 'user_password', with: password
click_on 'Se connecter'
end
def navigate_to_brouillon(dossier) def navigate_to_brouillon(dossier)
expect(page).to have_current_path(dossiers_path) expect(page).to have_current_path(dossiers_path)
click_on(dossier.id) click_on(dossier.id)
@ -127,7 +117,7 @@ feature 'Invitations' do
def navigate_to_invited_dossier(invite) def navigate_to_invited_dossier(invite)
visit users_dossiers_invite_path(invite) visit users_dossiers_invite_path(invite)
expect(page).to have_current_path(new_user_session_path) expect(page).to have_current_path(new_user_session_path)
submit_login_form(invited_user.email, invited_user.password) sign_in_with(invited_user.email, invited_user.password)
end end
def send_invite_to(invited_email) def send_invite_to(invited_email)

View file

@ -0,0 +1,36 @@
require 'spec_helper'
feature 'Signin up:' do
scenario 'a new user can sign-up' do
visit root_path
click_on 'Connexion'
click_on 'Créer un compte'
sign_up_with 'testuser@exemple.fr'
expect(page).to have_content "Nous vous avons envoyé un email contenant un lien d'activation"
click_confirmation_link_for 'testuser@exemple.fr'
expect(page).to have_content 'Votre compte a été activé'
expect(page).to have_current_path dossiers_path
end
context 'when visiting a procedure' do
let(:procedure) { create :simple_procedure }
before do
visit commencer_path(path: procedure.path)
end
scenario 'a new user can sign-up and fill the procedure' do
expect(page).to have_current_path new_user_session_path
click_on 'Créer un compte'
sign_up_with 'testuser@exemple.fr'
expect(page).to have_content "Nous vous avons envoyé un email contenant un lien d'activation"
click_confirmation_link_for 'testuser@exemple.fr'
expect(page).to have_content 'Votre compte a été activé'
expect(page).to have_content procedure.libelle
end
end
end

View file

@ -96,4 +96,108 @@ RSpec.describe DossierHelper, type: :helper do
it_behaves_like "returns false" it_behaves_like "returns false"
end end
end end
describe '.dossier_display_state' do
let(:dossier) { create(:dossier) }
subject { dossier_display_state(dossier) }
it 'brouillon is brouillon' do
dossier.brouillon!
expect(subject).to eq('Brouillon')
end
it 'en_construction is En construction' do
dossier.en_construction!
expect(subject).to eq('En construction')
end
it 'accepte is traité' do
dossier.accepte!
expect(subject).to eq('Accepté')
end
it 'en_instruction is reçu' do
dossier.en_instruction!
expect(subject).to eq('En instruction')
end
it 'sans_suite is traité' do
dossier.sans_suite!
expect(subject).to eq('Sans suite')
end
it 'refuse is traité' do
dossier.refuse!
expect(subject).to eq('Refusé')
end
context "lower: true" do
subject { dossier_display_state(dossier, lower: true) }
it 'brouillon is brouillon' do
dossier.brouillon!
expect(subject).to eq('brouillon')
end
it 'en_construction is En construction' do
dossier.en_construction!
expect(subject).to eq('en construction')
end
it 'accepte is traité' do
dossier.accepte!
expect(subject).to eq('accepté')
end
it 'en_instruction is reçu' do
dossier.en_instruction!
expect(subject).to eq('en instruction')
end
it 'sans_suite is traité' do
dossier.sans_suite!
expect(subject).to eq('sans suite')
end
it 'refuse is traité' do
dossier.refuse!
expect(subject).to eq('refusé')
end
end
end
describe '.dossier_legacy_state' do
subject { dossier_legacy_state(dossier) }
context 'when the dossier is en instruction' do
let(:dossier) { create(:dossier) }
it { is_expected.to eq('brouillon') }
end
context 'when the dossier is en instruction' do
let(:dossier) { create(:dossier, :en_instruction) }
it { is_expected.to eq('received') }
end
context 'when the dossier is accepte' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:accepte)) }
it { is_expected.to eq('closed') }
end
context 'when the dossier is refuse' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:refuse)) }
it { is_expected.to eq('refused') }
end
context 'when the dossier is sans_suite' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:sans_suite)) }
it { is_expected.to eq('without_continuation') }
end
end
end end

View file

@ -1,25 +0,0 @@
require 'rails_helper'
RSpec.describe TypeDeChampHelper, type: :helper do
describe ".tdc_options" do
let(:pj_option) { ["Pièce justificative", TypeDeChamp.type_champs.fetch(:piece_justificative)] }
subject { tdc_options }
context "when the champ_pj is enabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, true)
end
it { is_expected.to include(pj_option) }
end
context "when the champ_pj is disabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, false)
end
it { is_expected.not_to include(pj_option) }
end
end
end

View file

@ -600,10 +600,6 @@ describe Dossier do
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do
ActionMailer::Base.deliveries.clear
end
it "send an email when the dossier is created for the very first time" do it "send an email when the dossier is created for the very first time" do
dossier = nil dossier = nil
ActiveJob::Base.queue_adapter = :test ActiveJob::Base.queue_adapter = :test
@ -1004,32 +1000,4 @@ describe Dossier do
it { expect(long_expired_dossier).to be_retention_expired } it { expect(long_expired_dossier).to be_retention_expired }
end end
end end
describe 'old_state_value' do
subject { dossier.old_state_value }
context 'when the dossier is en instruction' do
let(:dossier) { create(:dossier, :en_instruction) }
it { is_expected.to eq('received') }
end
context 'when the dossier is accepte' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:accepte)) }
it { is_expected.to eq('closed') }
end
context 'when the dossier is refuse' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:refuse)) }
it { is_expected.to eq('refused') }
end
context 'when the dossier is sans_suite' do
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:sans_suite)) }
it { is_expected.to eq('without_continuation') }
end
end
end end

View file

@ -2,10 +2,12 @@ require 'spec_helper'
describe TypesDeChampService do describe TypesDeChampService do
let(:params) { ActionController::Parameters.new({ procedure: { types_de_champ_attributes: types_de_champ_attributes } }) } let(:params) { ActionController::Parameters.new({ procedure: { types_de_champ_attributes: types_de_champ_attributes } }) }
let(:procedure) { create(:procedure) }
let(:service) { TypesDeChampService.new(procedure) }
let(:result) { TypesDeChampService.create_update_procedure_params(params) } describe 'create_update_procedure_params' do
let(:result) { service.create_update_procedure_params(params) }
describe 'self.create_update_procedure_params' do
describe 'the drop down list attributes' do describe 'the drop down list attributes' do
let(:types_de_champ_attributes) do let(:types_de_champ_attributes) do
{ {
@ -99,4 +101,26 @@ describe TypesDeChampService do
end end
end end
end end
describe ".options" do
let(:pj_option) { ["Pièce justificative", TypeDeChamp.type_champs.fetch(:piece_justificative)] }
subject { service.options }
context "when the champ_pj is enabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, true)
end
it { is_expected.to include(pj_option) }
end
context "when the champ_pj is disabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, false)
end
it { is_expected.not_to include(pj_option) }
end
end
end end

View file

@ -23,6 +23,7 @@ require File.expand_path('../config/environment', __dir__)
require 'rspec/rails' require 'rspec/rails'
require 'capybara/rspec' require 'capybara/rspec'
require 'capybara-screenshot/rspec' require 'capybara-screenshot/rspec'
require 'capybara/email/rspec'
require 'database_cleaner' require 'database_cleaner'
require 'webmock/rspec' require 'webmock/rspec'
require 'shoulda-matchers' require 'shoulda-matchers'
@ -137,6 +138,8 @@ RSpec.configure do |config|
Typhoeus::Expectation.clear Typhoeus::Expectation.clear
ActionMailer::Base.deliveries.clear
if Flipflop.remote_storage? if Flipflop.remote_storage?
VCR.use_cassette("ovh_storage_init") do VCR.use_cassette("ovh_storage_init") do
CarrierWave.configure do |config| CarrierWave.configure do |config|

View file

@ -1,4 +1,6 @@
module FeatureHelpers module FeatureHelpers
include ActiveJob::TestHelper
def login_admin def login_admin
user = create :user user = create :user
login_as user, scope: :user login_as user, scope: :user
@ -14,6 +16,28 @@ module FeatureHelpers
dossier = FactoryBot.create(:dossier) dossier = FactoryBot.create(:dossier)
dossier dossier
end end
def sign_in_with(email, password)
fill_in :user_email, with: email
fill_in :user_password, with: password
click_on 'Se connecter'
end
def sign_up_with(email, password = 'testpassword')
fill_in :user_email, with: email
fill_in :user_password, with: password
perform_enqueued_jobs do
click_button 'Créer un compte'
end
end
def click_confirmation_link_for(email)
confirmation_email = open_email(email)
token_params = confirmation_email.body.match(/confirmation_token=[^"]+/)
visit "/users/confirmation?#{token_params}"
end
end end
RSpec.configure do |config| RSpec.configure do |config|

View file

@ -14,7 +14,7 @@ describe 'admin/types_de_champ/show.html.haml', type: :view do
before do before do
procedure.reload procedure.reload
assign(:procedure, procedure) assign(:procedure, procedure)
assign(:types_de_champ_facade, AdminTypesDeChampFacades.new(false, procedure)) assign(:type_de_champ_service, TypesDeChampService.new(procedure))
render render
end end
it 'sorts by order place' do it 'sorts by order place' do
@ -26,7 +26,7 @@ describe 'admin/types_de_champ/show.html.haml', type: :view do
subject do subject do
procedure.reload procedure.reload
assign(:procedure, procedure) assign(:procedure, procedure)
assign(:types_de_champ_facade, AdminTypesDeChampFacades.new(false, procedure)) assign(:type_de_champ_service, TypesDeChampService.new(procedure))
render render
rendered rendered
end end

View file

@ -14,7 +14,7 @@ describe 'admin/types_de_champ/show.html.haml', type: :view do
before do before do
procedure.reload procedure.reload
assign(:procedure, procedure) assign(:procedure, procedure)
assign(:types_de_champ_facade, AdminTypesDeChampFacades.new(true, procedure)) assign(:type_de_champ_service, TypesDeChampService.new(procedure, true))
render render
end end
it 'sorts by order place' do it 'sorts by order place' do
@ -26,7 +26,7 @@ describe 'admin/types_de_champ/show.html.haml', type: :view do
subject do subject do
procedure.reload procedure.reload
assign(:procedure, procedure) assign(:procedure, procedure)
assign(:types_de_champ_facade, AdminTypesDeChampFacades.new(true, procedure)) assign(:type_de_champ_service, TypesDeChampService.new(procedure, true))
render render
rendered rendered
end end