Merge branch 'develop'

This commit is contained in:
gregoirenovel 2017-07-05 11:19:10 +02:00
commit bd4810c42d
344 changed files with 4674 additions and 1407 deletions

View file

@ -1,6 +1,7 @@
version: 2
jobs:
build:
parallelism: 2
docker:
- image: ruby:2.3.1
- image: postgres:9.4.1
@ -53,6 +54,9 @@ jobs:
command: |
TESTFILES=$(circleci tests glob "spec/**/*.rb"| xargs -n 1 echo | grep -v "spec/factories/" | tr " " "\n" | circleci tests split --split-by=timings)
bundle exec rspec --color --require spec_helper -- ${TESTFILES}
- run:
name: Run rubocop
command: bundle exec rubocop
- run:
name: Run haml-lint
command: bundle exec haml-lint app/views/

1062
.rubocop.yml Normal file

File diff suppressed because it is too large Load diff

13
Gemfile
View file

@ -52,6 +52,7 @@ gem 'rest-client'
gem 'clamav-client', require: 'clamav/client'
gem 'carrierwave'
gem 'copy_carrierwave_file'
gem 'fog'
gem 'fog-openstack'
@ -101,6 +102,12 @@ gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git', require: false
gem 'select2-rails'
# PDF Generation
gem 'prawn', '~> 2.0.1'
gem 'prawn_rails', '~> 0.0.11'
gem 'sentry-raven'
group :test do
gem 'capybara'
gem 'launchy'
@ -122,6 +129,7 @@ group :development do
gem 'web-console'
gem 'rack-handlers'
gem 'xray-rails'
gem 'rubocop', require: false
gem 'haml-lint'
gem 'scss_lint', require: false
end
@ -142,8 +150,3 @@ group :development, :test do
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'dotenv-rails'
end
group :production, :staging do
gem 'sentry-raven'
end

View file

@ -120,6 +120,8 @@ GEM
coffee-script-source (1.12.2)
concurrent-ruby (1.0.5)
connection_pool (2.2.1)
copy_carrierwave_file (1.3.0)
carrierwave (>= 0.9)
crack (0.4.3)
safe_yaml (~> 1.0.0)
database_cleaner (1.5.3)
@ -417,14 +419,22 @@ GEM
openstack (3.3.7)
json
orm_adapter (0.5.0)
parallel (1.11.2)
parser (2.4.0.0)
ast (~> 2.2)
pdf-core (0.6.1)
pg (0.19.0)
poltergeist (1.14.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
websocket-driver (>= 0.2.0)
powerpack (0.1.1)
prawn (2.0.2)
pdf-core (~> 0.6.0)
ttfunk (~> 1.4.0)
prawn_rails (0.0.11)
prawn (>= 0.11.1)
railties (>= 3.0.0)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@ -471,7 +481,8 @@ GEM
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.1)
rainbow (2.2.2)
rake
raindrops (0.17.0)
rake (12.0.0)
rb-fsevent (0.9.8)
@ -522,7 +533,8 @@ GEM
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rubocop (0.48.1)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
@ -608,6 +620,7 @@ GEM
tilt (2.0.5)
timecop (0.8.1)
trollop (2.1.2)
ttfunk (1.4.0)
turbolinks (5.0.1)
turbolinks-source (~> 5)
turbolinks-source (5.0.0)
@ -618,7 +631,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.1.3)
unicode-display_width (1.2.1)
unicode_utils (1.4.0)
unicorn (5.2.0)
kgio (~> 2.6)
@ -674,6 +687,7 @@ DEPENDENCIES
carrierwave
chartkick
clamav-client
copy_carrierwave_file
database_cleaner
deep_cloneable (~> 2.2.1)
devise
@ -703,6 +717,8 @@ DEPENDENCIES
openstack
pg
poltergeist
prawn (~> 2.0.1)
prawn_rails (~> 0.0.11)
pry-byebug
rack-handlers
rails (= 5.0.0.1)
@ -711,6 +727,7 @@ DEPENDENCIES
rest-client
rgeo-geojson
rspec-rails (~> 3.0)
rubocop
sass-rails (~> 5.0)
scenic
scss_lint

View file

