2019-08-01-01 (#4162)

2019-08-01-01
This commit is contained in:
Pierre de La Morinerie 2019-08-01 12:05:08 +02:00 committed by GitHub
commit da692483cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 524 additions and 123 deletions

View file

@ -47,6 +47,7 @@ gem 'prawn' # PDF Generation
gem 'prawn_rails' gem 'prawn_rails'
gem 'premailer-rails' gem 'premailer-rails'
gem 'puma' # Use Puma as the app server gem 'puma' # Use Puma as the app server
gem 'pundit'
gem 'rack-mini-profiler' gem 'rack-mini-profiler'
gem 'rails' gem 'rails'
gem 'rails-i18n' # Locales par défaut gem 'rails-i18n' # Locales par défaut

View file

@ -431,6 +431,8 @@ GEM
pry (~> 0.10) pry (~> 0.10)
public_suffix (3.0.3) public_suffix (3.0.3)
puma (3.12.0) puma (3.12.0)
pundit (2.0.1)
activesupport (>= 3.0.0)
rack (2.0.6) rack (2.0.6)
rack-mini-profiler (1.0.1) rack-mini-profiler (1.0.1)
rack (>= 1.2.0) rack (>= 1.2.0)
@ -749,6 +751,7 @@ DEPENDENCIES
premailer-rails premailer-rails
pry-byebug pry-byebug
puma puma
pundit
rack-mini-profiler rack-mini-profiler
rails rails
rails-controller-testing rails-controller-testing

View file

@ -1,5 +1,6 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include TrustedDeviceConcern include TrustedDeviceConcern
include Pundit
MAINTENANCE_MESSAGE = 'Le site est actuellement en maintenance. Il sera à nouveau disponible dans un court instant.' MAINTENANCE_MESSAGE = 'Le site est actuellement en maintenance. Il sera à nouveau disponible dans un court instant.'
@ -41,12 +42,18 @@ class ApplicationController < ActionController::Base
logged_user.present? logged_user.present?
end end
def logged_user_ids
logged_users.map(&:id)
end
helper_method :logged_in? helper_method :logged_in?
def pundit_user
if administrateur_signed_in?
current_administrateur
elsif gestionnaire_signed_in?
current_gestionnaire
else
current_user
end
end
protected protected
def authenticate_logged_user! def authenticate_logged_user!

View file

@ -14,15 +14,9 @@ class Champs::CarteController < ApplicationController
end end
@champ = if params[:champ_id].present? @champ = if params[:champ_id].present?
Champ policy_scope(Champ).find(params[:champ_id])
.joins(:dossier)
.where(dossiers: { user_id: logged_user_ids })
.find(params[:champ_id])
else else
TypeDeChamp policy_scope(TypeDeChamp).find(params[:type_de_champ_id]).champ.build
.joins(:procedure)
.where(procedures: { administrateur_id: logged_user_ids })
.find(params[:type_de_champ_id]).champ.build
end end
geo_areas = [] geo_areas = []
@ -61,11 +55,15 @@ class Champs::CarteController < ApplicationController
end end
end end
selection_utilisateur = ApiCartoService.generate_selection_utilisateur(coordinates)
selection_utilisateur[:source] = GeoArea.sources.fetch(:selection_utilisateur)
geo_areas << selection_utilisateur
@champ.geo_areas = geo_areas.map do |geo_area| @champ.geo_areas = geo_areas.map do |geo_area|
GeoArea.new(geo_area) GeoArea.new(geo_area)
end end
@champ.value = GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates) @champ.value = coordinates.to_json
end end
if @champ.persisted? if @champ.persisted?

View file

@ -2,10 +2,7 @@ class Champs::RepetitionController < ApplicationController
before_action :authenticate_logged_user! before_action :authenticate_logged_user!
def show def show
@champ = Champ @champ = policy_scope(Champ).find(params[:champ_id])
.joins(:dossier)
.where(dossiers: { user_id: logged_user_ids })
.find(params[:champ_id])
@position = params[:position] @position = params[:position]
row = (@champ.champs.empty? ? 0 : @champ.champs.last.row) + 1 row = (@champ.champs.empty? ? 0 : @champ.champs.last.row) + 1

View file

@ -108,10 +108,14 @@ module Gestionnaires
def repasser_en_instruction def repasser_en_instruction
if dossier.en_instruction? if dossier.en_instruction?
flash.notice = 'Le dossier est déjà en instruction.' flash.notice = 'Le dossier est déjà en instruction.'
else
if dossier.accepte?
flash.notice = 'Il nest pas possible de repasser un dossier accepté en instruction.'
else else
flash.notice = "Le dossier #{dossier.id} a été repassé en instruction." flash.notice = "Le dossier #{dossier.id} a été repassé en instruction."
dossier.repasser_en_instruction!(current_gestionnaire) dossier.repasser_en_instruction!(current_gestionnaire)
end end
end
render partial: 'state_button_refresh', locals: { dossier: dossier } render partial: 'state_button_refresh', locals: { dossier: dossier }
end end

View file

@ -30,6 +30,16 @@ module Manager
redirect_to manager_dossier_path(dossier) redirect_to manager_dossier_path(dossier)
end end
def repasser_en_instruction
dossier = Dossier.find(params[:id])
dossier.repasser_en_instruction(current_administration)
logger.info("Le dossier #{dossier.id} est repassé en instruction par #{current_administration.email}")
flash[:notice] = "Le dossier #{dossier.id} est repassé en instruction."
redirect_to manager_dossier_path(dossier)
end
private private
def unfiltered_list? def unfiltered_list?

View file

@ -26,7 +26,7 @@ module ProcedureHelper
if logo.blank? if logo.blank?
ActionController::Base.helpers.image_url("marianne.svg") ActionController::Base.helpers.image_url("marianne.svg")
else else
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
RemoteDownloader.new(logo.filename).url RemoteDownloader.new(logo.filename).url
else else
LocalDownloader.new(logo.path, 'logo').url LocalDownloader.new(logo.path, 'logo').url

View file

