Merge pull request #756 from sgmap/filters

Filters
This commit is contained in:
gregoirenovel 2017-10-02 17:49:50 +02:00 committed by GitHub
commit 3b06c49065
28 changed files with 794 additions and 30 deletions

View file

@ -0,0 +1 @@
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#FFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.749 1L1 12.75M12.749 12.75L1 1"/></g><path d="M-5-5h24v24H-5z"/></g></svg>

After

Width:  |  Height:  |  Size: 286 B

View file

@ -0,0 +1 @@
<svg width="10" height="6" viewBox="0 0 10 6" xmlns="http://www.w3.org/2000/svg"><title>ic_dropdown</title><g fill="none" fill-rule="evenodd"><path stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M1 1l4 4 4-4"/><path d="M-7-9h24v24H-7z"/></g></svg>

After

Width:  |  Height:  |  Size: 281 B

View file

@ -0,0 +1 @@
<svg width="10" height="6" viewBox="0 0 10 6" xmlns="http://www.w3.org/2000/svg"><title>ic_dropdown</title><g fill="none" fill-rule="evenodd"><path stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M1 5l4-4 4 4"/><path d="M-7 15h24V-9H-7z"/></g></svg>

After

Width:  |  Height:  |  Size: 282 B

View file

@ -1,3 +1,9 @@
document.addEventListener('turbolinks:load', function() {
$('select.select2').select2();
$('select.select2-limited').select2({
'placeholder': 'Sélectionnez des colonnes',
'maximumSelectionLength': '2',
'width': '300px'
});
});

View file

@ -104,6 +104,11 @@
cursor: default;
z-index: 10;
&.left-aligned {
left: 0;
right: unset;
}
&.terminated {
width: 600px;
color: $black;
@ -172,6 +177,42 @@
}
}
.dropdown-form {
padding: 2 * $default-spacer;
.select2-container {
margin-bottom: 2 * $default-spacer;
}
.select2-selection {
border: 1px solid $border-grey;
&.select2-selection--multiple {
border: 1px solid $border-grey;
}
}
&.large {
width: 340px;
}
label {
width: 100px;
display: inline-block;
margin-bottom: 2 * $default-spacer;
}
input,
select {
width: 200px;
display: inline-block;
}
}
.select2-dropdown {
border: 1px solid $border-grey;
}
.link {
color: $blue;
}

View file

@ -6,6 +6,16 @@
padding: 0;
}
thead a {
color: #000000;
}
.caret-icon {
vertical-align: top;
margin-top: 9px;
margin-left: 4px;
}
.cell-link {
color: $black;
padding: (3 * $default-spacer) 2px;

View file

@ -26,4 +26,22 @@
min-width: 150px;
}
}
.filter {
display: inline-block;
padding-left: 10px;
padding-right: 10px;
background-color: $light-blue;
border-radius: 4px;
color: #FFFFFF;
height: 36px;
line-height: 36px;
}
.close-icon {
vertical-align: top;
margin-top: 12px;
margin-left: 6px;
display: inline-block;
}
}

View file

@ -35,9 +35,9 @@ class Backoffice::DossiersListController < ApplicationController
params[:dossiers_smart_listing] = {page: dossiers_list_facade.service.default_page}
end
default_smart_listing_create :new_dossiers, service.nouveaux
default_smart_listing_create :follow_dossiers, service.suivi
default_smart_listing_create :all_state_dossiers, service.all_state
default_smart_listing_create :new_dossiers, service.nouveaux.order_by_updated_at('asc')
default_smart_listing_create :follow_dossiers, service.suivi.order_by_updated_at('asc')
default_smart_listing_create :all_state_dossiers, service.all_state.order_by_updated_at('asc')
default_smart_listing_create :archived_dossiers, service.archive
@archived_dossiers = service.archive

View file

