diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml new file mode 100644 index 000000000..e1d5050d3 --- /dev/null +++ b/.github/workflows/sentry-release.yml @@ -0,0 +1,18 @@ +on: + push: + tags: + - '*' +name: Publish release on Sentry +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Sentry Release + uses: getsentry/action-release@v1.0.0 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: rails + with: + environment: production diff --git a/Gemfile b/Gemfile index af5ddcae9..c0c5e4e46 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ gem 'delayed_job_active_record' gem 'delayed_job_web' gem 'devise' # Gestion des comptes utilisateurs gem 'devise-async' +gem 'devise-i18n' gem 'discard' gem 'dotenv-rails', require: 'dotenv/rails-now' # dotenv should always be loaded before rails gem 'ffi-geos', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 960e5a887..754c8ec6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -205,6 +205,8 @@ GEM devise-async (1.0.0) activejob (>= 5.0) devise (>= 4.0) + devise-i18n (1.9.2) + devise (>= 4.7.1) diff-lcs (1.3) discard (1.2.0) activerecord (>= 4.2, < 7) @@ -787,6 +789,7 @@ DEPENDENCIES delayed_job_web devise devise-async + devise-i18n discard dotenv-rails factory_bot diff --git a/app/assets/stylesheets/new_design/actiontext.scss b/app/assets/stylesheets/new_design/actiontext.scss index b4560ecae..114c6aa76 100644 --- a/app/assets/stylesheets/new_design/actiontext.scss +++ b/app/assets/stylesheets/new_design/actiontext.scss @@ -5,32 +5,11 @@ // // = require trix -// We need to override trix.css’s image gallery styles to accommodate the -// element we wrap around attachments. Otherwise, -// images in galleries will be squished by the max-width: 33%; rule. -.trix-content { - .attachment-gallery { - > action-text-attachment, - > .attachment { - flex: 1 0 33%; - padding: 0 0.5em; - max-width: 33%; - } - - &.attachment-gallery--2, - &.attachment-gallery--4 { - > action-text-attachment, - > .attachment { - flex-basis: 50%; - max-width: 50%; - } - } - } - - action-text-attachment { - .attachment { - padding: 0 !important; - max-width: 100% !important; - } - } +.trix-button-group.trix-button-group--file-tools { + display: none; +} + +trix-editor { + min-height: 10em; + background-color: #FFFFFF; } diff --git a/app/assets/stylesheets/new_design/admin-procedures-list.scss b/app/assets/stylesheets/new_design/admin-procedures-list.scss new file mode 100644 index 000000000..ece3f2726 --- /dev/null +++ b/app/assets/stylesheets/new_design/admin-procedures-list.scss @@ -0,0 +1,11 @@ +// Push the timestamps column to the right of the row +.admin-procedures-list-timestamps { + margin-left: auto; +} + +// Fix a Safari flexbox bug where the inner procedure logo +// would stretch the container vertically. +// See https://stackoverflow.com/questions/57516373/image-stretching-in-flexbox-in-safari +.admin-procedures-list-row.infos { + align-items: flex-start; +} diff --git a/app/controllers/admin/mail_templates_controller.rb b/app/controllers/admin/mail_templates_controller.rb deleted file mode 100644 index 3ed7f277d..000000000 --- a/app/controllers/admin/mail_templates_controller.rb +++ /dev/null @@ -1,42 +0,0 @@ -class Admin::MailTemplatesController < AdminController - before_action :retrieve_procedure - - def index - @mail_templates = mail_templates - end - - def edit - @mail_template = find_mail_template_by_slug(params[:id]) - end - - def update - mail_template = find_mail_template_by_slug(params[:id]) - mail_template.update(update_params) - flash.notice = "Email mis à jour" - redirect_to edit_admin_procedure_mail_template_path(mail_template.procedure_id, params[:id]) - end - - private - - def mail_templates - [ - @procedure.initiated_mail_template, - @procedure.received_mail_template, - @procedure.closed_mail_template, - @procedure.refused_mail_template, - @procedure.without_continuation_mail_template - ] - end - - def find_mail_template_by_slug(slug) - mail_templates.find { |template| template.class.const_get(:SLUG) == slug } - end - - def update_params - { - procedure_id: params[:procedure_id], - subject: params[:mail_template][:subject], - body: params[:mail_template][:body] - } - end -end diff --git a/app/controllers/france_connect/particulier_controller.rb b/app/controllers/france_connect/particulier_controller.rb index c841ae8bf..e3ef69241 100644 --- a/app/controllers/france_connect/particulier_controller.rb +++ b/app/controllers/france_connect/particulier_controller.rb @@ -2,7 +2,11 @@ class FranceConnect::ParticulierController < ApplicationController before_action :redirect_to_login_if_fc_aborted, only: [:callback] def login - redirect_to FranceConnectService.authorization_uri + if FranceConnectService.enabled? + redirect_to FranceConnectService.authorization_uri + else + redirect_to new_user_session_path + end end def callback diff --git a/app/controllers/new_administrateur/mail_templates_controller.rb b/app/controllers/new_administrateur/mail_templates_controller.rb index 05089d6f2..872f76ae5 100644 --- a/app/controllers/new_administrateur/mail_templates_controller.rb +++ b/app/controllers/new_administrateur/mail_templates_controller.rb @@ -6,6 +6,24 @@ module NewAdministrateur @mail_templates = mail_templates end + def edit + @procedure = procedure + @mail_template = find_mail_template_by_slug(params[:id]) + end + + def update + @procedure = procedure + mail_template = find_mail_template_by_slug(params[:id]) + + if mail_template.update(update_params) + flash.notice = "Email mis à jour" + else + flash.alert = mail_template.errors.full_messages + end + + redirect_to edit_admin_procedure_mail_template_path(mail_template.procedure_id, params[:id]) + end + def preview mail_template = find_mail_template_by_slug(params[:id]) dossier = Dossier.new(id: '1', procedure: procedure) @@ -13,7 +31,7 @@ module NewAdministrateur @dossier = dossier @logo_url = procedure.logo_url @service = procedure.service - @rendered_template = sanitize(mail_template.body) + @rendered_template = sanitize(mail_template.rich_body.body.to_html) @actions = mail_template.actions_for_dossier(dossier) render(template: 'notification_mailer/send_notification', layout: 'mailers/notifications_layout') @@ -38,5 +56,14 @@ module NewAdministrateur def find_mail_template_by_slug(slug) mail_templates.find { |template| template.class.const_get(:SLUG) == slug } end + + def update_params + mail_template_id = params[:id] + { + procedure_id: params[:procedure_id], + subject: params["mails_#{mail_template_id}"] ? params["mails_#{mail_template_id}"][:subject] : params["mails_#{mail_template_id}_mail"][:subject], + body: params["mails_#{mail_template_id}"] ? params["mails_#{mail_template_id}"][:rich_body] : params["mails_#{mail_template_id}_mail"][:rich_body] + } + end end end diff --git a/app/controllers/new_administrateur/procedures_controller.rb b/app/controllers/new_administrateur/procedures_controller.rb index 70e0bbf9f..61a031c20 100644 --- a/app/controllers/new_administrateur/procedures_controller.rb +++ b/app/controllers/new_administrateur/procedures_controller.rb @@ -66,6 +66,7 @@ module NewAdministrateur def create @procedure = Procedure.new(procedure_params.merge(administrateurs: [current_administrateur])) + @procedure.draft_revision = @procedure.revisions.build if !@procedure.save flash.now.alert = @procedure.errors.full_messages @@ -73,8 +74,6 @@ module NewAdministrateur else flash.notice = 'Démarche enregistrée.' current_administrateur.instructeur.assign_to_procedure(@procedure) - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(@procedure) redirect_to champs_admin_procedure_path(@procedure) end diff --git a/app/controllers/new_administrateur/types_de_champ_controller.rb b/app/controllers/new_administrateur/types_de_champ_controller.rb index c7c663acb..b88cb1642 100644 --- a/app/controllers/new_administrateur/types_de_champ_controller.rb +++ b/app/controllers/new_administrateur/types_de_champ_controller.rb @@ -2,7 +2,6 @@ module NewAdministrateur class TypesDeChampController < AdministrateurController before_action :retrieve_procedure, only: [:create, :update, :move, :destroy] before_action :procedure_locked?, only: [:create, :update, :move, :destroy] - before_action :revisions_migration def create type_de_champ = @procedure.draft_revision.add_type_de_champ(type_de_champ_create_params) @@ -16,7 +15,7 @@ module NewAdministrateur end def update - type_de_champ = @procedure.draft_revision.find_or_clone_type_de_champ(type_de_champ_stable_id) + type_de_champ = @procedure.draft_revision.find_or_clone_type_de_champ(TypeDeChamp.to_stable_id(params[:id])) if type_de_champ.update(type_de_champ_update_params) reset_procedure @@ -27,13 +26,13 @@ module NewAdministrateur end def move - @procedure.draft_revision.move_type_de_champ(type_de_champ_stable_id, (params[:position] || params[:order_place]).to_i) + @procedure.draft_revision.move_type_de_champ(TypeDeChamp.to_stable_id(params[:id]), (params[:position] || params[:order_place]).to_i) head :no_content end def destroy - @procedure.draft_revision.remove_type_de_champ(type_de_champ_stable_id) + @procedure.draft_revision.remove_type_de_champ(TypeDeChamp.to_stable_id(params[:id])) reset_procedure head :no_content @@ -41,19 +40,11 @@ module NewAdministrateur private - def type_de_champ_stable_id - TypeDeChamp.find(params[:id]).stable_id - end - - def revisions_migration - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(@procedure) - end - def serialize_type_de_champ(type_de_champ) { type_de_champ: type_de_champ.as_json( except: [ + :id, :created_at, :options, :order_place, @@ -73,7 +64,7 @@ module NewAdministrateur :piece_justificative_template_url, :quartiers_prioritaires ] - ) + ).merge(id: TypeDeChamp.format_stable_id(type_de_champ.stable_id)) } end @@ -92,7 +83,7 @@ module NewAdministrateur :type_champ) if type_de_champ_params[:parent_id].present? - type_de_champ_params[:parent_id] = TypeDeChamp.find(type_de_champ_params[:parent_id]).stable_id + type_de_champ_params[:parent_id] = TypeDeChamp.to_stable_id(type_de_champ_params[:parent_id]) end type_de_champ_params diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index d676b5b40..5051f4153 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -313,7 +313,8 @@ class StatsController < ApplicationController procedure_id_type_de_champs_count = TypeDeChamp .where(private: false) - .group(:procedure_id) + .joins(:revision) + .group('procedure_revisions.procedure_id') .count groupe_instructeur_id_type_de_champs_count = groupe_instructeurs.reduce({}) do |acc, (gi_id, procedure_id)| diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 47536f28c..b7ba17dfe 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -259,8 +259,6 @@ module Users return redirect_to url_for dossiers_path end - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(procedure) dossier = Dossier.new( revision: procedure.active_revision, groupe_instructeur: procedure.defaut_groupe_instructeur, diff --git a/app/dashboards/procedure_dashboard.rb b/app/dashboards/procedure_dashboard.rb index 076df9cb3..aaf0717db 100644 --- a/app/dashboards/procedure_dashboard.rb +++ b/app/dashboards/procedure_dashboard.rb @@ -8,8 +8,8 @@ class ProcedureDashboard < Administrate::BaseDashboard # which determines how the attribute is displayed # on pages throughout the dashboard. ATTRIBUTE_TYPES = { - types_de_champ: TypesDeChampCollectionField, - types_de_champ_private: TypesDeChampCollectionField, + published_types_de_champ: TypesDeChampCollectionField, + published_types_de_champ_private: TypesDeChampCollectionField, path: ProcedureLinkField, dossiers: Field::HasMany, administrateurs: Field::HasMany, @@ -70,8 +70,8 @@ class ProcedureDashboard < Administrate::BaseDashboard :whitelisted_at, :hidden_at, :closed_at, - :types_de_champ, - :types_de_champ_private, + :published_types_de_champ, + :published_types_de_champ_private, :for_individual, :auto_archive_on, :initiated_mail_template, diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cc6adef9e..443d7c166 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -140,7 +140,7 @@ module ApplicationHelper end def try_format_date(date) - date.present? ? I18n.l(date) : '' + date.present? ? I18n.l(date, format: :long) : '' end def try_format_datetime(datetime) diff --git a/app/javascript/components/MapEditor/index.js b/app/javascript/components/MapEditor/index.js index 3bb828e29..c8bb44555 100644 --- a/app/javascript/components/MapEditor/index.js +++ b/app/javascript/components/MapEditor/index.js @@ -40,6 +40,19 @@ function MapEditor({ featureCollection, url, preview, hasCadastres, ign }) { hasCadastres ]); + const translations = [ + ['.mapbox-gl-draw_line', 'Tracer une ligne'], + ['.mapbox-gl-draw_polygon', 'Dessiner un polygone'], + ['.mapbox-gl-draw_point', 'Ajouter un point'], + ['.mapbox-gl-draw_trash', 'Supprimer'] + ]; + for (const [selector, translation] of translations) { + const element = document.querySelector(selector); + if (element) { + element.setAttribute('title', translation); + } + } + const onFeatureFocus = useCallback( ({ detail }) => { const { id } = detail; diff --git a/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js b/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js index 3787639e5..6d68ffbe1 100644 --- a/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js +++ b/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js @@ -210,7 +210,6 @@ export const FIELDS = [ 'drop_down_list_value', 'libelle', 'mandatory', - 'order_place', 'parcelles_agricoles', 'parent_id', 'piece_justificative_template', diff --git a/app/javascript/components/TypesDeChampEditor/operations.js b/app/javascript/components/TypesDeChampEditor/operations.js index c88215807..a2a04116c 100644 --- a/app/javascript/components/TypesDeChampEditor/operations.js +++ b/app/javascript/components/TypesDeChampEditor/operations.js @@ -22,7 +22,7 @@ export function moveTypeDeChampOperation(typeDeChamp, index, queue) { return queue.enqueue({ path: `/${typeDeChamp.id}/move`, method: 'patch', - payload: { order_place: index } + payload: { position: index } }); } diff --git a/app/javascript/components/TypesDeChampEditor/typeDeChampsReducer.js b/app/javascript/components/TypesDeChampEditor/typeDeChampsReducer.js index f594ecf67..35eb2d83a 100644 --- a/app/javascript/components/TypesDeChampEditor/typeDeChampsReducer.js +++ b/app/javascript/components/TypesDeChampEditor/typeDeChampsReducer.js @@ -37,8 +37,7 @@ export default function typeDeChampsReducer(state, { type, params, done }) { function addTypeDeChamp(state, typeDeChamps, insertAfter, done) { const typeDeChamp = { - ...state.defaultTypeDeChampAttributes, - order_place: typeDeChamps.length + ...state.defaultTypeDeChampAttributes }; createTypeDeChampOperation(typeDeChamp, state.queue) diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 70897efcc..3183dcf79 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,6 +1,7 @@ import '../shared/polyfills'; import Rails from '@rails/ujs'; import * as ActiveStorage from '@rails/activestorage'; +import 'trix'; import '@rails/actiontext'; import 'whatwg-fetch'; // window.fetch polyfill import ReactRailsUJS from 'react_ujs'; diff --git a/app/javascript/shared/activestorage/progress-bar.js b/app/javascript/shared/activestorage/progress-bar.js index 14efed061..fbc3797a9 100644 --- a/app/javascript/shared/activestorage/progress-bar.js +++ b/app/javascript/shared/activestorage/progress-bar.js @@ -7,6 +7,12 @@ const COMPLETE_CLASS = 'direct-upload--complete'; rendering upload progress bar. It is used to handle direct-upload form ujs events but also in the Uploader delegate used with uploads on json api. + + As the associated DOM element may disappear for some + reason (a dynamic React list, an element being removed + and recreated again later, etc.), this class doesn't + raise any error if the associated DOM element cannot + be found. */ export default class ProgressBar { static init(input, id, file) { @@ -17,27 +23,31 @@ export default class ProgressBar { static start(id) { const element = getDirectUploadElement(id); - - element.classList.remove(PENDING_CLASS); + if (element) { + element.classList.remove(PENDING_CLASS); + } } static progress(id, progress) { const element = getDirectUploadProgressElement(id); - - element.style.width = `${progress}%`; + if (element) { + element.style.width = `${progress}%`; + } } static error(id, error) { const element = getDirectUploadElement(id); - - element.classList.add(ERROR_CLASS); - element.setAttribute('title', error); + if (element) { + element.classList.add(ERROR_CLASS); + element.setAttribute('title', error); + } } static end(id) { const element = getDirectUploadElement(id); - - element.classList.add(COMPLETE_CLASS); + if (element) { + element.classList.add(COMPLETE_CLASS); + } } static render(id, filename) { diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index 2dfbc497c..856d1d570 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,6 +1,8 @@ class ApplicationJob < ActiveJob::Base DEFAULT_MAX_ATTEMPTS_JOBS = 25 + retry_on Excon::Error::BadRequest + before_perform do |job| Rails.logger.info("#{job.class.name} started at #{Time.zone.now}") end diff --git a/app/jobs/find_dubious_procedures_job.rb b/app/jobs/find_dubious_procedures_job.rb index 8f399f672..13c9a91ab 100644 --- a/app/jobs/find_dubious_procedures_job.rb +++ b/app/jobs/find_dubious_procedures_job.rb @@ -24,7 +24,7 @@ class FindDubiousProceduresJob < CronJob .where(procedures: { closed_at: nil, whitelisted_at: nil }) dubious_procedures_and_tdcs = forbidden_tdcs - .group_by(&:procedure_id) + .group_by { |type_de_champ| type_de_champ.procedure.id } .map { |_procedure_id, tdcs| [tdcs[0].procedure, tdcs] } AdministrationMailer.dubious_procedures(dubious_procedures_and_tdcs).deliver_later diff --git a/app/jobs/tmp_dossiers_migrate_revisions_job.rb b/app/jobs/tmp_dossiers_migrate_revisions_job.rb deleted file mode 100644 index 1e0669217..000000000 --- a/app/jobs/tmp_dossiers_migrate_revisions_job.rb +++ /dev/null @@ -1,21 +0,0 @@ -class TmpDossiersMigrateRevisionsJob < ApplicationJob - def perform(except) - dossiers = Dossier.with_discarded.where(revision_id: nil) - - dossiers.where - .not(id: except) - .includes(procedure: [:draft_revision, :published_revision]) - .limit(2000) - .find_each do |dossier| - if dossier.procedure.present? - dossier.update_column(:revision_id, dossier.procedure.active_revision.id) - else - except << dossier.id - end - end - - if dossiers.where.not(id: except).exists? - TmpDossiersMigrateRevisionsJob.perform_later(except) - end - end -end diff --git a/app/jobs/virus_scanner_job.rb b/app/jobs/virus_scanner_job.rb index 9cf005e00..29e6f1db6 100644 --- a/app/jobs/virus_scanner_job.rb +++ b/app/jobs/virus_scanner_job.rb @@ -1,7 +1,10 @@ class VirusScannerJob < ApplicationJob queue_as :active_storage_analysis + # If by the time the job runs the blob has been deleted, ignore the error discard_on ActiveRecord::RecordNotFound + # If the file is deleted during the scan, ignore the error + discard_on ActiveStorage::FileNotFoundError def perform(blob) metadata = extract_metadata_via_virus_scanner(blob) diff --git a/app/models/champ.rb b/app/models/champ.rb index f6861b498..b4231596c 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -47,6 +47,9 @@ class Champ < ApplicationRecord scope :public_only, -> { where(private: false) } scope :private_only, -> { where(private: true) } scope :ordered, -> { includes(:type_de_champ).order(:row, 'types_de_champ.order_place') } + scope :public_ordered, -> { public_only.joins(dossier: { revision: :revision_types_de_champ }).where('procedure_revision_types_de_champ.type_de_champ_id = champs.type_de_champ_id').order(:position) } + scope :private_ordered, -> { private_only.joins(dossier: { revision: :revision_types_de_champ_private }).where('procedure_revision_types_de_champ.type_de_champ_id = champs.type_de_champ_id').order(:position) } + scope :root, -> { where(parent_id: nil) } before_validation :set_dossier_id, if: :needs_dossier_id? diff --git a/app/models/concerns/mail_template_concern.rb b/app/models/concerns/mail_template_concern.rb index f720c8297..d4368957e 100644 --- a/app/models/concerns/mail_template_concern.rb +++ b/app/models/concerns/mail_template_concern.rb @@ -33,8 +33,9 @@ module MailTemplateConcern module ClassMethods def default_for_procedure(procedure) template_name = default_template_name_for_procedure(procedure) - body = ActionController::Base.new.render_to_string(template: template_name) - new(subject: const_get(:DEFAULT_SUBJECT), body: body, procedure: procedure) + rich_body = ActionController::Base.new.render_to_string(template: template_name) + trix_rich_body = rich_body.gsub(/(?)\n/, '') + new(subject: const_get(:DEFAULT_SUBJECT), rich_body: trix_rich_body, procedure: procedure) end def default_template_name_for_procedure(procedure) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 51237c304..1cca19ee4 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -62,8 +62,8 @@ class Dossier < ApplicationRecord has_one_attached :justificatif_motivation - has_many :champs, -> { root.public_only.ordered }, inverse_of: :dossier, dependent: :destroy - has_many :champs_private, -> { root.private_only.ordered }, class_name: 'Champ', inverse_of: :dossier, dependent: :destroy + has_many :champs, -> { root.public_ordered }, inverse_of: :dossier, dependent: :destroy + has_many :champs_private, -> { root.private_ordered }, class_name: 'Champ', inverse_of: :dossier, dependent: :destroy has_many :commentaires, inverse_of: :dossier, dependent: :destroy has_many :invites, dependent: :destroy has_many :follows, -> { active }, inverse_of: :dossier @@ -76,10 +76,13 @@ class Dossier < ApplicationRecord has_many :dossier_operation_logs, -> { order(:created_at) }, dependent: :nullify, inverse_of: :dossier belongs_to :groupe_instructeur, optional: false - has_one :procedure, through: :groupe_instructeur - belongs_to :revision, class_name: 'ProcedureRevision', optional: true + belongs_to :revision, class_name: 'ProcedureRevision', optional: false belongs_to :user, optional: false + has_one :procedure, through: :revision + has_many :types_de_champ, through: :revision + has_many :types_de_champ_private, through: :revision + accepts_nested_attributes_for :champs accepts_nested_attributes_for :champs_private @@ -315,7 +318,6 @@ class Dossier < ApplicationRecord accepts_nested_attributes_for :individual delegate :siret, :siren, to: :etablissement, allow_nil: true - delegate :types_de_champ, to: :procedure delegate :france_connect_information, to: :user before_save :build_default_champs, if: Proc.new { groupe_instructeur_id_was.nil? } @@ -326,7 +328,7 @@ class Dossier < ApplicationRecord after_create :send_draft_notification_email validates :user, presence: true - validates :individual, presence: true, if: -> { procedure.for_individual? } + validates :individual, presence: true, if: -> { revision.procedure.for_individual? } validates :groupe_instructeur, presence: true def motivation @@ -351,10 +353,10 @@ class Dossier < ApplicationRecord end def build_default_champs - procedure.build_champs.each do |champ| + revision.build_champs.each do |champ| champs << champ end - procedure.build_champs_private.each do |champ| + revision.build_champs_private.each do |champ| champs_private << champ end end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 2433679c5..83a3d54e1 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -56,13 +56,16 @@ class Procedure < ApplicationRecord MAX_DUREE_CONSERVATION = 36 MAX_DUREE_CONSERVATION_EXPORT = 3.hours - has_many :types_de_champ, -> { root.public_only.ordered }, inverse_of: :procedure, dependent: :destroy - has_many :types_de_champ_private, -> { root.private_only.ordered }, class_name: 'TypeDeChamp', inverse_of: :procedure, dependent: :destroy has_many :revisions, -> { order(:id) }, class_name: 'ProcedureRevision', inverse_of: :procedure, dependent: :destroy - belongs_to :draft_revision, class_name: 'ProcedureRevision', optional: true + belongs_to :draft_revision, class_name: 'ProcedureRevision', optional: false belongs_to :published_revision, class_name: 'ProcedureRevision', optional: true has_many :deleted_dossiers, dependent: :destroy + has_many :published_types_de_champ, through: :published_revision, source: :types_de_champ + has_many :published_types_de_champ_private, through: :published_revision, source: :types_de_champ_private + has_many :draft_types_de_champ, through: :draft_revision, source: :types_de_champ + has_many :draft_types_de_champ_private, through: :draft_revision, source: :types_de_champ_private + has_one :module_api_carto, dependent: :destroy has_one :attestation_template, dependent: :destroy @@ -74,6 +77,14 @@ class Procedure < ApplicationRecord brouillon? ? draft_revision : published_revision end + def types_de_champ + brouillon? ? draft_types_de_champ : published_types_de_champ + end + + def types_de_champ_private + brouillon? ? draft_types_de_champ_private : published_types_de_champ_private + end + has_many :administrateurs_procedures has_many :administrateurs, through: :administrateurs_procedures, after_remove: -> (procedure, _admin) { procedure.validate! } has_many :groupe_instructeurs, dependent: :destroy @@ -93,9 +104,6 @@ class Procedure < ApplicationRecord has_one_attached :notice has_one_attached :deliberation - accepts_nested_attributes_for :types_de_champ, reject_if: proc { |attributes| attributes['libelle'].blank? }, allow_destroy: true - accepts_nested_attributes_for :types_de_champ_private, reject_if: proc { |attributes| attributes['libelle'].blank? }, allow_destroy: true - scope :brouillons, -> { where(aasm_state: :brouillon) } scope :publiees, -> { where(aasm_state: :publiee) } scope :closes, -> { where(aasm_state: [:close, :depubliee]) } @@ -114,9 +122,15 @@ class Procedure < ApplicationRecord scope :for_api, -> { includes( :administrateurs, - :types_de_champ_private, - :types_de_champ, - :module_api_carto + :module_api_carto, + published_revision: [ + :types_de_champ_private, + :types_de_champ + ], + draft_revision: [ + :types_de_champ_private, + :types_de_champ + ] ) } @@ -289,22 +303,13 @@ class Procedure < ApplicationRecord # to save a dossier created from this method def new_dossier Dossier.new( - procedure: self, revision: active_revision, - champs: build_champs, - champs_private: build_champs_private, + champs: active_revision.build_champs, + champs_private: active_revision.build_champs_private, groupe_instructeur: defaut_groupe_instructeur ) end - def build_champs - types_de_champ.map(&:build_champ) - end - - def build_champs_private - types_de_champ_private.map(&:build_champ) - end - def path_customized? !path.match?(/[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}/) end @@ -318,9 +323,6 @@ class Procedure < ApplicationRecord end def clone(admin, from_library) - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(self) - is_different_admin = !admin.owns?(self) populate_champ_stable_ids @@ -371,19 +373,11 @@ class Procedure < ApplicationRecord end procedure.save - procedure.draft_revision.types_de_champ.update_all(revision_id: procedure.draft_revision.id) - procedure.draft_revision.types_de_champ_private.update_all(revision_id: procedure.draft_revision.id) - - # FIXUP: needed during transition to revisions - procedure.draft_revision.types_de_champ.each do |type_de_champ| - procedure.types_de_champ << type_de_champ - end - procedure.draft_revision.types_de_champ_private.each do |type_de_champ| - procedure.types_de_champ_private << type_de_champ - end + procedure.draft_types_de_champ.update_all(revision_id: procedure.draft_revision.id) + procedure.draft_types_de_champ_private.update_all(revision_id: procedure.draft_revision.id) if is_different_admin || from_library - procedure.types_de_champ.each { |tdc| tdc.options&.delete(:old_pj) } + procedure.draft_types_de_champ.each { |tdc| tdc.options&.delete(:old_pj) } end procedure @@ -480,7 +474,7 @@ class Procedure < ApplicationRecord def closed_mail_template_attestation_inconsistency_state # As an optimization, don’t check the predefined templates (they are presumed correct) if closed_mail.present? - tag_present = closed_mail.body.include?("--lien attestation--") + tag_present = closed_mail.body.to_s.include?("--lien attestation--") if attestation_template&.activated? && !tag_present :missing_tag elsif !attestation_template&.activated? && tag_present @@ -608,23 +602,14 @@ class Procedure < ApplicationRecord end def after_publish(canonical_procedure = nil) - # FIXUP: needed during transition to revisions - if RevisionsMigration.add_revisions(self) - update!(published_at: Time.zone.now, canonical_procedure: canonical_procedure) - else - update!(published_at: Time.zone.now, canonical_procedure: canonical_procedure, draft_revision: create_new_revision, published_revision: draft_revision) - end + update!(published_at: Time.zone.now, canonical_procedure: canonical_procedure, draft_revision: create_new_revision, published_revision: draft_revision) end def after_close - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(self) update!(closed_at: Time.zone.now) end def after_unpublish - # FIXUP: needed during transition to revisions - RevisionsMigration.add_revisions(self) update!(unpublished_at: Time.zone.now) end diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index a86d0fdb1..b97564455 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -9,15 +9,22 @@ # class ProcedureRevision < ApplicationRecord self.implicit_order_column = :created_at - belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions + belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions, optional: false has_many :revision_types_de_champ, -> { public_only.ordered }, class_name: 'ProcedureRevisionTypeDeChamp', foreign_key: :revision_id, dependent: :destroy, inverse_of: :revision has_many :revision_types_de_champ_private, -> { private_only.ordered }, class_name: 'ProcedureRevisionTypeDeChamp', foreign_key: :revision_id, dependent: :destroy, inverse_of: :revision has_many :types_de_champ, through: :revision_types_de_champ, source: :type_de_champ has_many :types_de_champ_private, through: :revision_types_de_champ_private, source: :type_de_champ + def build_champs + types_de_champ.map(&:build_champ) + end + + def build_champs_private + types_de_champ_private.map(&:build_champ) + end + def add_type_de_champ(params) - params[:procedure] = procedure params[:revision] = self if params[:parent_id] @@ -27,15 +34,9 @@ class ProcedureRevision < ApplicationRecord params[:order_place] = types_de_champ.present? ? types_de_champ.last.order_place + 1 : 0 end.create(params) elsif params[:private] - types_de_champ_private.tap do |types_de_champ| - # FIXUP: needed during transition to revisions - params[:order_place] = types_de_champ.present? ? types_de_champ.last.order_place + 1 : 0 - end.create(params) + types_de_champ_private.create(params) else - types_de_champ.tap do |types_de_champ| - # FIXUP: needed during transition to revisions - params[:order_place] = types_de_champ.present? ? types_de_champ.last.order_place + 1 : 0 - end.create(params) + types_de_champ.create(params) end end @@ -112,8 +113,6 @@ class ProcedureRevision < ApplicationRecord if types_de_champ.delete_at(old_index) types_de_champ.insert(new_index, type_de_champ) .map.with_index do |type_de_champ, index| - # FIXUP: needed during transition to revisions - type_de_champ.update!(order_place: index) [type_de_champ.id, index] end else diff --git a/app/models/procedure_revision_type_de_champ.rb b/app/models/procedure_revision_type_de_champ.rb index e4e243ec5..6859d1884 100644 --- a/app/models/procedure_revision_type_de_champ.rb +++ b/app/models/procedure_revision_type_de_champ.rb @@ -26,15 +26,11 @@ class ProcedureRevisionTypeDeChamp < ApplicationRecord private def set_position - self.position ||= if private? - if revision.types_de_champ_private.present? - revision.revision_types_de_champ_private.filter(&:persisted?).last.position + 1 - else - 0 - end - else - if revision.types_de_champ.present? - revision.revision_types_de_champ.filter(&:persisted?).last.position + 1 + self.position ||= begin + types_de_champ = (private? ? revision.revision_types_de_champ_private : revision.revision_types_de_champ).filter(&:persisted?) + + if types_de_champ.present? + types_de_champ.last.position + 1 else 0 end diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 68a031009..e0ff6990d 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -18,6 +18,8 @@ # stable_id :bigint # class TypeDeChamp < ApplicationRecord + self.ignored_columns = ['procedure_id'] + enum type_champs: { text: 'text', textarea: 'textarea', @@ -49,8 +51,8 @@ class TypeDeChamp < ApplicationRecord repetition: 'repetition' } - belongs_to :procedure, optional: false belongs_to :revision, class_name: 'ProcedureRevision', optional: true + has_one :procedure, through: :revision belongs_to :parent, class_name: 'TypeDeChamp', optional: true has_many :types_de_champ, -> { ordered }, foreign_key: :parent_id, class_name: 'TypeDeChamp', inverse_of: :parent, dependent: :destroy @@ -73,7 +75,6 @@ class TypeDeChamp < ApplicationRecord serialize :options, WithIndifferentAccess after_initialize :set_dynamic_type - before_validation :setup_procedure after_create :populate_stable_id attr_reader :dynamic_type @@ -298,9 +299,34 @@ class TypeDeChamp < ApplicationRecord .merge(include: { types_de_champ: TYPES_DE_CHAMP_BASE }) def self.as_json_for_editor - includes(piece_justificative_template_attachment: :blob, - types_de_champ: [piece_justificative_template_attachment: :blob]) - .as_json(TYPES_DE_CHAMP) + includes(piece_justificative_template_attachment: :blob, types_de_champ: [piece_justificative_template_attachment: :blob]).as_json(TYPES_DE_CHAMP) + end + + def read_attribute_for_serialization(name) + if name == 'id' + self.class.format_stable_id(stable_id) + else + super + end + end + + # FIXME: We are changing how id is exposed to the editor. + # We used to expose type_de_champ.id as primary key to the editor. With revisions + # we need primary key to be type_de_champ.stable_id because any update can create + # a new version but we do not want editor to know about this. + # This is only needed for a clean migration without downtime. We want to ensure + # that if editor send a simple id because it was loaded before deployment + # we would still do the right thing. + def self.format_stable_id(stable_id) + "stable:#{stable_id}" + end + + def self.to_stable_id(id_or_stable_id) + if id_or_stable_id.to_s =~ /^stable:/ + id_or_stable_id.to_s.gsub(/^stable:/, '') + else + find(id_or_stable_id).stable_id + end end private @@ -311,12 +337,6 @@ class TypeDeChamp < ApplicationRecord result.blank? ? [] : [''] + result end - def setup_procedure - types_de_champ.each do |type_de_champ| - type_de_champ.procedure = procedure - end - end - def populate_stable_id if !stable_id update_column(:stable_id, id) diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index d4b4a90d6..9fdebb9f0 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -67,7 +67,7 @@ class DossierSerializer < ActiveModel::Serializer end def types_de_piece_justificative - PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object) + PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object.revision) end def email diff --git a/app/serializers/procedure_serializer.rb b/app/serializers/procedure_serializer.rb index 6dd3a0df0..ef0eb2b35 100644 --- a/app/serializers/procedure_serializer.rb +++ b/app/serializers/procedure_serializer.rb @@ -48,6 +48,6 @@ class ProcedureSerializer < ActiveModel::Serializer end def types_de_piece_justificative - PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object) + PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object.active_revision) end end diff --git a/app/services/france_connect_service.rb b/app/services/france_connect_service.rb index 4249c4710..70e8785b8 100644 --- a/app/services/france_connect_service.rb +++ b/app/services/france_connect_service.rb @@ -1,4 +1,8 @@ class FranceConnectService + def self.enabled? + ENV.fetch("FRANCE_CONNECT_ENABLED", "enabled") == "enabled" + end + def self.authorization_uri client = FranceConnectParticulierClient.new diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 7b4632b09..2c4e6fe80 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -12,8 +12,8 @@ class PiecesJustificativesService .sum(&:byte_size) end - def self.serialize_types_de_champ_as_type_pj(procedure) - tdcs = procedure.types_de_champ.filter { |type_champ| type_champ.old_pj.present? } + def self.serialize_types_de_champ_as_type_pj(revision) + tdcs = revision.types_de_champ.filter { |type_champ| type_champ.old_pj.present? } tdcs.map.with_index do |type_champ, order_place| description = type_champ.description if /^(?.*?)(?:[\r\n]+)Récupérer le formulaire vierge pour mon dossier : (?http.*)$/m =~ description diff --git a/app/services/revisions_migration.rb b/app/services/revisions_migration.rb deleted file mode 100644 index df8f4c5b1..000000000 --- a/app/services/revisions_migration.rb +++ /dev/null @@ -1,36 +0,0 @@ -class RevisionsMigration - def self.add_revisions(procedure) - if procedure.draft_revision.present? - return false - end - - procedure.draft_revision = procedure.revisions.create - procedure.save!(validate: false) - - add_types_de_champs_to_revision(procedure, :types_de_champ) - add_types_de_champs_to_revision(procedure, :types_de_champ_private) - - if !procedure.brouillon? - published_revision = procedure.draft_revision - - procedure.draft_revision = procedure.create_new_revision - procedure.published_revision = published_revision - procedure.save!(validate: false) - end - - true - end - - def self.add_types_de_champs_to_revision(procedure, types_de_champ_scope) - types_de_champ = procedure.send(types_de_champ_scope) - types_de_champ.where(revision_id: nil).update_all(revision_id: procedure.draft_revision.id) - - types_de_champ.each.with_index do |type_de_champ, index| - type_de_champ.types_de_champ.where(revision_id: nil).update_all(revision_id: procedure.draft_revision.id) - procedure.draft_revision.send(:"revision_#{types_de_champ_scope}").create!( - type_de_champ: type_de_champ, - position: index - ) - end - end -end diff --git a/app/views/admin/mail_templates/edit.html.haml b/app/views/admin/mail_templates/edit.html.haml deleted file mode 100644 index a69a6a0f9..000000000 --- a/app/views/admin/mail_templates/edit.html.haml +++ /dev/null @@ -1,40 +0,0 @@ -- if params[:id] == 'closed_mail' - = render partial: 'admin/closed_mail_template_attestation_inconsistency_alert' -.white-back - %h3 - = @mail_template.class.const_get(:DISPLAYED_NAME) - - = form_for @mail_template, - as: 'mail_template', - url: admin_procedure_mail_template_path(@procedure, @mail_template.class.const_get(:SLUG)), - method: :put do |f| - .row - .col-md-6 - .form-group.string.optional.mail_template_subject - = f.label :subject, "Objet de l'email", class: 'control-label string optional' - = f.text_field :subject, class: 'form-control string optional' - - .form-group.text.optional.mail_template_body - = f.label :body, "Corps de l'email", class: 'control-label string optional' - = f.text_area :body, class: 'wysihtml5 form-control text optional' - - .text-right - = link_to "Annuler", admin_procedure_mail_templates_path(@procedure), class: "btn btn-default" - = f.submit 'Mettre à jour', class: "btn btn-default btn-success" - = link_to "Prévisualiser", preview_admin_procedure_mail_template_path(@procedure, @mail_template.class.const_get(:SLUG)), class: "btn btn-primary", target: "_blank" - - .row - .col-md-12 - %table.table - %tr - %th.col-md-3 - Balise - %th - Description - - @mail_template.tags.each do |tag| - %tr - %td - %code{ style: "white-space: pre-wrap;" } - = "--#{tag[:libelle]}--" - %td - = tag[:description] diff --git a/app/views/admin/mail_templates/index.html.haml b/app/views/admin/mail_templates/index.html.haml deleted file mode 100644 index 4deb55b4b..000000000 --- a/app/views/admin/mail_templates/index.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -= render partial: 'admin/closed_mail_template_attestation_inconsistency_alert' -#custom-mails - .wrapper - %h1 E-mails personnalisables - %table.table - %tr - %th{ colspan: 2 } - Type d'email - - @mail_templates.each do |mail_template| - %tr - %td - = mail_template.class.const_get(:DISPLAYED_NAME) - %td.text-right - = link_to "Personnaliser l'e-mail", edit_admin_procedure_mail_template_path(@procedure, mail_template.class.const_get(:SLUG)) diff --git a/app/views/layouts/mailers/_signature.html.haml b/app/views/layouts/mailers/_signature.html.haml index 82d83f7b1..985288c7b 100644 --- a/app/views/layouts/mailers/_signature.html.haml +++ b/app/views/layouts/mailers/_signature.html.haml @@ -4,5 +4,5 @@ - if defined?(service) && service && service.nom.present? = service.nom - else - -# The WORD JOINER unicode entity prevents email clients from auto-linking the signature - L’équipe demarches-simplifiees⁠.fr + -# The WORD JOINER unicode entity (⁠) prevents email clients from auto-linking the signature + L’équipe #{APPLICATION_NAME.gsub(".","⁠.").html_safe} diff --git a/app/views/new_administrateur/mail_templates/_apercu.html.haml b/app/views/new_administrateur/mail_templates/_apercu.html.haml new file mode 100644 index 000000000..c890c7399 --- /dev/null +++ b/app/views/new_administrateur/mail_templates/_apercu.html.haml @@ -0,0 +1 @@ +%iframe{ src: preview_admin_procedure_mail_template_path, width: '100%', height: '650px' } diff --git a/app/views/new_administrateur/mail_templates/_form.html.haml b/app/views/new_administrateur/mail_templates/_form.html.haml new file mode 100644 index 000000000..ee7d66864 --- /dev/null +++ b/app/views/new_administrateur/mail_templates/_form.html.haml @@ -0,0 +1,47 @@ + += f.label :subject do + Objet de l'email + %span.mandatory * += f.text_field :subject, required: true + += f.label :body do + Corps de l'email + %span.mandatory * += f.rich_text_area :rich_body, required: true, class: "mb-4" + +#tags-table + %h2.add-tag-title + Insérer une balise + %p.notice + Copiez-collez les balises ci-dessous pour afficher automatiquement l’information souhaitée. + .head + .tag Balise + .description Description + .items + - @mail_template.tags.each do |tag| + .item + %code.tag + = "--#{tag[:libelle]}--" + .description + = tag[:description] + + +-# Disable accepting dropped images and traduce toolbar tooltips +:javascript + addEventListener('trix-file-accept', function(e) { e.preventDefault(); }); + addEventListener("trix-initialize", function(e) { + document.querySelector('button[data-trix-attribute="bold"]').setAttribute('title', 'Gras'); + document.querySelector('button[data-trix-attribute="italic"]').setAttribute('title', 'Italique'); + document.querySelector('button[data-trix-attribute="strike"]').setAttribute('title', 'Barrer'); + document.querySelector('button[data-trix-attribute="href"]').setAttribute('title', 'Créer lien'); + document.querySelector('button[data-trix-attribute="heading1"]').setAttribute('title', 'Titre'); + document.querySelector('button[data-trix-attribute="quote"]').setAttribute('title', 'Citation'); + document.querySelector('button[data-trix-attribute="bullet"]').setAttribute('title', 'Liste à puce'); + document.querySelector('button[data-trix-attribute="number"]').setAttribute('title', 'Liste numérotée'); + document.querySelector('button[data-trix-action="increaseNestingLevel"]').setAttribute('title', 'Indenter'); + document.querySelector('button[data-trix-action="decreaseNestingLevel"]').setAttribute('title', 'Désindenter'); + document.querySelector('button[data-trix-action="undo"]').setAttribute('title', 'Annuler la modification'); + document.querySelector('button[data-trix-action="redo"]').setAttribute('title', 'Appliquer à nouveau la modification'); + document.querySelector('.trix-button.trix-button--dialog[data-trix-method="setAttribute"]').value = "Créer lien"; + document.querySelector('.trix-button.trix-button--dialog[data-trix-method="removeAttribute"]').value = "Effacer lien"; + }) diff --git a/app/views/new_administrateur/mail_templates/edit.html.haml b/app/views/new_administrateur/mail_templates/edit.html.haml new file mode 100644 index 000000000..7f56949fb --- /dev/null +++ b/app/views/new_administrateur/mail_templates/edit.html.haml @@ -0,0 +1,33 @@ +- if params[:id] == 'closed_mail' + = render partial: 'admin/closed_mail_template_attestation_inconsistency_alert' + += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to('Démarches', admin_procedures_path), + link_to(@procedure.libelle, admin_procedure_path(@procedure)), + link_to("Emails", admin_procedure_mail_templates_path(@procedure)), + @mail_template.class.const_get(:DISPLAYED_NAME)] } + +.procedure-form + .procedure-form__columns.container + + = form_for @mail_template, + url: admin_procedure_mail_template_path(@procedure, @mail_template.class.const_get(:SLUG)), + method: :put, + html: { class: 'form procedure-form__column--form' } do |f| + + %h1.page-title= @mail_template.class.const_get(:DISPLAYED_NAME) + = render partial: 'form', locals: { f: f } + .procedure-form__actions.sticky--bottom + .actions-right + = f.submit 'Enregistrer', class: 'button primary send' + + .procedure-form__column--preview + .procedure-form__preview.sticky--top + %h3 + .procedure-form__preview-title + Aperçu + .notice + Cet aperçu est mis à jour après chaque sauvegarde. + .procedure-preview + = render partial: 'apercu', locals: { procedure: @procedure } + diff --git a/app/views/new_administrateur/mail_templates/index.html.haml b/app/views/new_administrateur/mail_templates/index.html.haml index 00ee10d58..8d4963992 100644 --- a/app/views/new_administrateur/mail_templates/index.html.haml +++ b/app/views/new_administrateur/mail_templates/index.html.haml @@ -8,6 +8,10 @@ .flex.justify-between %div .card-title= mail_template.class.const_get(:DISPLAYED_NAME) - %p.notice= mail_template.class.const_get(:DISPLAYED_NAME) === 'Accusé de réception' ? 'Personnalisé' : 'Modèle standard' + - if mail_template.updated_at.blank? + %p.notice= mail_template.class.const_get(:DISPLAYED_NAME) === 'Accusé de réception' ? 'Personnalisé' : 'Modèle standard' + - else + %span.badge.baseline modifié le #{mail_template.updated_at.strftime('%d-%m-%Y')} + %div = link_to 'Modifier', edit_admin_procedure_mail_template_path(@procedure, mail_template.class.const_get(:SLUG)), class: 'button' diff --git a/app/views/new_administrateur/procedures/_procedures_list.html.haml b/app/views/new_administrateur/procedures/_procedures_list.html.haml index 8923f657e..2641df629 100644 --- a/app/views/new_administrateur/procedures/_procedures_list.html.haml +++ b/app/views/new_administrateur/procedures/_procedures_list.html.haml @@ -1,15 +1,14 @@ - procedures.each do |procedure| .card - .flex.justify-between - .flex - - if procedure.logo.present? - = image_tag procedure.logo, alt: procedure.libelle, width: '100' - .flex.column.ml-1 - .card-title - = link_to procedure.libelle, admin_procedure_path(procedure), style: 'color: black;' - = link_to(procedure_lien(procedure), procedure_lien(procedure), class: 'procedure-lien mb-1') + .admin-procedures-list-row.infos.flex + - if procedure.logo.present? + = image_tag procedure.logo, alt: procedure.libelle, width: '100' + .flex.column.ml-1 + .card-title + = link_to procedure.libelle, admin_procedure_path(procedure), style: 'color: black;' + = link_to(procedure_lien(procedure), procedure_lien(procedure), class: 'procedure-lien mb-1') - %div + .admin-procedures-list-timestamps %p.notice N° #{procedure.id} %p.notice créée le #{procedure.created_at.strftime('%d/%m/%Y')} - if procedure.published_at.present? @@ -17,7 +16,7 @@ - if procedure.closed_at.present? %p.notice archivée le #{procedure.closed_at.strftime('%d/%m/%Y')} - .flex.justify-between + .admin-procedures-list-row.actions.flex.justify-between %div - if feature_enabled?(:administrateur_routage) %span.icon.person diff --git a/app/views/new_administrateur/procedures/show.html.haml b/app/views/new_administrateur/procedures/show.html.haml index d8e40772b..ae27c63e7 100644 --- a/app/views/new_administrateur/procedures/show.html.haml +++ b/app/views/new_administrateur/procedures/show.html.haml @@ -40,7 +40,7 @@ - if !@procedure.locked? .card-admin - - if @procedure.types_de_champ.count > 0 + - if @procedure.draft_types_de_champ.count > 0 %div %span.icon.accept %p.card-admin-status-accept Validé @@ -50,7 +50,7 @@ %p.card-admin-status-todo À faire %div %p.card-admin-title - %span.badge.baseline= @procedure.types_de_champ.count + %span.badge.baseline= @procedure.draft_types_de_champ.count Champs du formulaire %p.card-admin-subtitle À remplir par les usagers .card-admin-action @@ -148,7 +148,7 @@ - if !@procedure.locked? .card-admin - - if @procedure.types_de_champ_private.present? + - if @procedure.draft_types_de_champ_private.present? %div %span.icon.accept %p.card-admin-status-accept Validé diff --git a/app/views/shared/_france_connect_login.html.haml b/app/views/shared/_france_connect_login.html.haml index 9f4a33646..9b1b9794c 100644 --- a/app/views/shared/_france_connect_login.html.haml +++ b/app/views/shared/_france_connect_login.html.haml @@ -1,11 +1,14 @@ -.france-connect-login - %h2 - = t('views.shared.france_connect_login.title') - %p - = t('views.shared.france_connect_login.description') - .france-connect-login-buttons - = link_to t('views.shared.france_connect_login.login_button'), url, class: "france-connect-login-button" - .france-connect-help-link - = link_to t('views.shared.france_connect_login.help_link'), "https://franceconnect.gouv.fr/", target: "_blank", rel: "noopener", class: "link" - .france-connect-login-separator - = t('views.shared.france_connect_login.separator') +- if FranceConnectService.enabled? + .france-connect-login + %h2 + = t('views.shared.france_connect_login.title') + %p + = t('views.shared.france_connect_login.description') + .france-connect-login-buttons + = link_to t('views.shared.france_connect_login.login_button'), url, class: "france-connect-login-button" + .france-connect-help-link + = link_to t('views.shared.france_connect_login.help_link'), "https://franceconnect.gouv.fr/", target: "_blank", rel: "noopener", class: "link" + .france-connect-login-separator + = t('views.shared.france_connect_login.separator') +- else + diff --git a/app/views/support/admin.html.haml b/app/views/support/admin.html.haml index 88177c590..7b07df30d 100644 --- a/app/views/support/admin.html.haml +++ b/app/views/support/admin.html.haml @@ -2,45 +2,41 @@ #contact-form .container - %h1.new-h1 Contactez notre équipe + %h1.new-h1 + = t('contact_team', scope: [:supportadmin]) .description - En tant qu'administration, vous pouvez nous contactez via ce formulaire. Nous vous répondrons dans les plus brefs délais, par email ou par téléphone. + = t('admin_intro_html', scope: [:supportadmin], contact_path: contact_path) %br - %br - %strong - Attention, ce formulaire est réservée uniquement aux organismes publics. - Il ne concerne ni les particuliers, ni les entreprises, ni les associations (sauf celles reconnues d'utilité publique). Si c'est votre cas, rendez-vous sur notre - = link_to contact_path do - formulaire de contact public - \. + %p.mandatory-explanation= t('asterisk_html', scope: [:utils]) = form_tag contact_path, method: :post, class: 'form' do |f| - if !user_signed_in? .contact-champ = label_tag :email do - Adresse email professionnelle + = t('pro_mail', scope: [:supportadmin]) %span.mandatory * = text_field_tag :email, params[:email], required: true .contact-champ = label_tag :type do - Catégorie + = t('your_question', scope: [:support, :question]) %span.mandatory * = select_tag :type, options_for_select(@options, params[:type]) .contact-champ - = label_tag :phone, 'Numéro de téléphone professionnel (ligne directe)' + = label_tag :phone do + = t('professional_phone_number', scope: [:supportadmin]) = text_field_tag :phone .contact-champ = label_tag :subject do - Sujet + = t('subject', scope: [:utils]) = text_field_tag :subject, params[:subject], required: false .contact-champ = label_tag :text do - Message + = t('message', scope: [:utils]) %span.mandatory * = text_area_tag :text, params[:text], rows: 6, required: true @@ -48,4 +44,4 @@ = hidden_field_tag :admin, true .send-wrapper - = button_tag 'Envoyer le message', type: :submit, class: 'button send primary' + = button_tag t('send_mail', scope: [:utils]), type: :submit, class: 'button send primary' diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index fe57eb71f..5305f9e8d 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -25,7 +25,7 @@ %hr %p.center - %span Vous êtes nouveau sur demarches‑simplifiees.fr ? + %span Vous êtes nouveau sur #{APPLICATION_NAME.gsub("-","‑").html_safe} ? %br %br = link_to "Trouvez votre démarche", COMMENT_TROUVER_MA_DEMARCHE_URL, target: "_blank", class: "button expend secondary" diff --git a/config/application.rb b/config/application.rb index 049f5f7ee..cc8508f4f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,13 +1,13 @@ require File.expand_path('boot', __dir__) require 'rails/all' -require_relative 'application_name' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) Dotenv::Railtie.load +require_relative 'application_name' module TPS class Application < Rails::Application diff --git a/config/env.example.optional b/config/env.example.optional index 20748e4be..fd59171fa 100644 --- a/config/env.example.optional +++ b/config/env.example.optional @@ -6,6 +6,9 @@ APPLICATION_NAME="demarches-simplifiees.fr" APPLICATION_SHORTNAME="d-s.fr" APPLICATION_BASE_URL="https://www.demarches-simplifiees.fr" +# Utilisation de France Connect +# FRANCE_CONNECT_ENABLED="disabled" # "enabled" par défaut + # Personnalisation d'instance - Adresses Email de l'application et téléphone # CONTACT_EMAIL="" # EQUIPE_EMAIL="" diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index 6be3549bc..2784f955f 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -1,5 +1,8 @@ Rails.application.config.active_storage.service_urls_expire_in = 1.hour +Rails.application.config.active_storage.analyzers.delete ActiveStorage::Analyzer::ImageAnalyzer +Rails.application.config.active_storage.analyzers.delete ActiveStorage::Analyzer::VideoAnalyzer + ActiveSupport.on_load(:active_storage_blob) do include BlobSignedIdConcern include BlobVirusScannerConcern diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml deleted file mode 100644 index 9573f3d1b..000000000 --- a/config/locales/devise.en.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Additional translations at https://github.com/plataformatec/devise/wiki/I18n - -en: - devise: - confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." - failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys} or password." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." - mailer: - confirmation_instructions: - subject: "Confirmation instructions" - reset_password_instructions: - subject: "Reset password instructions" - unlock_instructions: - subject: "Unlock instructions" - omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." - passwords: - no_token: "You can’t access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." - registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." - updated: "Your account has been updated successfully." - sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." - unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." - errors: - messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" - not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml deleted file mode 100755 index 958274540..000000000 --- a/config/locales/devise.fr.yml +++ /dev/null @@ -1,64 +0,0 @@ -# Additional translations at https://github.com/plataformatec/devise/wiki/I18n - -fr: - devise: - confirmations: - confirmed: "Votre compte a été activé." - send_instructions: "Vous allez recevoir un email avec les instructions nécessaires à l’activation de votre compte dans quelques minutes." - send_paranoid_instructions: "Si votre adresse email existe dans notre base de données, vous allez bientôt recevoir un email contenant les instructions d’activation de votre compte." - failure: - already_authenticated: "Vous êtes déjà connecté" - inactive: "Votre compte n’est pas encore activé." - invalid: "adresse email ou mot de passe incorrect." - last_attempt: "Vous avez droit à une tentative avant que votre compte ne soit verrouillé." - locked: "Votre compte est verrouillé." - not_found_in_database: "adresse email ou mot de passe invalide." - timeout: "Votre session est expirée. Veuillez vous reconnecter pour continuer." - unauthenticated: "Vous devez vous connecter ou vous inscrire pour continuer." - unconfirmed: "Vous devez confirmer votre adresse email pour continuer. Cliquez sur le lien qui vous a été envoyé par email." - mailer: - confirmation_instructions: - subject: "Instructions d’activation de votre compte" - reset_password_instructions: - subject: "Instructions pour changer le mot de passe" - unlock_instructions: - subject: "Instructions pour déverrouiller le compte" - email_changed: - subject: "Changement d’adresse email" - password_change: - subject: "Votre mot de passe a été modifié avec succés." - omniauth_callbacks: - failure: "Nous n’avons pas pu vous authentifier via %{kind} : '%{reason}'." - success: "Authentifié avec succès via %{kind}." - passwords: - no_token: "Vous ne pouvez accéder à cette page sans passer par un email de réinitialisation de mot de passe. Si vous êtes passé par un email de ce type, assurez-vous d’utiliser l’URL complète." - send_instructions: "Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants" - send_paranoid_instructions: "Si votre adresse email existe dans notre base de données, vous allez recevoir un lien de réinitialisation par email" - updated: "Votre mot de passe a été changé avec succès, vous êtes maintenant connecté" - updated_not_active: "Votre mot de passe a été changé avec succès." - registrations: - destroyed: "Votre compte a été supprimé avec succès. Nous espérons vous revoir bientôt." - signed_up: "Bienvenue, vous êtes connecté." - signed_up_but_inactive: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte n’est pas encore activé." - signed_up_but_locked: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte est verrouillé." - signed_up_but_unconfirmed: "Nous vous avons envoyé un email contenant un lien d’activation. Ouvrez ce lien pour activer votre compte." - update_needs_confirmation: "Vous devez confirmer votre nouvelle adresse email. Vérifiez vos emails, et cliquez sur le lien de confirmation pour confirmer votre changement d’adresse." - updated: "Votre compte a été modifié avec succès." - sessions: - signed_in: "Connecté." - signed_out: "Déconnecté." - already_signed_out: "Déconnecté." - unlocks: - send_instructions: "Vous allez recevoir les instructions nécessaires au déverrouillage de votre compte dans quelques instants" - send_paranoid_instructions: "Si votre compte existe, vous allez bientôt recevoir un email contenant les instructions pour le déverrouiller." - unlocked: "Votre compte a été déverrouillé avec succès, vous êtes maintenant connecté." - errors: - messages: - already_confirmed: "a déjà été validé(e), veuillez essayer de vous connecter" - confirmation_period_expired: "à activer dans les %{period}, merci de faire une nouvelle demande" - expired: "a expiré, merci d’en faire une nouvelle demande" - not_found: "n’a pas été trouvé(e)" - not_locked: "n’était pas verrouillé(e)" - not_saved: - one: "1 erreur a empêché ce(tte) %{resource} d’être sauvegardé(e) :" - other: "%{count} erreurs ont empêché ce(tte) %{resource} d’être sauvegardé(e) :" diff --git a/config/locales/en.yml b/config/locales/en.yml index 62bdf05ce..ee24bb44a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -22,9 +22,6 @@ en: utils: deconnexion: "Log out" - involved: "See concerned people" - no-commentaires: "There is no message yet, feel free to start the first one." - depositaire: "Dépositaire" pj: "Attachments" asterisk_html: Fields marked by an asterisk ( * ) are mandatory. file_number: File number @@ -60,63 +57,7 @@ en: submit: publish: Publish reopen: Reopen - supportadmin: - # admin demande rdv: Demande de RDV pour une présentation à distance de demarches-simplifiees.fr - admin question: I have a question about demarches-simplifiees.fr - admin soucis: I am facing a technical issue on demarches-simplifiees.fr - admin suggestion produit: I have a suggestion for an evolution - admin demande compte: I want to open an admin account with an Orange, Wanadoo, etc. email - admin autre: Other topic - number: - currency: - format: - delimiter: "," - format: "%u%n" - precision: 2 - separator: "." - significant: false - strip_insignificant_zeros: false - unit: "€" - format: - delimiter: "," - precision: 3 - separator: "." - significant: false - strip_insignificant_zeros: false - human: - decimal_units: - format: "%n %u" - units: - billion: Billion - million: Million - quadrillion: Quadrillion - thousand: Thousand - trillion: Trillion - unit: '' - format: - delimiter: '' - precision: 3 - significant: true - strip_insignificant_zeros: true - storage_units: - format: "%n %u" - units: - byte: - one: Byte - other: Bytes - gb: GB - kb: KB - mb: MB - pb: PB - tb: TB - percentage: - format: - delimiter: '' - format: "%n%" - precision: - format: - delimiter: '' activerecord: attributes: user: @@ -126,13 +67,7 @@ en: password: 'password' errors: messages: - blank: "must be filled" - not_a_number: 'must be a number' - not_an_integer: 'must be an integer (without digit after the comma)' - greater_than: "must be greater than %{count}" - greater_than_or_equal_to: "must be greater than or equal to %{count}" - less_than: "must be less than %{count}" - less_than_or_equal_to: "must be less than or equal to %{count}" + not_a_phone: 'Invalid phone number' models: attestation_template: attributes: @@ -197,98 +132,9 @@ en: # parcelles_agricoles_empty: # one: "Aucune parcelle agricole sur la zone sélectionnée" # other: "Aucune parcelle agricole sur les zones sélectionnées" + not_an_integer: "must be an integer (without decimal)" + blank: "can't be blank" - date: - abbr_day_names: - - Sun - - Mon - - Tue - - Wed - - Thu - - Fri - - Sat - abbr_month_names: - - - - Jan - - Feb - - Mar - - Apr - - May - - Jun - - Jul - - Aug - - Sep - - Oct - - Nov - - Dec - month_names: - - - - January - - February - - March - - April - - May - - June - - July - - August - - September - - October - - November - - December - order: - - :year - - :month - - :day - day_names: - - Sunday - - Monday - - Tuesday - - Wednesday - - Thursday - - Friday - - Saturday - formats: - default: "%Y-%m-%d" - long: "%B %d, %Y" - short: "%b %d" - datetime: - distance_in_words: - about_x_hours: - one: about an hour - other: about %{count} hours - about_x_months: - one: about a month - other: about %{count} months - about_x_years: - one: about a year - other: about %{count} years - almost_x_years: - one: almost a year - other: almost %{count} years - half_a_minute: half a minute - less_than_x_minutes: - zero: less than a minute - one: less than a minute - other: less than %{count} minutes - less_than_x_seconds: - zero: less than a second - one: less than a second - other: less than %{count} seconds - over_x_years: - one: more than a year - other: more than %{count} years - x_days: - one: 1 day - other: "%{count} days" - x_minutes: - one: 1 minute - other: "%{count} minutes" - x_months: - one: 1 month - other: "%{count} months" - x_seconds: - one: 1 second - other: "%{count} seconds" time: formats: default: "%B %d %Y %R" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 06bc5943e..465afd67b 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -22,9 +22,6 @@ fr: utils: deconnexion: "Déconnexion" - involved: "Voir les personnes impliquées" - no-commentaires: "Il n’y a aucun message dans le fil de discussion, n’hésitez pas à initier le premier." - depositaire: "Dépositaire" pj: "Pièces jointes" asterisk_html: Les champs suivis d’un astérisque ( * ) sont obligatoires. file_number: Numéro de dossier @@ -60,62 +57,7 @@ fr: submit: publish: Publier reopen: Réactiver - supportadmin: - admin demande rdv: Demande de RDV pour une présentation à distance de demarches-simplifiees.fr - admin question: J’ai une question sur demarches-simplifiees.fr - admin soucis: J’ai un problème technique avec demarches-simplifiees.fr - admin suggestion produit: J’ai une proposition d’évolution - admin demande compte: Je souhaite ouvrir un compte administrateur avec un email Orange, Wanadoo, etc. - admin autre: Autre sujet - number: - currency: - format: - delimiter: " " - format: "%n %u" - precision: 2 - separator: "," - significant: false - strip_insignificant_zeros: false - unit: "€" - format: - delimiter: " " - precision: 3 - separator: "," - significant: false - strip_insignificant_zeros: false - human: - decimal_units: - format: "%n %u" - units: - billion: milliard - million: million - quadrillion: million de milliards - thousand: millier - trillion: billion - unit: '' - format: - delimiter: '' - precision: 3 - significant: true - strip_insignificant_zeros: true - storage_units: - format: "%n %u" - units: - byte: - one: octet - other: octets - gb: Go - kb: ko - mb: Mo - tb: To - percentage: - format: - delimiter: '' - format: "%n%" - precision: - format: - delimiter: '' activerecord: attributes: user: @@ -125,14 +67,7 @@ fr: password: 'Le mot de passe' errors: messages: - blank: "doit être rempli" - not_a_number: 'doit être un nombre' - not_an_integer: 'doit être un nombre entier (sans chiffres après la virgule)' not_a_phone: 'Numéro de téléphone invalide' - greater_than: "doit être supérieur à %{count}" - greater_than_or_equal_to: "doit être supérieur ou égal à %{count}" - less_than: "doit être inférieur à %{count}" - less_than_or_equal_to: "doit être inférieur ou égal à %{count}" models: attestation_template: attributes: @@ -197,98 +132,9 @@ fr: parcelles_agricoles_empty: one: "Aucune parcelle agricole sur la zone sélectionnée" other: "Aucune parcelle agricole sur les zones sélectionnées" + not_an_integer: "doit être un nombre entier (sans chiffres après la virgule)" + blank: "doit être rempli" - date: - abbr_day_names: - - dim - - lun - - mar - - mer - - jeu - - ven - - sam - abbr_month_names: - - - - jan. - - fév. - - mar. - - avr. - - mai - - juin - - juil. - - août - - sept. - - oct. - - nov. - - déc. - month_names: - - - - janvier - - février - - mars - - avril - - mai - - juin - - juillet - - août - - septembre - - octobre - - novembre - - décembre - order: - - :day - - :month - - :year - day_names: - - dimanche - - lundi - - mardi - - mercredi - - jeudi - - vendredi - - samedi - formats: - default: "%d %B %Y" - short: "%e %b" - long: "%e %B %Y" - datetime: - distance_in_words: - about_x_hours: - one: environ une heure - other: environ %{count} heures - about_x_months: - one: environ un mois - other: environ %{count} mois - about_x_years: - one: environ un an - other: environ %{count} ans - almost_x_years: - one: presqu’un an - other: presque %{count} ans - half_a_minute: une demi-minute - less_than_x_minutes: - zero: moins d’une minute - one: moins d’une minute - other: moins de %{count} minutes - less_than_x_seconds: - zero: moins d’une seconde - one: moins d’une seconde - other: moins de %{count} secondes - over_x_years: - one: plus d’un an - other: plus de %{count} ans - x_days: - one: 1 jour - other: "%{count} jours" - x_minutes: - one: 1 minute - other: "%{count} minutes" - x_months: - one: 1 mois - other: "%{count} mois" - x_seconds: - one: 1 seconde - other: "%{count} secondes" time: formats: default: "%d %B %Y %R" diff --git a/config/locales/views/support/index.en.yml b/config/locales/views/support/index.en.yml index d2692528c..8d92f06f3 100644 --- a/config/locales/views/support/index.en.yml +++ b/config/locales/views/support/index.en.yml @@ -30,3 +30,17 @@ en:

