Merge branch 'dev' into 4482-echec-initilaisation-env-dev
This commit is contained in:
commit
8f5203cc2e
89 changed files with 1265 additions and 505 deletions
4
Gemfile
4
Gemfile
|
@ -13,12 +13,9 @@ gem 'bcrypt'
|
||||||
gem 'bootstrap-sass', '>= 3.4.1'
|
gem 'bootstrap-sass', '>= 3.4.1'
|
||||||
gem 'bootstrap-wysihtml5-rails', '~> 0.3.3.8'
|
gem 'bootstrap-wysihtml5-rails', '~> 0.3.3.8'
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'carrierwave'
|
|
||||||
gem 'carrierwave-i18n'
|
|
||||||
gem 'chartkick'
|
gem 'chartkick'
|
||||||
gem 'chunky_png'
|
gem 'chunky_png'
|
||||||
gem 'clamav-client', require: 'clamav/client'
|
gem 'clamav-client', require: 'clamav/client'
|
||||||
gem 'copy_carrierwave_file'
|
|
||||||
gem 'daemons'
|
gem 'daemons'
|
||||||
gem 'deep_cloneable' # Enable deep clone of active record models
|
gem 'deep_cloneable' # Enable deep clone of active record models
|
||||||
gem 'delayed_cron_job' # Cron jobs
|
gem 'delayed_cron_job' # Cron jobs
|
||||||
|
@ -93,6 +90,7 @@ group :test do
|
||||||
gem 'shoulda-matchers', require: false
|
gem 'shoulda-matchers', require: false
|
||||||
gem 'timecop'
|
gem 'timecop'
|
||||||
gem 'vcr'
|
gem 'vcr'
|
||||||
|
gem 'webdrivers', '~> 4.0'
|
||||||
gem 'webmock'
|
gem 'webmock'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
115
Gemfile.lock
115
Gemfile.lock
|
@ -20,25 +20,25 @@ GEM
|
||||||
specs:
|
specs:
|
||||||
aasm (5.0.1)
|
aasm (5.0.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
actioncable (5.2.2.1)
|
actioncable (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailer (5.2.2.1)
|
actionmailer (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.2.2.1)
|
activejob (= 5.2.3)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.2.2.1)
|
actionpack (5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.2.2.1)
|
actionview (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
|
@ -51,25 +51,25 @@ GEM
|
||||||
activemodel (>= 4.1, < 6)
|
activemodel (>= 4.1, < 6)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (5.2.2.1)
|
activejob (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.2.2.1)
|
activemodel (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
activerecord (5.2.2.1)
|
activerecord (5.2.3)
|
||||||
activemodel (= 5.2.2.1)
|
activemodel (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
arel (>= 9.0)
|
arel (>= 9.0)
|
||||||
activestorage (5.2.2.1)
|
activestorage (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
activerecord (= 5.2.2.1)
|
activerecord (= 5.2.3)
|
||||||
marcel (~> 0.3.1)
|
marcel (~> 0.3.1)
|
||||||
activestorage-openstack (1.0.0)
|
activestorage-openstack (1.2.0)
|
||||||
fog-openstack (~> 1.0)
|
fog-openstack (~> 1.0)
|
||||||
marcel
|
marcel
|
||||||
mime-types
|
mime-types
|
||||||
rails (<= 6)
|
rails (>= 5.2.2)
|
||||||
activesupport (5.2.2.1)
|
activesupport (5.2.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
@ -136,11 +136,6 @@ GEM
|
||||||
capybara-selenium (0.0.6)
|
capybara-selenium (0.0.6)
|
||||||
capybara
|
capybara
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
carrierwave (1.3.1)
|
|
||||||
activemodel (>= 4.0.0)
|
|
||||||
activesupport (>= 4.0.0)
|
|
||||||
mime-types (>= 1.16)
|
|
||||||
carrierwave-i18n (0.2.0)
|
|
||||||
case_transform (0.2)
|
case_transform (0.2)
|
||||||
activesupport
|
activesupport
|
||||||
chartkick (3.2.0)
|
chartkick (3.2.0)
|
||||||
|
@ -158,11 +153,9 @@ GEM
|
||||||
coffee-script-source (1.12.2)
|
coffee-script-source (1.12.2)
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
connection_pool (2.2.2)
|
connection_pool (2.2.2)
|
||||||
copy_carrierwave_file (1.3.0)
|
|
||||||
carrierwave (>= 0.9)
|
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
crass (1.0.4)
|
crass (1.0.5)
|
||||||
css_parser (1.6.0)
|
css_parser (1.6.0)
|
||||||
addressable
|
addressable
|
||||||
curb (0.9.10)
|
curb (0.9.10)
|
||||||
|
@ -203,7 +196,7 @@ GEM
|
||||||
em-websocket (0.5.1)
|
em-websocket (0.5.1)
|
||||||
eventmachine (>= 0.12.9)
|
eventmachine (>= 0.12.9)
|
||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0.6.0)
|
||||||
erubi (1.8.0)
|
erubi (1.9.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
ethon (0.11.0)
|
ethon (0.11.0)
|
||||||
ffi (>= 1.3.0)
|
ffi (>= 1.3.0)
|
||||||
|
@ -311,7 +304,7 @@ GEM
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.6.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ipaddress (0.8.3)
|
ipaddress (0.8.3)
|
||||||
jaro_winkler (1.5.2)
|
jaro_winkler (1.5.2)
|
||||||
|
@ -320,7 +313,7 @@ GEM
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
json (2.2.0)
|
json (2.2.0)
|
||||||
json-jwt (1.10.0)
|
json-jwt (1.11.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
aes_key_wrap
|
aes_key_wrap
|
||||||
bindata
|
bindata
|
||||||
|
@ -356,7 +349,7 @@ GEM
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
logstash-event (1.2.02)
|
logstash-event (1.2.02)
|
||||||
loofah (2.2.3)
|
loofah (2.3.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lumberjack (1.0.13)
|
lumberjack (1.0.13)
|
||||||
|
@ -375,7 +368,7 @@ GEM
|
||||||
mimemagic (0.3.3)
|
mimemagic (0.3.3)
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.11.3)
|
minitest (5.13.0)
|
||||||
momentjs-rails (2.20.1)
|
momentjs-rails (2.20.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
multi_json (1.14.1)
|
multi_json (1.14.1)
|
||||||
|
@ -384,8 +377,8 @@ GEM
|
||||||
mustermann (1.0.3)
|
mustermann (1.0.3)
|
||||||
nenv (0.3.0)
|
nenv (0.3.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.3.1)
|
nio4r (2.5.2)
|
||||||
nokogiri (1.10.4)
|
nokogiri (1.10.5)
|
||||||
mini_portile2 (~> 2.4.0)
|
mini_portile2 (~> 2.4.0)
|
||||||
notiffany (0.1.1)
|
notiffany (0.1.1)
|
||||||
nenv (~> 0.1)
|
nenv (~> 0.1)
|
||||||
|
@ -469,18 +462,18 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (5.2.2.1)
|
rails (5.2.3)
|
||||||
actioncable (= 5.2.2.1)
|
actioncable (= 5.2.3)
|
||||||
actionmailer (= 5.2.2.1)
|
actionmailer (= 5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.2.2.1)
|
activejob (= 5.2.3)
|
||||||
activemodel (= 5.2.2.1)
|
activemodel (= 5.2.3)
|
||||||
activerecord (= 5.2.2.1)
|
activerecord (= 5.2.3)
|
||||||
activestorage (= 5.2.2.1)
|
activestorage (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.2.2.1)
|
railties (= 5.2.3)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.4)
|
rails-controller-testing (1.0.4)
|
||||||
actionpack (>= 5.0.1.x)
|
actionpack (>= 5.0.1.x)
|
||||||
|
@ -489,14 +482,14 @@ GEM
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.2.0)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.2, >= 2.2.2)
|
loofah (~> 2.3)
|
||||||
rails-i18n (5.1.2)
|
rails-i18n (5.1.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 5.0, < 6)
|
railties (>= 5.0, < 6)
|
||||||
railties (5.2.2.1)
|
railties (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.19.0, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
|
@ -678,6 +671,10 @@ GEM
|
||||||
activemodel (>= 5.0)
|
activemodel (>= 5.0)
|
||||||
bindex (>= 0.4.0)
|
bindex (>= 0.4.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
|
webdrivers (4.1.3)
|
||||||
|
nokogiri (~> 1.6)
|
||||||
|
rubyzip (>= 1.3.0)
|
||||||
|
selenium-webdriver (>= 3.0, < 4.0)
|
||||||
webfinger (1.1.0)
|
webfinger (1.1.0)
|
||||||
activesupport
|
activesupport
|
||||||
httpclient (>= 2.4)
|
httpclient (>= 2.4)
|
||||||
|
@ -689,9 +686,9 @@ GEM
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
websocket-driver (0.7.0)
|
websocket-driver (0.7.1)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.3)
|
websocket-extensions (0.1.4)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
xray-rails (0.3.1)
|
xray-rails (0.3.1)
|
||||||
|
@ -726,12 +723,9 @@ DEPENDENCIES
|
||||||
capybara-email
|
capybara-email
|
||||||
capybara-screenshot
|
capybara-screenshot
|
||||||
capybara-selenium
|
capybara-selenium
|
||||||
carrierwave
|
|
||||||
carrierwave-i18n
|
|
||||||
chartkick
|
chartkick
|
||||||
chunky_png
|
chunky_png
|
||||||
clamav-client
|
clamav-client
|
||||||
copy_carrierwave_file
|
|
||||||
daemons
|
daemons
|
||||||
database_cleaner
|
database_cleaner
|
||||||
deep_cloneable
|
deep_cloneable
|
||||||
|
@ -812,6 +806,7 @@ DEPENDENCIES
|
||||||
vcr
|
vcr
|
||||||
warden
|
warden
|
||||||
web-console
|
web-console
|
||||||
|
webdrivers (~> 4.0)
|
||||||
webmock
|
webmock
|
||||||
webpacker
|
webpacker
|
||||||
xray-rails
|
xray-rails
|
||||||
|
|
|
@ -43,8 +43,14 @@ Les informations nécessaire à l'initialisation de la base doivent être pré-c
|
||||||
> create user tps_test with password 'tps_test' superuser;
|
> create user tps_test with password 'tps_test' superuser;
|
||||||
> \q
|
> \q
|
||||||
|
|
||||||
|
|
||||||
### Initialisation de l'environnement de développement
|
### Initialisation de l'environnement de développement
|
||||||
|
|
||||||
|
Sous Ubuntu, certains packages doivent être installés au préalable :
|
||||||
|
|
||||||
|
sudo apt-get install libcurl3 libcurl3-gnutls libcurl4-openssl-dev libcurl4-gnutls-dev zlib1g-dev
|
||||||
|
|
||||||
|
|
||||||
Afin d'initialiser l'environnement de développement, exécutez la commande suivante :
|
Afin d'initialiser l'environnement de développement, exécutez la commande suivante :
|
||||||
|
|
||||||
bin/setup
|
bin/setup
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import "colors";
|
||||||
@import "constants";
|
@import "constants";
|
||||||
|
|
||||||
%horizontal-list {
|
%horizontal-list {
|
||||||
|
@ -17,3 +18,10 @@
|
||||||
animation-fill-mode: forwards;
|
animation-fill-mode: forwards;
|
||||||
animation-duration: 0.3s;
|
animation-duration: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%outline {
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: 3px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
@import "constants";
|
@import "constants";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
|
@ -20,11 +23,6 @@
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
filter: saturate(50%);
|
filter: saturate(50%);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
@import "colors";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -14,5 +17,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
@import "constants";
|
@import "constants";
|
||||||
@import "colors";
|
@import "colors";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -177,6 +178,8 @@
|
||||||
|
|
||||||
input[type=checkbox],
|
input[type=checkbox],
|
||||||
input[type=radio] {
|
input[type=radio] {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
margin-bottom: 2 * $default-padding;
|
margin-bottom: 2 * $default-padding;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h4.help-dropdown-title {
|
.help-dropdown-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: $blue;
|
color: $blue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,6 +365,11 @@ $cta-panel-button-border-size: 2px;
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: 3px solid #FFFFFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta-panel-button-blue {
|
.cta-panel-button-blue {
|
||||||
|
|
|
@ -113,3 +113,19 @@ footer {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-site-links {
|
||||||
|
li {
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "-";
|
||||||
|
margin: $default-spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child::before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
12
app/assets/stylesheets/new_design/title.scss
Normal file
12
app/assets/stylesheets/new_design/title.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
@import "constants";
|
||||||
|
|
||||||
|
.huge-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
@media (max-width: $two-columns-breakpoint) {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,8 +17,9 @@ class Admin::AssignsController < AdminController
|
||||||
|
|
||||||
not_assign_scope = current_administrateur.instructeurs.where.not(id: assign_scope.ids)
|
not_assign_scope = current_administrateur.instructeurs.where.not(id: assign_scope.ids)
|
||||||
|
|
||||||
if params[:filter]
|
if params[:filter].present?
|
||||||
not_assign_scope = not_assign_scope.where("email LIKE ?", "%#{params[:filter]}%")
|
filter = params[:filter].downcase.strip
|
||||||
|
not_assign_scope = not_assign_scope.where('users.email LIKE ?', "%#{filter}%")
|
||||||
end
|
end
|
||||||
|
|
||||||
@instructeurs_not_assign = smart_listing_create :instructeurs_not_assign,
|
@instructeurs_not_assign = smart_listing_create :instructeurs_not_assign,
|
||||||
|
|
|
@ -2,6 +2,7 @@ class API::V1::DossiersController < APIController
|
||||||
before_action :fetch_procedure_and_check_token
|
before_action :fetch_procedure_and_check_token
|
||||||
|
|
||||||
DEFAULT_PAGE_SIZE = 100
|
DEFAULT_PAGE_SIZE = 100
|
||||||
|
MAX_PAGE_SIZE = 1000
|
||||||
ORDER_DIRECTIONS = { 'asc' => :asc, 'desc' => :desc }
|
ORDER_DIRECTIONS = { 'asc' => :asc, 'desc' => :desc }
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -33,7 +34,12 @@ class API::V1::DossiersController < APIController
|
||||||
end
|
end
|
||||||
|
|
||||||
def per_page # inherited value from will_paginate
|
def per_page # inherited value from will_paginate
|
||||||
[params[:resultats_par_page]&.to_i || DEFAULT_PAGE_SIZE, 1000].min
|
resultats_par_page = params[:resultats_par_page]&.to_i
|
||||||
|
if resultats_par_page && resultats_par_page > 0
|
||||||
|
[resultats_par_page, MAX_PAGE_SIZE].min
|
||||||
|
else
|
||||||
|
DEFAULT_PAGE_SIZE
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_procedure_and_check_token
|
def fetch_procedure_and_check_token
|
||||||
|
@ -47,7 +53,7 @@ class API::V1::DossiersController < APIController
|
||||||
end
|
end
|
||||||
|
|
||||||
order = ORDER_DIRECTIONS.fetch(params[:order], :asc)
|
order = ORDER_DIRECTIONS.fetch(params[:order], :asc)
|
||||||
@dossiers = @procedure.dossiers.state_not_brouillon.order_for_api(order)
|
@dossiers = @procedure.dossiers.state_not_brouillon.order_by_created_at(order)
|
||||||
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: :not_found
|
render json: {}, status: :not_found
|
||||||
|
|
|
@ -250,7 +250,7 @@ class ApplicationController < ActionController::Base
|
||||||
payload: {
|
payload: {
|
||||||
DS_SIGN_IN_COUNT: current_user&.sign_in_count,
|
DS_SIGN_IN_COUNT: current_user&.sign_in_count,
|
||||||
DS_CREATED_AT: current_administrateur&.created_at,
|
DS_CREATED_AT: current_administrateur&.created_at,
|
||||||
DS_ACTIVE: current_administrateur&.active?,
|
DS_ACTIVE: current_user&.active?,
|
||||||
DS_ID: current_administrateur&.id,
|
DS_ID: current_administrateur&.id,
|
||||||
DS_GESTIONNAIRE_ID: current_instructeur&.id,
|
DS_GESTIONNAIRE_ID: current_instructeur&.id,
|
||||||
DS_ROLES: current_user_roles
|
DS_ROLES: current_user_roles
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_instructeur
|
def add_instructeur
|
||||||
@instructeur = Instructeur.find_by(email: instructeur_email) ||
|
@instructeur = Instructeur.by_email(instructeur_email) ||
|
||||||
create_instructeur(instructeur_email)
|
create_instructeur(instructeur_email)
|
||||||
|
|
||||||
if groupe_instructeur.instructeurs.include?(@instructeur)
|
if groupe_instructeur.instructeurs.include?(@instructeur)
|
||||||
|
|
|
@ -47,7 +47,7 @@ module NewAdministrateur
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_instructeur
|
def add_instructeur
|
||||||
@instructeur = Instructeur.find_by(email: instructeur_email) ||
|
@instructeur = Instructeur.by_email(instructeur_email) ||
|
||||||
create_instructeur(instructeur_email)
|
create_instructeur(instructeur_email)
|
||||||
|
|
||||||
if groupe_instructeur.instructeurs.include?(@instructeur)
|
if groupe_instructeur.instructeurs.include?(@instructeur)
|
||||||
|
|
|
@ -48,8 +48,11 @@ module Users
|
||||||
end
|
end
|
||||||
|
|
||||||
def attestation
|
def attestation
|
||||||
if dossier.attestation.pdf.attached?
|
if dossier.attestation&.pdf&.attached?
|
||||||
redirect_to url_for(dossier.attestation.pdf)
|
redirect_to url_for(dossier.attestation.pdf)
|
||||||
|
else
|
||||||
|
flash.notice = "L'attestation n'est plus disponible sur ce dossier."
|
||||||
|
redirect_to dossier_path(dossier)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@ class ServiceDashboard < Administrate::BaseDashboard
|
||||||
email: Field::String,
|
email: Field::String,
|
||||||
telephone: Field::String,
|
telephone: Field::String,
|
||||||
horaires: Field::String,
|
horaires: Field::String,
|
||||||
adresse: Field::String,
|
adresse: Field::String
|
||||||
siret: Field::String
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
# COLLECTION_ATTRIBUTES
|
||||||
|
@ -45,8 +44,7 @@ class ServiceDashboard < Administrate::BaseDashboard
|
||||||
:email,
|
:email,
|
||||||
:telephone,
|
:telephone,
|
||||||
:horaires,
|
:horaires,
|
||||||
:adresse,
|
:adresse
|
||||||
:siret
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module Mutations
|
module Mutations
|
||||||
class BaseMutation < GraphQL::Schema::Mutation
|
class BaseMutation < GraphQL::Schema::RelayClassicMutation
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
41
app/graphql/mutations/create_direct_upload.rb
Normal file
41
app/graphql/mutations/create_direct_upload.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
module Mutations
|
||||||
|
class CreateDirectUpload < Mutations::BaseMutation
|
||||||
|
description "File information required to prepare a direct upload"
|
||||||
|
|
||||||
|
argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType
|
||||||
|
argument :filename, String, "Original file name", required: true
|
||||||
|
argument :byte_size, Int, "File size (bytes)", required: true
|
||||||
|
argument :checksum, String, "MD5 file checksum as base64", required: true
|
||||||
|
argument :content_type, String, "File content type", required: true
|
||||||
|
|
||||||
|
class DirectUpload < Types::BaseObject
|
||||||
|
description "Represents direct upload credentials"
|
||||||
|
|
||||||
|
field :url, String, "Upload URL", null: false
|
||||||
|
field :headers, String, "HTTP request headers (JSON-encoded)", null: false
|
||||||
|
field :blob_id, ID, "Created blob record ID", null: false
|
||||||
|
field :signed_blob_id, ID, "Created blob record signed ID", null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
field :direct_upload, DirectUpload, null: false
|
||||||
|
|
||||||
|
def resolve(filename:, byte_size:, checksum:, content_type:, dossier:)
|
||||||
|
blob = ActiveStorage::Blob.create_before_direct_upload!(
|
||||||
|
filename: filename,
|
||||||
|
byte_size: byte_size,
|
||||||
|
checksum: checksum,
|
||||||
|
content_type: content_type
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
direct_upload: {
|
||||||
|
url: blob.service_url_for_direct_upload,
|
||||||
|
# NOTE: we pass headers as JSON since they have no schema
|
||||||
|
headers: blob.service_headers_for_direct_upload.to_json,
|
||||||
|
blob_id: blob.id,
|
||||||
|
signed_blob_id: blob.signed_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
app/graphql/mutations/dossier_envoyer_message.rb
Normal file
27
app/graphql/mutations/dossier_envoyer_message.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierEnvoyerMessage < Mutations::BaseMutation
|
||||||
|
description "Envoyer un message à l'usager du dossier."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, required: true, loads: Types::ProfileType
|
||||||
|
argument :body, String, required: true
|
||||||
|
argument :attachment, ID, required: false
|
||||||
|
|
||||||
|
field :message, Types::MessageType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:, body:, attachment: nil)
|
||||||
|
message = CommentaireService.build(instructeur, dossier, body: body, piece_jointe: attachment)
|
||||||
|
|
||||||
|
if message.save
|
||||||
|
{ message: message }
|
||||||
|
else
|
||||||
|
{ errors: message.errors.full_messages }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:, body:)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,27 +10,66 @@ type Avis {
|
||||||
type CarteChamp implements Champ {
|
type CarteChamp implements Champ {
|
||||||
geoAreas: [GeoArea!]!
|
geoAreas: [GeoArea!]!
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Champ {
|
interface Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChampDescriptor {
|
type ChampDescriptor {
|
||||||
|
"""
|
||||||
|
Description du champ.
|
||||||
|
"""
|
||||||
description: String
|
description: String
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Est-ce que le champ est obligatoire ?
|
||||||
|
"""
|
||||||
required: Boolean!
|
required: Boolean!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Type de la valeur du champ.
|
||||||
|
"""
|
||||||
type: TypeDeChamp!
|
type: TypeDeChamp!
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckboxChamp implements Champ {
|
type CheckboxChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Boolean!
|
value: Boolean!
|
||||||
}
|
}
|
||||||
|
@ -40,16 +79,78 @@ GeoJSON coordinates
|
||||||
"""
|
"""
|
||||||
scalar Coordinates
|
scalar Coordinates
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of CreateDirectUpload
|
||||||
|
"""
|
||||||
|
input CreateDirectUploadInput {
|
||||||
|
"""
|
||||||
|
File size (bytes)
|
||||||
|
"""
|
||||||
|
byteSize: Int!
|
||||||
|
|
||||||
|
"""
|
||||||
|
MD5 file checksum as base64
|
||||||
|
"""
|
||||||
|
checksum: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
File content type
|
||||||
|
"""
|
||||||
|
contentType: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossier ID
|
||||||
|
"""
|
||||||
|
dossierId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Original file name
|
||||||
|
"""
|
||||||
|
filename: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of CreateDirectUpload
|
||||||
|
"""
|
||||||
|
type CreateDirectUploadPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
directUpload: DirectUpload!
|
||||||
|
}
|
||||||
|
|
||||||
type DateChamp implements Champ {
|
type DateChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: ISO8601DateTime
|
value: ISO8601DateTime
|
||||||
}
|
}
|
||||||
|
|
||||||
type DecimalNumberChamp implements Champ {
|
type DecimalNumberChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Float
|
value: Float
|
||||||
}
|
}
|
||||||
|
@ -82,25 +183,35 @@ type Demarche {
|
||||||
"""
|
"""
|
||||||
before: String
|
before: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossiers déposés depuis la date.
|
||||||
|
"""
|
||||||
|
createdSince: ISO8601DateTime
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns the first _n_ elements from the list.
|
Returns the first _n_ elements from the list.
|
||||||
"""
|
"""
|
||||||
first: Int
|
first: Int
|
||||||
|
|
||||||
"""
|
|
||||||
Filtrer les dossiers par ID.
|
|
||||||
"""
|
|
||||||
ids: [ID!]
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns the last _n_ elements from the list.
|
Returns the last _n_ elements from the list.
|
||||||
"""
|
"""
|
||||||
last: Int
|
last: Int
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dossiers crées depuis la date.
|
L'ordre des dossiers.
|
||||||
"""
|
"""
|
||||||
since: ISO8601DateTime
|
order: Order = ASC
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossiers avec statut.
|
||||||
|
"""
|
||||||
|
state: DossierState
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossiers mis à jour depuis la date.
|
||||||
|
"""
|
||||||
|
updatedSince: ISO8601DateTime
|
||||||
): DossierConnection!
|
): DossierConnection!
|
||||||
groupeInstructeurs: [GroupeInstructeur!]!
|
groupeInstructeurs: [GroupeInstructeur!]!
|
||||||
id: ID!
|
id: ID!
|
||||||
|
@ -108,7 +219,11 @@ type Demarche {
|
||||||
"""
|
"""
|
||||||
Le numero de la démarche.
|
Le numero de la démarche.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
|
|
||||||
|
"""
|
||||||
|
L'état de la démarche.
|
||||||
|
"""
|
||||||
state: DemarcheState!
|
state: DemarcheState!
|
||||||
title: String!
|
title: String!
|
||||||
updatedAt: ISO8601DateTime!
|
updatedAt: ISO8601DateTime!
|
||||||
|
@ -131,6 +246,31 @@ enum DemarcheState {
|
||||||
publiee
|
publiee
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Represents direct upload credentials
|
||||||
|
"""
|
||||||
|
type DirectUpload {
|
||||||
|
"""
|
||||||
|
Created blob record ID
|
||||||
|
"""
|
||||||
|
blobId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
HTTP request headers (JSON-encoded)
|
||||||
|
"""
|
||||||
|
headers: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Created blob record signed ID
|
||||||
|
"""
|
||||||
|
signedBlobId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Upload URL
|
||||||
|
"""
|
||||||
|
url: String!
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Un dossier
|
Un dossier
|
||||||
"""
|
"""
|
||||||
|
@ -163,7 +303,7 @@ type Dossier {
|
||||||
"""
|
"""
|
||||||
Le numero du dossier.
|
Le numero du dossier.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L'état du dossier.
|
L'état du dossier.
|
||||||
|
@ -212,10 +352,45 @@ type DossierEdge {
|
||||||
node: Dossier
|
node: Dossier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierEnvoyerMessage
|
||||||
|
"""
|
||||||
|
input DossierEnvoyerMessageInput {
|
||||||
|
attachment: ID
|
||||||
|
body: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossierId: ID!
|
||||||
|
instructeurId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierEnvoyerMessage
|
||||||
|
"""
|
||||||
|
type DossierEnvoyerMessagePayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
errors: [ValidationError!]
|
||||||
|
message: Message
|
||||||
|
}
|
||||||
|
|
||||||
type DossierLinkChamp implements Champ {
|
type DossierLinkChamp implements Champ {
|
||||||
dossier: Dossier
|
dossier: Dossier
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,22 +429,17 @@ interface GeoArea {
|
||||||
|
|
||||||
enum GeoAreaSource {
|
enum GeoAreaSource {
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.cadastre
|
Parcelle cadastrale
|
||||||
"""
|
"""
|
||||||
cadastre
|
cadastre
|
||||||
|
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.parcelle_agricole
|
Quartier prioritaire
|
||||||
"""
|
|
||||||
parcelle_agricole
|
|
||||||
|
|
||||||
"""
|
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.quartier_prioritaire
|
|
||||||
"""
|
"""
|
||||||
quartier_prioritaire
|
quartier_prioritaire
|
||||||
|
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.selection_utilisateur
|
Sélection utilisateur
|
||||||
"""
|
"""
|
||||||
selection_utilisateur
|
selection_utilisateur
|
||||||
}
|
}
|
||||||
|
@ -295,16 +465,32 @@ scalar ISO8601DateTime
|
||||||
|
|
||||||
type IntegerNumberChamp implements Champ {
|
type IntegerNumberChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Int
|
value: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinkedDropDownListChamp implements Champ {
|
type LinkedDropDownListChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
primaryValue: String
|
primaryValue: String
|
||||||
secondaryValue: String
|
secondaryValue: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,12 +504,41 @@ type Message {
|
||||||
|
|
||||||
type MultipleDropDownListChamp implements Champ {
|
type MultipleDropDownListChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
values: [String!]!
|
values: [String!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
"""
|
||||||
|
File information required to prepare a direct upload
|
||||||
|
"""
|
||||||
|
createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Envoyer un message à l'usager du dossier.
|
||||||
|
"""
|
||||||
|
dossierEnvoyerMessage(input: DossierEnvoyerMessageInput!): DossierEnvoyerMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Order {
|
||||||
|
"""
|
||||||
|
L‘ordre ascendant.
|
||||||
|
"""
|
||||||
|
ASC
|
||||||
|
|
||||||
|
"""
|
||||||
|
L‘ordre descendant.
|
||||||
|
"""
|
||||||
|
DESC
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -383,7 +598,15 @@ type PersonneMorale {
|
||||||
|
|
||||||
type PieceJustificativeChamp implements Champ {
|
type PieceJustificativeChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
url: URL
|
url: URL
|
||||||
}
|
}
|
||||||
|
@ -410,7 +633,7 @@ type Query {
|
||||||
"""
|
"""
|
||||||
Numéro de la démarche.
|
Numéro de la démarche.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
): Demarche!
|
): Demarche!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -420,14 +643,22 @@ type Query {
|
||||||
"""
|
"""
|
||||||
Numéro du dossier.
|
Numéro du dossier.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
): Dossier!
|
): Dossier!
|
||||||
}
|
}
|
||||||
|
|
||||||
type RepetitionChamp implements Champ {
|
type RepetitionChamp implements Champ {
|
||||||
champs: [Champ!]!
|
champs: [Champ!]!
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,13 +671,29 @@ type SelectionUtilisateur implements GeoArea {
|
||||||
type SiretChamp implements Champ {
|
type SiretChamp implements Champ {
|
||||||
etablissement: PersonneMorale
|
etablissement: PersonneMorale
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextChamp implements Champ {
|
type TextChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: String
|
value: String
|
||||||
}
|
}
|
||||||
|
@ -591,4 +838,14 @@ enum TypeDeChamp {
|
||||||
"""
|
"""
|
||||||
A valid URL, transported as a string
|
A valid URL, transported as a string
|
||||||
"""
|
"""
|
||||||
scalar URL
|
scalar URL
|
||||||
|
|
||||||
|
"""
|
||||||
|
Éreur de validation
|
||||||
|
"""
|
||||||
|
type ValidationError {
|
||||||
|
"""
|
||||||
|
A description of the error
|
||||||
|
"""
|
||||||
|
message: String!
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ module Types
|
||||||
end
|
end
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :type, TypeDeChampType, null: false, method: :type_champ
|
field :type, TypeDeChampType, "Type de la valeur du champ.", null: false, method: :type_champ
|
||||||
field :label, String, null: false, method: :libelle
|
field :label, String, "Libellé du champ.", null: false, method: :libelle
|
||||||
field :description, String, null: true
|
field :description, String, "Description du champ.", null: true
|
||||||
field :required, Boolean, null: false, method: :mandatory?
|
field :required, Boolean, "Est-ce que le champ est obligatoire ?", null: false, method: :mandatory?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,8 @@ module Types
|
||||||
include Types::BaseInterface
|
include Types::BaseInterface
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :label, String, null: false, method: :libelle
|
field :label, String, "Libellé du champ.", null: false, method: :libelle
|
||||||
field :string_value, String, null: true, method: :for_api_v2
|
field :string_value, String, "La valeur du champ sous forme texte.", null: true, method: :for_api_v2
|
||||||
|
|
||||||
definition_methods do
|
definition_methods do
|
||||||
def resolve_type(object, context)
|
def resolve_type(object, context)
|
||||||
|
|
|
@ -9,10 +9,10 @@ module Types
|
||||||
description "Une demarche"
|
description "Une demarche"
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :number, ID, "Le numero de la démarche.", null: false, method: :id
|
field :number, Int, "Le numero de la démarche.", null: false, method: :id
|
||||||
field :title, String, null: false, method: :libelle
|
field :title, String, null: false, method: :libelle
|
||||||
field :description, String, "Déscription de la démarche.", null: false
|
field :description, String, "Déscription de la démarche.", null: false
|
||||||
field :state, DemarcheState, null: false
|
field :state, DemarcheState, "L'état de la démarche.", null: false
|
||||||
|
|
||||||
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
|
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
|
@ -21,8 +21,10 @@ module Types
|
||||||
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
|
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
|
||||||
|
|
||||||
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d'une démarche.", null: false do
|
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d'une démarche.", null: false do
|
||||||
argument :ids, [ID], required: false, description: "Filtrer les dossiers par ID."
|
argument :order, Types::Order, default_value: :asc, required: false, description: "L'ordre des dossiers."
|
||||||
argument :since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers crées depuis la date."
|
argument :created_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers déposés depuis la date."
|
||||||
|
argument :updated_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers mis à jour depuis la date."
|
||||||
|
argument :state, Types::DossierType::DossierState, required: false, description: "Dossiers avec statut."
|
||||||
end
|
end
|
||||||
|
|
||||||
field :champ_descriptors, [Types::ChampDescriptorType], null: false, method: :types_de_champ
|
field :champ_descriptors, [Types::ChampDescriptorType], null: false, method: :types_de_champ
|
||||||
|
@ -36,15 +38,21 @@ module Types
|
||||||
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
|
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def dossiers(ids: nil, since: nil)
|
def dossiers(updated_since: nil, created_since: nil, state: nil, order:)
|
||||||
dossiers = object.dossiers.for_api_v2
|
dossiers = object.dossiers.state_not_brouillon.for_api_v2
|
||||||
|
|
||||||
if ids.present?
|
if state.present?
|
||||||
dossiers = dossiers.where(id: ids)
|
dossiers = dossiers.where(state: state)
|
||||||
end
|
end
|
||||||
|
|
||||||
if since.present?
|
if updated_since.present?
|
||||||
dossiers = dossiers.since(since)
|
dossiers = dossiers.updated_since(updated_since).order_by_updated_at(order)
|
||||||
|
else
|
||||||
|
if created_since.present?
|
||||||
|
dossiers = dossiers.created_since(created_since)
|
||||||
|
end
|
||||||
|
|
||||||
|
dossiers = dossiers.order_by_created_at(order)
|
||||||
end
|
end
|
||||||
|
|
||||||
dossiers
|
dossiers
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Types
|
||||||
description "Un dossier"
|
description "Un dossier"
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :number, ID, "Le numero du dossier.", null: false, method: :id
|
field :number, Int, "Le numero du dossier.", null: false, method: :id
|
||||||
field :state, DossierState, "L'état du dossier.", null: false
|
field :state, DossierState, "L'état du dossier.", null: false
|
||||||
field :updated_at, GraphQL::Types::ISO8601DateTime, "Date de dernière mise à jour.", null: false
|
field :updated_at, GraphQL::Types::ISO8601DateTime, "Date de dernière mise à jour.", null: false
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@ module Types
|
||||||
|
|
||||||
class GeoAreaSource < Types::BaseEnum
|
class GeoAreaSource < Types::BaseEnum
|
||||||
GeoArea.sources.each do |symbol_name, string_name|
|
GeoArea.sources.each do |symbol_name, string_name|
|
||||||
value(string_name,
|
if string_name != "parcelle_agricole"
|
||||||
I18n.t(symbol_name, scope: [:activerecord, :attributes, :geo_area, :source]),
|
value(string_name,
|
||||||
value: symbol_name)
|
I18n.t(symbol_name, scope: [:activerecord, :attributes, :geo_area, :source]),
|
||||||
|
value: symbol_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
module Types
|
module Types
|
||||||
class MutationType < Types::BaseObject
|
class MutationType < Types::BaseObject
|
||||||
|
field :create_direct_upload, mutation: Mutations::CreateDirectUpload
|
||||||
|
|
||||||
|
field :dossier_envoyer_message, mutation: Mutations::DossierEnvoyerMessage
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
6
app/graphql/types/order.rb
Normal file
6
app/graphql/types/order.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module Types
|
||||||
|
class Order < Types::BaseEnum
|
||||||
|
value('ASC', 'L‘ordre ascendant.', value: :asc)
|
||||||
|
value('DESC', 'L‘ordre descendant.', value: :desc)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,11 +1,11 @@
|
||||||
module Types
|
module Types
|
||||||
class QueryType < Types::BaseObject
|
class QueryType < Types::BaseObject
|
||||||
field :demarche, DemarcheType, null: false, description: "Informations concernant une démarche." do
|
field :demarche, DemarcheType, null: false, description: "Informations concernant une démarche." do
|
||||||
argument :number, ID, "Numéro de la démarche.", required: true
|
argument :number, Int, "Numéro de la démarche.", required: true
|
||||||
end
|
end
|
||||||
|
|
||||||
field :dossier, DossierType, null: false, description: "Informations sur un dossier d'une démarche." do
|
field :dossier, DossierType, null: false, description: "Informations sur un dossier d'une démarche." do
|
||||||
argument :number, ID, "Numéro du dossier.", required: true
|
argument :number, Int, "Numéro du dossier.", required: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def demarche(number:)
|
def demarche(number:)
|
||||||
|
|
11
app/graphql/types/validation_error_type.rb
Normal file
11
app/graphql/types/validation_error_type.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module Types
|
||||||
|
class ValidationErrorType < Types::BaseObject
|
||||||
|
description "Éreur de validation"
|
||||||
|
|
||||||
|
field :message, String, "A description of the error", null: false
|
||||||
|
|
||||||
|
def message
|
||||||
|
object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
module StringToHtmlHelper
|
module StringToHtmlHelper
|
||||||
def string_to_html(str)
|
def string_to_html(str, wrapper_tag = 'p')
|
||||||
html_formatted = simple_format(str)
|
html_formatted = simple_format(str, {}, { wrapper_tag: wrapper_tag })
|
||||||
with_links = html_formatted.gsub(URI.regexp, '<a target="_blank" rel="noopener" href="\0">\0</a>')
|
with_links = html_formatted.gsub(URI.regexp, '<a target="_blank" rel="noopener" href="\0">\0</a>')
|
||||||
sanitize(with_links, attributes: ['target', 'rel', 'href'])
|
sanitize(with_links, attributes: ['target', 'rel', 'href'])
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,9 @@ function addTypeDeChamp(state, typeDeChamps, insertAfter, done) {
|
||||||
state.flash.success();
|
state.flash.success();
|
||||||
done();
|
done();
|
||||||
if (insertAfter) {
|
if (insertAfter) {
|
||||||
scrollToComponent(insertAfter.target.nextElementSibling);
|
scrollToComponent(insertAfter.target.nextElementSibling, {
|
||||||
|
duration: 300
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(message => state.flash.error(message));
|
.catch(message => state.flash.error(message));
|
||||||
|
@ -219,7 +221,7 @@ function getUpdateHandler(typeDeChamp, { queue, flash }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function findItemToInsertAfter() {
|
function findItemToInsertAfter() {
|
||||||
const target = getFirstTarget();
|
const target = getLastVisibleTypeDeChamp();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
target,
|
target,
|
||||||
|
@ -227,8 +229,10 @@ function findItemToInsertAfter() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFirstTarget() {
|
function getLastVisibleTypeDeChamp() {
|
||||||
const [target] = document.querySelectorAll('[data-in-view]');
|
const typeDeChamps = document.querySelectorAll('[data-in-view]');
|
||||||
|
const target = typeDeChamps[typeDeChamps.length - 1];
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
const parentTarget = target.closest('[data-repetition]');
|
const parentTarget = target.closest('[data-repetition]');
|
||||||
if (parentTarget) {
|
if (parentTarget) {
|
||||||
|
|
|
@ -12,11 +12,14 @@ class ApiCarto::API
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.call(url, geojson)
|
def self.call(url, geojson)
|
||||||
params = geojson.to_s
|
response = Typhoeus.post(url, body: geojson.to_s, headers: { 'content-type' => 'application/json' })
|
||||||
RestClient.post(url, params, content_type: 'application/json')
|
|
||||||
|
|
||||||
rescue RestClient::InternalServerError, RestClient::BadGateway, RestClient::GatewayTimeout, RestClient::ServiceUnavailable => e
|
if response.success?
|
||||||
Rails.logger.error "[ApiCarto] Error on #{url}: #{e}"
|
response.body
|
||||||
raise RestClient::ResourceNotFound
|
else
|
||||||
|
message = response.code == 0 ? response.return_message : response.code.to_s
|
||||||
|
Rails.logger.error "[ApiCarto] Error on #{url}: #{message}"
|
||||||
|
raise RestClient::ResourceNotFound
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,11 @@ class ApplicationMailer < ActionMailer::Base
|
||||||
default from: "demarches-simplifiees.fr <#{CONTACT_EMAIL}>"
|
default from: "demarches-simplifiees.fr <#{CONTACT_EMAIL}>"
|
||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
|
|
||||||
|
# Don’t retry to send a message if the server rejects the recipient address
|
||||||
|
rescue_from Net::SMTPSyntaxError do |_error|
|
||||||
|
message.perform_deliveries = false
|
||||||
|
end
|
||||||
|
|
||||||
# Attach the procedure logo to the email (if any).
|
# Attach the procedure logo to the email (if any).
|
||||||
# Returns the attachment url.
|
# Returns the attachment url.
|
||||||
def attach_logo(procedure)
|
def attach_logo(procedure)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Administrateur < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def registration_state
|
def registration_state
|
||||||
if active?
|
if user.active?
|
||||||
'Actif'
|
'Actif'
|
||||||
elsif user.reset_password_period_valid?
|
elsif user.reset_password_period_valid?
|
||||||
'En attente'
|
'En attente'
|
||||||
|
@ -56,17 +56,7 @@ class Administrateur < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def invitation_expired?
|
def invitation_expired?
|
||||||
!active? && !user.reset_password_period_valid?
|
!user.active? && !user.reset_password_period_valid?
|
||||||
end
|
|
||||||
|
|
||||||
def self.reset_password(reset_password_token, password)
|
|
||||||
administrateur = self.reset_password_by_token({
|
|
||||||
password: password,
|
|
||||||
password_confirmation: password,
|
|
||||||
reset_password_token: reset_password_token
|
|
||||||
})
|
|
||||||
|
|
||||||
administrateur
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def owns?(procedure)
|
def owns?(procedure)
|
||||||
|
@ -80,8 +70,4 @@ class Administrateur < ApplicationRecord
|
||||||
def can_be_deleted?
|
def can_be_deleted?
|
||||||
dossiers.state_instruction_commencee.none? && procedures.none?
|
dossiers.state_instruction_commencee.none? && procedures.none?
|
||||||
end
|
end
|
||||||
|
|
||||||
def active?
|
|
||||||
user.last_sign_in_at.present?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,12 @@ class Champs::RepetitionChamp < Champ
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# We have to truncate the label here as spreadsheets have a (30 char) limit on length.
|
||||||
|
def libelle_for_export
|
||||||
|
str = "(#{type_de_champ.stable_id}) #{libelle}"
|
||||||
|
ActiveStorage::Filename.new(str).sanitized.truncate(30)
|
||||||
|
end
|
||||||
|
|
||||||
class Row < Hashie::Dash
|
class Row < Hashie::Dash
|
||||||
property :index
|
property :index
|
||||||
property :dossier_id
|
property :dossier_id
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Commentaire < ApplicationRecord
|
||||||
|
|
||||||
has_one_attached :piece_jointe
|
has_one_attached :piece_jointe
|
||||||
|
|
||||||
validates :body, presence: { message: "Votre message ne peut être vide" }
|
validates :body, presence: { message: "ne peut être vide" }
|
||||||
|
|
||||||
default_scope { order(created_at: :asc) }
|
default_scope { order(created_at: :asc) }
|
||||||
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
|
||||||
|
|
|
@ -105,7 +105,9 @@ class Dossier < ApplicationRecord
|
||||||
scope :not_archived, -> { where(archived: false) }
|
scope :not_archived, -> { where(archived: false) }
|
||||||
|
|
||||||
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
||||||
scope :order_for_api, -> (order = :asc) { order(en_construction_at: order, created_at: order, id: order) }
|
scope :order_by_created_at, -> (order = :asc) { order(en_construction_at: order, created_at: order, id: order) }
|
||||||
|
scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) }
|
||||||
|
scope :created_since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
|
||||||
|
|
||||||
scope :all_state, -> { not_archived.state_not_brouillon }
|
scope :all_state, -> { not_archived.state_not_brouillon }
|
||||||
scope :en_construction, -> { not_archived.state_en_construction }
|
scope :en_construction, -> { not_archived.state_en_construction }
|
||||||
|
@ -134,7 +136,6 @@ class Dossier < ApplicationRecord
|
||||||
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
|
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
|
||||||
scope :with_champs, -> { includes(champs: :type_de_champ) }
|
scope :with_champs, -> { includes(champs: :type_de_champ) }
|
||||||
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
|
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
|
||||||
scope :since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
|
|
||||||
scope :for_api, -> {
|
scope :for_api, -> {
|
||||||
includes(commentaires: { piece_jointe_attachment: :blob },
|
includes(commentaires: { piece_jointe_attachment: :blob },
|
||||||
champs: [
|
champs: [
|
||||||
|
@ -472,7 +473,19 @@ class Dossier < ApplicationRecord
|
||||||
log_dossier_operation(avis.claimant, :demander_un_avis, avis)
|
log_dossier_operation(avis.claimant, :demander_un_avis, avis)
|
||||||
end
|
end
|
||||||
|
|
||||||
def spreadsheet_columns
|
def spreadsheet_columns_csv
|
||||||
|
spreadsheet_columns(with_etablissement: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def spreadsheet_columns_xlsx
|
||||||
|
spreadsheet_columns
|
||||||
|
end
|
||||||
|
|
||||||
|
def spreadsheet_columns_ods
|
||||||
|
spreadsheet_columns
|
||||||
|
end
|
||||||
|
|
||||||
|
def spreadsheet_columns(with_etablissement: false)
|
||||||
columns = [
|
columns = [
|
||||||
['ID', id.to_s],
|
['ID', id.to_s],
|
||||||
['Email', user.email]
|
['Email', user.email]
|
||||||
|
@ -485,6 +498,39 @@ class Dossier < ApplicationRecord
|
||||||
['Prénom', individual&.prenom],
|
['Prénom', individual&.prenom],
|
||||||
['Date de naissance', individual&.birthdate]
|
['Date de naissance', individual&.birthdate]
|
||||||
]
|
]
|
||||||
|
elsif with_etablissement
|
||||||
|
columns += [
|
||||||
|
['Établissement SIRET', etablissement&.siret],
|
||||||
|
['Établissement siège social', etablissement&.siege_social],
|
||||||
|
['Établissement NAF', etablissement&.naf],
|
||||||
|
['Établissement libellé NAF', etablissement&.libelle_naf],
|
||||||
|
['Établissement Adresse', etablissement&.adresse],
|
||||||
|
['Établissement numero voie', etablissement&.numero_voie],
|
||||||
|
['Établissement type voie', etablissement&.type_voie],
|
||||||
|
['Établissement nom voie', etablissement&.nom_voie],
|
||||||
|
['Établissement complément adresse', etablissement&.complement_adresse],
|
||||||
|
['Établissement code postal', etablissement&.code_postal],
|
||||||
|
['Établissement localité', etablissement&.localite],
|
||||||
|
['Établissement code INSEE localité', etablissement&.code_insee_localite],
|
||||||
|
['Entreprise SIREN', etablissement&.entreprise_siren],
|
||||||
|
['Entreprise capital social', etablissement&.entreprise_capital_social],
|
||||||
|
['Entreprise numero TVA intracommunautaire', etablissement&.entreprise_numero_tva_intracommunautaire],
|
||||||
|
['Entreprise forme juridique', etablissement&.entreprise_forme_juridique],
|
||||||
|
['Entreprise forme juridique code', etablissement&.entreprise_forme_juridique_code],
|
||||||
|
['Entreprise nom commercial', etablissement&.entreprise_nom_commercial],
|
||||||
|
['Entreprise raison sociale', etablissement&.entreprise_raison_sociale],
|
||||||
|
['Entreprise SIRET siège social', etablissement&.entreprise_siret_siege_social],
|
||||||
|
['Entreprise code effectif entreprise', etablissement&.entreprise_code_effectif_entreprise],
|
||||||
|
['Entreprise date de création', etablissement&.entreprise_date_creation],
|
||||||
|
['Entreprise nom', etablissement&.entreprise_nom],
|
||||||
|
['Entreprise prénom', etablissement&.entreprise_prenom],
|
||||||
|
['Association RNA', etablissement&.association_rna],
|
||||||
|
['Association titre', etablissement&.association_titre],
|
||||||
|
['Association objet', etablissement&.association_objet],
|
||||||
|
['Association date de création', etablissement&.association_date_creation],
|
||||||
|
['Association date de déclaration', etablissement&.association_date_declaration],
|
||||||
|
['Association date de publication', etablissement&.association_date_publication]
|
||||||
|
]
|
||||||
else
|
else
|
||||||
columns << ['Entreprise raison sociale', etablissement&.entreprise_raison_sociale]
|
columns << ['Entreprise raison sociale', etablissement&.entreprise_raison_sociale]
|
||||||
end
|
end
|
||||||
|
|
16
app/models/dynamic_smtp_settings_interceptor.rb
Normal file
16
app/models/dynamic_smtp_settings_interceptor.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class DynamicSmtpSettingsInterceptor
|
||||||
|
def self.delivering_email(message)
|
||||||
|
if ENV['SENDINBLUE_BALANCING'] == 'enabled'
|
||||||
|
if rand(0..99) < ENV['SENDINBLUE_BALANCING_VALUE'].to_i
|
||||||
|
message.delivery_method.settings = {
|
||||||
|
user_name: ENV['SENDINBLUE_USER_NAME'],
|
||||||
|
password: ENV['SENDINBLUE_SMTP_KEY'],
|
||||||
|
address: 'smtp-relay.sendinblue.com',
|
||||||
|
domain: 'smtp-relay.sendinblue.com',
|
||||||
|
port: '587',
|
||||||
|
authentication: :cram_md5
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@ class Individual < ApplicationRecord
|
||||||
GENDER_FEMALE = 'Mme'
|
GENDER_FEMALE = 'Mme'
|
||||||
|
|
||||||
def self.create_from_france_connect(fc_information)
|
def self.create_from_france_connect(fc_information)
|
||||||
create(
|
create!(
|
||||||
nom: fc_information.family_name,
|
nom: fc_information.family_name,
|
||||||
prenom: fc_information.given_name,
|
prenom: fc_information.given_name,
|
||||||
gender: fc_information.gender == 'female' ? GENDER_FEMALE : GENDER_MALE
|
gender: fc_information.gender == 'female' ? GENDER_FEMALE : GENDER_MALE
|
||||||
|
|
|
@ -444,7 +444,7 @@ class Procedure < ApplicationRecord
|
||||||
version = options.delete(:version)
|
version = options.delete(:version)
|
||||||
if version == 'v2'
|
if version == 'v2'
|
||||||
options.delete(:tables)
|
options.delete(:tables)
|
||||||
ProcedureExportV2Service.new(self, dossiers, **options.to_h.symbolize_keys)
|
ProcedureExportV2Service.new(self, dossiers)
|
||||||
else
|
else
|
||||||
ProcedureExportService.new(self, dossiers, **options.to_h.symbolize_keys)
|
ProcedureExportService.new(self, dossiers, **options.to_h.symbolize_keys)
|
||||||
end
|
end
|
||||||
|
@ -595,14 +595,18 @@ class Procedure < ApplicationRecord
|
||||||
|
|
||||||
def move_type_de_champ_attributes(types_de_champ, type_de_champ, new_index)
|
def move_type_de_champ_attributes(types_de_champ, type_de_champ, new_index)
|
||||||
old_index = types_de_champ.index(type_de_champ)
|
old_index = types_de_champ.index(type_de_champ)
|
||||||
types_de_champ.insert(new_index, types_de_champ.delete_at(old_index))
|
if types_de_champ.delete_at(old_index)
|
||||||
.map.with_index do |type_de_champ, index|
|
types_de_champ.insert(new_index, type_de_champ)
|
||||||
{
|
.map.with_index do |type_de_champ, index|
|
||||||
id: type_de_champ.id,
|
{
|
||||||
libelle: type_de_champ.libelle,
|
id: type_de_champ.id,
|
||||||
order_place: index
|
libelle: type_de_champ.libelle,
|
||||||
}
|
order_place: index
|
||||||
end
|
}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def before_publish
|
def before_publish
|
||||||
|
|
|
@ -53,7 +53,7 @@ class TypesDeChamp::LinkedDropDownListTypeDeChamp < TypesDeChamp::TypeDeChampBas
|
||||||
|
|
||||||
def check_presence_of_primary_options
|
def check_presence_of_primary_options
|
||||||
if !PRIMARY_PATTERN.match?(drop_down_list.options.second)
|
if !PRIMARY_PATTERN.match?(drop_down_list.options.second)
|
||||||
errors.add(libelle, "doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>")
|
errors.add(libelle.presence || "La liste", "doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ class User < ApplicationRecord
|
||||||
def invite_administrateur!(administration_id)
|
def invite_administrateur!(administration_id)
|
||||||
reset_password_token = nil
|
reset_password_token = nil
|
||||||
|
|
||||||
if !administrateur.active?
|
if !active?
|
||||||
reset_password_token = set_reset_password_token
|
reset_password_token = set_reset_password_token
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -92,6 +92,10 @@ class User < ApplicationRecord
|
||||||
"User:#{id}"
|
"User:#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def active?
|
||||||
|
last_sign_in_at.present?
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def link_invites!
|
def link_invites!
|
||||||
|
|
|
@ -29,7 +29,7 @@ class AdministrateurUsageStatisticsService
|
||||||
result = {
|
result = {
|
||||||
ds_sign_in_count: administrateur.user.sign_in_count,
|
ds_sign_in_count: administrateur.user.sign_in_count,
|
||||||
ds_created_at: administrateur.created_at,
|
ds_created_at: administrateur.created_at,
|
||||||
ds_active: administrateur.active?,
|
ds_active: administrateur.user.active?,
|
||||||
ds_id: administrateur.id,
|
ds_id: administrateur.id,
|
||||||
nb_services: nb_services_by_administrateur_id[administrateur.id],
|
nb_services: nb_services_by_administrateur_id[administrateur.id],
|
||||||
nb_instructeurs: nb_instructeurs_by_administrateur_id[administrateur.id],
|
nb_instructeurs: nb_instructeurs_by_administrateur_id[administrateur.id],
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ProcedureExportService
|
||||||
:prenom
|
:prenom
|
||||||
]
|
]
|
||||||
|
|
||||||
def initialize(procedure, dossiers, tables: [], ids: nil, since: nil, limit: nil)
|
def initialize(procedure, dossiers, tables: [])
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
|
|
||||||
@attributes = ATTRIBUTES.dup
|
@attributes = ATTRIBUTES.dup
|
||||||
|
@ -59,15 +59,6 @@ class ProcedureExportService
|
||||||
end
|
end
|
||||||
|
|
||||||
@dossiers = dossiers.downloadable_sorted
|
@dossiers = dossiers.downloadable_sorted
|
||||||
if ids
|
|
||||||
@dossiers = @dossiers.where(id: ids)
|
|
||||||
end
|
|
||||||
if since
|
|
||||||
@dossiers = @dossiers.since(since)
|
|
||||||
end
|
|
||||||
if limit
|
|
||||||
@dossiers = @dossiers.limit(limit)
|
|
||||||
end
|
|
||||||
@dossiers = @dossiers.to_a
|
@dossiers = @dossiers.to_a
|
||||||
@tables = tables.map(&:to_sym)
|
@tables = tables.map(&:to_sym)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +1,27 @@
|
||||||
class ProcedureExportV2Service
|
class ProcedureExportV2Service
|
||||||
attr_reader :dossiers
|
attr_reader :dossiers
|
||||||
|
|
||||||
def initialize(procedure, dossiers, ids: nil, since: nil, limit: nil)
|
def initialize(procedure, dossiers)
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@dossiers = dossiers.downloadable_sorted
|
@dossiers = dossiers.downloadable_sorted
|
||||||
if ids
|
|
||||||
@dossiers = @dossiers.where(id: ids)
|
|
||||||
end
|
|
||||||
if since
|
|
||||||
@dossiers = @dossiers.since(since)
|
|
||||||
end
|
|
||||||
if limit
|
|
||||||
@dossiers = @dossiers.limit(limit)
|
|
||||||
end
|
|
||||||
@tables = [:dossiers, :etablissements, :avis] + champs_repetables_options
|
@tables = [:dossiers, :etablissements, :avis] + champs_repetables_options
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_csv(table = :dossiers)
|
def to_csv
|
||||||
SpreadsheetArchitect.to_csv(options_for(table))
|
SpreadsheetArchitect.to_csv(options_for(:dossiers, :csv))
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_xlsx
|
def to_xlsx
|
||||||
# We recursively build multi page spreadsheet
|
# We recursively build multi page spreadsheet
|
||||||
@tables.reduce(nil) do |package, table|
|
@tables.reduce(nil) do |package, table|
|
||||||
SpreadsheetArchitect.to_axlsx_package(options_for(table), package)
|
SpreadsheetArchitect.to_axlsx_package(options_for(table, :xlsx), package)
|
||||||
end.to_stream.read
|
end.to_stream.read
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_ods
|
def to_ods
|
||||||
# We recursively build multi page spreadsheet
|
# We recursively build multi page spreadsheet
|
||||||
@tables.reduce(nil) do |spreadsheet, table|
|
@tables.reduce(nil) do |spreadsheet, table|
|
||||||
SpreadsheetArchitect.to_rodf_spreadsheet(options_for(table), spreadsheet)
|
SpreadsheetArchitect.to_rodf_spreadsheet(options_for(table, :ods), spreadsheet)
|
||||||
end.bytes
|
end.bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,7 +44,7 @@ class ProcedureExportV2Service
|
||||||
[dossier.champs, dossier.champs_private]
|
[dossier.champs, dossier.champs_private]
|
||||||
.flatten
|
.flatten
|
||||||
.filter { |champ| champ.is_a?(Champs::RepetitionChamp) }
|
.filter { |champ| champ.is_a?(Champs::RepetitionChamp) }
|
||||||
end.group_by(&:libelle)
|
end.group_by(&:libelle_for_export)
|
||||||
end
|
end
|
||||||
|
|
||||||
def champs_repetables_options
|
def champs_repetables_options
|
||||||
|
@ -70,21 +61,16 @@ class ProcedureExportV2Service
|
||||||
row_style: { background_color: nil, color: "000000", font_size: 12 }
|
row_style: { background_color: nil, color: "000000", font_size: 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
def sanitize_sheet_name(name)
|
def options_for(table, format)
|
||||||
ActiveStorage::Filename.new(name.to_s).sanitized.truncate(30)
|
|
||||||
end
|
|
||||||
|
|
||||||
def options_for(table)
|
|
||||||
case table
|
case table
|
||||||
when :dossiers
|
when :dossiers
|
||||||
{ instances: dossiers.to_a, sheet_name: 'Dossiers' }.merge(DEFAULT_STYLES)
|
{ instances: dossiers.to_a, sheet_name: 'Dossiers', spreadsheet_columns: :"spreadsheet_columns_#{format}" }.merge(DEFAULT_STYLES)
|
||||||
when :etablissements
|
when :etablissements
|
||||||
{ instances: etablissements.to_a, sheet_name: 'Etablissements' }.merge(DEFAULT_STYLES)
|
{ instances: etablissements.to_a, sheet_name: 'Etablissements' }.merge(DEFAULT_STYLES)
|
||||||
when :avis
|
when :avis
|
||||||
{ instances: avis.to_a, sheet_name: 'Avis' }.merge(DEFAULT_STYLES)
|
{ instances: avis.to_a, sheet_name: 'Avis' }.merge(DEFAULT_STYLES)
|
||||||
when Array
|
when Array
|
||||||
# We have to truncate the label here as spreadsheets have a (30 char) limit on length.
|
{ instances: table.last, sheet_name: table.first }.merge(DEFAULT_STYLES)
|
||||||
{ instances: table.last, sheet_name: sanitize_sheet_name(table.first) }.merge(DEFAULT_STYLES)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
- champs = champ.rows.last
|
- champs = champ.rows.last
|
||||||
- index = (champ.rows.size - 1) * champs.size
|
- if champs.present?
|
||||||
%div{ class: "row row-#{champs.first.row}" }
|
- index = (champ.rows.size - 1) * champs.size
|
||||||
- champs.each.with_index(index) do |champ, index|
|
%div{ class: "row row-#{champs.first.row}" }
|
||||||
= fields_for "#{attribute}[#{index}]", champ do |form|
|
- champs.each.with_index(index) do |champ, index|
|
||||||
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: form }
|
= fields_for "#{attribute}[#{index}]", champ do |form|
|
||||||
= form.hidden_field :id
|
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: form }
|
||||||
= form.hidden_field :_destroy, disabled: true
|
= form.hidden_field :id
|
||||||
%button.button.danger.remove-row
|
= form.hidden_field :_destroy, disabled: true
|
||||||
Supprimer
|
%button.button.danger.remove-row
|
||||||
|
Supprimer
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
.commencer.form
|
.commencer.form
|
||||||
- if !user_signed_in?
|
- if !user_signed_in?
|
||||||
%h1 Commencer la démarche
|
%h2.huge-title Commencer la démarche
|
||||||
= link_to commencer_sign_up_path(path: @procedure.path), class: ['button large expand primary'] do
|
= link_to commencer_sign_up_path(path: @procedure.path), class: ['button large expand primary'] do
|
||||||
Créer un compte
|
Créer un compte
|
||||||
%span.optional-on-small-screens
|
%span.optional-on-small-screens
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
- elsif drafts.count == 1 && not_drafts.count == 0
|
- elsif drafts.count == 1 && not_drafts.count == 0
|
||||||
- dossier = drafts.first
|
- dossier = drafts.first
|
||||||
%h1 Vous avez déjà commencé à remplir un dossier
|
%h2.huge-title Vous avez déjà commencé à remplir un dossier
|
||||||
%p
|
%p
|
||||||
Il y a <strong>#{time_ago_in_words(dossier.created_at)}</strong>,
|
Il y a <strong>#{time_ago_in_words(dossier.created_at)}</strong>,
|
||||||
vous avez commencé à remplir un dossier sur la démarche « #{dossier.procedure.libelle} ».
|
vous avez commencé à remplir un dossier sur la démarche « #{dossier.procedure.libelle} ».
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
- elsif not_drafts.count == 1
|
- elsif not_drafts.count == 1
|
||||||
- dossier = not_drafts.first
|
- dossier = not_drafts.first
|
||||||
%h1 Vous avez déjà déposé un dossier
|
%h2.huge-title Vous avez déjà déposé un dossier
|
||||||
%p
|
%p
|
||||||
Il y a <strong>#{time_ago_in_words(dossier.en_construction_at)}</strong>,
|
Il y a <strong>#{time_ago_in_words(dossier.en_construction_at)}</strong>,
|
||||||
vous avez déposé un dossier sur la démarche « #{dossier.procedure.libelle} ».
|
vous avez déposé un dossier sur la démarche « #{dossier.procedure.libelle} ».
|
||||||
|
@ -37,6 +37,6 @@
|
||||||
= link_to 'Commencer un nouveau dossier', url_for_new_dossier(@procedure), class: ['button large expand']
|
= link_to 'Commencer un nouveau dossier', url_for_new_dossier(@procedure), class: ['button large expand']
|
||||||
|
|
||||||
- else
|
- else
|
||||||
%h1 Vous avez déjà des dossiers pour cette démarche
|
%h2.huge-title Vous avez déjà des dossiers pour cette démarche
|
||||||
= link_to 'Voir mes dossiers en cours', dossiers_path, class: ['button large expand primary']
|
= link_to 'Voir mes dossiers en cours', dossiers_path, class: ['button large expand primary']
|
||||||
= link_to 'Commencer un nouveau dossier', url_for_new_dossier(@procedure), class: ['button large expand']
|
= link_to 'Commencer un nouveau dossier', url_for_new_dossier(@procedure), class: ['button large expand']
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
- dossier = controller.try(:dossier_for_help)
|
- dossier = controller.try(:dossier_for_help)
|
||||||
- procedure = controller.try(:procedure_for_help)
|
- procedure = controller.try(:procedure_for_help)
|
||||||
|
|
||||||
.new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" }
|
%header.new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" }
|
||||||
.header-inner-content
|
.header-inner-content
|
||||||
|
|
||||||
.flex.align-center
|
.flex.align-center
|
||||||
|
|
|
@ -33,8 +33,9 @@
|
||||||
Env Test
|
Env Test
|
||||||
|
|
||||||
= render partial: "layouts/new_header"
|
= render partial: "layouts/new_header"
|
||||||
= render partial: "layouts/flash_messages"
|
%main
|
||||||
= content_for?(:content) ? yield(:content) : yield
|
= render partial: "layouts/flash_messages"
|
||||||
|
= content_for?(:content) ? yield(:content) : yield
|
||||||
|
|
||||||
- if content_for?(:footer)
|
- if content_for?(:footer)
|
||||||
= content_for(:footer)
|
= content_for(:footer)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.no-procedure
|
.no-procedure
|
||||||
= image_tag "landing/hero/dematerialiser.svg", class: "paperless-logo"
|
= image_tag "landing/hero/dematerialiser.svg", class: "paperless-logo", alt: "moins de papier"
|
||||||
.baseline.center
|
.baseline.center
|
||||||
%h3 Un outil simple
|
%h3 Un outil simple
|
||||||
%p
|
%p
|
||||||
|
|
|
@ -30,14 +30,14 @@
|
||||||
.procedure-list-element
|
.procedure-list-element
|
||||||
Administrateurs
|
Administrateurs
|
||||||
|
|
||||||
- if !feature_enabled?(:routage)
|
- if !feature_enabled?(:administrateur_routage)
|
||||||
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
||||||
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
||||||
Instructeurs
|
Instructeurs
|
||||||
- if @procedure.missing_steps.include?(:instructeurs)
|
- if @procedure.missing_steps.include?(:instructeurs)
|
||||||
%p.missing-steps (à compléter)
|
%p.missing-steps (à compléter)
|
||||||
|
|
||||||
- if feature_enabled?(:routage)
|
- if feature_enabled?(:administrateur_routage)
|
||||||
%a#onglet-instructeurs{ href: url_for(procedure_groupe_instructeurs_path(@procedure)) }
|
%a#onglet-instructeurs{ href: url_for(procedure_groupe_instructeurs_path(@procedure)) }
|
||||||
.procedure-list-element
|
.procedure-list-element
|
||||||
Groupe d'instructeurs
|
Groupe d'instructeurs
|
||||||
|
|
|
@ -6,22 +6,22 @@
|
||||||
%ul.footer-logos
|
%ul.footer-logos
|
||||||
%li.footer-text
|
%li.footer-text
|
||||||
Un service fourni par la
|
Un service fourni par la
|
||||||
= link_to "DINSIC", "http://www.modernisation.gouv.fr/"
|
= link_to "DINUM", "http://www.modernisation.gouv.fr/", title: "Direction Interministérielle au Numérique"
|
||||||
%br
|
%br
|
||||||
et incubé par
|
et incubé par
|
||||||
= link_to "beta.gouv.fr", "https://beta.gouv.fr"
|
= link_to "beta.gouv.fr", "https://beta.gouv.fr", title: "le site de Beta.gouv.fr"
|
||||||
%li
|
%li
|
||||||
= link_to "http://www.modernisation.gouv.fr/" do
|
= link_to "http://www.modernisation.gouv.fr/", title: "DINUM" do
|
||||||
%span.footer-logo.footer-logo-dinsic{ role: 'img', 'aria-label': 'DINSIC' }
|
%span.footer-logo.footer-logo-dinsic{ role: 'img', 'aria-label': 'DINSIC' }
|
||||||
= link_to "https://beta.gouv.fr" do
|
= link_to "https://beta.gouv.fr", title: "le site de Beta.gouv.fr" do
|
||||||
%span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': 'beta.gouv.fr' }
|
%span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': 'beta.gouv.fr' }
|
||||||
|
|
||||||
%li.footer-column
|
%li.footer-column
|
||||||
%ul.footer-links
|
%ul.footer-links
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Newsletter", "https://my.sendinblue.com/users/subscribe/js_id/3s2q1/id/1", :class => "footer-link", :target => "_blank", rel: "noopener"
|
= link_to "Newsletter", "https://my.sendinblue.com/users/subscribe/js_id/3s2q1/id/1", :title => "Notre newsletter", :class => "footer-link", :target => "_blank", rel: "noopener"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Nouveautés", "https://github.com/betagouv/demarches-simplifiees.fr/releases", :class => "footer-link"
|
= link_to "Nouveautés", "https://github.com/betagouv/demarches-simplifiees.fr/releases", :class => "footer-link", :title => "Nos nouveautés"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Statistiques", stats_path, :class => "footer-link", data: { turbolinks: false } # Turbolinks disabled for Chartkick. See Issue #350
|
= link_to "Statistiques", stats_path, :class => "footer-link", data: { turbolinks: false } # Turbolinks disabled for Chartkick. See Issue #350
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
%em.hero-tagline-em en ligne
|
%em.hero-tagline-em en ligne
|
||||||
|
|
||||||
.hero-illustration
|
.hero-illustration
|
||||||
%img{ :src => image_url("landing/hero/dematerialiser.svg"), alt: "" }
|
%img{ :src => image_url("landing/hero/dematerialiser.svg"), alt: "dématérialisez" }
|
||||||
|
|
||||||
.landing-panel.usagers-panel
|
.landing-panel.usagers-panel
|
||||||
.container
|
.container
|
||||||
.role-panel-wrapper
|
.role-panel-wrapper
|
||||||
.role-panel-30.role-usagers-image
|
.role-panel-30.role-usagers-image
|
||||||
%img.role-image{ :src => image_url("landing/roles/usagers.svg"), alt: "" }
|
%img.role-image{ :src => image_url("landing/roles/usagers.svg"), alt: "usager" }
|
||||||
|
|
||||||
.role-panel-70
|
.role-panel-70
|
||||||
%h1.role-panel-title Vous souhaitez effectuer une demande auprès d'une administration ?
|
%h1.role-panel-title Vous souhaitez effectuer une demande auprès d'une administration ?
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
.procedure-logos
|
.procedure-logos
|
||||||
= image_tag procedure.logo_url
|
= image_tag procedure.logo_url, alt: "logo #{procedure.libelle}"
|
||||||
- if procedure.euro_flag
|
- if procedure.euro_flag
|
||||||
= image_tag("flag_of_europe.svg", id: 'euro_flag', class: (!procedure.euro_flag ? "hidden" : ""))
|
= image_tag("flag_of_europe.svg", id: 'euro_flag', class: (!procedure.euro_flag ? "hidden" : ""))
|
||||||
%h2.procedure-title
|
%h1.procedure-title
|
||||||
= procedure.libelle
|
= procedure.libelle
|
||||||
.procedure-description
|
.procedure-description
|
||||||
.procedure-description-body.read-more-enabled.read-more-collapsed
|
.procedure-description-body.read-more-enabled.read-more-collapsed
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
= mail_to CONTACT_EMAIL do
|
= mail_to CONTACT_EMAIL do
|
||||||
%span.icon.mail
|
%span.icon.mail
|
||||||
.dropdown-description
|
.dropdown-description
|
||||||
%h4.help-dropdown-title Contact technique
|
%span.help-dropdown-title Contact technique
|
||||||
%p Envoyez nous un message à #{CONTACT_EMAIL}.
|
%p Envoyez nous un message à #{CONTACT_EMAIL}.
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
= link_to FAQ_URL, target: "_blank", rel: "noopener" do
|
= link_to FAQ_URL, target: "_blank", rel: "noopener" do
|
||||||
%span.icon.help
|
%span.icon.help
|
||||||
.dropdown-description
|
.dropdown-description
|
||||||
%h4.help-dropdown-title Un problème avec le site ?
|
%span.help-dropdown-title Un problème avec le site ?
|
||||||
%p Trouvez votre réponse dans l’aide en ligne.
|
%p Trouvez votre réponse dans l’aide en ligne.
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
= link_to messagerie_dossier_path(dossier) do
|
= link_to messagerie_dossier_path(dossier) do
|
||||||
%span.icon.mail
|
%span.icon.mail
|
||||||
.dropdown-description
|
.dropdown-description
|
||||||
%h4.help-dropdown-title= title
|
%span.help-dropdown-title= title
|
||||||
%p Envoyez directement un message à l’instructeur.
|
%p Envoyez directement un message à l’instructeur.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%li.help-dropdown-service
|
%li.help-dropdown-service
|
||||||
%span.icon.person
|
%span.icon.person
|
||||||
.dropdown-description
|
.dropdown-description
|
||||||
%h4.help-dropdown-title= title
|
%span.help-dropdown-title= title
|
||||||
.help-dropdown-service-action
|
.help-dropdown-service-action
|
||||||
%p Contactez directement l’administration :
|
%p Contactez directement l’administration :
|
||||||
%p.help-dropdown-service-item
|
%p.help-dropdown-service-item
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
= link_to "Accessibilité", accessibilite_path, :class => "footer-link"
|
%ul.footer-row.footer-bottom-line.footer-site-links
|
||||||
–
|
%li>= link_to "Accessibilité", accessibilite_path
|
||||||
= link_to "CGU", CGU_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
%li>= link_to "CGU", CGU_URL, target: "_blank", rel: "noopener noreferrer"
|
||||||
–
|
%li>= link_to "Mentions légales", MENTIONS_LEGALES_URL, target: "_blank", rel: "noopener noreferrer"
|
||||||
= link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
%li>= link_to 'Documentation', DOC_URL
|
||||||
–
|
%li>= contact_link "Contact technique", dossier_id: dossier&.id
|
||||||
= link_to 'Documentation', DOC_URL
|
%li>= link_to 'Aide', FAQ_URL
|
||||||
–
|
|
||||||
= contact_link "Contact technique", class: "footer-link", dossier_id: dossier&.id
|
|
||||||
–
|
|
||||||
= link_to 'Aide', FAQ_URL
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
.container
|
.container
|
||||||
- service = procedure.service
|
- service = procedure.service
|
||||||
- if service.present?
|
- if service.present?
|
||||||
%ul.footer-row.footer-columns
|
.footer-row.footer-columns
|
||||||
%li.footer-column
|
%ul.footer-column
|
||||||
%h3.footer-header Cette démarche est gérée par :
|
%p.footer-header Cette démarche est gérée par :
|
||||||
%p
|
%li
|
||||||
= service.nom
|
= service.nom
|
||||||
%br
|
%br
|
||||||
= service.organisme
|
= service.organisme
|
||||||
%br
|
%br
|
||||||
= string_to_html(service.adresse)
|
= string_to_html(service.adresse, wrapper_tag = 'span')
|
||||||
|
|
||||||
%li.footer-column
|
%ul.footer-column
|
||||||
%h3.footer-header Poser une question sur votre dossier :
|
%p.footer-header Poser une question sur votre dossier :
|
||||||
%p
|
%li
|
||||||
- if dossier.present? && dossier.messagerie_available?
|
- if dossier.present? && dossier.messagerie_available?
|
||||||
Directement
|
Directement
|
||||||
= link_to "par la messagerie", messagerie_dossier_path(dossier)
|
= link_to "par la messagerie", messagerie_dossier_path(dossier)
|
||||||
|
@ -22,21 +22,21 @@
|
||||||
Par email :
|
Par email :
|
||||||
= link_to service.email, "mailto:#{service.email}"
|
= link_to service.email, "mailto:#{service.email}"
|
||||||
|
|
||||||
%p
|
%li
|
||||||
Par téléphone :
|
Par téléphone :
|
||||||
%a{ href: "tel:#{service.telephone}" }= service.telephone
|
%a{ href: "tel:#{service.telephone}" }= service.telephone
|
||||||
|
|
||||||
%p
|
%li
|
||||||
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
||||||
= simple_format(horaires)
|
= simple_format(horaires, {}, wrapper_tag: 'span')
|
||||||
|
|
||||||
|
|
||||||
- politiques = politiques_conservation_de_donnees(procedure)
|
- politiques = politiques_conservation_de_donnees(procedure)
|
||||||
- if politiques.present?
|
- if politiques.present?
|
||||||
%li.footer-column
|
%ul.footer-column
|
||||||
%h3.footer-header Conservation des données :
|
%p.footer-header Conservation des données :
|
||||||
- politiques.each do |politique|
|
- politiques.each do |politique|
|
||||||
%p= politique
|
%li= politique
|
||||||
|
|
||||||
|
= render partial: 'users/general_footer_row', locals: { dossier: dossier }
|
||||||
|
|
||||||
.footer-row.footer-bottom-line
|
|
||||||
= render partial: 'users/general_footer_row', locals: { dossier: dossier }
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
%footer.procedure-footer
|
%footer.procedure-footer
|
||||||
.container
|
.container
|
||||||
.footer-row.footer-bottom-line
|
= render partial: "users/general_footer_row", locals: { dossier: nil }
|
||||||
= render partial: "users/general_footer_row", locals: { dossier: nil }
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
.auth-form.sign-in-form
|
.auth-form.sign-in-form
|
||||||
|
|
||||||
= form_for User.new, url: user_session_path, html: { class: "form" } do |f|
|
= form_for User.new, url: user_session_path, html: { class: "form" } do |f|
|
||||||
%h1 Connectez-vous
|
%h2.huge-title Connectez-vous
|
||||||
|
|
||||||
= f.label :email, "Email"
|
= f.label :email, "Email"
|
||||||
= f.text_field :email, autofocus: true
|
= f.text_field :email, autofocus: true
|
||||||
|
|
|
@ -18,6 +18,8 @@ chdir APP_ROOT do
|
||||||
system('bundle check') || system!('bundle install')
|
system('bundle check') || system!('bundle install')
|
||||||
system! 'bin/yarn install'
|
system! 'bin/yarn install'
|
||||||
|
|
||||||
|
puts "\n== Updating webdrivers =="
|
||||||
|
system! 'RAILS_ENV=test bin/rails webdrivers:chromedriver:update'
|
||||||
|
|
||||||
puts "\n== Copying sample files =="
|
puts "\n== Copying sample files =="
|
||||||
unless File.exist?('.env')
|
unless File.exist?('.env')
|
||||||
|
|
|
@ -18,6 +18,9 @@ chdir APP_ROOT do
|
||||||
system('bundle check') || system!('bundle install')
|
system('bundle check') || system!('bundle install')
|
||||||
system! 'bin/yarn install'
|
system! 'bin/yarn install'
|
||||||
|
|
||||||
|
puts "\n== Updating webdrivers =="
|
||||||
|
system! 'RAILS_ENV=test bin/rails webdrivers:chromedriver:update'
|
||||||
|
|
||||||
puts "\n== Updating database =="
|
puts "\n== Updating database =="
|
||||||
system! 'bin/rails db:migrate'
|
system! 'bin/rails db:migrate'
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ FOG_OPENSTACK_IDENTITY_API_VERSION=""
|
||||||
FOG_OPENSTACK_REGION=""
|
FOG_OPENSTACK_REGION=""
|
||||||
FOG_DIRECTORY=""
|
FOG_DIRECTORY=""
|
||||||
FOG_ENABLED=""
|
FOG_ENABLED=""
|
||||||
CARRIERWAVE_CACHE_DIR="/tmp/tps-local-cache"
|
|
||||||
DS_PROXY_URL=""
|
DS_PROXY_URL=""
|
||||||
|
|
||||||
FC_PARTICULIER_ID=""
|
FC_PARTICULIER_ID=""
|
||||||
|
@ -46,8 +45,14 @@ SENTRY_DSN_JS=""
|
||||||
MATOMO_ENABLED="disabled"
|
MATOMO_ENABLED="disabled"
|
||||||
MATOMO_ID="73"
|
MATOMO_ID="73"
|
||||||
|
|
||||||
SENDINBLUE_ENABLED="disabled"
|
SENDINBLUE_BALANCING=""
|
||||||
|
SENDINBLUE_BALANCING_VALUE=""
|
||||||
|
SENDINBLUE_ENABLED=""
|
||||||
SENDINBLUE_CLIENT_KEY=""
|
SENDINBLUE_CLIENT_KEY=""
|
||||||
|
SENDINBLUE_SMTP_KEY=""
|
||||||
|
SENDINBLUE_USER_NAME=""
|
||||||
|
|
||||||
|
|
||||||
CRISP_ENABLED="disabled"
|
CRISP_ENABLED="disabled"
|
||||||
CRISP_CLIENT_KEY=""
|
CRISP_CLIENT_KEY=""
|
||||||
|
|
||||||
|
|
|
@ -45,14 +45,26 @@ Rails.application.configure do
|
||||||
config.assets.raise_runtime_errors = true
|
config.assets.raise_runtime_errors = true
|
||||||
|
|
||||||
# Action Mailer settings
|
# Action Mailer settings
|
||||||
config.action_mailer.delivery_method = :letter_opener_web
|
|
||||||
# Configure default root URL for generating URLs to routes
|
if ENV['SENDINBLUE_ENABLED'] == 'enabled'
|
||||||
config.action_mailer.default_url_options = {
|
config.action_mailer.delivery_method = :smtp
|
||||||
host: 'localhost',
|
config.action_mailer.smtp_settings = {
|
||||||
port: 3000
|
user_name: Rails.application.secrets.sendinblue[:username],
|
||||||
}
|
password: Rails.application.secrets.sendinblue[:smtp_key],
|
||||||
# Configure default root URL for email assets
|
address: 'smtp-relay.sendinblue.com',
|
||||||
config.action_mailer.asset_host = "http://" + ENV['APP_HOST']
|
domain: 'smtp-relay.sendinblue.com',
|
||||||
|
port: '587',
|
||||||
|
authentication: :cram_md5
|
||||||
|
}
|
||||||
|
else
|
||||||
|
config.action_mailer.delivery_method = :letter_opener_web
|
||||||
|
config.action_mailer.default_url_options = {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 3000
|
||||||
|
}
|
||||||
|
|
||||||
|
config.action_mailer.asset_host = "http://" + ENV['APP_HOST']
|
||||||
|
end
|
||||||
|
|
||||||
Rails.application.routes.default_url_options = {
|
Rails.application.routes.default_url_options = {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
|
|
|
@ -77,6 +77,16 @@ Rails.application.configure do
|
||||||
port: '2525',
|
port: '2525',
|
||||||
authentication: :cram_md5
|
authentication: :cram_md5
|
||||||
}
|
}
|
||||||
|
elsif ENV['SENDINBLUE_ENABLED'] == 'enabled'
|
||||||
|
config.action_mailer.delivery_method = :smtp
|
||||||
|
config.action_mailer.smtp_settings = {
|
||||||
|
user_name: Rails.application.secrets.sendinblue[:username],
|
||||||
|
password: Rails.application.secrets.sendinblue[:smtp_key],
|
||||||
|
address: 'smtp-relay.sendinblue.com',
|
||||||
|
domain: 'smtp-relay.sendinblue.com',
|
||||||
|
port: '587',
|
||||||
|
authentication: :cram_md5
|
||||||
|
}
|
||||||
else
|
else
|
||||||
config.action_mailer.delivery_method = :mailjet
|
config.action_mailer.delivery_method = :mailjet
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
Rails.application.config.content_security_policy do |policy|
|
Rails.application.config.content_security_policy do |policy|
|
||||||
# En cas de non respect d'une des règles, faire un POST sur cette URL
|
if Rails.env.development?
|
||||||
if Rails.env.production?
|
# les CSP ne sont pas appliquées en dev: on notifie cependant une url quelconque de la violation
|
||||||
policy.report_uri "https://demarchessimplifieestest.report-uri.com/r/d/csp/reportOnly"
|
# pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement
|
||||||
else
|
policy.report_uri "http://#{ENV['APP_HOST']}/csp/"
|
||||||
policy.report_uri "http://#{ENV['APP_HOST']}/csp/" # ne pas notifier report-uri en dev/test
|
|
||||||
end
|
end
|
||||||
# Whitelist image
|
# Whitelist image
|
||||||
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*", :data
|
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*", :data
|
||||||
|
|
1
config/initializers/dynamic_smtp_settings_interceptor.rb
Normal file
1
config/initializers/dynamic_smtp_settings_interceptor.rb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ActionMailer::Base.register_interceptor "DynamicSmtpSettingsInterceptor"
|
79
config/initializers/graphiql.rb
Normal file
79
config/initializers/graphiql.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
DEFAULT_QUERY = "# La documentation officielle de la spécification (Anglais) : https://graphql.org/
|
||||||
|
# Une introduction aux concepts et raisons d'être de GraphQL (Français) : https://blog.octo.com/graphql-et-pourquoi-faire/
|
||||||
|
# Le schema GraphQL de demarches-simplifiees.fr : https://demarches-simplifiees-graphql.netlify.com
|
||||||
|
# Le endpoint GraphQL de demarches-simplifiees.fr : https://demarches-simplifiees.fr/api/v2/graphql
|
||||||
|
|
||||||
|
query getDemarche($demarcheNumber: Int!) {
|
||||||
|
demarche(number: $demarcheNumber) {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
title
|
||||||
|
champDescriptors {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
label
|
||||||
|
}
|
||||||
|
dossiers(first: 3) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
datePassageEnConstruction
|
||||||
|
datePassageEnInstruction
|
||||||
|
dateTraitement
|
||||||
|
usager {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
champs {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
... on TextChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DecimalNumberChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on IntegerNumberChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on CheckboxChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DateChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DossierLinkChamp {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on MultipleDropDownListChamp {
|
||||||
|
values
|
||||||
|
}
|
||||||
|
... on LinkedDropDownListChamp {
|
||||||
|
primaryValue
|
||||||
|
secondaryValue
|
||||||
|
}
|
||||||
|
... on PieceJustificativeChamp {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
... on CarteChamp {
|
||||||
|
geoAreas {
|
||||||
|
source
|
||||||
|
geometry {
|
||||||
|
type
|
||||||
|
coordinates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
|
||||||
|
GraphiQL::Rails.config.initial_query = DEFAULT_QUERY
|
||||||
|
GraphiQL::Rails.config.title = 'demarches-simplifiees.fr'
|
|
@ -2,4 +2,5 @@ fr:
|
||||||
activerecord:
|
activerecord:
|
||||||
attributes:
|
attributes:
|
||||||
commentaire:
|
commentaire:
|
||||||
|
body: 'Votre message'
|
||||||
file: fichier
|
file: fichier
|
||||||
|
|
8
config/locales/models/geo_area/fr.yml
Normal file
8
config/locales/models/geo_area/fr.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fr:
|
||||||
|
activerecord:
|
||||||
|
attributes:
|
||||||
|
geo_area:
|
||||||
|
source:
|
||||||
|
cadastre: Parcelle cadastrale
|
||||||
|
quartier_prioritaire: Quartier prioritaire
|
||||||
|
selection_utilisateur: Sélection utilisateur
|
|
@ -42,8 +42,6 @@ defaults: &defaults
|
||||||
openstack_identity_api_version: "<%= ENV['FOG_OPENSTACK_IDENTITY_API_VERSION'] %>"
|
openstack_identity_api_version: "<%= ENV['FOG_OPENSTACK_IDENTITY_API_VERSION'] %>"
|
||||||
openstack_region: <%= ENV['FOG_OPENSTACK_REGION'] %>
|
openstack_region: <%= ENV['FOG_OPENSTACK_REGION'] %>
|
||||||
directory: <%= ENV['FOG_DIRECTORY'] %>
|
directory: <%= ENV['FOG_DIRECTORY'] %>
|
||||||
carrierwave:
|
|
||||||
cache_dir: <%= ENV['CARRIERWAVE_CACHE_DIR'] %>
|
|
||||||
mailtrap:
|
mailtrap:
|
||||||
username: <%= ENV['MAILTRAP_USERNAME'] %>
|
username: <%= ENV['MAILTRAP_USERNAME'] %>
|
||||||
password: <%= ENV['MAILTRAP_PASSWORD'] %>
|
password: <%= ENV['MAILTRAP_PASSWORD'] %>
|
||||||
|
@ -54,7 +52,9 @@ defaults: &defaults
|
||||||
webhook_secret: <%= ENV['HELPSCOUT_WEBHOOK_SECRET'] %>
|
webhook_secret: <%= ENV['HELPSCOUT_WEBHOOK_SECRET'] %>
|
||||||
sendinblue:
|
sendinblue:
|
||||||
enabled: <%= ENV['SENDINBLUE_ENABLED'] == 'enabled' %>
|
enabled: <%= ENV['SENDINBLUE_ENABLED'] == 'enabled' %>
|
||||||
|
username: <%= ENV['SENDINBLUE_USER_NAME'] %>
|
||||||
client_key: <%= ENV['SENDINBLUE_CLIENT_KEY'] %>
|
client_key: <%= ENV['SENDINBLUE_CLIENT_KEY'] %>
|
||||||
|
smtp_key: <%= ENV['SENDINBLUE_SMTP_KEY'] %>
|
||||||
api_v3_key: <%= ENV['SENDINBLUE_API_V3_KEY'] %>
|
api_v3_key: <%= ENV['SENDINBLUE_API_V3_KEY'] %>
|
||||||
matomo:
|
matomo:
|
||||||
enabled: <%= ENV['MATOMO_ENABLED'] == 'enabled' %>
|
enabled: <%= ENV['MATOMO_ENABLED'] == 'enabled' %>
|
||||||
|
@ -82,8 +82,6 @@ test:
|
||||||
key: api_entreprise_test_key
|
key: api_entreprise_test_key
|
||||||
fog:
|
fog:
|
||||||
directory: tps_dev
|
directory: tps_dev
|
||||||
carrierwave:
|
|
||||||
cache_dir: /tmp/tps-test-cache
|
|
||||||
pipedrive:
|
pipedrive:
|
||||||
key: pipedrive_test_key
|
key: pipedrive_test_key
|
||||||
france_connect_particulier:
|
france_connect_particulier:
|
||||||
|
|
5
db/migrate/20191113142816_instructeurs_remove_email.rb
Normal file
5
db/migrate/20191113142816_instructeurs_remove_email.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class InstructeursRemoveEmail < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
remove_column :instructeurs, :email
|
||||||
|
end
|
||||||
|
end
|
|
@ -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: 2019_10_24_150452) do
|
ActiveRecord::Schema.define(version: 2019_11_13_142816) 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"
|
||||||
|
@ -414,12 +414,10 @@ ActiveRecord::Schema.define(version: 2019_10_24_150452) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "instructeurs", id: :serial, force: :cascade do |t|
|
create_table "instructeurs", id: :serial, force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.text "encrypted_login_token"
|
t.text "encrypted_login_token"
|
||||||
t.datetime "login_token_created_at"
|
t.datetime "login_token_created_at"
|
||||||
t.index ["email"], name: "index_instructeurs_on_email"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "invites", id: :serial, force: :cascade do |t|
|
create_table "invites", id: :serial, force: :cascade do |t|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
require Rails.root.join("lib", "tasks", "task_helper")
|
|
||||||
|
|
||||||
namespace :'2017_10_30_copy_commentaire_piece_justificative_to_file' do
|
|
||||||
task set: :environment do
|
|
||||||
commentaires_to_process = Commentaire.where(file: nil).where.not(piece_justificative_id: nil).reorder(id: :desc)
|
|
||||||
|
|
||||||
rake_puts "#{commentaires_to_process.count} commentaires to process..."
|
|
||||||
|
|
||||||
commentaires_to_process.each do |c|
|
|
||||||
process_commentaire(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task fix: :environment do
|
|
||||||
commentaires_to_fix = Commentaire.where.not(file: nil).where.not(piece_justificative_id: nil).reorder(id: :desc)
|
|
||||||
|
|
||||||
rake_puts "#{commentaires_to_fix.count} commentaires to fix..."
|
|
||||||
|
|
||||||
commentaires_to_fix.each do |c|
|
|
||||||
process_commentaire(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitize_name(name) # from https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/sanitized_file.rb#L323
|
|
||||||
name = name.gsub(/[^[:word:]\.\-\+]/, "_")
|
|
||||||
name = "_#{name}" if name.match?(/\A\.+\z/)
|
|
||||||
name = "unnamed" if name.empty?
|
|
||||||
return name.mb_chars.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_commentaire(commentaire)
|
|
||||||
rake_puts "Processing commentaire #{commentaire.id}"
|
|
||||||
if commentaire.piece_justificative.present?
|
|
||||||
# https://github.com/carrierwaveuploader/carrierwave#uploading-files-from-a-remote-location
|
|
||||||
commentaire.remote_file_url = commentaire.piece_justificative.content_url
|
|
||||||
|
|
||||||
if commentaire.piece_justificative.original_filename.present?
|
|
||||||
commentaire.file.define_singleton_method(:filename) { sanitize_name(commentaire.piece_justificative.original_filename) }
|
|
||||||
end
|
|
||||||
|
|
||||||
if commentaire.body.blank?
|
|
||||||
commentaire.body = commentaire.piece_justificative.original_filename || "."
|
|
||||||
end
|
|
||||||
|
|
||||||
commentaire.save
|
|
||||||
if commentaire.file.blank?
|
|
||||||
rake_puts "Failed to save file for commentaire #{commentaire.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,113 +0,0 @@
|
||||||
require Rails.root.join("lib", "tasks", "task_helper")
|
|
||||||
|
|
||||||
namespace :cloudstorage do
|
|
||||||
task init: :environment do
|
|
||||||
os_config = (YAML.load_file(Fog.credentials_path))['default']
|
|
||||||
@os = OpenStack::Connection.create(
|
|
||||||
{
|
|
||||||
username: os_config['openstack_username'],
|
|
||||||
api_key: os_config['openstack_api_key'],
|
|
||||||
auth_method: "password",
|
|
||||||
auth_url: "https://auth.cloud.ovh.net/v2.0/",
|
|
||||||
authtenant_name: os_config['openstack_tenant'],
|
|
||||||
service_type: "object-store",
|
|
||||||
region: os_config['openstack_region']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@cont = @os.container(CarrierWave::Uploader::Base.fog_directory)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Move local attestations on cloud storage'
|
|
||||||
task migrate: :environment do
|
|
||||||
puts 'Starting migration'
|
|
||||||
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
error_count = 0
|
|
||||||
[Cerfa, PieceJustificative, Procedure].each do |c|
|
|
||||||
c.all.each do |entry|
|
|
||||||
content = (c == Procedure) ? entry.logo : entry.content
|
|
||||||
if !(content.current_path.nil? || File.exist?(File.dirname(content.current_path) + '/uploaded'))
|
|
||||||
secure_token = SecureRandom.uuid
|
|
||||||
filename = "#{entry.class.to_s.underscore}-#{secure_token}#{File.extname(content.current_path)}"
|
|
||||||
rake_puts "Uploading #{content.current_path}"
|
|
||||||
begin
|
|
||||||
@cont.create_object(filename, {}, File.open(content.current_path))
|
|
||||||
|
|
||||||
File.open(File.dirname(content.current_path) + '/uploaded', "w+") { |f| f.write(File.basename(content.current_path)) }
|
|
||||||
File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "w+") { |f| f.write(filename) }
|
|
||||||
File.open(File.dirname(content.current_path) + '/secure_token_cloudstorage', "w+") { |f| f.write(secure_token) }
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, secure_token)
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
rake_puts "ERROR: #{content.current_path} does not exist!"
|
|
||||||
File.open('upload_errors.report', "a+") { |f| f.write(content.current_path) }
|
|
||||||
error_count += 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if content.current_path.present? && File.exist?(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
filename = File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "r").read
|
|
||||||
secure_token = File.open(File.dirname(content.current_path) + '/secure_token_cloudstorage', "r").read
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, secure_token)
|
|
||||||
|
|
||||||
rake_puts "RESTORE IN DATABASE: #{filename} "
|
|
||||||
elsif content.current_path.present?
|
|
||||||
rake_puts "Skipping #{content.current_path}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rake_puts "There were #{error_count} errors while uploading files. See upload_errors.report file for details."
|
|
||||||
puts 'Enf of migration'
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Clear documents in tenant and revert file entries in database'
|
|
||||||
task :revert do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
[Cerfa, PieceJustificative, Procedure].each do |c|
|
|
||||||
c.all.each do |entry|
|
|
||||||
content = (c == Procedure) ? entry.logo : entry.content
|
|
||||||
if content.current_path.present?
|
|
||||||
if File.exist?(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
previous_filename = File.read(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, previous_filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, nil)
|
|
||||||
|
|
||||||
rake_puts "restoring #{content.current_path} db data to #{previous_filename}"
|
|
||||||
|
|
||||||
@cont.delete_object(File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "r").read)
|
|
||||||
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/filename_cloudstorage')
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/secure_token_cloudstorage')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Clear old documents in tenant'
|
|
||||||
task :clear do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
@cont.objects.each do |object|
|
|
||||||
rake_puts "Removing #{object}"
|
|
||||||
@cont.delete_object(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task :clear_old_objects do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
@cont.objects_detail.each do |object, details|
|
|
||||||
last_modified = Time.zone.parse(details[:last_modified])
|
|
||||||
@cont.delete_object(object) if last_modified.utc <= (Time.zone.now - 2.years).utc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,19 +2,53 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe Admin::AssignsController, type: :controller do
|
describe Admin::AssignsController, type: :controller do
|
||||||
let(:admin) { create(:administrateur) }
|
let(:admin) { create(:administrateur) }
|
||||||
let(:procedure) { create :procedure, administrateur: admin }
|
|
||||||
let(:instructeur) { create :instructeur, administrateurs: [admin] }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
sign_in(admin.user)
|
sign_in(admin.user)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #show' do
|
describe 'GET #show' do
|
||||||
subject { get :show, params: { procedure_id: procedure.id } }
|
let(:procedure) { create :procedure, administrateur: admin, instructeurs: [instructeur_assigned_1, instructeur_assigned_2] }
|
||||||
it { expect(subject.status).to eq(200) }
|
let!(:instructeur_assigned_1) { create :instructeur, email: 'instructeur_1@ministere_a.gouv.fr', administrateurs: [admin] }
|
||||||
|
let!(:instructeur_assigned_2) { create :instructeur, email: 'instructeur_2@ministere_b.gouv.fr', administrateurs: [admin] }
|
||||||
|
let!(:instructeur_not_assigned_1) { create :instructeur, email: 'instructeur_3@ministere_a.gouv.fr', administrateurs: [admin] }
|
||||||
|
let!(:instructeur_not_assigned_2) { create :instructeur, email: 'instructeur_4@ministere_b.gouv.fr', administrateurs: [admin] }
|
||||||
|
let(:filter) { nil }
|
||||||
|
|
||||||
|
subject! { get :show, params: { procedure_id: procedure.id, filter: filter } }
|
||||||
|
|
||||||
|
it { expect(response.status).to eq(200) }
|
||||||
|
|
||||||
|
it 'sets the assigned and not assigned instructeurs' do
|
||||||
|
expect(assigns(:instructeurs_assign)).to match_array([instructeur_assigned_1, instructeur_assigned_2])
|
||||||
|
expect(assigns(:instructeurs_not_assign)).to match_array([instructeur_not_assigned_1, instructeur_not_assigned_2])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a search filter' do
|
||||||
|
let(:filter) { '@ministere_a.gouv.fr' }
|
||||||
|
|
||||||
|
it 'filters the unassigned instructeurs' do
|
||||||
|
expect(assigns(:instructeurs_not_assign)).to match_array([instructeur_not_assigned_1])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not filter the assigned instructeurs' do
|
||||||
|
expect(assigns(:instructeurs_assign)).to match_array([instructeur_assigned_1, instructeur_assigned_2])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the filter has spaces or a mixed case' do
|
||||||
|
let(:filter) { ' @ministere_A.gouv.fr ' }
|
||||||
|
|
||||||
|
it 'trims spaces and ignores the case' do
|
||||||
|
expect(assigns(:instructeurs_not_assign)).to match_array([instructeur_not_assigned_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT #update' do
|
describe 'PUT #update' do
|
||||||
|
let(:procedure) { create :procedure, administrateur: admin }
|
||||||
|
let(:instructeur) { create :instructeur, administrateurs: [admin] }
|
||||||
|
|
||||||
subject { put :update, params: { instructeur_id: instructeur.id, procedure_id: procedure.id, to: 'assign' } }
|
subject { put :update, params: { instructeur_id: instructeur.id, procedure_id: procedure.id, to: 'assign' } }
|
||||||
|
|
||||||
it { expect(subject).to redirect_to admin_procedure_assigns_path(procedure_id: procedure.id) }
|
it { expect(subject).to redirect_to admin_procedure_assigns_path(procedure_id: procedure.id) }
|
||||||
|
|
|
@ -12,6 +12,15 @@ describe API::V2::GraphqlController do
|
||||||
create(:commentaire, dossier: dossier, email: 'test@test.com')
|
create(:commentaire, dossier: dossier, email: 'test@test.com')
|
||||||
dossier
|
dossier
|
||||||
end
|
end
|
||||||
|
let(:dossier1) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 1.day.ago) }
|
||||||
|
let(:dossier2) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 3.days.ago) }
|
||||||
|
let!(:dossier_brouillon) { create(:dossier, procedure: procedure) }
|
||||||
|
let(:dossiers) { [dossier2, dossier1, dossier] }
|
||||||
|
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
instructeur.assign_to_procedure(procedure)
|
||||||
|
end
|
||||||
|
|
||||||
let(:query) do
|
let(:query) do
|
||||||
"{
|
"{
|
||||||
|
@ -62,31 +71,65 @@ describe API::V2::GraphqlController do
|
||||||
request.env['HTTP_AUTHORIZATION'] = authorization_header
|
request.env['HTTP_AUTHORIZATION'] = authorization_header
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return demarche" do
|
context "demarche" do
|
||||||
expect(gql_errors).to eq(nil)
|
it "should be returned" do
|
||||||
expect(gql_data).to eq(demarche: {
|
expect(gql_errors).to eq(nil)
|
||||||
id: procedure.to_typed_id,
|
expect(gql_data).to eq(demarche: {
|
||||||
number: procedure.id.to_s,
|
id: procedure.to_typed_id,
|
||||||
title: procedure.libelle,
|
number: procedure.id,
|
||||||
description: procedure.description,
|
title: procedure.libelle,
|
||||||
state: 'brouillon',
|
description: procedure.description,
|
||||||
archivedAt: nil,
|
state: 'brouillon',
|
||||||
createdAt: procedure.created_at.iso8601,
|
archivedAt: nil,
|
||||||
updatedAt: procedure.updated_at.iso8601,
|
createdAt: procedure.created_at.iso8601,
|
||||||
groupeInstructeurs: [{ instructeurs: [], label: "défaut" }],
|
updatedAt: procedure.updated_at.iso8601,
|
||||||
champDescriptors: procedure.types_de_champ.map do |tdc|
|
groupeInstructeurs: [
|
||||||
{
|
{
|
||||||
id: tdc.to_typed_id,
|
instructeurs: [{ email: instructeur.email }],
|
||||||
label: tdc.libelle,
|
label: "défaut"
|
||||||
type: tdc.type_champ,
|
}
|
||||||
description: tdc.description,
|
],
|
||||||
required: tdc.mandatory?
|
champDescriptors: procedure.types_de_champ.map do |tdc|
|
||||||
|
{
|
||||||
|
id: tdc.to_typed_id,
|
||||||
|
label: tdc.libelle,
|
||||||
|
type: tdc.type_champ,
|
||||||
|
description: tdc.description,
|
||||||
|
required: tdc.mandatory?
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
dossiers: {
|
||||||
|
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
|
||||||
}
|
}
|
||||||
end,
|
})
|
||||||
dossiers: {
|
end
|
||||||
nodes: []
|
|
||||||
}
|
context "filter dossiers" do
|
||||||
})
|
let(:query) do
|
||||||
|
"{
|
||||||
|
demarche(number: #{procedure.id}) {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
dossiers(createdSince: \"#{2.days.ago.iso8601}\") {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be returned" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(demarche: {
|
||||||
|
id: procedure.to_typed_id,
|
||||||
|
number: procedure.id,
|
||||||
|
dossiers: {
|
||||||
|
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "dossier" do
|
context "dossier" do
|
||||||
|
@ -130,11 +173,11 @@ describe API::V2::GraphqlController do
|
||||||
}"
|
}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return dossier" do
|
it "should be returned" do
|
||||||
expect(gql_errors).to eq(nil)
|
expect(gql_errors).to eq(nil)
|
||||||
expect(gql_data).to eq(dossier: {
|
expect(gql_data).to eq(dossier: {
|
||||||
id: dossier.to_typed_id,
|
id: dossier.to_typed_id,
|
||||||
number: dossier.id.to_s,
|
number: dossier.id,
|
||||||
state: 'en_construction',
|
state: 'en_construction',
|
||||||
updatedAt: dossier.updated_at.iso8601,
|
updatedAt: dossier.updated_at.iso8601,
|
||||||
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
||||||
|
@ -146,7 +189,12 @@ describe API::V2::GraphqlController do
|
||||||
id: dossier.user.to_typed_id,
|
id: dossier.user.to_typed_id,
|
||||||
email: dossier.user.email
|
email: dossier.user.email
|
||||||
},
|
},
|
||||||
instructeurs: [],
|
instructeurs: [
|
||||||
|
{
|
||||||
|
id: instructeur.to_typed_id,
|
||||||
|
email: instructeur.email
|
||||||
|
}
|
||||||
|
],
|
||||||
messages: dossier.commentaires.map do |commentaire|
|
messages: dossier.commentaires.map do |commentaire|
|
||||||
{
|
{
|
||||||
body: commentaire.body,
|
body: commentaire.body,
|
||||||
|
@ -166,6 +214,114 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_data[:dossier][:champs][0][:id]).to eq(dossier.champs[0].type_de_champ.to_typed_id)
|
expect(gql_data[:dossier][:champs][0][:id]).to eq(dossier.champs[0].type_de_champ.to_typed_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "mutations" do
|
||||||
|
describe 'dossierEnvoyerMessage' do
|
||||||
|
context 'success' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"Bonjour\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should post a message" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierEnvoyerMessage: {
|
||||||
|
message: {
|
||||||
|
body: "Bonjour"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'schema error' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_data).to eq(nil)
|
||||||
|
expect(gql_errors).not_to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierEnvoyerMessage: {
|
||||||
|
errors: [{ message: "Votre message ne peut être vide" }],
|
||||||
|
message: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'createDirectUpload' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
createDirectUpload(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
filename: \"hello.png\",
|
||||||
|
byteSize: 1234,
|
||||||
|
checksum: \"qwerty1234\",
|
||||||
|
contentType: \"image/png\"
|
||||||
|
}) {
|
||||||
|
directUpload {
|
||||||
|
url
|
||||||
|
headers
|
||||||
|
blobId
|
||||||
|
signedBlobId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should initiate a direct upload" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
data = gql_data[:createDirectUpload][:directUpload]
|
||||||
|
expect(data[:url]).not_to be_nil
|
||||||
|
expect(data[:headers]).not_to be_nil
|
||||||
|
expect(data[:blobId]).not_to be_nil
|
||||||
|
expect(data[:signedBlobId]).not_to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when not authenticated" do
|
context "when not authenticated" do
|
||||||
|
@ -182,5 +338,26 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors).not_to eq(nil)
|
expect(gql_errors).not_to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "mutation" do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"Bonjour\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return error" do
|
||||||
|
expect(gql_data[:dossierEnvoyerMessage]).to eq(nil)
|
||||||
|
expect(gql_errors).not_to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ feature 'As an administrateur', js: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'I can register' do
|
scenario 'I can register' do
|
||||||
expect(new_admin.reload.active?).to be(false)
|
expect(new_admin.reload.user.active?).to be(false)
|
||||||
|
|
||||||
confirmation_email = open_email(admin_email)
|
confirmation_email = open_email(admin_email)
|
||||||
token_params = confirmation_email.body.match(/token=[^"]+/)
|
token_params = confirmation_email.body.match(/token=[^"]+/)
|
||||||
|
@ -24,6 +24,6 @@ feature 'As an administrateur', js: true do
|
||||||
|
|
||||||
expect(page).to have_content 'Mot de passe enregistré'
|
expect(page).to have_content 'Mot de passe enregistré'
|
||||||
|
|
||||||
expect(new_admin.reload.active?).to be(true)
|
expect(new_admin.reload.user.active?).to be(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ feature 'The routing' do
|
||||||
let(:scientifique_user) { create(:user, password: password) }
|
let(:scientifique_user) { create(:user, password: password) }
|
||||||
let(:litteraire_user) { create(:user, password: password) }
|
let(:litteraire_user) { create(:user, password: password) }
|
||||||
|
|
||||||
before { Flipper.enable_actor(:routage, administrateur.user) }
|
before { Flipper.enable_actor(:administrateur_routage, administrateur.user) }
|
||||||
|
|
||||||
scenario 'works' do
|
scenario 'works' do
|
||||||
login_as administrateur.user, scope: :user
|
login_as administrateur.user, scope: :user
|
||||||
|
|
20
spec/mailers/application_mailer_spec.rb
Normal file
20
spec/mailers/application_mailer_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
RSpec.describe ApplicationMailer, type: :mailer do
|
||||||
|
describe 'dealing with invalid emails' do
|
||||||
|
let(:dossier) { create(:dossier, procedure: build(:simple_procedure)) }
|
||||||
|
subject { DossierMailer.notify_new_draft(dossier) }
|
||||||
|
|
||||||
|
describe 'invalid emails are not sent' do
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(DossierMailer)
|
||||||
|
.to receive(:notify_new_draft)
|
||||||
|
.and_raise(Net::SMTPSyntaxError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(subject.message).to be_an_instance_of(ActionMailer::Base::NullMail) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'valid emails are sent' do
|
||||||
|
it { expect(subject.message).not_to be_an_instance_of(ActionMailer::Base::NullMail) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -50,22 +50,4 @@ describe Administrateur, type: :model do
|
||||||
# it { expect(subject).to eq([]) }
|
# it { expect(subject).to eq([]) }
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
|
|
||||||
describe '#active?' do
|
|
||||||
let!(:administrateur) { create(:administrateur) }
|
|
||||||
|
|
||||||
subject { administrateur.active? }
|
|
||||||
|
|
||||||
context 'when the user has never signed in' do
|
|
||||||
before { administrateur.user.update(last_sign_in_at: nil) }
|
|
||||||
|
|
||||||
it { is_expected.to be false }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the user has already signed in' do
|
|
||||||
before { administrateur.user.update(last_sign_in_at: Time.zone.now) }
|
|
||||||
|
|
||||||
it { is_expected.to be true }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,4 +148,22 @@ shared_examples 'type_de_champ_spec' do
|
||||||
expect(cloned_procedure.types_de_champ.first.types_de_champ).not_to be_empty
|
expect(cloned_procedure.types_de_champ.first.types_de_champ).not_to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "linked_drop_down_list" do
|
||||||
|
let(:type_de_champ) { create(:type_de_champ_linked_drop_down_list) }
|
||||||
|
|
||||||
|
it 'should validate without label' do
|
||||||
|
type_de_champ.drop_down_list_value = 'toto'
|
||||||
|
expect(type_de_champ.validate).to be_falsey
|
||||||
|
messages = type_de_champ.errors.full_messages
|
||||||
|
expect(messages.size).to eq(1)
|
||||||
|
expect(messages.first.starts_with?("#{type_de_champ.libelle} doit commencer par")).to be_truthy
|
||||||
|
|
||||||
|
type_de_champ.libelle = ''
|
||||||
|
expect(type_de_champ.validate).to be_falsey
|
||||||
|
messages = type_de_champ.errors.full_messages
|
||||||
|
expect(messages.size).to eq(2)
|
||||||
|
expect(messages.last.starts_with?("La liste doit commencer par")).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -191,4 +191,22 @@ describe User, type: :model do
|
||||||
it { expect(AdministrationMailer).to have_received(:invite_admin).with(user, nil, administration.id) }
|
it { expect(AdministrationMailer).to have_received(:invite_admin).with(user, nil, administration.id) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#active?' do
|
||||||
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
|
subject { user.active? }
|
||||||
|
|
||||||
|
context 'when the user has never signed in' do
|
||||||
|
before { user.update(last_sign_in_at: nil) }
|
||||||
|
|
||||||
|
it { is_expected.to be false }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user has already signed in' do
|
||||||
|
before { user.update(last_sign_in_at: Time.zone.now) }
|
||||||
|
|
||||||
|
it { is_expected.to be true }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
describe ProcedureExportV2Service do
|
describe ProcedureExportV2Service do
|
||||||
describe 'to_data' do
|
describe 'to_data' do
|
||||||
|
@ -150,6 +151,91 @@ describe ProcedureExportV2Service do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'as csv' do
|
||||||
|
subject do
|
||||||
|
Tempfile.create do |f|
|
||||||
|
f << ProcedureExportV2Service.new(procedure, procedure.dossiers).to_csv
|
||||||
|
f.rewind
|
||||||
|
CSV.read(f.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:nominal_headers) do
|
||||||
|
[
|
||||||
|
"ID",
|
||||||
|
"Email",
|
||||||
|
"Établissement SIRET",
|
||||||
|
"Établissement siège social",
|
||||||
|
"Établissement NAF",
|
||||||
|
"Établissement libellé NAF",
|
||||||
|
"Établissement Adresse",
|
||||||
|
"Établissement numero voie",
|
||||||
|
"Établissement type voie",
|
||||||
|
"Établissement nom voie",
|
||||||
|
"Établissement complément adresse",
|
||||||
|
"Établissement code postal",
|
||||||
|
"Établissement localité",
|
||||||
|
"Établissement code INSEE localité",
|
||||||
|
"Entreprise SIREN",
|
||||||
|
"Entreprise capital social",
|
||||||
|
"Entreprise numero TVA intracommunautaire",
|
||||||
|
"Entreprise forme juridique",
|
||||||
|
"Entreprise forme juridique code",
|
||||||
|
"Entreprise nom commercial",
|
||||||
|
"Entreprise raison sociale",
|
||||||
|
"Entreprise SIRET siège social",
|
||||||
|
"Entreprise code effectif entreprise",
|
||||||
|
"Entreprise date de création",
|
||||||
|
"Entreprise nom",
|
||||||
|
"Entreprise prénom",
|
||||||
|
"Association RNA",
|
||||||
|
"Association titre",
|
||||||
|
"Association objet",
|
||||||
|
"Association date de création",
|
||||||
|
"Association date de déclaration",
|
||||||
|
"Association date de publication",
|
||||||
|
"Archivé",
|
||||||
|
"État du dossier",
|
||||||
|
"Dernière mise à jour le",
|
||||||
|
"Déposé le",
|
||||||
|
"Passé en instruction le",
|
||||||
|
"Traité le",
|
||||||
|
"Motivation de la décision",
|
||||||
|
"Instructeurs",
|
||||||
|
"textarea",
|
||||||
|
"date",
|
||||||
|
"datetime",
|
||||||
|
"number",
|
||||||
|
"decimal_number",
|
||||||
|
"integer_number",
|
||||||
|
"checkbox",
|
||||||
|
"civilite",
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
"address",
|
||||||
|
"yes_no",
|
||||||
|
"simple_drop_down_list",
|
||||||
|
"multiple_drop_down_list",
|
||||||
|
"linked_drop_down_list",
|
||||||
|
"pays",
|
||||||
|
"regions",
|
||||||
|
"departements",
|
||||||
|
"engagement",
|
||||||
|
"dossier_link",
|
||||||
|
"piece_justificative",
|
||||||
|
"siret",
|
||||||
|
"carte",
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:dossiers_sheet_headers) { subject.first }
|
||||||
|
|
||||||
|
it 'should have headers' do
|
||||||
|
expect(dossiers_sheet_headers).to match(nominal_headers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'should have headers' do
|
it 'should have headers' do
|
||||||
expect(dossiers_sheet.headers).to match(nominal_headers)
|
expect(dossiers_sheet.headers).to match(nominal_headers)
|
||||||
|
|
||||||
|
@ -225,7 +311,7 @@ describe ProcedureExportV2Service do
|
||||||
let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } }
|
let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } }
|
||||||
|
|
||||||
it 'should have sheets' do
|
it 'should have sheets' do
|
||||||
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle])
|
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have headers' do
|
it 'should have headers' do
|
||||||
|
@ -247,7 +333,18 @@ describe ProcedureExportV2Service do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have valid sheet name' do
|
it 'should have valid sheet name' do
|
||||||
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', "A - B - C"])
|
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', "(#{champ_repetition.type_de_champ.stable_id}) A - B - C"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with non unique labels' do
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) }
|
||||||
|
let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } }
|
||||||
|
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: procedure, libelle: champ_repetition.libelle) }
|
||||||
|
let!(:another_champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }
|
||||||
|
|
||||||
|
it 'should have sheets' do
|
||||||
|
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export, another_champ_repetition.libelle_for_export])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,7 +83,7 @@ VCR.configure do |c|
|
||||||
c.hook_into :webmock
|
c.hook_into :webmock
|
||||||
c.cassette_library_dir = 'spec/fixtures/cassettes'
|
c.cassette_library_dir = 'spec/fixtures/cassettes'
|
||||||
c.configure_rspec_metadata!
|
c.configure_rspec_metadata!
|
||||||
c.ignore_hosts 'test.host'
|
c.ignore_hosts 'test.host', 'chromedriver.storage.googleapis.com'
|
||||||
end
|
end
|
||||||
|
|
||||||
DatabaseCleaner.strategy = :transaction
|
DatabaseCleaner.strategy = :transaction
|
||||||
|
|
Loading…
Add table
Reference in a new issue