@ -3,6 +3,8 @@ module NewGestionnaire
before_action :ensure_ownership!, except: [:index]
before_action :redirect_to_avis_if_needed, only: [:index]
ITEMS_PER_PAGE = 12
def index
@procedures = current_gestionnaire.procedures.order(archived_at: :desc, published_at: :desc)
@ -26,6 +28,11 @@ module NewGestionnaire
def show
@procedure = procedure
@current_filters = current_filters
@available_fields_to_filters = available_fields_to_filters
@displayed_fields = procedure_presentation.displayed_fields
@displayed_fields_values = displayed_fields_values
@a_suivre_dossiers = procedure
.dossiers
.includes(:user)
@ -49,9 +56,7 @@ module NewGestionnaire
@archived_dossiers = procedure.dossiers.includes(:user).archived
@statut = params[:statut].present? ? params[:statut] : 'a-suivre'
@dossiers = case @statut
@dossiers = case statut
when 'a-suivre'
@a_suivre_dossiers
when 'suivis'
@ -64,11 +69,116 @@ module NewGestionnaire
@archived_dossiers
end
@dossiers = @dossiers.page([params[:page].to_i, 1].max)
sorted_ids = sorted_ids(@dossiers)
if @current_filters.count > 0
filtered_ids = filtered_ids(@dossiers)
filtered_sorted_ids = sorted_ids.select { |id| filtered_ids.include?(id) }
else
filtered_sorted_ids = sorted_ids
end
page = params[:page].present? ? params[:page] : 1
filtered_sorted_paginated_ids = Kaminari
.paginate_array(filtered_sorted_ids)
.page(page)
.per(ITEMS_PER_PAGE)
@dossiers = @dossiers.where(id: filtered_sorted_paginated_ids)
eager_load_displayed_fields
@dossiers = @dossiers.sort_by { |d| filtered_sorted_paginated_ids.index(d.id) }
kaminarize(page, filtered_sorted_ids.count)
end
def update_displayed_fields
values = params[:values]
if values.nil?
values = []
end
fields = values.map do |value|
table, column = value.split("/")
c = procedure.fields.find do |field|
field['table'] == table && field['column'] == column
end
c.to_json
end
procedure_presentation.update_attributes(displayed_fields: fields)
current_sort = procedure_presentation.sort
if !values.include?("#{current_sort['table']}/#{current_sort['column']}")
procedure_presentation.update_attributes(sort: Procedure.default_sort)
end
redirect_back(fallback_location: procedure_url(procedure))
end
def update_sort
current_sort = procedure_presentation.sort
table = params[:table]
column = params[:column]
if table == current_sort['table'] && column == current_sort['column']
order = current_sort['order'] == 'asc' ? 'desc' : 'asc'
else
order = 'asc'
end
sort = {
'table' => table,
'column' => column,
'order' => order
}.to_json
procedure_presentation.update_attributes(sort: sort)
redirect_back(fallback_location: procedure_url(procedure))
end
def add_filter
filters = procedure_presentation.filters
table, column = params[:field].split('/')
label = procedure.fields.find { |c| c['table'] == table && c['column'] == column }['label']
filters[statut] << {
'label' => label,
'table' => table,
'column' => column,
'value' => params[:value]
}
procedure_presentation.update_attributes(filters: filters.to_json)
redirect_back(fallback_location: procedure_url(procedure))
end
def remove_filter
filters = procedure_presentation.filters
filter_to_remove = current_filters.find do |filter|
filter['table'] == params[:table] && filter['column'] == params[:column]
end
filters[statut] = filters[statut] - [filter_to_remove]
procedure_presentation.update_attributes(filters: filters.to_json)
redirect_back(fallback_location: procedure_url(procedure))
end
private
def statut
@statut ||= params[:statut].present? ? params[:statut] : 'a-suivre'
end
def procedure
Procedure.find(params[:procedure_id])
end
@ -85,5 +195,134 @@ module NewGestionnaire
redirect_to avis_index_path
end
end
def procedure_presentation
@procedure_presentation ||= current_gestionnaire.procedure_presentation_for_procedure_id(params[:procedure_id])
end
def displayed_fields_values
procedure_presentation.displayed_fields.map do |field|
"#{field['table']}/#{field['column']}"
end
end
def filtered_ids(dossiers)
current_filters.map do |filter|
case filter['table']
when 'self'
dossiers.where("? LIKE ?", filter['column'], "%#{filter['value']}%")
when 'france_connect_information'
dossiers
.includes(user: :france_connect_information)
.where("? LIKE ?", "france_connect_informations.#{filter['column']}", "%#{filter['value']}%")
when 'type_de_champ', 'type_de_champ_private'
relation = filter['table'] == 'type_de_champ' ? :champs : :champs_private
dossiers
.includes(relation)
.where("champs.type_de_champ_id = ?", filter['column'].to_i)
.where("champs.value LIKE ?", "%#{filter['value']}%")
when 'user', 'etablissement', 'entreprise'
dossiers
.includes(filter['table'])
.where("#{filter['table'].pluralize}.#{filter['column']} LIKE ?", "%#{filter['value']}%")
end.pluck(:id)
end.reduce(:&)
end
def sorted_ids(dossiers)
table = procedure_presentation.sort['table']
column = procedure_presentation.sort['column']
order = procedure_presentation.sort['order']
includes = ''
where = ''
case table
when 'self'
order = "dossiers.#{column} #{order}"
when'france_connect_information'
includes = { user: :france_connect_information }
order = "france_connect_informations.#{column} #{order}"
when 'type_de_champ', 'type_de_champ_private'
includes = table == 'type_de_champ' ? :champs : :champs_private
where = "champs.type_de_champ_id = #{column.to_i}"
order = "champs.value #{order}"
else
includes = table
order = "#{table.pluralize}.#{column} #{order}"
end
dossiers.includes(includes).where(where).order(Dossier.sanitize_for_order(order)).pluck(:id)
end
def current_filters
@current_filters ||= procedure_presentation.filters[statut]
end
def available_fields_to_filters
current_filters_fields_ids = current_filters.map do |field|
"#{field['table']}/#{field['column']}"
end
procedure.fields_for_select.reject do |field|
current_filters_fields_ids.include?(field[1])
end
end
def eager_load_displayed_fields
@displayed_fields
.reject { |field| field['table'] == 'self' }
.group_by do |field|
if ['type_de_champ', 'type_de_champ_private'].include?(field['table'])
'type_de_champ_group'
else
field['table']
end
end.each do |group_key, fields|
case group_key
when'france_connect_information'
@dossiers = @dossiers.includes({ user: :france_connect_information })
when 'type_de_champ_group'
if fields.any? { |field| field['table'] == 'type_de_champ' }
@dossiers = @dossiers.includes(:champs)
end
if fields.any? { |field| field['table'] == 'type_de_champ_private' }
@dossiers = @dossiers.includes(:champs_private)
end
where_conditions = fields.map do |field|
"champs.type_de_champ_id = #{field['column']}"
end.join(" OR ")
@dossiers = @dossiers.where(where_conditions)
else
@dossiers = @dossiers.includes(fields.first['table'])
end
end
end
def kaminarize(current_page, total)
@dossiers.instance_eval <<-EVAL
def current_page
#{current_page}
end
def total_pages
(#{total} / #{ITEMS_PER_PAGE}.to_f).ceil
end
def limit_value
#{ITEMS_PER_PAGE}
end
def first_page?
current_page == 1
end
def last_page?
current_page == total_pages
end
EVAL
end
end
end

View file

@ -1,4 +1,9 @@
class AssignTo < ActiveRecord::Base
belongs_to :procedure
belongs_to :gestionnaire
has_one :procedure_presentation, dependent: :destroy
def procedure_presentation_or_default
self.procedure_presentation || build_procedure_presentation
end
end

View file

@ -54,12 +54,12 @@ class Dossier < ActiveRecord::Base
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) }
scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) }
scope :en_instruction, -> { not_archived.state_en_instruction.order_by_updated_at(:asc) }
scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) }
scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) }
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction.order_by_updated_at(:asc) }
scope :all_state, -> { not_archived.state_not_brouillon }
scope :nouveaux, -> { not_archived.state_nouveaux }
scope :en_instruction, -> { not_archived.state_en_instruction }
scope :termine, -> { not_archived.state_termine }
scope :downloadable, -> { state_not_brouillon }
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
scope :without_followers, -> { includes(:follows).where(follows: { id: nil }) }
scope :with_unread_notifications, -> { where(notifications: { already_read: false }) }
@ -330,6 +330,29 @@ class Dossier < ActiveRecord::Base
end
end
def get_value(table, column)
case table
when 'self'
self.send(column)
when 'user'
self.user.send(column)
when 'france_connect_information'
self.user.france_connect_information&.send(column)
when 'entreprise'
self.entreprise&.send(column)
when 'etablissement'
self.etablissement&.send(column)
when 'type_de_champ'
self.champs.find {|c| c.type_de_champ_id == column.to_i }.value
when 'type_de_champ_private'
self.champs_private.find {|c| c.type_de_champ_id == column.to_i }.value
end
end
def self.sanitize_for_order(order)
sanitize_sql_for_order(order)
end
private
def build_attestation