@ -68,9 +68,13 @@ const TypeDeChamp = sortableElement(
<div className="flex justify-start delete"> <div className="flex justify-start delete">
<button <button
className="button small icon-only danger" className="button small icon-only danger"
onClick={() => onClick={() => {
dispatch({ type: 'removeTypeDeChamp', params: { typeDeChamp } }) if (confirm('Êtes vous sûr de vouloir supprimer ce champ ?'))
} dispatch({
type: 'removeTypeDeChamp',
params: { typeDeChamp }
});
}}
> >
<FontAwesomeIcon icon="trash" title="Supprimer" /> <FontAwesomeIcon icon="trash" title="Supprimer" />
</button> </button>

View file

@ -3,7 +3,7 @@ class WeeklyOverviewJob < ApplicationJob
def perform(*args) def perform(*args)
# Feature flipped to avoid mails in staging due to unprocessed dossier # Feature flipped to avoid mails in staging due to unprocessed dossier
if Flipflop.weekly_overview? if Rails.application.config.ds_weekly_overview
Gestionnaire.all Gestionnaire.all
.map { |gestionnaire| [gestionnaire, gestionnaire.last_week_overview] } .map { |gestionnaire| [gestionnaire, gestionnaire.last_week_overview] }
.reject { |_, overview| overview.nil? } .reject { |_, overview| overview.nil? }

View file

@ -2,4 +2,13 @@ class Attestation < ApplicationRecord
belongs_to :dossier belongs_to :dossier
mount_uploader :pdf, AttestationUploader mount_uploader :pdf, AttestationUploader
def pdf_url
if Rails.application.secrets.fog[:enabled]
RemoteDownloader.new(pdf.path).url
elsif pdf&.url
# FIXME: this is horrible but used only in dev and will be removed after migration
File.join(LOCAL_DOWNLOAD_URL, pdf.url)
end
end
end end

View file

@ -5,10 +5,11 @@ class Champ < ApplicationRecord
has_many :commentaires has_many :commentaires
has_one_attached :piece_justificative_file has_one_attached :piece_justificative_file
# We declare champ specific relationships (Champs::CarteChamp and Champs::SiretChamp) # We declare champ specific relationships (Champs::CarteChamp, Champs::SiretChamp and Champs::RepetitionChamp)
# here because otherwise we can't easily use includes in our queries. # here because otherwise we can't easily use includes in our queries.
has_many :geo_areas, dependent: :destroy has_many :geo_areas, dependent: :destroy
belongs_to :etablissement, dependent: :destroy belongs_to :etablissement, dependent: :destroy
has_many :champs, -> { ordered }, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, :repetition?, to: :type_de_champ delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, :repetition?, to: :type_de_champ

View file

@ -19,6 +19,10 @@ class Champs::CarteChamp < Champ
end end
end end
def selection_utilisateur
geo_areas.find(&:selection_utilisateur?)
end
def cadastres? def cadastres?
type_de_champ&.cadastres && type_de_champ.cadastres != '0' type_de_champ&.cadastres && type_de_champ.cadastres != '0'
end end
@ -45,6 +49,49 @@ class Champs::CarteChamp < Champ
def geo_json def geo_json
@geo_json ||= begin @geo_json ||= begin
geo_area = selection_utilisateur
if geo_area
geo_area.geometry
else
geo_json_from_value
end
end
end
def selection_utilisateur_size
if geo_json.present?
geo_json['coordinates'].size
else
0
end
end
def to_render_data
{
position: position,
selection: user_geo_area&.geometry,
quartiersPrioritaires: quartiers_prioritaires? ? quartiers_prioritaires.as_json(except: :properties) : [],
cadastres: cadastres? ? cadastres.as_json(except: :properties) : [],
parcellesAgricoles: parcelles_agricoles? ? parcelles_agricoles.as_json(except: :properties) : []
}
end
def user_geo_area
geo_area = selection_utilisateur
if geo_area.present?
geo_area
elsif geo_json_from_value.present?
GeoArea.new(
geometry: geo_json_from_value,
source: GeoArea.sources.fetch(:selection_utilisateur)
)
end
end
def geo_json_from_value
@geo_json_from_value ||= begin
parsed_value = value.blank? ? nil : JSON.parse(value) parsed_value = value.blank? ? nil : JSON.parse(value)
# We used to store in the value column a json array with coordinates. # We used to store in the value column a json array with coordinates.
if parsed_value.is_a?(Array) if parsed_value.is_a?(Array)
@ -62,34 +109,11 @@ class Champs::CarteChamp < Champ
end end
end end
def selection_utilisateur_size
if geo_json.present?
geo_json['coordinates'].size
else
0
end
end
def to_render_data
{
position: position,
selection: geo_json,
quartiersPrioritaires: quartiers_prioritaires? ? quartiers_prioritaires.as_json(except: :properties) : [],
cadastres: cadastres? ? cadastres.as_json(except: :properties) : [],
parcellesAgricoles: parcelles_agricoles? ? parcelles_agricoles.as_json(except: :properties) : []
}
end
def user_geo_area
if geo_json.present?
GeoArea.new(
geometry: geo_json,
source: GeoArea.sources.fetch(:selection_utilisateur)
)
end
end
def for_api def for_api
geo_json&.to_json nil
end
def for_export
nil
end end
end end

View file

@ -1,6 +1,4 @@
class Champs::RepetitionChamp < Champ class Champs::RepetitionChamp < Champ
has_many :champs, -> { ordered }, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
accepts_nested_attributes_for :champs, allow_destroy: true accepts_nested_attributes_for :champs, allow_destroy: true
def rows def rows
@ -21,11 +19,21 @@ class Champs::RepetitionChamp < Champ
# The user cannot enter any information here so it doesnt make much sense to search # The user cannot enter any information here so it doesnt make much sense to search
end end
def rows_for_export
rows.each.with_index(1).map do |champs, index|
Champs::RepetitionChamp::Row.new(index: index, dossier_id: dossier_id.to_s, champs: champs)
end
end
class Row < Hashie::Dash class Row < Hashie::Dash
property :index property :index
property :dossier_id property :dossier_id
property :champs property :champs
def read_attribute_for_serialization(attribute)
self[attribute]
end
def spreadsheet_columns def spreadsheet_columns
[ [
['Dossier ID', :dossier_id], ['Dossier ID', :dossier_id],

View file

@ -52,7 +52,7 @@ class Commentaire < ApplicationRecord
if piece_jointe.virus_scanner.safe? if piece_jointe.virus_scanner.safe?
Rails.application.routes.url_helpers.url_for(piece_jointe) Rails.application.routes.url_helpers.url_for(piece_jointe)
end end
elsif Flipflop.remote_storage? elsif Rails.application.secrets.fog[:enabled]
RemoteDownloader.new(file.path).url RemoteDownloader.new(file.path).url
elsif file&.url elsif file&.url
# FIXME: this is horrible but used only in dev and will be removed after migration # FIXME: this is horrible but used only in dev and will be removed after migration

View file

@ -86,6 +86,7 @@ class Dossier < ApplicationRecord
event :repasser_en_instruction, after: :after_repasser_en_instruction do event :repasser_en_instruction, after: :after_repasser_en_instruction do
transitions from: :refuse, to: :en_instruction transitions from: :refuse, to: :en_instruction
transitions from: :sans_suite, to: :en_instruction transitions from: :sans_suite, to: :en_instruction
transitions from: :accepte, to: :en_instruction
end end
end end
@ -108,7 +109,24 @@ class Dossier < ApplicationRecord
scope :en_construction, -> { not_archived.state_en_construction } scope :en_construction, -> { not_archived.state_en_construction }
scope :en_instruction, -> { not_archived.state_en_instruction } scope :en_instruction, -> { not_archived.state_en_instruction }
scope :termine, -> { not_archived.state_termine } scope :termine, -> { not_archived.state_termine }
scope :downloadable_sorted, -> { state_not_brouillon.includes(:etablissement, :user, :individual, :followers_gestionnaires, :avis, champs: { etablissement: [:champ], type_de_champ: :drop_down_list }, champs_private: { etablissement: [:champ], type_de_champ: :drop_down_list }).order(en_construction_at: 'asc') } scope :downloadable_sorted, -> {
state_not_brouillon
.includes(
:user,
:individual,
:followers_gestionnaires,
:avis,
etablissement: :champ,
champs: {
etablissement: :champ,
type_de_champ: :drop_down_list
},
champs_private: {
etablissement: :champ,
type_de_champ: :drop_down_list
}
).order(en_construction_at: 'asc')
}
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction } scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) } scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
scope :followed_by, -> (gestionnaire) { joins(:follows).where(follows: { gestionnaire: gestionnaire }) } scope :followed_by, -> (gestionnaire) { joins(:follows).where(follows: { gestionnaire: gestionnaire }) }
@ -120,14 +138,22 @@ class Dossier < ApplicationRecord
champs: [ champs: [
:geo_areas, :geo_areas,
:etablissement, :etablissement,
piece_justificative_file_attachment: :blob,
champs: [
piece_justificative_file_attachment: :blob piece_justificative_file_attachment: :blob
]
], ],
champs_private: [ champs_private: [
:geo_areas, :geo_areas,
:etablissement, :etablissement,
piece_justificative_file_attachment: :blob,
champs: [
piece_justificative_file_attachment: :blob piece_justificative_file_attachment: :blob
]
], ],
avis: [], justificatif_motivation_attachment: :blob,
attestation: [],
avis: { piece_justificative_file_attachment: :blob },
etablissement: [], etablissement: [],
individual: [], individual: [],
user: []) user: [])

View file

@ -120,6 +120,6 @@ class Etablissement < ApplicationRecord
end end
def libelle_for_export def libelle_for_export
champ&.libelle champ&.libelle || 'Dossier'
end end
end end

View file

@ -30,4 +30,8 @@ class GeoArea < ApplicationRecord
scope :quartiers_prioritaires, -> { where(source: sources.fetch(:quartier_prioritaire)) } scope :quartiers_prioritaires, -> { where(source: sources.fetch(:quartier_prioritaire)) }
scope :cadastres, -> { where(source: sources.fetch(:cadastre)) } scope :cadastres, -> { where(source: sources.fetch(:cadastre)) }
scope :parcelles_agricoles, -> { where(source: sources.fetch(:parcelle_agricole)) } scope :parcelles_agricoles, -> { where(source: sources.fetch(:parcelle_agricole)) }
def selection_utilisateur?
source == self.class.sources.fetch(:selection_utilisateur)
end
end end

View file

@ -225,6 +225,10 @@ class Gestionnaire < ApplicationRecord
end end
end end
def user
User.find_by(email: email)
end
private private
def annotations_hash(demande, annotations_privees, avis, messagerie) def annotations_hash(demande, annotations_privees, avis, messagerie)

View file

@ -0,0 +1,49 @@
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end

View file

@ -0,0 +1,24 @@
class ChampPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.is_a?(User)
scope
.joins(:dossier)
.where({ dossiers: { user_id: user.id } })
elsif user.is_a?(Gestionnaire)
scope_with_join = scope.joins(dossier: :follows)
scope_with_left_join = scope.left_joins(dossier: :follows)
if user.user
scope_with_left_join
.where({ dossiers: { user_id: user.user.id } })
.or(scope_with_left_join.where(dossiers: { follows: { gestionnaire_id: user.id } }))
else
scope_with_join.where(dossiers: { follows: { gestionnaire_id: user.id } })
end
else
scope.none
end
end
end
end

View file

@ -0,0 +1,13 @@
class TypeDeChampPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.is_a?(Administrateur)
scope
.joins(procedure: [:administrateurs])
.where({ administrateurs: { id: user.id } })
else
scope.none
end
end
end
end

View file

@ -8,6 +8,7 @@ class ChampSerializer < ActiveModel::Serializer
has_many :geo_areas, if: :include_geo_areas? has_many :geo_areas, if: :include_geo_areas?
has_one :etablissement, if: :include_etablissement? has_one :etablissement, if: :include_etablissement?
has_one :entreprise, if: :include_etablissement? has_one :entreprise, if: :include_etablissement?
has_many :rows, serializer: RowSerializer, if: :include_rows?
def value def value
case object case object
@ -35,6 +36,10 @@ class ChampSerializer < ActiveModel::Serializer
object.etablissement&.entreprise object.etablissement&.entreprise
end end
def rows
object.rows_for_export
end
def include_etablissement? def include_etablissement?
object.is_a?(Champs::SiretChamp) object.is_a?(Champs::SiretChamp)
end end
@ -43,6 +48,10 @@ class ChampSerializer < ActiveModel::Serializer
object.is_a?(Champs::CarteChamp) object.is_a?(Champs::CarteChamp)
end end
def include_rows?
object.is_a?(Champs::RepetitionChamp)
end
private private
def legacy_type_de_champ def legacy_type_de_champ

