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:
Frederic Merizen 2019-01-17 17:52:16 +01:00 committed by GitHub
commit d1c36a3945
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 216 additions and 13 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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