feat(dossier): dossier can be forked for editing
This commit is contained in:
parent
5a3cbcdb72
commit
025bd5beaf
7 changed files with 317 additions and 35 deletions
7
app/jobs/destroy_record_later_job.rb
Normal file
7
app/jobs/destroy_record_later_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class DestroyRecordLaterJob < ApplicationJob
|
||||||
|
discard_on ActiveRecord::RecordNotFound
|
||||||
|
|
||||||
|
def perform(record)
|
||||||
|
record.destroy
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,7 @@
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# data :jsonb
|
# data :jsonb
|
||||||
# fetch_external_data_exceptions :string is an Array
|
# fetch_external_data_exceptions :string is an Array
|
||||||
# prefilled :boolean default(FALSE)
|
# prefilled :boolean
|
||||||
# private :boolean default(FALSE), not null
|
# private :boolean default(FALSE), not null
|
||||||
# rebased_at :datetime
|
# rebased_at :datetime
|
||||||
# type :string
|
# type :string
|
||||||
|
@ -111,6 +111,10 @@ class Champ < ApplicationRecord
|
||||||
parent_id.present?
|
parent_id.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stable_id_with_row
|
||||||
|
[row_id, stable_id].compact
|
||||||
|
end
|
||||||
|
|
||||||
def sections
|
def sections
|
||||||
@sections ||= dossier.sections_for(self)
|
@sections ||= dossier.sections_for(self)
|
||||||
end
|
end
|
||||||
|
@ -226,10 +230,10 @@ class Champ < ApplicationRecord
|
||||||
update!(data: data)
|
update!(data: data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone
|
def clone(fork = false)
|
||||||
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id]
|
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id]
|
||||||
value_attributes = private? ? [] : [:value, :value_json, :data, :external_id]
|
value_attributes = fork || !private? ? [:value, :value_json, :data, :external_id] : []
|
||||||
relationships = private? ? [] : [:etablissement, :geo_areas]
|
relationships = fork || !private? ? [:etablissement, :geo_areas] : []
|
||||||
|
|
||||||
deep_clone(only: champ_attributes + value_attributes, include: relationships) do |original, kopy|
|
deep_clone(only: champ_attributes + value_attributes, include: relationships) do |original, kopy|
|
||||||
PiecesJustificativesService.clone_attachments(original, kopy)
|
PiecesJustificativesService.clone_attachments(original, kopy)
|
||||||
|
|
169
app/models/concerns/dossier_clone_concern.rb
Normal file
169
app/models/concerns/dossier_clone_concern.rb
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
module DossierCloneConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
belongs_to :parent_dossier, class_name: 'Dossier', optional: true
|
||||||
|
has_many :cloned_dossiers, class_name: 'Dossier', foreign_key: :parent_dossier_id, dependent: :nullify, inverse_of: :parent_dossier
|
||||||
|
|
||||||
|
belongs_to :editing_fork_origin, class_name: 'Dossier', optional: true
|
||||||
|
has_many :editing_forks, class_name: 'Dossier', foreign_key: :editing_fork_origin_id, dependent: :destroy, inverse_of: :editing_fork_origin
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_create_editing_fork(user)
|
||||||
|
find_editing_fork(user) || clone(user:, fork: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_editing_fork(user)
|
||||||
|
editing_forks.find_by(user:)&.tap(&:rebase!)
|
||||||
|
end
|
||||||
|
|
||||||
|
def owner_editing_fork
|
||||||
|
find_or_create_editing_fork(user).tap { DossierPreloader.load_one(_1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_editing_fork!
|
||||||
|
if editing_fork? && forked_with_changes?
|
||||||
|
destroy_editing_fork!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_editing_fork!
|
||||||
|
if editing_fork?
|
||||||
|
DestroyRecordLaterJob.perform_later(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def editing_fork?
|
||||||
|
editing_fork_origin_id.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_diff(editing_fork)
|
||||||
|
origin_champs_index = champs_public_all.index_by(&:stable_id_with_row)
|
||||||
|
forked_champs_index = editing_fork.champs_public_all.index_by(&:stable_id_with_row)
|
||||||
|
updated_champs_index = editing_fork
|
||||||
|
.champs_public_all
|
||||||
|
.filter { _1.updated_at > editing_fork.created_at }
|
||||||
|
.index_by(&:stable_id_with_row)
|
||||||
|
|
||||||
|
added = forked_champs_index.keys - origin_champs_index.keys
|
||||||
|
removed = origin_champs_index.keys - forked_champs_index.keys
|
||||||
|
updated = updated_champs_index.keys - added
|
||||||
|
|
||||||
|
{
|
||||||
|
added: added.map { forked_champs_index[_1] },
|
||||||
|
updated: updated.map { forked_champs_index[_1] },
|
||||||
|
removed: removed.map { origin_champs_index[_1] }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_fork(editing_fork)
|
||||||
|
return false if invalid? || editing_fork.invalid?
|
||||||
|
return false if revision_id > editing_fork.revision_id
|
||||||
|
|
||||||
|
diff = make_diff(editing_fork)
|
||||||
|
|
||||||
|
transaction do
|
||||||
|
apply_diff(diff)
|
||||||
|
update(revision_id: editing_fork.revision_id, last_champ_updated_at: Time.zone.now)
|
||||||
|
assign_to_groupe_instructeur(editing_fork.groupe_instructeur)
|
||||||
|
end
|
||||||
|
reload
|
||||||
|
editing_fork.destroy_editing_fork!
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone(user: nil, fork: false)
|
||||||
|
dossier_attributes = [:autorisation_donnees, :revision_id, :groupe_instructeur_id]
|
||||||
|
relationships = [:individual, :etablissement]
|
||||||
|
|
||||||
|
cloned_champs = champs
|
||||||
|
.index_by(&:id)
|
||||||
|
.transform_values { [_1, _1.clone(fork)] }
|
||||||
|
|
||||||
|
cloned_dossier = deep_clone(only: dossier_attributes, include: relationships) do |original, kopy|
|
||||||
|
PiecesJustificativesService.clone_attachments(original, kopy)
|
||||||
|
|
||||||
|
if original.is_a?(Dossier)
|
||||||
|
if fork
|
||||||
|
kopy.editing_fork_origin = original
|
||||||
|
else
|
||||||
|
kopy.parent_dossier = original
|
||||||
|
end
|
||||||
|
|
||||||
|
kopy.user = user || original.user
|
||||||
|
kopy.state = Dossier.states.fetch(:brouillon)
|
||||||
|
|
||||||
|
kopy.champs = cloned_champs.values.map do |(_, champ)|
|
||||||
|
champ.dossier = kopy
|
||||||
|
champ.parent = cloned_champs[champ.parent_id].second if champ.child?
|
||||||
|
champ
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
transaction do
|
||||||
|
cloned_dossier.save!
|
||||||
|
|
||||||
|
if fork
|
||||||
|
cloned_champs.values.each do |(original, champ)|
|
||||||
|
champ.update_columns(created_at: original.created_at, updated_at: original.updated_at)
|
||||||
|
end
|
||||||
|
cloned_dossier.rebase!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cloned_dossier.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
def forked_with_changes?
|
||||||
|
if forked_diff.present?
|
||||||
|
forked_diff.values.any?(&:present?) || forked_groupe_instructeur_changed?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def champ_forked_with_changes?(champ)
|
||||||
|
if forked_diff.present?
|
||||||
|
forked_diff.values.any? { _1.include?(champ) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def forked_diff
|
||||||
|
@forked_diff ||= editing_fork? ? editing_fork_origin.make_diff(self) : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def forked_groupe_instructeur_changed?
|
||||||
|
editing_fork_origin.groupe_instructeur_id != groupe_instructeur_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_diff(diff)
|
||||||
|
champs_index = (champs + diff[:added]).index_by(&:stable_id_with_row)
|
||||||
|
|
||||||
|
diff[:added].each do |champ|
|
||||||
|
if champ.child?
|
||||||
|
champ.update_columns(dossier_id: id, parent_id: champs_index[champ.parent.stable_id_with_row].id)
|
||||||
|
else
|
||||||
|
champ.update_column(:dossier_id, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
champs_to_remove = []
|
||||||
|
diff[:updated].each do |champ|
|
||||||
|
old_champ = champs_index[champ.stable_id_with_row]
|
||||||
|
champs_to_remove << old_champ
|
||||||
|
|
||||||
|
if champ.child?
|
||||||
|
# we need to do that in order to avoid a foreign key constraint
|
||||||
|
old_champ.update(row_id: nil)
|
||||||
|
champ.update_columns(dossier_id: id, parent_id: champs_index[champ.parent.stable_id_with_row].id)
|
||||||
|
else
|
||||||
|
champ.update_column(:dossier_id, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
champs_to_remove += diff[:removed]
|
||||||
|
champs_to_remove
|
||||||
|
.filter { !_1.child? || !champs_to_remove.include?(_1.parent) }
|
||||||
|
.each(&:destroy!)
|
||||||
|
end
|
||||||
|
end
|
|
@ -40,6 +40,7 @@
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# batch_operation_id :bigint
|
# batch_operation_id :bigint
|
||||||
# dossier_transfer_id :bigint
|
# dossier_transfer_id :bigint
|
||||||
|
# editing_fork_origin_id :bigint
|
||||||
# groupe_instructeur_id :bigint
|
# groupe_instructeur_id :bigint
|
||||||
# parent_dossier_id :bigint
|
# parent_dossier_id :bigint
|
||||||
# revision_id :bigint
|
# revision_id :bigint
|
||||||
|
@ -50,6 +51,7 @@ class Dossier < ApplicationRecord
|
||||||
include DossierPrefillableConcern
|
include DossierPrefillableConcern
|
||||||
include DossierRebaseConcern
|
include DossierRebaseConcern
|
||||||
include DossierSectionsConcern
|
include DossierSectionsConcern
|
||||||
|
include DossierCloneConcern
|
||||||
|
|
||||||
enum state: {
|
enum state: {
|
||||||
brouillon: 'brouillon',
|
brouillon: 'brouillon',
|
||||||
|
@ -148,7 +150,6 @@ class Dossier < ApplicationRecord
|
||||||
belongs_to :groupe_instructeur, optional: true
|
belongs_to :groupe_instructeur, optional: true
|
||||||
belongs_to :revision, class_name: 'ProcedureRevision', optional: false
|
belongs_to :revision, class_name: 'ProcedureRevision', optional: false
|
||||||
belongs_to :user, optional: true
|
belongs_to :user, optional: true
|
||||||
belongs_to :parent_dossier, class_name: 'Dossier', optional: true
|
|
||||||
belongs_to :batch_operation, optional: true
|
belongs_to :batch_operation, optional: true
|
||||||
has_many :dossier_batch_operations, dependent: :destroy
|
has_many :dossier_batch_operations, dependent: :destroy
|
||||||
has_many :batch_operations, through: :dossier_batch_operations
|
has_many :batch_operations, through: :dossier_batch_operations
|
||||||
|
@ -161,7 +162,6 @@ class Dossier < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :transfer, class_name: 'DossierTransfer', foreign_key: 'dossier_transfer_id', optional: true, inverse_of: :dossiers
|
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 :transfer_logs, class_name: 'DossierTransferLog', dependent: :destroy
|
||||||
has_many :cloned_dossiers, class_name: 'Dossier', foreign_key: 'parent_dossier_id', dependent: :nullify, inverse_of: :parent_dossier
|
|
||||||
|
|
||||||
accepts_nested_attributes_for :champs
|
accepts_nested_attributes_for :champs
|
||||||
accepts_nested_attributes_for :champs_public
|
accepts_nested_attributes_for :champs_public
|
||||||
|
@ -236,7 +236,7 @@ class Dossier < ApplicationRecord
|
||||||
scope :prefilled, -> { where(prefilled: true) }
|
scope :prefilled, -> { where(prefilled: true) }
|
||||||
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
|
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
|
||||||
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
|
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
|
||||||
scope :visible_by_user, -> { where(for_procedure_preview: false).or(where(for_procedure_preview: nil)).where(hidden_by_user_at: nil) }
|
scope :visible_by_user, -> { where(for_procedure_preview: false).or(where(for_procedure_preview: nil)).where(hidden_by_user_at: nil, editing_fork_origin_id: nil) }
|
||||||
scope :visible_by_administration, -> {
|
scope :visible_by_administration, -> {
|
||||||
state_not_brouillon
|
state_not_brouillon
|
||||||
.where(hidden_by_administration_at: nil)
|
.where(hidden_by_administration_at: nil)
|
||||||
|
@ -247,6 +247,7 @@ class Dossier < ApplicationRecord
|
||||||
state_not_brouillon.hidden_by_administration.or(state_en_construction.hidden_by_user)
|
state_not_brouillon.hidden_by_administration.or(state_en_construction.hidden_by_user)
|
||||||
}
|
}
|
||||||
scope :for_procedure_preview, -> { where(for_procedure_preview: true) }
|
scope :for_procedure_preview, -> { where(for_procedure_preview: true) }
|
||||||
|
scope :for_editing_fork, -> { where.not(editing_fork_origin_id: nil) }
|
||||||
|
|
||||||
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
||||||
scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
|
scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
|
||||||
|
@ -453,7 +454,7 @@ class Dossier < ApplicationRecord
|
||||||
delegate :siret, :siren, to: :etablissement, allow_nil: true
|
delegate :siret, :siren, to: :etablissement, allow_nil: true
|
||||||
delegate :france_connect_information, to: :user, allow_nil: true
|
delegate :france_connect_information, to: :user, allow_nil: true
|
||||||
|
|
||||||
before_save :build_default_champs_for_new_dossier, if: Proc.new { revision_id_was.nil? && parent_dossier_id.nil? }
|
before_save :build_default_champs_for_new_dossier, if: Proc.new { revision_id_was.nil? && parent_dossier_id.nil? && editing_fork_origin_id.nil? }
|
||||||
before_save :update_search_terms
|
before_save :update_search_terms
|
||||||
|
|
||||||
after_save :send_web_hook
|
after_save :send_web_hook
|
||||||
|
@ -556,7 +557,7 @@ class Dossier < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_transition_to_en_construction?
|
def can_transition_to_en_construction?
|
||||||
brouillon? && procedure.dossier_can_transition_to_en_construction? && !for_procedure_preview?
|
brouillon? && procedure.dossier_can_transition_to_en_construction? && !for_procedure_preview? && !editing_fork?
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_terminer?
|
def can_terminer?
|
||||||
|
@ -1238,32 +1239,6 @@ class Dossier < ApplicationRecord
|
||||||
termine_expired_to_delete.find_each(&:purge_discarded)
|
termine_expired_to_delete.find_each(&:purge_discarded)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone
|
|
||||||
dossier_attributes = [:autorisation_donnees, :user_id, :revision_id, :groupe_instructeur_id]
|
|
||||||
relationships = [:individual, :etablissement]
|
|
||||||
|
|
||||||
cloned_dossier = deep_clone(only: dossier_attributes, include: relationships) do |original, kopy|
|
|
||||||
PiecesJustificativesService.clone_attachments(original, kopy)
|
|
||||||
|
|
||||||
if original.is_a?(Dossier)
|
|
||||||
kopy.parent_dossier_id = original.id
|
|
||||||
kopy.state = Dossier.states.fetch(:brouillon)
|
|
||||||
cloned_champs = original.champs
|
|
||||||
.index_by(&:id)
|
|
||||||
.transform_values(&:clone)
|
|
||||||
|
|
||||||
kopy.champs = cloned_champs.values.map do |champ|
|
|
||||||
champ.dossier = kopy
|
|
||||||
champ.parent = cloned_champs[champ.parent_id] if champ.child?
|
|
||||||
champ
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
transaction { cloned_dossier.save! }
|
|
||||||
cloned_dossier.reload
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_champs_by_stable_ids(stable_ids)
|
def find_champs_by_stable_ids(stable_ids)
|
||||||
return [] if stable_ids.compact.empty?
|
return [] if stable_ids.compact.empty?
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
class AddEditingForksToDossiers < ActiveRecord::Migration[6.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_belongs_to :dossiers, :editing_fork_origin, null: true, index: { algorithm: :concurrently }
|
||||||
|
end
|
||||||
|
end
|
|
@ -362,6 +362,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_02_160046) do
|
||||||
t.string "deleted_user_email_never_send"
|
t.string "deleted_user_email_never_send"
|
||||||
t.datetime "depose_at", precision: 6
|
t.datetime "depose_at", precision: 6
|
||||||
t.bigint "dossier_transfer_id"
|
t.bigint "dossier_transfer_id"
|
||||||
|
t.bigint "editing_fork_origin_id"
|
||||||
t.datetime "en_construction_at", precision: 6
|
t.datetime "en_construction_at", precision: 6
|
||||||
t.datetime "en_construction_close_to_expiration_notice_sent_at", precision: 6
|
t.datetime "en_construction_close_to_expiration_notice_sent_at", precision: 6
|
||||||
t.datetime "en_instruction_at", precision: 6
|
t.datetime "en_instruction_at", precision: 6
|
||||||
|
@ -393,6 +394,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_02_160046) do
|
||||||
t.index ["archived"], name: "index_dossiers_on_archived"
|
t.index ["archived"], name: "index_dossiers_on_archived"
|
||||||
t.index ["batch_operation_id"], name: "index_dossiers_on_batch_operation_id"
|
t.index ["batch_operation_id"], name: "index_dossiers_on_batch_operation_id"
|
||||||
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
|
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
|
||||||
|
t.index ["editing_fork_origin_id"], name: "index_dossiers_on_editing_fork_origin_id"
|
||||||
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
|
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
|
||||||
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"
|
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"
|
||||||
t.index ["prefill_token"], name: "index_dossiers_on_prefill_token", unique: true
|
t.index ["prefill_token"], name: "index_dossiers_on_prefill_token", unique: true
|
||||||
|
|
118
spec/models/concern/dossier_clone_concern_spec.rb
Normal file
118
spec/models/concern/dossier_clone_concern_spec.rb
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
RSpec.describe DossierCloneConcern do
|
||||||
|
let(:procedure) do
|
||||||
|
create(:procedure, types_de_champ_public: [
|
||||||
|
{ type: :text, libelle: "Un champ text", stable_id: 99 },
|
||||||
|
{ type: :text, libelle: "Un autre champ text", stable_id: 991 },
|
||||||
|
{ type: :yes_no, libelle: "Un champ yes no", stable_id: 992 },
|
||||||
|
{ type: :repetition, libelle: "Un champ répétable", stable_id: 993, mandatory: true, children: [{ type: :text, libelle: 'Nom', stable_id: 994 }] }
|
||||||
|
])
|
||||||
|
end
|
||||||
|
let(:dossier) { create(:dossier, procedure:) }
|
||||||
|
let(:forked_dossier) { dossier.find_or_create_editing_fork(dossier.user) }
|
||||||
|
|
||||||
|
before { procedure.publish! }
|
||||||
|
|
||||||
|
describe '#make_diff' do
|
||||||
|
subject { dossier.make_diff(forked_dossier) }
|
||||||
|
|
||||||
|
context 'with no changes' do
|
||||||
|
it { is_expected.to eq(added: [], updated: [], removed: []) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with updated groupe instructeur' do
|
||||||
|
before {
|
||||||
|
dossier.update(groupe_instructeur: nil)
|
||||||
|
forked_dossier.assign_to_groupe_instructeur(dossier.procedure.defaut_groupe_instructeur)
|
||||||
|
}
|
||||||
|
|
||||||
|
it { is_expected.to eq(added: [], updated: [], removed: []) }
|
||||||
|
it { expect(forked_dossier.forked_with_changes?).to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with updated champ' do
|
||||||
|
let(:updated_champ) { forked_dossier.champs.find { _1.stable_id == 99 } }
|
||||||
|
|
||||||
|
before { updated_champ.update(value: 'new value') }
|
||||||
|
|
||||||
|
it { is_expected.to eq(added: [], updated: [updated_champ], removed: []) }
|
||||||
|
it 'forked_with_changes? should reflect dossier state' do
|
||||||
|
expect(dossier.forked_with_changes?).to be_falsey
|
||||||
|
expect(forked_dossier.forked_with_changes?).to be_truthy
|
||||||
|
expect(updated_champ.forked_with_changes?).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with new revision' do
|
||||||
|
let(:added_champ) { forked_dossier.champs.find { _1.libelle == "Un nouveau champ text" } }
|
||||||
|
let(:removed_champ) { dossier.champs.find { _1.stable_id == 99 } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
procedure.draft_revision.add_type_de_champ({
|
||||||
|
type_champ: TypeDeChamp.type_champs.fetch(:text),
|
||||||
|
libelle: "Un nouveau champ text"
|
||||||
|
})
|
||||||
|
procedure.draft_revision.remove_type_de_champ(removed_champ.stable_id)
|
||||||
|
procedure.publish_revision!
|
||||||
|
end
|
||||||
|
|
||||||
|
it {
|
||||||
|
expect(dossier.revision_id).to eq(procedure.revisions.first.id)
|
||||||
|
expect(forked_dossier.revision_id).to eq(procedure.published_revision_id)
|
||||||
|
is_expected.to eq(added: [added_champ], updated: [], removed: [removed_champ])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#merge_fork' do
|
||||||
|
subject { dossier.merge_fork(forked_dossier) }
|
||||||
|
|
||||||
|
context 'with updated champ' do
|
||||||
|
let(:updated_champ) { forked_dossier.champs.find { _1.stable_id == 99 } }
|
||||||
|
let(:updated_repetition_champ) { forked_dossier.champs.find { _1.stable_id == 994 } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
dossier.champs.each do |champ|
|
||||||
|
champ.update(value: 'old value')
|
||||||
|
end
|
||||||
|
updated_champ.update(value: 'new value')
|
||||||
|
updated_repetition_champ.update(value: 'new value in repetition')
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect { subject }.to change { dossier.reload.champs.size }.by(0) }
|
||||||
|
it { expect { subject }.not_to change { dossier.reload.champs.order(:created_at).reject { _1.stable_id.in?([99, 994]) }.map(&:value) } }
|
||||||
|
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 99 }.value }.from('old value').to('new value') }
|
||||||
|
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 994 }.value }.from('old value').to('new value in repetition') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with new revision' do
|
||||||
|
let(:added_champ) { forked_dossier.champs.find { _1.libelle == "Un nouveau champ text" } }
|
||||||
|
let(:removed_champ) { dossier.champs.find { _1.stable_id == 99 } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
dossier.champs.each do |champ|
|
||||||
|
champ.update(value: 'old value')
|
||||||
|
end
|
||||||
|
procedure.draft_revision.add_type_de_champ({
|
||||||
|
type_champ: TypeDeChamp.type_champs.fetch(:text),
|
||||||
|
libelle: "Un nouveau champ text"
|
||||||
|
})
|
||||||
|
procedure.draft_revision.remove_type_de_champ(removed_champ.stable_id)
|
||||||
|
procedure.publish_revision!
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect { subject }.to change { dossier.reload.champs.size }.by(0) }
|
||||||
|
it { expect { subject }.to change { dossier.reload.champs.order(:created_at).map(&:to_s) }.from(['old value', 'old value', 'Non', 'old value', 'old value']).to(['old value', 'Non', 'old value', 'old value', '']) }
|
||||||
|
|
||||||
|
it "dossier after merge should be on last published revision" do
|
||||||
|
expect(dossier.revision_id).to eq(procedure.revisions.first.id)
|
||||||
|
expect(forked_dossier.revision_id).to eq(procedure.published_revision_id)
|
||||||
|
|
||||||
|
subject
|
||||||
|
perform_enqueued_jobs only: DestroyRecordLaterJob
|
||||||
|
|
||||||
|
expect(dossier.revision_id).to eq(procedure.published_revision_id)
|
||||||
|
expect(Dossier.exists?(forked_dossier.id)).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue