2019-05-28 16:02:55 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# migrate the invoices from being attached to a user to invoicing_profiles which are GDPR compliant
|
2019-05-22 17:49:22 +02:00
|
|
|
class MigrateUserToInvoicingProfile < ActiveRecord::Migration
|
|
|
|
def up
|
|
|
|
# first, check the footprints
|
2019-05-28 16:02:55 +02:00
|
|
|
check_footprints
|
|
|
|
|
2019-05-22 17:49:22 +02:00
|
|
|
# if everything is ok, proceed with migration
|
2019-05-28 16:02:55 +02:00
|
|
|
# remove and save periods in memory
|
|
|
|
periods = backup_and_remove_periods
|
|
|
|
# migrate invoices
|
|
|
|
puts 'Migrating invoices. This may take a while...'
|
|
|
|
Invoice.order(:id).all.each do |i|
|
2019-05-29 14:28:14 +02:00
|
|
|
user = User.find(i.user_id)
|
|
|
|
i.update_column('invoicing_profile_id', user.invoicing_profile.id)
|
2019-06-11 10:02:48 +02:00
|
|
|
i.update_column('statistic_profile_id', user.statistic_profile.id)
|
2019-05-22 17:49:22 +02:00
|
|
|
i.update_column('user_id', nil)
|
|
|
|
end
|
2019-05-28 16:02:55 +02:00
|
|
|
# chain all records
|
|
|
|
InvoiceItem.order(:id).all.each(&:chain_record)
|
|
|
|
Invoice.order(:id).all.each(&:chain_record)
|
|
|
|
# write memory dump into database
|
|
|
|
restore_periods(periods)
|
2019-05-22 17:49:22 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def down
|
2019-05-28 16:02:55 +02:00
|
|
|
# here we don't check footprints to save processing time and because this is pointless when reverting the migrations
|
|
|
|
|
|
|
|
# remove and save periods in memory
|
|
|
|
periods = backup_and_remove_periods
|
|
|
|
# reset invoices
|
2019-05-22 17:49:22 +02:00
|
|
|
Invoice.order(:created_at).all.each do |i|
|
|
|
|
i.update_column('user_id', i.invoicing_profile.user_id)
|
|
|
|
i.update_column('invoicing_profile_id', nil)
|
2019-06-11 10:02:48 +02:00
|
|
|
i.update_column('statistic_profile_id', nil)
|
2019-05-22 17:49:22 +02:00
|
|
|
end
|
2019-05-28 16:02:55 +02:00
|
|
|
# chain all records
|
|
|
|
InvoiceItem.order(:id).all.each(&:chain_record)
|
|
|
|
Invoice.order(:id).all.each(&:chain_record)
|
|
|
|
# write memory dump into database
|
|
|
|
restore_periods(periods)
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_footprints
|
|
|
|
if AccountingPeriod.count.positive?
|
|
|
|
last_period = AccountingPeriod.order(start_at: 'DESC').first
|
|
|
|
puts "Checking invoices footprints from #{last_period.end_at}. This may take a while..."
|
|
|
|
Invoice.where('created_at > ?', last_period.end_at).order(:id).each do |i|
|
|
|
|
raise "Invalid footprint for invoice #{i.id}" unless i.check_footprint
|
|
|
|
end
|
|
|
|
else
|
|
|
|
puts 'Checking all invoices footprints. This may take a while...'
|
|
|
|
Invoice.order(:id).all.each do |i|
|
|
|
|
raise "Invalid footprint for invoice #{i.id}" unless i.check_footprint
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# will return an array of hash containing the removed periods data
|
|
|
|
def backup_and_remove_periods
|
2019-05-29 12:01:24 +02:00
|
|
|
return [] unless AccountingPeriod.count.positive?
|
2019-05-28 16:02:55 +02:00
|
|
|
|
|
|
|
puts 'Removing accounting archives...'
|
|
|
|
# 1. remove protection for AccountingPeriods
|
|
|
|
execute("DROP RULE IF EXISTS accounting_periods_del_protect ON #{AccountingPeriod.arel_table.name};")
|
|
|
|
# 2. backup AccountingPeriods in memory
|
|
|
|
periods = []
|
|
|
|
AccountingPeriod.all.each do |p|
|
|
|
|
periods.push(
|
|
|
|
start_at: p.start_at,
|
|
|
|
end_at: p.end_at,
|
|
|
|
closed_at: p.closed_at,
|
|
|
|
closed_by: p.closed_by
|
|
|
|
)
|
|
|
|
end
|
|
|
|
# 3. Delete periods from database
|
|
|
|
AccountingPeriod.all.each do |ap|
|
|
|
|
execute("DELETE FROM accounting_periods WHERE ID=#{ap.id};")
|
|
|
|
end
|
|
|
|
periods
|
|
|
|
end
|
|
|
|
|
|
|
|
def restore_periods(periods)
|
|
|
|
return unless periods.size.positive?
|
|
|
|
|
|
|
|
# 1. recreate AccountingPeriods
|
|
|
|
puts 'Recreating accounting archives. This may take a while...'
|
|
|
|
periods.each do |p|
|
|
|
|
AccountingPeriod.create!(
|
|
|
|
start_at: p[:start_at],
|
|
|
|
end_at: p[:end_at],
|
|
|
|
closed_at: p[:closed_at],
|
|
|
|
closed_by: p[:closed_by]
|
|
|
|
)
|
|
|
|
end
|
|
|
|
# 2. reset protection for AccountingPeriods
|
|
|
|
execute("CREATE RULE accounting_periods_del_protect AS ON DELETE TO #{AccountingPeriod.arel_table.name} DO INSTEAD NOTHING;")
|
2019-05-22 17:49:22 +02:00
|
|
|
end
|
|
|
|
end
|