diff --git a/app/services/footprint_service.rb b/app/services/footprint_service.rb index c4d60b651..394280ee8 100644 --- a/app/services/footprint_service.rb +++ b/app/services/footprint_service.rb @@ -1,49 +1,62 @@ # frozen_string_literal: true +require 'checksum' + # Provides helper methods to compute footprints class FootprintService - # Compute the footprint - # @param klass Invoice|InvoiceItem|HistoryValue - # @param item an instance of the provided class - # @param sort the items in database by the provided criterion, to find the previous one - def self.compute_footprint(klass, item, sort_on = 'id') - Checksum.text(FootprintService.footprint_data(klass, item, sort_on)) - end + class << self + # Compute the footprint + # @param klass a class inheriting from Footprintable + # @param item an instance of the provided class + # @param sort_on the items in database by the provided criterion, to find the previous one + def compute_footprint(klass, item, sort_on = 'id') + Checksum.text(FootprintService.footprint_data(klass, item, sort_on)) + end - # Return the original data string used to compute the footprint - # @param klass Invoice|InvoiceItem|HistoryValue - # @param item an instance of the provided class - # @param sort the items in database by the provided criterion, to find the previous one - def self.footprint_data(klass, item, sort_on = 'id') - raise TypeError unless item.is_a? klass + # Return the original data string used to compute the footprint + # @param klass a class inheriting from Footprintable + # @param item an instance of the provided class + # @param sort_on the items in database by the provided criterion, to find the previous one + def footprint_data(klass, item, sort_on = 'id') + raise TypeError unless item.is_a? klass - previous = klass.where("#{sort_on} < ?", item[sort_on]) - .order("#{sort_on} DESC") - .limit(1) + previous = klass.where("#{sort_on} < ?", item[sort_on]) + .order("#{sort_on} DESC") + .limit(1) - columns = FootprintService.footprint_columns(klass) + columns = FootprintService.footprint_columns(klass) - "#{columns.map { |c| item[c] }.join}#{previous.first ? previous.first.footprint : ''}" - end + "#{columns.map { |c| comparable(item[c]) }.join}#{previous.first ? previous.first.footprint : ''}" + end - # Return an ordered array of the columns used in the footprint computation - # @param klass Invoice|InvoiceItem|HistoryValue - def self.footprint_columns(klass) - klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].concat(klass.columns_out_of_footprint).include? c } - end + # Return an ordered array of the columns used in the footprint computation + # @param klass a class inheriting from Footprintable + def footprint_columns(klass) + klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].concat(klass.columns_out_of_footprint).include? c } + end - # Logs a debugging message to help finding why a footprint is invalid - # @param klass Invoice|InvoiceItem|HistoryValue - # @param item an instance of the provided class - def self.debug_footprint(klass, item) - columns = FootprintService.footprint_columns(klass) - current = FootprintService.footprint_data(klass, item) - saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass.name) - puts "Debug footprint for #{klass} [ id: #{item.id} ]" - puts '-----------------------------------------' - puts "columns: [ #{columns.join(', ')} ]" - puts "current footprint: #{current}" - puts " saved footprint: #{saved&.data}" - puts '-----------------------------------------' + # Logs a debugging message to help finding why a footprint is invalid + # @param klass a class inheriting from Footprintable + # @param item an instance of the provided class + def debug_footprint(klass, item) + columns = FootprintService.footprint_columns(klass) + current = FootprintService.footprint_data(klass, item) + saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass.name) + puts "Debug footprint for #{klass} [ id: #{item.id} ]" + puts '-----------------------------------------' + puts "columns: [ #{columns.join(', ')} ]" + puts "current: #{current}" + puts " saved: #{saved&.data}" + puts '-----------------------------------------' + end + + private + + # Return a comparable value for jsonb fields (with keys ordered alphabetically) + def comparable(value) + return value unless value.class == Hash + + value.sort.to_h + end end end diff --git a/lib/tasks/fablab/maintenance.rake b/lib/tasks/fablab/maintenance.rake index f9f8d0523..caa4b1dee 100644 --- a/lib/tasks/fablab/maintenance.rake +++ b/lib/tasks/fablab/maintenance.rake @@ -98,7 +98,7 @@ namespace :fablab do desc 'save the footprint original data' task save_footprint_data: :environment do - [Invoice, InvoiceItem, HistoryValue].each do |klass| + [Invoice, InvoiceItem, HistoryValue, PaymentSchedule, PaymentScheduleItem].each do |klass| klass.all.each do |item| FootprintDebug.create!( footprint: item.footprint,