Merge branch 'develop' into staging

This commit is contained in:
Xavier J 2016-11-10 15:57:27 +01:00
commit fddf118884
32 changed files with 577 additions and 173 deletions

View file

@ -62,6 +62,7 @@ gem 'fog'
gem 'fog-openstack'
gem 'pg'
gem 'scenic'
gem 'rgeo-geojson'
gem 'leaflet-rails'

View file

@ -482,6 +482,9 @@ GEM
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
scenic (1.3.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
@ -635,6 +638,7 @@ DEPENDENCIES
rubocop-checkstyle_formatter
rubocop-rspec
sass-rails (~> 5.0)
scenic
sdoc (~> 0.4.0)
selenium-webdriver
sentry-raven
@ -654,4 +658,4 @@ DEPENDENCIES
will_paginate-bootstrap
BUNDLED WITH
1.12.5
1.13.2

View file

@ -0,0 +1,9 @@
$(document).on('page:load', link_init);
$(document).ready(link_init);
function link_init() {
$('#dossiers_list tr').on('click', function () {
$(location).attr('href', $(this).data('dossier_url'))
});
}

View file

@ -27,4 +27,9 @@ h5 span {
#validate_button {
float: right;
}
}
#dossiers_list tr:hover{
background-color: #eeeeee;
cursor: pointer;
}

View file

@ -27,7 +27,12 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
def search
@search_terms = params[:q]
@dossier = Dossier.search(current_gestionnaire, @search_terms)
@dossier = Search.new(
gestionnaire: current_gestionnaire,
query: @search_terms,
page: params[:page]
).results
smartlisting_dossier @dossier, 'search'

View file

@ -58,6 +58,10 @@ class DossiersListFacades
(@liste == 'a_traiter' ? 'active' : '')
end
def fige_class
(@liste == 'fige' ? 'active' : '')
end
def en_attente_class
(@liste == 'en_attente' ? 'active' : '')
end
@ -103,13 +107,17 @@ class DossiersListFacades
end
def a_traiter_total
service.waiting_for_gestionnaire.count
service.ouvert.count
end
def en_construction_total
service.en_construction.count
end
def fige_total
service.fige.count
end
def en_attente_total
service.waiting_for_user.count
end
@ -158,6 +166,10 @@ class DossiersListFacades
base_url 'a_traiter'
end
def fige_url
base_url 'fige'
end
def en_attente_url
base_url 'en_attente'
end

View file

@ -45,6 +45,7 @@ class Dossier < ActiveRecord::Base
BROUILLON = %w(draft)
NOUVEAUX = %w(initiated)
OUVERT = %w(updated replied)
WAITING_FOR_GESTIONNAIRE = %w(updated)
WAITING_FOR_USER = %w(replied validated)
EN_CONSTRUCTION = %w(initiated updated replied)
@ -194,6 +195,10 @@ class Dossier < ActiveRecord::Base
EN_CONSTRUCTION.include?(state)
end
def ouvert?
OUVERT.include?(state)
end
def deposes?
DEPOSES.include?(state)
end
@ -202,6 +207,10 @@ class Dossier < ActiveRecord::Base
VALIDES.include?(state)
end
def fige?
VALIDES.include?(state)
end
def a_instruire?
A_INSTRUIRE.include?(state)
end
@ -238,10 +247,18 @@ class Dossier < ActiveRecord::Base
where(state: EN_CONSTRUCTION, archived: false).order("updated_at #{order}")
end
def self.ouvert order = 'ASC'
where(state: OUVERT, archived: false).order("updated_at #{order}")
end
def self.valides order = 'ASC'
where(state: VALIDES, archived: false).order("updated_at #{order}")
end
def self.fige order = 'ASC'
where(state: VALIDES, archived: false).order("updated_at #{order}")
end
def self.deposes order = 'ASC'
where(state: DEPOSES, archived: false).order("updated_at #{order}")
end
@ -258,37 +275,6 @@ class Dossier < ActiveRecord::Base
where(state: TERMINE, archived: false).order("updated_at #{order}")
end
def self.search current_gestionnaire, terms
return [] if terms.blank?
dossiers = Dossier.arel_table
users = User.arel_table
etablissements = Etablissement.arel_table
entreprises = Entreprise.arel_table
composed_scope = self.joins('LEFT OUTER JOIN users ON users.id = dossiers.user_id')
.joins('LEFT OUTER JOIN entreprises ON entreprises.dossier_id = dossiers.id')
.joins('LEFT OUTER JOIN etablissements ON etablissements.dossier_id = dossiers.id')
terms.split.each do |word|
query_string = "%#{word}%"
query_string_start_with = "#{word}%"
composed_scope = composed_scope.where(
users[:email].matches(query_string).or\
etablissements[:siret].matches(query_string_start_with).or\
entreprises[:raison_sociale].matches(query_string).or\
dossiers[:id].eq(word_is_an_integer word))
end
composed_scope = composed_scope.where(
dossiers[:id].eq_any(current_gestionnaire.dossiers.ids).and\
dossiers[:state].does_not_match('draft').and\
dossiers[:archived].eq(false))
composed_scope
end
def cerfa_available?
procedure.cerfa_flag? && cerfa.size != 0
end

82
app/models/search.rb Normal file
View file

@ -0,0 +1,82 @@
# See:
# - https://robots.thoughtbot.com/implementing-multi-table-full-text-search-with-postgres
# - http://calebthompson.io/talks/search.html
class Search < ActiveRecord::Base
# :nodoc:
#
# Englobs a search result (actually a collection of Search objects) so it acts
# like a collection of regular Dossier objects, which can be decorated,
# paginated, ...
class Results
include Enumerable
def initialize(results)
@results = results
end
def each
@results.each do |search|
yield search.dossier
end
end
def method_missing(name, *args, &block)
@results.__send__(name, *args, &block)
end
def decorate!
@results.each do |search|
search.dossier = search.dossier.decorate
end
end
end
attr_accessor :gestionnaire
attr_accessor :query
attr_accessor :page
belongs_to :dossier
def results
unless @query.present?
return Search.none
end
search_term = Search.connection.quote(to_tsquery)
dossier_ids = @gestionnaire.dossiers
.select(:id)
.where(archived: false)
.where.not(state: "draft")
q = Search
.select("DISTINCT(searches.dossier_id)")
.select("COALESCE(ts_rank(to_tsvector('french', searches.term::text), to_tsquery('french', #{search_term})), 0) AS rank")
.joins(:dossier)
.where(dossier_id: dossier_ids)
.where("to_tsvector('french', searches.term::text) @@ to_tsquery('french', #{search_term})")
.order("rank DESC")
.preload(:dossier)
if @page.present?
q = q.paginate(page: @page)
end
Results.new(q)
end
#def self.refresh
# # TODO: could be executed concurrently
# # See https://github.com/thoughtbot/scenic#what-about-materialized-views
# Scenic.database.refresh_materialized_view(table_name, concurrently: false)
#end
private
def to_tsquery
@query.gsub(/['?\\:&|!]/, "") # drop disallowed characters
.split(/\s+/) # split words
.map { |x| "#{x}:*" } # enable prefix matching
.join(" & ")
end
end

View file

@ -7,8 +7,8 @@ class DossiersListGestionnaireService
def dossiers_to_display
{'nouveaux' => nouveaux,
'a_traiter' => waiting_for_gestionnaire,
'en_attente' => waiting_for_user,
'a_traiter' => ouvert,
'fige' => fige,
'deposes' => deposes,
'a_instruire' => a_instruire,
'termine' => termine,
@ -16,7 +16,7 @@ class DossiersListGestionnaireService
end
def self.dossiers_liste_libelle
['nouveaux', 'a_traiter', 'en_attente', 'deposes', 'a_instruire', 'termine', 'all_state']
['nouveaux', 'a_traiter', 'fige' ,'deposes', 'a_instruire', 'termine', 'all_state']
end
def all_state
@ -27,12 +27,12 @@ class DossiersListGestionnaireService
@nouveaux ||= filter_dossiers.nouveaux
end
def waiting_for_gestionnaire
@waiting_for_gestionnaire ||= filter_dossiers.waiting_for_gestionnaire
def ouvert
@ouvert ||= filter_dossiers.ouvert
end
def waiting_for_user
@waiting_for_user ||= filter_dossiers.waiting_for_user
def fige
@fige ||= filter_dossiers.fige
end
def deposes

View file

@ -1,4 +1,4 @@
%table.table
%table#dossiers_list.table
%thead
- @dossiers_list_facade.preference_list_dossiers_filter.each do |preference|
%th{class: "col-md-#{preference.bootstrap_lg} col-lg-#{preference.bootstrap_lg}"}
@ -15,7 +15,7 @@
- unless smart_listing.empty?
- @dossiers.each do |dossier|
%tr
%tr{id: "tr_dossier_#{dossier.id}", 'data-dossier_url' => backoffice_dossier_url(id: dossier.id)}
- @dossiers_list_facade.preference_list_dossiers_filter.each_with_index do |preference, index|
%td
- if preference.table.nil? || preference.table.empty?
@ -30,10 +30,7 @@
- rescue NoMethodError
- value = ''
- if index == 0
= link_to value, backoffice_dossier_path(id: dossier.id)
- else
= value
= value
%td.center
- if current_gestionnaire.follow?(dossier.id)
@ -48,4 +45,4 @@
- if smart_listing.empty?
%h4.center
Aucun dossier
Aucun dossier

View file

@ -19,18 +19,18 @@
=@dossiers_list_facade.nouveaux_total
%li{ class: (@dossiers_list_facade.a_traiter_class) }
%a{:href => "#{url_for @dossiers_list_facade.a_traiter_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui requièrent une action de votre part.'}
%a{:href => "#{url_for @dossiers_list_facade.a_traiter_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ne sont pas encore déclarés complets.'}
%h5.text-danger
= "Action requise"
="Ouverts"
.badge.progress-bar-danger
=@dossiers_list_facade.a_traiter_total
%li{ class: (@dossiers_list_facade.en_attente_class) }
%a{:href => "#{url_for @dossiers_list_facade.en_attente_url}", 'data-toggle' => :tooltip, title: 'Les dossiers en attentes d\'une action de la part de l\'usager.'}
%li{ class: (@dossiers_list_facade.fige_class) }
%a{:href => "#{url_for @dossiers_list_facade.fige_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui sont déclarés complets et donc figés.'}
%h5.text-default
="Attente usager "
="Figés"
.badge.progress-bar-default
=@dossiers_list_facade.en_attente_total
=@dossiers_list_facade.fige_total
%li{ class: (@dossiers_list_facade.deposes_class) }
%a{:href => "#{url_for @dossiers_list_facade.deposes_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ont été validés et déposés par les usager qui attendent une réponse de bonne réception avant examen.'}

View file

@ -11,12 +11,18 @@
n'ont jamais été ouvert par votre service.
Il attende une première lecture et intervention de votre part.
-elsif dossiers_list_facade.liste == 'a_traiter'
Tous les dossiers présents dans cette liste sont ceux qui
Tous les dossiers présents dans cette liste sont ceux qui sont
%b
attendent une action de votre part.
Cela peut être par exemple une demande client ou une relecture pour validation de complétude.
-elsif dossiers_list_facade.liste == 'en_attente'
Tous les dossiers présents dans cette liste sont ceux qui requière une action de la part de l'usager. À priori, vous n'avez donc pas d'intervention particulière à réaliser.
en cours de construction avec l'usager.
Ils ne sont pas figés et ne sont donc pas complets.
-elsif dossiers_list_facade.liste == 'fige'
Tous les dossiers présents dans cette liste ont été déclarés
%b
complets
et ne sont
%b
plus modifiables par l'usager.
Ils attendent donc leurs dépots officiels qui doit être effectué par l'usager.
-elsif dossiers_list_facade.liste == 'deposes'
Tous les dossiers présents dans cette liste ont été
%b

View file

@ -1,3 +1,4 @@
<%= smart_listing_update :dossiers %>
filters_init();
filters_init();
link_init();

View file

@ -2,7 +2,8 @@
%html
%head
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
%title TPS - Téléprocédures simplifiées
%title
=t('dynamics.page_title')
%meta{'http-equiv' => "X-UA-Compatible", :content => "IE=edge"}
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true

View file

@ -1,5 +1,5 @@
- unless smart_listing.empty?
%table.table
%table#dossiers_list.table
%thead
%th.col-md-1.col-lg-1= smart_listing.sortable 'Numéro', 'id'
%th.col-md-5.col-lg-5= smart_listing.sortable 'Procédure', 'procedure.libelle'
@ -13,14 +13,17 @@
-dossier = invite.dossier.decorate
- else
- dossier = dossier.decorate
%tr
- dossier_url = users_dossiers_invite_path(id: invite.id) unless invite.nil?
- if invite.nil?
- dossier_url = users_dossier_recapitulatif_path(dossier) unless dossier.brouillon?
- dossier_url = users_dossier_description_path(dossier) if dossier.brouillon?
%tr{id: "tr_dossier_#{dossier.id}", 'data-dossier_url' => dossier_url}
%td.center
= dossier.id
%td
= link_to(dossier.procedure.libelle, users_dossiers_invite_path(id: invite.id)) unless invite.nil?
- if invite.nil?
= link_to(dossier.procedure.libelle, users_dossier_recapitulatif_path(dossier)) unless dossier.brouillon?
= link_to(dossier.procedure.libelle, users_dossier_description_path(dossier)) if dossier.brouillon?
= dossier.procedure.libelle
%td{id: "dossier_#{dossier.id}_state"}= dossier.display_state
%td= dossier.last_update

View file

@ -1 +1,2 @@
<%= smart_listing_update :dossiers %>
link_init();

View file

@ -98,7 +98,8 @@ set :shared_paths, [
'config/france_connect.yml',
'config/initializers/mailjet.rb',
'config/initializers/storage_url.rb',
'app/views/root/landing.html.haml'
'app/views/root/landing.html.haml',
'app/views/cgu/index.html.haml'
]
@ -139,6 +140,9 @@ task :setup => :environment do
queue! %[mkdir -p "#{deploy_to}/shared/app"]
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/app"]
queue! %[mkdir -p "#{deploy_to}/shared/views/cgu"]
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/views/cgu"]
queue! %[mkdir -p "#{deploy_to}/shared/config/locales/dynamics"]
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config/locales/dynamics"]

View file

@ -1,5 +1,6 @@
fr:
dynamics:
page_title: TPS - Téléprocédures simplifiées
contact_email: contact@tps.apientreprise.fr
users:
connexion_title: Connexion

View file

@ -1,5 +1,6 @@
fr_opensimplif:
dynamics:
page_title: OpenSimplif
contact_email: simplification.sgmap@modernisation.gouv.fr
users:
connexion_title: "Connectez-vous en tant qu'utilisateur"

View file

@ -0,0 +1,27 @@
class CreateSearches < ActiveRecord::Migration
def up
add_index :champs, :dossier_id
add_index :champs, :type_de_champ_id
add_index :drop_down_lists, :type_de_champ_id
add_index :etablissements, :dossier_id
add_index :entreprises, :dossier_id
add_index :france_connect_informations, :user_id
add_index :individuals, :dossier_id
add_index :pieces_justificatives, :dossier_id
add_index :rna_informations, :entreprise_id
create_view :searches #, materialized: true
end
def down
remove_index :champs, :dossier_id
remove_index :champs, :type_de_champ_id
remove_index :drop_down_lists, :type_de_champ_id
remove_index :etablissements, :dossier_id
remove_index :entreprises, :dossier_id
remove_index :france_connect_informations, :user_id
remove_index :individuals, :dossier_id
remove_index :pieces_justificatives, :dossier_id
remove_index :rna_informations, :entreprise_id
drop_view :searches #, materialized: true
end
end

View file

@ -0,0 +1,9 @@
class UpdateSearchesToVersion2 < ActiveRecord::Migration
def up
replace_view :searches, version: 2
end
def down
replace_view :searches, version: 1
end
end

View file

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161011125345) do
ActiveRecord::Schema.define(version: 20161102154835) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -102,6 +102,9 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.string "type"
end
add_index "champs", ["dossier_id"], name: "index_champs_on_dossier_id", using: :btree
add_index "champs", ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id", using: :btree
create_table "commentaires", force: :cascade do |t|
t.string "email"
t.datetime "created_at", null: false
@ -135,6 +138,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.integer "type_de_champ_id"
end
add_index "drop_down_lists", ["type_de_champ_id"], name: "index_drop_down_lists_on_type_de_champ_id", using: :btree
create_table "entreprises", force: :cascade do |t|
t.string "siren"
t.integer "capital_social"
@ -151,6 +156,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.integer "dossier_id"
end
add_index "entreprises", ["dossier_id"], name: "index_entreprises_on_dossier_id", using: :btree
create_table "etablissements", force: :cascade do |t|
t.string "siret"
t.boolean "siege_social"
@ -168,6 +175,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.integer "entreprise_id"
end
add_index "etablissements", ["dossier_id"], name: "index_etablissements_on_dossier_id", using: :btree
create_table "exercices", force: :cascade do |t|
t.string "ca"
t.datetime "dateFinExercice"
@ -194,6 +203,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.string "email_france_connect"
end
add_index "france_connect_informations", ["user_id"], name: "index_france_connect_informations_on_user_id", using: :btree
create_table "gestionnaires", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
@ -221,6 +232,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.string "gender"
end
add_index "individuals", ["dossier_id"], name: "index_individuals_on_dossier_id", using: :btree
create_table "invites", force: :cascade do |t|
t.string "email"
t.string "email_sender"
@ -255,6 +268,7 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.string "content_secure_token"
end
add_index "pieces_justificatives", ["dossier_id"], name: "index_pieces_justificatives_on_dossier_id", using: :btree
add_index "pieces_justificatives", ["type_de_piece_justificative_id"], name: "index_pieces_justificatives_on_type_de_piece_justificative_id", using: :btree
create_table "preference_list_dossiers", force: :cascade do |t|
@ -324,6 +338,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
t.integer "entreprise_id"
end
add_index "rna_informations", ["entreprise_id"], name: "index_rna_informations_on_entreprise_id", using: :btree
create_table "types_de_champ", force: :cascade do |t|
t.string "libelle"
t.string "type_champ"
@ -369,4 +385,21 @@ ActiveRecord::Schema.define(version: 20161011125345) do
add_foreign_key "dossiers", "users"
add_foreign_key "procedure_paths", "administrateurs"
add_foreign_key "procedure_paths", "procedures"
create_view :searches, sql_definition: <<-SQL
SELECT dossiers.id AS dossier_id,
(((((((((((((((((((((((((((((((((((((((((((((((((((((((COALESCE(users.email, ''::character varying))::text || ' '::text) || (COALESCE(france_connect_informations.given_name, ''::character varying))::text) || ' '::text) || (COALESCE(france_connect_informations.family_name, ''::character varying))::text) || ' '::text) || (COALESCE(cerfas.content, ''::character varying))::text) || ' '::text) || (COALESCE(champs.value, ''::character varying))::text) || ' '::text) || (COALESCE(drop_down_lists.value, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siren, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.numero_tva_intracommunautaire, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique_code, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom_commercial, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.raison_sociale, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siret_siege_social, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.association_id, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.titre, ''::character varying))::text) || ' '::text) || COALESCE(rna_informations.objet, ''::text)) || ' '::text) || (COALESCE(etablissements.siret, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.libelle_naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.adresse, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_postal, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.localite, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_insee_localite, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.nom, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(pieces_justificatives.content, ''::character varying))::text) AS term
FROM ((((((((((dossiers
JOIN users ON ((users.id = dossiers.user_id)))
LEFT JOIN france_connect_informations ON ((france_connect_informations.user_id = dossiers.user_id)))
LEFT JOIN cerfas ON ((cerfas.dossier_id = dossiers.id)))
LEFT JOIN champs ON ((champs.dossier_id = dossiers.id)))
LEFT JOIN drop_down_lists ON ((drop_down_lists.type_de_champ_id = champs.type_de_champ_id)))
LEFT JOIN entreprises ON ((entreprises.dossier_id = dossiers.id)))
LEFT JOIN rna_informations ON ((rna_informations.entreprise_id = entreprises.id)))
LEFT JOIN etablissements ON ((etablissements.dossier_id = dossiers.id)))
LEFT JOIN individuals ON ((individuals.dossier_id = dossiers.id)))
LEFT JOIN pieces_justificatives ON ((pieces_justificatives.dossier_id = dossiers.id)));
SQL
end

59
db/views/searches_v01.sql Normal file
View file

@ -0,0 +1,59 @@
-- this version allows to search for a single term within many tables,
-- but behaves badly with multiple terms scattered in multiple tables.
SELECT dossiers.id AS dossier_id,
dossiers.id::text || ' ' ||
COALESCE(users.email, '') AS term
FROM dossiers
INNER JOIN users ON users.id = dossiers.user_id
UNION SELECT cerfas.dossier_id,
COALESCE(cerfas.content, '') AS term
FROM cerfas
UNION SELECT champs.dossier_id,
COALESCE(champs.value, '') || ' ' ||
COALESCE(drop_down_lists.value, '') AS term
FROM champs
INNER JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id
UNION SELECT entreprises.dossier_id,
COALESCE(entreprises.siren, '') || ' ' ||
COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' ||
COALESCE(entreprises.forme_juridique, '') || ' ' ||
COALESCE(entreprises.forme_juridique_code, '') || ' ' ||
COALESCE(entreprises.nom_commercial, '') || ' ' ||
COALESCE(entreprises.raison_sociale, '') || ' ' ||
COALESCE(entreprises.siret_siege_social, '') || ' ' ||
COALESCE(entreprises.nom, '') || ' ' ||
COALESCE(entreprises.prenom, '') || ' ' ||
COALESCE(rna_informations.association_id, '') || ' ' ||
COALESCE(rna_informations.titre, '') || ' ' ||
COALESCE(rna_informations.objet, '') AS term
FROM entreprises
LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id
UNION SELECT etablissements.dossier_id,
COALESCE(etablissements.siret, '') || ' ' ||
COALESCE(etablissements.naf, '') || ' ' ||
COALESCE(etablissements.libelle_naf, '') || ' ' ||
COALESCE(etablissements.adresse, '') || ' ' ||
COALESCE(etablissements.code_postal, '') || ' ' ||
COALESCE(etablissements.localite, '') || ' ' ||
COALESCE(etablissements.code_insee_localite, '') AS term
FROM etablissements
UNION SELECT individuals.dossier_id,
COALESCE(individuals.nom, '') || ' ' ||
COALESCE(individuals.prenom, '') AS term
FROM individuals
UNION SELECT pieces_justificatives.dossier_id,
COALESCE(pieces_justificatives.content, '') AS term
FROM pieces_justificatives
UNION SELECT dossiers.id,
COALESCE(france_connect_informations.given_name, '') || ' ' ||
COALESCE(france_connect_informations.family_name, '') AS term
FROM france_connect_informations
INNER JOIN dossiers ON dossiers.user_id = france_connect_informations.user_id

43
db/views/searches_v02.sql Normal file
View file

@ -0,0 +1,43 @@
-- this version merges all possible search terms together, complicating the
-- view, but enables searching for multiple terms from multiple tables at once.
SELECT dossiers.id AS dossier_id,
COALESCE(users.email, '') || ' ' ||
COALESCE(france_connect_informations.given_name, '') || ' ' ||
COALESCE(france_connect_informations.family_name, '') || ' ' ||
COALESCE(cerfas.content, '') || ' ' ||
COALESCE(champs.value, '') || ' ' ||
COALESCE(drop_down_lists.value, '') || ' ' ||
COALESCE(entreprises.siren, '') || ' ' ||
COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' ||
COALESCE(entreprises.forme_juridique, '') || ' ' ||
COALESCE(entreprises.forme_juridique_code, '') || ' ' ||
COALESCE(entreprises.nom_commercial, '') || ' ' ||
COALESCE(entreprises.raison_sociale, '') || ' ' ||
COALESCE(entreprises.siret_siege_social, '') || ' ' ||
COALESCE(entreprises.nom, '') || ' ' ||
COALESCE(entreprises.prenom, '') || ' ' ||
COALESCE(rna_informations.association_id, '') || ' ' ||
COALESCE(rna_informations.titre, '') || ' ' ||
COALESCE(rna_informations.objet, '') || ' ' ||
COALESCE(etablissements.siret, '') || ' ' ||
COALESCE(etablissements.naf, '') || ' ' ||
COALESCE(etablissements.libelle_naf, '') || ' ' ||
COALESCE(etablissements.adresse, '') || ' ' ||
COALESCE(etablissements.code_postal, '') || ' ' ||
COALESCE(etablissements.localite, '') || ' ' ||
COALESCE(etablissements.code_insee_localite, '') || ' ' ||
COALESCE(individuals.nom, '') || ' ' ||
COALESCE(individuals.prenom, '') || ' ' ||
COALESCE(pieces_justificatives.content, '') AS term
FROM dossiers
INNER JOIN users ON users.id = dossiers.user_id
LEFT JOIN france_connect_informations ON france_connect_informations.user_id = dossiers.user_id
LEFT JOIN cerfas ON cerfas.dossier_id = dossiers.id
LEFT JOIN champs ON champs.dossier_id = dossiers.id
LEFT JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id
LEFT JOIN entreprises ON entreprises.dossier_id = dossiers.id
LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id
LEFT JOIN etablissements ON etablissements.dossier_id = dossiers.id
LEFT JOIN individuals ON individuals.dossier_id = dossiers.id
LEFT JOIN pieces_justificatives ON pieces_justificatives.dossier_id = dossiers.id

View file

@ -0,0 +1,118 @@
require 'csv'
require 'json'
namespace :opensimplif_import do
task :import_all => :environment do
puts 'start opensimplif'
Rake::Task['opensimplif_import:import_proposition'].invoke
Rake::Task['opensimplif_import:import_piste'].invoke
Rake::Task['opensimplif_import:import_mesure'].invoke
puts 'end import opensimplif'
end
task :import_proposition do
file_path = "lib/tasks/161102_OS_Inputs_test_propositions.csv"
procedure_id = 35
matching = [
{id: 44, key: 'Intitulé de la proposition'},
{id: 43, key: 'Champ concerné'},
{id: 45, key: 'Champ ministériel chef de file'},
{id: 59, key: 'Date de la proposition'},
{id: 60, key: 'Moment de vie'},
{id: 61, key: 'Source'},
{id: 48, key: 'Description de la proposition'}
]
puts 'start propositions'
import file_path, procedure_id, matching
puts 'done propositions'
end
task :import_piste do
file_path = "lib/tasks/161102_OS_Inputs_test_pistes.csv"
procedure_id = 36
matching = [
{id: 81, key: 'Intitulé de la piste *'},
{id: 82, key: 'Usager concerné *'},
{id: 83, key: 'Champ ministériel chef de file *'},
{id: 84, key: 'Champ ministériel contributeur'},
{id: 85, key: 'Date de saisine'},
{id: 66, key: 'Moment de vie'},
{id: 80, key: 'Source de la piste'},
{id: 70, key: 'Description de la piste '},
{id: 68, key: 'Objectifs / bénéfices attendus'},
{id: 65, key: 'Description détaillée des démarches impactées par la piste'},
{id: 69, key: 'Levier de mise en oeuvre'},
{id: 67, key: 'Précision sur le levier de meo'},
{id: 64, key: 'Calendrier de mise en oeuvre'}
]
puts 'start piste'
import file_path, procedure_id, matching
puts 'done pistes'
end
task :import_mesure do
file_path = "lib/tasks/161102_OS_Inputs_test_mesures.csv"
procedure_id = 37
matching = [
{id: 107, key: 'Intitulé projet / mesure'},
{id: 104, key: 'Champ concerné'},
{id: 105, key: 'Champ ministériel chef de file'},
{id: 112, key: 'Direction chef de file'},
{id: 106, key: 'Champ ministériel contributeur'},
{id: 113, key: 'Direction contributrice'},
{id: 92, key: 'Moment de vie'},
{id: 109, key: 'Date d\'annonce'},
{id: 114, key: 'N° de la mesure'},
{id: 115, key: 'Responsable ministère'},
{id: 116, key: 'Responsable SGMAP'},
{id: 89, key: 'Actions réalisées'},
{id: 95, key: 'Etapes nécessaires à l\'atteinte de la cible et alertes'},
{id: 102, key: 'Alertes'},
{id: 101, key: 'Échéance initiale'},
{id: 96, key: 'Échéance prévisionnelle / réelle'},
{id: 94, key: 'Appréciation avancement'},
{id: 91, key: 'Etat d\'avancement LOLF'},
{id: 111, key: '§ de com'}
]
puts 'start mesures'
import file_path, procedure_id, matching
puts 'done mesures'
end
def self.import file_path, procedure_id, matching
user = User.find_or_create_by(email: 'import@opensimplif.modernisation.fr')
unless user.valid?
user.password = 'TPSpassword2016'
user.save
end
file ||= CSV.open(file_path, :col_sep => ";", :headers => true).map { |x| x.to_h }.to_json
file = JSON.parse(file)
procedure = Procedure.find(procedure_id)
user.dossiers.where(procedure_id: procedure.id).destroy_all
file.each do |proposition|
dossier = Dossier.create procedure: procedure, user: user, state: :initiated
dossier.champs.each do |champ|
matching.each do |match|
if match[:id] == champ.type_de_champ.id
champ.update_column :value, proposition[match[:key]]
break
end
end
end
end
end
end

View file

@ -63,14 +63,14 @@ describe Backoffice::DossiersController, type: :controller do
end
end
describe 'GET #en_attente' do
describe 'GET #fige' do
context 'when gestionnaire is connected' do
before do
sign_in gestionnaire
end
it 'returns http success' do
get :index, liste: :en_attente
get :index, liste: :fige
expect(response).to have_http_status(200)
end
end

View file

@ -20,9 +20,9 @@ feature 'on backoffice page' do
page.click_on 'Se connecter'
end
context 'when he click on first dossier' do
context 'when he click on first dossier', js: true do
before do
page.click_on dossier.id
page.find("#tr_dossier_#{dossier.id}").click
end
scenario 'it redirect to dossier page' do
@ -30,7 +30,7 @@ feature 'on backoffice page' do
end
end
context 'when gestionnaire have enterprise and individual dossier in his inbox' do
context 'when gestionnaire have enterprise and individual dossier in his inbox', js: true do
let!(:procedure_individual) { create :procedure, libelle: 'procedure individual', administrateur: administrateur, for_individual: true }
let!(:dossier_individual) { create :dossier, procedure: procedure_individual, state: 'updated' }
@ -38,7 +38,7 @@ feature 'on backoffice page' do
create :assign_to, gestionnaire: gestionnaire, procedure: procedure_individual
visit backoffice_path
page.click_on dossier_individual.id
page.find("#tr_dossier_#{dossier_individual.id}").click
end
scenario 'it redirect to dossier page' do

View file

@ -37,7 +37,7 @@ feature 'on click on tabs button' do
context 'when he click on tabs a traite' do
before do
visit backoffice_dossiers_url(liste: :a_traiter)
page.click_on 'Action requise 1'
page.click_on 'Ouverts 2'
end
scenario 'it redirect to backoffice dossier termine' do
@ -47,8 +47,8 @@ feature 'on click on tabs button' do
context 'when he click on tabs en attente' do
before do
visit backoffice_dossiers_url(liste: :en_attente)
page.click_on 'Attente usager 2'
visit backoffice_dossiers_url(liste: :figes)
page.click_on 'Figés 1'
end
scenario 'it redirect to backoffice dossier en attente' do

View file

@ -32,9 +32,9 @@ feature 'user access to the list of his dossier' do
expect(page).to have_css("#dossier_#{dossier1.id}_state")
end
context 'when user clicks on a projet in list' do
context 'when user clicks on a projet in list', js: true do
before do
page.click_on dossier1.procedure.libelle
page.find("#tr_dossier_#{dossier1.id}").click
end
scenario 'user is redirected to dossier page' do
expect(page).to have_css('#recap_dossier')

View file

@ -556,87 +556,6 @@ describe Dossier do
end
end
describe '.search' do
subject { liste_dossiers }
let(:liste_dossiers) { described_class.search(gestionnaire_1, terms) }
# let(:dossier) { described_class.search(gestionnaire_1, terms)[1] }
let(:administrateur_1) { create(:administrateur) }
let(:administrateur_2) { create(:administrateur) }
let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) }
let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) }
before do
create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1
create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2
end
let(:procedure_1) { create(:procedure, administrateur: administrateur_1) }
let(:procedure_2) { create(:procedure, administrateur: administrateur_2) }
let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) }
let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) }
let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) }
let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) }
let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') }
let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') }
let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') }
describe 'search is empty' do
let(:terms) { '' }
it { expect(subject.size).to eq(0) }
end
describe 'search draft file' do
let(:terms) { 'brouillon' }
it { expect(subject.size).to eq(0) }
it { expect(subject.class).to eq Dossier::ActiveRecord_Relation }
end
describe 'search on contact email' do
let(:terms) { 'clap' }
it { expect(subject.size).to eq(0) }
end
describe 'search on ID dossier' do
let(:terms) { "#{dossier_2.id}" }
it { expect(subject.size).to eq(1) }
end
describe 'search on SIRET' do
context 'when is part of SIRET' do
let(:terms) { '4181' }
it { expect(subject.size).to eq(1) }
end
context 'when is a complet SIRET' do
let(:terms) { '41816602300012' }
it { expect(subject.size).to eq(1) }
end
end
describe 'search on raison social' do
let(:terms) { 'OCTO' }
it { expect(subject.size).to eq(2) }
end
describe 'search on multiple fields' do
let(:terms) { 'octo test' }
it { expect(subject.size).to eq(1) }
end
end
end
describe '#cerfa_available?' do

View file

@ -0,0 +1,79 @@
require 'rails_helper'
describe Search do
describe '.results' do
subject { liste_dossiers }
let(:liste_dossiers) do
described_class.new(gestionnaire: gestionnaire_1, query: terms).results
end
let(:administrateur_1) { create(:administrateur) }
let(:administrateur_2) { create(:administrateur) }
let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) }
let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) }
before do
create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1
create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2
end
let(:procedure_1) { create(:procedure, administrateur: administrateur_1) }
let(:procedure_2) { create(:procedure, administrateur: administrateur_2) }
let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) }
let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) }
let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) }
let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) }
let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') }
let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') }
let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') }
describe 'search is empty' do
let(:terms) { '' }
it { expect(subject.size).to eq(0) }
end
describe 'search draft file' do
let(:terms) { 'brouillon' }
it { expect(subject.size).to eq(0) }
end
describe 'search on contact email' do
let(:terms) { 'clap' }
it { expect(subject.size).to eq(0) }
end
describe 'search on SIRET' do
context 'when is part of SIRET' do
let(:terms) { '4181' }
it { expect(subject.size).to eq(1) }
end
context 'when is a complet SIRET' do
let(:terms) { '41816602300012' }
it { expect(subject.size).to eq(1) }
end
end
describe 'search on raison social' do
let(:terms) { 'OCTO' }
it { expect(subject.size).to eq(2) }
end
describe 'search on multiple fields' do
let(:terms) { 'octo plop' }
it { expect(subject.size).to eq(1) }
end
end
end

View file

@ -95,29 +95,27 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do
end
describe 'on tab a_traiter' do
let(:total_dossiers) { 1 }
let(:total_dossiers) { 2 }
let(:active_class) { '.active .text-danger' }
let(:dossiers_to_display) { gestionnaire.dossiers.waiting_for_gestionnaire }
let(:dossiers_to_display) { gestionnaire.dossiers.ouvert }
let(:liste) { 'a_traiter' }
it_behaves_like 'check_tab_content' do
let(:decorate_dossier_at_check) { decorate_dossier_updated }
end
it_behaves_like 'check_tab_content' do
let(:decorate_dossier_at_check) { decorate_dossier_replied }
end
end
describe 'on tab en_attente' do
let(:total_dossiers) { 2 }
describe 'on tab figes' do
let(:total_dossiers) { 1 }
let(:active_class) { '.active .text-default' }
let(:dossiers_to_display) { gestionnaire.dossiers.waiting_for_user }
let(:liste) { 'en_attente' }
let(:dossiers_to_display) { gestionnaire.dossiers.fige }
let(:liste) { 'fige' }
describe 'for state replied' do
it_behaves_like 'check_tab_content' do
let(:decorate_dossier_at_check) { decorate_dossier_replied }
end
end
describe 'for state validated' do
it_behaves_like 'check_tab_content' do
let(:decorate_dossier_at_check) { decorate_dossier_validated }
end