View file

@ -123,6 +123,10 @@ class Gestionnaire < ActiveRecord::Base
end
end
def procedure_presentation_for_procedure_id(procedure_id)
assign_to.find_by(procedure_id: procedure_id).procedure_presentation_or_default
end
private
def valid_couple_table_attr? table, column

View file

@ -184,4 +184,69 @@ class Procedure < ActiveRecord::Base
def without_continuation_mail_template
without_continuation_mail || Mails::WithoutContinuationMail.default
end
def fields
fields = [
field_hash('Créé le', 'self', 'created_at'),
field_hash('Mis à jour le', 'self', 'updated_at'),
field_hash('Demandeur', 'user', 'email')
]
fields << [
field_hash('Civilité (FC)', 'france_connect_information', 'gender'),
field_hash('Prénom (FC)', 'france_connect_information', 'given_name'),
field_hash('Nom (FC)', 'france_connect_information', 'family_name')
]
if !for_individual || (for_individual && individual_with_siret)
fields << [
field_hash('SIREN', 'entreprise', 'siren'),
field_hash('Forme juridique', 'entreprise', 'forme_juridique'),
field_hash('Nom commercial', 'entreprise', 'nom_commercial'),
field_hash('Raison sociale', 'entreprise', 'raison_sociale'),
field_hash('SIRET siège social', 'entreprise', 'siret_siege_social'),
field_hash('Date de création', 'entreprise', 'date_creation')
]
fields << [
field_hash('SIRET', 'etablissement', 'siret'),
field_hash('Nom établissement', 'etablissement', 'libelle_naf'),
field_hash('Code postal', 'etablissement', 'code_postal')
]
end
types_de_champ.reject { |tdc| tdc.type_champ == 'header_section' }.each do |type_de_champ|
fields << field_hash(type_de_champ.libelle, 'type_de_champ', type_de_champ.id.to_s)
end
types_de_champ_private.reject { |tdc| tdc.type_champ == 'header_section' }.each do |type_de_champ|
fields << field_hash(type_de_champ.libelle, 'type_de_champ_private', type_de_champ.id.to_s)
end
fields.flatten
end
def fields_for_select
fields.map do |field|
[field['label'], "#{field['table']}/#{field['column']}"]
end
end
def self.default_sort
{
'table' => 'self',
'column' => 'id',
'order' => 'desc'
}.to_json
end
private
def field_hash(label, table, column)
{
'label' => label,
'table' => table,
'column' => column
}
end
end

View file

@ -0,0 +1,17 @@
class ProcedurePresentation < ActiveRecord::Base
belongs_to :assign_to
def displayed_fields
read_attribute(:displayed_fields).map do |field|
field = JSON.parse(field)
end
end
def sort
JSON.parse(read_attribute(:sort))
end
def filters
JSON.parse(read_attribute(:filters))
end
end

View file

@ -20,7 +20,7 @@ class DossiersListGestionnaireService
end
def all_state
@all_state ||= filter_dossiers.all_state
@all_state ||= filter_dossiers.all_state.order_by_updated_at('asc')
end
def suivi
@ -28,11 +28,11 @@ class DossiersListGestionnaireService
end
def nouveaux
@nouveaux ||= filter_dossiers.nouveaux
@nouveaux ||= filter_dossiers.nouveaux.order_by_updated_at('asc')
end
def a_instruire
@a_instruire ||= filter_dossiers.en_instruction
@a_instruire ||= filter_dossiers.en_instruction.order_by_updated_at('asc')
end
def archive
@ -40,7 +40,7 @@ class DossiersListGestionnaireService
end
def termine
@termine ||= filter_dossiers.termine
@termine ||= filter_dossiers.termine.order_by_updated_at('asc')
end
def filter_dossiers

View file

@ -0,0 +1,8 @@
%th{ class: classname }
= link_to update_sort_procedure_path(@procedure, table: field['table'], column: field['column']) do
= field['label']
- if @procedure_presentation.sort['table'] == field['table'] && @procedure_presentation.sort['column'] == field['column']
- if @procedure_presentation.sort['order'] == 'asc'
%img.caret-icon{ src: image_url("table/up_caret.svg") }
- else
%img.caret-icon{ src: image_url("table/down_caret.svg") }

View file

