From 651fd6149b3ba8887b8feff650929162b64ee824 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 13:34:47 +0100 Subject: [PATCH 01/10] [#2180] When cloning a procedure, convert types de PJ to types de champ PJ --- app/models/procedure.rb | 2 +- app/services/pieces_justificatives_service.rb | 33 +++++++++++++++++++ spec/models/procedure_spec.rb | 22 ++++++++----- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index b79d5ad47..05d1b33a8 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -187,7 +187,6 @@ class Procedure < ApplicationRecord 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 +202,7 @@ class Procedure < ApplicationRecord [:notice, :deliberation].each { |attachment| clone_attachment(procedure, attachment) } + procedure.types_de_champ += PiecesJustificativesService.types_pj_as_types_de_champ(self) procedure.administrateur = admin procedure.initiated_mail = initiated_mail&.dup procedure.received_mail = received_mail&.dup diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 6a68a0d7b..806f76caf 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -31,4 +31,37 @@ 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 + ) + end + if types_de_champ.count > 1 + types_de_champ + else + [] + end + end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 1e8d964cb..5fa6f8943 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -366,13 +366,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 +379,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 +388,18 @@ 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) + 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) } From 8cb509d192bfc2d238c67e02ed69c61ad88c1f40 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 13:36:38 +0100 Subject: [PATCH 02/10] [#2180] Keep information that this type de champ was cloned from a PJ --- app/models/type_de_champ.rb | 2 +- app/services/pieces_justificatives_service.rb | 5 ++++- spec/models/procedure_spec.rb | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 15927cf96..c801a4a56 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -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 diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 806f76caf..8a199a011 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -55,7 +55,10 @@ class PiecesJustificativesService type_champ: TypeDeChamp.type_champs.fetch(:piece_justificative), description: description, order_place: order_place, - mandatory: tpj.mandatory + mandatory: tpj.mandatory, + old_pj: { + stable_id: tpj.id + } ) end if types_de_champ.count > 1 diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 5fa6f8943..afbd0fee6 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -396,6 +396,7 @@ describe Procedure do 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 From 530079e4642cd155604abf3d52cdc632c5cdf766 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 13:37:40 +0100 Subject: [PATCH 03/10] [#2180] Make sure that old_pj is kept through successive clones --- spec/models/procedure_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index afbd0fee6..4cafa657a 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -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) } From d1c0418c89cef518b89d86a1fc484a6e40ce0805 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 13:42:40 +0100 Subject: [PATCH 04/10] [#2180] Refactor - extract local variable --- app/models/procedure.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 05d1b33a8..ec972a7c0 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -184,6 +184,8 @@ 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: { @@ -215,7 +217,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 From 5e37400834a6a282ed34a4b0bf365750b2db0863 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 13:43:22 +0100 Subject: [PATCH 05/10] [#2180] Only retain old PJ API when cloning for same admin --- app/models/procedure.rb | 4 ++++ spec/models/procedure_spec.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index ec972a7c0..276a90183 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -205,6 +205,10 @@ 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 diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 4cafa657a..f69fffc46 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -409,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 @@ -423,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 From 082255b4896c210be221c3296f4f93d294b77dc2 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 15:55:04 +0100 Subject: [PATCH 06/10] [#2180] Expose migrated types PJ as legacy types PJ in procedure API --- app/serializers/procedure_serializer.rb | 7 +++- app/services/pieces_justificatives_service.rb | 17 ++++++++++ spec/serializers/procedure_serializer_spec.rb | 34 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/serializers/procedure_serializer.rb b/app/serializers/procedure_serializer.rb index 674d8ab91..d219ee4b6 100644 --- a/app/serializers/procedure_serializer.rb +++ b/app/serializers/procedure_serializer.rb @@ -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,9 @@ class ProcedureSerializer < ActiveModel::Serializer ModuleAPICarto.new(procedure: object) end 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 diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 8a199a011..d0bc69815 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -67,4 +67,21 @@ class PiecesJustificativesService [] 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 /^(?.*?)(?:[\r\n]+)Récupérer le formulaire vierge pour mon dossier : (?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 end diff --git a/spec/serializers/procedure_serializer_spec.rb b/spec/serializers/procedure_serializer_spec.rb index 487866665..fa5bd006b 100644 --- a/spec/serializers/procedure_serializer_spec.rb +++ b/spec/serializers/procedure_serializer_spec.rb @@ -8,4 +8,38 @@ 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 + end end From 5f74ae1545ea646f2c5ae0982fdf17088587a506 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 15:56:18 +0100 Subject: [PATCH 07/10] [#2180] Do not double-expose migrated type PJs as champs in procedure API --- app/serializers/procedure_serializer.rb | 4 ++++ spec/serializers/procedure_serializer_spec.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/app/serializers/procedure_serializer.rb b/app/serializers/procedure_serializer.rb index d219ee4b6..3d259bfc8 100644 --- a/app/serializers/procedure_serializer.rb +++ b/app/serializers/procedure_serializer.rb @@ -44,6 +44,10 @@ class ProcedureSerializer < ActiveModel::Serializer 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) diff --git a/spec/serializers/procedure_serializer_spec.rb b/spec/serializers/procedure_serializer_spec.rb index fa5bd006b..be0d83669 100644 --- a/spec/serializers/procedure_serializer_spec.rb +++ b/spec/serializers/procedure_serializer_spec.rb @@ -41,5 +41,9 @@ describe ProcedureSerializer do ] ) 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 From bf23abdfe3c8ed148d2ea1ba61d2188524caf73d Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 18:46:19 +0100 Subject: [PATCH 08/10] [#2180] Expose migrated PJs as legacy PJs in dossier API --- app/serializers/dossier_serializer.rb | 5 +++ app/services/pieces_justificatives_service.rb | 11 +++++ spec/serializers/dossier_serializer_spec.rb | 42 +++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index 869aa727c..dbc3f90e9 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -47,6 +47,11 @@ class DossierSerializer < ActiveModel::Serializer [] end + def pieces_justificatives + ActiveModelSerializers::SerializableResource.new(object.pieces_justificatives).serializable_hash + + PiecesJustificativesService.serialize_champs_as_pjs(object) + end + def email object.user&.email end diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index d0bc69815..dd617b28b 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -84,4 +84,15 @@ class PiecesJustificativesService } 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 diff --git a/spec/serializers/dossier_serializer_spec.rb b/spec/serializers/dossier_serializer_spec.rb index aa9e51a28..730ea1e56 100644 --- a/spec/serializers/dossier_serializer_spec.rb +++ b/spec/serializers/dossier_serializer_spec.rb @@ -45,4 +45,46 @@ 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(: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( + 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 + end end From 63519f04c8c2f3c3672af649f80736de52ee7600 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 18:49:21 +0100 Subject: [PATCH 09/10] [#2180] Expose migrated types PJ as legacy type PJ in dossier API --- app/serializers/dossier_serializer.rb | 5 +++++ spec/serializers/dossier_serializer_spec.rb | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index dbc3f90e9..3737fd608 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -52,6 +52,11 @@ class DossierSerializer < ActiveModel::Serializer 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 diff --git a/spec/serializers/dossier_serializer_spec.rb b/spec/serializers/dossier_serializer_spec.rb index 730ea1e56..ac9b7bc97 100644 --- a/spec/serializers/dossier_serializer_spec.rb +++ b/spec/serializers/dossier_serializer_spec.rb @@ -65,6 +65,7 @@ describe DossierSerializer do 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 } @@ -76,6 +77,15 @@ describe DossierSerializer do 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, From d01270eff251dd5f1a27c9b08cabf2d46679b234 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Thu, 10 Jan 2019 18:50:12 +0100 Subject: [PATCH 10/10] [#2180] Do not double-expose migrated type PJ as type champ PJ --- app/serializers/dossier_serializer.rb | 2 +- spec/serializers/dossier_serializer_spec.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index 3737fd608..536c418cf 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -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| diff --git a/spec/serializers/dossier_serializer_spec.rb b/spec/serializers/dossier_serializer_spec.rb index ac9b7bc97..681baeab7 100644 --- a/spec/serializers/dossier_serializer_spec.rb +++ b/spec/serializers/dossier_serializer_spec.rb @@ -96,5 +96,9 @@ describe DossierSerializer do ] ) 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