View file

@ -14,6 +14,9 @@ class DossierSerializer < ActiveModel::Serializer
:motivation, :motivation,
:instructeurs :instructeurs
attribute :attestation, if: :include_attestation?
attribute :justificatif_motivation, if: :include_justificatif_motivation?
has_one :individual has_one :individual
has_one :entreprise has_one :entreprise
has_one :etablissement has_one :etablissement
@ -22,7 +25,6 @@ class DossierSerializer < ActiveModel::Serializer
has_many :champs_private has_many :champs_private
has_many :pieces_justificatives has_many :pieces_justificatives
has_many :types_de_piece_justificative has_many :types_de_piece_justificative
has_one :justificatif_motivation
has_many :avis has_many :avis
has_many :champs, serializer: ChampSerializer has_many :champs, serializer: ChampSerializer
@ -37,7 +39,9 @@ class DossierSerializer < ActiveModel::Serializer
if champ_carte.present? if champ_carte.present?
carto_champs = champ_carte.geo_areas.to_a carto_champs = champ_carte.geo_areas.to_a
if !carto_champs.find(&:selection_utilisateur?)
carto_champs << champ_carte.user_geo_area carto_champs << champ_carte.user_geo_area
end
champs += carto_champs.compact champs += carto_champs.compact
end end
end end
@ -53,10 +57,12 @@ class DossierSerializer < ActiveModel::Serializer
PiecesJustificativesService.serialize_champs_as_pjs(object) PiecesJustificativesService.serialize_champs_as_pjs(object)
end end
def justificatif_motivation def attestation
if object.justificatif_motivation.attached? object.attestation.pdf_url
Rails.application.routes.url_helpers.url_for(object.justificatif_motivation)
end end
def justificatif_motivation
Rails.application.routes.url_helpers.url_for(object.justificatif_motivation)
end end
def types_de_piece_justificative def types_de_piece_justificative
@ -102,4 +108,12 @@ class DossierSerializer < ActiveModel::Serializer
def processed_at def processed_at
object.processed_at&.in_time_zone('UTC') object.processed_at&.in_time_zone('UTC')
end end
def include_attestation?
object.accepte?
end
def include_justificatif_motivation?
object.justificatif_motivation.attached?
end
end end

View file

@ -0,0 +1,5 @@
class RowSerializer < ActiveModel::Serializer
has_many :champs, serializer: ChampSerializer
attribute :index, key: :id
end

View file

@ -22,4 +22,10 @@ class ApiCartoService
).results ).results
end end
end end
def self.generate_selection_utilisateur(coordinates)
{
geometry: JSON.parse(GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates))
}
end
end end

View file

@ -53,16 +53,14 @@ class ProcedureExportV2Service
[dossier.champs, dossier.champs_private] [dossier.champs, dossier.champs_private]
.flatten .flatten
.select { |champ| champ.is_a?(Champs::RepetitionChamp) } .select { |champ| champ.is_a?(Champs::RepetitionChamp) }
end end.group_by(&:libelle)
end end
def champs_repetables_options def champs_repetables_options
champs_repetables.map do |champ| champs_repetables.map do |libelle, champs|
[ [
champ.libelle, libelle,
champ.rows.each_with_index.map do |champs, index| champs.flat_map(&:rows_for_export)
Champs::RepetitionChamp::Row.new(index: index + 1, dossier_id: champ.dossier_id.to_s, champs: champs)
end
] ]
end end
end end

View file

@ -4,7 +4,7 @@ class AttestationTemplateLogoUploader < BaseUploader
end end
# Choose what kind of storage to use for this uploader: # Choose what kind of storage to use for this uploader:
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
storage :fog storage :fog
else else
storage :file storage :file
@ -13,7 +13,7 @@ class AttestationTemplateLogoUploader < BaseUploader
# Override the directory where uploaded files will be stored. # Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted: # This is a sensible default for uploaders that are meant to be mounted:
def store_dir def store_dir
if !Flipflop.remote_storage? if !Rails.application.secrets.fog[:enabled]
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end end
end end

View file

@ -4,7 +4,7 @@ class AttestationTemplateSignatureUploader < BaseUploader
end end
# Choose what kind of storage to use for this uploader: # Choose what kind of storage to use for this uploader:
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
storage :fog storage :fog
else else
storage :file storage :file
@ -13,7 +13,7 @@ class AttestationTemplateSignatureUploader < BaseUploader
# Override the directory where uploaded files will be stored. # Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted: # This is a sensible default for uploaders that are meant to be mounted:
def store_dir def store_dir
if !Flipflop.remote_storage? if !Rails.application.secrets.fog[:enabled]
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end end
end end

View file

@ -4,7 +4,7 @@ class AttestationUploader < BaseUploader
end end
# Choose what kind of storage to use for this uploader: # Choose what kind of storage to use for this uploader:
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
storage :fog storage :fog
else else
storage :file storage :file
@ -13,7 +13,7 @@ class AttestationUploader < BaseUploader
# Override the directory where uploaded files will be stored. # Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted: # This is a sensible default for uploaders that are meant to be mounted:
def store_dir def store_dir
if !Flipflop.remote_storage? if !Rails.application.secrets.fog[:enabled]
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end end
end end

View file

@ -3,7 +3,7 @@ class CommentaireFileUploader < BaseUploader
Rails.root.join("public") Rails.root.join("public")
end end
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
storage :fog storage :fog
else else
storage :file storage :file

View file