@ -51,14 +51,48 @@
= link_to "Au format .ods", backoffice_download_dossiers_tps_path(format: :ods, procedure_id: @procedure.id), target: "_blank"
.container
- if @dossiers.present?
- if @dossiers.present? || @current_filters.count > 0
%span.button.dropdown
Filtrer
.dropdown-content.left-aligned.fade-in-down
= form_tag add_filter_procedure_path(@procedure), method: :post, class: 'dropdown-form large' do
= label_tag :field, "Colonne"
= select_tag :field, options_for_select(@available_fields_to_filters)
%br
= label_tag :value, "Valeur"
= text_field_tag :value
= hidden_field_tag :statut, @statut
%br
= submit_tag "Ajouter le filtre", class: 'button'
- @current_filters.each do |filter|
%span.filter
= "#{filter['label']} : #{filter['value']}"
= link_to remove_filter_procedure_path(@procedure, statut: @statut, table: filter['table'], column: filter['column']) do
%img.close-icon{ src: image_url("close.svg") }
%table.table.dossiers-table.hoverable
%thead
%tr
%th.number-col Nº dossier
%th Demandeur
%th.status-col Statut
= render partial: "header_field", locals: { field: { "label" => "Nº dossier", "table" => "self", "column" => "id" }, classname: "number-col" }
- @displayed_fields.each do |field|
= render partial: "header_field", locals: { field: field, classname: "" }
= render partial: "header_field", locals: { field: { "label" => "Statut", "table" => "self", "column" => "state" }, classname: "status-col" }
%th.follow-col
%span.button.dropdown
Personnaliser
.dropdown-content.fade-in-down
= form_tag update_displayed_fields_procedure_path(@procedure), method: :patch, class: 'dropdown-form' do
= select_tag :values,
options_for_select(@procedure.fields_for_select,
selected: @displayed_fields_values),
multiple: true,
class: 'select2-limited'
= submit_tag "Enregistrer", class: 'button'
%tbody
- @dossiers.each do |dossier|
%tr
@ -68,7 +102,12 @@
- if @followed_dossiers.with_unread_notifications.include?(dossier)
%span.notifications{ 'aria-label': 'notifications' }
= dossier.id
%td= link_to(dossier.user.email, dossier_path(@procedure, dossier), class: 'cell-link')
- @displayed_fields.each do |field|
%td
= link_to(dossier_path(@procedure, dossier), class: 'cell-link') do
= dossier.get_value(field['table'], field['column'])
%td.status-col
= link_to(dossier_path(@procedure, dossier), class: 'cell-link') do
= render partial: 'status', locals: { dossier: dossier }

View file

@ -237,6 +237,11 @@ Rails.application.routes.draw do
scope module: 'new_gestionnaire' do
resources :procedures, only: [:index, :show], param: :procedure_id do
member do
patch 'update_displayed_fields'
get 'update_sort/:table/:column' => 'procedures#update_sort', as: 'update_sort'
post 'add_filter'
get 'remove_filter/:statut/:table/:column' => 'procedures#remove_filter', as: 'remove_filter'
resources :dossiers, only: [:show], param: :dossier_id do
member do
get 'attestation'

View file

@ -0,0 +1,10 @@
class CreateProcedurePresentations < ActiveRecord::Migration[5.0]
def change
create_table :procedure_presentations do |t|
t.references :assign_to, index: { unique: true }, foreign_key: true
t.text :displayed_fields, array: true, default: [{ "label" => "Demandeur", "table" => "user", "column" => "email" }.to_json], null: false
t.json :sort, default: { "table" => "self", "column" => "id", "order" => "desc" }.to_json, null: false
t.json :filters, default: { "a-suivre" => [], "suivis" => [], "traites" => [], "tous" => [], "archives" => [] }.to_json, null: false
end
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170926092716) do
ActiveRecord::Schema.define(version: 20170927092716) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -382,6 +382,14 @@ ActiveRecord::Schema.define(version: 20170926092716) do
t.index ["path"], name: "index_procedure_paths_on_path", using: :btree
end
create_table "procedure_presentations", force: :cascade do |t|
t.integer "assign_to_id"
t.text "displayed_fields", default: ["{\"label\":\"Demandeur\",\"table\":\"user\",\"column\":\"email\"}"], null: false, array: true
t.json "sort", default: "{\"table\":\"self\",\"column\":\"id\",\"order\":\"desc\"}", null: false
t.json "filters", default: "{\"a-suivre\":[],\"suivis\":[],\"traites\":[],\"tous\":[],\"archives\":[]}", null: false
t.index ["assign_to_id"], name: "index_procedure_presentations_on_assign_to_id", unique: true, using: :btree
end
create_table "procedures", force: :cascade do |t|
t.string "libelle"
t.string "description"
@ -503,6 +511,7 @@ ActiveRecord::Schema.define(version: 20170926092716) do
add_foreign_key "initiated_mails", "procedures"
add_foreign_key "procedure_paths", "administrateurs"
add_foreign_key "procedure_paths", "procedures"
add_foreign_key "procedure_presentations", "assign_tos"
add_foreign_key "received_mails", "procedures"
add_foreign_key "refused_mails", "procedures"
add_foreign_key "without_continuation_mails", "procedures"

View file

@ -172,15 +172,22 @@ describe NewGestionnaire::ProceduresController, type: :controller do
context "when logged in" do
before do
sign_in(gestionnaire)
get :show, params: { procedure_id: procedure.id }
end
it { expect(response).to have_http_status(:ok) }
it { expect(assigns(:procedure)).to eq(procedure) }
context "without anything" do
before { get :show, params: { procedure_id: procedure.id } }
it { expect(response).to have_http_status(:ok) }
it { expect(assigns(:procedure)).to eq(procedure) }
end
context 'with a new draft dossier' do
let!(:draft_dossier) { create(:dossier, procedure: procedure, state: 'draft') }
before do
get :show, params: { procedure_id: procedure.id }
end
it { expect(assigns(:a_suivre_dossiers)).to be_empty }
it { expect(assigns(:followed_dossiers)).to be_empty }
it { expect(assigns(:termines_dossiers)).to be_empty }
@ -191,6 +198,10 @@ describe NewGestionnaire::ProceduresController, type: :controller do
context 'with a new dossier without follower' do
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: 'received') }
before do
get :show, params: { procedure_id: procedure.id }
end
it { expect(assigns(:a_suivre_dossiers)).to match([new_unfollow_dossier]) }
it { expect(assigns(:followed_dossiers)).to be_empty }
it { expect(assigns(:termines_dossiers)).to be_empty }
@ -201,7 +212,10 @@ describe NewGestionnaire::ProceduresController, type: :controller do
context 'with a new dossier with a follower' do
let!(:new_followed_dossier) { create(:dossier, procedure: procedure, state: 'received') }
before { gestionnaire.followed_dossiers << new_followed_dossier }
before do
gestionnaire.followed_dossiers << new_followed_dossier
get :show, params: { procedure_id: procedure.id }
end
it { expect(assigns(:a_suivre_dossiers)).to be_empty }
it { expect(assigns(:followed_dossiers)).to match([new_followed_dossier]) }
@ -213,6 +227,10 @@ describe NewGestionnaire::ProceduresController, type: :controller do
context 'with a termine dossier with a follower' do
let!(:termine_dossier) { create(:dossier, procedure: procedure, state: 'closed') }
before do
get :show, params: { procedure_id: procedure.id }
end
it { expect(assigns(:a_suivre_dossiers)).to be_empty }
it { expect(assigns(:followed_dossiers)).to be_empty }
it { expect(assigns(:termines_dossiers)).to match([termine_dossier]) }
@ -223,6 +241,10 @@ describe NewGestionnaire::ProceduresController, type: :controller do
context 'with an archived dossier' do
let!(:archived_dossier) { create(:dossier, procedure: procedure, state: 'received', archived: true) }
before do
get :show, params: { procedure_id: procedure.id }
end
it { expect(assigns(:a_suivre_dossiers)).to be_empty }
it { expect(assigns(:followed_dossiers)).to be_empty }
it { expect(assigns(:termines_dossiers)).to be_empty }
@ -235,6 +257,7 @@ describe NewGestionnaire::ProceduresController, type: :controller do
let!(:new_followed_dossier) { Timecop.freeze(2.day.ago){ create(:dossier, procedure: procedure, state: 'received') } }
let!(:termine_dossier) { Timecop.freeze(3.day.ago){ create(:dossier, procedure: procedure, state: 'closed') } }
let!(:archived_dossier) { Timecop.freeze(4.day.ago){ create(:dossier, procedure: procedure, state: 'received', archived: true) } }
before do
gestionnaire.followed_dossiers << new_followed_dossier
get :show, params: { procedure_id: procedure.id, statut: statut }

View file

@ -44,10 +44,16 @@ FactoryGirl.define do
end
trait :with_type_de_champ_private do
after(:build) do |procedure, _evaluator|
type_de_champ = create(:type_de_champ_private)
transient do
types_de_champ_private_count 1
end
procedure.types_de_champ_private << type_de_champ
after(:build) do |procedure, evaluator|
evaluator.types_de_champ_private_count.times do
type_de_champ = create(:type_de_champ_private)
procedure.types_de_champ_private << type_de_champ
end
end
end

View file