@ -24,7 +24,6 @@
# * zeus: 'zeus rspec' (requires the server to be started separetly)
# * 'just' rspec: 'rspec'
guard 'livereload' do
extensions = {
css: :css,

View file

@ -73,6 +73,7 @@ Pour exécuter les tests de l'application, plusieurs possibilités :
## Linting
- Faire tourner RuboCop : `bundle exec rubocop`
- Linter les fichiers HAML : `bundle exec haml-lint app/views/`
- Linter les fichiers SCSS : `bundle exec scss-lint app/assets/stylesheets/`

View file

@ -19,7 +19,6 @@ task :deploy_ha do
end
end
task :deploy_old do
domains = %w(37.187.154.237 37.187.249.111)
domains.each do |domain|
@ -27,7 +26,6 @@ task :deploy_old do
end
end
task :deploy_test do
domains = %w(192.168.0.116)
branch = 'clamav'

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="810" height="540"><desc>European flag</desc><defs><g id="d"><g id="b"><path id="a" d="M0 0v1h.5z" transform="rotate(18 3.157 -.5)"/><use xlink:href="#a" transform="scale(-1 1)"/></g><g id="c"><use xlink:href="#b" transform="rotate(72)"/><use xlink:href="#b" transform="rotate(144)"/></g><use xlink:href="#c" transform="scale(-1 1)"/></g></defs><path fill="#039" d="M0 0h810v540H0z"/><g fill="#fc0" transform="matrix(30 0 0 30 405 270)"><use xlink:href="#d" y="-6"/><use xlink:href="#d" y="6"/><g id="e"><use xlink:href="#d" x="-6"/><use xlink:href="#d" transform="rotate(-144 -2.344 -2.11)"/><use xlink:href="#d" transform="rotate(144 -2.11 -2.344)"/><use xlink:href="#d" transform="rotate(72 -4.663 -2.076)"/><use xlink:href="#d" transform="rotate(72 -5.076 .534)"/></g><use xlink:href="#e" transform="scale(-1 1)"/></g></svg>

After

Width:  |  Height:  |  Size: 917 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><g stroke="#4393F3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M18.938 20.625C17.055 22.127 14.6 23.084 12 23.084c-2.614 0-5.015-.954-6.902-2.476l-.1-.943v-.831c0-2.33 4.67-3.5 7.002-3.5 2.33 0 6.999 1.17 6.999 3.5V20l-.063.625zM12 11.333a2.989 2.989 0 0 0 2.995-2.999c0-1.66-1.332-3-2.995-3a2.998 2.998 0 0 0-3.005 3A2.997 2.997 0 0 0 12 11.333z"/><path d="M23 12c0 6.075-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1s11 4.925 11 11z"/></g><path d="M0 0h24v24H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 620 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="M17 9A8 8 0 1 1 1 9a8 8 0 0 1 16 0zm-2.187 5.875l8 8" stroke="#4393F3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M0 0h24v24H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 298 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="M0 0h24v24H0z"/><g stroke="#4393F3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M13 12H3m6.438 4.242L13.68 12 9.437 7.757"/><path d="M3 16v3a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5c0-1.1-.901-2-2-2H5c-1.099 0-2 .9-2 2v3"/></g></g></svg>

After

Width:  |  Height:  |  Size: 385 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><g stroke="#4393F3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M11.595 11.813a2.319 2.319 0 0 0 2.323-2.327 2.319 2.319 0 0 0-2.323-2.326c-1.29 0-2.33 1.04-2.33 2.326a2.324 2.324 0 0 0 2.33 2.327zm-4.508 7.288V17.63c0-1.808 2.977-2.716 4.783-2.716 1.807 0 4.782.908 4.782 2.716v1.471l-.002.433a8.953 8.953 0 0 1-4.923 1.464A8.999 8.999 0 0 1 3.56 8.22m16.843 6.18A8.998 8.998 0 0 0 6.978 4.355"/><path d="M1 9.259l2.667-1.094 1.092 2.667m17.816 2.045l-2.154 1.606-1.607-2.152"/></g><path d="M0 0h24v24H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 665 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="550.801" height="550.801" viewBox="0 0 550.801 550.801"><path d="M160.381 282.225c0-14.832-10.299-23.684-28.474-23.684-7.414 0-12.437.715-15.071 1.432V307.6c3.114.707 6.942.949 12.192.949 19.391 0 31.353-9.809 31.353-26.324zM272.875 259.019c-8.145 0-13.397.717-16.519 1.435v105.523c3.116.729 8.142.729 12.69.729 33.017.231 54.554-17.946 54.554-56.474.242-33.513-19.385-51.213-50.725-51.213z"/><path d="M488.426 197.019H475.2v-63.816c0-.398-.063-.799-.116-1.202-.021-2.534-.827-5.023-2.562-6.995L366.325 3.694c-.032-.031-.063-.042-.085-.076-.633-.707-1.371-1.295-2.151-1.804a9.495 9.495 0 0 0-.706-.419 11.131 11.131 0 0 0-2.131-.896c-.2-.056-.38-.138-.58-.19A10.774 10.774 0 0 0 358.193 0H97.2C85.282 0 75.6 9.693 75.6 21.601v175.413H62.377c-17.049 0-30.873 13.818-30.873 30.873v160.545c0 17.043 13.824 30.87 30.873 30.87h13.224V529.2c0 11.907 9.682 21.601 21.6 21.601h356.4c11.907 0 21.6-9.693 21.6-21.601V419.302h13.226c17.044 0 30.871-13.827 30.871-30.87v-160.54c-.001-17.054-13.828-30.873-30.872-30.873zM97.2 21.605h250.193v110.513c0 5.967 4.841 10.8 10.8 10.8H453.6v54.108H97.2V21.605zm265.159 287.418c0 30.876-11.243 52.165-26.82 65.333-16.971 14.117-42.82 20.814-74.396 20.814-18.9 0-32.297-1.197-41.401-2.389V234.365c13.399-2.149 30.878-3.346 49.304-3.346 30.612 0 50.478 5.508 66.039 17.226 16.743 12.445 27.274 32.302 27.274 60.778zM80.7 393.499V234.365c11.241-1.904 27.042-3.346 49.296-3.346 22.491 0 38.527 4.308 49.291 12.928 10.292 8.131 17.215 21.534 17.215 37.328 0 15.799-5.25 29.198-14.829 38.285-12.442 11.728-30.865 16.996-52.407 16.996-4.778 0-9.1-.243-12.435-.723v57.67H80.7v-.004zm372.901 129.854H97.2V419.302h356.4v104.051zm31.297-261.226h-61.989v36.851h57.913v29.674h-57.913V393.5h-36.593V232.216h98.582v29.911z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,18 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require highcharts
//= require chartkick
//= require_tree .

View file

@ -0,0 +1,10 @@
window.TPS = window.TPS || {};
$(document).on("click", "body", function () {
$(".header-menu").removeClass("open fade-in-down");
});
TPS.toggleHeaderMenu = function(event) {
event.stopPropagation();
$(".header-menu").toggleClass("open fade-in-down");
}

View file

@ -44,6 +44,8 @@
// = require switch_menu
// = require typeahead
// = require users
// = require attestation_template_edit
// = require attestation_recapitulatif
// = require_self
// = require bootstrap-datepicker3

View file

@ -0,0 +1,31 @@
#attestation-recapitulatif {
margin-top: 40px;
.details {
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #FFFFFF;
.left {
position: relative;
padding-left: 30px;
img {
position: absolute;
left: 0px;
top: 15px;
}
.title {
font-weight: bold;
margin: 0;
}
.delivery {
color: #999999;
}
}
}
}

View file

@ -0,0 +1,30 @@
#attestation-template-edit {
.notice {
margin-bottom: 30px;
}
.image-upload {
display: flex;
align-items: center;
input {
margin: 10px 0;
}
}
.thumbnail {
width: 90px;
margin-right: 15px;
}
.balises {
max-height: 180px;
overflow-y: scroll;
margin-bottom: 20px;
}
.table {
border: 1px solid #DDDDDD;
margin-bottom: 0px;
}
}

View file

@ -281,3 +281,8 @@
}
}
}
.motivation-text-area {
color: #000000;
margin-bottom: 15px;
}

View file

@ -19,3 +19,8 @@
max-width: $page-width + 2 * $default-padding;
margin: 0 auto;
}
%animation {
animation-fill-mode: forwards;
animation-duration: 0.3s;
}

View file

@ -0,0 +1,18 @@
@import "placeholders";
@keyframes fade-in-down {
0% {
opacity: 0;
margin-top: -10px;
}
100% {
opacity: 1;
margin-top: 0;
}
}
.fade-in-down {
@extend %animation;
animation-name: fade-in-down;
}

View file

@ -0,0 +1,108 @@
@import "colors";
@import "placeholders";
@import "mixins";
$auth-breakpoint: 820px;
@media (max-width: $auth-breakpoint) {
.preview {
display: none;
}
.two-columns .column.auth-form {
@include horizontal-padding(0);
width: 100%;
}
}
@media (max-width: $auth-breakpoint) {
.two-columns.auth {
background: #FFFFFF;
}
}
.preview {
font-size: 24px;
.paperless-logo {
width: 100%;
margin-bottom: 60px;
}
h3 {
color: $blue;
font-weight: bold;
}
.close-procedure {
font-size: 12px;
}
.procedure-title {
font-size: 30px;
margin: 50px 0 32px;
}
.procedure-description {
font-size: 14px;
}
.procedure-logos {
display: flex;
justify-content: space-around;
img {
max-height: 130px;
margin: 0 10px;
}
}
}
.auth-form {
font-size: 14px;
.reset-password {
margin-top: 8px;
}
.separation {
font-size: 14px;
color: $grey;
margin: 24px 0;
}
.login-with-fc {
display: inline-block;
height: 52px;
width: 186px;
margin: auto;
margin-bottom: 8px;
background-image: image-url("login-with-fc.svg");
background-repeat: no-repeat;
background-size: cover;
cursor: pointer;
&:hover {
background-image: image-url("login-with-fc-hover.svg");
}
}
hr {
margin-top: 60px;
margin-bottom: 20px;
background-color: $grey;
border: none;
height: 1px;
}
.register {
display: flex;
justify-content: space-between;
align-items: center;
span {
font-size: 18px;
font-weight: bold;
}
}
}

View file

@ -2,14 +2,6 @@
@import "colors";
.avis-sign-up {
display: flex;
.left,
.right {
width: 50%;
padding: 60px 86px;
}
.left {
p {
margin: auto;
@ -28,64 +20,4 @@
margin-top: 15px;
}
}
.right {
background-color: $light-grey;
h1 {
font-size: 36px;
font-weight: bold;
margin-bottom: 60px;
}
form {
max-width: 420px;
}
label,
input {
display: block;
width: 100%;
}
label {
font-size: 14px;
line-height: 1.57;
margin: 24px 0 8px;
}
input {
border: solid 1px $border-grey;
border-radius: 4px;
height: 56px;
padding: 0 15px;
font-family: Muli;
font-size: 14px;
&:disabled {
background-color: $border-grey;
}
}
button {
display: inline-block;
height: 60px;
line-height: 60px;
border: none;
border-radius: 60px;
background-color: $blue;
color: #FFFFFF;
font-size: 16px;
text-align: center;
width: 100%;
margin: 55px 0;
&:hover {
color: #FFFFFF;
text-decoration: none;
background-color: $light-blue;
cursor: pointer;
}
}
}
}

