2024-04-29 00:17:15 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2023-03-21 15:59:03 +01:00
|
|
|
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
|
2023-04-27 12:25:30 +02:00
|
|
|
has_many :editing_forks, -> { where(hidden_by_reason: nil) }, class_name: 'Dossier', foreign_key: :editing_fork_origin_id, dependent: :destroy, inverse_of: :editing_fork_origin
|
2023-03-21 15:59:03 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def find_or_create_editing_fork(user)
|
|
|
|
find_editing_fork(user) || clone(user:, fork: true)
|
|
|
|
end
|
|
|
|
|
2023-04-23 20:44:35 +02:00
|
|
|
def find_editing_fork(user, rebase: true)
|
|
|
|
fork = editing_forks.find_by(user:)
|
|
|
|
fork.rebase! if rebase && fork
|
|
|
|
|
|
|
|
fork
|
2023-03-21 15:59:03 +01:00
|
|
|
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?
|
2023-04-27 12:25:30 +02:00
|
|
|
update!(hidden_by_administration_at: Time.current, hidden_by_reason: :stale_fork)
|
2023-03-21 15:59:03 +01:00
|
|
|
DestroyRecordLaterJob.perform_later(self)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def editing_fork?
|
|
|
|
editing_fork_origin_id.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_diff(editing_fork)
|
2024-10-06 18:28:19 +02:00
|
|
|
origin_champs_index = project_champs_public_all.index_by(&:public_id)
|
|
|
|
forked_champs_index = editing_fork.project_champs_public_all.index_by(&:public_id)
|
2023-03-21 15:59:03 +01:00
|
|
|
updated_champs_index = editing_fork
|
2024-10-06 18:28:19 +02:00
|
|
|
.project_champs_public_all
|
2023-03-21 15:59:03 +01:00
|
|
|
.filter { _1.updated_at > editing_fork.created_at }
|
2024-03-21 12:01:55 +01:00
|
|
|
.index_by(&:public_id)
|
2023-03-21 15:59:03 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
transaction do
|
2023-05-12 17:53:40 +02:00
|
|
|
rebase!(force: true)
|
|
|
|
diff = make_diff(editing_fork)
|
2023-03-21 15:59:03 +01:00
|
|
|
apply_diff(diff)
|
2023-05-12 17:53:40 +02:00
|
|
|
touch(:last_champ_updated_at)
|
2024-09-10 16:09:43 +02:00
|
|
|
touch(:last_champ_piece_jointe_updated_at) if diff[:updated].any? { |c| c.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp]) }
|
2023-03-21 15:59:03 +01:00
|
|
|
end
|
|
|
|
reload
|
2024-04-25 18:48:14 +02:00
|
|
|
index_search_terms_later
|
2023-03-21 15:59:03 +01:00
|
|
|
editing_fork.destroy_editing_fork!
|
|
|
|
end
|
|
|
|
|
|
|
|
def clone(user: nil, fork: false)
|
2023-07-18 11:29:44 +02:00
|
|
|
dossier_attributes = [:autorisation_donnees, :revision_id]
|
|
|
|
dossier_attributes += [:groupe_instructeur_id] if fork
|
2023-03-21 15:59:03 +01:00
|
|
|
relationships = [:individual, :etablissement]
|
|
|
|
|
2024-10-06 18:28:19 +02:00
|
|
|
cloned_champs = champs
|
2023-03-21 15:59:03 +01:00
|
|
|
.index_by(&:id)
|
|
|
|
.transform_values { [_1, _1.clone(fork)] }
|
|
|
|
|
|
|
|
cloned_dossier = deep_clone(only: dossier_attributes, include: relationships) do |original, kopy|
|
2024-02-07 17:29:47 +01:00
|
|
|
ClonePiecesJustificativesService.clone_attachments(original, kopy)
|
2023-03-21 15:59:03 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
transaction do
|
2024-04-02 11:04:00 +02:00
|
|
|
if fork
|
|
|
|
cloned_dossier.save!(validate: false)
|
|
|
|
else
|
|
|
|
cloned_dossier.validate(:champs_public_value)
|
|
|
|
cloned_dossier.save!
|
|
|
|
end
|
2023-09-19 17:25:00 +02:00
|
|
|
cloned_dossier.rebase!
|
2023-03-21 15:59:03 +01:00
|
|
|
end
|
|
|
|
|
2023-05-11 15:57:28 +02:00
|
|
|
if fork
|
|
|
|
cloned_champs.values.each do |(original, champ)|
|
|
|
|
champ.update_columns(created_at: original.created_at, updated_at: original.updated_at)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-04-25 18:48:14 +02:00
|
|
|
cloned_dossier.index_search_terms_later if !fork
|
2023-03-21 15:59:03 +01:00
|
|
|
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)
|
2024-10-06 18:28:19 +02:00
|
|
|
champs_added = diff[:added].filter(&:persisted?)
|
|
|
|
champs_updated = diff[:updated].filter(&:persisted?)
|
|
|
|
champs_removed = diff[:removed].filter(&:persisted?)
|
2023-03-21 15:59:03 +01:00
|
|
|
|
2024-10-06 18:28:19 +02:00
|
|
|
champs_added.each { _1.update_column(:dossier_id, id) }
|
2023-03-21 15:59:03 +01:00
|
|
|
|
2024-10-06 18:28:19 +02:00
|
|
|
if champs_updated.present?
|
|
|
|
champs_index = filled_champs_public.index_by(&:public_id)
|
|
|
|
champs_updated.each do |champ|
|
|
|
|
champs_index[champ.public_id]&.destroy!
|
|
|
|
champ.update_column(:dossier_id, id)
|
|
|
|
end
|
2023-03-21 15:59:03 +01:00
|
|
|
end
|
|
|
|
|
2024-10-06 18:28:19 +02:00
|
|
|
champs_removed.each(&:destroy!)
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
# This is a temporary method that is only used by diff/merge algorithm. Once it's gone, this method should be removed.
|
|
|
|
def project_champs_public_all
|
|
|
|
revision.types_de_champ_public.flat_map do |type_de_champ|
|
|
|
|
champ = project_champ(type_de_champ, nil)
|
|
|
|
if type_de_champ.repetition?
|
|
|
|
[champ] + project_rows_for(type_de_champ).flatten
|
|
|
|
else
|
|
|
|
champ
|
|
|
|
end
|
|
|
|
end
|
2023-03-21 15:59:03 +01:00
|
|
|
end
|
|
|
|
end
|