%{link_lost_user}

" notice_pj_product: A screenshot can help us identify the element to improve. notice_pj_other: A screenshot can help us identify the issue. + supportadmin: + admin_intro_html: "

As an administration, you can contact us through this form. We'll answer you as quickly as possibly by e-mail or phone.

+
+

Caution, this form is dedicated to public bodies only. + It does not concern individuals, companies nor associations (except those recognised of public utility). If you belong to one of these categories, contact us here.

" + contact_team: Contact our team + pro_phone_number: Professional phone number (direct line) + pro_mail: Professional email address + admin demande rdv: I request an appointment for an online presentation of demarches-simplifiees.fr + admin question: I have a question about demarches-simplifiees.fr + admin soucis: I am facing a technical issue on demarches-simplifiees.fr + admin suggestion produit: I have a suggestion for an evolution + admin demande compte: I want to open an admin account with an Orange, Wanadoo, etc. email + admin autre: Other topic diff --git a/config/locales/views/support/index.fr.yml b/config/locales/views/support/index.fr.yml index 06b0decbe..8c09f38e1 100644 --- a/config/locales/views/support/index.fr.yml +++ b/config/locales/views/support/index.fr.yml @@ -29,3 +29,18 @@ fr:

%{link_lost_user}

" notice_pj_product: Une capture d’écran peut nous aider à identifier plus facilement l’endroit à améliorer. notice_pj_other: Une capture d’écran peut nous aider à identifier plus facilement le problème. + supportadmin: + admin_intro_html: "

En tant qu'administration, vous pouvez nous contactez via ce formulaire. Nous vous répondrons dans les plus brefs délais, par email ou par téléphone.

+
+

Attention, ce formulaire est réservé uniquement aux organismes publics. + Il ne concerne ni les particuliers, ni les entreprises, ni les associations (sauf celles reconnues d'utilité publique). Si c'est votre cas, rendez-vous sur notre + formulaire de contact public.

" + contact_team: Contactez notre équipe + pro_phone_number: Numéro de téléphone professionnel (ligne directe) + pro_mail: Adresse e-mail professionnelle + admin demande rdv: Demande de RDV pour une présentation à distance de demarches-simplifiees.fr + admin question: J’ai une question sur demarches-simplifiees.fr + admin soucis: J’ai un problème technique avec demarches-simplifiees.fr + admin suggestion produit: J’ai une proposition d’évolution + admin demande compte: Je souhaite ouvrir un compte administrateur avec un email Orange, Wanadoo, etc. + admin autre: Autre sujet diff --git a/config/routes.rb b/config/routes.rb index beefd87d3..043fc16d8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -189,8 +189,6 @@ Rails.application.routes.draw do delete :delete_notice end - resources :mail_templates, only: [:edit, :update] - put 'archive' => 'procedures#archive', as: :archive get 'publish_validate' => 'procedures#publish_validate', as: :publish_validate put 'publish' => 'procedures#publish', as: :publish @@ -373,6 +371,8 @@ Rails.application.routes.draw do patch 'update_jeton' end + resources :mail_templates, only: [:edit, :update] + resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do member do post 'add_instructeur' diff --git a/spec/controllers/admin/mail_templates_controller_spec.rb b/spec/controllers/admin/mail_templates_controller_spec.rb deleted file mode 100644 index 29d2804ac..000000000 --- a/spec/controllers/admin/mail_templates_controller_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe Admin::MailTemplatesController, type: :controller do - let(:procedure) { create :procedure } - let(:initiated_mail) { Mails::InitiatedMail.default_for_procedure(procedure) } - - before do - sign_in(procedure.administrateurs.first.user) - end - - describe 'PATCH update' do - let(:mail_subject) { 'plop modif' } - let(:mail_body) { 'plip modif' } - - before :each do - patch :update, - params: { - procedure_id: procedure.id, - id: initiated_mail.class.const_get(:SLUG), - mail_template: { subject: mail_subject, body: mail_body } - } - end - - it { expect(response).to redirect_to edit_admin_procedure_mail_template_path(procedure, initiated_mail.class.const_get(:SLUG)) } - - context 'the mail template' do - subject { procedure.reload; procedure.initiated_mail_template } - - it { expect(subject.subject).to eq(mail_subject) } - it { expect(subject.body).to eq(mail_body) } - end - end -end diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index 1bc8b195b..75426fe7b 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -255,9 +255,8 @@ describe API::V1::DossiersController do end describe 'repetition' do - let(:procedure) { create(:procedure, administrateur: admin) } - let(:champ) { build(:champ_repetition) } - let(:dossier) { create(:dossier, :en_construction, champs: [champ], procedure: procedure) } + let(:procedure) { create(:procedure, :with_repetition, administrateur: admin) } + let(:dossier) { create(:dossier, :en_construction, :with_all_champs, procedure: procedure) } subject { super().first[:rows] } diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 31c8a377e..8da06e9b0 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -560,36 +560,34 @@ describe Instructeurs::DossiersController, type: :controller do end describe "#update_annotations" do + let(:procedure) do + create(:procedure, :published, types_de_champ_private: [ + build(:type_de_champ_multiple_drop_down_list, position: 0), + build(:type_de_champ_linked_drop_down_list, position: 1), + build(:type_de_champ_datetime, position: 2), + build(:type_de_champ_repetition, :with_types_de_champ, position: 3) + ], instructeurs: instructeurs) + end + let(:dossier) { create(:dossier, :en_construction, :with_all_annotations, procedure: procedure) } + let(:now) { Time.zone.parse('01/01/2100') } + let(:champ_multiple_drop_down_list) do - tdc = create(:type_de_champ_multiple_drop_down_list, :private, procedure: procedure, libelle: 'libelle') - create(:champ_multiple_drop_down_list, :private, type_de_champ: tdc, dossier: dossier) + dossier.champs_private.first end let(:champ_linked_drop_down_list) do - tdc = create(:type_de_champ_linked_drop_down_list, :private, procedure: procedure, libelle: 'libelle') - create(:champ_linked_drop_down_list, :private, type_de_champ: tdc, dossier: dossier) + dossier.champs_private.second end let(:champ_datetime) do - tdc = create(:type_de_champ_datetime, :private, procedure: procedure, libelle: 'libelle') - create(:champ_datetime, :private, type_de_champ: tdc, dossier: dossier) + dossier.champs_private.third end let(:champ_repetition) do - tdc = create(:type_de_champ_repetition, :private, :with_types_de_champ, procedure: procedure, libelle: 'libelle') - tdc.types_de_champ << create(:type_de_champ_text, procedure: procedure, libelle: 'libelle') - champ = create(:champ_repetition, :private, type_de_champ: tdc, dossier: dossier) - champ.add_row - champ + dossier.champs_private.fourth end - let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } - - let(:now) { Time.zone.parse('01/01/2100') } - before do - dossier.champs_private << [champ_multiple_drop_down_list, champ_linked_drop_down_list, champ_datetime, champ_repetition] - Timecop.freeze(now) patch :update_annotations, params: params @@ -607,64 +605,70 @@ describe Instructeurs::DossiersController, type: :controller do let(:params) do { procedure_id: procedure.id, - dossier_id: dossier.id, - dossier: { - champs_private_attributes: { - '0': { - id: champ_multiple_drop_down_list.id, - value: ['', 'un', 'deux'] - }, - '1': { - id: champ_datetime.id, - 'value(1i)': 2019, - 'value(2i)': 12, - 'value(3i)': 21, - 'value(4i)': 13, - 'value(5i)': 17 - }, - '2': { - id: champ_linked_drop_down_list.id, - primary_value: 'primary', - secondary_value: 'secondary' - }, - '3': { - id: champ_repetition.id, - champs_attributes: { - id: champ_repetition.champs.first.id, - value: 'text' + dossier_id: dossier.id, + dossier: { + champs_private_attributes: { + '0': { + id: champ_multiple_drop_down_list.id, + value: ['', 'un', 'deux'] + }, + '1': { + id: champ_datetime.id, + 'value(1i)': 2019, + 'value(2i)': 12, + 'value(3i)': 21, + 'value(4i)': 13, + 'value(5i)': 17 + }, + '2': { + id: champ_linked_drop_down_list.id, + primary_value: 'primary', + secondary_value: 'secondary' + }, + '3': { + id: champ_repetition.id, + champs_attributes: { + id: champ_repetition.champs.first.id, + value: 'text' + } } } } } - } end - it { expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]') } - it { expect(champ_linked_drop_down_list.primary_value).to eq('primary') } - it { expect(champ_linked_drop_down_list.secondary_value).to eq('secondary') } - it { expect(champ_datetime.value).to eq('21/12/2019 13:17') } - it { expect(champ_repetition.champs.first.value).to eq('text') } - it { expect(dossier.reload.last_champ_private_updated_at).to eq(now) } - it { expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) } + + it { + expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]') + expect(champ_linked_drop_down_list.primary_value).to eq('primary') + expect(champ_linked_drop_down_list.secondary_value).to eq('secondary') + expect(champ_datetime.value).to eq('21/12/2019 13:17') + expect(champ_repetition.champs.first.value).to eq('text') + expect(dossier.reload.last_champ_private_updated_at).to eq(now) + expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) + } end context "without new values for champs_private" do let(:params) do { procedure_id: procedure.id, - dossier_id: dossier.id, - dossier: { - champs_private_attributes: {}, - champs_attributes: { - '0': { - id: champ_multiple_drop_down_list.id, - value: ['', 'un', 'deux'] + dossier_id: dossier.id, + dossier: { + champs_private_attributes: {}, + champs_attributes: { + '0': { + id: champ_multiple_drop_down_list.id, + value: ['', 'un', 'deux'] + } } } } - } end - it { expect(dossier.reload.last_champ_private_updated_at).to eq(nil) } - it { expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) } + + it { + expect(dossier.reload.last_champ_private_updated_at).to eq(nil) + expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) + } end end diff --git a/spec/controllers/manager/procedures_controller_spec.rb b/spec/controllers/manager/procedures_controller_spec.rb index b8bebd461..04c4dc829 100644 --- a/spec/controllers/manager/procedures_controller_spec.rb +++ b/spec/controllers/manager/procedures_controller_spec.rb @@ -17,7 +17,7 @@ describe Manager::ProceduresController, type: :controller do describe '#show' do render_views - let(:procedure) { create(:procedure, :with_repetition) } + let(:procedure) { create(:procedure, :published, :with_repetition) } before do get :show, params: { id: procedure.id } diff --git a/spec/controllers/new_administrateur/mail_templates_controller_spec.rb b/spec/controllers/new_administrateur/mail_templates_controller_spec.rb index 8f66ad3fd..36a2a17a5 100644 --- a/spec/controllers/new_administrateur/mail_templates_controller_spec.rb +++ b/spec/controllers/new_administrateur/mail_templates_controller_spec.rb @@ -42,4 +42,27 @@ describe NewAdministrateur::MailTemplatesController, type: :controller do expect(response.body).to include(procedure.service.telephone) end end + + describe 'PATCH update' do + let(:mail_subject) { 'Mise à jour de votre démarche' } + let(:mail_body) { '
Une mise à jour a été effectuée sur votre démarche n° --demarche-id--.
' } + + before :each do + patch :update, + params: { + procedure_id: procedure.id, + id: initiated_mail.class.const_get(:SLUG), + mails_initiated_mail: { subject: mail_subject, rich_body: mail_body } + } + end + + it { expect(response).to redirect_to edit_admin_procedure_mail_template_path(procedure, initiated_mail.class.const_get(:SLUG)) } + + context 'the mail template' do + subject { procedure.reload; procedure.initiated_mail_template } + + it { expect(subject.subject).to eq(mail_subject) } + it { expect(subject.body).to eq(mail_body) } + end + end end diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index a16a9281d..796778a62 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -2,17 +2,17 @@ describe StatsController, type: :controller do describe "#last_four_months_hash" do context "while a regular user is logged in" do before do - FactoryBot.create(:procedure, :created_at => 6.months.ago, :updated_at => 6.months.ago) - FactoryBot.create(:procedure, :created_at => 2.months.ago, :updated_at => 62.days.ago) - FactoryBot.create(:procedure, :created_at => 2.months.ago, :updated_at => 62.days.ago) - FactoryBot.create(:procedure, :created_at => 2.months.ago, :updated_at => 31.days.ago) - FactoryBot.create(:procedure, :created_at => 2.months.ago, :updated_at => Time.zone.now) + create(:procedure, created_at: 6.months.ago, updated_at: 6.months.ago) + create(:procedure, created_at: 2.months.ago, updated_at: 62.days.ago) + create(:procedure, created_at: 2.months.ago, updated_at: 62.days.ago) + create(:procedure, created_at: 2.months.ago, updated_at: 31.days.ago) + create(:procedure, created_at: 2.months.ago, updated_at: Time.zone.now) @controller = StatsController.new allow(@controller).to receive(:administration_signed_in?).and_return(false) end - let (:association) { Procedure.all } + let(:association) { Procedure.all } subject { @controller.send(:last_four_months_hash, association, :updated_at) } @@ -26,10 +26,10 @@ describe StatsController, type: :controller do context "while a super admin is logged in" do before do - FactoryBot.create(:procedure, :updated_at => 6.months.ago) - FactoryBot.create(:procedure, :updated_at => 45.days.ago) - FactoryBot.create(:procedure, :updated_at => 1.day.ago) - FactoryBot.create(:procedure, :updated_at => 1.day.ago) + create(:procedure, updated_at: 6.months.ago) + create(:procedure, updated_at: 45.days.ago) + create(:procedure, updated_at: 1.day.ago) + create(:procedure, updated_at: 1.day.ago) @controller = StatsController.new @@ -52,11 +52,11 @@ describe StatsController, type: :controller do describe '#cumulative_hash' do before do Timecop.freeze(Time.zone.local(2016, 10, 2)) - FactoryBot.create(:procedure, :created_at => 55.days.ago, :updated_at => 43.days.ago) - FactoryBot.create(:procedure, :created_at => 45.days.ago, :updated_at => 40.days.ago) - FactoryBot.create(:procedure, :created_at => 45.days.ago, :updated_at => 20.days.ago) - FactoryBot.create(:procedure, :created_at => 15.days.ago, :updated_at => 20.days.ago) - FactoryBot.create(:procedure, :created_at => 15.days.ago, :updated_at => 1.hour.ago) + create(:procedure, created_at: 55.days.ago, updated_at: 43.days.ago) + create(:procedure, created_at: 45.days.ago, updated_at: 40.days.ago) + create(:procedure, created_at: 45.days.ago, updated_at: 20.days.ago) + create(:procedure, created_at: 15.days.ago, updated_at: 20.days.ago) + create(:procedure, created_at: 15.days.ago, updated_at: 1.hour.ago) end after { Timecop.return } @@ -104,24 +104,24 @@ describe StatsController, type: :controller do # dossier_p1_c: 5 days before do - procedure_1 = FactoryBot.create(:procedure) - procedure_2 = FactoryBot.create(:procedure) - dossier_p1_a = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :en_construction_at => 2.months.ago.beginning_of_month, - :processed_at => 2.months.ago.beginning_of_month + 3.days) - dossier_p1_b = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :en_construction_at => 2.months.ago.beginning_of_month, - :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_c = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :en_construction_at => 1.month.ago.beginning_of_month, - :processed_at => 1.month.ago.beginning_of_month + 5.days) - dossier_p2_a = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_2, - :en_construction_at => 2.months.ago.beginning_of_month, - :processed_at => 2.months.ago.beginning_of_month + 4.days) + procedure_1 = create(:procedure) + procedure_2 = create(:procedure) + dossier_p1_a = create(:dossier, :accepte, + procedure: procedure_1, + en_construction_at: 2.months.ago.beginning_of_month, + processed_at: 2.months.ago.beginning_of_month + 3.days) + dossier_p1_b = create(:dossier, :accepte, + procedure: procedure_1, + en_construction_at: 2.months.ago.beginning_of_month, + processed_at: 2.months.ago.beginning_of_month + 1.day) + dossier_p1_c = create(:dossier, :accepte, + procedure: procedure_1, + en_construction_at: 1.month.ago.beginning_of_month, + processed_at: 1.month.ago.beginning_of_month + 5.days) + dossier_p2_a = create(:dossier, :accepte, + procedure: procedure_2, + en_construction_at: 2.months.ago.beginning_of_month, + processed_at: 2.months.ago.beginning_of_month + 4.days) @expected_hash = { (2.months.ago.beginning_of_month).to_s => 3.0, @@ -149,28 +149,28 @@ describe StatsController, type: :controller do # dossier_p1_c: 50 minutes before do - procedure_1 = FactoryBot.create(:procedure, :with_type_de_champ, :types_de_champ_count => 24) - procedure_2 = FactoryBot.create(:procedure, :with_type_de_champ, :types_de_champ_count => 48) - dossier_p1_a = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :created_at => 2.months.ago.beginning_of_month, - :en_construction_at => 2.months.ago.beginning_of_month + 30.minutes, - :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_b = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :created_at => 2.months.ago.beginning_of_month, - :en_construction_at => 2.months.ago.beginning_of_month + 10.minutes, - :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_c = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_1, - :created_at => 1.month.ago.beginning_of_month, - :en_construction_at => 1.month.ago.beginning_of_month + 50.minutes, - :processed_at => 1.month.ago.beginning_of_month + 1.day) - dossier_p2_a = FactoryBot.create(:dossier, :accepte, - :procedure => procedure_2, - :created_at => 2.months.ago.beginning_of_month, - :en_construction_at => 2.months.ago.beginning_of_month + 80.minutes, - :processed_at => 2.months.ago.beginning_of_month + 1.day) + procedure_1 = create(:procedure, :with_type_de_champ, types_de_champ_count: 24) + procedure_2 = create(:procedure, :with_type_de_champ, types_de_champ_count: 48) + dossier_p1_a = create(:dossier, :accepte, + procedure: procedure_1, + created_at: 2.months.ago.beginning_of_month, + en_construction_at: 2.months.ago.beginning_of_month + 30.minutes, + processed_at: 2.months.ago.beginning_of_month + 1.day) + dossier_p1_b = create(:dossier, :accepte, + procedure: procedure_1, + created_at: 2.months.ago.beginning_of_month, + en_construction_at: 2.months.ago.beginning_of_month + 10.minutes, + processed_at: 2.months.ago.beginning_of_month + 1.day) + dossier_p1_c = create(:dossier, :accepte, + procedure: procedure_1, + created_at: 1.month.ago.beginning_of_month, + en_construction_at: 1.month.ago.beginning_of_month + 50.minutes, + processed_at: 1.month.ago.beginning_of_month + 1.day) + dossier_p2_a = create(:dossier, :accepte, + procedure: procedure_2, + created_at: 2.months.ago.beginning_of_month, + en_construction_at: 2.months.ago.beginning_of_month + 80.minutes, + processed_at: 2.months.ago.beginning_of_month + 1.day) @expected_hash = { (2.months.ago.beginning_of_month).to_s => 30.0, diff --git a/spec/controllers/users/profil_controller_spec.rb b/spec/controllers/users/profil_controller_spec.rb index 2d79fca85..cf163ba93 100644 --- a/spec/controllers/users/profil_controller_spec.rb +++ b/spec/controllers/users/profil_controller_spec.rb @@ -56,7 +56,7 @@ describe Users::ProfilController, type: :controller do end it { expect(response).to redirect_to(profil_path) } - it { expect(flash.alert).to eq(['Email invalide']) } + it { expect(flash.alert).to eq(['Courriel invalide']) } end context 'when the user has an instructeur role' do diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index 7734b9538..e9ca9fcee 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -62,7 +62,7 @@ describe Users::SessionsController, type: :controller do subject expect(response).to render_template(:new) - expect(flash.alert).to eq(I18n.t('devise.failure.invalid')) + expect(flash.alert).to eq('Courriel ou mot de passe incorrect.') end end end diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 1a20f4e50..7d057c320 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -161,23 +161,20 @@ FactoryBot.define do existing_type_de_champ_text = types_de_champ.find { |tdc| tdc.libelle == 'Nom' } type_de_champ_text = existing_type_de_champ_text || build( :type_de_champ_text, - order_place: 0, - procedure: champ_repetition.dossier.procedure, + position: 0, parent: champ_repetition.type_de_champ, libelle: 'Nom' ) - types_de_champ << type_de_champ_text existing_type_de_champ_number = types_de_champ.find { |tdc| tdc.libelle == 'Age' } type_de_champ_number = existing_type_de_champ_number || build( :type_de_champ_number, - order_place: 1, - procedure: champ_repetition.dossier.procedure, + position: 1, parent: champ_repetition.type_de_champ, libelle: 'Age' ) - types_de_champ << type_de_champ_number + champ_repetition.type_de_champ.types_de_champ << [type_de_champ_text, type_de_champ_number] champ_repetition.champs << [ build(:champ_text, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_text, parent: champ_repetition), build(:champ_number, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_number, parent: champ_repetition), @@ -198,13 +195,11 @@ FactoryBot.define do after(:build) do |champ_repetition, _evaluator| type_de_champ_pj0 = build(:type_de_champ_piece_justificative, - procedure: champ_repetition.dossier.procedure, - order_place: 0, + position: 0, parent: champ_repetition.type_de_champ, libelle: 'Justificatif de domicile') type_de_champ_pj1 = build(:type_de_champ_piece_justificative, - procedure: champ_repetition.dossier.procedure, - order_place: 1, + position: 1, parent: champ_repetition.type_de_champ, libelle: 'Carte d\'identité') diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index c5aa95d98..cb10c08b7 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -15,6 +15,8 @@ FactoryBot.define do procedure = create(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private) end + dossier.revision = procedure.active_revision + # Assign the procedure to the dossier through the groupe_instructeur if dossier.groupe_instructeur.nil? dossier.groupe_instructeur = procedure.defaut_groupe_instructeur @@ -71,7 +73,7 @@ FactoryBot.define do linked_dossier = create(:dossier, :en_construction) # find first type de champ dossier_link - type_de_champ = dossier.procedure.types_de_champ.find do |t| + type_de_champ = dossier.types_de_champ.find do |t| t.type_champ == TypeDeChamp.type_champs.fetch(:dossier_link) end @@ -202,7 +204,7 @@ FactoryBot.define do trait :with_all_champs do after(:create) do |dossier, _evaluator| - dossier.champs = dossier.procedure.types_de_champ.map do |type_de_champ| + dossier.champs = dossier.types_de_champ.map do |type_de_champ| build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ) end dossier.save! @@ -211,7 +213,7 @@ FactoryBot.define do trait :with_all_annotations do after(:create) do |dossier, _evaluator| - dossier.champs = dossier.procedure.types_de_champ.map do |type_de_champ| + dossier.champs = dossier.types_de_champ.map do |type_de_champ| build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ) end dossier.save! diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index cf379bac5..4466b1bde 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -16,6 +16,9 @@ FactoryBot.define do transient do administrateur { } instructeurs { [] } + types_de_champ { [] } + types_de_champ_private { [] } + updated_at { nil } end after(:build) do |procedure, evaluator| @@ -24,10 +27,35 @@ FactoryBot.define do elsif procedure.administrateurs.empty? procedure.administrateurs = [create(:administrateur)] end + procedure.draft_revision = build(:procedure_revision, procedure: procedure) + + evaluator.types_de_champ.each do |type_de_champ| + type_de_champ.revision = procedure.draft_revision + type_de_champ.private = false + type_de_champ.revision.revision_types_de_champ << build(:procedure_revision_type_de_champ, + revision: procedure.draft_revision, + position: type_de_champ.order_place, + type_de_champ: type_de_champ) + end + + evaluator.types_de_champ_private.each do |type_de_champ| + type_de_champ.revision = procedure.draft_revision + type_de_champ.private = true + type_de_champ.revision.revision_types_de_champ_private << build(:procedure_revision_type_de_champ, + revision: procedure.draft_revision, + position: type_de_champ.order_place, + type_de_champ: type_de_champ) + end end after(:create) do |procedure, evaluator| evaluator.instructeurs.each { |i| i.assign_to_procedure(procedure) } + + if evaluator.updated_at + procedure.update_column(:updated_at, evaluator.updated_at) + end + + procedure.reload end factory :procedure_with_dossiers do @@ -38,9 +66,7 @@ FactoryBot.define do after(:create) do |procedure, evaluator| user = create(:user) evaluator.dossiers_count.times do - dossier = procedure.new_dossier - dossier.user = user - dossier.save! + create(:dossier, procedure: procedure, user: user) end end end @@ -48,7 +74,7 @@ FactoryBot.define do factory :simple_procedure do after(:build) do |procedure, _evaluator| procedure.for_individual = true - procedure.types_de_champ << build(:type_de_champ, libelle: 'Texte obligatoire', mandatory: true) + build(:type_de_champ, libelle: 'Texte obligatoire', mandatory: true, procedure: procedure) procedure.path = generate(:published_path) procedure.publish! end @@ -96,10 +122,8 @@ FactoryBot.define do end after(:build) do |procedure, evaluator| - evaluator.types_de_champ_count.times do - type_de_champ = build(:type_de_champ) - - procedure.types_de_champ << type_de_champ + evaluator.types_de_champ_count.times do |position| + build(:type_de_champ, procedure: procedure, position: position) end end end @@ -110,68 +134,51 @@ FactoryBot.define do end after(:build) do |procedure, evaluator| - evaluator.types_de_champ_private_count.times do - type_de_champ = build(:type_de_champ, :private) - - procedure.types_de_champ_private << type_de_champ + evaluator.types_de_champ_private_count.times do |position| + build(:type_de_champ, :private, procedure: procedure, position: position) end end end trait :with_type_de_champ_mandatory do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ, mandatory: true) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ, mandatory: true, procedure: procedure) end end trait :with_datetime do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_datetime, mandatory: true) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ_datetime, mandatory: true, procedure: procedure) end end trait :with_dossier_link do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_dossier_link) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ_dossier_link, procedure: procedure) end end trait :with_yes_no do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_yes_no) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ_yes_no, procedure: procedure) end end trait :with_piece_justificative do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_piece_justificative) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ_piece_justificative, procedure: procedure) end end trait :with_repetition do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_repetition) - procedure.types_de_champ << type_de_champ - - type_de_champ.types_de_champ << build(:type_de_champ, libelle: 'sub type de champ') + build(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) end end trait :with_number do after(:build) do |procedure, _evaluator| - type_de_champ = build(:type_de_champ_number) - - procedure.types_de_champ << type_de_champ + build(:type_de_champ_number, procedure: procedure) end end @@ -228,35 +235,35 @@ FactoryBot.define do trait :with_all_champs_mandatory do after(:build) do |procedure, _evaluator| - procedure.types_de_champ = TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| + TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", procedure: procedure, mandatory: true, libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, mandatory: true, libelle: libelle, position: index) end - procedure.types_de_champ << build(:type_de_champ_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'simple_choice_drop_down_list_long') - procedure.types_de_champ << build(:type_de_champ_multiple_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'multiple_choice_drop_down_list_long') + build(:type_de_champ_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'simple_choice_drop_down_list_long', position: TypeDeChamp.type_champs.size) + build(:type_de_champ_multiple_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'multiple_choice_drop_down_list_long', position: TypeDeChamp.type_champs.size + 1) end end trait :with_all_champs do after(:build) do |procedure, _evaluator| - procedure.types_de_champ = TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| + TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", procedure: procedure, libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, libelle: libelle, position: index) end end end trait :with_all_annotations do after(:build) do |procedure, _evaluator| - procedure.types_de_champ_private = TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| + TypeDeChamp.type_champs.map.with_index do |(libelle, type_champ), index| if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", procedure: procedure, private: true, libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, private: true, libelle: libelle, position: index) end end end diff --git a/spec/factories/procedure_revision.rb b/spec/factories/procedure_revision.rb new file mode 100644 index 000000000..050749066 --- /dev/null +++ b/spec/factories/procedure_revision.rb @@ -0,0 +1,4 @@ +FactoryBot.define do + factory :procedure_revision do + end +end diff --git a/spec/factories/procedure_revision_type_de_champ.rb b/spec/factories/procedure_revision_type_de_champ.rb new file mode 100644 index 000000000..62359ab98 --- /dev/null +++ b/spec/factories/procedure_revision_type_de_champ.rb @@ -0,0 +1,4 @@ +FactoryBot.define do + factory :procedure_revision_type_de_champ do + end +end diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 340f6060d..0c4f2b7cc 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -7,7 +7,40 @@ FactoryBot.define do mandatory { false } add_attribute(:private) { false } - association :procedure + transient do + procedure { nil } + position { nil } + parent { nil } + end + + after(:build) do |type_de_champ, evaluator| + if evaluator.procedure + type_de_champ.revision = evaluator.procedure.active_revision + + build(:procedure_revision_type_de_champ, + position: evaluator.position, + revision: evaluator.procedure.active_revision, + type_de_champ: type_de_champ) + + if type_de_champ.private? + type_de_champ.revision.types_de_champ_private << type_de_champ + else + type_de_champ.revision.types_de_champ << type_de_champ + end + elsif evaluator.parent + type_de_champ.revision = evaluator.parent.revision + type_de_champ.order_place = evaluator.position || evaluator.parent.types_de_champ.size + evaluator.parent.types_de_champ << type_de_champ + else + type_de_champ.order_place = evaluator.position + end + end + + trait :private do + add_attribute(:private) { true } + sequence(:libelle) { |n| "Libelle champ privé #{n}" } + sequence(:description) { |n| "description du champ privé #{n}" } + end factory :type_de_champ_text do type_champ { TypeDeChamp.type_champs.fetch(:text) } @@ -96,8 +129,8 @@ FactoryBot.define do factory :type_de_champ_piece_justificative do type_champ { TypeDeChamp.type_champs.fetch(:piece_justificative) } - after(:build) do |tc, _evaluator| - tc.piece_justificative_template.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") + after(:build) do |type_de_champ, _evaluator| + type_de_champ.piece_justificative_template.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") end end factory :type_de_champ_siret do @@ -109,17 +142,23 @@ FactoryBot.define do factory :type_de_champ_repetition do type_champ { TypeDeChamp.type_champs.fetch(:repetition) } + transient do + types_de_champ { [] } + end + + after(:build) do |type_de_champ_repetition, evaluator| + evaluator.types_de_champ.each do |type_de_champ| + type_de_champ.revision = type_de_champ_repetition.revision + type_de_champ.order_place = type_de_champ_repetition.types_de_champ.size + type_de_champ_repetition.types_de_champ << type_de_champ + end + end + trait :with_types_de_champ do after(:build) do |type_de_champ, _evaluator| - type_de_champ.types_de_champ << build(:type_de_champ, procedure: type_de_champ.procedure, libelle: 'sub type de champ') + build(:type_de_champ, libelle: 'sub type de champ', parent: type_de_champ) end end end - - trait :private do - add_attribute(:private) { true } - sequence(:libelle) { |n| "Libelle champ privé #{n}" } - sequence(:description) { |n| "description du champ privé #{n}" } - end end end diff --git a/spec/features/admin/procedure_cloning_spec.rb b/spec/features/admin/procedure_cloning_spec.rb index 8f67aea46..3d490f9c0 100644 --- a/spec/features/admin/procedure_cloning_spec.rb +++ b/spec/features/admin/procedure_cloning_spec.rb @@ -6,11 +6,11 @@ feature 'As an administrateur I wanna clone a procedure', js: true do let(:administrateur) { create(:administrateur) } before do - create :procedure, :with_service, :with_instructeur, - aasm_state: :publiee, published_at: Time.zone.now, + create(:procedure, :with_service, :with_instructeur, + aasm_state: :publiee, administrateurs: [administrateur], libelle: 'libellé de la procédure', - path: 'libelle-de-la-procedure' + path: 'libelle-de-la-procedure') login_as administrateur.user, scope: :user end diff --git a/spec/features/instructeurs/instruction_spec.rb b/spec/features/instructeurs/instruction_spec.rb index f5a43c5b3..22e083ad4 100644 --- a/spec/features/instructeurs/instruction_spec.rb +++ b/spec/features/instructeurs/instruction_spec.rb @@ -91,7 +91,7 @@ feature 'Instructing a dossier:' do end scenario 'A instructeur can see the personnes impliquées' do - instructeur2 = FactoryBot.create(:instructeur, password: password) + instructeur2 = create(:instructeur, password: password) log_in(instructeur.email, password) @@ -113,8 +113,8 @@ feature 'Instructing a dossier:' do end scenario 'A instructeur can send a dossier to several instructeurs', js: true do - instructeur_2 = FactoryBot.create(:instructeur) - instructeur_3 = FactoryBot.create(:instructeur) + instructeur_2 = create(:instructeur) + instructeur_3 = create(:instructeur) procedure.defaut_groupe_instructeur.instructeurs << [instructeur_2, instructeur_3] send_dossier = double() diff --git a/spec/features/new_administrateur/types_de_champ_spec.rb b/spec/features/new_administrateur/types_de_champ_spec.rb index c3c5fb77a..20d908f3f 100644 --- a/spec/features/new_administrateur/types_de_champ_spec.rb +++ b/spec/features/new_administrateur/types_de_champ_spec.rb @@ -122,7 +122,7 @@ feature 'As an administrateur I can edit types de champ', js: true do fill_in 'champ-0-libelle', with: 'Libellé de champ carte', fill_options: { clear: :backspace } check 'Cadastres' - wait_until { procedure.types_de_champ.first.cadastres == true } + wait_until { procedure.draft_types_de_champ.first.cadastres == true } expect(page).to have_content('Formulaire enregistré') preview_window = window_opened_by { click_on 'Prévisualiser le formulaire' } @@ -139,7 +139,7 @@ feature 'As an administrateur I can edit types de champ', js: true do fill_in 'champ-0-libelle', with: 'Libellé de champ menu déroulant', fill_options: { clear: :backspace } fill_in 'champ-0-drop_down_list_value', with: 'Un menu', fill_options: { clear: :backspace } - wait_until { procedure.types_de_champ.first.drop_down_list_options == ['', 'Un menu'] } + wait_until { procedure.draft_types_de_champ.first.drop_down_list_options == ['', 'Un menu'] } expect(page).to have_content('Formulaire enregistré') page.refresh diff --git a/spec/features/sessions/sign_in_spec.rb b/spec/features/sessions/sign_in_spec.rb index b97980d6a..84361db70 100644 --- a/spec/features/sessions/sign_in_spec.rb +++ b/spec/features/sessions/sign_in_spec.rb @@ -60,7 +60,7 @@ feature 'Signin in:' do click_on 'Connexion' sign_in_with user.email, password - expect(page).to have_content 'Vous devez confirmer votre adresse email pour continuer' + expect(page).to have_content('Vous devez confirmer votre compte par courriel.') end end end diff --git a/spec/features/users/brouillon_spec.rb b/spec/features/users/brouillon_spec.rb index 2f17858a3..eed00238c 100644 --- a/spec/features/users/brouillon_spec.rb +++ b/spec/features/users/brouillon_spec.rb @@ -97,9 +97,7 @@ feature 'The user' do end let(:procedure_with_repetition) do - tdc = create(:type_de_champ_repetition, libelle: 'repetition') - tdc.types_de_champ << create(:type_de_champ_text, libelle: 'text') - create(:procedure, :published, :for_individual, types_de_champ: [tdc]) + create(:procedure, :published, :for_individual, :with_repetition) end scenario 'fill a dossier with repetition', js: true do @@ -107,13 +105,13 @@ feature 'The user' do fill_individual - fill_in('text', with: 'super texte') - expect(page).to have_field('text', with: 'super texte') + fill_in('sub type de champ', with: 'super texte') + expect(page).to have_field('sub type de champ', with: 'super texte') click_on 'Ajouter un élément pour' within '.row-1' do - fill_in('text', with: 'un autre texte') + fill_in('sub type de champ', with: 'un autre texte') end expect(page).to have_content('Supprimer', count: 2) @@ -132,7 +130,7 @@ feature 'The user' do end let(:simple_procedure) do - tdcs = [create(:type_de_champ, mandatory: true, libelle: 'texte obligatoire')] + tdcs = [build(:type_de_champ, mandatory: true, libelle: 'texte obligatoire')] create(:procedure, :published, :for_individual, types_de_champ: tdcs) end @@ -161,14 +159,14 @@ feature 'The user' do end let(:procedure_with_pj) do - tdcs = [create(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative')] + tdcs = [build(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative')] create(:procedure, :published, :for_individual, types_de_champ: tdcs) end let(:procedure_with_pjs) do tdcs = [ - create(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative 1', order_place: 1), - create(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative 2', order_place: 2) + build(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative 1', position: 1), + build(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative 2', position: 2) ] create(:procedure, :published, :for_individual, types_de_champ: tdcs) end diff --git a/spec/features/users/invite_spec.rb b/spec/features/users/invite_spec.rb index 9ca3c6217..e78fef219 100644 --- a/spec/features/users/invite_spec.rb +++ b/spec/features/users/invite_spec.rb @@ -41,7 +41,7 @@ feature 'Invitations' do # Confirm the account # (The user should be redirected to the dossier they was invited on) click_confirmation_link_for invite.email - expect(page).to have_content('Votre compte a été activé') + expect(page).to have_content('Votre compte a bien été confirmé.') expect(page).to have_current_path(brouillon_dossier_path(dossier)) end end diff --git a/spec/features/users/linked_dropdown_spec.rb b/spec/features/users/linked_dropdown_spec.rb index cc54de3ff..3ba3c45cd 100644 --- a/spec/features/users/linked_dropdown_spec.rb +++ b/spec/features/users/linked_dropdown_spec.rb @@ -13,12 +13,10 @@ feature 'linked dropdown lists' do Secondary 2.3 END_OF_LIST end - let(:type_de_champ) { create(:type_de_champ_linked_drop_down_list, libelle: 'linked dropdown', drop_down_list_value: list_items) } + let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, libelle: 'linked dropdown', drop_down_list_value: list_items) } let!(:procedure) do - p = create(:procedure, :published, :for_individual) - p.types_de_champ << type_de_champ - p + create(:procedure, :published, :for_individual, types_de_champ: [type_de_champ]) end let(:user_dossier) { user.dossiers.first } diff --git a/spec/features/users/managing_password_spec.rb b/spec/features/users/managing_password_spec.rb index 52aa4c109..96ee9bdd5 100644 --- a/spec/features/users/managing_password_spec.rb +++ b/spec/features/users/managing_password_spec.rb @@ -13,7 +13,7 @@ feature 'Managing password:' do perform_enqueued_jobs do click_on 'Réinitialiser' end - expect(page).to have_content 'vous allez recevoir un lien de réinitialisation par email' + expect(page).to have_content('Si votre courriel existe dans notre base de données, vous recevrez un lien vous permettant de récupérer votre mot de passe.') click_reset_password_link_for user.email expect(page).to have_content 'Changement de mot de passe' @@ -21,7 +21,7 @@ feature 'Managing password:' do fill_in 'user_password', with: new_password fill_in 'user_password_confirmation', with: new_password click_on 'Changer le mot de passe' - expect(page).to have_content('Votre mot de passe a été changé avec succès') + expect(page).to have_content('Votre mot de passe a bien été modifié.') end end @@ -40,7 +40,7 @@ feature 'Managing password:' do perform_enqueued_jobs do click_on 'Réinitialiser' end - expect(page).to have_content 'vous allez recevoir un lien de réinitialisation par email' + expect(page).to have_content('Si votre courriel existe dans notre base de données, vous recevrez un lien vous permettant de récupérer votre mot de passe.') click_reset_password_link_for user.email @@ -49,7 +49,7 @@ feature 'Managing password:' do fill_in 'user_password', with: new_password fill_in 'user_password_confirmation', with: new_password click_on 'Changer le mot de passe' - expect(page).to have_content('Votre mot de passe a été changé avec succès') + expect(page).to have_content('Votre mot de passe a bien été modifié.') end end end diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index 65182a303..000a7a3b4 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -10,7 +10,7 @@ feature 'Signing up:' do expect(page).to have_content "nous avons besoin de vérifier votre adresse #{user_email}" click_confirmation_link_for user_email - expect(page).to have_content 'Votre compte a été activé' + expect(page).to have_content('Votre compte a bien été confirmé.') expect(page).to have_current_path dossiers_path end @@ -80,7 +80,7 @@ feature 'Signing up:' do # After confirmation, the user is redirected to the procedure they were initially starting # (even when confirming the account in another browser). expect(page).to have_current_path(commencer_path(path: procedure.path)) - expect(page).to have_content 'Votre compte a été activé' + expect(page).to have_content I18n.t('devise.confirmations.confirmed') click_on 'Commencer la démarche' expect(page).to have_current_path identite_dossier_path(procedure.reload.dossiers.last) @@ -112,7 +112,7 @@ feature 'Signing up:' do # After confirmation, the user is redirected to the procedure they were initially starting # (even when confirming the account in another browser). expect(page).to have_current_path(commencer_path(path: procedure.path)) - expect(page).to have_content 'Votre compte a été activé' + expect(page).to have_content I18n.t('devise.confirmations.confirmed') expect(page).to have_content 'Commencer la démarche' end end diff --git a/spec/jobs/find_dubious_procedures_job_spec.rb b/spec/jobs/find_dubious_procedures_job_spec.rb index 3bd6fd50f..acbc8df52 100644 --- a/spec/jobs/find_dubious_procedures_job_spec.rb +++ b/spec/jobs/find_dubious_procedures_job_spec.rb @@ -1,23 +1,24 @@ RSpec.describe FindDubiousProceduresJob, type: :job do describe 'perform' do let(:mailer_double) { double('mailer', deliver_later: true) } - let(:procedure) { create(:procedure) } - let(:allowed_tdc) { create(:type_de_champ, libelle: 'fournir') } + let(:procedure) { create(:procedure, types_de_champ: tdcs) } + let(:allowed_tdc) { build(:type_de_champ, libelle: 'fournir') } before do + procedure + allow(AdministrationMailer).to receive(:dubious_procedures) do |arg| @dubious_procedures_args = arg end.and_return(mailer_double) - procedure.types_de_champ << tdcs FindDubiousProceduresJob.new.perform end context 'with suspicious champs' do let(:forbidden_tdcs) do [ - create(:type_de_champ, libelle: 'num de securite sociale, stp'), - create(:type_de_champ, libelle: "t'aurais une carte bancaire ?") + build(:type_de_champ, libelle: 'num de securite sociale, stp'), + build(:type_de_champ, libelle: "t'aurais une carte bancaire ?") ] end diff --git a/spec/jobs/tmp_dossiers_migrate_revisions_job_spec.rb b/spec/jobs/tmp_dossiers_migrate_revisions_job_spec.rb deleted file mode 100644 index ce2984175..000000000 --- a/spec/jobs/tmp_dossiers_migrate_revisions_job_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -RSpec.describe TmpDossiersMigrateRevisionsJob, type: :job do - let(:procedure) { create(:procedure, :published) } - let!(:dossier1) { create(:dossier, procedure: procedure, updated_at: 1.day.ago) } - let!(:dossier2) { create(:dossier, procedure: procedure, updated_at: 2.days.ago) } - - context "add revision to dossiers" do - before do - RevisionsMigration.add_revisions(procedure) - end - - it { - expect(dossier1.revision).to be_nil - expect(dossier2.revision).to be_nil - - TmpDossiersMigrateRevisionsJob.new.perform([]) - [dossier1, dossier2].each(&:reload) - - expect(dossier1.revision).to eq procedure.active_revision - expect(dossier2.revision).to eq procedure.active_revision - expect(dossier1.updated_at < 1.day.ago).to be_truthy - expect(dossier2.updated_at < 1.day.ago).to be_truthy - } - end -end diff --git a/spec/models/champ_spec.rb b/spec/models/champ_spec.rb index 62551648b..870aa3fc1 100644 --- a/spec/models/champ_spec.rb +++ b/spec/models/champ_spec.rb @@ -427,10 +427,9 @@ describe Champ do end describe 'repetition' do - let(:procedure) { build(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private) } - let(:tdc_text) { build(:type_de_champ_text, procedure: procedure) } - let(:tdc_integer) { build(:type_de_champ_integer_number, procedure: procedure) } - let(:tdc_repetition) { build(:type_de_champ_repetition, procedure: procedure, types_de_champ: [tdc_text, tdc_integer]) } + let(:procedure) { create(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private, types_de_champ: [build(:type_de_champ_repetition, types_de_champ: [tdc_text, tdc_integer])]) } + let(:tdc_text) { build(:type_de_champ_text) } + let(:tdc_integer) { build(:type_de_champ_integer_number) } let(:dossier) { create(:dossier, procedure: procedure) } let(:champ) { dossier.champs.find(&:repetition?) } @@ -438,12 +437,6 @@ describe Champ do let(:champ_integer) { champ.champs.find { |c| c.type_champ == 'integer_number' } } let(:champ_text_attrs) { attributes_for(:champ_text, type_de_champ: tdc_text, row: 1) } - before do - procedure.types_de_champ << tdc_repetition - procedure.save! - procedure.reload - end - context 'when creating the model directly' do let(:champ_text_row_1) { create(:champ_text, type_de_champ: tdc_text, row: 2, parent: champ, dossier: nil) } diff --git a/spec/models/champs/decimal_number_champ_spec.rb b/spec/models/champs/decimal_number_champ_spec.rb index 3684beeec..0ad8be67e 100644 --- a/spec/models/champs/decimal_number_champ_spec.rb +++ b/spec/models/champs/decimal_number_champ_spec.rb @@ -18,7 +18,7 @@ describe Champs::DecimalNumberChamp do let(:value) { 'toto' } it { is_expected.to_not be_valid } - it { expect(subject.errors[:value]).to eq(["« #{subject.libelle} » doit être un nombre"]) } + it { expect(subject.errors[:value]).to eq(["« #{subject.libelle} » n'est pas un nombre"]) } end context 'when the value is blank' do diff --git a/spec/models/champs/header_section_champ_spec.rb b/spec/models/champs/header_section_champ_spec.rb index 2e604bb5c..e09c5fb87 100644 --- a/spec/models/champs/header_section_champ_spec.rb +++ b/spec/models/champs/header_section_champ_spec.rb @@ -2,11 +2,11 @@ describe Champs::HeaderSectionChamp do describe '#section_index' do let(:types_de_champ) do [ - create(:type_de_champ_header_section, order_place: 1), - create(:type_de_champ_civilite, order_place: 2), - create(:type_de_champ_text, order_place: 3), - create(:type_de_champ_header_section, order_place: 4), - create(:type_de_champ_email, order_place: 5) + build(:type_de_champ_header_section, position: 1), + build(:type_de_champ_civilite, position: 2), + build(:type_de_champ_text, position: 3), + build(:type_de_champ_header_section, position: 4), + build(:type_de_champ_email, position: 5) ] end @@ -23,17 +23,12 @@ describe Champs::HeaderSectionChamp do end context 'for repetition champs' do - let(:procedure) { create(:procedure, :with_repetition) } + let(:procedure) { create(:procedure, types_de_champ: [build(:type_de_champ_repetition, types_de_champ: types_de_champ)]) } let(:dossier) { create(:dossier, procedure: procedure) } - let(:repetition_tdc) { procedure.types_de_champ.find(&:repetition?) } let(:first_header) { dossier.champs.first.champs[0] } let(:second_header) { dossier.champs.first.champs[3] } - before do - repetition_tdc.types_de_champ = types_de_champ - end - it 'returns the index of the section in the repetition (starting from 1)' do expect(first_header.section_index).to eq 1 expect(second_header.section_index).to eq 2 diff --git a/spec/models/concern/tags_substitution_concern_spec.rb b/spec/models/concern/tags_substitution_concern_spec.rb index 37acae977..e0aab4b00 100644 --- a/spec/models/concern/tags_substitution_concern_spec.rb +++ b/spec/models/concern/tags_substitution_concern_spec.rb @@ -97,8 +97,8 @@ describe TagsSubstitutionConcern, type: :model do context 'when the procedure has a type de champ named libelleA et libelleB' do let(:types_de_champ) do [ - create(:type_de_champ, libelle: 'libelleA'), - create(:type_de_champ, libelle: 'libelleB') + build(:type_de_champ, libelle: 'libelleA'), + build(:type_de_champ, libelle: 'libelleB') ] end @@ -141,7 +141,7 @@ describe TagsSubstitutionConcern, type: :model do context 'when the procedure has a type de champ with apostrophes' do let(:types_de_champ) do [ - create(:type_de_champ, libelle: "Intitulé de l'‘«\"évènement\"»’") + build(:type_de_champ, libelle: "Intitulé de l'‘«\"évènement\"»’") ] end @@ -165,9 +165,9 @@ describe TagsSubstitutionConcern, type: :model do let(:template) { '--Répétition--' } let(:types_de_champ) do [ - create(:type_de_champ_repetition, libelle: 'Répétition', types_de_champ: [ - create(:type_de_champ_text, libelle: 'Nom', order_place: 1), - create(:type_de_champ_text, libelle: 'Prénom', order_place: 2) + build(:type_de_champ_repetition, libelle: 'Répétition', types_de_champ: [ + build(:type_de_champ_text, libelle: 'Nom', order_place: 1), + build(:type_de_champ_text, libelle: 'Prénom', order_place: 2) ]) ] end @@ -190,7 +190,7 @@ describe TagsSubstitutionConcern, type: :model do context 'when the procedure has a linked drop down menus type de champ' do let(:type_de_champ) do - create(:type_de_champ_linked_drop_down_list, libelle: 'libelle') + build(:type_de_champ_linked_drop_down_list, libelle: 'libelle') end let(:types_de_champ) { [type_de_champ] } let(:template) { 'tout : --libelle--, primaire : --libelle/primaire--, secondaire : --libelle/secondaire--' } @@ -219,7 +219,7 @@ describe TagsSubstitutionConcern, type: :model do let(:types_de_champ) do [ type_de_champ, - create(:type_de_champ_header_section, libelle: 'libelle') + build(:type_de_champ_header_section, libelle: 'libelle') ] end @@ -253,7 +253,7 @@ describe TagsSubstitutionConcern, type: :model do end context 'when the procedure has a type de champ prive named libelleA' do - let(:types_de_champ_private) { [create(:type_de_champ, :private, libelle: 'libelleA')] } + let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'libelleA')] } context 'and it is used in the template' do let(:template) { '--libelleA--' } @@ -274,13 +274,13 @@ describe TagsSubstitutionConcern, type: :model do # The dossier just transitionned from brouillon to en construction, # so champs private are not valid tags yet - let(:types_de_champ_private) { [create(:type_de_champ, :private, libelle: 'libelleA')] } + let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'libelleA')] } it { is_expected.to eq('--libelleA--') } end context 'champs publics are valid tags' do - let(:types_de_champ) { [create(:type_de_champ, libelle: 'libelleA')] } + let(:types_de_champ) { [build(:type_de_champ, libelle: 'libelleA')] } before { dossier.champs.first.update(value: 'libelle1') } @@ -291,8 +291,8 @@ describe TagsSubstitutionConcern, type: :model do context 'when the procedure has 2 types de champ date and datetime' do let(:types_de_champ) do [ - create(:type_de_champ_date, libelle: TypeDeChamp.type_champs.fetch(:date)), - create(:type_de_champ_datetime, libelle: TypeDeChamp.type_champs.fetch(:datetime)) + build(:type_de_champ_date, libelle: TypeDeChamp.type_champs.fetch(:date)), + build(:type_de_champ_datetime, libelle: TypeDeChamp.type_champs.fetch(:datetime)) ] end @@ -358,13 +358,13 @@ describe TagsSubstitutionConcern, type: :model do shared_examples "treat all kinds of space as equivalent" do context 'and the champ has a non breaking space' do - let(:types_de_champ) { [create(:type_de_champ, libelle: 'mon tag')] } + let(:types_de_champ) { [build(:type_de_champ, libelle: 'mon tag')] } it { is_expected.to eq('valeur') } end context 'and the champ has an ordinary space' do - let(:types_de_champ) { [create(:type_de_champ, libelle: 'mon tag')] } + let(:types_de_champ) { [build(:type_de_champ, libelle: 'mon tag')] } it { is_expected.to eq('valeur') } end @@ -401,12 +401,12 @@ describe TagsSubstitutionConcern, type: :model do let(:types_de_champ) do [ - create(:type_de_champ, libelle: 'public'), - create(:type_de_champ_header_section, libelle: 'entête de section'), - create(:type_de_champ_explication, libelle: 'explication') + build(:type_de_champ, libelle: 'public'), + build(:type_de_champ_header_section, libelle: 'entête de section'), + build(:type_de_champ_explication, libelle: 'explication') ] end - let(:types_de_champ_private) { [create(:type_de_champ, :private, libelle: 'privé')] } + let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'privé')] } context 'do not generate tags for champs that cannot have usager content' do it { is_expected.not_to include(include({ libelle: 'entête de section' })) } diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index e699b4d13..4a61017a0 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -29,14 +29,8 @@ describe Dossier do end describe 'with_champs' do - let(:procedure) { create(:procedure) } - let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) } - - before do - create(:type_de_champ, libelle: 'l1', order_place: 1, procedure: procedure) - create(:type_de_champ, libelle: 'l3', order_place: 3, procedure: procedure) - create(:type_de_champ, libelle: 'l2', order_place: 2, procedure: procedure) - end + let(:procedure) { create(:procedure, types_de_champ: [build(:type_de_champ, libelle: 'l1', position: 1), build(:type_de_champ, libelle: 'l3', position: 3), build(:type_de_champ, libelle: 'l2', position: 2)]) } + let(:dossier) { create(:dossier, procedure: procedure) } it do expect(Dossier.with_champs.find(dossier.id).champs.map(&:libelle)).to match(['l1', 'l2', 'l3']) @@ -255,27 +249,15 @@ describe Dossier do end describe '#champs' do - let(:procedure) { create(:procedure) } - let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) } - - before do - create(:type_de_champ, libelle: 'l1', order_place: 1, procedure: procedure) - create(:type_de_champ, libelle: 'l3', order_place: 3, procedure: procedure) - create(:type_de_champ, libelle: 'l2', order_place: 2, procedure: procedure) - end + let(:procedure) { create(:procedure, types_de_champ: [build(:type_de_champ, :private, libelle: 'l1', position: 1), build(:type_de_champ, :private, libelle: 'l3', position: 3), build(:type_de_champ, :private, libelle: 'l2', position: 2)]) } + let(:dossier) { create(:dossier, procedure: procedure) } it { expect(dossier.champs.pluck(:libelle)).to match(['l1', 'l2', 'l3']) } end describe '#champs_private' do - let(:procedure) { create :procedure } - let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) } - - before do - create :type_de_champ, :private, libelle: 'l1', order_place: 1, procedure: procedure - create :type_de_champ, :private, libelle: 'l3', order_place: 3, procedure: procedure - create :type_de_champ, :private, libelle: 'l2', order_place: 2, procedure: procedure - end + let(:procedure) { create(:procedure, types_de_champ_private: [build(:type_de_champ, :private, libelle: 'l1', position: 1), build(:type_de_champ, :private, libelle: 'l3', position: 3), build(:type_de_champ, :private, libelle: 'l2', position: 2)]) } + let(:dossier) { create(:dossier, procedure: procedure) } it { expect(dossier.champs_private.pluck(:libelle)).to match(['l1', 'l2', 'l3']) } end @@ -525,7 +507,7 @@ describe Dossier do dossier = nil expect do perform_enqueued_jobs do - dossier = Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:brouillon), user: user) + dossier = create(:dossier, procedure: procedure, state: Dossier.states.fetch(:brouillon), user: user) end end.to change(ActionMailer::Base.deliveries, :size).from(0).to(1) @@ -535,17 +517,19 @@ describe Dossier do end it "does not send an email when the dossier is created with a non brouillon state" do - expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:en_construction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) - expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:en_instruction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) - expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:accepte), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) - expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:refuse), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) - expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:sans_suite), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) + expect { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) + expect { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) + expect { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:accepte), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) + expect { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:refuse), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) + expect { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:sans_suite), user: user) }.not_to change(ActionMailer::Base.deliveries, :size) end end describe "#unspecified_attestation_champs" do - let(:procedure) { create(:procedure, attestation_template: attestation_template) } + let(:procedure) { create(:procedure, attestation_template: attestation_template, types_de_champ: types_de_champ, types_de_champ_private: types_de_champ_private) } let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) } + let(:types_de_champ) { [] } + let(:types_de_champ_private) { [] } subject { dossier.unspecified_attestation_champs.map(&:libelle) } @@ -574,14 +558,17 @@ describe Dossier do context "wich is enabled" do let(:activated) { true } - let!(:tdc_1) { create(:type_de_champ, libelle: "specified champ-in-title", procedure: procedure) } - let!(:tdc_2) { create(:type_de_champ, libelle: "unspecified champ-in-title", procedure: procedure) } - let!(:tdc_3) { create(:type_de_champ, libelle: "specified champ-in-body", procedure: procedure) } - let!(:tdc_4) { create(:type_de_champ, libelle: "unspecified champ-in-body", procedure: procedure) } - let!(:tdc_5) { create(:type_de_champ, private: true, libelle: "specified annotation privée-in-title", procedure: procedure) } - let!(:tdc_6) { create(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-title", procedure: procedure) } - let!(:tdc_7) { create(:type_de_champ, private: true, libelle: "specified annotation privée-in-body", procedure: procedure) } - let!(:tdc_8) { create(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-body", procedure: procedure) } + let(:types_de_champ) { [tdc_1, tdc_2, tdc_3, tdc_4] } + let(:types_de_champ_private) { [tdc_5, tdc_6, tdc_7, tdc_8] } + + let(:tdc_1) { build(:type_de_champ, libelle: "specified champ-in-title") } + let(:tdc_2) { build(:type_de_champ, libelle: "unspecified champ-in-title") } + let(:tdc_3) { build(:type_de_champ, libelle: "specified champ-in-body") } + let(:tdc_4) { build(:type_de_champ, libelle: "unspecified champ-in-body") } + let(:tdc_5) { build(:type_de_champ, private: true, libelle: "specified annotation privée-in-title") } + let(:tdc_6) { build(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-title") } + let(:tdc_7) { build(:type_de_champ, private: true, libelle: "specified annotation privée-in-body") } + let(:tdc_8) { build(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-body") } before do (dossier.champs + dossier.champs_private) @@ -1014,7 +1001,7 @@ describe Dossier do end context "with mandatory SIRET champ" do - let(:type_de_champ) { create(:type_de_champ_siret, mandatory: true) } + let(:type_de_champ) { create(:type_de_champ_siret, mandatory: true, procedure: procedure) } let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champ) } before do @@ -1041,12 +1028,11 @@ describe Dossier do end context "with champ repetition" do - let(:procedure) { create(:procedure) } - let(:type_de_champ_repetition) { create(:type_de_champ_repetition, mandatory: true) } + let(:procedure) { create(:procedure, types_de_champ: [type_de_champ_repetition]) } + let(:type_de_champ_repetition) { build(:type_de_champ_repetition, mandatory: true) } before do - procedure.types_de_champ << type_de_champ_repetition - type_de_champ_repetition.types_de_champ << create(:type_de_champ_text, mandatory: true) + create(:type_de_champ_text, mandatory: true, parent: type_de_champ_repetition) end context "when no champs" do @@ -1261,9 +1247,14 @@ describe Dossier do end describe "to_feature_collection" do + let(:dossier) { create(:dossier) } + let(:type_de_champ_carte) { create(:type_de_champ_carte, procedure: dossier.procedure) } let(:geo_area) { create(:geo_area, :selection_utilisateur, :polygon) } - let(:champ) { create(:champ_carte, geo_areas: [geo_area]) } - let(:dossier) { create(:dossier, champs: [champ]) } + let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) } + + before do + dossier.champs << champ_carte + end it 'should have all champs carto' do expect(dossier.to_feature_collection).to eq({ @@ -1279,7 +1270,7 @@ describe Dossier do }, properties: { area: 219.0, - champ_id: champ.stable_id, + champ_id: champ_carte.stable_id, dossier_id: dossier.id, id: geo_area.id, source: 'selection_utilisateur' diff --git a/spec/models/procedure_presentation_spec.rb b/spec/models/procedure_presentation_spec.rb index 7f7b96655..915ba52c3 100644 --- a/spec/models/procedure_presentation_spec.rb +++ b/spec/models/procedure_presentation_spec.rb @@ -50,7 +50,7 @@ describe ProcedurePresentation do describe "#fields" do context 'when the procedure can have a SIRET number' do - let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, :types_de_champ_count => 4, :types_de_champ_private_count => 4) } + let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, types_de_champ_count: 4, types_de_champ_private_count: 4) } let(:tdc_1) { procedure.types_de_champ[0] } let(:tdc_2) { procedure.types_de_champ[1] } let(:tdc_private_1) { procedure.types_de_champ_private[0] } diff --git a/spec/models/procedure_revision_spec.rb b/spec/models/procedure_revision_spec.rb index b2772e000..7c3c39c79 100644 --- a/spec/models/procedure_revision_spec.rb +++ b/spec/models/procedure_revision_spec.rb @@ -9,10 +9,6 @@ describe ProcedureRevision do type_de_champ end - before do - RevisionsMigration.add_revisions(procedure) - end - describe '#add_type_de_champ' do it 'type_de_champ' do expect(revision.types_de_champ.size).to eq(2) @@ -20,11 +16,8 @@ describe ProcedureRevision do type_champ: TypeDeChamp.type_champs.fetch(:text), libelle: "Un champ text" }) - procedure.reload + revision.reload expect(revision.types_de_champ.size).to eq(3) - expect(procedure.types_de_champ.size).to eq(3) - - expect(procedure.types_de_champ.last).to eq(new_type_de_champ) expect(revision.types_de_champ.last).to eq(new_type_de_champ) expect(revision.revision_types_de_champ.last.position).to eq(2) expect(revision.revision_types_de_champ.last.type_de_champ).to eq(new_type_de_champ) @@ -37,9 +30,8 @@ describe ProcedureRevision do libelle: "Un champ text", private: true }) - procedure.reload + revision.reload expect(revision.types_de_champ_private.size).to eq(2) - expect(procedure.types_de_champ_private.size).to eq(2) end it 'type_de_champ_repetition' do @@ -49,7 +41,6 @@ describe ProcedureRevision do libelle: "Un champ text", parent_id: type_de_champ_repetition.stable_id }) - type_de_champ_repetition.reload expect(type_de_champ_repetition.types_de_champ.size).to eq(2) end end @@ -113,26 +104,21 @@ describe ProcedureRevision do revision.remove_type_de_champ(type_de_champ.stable_id) procedure.reload expect(revision.types_de_champ.size).to eq(1) - expect(procedure.types_de_champ.size).to eq(1) end it 'type_de_champ_private' do expect(revision.types_de_champ_private.size).to eq(1) revision.remove_type_de_champ(type_de_champ_private.stable_id) - procedure.reload expect(revision.types_de_champ_private.size).to eq(0) - expect(procedure.types_de_champ_private.size).to eq(0) end it 'type_de_champ_repetition' do expect(type_de_champ_repetition.types_de_champ.size).to eq(1) expect(revision.types_de_champ.size).to eq(2) revision.remove_type_de_champ(type_de_champ_repetition.types_de_champ.first.stable_id) - procedure.reload type_de_champ_repetition.reload expect(type_de_champ_repetition.types_de_champ.size).to eq(0) expect(revision.types_de_champ.size).to eq(2) - expect(procedure.types_de_champ.size).to eq(2) end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 135bd9b62..eda353e8c 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -42,7 +42,7 @@ describe Procedure do describe 'closed mail template body' do let(:procedure) { create(:procedure) } - subject { procedure.closed_mail_template.body } + subject { procedure.closed_mail_template.rich_body.body.to_html } context 'for procedures without an attestation' do it { is_expected.not_to include('lien attestation') } @@ -279,15 +279,6 @@ describe Procedure do end end - describe '#types_de_champ (ordered)' do - let(:procedure) { create(:procedure) } - let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 1) } - let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 0) } - subject { procedure.types_de_champ } - it { expect(subject.first).to eq(type_de_champ_1) } - it { expect(subject.last).to eq(type_de_champ_0) } - end - describe 'active' do let(:procedure) { create(:procedure) } subject { Procedure.active(procedure.id) } @@ -333,22 +324,22 @@ describe Procedure do end describe 'clone' do - let!(:service) { create(:service) } - let(:procedure) { create(:procedure, received_mail: received_mail, service: service) } - let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) } - let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) } - let!(:type_de_champ_2) { create(:type_de_champ_drop_down_list, procedure: procedure, order_place: 2) } - let!(:type_de_champ_pj) { create(:type_de_champ_piece_justificative, procedure: procedure, order_place: 3, old_pj: { stable_id: 2713 }) } - let!(:type_de_champ_private_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) } - let!(:type_de_champ_private_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) } - let!(:type_de_champ_private_2) { create(:type_de_champ_drop_down_list, :private, procedure: procedure, order_place: 2) } + let(:service) { create(:service) } + let(:procedure) { create(:procedure, received_mail: received_mail, service: service, types_de_champ: [type_de_champ_0, type_de_champ_1, type_de_champ_2, type_de_champ_pj], types_de_champ_private: [type_de_champ_private_0, type_de_champ_private_1, type_de_champ_private_2]) } + let(:type_de_champ_0) { build(:type_de_champ, position: 0) } + let(:type_de_champ_1) { build(:type_de_champ, position: 1) } + let(:type_de_champ_2) { build(:type_de_champ_drop_down_list, position: 2) } + let(:type_de_champ_pj) { build(:type_de_champ_piece_justificative, position: 3, old_pj: { stable_id: 2713 }) } + let(:type_de_champ_private_0) { build(:type_de_champ, :private, position: 0) } + let(:type_de_champ_private_1) { build(:type_de_champ, :private, position: 1) } + let(:type_de_champ_private_2) { build(:type_de_champ_drop_down_list, :private, position: 2) } let(:received_mail) { build(:received_mail) } let(:from_library) { false } let(:administrateur) { procedure.administrateurs.first } - let!(:groupe_instructeur_1) { create(:groupe_instructeur, procedure: procedure, label: "groupe_1") } - let!(:instructeur_1) { create(:instructeur) } - let!(:instructeur_2) { create(:instructeur) } + let(:groupe_instructeur_1) { create(:groupe_instructeur, procedure: procedure, label: "groupe_1") } + let(:instructeur_1) { create(:instructeur) } + let(:instructeur_2) { create(:instructeur) } let!(:assign_to_1) { create(:assign_to, procedure: procedure, groupe_instructeur: groupe_instructeur_1, instructeur: instructeur_1) } let!(:assign_to_2) { create(:assign_to, procedure: procedure, groupe_instructeur: groupe_instructeur_1, instructeur: instructeur_2) } @@ -379,28 +370,18 @@ describe Procedure do it 'should duplicate specific objects with different id' do expect(subject.id).not_to eq(procedure.id) - expect(subject.types_de_champ.size).to eq(procedure.types_de_champ.size) - expect(subject.types_de_champ_private.size).to eq procedure.types_de_champ_private.size - expect(subject.types_de_champ.map(&:drop_down_options).compact.size).to eq procedure.types_de_champ.map(&:drop_down_options).compact.size - expect(subject.types_de_champ_private.map(&:drop_down_options).compact.size).to eq procedure.types_de_champ_private.map(&:drop_down_options).compact.size - expect(subject.draft_revision.types_de_champ.size).to eq(procedure.draft_revision.types_de_champ.size) - expect(subject.draft_revision.types_de_champ_private.size).to eq(procedure.draft_revision.types_de_champ_private.size) + expect(subject.draft_types_de_champ.size).to eq(procedure.draft_types_de_champ.size) + expect(subject.draft_types_de_champ_private.size).to eq(procedure.draft_types_de_champ_private.size) - procedure.types_de_champ.zip(subject.types_de_champ).each do |ptc, stc| + procedure.draft_types_de_champ.zip(subject.draft_types_de_champ).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc, except: ["revision_id"]) expect(stc.revision).to eq(subject.draft_revision) end - procedure.types_de_champ.zip(procedure.draft_revision.types_de_champ).each do |ptc, rtc| - expect(ptc).to eq(rtc) - end - subject.types_de_champ_private.zip(procedure.types_de_champ_private).each do |stc, ptc| + procedure.draft_types_de_champ_private.zip(subject.draft_types_de_champ_private).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc, except: ["revision_id"]) expect(stc.revision).to eq(subject.draft_revision) end - procedure.types_de_champ_private.zip(procedure.draft_revision.types_de_champ_private).each do |ptc, rtc| - expect(ptc).to eq(rtc) - end expect(subject.attestation_template.title).to eq(procedure.attestation_template.title) @@ -423,7 +404,7 @@ describe Procedure do end it 'should discard old pj information' do - subject.types_de_champ.each do |stc| + subject.draft_types_de_champ.each do |stc| expect(stc.old_pj).to be_nil end end @@ -459,7 +440,7 @@ describe Procedure do end it 'should discard old pj information' do - subject.types_de_champ.each do |stc| + subject.draft_types_de_champ.each do |stc| expect(stc.old_pj).to be_nil end end @@ -519,12 +500,12 @@ describe Procedure do end it 'should keep types_de_champ ids stable' do - expect(subject.types_de_champ.first.id).not_to eq(procedure.types_de_champ.first.id) - expect(subject.types_de_champ.first.stable_id).to eq(procedure.types_de_champ.first.id) + expect(subject.draft_types_de_champ.first.id).not_to eq(procedure.draft_types_de_champ.first.id) + expect(subject.draft_types_de_champ.first.stable_id).to eq(procedure.draft_types_de_champ.first.id) end it 'should duplicate piece_justificative_template on a type_de_champ' do - expect(subject.types_de_champ.where(type_champ: "piece_justificative").first.piece_justificative_template.attached?).to be true + expect(subject.draft_types_de_champ.where(type_champ: "piece_justificative").first.piece_justificative_template.attached?).to be true end context 'with a notice attached' do @@ -805,7 +786,7 @@ describe Procedure do end describe 'suggested_path' do - let(:procedure) { create :procedure, aasm_state: :publiee, libelle: 'Inscription au Collège' } + let(:procedure) { create(:procedure, aasm_state: :publiee, libelle: 'Inscription au Collège') } subject { procedure.suggested_path(procedure.administrateurs.first) } @@ -821,7 +802,7 @@ describe Procedure do context 'when the suggestion conflicts with one procedure' do before do - create :procedure, aasm_state: :publiee, path: 'inscription-au-college' + create(:procedure, aasm_state: :publiee, path: 'inscription-au-college') end it { is_expected.to eq 'inscription-au-college-2' } @@ -829,8 +810,8 @@ describe Procedure do context 'when the suggestion conflicts with several procedures' do before do - create :procedure, aasm_state: :publiee, path: 'inscription-au-college' - create :procedure, aasm_state: :publiee, path: 'inscription-au-college-2' + create(:procedure, aasm_state: :publiee, path: 'inscription-au-college') + create(:procedure, aasm_state: :publiee, path: 'inscription-au-college-2') end it { is_expected.to eq 'inscription-au-college-3' } @@ -838,7 +819,7 @@ describe Procedure do context 'when the suggestion conflicts with another procedure of the same admin' do before do - create :procedure, aasm_state: :publiee, path: 'inscription-au-college', administrateurs: procedure.administrateurs + create(:procedure, aasm_state: :publiee, path: 'inscription-au-college', administrateurs: procedure.administrateurs) end it { is_expected.to eq 'inscription-au-college' } @@ -903,13 +884,14 @@ describe Procedure do describe '#new_dossier' do let(:procedure) do - procedure = create(:procedure) - - create(:type_de_champ_text, procedure: procedure, order_place: 1) - create(:type_de_champ_number, procedure: procedure, order_place: 2) - create(:type_de_champ_textarea, :private, procedure: procedure) - - procedure + create(:procedure, + types_de_champ: [ + build(:type_de_champ_text, position: 0), + build(:type_de_champ_number, position: 1) + ], + types_de_champ_private: [ + build(:type_de_champ_textarea, :private) + ]) end let(:dossier) { procedure.new_dossier } diff --git a/spec/models/type_de_champ_shared_example.rb b/spec/models/type_de_champ_shared_example.rb index 30c455363..d00712f3e 100644 --- a/spec/models/type_de_champ_shared_example.rb +++ b/spec/models/type_de_champ_shared_example.rb @@ -153,44 +153,6 @@ shared_examples 'type_de_champ_spec' do end end - describe "repetition" do - let(:procedure) { create(:procedure) } - let(:type_de_champ) { create(:type_de_champ_repetition, procedure: procedure) } - let(:type_de_champ_text) { create(:type_de_champ_text, procedure: procedure) } - let(:type_de_champ_integer_number_attrs) { attributes_for(:type_de_champ_integer_number) } - - it "associates nested types_de_champ to the parent procedure" do - expect(type_de_champ.types_de_champ.size).to eq(0) - expect(procedure.types_de_champ.size).to eq(1) - - procedure.update!(types_de_champ_attributes: [ - { - id: type_de_champ.id, - libelle: type_de_champ.libelle, - types_de_champ_attributes: [type_de_champ_integer_number_attrs] - } - ]) - procedure.reload - type_de_champ.reload - - expect(procedure.types_de_champ.size).to eq(1) - expect(type_de_champ.types_de_champ.size).to eq(1) - - expect(type_de_champ.types_de_champ.first.parent).to eq(type_de_champ) - expect(type_de_champ.types_de_champ.first.procedure).to eq(procedure) - expect(type_de_champ.types_de_champ.first.private?).to eq(false) - - type_de_champ.types_de_champ << type_de_champ_text - expect(type_de_champ.types_de_champ.size).to eq(2) - expect(type_de_champ_text.parent).to eq(type_de_champ) - - admin = create(:administrateur) - cloned_procedure = procedure.clone(admin, false) - - expect(cloned_procedure.types_de_champ.first.types_de_champ).not_to be_empty - end - end - describe "linked_drop_down_list" do let(:type_de_champ) { create(:type_de_champ_linked_drop_down_list) } diff --git a/spec/serializers/dossier_serializer_spec.rb b/spec/serializers/dossier_serializer_spec.rb index 69c8f624c..33a883d72 100644 --- a/spec/serializers/dossier_serializer_spec.rb +++ b/spec/serializers/dossier_serializer_spec.rb @@ -49,12 +49,11 @@ describe DossierSerializer do context 'when a type de champ PJ was cloned from a legacy PJ' do let(:original_pj_id) { 3 } let(:cloned_type_de_champ) do - tdc = create(:type_de_champ_piece_justificative, + build(:type_de_champ_piece_justificative, libelle: "Vidéo de votre demande de subvention", description: "Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique.\r\nRécupérer le formulaire vierge pour mon dossier : https://www.dance-academy.gouv.fr", - order_place: 0) - tdc.old_pj = { stable_id: original_pj_id } - tdc + old_pj: { stable_id: original_pj_id }, + position: 0) end let(:procedure) { create(:procedure, :published, types_de_champ: [cloned_type_de_champ]) } let(:dossier) { create(:dossier, procedure: procedure) } diff --git a/spec/serializers/procedure_serializer_spec.rb b/spec/serializers/procedure_serializer_spec.rb index 4271cd93e..e7f64b4bd 100644 --- a/spec/serializers/procedure_serializer_spec.rb +++ b/spec/serializers/procedure_serializer_spec.rb @@ -12,12 +12,11 @@ describe ProcedureSerializer do context 'when a type PJ was cloned to a type champ PJ' do let(:original_pj_id) { 3 } let(:cloned_type_de_champ) do - tdc = create(:type_de_champ_piece_justificative, + build(:type_de_champ_piece_justificative, libelle: "Vidéo de votre demande de subvention", description: "Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique.\r\nRécupérer le formulaire vierge pour mon dossier : https://www.dance-academy.gouv.fr", - order_place: 0) - tdc.old_pj = { stable_id: original_pj_id } - tdc + old_pj: { stable_id: original_pj_id }, + position: 0) end let(:procedure) { create(:procedure, :published, types_de_champ: [cloned_type_de_champ]) } diff --git a/spec/services/administrateur_usage_statistics_service_spec.rb b/spec/services/administrateur_usage_statistics_service_spec.rb index c40e33267..835206df7 100644 --- a/spec/services/administrateur_usage_statistics_service_spec.rb +++ b/spec/services/administrateur_usage_statistics_service_spec.rb @@ -61,7 +61,7 @@ describe AdministrateurUsageStatisticsService do end context 'with a freshly active procedure' do - let(:procedure) { create(:procedure, aasm_state: 'publiee') } + let(:procedure) { create(:procedure, :published) } it do is_expected.to include( @@ -82,7 +82,7 @@ describe AdministrateurUsageStatisticsService do end context 'with a procedure close' do - let(:procedure) { create(:procedure, aasm_state: 'close') } + let(:procedure) { create(:procedure, :closed) } let!(:dossiers) do (1..7).flat_map do [ @@ -163,7 +163,7 @@ describe AdministrateurUsageStatisticsService do end context 'with a procedure en prod' do - let(:procedure) { create(:procedure, aasm_state: 'publiee') } + let(:procedure) { create(:procedure, :published) } let!(:dossiers) do [ create(:dossier, :en_construction, procedure: procedure), @@ -191,7 +191,7 @@ describe AdministrateurUsageStatisticsService do end context 'with a procedure en prod and more than 20 dossiers' do - let(:procedure) { create(:procedure, aasm_state: 'publiee') } + let(:procedure) { create(:procedure, :published) } let!(:dossiers) do (1..7).flat_map do [ diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 38eb94b19..7f2945981 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -18,10 +18,10 @@ describe ProcedureExportService do before do # change one tdc place to check if the header is ordered - tdc_first = procedure.types_de_champ.first - tdc_last = procedure.types_de_champ.last + tdc_first = procedure.active_revision.revision_types_de_champ.first + tdc_last = procedure.active_revision.revision_types_de_champ.last - tdc_first.update(order_place: tdc_last.order_place + 1) + tdc_first.update(position: tdc_last.position + 1) procedure.reload end diff --git a/spec/support/feature_helpers.rb b/spec/support/feature_helpers.rb index 5f28090b9..e4f26d2f1 100644 --- a/spec/support/feature_helpers.rb +++ b/spec/support/feature_helpers.rb @@ -12,11 +12,6 @@ module FeatureHelpers login_as instructeur, scope: :instructeur end - def create_dossier - dossier = FactoryBot.create(:dossier) - dossier - end - def sign_in_with(email, password, sign_in_by_link = false) fill_in :user_email, with: email fill_in :user_password, with: password diff --git a/spec/views/admin/mail_templates/edit.html.haml_spec.rb b/spec/views/new_administrateur/mail_templates/edit.html.haml.spec.rb similarity index 69% rename from spec/views/admin/mail_templates/edit.html.haml_spec.rb rename to spec/views/new_administrateur/mail_templates/edit.html.haml.spec.rb index e67d29681..b0317931d 100644 --- a/spec/views/admin/mail_templates/edit.html.haml_spec.rb +++ b/spec/views/new_administrateur/mail_templates/edit.html.haml.spec.rb @@ -1,10 +1,10 @@ -describe 'admin/mail_templates/edit.html.haml', type: :view do +describe 'new_administrateur/mail_templates/edit.html.haml', type: :view do let(:procedure) { create(:procedure) } let(:mail_template) { create(:received_mail, procedure: procedure) } let(:all_tags) { mail_template.tags } before do - allow(view).to receive(:admin_procedure_mail_template_path).and_return("/toto") + allow(view).to receive(:admin_procedure_mail_templates_path).and_return("/toto") allow(view).to receive(:admin_procedure_mail_templates_path).and_return("/toto") assign(:mail_template, mail_template) @@ -13,6 +13,5 @@ describe 'admin/mail_templates/edit.html.haml', type: :view do context "Champs are listed in the page" do it { expect(all_tags).to include(include({ libelle: 'numéro du dossier' })) } - it { expect(render).to include("numéro du dossier") } end end diff --git a/spec/views/shared/dossiers/_demande.html.haml_spec.rb b/spec/views/shared/dossiers/_demande.html.haml_spec.rb index f435e8e6a..cb48f4de2 100644 --- a/spec/views/shared/dossiers/_demande.html.haml_spec.rb +++ b/spec/views/shared/dossiers/_demande.html.haml_spec.rb @@ -38,7 +38,7 @@ describe 'shared/dossiers/demande.html.haml', type: :view do expect(subject).to include(individual.gender) expect(subject).to include(individual.nom) expect(subject).to include(individual.prenom) - expect(subject).to include(I18n.l(individual.birthdate)) + expect(subject).to include(I18n.l(individual.birthdate, format: :long)) end end