@ -0,0 +1,104 @@
require "spec_helper"
feature "procedure filters" do
let(:procedure) { create(:procedure, :published, :with_type_de_champ) }
let(:gestionnaire) { create(:gestionnaire, procedures: [procedure]) }
let!(:type_de_champ) { procedure.types_de_champ.first }
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: "received") }
let!(:champ) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier.id) }
let!(:new_unfollow_dossier_2) { create(:dossier, procedure: procedure, state: "received") }
before do
champ.update_attributes(value: "Mon champ rempli")
login_as gestionnaire, scope: :gestionnaire
visit procedure_path(procedure)
end
scenario "should display demandeur by default" do
within ".dossiers-table" do
expect(page).to have_link("Demandeur")
expect(page).to have_link(new_unfollow_dossier.user.email)
end
end
scenario "should list all dossiers" do
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id)
expect(page).to have_link(new_unfollow_dossier.user.email)
expect(page).to have_link(new_unfollow_dossier_2.id)
expect(page).to have_link(new_unfollow_dossier_2.user.email)
end
end
scenario "should add be able to add created_at column", js: true do
add_column("Créé le")
within ".dossiers-table" do
expect(page).to have_link("Créé le")
expect(page).to have_link(new_unfollow_dossier.created_at)
end
end
scenario "should add be able to add and remove custom type_de_champ column", js: true do
add_column(type_de_champ.libelle)
within ".dossiers-table" do
expect(page).to have_link(type_de_champ.libelle)
expect(page).to have_link(champ.value)
end
remove_column(type_de_champ.libelle)
within ".dossiers-table" do
expect(page).not_to have_link(type_de_champ.libelle)
expect(page).not_to have_link(champ.value)
end
end
scenario "should be able to add and remove filter", js: true do
add_filter(type_de_champ.libelle, champ.value)
expect(page).to have_content("#{type_de_champ.libelle} : #{champ.value}")
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id)
expect(page).to have_link(new_unfollow_dossier.user.email)
expect(page).not_to have_link(new_unfollow_dossier_2.id)
expect(page).not_to have_link(new_unfollow_dossier_2.user.email)
end
remove_filter
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id)
expect(page).to have_link(new_unfollow_dossier.user.email)
expect(page).to have_link(new_unfollow_dossier_2.id)
expect(page).to have_link(new_unfollow_dossier_2.user.email)
end
end
def remove_filter
find(:xpath, "//span[contains(@class, 'filter')]/a").click
end
def add_filter(column_name, filter_value)
find(:xpath, "//span[contains(text(), 'Filtrer')]").click
select column_name, from: "Colonne"
fill_in "Valeur", with: filter_value
click_button "Ajouter le filtre"
end
def add_column(column_name)
find(:xpath, "//span[contains(text(), 'Personnaliser')]").click
find("span.select2-container").click
find(:xpath, "//li[text()='#{column_name}']").click
click_button "Enregistrer"
end
def remove_column(column_name)
find(:xpath, "//span[contains(text(), 'Personnaliser')]").click
find(:xpath, "//li[contains(@title, '#{column_name}')]/span[contains(text(), '×')]").click
find(:xpath, "//span[contains(text(), 'Personnaliser')]//span[contains(@class, 'select2-container')]").click
click_button "Enregistrer"
end
end

View file

@ -0,0 +1,16 @@
describe AssignTo, type: :model do
describe '#procedure_presentation_or_default' do
context "without a procedure_presentation" do
let!(:assign_to) { AssignTo.create }
it { expect(assign_to.procedure_presentation_or_default.persisted?).to be_falsey }
end
context "with a procedure_presentation" do
let!(:assign_to) { AssignTo.create }
let!(:procedure_presentation) { ProcedurePresentation.create(assign_to: assign_to) }
it { expect(assign_to.procedure_presentation_or_default).to eq(procedure_presentation) }
end
end
end

View file

@ -835,4 +835,28 @@ describe Dossier do
it { expect(Dossier.count).to eq(0) }
end
end
describe "#get_value" do
let(:dossier) { create(:dossier, :with_entreprise, user: user) }
before do
FranceConnectInformation.create(france_connect_particulier_id: 123, user: user, gender: 'male')
@champ_public = dossier.champs.first
@champ_public.value = "kiwi"
@champ_public.save
@champ_private = dossier.champs_private.first
@champ_private.value = "banane"
@champ_private.save
end
it { expect(dossier.get_value('self', 'created_at')).to eq(dossier.created_at) }
it { expect(dossier.get_value('user', 'email')).to eq(user.email) }
it { expect(dossier.get_value('france_connect_information', 'gender')).to eq(user.france_connect_information.gender) }
it { expect(dossier.get_value('entreprise', 'siren')).to eq(dossier.entreprise.siren) }
it { expect(dossier.get_value('etablissement', 'siret')).to eq(dossier.etablissement.siret) }
it { expect(dossier.get_value('type_de_champ', @champ_public.type_de_champ.id.to_s)).to eq(dossier.champs.first.value) }
it { expect(dossier.get_value('type_de_champ_private', @champ_private.type_de_champ.id.to_s)).to eq(dossier.champs_private.first.value) }
end
end

View file

