Suppression du code des anciennes pièces jointes (#4142)
Suppression du code des anciennes pièces jointes
This commit is contained in:
commit
d1ff311879
75 changed files with 99 additions and 10570 deletions
|
@ -25,7 +25,6 @@
|
|||
// = require login
|
||||
// = require main_container
|
||||
// = require navbar
|
||||
// = require pieces_justificatives_fields
|
||||
// = require pj_modal
|
||||
// = require print
|
||||
// = require procedure
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.pieces-justificatives-fields {
|
||||
.form-inline > .form-group {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
class Admin::PiecesJustificativesController < AdminController
|
||||
before_action :retrieve_procedure
|
||||
before_action :procedure_locked?
|
||||
before_action :reset_procedure, only: [:update, :destroy, :move_up, :move_down]
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
if @procedure.update(update_params)
|
||||
flash.now.notice = 'Modifications sauvegardées'
|
||||
else
|
||||
flash.now.notice = 'Une erreur est survenue'
|
||||
end
|
||||
render 'show', format: :js
|
||||
end
|
||||
|
||||
def destroy
|
||||
@procedure.types_de_piece_justificative.find(params[:id]).destroy
|
||||
|
||||
render 'show', format: :js
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render json: { message: 'Type de piece justificative not found' }, status: 404
|
||||
end
|
||||
|
||||
def move_up
|
||||
index = params[:index].to_i - 1
|
||||
if @procedure.switch_types_de_piece_justificative index
|
||||
render 'show', format: :js
|
||||
else
|
||||
render json: {}, status: 400
|
||||
end
|
||||
end
|
||||
|
||||
def move_down
|
||||
if @procedure.switch_types_de_piece_justificative params[:index].to_i
|
||||
render 'show', format: :js
|
||||
else
|
||||
render json: {}, status: 400
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_params
|
||||
params
|
||||
.require(:procedure)
|
||||
.permit(types_de_piece_justificative_attributes: [:libelle, :description, :id, :order_place, :mandatory, :lien_demarche])
|
||||
end
|
||||
end
|
|
@ -132,7 +132,6 @@ module Users
|
|||
end
|
||||
|
||||
# FIXME:
|
||||
# - remove PiecesJustificativesService
|
||||
# - delegate draft save logic to champ ?
|
||||
def update_brouillon
|
||||
@dossier = dossier_with_champs
|
||||
|
@ -158,8 +157,6 @@ module Users
|
|||
@dossier = dossier_with_champs
|
||||
end
|
||||
|
||||
# FIXME:
|
||||
# - remove PiecesJustificativesService
|
||||
def update
|
||||
@dossier = dossier_with_champs
|
||||
|
||||
|
@ -297,7 +294,7 @@ module Users
|
|||
end
|
||||
|
||||
def update_dossier_and_compute_errors
|
||||
errors = PiecesJustificativesService.upload!(@dossier, current_user, params)
|
||||
errors = []
|
||||
|
||||
if champs_params[:dossier] && !@dossier.update(champs_params[:dossier])
|
||||
errors += @dossier.errors.full_messages
|
||||
|
@ -305,7 +302,6 @@ module Users
|
|||
|
||||
if !save_draft?
|
||||
errors += @dossier.check_mandatory_champs
|
||||
errors += PiecesJustificativesService.missing_pj_error_messages(@dossier)
|
||||
end
|
||||
|
||||
errors
|
||||
|
|
|
@ -8,7 +8,6 @@ class ProcedureDashboard < Administrate::BaseDashboard
|
|||
# which determines how the attribute is displayed
|
||||
# on pages throughout the dashboard.
|
||||
ATTRIBUTE_TYPES = {
|
||||
types_de_piece_justificative: TypesDePieceJustificativeCollectionField,
|
||||
types_de_champ: TypesDeChampCollectionField,
|
||||
types_de_champ_private: TypesDeChampCollectionField,
|
||||
path: ProcedureLinkField,
|
||||
|
@ -72,7 +71,6 @@ class ProcedureDashboard < Administrate::BaseDashboard
|
|||
:archived_at,
|
||||
:types_de_champ,
|
||||
:types_de_champ_private,
|
||||
:types_de_piece_justificative,
|
||||
:for_individual,
|
||||
:auto_archive_on,
|
||||
:gestionnaires,
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
require "administrate/field/base"
|
||||
|
||||
class TypesDePieceJustificativeCollectionField < Administrate::Field::Base
|
||||
def to_s
|
||||
data
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
module AdminFormulaireHelper
|
||||
BASE_CLASSES = ['btn', 'btn-default', 'form-control', 'fa']
|
||||
|
||||
def button_up(procedure, kind, index, url)
|
||||
if display_up_button?(index, procedure, kind)
|
||||
button(up_classes, "btn_up_#{index}", url)
|
||||
end
|
||||
end
|
||||
|
||||
def button_down(procedure, kind, index, url)
|
||||
if display_down_button?(index, procedure, kind)
|
||||
button(down_classes, "btn_down_#{index}", url)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def button(classes, id, url)
|
||||
link_to(
|
||||
'',
|
||||
url,
|
||||
class: classes,
|
||||
id: id,
|
||||
remote: true,
|
||||
method: :post
|
||||
)
|
||||
end
|
||||
|
||||
def up_classes
|
||||
BASE_CLASSES + ['fa-chevron-up']
|
||||
end
|
||||
|
||||
def down_classes
|
||||
BASE_CLASSES + ['fa-chevron-down']
|
||||
end
|
||||
|
||||
def display_up_button?(index, procedure, kind)
|
||||
index != 0 && count_type_de_champ(procedure, kind) > 1
|
||||
end
|
||||
|
||||
def display_down_button?(index, procedure, kind)
|
||||
(index + 1) < count_type_de_champ(procedure, kind)
|
||||
end
|
||||
|
||||
def count_type_de_champ(procedure, kind)
|
||||
case kind
|
||||
when "public"
|
||||
@count_type_de_champ_public ||= procedure.types_de_champ.count
|
||||
when "private"
|
||||
@count_type_de_champ_private ||= procedure.types_de_champ_private.count
|
||||
when "piece_justificative"
|
||||
@count_type_de_piece_justificative ||= procedure.types_de_piece_justificative.count
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
module PieceJustificativeHelper
|
||||
def display_pj_filename(pj)
|
||||
truncate(pj.original_filename, length: 60)
|
||||
end
|
||||
end
|
|
@ -1,94 +0,0 @@
|
|||
# Source: https://github.com/gitlabhq/gitlabhq/blob/master/lib/file_size_validator.rb
|
||||
class FileSizeValidator < ActiveModel::EachValidator
|
||||
MESSAGES = { is: :wrong_size, minimum: :size_too_small, maximum: :size_too_big }.freeze
|
||||
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
|
||||
|
||||
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
|
||||
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
|
||||
|
||||
def initialize(options)
|
||||
range = options.delete(:in) || options.delete(:within)
|
||||
|
||||
if range.present?
|
||||
if !range.is_a?(Range)
|
||||
raise ArgumentError, ":in and :within must be a Range"
|
||||
end
|
||||
|
||||
options[:minimum], options[:maximum] = range.begin, range.end
|
||||
|
||||
if range.exclude_end?
|
||||
options[:maximum] -= 1
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def check_validity!
|
||||
keys = CHECKS.keys & options.keys
|
||||
|
||||
if keys.empty?
|
||||
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
|
||||
end
|
||||
|
||||
keys.each do |key|
|
||||
value = options[key]
|
||||
|
||||
if !(value.is_a?(Integer) && value >= 0) && !value.is_a?(Symbol)
|
||||
raise ArgumentError, ":#{key} must be a nonnegative Integer or symbol"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
if !value.is_a?(CarrierWave::Uploader::Base)
|
||||
raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected")
|
||||
end
|
||||
|
||||
if value.is_a?(String)
|
||||
value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value)
|
||||
end
|
||||
|
||||
CHECKS.each do |key, validity_check|
|
||||
if !check_value = options[key]
|
||||
next
|
||||
end
|
||||
|
||||
check_value =
|
||||
case check_value
|
||||
when Integer
|
||||
check_value
|
||||
when Symbol
|
||||
record.send(check_value)
|
||||
end
|
||||
|
||||
if key == :maximum
|
||||
value ||= []
|
||||
end
|
||||
|
||||
value_size = value.size
|
||||
if value_size.send(validity_check, check_value)
|
||||
next
|
||||
end
|
||||
|
||||
errors_options = options.except(*RESERVED_OPTIONS)
|
||||
errors_options[:file_size] = help.number_to_human_size check_value
|
||||
|
||||
default_message = options[MESSAGES[key]]
|
||||
if default_message
|
||||
errors_options[:message] ||= default_message
|
||||
end
|
||||
|
||||
record.errors.add(attribute, MESSAGES[key], errors_options)
|
||||
end
|
||||
end
|
||||
|
||||
def help
|
||||
Helper.instance
|
||||
end
|
||||
|
||||
class Helper
|
||||
include Singleton
|
||||
include ActionView::Helpers::NumberHelper
|
||||
end
|
||||
end
|
|
@ -21,7 +21,6 @@ class Dossier < ApplicationRecord
|
|||
has_one :individual, dependent: :destroy
|
||||
has_one :attestation, dependent: :destroy
|
||||
|
||||
has_many :pieces_justificatives, inverse_of: :dossier, dependent: :destroy
|
||||
has_one_attached :justificatif_motivation
|
||||
|
||||
has_many :champs, -> { root.public_only.ordered }, inverse_of: :dossier, dependent: :destroy
|
||||
|
@ -128,7 +127,6 @@ class Dossier < ApplicationRecord
|
|||
:etablissement,
|
||||
piece_justificative_file_attachment: :blob
|
||||
],
|
||||
pieces_justificatives: [],
|
||||
avis: [],
|
||||
etablissement: [],
|
||||
individual: [],
|
||||
|
@ -138,7 +136,6 @@ class Dossier < ApplicationRecord
|
|||
accepts_nested_attributes_for :individual
|
||||
|
||||
delegate :siret, :siren, to: :etablissement, allow_nil: true
|
||||
delegate :types_de_piece_justificative, to: :procedure
|
||||
delegate :types_de_champ, to: :procedure
|
||||
delegate :france_connect_information, to: :user
|
||||
|
||||
|
@ -165,18 +162,6 @@ class Dossier < ApplicationRecord
|
|||
self.private_search_terms = champs_private.flat_map(&:search_terms).compact.join(' ')
|
||||
end
|
||||
|
||||
def was_piece_justificative_uploaded_for_type_id?(type_id)
|
||||
pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0
|
||||
end
|
||||
|
||||
def retrieve_last_piece_justificative_by_type(type)
|
||||
pieces_justificatives.where(type_de_piece_justificative_id: type).last
|
||||
end
|
||||
|
||||
def retrieve_all_piece_justificative_by_type(type)
|
||||
pieces_justificatives.where(type_de_piece_justificative_id: type).order(created_at: :DESC)
|
||||
end
|
||||
|
||||
def build_default_champs
|
||||
procedure.build_champs.each do |champ|
|
||||
champs << champ
|
||||
|
|
|
@ -93,11 +93,7 @@ class Gestionnaire < ApplicationRecord
|
|||
.find_by(gestionnaire: self, dossier: dossier)
|
||||
|
||||
if follow.present?
|
||||
champs_publiques = follow.dossier.champs.updated_since?(follow.demande_seen_at).any?
|
||||
|
||||
pieces_justificatives = follow.dossier.pieces_justificatives.updated_since?(follow.demande_seen_at).any?
|
||||
|
||||
demande = champs_publiques || pieces_justificatives
|
||||
demande = follow.dossier.champs.updated_since?(follow.demande_seen_at).any?
|
||||
|
||||
annotations_privees = follow.dossier.champs_private.updated_since?(follow.annotations_privees_seen_at).any?
|
||||
|
||||
|
@ -154,10 +150,6 @@ class Gestionnaire < ApplicationRecord
|
|||
.joins(:champs)
|
||||
.where('champs.updated_at > follows.demande_seen_at')
|
||||
|
||||
updated_pieces_justificatives = dossiers
|
||||
.joins(:pieces_justificatives)
|
||||
.where('pieces_justificatives.updated_at > follows.demande_seen_at')
|
||||
|
||||
updated_annotations = dossiers
|
||||
.joins(:champs_private)
|
||||
.where('champs.updated_at > follows.annotations_privees_seen_at')
|
||||
|
@ -174,7 +166,6 @@ class Gestionnaire < ApplicationRecord
|
|||
|
||||
[
|
||||
updated_demandes,
|
||||
updated_pieces_justificatives,
|
||||
updated_annotations,
|
||||
updated_avis,
|
||||
updated_messagerie
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
class PieceJustificative < ApplicationRecord
|
||||
belongs_to :dossier, inverse_of: :pieces_justificatives, touch: true
|
||||
belongs_to :type_de_piece_justificative
|
||||
has_one :commentaire
|
||||
|
||||
belongs_to :user
|
||||
|
||||
delegate :api_entreprise, :libelle, :order_place, to: :type_de_piece_justificative
|
||||
|
||||
alias_attribute :type, :type_de_piece_justificative_id
|
||||
|
||||
mount_uploader :content, PieceJustificativeUploader
|
||||
validates :content, :file_size => { :maximum => 20.megabytes }
|
||||
validates :content, presence: true, allow_blank: false, allow_nil: false
|
||||
|
||||
scope :updated_since?, -> (date) { where('pieces_justificatives.updated_at > ?', date) }
|
||||
|
||||
def empty?
|
||||
content.blank?
|
||||
end
|
||||
|
||||
def libelle
|
||||
if type_de_piece_justificative.nil?
|
||||
return content.to_s
|
||||
else
|
||||
type_de_piece_justificative.libelle
|
||||
end
|
||||
end
|
||||
|
||||
def content_url
|
||||
if content.url.present?
|
||||
if Flipflop.remote_storage?
|
||||
(RemoteDownloader.new content.filename).url
|
||||
else
|
||||
(LocalDownloader.new content.path,
|
||||
(type_de_piece_justificative.nil? ? content.original_filename : type_de_piece_justificative.libelle)).url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.accept_format
|
||||
" application/pdf,
|
||||
application/msword,
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
||||
application/vnd.ms-excel,
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
||||
application/vnd.ms-powerpoint,
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
||||
application/vnd.oasis.opendocument.text,
|
||||
application/vnd.oasis.opendocument.presentation,
|
||||
application/vnd.oasis.opendocument.spreadsheet,
|
||||
image/png,
|
||||
image/jpeg
|
||||
"
|
||||
end
|
||||
end
|
|
@ -3,7 +3,6 @@ require Rails.root.join('lib', 'percentile')
|
|||
class Procedure < ApplicationRecord
|
||||
MAX_DUREE_CONSERVATION = 36
|
||||
|
||||
has_many :types_de_piece_justificative, -> { ordered }, inverse_of: :procedure, dependent: :destroy
|
||||
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 :dossiers, dependent: :restrict_with_exception
|
||||
|
@ -31,7 +30,6 @@ class Procedure < ApplicationRecord
|
|||
|
||||
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
|
||||
accepts_nested_attributes_for :types_de_piece_justificative, reject_if: proc { |attributes| attributes['libelle'].blank? }, allow_destroy: true
|
||||
|
||||
mount_uploader :logo, ProcedureLogoUploader
|
||||
|
||||
|
@ -51,7 +49,6 @@ class Procedure < ApplicationRecord
|
|||
:administrateurs,
|
||||
:types_de_champ_private,
|
||||
:types_de_champ,
|
||||
:types_de_piece_justificative,
|
||||
:module_api_carto
|
||||
)
|
||||
}
|
||||
|
@ -187,10 +184,6 @@ class Procedure < ApplicationRecord
|
|||
switch_list_order(types_de_champ_private, index_of_first_element)
|
||||
end
|
||||
|
||||
def switch_types_de_piece_justificative(index_of_first_element)
|
||||
switch_list_order(types_de_piece_justificative, index_of_first_element)
|
||||
end
|
||||
|
||||
def switch_list_order(list, index_of_first_element)
|
||||
if index_of_first_element < 0 ||
|
||||
index_of_first_element == list.count - 1 ||
|
||||
|
@ -225,7 +218,6 @@ class Procedure < ApplicationRecord
|
|||
procedure.remote_logo_url = self.logo_url
|
||||
procedure.lien_notice = nil
|
||||
|
||||
procedure.types_de_champ += PiecesJustificativesService.types_pj_as_types_de_champ(self)
|
||||
if is_different_admin || from_library
|
||||
procedure.types_de_champ.each { |tdc| tdc.options&.delete(:old_pj) }
|
||||
end
|
||||
|
@ -280,10 +272,6 @@ class Procedure < ApplicationRecord
|
|||
whitelisted_at.present?
|
||||
end
|
||||
|
||||
def has_old_pjs?
|
||||
types_de_piece_justificative.any?
|
||||
end
|
||||
|
||||
def total_dossier
|
||||
self.dossiers.state_not_brouillon.size
|
||||
end
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
class TypeDePieceJustificative < ApplicationRecord
|
||||
has_many :pieces_justificatives, dependent: :destroy
|
||||
|
||||
belongs_to :procedure
|
||||
|
||||
validates :libelle, presence: true, allow_blank: false, allow_nil: false
|
||||
|
||||
validates :lien_demarche, format: { with: URI.regexp }, allow_blank: true, allow_nil: true
|
||||
scope :ordered, -> { order(order_place: :asc) }
|
||||
end
|
|
@ -15,7 +15,6 @@ class User < ApplicationRecord
|
|||
has_many :dossiers, dependent: :destroy
|
||||
has_many :invites, dependent: :destroy
|
||||
has_many :dossiers_invites, through: :invites, source: :dossier
|
||||
has_many :piece_justificative, dependent: :destroy
|
||||
has_many :feedbacks, dependent: :destroy
|
||||
has_one :france_connect_information, dependent: :destroy
|
||||
|
||||
|
|
|
@ -50,8 +50,7 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def pieces_justificatives
|
||||
ActiveModelSerializers::SerializableResource.new(object.pieces_justificatives).serializable_hash +
|
||||
PiecesJustificativesService.serialize_champs_as_pjs(object)
|
||||
PiecesJustificativesService.serialize_champs_as_pjs(object)
|
||||
end
|
||||
|
||||
def justificatif_motivation
|
||||
|
@ -61,8 +60,7 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def types_de_piece_justificative
|
||||
ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash +
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
end
|
||||
|
||||
def email
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
class PieceJustificativeSerializer < ActiveModel::Serializer
|
||||
attributes :created_at,
|
||||
:type_de_piece_justificative_id,
|
||||
:content_url
|
||||
|
||||
has_one :user
|
||||
|
||||
def created_at
|
||||
object.created_at&.in_time_zone('UTC')
|
||||
end
|
||||
end
|
|
@ -49,7 +49,6 @@ class ProcedureSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def types_de_piece_justificative
|
||||
ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash +
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
class TypeDePieceJustificativeSerializer < ActiveModel::Serializer
|
||||
attributes :id,
|
||||
:libelle,
|
||||
:description,
|
||||
:order_place,
|
||||
:lien_demarche
|
||||
end
|
|
@ -1,173 +0,0 @@
|
|||
class CarrierwaveActiveStorageMigrationService
|
||||
def ensure_openstack_copy_possible!(uploader)
|
||||
ensure_active_storage_openstack!
|
||||
ensure_carrierwave_openstack!(uploader)
|
||||
ensure_active_storage_and_carrierwave_credetials_match(uploader)
|
||||
end
|
||||
|
||||
def ensure_active_storage_openstack!
|
||||
# If we manage to get the client, it means that ActiveStorage is on OpenStack
|
||||
openstack_client!
|
||||
end
|
||||
|
||||
def openstack_client!
|
||||
@openstack_client ||= active_storage_openstack_client!
|
||||
end
|
||||
|
||||
def active_storage_openstack_client!
|
||||
service = ActiveStorage::Blob.service
|
||||
|
||||
if defined?(ActiveStorage::Service::DsProxyService) &&
|
||||
service.is_a?(ActiveStorage::Service::DsProxyService)
|
||||
service = service.wrapped
|
||||
end
|
||||
|
||||
if !defined?(ActiveStorage::Service::OpenStackService) ||
|
||||
!service.is_a?(ActiveStorage::Service::OpenStackService)
|
||||
raise StandardError, 'ActiveStorage must be backed by OpenStack'
|
||||
end
|
||||
|
||||
service.client
|
||||
end
|
||||
|
||||
def ensure_carrierwave_openstack!(uploader)
|
||||
storage = fog_client!(uploader)
|
||||
|
||||
if !defined?(Fog::OpenStack::Storage::Real) ||
|
||||
!storage.is_a?(Fog::OpenStack::Storage::Real)
|
||||
raise StandardError, 'Carrierwave must be backed by OpenStack'
|
||||
end
|
||||
end
|
||||
|
||||
def fog_client!(uploader)
|
||||
storage = uploader.new.send(:storage)
|
||||
|
||||
if !defined?(CarrierWave::Storage::Fog) ||
|
||||
!storage.is_a?(CarrierWave::Storage::Fog)
|
||||
raise StandardError, 'Carrierwave must be backed by a Fog provider'
|
||||
end
|
||||
|
||||
storage.connection
|
||||
end
|
||||
|
||||
# OpenStack Swift's COPY object command works across different buckets, but they still need
|
||||
# to be on the same object store. This method tries to ensure that Carrierwave and ActiveStorage
|
||||
# are indeed pointing to the same Swift store.
|
||||
def ensure_active_storage_and_carrierwave_credetials_match(uploader)
|
||||
auth_keys = [
|
||||
:openstack_tenant,
|
||||
:openstack_api_key,
|
||||
:openstack_username,
|
||||
:openstack_region,
|
||||
:openstack_management_url
|
||||
]
|
||||
|
||||
active_storage_creds = openstack_client!.credentials.slice(*auth_keys)
|
||||
carrierwave_creds = fog_client!(uploader).credentials.slice(*auth_keys)
|
||||
|
||||
if active_storage_creds != carrierwave_creds
|
||||
raise StandardError, "Active Storage and Carrierwave credentials must match"
|
||||
end
|
||||
end
|
||||
|
||||
# If identify is true, force ActiveStorage to examine the beginning of the file
|
||||
# to determine its MIME type. This identification does not happen immediately,
|
||||
# but when the first attachment that references this blob is created.
|
||||
def make_blob(uploader, created_at, filename: nil, identify: false)
|
||||
content_type = uploader.content_type
|
||||
identified = content_type.present? && !identify
|
||||
|
||||
ActiveStorage::Blob.create(
|
||||
filename: filename || uploader.filename,
|
||||
content_type: content_type,
|
||||
byte_size: uploader.size,
|
||||
checksum: checksum(uploader),
|
||||
created_at: created_at,
|
||||
metadata: { identified: identified, virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
end
|
||||
|
||||
def make_empty_blob(uploader, created_at, filename: nil)
|
||||
content_type = uploader.content_type || 'text/plain'
|
||||
|
||||
blob = ActiveStorage::Blob.build_after_upload(
|
||||
io: StringIO.new('File not found when migrating from CarrierWave.'),
|
||||
filename: filename || uploader.filename,
|
||||
content_type: content_type || 'text/plain',
|
||||
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
blob.created_at = created_at
|
||||
blob.save!
|
||||
blob
|
||||
end
|
||||
|
||||
def checksum(uploader)
|
||||
hex_to_base64(uploader.file.send(:file).etag)
|
||||
end
|
||||
|
||||
def hex_to_base64(hexdigest)
|
||||
[[hexdigest].pack("H*")].pack("m0")
|
||||
end
|
||||
|
||||
def copy_from_carrierwave_to_active_storage!(source_name, blob)
|
||||
openstack_client!.copy_object(
|
||||
carrierwave_container_name,
|
||||
source_name,
|
||||
active_storage_container_name,
|
||||
blob.key
|
||||
)
|
||||
|
||||
fix_content_type(blob)
|
||||
end
|
||||
|
||||
def carrierwave_container_name
|
||||
Rails.application.secrets.fog[:directory]
|
||||
end
|
||||
|
||||
def active_storage_container_name
|
||||
ENV['FOG_ACTIVESTORAGE_DIRECTORY']
|
||||
end
|
||||
|
||||
def delete_from_active_storage!(blob)
|
||||
openstack_client!.delete_object(
|
||||
active_storage_container_name,
|
||||
blob.key
|
||||
)
|
||||
end
|
||||
|
||||
# Before calling this method, you must make sure the file has been uploaded for the blob.
|
||||
# Otherwise, this method might fail if it needs to read the beginning of the file to
|
||||
# update the blob’s MIME type.
|
||||
def make_attachment(model, attachment_name, blob)
|
||||
attachment = ActiveStorage::Attachment.create(
|
||||
name: attachment_name,
|
||||
record_type: model.class.base_class.name,
|
||||
record_id: model.id,
|
||||
blob: blob,
|
||||
created_at: model.updated_at.iso8601
|
||||
)
|
||||
|
||||
# Making the attachment may have triggerred MIME type auto detection on the blob,
|
||||
# so we make sure to sync that potentially new MIME type to the object in OpenStack
|
||||
fix_content_type(blob)
|
||||
|
||||
attachment
|
||||
end
|
||||
|
||||
def fix_content_type(blob, retry_delay: 5)
|
||||
retries ||= 0
|
||||
# In OpenStack, ActiveStorage cannot inject the MIME type on the fly during direct
|
||||
# download. Instead, the MIME type needs to be stored statically on the file object
|
||||
# in OpenStack. This is what this call does.
|
||||
blob.service.change_content_type(blob.key, blob.content_type)
|
||||
rescue
|
||||
# When we quickly create a new attachment, and then change its content type,
|
||||
# the Object Storage may not be synchronized yet. It this cas, it will return a
|
||||
# "409 Conflict" error.
|
||||
#
|
||||
# Wait for a while, then try again twice (before giving up).
|
||||
sleep(retry_delay)
|
||||
retry if (retries += 1) < 3
|
||||
raise
|
||||
end
|
||||
end
|
|
@ -1,187 +0,0 @@
|
|||
require Rails.root.join("lib", "tasks", "task_helper")
|
||||
|
||||
class PieceJustificativeToChampPieceJointeMigrationService
|
||||
def initialize(**params)
|
||||
params.each do |key, value|
|
||||
instance_variable_set("@#{key}", value)
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_correct_storage_configuration!
|
||||
storage_service.ensure_openstack_copy_possible!(PieceJustificativeUploader)
|
||||
end
|
||||
|
||||
def procedures_with_pjs_in_range(ids_range)
|
||||
procedures_with_pj = Procedure.unscope(where: :hidden_at).joins(:types_de_piece_justificative).distinct
|
||||
procedures_with_pj.where(id: ids_range)
|
||||
end
|
||||
|
||||
def number_of_champs_to_migrate(procedure)
|
||||
(procedure.types_de_piece_justificative.count + 1) * procedure.dossiers.unscope(where: :hidden_at).count
|
||||
end
|
||||
|
||||
def convert_procedure_pjs_to_champ_pjs(procedure, &progress)
|
||||
types_de_champ_pj = PiecesJustificativesService.types_pj_as_types_de_champ(procedure)
|
||||
populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
|
||||
|
||||
# Only destroy the old types PJ once everything has been safely migrated to
|
||||
# champs PJs.
|
||||
|
||||
# First destroy the individual PJ champs on all dossiers.
|
||||
# It will cascade and destroy the PJs, and delete the linked objects from remote storage.
|
||||
procedure.dossiers.unscope(where: :hidden_at).includes(:champs).find_each do |dossier|
|
||||
destroy_pieces_justificatives(dossier)
|
||||
end
|
||||
|
||||
# Now we can destroy the type de champ themselves,
|
||||
# without cascading the timestamp update on all attached dossiers.
|
||||
procedure.types_de_piece_justificative.destroy_all
|
||||
end
|
||||
|
||||
def storage_service
|
||||
@storage_service ||= CarrierwaveActiveStorageMigrationService.new
|
||||
end
|
||||
|
||||
def populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
|
||||
procedure.types_de_champ += types_de_champ_pj
|
||||
|
||||
# Unscope to make sure all dossiers are migrated, even the soft-deleted ones
|
||||
procedure.dossiers.unscope(where: :hidden_at).includes(:champs).find_each do |dossier|
|
||||
migrate_dossier!(dossier, types_de_champ_pj, &progress)
|
||||
end
|
||||
|
||||
rescue StandardError, SignalException
|
||||
# If anything goes wrong, we roll back the migration by destroying the newly created
|
||||
# types de champ, champs blobs and attachments.
|
||||
rake_puts "Error received. Rolling back migration of procedure #{procedure.id}…"
|
||||
rollback_migration!(types_de_champ_pj)
|
||||
rake_puts "Migration of procedure #{procedure.id} rolled back."
|
||||
|
||||
# Reraise the exception to abort the migration.
|
||||
raise
|
||||
end
|
||||
|
||||
def migrate_dossier!(dossier, types_de_champ_pj)
|
||||
# Add the new pieces justificatives champs to the dossier
|
||||
champs_pj = types_de_champ_pj.map(&:build_champ)
|
||||
preserving_updated_at(dossier) do
|
||||
dossier.champs += champs_pj
|
||||
end
|
||||
|
||||
# Copy the dossier old pieces jointes to the new champs
|
||||
# (even if the champs already existed, so that we ensure a clean state)
|
||||
champs_pj.each do |champ|
|
||||
type_pj_id = champ.type_de_champ.old_pj&.fetch('stable_id', nil)
|
||||
pj = dossier.retrieve_last_piece_justificative_by_type(type_pj_id)
|
||||
|
||||
if pj.present?
|
||||
preserving_updated_at(dossier) do
|
||||
convert_pj_to_champ!(pj, champ)
|
||||
end
|
||||
|
||||
champ.update_columns(
|
||||
updated_at: pj.updated_at,
|
||||
created_at: pj.created_at
|
||||
)
|
||||
else
|
||||
champ.update_columns(
|
||||
created_at: dossier.created_at,
|
||||
# Set an updated_at date that won't cause notifications to appear
|
||||
# on gestionnaires' dashboard.
|
||||
updated_at: dossier.created_at
|
||||
)
|
||||
end
|
||||
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def convert_pj_to_champ!(pj, champ)
|
||||
actual_file_exists = pj.content.file.send(:file)
|
||||
if actual_file_exists
|
||||
blob = make_blob(pj)
|
||||
|
||||
# Upload the file before creating the attachment to make sure MIME type
|
||||
# identification doesn’t fail.
|
||||
storage_service.copy_from_carrierwave_to_active_storage!(pj.content.path, blob)
|
||||
attachment = storage_service.make_attachment(champ, 'piece_justificative_file', blob)
|
||||
|
||||
else
|
||||
make_empty_blob(pj)
|
||||
rake_puts "Notice: attached file for champ #{champ.id} not found. An empty blob has been attached instead."
|
||||
end
|
||||
|
||||
# By reloading, we force ActiveStorage to look at the attachment again, and see
|
||||
# that one exists now. We do this so that, if we need to roll back and destroy the champ,
|
||||
# the blob, the attachment and the actual file on OpenStack also get deleted.
|
||||
champ.reload
|
||||
rescue StandardError, SignalException
|
||||
# Destroy partially attached object that the more general rescue in `populate_champs_pjs!`
|
||||
# might not be able to handle.
|
||||
|
||||
if blob&.key.present?
|
||||
begin
|
||||
storage_service.delete_from_active_storage!(blob)
|
||||
rescue => e
|
||||
# The cleanup attempt failed, perhaps because the object had not been
|
||||
# successfully copied to the Active Storage bucket yet.
|
||||
# Continue trying to clean up the rest anyway.
|
||||
pp e
|
||||
end
|
||||
end
|
||||
|
||||
blob&.destroy
|
||||
attachment&.destroy
|
||||
champ.reload
|
||||
|
||||
# Reraise the exception to abort the migration.
|
||||
raise
|
||||
end
|
||||
|
||||
def rollback_migration!(types_de_champ_pj)
|
||||
types_de_champ_pj.each do |type_champ|
|
||||
# First destroy all the individual champs on dossiers
|
||||
type_champ.champ.each do |champ|
|
||||
begin
|
||||
destroy_champ_pj(Dossier.unscope(where: :hidden_at).find(champ.dossier_id), champ)
|
||||
rescue => e
|
||||
rake_puts e
|
||||
rake_puts "Rolling back of champ #{champ.id} failed. Continuing to roll back…"
|
||||
end
|
||||
end
|
||||
# Now we can destroy the type de champ itself,
|
||||
# without cascading the timestamp update on all attached dossiers.
|
||||
type_champ.reload.destroy
|
||||
end
|
||||
end
|
||||
|
||||
def make_blob(pj)
|
||||
storage_service.make_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename)
|
||||
end
|
||||
|
||||
def make_empty_blob(pj)
|
||||
storage_service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename)
|
||||
end
|
||||
|
||||
def preserving_updated_at(model)
|
||||
original_modification_date = model.updated_at
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
model.update_column(:updated_at, original_modification_date)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_pieces_justificatives(dossier)
|
||||
preserving_updated_at(dossier) do
|
||||
dossier.pieces_justificatives.destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_champ_pj(dossier, champ)
|
||||
preserving_updated_at(dossier) do
|
||||
champ.piece_justificative_file.purge
|
||||
champ.destroy
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,75 +1,4 @@
|
|||
class PiecesJustificativesService
|
||||
def self.upload!(dossier, user, params)
|
||||
tpj_contents = dossier.types_de_piece_justificative
|
||||
.map { |tpj| [tpj, params["piece_justificative_#{tpj.id}"]] }
|
||||
.select { |_, content| content.present? }
|
||||
|
||||
without_virus, with_virus = tpj_contents
|
||||
.partition { |_, content| ClamavService.safe_file?(content.path) }
|
||||
|
||||
errors = with_virus
|
||||
.map { |_, content| "#{content.original_filename} : virus détecté" }
|
||||
|
||||
errors + without_virus
|
||||
.map { |tpj, content| save_pj(content, dossier, tpj, user) }
|
||||
.compact()
|
||||
end
|
||||
|
||||
def self.save_pj(content, dossier, tpj, user)
|
||||
pj = PieceJustificative.new(content: content,
|
||||
dossier: dossier,
|
||||
type_de_piece_justificative: tpj,
|
||||
user: user)
|
||||
|
||||
pj.save ? nil : "le fichier #{content.original_filename} (#{pj.libelle.truncate(200)}) n'a pas pu être sauvegardé"
|
||||
end
|
||||
|
||||
def self.missing_pj_error_messages(dossier)
|
||||
mandatory_pjs = dossier.types_de_piece_justificative.select(&:mandatory)
|
||||
present_pjs = dossier.pieces_justificatives.map(&:type_de_piece_justificative)
|
||||
missing_pjs = mandatory_pjs - present_pjs
|
||||
|
||||
missing_pjs.map { |pj| "La pièce jointe #{pj.libelle.truncate(200)} doit être fournie." }
|
||||
end
|
||||
|
||||
def self.types_pj_as_types_de_champ(procedure)
|
||||
max_order_place = procedure.types_de_champ.pluck(:order_place).compact.max || -1
|
||||
order_place = max_order_place + 1
|
||||
|
||||
types_de_champ = [
|
||||
TypeDeChamp.new(
|
||||
libelle: "Pièces jointes",
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:header_section),
|
||||
order_place: order_place
|
||||
)
|
||||
]
|
||||
types_de_champ += procedure.types_de_piece_justificative.map do |tpj|
|
||||
order_place += 1
|
||||
description = tpj.description
|
||||
if tpj.lien_demarche.present?
|
||||
if description.present?
|
||||
description += "\n"
|
||||
end
|
||||
description += "Récupérer le formulaire vierge pour mon dossier : #{tpj.lien_demarche}"
|
||||
end
|
||||
TypeDeChamp.new(
|
||||
libelle: tpj.libelle,
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:piece_justificative),
|
||||
description: description,
|
||||
order_place: order_place,
|
||||
mandatory: tpj.mandatory,
|
||||
old_pj: {
|
||||
stable_id: tpj.id
|
||||
}
|
||||
)
|
||||
end
|
||||
if types_de_champ.count > 1
|
||||
types_de_champ
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def self.liste_pieces_justificatives(dossier)
|
||||
dossier.champs
|
||||
.select { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) }
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
class PieceJustificativeUploader < BaseUploader
|
||||
before :cache, :set_original_filename
|
||||
|
||||
# Choose what kind of storage to use for this uploader:
|
||||
if Flipflop.remote_storage?
|
||||
storage :fog
|
||||
else
|
||||
storage :file
|
||||
end
|
||||
|
||||
# Override the directory where uploaded files will be stored.
|
||||
# This is a sensible default for uploaders that are meant to be mounted:
|
||||
def store_dir
|
||||
if !Flipflop.remote_storage?
|
||||
"./uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
end
|
||||
|
||||
# Add a white list of extensions which are allowed to be uploaded.
|
||||
# For images you might use something like this:
|
||||
def extension_whitelist
|
||||
['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp', 'jpg', 'jpeg', 'png']
|
||||
end
|
||||
|
||||
def filename
|
||||
if original_filename.present? || model.content_secure_token
|
||||
if Flipflop.remote_storage?
|
||||
filename = "#{model.class.to_s.underscore}-#{secure_token}.#{file.extension&.downcase}"
|
||||
else
|
||||
filename = "#{model.class.to_s.underscore}.#{file.extension&.downcase}"
|
||||
end
|
||||
end
|
||||
filename
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def secure_token
|
||||
model.content_secure_token ||= generate_secure_token
|
||||
end
|
||||
|
||||
def generate_secure_token
|
||||
SecureRandom.uuid
|
||||
end
|
||||
|
||||
def set_original_filename(file)
|
||||
if file.respond_to?(:original_filename)
|
||||
model.original_filename ||= file.original_filename
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
.pieces-justificatives-fields
|
||||
= f.fields_for :types_de_piece_justificative, types_de_piece_justificative, remote: true do |ff|
|
||||
.form-inline
|
||||
.form-group
|
||||
%h4 Libellé
|
||||
= ff.text_field :libelle, class: 'form-control libelle', placeholder: 'Libellé'
|
||||
.form-group
|
||||
%h4 Description
|
||||
= ff.text_area :description, class: 'form-control description', placeholder: 'Description'
|
||||
.form-group
|
||||
%h4
|
||||
Lien du formulaire vierge
|
||||
%small
|
||||
(optionel)
|
||||
= ff.url_field :lien_demarche, class: 'form-control', placeholder: 'Lien du document vierge'
|
||||
|
||||
.form-group
|
||||
= ff.hidden_field :order_place, value: ff.index
|
||||
= ff.hidden_field :id
|
||||
- if ff.object.id.present?
|
||||
.form-group
|
||||
%br
|
||||
= button_up(@procedure, "piece_justificative", ff.index, move_up_admin_procedure_pieces_justificatives_path(@procedure, ff.index))
|
||||
= button_down(@procedure, "piece_justificative", ff.index, move_down_admin_procedure_pieces_justificatives_path(@procedure, ff.index))
|
||||
|
||||
.form-group
|
||||
%h4 Obligatoire ?
|
||||
.center
|
||||
= ff.check_box :mandatory
|
||||
|
||||
.form-group
|
||||
%br
|
||||
- if ff.object.id.nil?
|
||||
= f.submit('Ajouter la pièce', class: 'btn btn-success', id: 'add_piece_justificative')
|
||||
- else
|
||||
= link_to("", admin_procedure_piece_justificative_path(@procedure, ff.object.id), method: :delete, remote: true, id: "delete_type_de_piece_justificative_#{ff.object.id}", class: %w(form-control btn btn-danger fa fa-trash-o) )
|
|
@ -1,7 +0,0 @@
|
|||
= form_for [:admin, @procedure], url: admin_procedure_pieces_justificatives_path(@procedure), remote: true do |f|
|
||||
#liste_piece_justificative
|
||||
= render partial: 'fields', locals: { types_de_piece_justificative: @procedure.types_de_piece_justificative, f: f }
|
||||
= f.submit "Enregistrer", class: 'btn btn-success', id: :save
|
||||
%hr
|
||||
#new_type_de_piece_justificative
|
||||
= render partial: 'fields', locals: { types_de_piece_justificative: TypeDePieceJustificative.new, f: f }
|
|
@ -1,25 +0,0 @@
|
|||
.row.white-back
|
||||
.alert.alert-info
|
||||
.form-group
|
||||
%p
|
||||
Pour vos nouveaux besoins de pièces jointes, nous vous invitons à
|
||||
= link_to(champs_procedure_path(@procedure)) do
|
||||
rajouter des champs
|
||||
\ <em>pièce justificative</em> à votre formulaire.
|
||||
|
||||
%p
|
||||
Ils offrent les avantages suivants :
|
||||
|
||||
%ul
|
||||
%li Pièces justificatives au fil du formulaire, pour un déroulé plus fluide
|
||||
%li Possibilité de fournir à l’usager un fichier type à remplir et renvoyer
|
||||
%li Possibilité pour l’usager de supprimer les documents joints par erreur
|
||||
%li Support des pièces de grande taille (jusqu’à 200 Mo par pièce)
|
||||
%li Pas de limite de soumission simultanée de plusieurs pièces, pour une expérience usager plus confortable
|
||||
|
||||
= link_to(champs_procedure_path(@procedure), class: 'btn btn-success') do
|
||||
Ajouter un champ PJ
|
||||
|
||||
- if @procedure.has_old_pjs?
|
||||
#piece_justificative_form
|
||||
= render 'form'
|
|
@ -1,2 +0,0 @@
|
|||
<%= render_flash(timeout: 3000, sticky: true) %>
|
||||
<%= render_to_element('#piece_justificative_form', partial: 'admin/pieces_justificatives/form', locals: { procedure: @procedure }) %>
|
|
@ -1,4 +0,0 @@
|
|||
.field-unit__label
|
||||
= f.label field.attribute
|
||||
.field-unit__field
|
||||
= f.text_field field.attribute
|
|
@ -1 +0,0 @@
|
|||
= field.to_s
|
|
@ -1,12 +0,0 @@
|
|||
- if field.data.any?
|
||||
%table.collection-data{ "aria-labelledby": "page-title" }
|
||||
%thead
|
||||
%tr
|
||||
%td.cell-label Libelle
|
||||
%tbody
|
||||
- field.data.order(:order_place).each do |f|
|
||||
%tr
|
||||
%td.cell-data
|
||||
= f.libelle
|
||||
- else
|
||||
Aucun
|
|
@ -17,20 +17,6 @@
|
|||
- if champs.any?
|
||||
= render partial: "shared/dossiers/champs", locals: { champs: champs, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' }
|
||||
|
||||
- if @dossier.types_de_piece_justificative.any?
|
||||
%h3 Pièces jointes
|
||||
|
||||
%table
|
||||
- @dossier.procedure.types_de_piece_justificative.each do |type_de_piece_justificative|
|
||||
%tr
|
||||
%th= "#{type_de_piece_justificative.libelle} :"
|
||||
%td
|
||||
- pj = @dossier.retrieve_last_piece_justificative_by_type(type_de_piece_justificative.id)
|
||||
- if pj.present?
|
||||
#{pj.original_filename}
|
||||
- else
|
||||
Pièce non fournie
|
||||
|
||||
%h2 Annotations privées
|
||||
|
||||
- champs_annotations_privees = @dossier.champs_private
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
= render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: 'Pieces' }
|
|
@ -42,11 +42,6 @@
|
|||
.procedure-list-element{ class: ('active' if active == 'Champs') }
|
||||
Champs
|
||||
|
||||
- if !@procedure.locked?
|
||||
%a#onglet-pieces{ href: url_for(admin_procedure_pieces_justificatives_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Pieces') }
|
||||
Pièces jointes
|
||||
|
||||
- if !@procedure.locked?
|
||||
%a#onglet-private-champs{ href: annotations_procedure_path(@procedure) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Annotations privées') }
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
- case notification.type_notif
|
||||
- when "commentaire"
|
||||
.type-notif.fa.fa-comments-o
|
||||
- when "submitted"
|
||||
.type-notif.fa.fa-thumbs-o-up
|
||||
- when "champs"
|
||||
.type-notif.fa.fa-list-alt
|
||||
- when "piece_justificative"
|
||||
.type-notif.fa.fa-chain
|
|
@ -26,8 +26,3 @@
|
|||
- if champs.any?
|
||||
.card
|
||||
= render partial: "shared/dossiers/champs", locals: { champs: champs, demande_seen_at: demande_seen_at, profile: profile }
|
||||
|
||||
- if dossier.types_de_piece_justificative.any?
|
||||
.tab-title Pièces jointes
|
||||
.card
|
||||
= render partial: "shared/dossiers/pieces_jointes", locals: { dossier: dossier, demande_seen_at: demande_seen_at }
|
||||
|
|
|
@ -31,45 +31,6 @@
|
|||
= render partial: "shared/dossiers/editable_champs/editable_champ",
|
||||
locals: { champ: champ, form: champ_form }
|
||||
|
||||
- tpjs = dossier.types_de_piece_justificative.order('order_place ASC')
|
||||
- if tpjs.present?
|
||||
.card.featured
|
||||
.card-title
|
||||
Pièces jointes
|
||||
|
||||
.warning
|
||||
- if tpjs.many?
|
||||
Pour éviter toute erreur, nous vous conseillons de limiter la taille de chaque pièce jointe à 20 Mo, et de les ajouter une par une, en enregistrant votre
|
||||
= dossier.brouillon? ? "brouillon" : "dossier"
|
||||
après chaque ajout.
|
||||
- else
|
||||
Pour éviter toute erreur, nous vous conseillons de limiter la taille de votre pièce jointe à 20 Mo.
|
||||
|
||||
- tpjs.each do |tpj|
|
||||
.pj-input
|
||||
%label{ for: "piece_justificative_#{tpj.id}" }
|
||||
= tpj.libelle
|
||||
- if tpj.mandatory?
|
||||
%span.mandatory *
|
||||
|
||||
%p.piece-description= tpj.description
|
||||
|
||||
- if tpj.lien_demarche.present?
|
||||
%p.piece-description
|
||||
Récupérer le formulaire vierge pour mon dossier :
|
||||
= link_to "Télécharger", tpj.lien_demarche, target: :blank, rel: :noopener
|
||||
|
||||
- if dossier.was_piece_justificative_uploaded_for_type_id?(tpj.id)
|
||||
- pj = dossier.retrieve_last_piece_justificative_by_type(tpj.id)
|
||||
%p
|
||||
Pièce jointe déjà importée :
|
||||
= link_to pj.original_filename, pj.content_url, target: :blank, rel: :noopener
|
||||
|
||||
= file_field_tag "piece_justificative_#{tpj.id}",
|
||||
accept: PieceJustificative.accept_format,
|
||||
max_file_size: 6.megabytes,
|
||||
required: (tpj.mandatory? && !dossier.was_piece_justificative_uploaded_for_type_id?(tpj.id))
|
||||
|
||||
- if !apercu
|
||||
.send-wrapper
|
||||
- if dossier.brouillon?
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
%table.table.vertical.pj.dossier-champs
|
||||
%tbody
|
||||
- dossier.procedure.types_de_piece_justificative.each do |type_de_piece_justificative|
|
||||
- pjs = dossier.retrieve_all_piece_justificative_by_type(type_de_piece_justificative.id).to_ary.dup
|
||||
- pj = pjs.shift if pjs.present?
|
||||
%tr
|
||||
%th= "#{type_de_piece_justificative.libelle} :"
|
||||
- if pj
|
||||
%td
|
||||
%span{ class: highlight_if_unseen_class(demande_seen_at, pj.updated_at) }
|
||||
= display_pj_filename(pj)
|
||||
·
|
||||
= link_to "Télécharger", pj.content_url, class: "link", target: :blank, rel: :noopener
|
||||
- if pjs.present?
|
||||
%br
|
||||
%span.dropdown
|
||||
%button.button.dropdown-button
|
||||
anciennes versions
|
||||
.dropdown-content.fade-in-down
|
||||
%ul.dropdown-items
|
||||
- pjs.each do |pj|
|
||||
%li
|
||||
= link_to pj.content_url, { target: :blank, rel: :noopener } do
|
||||
%span.filename= display_pj_filename(pj)
|
||||
%span
|
||||
ajoutée le #{try_format_datetime(pj.created_at)}
|
||||
- else
|
||||
%td Pièce non fournie
|
||||
%td.updated-at
|
||||
- if pj
|
||||
%span{ class: highlight_if_unseen_class(demande_seen_at, pj.updated_at) }
|
||||
modifié le
|
||||
= try_format_datetime(pj.updated_at)
|
|
@ -10,8 +10,6 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|||
# inflect.uncountable %w( fish sheep )
|
||||
inflect.acronym 'API'
|
||||
inflect.acronym 'RNA'
|
||||
inflect.irregular 'piece_justificative', 'pieces_justificatives'
|
||||
inflect.irregular 'type_de_piece_justificative', 'types_de_piece_justificative'
|
||||
inflect.irregular 'type_de_champ', 'types_de_champ'
|
||||
inflect.irregular 'type_de_champ_private', 'types_de_champ_private'
|
||||
inflect.irregular 'assign_to', 'assign_tos'
|
||||
|
|
|
@ -138,10 +138,6 @@ fr:
|
|||
attributes:
|
||||
footer:
|
||||
too_long: ": le pied de page est trop long."
|
||||
piece_justificative:
|
||||
attributes:
|
||||
content:
|
||||
size_too_big: "La taille du fichier joint est trop importante. Elle doit être inférieure à 20Mo."
|
||||
user:
|
||||
attributes:
|
||||
reset_password_token:
|
||||
|
|
|
@ -193,13 +193,6 @@ Rails.application.routes.draw do
|
|||
delete :delete_notice
|
||||
end
|
||||
|
||||
resource :pieces_justificatives, only: [:show, :update]
|
||||
resources :pieces_justificatives, only: :destroy
|
||||
resource :pieces_justificatives, only: [:show, :update] do
|
||||
post '/:index/move_up' => 'pieces_justificatives#move_up', as: :move_up
|
||||
post '/:index/move_down' => 'pieces_justificatives#move_down', as: :move_down
|
||||
end
|
||||
|
||||
resources :mail_templates, only: [:index, :edit, :update]
|
||||
|
||||
put 'archive' => 'procedures#archive', as: :archive
|
||||
|
|
|
@ -1,291 +0,0 @@
|
|||
module Tasks
|
||||
class DossierProcedureMigrator
|
||||
# Migrates dossiers from an old source procedure to a revised destination procedure.
|
||||
|
||||
class ChampMapping
|
||||
def initialize(source_procedure, destination_procedure, is_private)
|
||||
@source_procedure = source_procedure
|
||||
@destination_procedure = destination_procedure
|
||||
@is_private = is_private
|
||||
|
||||
@expected_source_types_de_champ = {}
|
||||
@expected_destination_types_de_champ = {}
|
||||
@source_to_destination_mapping = {}
|
||||
@source_champs_to_discard = Set[]
|
||||
@destination_champ_computations = []
|
||||
|
||||
setup_mapping
|
||||
end
|
||||
|
||||
def check_source_destination_consistency
|
||||
check_champs_consistency("#{privacy_label}source", @expected_source_types_de_champ, types_de_champ(@source_procedure))
|
||||
check_champs_consistency("#{privacy_label}destination", @expected_destination_types_de_champ, types_de_champ(@destination_procedure))
|
||||
end
|
||||
|
||||
def can_migrate?(dossier)
|
||||
true
|
||||
end
|
||||
|
||||
def migrate(dossier)
|
||||
# Since we’re going to iterate and change the champs at the same time,
|
||||
# we use to_a to make the list static and avoid nasty surprises
|
||||
original_champs = champs(dossier).to_a
|
||||
|
||||
compute_new_champs(dossier)
|
||||
|
||||
original_champs.each do |c|
|
||||
tdc_to = destination_type_de_champ(c)
|
||||
if tdc_to.present?
|
||||
c.update_columns(type_de_champ_id: tdc_to.id)
|
||||
elsif discard_champ?(c)
|
||||
champs(dossier).destroy(c)
|
||||
else
|
||||
fail "Unhandled source #{privacy_label}type de champ #{c.type_de_champ.order_place}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_new_champs(dossier)
|
||||
@destination_champ_computations.each do |tdc, block|
|
||||
champs(dossier) << block.call(dossier, tdc)
|
||||
end
|
||||
end
|
||||
|
||||
def destination_type_de_champ(champ)
|
||||
@source_to_destination_mapping[champ.type_de_champ.order_place]
|
||||
end
|
||||
|
||||
def discard_champ?(champ)
|
||||
@source_champs_to_discard.member?(champ.type_de_champ.order_place)
|
||||
end
|
||||
|
||||
def setup_mapping
|
||||
end
|
||||
|
||||
def champs(dossier)
|
||||
@is_private ? dossier.champs_private : dossier.champs
|
||||
end
|
||||
|
||||
def types_de_champ(procedure)
|
||||
@is_private ? procedure.types_de_champ_private : procedure.types_de_champ
|
||||
end
|
||||
|
||||
def privacy_label
|
||||
@is_private ? 'private ' : ''
|
||||
end
|
||||
|
||||
def check_champs_consistency(label, expected_tdcs, actual_tdcs)
|
||||
if actual_tdcs.size != expected_tdcs.size
|
||||
raise "Incorrect #{label} size #{actual_tdcs.size} (expected #{expected_tdcs.size})"
|
||||
end
|
||||
actual_tdcs.each { |tdc| check_champ_consistency(label, expected_tdcs[tdc.order_place], tdc) }
|
||||
end
|
||||
|
||||
def check_champ_consistency(label, expected_tdc, actual_tdc)
|
||||
errors = []
|
||||
if actual_tdc.libelle != expected_tdc['libelle']
|
||||
errors.append("incorrect libelle #{actual_tdc.libelle} (expected #{expected_tdc['libelle']})")
|
||||
end
|
||||
if actual_tdc.type_champ != expected_tdc['type_champ']
|
||||
errors.append("incorrect type champ #{actual_tdc.type_champ} (expected #{expected_tdc['type_champ']})")
|
||||
end
|
||||
if (!actual_tdc.mandatory) && expected_tdc['mandatory']
|
||||
errors.append("champ should be mandatory")
|
||||
end
|
||||
drop_down = actual_tdc.drop_down_list.presence&.options&.presence
|
||||
if drop_down != expected_tdc['drop_down']
|
||||
errors.append("incorrect drop down list #{drop_down} (expected #{expected_tdc['drop_down']})")
|
||||
end
|
||||
if errors.present?
|
||||
fail "On #{label} type de champ #{actual_tdc.order_place} (#{actual_tdc.libelle}) " + errors.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def map_source_to_destination_champ(source_order_place, destination_order_place, source_overrides: {}, destination_overrides: {})
|
||||
destination_type_de_champ = types_de_champ(@destination_procedure).find_by(order_place: destination_order_place)
|
||||
@expected_source_types_de_champ[source_order_place] =
|
||||
type_de_champ_to_expectation(destination_type_de_champ)
|
||||
.merge!(source_overrides)
|
||||
@expected_destination_types_de_champ[destination_order_place] =
|
||||
type_de_champ_to_expectation(types_de_champ(@source_procedure).find_by(order_place: source_order_place))
|
||||
.merge!({ "mandatory" => false }) # Even if the source was mandatory, it’s ok for the destination to be optional
|
||||
.merge!(destination_overrides)
|
||||
@source_to_destination_mapping[source_order_place] = destination_type_de_champ
|
||||
end
|
||||
|
||||
def discard_source_champ(source_type_de_champ)
|
||||
@expected_source_types_de_champ[source_type_de_champ.order_place] = type_de_champ_to_expectation(source_type_de_champ)
|
||||
@source_champs_to_discard << source_type_de_champ.order_place
|
||||
end
|
||||
|
||||
def compute_destination_champ(destination_type_de_champ, &block)
|
||||
@expected_destination_types_de_champ[destination_type_de_champ.order_place] = type_de_champ_to_expectation(destination_type_de_champ)
|
||||
@destination_champ_computations << [types_de_champ(@destination_procedure).find_by(order_place: destination_type_de_champ.order_place), block]
|
||||
end
|
||||
|
||||
def type_de_champ_to_expectation(tdc)
|
||||
if tdc.present?
|
||||
expectation = tdc.as_json(only: [:libelle, :type_champ, :mandatory])
|
||||
expectation['drop_down'] = tdc.drop_down_list.presence&.options&.presence
|
||||
expectation
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PieceJustificativeMapping
|
||||
def initialize(source_procedure, destination_procedure)
|
||||
@source_procedure = source_procedure
|
||||
@destination_procedure = destination_procedure
|
||||
|
||||
@expected_source_pj = {}
|
||||
@expected_destination_pj = {}
|
||||
@source_to_destination_mapping = {}
|
||||
|
||||
setup_mapping
|
||||
end
|
||||
|
||||
def check_source_destination_consistency
|
||||
check_pjs_consistency('source', @expected_source_pj, @source_procedure.types_de_piece_justificative)
|
||||
check_pjs_consistency('destination', @expected_destination_pj, @destination_procedure.types_de_piece_justificative)
|
||||
end
|
||||
|
||||
def can_migrate?(dossier)
|
||||
true
|
||||
end
|
||||
|
||||
def migrate(dossier)
|
||||
# Since we’re going to iterate and change the pjs at the same time,
|
||||
# we use to_a to make the list static and avoid nasty surprises
|
||||
original_pjs = dossier.pieces_justificatives.to_a
|
||||
|
||||
original_pjs.each do |pj|
|
||||
pj_to = destination_pj(pj)
|
||||
if pj_to.present?
|
||||
pj.update_columns(type_de_piece_justificative_id: pj_to.id)
|
||||
elsif discard_pj?(pj)
|
||||
dossier.pieces_justificatives.destroy(pj)
|
||||
else
|
||||
fail "Unhandled source pièce justificative #{c.type_de_piece_justificative.order_place}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def destination_pj(pj)
|
||||
@source_to_destination_mapping[pj.order_place]
|
||||
end
|
||||
|
||||
def discard_pj?(champ)
|
||||
@source_pjs_to_discard.member?(pj.order_place)
|
||||
end
|
||||
|
||||
def setup_mapping
|
||||
end
|
||||
|
||||
def map_source_to_destination_pj(source_order_place, destination_order_place, source_overrides: {}, destination_overrides: {})
|
||||
destination_pj = @destination_procedure.types_de_piece_justificative.find_by(order_place: destination_order_place)
|
||||
@expected_source_pj[source_order_place] =
|
||||
pj_to_expectation(destination_pj)
|
||||
.merge!(source_overrides)
|
||||
@expected_destination_pj[destination_order_place] =
|
||||
pj_to_expectation(@source_procedure.types_de_piece_justificative.find_by(order_place: source_order_place))
|
||||
.merge!({ "mandatory" => false }) # Even if the source was mandatory, it’s ok for the destination to be optional
|
||||
.merge!(destination_overrides)
|
||||
@source_to_destination_mapping[source_order_place] = destination_pj
|
||||
end
|
||||
|
||||
def discard_source_pj(source_pj)
|
||||
@expected_source_pj[source_pj.order_place] = pj_to_expectation(source_pj)
|
||||
@source_pjs_to_discard << source_pj.order_place
|
||||
end
|
||||
|
||||
def leave_destination_pj_blank(destination_pj)
|
||||
@expected_destination_pj[destination_pj.order_place] = pj_to_expectation(destination_pj)
|
||||
end
|
||||
|
||||
def pj_to_expectation(pj)
|
||||
pj&.as_json(only: [:libelle, :mandatory]) || {}
|
||||
end
|
||||
|
||||
def check_pjs_consistency(label, expected_pjs, actual_pjs)
|
||||
if actual_pjs.size != expected_pjs.size
|
||||
raise "Incorrect #{label} pièce justificative count #{actual_pjs.size} (expected #{expected_pjs.size})"
|
||||
end
|
||||
actual_pjs.each { |pj| check_pj_consistency(label, expected_pjs[pj.order_place], pj) }
|
||||
end
|
||||
|
||||
def check_pj_consistency(label, expected_pj, actual_pj)
|
||||
errors = []
|
||||
if actual_pj.libelle != expected_pj['libelle']
|
||||
errors.append("incorrect libelle #{actual_pj.libelle} (expected #{expected_pj['libelle']})")
|
||||
end
|
||||
if (!actual_pj.mandatory) && expected_pj['mandatory']
|
||||
errors.append("pj should be mandatory")
|
||||
end
|
||||
if errors.present?
|
||||
fail "On #{label} type de pièce justificative #{actual_pj.order_place} (#{actual_pj.libelle}) " + errors.join(', ')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(source_procedure, destination_procedure, champ_mapping, private_champ_mapping = ChampMapping, piece_justificative_mapping = PieceJustificativeMapping, &block)
|
||||
@source_procedure = source_procedure
|
||||
@destination_procedure = destination_procedure
|
||||
@champ_mapping = champ_mapping.new(source_procedure, destination_procedure, false)
|
||||
@private_champ_mapping = private_champ_mapping.new(source_procedure, destination_procedure, true)
|
||||
@piece_justificative_mapping = piece_justificative_mapping.new(source_procedure, destination_procedure)
|
||||
|
||||
if block_given?
|
||||
@on_dossier_migration = block
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_procedure
|
||||
check_consistency
|
||||
migrate_dossiers
|
||||
migrate_gestionnaires
|
||||
publish_destination_procedure_in_place_of_source
|
||||
end
|
||||
|
||||
def check_consistency
|
||||
check_same_administrateur
|
||||
@champ_mapping.check_source_destination_consistency
|
||||
@private_champ_mapping.check_source_destination_consistency
|
||||
@piece_justificative_mapping.check_source_destination_consistency
|
||||
end
|
||||
|
||||
def check_same_administrateur
|
||||
if @source_procedure.administrateur_ids.sort != @destination_procedure.administrateur_ids.sort
|
||||
raise "Mismatching administrateurs #{@source_procedure.administrateurs.pluck(:email)} → #{@destination_procedure.administrateurs.pluck(:email)}"
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_dossiers
|
||||
@source_procedure.dossiers.find_each(batch_size: 100) do |d|
|
||||
if @champ_mapping.can_migrate?(d) && @private_champ_mapping.can_migrate?(d) && @piece_justificative_mapping.can_migrate?(d)
|
||||
@champ_mapping.migrate(d)
|
||||
@private_champ_mapping.migrate(d)
|
||||
@piece_justificative_mapping.migrate(d)
|
||||
|
||||
# Use update_columns to avoid triggering build_default_champs
|
||||
d.update_columns(procedure_id: @destination_procedure.id)
|
||||
@on_dossier_migration&.call(d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_gestionnaires
|
||||
@source_procedure.gestionnaires.find_each(batch_size: 100) { |g| g.assign_to_procedure(@destination_procedure) }
|
||||
end
|
||||
|
||||
def publish_destination_procedure_in_place_of_source
|
||||
@destination_procedure.publish!(@source_procedure.path)
|
||||
@source_procedure.archive
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
require Rails.root.join("lib", "tasks", "task_helper")
|
||||
|
||||
namespace :pieces_justificatives do
|
||||
desc <<~EOD
|
||||
Migrate the PJ to champs for a single PROCEDURE_ID.
|
||||
EOD
|
||||
task migrate_procedure_to_champs: :environment do
|
||||
procedure_id = ENV['PROCEDURE_ID']
|
||||
procedure = Procedure.find(procedure_id)
|
||||
|
||||
service = PieceJustificativeToChampPieceJointeMigrationService.new
|
||||
service.ensure_correct_storage_configuration!
|
||||
|
||||
progress = ProgressReport.new(service.number_of_champs_to_migrate(procedure))
|
||||
|
||||
service.convert_procedure_pjs_to_champ_pjs(procedure) do
|
||||
progress.inc
|
||||
end
|
||||
|
||||
progress.finish
|
||||
end
|
||||
|
||||
desc <<~EOD
|
||||
Migrate the PJ to champs for several procedures ids, from RANGE_START to RANGE_END.
|
||||
EOD
|
||||
task migrate_procedures_range_to_champs: :environment do
|
||||
if ENV['RANGE_START'].nil? || ENV['RANGE_END'].nil?
|
||||
fail "RANGE_START and RANGE_END must be specified"
|
||||
end
|
||||
procedures_range = ENV['RANGE_START']..ENV['RANGE_END']
|
||||
|
||||
service = PieceJustificativeToChampPieceJointeMigrationService.new
|
||||
service.ensure_correct_storage_configuration!
|
||||
procedures_to_migrate = service.procedures_with_pjs_in_range(procedures_range)
|
||||
|
||||
total_number_of_champs_to_migrate = procedures_to_migrate
|
||||
.map { |p| service.number_of_champs_to_migrate(p) }
|
||||
.sum
|
||||
progress = ProgressReport.new(total_number_of_champs_to_migrate)
|
||||
|
||||
procedures_to_migrate.find_each do |procedure|
|
||||
rake_puts ''
|
||||
rake_puts "Migrating procedure #{procedure.id}…"
|
||||
|
||||
service.convert_procedure_pjs_to_champ_pjs(procedure) do
|
||||
progress.inc
|
||||
end
|
||||
end
|
||||
|
||||
progress.finish
|
||||
end
|
||||
end
|
|
@ -1,170 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Admin::PiecesJustificativesController, type: :controller do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, administrateur: admin) }
|
||||
before do
|
||||
sign_in admin
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
let(:procedure_id) { procedure.id }
|
||||
|
||||
subject { get :show, params: { procedure_id: procedure_id } }
|
||||
|
||||
context 'when procedure is not found' do
|
||||
let(:procedure_id) { 9_999_999 }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
|
||||
context 'when procedure is published' do
|
||||
let(:procedure) { create(:procedure, :published, administrateur: admin) }
|
||||
it { is_expected.to redirect_to admin_procedure_path id: procedure_id }
|
||||
end
|
||||
|
||||
context 'when procedure does not belong to admin' do
|
||||
let(:admin_2) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, administrateur: admin_2) }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
let(:procedure_id) { procedure.id }
|
||||
let(:libelle) { 'RIB' }
|
||||
let(:description) { "relevé d'identité bancaire" }
|
||||
let(:update_params) do
|
||||
{
|
||||
types_de_piece_justificative_attributes:
|
||||
{
|
||||
'0' =>
|
||||
{
|
||||
libelle: libelle,
|
||||
description: description
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:request) { put :update, params: { procedure_id: procedure_id, format: :js, procedure: update_params } }
|
||||
subject { request }
|
||||
|
||||
it { is_expected.to render_template('show') }
|
||||
it { expect { subject }.to change(TypeDePieceJustificative, :count).by(1) }
|
||||
it 'adds type de pj to procedure' do
|
||||
request
|
||||
procedure.reload
|
||||
pj = procedure.types_de_piece_justificative.first
|
||||
expect(pj.libelle).to eq(libelle)
|
||||
expect(pj.description).to eq(description)
|
||||
end
|
||||
|
||||
context 'when procedure is not found' do
|
||||
let(:procedure_id) { 9_999_999 }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
|
||||
context 'when libelle is blank' do
|
||||
let(:libelle) { '' }
|
||||
it { expect { subject }.not_to change(TypeDePieceJustificative, :count) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let!(:pj) { create(:type_de_piece_justificative, procedure: procedure) }
|
||||
let(:procedure_id) { procedure.id }
|
||||
let(:pj_id) { pj.id }
|
||||
let(:request) { delete :destroy, params: { procedure_id: procedure_id, id: pj_id } }
|
||||
subject { request }
|
||||
context 'when procedure is not found' do
|
||||
let(:procedure_id) { 9_999_999 }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
context 'when pj id does not exist' do
|
||||
let(:pj_id) { 9_999_999 }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
context 'when pj id exist but is not linked to procedure' do
|
||||
let(:procedure_1) { create(:procedure, administrateur: admin) }
|
||||
let!(:pj_1) { create(:type_de_piece_justificative, procedure: procedure_1) }
|
||||
let(:pj_id) { pj_1 }
|
||||
it { expect(subject.status).to eq(404) }
|
||||
end
|
||||
context 'when pj is found' do
|
||||
it { expect(subject.status).to eq(200) }
|
||||
it { expect { subject }.to change(TypeDePieceJustificative, :count).by(-1) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #move_up' do
|
||||
subject { post :move_up, params: { procedure_id: procedure.id, index: index, format: :js } }
|
||||
|
||||
context 'when procedure have no type de champ' do
|
||||
let(:index) { 0 }
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when procedure have only one type de champ' do
|
||||
let(:index) { 1 }
|
||||
let!(:type_de_piece_justificative) { create(:type_de_piece_justificative, procedure: procedure) }
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when procedure have tow type de champs' do
|
||||
context 'when index == 0' do
|
||||
let(:index) { 0 }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure) }
|
||||
let!(:type_de_piece_justificative_2) { create(:type_de_piece_justificative, procedure: procedure) }
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when index > 0' do
|
||||
let(:index) { 1 }
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
|
||||
|
||||
it { expect(subject.status).to eq(200) }
|
||||
it { expect(subject).to render_template('show') }
|
||||
it 'changes order places' do
|
||||
post :move_up, params: { procedure_id: procedure.id, index: index, format: :js }
|
||||
type_de_piece_justificative_0.reload
|
||||
type_de_piece_justificative_1.reload
|
||||
expect(type_de_piece_justificative_0.order_place).to eq(1)
|
||||
expect(type_de_piece_justificative_1.order_place).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #move_down' do
|
||||
let(:request) { post :move_down, params: { procedure_id: procedure.id, index: index, format: :js } }
|
||||
let(:index) { 0 }
|
||||
|
||||
subject { request }
|
||||
|
||||
context 'when procedure have no type de champ' do
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when procedure have only one type de champ' do
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure) }
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when procedure have 2 type de champ' do
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
|
||||
context 'when index represent last type_de_piece_justificative' do
|
||||
let(:index) { 1 }
|
||||
it { expect(subject.status).to eq(400) }
|
||||
end
|
||||
context 'when index does not represent last type_de_piece_justificative' do
|
||||
let(:index) { 0 }
|
||||
it { expect(subject.status).to eq(200) }
|
||||
it { expect(subject).to render_template('show') }
|
||||
it 'changes order place' do
|
||||
request
|
||||
type_de_piece_justificative_0.reload
|
||||
type_de_piece_justificative_1.reload
|
||||
expect(type_de_piece_justificative_0.order_place).to eq(1)
|
||||
expect(type_de_piece_justificative_1.order_place).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -248,7 +248,7 @@ describe Admin::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
let!(:procedure) { create(:procedure, :with_type_de_champ, :with_two_type_de_piece_justificative, administrateur: admin) }
|
||||
let!(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
|
||||
|
||||
context 'when administrateur is not connected' do
|
||||
before do
|
||||
|
@ -302,7 +302,7 @@ describe Admin::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when procedure is brouillon' do
|
||||
let(:procedure) { create(:procedure_with_dossiers, :with_path, :with_type_de_champ, :with_two_type_de_piece_justificative, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure_with_dossiers, :with_path, :with_type_de_champ, administrateur: admin) }
|
||||
let!(:dossiers_count) { procedure.dossiers.count }
|
||||
|
||||
describe 'dossiers are dropped' do
|
||||
|
@ -316,7 +316,7 @@ describe Admin::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when procedure is published' do
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_two_type_de_piece_justificative, :published, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :published, administrateur: admin) }
|
||||
|
||||
subject { update_procedure }
|
||||
|
||||
|
@ -774,7 +774,7 @@ describe Admin::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
describe 'PATCH #monavis' do
|
||||
let!(:procedure) { create(:procedure, :with_type_de_champ, :with_two_type_de_piece_justificative, administrateur: admin) }
|
||||
let!(:procedure) { create(:procedure, administrateur: admin) }
|
||||
let(:procedure_params) {
|
||||
{
|
||||
monavis_embed: monavis_embed
|
||||
|
@ -835,7 +835,7 @@ describe Admin::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when procedure is published' do
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_two_type_de_piece_justificative, :published, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure, :published, administrateur: admin) }
|
||||
|
||||
subject { update_monavis }
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'spec_helper'
|
|||
describe API::V1::DossiersController do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:token) { admin.renew_api_token }
|
||||
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_type_de_champ_private, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, administrateur: admin) }
|
||||
let(:wrong_procedure) { create(:procedure) }
|
||||
|
||||
it { expect(described_class).to be < APIController }
|
||||
|
@ -204,49 +204,13 @@ describe API::V1::DossiersController do
|
|||
it { expect(subject.keys).to match_array(field_list) }
|
||||
end
|
||||
|
||||
describe 'types_de_piece_justificative' do
|
||||
let(:field_list) { [:id, :libelle, :description] }
|
||||
subject { super()[:types_de_piece_justificative] }
|
||||
|
||||
it { expect(subject.length).to eq 2 }
|
||||
|
||||
describe 'first type de piece justificative' do
|
||||
subject { super().first }
|
||||
|
||||
it { expect(subject.key?(:id)).to be_truthy }
|
||||
it { expect(subject[:libelle]).to eq('RIB') }
|
||||
it { expect(subject[:description]).to eq('Releve identité bancaire') }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'piece justificative', vcr: { cassette_name: 'controllers_api_v1_dossiers_controller_piece_justificative' } do
|
||||
before do
|
||||
create :piece_justificative, :rib, dossier: dossier, type_de_piece_justificative: dossier.procedure.types_de_piece_justificative.first, user: dossier.user
|
||||
end
|
||||
|
||||
let(:field_list) { [:url, :created_at, :type_de_piece_justificative_id] }
|
||||
subject { super()[:pieces_justificatives].first }
|
||||
|
||||
it { expect(subject.key?(:content_url)).to be_truthy }
|
||||
it { expect(subject[:created_at]).not_to be_nil }
|
||||
it { expect(subject[:type_de_piece_justificative_id]).not_to be_nil }
|
||||
|
||||
it { expect(subject.key?(:user)).to be_truthy }
|
||||
|
||||
describe 'user' do
|
||||
subject { super()[:user] }
|
||||
|
||||
it { expect(subject[:email]).not_to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'champs' do
|
||||
let(:field_list) { [:url] }
|
||||
subject { super()[:champs] }
|
||||
|
||||
it { expect(subject.length).to eq 1 }
|
||||
|
||||
describe 'first champs' do
|
||||
describe 'first champ' do
|
||||
subject { super().first }
|
||||
|
||||
it { expect(subject.key?(:value)).to be_truthy }
|
||||
|
|
|
@ -25,7 +25,7 @@ describe API::V1::ProceduresController, type: :controller do
|
|||
it { is_expected.to have_http_status(200) }
|
||||
|
||||
describe 'body' do
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_two_type_de_piece_justificative, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
|
||||
let(:response) { get :show, params: { id: procedure.id, token: token } }
|
||||
|
||||
subject { JSON.parse(response.body, symbolize_names: true)[:procedure] }
|
||||
|
@ -51,19 +51,6 @@ describe API::V1::ProceduresController, type: :controller do
|
|||
it { expect(subject[:order_place]).to eq(champ.order_place) }
|
||||
it { expect(subject[:description]).to eq(champ.description) }
|
||||
end
|
||||
|
||||
it { is_expected.to have_key(:types_de_piece_justificative) }
|
||||
it { expect(subject[:types_de_piece_justificative]).to be_an(Array) }
|
||||
|
||||
describe 'type_de_piece_jointe' do
|
||||
subject { super()[:types_de_piece_justificative][0] }
|
||||
|
||||
let(:pj) { procedure.types_de_piece_justificative.first }
|
||||
|
||||
it { expect(subject[:id]).to eq(pj.id) }
|
||||
it { expect(subject[:libelle]).to eq(pj.libelle) }
|
||||
it { expect(subject[:description]).to eq(pj.description) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -476,29 +476,16 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the pj service returns an error' do
|
||||
before do
|
||||
expect(PiecesJustificativesService).to receive(:upload!).and_return(['nop'])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to render_template(:brouillon) }
|
||||
it { expect(flash.alert).to eq(['nop']) }
|
||||
end
|
||||
|
||||
context 'when a mandatory champ is missing' do
|
||||
let(:value) { nil }
|
||||
|
||||
before do
|
||||
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
||||
allow(PiecesJustificativesService).to receive(:missing_pj_error_messages).and_return(['pj'])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to render_template(:brouillon) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.', 'pj']) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.']) }
|
||||
|
||||
context 'and the user saves a draft' do
|
||||
let(:payload) { submit_payload.merge(save_draft: true) }
|
||||
|
@ -511,7 +498,7 @@ describe Users::DossiersController, type: :controller do
|
|||
let!(:dossier) { create(:dossier, :en_construction, user: user) }
|
||||
|
||||
it { expect(response).to render_template(:brouillon) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.', 'pj']) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.']) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -535,8 +522,6 @@ describe Users::DossiersController, type: :controller do
|
|||
|
||||
before do
|
||||
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
||||
allow(PiecesJustificativesService).to receive(:missing_pj_error_messages).and_return(['pj'])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
|
@ -644,29 +629,16 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the pj service returns an error' do
|
||||
before do
|
||||
expect(PiecesJustificativesService).to receive(:upload!).and_return(['nop'])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to render_template(:modifier) }
|
||||
it { expect(flash.alert).to eq(['nop']) }
|
||||
end
|
||||
|
||||
context 'when a mandatory champ is missing' do
|
||||
let(:value) { nil }
|
||||
|
||||
before do
|
||||
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
||||
allow(PiecesJustificativesService).to receive(:missing_pj_error_messages).and_return(['pj'])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to render_template(:modifier) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.', 'pj']) }
|
||||
it { expect(flash.alert).to eq(['Le champ l doit être rempli.']) }
|
||||
end
|
||||
|
||||
context 'when dossier has no champ' do
|
||||
|
|
|
@ -6,7 +6,7 @@ FactoryBot.define do
|
|||
|
||||
before(:create) do |dossier, _evaluator|
|
||||
if !dossier.procedure
|
||||
procedure = create(:procedure, :published, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_type_de_champ_private)
|
||||
procedure = create(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private)
|
||||
dossier.procedure = procedure
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
FactoryBot.define do
|
||||
factory :piece_justificative do
|
||||
trait :rib do
|
||||
content { Rack::Test::UploadedFile.new("./spec/fixtures/files/RIB.pdf", 'application/pdf') }
|
||||
end
|
||||
|
||||
trait :contrat do
|
||||
content { Rack::Test::UploadedFile.new("./spec/fixtures/files/Contrat.pdf", 'application/pdf') }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -135,17 +135,6 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
# Deprecated
|
||||
trait :with_two_type_de_piece_justificative do
|
||||
after(:build) do |procedure, _evaluator|
|
||||
rib = create(:type_de_piece_justificative, :rib, order_place: 1)
|
||||
msa = create(:type_de_piece_justificative, :msa, order_place: 2)
|
||||
|
||||
procedure.types_de_piece_justificative << rib
|
||||
procedure.types_de_piece_justificative << msa
|
||||
end
|
||||
end
|
||||
|
||||
trait :published do
|
||||
after(:build) do |procedure, _evaluator|
|
||||
procedure.publish!(procedure.administrateurs.first, generate(:published_path), procedure.lien_site_web)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
FactoryBot.define do
|
||||
factory :type_de_piece_justificative do
|
||||
libelle { 'RIB' }
|
||||
description { 'Releve identité bancaire' }
|
||||
|
||||
trait :rib do
|
||||
libelle { 'RIB' }
|
||||
description { 'Releve identité bancaire' }
|
||||
api_entreprise { false }
|
||||
end
|
||||
|
||||
trait :msa do
|
||||
libelle { 'Attestation MSA' }
|
||||
description { 'recuperation automatique' }
|
||||
api_entreprise { true }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -65,71 +65,50 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
|
|||
procedure.update(service: create(:service))
|
||||
end
|
||||
|
||||
context 'With old PJ' do
|
||||
before do
|
||||
# Create a dummy PJ, because adding PJs is no longer allowed on procedures that
|
||||
# do not already have one
|
||||
Procedure.last.types_de_piece_justificative.create(libelle: "dummy PJ")
|
||||
scenario 'Add champ, add file, visualize them in procedure preview' do
|
||||
page.refresh
|
||||
expect(page).to have_current_path(champs_procedure_path(Procedure.last))
|
||||
|
||||
expect(page).to have_selector('#champ-0-libelle')
|
||||
fill_in 'champ-0-libelle', with: 'libelle de champ'
|
||||
blur
|
||||
expect(page).to have_content('Formulaire enregistré')
|
||||
|
||||
within '.buttons' do
|
||||
click_on 'Ajouter un champ'
|
||||
end
|
||||
expect(page).to have_selector('#champ-1-libelle')
|
||||
|
||||
click_on Procedure.last.libelle
|
||||
|
||||
preview_window = window_opened_by { click_on 'onglet-preview' }
|
||||
within_window(preview_window) do
|
||||
expect(page).to have_current_path(apercu_procedure_path(Procedure.last))
|
||||
expect(page).to have_field('libelle de champ')
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'After adding champ and file, make publication' do
|
||||
page.refresh
|
||||
|
||||
fill_in 'champ-0-libelle', with: 'libelle de champ'
|
||||
blur
|
||||
expect(page).to have_content('Formulaire enregistré')
|
||||
|
||||
click_on Procedure.last.libelle
|
||||
expect(page).to have_current_path(admin_procedure_path(Procedure.last))
|
||||
|
||||
expect(page).to have_selector('#publish-procedure', visible: true)
|
||||
find('#publish-procedure').click
|
||||
|
||||
within '#publish-modal' do
|
||||
expect(page).to have_field('procedure_path')
|
||||
fill_in 'lien_site_web', with: 'http://some.website'
|
||||
click_on 'publish'
|
||||
end
|
||||
|
||||
scenario 'Add champ, add file, visualize them in procedure preview' do
|
||||
page.refresh
|
||||
expect(page).to have_current_path(champs_procedure_path(Procedure.last))
|
||||
|
||||
expect(page).to have_selector('#champ-0-libelle')
|
||||
fill_in 'champ-0-libelle', with: 'libelle de champ'
|
||||
blur
|
||||
expect(page).to have_content('Formulaire enregistré')
|
||||
|
||||
within '.buttons' do
|
||||
click_on 'Ajouter un champ'
|
||||
end
|
||||
expect(page).to have_selector('#champ-1-libelle')
|
||||
|
||||
click_on Procedure.last.libelle
|
||||
click_on 'onglet-pieces'
|
||||
expect(page).to have_current_path(admin_procedure_pieces_justificatives_path(Procedure.last))
|
||||
fill_in 'procedure_types_de_piece_justificative_attributes_0_libelle', with: 'libelle de piece'
|
||||
click_on 'add_piece_justificative'
|
||||
expect(page).to have_current_path(admin_procedure_pieces_justificatives_path(Procedure.last))
|
||||
expect(page).to have_selector('#procedure_types_de_piece_justificative_attributes_1_libelle')
|
||||
|
||||
preview_window = window_opened_by { click_on 'onglet-preview' }
|
||||
within_window(preview_window) do
|
||||
expect(page).to have_current_path(apercu_procedure_path(Procedure.last))
|
||||
expect(page).to have_field('libelle de champ')
|
||||
expect(page).to have_field('libelle de piece')
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'After adding champ and file, make publication' do
|
||||
page.refresh
|
||||
|
||||
fill_in 'champ-0-libelle', with: 'libelle de champ'
|
||||
blur
|
||||
expect(page).to have_content('Formulaire enregistré')
|
||||
|
||||
click_on Procedure.last.libelle
|
||||
click_on 'onglet-pieces'
|
||||
|
||||
expect(page).to have_current_path(admin_procedure_pieces_justificatives_path(Procedure.last))
|
||||
fill_in 'procedure_types_de_piece_justificative_attributes_0_libelle', with: 'libelle de piece'
|
||||
click_on 'add_piece_justificative'
|
||||
|
||||
click_on 'onglet-infos'
|
||||
expect(page).to have_current_path(admin_procedure_path(Procedure.last))
|
||||
expect(page).to have_selector('#publish-procedure', visible: true)
|
||||
find('#publish-procedure').click
|
||||
|
||||
within '#publish-modal' do
|
||||
expect(page).to have_field('procedure_path')
|
||||
fill_in 'lien_site_web', with: 'http://some.website'
|
||||
click_on 'publish'
|
||||
end
|
||||
|
||||
expect(page).to have_text('Démarche publiée')
|
||||
expect(page).to have_selector('.procedure-lien')
|
||||
end
|
||||
expect(page).to have_text('Démarche publiée')
|
||||
expect(page).to have_selector('.procedure-lien')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ feature 'Creating a new dossier:' do
|
|||
end
|
||||
|
||||
context 'when the procedure has identification by individual' do
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_type_de_champ, :with_two_type_de_piece_justificative, ask_birthday: ask_birthday) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, ask_birthday: ask_birthday) }
|
||||
let(:ask_birthday) { false }
|
||||
let(:expected_birthday) { nil }
|
||||
|
||||
|
@ -63,7 +63,7 @@ feature 'Creating a new dossier:' do
|
|||
end
|
||||
|
||||
context 'when identifying through SIRET' do
|
||||
let(:procedure) { create(:procedure, :published, :with_service, :with_type_de_champ, :with_two_type_de_piece_justificative) }
|
||||
let(:procedure) { create(:procedure, :published, :with_service, :with_type_de_champ) }
|
||||
let(:dossier) { procedure.dossiers.last }
|
||||
|
||||
before do
|
||||
|
|
7833
spec/fixtures/cassettes/model_piece_justificative.yml
vendored
7833
spec/fixtures/cassettes/model_piece_justificative.yml
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,62 +0,0 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AdminFormulaireHelper, type: :helper do
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:kind) { 'piece_justificative' }
|
||||
let(:url) { 'http://localhost' }
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
|
||||
let!(:type_de_piece_justificative_2) { create(:type_de_piece_justificative, procedure: procedure, order_place: 2) }
|
||||
|
||||
describe '#button_up' do
|
||||
describe 'with first piece justificative' do
|
||||
let(:index) { 0 }
|
||||
|
||||
it 'returns a button up' do
|
||||
expect(button_up(procedure, kind, index, url)).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with second out of three piece justificative' do
|
||||
let(:index) { 1 }
|
||||
|
||||
it 'returns a button up' do
|
||||
expect(button_up(procedure, kind, index, url)).to match(/fa-chevron-up/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with last piece justificative' do
|
||||
let(:index) { 2 }
|
||||
|
||||
it 'returns a button up' do
|
||||
expect(button_up(procedure, kind, index, url)).to match(/fa-chevron-up/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#button_down' do
|
||||
describe 'with first piece justificative' do
|
||||
let(:index) { 0 }
|
||||
|
||||
it 'returns a button down' do
|
||||
expect(button_down(procedure, kind, index, url)).to match(/fa-chevron-down/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with second out of three piece justificative' do
|
||||
let(:index) { 1 }
|
||||
|
||||
it 'returns a button down' do
|
||||
expect(button_down(procedure, kind, index, url)).to match(/fa-chevron-down/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with last piece justificative' do
|
||||
let(:index) { 2 }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(button_down(procedure, kind, index, url)).to be(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +1,7 @@
|
|||
describe ActiveStorage::DownloadableFile do
|
||||
let(:tpjs) { [tpj_not_mandatory] }
|
||||
let!(:tpj_not_mandatory) do
|
||||
TypeDePieceJustificative.create(libelle: 'not mandatory', mandatory: false)
|
||||
end
|
||||
let(:procedure) { Procedure.create(types_de_piece_justificative: tpjs) }
|
||||
let(:dossier) { Dossier.create(procedure: procedure) }
|
||||
let(:procedure) { Procedure.create(types_de_piece_justificative: tpjs) }
|
||||
let(:dossier) { Dossier.create(procedure: procedure) }
|
||||
let(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) }
|
||||
let(:dossier) { create(:dossier) }
|
||||
|
||||
subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) }
|
||||
|
||||
describe 'create_list_from_dossier' do
|
||||
context 'when no piece_justificative is present' do
|
||||
|
@ -15,9 +9,8 @@ describe ActiveStorage::DownloadableFile do
|
|||
end
|
||||
|
||||
context 'when there is a piece_justificative' do
|
||||
let (:pj) { create(:champ, :piece_justificative, :with_piece_justificative_file) }
|
||||
before do
|
||||
dossier.champs = [pj]
|
||||
dossier.champs << create(:champ, :piece_justificative, :with_piece_justificative_file)
|
||||
end
|
||||
|
||||
it { expect(list.length).to be 1 }
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe FileSizeValidator, lib: true do
|
||||
let(:validator) { FileSizeValidator.new(options) }
|
||||
let(:attachment) { PieceJustificativeUploader.new }
|
||||
let(:note) { create(:piece_justificative, :contrat) }
|
||||
|
||||
describe 'options uses an integer' do
|
||||
let(:options) { { maximum: 10, attributes: { content: attachment } } }
|
||||
|
||||
it 'attachment exceeds maximum limit' do
|
||||
allow(attachment).to receive(:size) { 100 }
|
||||
validator.validate_each(note, :content, attachment)
|
||||
expect(note.errors).to have_key(:content)
|
||||
end
|
||||
|
||||
it 'attachment under maximum limit' do
|
||||
allow(attachment).to receive(:size) { 1 }
|
||||
validator.validate_each(note, :content, attachment)
|
||||
expect(note.errors).not_to have_key(:content)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'options uses a symbol' do
|
||||
let(:options) do
|
||||
{
|
||||
maximum: :test,
|
||||
attributes: { content: attachment }
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(note).to receive(:test) { 10 }
|
||||
end
|
||||
|
||||
it 'attachment exceeds maximum limit' do
|
||||
allow(attachment).to receive(:size) { 100 }
|
||||
validator.validate_each(note, :content, attachment)
|
||||
expect(note.errors).to have_key(:content)
|
||||
end
|
||||
|
||||
it 'attachment under maximum limit' do
|
||||
allow(attachment).to receive(:size) { 1 }
|
||||
validator.validate_each(note, :content, attachment)
|
||||
expect(note.errors).not_to have_key(:content)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,51 +0,0 @@
|
|||
describe 'pieces_justificatives' do
|
||||
describe 'migrate_procedure_to_champs' do
|
||||
let(:rake_task) { Rake::Task['pieces_justificatives:migrate_procedure_to_champs'] }
|
||||
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative) }
|
||||
|
||||
before do
|
||||
ENV['PROCEDURE_ID'] = procedure.id.to_s
|
||||
|
||||
allow_any_instance_of(PieceJustificativeToChampPieceJointeMigrationService).to receive(:ensure_correct_storage_configuration!)
|
||||
|
||||
rake_task.invoke
|
||||
end
|
||||
|
||||
after { rake_task.reenable }
|
||||
|
||||
it 'migrates the procedure' do
|
||||
expect(procedure.reload.types_de_piece_justificative).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe 'migrate_procedures_range_to_champs' do
|
||||
let(:rake_task) { Rake::Task['pieces_justificatives:migrate_procedures_range_to_champs'] }
|
||||
let(:procedure_in_range_1) { create(:procedure, :with_two_type_de_piece_justificative) }
|
||||
let(:procedure_in_range_2) { create(:procedure, :with_two_type_de_piece_justificative) }
|
||||
let(:procedure_out_of_range) { create(:procedure, :with_two_type_de_piece_justificative) }
|
||||
|
||||
before do
|
||||
procedure_in_range_1
|
||||
procedure_in_range_2
|
||||
procedure_out_of_range
|
||||
|
||||
ENV['RANGE_START'] = procedure_in_range_1.id.to_s
|
||||
ENV['RANGE_END'] = procedure_in_range_2.id.to_s
|
||||
|
||||
allow_any_instance_of(PieceJustificativeToChampPieceJointeMigrationService).to receive(:ensure_correct_storage_configuration!)
|
||||
|
||||
rake_task.invoke
|
||||
end
|
||||
|
||||
after { rake_task.reenable }
|
||||
|
||||
it 'migrates procedures in the ids range' do
|
||||
expect(procedure_in_range_1.reload.types_de_piece_justificative).to be_empty
|
||||
expect(procedure_in_range_2.reload.types_de_piece_justificative).to be_empty
|
||||
end
|
||||
|
||||
it 'doesn’t migrate procedures not in the range' do
|
||||
expect(procedure_out_of_range.reload.types_de_piece_justificative).to be_present
|
||||
end
|
||||
end
|
||||
end
|
|
@ -91,50 +91,6 @@ describe Dossier do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#types_de_piece_justificative' do
|
||||
subject { dossier.types_de_piece_justificative }
|
||||
it 'returns list of required piece justificative' do
|
||||
expect(subject.size).to eq(2)
|
||||
expect(subject).to include(TypeDePieceJustificative.find(TypeDePieceJustificative.first.id))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#types_de_piece_justificative' do
|
||||
subject { dossier.types_de_piece_justificative }
|
||||
it 'returns list of required piece justificative' do
|
||||
expect(subject.size).to eq(2)
|
||||
expect(subject).to include(TypeDePieceJustificative.find(TypeDePieceJustificative.first.id))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#retrieve_last_piece_justificative_by_type', vcr: { cassette_name: 'models_dossier_retrieve_last_piece_justificative_by_type' } do
|
||||
let(:types_de_pj_dossier) { dossier.procedure.types_de_piece_justificative }
|
||||
|
||||
subject { dossier.retrieve_last_piece_justificative_by_type types_de_pj_dossier.first }
|
||||
|
||||
before do
|
||||
create :piece_justificative, :rib, dossier: dossier, type_de_piece_justificative: types_de_pj_dossier.first
|
||||
end
|
||||
|
||||
it 'returns piece justificative with given type' do
|
||||
expect(subject.type).to eq(types_de_pj_dossier.first.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#retrieve_all_piece_justificative_by_type' do
|
||||
let(:types_de_pj_dossier) { dossier.procedure.types_de_piece_justificative }
|
||||
|
||||
subject { dossier.retrieve_all_piece_justificative_by_type types_de_pj_dossier.first }
|
||||
|
||||
before do
|
||||
create :piece_justificative, :rib, dossier: dossier, type_de_piece_justificative: types_de_pj_dossier.first
|
||||
end
|
||||
|
||||
it 'returns a list of the piece justificative' do
|
||||
expect(subject).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build_default_champs' do
|
||||
context 'when dossier is linked to a procedure with type_de_champ_public and private' do
|
||||
let(:dossier) { create(:dossier, user: user) }
|
||||
|
@ -581,12 +537,6 @@ describe Dossier do
|
|||
|
||||
it { is_expected.not_to eq(modif_date) }
|
||||
|
||||
context 'when a piece justificative is modified' do
|
||||
before { dossier.pieces_justificatives << create(:piece_justificative, :contrat) }
|
||||
|
||||
it { is_expected.to eq(modif_date) }
|
||||
end
|
||||
|
||||
context 'when a champ is modified' do
|
||||
before { dossier.champs.first.update_attribute('value', 'yop') }
|
||||
|
||||
|
|
|
@ -251,12 +251,6 @@ describe Gestionnaire, type: :model do
|
|||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on a piece jusitificative' do
|
||||
before { dossier.pieces_justificatives << create(:piece_justificative, :contrat) }
|
||||
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on private champs' do
|
||||
before { dossier.champs_private.first.update_attribute('value', 'toto') }
|
||||
|
||||
|
@ -326,12 +320,6 @@ describe Gestionnaire, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when there is a modification on a piece justificative' do
|
||||
before { dossier.pieces_justificatives << create(:piece_justificative, :contrat) }
|
||||
|
||||
it { is_expected.to match([dossier.id]) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on public champs on a followed dossier from another procedure' do
|
||||
before { dossier_on_procedure_2.champs.first.update_attribute('value', 'toto') }
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PieceJustificative do
|
||||
describe 'validations' do
|
||||
context 'content' do
|
||||
it { is_expected.not_to allow_value(nil).for(:content) }
|
||||
it { is_expected.not_to allow_value('').for(:content) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty?', vcr: { cassette_name: 'model_piece_justificative' } do
|
||||
let(:piece_justificative) { create(:piece_justificative, content: content) }
|
||||
subject { piece_justificative.empty? }
|
||||
|
||||
context 'when content exist' do
|
||||
let(:content) { File.open('./spec/fixtures/files/piece_justificative_388.pdf') }
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -381,8 +381,6 @@ describe Procedure do
|
|||
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!(:piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
|
||||
let(:received_mail) { create(:received_mail) }
|
||||
let(:from_library) { false }
|
||||
let(:administrateur) { procedure.administrateurs.first }
|
||||
|
@ -408,7 +406,7 @@ 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 + 1 + procedure.types_de_piece_justificative.size)
|
||||
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_list).compact.size).to eq procedure.types_de_champ.map(&:drop_down_list).compact.size
|
||||
expect(subject.types_de_champ_private.map(&:drop_down_list).compact.size).to eq procedure.types_de_champ_private.map(&:drop_down_list).compact.size
|
||||
|
@ -430,18 +428,6 @@ describe Procedure do
|
|||
expect(cloned_procedure).to have_same_attributes_as(procedure, except: ["path"])
|
||||
end
|
||||
|
||||
it 'should not clone piece justificatives but create corresponding champs' do
|
||||
expect(subject.types_de_piece_justificative.size).to eq(0)
|
||||
|
||||
champs_pj = subject.types_de_champ[procedure.types_de_champ.size + 1, procedure.types_de_piece_justificative.size]
|
||||
champs_pj.zip(procedure.types_de_piece_justificative).each do |stc, ptpj|
|
||||
expect(stc.libelle).to eq(ptpj.libelle)
|
||||
expect(stc.description).to eq(ptpj.description)
|
||||
expect(stc.mandatory).to eq(ptpj.mandatory)
|
||||
expect(stc.old_pj[:stable_id]).to eq(ptpj.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the procedure is cloned from the library' do
|
||||
let(:from_library) { true }
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe TypeDePieceJustificative do
|
||||
let!(:procedure) { create(:procedure) }
|
||||
|
||||
describe 'validation' do
|
||||
context 'libelle' do
|
||||
it { is_expected.not_to allow_value(nil).for(:libelle) }
|
||||
it { is_expected.not_to allow_value('').for(:libelle) }
|
||||
it { is_expected.to allow_value('RIB').for(:libelle) }
|
||||
end
|
||||
|
||||
context 'order_place' do
|
||||
# it { is_expected.not_to allow_value(nil).for(:order_place) }
|
||||
# it { is_expected.not_to allow_value('').for(:order_place) }
|
||||
it { is_expected.to allow_value(1).for(:order_place) }
|
||||
end
|
||||
|
||||
context 'lien_demarche' do
|
||||
it { is_expected.to allow_value(nil).for(:lien_demarche) }
|
||||
it { is_expected.to allow_value('').for(:lien_demarche) }
|
||||
it { is_expected.not_to allow_value('not-a-link').for(:lien_demarche) }
|
||||
it { is_expected.to allow_value('http://link').for(:lien_demarche) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -46,26 +46,17 @@ describe DossierSerializer do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when a type PJ was cloned to a type champ PJ' do
|
||||
let(:original_procedure) do
|
||||
p = create(:procedure, :published)
|
||||
p.types_de_piece_justificative.create(
|
||||
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,
|
||||
libelle: "Vidéo de votre demande de subvention",
|
||||
description: "Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique",
|
||||
lien_demarche: "https://www.dance-academy.gouv.fr",
|
||||
order_place: 0
|
||||
)
|
||||
p
|
||||
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
|
||||
end
|
||||
|
||||
let(:procedure) do
|
||||
p = original_procedure.clone(original_procedure.administrateurs.first, false)
|
||||
p.save
|
||||
p
|
||||
end
|
||||
|
||||
let(:type_pj) { original_procedure.types_de_piece_justificative.first }
|
||||
let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ: [cloned_type_de_champ]) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:champ_pj) { dossier.champs.last }
|
||||
|
||||
|
@ -79,18 +70,18 @@ describe DossierSerializer do
|
|||
is_expected.to include(
|
||||
types_de_piece_justificative: [
|
||||
{
|
||||
"id" => type_pj.id,
|
||||
"libelle" => type_pj.libelle,
|
||||
"description" => type_pj.description,
|
||||
"lien_demarche" => type_pj.lien_demarche,
|
||||
"order_place" => type_pj.order_place
|
||||
"id" => original_pj_id,
|
||||
"libelle" => cloned_type_de_champ.libelle,
|
||||
"description" => 'Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique.',
|
||||
"lien_demarche" => 'https://www.dance-academy.gouv.fr',
|
||||
"order_place" => cloned_type_de_champ.order_place
|
||||
}
|
||||
],
|
||||
pieces_justificatives: [
|
||||
{
|
||||
"content_url" => champ_pj.for_api,
|
||||
"created_at" => champ_pj.created_at.in_time_zone('UTC').iso8601(3),
|
||||
"type_de_piece_justificative_id" => type_pj.id,
|
||||
"type_de_piece_justificative_id" => original_pj_id,
|
||||
"user" => a_hash_including("id" => dossier.user.id)
|
||||
}
|
||||
]
|
||||
|
@ -98,7 +89,7 @@ describe DossierSerializer do
|
|||
end
|
||||
|
||||
it "does not expose the PJ as a champ" do
|
||||
expect(subject[:champs]).not_to include(a_hash_including(type_de_champ: a_hash_including(id: migrated_type_champ.id)))
|
||||
expect(subject[:champs]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,21 +10,16 @@ describe ProcedureSerializer do
|
|||
end
|
||||
|
||||
context 'when a type PJ was cloned to a type champ PJ' do
|
||||
let(:original_procedure) do
|
||||
p = create(:procedure, :published)
|
||||
p.types_de_piece_justificative.create(
|
||||
let(:original_pj_id) { 3 }
|
||||
let(:cloned_type_de_champ) do
|
||||
tdc = create(: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",
|
||||
lien_demarche: "https://www.dance-academy.gouv.fr",
|
||||
order_place: 0
|
||||
)
|
||||
p
|
||||
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
|
||||
end
|
||||
|
||||
let(:procedure) { original_procedure.clone(original_procedure.administrateurs.first, false) }
|
||||
|
||||
let(:type_pj) { original_procedure.types_de_piece_justificative.first }
|
||||
let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ: [cloned_type_de_champ]) }
|
||||
|
||||
subject { ProcedureSerializer.new(procedure).serializable_hash }
|
||||
|
||||
|
@ -32,18 +27,18 @@ describe ProcedureSerializer do
|
|||
is_expected.to include(
|
||||
types_de_piece_justificative: [
|
||||
{
|
||||
"id" => type_pj.id,
|
||||
"libelle" => type_pj.libelle,
|
||||
"description" => type_pj.description,
|
||||
"lien_demarche" => type_pj.lien_demarche,
|
||||
"order_place" => type_pj.order_place
|
||||
"id" => original_pj_id,
|
||||
"libelle" => cloned_type_de_champ.libelle,
|
||||
"description" => 'Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique.',
|
||||
"lien_demarche" => 'https://www.dance-academy.gouv.fr',
|
||||
"order_place" => cloned_type_de_champ.order_place
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it "is not exposed as a type de champ" do
|
||||
expect(subject[:types_de_champ]).not_to include(a_hash_including(libelle: type_pj.libelle))
|
||||
expect(subject[:types_de_champ]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe CarrierwaveActiveStorageMigrationService do
|
||||
let(:service) { CarrierwaveActiveStorageMigrationService.new }
|
||||
|
||||
describe '#hex_to_base64' do
|
||||
it { expect(service.hex_to_base64('deadbeef')).to eq('3q2+7w==') }
|
||||
end
|
||||
|
||||
describe '.make_blob' do
|
||||
let(:pj) { create(:piece_justificative, :rib, updated_at: Time.zone.local(2019, 01, 01, 12, 00)) }
|
||||
let(:identify) { false }
|
||||
|
||||
before do
|
||||
allow(service).to receive(:checksum).and_return('cafe')
|
||||
end
|
||||
|
||||
subject(:blob) { service.make_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename, identify: identify) }
|
||||
|
||||
it { expect(blob.created_at).to eq pj.updated_at }
|
||||
|
||||
it 'marks the blob as already scanned by the antivirus' do
|
||||
expect(blob.metadata[:virus_scan_result]).to eq(ActiveStorage::VirusScanner::SAFE)
|
||||
end
|
||||
|
||||
it 'sets the blob MIME type from the file' do
|
||||
expect(blob.identified).to be true
|
||||
expect(blob.content_type).to eq 'application/pdf'
|
||||
end
|
||||
|
||||
context 'when asking for explicit MIME type identification' do
|
||||
let(:identify) { true }
|
||||
|
||||
it 'marks the file as needing MIME type detection' do
|
||||
expect(blob.identified).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.make_empty_blob' do
|
||||
let(:pj) { create(:piece_justificative, :rib, updated_at: Time.zone.local(2019, 01, 01, 12, 00)) }
|
||||
|
||||
before 'set the underlying stored file as missing' do
|
||||
allow(pj.content.file).to receive(:file).and_return(nil)
|
||||
end
|
||||
|
||||
subject(:blob) { service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename) }
|
||||
|
||||
it { expect(blob.created_at).to eq pj.updated_at }
|
||||
|
||||
it 'marks the blob as already scanned by the antivirus' do
|
||||
expect(blob.metadata[:virus_scan_result]).to eq(ActiveStorage::VirusScanner::SAFE)
|
||||
end
|
||||
|
||||
it 'sets the blob MIME type from the file' do
|
||||
expect(blob.identified).to be true
|
||||
expect(blob.content_type).to eq 'application/pdf'
|
||||
end
|
||||
|
||||
context 'when the file metadata are also missing' do
|
||||
before do
|
||||
allow(pj).to receive(:original_filename).and_return(nil)
|
||||
allow(pj.content).to receive(:content_type).and_return(nil)
|
||||
end
|
||||
|
||||
it 'fallbacks on default values' do
|
||||
expect(blob.filename).to eq pj.content.filename
|
||||
expect(blob.content_type).to eq 'text/plain'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.fix_content_type' do
|
||||
let(:pj) { create(:piece_justificative, :rib, updated_at: Time.zone.local(2019, 01, 01, 12, 00)) }
|
||||
let(:blob) { service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename) }
|
||||
|
||||
context 'when the request is ok' do
|
||||
it 'succeeds' do
|
||||
expect(blob.service).to receive(:change_content_type).and_return(true)
|
||||
expect { service.fix_content_type(blob) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the request fails initially' do
|
||||
it 'retries the request' do
|
||||
expect(blob.service).to receive(:change_content_type).and_raise(StandardError).ordered
|
||||
expect(blob.service).to receive(:change_content_type).and_return(true).ordered
|
||||
expect { service.fix_content_type(blob, retry_delay: 0.01) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the request fails too many times' do
|
||||
it 'gives up' do
|
||||
expect(blob.service).to receive(:change_content_type).and_raise(StandardError).thrice
|
||||
expect { service.fix_content_type(blob, retry_delay: 0.01) }.to raise_error(StandardError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,317 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PieceJustificativeToChampPieceJointeMigrationService do
|
||||
let(:service) { PieceJustificativeToChampPieceJointeMigrationService.new(storage_service: storage_service) }
|
||||
let(:storage_service) { CarrierwaveActiveStorageMigrationService.new }
|
||||
let(:pj_uploader) { class_double(PieceJustificativeUploader) }
|
||||
let(:pj_service) { class_double(PiecesJustificativesService) }
|
||||
|
||||
let(:procedure) { create(:procedure, types_de_piece_justificative: types_pj) }
|
||||
let(:types_pj) { [create(:type_de_piece_justificative)] }
|
||||
|
||||
let!(:dossier) { make_dossier }
|
||||
|
||||
let(:pjs) { [] }
|
||||
|
||||
def make_dossier(hidden: false)
|
||||
create(:dossier,
|
||||
procedure: procedure,
|
||||
pieces_justificatives: pjs,
|
||||
hidden_at: hidden ? Time.zone.now : nil)
|
||||
end
|
||||
|
||||
def make_pjs
|
||||
types_pj.map do |tpj|
|
||||
create(:piece_justificative, :contrat, type_de_piece_justificative: tpj)
|
||||
end
|
||||
end
|
||||
|
||||
def timestamps(dossier)
|
||||
# Reload dossier because the resolution of in-database timestamps is
|
||||
# different from the resolution of in-memory timestamps, causing the
|
||||
# tests to fail on fractional time differences.
|
||||
dossier.reload
|
||||
|
||||
{
|
||||
created_at: dossier.created_at,
|
||||
updated_at: dossier.updated_at
|
||||
}
|
||||
end
|
||||
|
||||
def expect_storage_service_to_convert_object
|
||||
expect(storage_service).to receive(:make_blob)
|
||||
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!)
|
||||
expect(storage_service).to receive(:make_attachment)
|
||||
end
|
||||
|
||||
describe '.number_of_champs_to_migrate' do
|
||||
let!(:other_dossier) { make_dossier }
|
||||
|
||||
it 'reports the numbers of champs to be migrated' do
|
||||
expect(service.number_of_champs_to_migrate(procedure)).to eq(4)
|
||||
end
|
||||
|
||||
context 'when the procedure has hidden dossiers' do
|
||||
let!(:hidden_dossier) { make_dossier(hidden: true) }
|
||||
|
||||
it 'reports the numbers of champs including those of hidden dossiers' do
|
||||
expect(service.number_of_champs_to_migrate(procedure)).to eq(6)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when conversion succeeds' do
|
||||
context 'for the procedure' do
|
||||
it 'types de champ are created for the "pièces jointes" header and for each PJ' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { procedure.types_de_champ.count }
|
||||
.by(types_pj.count + 1)
|
||||
end
|
||||
|
||||
it 'the old types de pj are removed' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { procedure.types_de_piece_justificative.count }
|
||||
.to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'no notifications are sent to instructeurs' do
|
||||
let!(:initial_dossier_timestamps) { timestamps(dossier) }
|
||||
|
||||
context 'when there is a PJ' do
|
||||
let(:pjs) { make_pjs }
|
||||
|
||||
before do
|
||||
# Reload PJ because the resolution of in-database timestamps is
|
||||
# different from the resolution of in-memory timestamps, causing the
|
||||
# tests to fail on fractional time differences.
|
||||
pjs.last.reload
|
||||
|
||||
expect_storage_service_to_convert_object
|
||||
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
|
||||
# Reload the dossier to see the newly created champs
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'the champ has the same timestamps as the PJ' do
|
||||
expect(dossier.champs.last.created_at).to eq(pjs.last.created_at)
|
||||
expect(dossier.champs.last.updated_at).to eq(pjs.last.updated_at)
|
||||
end
|
||||
|
||||
it 'does not change the dossier timestamps' do
|
||||
expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
|
||||
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no PJ' do
|
||||
before do
|
||||
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
|
||||
# Reload the dossier to see the newly created champs
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'the champ doesn’t trigger a notification' do
|
||||
expect(dossier.champs.last.created_at).to eq(initial_dossier_timestamps[:created_at])
|
||||
expect(dossier.champs.last.updated_at).to eq(initial_dossier_timestamps[:created_at])
|
||||
end
|
||||
|
||||
it 'does not change the dossier timestamps' do
|
||||
expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
|
||||
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for the dossier' do
|
||||
let(:pjs) { make_pjs }
|
||||
|
||||
before { expect_storage_service_to_convert_object }
|
||||
|
||||
it 'champs are created for the "pièces jointes" header and for each PJ' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { dossier.champs.count }
|
||||
.by(types_pj.count + 1)
|
||||
end
|
||||
|
||||
it 'the old pjs are removed' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { dossier.pieces_justificatives.count }
|
||||
.to(0)
|
||||
end
|
||||
|
||||
context 'when the procedure has several dossiers' do
|
||||
let!(:other_dossier) { make_dossier }
|
||||
|
||||
it 'sends progress callback for each migrated champ' do
|
||||
number_of_champs_to_migrate = service.number_of_champs_to_migrate(procedure)
|
||||
|
||||
progress_count = 0
|
||||
service.convert_procedure_pjs_to_champ_pjs(procedure) do
|
||||
progress_count += 1
|
||||
end
|
||||
|
||||
expect(progress_count).to eq(number_of_champs_to_migrate)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the dossier is soft-deleted it still gets converted' do
|
||||
let(:pjs) { make_pjs }
|
||||
let!(:dossier) { make_dossier(hidden: true) }
|
||||
|
||||
before { expect_storage_service_to_convert_object }
|
||||
|
||||
it 'champs are created for the "pièces jointes" header and for each PJ' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { dossier.champs.count }
|
||||
.by(types_pj.count + 1)
|
||||
end
|
||||
|
||||
it 'the old pjs are removed' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to change { dossier.pieces_justificatives.count }
|
||||
.to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are several pjs for one type' do
|
||||
let(:pjs) { make_pjs + make_pjs }
|
||||
|
||||
it 'only converts the most recent PJ for each type PJ' do
|
||||
expect(storage_service).to receive(:make_blob).exactly(types_pj.count)
|
||||
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!).exactly(types_pj.count)
|
||||
expect(storage_service).to receive(:make_attachment).exactly(types_pj.count)
|
||||
|
||||
service.convert_procedure_pjs_to_champ_pjs(procedure)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'cleanup when conversion fails' do
|
||||
let(:pjs) { make_pjs }
|
||||
let(:exception) { 'LOL no!' }
|
||||
|
||||
let!(:failing_dossier) do
|
||||
create(
|
||||
:dossier,
|
||||
procedure: procedure,
|
||||
pieces_justificatives: make_pjs
|
||||
)
|
||||
end
|
||||
|
||||
let!(:initial_dossier_timestamps) { timestamps(dossier) }
|
||||
let!(:initial_failing_dossier_timestamps) { timestamps(failing_dossier) }
|
||||
|
||||
before do
|
||||
allow(storage_service).to receive(:checksum).and_return('cafe')
|
||||
allow(storage_service).to receive(:fix_content_type)
|
||||
|
||||
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!)
|
||||
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!)
|
||||
.and_raise(exception)
|
||||
|
||||
expect(storage_service).to receive(:delete_from_active_storage!)
|
||||
end
|
||||
|
||||
def try_convert(procedure)
|
||||
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
rescue StandardError, SignalException => e
|
||||
dossier.reload
|
||||
failing_dossier.reload
|
||||
e
|
||||
end
|
||||
|
||||
it 'passes on the exception' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }
|
||||
.to raise_error('LOL no!')
|
||||
end
|
||||
|
||||
it 'does not create champs' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { dossier.champs.count }
|
||||
end
|
||||
|
||||
it 'does not remove any old pjs' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { dossier.pieces_justificatives.count }
|
||||
end
|
||||
|
||||
it 'does not creates types de champ' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { procedure.types_de_champ.count }
|
||||
end
|
||||
|
||||
it 'does not remove old types de pj' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { procedure.types_de_piece_justificative.count }
|
||||
end
|
||||
|
||||
it 'does not change the dossiers timestamps' do
|
||||
try_convert(procedure)
|
||||
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
|
||||
expect(failing_dossier.updated_at).to eq(initial_failing_dossier_timestamps[:updated_at])
|
||||
end
|
||||
|
||||
it 'does not leave stale blobs behind' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { ActiveStorage::Blob.count }
|
||||
end
|
||||
|
||||
it 'does not leave stale attachments behind' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { ActiveStorage::Attachment.count }
|
||||
end
|
||||
|
||||
context 'when some dossiers to roll back are hidden' do
|
||||
before do
|
||||
dossier.update_column(:hidden_at, Time.zone.now)
|
||||
end
|
||||
|
||||
it 'does not create champs' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { dossier.champs.count }
|
||||
end
|
||||
|
||||
it 'does not change the hidden dossier timestamps' do
|
||||
try_convert(procedure)
|
||||
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when receiving a Signal interruption (like Ctrl+C)' do
|
||||
let(:exception) { Interrupt }
|
||||
|
||||
it 'handles the exception as well' do
|
||||
expect { service.convert_procedure_pjs_to_champ_pjs(procedure) }.to raise_error { Interrupt }
|
||||
end
|
||||
|
||||
it 'does not create champs' do
|
||||
expect { try_convert(procedure) }.not_to change { dossier.champs.count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when rolling back a dossier fails' do
|
||||
before do
|
||||
allow(service).to receive(:destroy_champ_pj)
|
||||
.with(having_attributes(id: dossier.id), anything)
|
||||
.and_raise(StandardError)
|
||||
allow(service).to receive(:destroy_champ_pj)
|
||||
.with(any_args)
|
||||
.and_call_original
|
||||
end
|
||||
|
||||
it 'continues to roll back the other dossiers' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { failing_dossier.champs.count }
|
||||
end
|
||||
|
||||
it 'does not creates types de champ on the procedure' do
|
||||
expect { try_convert(procedure) }
|
||||
.not_to change { procedure.types_de_champ.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,159 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PiecesJustificativesService do
|
||||
let(:user) { create(:user) }
|
||||
let(:safe_file) { true }
|
||||
|
||||
before :each do
|
||||
allow(ClamavService).to receive(:safe_file?).and_return(safe_file)
|
||||
end
|
||||
|
||||
let(:hash) { {} }
|
||||
let!(:tpj_not_mandatory) do
|
||||
TypeDePieceJustificative.create(libelle: 'not mandatory', mandatory: false)
|
||||
end
|
||||
let!(:tpj_mandatory) do
|
||||
TypeDePieceJustificative.create(libelle: 'justificatif', mandatory: true)
|
||||
end
|
||||
let(:procedure) { Procedure.create(types_de_piece_justificative: tpjs) }
|
||||
let(:dossier) { Dossier.create(procedure: procedure) }
|
||||
let(:errors) { PiecesJustificativesService.upload!(dossier, user, hash) }
|
||||
let(:tpjs) { [tpj_not_mandatory] }
|
||||
|
||||
let(:attachment_list) { PiecesJustificativesService.liste_pieces_justificatives(dossier) }
|
||||
let(:poids_total) { PiecesJustificativesService.pieces_justificatives_total_size(dossier) }
|
||||
|
||||
describe 'self.upload!' do
|
||||
context 'when no params are given' do
|
||||
it { expect(errors).to eq([]) }
|
||||
end
|
||||
|
||||
context 'when there is something wrong with file save' do
|
||||
let(:hash) do
|
||||
{
|
||||
"piece_justificative_#{tpj_not_mandatory.id}" =>
|
||||
double(path: '', original_filename: 'filename')
|
||||
}
|
||||
end
|
||||
|
||||
it { expect(errors).to match(["le fichier filename (not mandatory) n'a pas pu être sauvegardé"]) }
|
||||
end
|
||||
|
||||
context 'when a virus is provided' do
|
||||
let(:safe_file) { false }
|
||||
let(:hash) do
|
||||
{
|
||||
"piece_justificative_#{tpj_not_mandatory.id}" =>
|
||||
double(path: '', original_filename: 'bad_file')
|
||||
}
|
||||
end
|
||||
|
||||
it { expect(errors).to match(['bad_file : virus détecté']) }
|
||||
end
|
||||
|
||||
context 'when a regular file is provided' do
|
||||
let(:content) { double(path: '', original_filename: 'filename') }
|
||||
let(:hash) do
|
||||
{
|
||||
"piece_justificative_#{tpj_not_mandatory.id}" =>
|
||||
content
|
||||
}
|
||||
end
|
||||
|
||||
before :each do
|
||||
expect(PiecesJustificativesService).to receive(:save_pj)
|
||||
.with(content, dossier, tpj_not_mandatory, user)
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
it 'is saved' do
|
||||
expect(errors).to match([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'missing_pj_error_messages' do
|
||||
let(:errors) { PiecesJustificativesService.missing_pj_error_messages(dossier) }
|
||||
let(:tpjs) { [tpj_mandatory] }
|
||||
|
||||
context 'when no params are given' do
|
||||
it { expect(errors).to match(['La pièce jointe justificatif doit être fournie.']) }
|
||||
end
|
||||
|
||||
context 'when the piece justificative is provided' do
|
||||
before :each do
|
||||
# we are messing around piece_justificative
|
||||
# because directly doubling carrierwave params seems complicated
|
||||
piece_justificative_double = double(type_de_piece_justificative: tpj_mandatory)
|
||||
expect(dossier).to receive(:pieces_justificatives).and_return([piece_justificative_double])
|
||||
end
|
||||
|
||||
it {
|
||||
expect(errors).to match([])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe '#attachment_list' do
|
||||
context 'when no piece_justificative is present' do
|
||||
it { expect(attachment_list).to match([]) }
|
||||
it { expect(poids_total).to be 0 }
|
||||
end
|
||||
|
||||
context 'when there is a piece_justificative' do
|
||||
let (:pj) { create(:champ, :piece_justificative, :with_piece_justificative_file) }
|
||||
before do
|
||||
dossier.champs = [pj]
|
||||
end
|
||||
|
||||
it { expect(attachment_list).not_to be_empty }
|
||||
it { expect(poids_total).to be pj.piece_justificative_file.byte_size }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'types_pj_as_types_de_champ' do
|
||||
subject { PiecesJustificativesService.types_pj_as_types_de_champ(procedure) }
|
||||
|
||||
it 'generates one header champ, plus one champ per PJ' do
|
||||
expect(subject.pluck(:libelle)).to contain_exactly("Pièces jointes", "not mandatory")
|
||||
end
|
||||
|
||||
it 'remembers the id of the PJ that got converted into a champ' do
|
||||
expect(subject.map(&:old_pj)).to include({ 'stable_id' => tpj_not_mandatory.id })
|
||||
end
|
||||
|
||||
context 'without pre-existing champs' do
|
||||
it 'generates a sequence of order_places incrementing from zero' do
|
||||
expect(subject.pluck(:order_place)).to contain_exactly(0, 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pre-existing champs' do
|
||||
let(:procedure) do
|
||||
create(
|
||||
:procedure,
|
||||
types_de_piece_justificative: tpjs,
|
||||
types_de_champ: [build(:type_de_champ, order_place: 0), build(:type_de_champ, order_place: 1)]
|
||||
)
|
||||
end
|
||||
|
||||
it 'generates a sequence of incrementing order_places that continues where the last type de champ left off' do
|
||||
expect(subject.pluck(:order_place)).to contain_exactly(2, 3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pre-existing champs without an order place' do
|
||||
let(:procedure) do
|
||||
create(
|
||||
:procedure,
|
||||
types_de_piece_justificative: tpjs,
|
||||
types_de_champ: [build(:type_de_champ, order_place: 0), build(:type_de_champ, order_place: nil)]
|
||||
)
|
||||
end
|
||||
|
||||
it 'ignores champs without an order place' do
|
||||
expect(subject.pluck(:order_place)).to contain_exactly(1, 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -134,7 +134,6 @@ RSpec.configure do |config|
|
|||
config.include FactoryBot::Syntax::Methods
|
||||
|
||||
config.before(:each) do
|
||||
allow_any_instance_of(PieceJustificativeUploader).to receive(:generate_secure_token).and_return("3dbb3535-5388-4a37-bc2d-778327b9f997")
|
||||
allow_any_instance_of(ProcedureLogoUploader).to receive(:generate_secure_token).and_return("3dbb3535-5388-4a37-bc2d-778327b9f998")
|
||||
|
||||
Flipflop::FeatureSet.current.test!.reset!
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PieceJustificativeUploader do
|
||||
let(:pj) { create(:piece_justificative, :rib) }
|
||||
|
||||
it { expect(pj.content.filename).to eq 'piece_justificative.pdf' }
|
||||
|
||||
context 'when extension is nil' do
|
||||
it do
|
||||
expect(pj.content.file).to receive(:extension).and_return(nil)
|
||||
expect(pj.content.filename).to eq 'piece_justificative.'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'admin/pieces_justificatives/show.html.haml', type: :view do
|
||||
let(:procedure) { create(:procedure) }
|
||||
|
||||
describe 'fields sorted' do
|
||||
let(:first_libelle) { 'salut la compagnie' }
|
||||
let(:last_libelle) { 'je suis bien sur la page' }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1, libelle: last_libelle) }
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0, libelle: first_libelle) }
|
||||
before do
|
||||
procedure.reload
|
||||
assign(:procedure, procedure)
|
||||
render
|
||||
end
|
||||
it 'sorts by order place' do
|
||||
expect(rendered).to match(/#{first_libelle}.*#{last_libelle}/m)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'arrow button' do
|
||||
subject do
|
||||
procedure.reload
|
||||
assign(:procedure, procedure)
|
||||
render
|
||||
rendered
|
||||
end
|
||||
context 'when there is no field in database' do
|
||||
it { expect(subject).not_to have_css('.fa-chevron-down') }
|
||||
it { expect(subject).not_to have_css('.fa-chevron-up') }
|
||||
end
|
||||
context 'when there is only one field in database' do
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
it { expect(subject).not_to have_css('#btn_down_0') }
|
||||
it { expect(subject).not_to have_css('#btn_up_0') }
|
||||
it { expect(subject).not_to have_css('#btn_up_1') }
|
||||
it { expect(subject).not_to have_css('#btn_down_1') }
|
||||
end
|
||||
context 'when there are 2 fields in database' do
|
||||
let!(:type_de_piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
|
||||
it { expect(subject).to have_css('#btn_down_0') }
|
||||
it { expect(subject).not_to have_css('#btn_up_0') }
|
||||
it { expect(subject).to have_css('#btn_up_1') }
|
||||
it { expect(subject).not_to have_css('#btn_down_1') }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,6 +16,5 @@ describe 'gestionnaires/dossiers/show.html.haml', type: :view do
|
|||
it 'renders the dossier infos' do
|
||||
expect(rendered).to have_text('Identité')
|
||||
expect(rendered).to have_text('Demande')
|
||||
expect(rendered).to have_text('Pièces jointes')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -51,12 +51,4 @@ describe 'shared/dossiers/demande.html.haml', type: :view do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the dossier has pièces justificatives' do
|
||||
let(:procedure) { create(:procedure, :published, :with_two_type_de_piece_justificative) }
|
||||
|
||||
it 'renders the pièces justificatives' do
|
||||
expect(rendered).to have_text('Pièces jointes')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'users/dossiers/brouillon.html.haml', type: :view do
|
||||
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, :with_notice, for_individual: true) }
|
||||
let(:dossier) { create(:dossier, :with_entreprise, :with_service, state: Dossier.states.fetch(:brouillon), procedure: procedure) }
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_notice, :with_service, for_individual: true) }
|
||||
let(:dossier) { create(:dossier, :with_entreprise, state: Dossier.states.fetch(:brouillon), procedure: procedure) }
|
||||
let(:footer) { view.content_for(:footer) }
|
||||
|
||||
before do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'users/dossiers/demande.html.haml', type: :view do
|
||||
let(:procedure) { create(:procedure, :published, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_type_de_champ_private) }
|
||||
let(:procedure) { create(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, procedure: procedure) }
|
||||
|
||||
before do
|
||||
|
@ -19,7 +19,6 @@ describe 'users/dossiers/demande.html.haml', type: :view do
|
|||
expect(rendered).to have_text('Déposé le')
|
||||
expect(rendered).to have_text('Identité')
|
||||
expect(rendered).to have_text('Demande')
|
||||
expect(rendered).to have_text('Pièces jointes')
|
||||
end
|
||||
|
||||
context 'when the dossier is editable' do
|
||||
|
|
Loading…
Reference in a new issue