View file

@ -0,0 +1,51 @@
@import "colors";
.button {
display: inline-block;
padding: 8px 16px;
border-radius: 30px;
border: 1px solid $border-grey;
font-size: 14px;
background-color: #FFFFFF;
color: $black;
cursor: pointer;
&:hover {
background: $light-grey;
text-decoration: none;
}
&.primary {
color: #FFFFFF;
border-color: $blue;
background-color: $blue;
&:hover {
background: $light-blue;
}
}
&.secondary {
color: $blue;
border-color: $blue;
background-color: #FFFFFF;
&:hover {
color: #FFFFFF;
background: $light-blue;
}
}
&.large {
font-size: 18px;
padding: 15px 32px;
}
&.expand {
width: 100%;
}
}
.link {
color: $blue;
}

View file

@ -5,3 +5,8 @@ body {
font-size: 16px;
line-height: 1.42857143;
}
h1 {
font-size: 36px;
font-weight: bold;
}

View file

@ -0,0 +1,37 @@
@import "colors";
.form {
h1 {
text-align: center;
margin-bottom: 20px;
}
label,
input {
font-size: 14px;
}
label,
input[type=submit] {
margin-top: 24px;
}
label {
margin-bottom: 8px;
display: inline-block;
}
input[type=text],
input[type=email],
input[type=password] {
display: block;
width: 100%;
border-radius: 4px;
border: solid 1px $border-grey;
padding: 16px;
&:disabled {
background-color: $border-grey;
}
}
}

View file

