Merge branch 'develop' into staging
This commit is contained in:
commit
e05e40f431
71 changed files with 810 additions and 86 deletions
5
Gemfile
5
Gemfile
|
@ -3,6 +3,9 @@ source 'https://rubygems.org'
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'rails', '5.0.0.1'
|
gem 'rails', '5.0.0.1'
|
||||||
|
|
||||||
|
gem 'actioncable', '5.0.0.1'
|
||||||
|
gem 'redis'
|
||||||
|
|
||||||
# Use SCSS for stylesheets
|
# Use SCSS for stylesheets
|
||||||
gem 'sass-rails', '~> 5.0'
|
gem 'sass-rails', '~> 5.0'
|
||||||
# Use Uglifier as compressor for JavaScript assets
|
# Use Uglifier as compressor for JavaScript assets
|
||||||
|
@ -116,6 +119,8 @@ group :development do
|
||||||
# Access an IRB console on exception pages or by using <%= console %> in views
|
# Access an IRB console on exception pages or by using <%= console %> in views
|
||||||
|
|
||||||
gem 'web-console'
|
gem 'web-console'
|
||||||
|
|
||||||
|
gem 'rack-handlers'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
|
|
@ -417,6 +417,8 @@ GEM
|
||||||
pry (~> 0.10)
|
pry (~> 0.10)
|
||||||
public_suffix (2.0.4)
|
public_suffix (2.0.4)
|
||||||
rack (2.0.1)
|
rack (2.0.1)
|
||||||
|
rack-handlers (0.7.3)
|
||||||
|
rack
|
||||||
rack-oauth2 (1.4.0)
|
rack-oauth2 (1.4.0)
|
||||||
activesupport (>= 2.3)
|
activesupport (>= 2.3)
|
||||||
attr_required (>= 0.0.5)
|
attr_required (>= 0.0.5)
|
||||||
|
@ -465,6 +467,7 @@ GEM
|
||||||
nokogiri (~> 1.5)
|
nokogiri (~> 1.5)
|
||||||
trollop (~> 2.1)
|
trollop (~> 2.1)
|
||||||
rdoc (4.3.0)
|
rdoc (4.3.0)
|
||||||
|
redis (3.3.0)
|
||||||
ref (2.0.0)
|
ref (2.0.0)
|
||||||
request_store (1.3.1)
|
request_store (1.3.1)
|
||||||
responders (2.3.0)
|
responders (2.3.0)
|
||||||
|
@ -624,6 +627,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
actioncable (= 5.0.0.1)
|
||||||
active_model_serializers
|
active_model_serializers
|
||||||
apipie-rails
|
apipie-rails
|
||||||
as_csv
|
as_csv
|
||||||
|
@ -665,9 +669,11 @@ DEPENDENCIES
|
||||||
pg
|
pg
|
||||||
poltergeist
|
poltergeist
|
||||||
pry-byebug
|
pry-byebug
|
||||||
|
rack-handlers
|
||||||
railroady
|
railroady
|
||||||
rails (= 5.0.0.1)
|
rails (= 5.0.0.1)
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
|
redis
|
||||||
rest-client
|
rest-client
|
||||||
rgeo-geojson
|
rgeo-geojson
|
||||||
rspec-rails (~> 3.0)
|
rspec-rails (~> 3.0)
|
||||||
|
|
13
app/assets/javascripts/cable.js
Normal file
13
app/assets/javascripts/cable.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Action Cable provides the framework to deal with WebSockets in Rails.
|
||||||
|
// You can generate new channels where WebSocket features live using the rails generate channel command.
|
||||||
|
//
|
||||||
|
//= require action_cable
|
||||||
|
//= require_self
|
||||||
|
//= require_tree ./channels
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
this.App || (this.App = {});
|
||||||
|
|
||||||
|
App.cable = ActionCable.createConsumer();
|
||||||
|
|
||||||
|
}).call(this);
|
23
app/assets/javascripts/channels/notifications.js
Normal file
23
app/assets/javascripts/channels/notifications.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
App.messages = App.cable.subscriptions.create('NotificationsChannel', {
|
||||||
|
received: function (data) {
|
||||||
|
if (window.location.href.indexOf('backoffice') !== -1) {
|
||||||
|
$("#notification_alert").html(data['message']);
|
||||||
|
|
||||||
|
slideIn_notification_alert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function slideIn_notification_alert (){
|
||||||
|
$("#notification_alert").animate({
|
||||||
|
right: '20px'
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
setTimeout(slideOut_notification_alert, 3500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slideOut_notification_alert (){
|
||||||
|
$("#notification_alert").animate({
|
||||||
|
right: '-250px'
|
||||||
|
}, 200);
|
||||||
|
}
|
|
@ -1,5 +1,22 @@
|
||||||
$(document).on('page:load', the_terms);
|
$(document).on('page:load', the_terms);
|
||||||
$(document).ready(the_terms);
|
$(document).ready(the_terms);
|
||||||
|
$(document).on('page:load', pannel_switch);
|
||||||
|
$(document).ready(pannel_switch);
|
||||||
|
|
||||||
|
function pannel_switch() {
|
||||||
|
$('#switch-notifications').click(function () {
|
||||||
|
$('#procedure_list').addClass('hidden');
|
||||||
|
$('#notifications_list').removeClass('hidden');
|
||||||
|
$(this).addClass('active');
|
||||||
|
$('#switch-procedures').removeClass('active');
|
||||||
|
})
|
||||||
|
$('#switch-procedures').click(function () {
|
||||||
|
$('#notifications_list').addClass('hidden');
|
||||||
|
$('#procedure_list').removeClass('hidden');
|
||||||
|
$(this).addClass('active');
|
||||||
|
$('#switch-notifications').removeClass('active');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function the_terms() {
|
function the_terms() {
|
||||||
var the_terms = $("#dossier_autorisation_donnees");
|
var the_terms = $("#dossier_autorisation_donnees");
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
$(document).on('page:load', link_init);
|
$(document).on('page:load', link_init);
|
||||||
$(document).ready(link_init);
|
$(document).ready(link_init);
|
||||||
|
|
||||||
|
|
||||||
function link_init() {
|
function link_init() {
|
||||||
$('#dossiers_list tr').on('click', function () {
|
$('#dossiers_list tr').on('click', function () {
|
||||||
$(location).attr('href', $(this).data('dossier_url'))
|
$(location).attr('href', $(this).data('dossier_url'))
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -42,24 +42,22 @@ h5 span {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#procedure_list {
|
#procedure_list, #notifications_list {
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
a, a:hover {
|
a, a:hover {
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
.procedure_list_element.active, .notification.active {
|
||||||
.procedure_list_element.active{
|
|
||||||
background-color: #668ABD;
|
background-color: #668ABD;
|
||||||
}
|
}
|
||||||
|
.procedure_list_element, .notification {
|
||||||
.procedure_list_element {
|
|
||||||
padding: 15px 40px 15px 20px;
|
padding: 15px 40px 15px 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 1.8em;
|
line-height: 1.8em;
|
||||||
}
|
}
|
||||||
.procedure_list_element:hover{
|
.procedure_list_element:hover, .notification:hover {
|
||||||
background-color: #668ABD;
|
background-color: #668ABD;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -67,5 +65,5 @@ h5 span {
|
||||||
|
|
||||||
.split-hr-left {
|
.split-hr-left {
|
||||||
border-bottom: 1px solid #FFFFFF;
|
border-bottom: 1px solid #FFFFFF;
|
||||||
margin: 20px 10px 0px 0;
|
margin: 20px 10px 0px 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#left-pannel {
|
#left-pannel {
|
||||||
margin-top: 60px;
|
margin-top: 60px;
|
||||||
padding: 0 0 0 10px;
|
padding: 0;
|
||||||
background-color: #003189;
|
background-color: #003189;
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -51,6 +51,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#menu-block {
|
#menu-block {
|
||||||
|
#switch-buttons {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 205px;
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
.active {
|
||||||
|
background-color: #668ABD !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.separator {
|
||||||
|
height: 26px;
|
||||||
|
width: 1px;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
}
|
||||||
|
#switch-procedures:hover, #switch-notifications:hover {
|
||||||
|
background-color: #668AEA;
|
||||||
|
}
|
||||||
|
#switch-procedures {
|
||||||
|
height: 28px;
|
||||||
|
margin: 0 0 0 -10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
width: 100px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 25px 0 0 25px;
|
||||||
|
}
|
||||||
|
#switch-notifications {
|
||||||
|
width: 103px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 0 25px 25px 0;
|
||||||
|
height: 28px;
|
||||||
|
margin: 0 0 0 -5px;
|
||||||
|
padding: 0 0 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.split-hr {
|
||||||
|
border-bottom: 1px solid #FFFFFF;
|
||||||
|
width: 200px;
|
||||||
|
margin: 20px 0 20px 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#infos-block {
|
#infos-block {
|
||||||
.split-hr {
|
.split-hr {
|
||||||
|
@ -63,18 +110,42 @@
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
width: 200px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
.tips {
|
#notifications_list {
|
||||||
margin: 0 10px 0 5px;
|
.notification {
|
||||||
.fa {
|
padding: 10px 10px 10px 20px;
|
||||||
color: #FFFFFF;
|
.dossier, .updated-at {
|
||||||
font-size: 40px;
|
display: inline-block;
|
||||||
width: inherit;
|
color: #CCCCCC;
|
||||||
padding: 5px;
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.notice {
|
}
|
||||||
font-size: 18px;
|
.notifications {
|
||||||
display: initial;
|
margin: 20px 10px 0 5px;
|
||||||
|
.fa {
|
||||||
|
font-size: 25px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
}
|
||||||
|
.notification {
|
||||||
|
margin: 10px 0 10px 10px;
|
||||||
|
.type {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.updated-at {
|
||||||
|
color: #CCCCCC;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.split-hr {
|
||||||
|
width: 40px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
app/assets/stylesheets/notification_alert.scss
Normal file
12
app/assets/stylesheets/notification_alert.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#notification_alert {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: -250px;
|
||||||
|
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
width: 250px;
|
||||||
|
height: 80px;
|
||||||
|
|
||||||
|
border: solid black 1px;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
#search-block{
|
#search-block{
|
||||||
margin: 15px 10px 0 0;
|
margin: 15px 10px 0 10px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
app/channels/application_cable/channel.rb
Normal file
5
app/channels/application_cable/channel.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
|
||||||
|
module ApplicationCable
|
||||||
|
class Channel < ActionCable::Channel::Base
|
||||||
|
end
|
||||||
|
end
|
5
app/channels/application_cable/connection.rb
Normal file
5
app/channels/application_cable/connection.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
|
||||||
|
module ApplicationCable
|
||||||
|
class Connection < ActionCable::Connection::Base
|
||||||
|
end
|
||||||
|
end
|
5
app/channels/notifications_channel.rb
Normal file
5
app/channels/notifications_channel.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class NotificationsChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_from 'notifications'
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,6 +19,7 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
create_dossier_facade params[:id]
|
create_dossier_facade params[:id]
|
||||||
|
|
||||||
unless @facade.nil?
|
unless @facade.nil?
|
||||||
@champs_private = @facade.champs_private
|
@champs_private = @facade.champs_private
|
||||||
|
|
||||||
|
@ -27,6 +28,8 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
|
||||||
acc
|
acc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Notification.where(dossier_id: params[:id].to_i).update_all already_read: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter
|
def filter
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
class CommentairesController < ApplicationController
|
class CommentairesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@facade = DossierFacades.new(
|
@facade = DossierFacades.new(
|
||||||
params[:dossier_id],
|
params[:dossier_id],
|
||||||
(current_gestionnaire || current_user).email,
|
(current_gestionnaire || current_user).email,
|
||||||
params[:champs_id]
|
params[:champs_id]
|
||||||
)
|
)
|
||||||
render layout: false
|
render layout: false
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
@ -48,12 +48,15 @@ class CommentairesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
NotificationMailer.new_answer(@commentaire.dossier).deliver_now! if saved
|
NotificationMailer.new_answer(@commentaire.dossier).deliver_now! if saved
|
||||||
|
|
||||||
redirect_to url_for(controller: 'backoffice/dossiers', action: :show, id: params['dossier_id'])
|
redirect_to url_for(controller: 'backoffice/dossiers', action: :show, id: params['dossier_id'])
|
||||||
elsif current_user.email != @commentaire.dossier.user.email
|
|
||||||
invite = Invite.where(dossier: @commentaire.dossier, user: current_user).first
|
|
||||||
redirect_to url_for(controller: 'users/dossiers/invites', action: :show, id: invite.id)
|
|
||||||
else
|
else
|
||||||
redirect_to url_for(controller: :recapitulatif, action: :show, dossier_id: params['dossier_id'])
|
if current_user.email != @commentaire.dossier.user.email
|
||||||
|
invite = Invite.where(dossier: @commentaire.dossier, user: current_user).first
|
||||||
|
redirect_to url_for(controller: 'users/dossiers/invites', action: :show, id: invite.id)
|
||||||
|
else
|
||||||
|
redirect_to url_for(controller: :recapitulatif, action: :show, dossier_id: params['dossier_id'])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
8
app/decorators/notification_decorator.rb
Normal file
8
app/decorators/notification_decorator.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class NotificationDecorator < Draper::Decorator
|
||||||
|
delegate_all
|
||||||
|
|
||||||
|
def index_display
|
||||||
|
['champs', 'piece_justificative'].include?(type_notif) ? type = liste.join(" ") : type = liste.last
|
||||||
|
{ dossier: "Dossier n°#{dossier.id}", date: updated_at.strftime('%d/%m %H:%M'), type: type }
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,10 @@ class DossierFacades
|
||||||
@dossier.decorate
|
@dossier.decorate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def last_notifications
|
||||||
|
@dossier.notifications.order("updated_at DESC").limit(5)
|
||||||
|
end
|
||||||
|
|
||||||
def champs
|
def champs
|
||||||
@dossier.ordered_champs
|
@dossier.ordered_champs
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,8 +26,16 @@ class DossiersListFacades
|
||||||
current_devise_profil.dossiers.where(state: :initiated, archived: false).count
|
current_devise_profil.dossiers.where(state: :initiated, archived: false).count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_dossier_number procedure_id
|
||||||
|
current_devise_profil.dossiers.where(state: :initiated, archived: false, procedure_id: procedure_id).count
|
||||||
|
end
|
||||||
|
|
||||||
def gestionnaire_procedures_name_and_id_list
|
def gestionnaire_procedures_name_and_id_list
|
||||||
@current_devise_profil.procedures.order('libelle ASC').inject([]) { |acc, procedure| acc.push({id: procedure.id, libelle: procedure.libelle}) }
|
@current_devise_profil.procedures.order('libelle ASC').inject([]) { |acc, procedure| acc.push({id: procedure.id, libelle: procedure.libelle, unread_notifications: @current_devise_profil.notifications_for(procedure)}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def unread_notifications
|
||||||
|
current_devise_profil.notifications
|
||||||
end
|
end
|
||||||
|
|
||||||
def procedure_id
|
def procedure_id
|
||||||
|
|
|
@ -2,6 +2,6 @@ class InviteDossierFacades < DossierFacades
|
||||||
|
|
||||||
#TODO rechercher en fonction de la personne/email
|
#TODO rechercher en fonction de la personne/email
|
||||||
def initialize dossier_id, email
|
def initialize dossier_id, email
|
||||||
@dossier = (Invite.where(email: email).find(dossier_id)).dossier
|
@dossier = Invite.where(email: email, dossier_id: dossier_id).first!.dossier
|
||||||
end
|
end
|
||||||
end
|
end
|
2
app/jobs/application_job.rb
Normal file
2
app/jobs/application_job.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
end
|
3
app/models/application_record.rb
Normal file
3
app/models/application_record.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
|
self.abstract_class = true
|
||||||
|
end
|
|
@ -5,6 +5,8 @@ class Cerfa < ActiveRecord::Base
|
||||||
mount_uploader :content, CerfaUploader
|
mount_uploader :content, CerfaUploader
|
||||||
validates :content, :file_size => {:maximum => 20.megabytes}
|
validates :content, :file_size => {:maximum => 20.megabytes}
|
||||||
|
|
||||||
|
after_save :internal_notification, if: Proc.new { !dossier.nil? }
|
||||||
|
|
||||||
def empty?
|
def empty?
|
||||||
content.blank?
|
content.blank?
|
||||||
end
|
end
|
||||||
|
@ -18,4 +20,12 @@ class Cerfa < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def internal_notification
|
||||||
|
unless dossier.state == 'draft'
|
||||||
|
NotificationService.new('cerfa', self.dossier.id).notify
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -5,6 +5,8 @@ class Champ < ActiveRecord::Base
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
after_save :internal_notification, if: Proc.new { !dossier.nil? }
|
||||||
|
|
||||||
def mandatory?
|
def mandatory?
|
||||||
mandatory
|
mandatory
|
||||||
end
|
end
|
||||||
|
@ -36,14 +38,22 @@ class Champ < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.regions
|
def self.regions
|
||||||
JSON.parse(Carto::GeoAPI::Driver.regions).sort_by{|e| e['nom']}.inject([]){|acc, liste| acc.push(liste['nom']) }
|
JSON.parse(Carto::GeoAPI::Driver.regions).sort_by { |e| e['nom'] }.inject([]) { |acc, liste| acc.push(liste['nom']) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.departements
|
def self.departements
|
||||||
JSON.parse(Carto::GeoAPI::Driver.departements).inject([]){|acc, liste| acc.push(liste['code'] + ' - ' + liste['nom']) }.push('99 - Étranger')
|
JSON.parse(Carto::GeoAPI::Driver.departements).inject([]) { |acc, liste| acc.push(liste['code'] + ' - ' + liste['nom']) }.push('99 - Étranger')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pays
|
def self.pays
|
||||||
JSON.parse(Carto::GeoAPI::Driver.pays).inject([]){|acc, liste| acc.push(liste['nom']) }
|
JSON.parse(Carto::GeoAPI::Driver.pays).inject([]) { |acc, liste| acc.push(liste['nom']) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def internal_notification
|
||||||
|
unless dossier.state == 'draft'
|
||||||
|
NotificationService.new('champs', self.dossier.id, self.libelle).notify
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,17 @@ class Commentaire < ActiveRecord::Base
|
||||||
|
|
||||||
belongs_to :piece_justificative
|
belongs_to :piece_justificative
|
||||||
|
|
||||||
|
after_save :internal_notification
|
||||||
|
|
||||||
def header
|
def header
|
||||||
"#{email}, " + created_at.localtime.strftime('%d %b %Y %H:%M')
|
"#{email}, " + created_at.localtime.strftime('%d %b %Y %H:%M')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def internal_notification
|
||||||
|
if email == dossier.user.email || dossier.invites_user.pluck(:email).to_a.include?(email)
|
||||||
|
NotificationService.new('commentaire', self.dossier.id).notify
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,7 @@ class Dossier < ActiveRecord::Base
|
||||||
has_many :invites, dependent: :destroy
|
has_many :invites, dependent: :destroy
|
||||||
has_many :invites_user, class_name: 'InviteUser', dependent: :destroy
|
has_many :invites_user, class_name: 'InviteUser', dependent: :destroy
|
||||||
has_many :follows
|
has_many :follows
|
||||||
|
has_many :notifications, dependent: :destroy
|
||||||
|
|
||||||
belongs_to :procedure
|
belongs_to :procedure
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
@ -41,6 +42,7 @@ class Dossier < ActiveRecord::Base
|
||||||
|
|
||||||
after_save :build_default_champs, if: Proc.new { procedure_id_changed? }
|
after_save :build_default_champs, if: Proc.new { procedure_id_changed? }
|
||||||
after_save :build_default_individual, if: Proc.new { procedure.for_individual? }
|
after_save :build_default_individual, if: Proc.new { procedure.for_individual? }
|
||||||
|
after_save :internal_notification
|
||||||
|
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
|
|
||||||
|
@ -326,4 +328,12 @@ class Dossier < ActiveRecord::Base
|
||||||
def invite_by_user? email
|
def invite_by_user? email
|
||||||
(invites_user.pluck :email).include? email
|
(invites_user.pluck :email).include? email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def internal_notification
|
||||||
|
if state_changed? && state == 'submitted'
|
||||||
|
NotificationService.new('submitted', self.id).notify
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,6 +63,22 @@ class Gestionnaire < ActiveRecord::Base
|
||||||
PreferenceSmartListingPage.create(page: 1, procedure: nil, gestionnaire: self, liste: 'a_traiter')
|
PreferenceSmartListingPage.create(page: 1, procedure: nil, gestionnaire: self, liste: 'a_traiter')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def notifications
|
||||||
|
Notification.where(already_read: false, dossier_id: follows.pluck(:dossier_id) ).order("updated_at DESC")
|
||||||
|
end
|
||||||
|
|
||||||
|
def notifications_for procedure
|
||||||
|
procedure_ids = dossiers_follow.pluck(:procedure_id)
|
||||||
|
|
||||||
|
if procedure_ids.include?(procedure.id)
|
||||||
|
return dossiers_follow.where(procedure_id: procedure.id)
|
||||||
|
.inject(0) do |acc, dossier|
|
||||||
|
acc += dossier.notifications.where(already_read: false).count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def valid_couple_table_attr? table, column
|
def valid_couple_table_attr? table, column
|
||||||
|
|
19
app/models/notification.rb
Normal file
19
app/models/notification.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
class Notification < ActiveRecord::Base
|
||||||
|
belongs_to :dossier
|
||||||
|
|
||||||
|
# after_save :broadcast_notification
|
||||||
|
|
||||||
|
enum type_notif: {
|
||||||
|
commentaire: 'commentaire',
|
||||||
|
cerfa: 'cerfa',
|
||||||
|
piece_justificative: 'piece_justificative',
|
||||||
|
champs: 'champs',
|
||||||
|
submitted: 'submitted'
|
||||||
|
}
|
||||||
|
|
||||||
|
# def broadcast_notification
|
||||||
|
# ActionCable.server.broadcast 'notifications',
|
||||||
|
# message: "Dossier n°#{self.dossier.id} : #{self.liste.last}",
|
||||||
|
# dossier: {id: self.dossier.id}
|
||||||
|
# end
|
||||||
|
end
|
|
@ -13,6 +13,8 @@ class PieceJustificative < ActiveRecord::Base
|
||||||
validates :content, :file_size => {:maximum => 20.megabytes}
|
validates :content, :file_size => {:maximum => 20.megabytes}
|
||||||
validates :content, presence: true, allow_blank: false, allow_nil: false
|
validates :content, presence: true, allow_blank: false, allow_nil: false
|
||||||
|
|
||||||
|
after_save :internal_notification, if: Proc.new { !dossier.nil? }
|
||||||
|
|
||||||
def empty?
|
def empty?
|
||||||
content.blank?
|
content.blank?
|
||||||
end
|
end
|
||||||
|
@ -43,4 +45,12 @@ class PieceJustificative < ActiveRecord::Base
|
||||||
image/jpeg
|
image/jpeg
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def internal_notification
|
||||||
|
unless self.type_de_piece_justificative.nil? && dossier.state == 'draft'
|
||||||
|
NotificationService.new('piece_justificative', self.dossier.id, self.libelle).notify
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@ class ChampsService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
champ.save
|
champ.save if champ.changed?
|
||||||
end
|
end
|
||||||
|
|
||||||
errors
|
errors
|
||||||
|
|
42
app/services/notification_service.rb
Normal file
42
app/services/notification_service.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class NotificationService
|
||||||
|
|
||||||
|
def initialize type_notif, dossier_id, attribut_change=''
|
||||||
|
@type_notif = type_notif
|
||||||
|
@dossier_id = dossier_id
|
||||||
|
|
||||||
|
notification.liste.push text_for_notif attribut_change
|
||||||
|
notification.liste = notification.liste.uniq
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify
|
||||||
|
notification.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def notification
|
||||||
|
@notification ||=
|
||||||
|
begin
|
||||||
|
Notification.find_by! dossier_id: @dossier_id, already_read: false, type_notif: @type_notif
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
Notification.new dossier_id: @dossier_id, type_notif: @type_notif, liste: []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def text_for_notif attribut=''
|
||||||
|
case @type_notif
|
||||||
|
when 'commentaire'
|
||||||
|
"#{notification.liste.size + 1} nouveau(x) commentaire(s) déposé(s)."
|
||||||
|
when 'cerfa'
|
||||||
|
"Un nouveau formulaire a été déposé."
|
||||||
|
when 'piece_justificative'
|
||||||
|
attribut
|
||||||
|
when 'champs'
|
||||||
|
attribut
|
||||||
|
when 'submitted'
|
||||||
|
"Le dossier n°#{@dossier_id} a été déposé."
|
||||||
|
else
|
||||||
|
'Notification par défaut'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,7 +19,7 @@
|
||||||
%td
|
%td
|
||||||
= procedure.created_at_fr
|
= procedure.created_at_fr
|
||||||
%td
|
%td
|
||||||
= link_to('Cloner', admin_procedure_clone_path(procedure.id), 'data-method' => :put, class: 'btn-sm btn-primary')
|
= link_to('Cloner', admin_procedure_clone_path(procedure.id), 'data-method' => :put, class: 'btn-sm btn-primary clone-btn')
|
||||||
- unless procedure.published? || procedure.archived?
|
- unless procedure.published? || procedure.archived?
|
||||||
= link_to('X', url_for(controller: 'admin/procedures', action: :destroy, id: procedure.id), 'data-method' => :delete, class: 'btn-sm btn-danger')
|
= link_to('X', url_for(controller: 'admin/procedures', action: :destroy, id: procedure.id), 'data-method' => :delete, class: 'btn-sm btn-danger')
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
%table#dossiers_list.table
|
%table#dossiers_list.table
|
||||||
%thead
|
%thead
|
||||||
|
- if smart_listing.name.to_s == 'follow_dossiers'
|
||||||
|
%th
|
||||||
|
%i.fa.fa-bell
|
||||||
- @facade_data_view.preference_list_dossiers_filter.each do |preference|
|
- @facade_data_view.preference_list_dossiers_filter.each do |preference|
|
||||||
%th{class: "col-md-#{preference.bootstrap_lg} col-lg-#{preference.bootstrap_lg}"}
|
%th{class: "col-md-#{preference.bootstrap_lg} col-lg-#{preference.bootstrap_lg}"}
|
||||||
- if preference.table.to_s.include? 'champs'
|
- if preference.table.to_s.include? 'champs'
|
||||||
|
@ -16,6 +19,15 @@
|
||||||
- unless smart_listing.empty?
|
- unless smart_listing.empty?
|
||||||
- smart_listing.collection.each do |dossier|
|
- smart_listing.collection.each do |dossier|
|
||||||
%tr.dossier-row{id: "tr_dossier_#{dossier.id}", 'data-dossier_url' => backoffice_dossier_url(id: dossier.id)}
|
%tr.dossier-row{id: "tr_dossier_#{dossier.id}", 'data-dossier_url' => backoffice_dossier_url(id: dossier.id)}
|
||||||
|
- if smart_listing.name.to_s == 'follow_dossiers'
|
||||||
|
%td.center
|
||||||
|
- total_notif = dossier.notifications.where(already_read: false).count
|
||||||
|
- if total_notif == 0
|
||||||
|
.badge.progress-bar-default
|
||||||
|
= total_notif
|
||||||
|
- else
|
||||||
|
.badge.progress-bar-warning
|
||||||
|
= total_notif
|
||||||
- @facade_data_view.preference_list_dossiers_filter.each_with_index do |preference, index|
|
- @facade_data_view.preference_list_dossiers_filter.each_with_index do |preference, index|
|
||||||
%td
|
%td
|
||||||
- if preference.table.nil? || preference.table.empty?
|
- if preference.table.nil? || preference.table.empty?
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
Aucune personne invitée
|
Aucune personne invitée
|
||||||
|
|
||||||
.col-md-3.col-sm-3.col-xs-3.col-lg-3
|
.col-md-3.col-sm-3.col-xs-3.col-lg-3
|
||||||
=form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline' do
|
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline' do
|
||||||
=text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation'
|
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation'
|
||||||
=submit_tag 'Ajouter', class: 'btn btn-success'
|
= submit_tag 'Ajouter', class: 'btn btn-success', id: 'send-invitation'
|
||||||
|
|
||||||
|
|
1
app/views/layouts/_notifications_alert.html.haml
Normal file
1
app/views/layouts/_notifications_alert.html.haml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#notification_alert.alert.alert-success
|
|
@ -9,6 +9,7 @@
|
||||||
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
||||||
|
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
|
= action_cable_meta_tag
|
||||||
%body
|
%body
|
||||||
= render partial: 'layouts/support_navigator_banner'
|
= render partial: 'layouts/support_navigator_banner'
|
||||||
#beta{class:(Rails.env == 'production' ? '' : 'beta_staging')}
|
#beta{class:(Rails.env == 'production' ? '' : 'beta_staging')}
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
%i.fa.fa-times{style:'position: fixed; top: 10; right: 30; color: white;'}
|
%i.fa.fa-times{style:'position: fixed; top: 10; right: 30; color: white;'}
|
||||||
|
|
||||||
= render partial: 'layouts/switch_devise_profile_module'
|
= render partial: 'layouts/switch_devise_profile_module'
|
||||||
|
= render partial: 'layouts/notifications_alert'
|
||||||
|
|
||||||
= render partial: 'layouts/footer', locals: {main_container_size: main_container_size}
|
= render partial: 'layouts/footer', locals: {main_container_size: main_container_size}
|
||||||
= render partial: 'layouts/google_analytics'
|
= render partial: 'layouts/google_analytics'
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
%div#action-block
|
%div#action-block
|
||||||
|
|
||||||
%div#menu-block
|
%div#menu-block
|
||||||
|
%div.split-hr-left
|
||||||
|
#switch-buttons
|
||||||
|
#switch-procedures.active Procédures
|
||||||
|
#switch-notifications Notifications
|
||||||
|
|
||||||
%div#infos-block
|
%div#infos-block
|
||||||
%div.split-hr-left
|
%div.split-hr-left
|
||||||
|
@ -17,3 +21,17 @@
|
||||||
= link_to backoffice_dossiers_procedure_path(procedure[:id]), {title: procedure[:libelle]} do
|
= link_to backoffice_dossiers_procedure_path(procedure[:id]), {title: procedure[:libelle]} do
|
||||||
%div.procedure_list_element{ class: ('active' if procedure[:id] == @facade_data_view.procedure.id rescue '') }
|
%div.procedure_list_element{ class: ('active' if procedure[:id] == @facade_data_view.procedure.id rescue '') }
|
||||||
= truncate(procedure[:libelle], length: 50)
|
= truncate(procedure[:libelle], length: 50)
|
||||||
|
- total_new = @facade_data_view.new_dossier_number procedure[:id]
|
||||||
|
- if total_new > 0
|
||||||
|
.badge.progress-bar-success{title:'Nouveaux dossiers'}
|
||||||
|
= total_new
|
||||||
|
-if procedure[:unread_notifications] > 0
|
||||||
|
.badge.progress-bar-warning{title: 'Notifications'}
|
||||||
|
= procedure[:unread_notifications]
|
||||||
|
#notifications_list.hidden
|
||||||
|
- @facade_data_view.unread_notifications.each do |notification|
|
||||||
|
= link_to backoffice_dossier_path(notification.dossier.id) do
|
||||||
|
.notification
|
||||||
|
.dossier= notification.decorate.index_display[:dossier]
|
||||||
|
.updated-at= notification.decorate.index_display[:date]
|
||||||
|
.type= notification.decorate.index_display[:type]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
%div#first-block
|
%div#first-block
|
||||||
%div.infos
|
%div.infos
|
||||||
%div.projet-name #{@facade.dossier.nom_projet.capitalize rescue nil}
|
|
||||||
#dossier_id= t('dynamics.dossiers.numéro') + @facade.dossier.id.to_s
|
#dossier_id= t('dynamics.dossiers.numéro') + @facade.dossier.id.to_s
|
||||||
|
|
||||||
%div#action-block
|
%div#action-block
|
||||||
|
@ -30,6 +29,24 @@
|
||||||
%div.split-hr-left
|
%div.split-hr-left
|
||||||
%div.dossier-state= @facade.dossier.display_state
|
%div.dossier-state= @facade.dossier.display_state
|
||||||
%div.split-hr-left
|
%div.split-hr-left
|
||||||
%div.tips.hidden
|
%div.notifications
|
||||||
%i.fa.fa-lightbulb-o
|
- if @facade.dossier.notifications.empty?
|
||||||
%div.notice= "Ceci est un bloc destiné à contenir des informations sur ce que vous êtes censé pouvoir faire à ce stade de traitement du dossier."
|
= "Aucune notification pour le moment."
|
||||||
|
- else
|
||||||
|
%i.fa.fa-bell-o
|
||||||
|
- @facade.last_notifications.each do |notification|
|
||||||
|
.notification
|
||||||
|
.updated-at= notification.updated_at.strftime('%d/%m/%Y %H:%M')
|
||||||
|
- if ['champs'].include?(notification.type_notif)
|
||||||
|
- if notification.liste.size > 1
|
||||||
|
.type= "Plusieurs attributs ont été changés, dont: #{notification.liste.join(" ")}"
|
||||||
|
- else
|
||||||
|
.type= "Un attribut à été changé: #{notification.liste.last}"
|
||||||
|
- elsif ['piece_justificative'].include?(notification.type_notif)
|
||||||
|
- if notification.liste.size > 1
|
||||||
|
.type= "Plusieurs pièces justificatives ont été changés, dont: #{notification.liste.join(" ")}"
|
||||||
|
- else
|
||||||
|
.type= "Une pièce justificative à été changée: #{notification.liste.last}"
|
||||||
|
- else
|
||||||
|
.type= notification.liste.last
|
||||||
|
.split-hr
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
%div.en-cours
|
%div.en-cours
|
||||||
%h2 Récapitulatif
|
%h2 Récapitulatif
|
||||||
%div.infos
|
%div.infos
|
||||||
%div #{@facade.dossier.nom_projet}
|
|
||||||
%div= t('dynamics.dossiers.numéro') + @facade.dossier.id.to_s
|
%div= t('dynamics.dossiers.numéro') + @facade.dossier.id.to_s
|
||||||
|
|
||||||
%div#action-block
|
%div#action-block
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
Suivre le dossier
|
Suivre le dossier
|
||||||
%div.row
|
%div.row
|
||||||
%div.col-lg-12.col-md-12.col-sm-12.col-xs-12
|
%div.col-lg-12.col-md-12.col-sm-12.col-xs-12
|
||||||
%div.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
|
%div#invitations.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
|
||||||
%i.fa.fa-user
|
%i.fa.fa-user
|
||||||
= t('utils.involved')
|
= t('utils.involved')
|
||||||
%div.dropdown-menu.dropdown-menu-right.dropdown-pannel
|
%div.dropdown-menu.dropdown-menu-right.dropdown-pannel
|
||||||
|
@ -34,7 +34,6 @@
|
||||||
= t('dynamics.dossiers.invites.empty')
|
= t('dynamics.dossiers.invites.empty')
|
||||||
|
|
||||||
%li
|
%li
|
||||||
=form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline' do
|
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
|
||||||
=text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation'
|
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email'
|
||||||
=submit_tag 'Ajouter', class: 'btn btn-success'
|
= submit_tag 'Ajouter', class: 'btn btn-success'
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
%div.col-lg-3.col-md-3.col-sm-3.col-xs-3.options
|
%div.col-lg-3.col-md-3.col-sm-3.col-xs-3.options
|
||||||
%div.row.centered-option
|
%div.row.centered-option
|
||||||
%div.col-lg-12.col-md-12.col-sm-12.col-xs-12
|
%div.col-lg-12.col-md-12.col-sm-12.col-xs-12
|
||||||
%div.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
|
%div#invitations.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
|
||||||
%i.fa.fa-user
|
%i.fa.fa-user
|
||||||
= t('utils.involved')
|
= t('utils.involved')
|
||||||
%div.dropdown-menu.dropdown-menu-right.dropdown-pannel
|
%div.dropdown-menu.dropdown-menu-right.dropdown-pannel
|
||||||
|
@ -24,6 +24,6 @@
|
||||||
= t('dynamics.dossiers.invites.empty')
|
= t('dynamics.dossiers.invites.empty')
|
||||||
|
|
||||||
%li
|
%li
|
||||||
=form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline' do
|
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
|
||||||
=text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation'
|
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email'
|
||||||
=submit_tag 'Ajouter', class: 'btn btn-success'
|
= submit_tag 'Ajouter', class: 'btn btn-success'
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
# This file is used by Rack-based servers to start the application.
|
# This file is used by Rack-based servers to start the application.
|
||||||
|
|
||||||
require ::File.expand_path('../config/environment', __FILE__)
|
require ::File.expand_path('../config/environment', __FILE__)
|
||||||
|
|
||||||
|
# Action Cable requires that all classes are loaded in advance
|
||||||
|
Rails.application.eager_load!
|
||||||
|
|
||||||
run Rails.application
|
run Rails.application
|
||||||
|
|
10
config/cable.yml
Normal file
10
config/cable.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
production:
|
||||||
|
adapter: redis
|
||||||
|
url: redis://localhost:6379
|
||||||
|
|
||||||
|
development:
|
||||||
|
adapter: redis
|
||||||
|
url: redis://localhost:6379
|
||||||
|
|
||||||
|
test:
|
||||||
|
adapter: async
|
|
@ -45,5 +45,5 @@ Rails.application.configure do
|
||||||
# Raises error for missing translations
|
# Raises error for missing translations
|
||||||
# config.action_view.raise_on_missing_translations = true
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
|
||||||
|
config.action_cable.url = "ws://localhost:3000/cable"
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,10 +26,6 @@ fr:
|
||||||
mail_contact:
|
mail_contact:
|
||||||
blank: 'doit être rempli'
|
blank: 'doit être rempli'
|
||||||
invalid: 'est incorrect'
|
invalid: 'est incorrect'
|
||||||
nom_projet:
|
|
||||||
blank: 'doit être rempli'
|
|
||||||
description:
|
|
||||||
blank: 'doit être remplie'
|
|
||||||
montant_projet:
|
montant_projet:
|
||||||
blank: 'doit être rempli'
|
blank: 'doit être rempli'
|
||||||
montant_aide_demande:
|
montant_aide_demande:
|
||||||
|
|
|
@ -202,4 +202,6 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
apipie
|
apipie
|
||||||
|
|
||||||
|
mount ActionCable.server => '/cable'
|
||||||
end
|
end
|
||||||
|
|
109
config/unicorn.rb
Normal file
109
config/unicorn.rb
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# Sample verbose configuration file for Unicorn (not Rack)
|
||||||
|
#
|
||||||
|
# This configuration file documents many features of Unicorn
|
||||||
|
# that may not be needed for some applications. See
|
||||||
|
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
|
||||||
|
# for a much simpler configuration file.
|
||||||
|
#
|
||||||
|
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Use at least one worker per core if you're on a dedicated server,
|
||||||
|
# more will usually help for _short_ waits on databases/caches.
|
||||||
|
worker_processes 2
|
||||||
|
|
||||||
|
# Since Unicorn is never exposed to outside clients, it does not need to
|
||||||
|
# run on the standard HTTP port (80), there is no reason to start Unicorn
|
||||||
|
# as root unless it's from system init scripts.
|
||||||
|
# If running the master process as root and the workers as an unprivileged
|
||||||
|
# user, do this to switch euid/egid in the workers (also chowns logs):
|
||||||
|
# user "unprivileged_user", "unprivileged_group"
|
||||||
|
|
||||||
|
# Help ensure your application will always spawn in the symlinked
|
||||||
|
# "current" directory that Capistrano sets up.
|
||||||
|
|
||||||
|
# listen on both a Unix domain socket and a TCP port,
|
||||||
|
# we use a shorter backlog for quicker failover when busy
|
||||||
|
listen "127.0.0.1:3000", :tcp_nopush => true
|
||||||
|
|
||||||
|
# nuke workers after 30 seconds instead of 60 seconds (the default)
|
||||||
|
timeout 30
|
||||||
|
|
||||||
|
# By default, the Unicorn logger will write to stderr.
|
||||||
|
# Additionally, ome applications/frameworks log to stderr or stdout,
|
||||||
|
# so prevent them from going to /dev/null when daemonized here:
|
||||||
|
|
||||||
|
# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
|
||||||
|
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
|
||||||
|
preload_app true
|
||||||
|
GC.respond_to?(:copy_on_write_friendly=) and
|
||||||
|
GC.copy_on_write_friendly = true
|
||||||
|
|
||||||
|
# Enable this flag to have unicorn test client connections by writing the
|
||||||
|
# beginning of the HTTP headers before calling the application. This
|
||||||
|
# prevents calling the application for connections that have disconnected
|
||||||
|
# while queued. This is only guaranteed to detect clients on the same
|
||||||
|
# host unicorn runs on, and unlikely to detect disconnects even on a
|
||||||
|
# fast LAN.
|
||||||
|
check_client_connection false
|
||||||
|
|
||||||
|
# local variable to guard against running a hook multiple times
|
||||||
|
run_once = true
|
||||||
|
|
||||||
|
|
||||||
|
before_fork do |server, worker|
|
||||||
|
# the following is highly recomended for Rails + "preload_app true"
|
||||||
|
# as there's no need for the master process to hold a connection
|
||||||
|
defined?(ActiveRecord::Base) and
|
||||||
|
ActiveRecord::Base.connection.disconnect!
|
||||||
|
|
||||||
|
# Occasionally, it may be necessary to run non-idempotent code in the
|
||||||
|
# master before forking. Keep in mind the above disconnect! example
|
||||||
|
# is idempotent and does not need a guard.
|
||||||
|
if run_once
|
||||||
|
# do_something_once_here ...
|
||||||
|
run_once = false # prevent from firing again
|
||||||
|
end
|
||||||
|
|
||||||
|
# The following is only recommended for memory/DB-constrained
|
||||||
|
# installations. It is not needed if your system can house
|
||||||
|
# twice as many worker_processes as you have configured.
|
||||||
|
#
|
||||||
|
# # This allows a new master process to incrementally
|
||||||
|
# # phase out the old master process with SIGTTOU to avoid a
|
||||||
|
# # thundering herd (especially in the "preload_app false" case)
|
||||||
|
# # when doing a transparent upgrade. The last worker spawned
|
||||||
|
# # will then kill off the old master process with a SIGQUIT.
|
||||||
|
old_pid = "#{server.config[:pid]}.oldbin"
|
||||||
|
if old_pid != server.pid
|
||||||
|
begin
|
||||||
|
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
|
||||||
|
Process.kill(sig, File.read(old_pid).to_i)
|
||||||
|
rescue Errno::ENOENT, Errno::ESRCH
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#
|
||||||
|
# Throttle the master from forking too quickly by sleeping. Due
|
||||||
|
# to the implementation of standard Unix signal handlers, this
|
||||||
|
# helps (but does not completely) prevent identical, repeated signals
|
||||||
|
# from being lost when the receiving process is busy.
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
|
after_fork do |server, worker|
|
||||||
|
# per-process listener ports for debugging/admin/migrations
|
||||||
|
# addr = "127.0.0.1:#{9293 + worker.nr}"
|
||||||
|
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
|
||||||
|
|
||||||
|
# the following is *required* for Rails + "preload_app true",
|
||||||
|
defined?(ActiveRecord::Base) and
|
||||||
|
ActiveRecord::Base.establish_connection
|
||||||
|
|
||||||
|
# if preload_app is true, then you may also want to check and
|
||||||
|
# restart any other shared sockets/descriptors such as Memcached,
|
||||||
|
# and Redis. TokyoCabinet file handles are safe to reuse
|
||||||
|
# between any number of forked children (assuming your kernel
|
||||||
|
# correctly implements pread()/pwrite() system calls)
|
||||||
|
end
|
16
db/migrate/20161221153929_create_notification.rb
Normal file
16
db/migrate/20161221153929_create_notification.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class CreateNotification < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :notifications do |t|
|
||||||
|
|
||||||
|
t.boolean :already_read, default: false
|
||||||
|
t.string :liste, array: true
|
||||||
|
t.boolean :multiple, default: false
|
||||||
|
t.string :type_notif
|
||||||
|
t.datetime :created_at
|
||||||
|
t.datetime :updated_at
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
add_belongs_to :notifications, :dossier
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
class DeleteOldAttrInDataBase < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
remove_column :dossiers, :nom_projet
|
||||||
|
remove_column :procedures, :test
|
||||||
|
remove_column :notifications, :multiple
|
||||||
|
end
|
||||||
|
end
|
14
db/schema.rb
14
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20161205110427) do
|
ActiveRecord::Schema.define(version: 20161227103823) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -118,7 +118,6 @@ ActiveRecord::Schema.define(version: 20161205110427) do
|
||||||
|
|
||||||
create_table "dossiers", force: :cascade do |t|
|
create_table "dossiers", force: :cascade do |t|
|
||||||
t.boolean "autorisation_donnees"
|
t.boolean "autorisation_donnees"
|
||||||
t.string "nom_projet"
|
|
||||||
t.integer "procedure_id"
|
t.integer "procedure_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
|
@ -249,6 +248,16 @@ ActiveRecord::Schema.define(version: 20161205110427) do
|
||||||
t.index ["procedure_id"], name: "index_module_api_cartos_on_procedure_id", unique: true, using: :btree
|
t.index ["procedure_id"], name: "index_module_api_cartos_on_procedure_id", unique: true, using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "notifications", force: :cascade do |t|
|
||||||
|
t.boolean "already_read", default: false
|
||||||
|
t.string "liste", array: true
|
||||||
|
t.string "type_notif"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
t.integer "dossier_id"
|
||||||
|
t.index ["dossier_id"], name: "index_notifications_on_dossier_id", using: :btree
|
||||||
|
end
|
||||||
|
|
||||||
create_table "pieces_justificatives", force: :cascade do |t|
|
create_table "pieces_justificatives", force: :cascade do |t|
|
||||||
t.string "content"
|
t.string "content"
|
||||||
t.integer "dossier_id"
|
t.integer "dossier_id"
|
||||||
|
@ -302,7 +311,6 @@ ActiveRecord::Schema.define(version: 20161205110427) do
|
||||||
t.string "lien_demarche"
|
t.string "lien_demarche"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.boolean "test"
|
|
||||||
t.integer "administrateur_id"
|
t.integer "administrateur_id"
|
||||||
t.boolean "archived", default: false
|
t.boolean "archived", default: false
|
||||||
t.boolean "euro_flag", default: false
|
t.boolean "euro_flag", default: false
|
||||||
|
|
|
@ -36,6 +36,10 @@ describe Backoffice::CommentairesController, type: :controller do
|
||||||
expect { subject }.to change(Follow, :count).by(0)
|
expect { subject }.to change(Follow, :count).by(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'Internal notification is not create' do
|
||||||
|
expect { subject }.to change(Notification, :count).by (0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when document is upload whith a commentaire', vcr: {cassette_name: 'controllers_backoffice_commentaires_controller_doc_upload_with_comment'} do
|
context 'when document is upload whith a commentaire', vcr: {cassette_name: 'controllers_backoffice_commentaires_controller_doc_upload_with_comment'} do
|
||||||
|
@ -54,6 +58,10 @@ describe Backoffice::CommentairesController, type: :controller do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'Internal notification is not create' do
|
||||||
|
expect { subject }.to change(Notification, :count).by (0)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'piece justificative created' do
|
describe 'piece justificative created' do
|
||||||
let(:pj) { PieceJustificative.last }
|
let(:pj) { PieceJustificative.last }
|
||||||
|
|
||||||
|
|
|
@ -38,34 +38,40 @@ describe Backoffice::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #show' do
|
describe 'GET #show' do
|
||||||
|
subject { get :show, params: {id: dossier_id} }
|
||||||
|
|
||||||
context 'gestionnaire is connected' do
|
context 'gestionnaire is connected' do
|
||||||
before do
|
before do
|
||||||
sign_in gestionnaire
|
sign_in gestionnaire
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
get :show, params: {id: dossier_id}
|
expect(subject).to have_http_status(200)
|
||||||
expect(response).to have_http_status(200)
|
end
|
||||||
|
|
||||||
|
describe 'all notifications unread are changed' do
|
||||||
|
it do
|
||||||
|
expect(Notification).to receive(:where).with(dossier_id: dossier_id).and_return(Notification::ActiveRecord_Relation)
|
||||||
|
expect(Notification::ActiveRecord_Relation).to receive(:update_all).with(already_read: true).and_return(true)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ' when dossier is archived' do
|
context ' when dossier is archived' do
|
||||||
before do
|
let(:dossier_id) { dossier_archived }
|
||||||
get :show, params: {id: dossier_archived.id}
|
|
||||||
end
|
it { expect(subject).to redirect_to('/backoffice') }
|
||||||
it { expect(response).to redirect_to('/backoffice') }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when dossier id does not exist' do
|
context 'when dossier id does not exist' do
|
||||||
before do
|
let(:dossier_id) { bad_dossier_id }
|
||||||
get :show, params: {id: bad_dossier_id}
|
|
||||||
end
|
it { expect(subject).to redirect_to('/backoffice') }
|
||||||
it { expect(response).to redirect_to('/backoffice') }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'gestionnaire does not connected but dossier id is correct' do
|
context 'gestionnaire does not connected but dossier id is correct' do
|
||||||
subject { get :show, params: {id: dossier_id} }
|
|
||||||
|
|
||||||
it { is_expected.to redirect_to('/gestionnaires/sign_in') }
|
it { is_expected.to redirect_to('/gestionnaires/sign_in') }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,6 +28,10 @@ describe Users::CommentairesController, type: :controller do
|
||||||
|
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'Notification interne is create' do
|
||||||
|
expect { subject }.to change(Notification, :count).by (1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when document is upload whith a commentaire', vcr: {cassette_name: 'controllers_sers_commentaires_controller_upload_doc'} do
|
context 'when document is upload whith a commentaire', vcr: {cassette_name: 'controllers_sers_commentaires_controller_upload_doc'} do
|
||||||
|
|
|
@ -145,6 +145,13 @@ shared_examples 'description_controller_spec' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Quand la procédure accepte les CERFA' do
|
context 'Quand la procédure accepte les CERFA' do
|
||||||
|
subject { post :create, params: {dossier_id: dossier_id,
|
||||||
|
cerfa_pdf: cerfa_pdf} }
|
||||||
|
|
||||||
|
it 'Notification interne is create' do
|
||||||
|
expect { subject }.to change(Notification, :count).by (1)
|
||||||
|
end
|
||||||
|
|
||||||
context 'Sauvegarde du CERFA PDF', vcr: {cassette_name: 'controllers_users_description_controller_save_cerfa'} do
|
context 'Sauvegarde du CERFA PDF', vcr: {cassette_name: 'controllers_users_description_controller_save_cerfa'} do
|
||||||
before do
|
before do
|
||||||
post :create, params: {dossier_id: dossier_id,
|
post :create, params: {dossier_id: dossier_id,
|
||||||
|
@ -292,6 +299,10 @@ shared_examples 'description_controller_spec' do
|
||||||
sign_in guest
|
sign_in guest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'Notification interne is create' do
|
||||||
|
expect { subject }.to change(Notification, :count).by (1)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when PJ have no documents' do
|
context 'when PJ have no documents' do
|
||||||
it { expect(dossier.pieces_justificatives.size).to eq 0 }
|
it { expect(dossier.pieces_justificatives.size).to eq 0 }
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: '
|
||||||
let(:invite_by_user) { create :user, email: 'invite@plop.com' }
|
let(:invite_by_user) { create :user, email: 'invite@plop.com' }
|
||||||
|
|
||||||
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_datetime, cerfa_flag: true) }
|
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_datetime, cerfa_flag: true) }
|
||||||
let(:dossier) { create(:dossier, procedure: procedure, user: owner_user) }
|
let(:dossier) { create(:dossier, procedure: procedure, user: owner_user, state: 'initiated') }
|
||||||
|
|
||||||
let(:dossier_id) { dossier.id }
|
let(:dossier_id) { dossier.id }
|
||||||
let(:bad_dossier_id) { Dossier.count + 10000 }
|
let(:bad_dossier_id) { Dossier.count + 10000 }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
RSpec.describe Users::Dossiers::InvitesController, type: :controller do
|
describe Users::Dossiers::InvitesController, type: :controller do
|
||||||
describe '#authenticate_user!' do
|
describe '#authenticate_user!' do
|
||||||
let(:user) { create :user }
|
let(:user) { create :user }
|
||||||
let(:invite) { create :invite }
|
let(:invite) { create :invite }
|
||||||
|
|
|
@ -82,6 +82,10 @@ describe Users::RecapitulatifController, type: :controller do
|
||||||
dossier.validated!
|
dossier.validated!
|
||||||
post :submit, params: {dossier_id: dossier.id}
|
post :submit, params: {dossier_id: dossier.id}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'Internal notification is created' do
|
||||||
|
expect(Notification.where(dossier_id: dossier.id, type_notif: 'submitted').first).not_to be_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,10 +50,12 @@ describe DossiersListFacades do
|
||||||
|
|
||||||
it { expect(subject.first[:id]).to eq procedure.id }
|
it { expect(subject.first[:id]).to eq procedure.id }
|
||||||
it { expect(subject.first[:libelle]).to eq procedure.libelle }
|
it { expect(subject.first[:libelle]).to eq procedure.libelle }
|
||||||
|
it { expect(subject.first[:unread_notifications]).to eq 0 }
|
||||||
|
|
||||||
|
|
||||||
it { expect(subject.last[:id]).to eq procedure_2.id }
|
it { expect(subject.last[:id]).to eq procedure_2.id }
|
||||||
it { expect(subject.last[:libelle]).to eq procedure_2.libelle }
|
it { expect(subject.last[:libelle]).to eq procedure_2.libelle }
|
||||||
|
it { expect(subject.last[:unread_notifications]).to eq 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#active_filter?' do
|
describe '#active_filter?' do
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
FactoryGirl.define do
|
FactoryGirl.define do
|
||||||
factory :commentaire do
|
factory :commentaire do
|
||||||
body 'plop'
|
body 'plop'
|
||||||
|
|
||||||
|
before(:create) do |commentaire, _evaluator|
|
||||||
|
unless commentaire.dossier
|
||||||
|
commentaire.dossier = create :dossier
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
FactoryGirl.define do
|
FactoryGirl.define do
|
||||||
sequence(:gestionnaire_email) { |n| "plop#{n}@plop.com" }
|
sequence(:gestionnaire_email) { |n| "gest#{n}@plop.com" }
|
||||||
factory :gestionnaire do
|
factory :gestionnaire do
|
||||||
email { generate(:gestionnaire_email) }
|
email { generate(:gestionnaire_email) }
|
||||||
password 'password'
|
password 'password'
|
||||||
|
|
12
spec/factories/notification.rb
Normal file
12
spec/factories/notification.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :notification do
|
||||||
|
type_notif 'commentaire'
|
||||||
|
liste []
|
||||||
|
|
||||||
|
before(:create) do |notification, _evaluator|
|
||||||
|
unless notification.dossier
|
||||||
|
notification.dossier = create :dossier
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
spec/features/admin/procedure_cloning_spec.rb
Normal file
29
spec/features/admin/procedure_cloning_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'As an administrateur I wanna clone a procedure', js: true do
|
||||||
|
|
||||||
|
let(:administrateur) { create(:administrateur) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
login_as administrateur, scope: :administrateur
|
||||||
|
visit root_path
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Cloning procedure' do
|
||||||
|
|
||||||
|
before 'Create procedure' do
|
||||||
|
page.find_by_id('new-procedure').click
|
||||||
|
fill_in 'procedure_libelle', with: 'libelle de la procedure'
|
||||||
|
page.execute_script("$('#procedure_description').data('wysihtml5').editor.setValue('description de la procedure')")
|
||||||
|
page.find_by_id('save-procedure').click
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Cloning' do
|
||||||
|
visit admin_procedures_draft_path
|
||||||
|
expect(page.find_by_id('procedures')['data-item-count']).to eq('1')
|
||||||
|
page.all('.clone-btn').first.click
|
||||||
|
visit admin_procedures_draft_path
|
||||||
|
expect(page.find_by_id('procedures')['data-item-count']).to eq('2')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
feature 'as an administrateur I wanna create a new procedure', js: true do
|
feature 'As an administrateur I wanna create a new procedure', js: true do
|
||||||
|
|
||||||
let(:administrateur) { create(:administrateur) }
|
let(:administrateur) { create(:administrateur) }
|
||||||
|
|
||||||
|
|
25
spec/features/backoffice/invitation_spec.rb
Normal file
25
spec/features/backoffice/invitation_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'As an Accompagnateur I can send invitations from dossiers', js: true do
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:gestionnaire) { create(:gestionnaire) }
|
||||||
|
let(:procedure_1) { create(:procedure, :with_type_de_champ, libelle: 'procedure 1') }
|
||||||
|
|
||||||
|
before 'Assign procedures to Accompagnateur and generating dossiers for each' do
|
||||||
|
create :assign_to, gestionnaire: gestionnaire, procedure: procedure_1
|
||||||
|
Dossier.create(procedure_id: procedure_1.id.to_s, user: user, state: 'initiated')
|
||||||
|
login_as gestionnaire, scope: :gestionnaire
|
||||||
|
visit backoffice_dossier_path(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'On dossier show' do
|
||||||
|
|
||||||
|
scenario 'Sending invitation' do
|
||||||
|
page.find('#invitations').click
|
||||||
|
page.find('#invitation-email').set('toto@email.com')
|
||||||
|
page.find('#send-invitation .btn-success').trigger('click')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
24
spec/features/users/invitation_spec.rb
Normal file
24
spec/features/users/invitation_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'As a User I can send invitations from dossiers', js: true do
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:procedure_1) { create(:procedure, :with_type_de_champ, libelle: 'procedure 1') }
|
||||||
|
|
||||||
|
before 'Assign procedures to Accompagnateur and generating dossiers for each' do
|
||||||
|
Dossier.create(procedure_id: procedure_1.id.to_s, user: user, state: 'initiated')
|
||||||
|
login_as user, scope: :user
|
||||||
|
visit users_dossier_recapitulatif_path(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'On dossier show' do
|
||||||
|
|
||||||
|
scenario 'Sending invitation' do
|
||||||
|
page.find('#invitations').click
|
||||||
|
fill_in 'invitation-email', with: 'toto@email.com'
|
||||||
|
page.find('#send-invitation .btn-success').trigger('click')
|
||||||
|
save_and_open_page
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,6 @@ describe Dossier do
|
||||||
|
|
||||||
describe 'database columns' do
|
describe 'database columns' do
|
||||||
it { is_expected.to have_db_column(:autorisation_donnees) }
|
it { is_expected.to have_db_column(:autorisation_donnees) }
|
||||||
it { is_expected.to have_db_column(:nom_projet) }
|
|
||||||
it { is_expected.to have_db_column(:created_at) }
|
it { is_expected.to have_db_column(:created_at) }
|
||||||
it { is_expected.to have_db_column(:updated_at) }
|
it { is_expected.to have_db_column(:updated_at) }
|
||||||
it { is_expected.to have_db_column(:state) }
|
it { is_expected.to have_db_column(:state) }
|
||||||
|
@ -27,6 +26,7 @@ describe Dossier do
|
||||||
it { is_expected.to belong_to(:user) }
|
it { is_expected.to belong_to(:user) }
|
||||||
it { is_expected.to have_many(:invites) }
|
it { is_expected.to have_many(:invites) }
|
||||||
it { is_expected.to have_many(:follows) }
|
it { is_expected.to have_many(:follows) }
|
||||||
|
it { is_expected.to have_many(:notifications) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'delegation' do
|
describe 'delegation' do
|
||||||
|
|
|
@ -208,4 +208,40 @@ describe Gestionnaire, type: :model do
|
||||||
expect(admin.valid_password?('super secret')).to be(true)
|
expect(admin.valid_password?('super secret')).to be(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#notifications_for' do
|
||||||
|
subject { gestionnaire.notifications_for procedure }
|
||||||
|
|
||||||
|
context 'when gestionnaire follow any dossier' do
|
||||||
|
it { is_expected.to eq 0 }
|
||||||
|
it { expect(gestionnaire.follows.count).to eq 0 }
|
||||||
|
it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).not_to receive(:inject)
|
||||||
|
subject }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gestionnaire follow any dossier into the procedure past in params' do
|
||||||
|
before do
|
||||||
|
create :follow, gestionnaire: gestionnaire, dossier: create(:dossier, procedure: procedure_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq 0 }
|
||||||
|
it { expect(gestionnaire.follows.count).to eq 1 }
|
||||||
|
it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).not_to receive(:inject)
|
||||||
|
subject }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gestionnaire follow a dossier with a notification into the procedure past in params' do
|
||||||
|
let(:dossier) { create(:dossier, procedure: procedure, state: 'initiated') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create :follow, gestionnaire: gestionnaire, dossier: dossier
|
||||||
|
create :notification, dossier: dossier
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq 1 }
|
||||||
|
it { expect(gestionnaire.follows.count).to eq 1 }
|
||||||
|
it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).to receive(:inject)
|
||||||
|
subject }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
11
spec/models/notification_spec.rb
Normal file
11
spec/models/notification_spec.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Notification do
|
||||||
|
it { is_expected.to have_db_column(:already_read) }
|
||||||
|
it { is_expected.to have_db_column(:liste) }
|
||||||
|
it { is_expected.to have_db_column(:type_notif) }
|
||||||
|
it { is_expected.to have_db_column(:created_at) }
|
||||||
|
it { is_expected.to have_db_column(:updated_at) }
|
||||||
|
|
||||||
|
it { is_expected.to belong_to(:dossier) }
|
||||||
|
end
|
29
spec/services/notification_service_spec.rb
Normal file
29
spec/services/notification_service_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe NotificationService do
|
||||||
|
|
||||||
|
describe '.notify' do
|
||||||
|
let(:dossier) { create :dossier }
|
||||||
|
let(:service) { described_class.new type_notif, dossier.id }
|
||||||
|
|
||||||
|
subject { service.notify }
|
||||||
|
|
||||||
|
context 'when is the first notification for dossier_id and type_notif and alread_read is false' do
|
||||||
|
let(:type_notif) { 'commentaire' }
|
||||||
|
|
||||||
|
it { expect { subject }.to change(Notification, :count).by (1) }
|
||||||
|
|
||||||
|
context 'when is not the first notification' do
|
||||||
|
before do
|
||||||
|
create :notification, dossier: dossier, type_notif: type_notif
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect { subject }.to change(Notification, :count).by (0) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'text_for_notif' do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
end
|
|
@ -41,7 +41,7 @@ Capybara.register_driver :poltergeist do |app|
|
||||||
Capybara::Poltergeist::Driver.new(app, js_errors: true, port: 44_678 + ENV['TEST_ENV_NUMBER'].to_i, phantomjs_options: ['--proxy-type=none'], timeout: 180)
|
Capybara::Poltergeist::Driver.new(app, js_errors: true, port: 44_678 + ENV['TEST_ENV_NUMBER'].to_i, phantomjs_options: ['--proxy-type=none'], timeout: 180)
|
||||||
end
|
end
|
||||||
|
|
||||||
#ActiveSupport::Deprecation.silenced = true
|
ActiveSupport::Deprecation.silenced = true
|
||||||
|
|
||||||
Capybara.default_max_wait_time = 1
|
Capybara.default_max_wait_time = 1
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,6 @@ describe 'admin/gestionnaires/index.html.haml', type: :view do
|
||||||
array: true))
|
array: true))
|
||||||
render
|
render
|
||||||
end
|
end
|
||||||
it { expect(rendered).to match(/plop\d+@plop.com/) }
|
it { expect(rendered).to match(/gest\d+@plop.com/) }
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Reference in a new issue