@ -4,7 +4,7 @@ class ProcedureLogoUploader < BaseUploader
end end
# Choose what kind of storage to use for this uploader: # Choose what kind of storage to use for this uploader:
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
storage :fog storage :fog
else else
storage :file storage :file
@ -13,7 +13,7 @@ class ProcedureLogoUploader < BaseUploader
# Override the directory where uploaded files will be stored. # Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted: # This is a sensible default for uploaders that are meant to be mounted:
def store_dir def store_dir
if !Flipflop.remote_storage? if !Rails.application.secrets.fog[:enabled]
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end end
end end
@ -27,7 +27,7 @@ class ProcedureLogoUploader < BaseUploader
def filename def filename
if file.present? if file.present?
if original_filename.present? || model.logo_secure_token if original_filename.present? || model.logo_secure_token
if Flipflop.remote_storage? if Rails.application.secrets.fog[:enabled]
filename = "#{model.class.to_s.underscore}-#{secure_token}.#{file.extension&.downcase}" filename = "#{model.class.to_s.underscore}-#{secure_token}.#{file.extension&.downcase}"
else else
filename = "logo-#{secure_token}.#{file.extension&.downcase}" filename = "logo-#{secure_token}.#{file.extension&.downcase}"

View file

@ -28,6 +28,9 @@ as well as a link to its edit page.
</h1> </h1>
<div> <div>
<% if dossier.accepte? %>
<%= link_to 'Repasser en instruction', repasser_en_instruction_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous le passage en instruction du dossier ?" } %>
<% end %>
<% if dossier.hidden_at.nil? %> <% if dossier.hidden_at.nil? %>
<%= link_to 'Supprimer le dossier', hide_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous la suppression du dossier ?" } %> <%= link_to 'Supprimer le dossier', hide_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous la suppression du dossier ?" } %>
<% end %> <% end %>

View file

@ -39,5 +39,7 @@ module TPS
# Make main application helpers available in administrate # Make main application helpers available in administrate
Administrate::ApplicationController.helper(TPS::Application.helpers) Administrate::ApplicationController.helper(TPS::Application.helpers)
end end
config.ds_weekly_overview = ENV['APP_NAME'] == 'tps'
end end
end end

View file

@ -29,12 +29,8 @@ Flipflop.configure do
end end
group :production do group :production do
feature :remote_storage,
default: ENV['FOG_ENABLED'] == 'enabled'
feature :insee_api_v3, feature :insee_api_v3,
default: true default: true
feature :weekly_overview,
default: ENV['APP_NAME'] == 'tps'
feature :pre_maintenance_mode feature :pre_maintenance_mode
feature :maintenance_mode feature :maintenance_mode
end end

View file

@ -16,6 +16,7 @@ Rails.application.routes.draw do
resources :dossiers, only: [:index, :show] do resources :dossiers, only: [:index, :show] do
post 'hide', on: :member post 'hide', on: :member
post 'repasser_en_instruction', on: :member
end end
resources :administrateurs, only: [:index, :show, :new, :create] do resources :administrateurs, only: [:index, :show, :new, :create] do

View file

@ -34,6 +34,7 @@ defaults: &defaults
pipedrive: pipedrive:
key: <%= ENV['PIPEDRIVE_KEY'] %> key: <%= ENV['PIPEDRIVE_KEY'] %>
fog: fog:
enabled: <%= ENV['FOG_ENABLED'] == 'enabled' %>
openstack_tenant: <%= ENV['FOG_OPENSTACK_TENANT'] %> openstack_tenant: <%= ENV['FOG_OPENSTACK_TENANT'] %>
openstack_api_key: <%= ENV['FOG_OPENSTACK_API_KEY'] %> openstack_api_key: <%= ENV['FOG_OPENSTACK_API_KEY'] %>
openstack_username: <%= ENV['FOG_OPENSTACK_USERNAME'] %> openstack_username: <%= ENV['FOG_OPENSTACK_USERNAME'] %>

View file

@ -513,6 +513,7 @@ ActiveRecord::Schema.define(version: 2019_07_17_151228) do
t.boolean "durees_conservation_required", default: true t.boolean "durees_conservation_required", default: true
t.string "path" t.string "path"
t.string "declarative_with_state" t.string "declarative_with_state"
t.text "monavis"
t.text "monavis_embed" t.text "monavis_embed"
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state" t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
t.index ["hidden_at"], name: "index_procedures_on_hidden_at" t.index ["hidden_at"], name: "index_procedures_on_hidden_at"

View file

@ -0,0 +1,28 @@
namespace :after_party do
desc 'Deployment task: migrate_geo_area_data'
task migrate_geo_area_data: :environment do
puts "Running deploy task 'migrate_geo_area_data'"
progress = ProgressReport.new(Champs::CarteChamp.count)
Champs::CarteChamp.includes(:geo_areas).find_each do |champ|
geo_area = champ.geo_areas.find(&:selection_utilisateur?)
geo_json = champ.geo_json_from_value
if geo_area.blank? && geo_json.present?
GeoArea.create(
champ: champ,
geometry: geo_json,
source: GeoArea.sources.fetch(:selection_utilisateur)
)
progress.inc
end
end
progress.finish
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord.create version: '20190731152733'
end
end

View file

@ -154,10 +154,10 @@ describe API::V1::DossiersController do
context 'when dossier exists and belongs to procedure' do context 'when dossier exists and belongs to procedure' do
let(:procedure_id) { procedure.id } let(:procedure_id) { procedure.id }
let(:date_creation) { Time.zone.local(2008, 9, 1, 10, 5, 0) } let(:date_creation) { Time.zone.local(2008, 9, 1, 10, 5, 0) }
let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :en_construction, procedure: procedure, motivation: "Motivation") } } let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :with_attestation, :accepte, procedure: procedure, motivation: "Motivation") } }
let(:dossier_id) { dossier.id } let(:dossier_id) { dossier.id }
let(:body) { JSON.parse(retour.body, symbolize_names: true) } let(:body) { JSON.parse(retour.body, symbolize_names: true) }
let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs, :justificatif_motivation, :avis] } let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs, :attestation, :avis] }
subject { body[:dossier] } subject { body[:dossier] }
it 'return REST code 200', :show_in_doc do it 'return REST code 200', :show_in_doc do
@ -165,7 +165,7 @@ describe API::V1::DossiersController do
end end
it { expect(subject[:id]).to eq(dossier.id) } it { expect(subject[:id]).to eq(dossier.id) }
it { expect(subject[:state]).to eq('initiated') } it { expect(subject[:state]).to eq('closed') }
it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') }
it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') }
it { expect(subject[:archived]).to eq(dossier.archived) } it { expect(subject[:archived]).to eq(dossier.archived) }
@ -235,6 +235,22 @@ describe API::V1::DossiersController do
it { expect(subject[:type_champ]).to eq('text') } it { expect(subject[:type_champ]).to eq('text') }
end end
end end
describe 'repetition' do
let(:procedure) { create(:procedure, administrateur: admin) }
let(:champ) { build(:champ_repetition) }
let(:dossier) { create(:dossier, :en_construction, champs: [champ], procedure: procedure) }
subject { super().first[:rows] }
it 'should have rows' do
expect(subject.size).to eq(2)
expect(subject[0][:id]).to eq(1)
expect(subject[0][:champs].size).to eq(2)
expect(subject[0][:champs].map { |c| c[:value] }).to eq(['text', '42'])
expect(subject[0][:champs].map { |c| c[:type_de_champ][:type_champ] }).to eq(['text', 'number'])
end
end
end end
describe 'champs_private' do describe 'champs_private' do

View file

@ -4,6 +4,8 @@ describe Gestionnaires::DossiersController, type: :controller do
render_views render_views
let(:gestionnaire) { create(:gestionnaire) } let(:gestionnaire) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur) }
let(:administration) { create(:administration) }
let(:gestionnaires) { [gestionnaire] } let(:gestionnaires) { [gestionnaire] }
let(:procedure) { create(:procedure, :published, gestionnaires: gestionnaires) } let(:procedure) { create(:procedure, :published, gestionnaires: gestionnaires) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
@ -153,9 +155,10 @@ describe Gestionnaires::DossiersController, type: :controller do
describe '#repasser_en_instruction' do describe '#repasser_en_instruction' do
let(:dossier) { create(:dossier, :refuse, procedure: procedure) } let(:dossier) { create(:dossier, :refuse, procedure: procedure) }
let(:current_user) { gestionnaire }
before do before do
sign_in gestionnaire sign_in current_user
post :repasser_en_instruction, post :repasser_en_instruction,
params: { procedure_id: procedure.id, dossier_id: dossier.id }, params: { procedure_id: procedure.id, dossier_id: dossier.id },
format: 'js' format: 'js'
@ -173,6 +176,29 @@ describe Gestionnaires::DossiersController, type: :controller do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
end end
end end
context 'when the dossier is accepte' do
let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
it 'it is not possible to go back to en_instruction as gestionnaire' do
expect(dossier.reload.state).to eq(Dossier.states.fetch(:accepte))
expect(response).to have_http_status(:ok)
end
context 'as administrateur' do
let (:current_user) { administrateur }
it 'it is not possible to go back to en_instruction' do
expect(dossier.reload.state).to eq(Dossier.states.fetch(:accepte))
expect(response).to have_http_status(:ok)
end
end
context 'as superadmin' do
let (:current_user) { administration }
it 'it is not possible to go back to en_instruction' do
expect(dossier.reload.state).to eq(Dossier.states.fetch(:accepte))
expect(response).to have_http_status(:ok)
end
end
end
end end
describe '#terminer' do describe '#terminer' do

View file

@ -11,4 +11,17 @@ describe Manager::DossiersController, type: :controller do
it { expect(dossier.hidden_at).not_to be_nil } it { expect(dossier.hidden_at).not_to be_nil }
end end
describe '#repasser_en_instruction' do
let(:administration) { create :administration }
let!(:dossier) { create(:dossier, :accepte) }
before do
sign_in administration
post :repasser_en_instruction, params: { id: dossier.id }
dossier.reload
end
it { expect(dossier.en_instruction?).to be true }
end
end end

View file

@ -9,5 +9,9 @@ FactoryBot.define do
nom { 'XYZ' } nom { 'XYZ' }
commune { 'Paris' } commune { 'Paris' }
end end
trait :selection_utilisateur do
source { GeoArea.sources.fetch(:selection_utilisateur) }
end
end end
end end

View file

@ -11,7 +11,9 @@ feature 'As an administrateur I can edit types de champ', js: true do
end end
it "Add a new champ" do it "Add a new champ" do
page.accept_alert do
click_on 'Supprimer' click_on 'Supprimer'
end
within '.buttons' do within '.buttons' do
click_on 'Ajouter un champ' click_on 'Ajouter un champ'
@ -48,8 +50,10 @@ feature 'As an administrateur I can edit types de champ', js: true do
expect(page).to have_selector('#champ-3-libelle') expect(page).to have_selector('#champ-3-libelle')
within '.type-de-champ[data-index="2"]' do within '.type-de-champ[data-index="2"]' do
page.accept_alert do
click_on 'Supprimer' click_on 'Supprimer'
end end
end
expect(page).not_to have_selector('#champ-3-libelle') expect(page).not_to have_selector('#champ-3-libelle')
fill_in 'champ-2-libelle', with: 'libellé de champ 2' fill_in 'champ-2-libelle', with: 'libellé de champ 2'
@ -68,8 +72,9 @@ feature 'As an administrateur I can edit types de champ', js: true do
blur blur
expect(page).to have_content('Formulaire enregistré') expect(page).to have_content('Formulaire enregistré')
page.refresh page.refresh
page.accept_alert do
click_on 'Supprimer' click_on 'Supprimer'
end
expect(page).to have_content('Formulaire enregistré') expect(page).to have_content('Formulaire enregistré')
expect(page).to have_content('Supprimer', count: 1) expect(page).to have_content('Supprimer', count: 1)
page.refresh page.refresh

View file

@ -8,7 +8,10 @@ RSpec.describe WeeklyOverviewJob, type: :job do
context 'if the feature is enabled' do context 'if the feature is enabled' do
before do before do
Flipflop::FeatureSet.current.test!.switch!(:weekly_overview, true) Rails.application.config.ds_weekly_overview = true
end
after do
Rails.application.config.ds_weekly_overview = false
end end
context 'with one gestionnaire with one overview' do context 'with one gestionnaire with one overview' do
@ -35,7 +38,6 @@ RSpec.describe WeeklyOverviewJob, type: :job do
context 'if the feature is disabled' do context 'if the feature is disabled' do
before do before do
Flipflop::FeatureSet.current.test!.switch!(:weekly_overview, false)
allow(Gestionnaire).to receive(:all) allow(Gestionnaire).to receive(:all)
WeeklyOverviewJob.new.perform WeeklyOverviewJob.new.perform
end end

View file

@ -3,9 +3,9 @@ require 'spec_helper'
describe Champs::CarteChamp do describe Champs::CarteChamp do
let(:champ) { Champs::CarteChamp.new(value: value) } let(:champ) { Champs::CarteChamp.new(value: value) }
let(:value) { '' } let(:value) { '' }
let(:geo_json) { GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates) }
let(:coordinates) { [[{ "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }, { "lat" => 48.87273183590832, "lng" => 2.3850631713867183 }, { "lat" => 48.87081237174292, "lng" => 2.3809432983398438 }, { "lat" => 48.8712640169951, "lng" => 2.377510070800781 }, { "lat" => 48.87510283703279, "lng" => 2.3778533935546875 }, { "lat" => 48.87544154230615, "lng" => 2.382831573486328 }, { "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }]] } let(:coordinates) { [[{ "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }, { "lat" => 48.87273183590832, "lng" => 2.3850631713867183 }, { "lat" => 48.87081237174292, "lng" => 2.3809432983398438 }, { "lat" => 48.8712640169951, "lng" => 2.377510070800781 }, { "lat" => 48.87510283703279, "lng" => 2.3778533935546875 }, { "lat" => 48.87544154230615, "lng" => 2.382831573486328 }, { "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }]] }
let(:parsed_geo_json) { JSON.parse(geo_json) } let(:geo_json_as_string) { GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates) }
let(:geo_json) { JSON.parse(geo_json_as_string) }
describe '#to_render_data' do describe '#to_render_data' do
subject { champ.to_render_data } subject { champ.to_render_data }
@ -47,15 +47,15 @@ describe Champs::CarteChamp do
context 'when the value is coordinates' do context 'when the value is coordinates' do
let(:value) { coordinates.to_json } let(:value) { coordinates.to_json }
let(:selection) { parsed_geo_json } let(:selection) { geo_json }
it { is_expected.to eq(render_data) } it { is_expected.to eq(render_data) }
end end
context 'when the value is geojson' do context 'when the value is geojson' do
let(:value) { geo_json } let(:value) { geo_json.to_json }
let(:selection) { parsed_geo_json } let(:selection) { geo_json }
it { is_expected.to eq(render_data) } it { is_expected.to eq(render_data) }
end end
@ -89,7 +89,7 @@ describe Champs::CarteChamp do
end end
context 'when the value is geojson' do context 'when the value is geojson' do
let(:value) { geo_json } let(:value) { geo_json.to_json }
it { is_expected.to eq(1) } it { is_expected.to eq(1) }
end end

