Merge pull request #3276 from betagouv/frederic/2180-migrate_old_type_pj_to_new_type_champ_pj
Migration PJ => champ PJ en cas de clonage de la procédure
This commit is contained in:
commit
d1c36a3945
8 changed files with 216 additions and 13 deletions
|
@ -184,10 +184,11 @@ class Procedure < ApplicationRecord
|
|||
end
|
||||
|
||||
def clone(admin, from_library)
|
||||
is_different_admin = self.administrateur_id != admin.id
|
||||
|
||||
populate_champ_stable_ids
|
||||
procedure = self.deep_clone(include:
|
||||
{
|
||||
types_de_piece_justificative: nil,
|
||||
attestation_template: nil,
|
||||
types_de_champ: :drop_down_list,
|
||||
types_de_champ_private: :drop_down_list
|
||||
|
@ -203,6 +204,11 @@ class Procedure < ApplicationRecord
|
|||
|
||||
[:notice, :deliberation].each { |attachment| clone_attachment(procedure, attachment) }
|
||||
|
||||
procedure.types_de_champ += PiecesJustificativesService.types_pj_as_types_de_champ(self)
|
||||
if is_different_admin || from_library
|
||||
procedure.types_de_champ.each { |tdc| tdc.options&.delete(:old_pj) }
|
||||
end
|
||||
|
||||
procedure.administrateur = admin
|
||||
procedure.initiated_mail = initiated_mail&.dup
|
||||
procedure.received_mail = received_mail&.dup
|
||||
|
@ -215,7 +221,7 @@ class Procedure < ApplicationRecord
|
|||
|
||||
if from_library
|
||||
procedure.service = nil
|
||||
elsif self.service.present? && (self.administrateur_id != admin.id)
|
||||
elsif self.service.present? && is_different_admin
|
||||
procedure.service = self.service.clone_and_assign_to_administrateur(admin)
|
||||
end
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
belongs_to :parent, class_name: 'TypeDeChamp'
|
||||
has_many :types_de_champ, foreign_key: :parent_id, class_name: 'TypeDeChamp', dependent: :destroy
|
||||
|
||||
store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles
|
||||
store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles, :old_pj
|
||||
|
||||
# TODO simplify after migrating `options` column to (non YAML encoded) JSON
|
||||
class MaybeYaml
|
||||
|
|
|
@ -26,7 +26,7 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
has_many :champs, serializer: ChampSerializer
|
||||
|
||||
def champs
|
||||
champs = object.champs.to_a
|
||||
champs = object.champs.reject { |c| c.type_de_champ.old_pj.present? }
|
||||
|
||||
if object.expose_legacy_carto_api?
|
||||
champ_carte = champs.find do |champ|
|
||||
|
@ -47,6 +47,16 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
[]
|
||||
end
|
||||
|
||||
def pieces_justificatives
|
||||
ActiveModelSerializers::SerializableResource.new(object.pieces_justificatives).serializable_hash +
|
||||
PiecesJustificativesService.serialize_champs_as_pjs(object)
|
||||
end
|
||||
|
||||
def types_de_piece_justificative
|
||||
ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash +
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
end
|
||||
|
||||
def email
|
||||
object.user&.email
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ class ProcedureSerializer < ActiveModel::Serializer
|
|||
has_one :geographic_information, serializer: ModuleApiCartoSerializer
|
||||
has_many :types_de_champ, serializer: TypeDeChampSerializer
|
||||
has_many :types_de_champ_private, serializer: TypeDeChampSerializer
|
||||
has_many :types_de_piece_justificative, serializer: TypeDePieceJustificativeSerializer
|
||||
has_many :types_de_piece_justificative
|
||||
|
||||
def archived_at
|
||||
object.archived_at&.in_time_zone('UTC')
|
||||
|
@ -43,4 +43,13 @@ class ProcedureSerializer < ActiveModel::Serializer
|
|||
ModuleAPICarto.new(procedure: object)
|
||||
end
|
||||
end
|
||||
|
||||
def types_de_champ
|
||||
object.types_de_champ.reject { |c| c.old_pj.present? }
|
||||
end
|
||||
|
||||
def types_de_piece_justificative
|
||||
ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash +
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,4 +31,68 @@ class PiecesJustificativesService
|
|||
|
||||
missing_pjs.map { |pj| "La pièce jointe #{pj.libelle.truncate(200)} doit être fournie." }
|
||||
end
|
||||
|
||||
def self.types_pj_as_types_de_champ(procedure)
|
||||
order_place = procedure.types_de_champ.last&.order_place || 0
|
||||
types_de_champ = [
|
||||
TypeDeChamp.new(
|
||||
libelle: "Pièces jointes",
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:header_section),
|
||||
order_place: order_place
|
||||
)
|
||||
]
|
||||
types_de_champ += procedure.types_de_piece_justificative.map do |tpj|
|
||||
order_place += 1
|
||||
description = tpj.description
|
||||
if tpj.lien_demarche.present?
|
||||
if description.present?
|
||||
description += "\n"
|
||||
end
|
||||
description += "Récupérer le formulaire vierge pour mon dossier : #{tpj.lien_demarche}"
|
||||
end
|
||||
TypeDeChamp.new(
|
||||
libelle: tpj.libelle,
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:piece_justificative),
|
||||
description: description,
|
||||
order_place: order_place,
|
||||
mandatory: tpj.mandatory,
|
||||
old_pj: {
|
||||
stable_id: tpj.id
|
||||
}
|
||||
)
|
||||
end
|
||||
if types_de_champ.count > 1
|
||||
types_de_champ
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def self.serialize_types_de_champ_as_type_pj(procedure)
|
||||
tdcs = procedure.types_de_champ.select { |type_champ| type_champ.old_pj.present? }
|
||||
tdcs.map.with_index do |type_champ, order_place|
|
||||
description = type_champ.description
|
||||
if /^(?<original_description>.*?)(?:[\r\n]+)Récupérer le formulaire vierge pour mon dossier : (?<lien_demarche>http.*)$/m =~ description
|
||||
description = original_description
|
||||
end
|
||||
{
|
||||
id: type_champ.old_pj[:stable_id],
|
||||
libelle: type_champ.libelle,
|
||||
description: description,
|
||||
order_place: order_place,
|
||||
lien_demarche: lien_demarche
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.serialize_champs_as_pjs(dossier)
|
||||
dossier.champs.select { |champ| champ.type_de_champ.old_pj }.map do |champ|
|
||||
{
|
||||
created_at: champ.created_at&.in_time_zone('UTC'),
|
||||
type_de_piece_justificative_id: champ.type_de_champ.old_pj[:stable_id],
|
||||
content_url: champ.for_api,
|
||||
user: champ.dossier.user
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -336,6 +336,7 @@ describe Procedure do
|
|||
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
|
||||
let!(:type_de_champ_2) { create(:type_de_champ_drop_down_list, procedure: procedure, order_place: 2) }
|
||||
let!(:type_de_champ_pj) { create(:type_de_champ_piece_justificative, procedure: procedure, order_place: 3, old_pj: { stable_id: 2713 }) }
|
||||
let!(:type_de_champ_private_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
|
||||
let!(:type_de_champ_private_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
|
||||
let!(:type_de_champ_private_2) { create(:type_de_champ_drop_down_list, :private, procedure: procedure, order_place: 2) }
|
||||
|
@ -366,13 +367,12 @@ describe Procedure do
|
|||
it 'should duplicate specific objects with different id' do
|
||||
expect(subject.id).not_to eq(procedure.id)
|
||||
|
||||
expect(subject.types_de_piece_justificative.size).to eq procedure.types_de_piece_justificative.size
|
||||
expect(subject.types_de_champ.size).to eq procedure.types_de_champ.size
|
||||
expect(subject.types_de_champ.size).to eq(procedure.types_de_champ.size + 1 + procedure.types_de_piece_justificative.size)
|
||||
expect(subject.types_de_champ_private.size).to eq procedure.types_de_champ_private.size
|
||||
expect(subject.types_de_champ.map(&:drop_down_list).compact.size).to eq procedure.types_de_champ.map(&:drop_down_list).compact.size
|
||||
expect(subject.types_de_champ_private.map(&:drop_down_list).compact.size).to eq procedure.types_de_champ_private.map(&:drop_down_list).compact.size
|
||||
|
||||
subject.types_de_champ.zip(procedure.types_de_champ).each do |stc, ptc|
|
||||
procedure.types_de_champ.zip(subject.types_de_champ).each do |ptc, stc|
|
||||
expect(stc).to have_same_attributes_as(ptc)
|
||||
end
|
||||
|
||||
|
@ -380,10 +380,6 @@ describe Procedure do
|
|||
expect(stc).to have_same_attributes_as(ptc)
|
||||
end
|
||||
|
||||
subject.types_de_piece_justificative.zip(procedure.types_de_piece_justificative).each do |stc, ptc|
|
||||
expect(stc).to have_same_attributes_as(ptc)
|
||||
end
|
||||
|
||||
expect(subject.attestation_template.title).to eq(procedure.attestation_template.title)
|
||||
|
||||
expect(subject.cloned_from_library).to be(false)
|
||||
|
@ -393,7 +389,19 @@ describe Procedure do
|
|||
expect(cloned_procedure).to have_same_attributes_as(procedure)
|
||||
end
|
||||
|
||||
context 'when the procedure is clone from the library' do
|
||||
it 'should not clone piece justificatives but create corresponding champs' do
|
||||
expect(subject.types_de_piece_justificative.size).to eq(0)
|
||||
|
||||
champs_pj = subject.types_de_champ[procedure.types_de_champ.size + 1, procedure.types_de_piece_justificative.size]
|
||||
champs_pj.zip(procedure.types_de_piece_justificative).each do |stc, ptpj|
|
||||
expect(stc.libelle).to eq(ptpj.libelle)
|
||||
expect(stc.description).to eq(ptpj.description)
|
||||
expect(stc.mandatory).to eq(ptpj.mandatory)
|
||||
expect(stc.old_pj[:stable_id]).to eq(ptpj.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the procedure is cloned from the library' do
|
||||
let(:from_library) { true }
|
||||
|
||||
it { expect(subject.cloned_from_library).to be(true) }
|
||||
|
@ -401,6 +409,12 @@ describe Procedure do
|
|||
it 'should set service_id to nil' do
|
||||
expect(subject.service).to eq(nil)
|
||||
end
|
||||
|
||||
it 'should discard old pj information' do
|
||||
subject.types_de_champ.each do |stc|
|
||||
expect(stc.old_pj).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'should keep service_id' do
|
||||
|
@ -415,6 +429,12 @@ describe Procedure do
|
|||
expect(subject.service.administrateur_id).not_to eq(service.administrateur_id)
|
||||
expect(subject.service.attributes.except("id", "administrateur_id", "created_at", "updated_at")).to eq(service.attributes.except("id", "administrateur_id", "created_at", "updated_at"))
|
||||
end
|
||||
|
||||
it 'should discard old pj information' do
|
||||
subject.types_de_champ.each do |stc|
|
||||
expect(stc.old_pj).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'should duplicate existing mail_templates' do
|
||||
|
|
|
@ -45,4 +45,60 @@ describe DossierSerializer do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a type PJ was cloned to a type champ PJ' do
|
||||
let(:original_procedure) do
|
||||
p = create(:procedure, :published)
|
||||
p.types_de_piece_justificative.create(
|
||||
libelle: "Vidéo de votre demande de subvention",
|
||||
description: "Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique",
|
||||
lien_demarche: "https://www.dance-academy.gouv.fr",
|
||||
order_place: 0
|
||||
)
|
||||
p
|
||||
end
|
||||
|
||||
let(:procedure) do
|
||||
p = original_procedure.clone(original_procedure.administrateur, false)
|
||||
p.save
|
||||
p
|
||||
end
|
||||
|
||||
let(:type_pj) { original_procedure.types_de_piece_justificative.first }
|
||||
let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:champ_pj) { dossier.champs.last }
|
||||
|
||||
before do
|
||||
champ_pj.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||
end
|
||||
|
||||
subject { DossierSerializer.new(dossier).serializable_hash }
|
||||
|
||||
it "exposes the PJ in the legacy format" do
|
||||
is_expected.to include(
|
||||
types_de_piece_justificative: [
|
||||
{
|
||||
"id" => type_pj.id,
|
||||
"libelle" => type_pj.libelle,
|
||||
"description" => type_pj.description,
|
||||
"lien_demarche" => type_pj.lien_demarche,
|
||||
"order_place" => type_pj.order_place
|
||||
}
|
||||
],
|
||||
pieces_justificatives: [
|
||||
{
|
||||
"content_url" => champ_pj.for_api,
|
||||
"created_at" => champ_pj.created_at.in_time_zone('UTC').iso8601(3),
|
||||
"type_de_piece_justificative_id" => type_pj.id,
|
||||
"user" => a_hash_including("id" => dossier.user.id)
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it "does not expose the PJ as a champ" do
|
||||
expect(subject[:champs]).not_to include(a_hash_including(type_de_champ: a_hash_including(id: migrated_type_champ.id)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,4 +8,42 @@ describe ProcedureSerializer do
|
|||
is_expected.to include(state: "publiee")
|
||||
}
|
||||
end
|
||||
|
||||
context 'when a type PJ was cloned to a type champ PJ' do
|
||||
let(:original_procedure) do
|
||||
p = create(:procedure, :published)
|
||||
p.types_de_piece_justificative.create(
|
||||
libelle: "Vidéo de votre demande de subvention",
|
||||
description: "Pour optimiser vos chances, soignez la chorégraphie et privilégiez le chant polyphonique",
|
||||
lien_demarche: "https://www.dance-academy.gouv.fr",
|
||||
order_place: 0
|
||||
)
|
||||
p
|
||||
end
|
||||
|
||||
let(:procedure) { original_procedure.clone(original_procedure.administrateur, false) }
|
||||
|
||||
let(:type_pj) { original_procedure.types_de_piece_justificative.first }
|
||||
let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }
|
||||
|
||||
subject { ProcedureSerializer.new(procedure).serializable_hash }
|
||||
|
||||
it "is exposed as a legacy type PJ" do
|
||||
is_expected.to include(
|
||||
types_de_piece_justificative: [
|
||||
{
|
||||
"id" => type_pj.id,
|
||||
"libelle" => type_pj.libelle,
|
||||
"description" => type_pj.description,
|
||||
"lien_demarche" => type_pj.lien_demarche,
|
||||
"order_place" => type_pj.order_place
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it "is not exposed as a type de champ" do
|
||||
expect(subject[:types_de_champ]).not_to include(a_hash_including(libelle: type_pj.libelle))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue