commit
5377a408f6
27 changed files with 769 additions and 226 deletions
|
@ -39,7 +39,8 @@ class API::V2::Schema < GraphQL::Schema
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
orphan_types Types::Champs::CarteChampType,
|
orphan_types Types::Champs::AddressChampType,
|
||||||
|
Types::Champs::CarteChampType,
|
||||||
Types::Champs::CheckboxChampType,
|
Types::Champs::CheckboxChampType,
|
||||||
Types::Champs::CiviliteChampType,
|
Types::Champs::CiviliteChampType,
|
||||||
Types::Champs::DateChampType,
|
Types::Champs::DateChampType,
|
||||||
|
|
|
@ -1,3 +1,107 @@
|
||||||
|
type Address {
|
||||||
|
"""
|
||||||
|
code INSEE de la commune
|
||||||
|
"""
|
||||||
|
cityCode: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
nom de la commune
|
||||||
|
"""
|
||||||
|
cityName: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
n° de département
|
||||||
|
"""
|
||||||
|
departmentCode: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
nom de département
|
||||||
|
"""
|
||||||
|
departmentName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
coordonnées géographique
|
||||||
|
"""
|
||||||
|
geometry: GeoJSON
|
||||||
|
|
||||||
|
"""
|
||||||
|
libellé complet de l’adresse
|
||||||
|
"""
|
||||||
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
code postal
|
||||||
|
"""
|
||||||
|
postalCode: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
n° de region
|
||||||
|
"""
|
||||||
|
regionCode: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
nom de région
|
||||||
|
"""
|
||||||
|
regionName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
numéro éventuel et nom de voie ou lieu dit
|
||||||
|
"""
|
||||||
|
streetAddress: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
nom de voie ou lieu dit
|
||||||
|
"""
|
||||||
|
streetName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
numéro avec indice de répétition éventuel (bis, ter, A, B)
|
||||||
|
"""
|
||||||
|
streetNumber: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
type de résultat trouvé
|
||||||
|
"""
|
||||||
|
type: AddressType!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressChamp implements Champ {
|
||||||
|
address: Address
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
|
stringValue: String
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AddressType {
|
||||||
|
"""
|
||||||
|
numéro « à la plaque »
|
||||||
|
"""
|
||||||
|
housenumber
|
||||||
|
|
||||||
|
"""
|
||||||
|
lieu-dit
|
||||||
|
"""
|
||||||
|
locality
|
||||||
|
|
||||||
|
"""
|
||||||
|
numéro « à la commune »
|
||||||
|
"""
|
||||||
|
municipality
|
||||||
|
|
||||||
|
"""
|
||||||
|
position « à la voie », placé approximativement au centre de celle-ci
|
||||||
|
"""
|
||||||
|
street
|
||||||
|
}
|
||||||
|
|
||||||
type Association {
|
type Association {
|
||||||
dateCreation: ISO8601Date!
|
dateCreation: ISO8601Date!
|
||||||
dateDeclaration: ISO8601Date!
|
dateDeclaration: ISO8601Date!
|
||||||
|
@ -1362,21 +1466,22 @@ type ParcelleCadastrale implements GeoArea {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersonneMorale implements Demandeur {
|
type PersonneMorale implements Demandeur {
|
||||||
adresse: String!
|
address: Address!
|
||||||
|
adresse: String! @deprecated(reason: "Utilisez le champ `address.label` à la place.")
|
||||||
association: Association
|
association: Association
|
||||||
codeInseeLocalite: String!
|
codeInseeLocalite: String! @deprecated(reason: "Utilisez le champ `address.city_code` à la place.")
|
||||||
codePostal: String!
|
codePostal: String! @deprecated(reason: "Utilisez le champ `address.postal_code` à la place.")
|
||||||
complementAdresse: String
|
complementAdresse: String @deprecated(reason: "Utilisez le champ `address` à la place.")
|
||||||
entreprise: Entreprise
|
entreprise: Entreprise
|
||||||
id: ID!
|
id: ID!
|
||||||
libelleNaf: String!
|
libelleNaf: String!
|
||||||
localite: String!
|
localite: String! @deprecated(reason: "Utilisez le champ `address.city_name` à la place.")
|
||||||
naf: String!
|
naf: String!
|
||||||
nomVoie: String
|
nomVoie: String @deprecated(reason: "Utilisez le champ `address.street_name` à la place.")
|
||||||
numeroVoie: String
|
numeroVoie: String @deprecated(reason: "Utilisez le champ `address.street_number` à la place.")
|
||||||
siegeSocial: Boolean!
|
siegeSocial: Boolean!
|
||||||
siret: String!
|
siret: String!
|
||||||
typeVoie: String
|
typeVoie: String @deprecated(reason: "Utilisez le champ `address.street_address` à la place.")
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersonnePhysique implements Demandeur {
|
type PersonnePhysique implements Demandeur {
|
||||||
|
|
29
app/graphql/types/address_type.rb
Normal file
29
app/graphql/types/address_type.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module Types
|
||||||
|
class AddressType < Types::BaseObject
|
||||||
|
class AddressTypeType < Types::BaseEnum
|
||||||
|
value(:housenumber, "numéro « à la plaque »", value: :housenumber)
|
||||||
|
value(:street, "position « à la voie », placé approximativement au centre de celle-ci", value: :street)
|
||||||
|
value(:municipality, "numéro « à la commune »", value: :municipality)
|
||||||
|
value(:locality, "lieu-dit", value: :locality)
|
||||||
|
end
|
||||||
|
|
||||||
|
field :label, String, "libellé complet de l’adresse", null: false
|
||||||
|
field :type, AddressTypeType, "type de résultat trouvé", null: false
|
||||||
|
|
||||||
|
field :street_address, String, "numéro éventuel et nom de voie ou lieu dit", null: false
|
||||||
|
field :street_number, String, "numéro avec indice de répétition éventuel (bis, ter, A, B)", null: true
|
||||||
|
field :street_name, String, "nom de voie ou lieu dit", null: true
|
||||||
|
|
||||||
|
field :postal_code, String, "code postal", null: false
|
||||||
|
field :city_name, String, "nom de la commune", null: false
|
||||||
|
field :city_code, String, "code INSEE de la commune", null: false
|
||||||
|
|
||||||
|
field :department_name, String, "nom de département", null: true
|
||||||
|
field :department_code, String, "n° de département", null: true
|
||||||
|
|
||||||
|
field :region_name, String, "nom de région", null: true
|
||||||
|
field :region_code, String, "n° de region", null: true
|
||||||
|
|
||||||
|
field :geometry, Types::GeoJSON, "coordonnées géographique", null: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,12 @@ module Types
|
||||||
definition_methods do
|
definition_methods do
|
||||||
def resolve_type(object, context)
|
def resolve_type(object, context)
|
||||||
case object
|
case object
|
||||||
|
when ::Champs::AddressChamp
|
||||||
|
if context.has_fragment?(:AddressChamp)
|
||||||
|
Types::Champs::AddressChampType
|
||||||
|
else
|
||||||
|
Types::Champs::TextChampType
|
||||||
|
end
|
||||||
when ::Champs::EngagementChamp, ::Champs::YesNoChamp, ::Champs::CheckboxChamp
|
when ::Champs::EngagementChamp, ::Champs::YesNoChamp, ::Champs::CheckboxChamp
|
||||||
Types::Champs::CheckboxChampType
|
Types::Champs::CheckboxChampType
|
||||||
when ::Champs::DateChamp
|
when ::Champs::DateChamp
|
||||||
|
|
7
app/graphql/types/champs/address_champ_type.rb
Normal file
7
app/graphql/types/champs/address_champ_type.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module Types::Champs
|
||||||
|
class AddressChampType < Types::BaseObject
|
||||||
|
implements Types::ChampType
|
||||||
|
|
||||||
|
field :address, Types::AddressType, null: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -86,17 +86,34 @@ module Types
|
||||||
field :siege_social, Boolean, null: false
|
field :siege_social, Boolean, null: false
|
||||||
field :naf, String, null: false
|
field :naf, String, null: false
|
||||||
field :libelle_naf, String, null: false
|
field :libelle_naf, String, null: false
|
||||||
field :adresse, String, null: false
|
|
||||||
field :numero_voie, String, null: true
|
field :address, Types::AddressType, null: false
|
||||||
field :type_voie, String, null: true
|
|
||||||
field :nom_voie, String, null: true
|
|
||||||
field :complement_adresse, String, null: true
|
|
||||||
field :code_postal, String, null: false
|
|
||||||
field :localite, String, null: false
|
|
||||||
field :code_insee_localite, String, null: false
|
|
||||||
field :entreprise, EntrepriseType, null: true
|
field :entreprise, EntrepriseType, null: true
|
||||||
field :association, AssociationType, null: true
|
field :association, AssociationType, null: true
|
||||||
|
|
||||||
|
field :adresse, String, null: false, deprecation_reason: "Utilisez le champ `address.label` à la place."
|
||||||
|
field :numero_voie, String, null: true, deprecation_reason: "Utilisez le champ `address.street_number` à la place."
|
||||||
|
field :type_voie, String, null: true, deprecation_reason: "Utilisez le champ `address.street_address` à la place."
|
||||||
|
field :nom_voie, String, null: true, deprecation_reason: "Utilisez le champ `address.street_name` à la place."
|
||||||
|
field :code_postal, String, null: false, deprecation_reason: "Utilisez le champ `address.postal_code` à la place."
|
||||||
|
field :localite, String, null: false, deprecation_reason: "Utilisez le champ `address.city_name` à la place."
|
||||||
|
field :code_insee_localite, String, null: false, deprecation_reason: "Utilisez le champ `address.city_code` à la place."
|
||||||
|
field :complement_adresse, String, null: true, deprecation_reason: "Utilisez le champ `address` à la place."
|
||||||
|
|
||||||
|
def address
|
||||||
|
{
|
||||||
|
label: object.adresse,
|
||||||
|
type: :housenumber,
|
||||||
|
street_number: object.numero_voie,
|
||||||
|
street_name: object.nom_voie,
|
||||||
|
street_address: object.nom_voie.present? ? [object.numero_voie, object.type_voie, object.nom_voie].compact.join(' ') : nil,
|
||||||
|
postal_code: object.code_postal,
|
||||||
|
city_name: object.localite,
|
||||||
|
city_code: object.code_insee_localite
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def entreprise
|
def entreprise
|
||||||
if object.entreprise_siren.present?
|
if object.entreprise_siren.present?
|
||||||
object.entreprise
|
object.entreprise
|
||||||
|
|
|
@ -31,23 +31,46 @@ function ComboMultipleDropdownList({
|
||||||
if (label == undefined) {
|
if (label == undefined) {
|
||||||
label = 'Choisir une option';
|
label = 'Choisir une option';
|
||||||
}
|
}
|
||||||
if (Array.isArray(options[0]) == false) {
|
if (!Array.isArray(options[0])) {
|
||||||
options = options.map((o) => [o, o]);
|
options = options.filter((o) => o).map((o) => [o, o]);
|
||||||
}
|
}
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
const [term, setTerm] = useState('');
|
const [term, setTerm] = useState('');
|
||||||
const [selections, setSelections] = useState(selected);
|
const [selections, setSelections] = useState(selected);
|
||||||
const [newValues, setNewValues] = useState([]);
|
const [newValues, setNewValues] = useState([]);
|
||||||
|
|
||||||
|
const optionValueByLabel = (label) => {
|
||||||
|
const maybeOption = newValues.includes(label)
|
||||||
|
? [label, label]
|
||||||
|
: options.find(([optionLabel]) => optionLabel == label);
|
||||||
|
return maybeOption ? maybeOption[1] : undefined;
|
||||||
|
};
|
||||||
|
const optionLabelByValue = (value) => {
|
||||||
|
const maybeOption = newValues.includes(value)
|
||||||
|
? [value, value]
|
||||||
|
: options.find(([, optionValue]) => optionValue == value);
|
||||||
|
return maybeOption ? maybeOption[0] : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extraOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
acceptNewValues && term && term.length > 2 && !optionLabelByValue(term)
|
||||||
|
? [[term, term]]
|
||||||
|
: [],
|
||||||
|
[acceptNewValues, term, newValues.join(',')]
|
||||||
|
);
|
||||||
const results = useMemo(
|
const results = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(term
|
[
|
||||||
? matchSorter(
|
...extraOptions,
|
||||||
options.filter((o) => !o[0].startsWith('--')),
|
...(term
|
||||||
term
|
? matchSorter(
|
||||||
)
|
options.filter(([label]) => !label.startsWith('--')),
|
||||||
: options
|
term
|
||||||
).filter((o) => o[0] && !selections.includes(o[1])),
|
)
|
||||||
[term, selections.join(',')]
|
: options)
|
||||||
|
].filter(([, value]) => !selections.includes(value)),
|
||||||
|
[term, selections.join(','), newValues.join(',')]
|
||||||
);
|
);
|
||||||
const hiddenField = useMemo(
|
const hiddenField = useMemo(
|
||||||
() => document.querySelector(`input[data-uuid="${hiddenFieldId}"]`),
|
() => document.querySelector(`input[data-uuid="${hiddenFieldId}"]`),
|
||||||
|
@ -60,22 +83,12 @@ function ComboMultipleDropdownList({
|
||||||
|
|
||||||
const onKeyDown = (event) => {
|
const onKeyDown = (event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
if (term && options.map((o) => o[0]).includes(term)) {
|
|
||||||
event.preventDefault();
|
|
||||||
return onSelect(term);
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
acceptNewValues &&
|
|
||||||
term &&
|
term &&
|
||||||
matchSorter(
|
[...extraOptions, ...options].map(([label]) => label).includes(term)
|
||||||
options.map((o) => o[0]),
|
|
||||||
term
|
|
||||||
).length == 0 // ignore when was pressed for selecting popover option
|
|
||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setNewValues([...newValues, term]);
|
return onSelect(term);
|
||||||
saveSelection([...selections, term]);
|
|
||||||
setTerm('');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -89,19 +102,23 @@ function ComboMultipleDropdownList({
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelect = (value) => {
|
const onSelect = (value) => {
|
||||||
let sel = options.find((o) => o[0] == value)[1];
|
const maybeValue = [...extraOptions, ...options].find(
|
||||||
saveSelection([...selections, sel]);
|
([val]) => val == value
|
||||||
|
);
|
||||||
|
const selectedValue = maybeValue && maybeValue[1];
|
||||||
|
if (value) {
|
||||||
|
setNewValues([...newValues, selectedValue]);
|
||||||
|
saveSelection([...selections, selectedValue]);
|
||||||
|
}
|
||||||
setTerm('');
|
setTerm('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemove = (value) => {
|
const onRemove = (label) => {
|
||||||
saveSelection(
|
const optionValue = optionValueByLabel(label);
|
||||||
selections.filter((s) =>
|
if (optionValue) {
|
||||||
newValues.includes(value)
|
saveSelection(selections.filter((value) => value != optionValue));
|
||||||
? s != value
|
setNewValues(newValues.filter((value) => value != optionValue));
|
||||||
: s !== options.find((o) => o[0] == value)[1]
|
}
|
||||||
)
|
|
||||||
);
|
|
||||||
inputRef.current.focus();
|
inputRef.current.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -116,10 +133,7 @@ function ComboMultipleDropdownList({
|
||||||
{selections.map((selection) => (
|
{selections.map((selection) => (
|
||||||
<ComboboxToken
|
<ComboboxToken
|
||||||
key={selection}
|
key={selection}
|
||||||
value={
|
value={optionLabelByValue(selection)}
|
||||||
newValues.find((newValue) => newValue == selection) ||
|
|
||||||
options.find((o) => o[1] == selection)[0]
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -136,21 +150,15 @@ function ComboMultipleDropdownList({
|
||||||
{results.length === 0 && (
|
{results.length === 0 && (
|
||||||
<p>
|
<p>
|
||||||
Aucun résultat{' '}
|
Aucun résultat{' '}
|
||||||
<button
|
<button onClick={() => setTerm('')}>Effacer</button>
|
||||||
onClick={() => {
|
|
||||||
setTerm('');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Effacer
|
|
||||||
</button>
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<ComboboxList>
|
<ComboboxList>
|
||||||
{results.map((value, index) => {
|
{results.map(([label], index) => {
|
||||||
if (value[0].startsWith('--')) {
|
if (label.startsWith('--')) {
|
||||||
return <ComboboxSeparator key={index} value={value[0]} />;
|
return <ComboboxSeparator key={index} value={label} />;
|
||||||
}
|
}
|
||||||
return <ComboboxOption key={index} value={value[0]} />;
|
return <ComboboxOption key={index} value={label} />;
|
||||||
})}
|
})}
|
||||||
</ComboboxList>
|
</ComboboxList>
|
||||||
</ComboboxPopover>
|
</ComboboxPopover>
|
||||||
|
|
11
app/jobs/champ_fetch_external_data_job.rb
Normal file
11
app/jobs/champ_fetch_external_data_job.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class ChampFetchExternalDataJob < ApplicationJob
|
||||||
|
def perform(champ)
|
||||||
|
if champ.external_id.present?
|
||||||
|
data = champ.fetch_external_data
|
||||||
|
|
||||||
|
if data.present?
|
||||||
|
champ.update!(data: data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
46
app/lib/api_address/address_adapter.rb
Normal file
46
app/lib/api_address/address_adapter.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
require 'json_schemer'
|
||||||
|
|
||||||
|
class APIAddress::AddressAdapter
|
||||||
|
class InvalidSchemaError < ::StandardError
|
||||||
|
def initialize(errors)
|
||||||
|
super(errors.map(&:to_json).join("\n"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(search_term)
|
||||||
|
@search_term = search_term
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_params
|
||||||
|
result = Geocoder.search(@search_term, limit: 1).first
|
||||||
|
if result.present? && result.national_address == @search_term
|
||||||
|
feature = result.data['features'].first
|
||||||
|
if schemer.valid?(feature)
|
||||||
|
{
|
||||||
|
label: result.national_address,
|
||||||
|
type: result.result_type,
|
||||||
|
street_address: result.street_address,
|
||||||
|
street_number: result.street_number,
|
||||||
|
street_name: result.street_name,
|
||||||
|
postal_code: result.postal_code,
|
||||||
|
city_name: result.city_name,
|
||||||
|
city_code: result.city_code,
|
||||||
|
department_name: result.department_name,
|
||||||
|
department_code: result.department_code,
|
||||||
|
region_name: result.region_name,
|
||||||
|
region_code: result.region_code,
|
||||||
|
geometry: result.geometry
|
||||||
|
}
|
||||||
|
else
|
||||||
|
errors = schemer.validate(feature).to_a
|
||||||
|
raise InvalidSchemaError.new(errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def schemer
|
||||||
|
@schemer ||= JSONSchemer.schema(Rails.root.join('app/schemas/adresse-ban.json'))
|
||||||
|
end
|
||||||
|
end
|
|
@ -67,6 +67,8 @@ class Champ < ApplicationRecord
|
||||||
|
|
||||||
before_create :set_dossier_id, if: :needs_dossier_id?
|
before_create :set_dossier_id, if: :needs_dossier_id?
|
||||||
before_validation :set_dossier_id, if: :needs_dossier_id?
|
before_validation :set_dossier_id, if: :needs_dossier_id?
|
||||||
|
before_save :cleanup_if_empty
|
||||||
|
after_update_commit :fetch_external_data_later
|
||||||
|
|
||||||
validates :type_de_champ_id, uniqueness: { scope: [:dossier_id, :row] }
|
validates :type_de_champ_id, uniqueness: { scope: [:dossier_id, :row] }
|
||||||
|
|
||||||
|
@ -143,6 +145,14 @@ class Champ < ApplicationRecord
|
||||||
update_column(:fetch_external_data_exceptions, exceptions)
|
update_column(:fetch_external_data_exceptions, exceptions)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_external_data?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_external_data
|
||||||
|
raise NotImplemented.new(:fetch_external_data)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def needs_dossier_id?
|
def needs_dossier_id?
|
||||||
|
@ -152,4 +162,22 @@ class Champ < ApplicationRecord
|
||||||
def set_dossier_id
|
def set_dossier_id
|
||||||
self.dossier_id = parent.dossier_id
|
self.dossier_id = parent.dossier_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cleanup_if_empty
|
||||||
|
if external_id_changed?
|
||||||
|
self.data = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_external_data_later
|
||||||
|
if fetch_external_data? && external_id.present? && data.nil?
|
||||||
|
ChampFetchExternalDataJob.perform_later(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NotImplemented < ::StandardError
|
||||||
|
def initialize(method)
|
||||||
|
super(":#{method} not implemented")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,4 +18,47 @@
|
||||||
# type_de_champ_id :integer
|
# type_de_champ_id :integer
|
||||||
#
|
#
|
||||||
class Champs::AddressChamp < Champs::TextChamp
|
class Champs::AddressChamp < Champs::TextChamp
|
||||||
|
def full_address?
|
||||||
|
data.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def address
|
||||||
|
full_address? ? data : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def address_label
|
||||||
|
full_address? ? data['label'] : value
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_terms
|
||||||
|
if full_address?
|
||||||
|
[data['label'], data['departement'], data['region'], data['city']]
|
||||||
|
else
|
||||||
|
[address_label]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
address_label
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_tag
|
||||||
|
address_label
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_export
|
||||||
|
value.present? ? address_label : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_api
|
||||||
|
address_label
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_external_data?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_external_data
|
||||||
|
APIAddress::AddressAdapter.new(external_id).to_params
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,20 +18,11 @@
|
||||||
# type_de_champ_id :integer
|
# type_de_champ_id :integer
|
||||||
#
|
#
|
||||||
class Champs::AnnuaireEducationChamp < Champs::TextChamp
|
class Champs::AnnuaireEducationChamp < Champs::TextChamp
|
||||||
before_save :cleanup_if_empty
|
def fetch_external_data?
|
||||||
after_update_commit :fetch_data
|
true
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def cleanup_if_empty
|
|
||||||
if external_id_changed?
|
|
||||||
self.data = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_data
|
def fetch_external_data
|
||||||
if external_id.present? && data.nil?
|
ApiEducation::AnnuaireEducationAdapter.new(external_id).to_params
|
||||||
AnnuaireEducationUpdateJob.perform_later(self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -188,7 +188,7 @@ class Procedure < ApplicationRecord
|
||||||
validate :check_juridique
|
validate :check_juridique
|
||||||
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
|
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
|
||||||
validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }
|
validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }
|
||||||
validates :duree_conservation_dossiers_hors_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
validates :duree_conservation_dossiers_hors_ds, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||||
validates_with MonAvisEmbedValidator
|
validates_with MonAvisEmbedValidator
|
||||||
validates :notice, content_type: [
|
validates :notice, content_type: [
|
||||||
"application/msword",
|
"application/msword",
|
||||||
|
|
39
app/schemas/adresse-ban.json
Normal file
39
app/schemas/adresse-ban.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "http://demarches-simplifiees.fr/adresse-ban.schema.json",
|
||||||
|
"title": "Adresse BAN",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": { "type": "string" },
|
||||||
|
"housenumber": { "type": "string" },
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"postcode": { "type": "string" },
|
||||||
|
"citycode": { "type": "string" },
|
||||||
|
"city": { "type": "string" },
|
||||||
|
"district": { "type": "string" },
|
||||||
|
"context": { "type": "string" },
|
||||||
|
"type": {
|
||||||
|
"enum": ["housenumber", "street", "locality", "municipality"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["label", "type", "name", "postcode", "citycode", "city"]
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "Point"
|
||||||
|
},
|
||||||
|
"coordinates": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["properties"]
|
||||||
|
}
|
|
@ -16,19 +16,13 @@
|
||||||
%h3.header-subsection Logo de la démarche
|
%h3.header-subsection Logo de la démarche
|
||||||
= image_upload_and_render f, @procedure.logo
|
= image_upload_and_render f, @procedure.logo
|
||||||
|
|
||||||
- if !@procedure.locked?
|
%h3.header-subsection Conservation des données
|
||||||
%h3.header-subsection Conservation des données
|
= f.label :duree_conservation_dossiers_dans_ds do
|
||||||
= f.label :duree_conservation_dossiers_dans_ds do
|
Sur #{APPLICATION_NAME}
|
||||||
Sur #{APPLICATION_NAME}
|
%span.mandatory *
|
||||||
%span.mandatory *
|
|
||||||
%p.notice (durée en mois après le début de l’instruction)
|
|
||||||
= f.number_field :duree_conservation_dossiers_dans_ds, class: 'form-control', placeholder: '6', required: true
|
|
||||||
|
|
||||||
= f.label :duree_conservation_dossiers_hors_ds do
|
%p.notice (durée en mois après le début de l’instruction)
|
||||||
Hors #{APPLICATION_NAME}
|
= f.number_field :duree_conservation_dossiers_dans_ds, class: 'form-control', placeholder: '6', required: true
|
||||||
%span.mandatory *
|
|
||||||
%p.notice (durée en mois après la fin de l'instruction)
|
|
||||||
= f.number_field :duree_conservation_dossiers_hors_ds, class: 'form-control', placeholder: '6', required: true
|
|
||||||
|
|
||||||
- if @procedure.created_at.present?
|
- if @procedure.created_at.present?
|
||||||
= f.label :lien_site_web do
|
= f.label :lien_site_web do
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Geocoder.configure(lookup: :ban_data_gouv_fr)
|
Geocoder.configure(lookup: :ban_data_gouv_fr, use_https: true)
|
||||||
|
|
|
@ -8,7 +8,6 @@ FactoryBot.define do
|
||||||
cadre_juridique { "un cadre juridique important" }
|
cadre_juridique { "un cadre juridique important" }
|
||||||
published_at { nil }
|
published_at { nil }
|
||||||
duree_conservation_dossiers_dans_ds { 3 }
|
duree_conservation_dossiers_dans_ds { 3 }
|
||||||
duree_conservation_dossiers_hors_ds { 6 }
|
|
||||||
ask_birthday { false }
|
ask_birthday { false }
|
||||||
lien_site_web { "https://mon-site.gouv" }
|
lien_site_web { "https://mon-site.gouv" }
|
||||||
path { SecureRandom.uuid }
|
path { SecureRandom.uuid }
|
||||||
|
|
|
@ -38,7 +38,6 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
|
||||||
expect(find('#procedure_for_individual_true')).to be_checked
|
expect(find('#procedure_for_individual_true')).to be_checked
|
||||||
expect(find('#procedure_for_individual_false')).not_to be_checked
|
expect(find('#procedure_for_individual_false')).not_to be_checked
|
||||||
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
|
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
|
||||||
fill_in 'procedure_duree_conservation_dossiers_hors_ds', with: '6'
|
|
||||||
click_on 'Créer la démarche'
|
click_on 'Créer la démarche'
|
||||||
|
|
||||||
expect(page).to have_text('Libelle doit être rempli')
|
expect(page).to have_text('Libelle doit être rempli')
|
||||||
|
|
|
@ -4,6 +4,5 @@ module ProcedureSpecHelper
|
||||||
fill_in 'procedure_description', with: 'description de la procedure'
|
fill_in 'procedure_description', with: 'description de la procedure'
|
||||||
fill_in 'procedure_cadre_juridique', with: 'cadre juridique'
|
fill_in 'procedure_cadre_juridique', with: 'cadre juridique'
|
||||||
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
|
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
|
||||||
fill_in 'procedure_duree_conservation_dossiers_hors_ds', with: '6'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
131
spec/fixtures/files/api_address/address.json
vendored
Normal file
131
spec/fixtures/files/api_address/address.json
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"version": "draft",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
2.347,
|
||||||
|
48.859
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris",
|
||||||
|
"score": 0.9704590909090908,
|
||||||
|
"id": "75056",
|
||||||
|
"type": "municipality",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "75001",
|
||||||
|
"citycode": "75056",
|
||||||
|
"x": 652089.7,
|
||||||
|
"y": 6862305.26,
|
||||||
|
"population": 2190327,
|
||||||
|
"city": "Paris",
|
||||||
|
"context": "75, Paris, Île-de-France",
|
||||||
|
"importance": 0.67505
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
2.551187,
|
||||||
|
48.357318
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris Foret 77760 Achères-la-Forêt",
|
||||||
|
"score": 0.8608,
|
||||||
|
"id": "77001_b064",
|
||||||
|
"name": "Paris Foret",
|
||||||
|
"postcode": "77760",
|
||||||
|
"citycode": "77001",
|
||||||
|
"x": 666753.6,
|
||||||
|
"y": 6806428.85,
|
||||||
|
"city": "Achères-la-Forêt",
|
||||||
|
"context": "77, Seine-et-Marne, Île-de-France",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.4688
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
6.069561,
|
||||||
|
43.415211
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris 83170 Brignoles",
|
||||||
|
"score": 0.8607481818181817,
|
||||||
|
"id": "83023_05n1tm",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "83170",
|
||||||
|
"citycode": "83023",
|
||||||
|
"x": 948661.53,
|
||||||
|
"y": 6262177.77,
|
||||||
|
"city": "Brignoles",
|
||||||
|
"context": "83, Var, Provence-Alpes-Côte d'Azur",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.46823
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
-0.418837,
|
||||||
|
44.758777
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris 33880 Saint-Caprais-de-Bordeaux",
|
||||||
|
"score": 0.8593318181818181,
|
||||||
|
"id": "33381_sy62ut",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "33880",
|
||||||
|
"citycode": "33381",
|
||||||
|
"x": 429522.28,
|
||||||
|
"y": 6412482.95,
|
||||||
|
"city": "Saint-Caprais-de-Bordeaux",
|
||||||
|
"context": "33, Gironde, Nouvelle-Aquitaine",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.45265
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
0.156876,
|
||||||
|
47.33671
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris Buton 37140 Bourgueil",
|
||||||
|
"score": 0.8570918181818181,
|
||||||
|
"id": "37031_b165",
|
||||||
|
"name": "Paris Buton",
|
||||||
|
"postcode": "37140",
|
||||||
|
"citycode": "37031",
|
||||||
|
"x": 485353.99,
|
||||||
|
"y": 6696795.92,
|
||||||
|
"city": "Bourgueil",
|
||||||
|
"context": "37, Indre-et-Loire, Centre-Val de Loire",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.42801
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attribution": "BAN",
|
||||||
|
"licence": "ETALAB-2.0",
|
||||||
|
"query": "Paris",
|
||||||
|
"limit": 5
|
||||||
|
}
|
130
spec/fixtures/files/api_address/address_invalid.json
vendored
Normal file
130
spec/fixtures/files/api_address/address_invalid.json
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"version": "draft",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
2.347,
|
||||||
|
48.859
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris",
|
||||||
|
"score": 0.9704590909090908,
|
||||||
|
"id": "75056",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "75001",
|
||||||
|
"citycode": "75056",
|
||||||
|
"x": 652089.7,
|
||||||
|
"y": 6862305.26,
|
||||||
|
"population": 2190327,
|
||||||
|
"city": "Paris",
|
||||||
|
"context": "75, Paris, Île-de-France",
|
||||||
|
"importance": 0.67505
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
2.551187,
|
||||||
|
48.357318
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris Foret 77760 Achères-la-Forêt",
|
||||||
|
"score": 0.8608,
|
||||||
|
"id": "77001_b064",
|
||||||
|
"name": "Paris Foret",
|
||||||
|
"postcode": "77760",
|
||||||
|
"citycode": "77001",
|
||||||
|
"x": 666753.6,
|
||||||
|
"y": 6806428.85,
|
||||||
|
"city": "Achères-la-Forêt",
|
||||||
|
"context": "77, Seine-et-Marne, Île-de-France",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.4688
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
6.069561,
|
||||||
|
43.415211
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris 83170 Brignoles",
|
||||||
|
"score": 0.8607481818181817,
|
||||||
|
"id": "83023_05n1tm",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "83170",
|
||||||
|
"citycode": "83023",
|
||||||
|
"x": 948661.53,
|
||||||
|
"y": 6262177.77,
|
||||||
|
"city": "Brignoles",
|
||||||
|
"context": "83, Var, Provence-Alpes-Côte d'Azur",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.46823
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
-0.418837,
|
||||||
|
44.758777
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris 33880 Saint-Caprais-de-Bordeaux",
|
||||||
|
"score": 0.8593318181818181,
|
||||||
|
"id": "33381_sy62ut",
|
||||||
|
"name": "Paris",
|
||||||
|
"postcode": "33880",
|
||||||
|
"citycode": "33381",
|
||||||
|
"x": 429522.28,
|
||||||
|
"y": 6412482.95,
|
||||||
|
"city": "Saint-Caprais-de-Bordeaux",
|
||||||
|
"context": "33, Gironde, Nouvelle-Aquitaine",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.45265
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
0.156876,
|
||||||
|
47.33671
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "Paris Buton 37140 Bourgueil",
|
||||||
|
"score": 0.8570918181818181,
|
||||||
|
"id": "37031_b165",
|
||||||
|
"name": "Paris Buton",
|
||||||
|
"postcode": "37140",
|
||||||
|
"citycode": "37031",
|
||||||
|
"x": 485353.99,
|
||||||
|
"y": 6696795.92,
|
||||||
|
"city": "Bourgueil",
|
||||||
|
"context": "37, Indre-et-Loire, Centre-Val de Loire",
|
||||||
|
"type": "street",
|
||||||
|
"importance": 0.42801
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attribution": "BAN",
|
||||||
|
"licence": "ETALAB-2.0",
|
||||||
|
"query": "Paris",
|
||||||
|
"limit": 5
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"limit": 5,
|
|
||||||
"attribution": "BAN",
|
|
||||||
"version": "draft",
|
|
||||||
"licence": "ODbL 1.0",
|
|
||||||
"query": "Paris",
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"features": []
|
|
||||||
}
|
|
117
spec/fixtures/files/api_adresse/search_results.json
vendored
117
spec/fixtures/files/api_adresse/search_results.json
vendored
|
@ -1,117 +0,0 @@
|
||||||
{
|
|
||||||
"limit": 5,
|
|
||||||
"attribution": "BAN",
|
|
||||||
"version": "draft",
|
|
||||||
"licence": "ODbL 1.0",
|
|
||||||
"query": "Paris",
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
2.3469,
|
|
||||||
48.8589
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"adm_weight": "6",
|
|
||||||
"citycode": "75056",
|
|
||||||
"name": "Paris",
|
|
||||||
"city": "Paris",
|
|
||||||
"postcode": "75000",
|
|
||||||
"context": "75, \u00cele-de-France",
|
|
||||||
"score": 1.0,
|
|
||||||
"label": "Paris",
|
|
||||||
"id": "75056",
|
|
||||||
"type": "city",
|
|
||||||
"population": "2244"
|
|
||||||
},
|
|
||||||
"type": "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
4.366801,
|
|
||||||
44.425528
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"citycode": "07330",
|
|
||||||
"postcode": "07150",
|
|
||||||
"name": "Paris",
|
|
||||||
"id": "07330_B095_bd3524",
|
|
||||||
"context": "07, Ard\u00e8che, Rh\u00f4ne-Alpes",
|
|
||||||
"score": 0.8291454545454544,
|
|
||||||
"label": "Paris 07150 Vallon-Pont-d'Arc",
|
|
||||||
"city": "Vallon-Pont-d'Arc",
|
|
||||||
"type": "locality"
|
|
||||||
},
|
|
||||||
"type": "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
3.564293,
|
|
||||||
45.766413
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"citycode": "63125",
|
|
||||||
"postcode": "63120",
|
|
||||||
"name": "Paris",
|
|
||||||
"city": "Courpi\u00e8re",
|
|
||||||
"context": "63, Puy-de-D\u00f4me, Auvergne",
|
|
||||||
"score": 0.8255363636363636,
|
|
||||||
"label": "Paris 63120 Courpi\u00e8re",
|
|
||||||
"id": "63125_B221_03549b",
|
|
||||||
"type": "locality"
|
|
||||||
},
|
|
||||||
"type": "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
1.550208,
|
|
||||||
44.673592
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"citycode": "46138",
|
|
||||||
"postcode": "46240",
|
|
||||||
"name": "PARIS (Vaillac)",
|
|
||||||
"city": "C\u0153ur de Causse",
|
|
||||||
"context": "46, Lot, Midi-Pyr\u00e9n\u00e9es",
|
|
||||||
"score": 0.824090909090909,
|
|
||||||
"label": "PARIS (Vaillac) 46240 C\u0153ur de Causse",
|
|
||||||
"id": "46138_XXXX_6ee4ec",
|
|
||||||
"type": "street"
|
|
||||||
},
|
|
||||||
"type": "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
-0.526884,
|
|
||||||
43.762253
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"citycode": "40282",
|
|
||||||
"postcode": "40500",
|
|
||||||
"name": "Paris",
|
|
||||||
"city": "Saint-Sever",
|
|
||||||
"context": "40, Landes, Aquitaine",
|
|
||||||
"score": 0.8236181818181818,
|
|
||||||
"label": "Paris 40500 Saint-Sever",
|
|
||||||
"id": "40282_B237_2364e3",
|
|
||||||
"type": "locality"
|
|
||||||
},
|
|
||||||
"type": "Feature"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
45
spec/lib/api_address/address_adapter_spec.rb
Normal file
45
spec/lib/api_address/address_adapter_spec.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
describe APIAddress::AddressAdapter do
|
||||||
|
let(:search_term) { 'Paris' }
|
||||||
|
let(:adapter) { described_class.new(search_term) }
|
||||||
|
subject { adapter.to_params }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Geocoder.configure(lookup: :ban_data_gouv_fr, use_https: true)
|
||||||
|
stub_request(:get, /https:\/\/api-adresse.data.gouv.fr\/search/)
|
||||||
|
.to_return(body: body, status: status)
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
Geocoder.configure(lookup: :test)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when responds with valid schema" do
|
||||||
|
let(:body) { File.read('spec/fixtures/files/api_address/address.json') }
|
||||||
|
let(:status) { 200 }
|
||||||
|
|
||||||
|
it '#to_params returns a valid' do
|
||||||
|
expect(subject).to be_an_instance_of(Hash)
|
||||||
|
expect(subject[:city_name]).to eq(search_term)
|
||||||
|
expect(subject[:city_code]).to eq('75056')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when responds with an address which is not a direct match to search term" do
|
||||||
|
let(:body) { File.read('spec/fixtures/files/api_address/address.json') }
|
||||||
|
let(:status) { 200 }
|
||||||
|
let(:search_term) { 'Lyon' }
|
||||||
|
|
||||||
|
it '#to_params ignores the response' do
|
||||||
|
expect(subject).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when responds with invalid schema" do
|
||||||
|
let(:body) { File.read('spec/fixtures/files/api_address/address_invalid.json') }
|
||||||
|
let(:status) { 200 }
|
||||||
|
|
||||||
|
it '#to_params raise exception' do
|
||||||
|
expect { subject }.to raise_exception(APIAddress::AddressAdapter::InvalidSchemaError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -522,4 +522,31 @@ describe Champ do
|
||||||
it { expect(champ.fetch_external_data_exceptions).to eq(['#<StandardError: My special exception!>']) }
|
it { expect(champ.fetch_external_data_exceptions).to eq(['#<StandardError: My special exception!>']) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "fetch_external_data" do
|
||||||
|
let(:champ) { create(:champ_text, data: 'some data') }
|
||||||
|
|
||||||
|
context "cleanup_if_empty" do
|
||||||
|
it "remove data if external_id changes" do
|
||||||
|
expect(champ.data).to_not be_nil
|
||||||
|
champ.update(external_id: 'external_id')
|
||||||
|
expect(champ.data).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "fetch_external_data_later" do
|
||||||
|
include ActiveJob::TestHelper
|
||||||
|
let(:data) { 'some other data' }
|
||||||
|
|
||||||
|
it "fill data from external source" do
|
||||||
|
expect(champ).to receive(:fetch_external_data?) { true }
|
||||||
|
expect_any_instance_of(Champs::TextChamp).to receive(:fetch_external_data) { data }
|
||||||
|
|
||||||
|
perform_enqueued_jobs do
|
||||||
|
champ.update(external_id: 'external_id')
|
||||||
|
end
|
||||||
|
expect(champ.reload.data).to eq data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
20
spec/models/champs/address_champ_spec.rb
Normal file
20
spec/models/champs/address_champ_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
describe Champs::AddressChamp do
|
||||||
|
let(:champ) { Champs::AddressChamp.new(value: value, data: data, type_de_champ: create(:type_de_champ_address)) }
|
||||||
|
let(:value) { '' }
|
||||||
|
let(:data) { nil }
|
||||||
|
|
||||||
|
context "with value but no data" do
|
||||||
|
let(:value) { 'Paris' }
|
||||||
|
|
||||||
|
it { expect(champ.address_label).to eq('Paris') }
|
||||||
|
it { expect(champ.full_address?).to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with value and data" do
|
||||||
|
let(:value) { 'Paris' }
|
||||||
|
let(:data) { { label: 'Paris' } }
|
||||||
|
|
||||||
|
it { expect(champ.address_label).to eq('Paris') }
|
||||||
|
it { expect(champ.full_address?).to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
|
@ -271,12 +271,6 @@ describe Procedure do
|
||||||
|
|
||||||
it_behaves_like 'duree de conservation'
|
it_behaves_like 'duree de conservation'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'duree de conservation hors ds' do
|
|
||||||
let(:field_name) { :duree_conservation_dossiers_hors_ds }
|
|
||||||
|
|
||||||
it_behaves_like 'duree de conservation'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'active' do
|
describe 'active' do
|
||||||
|
|
Loading…
Reference in a new issue