View file

@ -0,0 +1,56 @@
require 'spec_helper'
describe ChampPolicy do
let(:user) { create(:user) }
let(:dossier) { create(:dossier, user: user) }
let!(:champ) { create(:champ_text, dossier: dossier) }
let(:pundit_user) { user }
subject { Pundit.policy_scope(pundit_user, Champ) }
context 'when the user has only user rights' do
context 'cannot access champs for other dossiers' do
let(:pundit_user) { create(:user) }
it { expect(subject.find_by(id: champ.id)).to eq(nil) }
end
context 'can access champs for its own dossiers' do
it {
expect(subject.find(champ.id)).to eq(champ)
}
end
end
context 'when the user has only gestionnaire rights' do
context 'can access champs for dossiers it follows' do
let(:dossier) { create(:dossier, :followed) }
let(:pundit_user) { dossier.followers_gestionnaires.first }
it { expect(subject.find(champ.id)).to eq(champ) }
end
end
context 'when the user has user and gestionnaire rights' do
let(:pundit_user) { dossier.followers_gestionnaires.first }
let(:dossier) { create(:dossier, :followed) }
let(:user) { create(:user, email: pundit_user.email) }
let(:dossier2) { create(:dossier, user: user) }
let!(:champ_2) { create(:champ_text, dossier: dossier2) }
context 'can access champs for dossiers it follows' do
it do
expect(pundit_user.user).to eq(user)
expect(subject.find(champ.id)).to eq(champ)
end
end
context 'can access champs for its own dossiers' do
it do
expect(pundit_user.user).to eq(user)
expect(subject.find(champ_2.id)).to eq(champ_2)
end
end
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe TypeDeChampPolicy do
let(:procedure) { create(:procedure) }
let!(:type_de_champ) { create(:type_de_champ_text, procedure: procedure) }
let(:pundit_user) { create(:user) }
subject { Pundit.policy_scope(pundit_user, TypeDeChamp) }
context 'when the user has only user rights' do
it 'can not access' do
expect(subject.find_by(id: type_de_champ.id)).to eq(nil)
end
end
context 'when the user has administrateur rights' do
let(:pundit_user) { procedure.administrateurs.first }
it 'can access' do
expect(subject.find(type_de_champ.id)).to eq(type_de_champ)
end
end
end

View file

