Merge branch 'main' into feature/add_rna_type_de_champs
This commit is contained in:
commit
0131a41266
253 changed files with 2393 additions and 1301 deletions
|
@ -54,7 +54,7 @@ class Administrateur < ApplicationRecord
|
|||
api_token = Administrateur.generate_unique_secure_token
|
||||
encrypted_token = BCrypt::Password.create(api_token)
|
||||
update(encrypted_token: encrypted_token)
|
||||
api_token
|
||||
APIToken.signe(id, api_token)
|
||||
end
|
||||
|
||||
def valid_api_token?(api_token)
|
||||
|
|
27
app/models/api_token.rb
Normal file
27
app/models/api_token.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
class APIToken
|
||||
attr_reader :administrateur_id, :token
|
||||
|
||||
def initialize(token)
|
||||
@token = token
|
||||
verify!
|
||||
end
|
||||
|
||||
def administrateur?
|
||||
administrateur_id.present?
|
||||
end
|
||||
|
||||
def self.message_verifier
|
||||
Rails.application.message_verifier('api_v2_token')
|
||||
end
|
||||
|
||||
def self.signe(administrateur_id, token)
|
||||
message_verifier.generate([administrateur_id, token])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def verify!
|
||||
@administrateur_id, @token = self.class.message_verifier.verified(@token) || [nil, @token]
|
||||
rescue
|
||||
end
|
||||
end
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champ < ApplicationRecord
|
||||
belongs_to :dossier, inverse_of: false, touch: true, optional: false
|
||||
|
@ -48,6 +48,7 @@ class Champ < ApplicationRecord
|
|||
:exclude_from_export?,
|
||||
:exclude_from_view?,
|
||||
:repetition?,
|
||||
:block?,
|
||||
:dossier_link?,
|
||||
:titre_identite?,
|
||||
:header_section?,
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::AddressChamp < Champs::TextChamp
|
||||
def full_address?
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::AnnuaireEducationChamp < Champs::TextChamp
|
||||
def fetch_external_data?
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::CarteChamp < Champ
|
||||
# Default map location. Center of the World, ahm, France...
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::CheckboxChamp < Champs::YesNoChamp
|
||||
def true?
|
||||
|
@ -27,4 +27,8 @@ class Champs::CheckboxChamp < Champs::YesNoChamp
|
|||
def for_export
|
||||
true? ? 'on' : 'off'
|
||||
end
|
||||
|
||||
def mandatory_blank_and_visible?
|
||||
mandatory? && (blank? || !true?)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::CiviliteChamp < Champ
|
||||
def html_label?
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::CnafChamp < Champs::TextChamp
|
||||
# see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/cnaf-input-validation.middleware.ts
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::CommuneChamp < Champs::TextChamp
|
||||
store_accessor :value_json, :departement, :code_departement
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DateChamp < Champ
|
||||
before_save :format_before_save
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DatetimeChamp < Champ
|
||||
before_save :format_before_save
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DecimalNumberChamp < Champ
|
||||
validates :value, numericality: {
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DepartementChamp < Champs::TextChamp
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DgfipChamp < Champs::TextChamp
|
||||
# see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/dgfip-input-validation.middleware.ts
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DossierLinkChamp < Champ
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::DropDownListChamp < Champ
|
||||
THRESHOLD_NB_OPTIONS_AS_RADIO = 5
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::EmailChamp < Champs::TextChamp
|
||||
end
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: champs
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# data :jsonb
|
||||
# fetch_external_data_exceptions :string is an Array
|
||||
# private :boolean default(FALSE), not null
|
||||
# rebased_at :datetime
|
||||
# row :integer
|
||||
# type :string
|
||||
# value :string
|
||||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
#
|
||||
class Champs::EngagementChamp < Champs::CheckboxChamp
|
||||
end
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::ExplicationChamp < Champs::TextChamp
|
||||
def search_terms
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::HeaderSectionChamp < Champ
|
||||
def search_terms
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::IbanChamp < Champ
|
||||
validates_with IbanValidator
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::IntegerNumberChamp < Champ
|
||||
validates :value, numericality: {
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::LinkedDropDownListChamp < Champ
|
||||
delegate :primary_options, :secondary_options, to: 'type_de_champ.dynamic_type'
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::MesriChamp < Champs::TextChamp
|
||||
# see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/mesri-input-validation.middleware.ts
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::MultipleDropDownListChamp < Champ
|
||||
before_save :format_before_save
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::NumberChamp < Champ
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::PaysChamp < Champs::TextChamp
|
||||
def localized_value
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::PhoneChamp < Champs::TextChamp
|
||||
# We want to allow:
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::PieceJustificativeChamp < Champ
|
||||
FILE_MAX_SIZE = 200.megabytes
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::PoleEmploiChamp < Champs::TextChamp
|
||||
# see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/pole-emploi-input-validation.middleware.ts
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::RegionChamp < Champs::TextChamp
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::RepetitionChamp < Champ
|
||||
accepts_nested_attributes_for :champs, allow_destroy: true
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::SiretChamp < Champ
|
||||
def search_terms
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::TextChamp < Champ
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::TextareaChamp < Champs::TextChamp
|
||||
def for_export
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::TitreIdentiteChamp < Champ
|
||||
FILE_MAX_SIZE = 20.megabytes
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::YesNoChamp < Champ
|
||||
def search_terms
|
||||
|
|
|
@ -2,13 +2,18 @@ module DossierFilteringConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
DATE_SINCE_MAPPING = {
|
||||
'updated_since' => 'updated_at',
|
||||
'depose_since' => 'depose_at',
|
||||
'en_construction_since' => 'en_construction_at',
|
||||
'en_instruction_since' => 'en_instruction_at',
|
||||
'processed_since' => 'processed_at'
|
||||
}
|
||||
scope :filter_by_datetimes, lambda { |column, dates|
|
||||
if dates.present?
|
||||
case column
|
||||
when 'depose_since'
|
||||
where('dossiers.depose_at >= ?', dates.sort.first)
|
||||
when 'updated_since'
|
||||
where('dossiers.updated_at >= ?', dates.sort.first)
|
||||
when *DATE_SINCE_MAPPING.keys
|
||||
where("dossiers.#{DATE_SINCE_MAPPING.fetch(column)} >= ?", dates.sort.first)
|
||||
else
|
||||
dates
|
||||
.map { |date| self.where(column => date..(date + 1.day)) }
|
||||
|
|
|
@ -4,6 +4,45 @@ module TagsSubstitutionConcern
|
|||
include Rails.application.routes.url_helpers
|
||||
include ActionView::Helpers::UrlHelper
|
||||
|
||||
module TagsParser
|
||||
include Parsby::Combinators
|
||||
extend self
|
||||
|
||||
def parse(io)
|
||||
doc.parse io
|
||||
end
|
||||
|
||||
define_combinator :doc do
|
||||
many(tag | text) < eof
|
||||
end
|
||||
|
||||
define_combinator :text do
|
||||
join(many(any_char.that_fail(tag))).fmap do |str|
|
||||
{ text: str.force_encoding('utf-8').encode }
|
||||
end
|
||||
end
|
||||
|
||||
define_combinator :tag do
|
||||
between(tag_delimiter, tag_delimiter, tag_text).fmap do |tag|
|
||||
{ tag: tag }
|
||||
end
|
||||
end
|
||||
|
||||
define_combinator :tag_delimiter do
|
||||
lit('--')
|
||||
end
|
||||
|
||||
define_combinator :tag_text do
|
||||
join(many(any_char.that_fail(tag_delimiter | eol))).fmap do |str|
|
||||
str.force_encoding('utf-8').encode
|
||||
end
|
||||
end
|
||||
|
||||
define_combinator :eol do
|
||||
lit("\r\n") | lit("\n")
|
||||
end
|
||||
end
|
||||
|
||||
DOSSIER_TAGS = [
|
||||
{
|
||||
libelle: 'motivation',
|
||||
|
@ -141,8 +180,6 @@ module TagsSubstitutionConcern
|
|||
|
||||
SHARED_TAG_LIBELLES = (DOSSIER_TAGS + DOSSIER_TAGS_FOR_MAIL + INDIVIDUAL_TAGS + ENTREPRISE_TAGS + ROUTAGE_TAGS).map { |tag| tag[:libelle] }
|
||||
|
||||
TAG_DELIMITERS_REGEX = /--(?<capture>((?!--).)*)--/
|
||||
|
||||
def tags
|
||||
if procedure.for_individual?
|
||||
identity_tags = INDIVIDUAL_TAGS
|
||||
|
@ -159,7 +196,7 @@ module TagsSubstitutionConcern
|
|||
end
|
||||
|
||||
def used_type_de_champ_tags(text)
|
||||
used_tags_for(text, with_libelle: true).filter_map do |(tag, libelle)|
|
||||
used_tags_and_libelle_for(text).filter_map do |(tag, libelle)|
|
||||
if !tag.in?(SHARED_TAG_LIBELLES)
|
||||
if tag.start_with?('tdc')
|
||||
[libelle, tag.gsub('tdc', '').to_i]
|
||||
|
@ -170,19 +207,8 @@ module TagsSubstitutionConcern
|
|||
end
|
||||
end
|
||||
|
||||
def used_tags_for(text, with_libelle: false)
|
||||
text, tags = normalize_tags(text)
|
||||
text
|
||||
.scan(TAG_DELIMITERS_REGEX)
|
||||
.flatten
|
||||
.map do |tag_str|
|
||||
if with_libelle
|
||||
tag = tags.find { |tag| tag[:id] == tag_str }
|
||||
[tag_str, tag ? tag[:libelle] : nil]
|
||||
else
|
||||
tag_str
|
||||
end
|
||||
end
|
||||
def used_tags_for(text)
|
||||
used_tags_and_libelle_for(text).map { |(tag, _)| tag }
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -250,7 +276,7 @@ module TagsSubstitutionConcern
|
|||
return ''
|
||||
end
|
||||
|
||||
text, _ = normalize_tags(text)
|
||||
tokens = parse_tags(text)
|
||||
|
||||
tags_and_datas = [
|
||||
[champ_public_tags(dossier: dossier), dossier.champs],
|
||||
|
@ -259,51 +285,68 @@ module TagsSubstitutionConcern
|
|||
[ROUTAGE_TAGS, dossier],
|
||||
[INDIVIDUAL_TAGS, dossier.individual],
|
||||
[ENTREPRISE_TAGS, dossier.etablissement&.entreprise]
|
||||
]
|
||||
].filter_map do |(tags, data)|
|
||||
data && [filter_tags(tags).index_by { _1[:id].presence || _1[:libelle] }, data]
|
||||
end
|
||||
|
||||
tags_and_datas
|
||||
.map { |(tags, data)| [filter_tags(tags), data] }
|
||||
.reduce(text) { |acc, (tags, data)| replace_tags_with_values_from_data(acc, tags, data) }
|
||||
end
|
||||
|
||||
def replace_tags_with_values_from_data(text, tags, data)
|
||||
if data.present?
|
||||
tags.reduce(text) do |acc, tag|
|
||||
replace_tag(acc, tag, data)
|
||||
tags_and_datas.reduce(tokens) do |tokens, (tags, data)|
|
||||
# Replace tags with their value
|
||||
tokens.map do |token|
|
||||
case token
|
||||
in { tag: _, id: id } if tags.key?(id)
|
||||
{ text: replace_tag(tags.fetch(id), data) }
|
||||
in { tag: tag } if tags.key?(tag)
|
||||
{ text: replace_tag(tags.fetch(tag), data) }
|
||||
else
|
||||
token
|
||||
end
|
||||
end
|
||||
else
|
||||
text
|
||||
end
|
||||
end.map do |token|
|
||||
# Get tokens text representation
|
||||
case token
|
||||
in { tag: tag }
|
||||
"--#{tag}--"
|
||||
in { text: text }
|
||||
text
|
||||
end
|
||||
end.join('')
|
||||
end
|
||||
|
||||
def replace_tag(text, tag, data)
|
||||
libelle = Regexp.quote(tag[:id].presence || tag[:libelle])
|
||||
|
||||
# allow any kind of space (non-breaking or other) in the tag’s libellé to match any kind of space in the template
|
||||
# (the '\\ |' is there because plain ASCII spaces were escaped by preceding Regexp.quote)
|
||||
libelle.gsub!(/\\ |[[:blank:]]/, "[[:blank:]]")
|
||||
|
||||
def replace_tag(tag, data)
|
||||
if tag.key?(:target)
|
||||
value = data.send(tag[:target])
|
||||
data.public_send(tag[:target])
|
||||
else
|
||||
value = instance_exec(data, &tag[:lambda])
|
||||
instance_exec(data, &tag[:lambda])
|
||||
end
|
||||
|
||||
text.gsub(/--#{libelle}--/, value.to_s)
|
||||
end
|
||||
|
||||
def normalize_tags(text)
|
||||
tags = types_de_champ_tags(procedure.types_de_champ_public_for_tags, Dossier::SOUMIS) + types_de_champ_tags(procedure.types_de_champ_private_for_tags, Dossier::INSTRUCTION_COMMENCEE)
|
||||
[filter_tags(tags).reduce(text) { |text, tag| normalize_tag(text, tag) }, tags]
|
||||
def procedure_types_de_champ_tags
|
||||
filter_tags(types_de_champ_tags(procedure.types_de_champ_public_for_tags, Dossier::SOUMIS) + types_de_champ_tags(procedure.types_de_champ_private_for_tags, Dossier::INSTRUCTION_COMMENCEE))
|
||||
end
|
||||
|
||||
def normalize_tag(text, tag)
|
||||
libelle = Regexp.quote(tag[:libelle])
|
||||
def parse_tags(text)
|
||||
tags = procedure_types_de_champ_tags.index_by { _1[:libelle] }
|
||||
|
||||
# allow any kind of space (non-breaking or other) in the tag’s libellé to match any kind of space in the template
|
||||
# (the '\\ |' is there because plain ASCII spaces were escaped by preceding Regexp.quote)
|
||||
libelle.gsub!(/\\ |[[:blank:]]/, "[[:blank:]]")
|
||||
TagsParser.parse(text).map do |token|
|
||||
case token
|
||||
in { tag: tag } if tags.key?(tag)
|
||||
{ tag: tag, id: tags.fetch(tag).fetch(:id) }
|
||||
else
|
||||
token
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
text.gsub(/--#{libelle}--/, "--#{tag[:id]}--")
|
||||
def used_tags_and_libelle_for(text)
|
||||
parse_tags(text).filter_map do |token|
|
||||
case token
|
||||
in { tag: tag, id: id }
|
||||
[id, tag]
|
||||
in { tag: tag }
|
||||
[tag]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ class ConditionForm
|
|||
include ActiveModel::Model
|
||||
include Logic
|
||||
|
||||
attr_accessor :top_operator_name, :rows
|
||||
attr_accessor :top_operator_name, :rows, :upper_tdcs
|
||||
|
||||
def to_condition
|
||||
case sub_conditions.count
|
||||
|
@ -22,7 +22,7 @@ class ConditionForm
|
|||
end
|
||||
|
||||
def change_champ(i)
|
||||
sub_conditions[i] = Logic.ensure_compatibility_from_left(sub_conditions[i])
|
||||
sub_conditions[i] = Logic.ensure_compatibility_from_left(sub_conditions[i], upper_tdcs)
|
||||
|
||||
self
|
||||
end
|
||||
|
@ -39,7 +39,7 @@ class ConditionForm
|
|||
|
||||
def row_to_condition(row)
|
||||
left = Logic.from_json(row[:targeted_champ])
|
||||
right = parse_value(left.type, row[:value])
|
||||
right = parse_value(left.type(upper_tdcs), row[:value])
|
||||
|
||||
Logic.class_from_name(row[:operator_name]).new(left, right)
|
||||
end
|
||||
|
|
|
@ -170,19 +170,19 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
event :accepter, after: :after_accepter do
|
||||
transitions from: :en_instruction, to: :accepte
|
||||
transitions from: :en_instruction, to: :accepte, guard: :can_terminer?
|
||||
end
|
||||
|
||||
event :accepter_automatiquement, after: :after_accepter_automatiquement do
|
||||
transitions from: :en_construction, to: :accepte
|
||||
transitions from: :en_construction, to: :accepte, guard: :can_terminer?
|
||||
end
|
||||
|
||||
event :refuser, after: :after_refuser do
|
||||
transitions from: :en_instruction, to: :refuse
|
||||
transitions from: :en_instruction, to: :refuse, guard: :can_terminer?
|
||||
end
|
||||
|
||||
event :classer_sans_suite, after: :after_classer_sans_suite do
|
||||
transitions from: :en_instruction, to: :sans_suite
|
||||
transitions from: :en_instruction, to: :sans_suite, guard: :can_terminer?
|
||||
end
|
||||
|
||||
event :repasser_en_instruction, after: :after_repasser_en_instruction do
|
||||
|
@ -515,6 +515,12 @@ class Dossier < ApplicationRecord
|
|||
brouillon? && procedure.dossier_can_transition_to_en_construction? && !for_procedure_preview?
|
||||
end
|
||||
|
||||
def can_terminer?
|
||||
return false if etablissement&.as_degraded_mode?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def can_repasser_en_instruction?
|
||||
termine? && !user_deleted?
|
||||
end
|
||||
|
@ -978,7 +984,7 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def check_mandatory_champs
|
||||
(champs + champs.filter(&:repetition?).filter(&:visible?).flat_map(&:champs))
|
||||
(champs + champs.filter(&:block?).filter(&:visible?).flat_map(&:champs))
|
||||
.filter(&:mandatory_blank_and_visible?)
|
||||
.map do |champ|
|
||||
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
|
||||
|
|
|
@ -87,7 +87,7 @@ class DossierPreloader
|
|||
end
|
||||
|
||||
# Load children champs
|
||||
champs.filter(&:repetition?).each do |parent_champ|
|
||||
champs.filter(&:block?).each do |parent_champ|
|
||||
champs = children_by_parent[parent_champ.id] || []
|
||||
parent_champ.association(:dossier).target = dossier
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class Entreprise < Hashie::Dash
|
|||
property :effectif_mensuel
|
||||
property :effectif_annuel
|
||||
property :effectif_annuel_annee
|
||||
property :date_creation
|
||||
property :date_creation, default: nil
|
||||
property :nom, default: nil
|
||||
property :prenom, default: nil
|
||||
|
||||
|
|
|
@ -56,4 +56,8 @@ class FranceConnectInformation < ApplicationRecord
|
|||
def delete_merge_token!
|
||||
update(merge_token: nil, merge_token_created_at: nil)
|
||||
end
|
||||
|
||||
def full_name
|
||||
[given_name, family_name].compact.join(" ")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,35 +8,37 @@ module Logic
|
|||
end
|
||||
|
||||
def self.class_from_name(name)
|
||||
[ChampValue, Constant, Empty, LessThan, LessThanEq, Eq, NotEq, GreaterThanEq, GreaterThan, EmptyOperator, And, Or]
|
||||
[ChampValue, Constant, Empty, LessThan, LessThanEq, Eq, NotEq, GreaterThanEq, GreaterThan, EmptyOperator, IncludeOperator, And, Or]
|
||||
.find { |c| c.name == name }
|
||||
end
|
||||
|
||||
def self.ensure_compatibility_from_left(condition)
|
||||
def self.ensure_compatibility_from_left(condition, type_de_champs)
|
||||
left = condition.left
|
||||
right = condition.right
|
||||
operator_class = condition.class
|
||||
|
||||
case [left.type, condition]
|
||||
case [left.type(type_de_champs), condition]
|
||||
in [:boolean, _]
|
||||
operator_class = Eq
|
||||
in [:empty, _]
|
||||
operator_class = EmptyOperator
|
||||
in [:enum, _]
|
||||
operator_class = Eq
|
||||
in [:enums, _]
|
||||
operator_class = IncludeOperator
|
||||
in [:number, EmptyOperator]
|
||||
operator_class = Eq
|
||||
in [:number, _]
|
||||
end
|
||||
|
||||
if !compatible_type?(left, right)
|
||||
right = case left.type
|
||||
if !compatible_type?(left, right, type_de_champs)
|
||||
right = case left.type(type_de_champs)
|
||||
when :boolean
|
||||
Constant.new(true)
|
||||
when :empty
|
||||
Empty.new
|
||||
when :enum
|
||||
Constant.new(left.options.first.second)
|
||||
when :enum, :enums
|
||||
Constant.new(left.options(type_de_champs).first.second)
|
||||
when :number
|
||||
Constant.new(0)
|
||||
end
|
||||
|
@ -45,12 +47,12 @@ module Logic
|
|||
operator_class.new(left, right)
|
||||
end
|
||||
|
||||
def self.compatible_type?(left, right)
|
||||
case [left.type, right.type]
|
||||
def self.compatible_type?(left, right, type_de_champs)
|
||||
case [left.type(type_de_champs), right.type(type_de_champs)]
|
||||
in [a, ^a] # syntax for same type
|
||||
true
|
||||
in [:enum, :string]
|
||||
left.options.map(&:second).include?(right.value)
|
||||
in [:enum, :string] | [:enums, :string]
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
|
@ -84,6 +86,8 @@ module Logic
|
|||
|
||||
def less_than_eq(left, right) = Logic::LessThanEq.new(left, right)
|
||||
|
||||
def ds_include(left, right) = Logic::IncludeOperator.new(left, right)
|
||||
|
||||
def constant(value) = Logic::Constant.new(value)
|
||||
|
||||
def champ_value(stable_id) = Logic::ChampValue.new(stable_id)
|
||||
|
|
|
@ -7,5 +7,5 @@ class Logic::And < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.all?
|
||||
end
|
||||
|
||||
def to_s = "(#{@operands.map(&:to_s).join(' && ')})"
|
||||
def to_s(type_de_champs) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' && ')})"
|
||||
end
|
||||
|
|
|
@ -17,17 +17,17 @@ class Logic::BinaryOperator < Logic::Term
|
|||
self.new(Logic.from_h(h['left']), Logic.from_h(h['right']))
|
||||
end
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
errors = []
|
||||
|
||||
if @left.type != :number || @right.type != :number
|
||||
errors += ["les types sont incompatibles : #{self}"]
|
||||
if @left.type(type_de_champs) != :number || @right.type(type_de_champs) != :number
|
||||
errors << { type: :required_number, operator_name: self.class.name }
|
||||
end
|
||||
|
||||
errors + @left.errors(stable_ids) + @right.errors(stable_ids)
|
||||
errors + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
def type = :boolean
|
||||
def type(type_de_champs = []) = :boolean
|
||||
|
||||
def compute(champs = [])
|
||||
l = @left.compute(champs)
|
||||
|
@ -36,7 +36,7 @@ class Logic::BinaryOperator < Logic::Term
|
|||
l&.send(operation, r) || false
|
||||
end
|
||||
|
||||
def to_s = "(#{@left} #{operation} #{@right})"
|
||||
def to_s(type_de_champs) = "(#{@left.to_s(type_de_champs)} #{operation} #{@right.to_s(type_de_champs)})"
|
||||
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
|
|
|
@ -4,13 +4,15 @@ class Logic::ChampValue < Logic::Term
|
|||
:checkbox,
|
||||
:integer_number,
|
||||
:decimal_number,
|
||||
:drop_down_list
|
||||
:drop_down_list,
|
||||
:multiple_drop_down_list
|
||||
)
|
||||
|
||||
CHAMP_VALUE_TYPE = {
|
||||
boolean: :boolean,
|
||||
number: :number,
|
||||
enum: :enum,
|
||||
boolean: :boolean, # from yes_no or checkbox champ
|
||||
number: :number, # from integer or decimal number champ
|
||||
enum: :enum, # a choice from a dropdownlist
|
||||
enums: :enums, # multiple choice from a dropdownlist (multipledropdownlist)
|
||||
empty: :empty,
|
||||
unmanaged: :unmanaged
|
||||
}
|
||||
|
@ -27,7 +29,7 @@ class Logic::ChampValue < Logic::Term
|
|||
return nil if !targeted_champ.visible?
|
||||
return nil if targeted_champ.blank?
|
||||
|
||||
case type_de_champ.type_champ
|
||||
case type_de_champ(champs.map(&:type_de_champ)).type_champ
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:yes_no),
|
||||
MANAGED_TYPE_DE_CHAMP.fetch(:checkbox)
|
||||
targeted_champ.true?
|
||||
|
@ -35,13 +37,15 @@ class Logic::ChampValue < Logic::Term
|
|||
targeted_champ.for_api
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:drop_down_list)
|
||||
targeted_champ.selected
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:multiple_drop_down_list)
|
||||
targeted_champ.selected_options
|
||||
end
|
||||
end
|
||||
|
||||
def to_s = type_de_champ&.libelle # TODO: gerer le cas ou un tdc est supprimé
|
||||
def to_s(type_de_champs) = type_de_champ(type_de_champs)&.libelle # TODO: gerer le cas ou un tdc est supprimé
|
||||
|
||||
def type
|
||||
case type_de_champ&.type_champ # TODO: gerer le cas ou un tdc est supprimé
|
||||
def type(type_de_champs)
|
||||
case type_de_champ(type_de_champs)&.type_champ # TODO: gerer le cas ou un tdc est supprimé
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:yes_no),
|
||||
MANAGED_TYPE_DE_CHAMP.fetch(:checkbox)
|
||||
CHAMP_VALUE_TYPE.fetch(:boolean)
|
||||
|
@ -49,14 +53,16 @@ class Logic::ChampValue < Logic::Term
|
|||
CHAMP_VALUE_TYPE.fetch(:number)
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:drop_down_list)
|
||||
CHAMP_VALUE_TYPE.fetch(:enum)
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:multiple_drop_down_list)
|
||||
CHAMP_VALUE_TYPE.fetch(:enums)
|
||||
else
|
||||
CHAMP_VALUE_TYPE.fetch(:unmanaged)
|
||||
end
|
||||
end
|
||||
|
||||
def errors(stable_ids)
|
||||
if !stable_ids.include?(stable_id)
|
||||
["le type de champ stable_id=#{stable_id} n'est pas disponible"]
|
||||
def errors(type_de_champs)
|
||||
if !type_de_champs.map(&:stable_id).include?(stable_id)
|
||||
[{ type: :not_available }]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
@ -77,9 +83,10 @@ class Logic::ChampValue < Logic::Term
|
|||
self.class == other.class && @stable_id == other.stable_id
|
||||
end
|
||||
|
||||
def options
|
||||
opts = type_de_champ.drop_down_list_enabled_non_empty_options.map { |option| [option, option] }
|
||||
if type_de_champ.drop_down_other?
|
||||
def options(type_de_champs)
|
||||
tdc = type_de_champ(type_de_champs)
|
||||
opts = tdc.drop_down_list_enabled_non_empty_options.map { |option| [option, option] }
|
||||
if tdc.drop_down_other?
|
||||
opts + [["Autre", Champs::DropDownListChamp::OTHER]]
|
||||
else
|
||||
opts
|
||||
|
@ -88,8 +95,8 @@ class Logic::ChampValue < Logic::Term
|
|||
|
||||
private
|
||||
|
||||
def type_de_champ
|
||||
TypeDeChamp.find_by(stable_id: stable_id)
|
||||
def type_de_champ(type_de_champs)
|
||||
type_de_champs.find { |c| c.stable_id == stable_id }
|
||||
end
|
||||
|
||||
def champ(champs)
|
||||
|
|
|
@ -7,7 +7,7 @@ class Logic::Constant < Logic::Term
|
|||
|
||||
def compute(_champs = nil) = @value
|
||||
|
||||
def to_s
|
||||
def to_s(_type_de_champs = [])
|
||||
case @value
|
||||
when TrueClass
|
||||
I18n.t('utils.yes')
|
||||
|
@ -18,7 +18,7 @@ class Logic::Constant < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def type
|
||||
def type(_type_de_champs = [])
|
||||
case @value
|
||||
when TrueClass, FalseClass
|
||||
:boolean
|
||||
|
@ -29,7 +29,7 @@ class Logic::Constant < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def errors(_stable_ids = nil) = []
|
||||
def errors(_type_de_champs = nil) = []
|
||||
|
||||
def to_h
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class Logic::Empty < Logic::Term
|
||||
def to_s = I18n.t('logic.empty')
|
||||
def to_s(_type_de_champs = []) = I18n.t('logic.empty')
|
||||
|
||||
def type = :empty
|
||||
def type(_type_de_champs = []) = :empty
|
||||
|
||||
def errors(_stable_ids = nil) = ['empty']
|
||||
def errors(_type_de_champs = []) = ['empty']
|
||||
|
||||
def to_h
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class Logic::EmptyOperator < Logic::BinaryOperator
|
||||
def to_s = "empty operator"
|
||||
def to_s(_type_de_champs = []) = "empty operator"
|
||||
|
||||
def type = :empty
|
||||
def type(_type_de_champs = []) = :empty
|
||||
|
||||
def errors(_stable_ids = nil) = []
|
||||
def errors(_type_de_champs = []) = []
|
||||
|
||||
def compute(_champs = [])
|
||||
true
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
class Logic::Eq < Logic::BinaryOperator
|
||||
def operation = :==
|
||||
|
||||
def errors(stable_ids = [])
|
||||
errors = []
|
||||
def errors(type_de_champs = [])
|
||||
errors = [@left, @right]
|
||||
.filter { |term| term.type(type_de_champs) == :unmanaged }
|
||||
.map { |term| { type: :unmanaged, stable_id: term.stable_id } }
|
||||
|
||||
if !Logic.compatible_type?(@left, @right)
|
||||
errors += ["les types sont incompatibles : #{self}"]
|
||||
if !Logic.compatible_type?(@left, @right, type_de_champs)
|
||||
errors << {
|
||||
type: :incompatible,
|
||||
stable_id: @left.try(:stable_id),
|
||||
right: @right,
|
||||
operator_name: self.class.name
|
||||
}
|
||||
elsif @left.type(type_de_champs) == :enum &&
|
||||
!left.options(type_de_champs).map(&:second).include?(right.value)
|
||||
errors << {
|
||||
type: :not_included,
|
||||
stable_id: @left.stable_id,
|
||||
right: @right
|
||||
}
|
||||
end
|
||||
|
||||
errors + @left.errors(stable_ids) + @right.errors(stable_ids)
|
||||
errors + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
|
|
29
app/models/logic/include_operator.rb
Normal file
29
app/models/logic/include_operator.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
class Logic::IncludeOperator < Logic::BinaryOperator
|
||||
def operation = :include?
|
||||
|
||||
def errors(type_de_champs = [])
|
||||
result = []
|
||||
|
||||
if left_not_a_list?(type_de_champs)
|
||||
result << { type: :required_list }
|
||||
elsif right_value_not_in_list?(type_de_champs)
|
||||
result << {
|
||||
type: :not_included,
|
||||
stable_id: @left.stable_id,
|
||||
right: @right
|
||||
}
|
||||
end
|
||||
|
||||
result + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def left_not_a_list?(type_de_champs)
|
||||
@left.type(type_de_champs) != :enums
|
||||
end
|
||||
|
||||
def right_value_not_in_list?(type_de_champs)
|
||||
!@left.options(type_de_champs).map(&:second).include?(@right.value)
|
||||
end
|
||||
end
|
|
@ -16,22 +16,22 @@ class Logic::NAryOperator < Logic::Term
|
|||
self.new(h['operands'].map { |operand_h| Logic.from_h(operand_h) })
|
||||
end
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
errors = []
|
||||
|
||||
if @operands.empty?
|
||||
errors += ["opérateur '#{operator_name}' vide"]
|
||||
end
|
||||
|
||||
not_booleans = @operands.filter { |operand| operand.type != :boolean }
|
||||
not_booleans = @operands.filter { |operand| operand.type(type_de_champs) != :boolean }
|
||||
if not_booleans.present?
|
||||
errors += ["'#{operator_name}' ne contient pas que des booléens : #{not_booleans.map(&:to_s).join(', ')}"]
|
||||
errors += ["'#{operator_name}' ne contient pas que des booléens : #{not_booleans.map { |o| o.to_s(type_de_champs) }.join(', ')}"]
|
||||
end
|
||||
|
||||
errors + @operands.flat_map { |operand| operand.errors(stable_ids) }
|
||||
errors + @operands.flat_map { |operand| operand.errors(type_de_champs) }
|
||||
end
|
||||
|
||||
def type = :boolean
|
||||
def type(_type_de_champs = []) = :boolean
|
||||
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
|
|
|
@ -7,5 +7,5 @@ class Logic::Or < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.any?
|
||||
end
|
||||
|
||||
def to_s = "(#{@operands.map(&:to_s).join(' || ')})"
|
||||
def to_s(type_de_champs = []) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' || ')})"
|
||||
end
|
||||
|
|
|
@ -261,11 +261,11 @@ class Procedure < ApplicationRecord
|
|||
validates :administrateurs, presence: true
|
||||
validates :lien_site_web, presence: true, if: :publiee?
|
||||
validates :draft_types_de_champ,
|
||||
'types_de_champ/no_empty_repetition': true,
|
||||
'types_de_champ/no_empty_block': true,
|
||||
'types_de_champ/no_empty_drop_down': true,
|
||||
if: :validate_for_publication?
|
||||
validates :draft_types_de_champ_private,
|
||||
'types_de_champ/no_empty_repetition': true,
|
||||
'types_de_champ/no_empty_block': true,
|
||||
'types_de_champ/no_empty_drop_down': true,
|
||||
if: :validate_for_publication?
|
||||
validate :check_juridique
|
||||
|
@ -640,7 +640,9 @@ class Procedure < ApplicationRecord
|
|||
dossiers
|
||||
.state_en_construction
|
||||
.where(declarative_triggered_at: nil)
|
||||
.find_each(&:accepter_automatiquement!)
|
||||
.find_each do |dossier|
|
||||
dossier.accepter_automatiquement! if dossier.may_accepter_automatiquement?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -810,6 +812,10 @@ class Procedure < ApplicationRecord
|
|||
api_particulier_sources['mesri'].present?
|
||||
end
|
||||
|
||||
def published_or_created_at
|
||||
published_at || created_at
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def move_new_children_to_new_parent_coordinate(new_draft)
|
||||
|
|
|
@ -18,6 +18,8 @@ class ProcedurePresentation < ApplicationRecord
|
|||
|
||||
TABLE = 'table'
|
||||
COLUMN = 'column'
|
||||
ORDER = 'order'
|
||||
|
||||
SLASH = '/'
|
||||
TYPE_DE_CHAMP = 'type_de_champ'
|
||||
TYPE_DE_CHAMP_PRIVATE = 'type_de_champ_private'
|
||||
|
@ -35,47 +37,60 @@ class ProcedurePresentation < ApplicationRecord
|
|||
validate :check_allowed_filter_columns
|
||||
validate :check_filters_max_length
|
||||
|
||||
def fields
|
||||
fields = [
|
||||
field_hash('self', 'created_at'),
|
||||
field_hash('self', 'en_construction_at'),
|
||||
field_hash('self', 'depose_at'),
|
||||
field_hash('self', 'updated_at'),
|
||||
field_hash('self', 'depose_since', virtual: true),
|
||||
field_hash('self', 'updated_since', virtual: true),
|
||||
field_hash('user', 'email'),
|
||||
field_hash('followers_instructeurs', 'email'),
|
||||
field_hash('groupe_instructeur', 'label')
|
||||
def self_fields
|
||||
[
|
||||
field_hash('self', 'created_at', type: :date),
|
||||
field_hash('self', 'updated_at', type: :date),
|
||||
field_hash('self', 'depose_at', type: :date),
|
||||
field_hash('self', 'en_construction_at', type: :date),
|
||||
field_hash('self', 'en_instruction_at', type: :date),
|
||||
field_hash('self', 'processed_at', type: :date),
|
||||
field_hash('self', 'updated_since', type: :date, virtual: true),
|
||||
field_hash('self', 'depose_since', type: :date, virtual: true),
|
||||
field_hash('self', 'en_construction_since', type: :date, virtual: true),
|
||||
field_hash('self', 'en_instruction_since', type: :date, virtual: true),
|
||||
field_hash('self', 'processed_since', type: :date, virtual: true),
|
||||
field_hash('self', 'state', type: :enum, scope: 'instructeurs.dossiers.filterable_state', virtual: true)
|
||||
]
|
||||
end
|
||||
|
||||
def fields
|
||||
fields = self_fields
|
||||
|
||||
fields.push(
|
||||
field_hash('user', 'email', type: :text),
|
||||
field_hash('followers_instructeurs', 'email', type: :text),
|
||||
field_hash('groupe_instructeur', 'label', type: :text)
|
||||
)
|
||||
|
||||
if procedure.for_individual
|
||||
fields.push(
|
||||
field_hash("individual", "prenom"),
|
||||
field_hash("individual", "nom"),
|
||||
field_hash("individual", "gender")
|
||||
field_hash("individual", "prenom", type: :text),
|
||||
field_hash("individual", "nom", type: :text),
|
||||
field_hash("individual", "gender", type: :text)
|
||||
)
|
||||
end
|
||||
|
||||
if !procedure.for_individual
|
||||
fields.push(
|
||||
field_hash('etablissement', 'entreprise_siren'),
|
||||
field_hash('etablissement', 'entreprise_forme_juridique'),
|
||||
field_hash('etablissement', 'entreprise_nom_commercial'),
|
||||
field_hash('etablissement', 'entreprise_raison_sociale'),
|
||||
field_hash('etablissement', 'entreprise_siret_siege_social'),
|
||||
field_hash('etablissement', 'entreprise_date_creation')
|
||||
field_hash('etablissement', 'entreprise_siren', type: :text),
|
||||
field_hash('etablissement', 'entreprise_forme_juridique', type: :text),
|
||||
field_hash('etablissement', 'entreprise_nom_commercial', type: :text),
|
||||
field_hash('etablissement', 'entreprise_raison_sociale', type: :text),
|
||||
field_hash('etablissement', 'entreprise_siret_siege_social', type: :text),
|
||||
field_hash('etablissement', 'entreprise_date_creation', type: :date)
|
||||
)
|
||||
|
||||
fields.push(
|
||||
field_hash('etablissement', 'siret'),
|
||||
field_hash('etablissement', 'libelle_naf'),
|
||||
field_hash('etablissement', 'code_postal')
|
||||
field_hash('etablissement', 'siret', type: :text),
|
||||
field_hash('etablissement', 'libelle_naf', type: :text),
|
||||
field_hash('etablissement', 'code_postal', type: :text)
|
||||
)
|
||||
end
|
||||
|
||||
fields.concat procedure.types_de_champ_for_procedure_presentation
|
||||
.pluck(:libelle, :private, :stable_id)
|
||||
.map { |(libelle, is_private, stable_id)| field_hash(is_private ? TYPE_DE_CHAMP_PRIVATE : TYPE_DE_CHAMP, stable_id.to_s, label: libelle) }
|
||||
.map { |(libelle, is_private, stable_id)| field_hash(is_private ? TYPE_DE_CHAMP_PRIVATE : TYPE_DE_CHAMP, stable_id.to_s, label: libelle, type: :text) }
|
||||
|
||||
fields
|
||||
end
|
||||
|
@ -89,7 +104,9 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def filterable_fields_options
|
||||
fields.map { |field| [field['label'], field_id(field)] }
|
||||
fields.map do |field|
|
||||
[field['label'], field_id(field)]
|
||||
end
|
||||
end
|
||||
|
||||
def displayed_fields_for_headers
|
||||
|
@ -152,14 +169,21 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def filtered_ids(dossiers, statut)
|
||||
filters[statut].group_by { |filter| filter.values_at(TABLE, COLUMN) } .map do |(table, column), filters|
|
||||
filters.fetch(statut)
|
||||
.group_by { |filter| filter.values_at(TABLE, COLUMN) }
|
||||
.map do |(table, column), filters|
|
||||
values = filters.pluck('value')
|
||||
case table
|
||||
when 'self'
|
||||
dates = values
|
||||
.filter_map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
|
||||
field = self_fields.find { |h| h['column'] == column }
|
||||
if field['type'] == :date
|
||||
dates = values
|
||||
.filter_map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
|
||||
|
||||
dossiers.filter_by_datetimes(column, dates)
|
||||
dossiers.filter_by_datetimes(column, dates)
|
||||
else
|
||||
dossiers.where("dossiers.#{column} IN (?)", values)
|
||||
end
|
||||
when TYPE_DE_CHAMP
|
||||
dossiers.with_type_de_champ(column)
|
||||
.filter_ilike(:champs, :value, values)
|
||||
|
@ -264,24 +288,34 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def update_sort(table, column)
|
||||
order = if sort.values_at(TABLE, COLUMN) == [table, column]
|
||||
def update_sort(table, column, order)
|
||||
update!(sort: {
|
||||
TABLE => table,
|
||||
COLUMN => column,
|
||||
ORDER => opposite_order_for(table, column)
|
||||
})
|
||||
end
|
||||
|
||||
def opposite_order_for(table, column)
|
||||
if sort.values_at(TABLE, COLUMN) == [table, column]
|
||||
sort['order'] == 'asc' ? 'desc' : 'asc'
|
||||
else
|
||||
'asc'
|
||||
end
|
||||
|
||||
update!(sort: {
|
||||
TABLE => table,
|
||||
COLUMN => column,
|
||||
'order' => order
|
||||
})
|
||||
end
|
||||
|
||||
def snapshot
|
||||
slice(:filters, :sort, :displayed_fields)
|
||||
end
|
||||
|
||||
def field_type(field_id)
|
||||
find_field(*field_id.split(SLASH))['type']
|
||||
end
|
||||
|
||||
def field_enum(field_id)
|
||||
find_field(*field_id.split(SLASH))['scope']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def field_id(field)
|
||||
|
@ -342,13 +376,15 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def field_hash(table, column, label: nil, classname: '', virtual: false)
|
||||
def field_hash(table, column, label: nil, classname: '', virtual: false, type: :text, scope: '')
|
||||
{
|
||||
'label' => label || I18n.t(column, scope: [:activerecord, :attributes, :procedure_presentation, :fields, table]),
|
||||
TABLE => table,
|
||||
COLUMN => column,
|
||||
'classname' => classname,
|
||||
'virtual' => virtual
|
||||
'virtual' => virtual,
|
||||
'type' => type,
|
||||
'scope' => scope
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -317,7 +317,7 @@ class ProcedureRevision < ApplicationRecord
|
|||
changed = kept
|
||||
.map { |sid| [sid, from_h[sid], to_h[sid]] }
|
||||
.flat_map do |sid, from, to|
|
||||
compare_type_de_champ(from.type_de_champ, to.type_de_champ)
|
||||
compare_type_de_champ(from.type_de_champ, to.type_de_champ, from_coordinates, to_coordinates)
|
||||
.each { |h| h[:_position] = to_sids.index(sid) }
|
||||
end
|
||||
|
||||
|
@ -327,7 +327,7 @@ class ProcedureRevision < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def compare_type_de_champ(from_type_de_champ, to_type_de_champ)
|
||||
def compare_type_de_champ(from_type_de_champ, to_type_de_champ, from_coordinates, to_coordinates)
|
||||
changes = []
|
||||
if from_type_de_champ.type_champ != to_type_de_champ.type_champ
|
||||
changes << {
|
||||
|
@ -385,8 +385,8 @@ class ProcedureRevision < ApplicationRecord
|
|||
attribute: :condition,
|
||||
label: from_type_de_champ.libelle,
|
||||
private: from_type_de_champ.private?,
|
||||
from: from_type_de_champ.condition&.to_s,
|
||||
to: to_type_de_champ.condition&.to_s,
|
||||
from: from_type_de_champ.condition&.to_s(from_coordinates.map(&:type_de_champ)),
|
||||
to: to_type_de_champ.condition&.to_s(to_coordinates.map(&:type_de_champ)),
|
||||
stable_id: from_type_de_champ.stable_id
|
||||
}
|
||||
end
|
||||
|
@ -479,12 +479,12 @@ class ProcedureRevision < ApplicationRecord
|
|||
end
|
||||
|
||||
def conditions_are_valid?
|
||||
stable_ids = types_de_champ_public.map(&:stable_id)
|
||||
public_tdcs = types_de_champ_public.to_a
|
||||
|
||||
types_de_champ_public
|
||||
public_tdcs
|
||||
.map.with_index
|
||||
.filter_map { |tdc, i| tdc.condition.present? ? [tdc, i] : nil }
|
||||
.map { |tdc, i| [tdc, tdc.condition.errors(stable_ids.take(i))] }
|
||||
.map { |tdc, i| [tdc, tdc.condition.errors(public_tdcs.take(i))] }
|
||||
.filter { |_tdc, errors| errors.present? }
|
||||
.each { |tdc, message| errors.add(:condition, message, type_de_champ: tdc) }
|
||||
end
|
||||
|
|
|
@ -20,6 +20,53 @@ class TypeDeChamp < ApplicationRecord
|
|||
FILE_MAX_SIZE = 200.megabytes
|
||||
FEATURE_FLAGS = {}
|
||||
|
||||
CADRE = :cadre
|
||||
STANDARD = :standard
|
||||
CHOICE = :choice
|
||||
ETAT_CIVIL = :etat_civil
|
||||
PAIEMENT_IDENTIFICATION = :paiement_identification
|
||||
REFERENTIEL_EXTERNE = :referentiel_externe
|
||||
LOCALISATION = :localisation
|
||||
|
||||
CATEGORIES = [CADRE, ETAT_CIVIL, LOCALISATION, PAIEMENT_IDENTIFICATION, STANDARD, CHOICE, REFERENTIEL_EXTERNE]
|
||||
|
||||
TYPE_DE_CHAMP_TO_CATEGORIE = {
|
||||
text: STANDARD,
|
||||
textarea: STANDARD,
|
||||
date: STANDARD,
|
||||
datetime: STANDARD,
|
||||
number: STANDARD,
|
||||
decimal_number: STANDARD,
|
||||
integer_number: STANDARD,
|
||||
checkbox: CHOICE,
|
||||
civilite: ETAT_CIVIL,
|
||||
email: ETAT_CIVIL,
|
||||
phone: ETAT_CIVIL,
|
||||
address: LOCALISATION,
|
||||
yes_no: CHOICE,
|
||||
drop_down_list: CHOICE,
|
||||
multiple_drop_down_list: CHOICE,
|
||||
linked_drop_down_list: CHOICE,
|
||||
pays: LOCALISATION,
|
||||
regions: LOCALISATION,
|
||||
departements: LOCALISATION,
|
||||
communes: LOCALISATION,
|
||||
header_section: CADRE,
|
||||
explication: CADRE,
|
||||
dossier_link: CADRE,
|
||||
piece_justificative: STANDARD,
|
||||
siret: PAIEMENT_IDENTIFICATION,
|
||||
carte: REFERENTIEL_EXTERNE,
|
||||
repetition: CADRE,
|
||||
titre_identite: ETAT_CIVIL,
|
||||
iban: PAIEMENT_IDENTIFICATION,
|
||||
annuaire_education: REFERENTIEL_EXTERNE,
|
||||
cnaf: REFERENTIEL_EXTERNE,
|
||||
dgfip: REFERENTIEL_EXTERNE,
|
||||
pole_emploi: REFERENTIEL_EXTERNE,
|
||||
mesri: REFERENTIEL_EXTERNE
|
||||
}
|
||||
|
||||
enum type_champs: {
|
||||
text: 'text',
|
||||
textarea: 'textarea',
|
||||
|
@ -37,21 +84,20 @@ class TypeDeChamp < ApplicationRecord
|
|||
drop_down_list: 'drop_down_list',
|
||||
multiple_drop_down_list: 'multiple_drop_down_list',
|
||||
linked_drop_down_list: 'linked_drop_down_list',
|
||||
pays: 'pays',
|
||||
regions: 'regions',
|
||||
departements: 'departements',
|
||||
communes: 'communes',
|
||||
engagement: 'engagement',
|
||||
departements: 'departements',
|
||||
regions: 'regions',
|
||||
pays: 'pays',
|
||||
header_section: 'header_section',
|
||||
explication: 'explication',
|
||||
dossier_link: 'dossier_link',
|
||||
piece_justificative: 'piece_justificative',
|
||||
siret: 'siret',
|
||||
rna: 'rna',
|
||||
carte: 'carte',
|
||||
repetition: 'repetition',
|
||||
titre_identite: 'titre_identite',
|
||||
iban: 'iban',
|
||||
siret: 'siret',
|
||||
annuaire_education: 'annuaire_education',
|
||||
cnaf: 'cnaf',
|
||||
dgfip: 'dgfip',
|
||||
|
@ -134,7 +180,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
before_validation :check_mandatory
|
||||
before_save :remove_piece_justificative_template, if: -> { type_champ_changed? }
|
||||
before_validation :remove_drop_down_list, if: -> { type_champ_changed? }
|
||||
before_save :remove_repetition, if: -> { type_champ_changed? }
|
||||
before_save :remove_block, if: -> { type_champ_changed? }
|
||||
|
||||
after_save if: -> { @remove_piece_justificative_template } do
|
||||
piece_justificative_template.purge_later
|
||||
|
@ -222,6 +268,10 @@ class TypeDeChamp < ApplicationRecord
|
|||
type_champ == TypeDeChamp.type_champs.fetch(:linked_drop_down_list)
|
||||
end
|
||||
|
||||
def block?
|
||||
type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
||||
end
|
||||
|
||||
def header_section?
|
||||
type_champ == TypeDeChamp.type_champs.fetch(:header_section)
|
||||
end
|
||||
|
@ -411,8 +461,8 @@ class TypeDeChamp < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def remove_repetition
|
||||
if !repetition? && procedure.present?
|
||||
def remove_block
|
||||
if !block? && procedure.present?
|
||||
procedure
|
||||
.draft_revision # action occurs only on draft
|
||||
.remove_children_of(self)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
class TypesDeChamp::EngagementTypeDeChamp < TypesDeChamp::CheckboxTypeDeChamp
|
||||
end
|
|
@ -10,5 +10,23 @@
|
|||
#
|
||||
class Zone < ApplicationRecord
|
||||
validates :acronym, presence: true, uniqueness: true
|
||||
has_many :labels, -> { order(designated_on: :desc) }, class_name: 'ZoneLabel', inverse_of: :zone
|
||||
has_many :procedures, -> { order(published_at: :desc) }, inverse_of: :zone
|
||||
|
||||
def current_label
|
||||
labels.first.name
|
||||
end
|
||||
|
||||
def label_at(date)
|
||||
label = labels.where('designated_on < ?', date)&.first || labels.last
|
||||
label.name
|
||||
end
|
||||
|
||||
def available_at?(date)
|
||||
label_at(date) != 'Non attribué'
|
||||
end
|
||||
|
||||
def self.available_at(date)
|
||||
Zone.all.filter { |zone| zone.available_at?(date) }.sort_by { |zone| zone.label_at(date) }
|
||||
end
|
||||
end
|
||||
|
|
14
app/models/zone_label.rb
Normal file
14
app/models/zone_label.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: zone_labels
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# designated_on :date not null
|
||||
# name :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# zone_id :bigint not null
|
||||
#
|
||||
class ZoneLabel < ApplicationRecord
|
||||
belongs_to :zone
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue