diff --git a/app/assets/fonts/Muli-Italic.woff b/app/assets/fonts/Muli-Italic.woff new file mode 100644 index 000000000..9358d8ea3 Binary files /dev/null and b/app/assets/fonts/Muli-Italic.woff differ diff --git a/app/assets/images/user/envoi-dossier.svg b/app/assets/images/user/envoi-dossier.svg new file mode 100644 index 000000000..27b5097bd --- /dev/null +++ b/app/assets/images/user/envoi-dossier.svg @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/new_design/direct_uploads.js b/app/assets/javascripts/new_design/direct_uploads.js index 1ac788eec..47b5aa23c 100644 --- a/app/assets/javascripts/new_design/direct_uploads.js +++ b/app/assets/javascripts/new_design/direct_uploads.js @@ -42,4 +42,16 @@ addEventListener("direct-upload:end", function (event) { element = document.getElementById("direct-upload-" + id); element.classList.add("direct-upload--complete"); -}); \ No newline at end of file +}); + +addEventListener('load', function() { + var submitButtons = document.querySelectorAll('form button[type=submit][data-action]'); + var hiddenInput = document.querySelector('form input[type=hidden][name=submit_action]'); + submitButtons = [].slice.call(submitButtons); + + submitButtons.forEach(function(button) { + button.addEventListener('click', function() { + hiddenInput.value = button.getAttribute('data-action'); + }); + }); +}); diff --git a/app/assets/stylesheets/new_design/_constants.scss b/app/assets/stylesheets/new_design/_constants.scss index f652937cc..0c9aaf6c7 100644 --- a/app/assets/stylesheets/new_design/_constants.scss +++ b/app/assets/stylesheets/new_design/_constants.scss @@ -3,11 +3,6 @@ $page-width: 1040px; $default-spacer: 8px; $default-padding: 2 * $default-spacer; -$footer-height: 267px; -$footer-height-mobile: 531px; - -$small-footer-height: 2 * $default-padding; - // layouts $two-columns-padding: 60px; $two-columns-breakpoint: $page-width + (2 * $two-columns-padding); diff --git a/app/assets/stylesheets/new_design/accessibilite.scss b/app/assets/stylesheets/new_design/accessibilite.scss index 3b844f587..eb2953da6 100644 --- a/app/assets/stylesheets/new_design/accessibilite.scss +++ b/app/assets/stylesheets/new_design/accessibilite.scss @@ -1,11 +1,18 @@ $default-space: 15px; +$new-p-margin-bottom: 3 * $default-space; .accessibilite { width: 1040px; margin: 0 auto; padding-top: $default-space * 2; + padding-bottom: $default-space * 2; ul { list-style-type: disc; + margin-top: -($default-space * 2); } } + +.new-p { + margin-bottom: $new-p-margin-bottom; +} diff --git a/app/assets/stylesheets/new_design/common.scss b/app/assets/stylesheets/new_design/common.scss index 5901a5db6..e98f7270c 100644 --- a/app/assets/stylesheets/new_design/common.scss +++ b/app/assets/stylesheets/new_design/common.scss @@ -15,16 +15,7 @@ select { .page-wrapper { position: relative; - padding-bottom: $small-footer-height; min-height: 100%; - - &.with-footer { - padding-bottom: $footer-height; - - @media (max-width: 1000px) { - padding-bottom: $footer-height-mobile; - } - } } h1 { diff --git a/app/assets/stylesheets/new_design/dossier-edit.scss b/app/assets/stylesheets/new_design/dossier-edit.scss new file mode 100644 index 000000000..e906c529b --- /dev/null +++ b/app/assets/stylesheets/new_design/dossier-edit.scss @@ -0,0 +1,20 @@ +@import "colors"; +@import "constants"; + +.dossier-edit { + .dossier-header { + background-color: $light-grey; + + .container { + padding: $default-padding; + } + + h1 { + font-size: 22px; + } + } + + .thanks { + padding: (1.5 * $default-padding) 0; + } +} diff --git a/app/assets/stylesheets/new_design/fonts.scss b/app/assets/stylesheets/new_design/fonts.scss index edc222672..2e093a609 100644 --- a/app/assets/stylesheets/new_design/fonts.scss +++ b/app/assets/stylesheets/new_design/fonts.scss @@ -11,3 +11,10 @@ font-weight: bold; font-style: normal; } + +@font-face { + font-family: "Muli"; + src: asset-url("Muli-Italic.woff") format("woff"); + font-weight: normal; + font-style: italic; +} diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss index 13486ec9f..17ef315f6 100644 --- a/app/assets/stylesheets/new_design/forms.scss +++ b/app/assets/stylesheets/new_design/forms.scss @@ -15,6 +15,7 @@ label { margin-bottom: $default-padding; display: block; + font-weight: bold; .mandatory { color: $dark-red; @@ -115,7 +116,7 @@ input.touched:invalid, textarea.touched:invalid { - border-color: $dark-red; + border: 1px solid $dark-red; box-shadow: 0px 0px 5px $dark-red; } @@ -239,4 +240,15 @@ margin-right: 0; } } + + .pj-input { + input[type=file] { + margin: $default-padding 0 (2 * $default-padding); + padding: 2px; + } + + .piece-description { + margin-bottom: $default-padding; + } + } } diff --git a/app/assets/stylesheets/new_design/merci.scss b/app/assets/stylesheets/new_design/merci.scss new file mode 100644 index 000000000..f3ccd7bcd --- /dev/null +++ b/app/assets/stylesheets/new_design/merci.scss @@ -0,0 +1,32 @@ +@import "colors"; +@import "common"; +@import "constants"; + +.merci { + text-align: center; + + img { + margin-top: 4 * $default-padding; + } + + h1 { + margin: (2 * $default-padding) 0; + } + + b { + font-weight: bold; + } + + .send { + margin-bottom: 2 * $default-padding; + font-size: 20px; + } + + p { + margin: $default-padding; + } + + a { + margin-top: 40px; + } +} diff --git a/app/assets/stylesheets/new_design/new_footer.scss b/app/assets/stylesheets/new_design/new_footer.scss index 5f34bc894..91aa8fe19 100644 --- a/app/assets/stylesheets/new_design/new_footer.scss +++ b/app/assets/stylesheets/new_design/new_footer.scss @@ -7,14 +7,8 @@ footer { @include vertical-padding(72px); background-color: $light-grey; border-top: 1px solid $border-grey; - position: absolute; bottom: 0; width: 100%; - height: $footer-height; - - @media (max-width: 1000px) { - height: $footer-height-mobile; - } } .footer-columns { @@ -50,26 +44,32 @@ footer { } } -.footer-logo:hover { - opacity: 0.8; +.footer-text { + font-style: italic; + margin-bottom: 14px; } -.footer-logo-beta-gouv-fr { - @include ie-compatible-background-image("footer/logo-beta-gouv-fr.svg"); - - width: 190px; - height: 32px; - margin-bottom: 14px; +.footer-logo:hover { + opacity: 0.8; } .footer-logo-dinsic { @include ie-compatible-background-image("footer/logo-dinsic.svg"); - height: 85px; - width: 74px; + height: 104px; + width: 90px; + margin-bottom: 14px; } -.footer-link a { +.footer-logo-beta-gouv-fr { + @include ie-compatible-background-image("footer/logo-beta-gouv-fr.svg"); + + width: 150px; + height: 25px; +} + +.footer-link a, +.footer-text a { color: $black; text-decoration: none; diff --git a/app/controllers/new_user/dossiers_controller.rb b/app/controllers/new_user/dossiers_controller.rb index 02f02b5f2..c478d14b1 100644 --- a/app/controllers/new_user/dossiers_controller.rb +++ b/app/controllers/new_user/dossiers_controller.rb @@ -23,7 +23,7 @@ module NewUser if @dossier.procedure.module_api_carto.use_api_carto redirect_to users_dossier_carte_path(@dossier.id) else - redirect_to users_dossier_description_path(@dossier) # Simon should replace this with dossier_path when done + redirect_to modifier_dossier_path(@dossier) end else flash.now.alert = @dossier.errors.full_messages @@ -31,12 +31,71 @@ module NewUser end end + def modifier + @dossier = dossier_with_champs + + # TODO: remove when the champs are unifed + if !@dossier.autorisation_donnees + if dossier.procedure.for_individual + redirect_to identite_dossier_path(@dossier) + else + redirect_to users_dossier_path(@dossier) + end + end + end + + # FIXME: remove PiecesJustificativesService + # delegate draft save logic to champ ? + def update + @dossier = dossier_with_champs + + errors = PiecesJustificativesService.upload!(@dossier, current_user, params) + + if champs_params[:dossier] && !@dossier.update(champs_params[:dossier]) + errors += @dossier.errors.full_messages + end + + if !draft? + errors += @dossier.champs.select(&:mandatory_and_blank?) + .map { |c| "Le champ #{c.libelle.truncate(200)} doit être rempli." } + errors += PiecesJustificativesService.missing_pj_error_messages(@dossier) + end + + if errors.present? + flash.now.alert = errors + render :modifier + elsif draft? + flash.now.notice = 'Votre brouillon a bien été sauvegardé.' + render :modifier + else + if @dossier.brouillon? + redirect_to merci_dossier_path(@dossier) + else + redirect_to users_dossier_recapitulatif_path(@dossier) + end + @dossier.en_construction! + end + end + + def merci + @dossier = current_user.dossiers.includes(:procedure).find(params[:id]) + end + private + # FIXME: require(:dossier) when all the champs are united + def champs_params + params.permit(dossier: { champs_attributes: [:id, :value, :piece_justificative_file, value: []] }) + end + def dossier Dossier.find(params[:id] || params[:dossier_id]) end + def dossier_with_champs + @dossier_with_champs ||= current_user.dossiers.includes(champs: :type_de_champ).find(params[:id]) + end + def ensure_ownership! if dossier.user != current_user flash[:alert] = "Vous n'avez pas accès à ce dossier" @@ -51,5 +110,9 @@ module NewUser def dossier_params params.require(:dossier).permit(:autorisation_donnees) end + + def draft? + params[:submit_action] == 'draft' + end end end diff --git a/app/controllers/users/carte_controller.rb b/app/controllers/users/carte_controller.rb index 08f04f9cd..8ef350893 100644 --- a/app/controllers/users/carte_controller.rb +++ b/app/controllers/users/carte_controller.rb @@ -24,10 +24,7 @@ class Users::CarteController < UsersController dossier.update_attributes(json_latlngs: params[:json_latlngs]) - controller = :recapitulatif - controller = :description if dossier.brouillon? - - redirect_to url_for(controller: controller, action: :show, dossier_id: params[:dossier_id]) + redirect_to modifier_dossier_path(dossier) end def get_position diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index a5042fc7a..719682931 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -103,11 +103,19 @@ class Users::DossiersController < UsersController def siret_informations @facade = facade params[:dossier_id] - update_current_user_siret! siret + update_current_user_siret!(siret) - dossier = DossierService.new(@facade.dossier, siret, current_user.france_connect_information).dossier_informations! + etablissement_attributes = SIRETService.fetch(siret, @facade.dossier) - if dossier.entreprise.nil? || dossier.etablissement.nil? + if etablissement_attributes.present? + etablissement_attributes = ActionController::Parameters.new(etablissement_attributes).permit! + etablissement = @facade.dossier.create_etablissement(etablissement_attributes) + if etablissement.save + @facade.dossier.mandataire_social!(current_user.france_connect_information) + else + return errors_valid_siret + end + else return errors_valid_siret end @@ -154,7 +162,7 @@ class Users::DossiersController < UsersController if @facade.dossier.procedure.module_api_carto.use_api_carto redirect_to url_for(controller: :carte, action: :show, dossier_id: @facade.dossier.id) else - redirect_to url_for(controller: :description, action: :show, dossier_id: @facade.dossier.id) + redirect_to modifier_dossier_path(@facade.dossier) end end end diff --git a/app/decorators/dossier_decorator.rb b/app/decorators/dossier_decorator.rb index c8d71c0b9..532be3f41 100644 --- a/app/decorators/dossier_decorator.rb +++ b/app/decorators/dossier_decorator.rb @@ -16,14 +16,6 @@ class DossierDecorator < Draper::Decorator DossierDecorator.case_state_fr state end - def url(gestionnaire_signed_in) - if brouillon? - users_dossier_description_path(id) - else - users_dossier_recapitulatif_path(id) - end - end - def self.case_state_fr state = self.state h.t("activerecord.attributes.dossier.state.#{state}") end diff --git a/app/lib/siade/entreprise_adapter.rb b/app/lib/siade/entreprise_adapter.rb index 97723db98..b86833225 100644 --- a/app/lib/siade/entreprise_adapter.rb +++ b/app/lib/siade/entreprise_adapter.rb @@ -9,19 +9,22 @@ class SIADE::EntrepriseAdapter @data_source = nil end + def success? + data_source + rescue + false + end + def to_params - params = {} - - data_source[:entreprise].each do |k, v| - params[k] = v if attr_to_fetch.include?(k) - end + params = data_source[:entreprise].slice(*attr_to_fetch) params[:date_creation] = Time.at(params[:date_creation]).to_datetime - params rescue nil end + private + def attr_to_fetch [ :siren, @@ -29,6 +32,7 @@ class SIADE::EntrepriseAdapter :numero_tva_intracommunautaire, :forme_juridique, :forme_juridique_code, + :mandataires_sociaux, :nom_commercial, :raison_sociale, :siret_siege_social, @@ -38,10 +42,4 @@ class SIADE::EntrepriseAdapter :prenom ] end - - def mandataires_sociaux - data_source[:entreprise][:mandataires_sociaux] - rescue - nil - end end diff --git a/app/lib/siade/etablissement_adapter.rb b/app/lib/siade/etablissement_adapter.rb index 6b52d6bda..80adf6650 100644 --- a/app/lib/siade/etablissement_adapter.rb +++ b/app/lib/siade/etablissement_adapter.rb @@ -7,23 +7,27 @@ class SIADE::EtablissementAdapter @data_source ||= JSON.parse(SIADE::API.etablissement(@siret), symbolize_names: true) end - def to_params - params = {} + def success? + data_source + rescue + false + end - data_source[:etablissement].each do |k, v| - params[k] = v if attr_to_fetch.include?(k) - end - params[:adresse] = adresse - data_source[:etablissement][:adresse].each do |k, v| - params[k] = v if address_attribut_to_fetch.include?(k) - end + def to_params + params = data_source[:etablissement].slice(*attr_to_fetch) + adresse_line = params[:adresse].slice(*address_lines_to_fetch).values.compact.join("\r\n") + params.merge!(params[:adresse].slice(*address_attr_to_fetch)) + params[:adresse] = adresse_line params rescue nil end + private + def attr_to_fetch [ + :adresse, :siret, :siege_social, :naf, @@ -31,13 +35,7 @@ class SIADE::EtablissementAdapter ] end - def adresse - [:l1, :l2, :l3, :l4, :l5, :l6, :l7].map do |line| - data_source[:etablissement][:adresse][line] - end.compact.join("\r\n") - end - - def address_attribut_to_fetch + def address_attr_to_fetch [ :numero_voie, :type_voie, @@ -48,4 +46,8 @@ class SIADE::EtablissementAdapter :code_insee_localite ] end + + def address_lines_to_fetch + [:l1, :l2, :l3, :l4, :l5, :l6, :l7] + end end diff --git a/app/lib/siade/exercices_adapter.rb b/app/lib/siade/exercices_adapter.rb index 59e2af174..db8193829 100644 --- a/app/lib/siade/exercices_adapter.rb +++ b/app/lib/siade/exercices_adapter.rb @@ -11,13 +11,15 @@ class SIADE::ExercicesAdapter def to_params data_source[:exercices].map do |exercice| - { - ca: exercice[:ca], - dateFinExercice: exercice[:date_fin_exercice], - date_fin_exercice_timestamp: exercice[:date_fin_exercice_timestamp] - } + exercice.slice(*attr_to_fetch) end rescue - nil + [] + end + + private + + def attr_to_fetch + [:ca, :date_fin_exercice, :date_fin_exercice_timestamp] end end diff --git a/app/lib/siade/rna_adapter.rb b/app/lib/siade/rna_adapter.rb index fd4686eee..cfae4ec9f 100644 --- a/app/lib/siade/rna_adapter.rb +++ b/app/lib/siade/rna_adapter.rb @@ -8,23 +8,20 @@ class SIADE::RNAAdapter end def to_params - params = {} - - data_source[:association].each do |k, v| - params[k] = v if attr_to_fetch.include?(k) + if data_source[:association][:id].nil? + return nil end - - params[:association_id] = params[:id] - params.delete(:id) - + params = data_source[:association].slice(*attr_to_fetch) + params[:rna] = data_source[:association][:id] params rescue nil end + private + def attr_to_fetch [ - :id, :titre, :objet, :date_creation, diff --git a/app/models/dossier.rb b/app/models/dossier.rb index e4f2be5ab..cf266f3d8 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -108,6 +108,10 @@ class Dossier < ActiveRecord::Base champs.joins(', types_de_champ').where("champs.type_de_champ_id = types_de_champ.id AND types_de_champ.procedure_id = #{procedure.id}").order('order_place') end + def ordered_champs_v2 + champs.includes(:type_de_champ).order('types_de_champ.order_place') + end + def ordered_champs_private # TODO: use the line below when the procedure preview does not leak champ with dossier_id == 0 # champs_private.includes(:type_de_champ).order('types_de_champ.order_place') @@ -289,6 +293,12 @@ class Dossier < ActiveRecord::Base end end + def mandataire_social!(france_connect_information) + if etablissement.mandataire_social?(france_connect_information) + update_column(:mandataire_social, true) + end + end + private def update_state_dates diff --git a/app/models/entreprise.rb b/app/models/entreprise.rb index 5e24a31bb..3e92635c9 100644 --- a/app/models/entreprise.rb +++ b/app/models/entreprise.rb @@ -6,9 +6,13 @@ class Entreprise < ActiveRecord::Base validates_presence_of :siren validates_uniqueness_of :dossier_id + accepts_nested_attributes_for :rna_information + before_save :default_values def default_values self.raison_sociale ||= '' end + + attr_writer :mandataires_sociaux end diff --git a/app/models/etablissement.rb b/app/models/etablissement.rb index 23fc3b0c0..2b51f17ea 100644 --- a/app/models/etablissement.rb +++ b/app/models/etablissement.rb @@ -4,6 +4,9 @@ class Etablissement < ActiveRecord::Base has_many :exercices, dependent: :destroy + accepts_nested_attributes_for :exercices + accepts_nested_attributes_for :entreprise + validates_uniqueness_of :dossier_id def geo_adresse @@ -14,4 +17,14 @@ class Etablissement < ActiveRecord::Base # squeeze needed because of space in excess in the data "#{numero_voie} #{type_voie} #{nom_voie}, #{complement_adresse}, #{code_postal} #{localite}".squeeze(' ') end + + attr_accessor :entreprise_mandataires_sociaux + + def mandataire_social?(france_connect_information) + if france_connect_information.present? + entreprise_mandataires_sociaux&.find do |mandataire| + france_connect_information.mandataire_social?(mandataire) + end + end + end end diff --git a/app/models/exercice.rb b/app/models/exercice.rb index 6b002c092..7336d3f38 100644 --- a/app/models/exercice.rb +++ b/app/models/exercice.rb @@ -2,4 +2,8 @@ class Exercice < ActiveRecord::Base belongs_to :etablissement validates :ca, presence: true, allow_blank: false, allow_nil: false + + def date_fin_exercice + super || dateFinExercice + end end diff --git a/app/models/france_connect_information.rb b/app/models/france_connect_information.rb index 76e804399..a4cdcad85 100644 --- a/app/models/france_connect_information.rb +++ b/app/models/france_connect_information.rb @@ -2,4 +2,10 @@ class FranceConnectInformation < ActiveRecord::Base belongs_to :user validates :france_connect_particulier_id, presence: true, allow_blank: false, allow_nil: false + + def mandataire_social?(params) + params[:nom].casecmp(family_name).zero? && + params[:prenom].casecmp(given_name).zero? && + params[:date_naissance_timestamp] == birthdate.to_time.to_i + end end diff --git a/app/models/rna_information.rb b/app/models/rna_information.rb index 6b7d4f898..409f7bd10 100644 --- a/app/models/rna_information.rb +++ b/app/models/rna_information.rb @@ -2,4 +2,8 @@ class RNAInformation < ActiveRecord::Base belongs_to :entreprise validates :association_id, presence: true, allow_blank: false, allow_nil: false + + def rna=(id) + write_attribute(:association_id, id) + end end diff --git a/app/services/dossier_service.rb b/app/services/dossier_service.rb deleted file mode 100644 index b43015a47..000000000 --- a/app/services/dossier_service.rb +++ /dev/null @@ -1,54 +0,0 @@ -class DossierService - def initialize dossier, siret, france_connect_information - @dossier = dossier - @siret = siret - @france_connect_information = france_connect_information - end - - def dossier_informations! - @entreprise_adapter = SIADE::EntrepriseAdapter.new(DossierService.siren @siret) - - if @entreprise_adapter.to_params.nil? - raise RestClient::ResourceNotFound - end - - @etablissement_adapter = SIADE::EtablissementAdapter.new(@siret) - - if @etablissement_adapter.to_params.nil? - raise RestClient::ResourceNotFound - end - - @dossier.create_entreprise(@entreprise_adapter.to_params) - @dossier.create_etablissement(@etablissement_adapter.to_params) - - @rna_adapter = SIADE::RNAAdapter.new(@siret) - @dossier.entreprise.create_rna_information(@rna_adapter.to_params) - - @exercices_adapter = SIADE::ExercicesAdapter.new(@siret) - @dossier.etablissement.exercices.create(@exercices_adapter.to_params) - - @dossier.update_attributes(mandataire_social: mandataire_social?(@entreprise_adapter.mandataires_sociaux)) - @dossier.etablissement.update_attributes(entreprise: @dossier.entreprise) - - @dossier - end - - def self.siren siret - siret[0..8] - end - - private - - def mandataire_social? mandataires_list - if @france_connect_information.present? - - mandataires_list.each do |mandataire| - return true if mandataire[:nom].casecmp(@france_connect_information.family_name).zero? && - mandataire[:prenom].casecmp(@france_connect_information.given_name).zero? && - mandataire[:date_naissance_timestamp] == @france_connect_information.birthdate.to_time.to_i - end - end - - false - end -end diff --git a/app/services/siret_service.rb b/app/services/siret_service.rb new file mode 100644 index 000000000..f4df8856b --- /dev/null +++ b/app/services/siret_service.rb @@ -0,0 +1,33 @@ +class SIRETService + def self.fetch(siret, dossier = nil) + etablissement = SIADE::EtablissementAdapter.new(siret) + entreprise = SIADE::EntrepriseAdapter.new(siren(siret)) + + if etablissement.success? && entreprise.success? + association = SIADE::RNAAdapter.new(siret) + exercices = SIADE::ExercicesAdapter.new(siret) + + params = etablissement.to_params + .merge(entreprise.to_params.map { |k,v| ["entreprise_#{k}", v] }.to_h) + .merge(association.to_params&.map { |k,v| ["association_#{k}", v] }.to_h) + .merge(exercices_attributes: exercices.to_params) + + # This is to fill legacy models and relationships + if dossier.present? + return params.merge( + entreprise_attributes: entreprise.to_params + .merge({ + dossier: dossier, + rna_information_attributes: association.to_params + }.compact) + ) + else + return params + end + end + end + + def self.siren(siret) + siret[0..8] + end +end diff --git a/app/views/accessibilite/index.html.haml b/app/views/accessibilite/index.html.haml index c07140b08..02dc7a238 100644 --- a/app/views/accessibilite/index.html.haml +++ b/app/views/accessibilite/index.html.haml @@ -2,5 +2,17 @@ %h1.new-h1 Accessibilité - %p - Nous travaillons à rendre ce site conforme avec les normes en matière d'accessibilité. Une premiere version est attendue pour août 2018 pour la partie "Usager" du site. + %p.new-p + Nous travaillons à améliorer le niveau d'accessibilité du site et sa conformité avec les normes en la matière. Une première version optimisée pour la partie « Usager » du site ainsi qu'une déclaration de conformité RGAA seront disponibles en août 2018. + + %h2.new-h2 Signaler un dysfonctionnement + %p.new-p + Si, malgré notre vigilance, vous rencontriez le moindre problème d’accessibilité sur notre site, n’hésitez pas à nous écrire à contact@tps.apientreprise.fr. + + %h2.new-h2 Défenseur des droits + %p.new-p + Si vous constatez un défaut d'accessibilité vous empêchant d'accéder à un contenu ou une fonctionnalité du site, que vous nous le signalez et que vous ne parvenez pas à obtenir une réponse rapide de notre part, vous êtes en droit de faire parvenir vos doléances ou une demande de saisine au Défenseur des droits. Plusieurs moyens sont à votre disposition : + %ul + %li un formulaire de contact ; + %li la liste du ou des délégués de votre région avec leurs informations de contact direct ; + %li une adresse postale : Le Défenseur des droits - 7 rue Saint-Florentin - 75409 Paris Cedex 08. diff --git a/app/views/dossiers/_edit_dossier.html.haml b/app/views/dossiers/_edit_dossier.html.haml index 258bf2b4b..17356d1c7 100644 --- a/app/views/dossiers/_edit_dossier.html.haml +++ b/app/views/dossiers/_edit_dossier.html.haml @@ -1,5 +1,5 @@ - if !@facade.dossier.read_only? - if user_signed_in? && (@facade.dossier.owner?(current_user.email) || @facade.dossier.invite_by_user?(current_user.email)) - %a#maj_infos.action{ href: "/users/dossiers/#{@facade.dossier.id}/description" } + = link_to modifier_dossier_path(@facade.dossier), class: 'action', id: 'maj_infos' do #edit-dossier.col-lg-2.col-md-2.col-sm-2.col-xs-2.action = "éditer".upcase diff --git a/app/views/dossiers/_infos_dossier.html.haml b/app/views/dossiers/_infos_dossier.html.haml index de2ee2545..7fae0d674 100644 --- a/app/views/dossiers/_infos_dossier.html.haml +++ b/app/views/dossiers/_infos_dossier.html.haml @@ -41,7 +41,7 @@ - if champ.type_champ == 'dossier_link' - dossier = Dossier.includes(:procedure).find_by(id: champ.decorate.value) - if dossier - = link_to("Dossier #{dossier.id}", dossier.decorate.url(gestionnaire_signed_in?), target: '_blank') + = link_to("Dossier #{dossier.id}", modifier_dossier_path(dossier), target: '_blank') %br = sanitize(dossier.text_summary) - else diff --git a/app/views/dossiers/_infos_entreprise.html.haml b/app/views/dossiers/_infos_entreprise.html.haml index 6938e2163..aad89acb7 100644 --- a/app/views/dossiers/_infos_entreprise.html.haml +++ b/app/views/dossiers/_infos_entreprise.html.haml @@ -49,7 +49,7 @@ .col-xs-8.entreprise-info - @facade.etablissement.exercices.each_with_index do |exercice, index| %strong - = "#{exercice.dateFinExercice.year} : " + = "#{exercice.date_fin_exercice.year} : " = number_to_currency(exercice.ca) %br diff --git a/app/views/layouts/_new_footer.html.haml b/app/views/layouts/_new_footer.html.haml index d04de7f71..5cc5e2a07 100644 --- a/app/views/layouts/_new_footer.html.haml +++ b/app/views/layouts/_new_footer.html.haml @@ -3,13 +3,15 @@ %ul.footer-columns %li.footer-column %ul.footer-logos - %li - = link_to "https://beta.gouv.fr/" do - %span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': 'Logo de beta.gouv.fr' } + %li.footer-text + Un service fourni par la DINSIC + %br + et incubé par beta.gouv.fr %li = link_to "http://www.modernisation.gouv.fr/" do %span.footer-logo.footer-logo-dinsic{ role: 'img', 'aria-label': 'Logo de la DINSIC' } - + = link_to "https://beta.gouv.fr" do + %span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': 'Logo de beta.gouv.fr' } %li.footer-column %ul.footer-links diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index f4e088c14..b6718a0cb 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -30,6 +30,11 @@ %br – par email : contact@tps.apientreprise.fr + - if nav_bar_profile == :user + %ul.header-tabs + %li + = link_to "Mes dossiers", users_dossiers_path, class: 'tab-link' + %ul.header-right-content - if nav_bar_profile == :gestionnaire && gestionnaire_signed_in? %li diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index 973899ddf..dcccd8024 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -17,7 +17,7 @@ = stylesheet_link_tag "new_design/print", media: "print", "data-turbolinks-track": true %body - .page-wrapper{ class: content_for?(:display_footer) ? 'with-footer' : 'nil' } + .page-wrapper = render partial: "layouts/support_navigator_banner" = render partial: "layouts/ie_lt_10" - if Rails.env == "staging" diff --git a/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml b/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml index a60448c29..b006acc4f 100644 --- a/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml +++ b/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml @@ -44,7 +44,7 @@ %th Exercices : %td - etablissement.exercices.each_with_index do |exercice, index| - = "#{exercice.dateFinExercice.year} : " + = "#{exercice.date_fin_exercice.year} : " = number_to_currency(exercice.ca) %br - if entreprise.rna_information.present? @@ -59,10 +59,10 @@ %td= entreprise.rna_information.objet %tr %th Date de création : - %td= entreprise.rna_information.date_creation.strftime("%d/%m/%Y") + %td= entreprise.rna_information.date_creation&.strftime("%d/%m/%Y") %tr %th Date de publication : - %td= entreprise.rna_information.date_publication.try(:strftime, "%d/%m/%Y") + %td= entreprise.rna_information.date_publication&.strftime("%d/%m/%Y") %tr %th Date de déclaration : - %td= entreprise.rna_information.date_declaration.strftime("%d/%m/%Y") + %td= entreprise.rna_information.date_declaration&.strftime("%d/%m/%Y") diff --git a/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml b/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml index 2770eac7e..ede49a733 100644 --- a/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml +++ b/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml @@ -8,7 +8,7 @@ = form_for @dossier, url: annotations_gestionnaire_dossier_path(@dossier.procedure, @dossier), html: { class: 'form' } do |f| = f.fields_for :champs_private, f.object.ordered_champs_private do |champ_form| - champ = champ_form.object - = render partial: "new_gestionnaire/dossiers/editable_champs/editable_champ", + = render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: champ_form, seen_at: @annotations_privees_seen_at } .send-wrapper diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_editable_champ.html.haml b/app/views/new_gestionnaire/dossiers/editable_champs/_editable_champ.html.haml deleted file mode 100644 index 6c37e74fd..000000000 --- a/app/views/new_gestionnaire/dossiers/editable_champs/_editable_champ.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.editable-champ - - if is_not_header_nor_explication?(champ) - = render partial: 'new_gestionnaire/dossiers/editable_champs/champ_label', locals: { form: form, champ: champ, seen_at: seen_at } - - = render partial: "new_gestionnaire/dossiers/editable_champs/#{champ.type_champ}", - locals: { champ: champ, form: form } diff --git a/app/views/new_user/dossiers/merci.html.haml b/app/views/new_user/dossiers/merci.html.haml new file mode 100644 index 000000000..7fddc5696 --- /dev/null +++ b/app/views/new_user/dossiers/merci.html.haml @@ -0,0 +1,19 @@ +.merci + .container + = image_tag('user/envoi-dossier.svg') + %h1 Merci ! + %p.send + Votre dossier sur la procédure + %b= @dossier.procedure.libelle + a bien été envoyé. + %p + Vous avez désormais accès à votre + %b dossier en ligne. + %p + Vous pouvez + %b le modifier + et + %b échanger avec un instructeur + lors de sa construction et de son instruction + + = link_to 'Accéder à votre dossier', users_dossier_recapitulatif_path(@dossier), class: 'button large primary' diff --git a/app/views/new_user/dossiers/modifier.html.haml b/app/views/new_user/dossiers/modifier.html.haml new file mode 100644 index 000000000..c409b495d --- /dev/null +++ b/app/views/new_user/dossiers/modifier.html.haml @@ -0,0 +1,62 @@ +.dossier-edit + .dossier-header + .container + %h1= @dossier.procedure.libelle + + .container + %p.thanks Les champs avec une asterisque (*) sont obligatoires. + + = form_for @dossier, html: { class: 'form', multipart: true } do |f| + = f.fields_for :champs, @dossier.ordered_champs_v2 do |champ_form| + - champ = champ_form.object + = render partial: "shared/dossiers/editable_champs/editable_champ", + locals: { champ: champ, form: champ_form } + + - tpjs = @dossier.types_de_piece_justificative.order('order_place ASC') + - if tpjs.present? + .card.featured + .card-title + Pièces-jointes + + - tpjs.each do |tpj| + .pj-input + %label{ for: "piece_justificative_#{tpj.id}" } + = tpj.libelle + - if tpj.mandatory? + %span.mandatory * + + %p.piece-description= tpj.description + + - if tpj.lien_demarche.present? + %p.piece-description + Récupérer le formulaire vierge pour mon dossier : + = link_to "Télécharger", tpj.lien_demarche, target: :blank + + - if @dossier.was_piece_justificative_uploaded_for_type_id?(tpj.id) + - pj = @dossier.retrieve_last_piece_justificative_by_type(tpj.id) + %p + Pièce-jointe déjà importée : + = link_to pj.original_filename, pj.content_url, target: :blank + + = file_field_tag "piece_justificative_#{tpj.id}", + accept: PieceJustificative.accept_format, + max_file_size: 6.megabytes, + required: (tpj.mandatory? && !@dossier.was_piece_justificative_uploaded_for_type_id?(tpj.id)) + + .send-wrapper + = hidden_field_tag 'submit_action', 'draft' + + - if @dossier.brouillon? + = f.button 'Enregistrer le brouillon', + formnovalidate: true, + class: 'button send', + data: { action: 'draft', disable_with: 'Envoi...' } + + = f.button 'Soumettre le dossier', + class: 'button send primary', + data: { action: 'submit', disable_with: 'Envoi...' } + + - else + = f.button 'Modifier le dossier', + class: 'button send primary', + data: { action: 'submit', disable_with: 'Envoi...' } diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index cd02bc74b..0bf202cb3 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -23,7 +23,7 @@ = form_for @dossier, url: '', html: { class: 'form' } do |f| = f.fields_for :champs do |champ_form| - champ = champ_form.object - = render partial: "new_gestionnaire/dossiers/editable_champs/editable_champ", + = render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: champ_form, seen_at: nil } %input{ type: "password", value: "12345678" } diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_address.html.haml b/app/views/shared/dossiers/editable_champs/_address.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_address.html.haml rename to app/views/shared/dossiers/editable_champs/_address.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_champ_label.html.haml b/app/views/shared/dossiers/editable_champs/_champ_label.html.haml similarity index 85% rename from app/views/new_gestionnaire/dossiers/editable_champs/_champ_label.html.haml rename to app/views/shared/dossiers/editable_champs/_champ_label.html.haml index 38749d6da..cd38c8515 100644 --- a/app/views/new_gestionnaire/dossiers/editable_champs/_champ_label.html.haml +++ b/app/views/shared/dossiers/editable_champs/_champ_label.html.haml @@ -3,7 +3,7 @@ - if champ.mandatory? %span.mandatory * - - if champ.updated_at.present? + - if champ.updated_at.present? && seen_at.present? %span.updated-at{ class: highlight_if_unseen_class(seen_at, champ.updated_at) } = "modifié le #{champ.updated_at.strftime('%d/%m/%Y à %H:%M')}" diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_checkbox.html.haml b/app/views/shared/dossiers/editable_champs/_checkbox.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_checkbox.html.haml rename to app/views/shared/dossiers/editable_champs/_checkbox.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_civilite.html.haml b/app/views/shared/dossiers/editable_champs/_civilite.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_civilite.html.haml rename to app/views/shared/dossiers/editable_champs/_civilite.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_date.html.haml b/app/views/shared/dossiers/editable_champs/_date.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_date.html.haml rename to app/views/shared/dossiers/editable_champs/_date.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_datetime.html.haml b/app/views/shared/dossiers/editable_champs/_datetime.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_datetime.html.haml rename to app/views/shared/dossiers/editable_champs/_datetime.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_departements.html.haml b/app/views/shared/dossiers/editable_champs/_departements.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_departements.html.haml rename to app/views/shared/dossiers/editable_champs/_departements.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_dossier_link.html.haml b/app/views/shared/dossiers/editable_champs/_dossier_link.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_dossier_link.html.haml rename to app/views/shared/dossiers/editable_champs/_dossier_link.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_drop_down_list.html.haml b/app/views/shared/dossiers/editable_champs/_drop_down_list.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_drop_down_list.html.haml rename to app/views/shared/dossiers/editable_champs/_drop_down_list.html.haml diff --git a/app/views/shared/dossiers/editable_champs/_editable_champ.html.haml b/app/views/shared/dossiers/editable_champs/_editable_champ.html.haml new file mode 100644 index 000000000..170ef3ef8 --- /dev/null +++ b/app/views/shared/dossiers/editable_champs/_editable_champ.html.haml @@ -0,0 +1,6 @@ +.editable-champ + - if is_not_header_nor_explication?(champ) + = render partial: 'shared/dossiers/editable_champs/champ_label', locals: { form: form, champ: champ, seen_at: defined?(seen_at) ? seen_at : nil } + + = render partial: "shared/dossiers/editable_champs/#{champ.type_champ}", + locals: { champ: champ, form: form } diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_email.html.haml b/app/views/shared/dossiers/editable_champs/_email.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_email.html.haml rename to app/views/shared/dossiers/editable_champs/_email.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_engagement.html.haml b/app/views/shared/dossiers/editable_champs/_engagement.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_engagement.html.haml rename to app/views/shared/dossiers/editable_champs/_engagement.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_explication.html.haml b/app/views/shared/dossiers/editable_champs/_explication.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_explication.html.haml rename to app/views/shared/dossiers/editable_champs/_explication.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_header_section.html.haml b/app/views/shared/dossiers/editable_champs/_header_section.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_header_section.html.haml rename to app/views/shared/dossiers/editable_champs/_header_section.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_multiple_drop_down_list.html.haml b/app/views/shared/dossiers/editable_champs/_multiple_drop_down_list.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_multiple_drop_down_list.html.haml rename to app/views/shared/dossiers/editable_champs/_multiple_drop_down_list.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_number.html.haml b/app/views/shared/dossiers/editable_champs/_number.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_number.html.haml rename to app/views/shared/dossiers/editable_champs/_number.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_pays.html.haml b/app/views/shared/dossiers/editable_champs/_pays.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_pays.html.haml rename to app/views/shared/dossiers/editable_champs/_pays.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_phone.html.haml b/app/views/shared/dossiers/editable_champs/_phone.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_phone.html.haml rename to app/views/shared/dossiers/editable_champs/_phone.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_piece_justificative.html.haml b/app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_piece_justificative.html.haml rename to app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_regions.html.haml b/app/views/shared/dossiers/editable_champs/_regions.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_regions.html.haml rename to app/views/shared/dossiers/editable_champs/_regions.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_text.html.haml b/app/views/shared/dossiers/editable_champs/_text.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_text.html.haml rename to app/views/shared/dossiers/editable_champs/_text.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_textarea.html.haml b/app/views/shared/dossiers/editable_champs/_textarea.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_textarea.html.haml rename to app/views/shared/dossiers/editable_champs/_textarea.html.haml diff --git a/app/views/new_gestionnaire/dossiers/editable_champs/_yes_no.html.haml b/app/views/shared/dossiers/editable_champs/_yes_no.html.haml similarity index 100% rename from app/views/new_gestionnaire/dossiers/editable_champs/_yes_no.html.haml rename to app/views/shared/dossiers/editable_champs/_yes_no.html.haml diff --git a/app/views/users/dossiers/_list.html.haml b/app/views/users/dossiers/_list.html.haml index 592f5695e..32eeb0b42 100644 --- a/app/views/users/dossiers/_list.html.haml +++ b/app/views/users/dossiers/_list.html.haml @@ -17,7 +17,7 @@ - dossier_url = users_dossiers_invite_path(id: invite.id) if invite.present? - if invite.nil? - dossier_url = users_dossier_recapitulatif_path(dossier) if !dossier.brouillon? - - dossier_url = users_dossier_description_path(dossier) if dossier.brouillon? + - dossier_url = modifier_dossier_path(dossier) if dossier.brouillon? %tr{ id: "tr_dossier_#{dossier.id}", 'data-dossier_url' => dossier_url } %td.center diff --git a/config/routes.rb b/config/routes.rb index aece4acc4..3c2c56c1e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -202,13 +202,18 @@ Rails.application.routes.draw do get "patron" => "root#patron" scope module: 'new_user' do - resources :dossiers, only: [] do + resources :dossiers, only: [:update] do member do get 'identite' patch 'update_identite' + get 'modifier' + get 'merci' end get 'attestation' end + # FIXME: to remove when show is implemeted + # needed to fix refresh after dossier draft save + get 'dossiers/:id', to: redirect('/dossiers/%{id}/modifier') end scope module: 'new_gestionnaire', as: 'gestionnaire' do diff --git a/db/migrate/20180221104139_add_entreprise_to_etablissement.rb b/db/migrate/20180221104139_add_entreprise_to_etablissement.rb new file mode 100644 index 000000000..81889d58c --- /dev/null +++ b/db/migrate/20180221104139_add_entreprise_to_etablissement.rb @@ -0,0 +1,26 @@ +class AddEntrepriseToEtablissement < ActiveRecord::Migration[5.2] + def change + add_column :etablissements, :entreprise_siren, :string + add_column :etablissements, :entreprise_capital_social, :integer + add_column :etablissements, :entreprise_numero_tva_intracommunautaire, :string + add_column :etablissements, :entreprise_forme_juridique, :string + add_column :etablissements, :entreprise_forme_juridique_code, :string + add_column :etablissements, :entreprise_nom_commercial, :string + add_column :etablissements, :entreprise_raison_sociale, :string + add_column :etablissements, :entreprise_siret_siege_social, :string + add_column :etablissements, :entreprise_code_effectif_entreprise, :string + add_column :etablissements, :entreprise_date_creation, :date + add_column :etablissements, :entreprise_nom, :string + add_column :etablissements, :entreprise_prenom, :string + + add_column :etablissements, :association_rna, :string + add_column :etablissements, :association_titre, :string + add_column :etablissements, :association_objet, :text + add_column :etablissements, :association_date_creation, :date + add_column :etablissements, :association_date_declaration, :date + add_column :etablissements, :association_date_publication, :date + + add_column :champs, :etablissement_id, :integer, index: true + add_column :exercices, :date_fin_exercice, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index c85827d5a..d7e76bbf8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -161,6 +161,7 @@ ActiveRecord::Schema.define(version: 2018_02_26_162351) do t.datetime "created_at" t.datetime "updated_at" t.boolean "private" + t.integer "etablissement_id" t.index ["dossier_id"], name: "index_champs_on_dossier_id" t.index ["private"], name: "index_champs_on_private" t.index ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id" @@ -264,6 +265,24 @@ ActiveRecord::Schema.define(version: 2018_02_26_162351) do t.string "code_insee_localite" t.integer "dossier_id" t.integer "entreprise_id" + t.string "entreprise_siren" + t.integer "entreprise_capital_social" + t.string "entreprise_numero_tva_intracommunautaire" + t.string "entreprise_forme_juridique" + t.string "entreprise_forme_juridique_code" + t.string "entreprise_nom_commercial" + t.string "entreprise_raison_sociale" + t.string "entreprise_siret_siege_social" + t.string "entreprise_code_effectif_entreprise" + t.date "entreprise_date_creation" + t.string "entreprise_nom" + t.string "entreprise_prenom" + t.string "association_rna" + t.string "association_titre" + t.text "association_objet" + t.date "association_date_creation" + t.date "association_date_declaration" + t.date "association_date_publication" t.index ["dossier_id"], name: "index_etablissements_on_dossier_id" end @@ -272,6 +291,7 @@ ActiveRecord::Schema.define(version: 2018_02_26_162351) do t.datetime "dateFinExercice" t.integer "date_fin_exercice_timestamp" t.integer "etablissement_id" + t.datetime "date_fin_exercice" end create_table "follows", id: :serial, force: :cascade do |t| diff --git a/lib/active_storage/service/cellar_service.rb b/lib/active_storage/service/cellar_service.rb index 3cb916d3b..b19d078aa 100644 --- a/lib/active_storage/service/cellar_service.rb +++ b/lib/active_storage/service/cellar_service.rb @@ -1,65 +1,53 @@ -require 'base64' -require 'net/http' -require 'openssl' - module ActiveStorage class Service::CellarService < Service def initialize(access_key_id:, secret_access_key:, bucket:, **) - @endpoint = URI::HTTPS.build(host: "#{bucket}.cellar.services.clever-cloud.com") - @access_key_id = access_key_id - @secret_access_key = secret_access_key - @bucket = bucket + @adapter = Cellar::CellarAdapter.new(access_key_id, secret_access_key, bucket) end - def download(key) - # TODO: error handling + def upload(key, io, checksum: nil) + instrument :upload, key: key, checksum: checksum do + @adapter.session { |s| s.upload(key, io, checksum) } + end + end + + def download(key, &block) if block_given? instrument :streaming_download, key: key do - http_start do |http| - http.request(get_request(key)) do |response| - response.read_body do |chunk| - yield(chunk.force_encoding(Encoding::BINARY)) - end - end - end + @adapter.session { |s| s.download(key, &block) } end else instrument :download, key: key do - http_start do |http| - response = http.request(get_request(key)) - if response.is_a?(Net::HTTPSuccess) - response.body.force_encoding(Encoding::BINARY) - end - end + @adapter.session { |s| s.download(key) } end end end def delete(key) - # TODO: error handling instrument :delete, key: key do - http_start do |http| - perform_delete(http, key) - end + @adapter.session { |s| s.delete(key) } end end def delete_prefixed(prefix) - # TODO: error handling - # TODO: handle pagination if more than 1000 keys instrument :delete_prefixed, prefix: prefix do - http_start do |http| - list_prefixed(http, prefix).each do |key| - # TODO: use bulk delete instead - perform_delete(http, key) - end + @adapter.session do |s| + keys = s.list_prefixed(prefix) + s.delete_keys(keys) end end end + def exist?(key) + instrument :exist, key: key do |payload| + answer = @adapter.session { |s| s.exist?(key) } + payload[:exist] = answer + answer + end + end + def url(key, expires_in:, filename:, disposition:, content_type:) instrument :url, key: key do |payload| - generated_url = presigned_url( + generated_url = @adapter.presigned_url( method: 'GET', key: key, expires_in: expires_in, @@ -73,7 +61,7 @@ module ActiveStorage def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) instrument :url, key: key do |payload| - generated_url = presigned_url( + generated_url = @adapter.presigned_url( method: 'PUT', key: key, expires_in: expires_in, @@ -88,80 +76,5 @@ module ActiveStorage def headers_for_direct_upload(key, content_type:, checksum:, **) { "Content-Type" => content_type, "Content-MD5" => checksum } end - - private - - def http_start(&block) - Net::HTTP.start(@endpoint.host, @endpoint.port, use_ssl: true, &block) - end - - def sign(request, key, checksum: '') - date = Time.now.httpdate - sig = signature(method: request.method, key: key, date: date, checksum: checksum) - request['date'] = date - request['authorization'] = "AWS #{@access_key_id}:#{sig}" - end - - def presigned_url(method:, key:, expires_in:, content_type: '', checksum: '', **query_params) - expires = expires_in.from_now.to_i - - query = query_params.merge({ - AWSAccessKeyId: @access_key_id, - Expires: expires, - Signature: signature(method: method, key: key, expires: expires, content_type: content_type, checksum: checksum) - }) - - generated_url = URI::join(@endpoint, "/#{key}","?#{query.to_query}").to_s - end - - def signature(method:, key:, expires: '', date: '', content_type: '', checksum: '') - canonicalized_amz_headers = "" - canonicalized_resource = "/#{@bucket}/#{key}" - string_to_sign = "#{method}\n#{checksum}\n#{content_type}\n#{expires}#{date}\n" + - "#{canonicalized_amz_headers}#{canonicalized_resource}" - Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), @secret_access_key, string_to_sign)).strip - end - - def list_prefixed(http, prefix) - request = Net::HTTP::Get.new(URI::join(@endpoint, "/?list-type=2&prefix=#{prefix}")) - sign(request, "") - response = http.request(request) - if response.is_a?(Net::HTTPSuccess) - parse_bucket_listing(response.body) - end - end - - def parse_bucket_listing(bucket_listing_xml) - doc = Nokogiri::XML(bucket_listing_xml) - doc - .xpath('//xmlns:Contents/xmlns:Key') - .map{ |k| k.text } - end - - def get_request(key) - request = Net::HTTP::Get.new(URI::join(@endpoint, "/#{key}")) - sign(request, key) - request - end - - def bulk_deletion_request_body(keys) - builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml| - xml.Delete do - xml.Quiet("true") - keys.each do |k| - xml.Object do - xml.Key(k) - end - end - end - end - builder.to_xml - end - - def perform_delete(http, key) - request = Net::HTTP::Delete.new(URI::join(@endpoint, "/#{key}")) - sign(request, key) - http.request(request) - end end end diff --git a/lib/cellar/amazon_v2_request_signer.rb b/lib/cellar/amazon_v2_request_signer.rb new file mode 100644 index 000000000..6e2146b59 --- /dev/null +++ b/lib/cellar/amazon_v2_request_signer.rb @@ -0,0 +1,43 @@ +require 'base64' +require 'openssl' + +module Cellar + class AmazonV2RequestSigner + def initialize(access_key_id, secret_access_key, bucket) + @access_key_id = access_key_id + @secret_access_key = secret_access_key + @bucket = bucket + end + + def sign(request, key) + date = Time.now.httpdate + sig = signature( + method: request.method, + key: key, + date: date, + checksum: request['Content-MD5'] || '', + content_type: request.content_type || '' + ) + request['date'] = date + request['authorization'] = "AWS #{@access_key_id}:#{sig}" + end + + def url_signature_params(method:, key:, expires_in:, content_type: '', checksum: '') + expires = expires_in.from_now.to_i + + { + AWSAccessKeyId: @access_key_id, + Expires: expires, + Signature: signature(method: method, key: key, expires: expires, content_type: content_type, checksum: checksum) + } + end + + def signature(method:, key:, expires: '', date: '', content_type: '', checksum: '') + canonicalized_amz_headers = "" + canonicalized_resource = "/#{@bucket}/#{key}" + string_to_sign = "#{method}\n#{checksum}\n#{content_type}\n#{expires}#{date}\n" + + "#{canonicalized_amz_headers}#{canonicalized_resource}" + Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), @secret_access_key, string_to_sign)).strip + end + end +end diff --git a/lib/cellar/cellar_adapter.rb b/lib/cellar/cellar_adapter.rb new file mode 100644 index 000000000..ea2340b61 --- /dev/null +++ b/lib/cellar/cellar_adapter.rb @@ -0,0 +1,146 @@ +require 'net/http' +require 'openssl' + +module Cellar + class CellarAdapter + def initialize(access_key_id, secret_access_key, bucket) + @endpoint = URI::HTTPS.build(host: "#{bucket}.cellar.services.clever-cloud.com") + @signer = AmazonV2RequestSigner.new(access_key_id, secret_access_key, bucket) + end + + def presigned_url(method:, key:, expires_in:, content_type: '', checksum: '', **query_params) + query = query_params.merge( + @signer.url_signature_params( + method: method, + key: key, + expires_in: expires_in, + content_type: content_type, + checksum: checksum + ) + ) + + URI::join(@endpoint, "/#{key}", "?#{query.to_query}").to_s + end + + def session + Net::HTTP.start(@endpoint.host, @endpoint.port, use_ssl: true) do |http| + yield Session.new(http, @signer) + end + end + + class Session + def initialize(http, signer) + @http = http + @signer = signer + end + + def upload(key, io, checksum) + with_io_length(io) do |io, length| + request = Net::HTTP::Put.new("/#{key}") + request.content_type = 'application/octet-stream' + request['Content-MD5'] = checksum + request['Content-Length'] = length + request.body_stream = io + @signer.sign(request, key) + @http.request(request) + # TODO: error handling + end + end + + def download(key) + request = Net::HTTP::Get.new("/#{key}") + @signer.sign(request, key) + if block_given? + @http.request(request) do |response| + if response.is_a?(Net::HTTPSuccess) + response.read_body do |chunk| + yield(chunk.force_encoding(Encoding::BINARY)) + end + else + # TODO: error handling + end + end + else + response = @http.request(request) + if response.is_a?(Net::HTTPSuccess) + response.body.force_encoding(Encoding::BINARY) + else + # TODO: error handling + end + end + end + + def delete(key) + # TODO: error handling + request = Net::HTTP::Delete.new("/#{key}") + @signer.sign(request, key) + @http.request(request) + end + + def list_prefixed(prefix) + request = Net::HTTP::Get.new("/?prefix=#{prefix}") + @signer.sign(request, "") + response = @http.request(request) + if response.is_a?(Net::HTTPSuccess) + parse_bucket_listing(response.body) + end + end + + def delete_keys(keys) + request_body = bulk_deletion_request_body(keys) + request = Net::HTTP::Post.new("/?delete") + request.content_type = 'text/xml' + request['Content-MD5'] = Digest::MD5.base64digest(request_body) + request['Content-Length'] = request_body.length + request.body = request_body + @signer.sign(request, "?delete") + @http.request(request) + end + + def exist?(key) + request = Net::HTTP::Head.new("/#{key}") + @signer.sign(request, key) + response = @http.request(request) + response.is_a?(Net::HTTPSuccess) + end + + private + + def parse_bucket_listing(bucket_listing_xml) + doc = Nokogiri::XML(bucket_listing_xml) + doc + .xpath('//xmlns:Contents/xmlns:Key') + .map{ |k| k.text } + end + + def bulk_deletion_request_body(keys) + builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml| + xml.Delete do + keys.each do |k| + xml.Object do + xml.Key(k) + end + end + end + end + builder.to_xml + end + + def with_io_length(io) + if io.respond_to?(:size) && io.respond_to?(:pos) + yield(io, io.size - io.pos) + else + tmp_file = Tempfile.new('cellar_io_length') + begin + IO.copy_stream(io, tmp_file) + length = tmp_file.pos + tmp_file.rewind + yield(tmp_file, length) + ensure + tmp_file.close! + end + end + end + end + end +end diff --git a/spec/controllers/new_user/dossiers_controller_spec.rb b/spec/controllers/new_user/dossiers_controller_spec.rb index 52795cd5c..3839bed55 100644 --- a/spec/controllers/new_user/dossiers_controller_spec.rb +++ b/spec/controllers/new_user/dossiers_controller_spec.rb @@ -81,7 +81,7 @@ describe NewUser::DossiersController, type: :controller do let(:dossier_params) { { autorisation_donnees: true } } it do - expect(response).to redirect_to(users_dossier_description_path(dossier)) + expect(response).to redirect_to(modifier_dossier_path(dossier)) end context 'on a procedure with carto' do @@ -103,4 +103,124 @@ describe NewUser::DossiersController, type: :controller do end end end + + describe '#modifier' do + before { sign_in(user) } + let!(:dossier) { create(:dossier, user: user, autorisation_donnees: true) } + + subject { get :modifier, params: { id: dossier.id } } + + context 'when autorisation_donnees is checked' do + it { is_expected.to render_template(:modifier) } + end + + context 'when autorisation_donnees is not checked' do + before { dossier.update_columns(autorisation_donnees: false) } + + context 'when the dossier is for personne morale' do + it { is_expected.to redirect_to(users_dossier_path(dossier)) } + end + + context 'when the dossier is for an personne physique' do + before { dossier.procedure.update_attributes(for_individual: true) } + + it { is_expected.to redirect_to(identite_dossier_path(dossier)) } + end + end + end + + describe '#edit' do + before { sign_in(user) } + let!(:dossier) { create(:dossier, user: user) } + + it 'returns the edit page' do + get :modifier, params: { id: dossier.id } + expect(response).to have_http_status(:success) + end + end + + describe '#update' do + before { sign_in(user) } + let!(:dossier) { create(:dossier, user: user) } + let(:first_champ) { dossier.champs.first } + let(:value) { 'beautiful value' } + let(:submit_payload) do + { + id: dossier.id, + dossier: { + champs_attributes: { + id: first_champ.id, + value: value + } + } + } + end + let(:payload) { submit_payload } + + subject { patch :update, params: payload } + + it 'updates the champs' do + subject + + expect(response).to redirect_to(merci_dossier_path(dossier)) + expect(first_champ.reload.value).to eq('beautiful value') + expect(dossier.reload.state).to eq('en_construction') + end + + context 'when the update fails' do + before do + expect_any_instance_of(Dossier).to receive(:update).and_return(false) + expect_any_instance_of(Dossier).to receive(:errors) + .and_return(double(full_messages: ['nop'])) + + subject + end + + it { expect(response).to render_template(:modifier) } + it { expect(flash.alert).to eq(['nop']) } + end + + context 'when the pj service returns an error' do + before do + expect(PiecesJustificativesService).to receive(:upload!).and_return(['nop']) + + subject + end + + it { expect(response).to render_template(:modifier) } + it { expect(flash.alert).to eq(['nop']) } + end + + context 'when a mandatory champ is missing' do + let(:value) { nil } + + before do + first_champ.type_de_champ.update_attributes(mandatory: true, libelle: 'l') + allow(PiecesJustificativesService).to receive(:missing_pj_error_messages).and_return(['pj']) + + subject + end + + it { expect(response).to render_template(:modifier) } + it { expect(flash.alert).to eq(['Le champ l doit être rempli.', 'pj']) } + + context 'and the user saves a draft' do + let(:payload) { submit_payload.merge(submit_action: 'draft') } + + it { expect(response).to render_template(:modifier) } + it { expect(flash.notice).to eq('Votre brouillon a bien été sauvegardé.') } + it { expect(dossier.reload.state).to eq('brouillon') } + end + end + + context 'when dossier has no champ' do + let(:submit_payload) { { id: dossier.id } } + + it 'does not raise any errors' do + subject + + expect(response).to redirect_to(merci_dossier_path(dossier)) + end + end + end end diff --git a/spec/controllers/users/carte_controller_shared_example.rb b/spec/controllers/users/carte_controller_shared_example.rb index 4e250c772..229d2d3a3 100644 --- a/spec/controllers/users/carte_controller_shared_example.rb +++ b/spec/controllers/users/carte_controller_shared_example.rb @@ -59,21 +59,14 @@ shared_examples 'carte_controller_spec' do end describe 'POST #save' do - context 'Aucune localisation n\'a jamais été enregistrée' do - it do - post :save, params: { dossier_id: dossier.id, json_latlngs: '' } - expect(response).to redirect_to("/users/dossiers/#{dossier.id}/description") - end - end - context 'En train de modifier la localisation' do let(:dossier) { create(:dossier, state: 'en_construction') } before do post :save, params: { dossier_id: dossier.id, json_latlngs: '' } end - it 'Redirection vers la page récapitulatif' do - expect(response).to redirect_to("/users/dossiers/#{dossier.id}/recapitulatif") + it 'Redirection vers le formulaire de la procedure' do + expect(response).to redirect_to(modifier_dossier_path(dossier)) end end diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 0525b1ec7..7da275a7c 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -398,7 +398,7 @@ describe Users::DossiersController, type: :controller do context 'procedure not use api carto' do it 'redirects to demande' do - expect(response).to redirect_to(controller: :description, action: :show, dossier_id: dossier.id) + expect(response).to redirect_to(modifier_dossier_path(dossier)) end end diff --git a/spec/decorators/dossier_decorator_spec.rb b/spec/decorators/dossier_decorator_spec.rb index b86fcf5c8..a6be8c83e 100644 --- a/spec/decorators/dossier_decorator_spec.rb +++ b/spec/decorators/dossier_decorator_spec.rb @@ -52,30 +52,4 @@ describe DossierDecorator do expect(subject).to eq('Refusé') end end - - describe '#url' do - context "when a gestionnaire is not signed_in" do - context "when the dossier is in brouillon state" do - before do - dossier.state = 'brouillon' - dossier.save - end - - subject { super().url(false) } - - it { is_expected.to eq("/users/dossiers/#{dossier.id}/description") } - end - - context "when the dossier is not in brouillon state" do - before do - dossier.state = 'en_construction' - dossier.save - end - - subject { super().url(false) } - - it { is_expected.to eq("/users/dossiers/#{dossier.id}/recapitulatif") } - end - end - end end diff --git a/spec/factories/exercice.rb b/spec/factories/exercice.rb index b9c6f38cc..fedc9ce47 100644 --- a/spec/factories/exercice.rb +++ b/spec/factories/exercice.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :exercice do ca '12345678' - dateFinExercice "2014-12-30 23:00:00" + date_fin_exercice "2014-12-30 23:00:00" date_fin_exercice_timestamp 1419980400 association :etablissement, factory: [:etablissement] end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 656a0631b..bb2597ccd 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -110,5 +110,33 @@ FactoryBot.define do procedure.archived_at = Time.now end end + + trait :with_all_champs_mandatory do + after(:build) do |procedure, _evaluator| + tdcs = [] + tdcs << create(:type_de_champ, type_champ: 'text', mandatory: true, libelle: 'text') + tdcs << create(:type_de_champ, type_champ: 'textarea', mandatory: true, libelle: 'textarea') + tdcs << create(:type_de_champ, type_champ: 'date', mandatory: true, libelle: 'date') + tdcs << create(:type_de_champ, type_champ: 'datetime', mandatory: true, libelle: 'datetime') + tdcs << create(:type_de_champ, type_champ: 'number', mandatory: true, libelle: 'number') + tdcs << create(:type_de_champ, type_champ: 'checkbox', mandatory: true, libelle: 'checkbox') + tdcs << create(:type_de_champ, type_champ: 'civilite', mandatory: true, libelle: 'civilite') + tdcs << create(:type_de_champ, type_champ: 'email', mandatory: true, libelle: 'email') + tdcs << create(:type_de_champ, type_champ: 'phone', mandatory: true, libelle: 'phone') + tdcs << create(:type_de_champ, type_champ: 'yes_no', mandatory: true, libelle: 'yes_no') + tdcs << create(:type_de_champ, :type_drop_down_list, mandatory: true, libelle: 'simple_drop_down_list') + tdcs << create(:type_de_champ, :type_drop_down_list, type_champ: 'multiple_drop_down_list', mandatory: true, libelle: 'multiple_drop_down_list') + tdcs << create(:type_de_champ, type_champ: 'pays', mandatory: true, libelle: 'pays') + tdcs << create(:type_de_champ, type_champ: 'regions', mandatory: true, libelle: 'regions') + tdcs << create(:type_de_champ, type_champ: 'departements', mandatory: true, libelle: 'departements') + tdcs << create(:type_de_champ, type_champ: 'engagement', mandatory: true, libelle: 'engagement') + tdcs << create(:type_de_champ, type_champ: 'header_section', mandatory: true, libelle: 'header_section') + tdcs << create(:type_de_champ, type_champ: 'explication', mandatory: true, libelle: 'explication') + tdcs << create(:type_de_champ, :type_dossier_link, mandatory: true, libelle: 'dossier_link') + tdcs << create(:type_de_champ, type_champ: 'piece_justificative', mandatory: true, libelle: 'piece_justificative') + + procedure.types_de_champ = tdcs + end + end end end diff --git a/spec/features/new_user/dossier_spec.rb b/spec/features/new_user/dossier_spec.rb new file mode 100644 index 000000000..f6b7aa135 --- /dev/null +++ b/spec/features/new_user/dossier_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' + +feature 'The user' do + let(:password) { 'secret_password' } + let!(:user) { create(:user, password: password) } + + let!(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs_mandatory) } + let(:user_dossier) { user.dossiers.first } + + # TODO: check + # the order + # there are no extraneous input + # attached file works + scenario 'fill a dossier', js: true do + allow(Champ).to receive(:regions).and_return(['region1', 'region2']).at_least(:once) + allow(Champ).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once) + + log_in(user.email, password, procedure) + + fill_individual + + # fill data + fill_in('text', with: 'super texte') + fill_in('textarea', with: 'super textarea') + fill_in('date', with: '12/12/2012') + select_date_and_time(DateTime.parse('06/01/1985 7h05'), form_id_for('datetime')) + fill_in('number', with: '42') + check('checkbox') + choose('Madame') + fill_in('email', with: 'loulou@yopmail.com') + fill_in('phone', with: '1234567890') + choose('Non') + select('val2', from: form_id_for('simple_drop_down_list')) + select('val1', from: form_id_for('multiple_drop_down_list')) + select('val3', from: form_id_for('multiple_drop_down_list')) + select('AUSTRALIE', from: 'pays') + select('region2', from: 'regions') + select('dep2', from: 'departements') + check('engagement') + fill_in('dossier_link', with: '123') + # do not know how to make it work... + # find('form input[type="file"]').set(Rails.root.join('spec/fixtures/white.png')) + + click_on 'Enregistrer le brouillon' + + # check data on the dossier + expect(user_dossier.brouillon?).to be true + expect(champ_value_for('text')).to eq('super texte') + expect(champ_value_for('textarea')).to eq('super textarea') + expect(champ_value_for('date')).to eq('2012-12-12') + expect(champ_value_for('datetime')).to eq('06/01/1985 07:05') + expect(champ_value_for('number')).to eq('42') + expect(champ_value_for('checkbox')).to eq('on') + expect(champ_value_for('civilite')).to eq('Mme.') + expect(champ_value_for('email')).to eq('loulou@yopmail.com') + expect(champ_value_for('phone')).to eq('1234567890') + expect(champ_value_for('yes_no')).to eq('false') + expect(champ_value_for('simple_drop_down_list')).to eq('val2') + expect(JSON.parse(champ_value_for('multiple_drop_down_list'))).to match(['val1', 'val3']) + expect(champ_value_for('pays')).to eq('AUSTRALIE') + expect(champ_value_for('regions')).to eq('region2') + expect(champ_value_for('departements')).to eq('dep2') + expect(champ_value_for('engagement')).to eq('on') + expect(champ_value_for('dossier_link')).to eq('123') + + ## check data on the gui + expect(page).to have_field('text', with: 'super texte') + expect(page).to have_field('textarea', with: 'super textarea') + expect(page).to have_field('date', with: '2012-12-12') + check_date_and_time(DateTime.parse('06/01/1985 7:05'), form_id_for('datetime')) + expect(page).to have_field('number', with: '42') + expect(page).to have_checked_field('checkbox') + expect(page).to have_checked_field('Madame') + expect(page).to have_field('email', with: 'loulou@yopmail.com') + expect(page).to have_field('phone', with: '1234567890') + expect(page).to have_checked_field('Non') + expect(page).to have_select('simple_drop_down_list', selected: 'val2') + expect(page).to have_select('multiple_drop_down_list', selected: ['val1', 'val3']) + expect(page).to have_select('pays', selected: 'AUSTRALIE') + expect(page).to have_select('regions', selected: 'region2') + expect(page).to have_select('departement', selected: 'dep2') + expect(page).to have_checked_field('engagement') + expect(page).to have_field('dossier_link', with: '123') + end + + let(:simple_procedure) do + tdcs = [create(:type_de_champ, type_champ: 'text', mandatory: true, libelle: 'text')] + create(:procedure, :published, :for_individual, types_de_champ: tdcs) + end + + scenario 'save an incomplete dossier as draft but cannot not submit it', js: true do + log_in(user.email, password, simple_procedure) + fill_individual + + click_on 'Enregistrer le brouillon' + expect(page).to have_content('Votre brouillon a bien été sauvegardé') + expect(page).to have_current_path(dossier_path(user_dossier)) + + click_on 'Soumettre le dossier' + expect(user_dossier.reload.brouillon?).to be(true) + expect(page).to have_current_path(dossier_path(user_dossier)) + + fill_in('text', with: 'super texte') + + click_on 'Soumettre le dossier' + expect(user_dossier.reload.en_construction?).to be(true) + expect(champ_value_for('text')).to eq('super texte') + expect(page).to have_current_path(merci_dossier_path(user_dossier)) + end + + private + + def log_in(email, password, procedure) + visit "/commencer/#{procedure.procedure_path.path}" + expect(page).to have_current_path(new_user_session_path) + + fill_in 'user_email', with: email + fill_in 'user_password', with: password + click_on 'Se connecter' + expect(page).to have_current_path(identite_dossier_path(user_dossier)) + end + + def form_id_for(libelle) + find(:xpath, ".//label[contains(text()[normalize-space()], '#{libelle}')]")[:for] + end + + def champ_value_for(libelle) + champs = user_dossier.champs + champs.find { |c| c.libelle == libelle }.value + end + + def fill_individual + fill_in('individual_prenom', with: 'prenom') + fill_in('individual_nom', with: 'nom') + check 'dossier_autorisation_donnees' + click_on 'Continuer' + expect(page).to have_current_path(modifier_dossier_path(user_dossier)) + end + + def select_date_and_time(date, field) + select date.strftime('%Y'), from: "#{field}_1i" # year + select I18n.l(date, format: '%B'), from: "#{field}_2i" # month + select date.strftime('%-d'), from: "#{field}_3i" # day + select date.strftime('%H'), from: "#{field}_4i" # hour + select date.strftime('%M'), from: "#{field}_5i" # minute + end + + def check_date_and_time(date, field) + expect(page).to have_select("#{field}_1i", selected: date.strftime('%Y')) + expect(page).to have_select("#{field}_2i", selected: I18n.l(date, format: '%B')) + expect(page).to have_select("#{field}_3i", selected: date.strftime('%-d')) + expect(page).to have_select("#{field}_4i", selected: date.strftime('%H')) + expect(page).to have_select("#{field}_5i", selected: date.strftime('%M')) + end +end diff --git a/spec/features/users/complete_demande_spec.rb b/spec/features/users/complete_demande_spec.rb index d763ec450..bd782c5a2 100644 --- a/spec/features/users/complete_demande_spec.rb +++ b/spec/features/users/complete_demande_spec.rb @@ -63,16 +63,16 @@ feature 'user path for dossier creation' do page.check('dossier_autorisation_donnees') page.find_by_id('etape_suivante').click end - scenario 'user is on description page' do - expect(page).to have_css('#description-page') + scenario 'user is on edition page' do + expect(page).to have_current_path(modifier_dossier_path(Dossier.last)) end context 'user fill and validate description page' do before do - page.find_by_id("champs_#{Dossier.last.champs.first.id}").set 'Mon super projet' - page.find_by_id('suivant').click + page.find_by_id("dossier_champs_attributes_0_value").set 'Mon super projet' + click_on 'Soumettre le dossier' end - scenario 'user is on recap page' do - expect(page).to have_css('#users-recapitulatif-dossier-show') + scenario 'user is on merci page' do + expect(page).to have_current_path(merci_dossier_path(Dossier.last)) end end end diff --git a/spec/features/users/dossier_creation_spec.rb b/spec/features/users/dossier_creation_spec.rb index 19f7fb6fa..989822067 100644 --- a/spec/features/users/dossier_creation_spec.rb +++ b/spec/features/users/dossier_creation_spec.rb @@ -28,13 +28,9 @@ feature 'As a User I wanna create a dossier' do expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id)) click_button('Etape suivante') - expect(page).to have_current_path(users_dossier_description_path(procedure_for_individual.dossiers.last.id)) - fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1' - find(:css, '[name=submit_action]').set('nouveaux') - click_button('suivant') + expect(page).to have_current_path(modifier_dossier_path(procedure_for_individual.dossiers.last)) expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14") - expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s)) end scenario "with a basic text input field for birthdate (type='date' unsupported)" do @@ -44,12 +40,9 @@ feature 'As a User I wanna create a dossier' do expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s)) click_button('Etape suivante') - fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1' - find(:css, '[name=submit_action]').set('nouveaux') - page.find_by_id('suivant').click + expect(page).to have_current_path(modifier_dossier_path(procedure_for_individual.dossiers.last)) expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14") - expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s)) end end @@ -59,15 +52,12 @@ feature 'As a User I wanna create a dossier' do scenario "no need for birthday" do click_button('Continuer') - expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s)) + expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last)) click_button('Etape suivante') - fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1' - find(:css, '[name=submit_action]').set('nouveaux') - click_button('suivant') + expect(page).to have_current_path(modifier_dossier_path(procedure_for_individual.dossiers.last)) expect(user.dossiers.first.individual.birthdate).to eq(nil) - expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s)) end end end @@ -92,9 +82,7 @@ feature 'As a User I wanna create a dossier' do page.find_by_id('etape_suivante').click expect(page).to have_current_path(users_dossier_carte_path(procedure_with_siret.dossiers.last.id.to_s)) page.find_by_id('etape_suivante').click - fill_in "champs_#{procedure_with_siret.dossiers.last.champs.first.id}", with: 'contenu du champ 1' - page.find_by_id('suivant').click - expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_with_siret.dossiers.last.id.to_s)) + expect(page).to have_current_path(modifier_dossier_path(procedure_with_siret.dossiers.last)) end end end diff --git a/spec/features/users/dossier_edition_spec.rb b/spec/features/users/dossier_edition_spec.rb deleted file mode 100644 index 3de44ef52..000000000 --- a/spec/features/users/dossier_edition_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -feature 'As a User I want to edit a dossier I own' do - let(:user) { create(:user) } - let(:procedure_for_individual) { create(:procedure, :published, :for_individual, :with_type_de_champ, :with_two_type_de_piece_justificative, :with_dossier_link) } - let!(:dossier) { create(:dossier, :with_entreprise, :for_individual, :with_dossier_link, procedure: procedure_for_individual, user: user, autorisation_donnees: true, state: 'en_construction') } - - before "Create dossier and visit root path" do - login_as user, scope: :user - visit root_path - end - - context 'After sign_in, I can navigate through dossiers indexes and edit a dossier' do - scenario 'After sign_in, I can see dossiers "à traiter" (default), and other indexes' do - expect(page.find('#a_traiter')['class']).to eq('active procedure-list-element') - page.find_by_id('brouillon').click - page.find_by_id('a_traiter').click - page.find_by_id('en_instruction').click - page.find_by_id('termine').click - page.find_by_id('invite').click - end - - scenario 'Getting a dossier, I want to create a new message on', js: true do - page.find_by_id("tr_dossier_#{dossier.id.to_s}").click - expect(page).to have_current_path(users_dossier_recapitulatif_path(Dossier.first.id.to_s)) - page.find_by_id('open-message').click - page.execute_script("$('#texte_commentaire').data('wysihtml5').editor.setValue('Contenu du nouveau message')") - page.find_by_id('save-message').click - expect(page.find('.last-commentaire .content').text).to eq('Contenu du nouveau message') - end - - scenario 'On the same dossier, I want to edit informations', js: true do - page.find_by_id("tr_dossier_#{dossier.id.to_s}").click - expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier.id.to_s)) - - # Linked Dossier - linked_dossier_id = dossier.champs.find { |c| c.type_de_champ.type_champ == 'dossier_link' }.value - expect(page).to have_link("Dossier #{linked_dossier_id}") - - page.find_by_id('edit-dossier').click - expect(page).to have_current_path(users_dossier_description_path(dossier.id.to_s)) - champ_id = dossier.champs.find { |t| t.type_champ == "text" }.id - fill_in "champs_#{champ_id.to_s}", with: 'Contenu du champ 1' - page.find_by_id('modification_terminee').click - expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier.id.to_s)) - expect(page.find("#champ-#{champ_id}-value").text).to eq('Contenu du champ 1') - end - end -end diff --git a/spec/lib/active_storage/service/cellar_service_spec.rb b/spec/lib/active_storage/service/cellar_service_spec.rb index eae73bd32..5bbe76d87 100644 --- a/spec/lib/active_storage/service/cellar_service_spec.rb +++ b/spec/lib/active_storage/service/cellar_service_spec.rb @@ -19,34 +19,6 @@ describe 'CellarService' do before { Timecop.freeze(Time.gm(2016, 10, 2)) } after { Timecop.return } - describe 'signature generation' do - context 'for presigned URLs' do - subject do - cellar_service.send( - :signature, - { - method: 'GET', - key: 'fichier', - expires: 5.minutes.from_now.to_i - } - ) - end - - it { is_expected.to eq('nzCsB6cip8oofkuOdvvJs6FafkA=') } - end - - context 'for server-side requests' do - subject do - Net::HTTP::Delete.new('https://rogets.cellar.services.clever-cloud.com/fichier') - end - - before { cellar_service.send(:sign, subject, 'fichier') } - - it { expect(subject['date']).to eq(Time.now.httpdate) } - it { expect(subject['authorization']).to eq('AWS AKIAJFTRSGRH3RXX6D5Q:nkvviwZYb1V9HDrKyJZmY3Z8sSA=') } - end - end - describe 'presigned url for download' do subject do URI.parse( @@ -112,55 +84,4 @@ describe 'CellarService' do ) end end - - describe 'parse_bucket_listing' do - let(:response) do - ' - example-bucket - - 2 - 1000 - / - false - - sample1.jpg - 2011-02-26T01:56:20.000Z - "bf1d737a4d46a19f3bced6905cc8b902" - 142863 - STANDARD - - - sample2.jpg - 2011-02-26T01:56:20.000Z - "bf1d737a4d46a19f3bced6905cc8b902" - 142863 - STANDARD - - ' - end - - subject { cellar_service.send(:parse_bucket_listing, response) } - - it { is_expected.to eq(["sample1.jpg", "sample2.jpg"]) } - end - - describe 'bulk_deletion_request_body' do - let(:expected_response) do - ' - - true - - chapi - - - chapo - - -' - end - - subject { cellar_service.send(:bulk_deletion_request_body, ['chapi', 'chapo']) } - - it { is_expected.to eq(expected_response) } - end end diff --git a/spec/lib/cellar/amazon_v2_request_signer_spec.rb b/spec/lib/cellar/amazon_v2_request_signer_spec.rb new file mode 100644 index 000000000..399975e63 --- /dev/null +++ b/spec/lib/cellar/amazon_v2_request_signer_spec.rb @@ -0,0 +1,43 @@ +require 'net/http' + +describe 'AmazonV2RequestSigner' do + let(:request_signer) do + # These are actual keys, but they’re safe to put here because + # - they never had any rights attached, and + # - the keys were revoked before copying them here + + Cellar::AmazonV2RequestSigner.new( + 'AKIAJFTRSGRH3RXX6D5Q', + '3/y/3Tf5zkfcrTaLFxyKB/oU2/7ay7/Dz8UdEHC7', + 'rogets' + ) + end + + before { Timecop.freeze(Time.gm(2016, 10, 2)) } + after { Timecop.return } + + describe 'signature generation' do + context 'for presigned URLs' do + subject do + request_signer.signature( + method: 'GET', + key: 'fichier', + expires: 5.minutes.from_now.to_i + ) + end + + it { is_expected.to eq('nzCsB6cip8oofkuOdvvJs6FafkA=') } + end + + context 'for server-side requests' do + subject do + Net::HTTP::Delete.new('https://rogets.cellar.services.clever-cloud.com/fichier') + end + + before { request_signer.sign(subject, 'fichier') } + + it { expect(subject['date']).to eq(Time.now.httpdate) } + it { expect(subject['authorization']).to eq('AWS AKIAJFTRSGRH3RXX6D5Q:nkvviwZYb1V9HDrKyJZmY3Z8sSA=') } + end + end +end diff --git a/spec/lib/cellar/cellar_adapter_spec.rb b/spec/lib/cellar/cellar_adapter_spec.rb new file mode 100644 index 000000000..8dda19d0e --- /dev/null +++ b/spec/lib/cellar/cellar_adapter_spec.rb @@ -0,0 +1,56 @@ +describe 'CellarAdapter' do + let(:session) { Cellar::CellarAdapter::Session.new(nil, nil) } + + before { Timecop.freeze(Time.gm(2016, 10, 2)) } + after { Timecop.return } + + describe 'parse_bucket_listing' do + let(:response) do + ' + example-bucket + + 2 + 1000 + / + false + + sample1.jpg + 2011-02-26T01:56:20.000Z + "bf1d737a4d46a19f3bced6905cc8b902" + 142863 + STANDARD + + + sample2.jpg + 2011-02-26T01:56:20.000Z + "bf1d737a4d46a19f3bced6905cc8b902" + 142863 + STANDARD + + ' + end + + subject { session.send(:parse_bucket_listing, response) } + + it { is_expected.to eq(["sample1.jpg", "sample2.jpg"]) } + end + + describe 'bulk_deletion_request_body' do + let(:expected_response) do + ' + + + chapi + + + chapo + + +' + end + + subject { session.send(:bulk_deletion_request_body, ['chapi', 'chapo']) } + + it { is_expected.to eq(expected_response) } + end +end diff --git a/spec/lib/siade/entreprise_adapter_spec.rb b/spec/lib/siade/entreprise_adapter_spec.rb index 9cd14a50f..161aac0a2 100644 --- a/spec/lib/siade/entreprise_adapter_spec.rb +++ b/spec/lib/siade/entreprise_adapter_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' describe SIADE::EntrepriseAdapter do - subject { described_class.new('418166096').to_params } + let(:siren) { '418166096' } + let(:adapter) { described_class.new(siren) } + subject { adapter.to_params } before do - stub_request(:get, "https://staging.entreprise.api.gouv.fr/v2/entreprises/418166096?token=#{SIADETOKEN}") + stub_request(:get, "https://staging.entreprise.api.gouv.fr/v2/entreprises/#{siren}?token=#{SIADETOKEN}") .to_return(body: File.read('spec/support/files/entreprise.json', status: 200)) end @@ -14,7 +16,7 @@ describe SIADE::EntrepriseAdapter do context 'Attributs Entreprises' do it 'L\'entreprise contient bien un siren' do - expect(subject[:siren]).to eq('418166096') + expect(subject[:siren]).to eq(siren) end it 'L\'entreprise contient bien un capital_social' do @@ -60,10 +62,14 @@ describe SIADE::EntrepriseAdapter do it 'L\'entreprise contient bien un prenom' do expect(subject[:prenom]).to eq('test_prenom') end + + it 'L\'entreprise contient bien les mandataires_sociaux' do + expect(subject[:mandataires_sociaux]).to be_an_instance_of(Array) + end end context 'Mandataire sociaux' do - subject { described_class.new('418166096').mandataires_sociaux } + subject { described_class.new(siren).to_params[:mandataires_sociaux] } it '#to_params class est une Hash ?' do expect(subject).to be_an_instance_of(Array) diff --git a/spec/lib/siade/etablissement_adapter_spec.rb b/spec/lib/siade/etablissement_adapter_spec.rb index 3e5092692..4a328aa03 100644 --- a/spec/lib/siade/etablissement_adapter_spec.rb +++ b/spec/lib/siade/etablissement_adapter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe SIADE::EtablissementAdapter do context 'SIRET valide' do - let(:siret) { 41_816_609_600_051 } + let(:siret) { '41816609600051' } subject { described_class.new(siret).to_params } before do @@ -16,7 +16,7 @@ describe SIADE::EtablissementAdapter do context 'Attributs Etablissements' do it 'L\'entreprise contient bien un siret' do - expect(subject[:siret]).to eq('41816609600051') + expect(subject[:siret]).to eq(siret) end it 'L\'entreprise contient bien un siege_social' do diff --git a/spec/lib/siade/exercices_adapter_spec.rb b/spec/lib/siade/exercices_adapter_spec.rb index eaab69563..10a244faf 100644 --- a/spec/lib/siade/exercices_adapter_spec.rb +++ b/spec/lib/siade/exercices_adapter_spec.rb @@ -23,7 +23,7 @@ describe SIADE::ExercicesAdapter do end it 'L\'exercice contient bien une date de fin d\'exercice' do - expect(subject[0][:dateFinExercice]).to eq("2013-12-31T00:00:00+01:00") + expect(subject[0][:date_fin_exercice]).to eq("2013-12-31T00:00:00+01:00") end it 'L\'exercice contient bien une date_fin_exercice_timestamp' do diff --git a/spec/lib/siade/rna_adapter_spec.rb b/spec/lib/siade/rna_adapter_spec.rb index 116b14761..dda9c8275 100644 --- a/spec/lib/siade/rna_adapter_spec.rb +++ b/spec/lib/siade/rna_adapter_spec.rb @@ -4,8 +4,9 @@ describe SIADE::RNAAdapter do let(:siret) { '50480511000013' } let(:body) { File.read('spec/support/files/rna.json') } let(:status) { 200 } + let(:adapter) { described_class.new(siret) } - subject { described_class.new(siret).to_params } + subject { adapter.to_params } before do stub_request(:get, /https:\/\/staging.entreprise.api.gouv.fr\/v2\/associations\/.*token=/) @@ -23,7 +24,7 @@ describe SIADE::RNAAdapter do it { expect(subject).to be_an_instance_of(Hash) } describe 'Attributs Associations' do - it { expect(subject[:association_id]).to eq('W595001988') } + it { expect(subject[:rna]).to eq('W595001988') } it { expect(subject[:titre]).to eq('UN SUR QUATRE') } diff --git a/spec/views/users/recapitulatif/show.html.haml_spec.rb b/spec/views/users/recapitulatif/show.html.haml_spec.rb index 27c86c652..0681bd895 100644 --- a/spec/views/users/recapitulatif/show.html.haml_spec.rb +++ b/spec/views/users/recapitulatif/show.html.haml_spec.rb @@ -30,8 +30,8 @@ describe 'users/recapitulatif/show.html.haml', type: :view do expect(rendered).to have_css('#maj_infos') end - it 'le lien vers description est correct' do - expect(rendered).to have_selector("a[id=maj_infos][href='/users/dossiers/#{dossier_id}/description']") + it 'le lien vers l édition est correct' do + expect(rendered).to have_selector("a[id=maj_infos][href='/dossiers/#{dossier_id}/modifier']") end end