@ -382,4 +382,12 @@ describe Gestionnaire, type: :model do
it { is_expected.to eq({ }) }
end
end
describe "procedure_presentation_for_procedure_id" do
let!(:procedure_assign_2) { create :assign_to, gestionnaire: gestionnaire, procedure: procedure_2 }
let!(:pp) { ProcedurePresentation.create(assign_to: procedure_assign) }
it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure.id)).to eq(pp)}
it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure_2.id).persisted?).to be_falsey}
end
end

View file

@ -0,0 +1,26 @@
require 'spec_helper'
describe ProcedurePresentation do
let (:procedure_presentation_id) {
ProcedurePresentation.create(
displayed_fields: [
{ "label" => "test1", "table" => "user" }.to_json,
{ "label" => "test2", "table" => "champs" }.to_json],
sort: { "table" => "user","column" => "email","order" => "asc" }.to_json,
filters: { "a-suivre" => [], "suivis" => [{ "label" => "label1", "table" => "table1", "column" => "column1" }] }.to_json
).id
}
let (:procedure_presentation) { ProcedurePresentation.find(procedure_presentation_id) }
describe "#displayed_fields" do
it { expect(procedure_presentation.displayed_fields).to eq([{"label" => "test1", "table" => "user"}, {"label" => "test2", "table" => "champs"}]) }
end
describe "#sort" do
it { expect(procedure_presentation.sort).to eq({ "table" => "user","column" => "email","order" => "asc" }) }
end
describe "#filters" do
it { expect(procedure_presentation.filters).to eq({ "a-suivre" => [], "suivis" => [{ "label" => "label1", "table" => "table1", "column" => "column1" }] }) }
end
end

View file

@ -421,4 +421,59 @@ describe Procedure do
it { expect(Dossier.count).to eq(0) }
end
end
describe "#fields" do
subject { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, :types_de_champ_count => 2, :types_de_champ_private_count => 2) }
let(:tdc_1) { subject.types_de_champ.first }
let(:tdc_2) { subject.types_de_champ.last }
let(:tdc_private_1) { subject.types_de_champ_private.first }
let(:tdc_private_2) { subject.types_de_champ_private.last }
let(:expected) {
[
{ "label" => 'Créé le', "table" => 'self', "column" => 'created_at' },
{ "label" => 'Mis à jour le', "table" => 'self', "column" => 'updated_at' },
{ "label" => 'Demandeur', "table" => 'user', "column" => 'email' },
{ "label" => 'Civilité (FC)', "table" => 'france_connect_information', "column" => 'gender' },
{ "label" => 'Prénom (FC)', "table" => 'france_connect_information', "column" => 'given_name' },
{ "label" => 'Nom (FC)', "table" => 'france_connect_information', "column" => 'family_name' },
{ "label" => 'SIREN', "table" => 'entreprise', "column" => 'siren' },
{ "label" => 'Forme juridique', "table" => 'entreprise', "column" => 'forme_juridique' },
{ "label" => 'Nom commercial', "table" => 'entreprise', "column" => 'nom_commercial' },
{ "label" => 'Raison sociale', "table" => 'entreprise', "column" => 'raison_sociale' },
{ "label" => 'SIRET siège social', "table" => 'entreprise', "column" => 'siret_siege_social' },
{ "label" => 'Date de création', "table" => 'entreprise', "column" => 'date_creation' },
{ "label" => 'SIRET', "table" => 'etablissement', "column" => 'siret' },
{ "label" => 'Nom établissement', "table" => 'etablissement', "column" => 'libelle_naf' },
{ "label" => 'Code postal', "table" => 'etablissement', "column" => 'code_postal' },
{ "label" => tdc_1.libelle, "table" => 'type_de_champ', "column" => tdc_1.id.to_s },
{ "label" => tdc_2.libelle, "table" => 'type_de_champ', "column" => tdc_2.id.to_s },
{ "label" => tdc_private_1.libelle, "table" => 'type_de_champ_private', "column" => tdc_private_1.id.to_s },
{ "label" => tdc_private_2.libelle, "table" => 'type_de_champ_private', "column" => tdc_private_2.id.to_s }
]
}
it { expect(subject.fields).to eq(expected) }
end
describe "#fields_for_select" do
subject { create(:procedure) }
before do
allow(subject).to receive(:fields).and_return([{
"label" => "label1",
"table" => "table1",
"column" => "column1"
}, {
"label" => "label2",
"table" => "table2",
"column" => "column2"
}])
end
it { expect(subject.fields_for_select).to eq([["label1", "table1/column1"], ["label2", "table2/column2"]]) }
end
describe ".default_sort" do
it { expect(Procedure.default_sort).to eq("{\"table\":\"self\",\"column\":\"id\",\"order\":\"desc\"}") }
end
end