demarches-normaliennes/app/models/concerns/dossier_rebase_concern.rb
2023-01-31 16:39:00 +01:00

148 lines
4.5 KiB
Ruby

module DossierRebaseConcern
extend ActiveSupport::Concern
def rebase!
if can_rebase?
transaction do
rebase
end
end
end
def rebase_later
DossierRebaseJob.perform_later(self)
end
def can_rebase?
revision != procedure.published_revision &&
(brouillon? || accepted_en_construction_changes? || accepted_en_instruction_changes?)
end
def pending_changes
revision.compare(procedure.published_revision)
end
def can_rebase_mandatory_change?(stable_id)
!champs.filter { _1.stable_id == stable_id }.any?(&:blank?)
end
def can_rebase_drop_down_options_change?(stable_id, options)
!champs.filter { _1.stable_id == stable_id }.any? { _1.in?(options) }
end
private
def accepted_en_construction_changes?
en_construction? && pending_changes.all? { _1.can_rebase?(self) }
end
def accepted_en_instruction_changes?
en_instruction? && pending_changes.all? { _1.can_rebase?(self) }
end
def rebase
# revision we are rebasing to
target_revision = procedure.published_revision
# index published types de champ coordinates by stable_id
target_coordinates_by_stable_id = target_revision
.revision_types_de_champ
.includes(:type_de_champ, :parent)
.index_by(&:stable_id)
changes_by_op = pending_changes
.group_by(&:op)
.tap { _1.default = [] }
champs_by_stable_id = champs
.joins(:type_de_champ)
.group_by(&:stable_id)
.transform_values { Champ.where(id: _1) }
# add champ
changes_by_op[:add]
.each { add_new_champs_for_revision(target_coordinates_by_stable_id[_1.stable_id]) }
# remove champ
changes_by_op[:remove]
.each { champs_by_stable_id[_1.stable_id].destroy_all }
if brouillon?
changes_by_op[:update]
.each { apply(_1, champs_by_stable_id[_1.stable_id]) }
end
# due to repetition tdc clone on update or erase
# we must reassign tdc to the latest version
champs_by_stable_id
.filter_map { |stable_id, champs| [target_coordinates_by_stable_id[stable_id].type_de_champ_id, champs] if champs.present? }
.each { |type_de_champ_id, champs| champs.update_all(type_de_champ_id:) }
# update dossier revision
update_column(:revision_id, target_revision.id)
end
def apply(change, champs)
case change.attribute
when :type_champ
champs.each { purge_piece_justificative_file(_1) }
GeoArea.where(champ: champs).destroy_all
Etablissement.where(champ: champs).destroy_all
champs.update_all(type: "Champs::#{change.to.classify}Champ",
value: nil,
value_json: nil,
external_id: nil,
data: nil,
rebased_at: Time.zone.now)
when :drop_down_options
# we are removing options, we need to remove the value if it contains one of the removed options
removed_options = change.from - change.to
if removed_options.present? && champs.any? { _1.in?(removed_options) }
champs.filter { _1.in?(removed_options) }.each do
_1.remove_option(removed_options)
_1.update_column(:rebased_at, Time.zone.now)
end
end
when :carte_layers
# if we are removing cadastres layer, we need to remove cadastre geo areas
if change.from.include?(:cadastres) && !change.to.include?(:cadastres)
champs.filter { _1.cadastres.present? }.each do
_1.cadastres.each(&:destroy)
_1.update_column(:rebased_at, Time.zone.now)
end
end
else
champs.update_all(rebased_at: Time.zone.now)
end
end
def add_new_champs_for_revision(target_coordinate)
if target_coordinate.child?
# If this type de champ is a child, we create a new champ for each row of the parent
parent_stable_id = target_coordinate.parent.stable_id
champs_repetition = champs
.includes(:champs, :type_de_champ)
.where(type_de_champ: { stable_id: parent_stable_id })
champs_repetition.each do |champ_repetition|
champ_repetition.champs.map(&:row_id).uniq.each do |row_id|
create_champ(target_coordinate, champ_repetition, row_id:)
end
end
else
create_champ(target_coordinate, self)
end
end
def create_champ(target_coordinate, parent, row_id: nil)
params = { revision: target_coordinate.revision, rebased_at: Time.zone.now, row_id: }.compact
champ = target_coordinate
.type_de_champ
.build_champ(params)
parent.champs << champ
end
def purge_piece_justificative_file(champ)
ActiveStorage::Attachment.where(id: champ.piece_justificative_file.ids).delete_all
end
end