diff --git a/app/jobs/clone_piece_justificative_job.rb b/app/jobs/clone_piece_justificative_job.rb deleted file mode 100644 index dea65e962..000000000 --- a/app/jobs/clone_piece_justificative_job.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ClonePieceJustificativeJob < ApplicationJob - def perform(from_champ, kopy_champ) - from_champ.clone_piece_justificative(kopy_champ) - end -end diff --git a/app/models/champ.rb b/app/models/champ.rb index 55c50c728..7db57ccb9 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -30,7 +30,6 @@ class Champ < ApplicationRecord has_many :geo_areas, -> { order(:created_at) }, dependent: :destroy, inverse_of: :champ belongs_to :etablissement, optional: true, dependent: :destroy has_many :champs, -> { ordered }, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy - after_create_commit :after_clone, if: :cloned? delegate :libelle, :type_champ, @@ -217,26 +216,20 @@ class Champ < ApplicationRecord end def clone(dossier:, parent: nil) - kopy = deep_clone(only: [:data, :private, :row, :type, :value, :value_json, :external_id, :type_de_champ_id]) + kopy = deep_clone(only: (private? ? [] : [:value, :value_json]) + [:data, :private, :row, :type, :external_id, :type_de_champ_id], + include: private? ? [] : [:etablissement, :geo_areas]) kopy.dossier = dossier kopy.parent = parent if parent - kopy - end - - def mark_for_delayed_clone_piece_justificative(from) - if from.piece_justificative_file.attached? - @cloned_from = from - @cloned_kopy = self + case self + when Champs::RepetitionChamp + kopy.champs = (private? ? champs.where(row: 0) : champs).map do |champ_de_repetition| + champ_de_repetition.clone(dossier: dossier, parent: kopy) + end + when Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp + PiecesJustificativesService.clone_attachments(self, kopy) if !private? && piece_justificative_file.attached? end - end - - def after_clone - ClonePieceJustificativeJob.perform_later(@cloned_from, @cloned_kopy) - end - - def cloned? - @cloned_from && @cloned_kopy + kopy end def clone_piece_justificative(kopy) diff --git a/app/models/champs/carte_champ.rb b/app/models/champs/carte_champ.rb index d0c83a519..4f351ba0e 100644 --- a/app/models/champs/carte_champ.rb +++ b/app/models/champs/carte_champ.rb @@ -115,13 +115,6 @@ class Champs::CarteChamp < Champ geo_areas.blank? end - def clone(dossier:, parent: nil) - kopy = super(dossier: dossier, parent: parent) - - kopy.geo_areas = geo_areas.map(&:dup) - kopy - end - private def selection_utilisateur_legacy_geometry diff --git a/app/models/champs/piece_justificative_champ.rb b/app/models/champs/piece_justificative_champ.rb index 8d9432d45..dfde878e5 100644 --- a/app/models/champs/piece_justificative_champ.rb +++ b/app/models/champs/piece_justificative_champ.rb @@ -51,11 +51,4 @@ class Champs::PieceJustificativeChamp < Champ piece_justificative_file.service_url end end - - def clone(dossier:, parent: nil) - kopy = super(dossier: dossier, parent: parent) - - kopy.mark_for_delayed_clone_piece_justificative(self) - kopy - end end diff --git a/app/models/champs/repetition_champ.rb b/app/models/champs/repetition_champ.rb index 62772608f..83d691ffc 100644 --- a/app/models/champs/repetition_champ.rb +++ b/app/models/champs/repetition_champ.rb @@ -77,13 +77,4 @@ class Champs::RepetitionChamp < Champ ] + Dossier.champs_for_export(champs, types_de_champ) end end - - def clone(dossier:, parent: nil) - kopy = super(dossier: dossier, parent: parent) - - kopy.champs = champs.map do |champ_de_repetition| - champ_de_repetition.clone(dossier: dossier, parent: kopy) - end - kopy - end end diff --git a/app/models/champs/siret_champ.rb b/app/models/champs/siret_champ.rb index 0c87e603c..2b527caf9 100644 --- a/app/models/champs/siret_champ.rb +++ b/app/models/champs/siret_champ.rb @@ -27,11 +27,4 @@ class Champs::SiretChamp < Champ def mandatory_blank? mandatory? && Siret.new(siret: value).invalid? end - - def clone(dossier:, parent: nil) - kopy = super(dossier: dossier, parent: parent) - - kopy.etablissement = etablissement.dup - kopy - end end diff --git a/app/models/champs/titre_identite_champ.rb b/app/models/champs/titre_identite_champ.rb index 2fa4284b2..e93da6768 100644 --- a/app/models/champs/titre_identite_champ.rb +++ b/app/models/champs/titre_identite_champ.rb @@ -43,11 +43,4 @@ class Champs::TitreIdentiteChamp < Champ def for_api nil end - - def clone(dossier:, parent: nil) - kopy = super(dossier: dossier, parent: parent) - - mark_for_delayed_clone_piece_justificative(kopy) - kopy - end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 63728f4f8..7599582e4 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -131,6 +131,8 @@ class Dossier < ApplicationRecord belongs_to :groupe_instructeur, optional: true belongs_to :revision, class_name: 'ProcedureRevision', optional: false belongs_to :user, optional: true + belongs_to :parent_dossier, class_name: 'Dossier', optional: true + has_one :france_connect_information, through: :user has_one :attestation_template, through: :revision @@ -140,6 +142,7 @@ class Dossier < ApplicationRecord belongs_to :transfer, class_name: 'DossierTransfer', foreign_key: 'dossier_transfer_id', optional: true, inverse_of: :dossiers has_many :transfer_logs, class_name: 'DossierTransferLog', dependent: :destroy + has_many :parent_dossiers, class_name: 'Dossier', foreign_key: 'parent_dossier_id', dependent: :nullify, inverse_of: :parent_dossier accepts_nested_attributes_for :champs_public accepts_nested_attributes_for :champs_private @@ -475,7 +478,7 @@ class Dossier < ApplicationRecord end def build_default_champs_for_new_dossier - revision.build_champs.each do |champ| + revision.build_champs_public.each do |champ| champs_public << champ end revision.build_champs_private.each do |champ| @@ -1179,14 +1182,20 @@ class Dossier < ApplicationRecord @sections[champ.parent || (champ.public? ? :public : :private)] end + # while cloning we do not have champ.id. it comes after transaction + # so we collect a list of jobs to process. then enqueue this list def clone cloned_dossier = deep_clone(only: [:autorisation_donnees, :user_id, :revision_id, :groupe_instructeur_id], include: [:individual, :etablissement]) do |original, kopy| if original.is_a?(Dossier) kopy.parent_dossier_id = original.id kopy.state = Dossier.states.fetch(:brouillon) - kopy.champs = original.champs.map { |champ| champ.clone(dossier: kopy) } - kopy.champs_private = kopy.revision.types_de_champ_private.map { |tdc| tdc.build_champ(revision: kopy.revision, dossier: kopy) } + kopy.champs_public = original.champs_public.map do |champ| + champ.clone(dossier: kopy) + end + kopy.champs_private = original.champs_private.map do |champ| + champ.clone(dossier: kopy) + end end end diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 7cdeec88b..1d2cd9950 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -57,6 +57,8 @@ class PiecesJustificativesService def self.clone_attachments(original, kopy) case original + when Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp + clone_attachment(original.piece_justificative_file, kopy.piece_justificative_file) when TypeDeChamp clone_attachment(original.piece_justificative_template, kopy.piece_justificative_template) when Procedure diff --git a/config/locales/en.yml b/config/locales/en.yml index 27a5ff473..b649bb403 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -249,8 +249,8 @@ en: actions: "Actions" dossier_action: edit_dossier: "Edit the file" - start_other_dossier: "Start an other empty file" - clone: "Clone this file" + start_other_dossier: "Start another empty file" + clone: "Duplicate the file" delete_dossier: "Delete the file" transfer_dossier: "Transfer the file" edit_draft: "Edit the draft" diff --git a/db/migrate/20221110100622_add_foreign_key_to_parent_dossier_id.rb b/db/migrate/20221110100622_add_foreign_key_to_parent_dossier_id.rb new file mode 100644 index 000000000..437f56e02 --- /dev/null +++ b/db/migrate/20221110100622_add_foreign_key_to_parent_dossier_id.rb @@ -0,0 +1,5 @@ +class AddForeignKeyToParentDossierId < ActiveRecord::Migration[6.1] + def change + add_foreign_key "dossiers", "dossiers", column: "parent_dossier_id", validate: false + end +end diff --git a/db/migrate/20221110100759_validate_foreign_key_to_parent_dossier_id.rb b/db/migrate/20221110100759_validate_foreign_key_to_parent_dossier_id.rb new file mode 100644 index 000000000..6452bb1b5 --- /dev/null +++ b/db/migrate/20221110100759_validate_foreign_key_to_parent_dossier_id.rb @@ -0,0 +1,5 @@ +class ValidateForeignKeyToParentDossierId < ActiveRecord::Migration[6.1] + def change + validate_foreign_key "dossiers", "dossiers" + end +end diff --git a/db/schema.rb b/db/schema.rb index 6b3020205..d0fdead37 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,8 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. +ActiveRecord::Schema.define(version: 2022_11_10_100759) do -ActiveRecord::Schema.define(version: 2022_11_07_163131) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -921,6 +921,7 @@ ActiveRecord::Schema.define(version: 2022_11_07_163131) do add_foreign_key "dossier_operation_logs", "bill_signatures" add_foreign_key "dossier_transfer_logs", "dossiers" add_foreign_key "dossiers", "dossier_transfers" + add_foreign_key "dossiers", "dossiers", column: "parent_dossier_id" add_foreign_key "dossiers", "groupe_instructeurs" add_foreign_key "dossiers", "procedure_revisions", column: "revision_id" add_foreign_key "dossiers", "users" diff --git a/spec/jobs/clone_piece_justificative_job_spec.rb b/spec/jobs/clone_piece_justificative_job_spec.rb deleted file mode 100644 index e1a2bfa5e..000000000 --- a/spec/jobs/clone_piece_justificative_job_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -describe ClonePieceJustificativeJob, type: :job do - describe 'perform' do - let(:dossier_from) { create(:dossier) } - let(:dossier_to) { create(:dossier, procedure: dossier_from.procedure) } - let(:champ_piece_justificative_from) { create(:champ, :with_piece_justificative_file, dossier_id: dossier_from.id) } - let(:champ_piece_justificative_to) { create(:champ_without_piece_justificative, dossier_id: dossier_to.id, piece_justificative_file: nil) } - - it 'creates a piece_justificative_file' do - expect { - ClonePieceJustificativeJob.perform_now(champ_piece_justificative_from, champ_piece_justificative_to) - }.to change { champ_piece_justificative_to.piece_justificative_file.blob }.from(nil).to an_instance_of(ActiveStorage::Blob) - end - it 'creates a piece_justificative_file' do - ClonePieceJustificativeJob.perform_now(champ_piece_justificative_from, champ_piece_justificative_to) - expect(champ_piece_justificative_to.piece_justificative_file.blob.download) - .to eq(champ_piece_justificative_from.piece_justificative_file.blob.download) - end - end -end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 52c546b71..72c57f467 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -1732,14 +1732,15 @@ describe Dossier do let(:dossier) { create(:dossier, :accepte) } it { expect(new_dossier.brouillon?).to eq(true) } - it { expect(new_dossier.parent_dossier_id).to eq(dossier.id) } - end + it { expect(new_dossier.parent_dossier).to eq(dossier) } - context 'links type_de_champ' do - it { expect(new_dossier.types_de_champ.count).to eq(1) } - it { expect(new_dossier.types_de_champ).to eq(dossier.types_de_champ) } - it { expect(new_dossier.types_de_champ_private.count).to eq(1) } - it { expect(new_dossier.types_de_champ_private).to eq(dossier.types_de_champ_private) } + context 'destroy parent' do + before { new_dossier } + + it 'clean fk' do + expect { dossier.destroy }.to change { new_dossier.reload.parent_dossier_id }.from(dossier.id).to(nil) + end + end end context 'procedure with_individual' do @@ -1754,55 +1755,25 @@ describe Dossier do it { expect(new_dossier.etablissement.id).not_to eq(dossier.etablissement.id) } end - describe 'skips commentaires' do - let(:dossier) { create(:dossier, :with_commentaires) } - - it { expect(new_dossier.commentaires.count).not_to eq(dossier.commentaires.count) } - it { expect(new_dossier.commentaires.size).to eq(0) } - end - - describe 'skips invites' do - let(:dossier) { create(:dossier, :with_invites) } - - it { expect(new_dossier.invites.count).not_to eq(dossier.invites.count) } - it { expect(new_dossier.invites.count).to eq(0) } - end - - describe 'skips avis' do - let(:dossier) { create(:dossier, :with_avis) } - - it { expect(new_dossier.avis.count).not_to eq(dossier.avis.count) } - it { expect(new_dossier.avis.count).to eq(0) } - it { expect(new_dossier.experts.count).not_to eq(dossier.experts.count) } - it { expect(new_dossier.experts.count).to eq(0) } - end - - describe 'skips dossier_operation_logs' do - let(:dossier) { create(:dossier, :accepte, :with_dossier_operation_logs) } - - it { expect(new_dossier.dossier_operation_logs.count).not_to eq(dossier.dossier_operation_logs.count) } - it { expect(new_dossier.dossier_operation_logs.count).to eq(0) } - end - describe 'champs' do it { expect(new_dossier.id).not_to eq(dossier.id) } context 'public are duplicated' do - it { expect(new_dossier.champs.count).to eq(dossier.champs.count) } - it { expect(new_dossier.champs.ids).not_to eq(dossier.champs.ids) } + it { expect(new_dossier.champs_public.count).to eq(dossier.champs_public.count) } + it { expect(new_dossier.champs_public.ids).not_to eq(dossier.champs_public.ids) } it 'keeps champs.values' do - original_first_champ = dossier.champs.first + original_first_champ = dossier.champs_public.first original_first_champ.update!(value: 'kthxbye') - expect(new_dossier.champs.first.value).to eq(original_first_champ.value) + expect(new_dossier.champs_public.first.value).to eq(original_first_champ.value) end context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do let(:dossier) { create(:dossier) } let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: dossier.procedure) } let(:champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) } - before { dossier.champs << champ_repetition } + before { dossier.champs_public << champ_repetition } it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(4) } it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.ids).not_to eq(champ_repetition.champs.ids) } @@ -1813,7 +1784,7 @@ describe Dossier do let(:type_de_champ_carte) { create(:type_de_champ_carte, procedure: dossier.procedure) } let(:geo_area) { create(:geo_area, :selection_utilisateur, :polygon) } let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) } - before { dossier.champs << champ_carte } + before { dossier.champs_public << champ_carte } it { expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.count).to eq(1) } it { expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.ids).not_to eq(champ_carte.geo_areas.ids) } @@ -1824,7 +1795,7 @@ describe Dossier do let(:type_de_champs_siret) { create(:type_de_champ_siret, procedure: dossier.procedure) } let(:etablissement) { create(:etablissement) } let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champs_siret, etablissement: create(:etablissement)) } - before { dossier.champs << champ_siret } + before { dossier.champs_public << champ_siret } it { expect(Champs::SiretChamp.where(dossier: dossier).first.etablissement).not_to be_nil } it { expect(Champs::SiretChamp.where(dossier: new_dossier).first.etablissement.id).not_to eq(champ_siret.etablissement.id) } @@ -1833,9 +1804,8 @@ describe Dossier do context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do let(:dossier) { create(:dossier) } let(:champ_piece_justificative) { create(:champ_piece_justificative, dossier_id: dossier.id) } - before { dossier.champs << champ_piece_justificative } - it { expect(Champs::PieceJustificativeChamp.where(dossier: new_dossier).first.piece_justificative_file.blob).to be_nil } - it { expect { new_dossier }.to have_enqueued_job(ClonePieceJustificativeJob) } + before { dossier.champs_public << champ_piece_justificative } + it { expect(Champs::PieceJustificativeChamp.where(dossier: new_dossier).first.piece_justificative_file.blob).to eq(champ_piece_justificative.piece_justificative_file.blob) } end end