@ -70,6 +70,7 @@ $landing-breakpoint: 1040px;
color: #FFFFFF;
font-size: 24px;
margin-top: 30px;
cursor: pointer;
&:hover {
color: #FFFFFF;
@ -286,6 +287,7 @@ $cta-panel-button-border-size: 2px;
color: #FFFFFF;
font-size: 24px;
text-align: center;
cursor: pointer;
&:hover {
color: #FFFFFF;

View file

@ -0,0 +1,32 @@
@import "colors";
@import "constants";
@import "placeholders";
.two-columns {
$column-padding: 60px;
$two-columns-breakpoint: $page-width + (2 * $column-padding);
background: linear-gradient(to right, #FFFFFF 0%, #FFFFFF 50%, $light-grey 50%, $light-grey 100%);
.columns-container {
@extend %page-width-container;
display: flex;
flex-direction: row;
align-items: center;
}
.column {
width: 50%;
padding: $column-padding;
@media (min-width: $two-columns-breakpoint) {
&:first-child {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
}
}
}

View file

@ -17,39 +17,100 @@
@extend %page-width-container;
display: flex;
justify-content: space-between;
padding-top: 17px;
}
.header-logo {
margin-top: 17px;
.header-right-content {
display: flex;
align-items: center;
> li {
@include horizontal-padding(8px);
&:last-child {
padding-right: 0;
}
}
}
$header-login-button-height: 36px;
$header-login-button-border-size: 1px;
.header-search {
position: relative;
.header-login-button {
@include horizontal-padding(16px);
.form input[type=text] {
padding: 9px;
padding-right: 42px;
float: right;
width: 300px;
}
display: inline-block;
height: $header-login-button-height;
line-height: $header-login-button-height - (2 * $header-login-button-border-size);
border-radius: $header-login-button-height;
border: $header-login-button-border-size solid $blue;
color: $blue;
button {
padding: 6px 9px;
border: none;
background: none;
cursor: pointer;
position: absolute;
right: 0;
&:hover {
opacity: 0.8;
}
}
}
.header-menu-opener {
position: relative;
img {
cursor: pointer;
&:hover {
opacity: 0.8;
}
}
}
.header-menu {
display: none;
position: absolute;
right: 0;
top: 34px;
font-size: 14px;
margin-top: 18px;
background: #FFFFFF;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
border: 1px solid $border-grey;
min-width: 270px;
max-width: 340px;
&:hover {
color: #FFFFFF;
text-decoration: none;
background-color: $light-blue;
&.open {
display: block;
}
&:focus {
color: $blue;
text-decoration: none;
}
li {
border-bottom: 1px solid $border-grey;
&:hover:focus {
color: #FFFFFF;
&:last-child {
border-bottom: none;
}
.menu-item {
align-items: center;
padding: 14px;
color: $grey;
overflow: hidden;
text-overflow: ellipsis;
img {
margin-right: 14px;
}
}
.menu-link {
display: flex;
color: $black;
&:hover {
background: $light-grey;
}
}
}
}

View file

@ -0,0 +1,11 @@
@import "placeholders";
.patron {
.patron-container {
@extend %page-width-container;
}
p {
margin-bottom: 20px;
}
}

View file

@ -14,6 +14,10 @@
text-align: center;
}
.text-right {
text-align: right;
}
.hidden {
display: none;
}

View file

@ -7,17 +7,17 @@ class Admin::AccompagnateursController < AdminController
def show
assign_scope = @procedure.gestionnaires
@accompagnateurs_assign = smart_listing_create :accompagnateurs_assign,
assign_scope,
partial: "admin/accompagnateurs/list_assign",
array: true
assign_scope,
partial: "admin/accompagnateurs/list_assign",
array: true
not_assign_scope = current_administrateur.gestionnaires.where.not(id: assign_scope.ids)
not_assign_scope = not_assign_scope.where("email LIKE '%#{params[:filter]}%'") if params[:filter]
@accompagnateurs_not_assign = smart_listing_create :accompagnateurs_not_assign,
not_assign_scope,
partial: "admin/accompagnateurs/list_not_assign",
array: true
not_assign_scope,
partial: "admin/accompagnateurs/list_not_assign",
array: true
@gestionnaire ||= Gestionnaire.new
end

View file

@ -0,0 +1,64 @@
class Admin::AttestationTemplatesController < AdminController
before_action :retrieve_procedure
def edit
@attestation_template = @procedure.attestation_template || AttestationTemplate.new(procedure: @procedure)
end
def update
attestation_template = @procedure.attestation_template
if attestation_template.update_attributes(activated_attestation_params)
flash.notice = "L'attestation a bien été modifiée"
else
flash.alert = attestation_template.errors.full_messages.join('<br>')
end
redirect_to edit_admin_procedure_attestation_template_path(@procedure)
end
def create
attestation_template = AttestationTemplate.new(activated_attestation_params.merge(procedure_id: @procedure.id))
if attestation_template.save
flash.notice = "L'attestation a bien été sauvegardée"
else
flash.alert = attestation_template.errors.full_messages.join('<br>')
end
redirect_to edit_admin_procedure_attestation_template_path(@procedure)
end
def disactivate
attestation_template = @procedure.attestation_template
attestation_template.activated = false
attestation_template.save
flash.notice = "L'attestation a bien été désactivée"
redirect_to edit_admin_procedure_attestation_template_path(@procedure)
end
def preview
@title = activated_attestation_params[:title]
@body = activated_attestation_params[:body]
@footer = activated_attestation_params[:footer]
@created_at = DateTime.now
# In a case of a preview, when the user does not change its images,
# the images are not uploaded and thus should be retrieved from previous
# attestation_template
@logo = activated_attestation_params[:logo] || @procedure.attestation_template&.logo
@signature = activated_attestation_params[:signature] || @procedure.attestation_template&.signature
render 'admin/attestation_templates/show', formats: [:pdf]
end
private
def activated_attestation_params
params.require(:attestation_template)
.permit(:title, :body, :footer, :logo, :signature)
.merge(activated: true)
end
end

View file

@ -4,13 +4,13 @@ class Admin::GestionnairesController < AdminController
def index
@gestionnaires = smart_listing_create :gestionnaires,
current_administrateur.gestionnaires,
partial: "admin/gestionnaires/list",
array: true
current_administrateur.gestionnaires,
partial: "admin/gestionnaires/list",
array: true
@gestionnaire ||= Gestionnaire.new
end
def create
@gestionnaire = Gestionnaire.find_by_email(params[:gestionnaire][:email])
procedure_id = params[:procedure_id]
@ -47,7 +47,6 @@ class Admin::GestionnairesController < AdminController
User.create(attributes)
flash.notice = 'Accompagnateur ajouté'
GestionnaireMailer.new_gestionnaire(@gestionnaire.email, @gestionnaire.password).deliver_now!
GestionnaireMailer.new_assignement(@gestionnaire.email, current_administrateur.email).deliver_now!
else
flash.alert = @gestionnaire.errors.full_messages.join('<br />').html_safe
end
@ -57,7 +56,6 @@ class Admin::GestionnairesController < AdminController
if current_administrateur.gestionnaires.include? @gestionnaire
flash.alert = 'Accompagnateur déjà ajouté'
else
GestionnaireMailer.new_assignement(@gestionnaire.email, current_administrateur.email).deliver_now!
@gestionnaire.administrateurs.push current_administrateur
flash.notice = 'Accompagnateur ajouté'
#TODO Mailer no assign_to

View file

@ -6,18 +6,18 @@ class Admin::ProceduresController < AdminController
def index
@procedures = smart_listing_create :procedures,
current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
active_class
end
def archived
@procedures = smart_listing_create :procedures,
current_administrateur.procedures.where(archived: true).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
current_administrateur.procedures.where(archived: true).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
archived_class
@ -26,9 +26,9 @@ class Admin::ProceduresController < AdminController
def draft
@procedures = smart_listing_create :procedures,
current_administrateur.procedures.where(published: false, archived: false).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
current_administrateur.procedures.where(published: false, archived: false).order(created_at: :desc),
partial: "admin/procedures/list",
array: true
draft_class
@ -40,7 +40,14 @@ class Admin::ProceduresController < AdminController
end
def edit
end
def hide
procedure = Procedure.find(params[:id])
procedure.hide!
flash.notice = "Procédure supprimée, en cas d'erreur contactez nous : contact@tps.apientreprise.fr"
redirect_to admin_procedures_draft_path
end
def destroy
@ -122,19 +129,21 @@ class Admin::ProceduresController < AdminController
end
def transfer
admin = Administrateur.find_by_email(params[:email_admin])
admin = Administrateur.find_by_email(params[:email_admin].downcase)
return render '/admin/procedures/transfer', formats: 'js', status: 404 if admin.nil?
if admin.nil?
render '/admin/procedures/transfer', formats: 'js', status: 404
else
procedure = current_administrateur.procedures.find(params[:procedure_id])
clone_procedure = procedure.clone
procedure = current_administrateur.procedures.find(params[:procedure_id])
clone_procedure = procedure.clone
clone_procedure.administrateur = admin
clone_procedure.save
clone_procedure.administrateur = admin
clone_procedure.save
flash.now.notice = "La procédure a correctement été clonée vers le nouvel administrateur."
flash.now.notice = "La procédure a correctement été cloné vers le nouvel administrateur."
render '/admin/procedures/transfer', formats: 'js', status: 200
render '/admin/procedures/transfer', formats: 'js', status: 200
end
end
def archive

View file

@ -1,4 +1,6 @@
class Administrateurs::SessionsController < Sessions::SessionsController
layout "new_application"
def demo
return redirect_to root_path if Rails.env.production?

View file

@ -8,11 +8,9 @@ class AdministrationsController < ApplicationController
@admin = Administrateur.new
@admins = smart_listing_create :admins,
Administrateur.all.order(:email),
partial: "administrations/list",
array: true
Administrateur.all.order(:email),
partial: "administrations/list",
array: true
end
def create
@ -20,7 +18,7 @@ class AdministrationsController < ApplicationController
if admin.save
flash.notice = "Administrateur créé"
NewAdminMailer.new_admin_email(admin, params[:administrateur][:password]).deliver_now!
NewAdminMailer.new_admin_email(admin).deliver_now!
else
flash.alert = admin.errors.full_messages.join('<br>').html_safe
end

View file

@ -13,5 +13,4 @@ class API::V1::ProceduresController < APIController
Rails.logger.error(e.message)
render json: {}, status: 404
end
end

View file

@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :check_browser
before_action :load_navbar_left_pannel_partial_url
before_action :set_raven_context
def default_url_options
return {protocol: 'https'} if Rails.env.staging? || Rails.env.production?
@ -40,4 +41,25 @@ class ApplicationController < ActionController::Base
redirect_to new_user_session_path
end
end
private
def set_raven_context
context = { ip_address: request.ip }
logged_models = [
current_user,
current_gestionnaire,
current_administrateur,
current_administration
].compact
context[:email] = logged_models.first&.email
context[:id] = logged_models.first&.id
class_names = logged_models.map { |model| model.class.name }
context[:classes] = class_names.any? ? class_names.join(', ') : 'Guest'
Raven.user_context(context)
end
end

View file

@ -1,5 +1,4 @@
class Backoffice::AvisController < ApplicationController
before_action :authenticate_gestionnaire!, except: [:sign_up, :create_gestionnaire]
before_action :redirect_if_no_sign_up_needed, only: [:sign_up]
before_action :check_avis_exists_and_email_belongs_to_avis, only: [:sign_up, :create_gestionnaire]
@ -50,7 +49,7 @@ class Backoffice::AvisController < ApplicationController
avis = Avis.find(params[:id])
redirect_to url_for(backoffice_dossier_path(avis.dossier_id))
else
flash[:alert] = gestionnaire.errors.full_messages.join('<br>')
flash[:alert] = gestionnaire.errors.full_messages
redirect_to url_for(avis_sign_up_path(params[:id], email))
end
end

View file

@ -21,5 +21,4 @@ class Backoffice::Dossiers::ProcedureController < Backoffice::DossiersListContro
def retrieve_procedure
current_gestionnaire.procedures.find params[:id]
end
end

View file

@ -1,4 +1,6 @@
class Backoffice::DossiersController < Backoffice::DossiersListController
include ActionView::Helpers::NumberHelper
respond_to :html, :xlsx, :ods, :csv
prepend_before_action :store_current_location, only: :show
@ -79,17 +81,17 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
end
smart_listing_create :search,
@dossiers,
partial: "backoffice/dossiers/list",
array: true,
default_sort: dossiers_list_facade.service.default_sort
@dossiers,
partial: "backoffice/dossiers/list",
array: true,
default_sort: dossiers_list_facade.service.default_sort
rescue RuntimeError
smart_listing_create :search,
[],
partial: "backoffice/dossiers/list",
array: true,
default_sort: dossiers_list_facade.service.default_sort
[],
partial: "backoffice/dossiers/list",
array: true,
default_sort: dossiers_list_facade.service.default_sort
end
def receive
@ -103,41 +105,44 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
redirect_to backoffice_dossier_path(id: dossier.id)
end
def refuse
def process_dossier
create_dossier_facade params[:dossier_id]
if params[:dossier] && params[:dossier][:motivation].present?
motivation = params[:dossier][:motivation]
end
dossier = @facade.dossier
dossier.next_step! 'gestionnaire', 'refuse'
flash.notice = 'Dossier considéré comme refusé.'
case params[:process_action]
when "refuse"
next_step = "refuse"
notice = "Dossier considéré comme refusé."
template = dossier.procedure.refused_mail_template
when "without_continuation"
next_step = "without_continuation"
notice = "Dossier considéré comme sans suite."
template = dossier.procedure.without_continuation_mail_template
when "close"
next_step = "close"
notice = "Dossier traité avec succès."
template = dossier.procedure.closed_mail_template
end
NotificationMailer.send_notification(dossier, dossier.procedure.refused_mail_template).deliver_now!
dossier.next_step! 'gestionnaire', next_step, motivation
redirect_to backoffice_dossier_path(id: dossier.id)
end
# needed to force Carrierwave to provide dossier.attestation.pdf.read
# when the Feature.remote_storage is true, otherwise pdf.read is a closed stream.
dossier.reload
def without_continuation
create_dossier_facade params[:dossier_id]
attestation_pdf = nil
if check_attestation_emailable(dossier)
attestation_pdf = dossier.attestation.pdf.read
end
dossier = @facade.dossier
flash.notice = notice
dossier.next_step! 'gestionnaire', 'without_continuation'
flash.notice = 'Dossier considéré comme sans suite.'
NotificationMailer.send_notification(dossier, dossier.procedure.without_continuation_mail_template).deliver_now!
redirect_to backoffice_dossier_path(id: dossier.id)
end
def close
create_dossier_facade params[:dossier_id]
dossier = @facade.dossier
dossier.next_step! 'gestionnaire', 'close'
flash.notice = 'Dossier traité avec succès.'
NotificationMailer.send_notification(dossier, dossier.procedure.closed_mail_template).deliver_now!
NotificationMailer.send_notification(dossier, template, attestation_pdf).deliver_now!
redirect_to backoffice_dossier_path(id: dossier.id)
end
@ -172,7 +177,6 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
redirect_to backoffice_dossiers_path
end
def unarchive
@dossier = Dossier.find(params[:id])
if @dossier.archived
@ -193,6 +197,16 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
private
def check_attestation_emailable(dossier)
if dossier&.attestation&.emailable? == false
human_size = number_to_human_size(dossier.attestation.pdf.size)
msg = "the attestation of the dossier #{dossier.id} cannot be mailed because it is too heavy: #{human_size}"
capture_message(msg, level: 'error')
end
dossier&.attestation&.emailable?
end
def store_current_location
if !gestionnaire_signed_in?
store_location_for(:gestionnaire, request.url)

View file

@ -31,7 +31,6 @@ class Backoffice::DossiersListController < ApplicationController
dossiers_list_facade liste
service = dossiers_list_facade.service
if param_page.nil?
params[:dossiers_smart_listing] = {page: dossiers_list_facade.service.default_page}
end
@ -48,10 +47,10 @@ class Backoffice::DossiersListController < ApplicationController
def default_smart_listing_create name, collection
smart_listing_create name,
collection,
partial: 'backoffice/dossiers/list',
array: true,
default_sort: dossiers_list_facade.service.default_sort
collection,
partial: 'backoffice/dossiers/list',
array: true,
default_sort: dossiers_list_facade.service.default_sort
end
def param_smart_listing

View file

@ -11,7 +11,7 @@ class Backoffice::PrivateFormulairesController < ApplicationController
if champs_service_errors.empty?
flash[:notice] = "Formulaire enregistré"
else
flash[:alert] = champs_service_errors.join('<br>').html_safe
flash[:alert] = champs_service_errors
end
end

View file

@ -15,14 +15,14 @@ class BackofficeController < ApplicationController
def invitations
pending_avis = current_gestionnaire.avis.without_answer.includes(dossier: [:procedure]).by_latest
@pending_avis = smart_listing_create :pending_avis,
pending_avis,
partial: 'backoffice/dossiers/list_invitations',
array: true
pending_avis,
partial: 'backoffice/dossiers/list_invitations',
array: true
avis_with_answer = current_gestionnaire.avis.with_answer.includes(dossier: [:procedure]).by_latest
@avis_with_answer = smart_listing_create :avis_with_answer,
avis_with_answer,
partial: 'backoffice/dossiers/list_invitations',
array: true
avis_with_answer,
partial: 'backoffice/dossiers/list_invitations',
array: true
end
end

View file

@ -1,5 +1,4 @@
class CguController < ApplicationController
def index
end
end

View file

@ -1,9 +1,9 @@
class CommentairesController < ApplicationController
def index
@facade = DossierFacades.new(
params[:dossier_id],
(current_gestionnaire || current_user).email,
params[:champs_id]
params[:dossier_id],
(current_gestionnaire || current_user).email,
params[:champs_id]
)
render layout: false
rescue ActiveRecord::RecordNotFound

View file

@ -6,8 +6,8 @@ class DemoController < ApplicationController
return redirect_to root_path if Rails.env.production?
smart_listing_create :procedures,
Procedure.where(archived: false, published: true).order("id DESC"),
partial: "demo/list",
array: true
Procedure.where(archived: false, published: true).order("id DESC"),
partial: "demo/list",
array: true
end
end

View file

@ -1,4 +1,6 @@
class Gestionnaires::SessionsController < Sessions::SessionsController
layout "new_application"
def demo
return redirect_to root_path if Rails.env.production?

View file

@ -0,0 +1,13 @@
module NewGestionnaire
class DossiersController < ProceduresController
def attestation
send_data(dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf')
end
private
def dossier
Dossier.find(params[:dossier_id])
end
end
end

View file

@ -0,0 +1,5 @@
module NewGestionnaire
class GestionnaireController < ApplicationController
before_action :authenticate_gestionnaire!
end
end

View file

@ -0,0 +1,18 @@
module NewGestionnaire
class ProceduresController < GestionnaireController
before_action :ensure_ownership!
private
def procedure
Procedure.find(params[:procedure_id])
end
def ensure_ownership!
if !procedure.gestionnaires.include?(current_gestionnaire)
flash[:alert] = "Vous n'avez pas accès à cette procédure"
redirect_to root_path
end
end
end
end

View file

@ -0,0 +1,22 @@
module NewUser
class DossiersController < UserController
before_action :ensure_ownership!
def attestation
send_data(dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf')
end
private
def dossier
Dossier.find(params[:dossier_id])
end
def ensure_ownership!
if dossier.user != current_user
flash[:alert] = "Vous n'avez pas accès à ce dossier"
redirect_to root_path
end
end
end
end

View file

@ -0,0 +1,5 @@
module NewUser
class UserController < ApplicationController
before_action :authenticate_user!
end
end

View file

@ -1,4 +1,6 @@
class RootController < ApplicationController
layout 'new_application'
def index
if administrateur_signed_in?
return redirect_to admin_procedures_path
@ -26,6 +28,9 @@ class RootController < ApplicationController
return redirect_to administrations_path
end
render 'landing', :layout => 'new_application'
render 'landing'
end
def patron
end
end

View file

@ -29,13 +29,20 @@ class StatsController < ApplicationController
@avis_usage = avis_usage
@avis_average_answer_time = avis_average_answer_time
@avis_answer_percentages = avis_answer_percentages
@motivation_usage_dossier = motivation_usage_dossier
@motivation_usage_procedure = motivation_usage_procedure
end
private
def last_four_months_hash(association, date_attribute = :created_at)
min_date = 3.months.ago.beginning_of_month.to_date
max_date = Time.now.to_date
if administration_signed_in?
max_date = Time.now.to_date
else
max_date = Time.now.beginning_of_month - 1.second
end
association
.where(date_attribute => min_date..max_date)
@ -61,7 +68,7 @@ class StatsController < ApplicationController
count_per_administrateur = procedures.group(:administrateur_id).count.values
{
'Une procédure' => count_per_administrateur.select { |count| count == 1 }.count,
'Entre deux et cinq procédures' => count_per_administrateur.select { |count| 2 <= count && count <= 5 }.count,
'Entre deux et cinq procédures' => count_per_administrateur.select { |count| 2 <= count && count <= 5 }.count,
'Plus de cinq procédures' => count_per_administrateur.select { |count| 5 < count }.count
}
end
@ -70,6 +77,10 @@ class StatsController < ApplicationController
(collection.sum.to_f / collection.size).round(2)
end
def percentage(numerator, denominator)
((numerator.to_f / denominator) * 100).round(2)
end
def dossier_instruction_mean_time(dossiers)
# In the 12 last months, we compute for each month
# the average time it took to instruct a dossier
@ -166,7 +177,7 @@ class StatsController < ApplicationController
result = 0
else
weekly_dossier_with_avis_count = weekly_dossiers.select { |dossier| dossier.avis.present? }.count
result = ((weekly_dossier_with_avis_count.to_f / weekly_dossiers_count) * 100).round(2)
result = percentage(weekly_dossier_with_avis_count, weekly_dossiers_count)
end
[min_date.to_i, result]
@ -199,10 +210,58 @@ class StatsController < ApplicationController
[min_date.to_i, 0]
else
answered_weekly_avis_count = weekly_avis.with_answer.count
result = ((answered_weekly_avis_count.to_f / weekly_avis_count) * 100).round(2)
result = percentage(answered_weekly_avis_count, weekly_avis_count)
[min_date.to_i, result]
end
end
end
def motivation_usage_dossier
[3.week.ago, 2.week.ago, 1.week.ago].map do |date|
min_date = date.beginning_of_week
max_date = date.end_of_week
weekly_termine_dossiers = Dossier.where(processed_at: min_date..max_date)
weekly_termine_dossiers_count = weekly_termine_dossiers.count
weekly_termine_dossiers_with_motivation_count = weekly_termine_dossiers.where.not(motivation: nil).count
if weekly_termine_dossiers_count == 0
result = 0
else
result = percentage(weekly_termine_dossiers_with_motivation_count, weekly_termine_dossiers_count)
end
[l(max_date, format: '%d/%m/%Y'), result]
end
end
def motivation_usage_procedure
[3.week.ago, 2.week.ago, 1.week.ago].map do |date|
min_date = date.beginning_of_week
max_date = date.end_of_week
procedures_with_dossier_processed_this_week = Procedure
.joins(:dossiers)
.where(dossiers: { processed_at: min_date..max_date })
procedures_with_dossier_processed_this_week_count = procedures_with_dossier_processed_this_week
.uniq
.count
procedures_with_dossier_processed_this_week_and_with_motivation_count = procedures_with_dossier_processed_this_week
.where
.not(dossiers: { motivation: nil })
.uniq
.count
if procedures_with_dossier_processed_this_week_count == 0
result = 0
else
result = percentage(procedures_with_dossier_processed_this_week_and_with_motivation_count, procedures_with_dossier_processed_this_week_count)
end
[l(max_date, format: '%d/%m/%Y'), result]
end
end
end

View file

@ -1,6 +1,6 @@
class Users::Dossiers::AddSiretController < ApplicationController
def show
@facade = DossierFacades.new params[:dossier_id], current_user.email
@facade = DossierFacades.new params[:dossier_id], current_user.email
raise ActiveRecord::RecordNotFound unless @facade.procedure.individual_with_siret?

View file

@ -30,9 +30,9 @@ class Users::DossiersController < UsersController
end
@dossiers = smart_listing_create :dossiers,
@dossiers_filtered,
partial: "users/dossiers/list",
array: true
@dossiers_filtered,
partial: "users/dossiers/list",
array: true
end
def commencer

View file

@ -1,6 +1,8 @@
class Users::RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
layout "new_application"
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
def after_sign_up_path_for(resource_or_scope)
WelcomeMailer.welcome_email(resource_or_scope).deliver_now!

View file

@ -1,5 +1,7 @@
class Users::SessionsController < Sessions::SessionsController
# before_action :configure_sign_in_params, only: [:create]
layout "new_application"
# before_action :configure_sign_in_params, only: [:create]
def demo
return redirect_to root_path if Rails.env.production?
@ -8,7 +10,7 @@ class Users::SessionsController < Sessions::SessionsController
render 'new'
end
# GET /resource/sign_in
# GET /resource/sign_in
def new
unless user_return_to_procedure_id.nil? # WTF ?
@dossier = Dossier.new(procedure: Procedure.active(user_return_to_procedure_id))
@ -19,7 +21,7 @@ class Users::SessionsController < Sessions::SessionsController
error_procedure
end
#POST /resource/sign_in
#POST /resource/sign_in
def create
remember_me = params[:user][:remember_me] == '1'
try_to_authenticate(User, remember_me)
@ -44,7 +46,7 @@ class Users::SessionsController < Sessions::SessionsController
end
end
# DELETE /resource/sign_out
# DELETE /resource/sign_out
def destroy
sign_out :gestionnaire if gestionnaire_signed_in?
sign_out :administrateur if administrateur_signed_in?

View file

@ -16,9 +16,9 @@ class UsersController < ApplicationController
end
def authorized_routes? controller
redirect_to_root_path 'Le status de votre dossier n\'autorise pas cette URL' unless UserRoutesAuthorizationService.authorized_route?(
controller,
current_user_dossier)
redirect_to_root_path 'Le statut de votre dossier n\'autorise pas cette URL' unless UserRoutesAuthorizationService.authorized_route?(
controller,
current_user_dossier)
rescue ActiveRecord::RecordNotFound
redirect_to_root_path 'Vous navez pas accès à ce dossier.'
end

View file

@ -1,4 +1,3 @@
class DossiersDecorator < Draper::CollectionDecorator
delegate :current_page, :per_page, :offset, :total_entries, :total_pages
end

View file

@ -1,4 +1,3 @@
class EtablissementDecorator < Draper::Decorator
delegate_all
end

View file

@ -1,5 +1,4 @@
class ProcedureDecorator < Draper::Decorator
delegate_all
def lien

View file

@ -1,4 +1,3 @@
class ProceduresDecorator < Draper::CollectionDecorator
delegate :current_page, :per_page, :offset, :total_entries, :total_pages
end

View file

@ -1,5 +1,4 @@
class TypeDeChampDecorator < Draper::Decorator
delegate_all
def button_up params

View file

@ -1,3 +1,2 @@
class TypeDeChampPrivateDecorator < TypeDeChampDecorator
end

View file

@ -1,5 +1,4 @@
class DossierFacades
#TODO rechercher en fonction de la personne/email
def initialize(dossier_id, email, champ_id = nil)
@dossier = Dossier.find(dossier_id)

View file

@ -80,5 +80,4 @@ class DossiersListFacades
def base_url liste
@procedure.nil? ? backoffice_dossiers_path(liste: liste) : backoffice_dossiers_procedure_path(id: @procedure.id, liste: liste)
end
end

View file

@ -1,5 +1,4 @@
class InviteDossierFacades < DossierFacades
#TODO rechercher en fonction de la personne/email
def initialize id, email
@dossier = Invite.where(email: email, id: id).first!.dossier

View file

@ -0,0 +1,14 @@
module ApplicationHelper
def flash_class(level)
case level
when "notice" then "alert-success"
when "alert" then "alert-danger"
end
end
def current_email
current_user.try(:email) ||
current_gestionnaire.try(:email) ||
current_administrateur.try(:email)
end
end

View file

@ -0,0 +1,8 @@
module DeviseHelper
def devise_error_messages!
if resource.errors.full_messages.any?
flash.now[:alert] = resource.errors.full_messages
end
''
end
end

View file

@ -1,7 +1,6 @@
module Carto
module GeoAPI
class Driver
def self.regions
call regions_url
end
@ -29,7 +28,6 @@ module Carto
rescue RestClient::ServiceUnavailable
nil
end
end
end
end

View file

@ -18,8 +18,8 @@ class CARTO::SGMAP::API
verify_ssl_mode = OpenSSL::SSL::VERIFY_NONE
RestClient::Resource.new(
url,
verify_ssl: verify_ssl_mode,
url,
verify_ssl: verify_ssl_mode,
).post params[:geojson], content_type: 'application/json'
rescue RestClient::InternalServerError

View file

@ -12,7 +12,6 @@ class SIADE::RNAAdapter
data_source[:association].each do |k, v|
params[k] = v if attr_to_fetch.include?(k)
end
params[:association_id] = params[:id]

View file

@ -1,9 +1,7 @@
class AvisMailer < ApplicationMailer
def avis_invitation(avis)
@avis = avis
email = @avis.gestionnaire.try(:email) || @avis.email
mail(to: email, subject: "Donnez votre avis sur le dossier nº #{@avis.dossier.id} (#{@avis.dossier.procedure.libelle})")
end
end

View file

@ -1,16 +1,13 @@
class GestionnaireMailer < ApplicationMailer
layout 'mailers/layout'
def new_gestionnaire email, password
send_mail email, password, "Vous avez été nommé accompagnateur sur la plateforme TPS"
end
def new_assignement email, email_admin
send_mail email, email_admin, "Vous avez été assigné à un nouvel administrateur sur la plateforme TPS"
end
def last_week_overview(gestionnaire, overview)
headers['X-mailjet-campaign'] = 'last_week_overview'
send_mail gestionnaire.email, overview, 'Résumé de la semaine'
send_mail gestionnaire.email, overview, 'Vos activités sur TPS'
end
private

View file

@ -1,5 +1,4 @@
class InviteMailer < ApplicationMailer
def invite_user invite
vars_mailer invite

View file

@ -1,8 +1,6 @@
class NewAdminMailer < ApplicationMailer
def new_admin_email admin, password
def new_admin_email admin
@admin = admin
@password = password
mail(to: 'tech@tps.apientreprise.fr',
subject: "Création d'un compte Admin TPS")

View file

@ -3,12 +3,16 @@ class NotificationMailer < ApplicationMailer
after_action :create_commentaire_for_notification, only: :send_notification
def send_notification(dossier, mail_template)
def send_notification(dossier, mail_template, attestation = nil)
vars_mailer(dossier)
@object = mail_template.object_for_dossier dossier
@body = mail_template.body_for_dossier dossier
if attestation.present?
attachments['attestation.pdf'] = attestation
end
mail(subject: @object) { |format| format.html { @body } }
end

View file

@ -1,6 +1,5 @@
class WelcomeMailer < ApplicationMailer
def welcome_email user
@user = user
mail(to: user.email,

View file

@ -1,6 +1,6 @@
class Administrateur < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :gestionnaires
has_many :procedures

11
app/models/attestation.rb Normal file
View file

@ -0,0 +1,11 @@
class Attestation < ApplicationRecord
belongs_to :dossier
mount_uploader :pdf, AttestationUploader
MAX_SIZE_EMAILABLE = 2.megabytes
def emailable?
pdf.size <= MAX_SIZE_EMAILABLE
end
end

View file

@ -0,0 +1,145 @@
class AttestationTemplate < ApplicationRecord
include ActionView::Helpers::NumberHelper
belongs_to :procedure
mount_uploader :logo, AttestationTemplateImageUploader
mount_uploader :signature, AttestationTemplateImageUploader
validate :logo_signature_file_size
validates :footer, length: { maximum: 190 }
FILE_MAX_SIZE_IN_MB = 0.5
def tags
if procedure.for_individual?
identity_tags = individual_tags
else
identity_tags = entreprise_tags + etablissement_tags
end
identity_tags + dossier_tags + procedure_type_de_champ_public_private_tags
end
def attestation_for(dossier)
Attestation.new(title: replace_tags(title, dossier), pdf: build_pdf(dossier))
end
def dup
result = AttestationTemplate.new(title: title, body: body, footer: footer, activated: activated)
if logo.present?
CopyCarrierwaveFile::CopyFileService.new(self, result, :logo).set_file
end
if signature.present?
CopyCarrierwaveFile::CopyFileService.new(self, result, :signature).set_file
end
result
end
private
def logo_signature_file_size
%i[logo signature]
.select { |file_name| send(file_name).present? }
.each { |file_name| file_size_check(file_name) }
end
def file_size_check(file_name)
if send(file_name).file.size.to_f > FILE_MAX_SIZE_IN_MB.megabyte.to_f
errors.add(file_name, " : vous ne pouvez pas charger une image de plus de #{number_with_delimiter(FILE_MAX_SIZE_IN_MB, locale: :fr)} Mo")
end
end
def procedure_type_de_champ_public_private_tags
(procedure.types_de_champ + procedure.types_de_champ_private)
.map { |tdc| { libelle: tdc.libelle, description: tdc.description } }
end
def dossier_tags
[{ libelle: 'motivation', description: '', target: 'motivation' },
{ libelle: 'numéro du dossier', description: '', target: 'id' }]
end
def individual_tags
[{ libelle: 'civilité', description: 'M., Mme', target: 'gender' },
{ libelle: 'nom', description: "nom de l'usager", target: 'nom' },
{ libelle: 'prénom', description: "prénom de l'usager", target: 'prenom' }]
end
def entreprise_tags
[{ libelle: 'SIREN', description: '', target: 'siren' },
{ libelle: 'numéro de TVA intracommunautaire', description: '', target: 'numero_tva_intracommunautaire' },
{ libelle: 'SIRET du siège social', description: '', target: 'siret_siege_social' },
{ libelle: 'raison sociale', description: '', target: 'raison_sociale' }]
end
def etablissement_tags
[{ libelle: 'adresse', description: '', target: 'inline_adresse' }]
end
def build_pdf(dossier)
action_view = ActionView::Base.new(ActionController::Base.view_paths,
logo: logo,
title: replace_tags(title, dossier),
body: replace_tags(body, dossier),
signature: signature,
footer: footer,
created_at: Time.now)
attestation_view = action_view.render(file: 'admin/attestation_templates/show',
formats: [:pdf])
view_to_memory_file(attestation_view)
end
def view_to_memory_file(view)
pdf = StringIO.new(view)
def pdf.original_filename
'attestation'
end
pdf
end
def replace_tags(text, dossier)
if text.nil?
return ''
end
text = replace_type_de_champ_tags(text, procedure.types_de_champ, dossier.champs)
text = replace_type_de_champ_tags(text, procedure.types_de_champ_private, dossier.champs_private)
tags_and_datas = [
[dossier_tags, dossier],
[individual_tags, dossier.individual],
[entreprise_tags, dossier.entreprise],
[etablissement_tags, dossier.entreprise&.etablissement]]
tags_and_datas.inject(text) { |acc, (tags, data)| replace_tags_with_values_from_data(acc, tags, data) }
end
def replace_type_de_champ_tags(text, types_de_champ, dossier_champs)
types_de_champ.inject(text) do |acc, tag|
value = dossier_champs
.select { |champ| champ.libelle == tag[:libelle] }
.first
.value
acc.gsub("--#{tag[:libelle]}--", value.to_s)
end
end
def replace_tags_with_values_from_data(text, tags, data)
if data.present?
tags.inject(text) do |acc, tag|
acc.gsub("--#{tag[:libelle]}--", data.send(tag[:target].to_sym).to_s)
end
else
text
end
end
end

View file

@ -1,3 +1,2 @@
class ChampPrivate < Champ
end

View file

@ -1,3 +1,2 @@
class ChampPublic < Champ
end

View file

@ -15,5 +15,4 @@ module CredentialsSyncableConcern
def force_sync_credentials
SyncCredentialsService.new(self.class, email_was, email, encrypted_password).change_credentials!
end
end

View file

@ -21,6 +21,10 @@ module MailTemplateConcern
name: "date_de_decision",
description: "Permet d'afficher la date à laquelle la décision finale (acceptation, refus, classement sans suite) sur le dossier a été prise."
}
TAGS << TAG_MOTIVATION = {
name: "motivation",
description: "Permet d'afficher la motivation associée à la décision finale (acceptation, refus, classement sans suite) sur le dossier. Attention, elle est facultative."
}
def object_for_dossier(dossier)
replace_tags(object, dossier)
@ -55,6 +59,8 @@ module MailTemplateConcern
dossier.procedure.libelle
when TAG_DATE_DE_DECISION
dossier.processed_at.present? ? dossier.processed_at.localtime.strftime("%d/%m/%Y") : ""
when TAG_MOTIVATION
dossier.motivation || ""
else
'--BALISE_NON_RECONNUE--'
end

View file

@ -23,6 +23,7 @@ class Dossier < ActiveRecord::Base
has_one :etablissement, dependent: :destroy
has_one :entreprise, dependent: :destroy
has_one :individual, dependent: :destroy
has_one :attestation
has_many :cerfa, dependent: :destroy
has_many :pieces_justificatives, dependent: :destroy
@ -41,6 +42,7 @@ class Dossier < ActiveRecord::Base
belongs_to :procedure
belongs_to :user
default_scope { where(hidden_at: nil) }
scope :state_brouillon, -> { where(state: BROUILLON) }
scope :state_not_brouillon, -> { where.not(state: BROUILLON) }
scope :state_nouveaux, -> { where(state: NOUVEAUX) }
@ -130,7 +132,7 @@ class Dossier < ActiveRecord::Base
commentaires.order(created_at: :desc)
end
def next_step! role, action
def next_step! role, action, motivation = nil
unless %w(initiate follow update comment receive refuse without_continuation close).include?(action)
fail 'action is not valid'
end
@ -169,15 +171,33 @@ class Dossier < ActiveRecord::Base
end
when 'close'
if received?
self.attestation = build_attestation
save
closed!
if motivation
self.motivation = motivation
save
end
end
when 'refuse'
if received?
refused!
if motivation
self.motivation = motivation
save
end
end
when 'without_continuation'
if received?
without_continuation!
if motivation
self.motivation = motivation
save
end
end
end
end
@ -223,6 +243,7 @@ class Dossier < ActiveRecord::Base
serialized_dossier = DossierTableExportSerializer.new(self)
data = serialized_dossier.attributes.values
data += self.champs.order('type_de_champ_id ASC').map(&:value)
data += self.champs_private.order('type_de_champ_id ASC').map(&:value)
data += self.export_entreprise_data.values
return data
end
@ -231,6 +252,7 @@ class Dossier < ActiveRecord::Base
serialized_dossier = DossierTableExportSerializer.new(self)
headers = serialized_dossier.attributes.keys
headers += self.procedure.types_de_champ.order('id ASC').map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
headers += self.procedure.types_de_champ_private.order('id ASC').map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym }
headers += self.export_entreprise_data.keys
return headers
end
@ -290,6 +312,12 @@ class Dossier < ActiveRecord::Base
private
def build_attestation
if procedure.attestation_template.present? && procedure.attestation_template.activated?
procedure.attestation_template.attestation_for(self)
end
end
def update_state_dates
if initiated? && !self.initiated_at
self.initiated_at = DateTime.now

View file

@ -9,4 +9,9 @@ class Etablissement < ActiveRecord::Base
def geo_adresse
[numero_voie, type_voie, nom_voie, complement_adresse, code_postal, localite].join(' ')
end
def inline_adresse
#squeeze needed because of space in excess in the data
"#{numero_voie} #{type_voie} #{nom_voie}, #{complement_adresse}, #{code_postal} #{localite}".squeeze(' ')
end
end

View file

@ -1,6 +1,6 @@
class Gestionnaire < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :administrateurs
@ -20,9 +20,13 @@ class Gestionnaire < ActiveRecord::Base
include CredentialsSyncableConcern
def procedure_filter
return nil unless assign_to.pluck(:procedure_id).include?(self[:procedure_filter])
self[:procedure_filter]
procedure_id = self[:procedure_filter]
if procedures.find_by(id: procedure_id).present?
procedure_id
else
self.update_column(:procedure_filter, nil)
nil
end
end
def can_view_dossier?(dossier_id)
@ -52,10 +56,8 @@ class Gestionnaire < ActiveRecord::Base
end
def build_default_preferences_list_dossier procedure_id=nil
PreferenceListDossier.available_columns_for(procedure_id).each do |table|
table.second.each do |column|
if valid_couple_table_attr? table.first, column.first
PreferenceListDossier.create(
libelle: column.second[:libelle],
@ -108,16 +110,15 @@ class Gestionnaire < ActiveRecord::Base
active_procedure_overviews = procedures
.where(published: true)
.all
.map { |procedure| procedure.procedure_overview(start_date, dossiers_with_notifications_count_for_procedure(procedure)) }
.map { |procedure| procedure.procedure_overview(start_date) }
.select(&:had_some_activities?)
if active_procedure_overviews.count == 0 && notifications.count == 0
if active_procedure_overviews.count == 0
nil
else
{
start_date: start_date,
procedure_overviews: active_procedure_overviews,
notifications: notifications
}
end
end

View file

@ -1,5 +1,4 @@
class Invite < ActiveRecord::Base
belongs_to :dossier
belongs_to :user

View file

@ -1,3 +1,2 @@
class InviteGestionnaire < Invite
end

View file

@ -1,3 +1,2 @@
class InviteUser < Invite
end

View file

@ -6,6 +6,6 @@ module Mails
TEMPLATE_NAME = "mails/closed_mail"
DISPLAYED_NAME = "Accusé d'acceptation"
DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été accepté'
ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION]
ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION, TAG_MOTIVATION]
end
end

View file

@ -5,7 +5,7 @@ module Mails
SLUG = "initiated_mail"
TEMPLATE_NAME = "mails/initiated_mail"
DISPLAYED_NAME = 'Accusé de réception'
DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été bien reçu'
DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a bien été reçu'
ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE]
end
end

Some files were not shown because too many files have changed in this diff Show more