@ -26,9 +26,9 @@ describe ChampSerializer do
context 'when type champ is carte' do context 'when type champ is carte' do
let(:champ) { create(:champ_carte, value: value, geo_areas: [geo_area].compact) } let(:champ) { create(:champ_carte, value: value, geo_areas: [geo_area].compact) }
let(:value) { nil } let(:value) { nil }
let(:geo_area) { create(:geo_area, geometry: parsed_geo_json) } let(:geo_area) { create(:geo_area, geometry: geo_json) }
let(:parsed_geo_json) { JSON.parse(geo_json) } let(:geo_json_as_string) { GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates) }
let(:geo_json) { GeojsonService.to_json_polygon_for_selection_utilisateur(coordinates) } let(:geo_json) { JSON.parse(geo_json_as_string) }
let(:coordinates) { [[{ "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }, { "lat" => 48.87273183590832, "lng" => 2.3850631713867183 }, { "lat" => 48.87081237174292, "lng" => 2.3809432983398438 }, { "lat" => 48.8712640169951, "lng" => 2.377510070800781 }, { "lat" => 48.87510283703279, "lng" => 2.3778533935546875 }, { "lat" => 48.87544154230615, "lng" => 2.382831573486328 }, { "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }]] } let(:coordinates) { [[{ "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }, { "lat" => 48.87273183590832, "lng" => 2.3850631713867183 }, { "lat" => 48.87081237174292, "lng" => 2.3809432983398438 }, { "lat" => 48.8712640169951, "lng" => 2.377510070800781 }, { "lat" => 48.87510283703279, "lng" => 2.3778533935546875 }, { "lat" => 48.87544154230615, "lng" => 2.382831573486328 }, { "lat" => 48.87442541960633, "lng" => 2.3859214782714844 }]] }
let(:serialized_champ) { let(:serialized_champ) {
@ -49,10 +49,14 @@ describe ChampSerializer do
let(:serialized_id) { -1 } let(:serialized_id) { -1 }
let(:serialized_description) { "" } let(:serialized_description) { "" }
let(:serialized_order_place) { -1 } let(:serialized_order_place) { -1 }
let(:serialized_value) { parsed_geo_json } let(:serialized_value) { geo_json }
context 'and geo_area is selection_utilisateur' do context 'and geo_area is selection_utilisateur' do
let(:geo_area) { create(:geo_area, :selection_utilisateur, geometry: geo_json) }
context 'value is empty' do context 'value is empty' do
let(:geo_area) { nil }
context 'when value is nil' do context 'when value is nil' do
let(:value) { nil } let(:value) { nil }
@ -85,7 +89,7 @@ describe ChampSerializer do
end end
context 'when value is geojson' do context 'when value is geojson' do
let(:value) { geo_json } let(:value) { geo_json.to_json }
it { expect(subject).to eq(serialized_champ) } it { expect(subject).to eq(serialized_champ) }
end end
@ -105,7 +109,7 @@ describe ChampSerializer do
let(:serialized_order_place) { champ.order_place } let(:serialized_order_place) { champ.order_place }
let(:serialized_libelle) { champ.libelle } let(:serialized_libelle) { champ.libelle }
let(:serialized_type_champ) { champ.type_champ } let(:serialized_type_champ) { champ.type_champ }
let(:serialized_value) { geo_json } let(:serialized_value) { nil }
context 'when value is coordinates' do context 'when value is coordinates' do
let(:value) { coordinates.to_json } let(:value) { coordinates.to_json }
@ -114,7 +118,7 @@ describe ChampSerializer do
end end
context 'when value is geojson' do context 'when value is geojson' do
let(:value) { geo_json } let(:value) { geo_json.to_json }
it { expect(subject).to eq(serialized_champ) } it { expect(subject).to eq(serialized_champ) }
end end
@ -147,7 +151,7 @@ describe ChampSerializer do
it { it {
expect(subject[:geo_areas].first).to include( expect(subject[:geo_areas].first).to include(
source: GeoArea.sources.fetch(:cadastre), source: GeoArea.sources.fetch(:cadastre),
geometry: parsed_geo_json, geometry: geo_json,
numero: '42', numero: '42',
feuille: 'A11' feuille: 'A11'
) )
@ -165,13 +169,13 @@ describe ChampSerializer do
end end
context 'and geo_area is quartier_prioritaire' do context 'and geo_area is quartier_prioritaire' do
let(:geo_area) { create(:geo_area, :quartier_prioritaire, geometry: parsed_geo_json) } let(:geo_area) { create(:geo_area, :quartier_prioritaire, geometry: geo_json) }
context 'new_api' do context 'new_api' do
it { it {
expect(subject[:geo_areas].first).to include( expect(subject[:geo_areas].first).to include(
source: GeoArea.sources.fetch(:quartier_prioritaire), source: GeoArea.sources.fetch(:quartier_prioritaire),
geometry: parsed_geo_json, geometry: geo_json,
nom: 'XYZ', nom: 'XYZ',
commune: 'Paris' commune: 'Paris'
) )

View file

@ -93,6 +93,9 @@ describe ProcedureExportV2Service do
context 'with etablissement' do context 'with etablissement' do
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) }
let(:dossier_etablissement) { etablissements_sheet.data[1] }
let(:champ_etablissement) { etablissements_sheet.data[0] }
it 'should have headers' do it 'should have headers' do
expect(etablissements_sheet.headers).to eq([ expect(etablissements_sheet.headers).to eq([
"Dossier ID", "Dossier ID",
@ -132,6 +135,8 @@ describe ProcedureExportV2Service do
it 'should have data' do it 'should have data' do
expect(etablissements_sheet.data.size).to eq(2) expect(etablissements_sheet.data.size).to eq(2)
expect(dossier_etablissement[1]).to eq("Dossier")
expect(champ_etablissement[1]).to eq("siret")
end end
end end
@ -155,8 +160,13 @@ describe ProcedureExportV2Service do
end end
context 'with repetitions' do context 'with repetitions' do
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } let!(:dossiers) do
let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } } [
create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure),
create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure)
]
end
let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } }
it 'should have sheets' do it 'should have sheets' do
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle]) expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle])
@ -172,7 +182,7 @@ describe ProcedureExportV2Service do
end end
it 'should have data' do it 'should have data' do
expect(repetition_sheet.data.size).to eq(2) expect(repetition_sheet.data.size).to eq(4)
end end
end end
end end

View file

@ -147,14 +147,6 @@ RSpec.configure do |config|
Typhoeus::Expectation.clear Typhoeus::Expectation.clear
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
if Flipflop.remote_storage?
VCR.use_cassette("ovh_storage_init") do
CarrierWave.configure do |config|
config.fog_credentials = { provider: 'OpenStack' }
end
end
end
} }
RSpec::Matchers.define :have_same_attributes_as do |expected, options| RSpec::Matchers.define :have_same_attributes_as do |expected, options|