aboutsummaryrefslogtreecommitdiffstats
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/chouette/journey_pattern.rb2
-rw-r--r--app/models/chouette/line.rb1
-rw-r--r--app/models/chouette/time_table.rb51
-rw-r--r--app/models/chouette/time_table_period.rb7
-rw-r--r--app/models/chouette/vehicle_journey.rb2
-rw-r--r--app/models/concerns/checksum_support.rb7
-rw-r--r--app/models/generic_attribute_control/min_max.rb4
-rw-r--r--app/models/merge.rb426
-rw-r--r--app/models/referential.rb50
-rw-r--r--app/models/referential_cloning.rb15
-rw-r--r--app/models/referential_suite.rb6
-rw-r--r--app/models/vehicle_journey_control/delta.rb2
-rw-r--r--app/models/vehicle_journey_control/speed.rb4
-rw-r--r--app/models/workbench.rb3
14 files changed, 557 insertions, 23 deletions
diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb
index 367b00449..366fde188 100644
--- a/app/models/chouette/journey_pattern.rb
+++ b/app/models/chouette/journey_pattern.rb
@@ -27,7 +27,7 @@ module Chouette
def checksum_attributes
values = self.slice(*['name', 'published_name', 'registration_number']).values
- values << self.stop_points.map(&:stop_area).map(&:user_objectid)
+ values << self.stop_points.sort_by(&:position).map(&:stop_area).map(&:user_objectid)
values.flatten
end
diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb
index 2d776e94b..389240ec7 100644
--- a/app/models/chouette/line.rb
+++ b/app/models/chouette/line.rb
@@ -21,6 +21,7 @@ module Chouette
has_many :journey_patterns, :through => :routes
has_many :vehicle_journeys, :through => :journey_patterns
has_many :routing_constraint_zones, through: :routes
+ has_many :time_tables, -> { distinct }, :through => :vehicle_journeys
has_and_belongs_to_many :group_of_lines, :class_name => 'Chouette::GroupOfLine', :order => 'group_of_lines.name'
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 74c20f061..db97dd2fa 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -569,5 +569,56 @@ module Chouette
tt.comment = I18n.t("activerecord.copy", :name => self.comment)
tt
end
+
+ def intersect_periods!(mask_periods)
+ dates.each do |date|
+ unless mask_periods.any? { |p| p.include? date.date }
+ dates.delete date
+ end
+ end
+
+ periods.each do |period|
+ mask_periods_with_common_part = mask_periods.select { |p| p.intersect? period.range }
+
+ if mask_periods_with_common_part.empty?
+ self.periods.delete period
+ else
+ mask_periods_with_common_part.each do |mask_period|
+ intersection = (mask_period & period.range)
+ period.period_start, period.period_end = intersection.begin, intersection.end
+ end
+ end
+ end
+ end
+
+ def remove_periods!(removed_periods)
+ dates.each do |date|
+ if removed_periods.any? { |p| p.include? date.date }
+ dates.delete date
+ end
+ end
+
+ periods.each do |period|
+ modified_ranges = removed_periods.inject([period.range]) do |period_ranges, removed_period|
+ period_ranges.map { |p| p.remove removed_period }.flatten
+ end
+
+ unless modified_ranges.empty?
+ modified_ranges.each_with_index do |modified_range, index|
+ new_period = index == 0 ? period : periods.build
+
+ new_period.period_start, new_period.period_end =
+ modified_range.min, modified_range.max
+ end
+ else
+ periods.delete period
+ end
+ end
+ end
+
+ def empty?
+ dates.empty? && periods.empty?
+ end
+
end
end
diff --git a/app/models/chouette/time_table_period.rb b/app/models/chouette/time_table_period.rb
index ab3e79d7e..d9b707675 100644
--- a/app/models/chouette/time_table_period.rb
+++ b/app/models/chouette/time_table_period.rb
@@ -42,5 +42,10 @@ module Chouette
def contains?(p)
(p.period_start >= self.period_start && p.period_end <= self.period_end)
end
+
+ def range
+ period_start..period_end
+ end
+
end
-end \ No newline at end of file
+end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index d84bebf18..d4dc82a56 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -70,7 +70,7 @@ module Chouette
attrs << self.published_journey_identifier
attrs << self.try(:company).try(:get_objectid).try(:local_id)
attrs << self.footnotes.map(&:checksum).sort
- attrs << self.vehicle_journey_at_stops.map(&:checksum).sort
+ attrs << self.vehicle_journey_at_stops.sort_by { |s| s.stop_point&.position }.map(&:checksum).sort
end
end
diff --git a/app/models/concerns/checksum_support.rb b/app/models/concerns/checksum_support.rb
index c95e23bcf..b700ef286 100644
--- a/app/models/concerns/checksum_support.rb
+++ b/app/models/concerns/checksum_support.rb
@@ -26,4 +26,11 @@ module ChecksumSupport
self.checksum = Digest::SHA256.new.hexdigest(self.checksum_source)
end
end
+
+ def update_checksum!
+ set_current_checksum_source
+ if checksum_source_changed?
+ update checksum: Digest::SHA256.new.hexdigest(checksum_source)
+ end
+ end
end
diff --git a/app/models/generic_attribute_control/min_max.rb b/app/models/generic_attribute_control/min_max.rb
index 1c429b9a4..18873b683 100644
--- a/app/models/generic_attribute_control/min_max.rb
+++ b/app/models/generic_attribute_control/min_max.rb
@@ -2,8 +2,8 @@ module GenericAttributeControl
class MinMax < ComplianceControl
store_accessor :control_attributes, :minimum, :maximum, :target
- validates :minimum, numericality: true, allow_nil: true
- validates :maximum, numericality: true, allow_nil: true
+ validates_numericality_of :minimum, allow_nil: true, greater_than_or_equal_to: 0
+ validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0
validates :target, presence: true
include MinMaxValuesValidation
diff --git a/app/models/merge.rb b/app/models/merge.rb
new file mode 100644
index 000000000..4cbbd18ab
--- /dev/null
+++ b/app/models/merge.rb
@@ -0,0 +1,426 @@
+class Merge < ActiveRecord::Base
+ extend Enumerize
+
+ belongs_to :workbench
+ validates :workbench, presence: true
+
+ enumerize :status, in: %w[new pending successful failed running], default: :new
+
+ has_array_of :referentials, class_name: 'Referential'
+
+ delegate :output, to: :workbench
+
+ after_commit :merge, :on => :create
+
+ def merge
+ MergeWorker.perform_async(id)
+ end
+
+ def name
+ "Dummy" # FIXME
+ end
+
+ attr_reader :new
+
+ def merge!
+ update started_at: Time.now, status: :running
+
+ prepare_new
+
+ referentials.each do |referential|
+ merge_referential referential
+ end
+
+ save_current
+ rescue => e
+ Rails.logger.error "Merge failed: #{e} #{e.backtrace.join("\n")}"
+ update status: :failed
+ raise e if Rails.env.test?
+ ensure
+ attributes = { ended_at: Time.now }
+ attributes[:status] = :successful if status == :running
+ update attributes
+ end
+
+ def prepare_new
+ new =
+ if workbench.output.current
+ Rails.logger.debug "Clone current output"
+ Referential.new_from(workbench.output.current, fixme_functional_scope).tap do |clone|
+ clone.inline_clone = true
+ end
+ else
+ Rails.logger.debug "Create a new output"
+ # 'empty' one
+ attributes = {
+ workbench: workbench,
+ organisation: workbench.organisation, # TODO could be workbench.organisation by default
+ name: I18n.t("merges.referential_name"),
+ }
+ workbench.output.referentials.new attributes
+ end
+
+ new.referential_suite = output
+ new.organisation = workbench.organisation
+ new.slug = "output_#{workbench.id}_#{Time.now.to_i}"
+
+ unless new.valid?
+ Rails.logger.error "New referential isn't valid : #{new.errors.inspect}"
+ end
+
+ new.save!
+
+ output.update new: new
+ @new = new
+ end
+
+ def merge_referential(referential)
+ Rails.logger.debug "Merge #{referential.slug}"
+
+ metadata_merger = MetadatasMerger.new new, referential
+ metadata_merger.merge
+
+ new.metadatas.delete metadata_merger.empty_metadatas
+
+ new.save!
+
+ line_periods = LinePeriods.from_metadatas(referential.metadatas)
+
+ new.switch do
+ line_periods.each do |line_id, periods|
+ Rails.logger.debug "Clean data for #{line_id} #{periods.inspect}"
+
+ new.lines.find(line_id).time_tables.find_each do |time_table|
+ time_table.remove_periods! periods
+ unless time_table.empty?
+ puts "Remove period on #{time_table.inspect}"
+ time_table.save!
+ else
+ puts "Remove TimeTable #{time_table.inspect}"
+ time_table.destroy
+ end
+ end
+ end
+ end
+
+ # let's merge data :)
+
+ # Routes
+
+ # Always the same pattern :
+ # - load models from original Referential
+ # - load associated datas (children, checksum for associated models)
+ # - switch to new Referential
+ # - enumerate loaded models
+ # - skip model if its checksum exists "in the same line"
+ # - prepare attributes for a fresh model
+ # - remove all primary keys
+ # - compute an ObjectId (TODO)
+ # - process children models as nested attributes
+ # - associated other models (by line/checksum)
+ # - save! and next one
+
+ referential_routes = referential.switch do
+ referential.routes.all.to_a
+ end
+
+ referential_routes_checksums = Hash[referential_routes.map { |r| [ r.id, r.checksum ] }]
+
+ referential_stop_points = referential.switch do
+ referential.stop_points.all.to_a
+ end
+
+ referential_stop_points_by_route = referential_stop_points.group_by(&:route_id)
+
+ new.switch do
+ referential_routes.each do |route|
+ existing_route = new.routes.find_by line_id: route.line_id, checksum: route.checksum
+ unless existing_route
+ attributes = route.attributes.merge(
+ id: nil,
+ objectid: "merge:route:#{route.checksum}", #FIXME
+ # line_id is the same
+ # all other primary must be changed
+ opposite_route_id: nil #FIXME
+ )
+ new_route = new.routes.build attributes
+
+ route_stop_points = referential_stop_points_by_route[route.id]
+
+ # Stop Points
+ route_stop_points.each do |stop_point|
+ attributes = stop_point.attributes.merge(
+ id: nil,
+ route_id: nil,
+ objectid: "merge:stop_point:#{route.checksum}-#{stop_point.position}", #FIXME
+ )
+
+ new_route.stop_points.build attributes
+ end
+
+ new_route.save!
+
+ if new_route.checksum != route.checksum
+ raise "Checksum has changed: #{route.inspect} #{new_route.inspect}"
+ end
+ end
+ end
+ end
+
+ # JourneyPatterns
+
+ referential_journey_patterns, referential_journey_patterns_stop_areas_objectids = referential.switch do
+ journey_patterns = referential.journey_patterns.includes(:stop_points)
+
+ journey_patterns_stop_areas_objectids = Hash[
+ journey_patterns.map do |journey_pattern|
+ [ journey_pattern.id, journey_pattern.stop_points.map(&:stop_area).map(&:objectid)]
+ end
+ ]
+
+ [journey_patterns, journey_patterns_stop_areas_objectids]
+ end
+
+ referential_journey_patterns_checksums = Hash[referential_journey_patterns.map { |j| [ j.id, j.checksum ] }]
+
+ new.switch do
+ referential_journey_patterns.each do |journey_pattern|
+ # find parent route by checksum
+ # TODO add line_id for security
+ associated_route_checksum = referential_routes_checksums[journey_pattern.route_id]
+ existing_associated_route = new.routes.find_by checksum: associated_route_checksum
+
+ existing_journey_pattern = new.journey_patterns.find_by route_id: existing_associated_route.id, checksum: journey_pattern.checksum
+
+ unless existing_journey_pattern
+ attributes = journey_pattern.attributes.merge(
+ id: nil,
+
+ objectid: "merge:journey_pattern:#{existing_associated_route.checksum}-#{journey_pattern.checksum}", #FIXME
+
+ # all other primary must be changed
+ route_id: existing_associated_route.id,
+
+ departure_stop_point_id: nil, # FIXME
+ arrival_stop_point_id: nil
+ )
+
+ stop_areas_objectids = referential_journey_patterns_stop_areas_objectids[journey_pattern.id]
+
+ stop_points = existing_associated_route.stop_points.joins(:stop_area).where("stop_areas.objectid": stop_areas_objectids).order(:position)
+ attributes.merge!(stop_points: stop_points)
+
+ new_journey_pattern = new.journey_patterns.create! attributes
+ if new_journey_pattern.checksum != journey_pattern.checksum
+ raise "Checksum has changed: #{journey_pattern.checksum_source} #{new_journey_pattern.checksum_source}"
+ end
+ end
+ end
+ end
+
+ # Vehicle Journeys
+
+ referential_vehicle_journeys = referential.switch do
+ referential.vehicle_journeys.includes(:vehicle_journey_at_stops).all.to_a
+ end
+
+ new.switch do
+ referential_vehicle_journeys.each do |vehicle_journey|
+ # find parent journey pattern by checksum
+ # TODO add line_id for security
+ associated_journey_pattern_checksum = referential_journey_patterns_checksums[vehicle_journey.journey_pattern_id]
+ existing_associated_journey_pattern = new.journey_patterns.find_by checksum: associated_journey_pattern_checksum
+
+ existing_vehicle_journey = new.vehicle_journeys.find_by journey_pattern_id: existing_associated_journey_pattern.id, checksum: vehicle_journey.checksum
+
+ unless existing_vehicle_journey
+ attributes = vehicle_journey.attributes.merge(
+ id: nil,
+
+ objectid: "merge:vehicle_journey:#{existing_associated_journey_pattern.checksum}-#{vehicle_journey.checksum}", #FIXME
+
+ # all other primary must be changed
+ route_id: existing_associated_journey_pattern.route_id,
+ journey_pattern_id: existing_associated_journey_pattern.id,
+ )
+ new_vehicle_journey = new.vehicle_journeys.build attributes
+
+ # Create VehicleJourneyAtStops
+
+ vehicle_journey.vehicle_journey_at_stops.each_with_index do |vehicle_journey_at_stop, index|
+ at_stop_attributes = vehicle_journey_at_stop.attributes.merge(
+ id: nil,
+ stop_point_id: existing_associated_journey_pattern.stop_points[index].id
+ )
+ new_vehicle_journey.vehicle_journey_at_stops.build at_stop_attributes
+ end
+
+ new_vehicle_journey.save!
+
+ if new_vehicle_journey.checksum != vehicle_journey.checksum
+ raise "Checksum has changed: #{vehicle_journey.checksum_source} #{new_vehicle_journey.checksum_source}"
+ end
+ end
+
+ end
+ end
+
+ # Time Tables
+
+ referential_time_tables_by_id, referential_time_tables_with_lines = referential.switch do
+ time_tables_by_id = Hash[referential.time_tables.includes(:dates, :periods).all.to_a.map { |t| [t.id, t] }]
+
+ time_tables_with_associated_lines =
+ referential.time_tables.joins(vehicle_journeys: {route: :line}).pluck("lines.id", :id, "vehicle_journeys.checksum")
+
+ # Because TimeTables will be modified according metadata periods
+ # we're loading timetables per line (line is associated to a period list)
+ #
+ # line_id: [ { time_table.id, vehicle_journey.checksum } ]
+ time_tables_by_lines = time_tables_with_associated_lines.inject(Hash.new { |h,k| h[k] = [] }) do |hash, row|
+ hash[row.shift] << {id: row.first, vehicle_journey_checksum: row.second}
+ hash
+ end
+
+ [ time_tables_by_id, time_tables_by_lines ]
+ end
+
+ new.switch do
+ referential_time_tables_with_lines.each do |line_id, time_tables_properties|
+ # Because TimeTables will be modified according metadata periods
+ # we're loading timetables per line (line is associated to a period list)
+ line = workbench.line_referential.lines.find(line_id)
+
+ time_tables_properties.each do |properties|
+ time_table = referential_time_tables_by_id[properties[:id]]
+
+ # we can't test if TimeTable already exist by checksum
+ # because checksum is modified by intersect_periods!
+
+ attributes = time_table.attributes.merge(
+ id: nil,
+ comment: "Ligne #{line.name} - #{time_table.comment}",
+ calendar_id: nil
+ )
+ candidate_time_table = new.time_tables.build attributes
+
+ time_table.dates.each do |date|
+ date_attributes = date.attributes.merge(
+ id: nil,
+ time_table_id: nil
+ )
+ candidate_time_table.dates.build date_attributes
+ end
+ time_table.periods.each do |period|
+ period_attributes = period.attributes.merge(
+ id: nil,
+ time_table_id: nil
+ )
+ candidate_time_table.periods.build period_attributes
+ end
+
+ candidate_time_table.intersect_periods! line_periods.periods(line_id)
+
+ # FIXME
+ candidate_time_table.set_current_checksum_source
+ candidate_time_table.update_checksum
+
+ # after intersect_periods!, the checksum is the expected one
+ # we can search an existing TimeTable
+
+ existing_time_table = line.time_tables.find_by checksum: candidate_time_table.checksum
+
+ unless existing_time_table
+ # FIXME use real ObjectId
+ # Referential id is (temporary) used because the "same" TimeTable can be defined in several merged Referentials
+ # and checksum are modified by clean/remove_periods! but this temporary object id is constant
+ candidate_time_table.objectid = "merge:time_table:#{line.id}-#{candidate_time_table.checksum}-#{referential.id}:LOC"
+
+ candidate_time_table.save!
+
+ # Checksum is changed by #intersect_periods
+ # if new_time_table.checksum != time_table.checksum
+ # raise "Checksum has changed: #{time_table.checksum_source} #{new_time_table.checksum_source}"
+ # end
+
+ existing_time_table = candidate_time_table
+ end
+
+ # associate VehicleJourney
+
+ associated_vehicle_journey = line.vehicle_journeys.find_by!(checksum: properties[:vehicle_journey_checksum])
+ associated_vehicle_journey.time_tables << existing_time_table
+ end
+ end
+ end
+ end
+
+ def save_current
+ output.update current: new, new: nil
+ output.current.update referential_suite: output
+ end
+
+ def fixme_functional_scope
+ if attribute = workbench.organisation.sso_attributes.try(:[], "functional_scope")
+ JSON.parse(attribute)
+ end
+ end
+
+ def child_change
+
+ end
+
+ class MetadatasMerger
+
+ attr_reader :merge_metadatas, :referential
+ def initialize(merge_referential, referential)
+ @merge_metadatas = merge_referential.metadatas
+ @referential = referential
+ end
+
+ delegate :metadatas, to: :referential, prefix: :referential
+
+ def merge
+ referential_metadatas.each do |metadata|
+ merge_one metadata
+ end
+ end
+
+ def merged_line_metadatas(line_id)
+ merge_metadatas.select do |m|
+ m.line_ids.include? line_id
+ end
+ end
+
+ def merge_one(metadata)
+ metadata.line_ids.each do |line_id|
+ line_metadatas = merged_line_metadatas(line_id)
+
+ metadata.periodes.each do |period|
+ line_metadatas.each do |m|
+ m.periodes = m.periodes.map do |existing_period|
+ existing_period.remove period
+ end.flatten
+ end
+
+ attributes = {
+ line_ids: [line_id],
+ periodes: [period],
+ referential_source_id: referential.id,
+ created_at: metadata.created_at # TODO check required dates
+ }
+
+ # line_metadatas should not contain conflicted metadatas
+ merge_metadatas << ReferentialMetadata.new(attributes)
+ end
+ end
+ end
+
+ def empty_metadatas
+ merge_metadatas.select { |m| m.periodes.empty? }
+ end
+
+
+ end
+
+end
diff --git a/app/models/referential.rb b/app/models/referential.rb
index 1cdda9e6a..a5d5acbf9 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -61,6 +61,7 @@ class Referential < ActiveRecord::Base
scope :include_metadatas_lines, ->(line_ids) { where('referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids) }
scope :order_by_validity_period, ->(dir) { joins(:metadatas).order("unnest(periodes) #{dir}") }
scope :order_by_lines, ->(dir) { joins(:metadatas).group("referentials.id").order("sum(array_length(referential_metadata.line_ids,1)) #{dir}") }
+ scope :not_in_referential_suite, -> { where referential_suite_id: nil }
def save_with_table_lock_timeout(options = {})
save_without_table_lock_timeout(options)
@@ -144,6 +145,22 @@ class Referential < ActiveRecord::Base
Chouette::PurchaseWindow.all
end
+ def routes
+ Chouette::Route.all
+ end
+
+ def journey_patterns
+ Chouette::JourneyPattern.all
+ end
+
+ def stop_points
+ Chouette::StopPoint.all
+ end
+
+ def compliance_check_sets
+ ComplianceCheckSet.all
+ end
+
before_validation :define_default_attributes
def define_default_attributes
@@ -151,10 +168,22 @@ class Referential < ActiveRecord::Base
self.objectid_format ||= workbench.objectid_format if workbench
end
- def switch
+ def switch(&block)
raise "Referential not created" if new_record?
- Apartment::Tenant.switch!(slug)
- self
+
+ unless block_given?
+ Rails.logger.debug "Referential switch to #{slug}"
+ Apartment::Tenant.switch! slug
+ self
+ else
+ result = nil
+ Apartment::Tenant.switch slug do
+ Rails.logger.debug "Referential switch to #{slug}"
+ result = yield
+ end
+ Rails.logger.debug "Referential back"
+ result
+ end
end
def self.new_from(from, functional_scope)
@@ -296,7 +325,7 @@ class Referential < ActiveRecord::Base
overlapped_referential_ids.present?
end
- validate :detect_overlapped_referentials
+ validate :detect_overlapped_referentials, unless: :in_referential_suite?
def detect_overlapped_referentials
self.class.where(id: overlapped_referential_ids).each do |referential|
@@ -305,8 +334,19 @@ class Referential < ActiveRecord::Base
end
end
+ def in_referential_suite?
+ referential_suite_id.present?
+ end
+
+ attr_accessor :inline_clone
def clone_schema
- ReferentialCloning.create(source_referential: created_from, target_referential: self)
+ cloning = ReferentialCloning.new source_referential: created_from, target_referential: self
+
+ if inline_clone
+ cloning.clone!
+ else
+ cloning.save!
+ end
end
def create_schema
diff --git a/app/models/referential_cloning.rb b/app/models/referential_cloning.rb
index 24117e6c8..a2b23e819 100644
--- a/app/models/referential_cloning.rb
+++ b/app/models/referential_cloning.rb
@@ -8,19 +8,22 @@ class ReferentialCloning < ActiveRecord::Base
ReferentialCloningWorker.perform_async(id)
end
- def clone!
+ def clone_with_status!
run!
-
- AF83::SchemaCloner
- .new(source_referential.slug, target_referential.slug)
- .clone_schema
-
+ clone!
successful!
rescue Exception => e
Rails.logger.error "Clone failed : #{e}"
+ Rails.logger.error e.backtrace.join('\n')
failed!
end
+ def clone!
+ AF83::SchemaCloner
+ .new(source_referential.slug, target_referential.slug)
+ .clone_schema
+ end
+
private
aasm column: :status do
diff --git a/app/models/referential_suite.rb b/app/models/referential_suite.rb
index 93c2c3f36..4f825628c 100644
--- a/app/models/referential_suite.rb
+++ b/app/models/referential_suite.rb
@@ -1,7 +1,7 @@
class ReferentialSuite < ActiveRecord::Base
belongs_to :new, class_name: 'Referential'
validate def validate_consistent_new
- return true if new_id.nil?
+ return true if new_id.nil? || new.nil?
return true if new.referential_suite_id == id
errors.add(:inconsistent_new,
I18n.t('referential_suites.errors.inconsistent_new', name: new.name))
@@ -9,11 +9,11 @@ class ReferentialSuite < ActiveRecord::Base
belongs_to :current, class_name: 'Referential'
validate def validate_consistent_current
- return true if current_id.nil?
+ return true if current_id.nil? || current.nil?
return true if current.referential_suite_id == id
errors.add(:inconsistent_current,
I18n.t('referential_suites.errors.inconsistent_current', name: current.name))
end
- has_many :referentials
+ has_many :referentials, -> { order "created_at desc" }
end
diff --git a/app/models/vehicle_journey_control/delta.rb b/app/models/vehicle_journey_control/delta.rb
index 077dd6c4a..f061b9fdd 100644
--- a/app/models/vehicle_journey_control/delta.rb
+++ b/app/models/vehicle_journey_control/delta.rb
@@ -3,7 +3,7 @@ module VehicleJourneyControl
store_accessor :control_attributes, :maximum
- validates :maximum, numericality: true, allow_nil: true
+ validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0
def self.default_code; "3-VehicleJourney-3" end
end
diff --git a/app/models/vehicle_journey_control/speed.rb b/app/models/vehicle_journey_control/speed.rb
index 14fad9139..e5e331b50 100644
--- a/app/models/vehicle_journey_control/speed.rb
+++ b/app/models/vehicle_journey_control/speed.rb
@@ -2,8 +2,8 @@ module VehicleJourneyControl
class Speed < ComplianceControl
store_accessor :control_attributes, :minimum, :maximum
- validates :minimum, numericality: true, allow_nil: true
- validates :maximum, numericality: true, allow_nil: true
+ validates_numericality_of :minimum, allow_nil: true, greater_than_or_equal_to: 0
+ validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0
include MinMaxValuesValidation
def self.default_code; "3-VehicleJourney-2" end
diff --git a/app/models/workbench.rb b/app/models/workbench.rb
index e36589210..3190246ae 100644
--- a/app/models/workbench.rb
+++ b/app/models/workbench.rb
@@ -14,6 +14,7 @@ class Workbench < ActiveRecord::Base
has_many :workbench_imports
has_many :compliance_check_sets
has_many :compliance_control_sets
+ has_many :merges
validates :name, presence: true
validates :organisation, presence: true
@@ -29,7 +30,7 @@ class Workbench < ActiveRecord::Base
if line_ids.empty?
Referential.none
else
- Referential.joins(:metadatas).where(['referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids]).ready
+ Referential.joins(:metadatas).where(['referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids]).ready.not_in_referential_suite
end
end