diff options
51 files changed, 990 insertions, 217 deletions
| @@ -65,6 +65,7 @@ end  gem 'activerecord-postgis-adapter', "~> 3.0.0"  gem 'polylines' +gem 'activerecord-nulldb-adapter', require: false  # Codifligne API  gem 'codifligne', af83: 'stif-codifline-api' diff --git a/Gemfile.lock b/Gemfile.lock index f9682dff1..27d3c18eb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,6 +75,8 @@ GEM        activemodel (= 4.2.8)        activesupport (= 4.2.8)        arel (~> 6.0) +    activerecord-nulldb-adapter (0.3.7) +      activerecord (>= 2.0.0)      activerecord-postgis-adapter (3.0.0)        activerecord (~> 4.2)        rgeo-activerecord (~> 4.0) @@ -566,6 +568,7 @@ DEPENDENCIES    SyslogLogger    aasm    active_attr +  activerecord-nulldb-adapter    activerecord-postgis-adapter (~> 3.0.0)    acts-as-taggable-on (~> 4.0.0)    acts_as_list (~> 0.6.0) @@ -683,4 +686,4 @@ DEPENDENCIES    will_paginate-bootstrap  BUNDLED WITH -   1.15.4 +   1.16.0 diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee index a12c47576..1c39be736 100644 --- a/app/assets/javascripts/main_menu.coffee +++ b/app/assets/javascripts/main_menu.coffee @@ -24,6 +24,7 @@ $ ->      limit = 51      if $(window).scrollTop() >= limit +      data = ""        if ($('.page-action .small').length > 0)          data = $('.page-action .small')[0].innerHTML diff --git a/app/assets/stylesheets/components/_color_selector.sass b/app/assets/stylesheets/components/_color_selector.sass new file mode 100644 index 000000000..07bfa0c80 --- /dev/null +++ b/app/assets/stylesheets/components/_color_selector.sass @@ -0,0 +1,21 @@ +select.color_selector +  option[value='#9B9B9B'] +    background-color: #9B9B9B +  option[value='#FFA070'] +    background-color: #FFA070 +  option[value='#C67300'] +    background-color: #C67300 +  option[value='#7F551B'] +    background-color: #7F551B +  option[value='#41CCE3'] +    background-color: #41CCE3 +  option[value='#09B09C'] +    background-color: #09B09C +  option[value='#3655D7'] +    background-color: #3655D7 +  option[value='#6321A0'] +    background-color: #6321A0 +  option[value='#E796C6'] +    background-color: #E796C6 +  option[value='#DD2DAA'] +    background-color: #DD2DAA
\ No newline at end of file diff --git a/app/assets/stylesheets/typography/_sboiv.sass b/app/assets/stylesheets/typography/_sboiv.sass index f694306c4..0ed2890fa 100644 --- a/app/assets/stylesheets/typography/_sboiv.sass +++ b/app/assets/stylesheets/typography/_sboiv.sass @@ -89,7 +89,7 @@  .sb-OAS:before    content: '\e90f' -.sb-calendar:before +.sb-calendar:before, .sb-purchase_window:before    content: '\e910'  .sb-journey_pattern:before diff --git a/app/controllers/purchase_windows_controller.rb b/app/controllers/purchase_windows_controller.rb new file mode 100644 index 000000000..04b5736bb --- /dev/null +++ b/app/controllers/purchase_windows_controller.rb @@ -0,0 +1,75 @@ +class PurchaseWindowsController < ChouetteController +  include ReferentialSupport +  include RansackDateFilter +  include PolicyChecker +  before_action :ransack_contains_date, only: [:index] +  defaults :resource_class => Chouette::PurchaseWindow, collection_name: 'purchase_windows', instance_name: 'purchase_window' +  belongs_to :referential + +  requires_feature :purchase_windows + +  def index +    index! do +      @purchase_windows = decorate_purchase_windows(@purchase_windows) +    end +  end + +  def show +    show! do +      @purchase_window = @purchase_window.decorate(context: { +        referential: @referential +      }) +    end +  end + +  protected + +  def create_resource(purchase_window) +    purchase_window.referential = @referential +    super +  end + +  private + +  def purchase_window_params +    params.require(:purchase_window).permit(:id, :name, :color, :referential_id, periods_attributes: [:id, :begin, :end, :_destroy]) +  end + +  def decorate_purchase_windows(purchase_windows) +    ModelDecorator.decorate( +      purchase_windows, +      with: PurchaseWindowDecorator, +      context: { +        referential: @referential +        } +      ) +  end + +   def sort_column +    Chouette::PurchaseWindow.column_names.include?(params[:sort]) ? params[:sort] : 'name' +  end + +  def sort_direction +    %w[asc desc].include?(params[:direction]) ?  params[:direction] : 'asc' +  end + +  def collection +    return @purchase_windows if @purchase_windows +    @q = Chouette::PurchaseWindow.ransack(params[:q]) + +    purchase_windows = @q.result +    purchase_windows = purchase_windows.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction +    @purchase_windows = purchase_windows.paginate(page: params[:page]) +  end + +  def ransack_contains_date +    date =[] +    if params[:q] && params[:q]['contains_date(1i)'].present? +      ['contains_date(1i)', 'contains_date(2i)', 'contains_date(3i)'].each do |key| +        date << params[:q][key].to_i +        params[:q].delete(key) +      end +      params[:q]['contains_date'] = @date = Date.new(*date) rescue nil +    end +  end +end diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb new file mode 100644 index 000000000..13bc4d666 --- /dev/null +++ b/app/decorators/purchase_window_decorator.rb @@ -0,0 +1,34 @@ +class PurchaseWindowDecorator < Draper::Decorator +  decorates Chouette::PurchaseWindow +  delegate_all + +  def action_links +    policy = h.policy(object) +    links = [] + +    if policy.update? +      links << Link.new( +        content: I18n.t('actions.edit'), +        href: h.edit_referential_purchase_window_path(context[:referential].id, object) +      ) +    end + +    if policy.destroy? +      links << Link.new( +        content: I18n.t('actions.destroy'), +        href: h.referential_purchase_window_path(context[:referential].id, object), +        method: :delete, +        data: { confirm: h.t('purchase_window.actions.destroy_confirm') } +      ) +    end + +    links +  end + +  def bounding_dates +    unless object.date_ranges.empty? +      object.date_ranges.map(&:min).min..object.date_ranges.map(&:max).max +    end +  end + +end diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb index 0863b7f4b..d75ad1050 100644 --- a/app/decorators/referential_decorator.rb +++ b/app/decorators/referential_decorator.rb @@ -12,6 +12,13 @@ class ReferentialDecorator < Draper::Decorator        )      end +    if has_feature?(:purchase_windows) +      links << Link.new( +        content: h.t('purchase_windows.index.title'), +        href: h.referential_purchase_windows_path(object) +      ) +    end +      links << Link.new(        content: h.t('time_tables.index.title'),        href: h.referential_time_tables_path(object) diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb index a53ac5620..28f1ade76 100644 --- a/app/helpers/table_builder_helper/url.rb +++ b/app/helpers/table_builder_helper/url.rb @@ -10,7 +10,7 @@ module TableBuilderHelper            polymorph_url << item.route.line if item.is_a?(Chouette::RoutingConstraintZone)            polymorph_url << item if item.respond_to? :line_referential            polymorph_url << item.stop_area if item.respond_to? :stop_area -          polymorph_url << item if item.respond_to?(:stop_points) || item.is_a?(Chouette::TimeTable) +          polymorph_url << item if item.respond_to?(:stop_points) || item.is_a?(Chouette::TimeTable) || item.is_a?(Chouette::PurchaseWindow)          elsif item.respond_to? :referential            if item.respond_to? :workbench              polymorph_url << item.workbench diff --git a/app/models/calendar.rb b/app/models/calendar.rb index b2e73929f..34ed51374 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -3,22 +3,19 @@ require_relative 'calendar/date_value'  require_relative 'calendar/period'  class Calendar < ActiveRecord::Base +  include DateSupport +  include PeriodSupport +    has_paper_trail    belongs_to :organisation -  has_many :time_tables    validates_presence_of :name, :short_name, :organisation    validates_uniqueness_of :short_name -  after_initialize :init_dates_and_date_ranges +  has_many :time_tables    scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) } -  def init_dates_and_date_ranges -    self.dates ||= [] -    self.date_ranges ||= [] -  end -    def self.ransackable_scopes(auth_object = nil)      [:contains_date]    end @@ -35,144 +32,4 @@ class Calendar < ActiveRecord::Base      end    end - -  ### Calendar::Period -  # Required by coocon -  def build_period -    Calendar::Period.new -  end - -  def periods -    @periods ||= init_periods -  end - -  def init_periods -    (date_ranges || []) -      .each_with_index -      .map( &Calendar::Period.method(:from_range) ) -  end -  private :init_periods - -  validate :validate_periods - -  def validate_periods -    periods_are_valid = periods.all?(&:valid?) - -    periods.each do |period| -      if period.intersect?(periods) -        period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods')) -        periods_are_valid = false -      end -    end - -    unless periods_are_valid -      errors.add(:periods, :invalid) -    end -  end - -  def flatten_date_array attributes, key -    date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i } -    Date.new(*date_int) -  end - -  def periods_attributes=(attributes = {}) -    @periods = [] -    attributes.each do |index, period_attribute| -      # Convert date_select to date -      ['begin', 'end'].map do |attr| -        period_attribute[attr] = flatten_date_array(period_attribute, attr) -      end -      period = Calendar::Period.new(period_attribute.merge(id: index)) -      @periods << period unless period.marked_for_destruction? -    end - -    date_ranges_will_change! -  end - -  before_validation :fill_date_ranges - -  def fill_date_ranges -    if @periods -      self.date_ranges = @periods.map(&:range).compact.sort_by(&:begin) -    end -  end - -  after_save :clear_periods - -  def clear_periods -    @periods = nil -  end - -  private :clear_periods - -  ### Calendar::DateValue - -  # Required by coocon -  def build_date_value -    Calendar::DateValue.new -  end - -  def date_values -    @date_values ||= init_date_values -  end - -  def init_date_values -    if dates -      dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) } -    else -      [] -    end -  end -  private :init_date_values - -  validate :validate_date_values - -  def validate_date_values -    date_values_are_valid = date_values.all?(&:valid?) - -    date_values.each do |date_value| -      if date_values.count { |d| d.value == date_value.value } > 1 -        date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates')) -        date_values_are_valid = false -      end -      date_ranges.each do |date_range| -        if date_range.cover? date_value.value -          date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) -          date_values_are_valid = false -        end -      end -    end - -    unless date_values_are_valid -      errors.add(:date_values, :invalid) -    end -  end - -  def date_values_attributes=(attributes = {}) -    @date_values = [] -    attributes.each do |index, date_value_attribute| -      date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value') -      date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index)) -      @date_values << date_value unless date_value.marked_for_destruction? -    end - -    dates_will_change! -  end - -  before_validation :fill_dates - -  def fill_dates -    if @date_values -      self.dates = @date_values.map(&:value).compact.sort -    end -  end - -  after_save :clear_date_values - -  def clear_date_values -    @date_values = nil -  end - -  private :clear_date_values -  end diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb new file mode 100644 index 000000000..5368d790a --- /dev/null +++ b/app/models/chouette/purchase_window.rb @@ -0,0 +1,31 @@ +require 'range_ext' +require_relative '../calendar/period' + +module Chouette +  class PurchaseWindow < Chouette::TridentActiveRecord +    # include ChecksumSupport +    include ObjectidSupport +    include PeriodSupport +    extend Enumerize +    enumerize :color, in: %w(#9B9B9B #FFA070 #C67300 #7F551B #41CCE3 #09B09C #3655D7 #6321A0 #E796C6 #DD2DAA) + +    has_paper_trail +    belongs_to :referential + +    validates_presence_of :name, :referential + +    scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) } + +  def self.ransackable_scopes(auth_object = nil) +    [:contains_date] +  end + +    def local_id +      "IBOO-#{self.referential.id}-#{self.id}" +    end + +    # def checksum_attributes +    # end + +  end +end
\ No newline at end of file diff --git a/app/models/concerns/date_support.rb b/app/models/concerns/date_support.rb new file mode 100644 index 000000000..fbfe19af1 --- /dev/null +++ b/app/models/concerns/date_support.rb @@ -0,0 +1,80 @@ +module DateSupport +  extend ActiveSupport::Concern + +  included do +    after_initialize :init_dates + +    def init_dates +      self.dates ||= [] +    end + +    ### Calendar::DateValue +    # Required by coocon +    def build_date_value +      Calendar::DateValue.new +    end + +    def date_values +      @date_values ||= init_date_values +    end + +    def init_date_values +      if dates +        dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) } +      else +        [] +      end +    end +    private :init_date_values + +    validate :validate_date_values + +    def validate_date_values +      date_values_are_valid = date_values.all?(&:valid?) + +      date_values.each do |date_value| +        if date_values.count { |d| d.value == date_value.value } > 1 +          date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates')) +          date_values_are_valid = false +        end +        date_ranges.each do |date_range| +          if date_range.cover? date_value.value +            date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) +            date_values_are_valid = false +          end +        end +      end + +      unless date_values_are_valid +        errors.add(:date_values, :invalid) +      end +    end + +    def date_values_attributes=(attributes = {}) +      @date_values = [] +      attributes.each do |index, date_value_attribute| +        date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value') +        date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index)) +        @date_values << date_value unless date_value.marked_for_destruction? +      end + +      dates_will_change! +    end + +    before_validation :fill_dates + +    def fill_dates +      if @date_values +        self.dates = @date_values.map(&:value).compact.sort +      end +    end + +    after_save :clear_date_values + +    def clear_date_values +      @date_values = nil +    end + +    private :clear_date_values +  end +end
\ No newline at end of file diff --git a/app/models/concerns/period_support.rb b/app/models/concerns/period_support.rb new file mode 100644 index 000000000..f512c4e89 --- /dev/null +++ b/app/models/concerns/period_support.rb @@ -0,0 +1,80 @@ +module PeriodSupport +  extend ActiveSupport::Concern + +  included do +    after_initialize :init_date_ranges + +    def init_date_ranges +      self.date_ranges ||= [] +    end +     +    ### Calendar::Period +    # Required by coocon +    def build_period +      Calendar::Period.new +    end + +    def periods +      @periods ||= init_periods +    end + +    def init_periods +      (date_ranges || []) +        .each_with_index +        .map( &Calendar::Period.method(:from_range) ) +    end +    private :init_periods + +    validate :validate_periods + +    def validate_periods +      periods_are_valid = periods.all?(&:valid?) + +      periods.each do |period| +        if period.intersect?(periods) +          period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods')) +          periods_are_valid = false +        end +      end + +      unless periods_are_valid +        errors.add(:periods, :invalid) +      end +    end + +    def flatten_date_array attributes, key +      date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i } +      Date.new(*date_int) +    end + +    def periods_attributes=(attributes = {}) +      @periods = [] +      attributes.each do |index, period_attribute| +        # Convert date_select to date +        ['begin', 'end'].map do |attr| +          period_attribute[attr] = flatten_date_array(period_attribute, attr) +        end +        period = Calendar::Period.new(period_attribute.merge(id: index)) +        @periods << period unless period.marked_for_destruction? +      end + +      date_ranges_will_change! +    end + +    before_validation :fill_date_ranges + +    def fill_date_ranges +      if @periods +        self.date_ranges = @periods.map(&:range).compact.sort_by(&:begin) +      end +    end + +    after_save :clear_periods + +    def clear_periods +      @periods = nil +    end + +    private :clear_periods +  end +end
\ No newline at end of file diff --git a/app/models/referential.rb b/app/models/referential.rb index 851a33653..122af65a1 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -128,6 +128,10 @@ class Referential < ActiveRecord::Base      Chouette::RoutingConstraintZone.all    end +  def purchase_windows +    Chouette::PurchaseWindow.all +  end +    before_validation :define_default_attributes    def define_default_attributes diff --git a/app/policies/purchase_window_policy.rb b/app/policies/purchase_window_policy.rb new file mode 100644 index 000000000..75143a8bd --- /dev/null +++ b/app/policies/purchase_window_policy.rb @@ -0,0 +1,20 @@ +class PurchaseWindowPolicy < ApplicationPolicy +  class Scope < Scope +    def resolve +      scope +    end +  end + +  def create? +     !archived? && organisation_match? && user.has_permission?('purchase_windows.create') +  end + +  def update? +    !archived? && organisation_match? && user.has_permission?('purchase_windows.update') +  end + +  def destroy? +    !archived? && organisation_match? && user.has_permission?('purchase_windows.destroy') +  end + +end
\ No newline at end of file diff --git a/app/views/compliance_control_sets/index.html.slim b/app/views/compliance_control_sets/index.html.slim index 2a5651280..28d2254bf 100644 --- a/app/views/compliance_control_sets/index.html.slim +++ b/app/views/compliance_control_sets/index.html.slim @@ -30,7 +30,7 @@                  ), \                  TableBuilderHelper::Column.new( \                    key: :control_numbers, \ -                  attribute: 'control_numbers' \ +                  attribute: Proc.new {|n| n.compliance_controls.count }\                  ), \                  TableBuilderHelper::Column.new( \                    key: :updated_at, \ diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim index 7d547bf4c..075b94ddc 100644 --- a/app/views/dashboards/_dashboard.html.slim +++ b/app/views/dashboards/_dashboard.html.slim @@ -31,21 +31,24 @@              = link_to calendar.name, calendar_path(calendar), class: 'list-group-item'        - else          .panel-body -          em.small.text-muted Aucun modèle de calendrier défini +          em.small.text-muted +            = t('dasboard.calendars.none')    .col-lg-6.col-md-6.col-sm-6.col-xs-12      .panel.panel-default -      .panel-heading -        h3.panel-title -          = "Référentiels d'arrêts" -      .list-group -        - @dashboard.current_organisation.stop_area_referentials.each do |referential| -          = link_to referential.name, stop_area_referential_stop_areas_path(referential), class: 'list-group-item' +      - @dashboard.current_organisation.stop_area_referentials.each do |referential| +        .panel-heading +          h3.panel-title +            = referential.name +        .list-group +          = link_to Chouette::StopArea.model_name.human.pluralize.capitalize, stop_area_referential_stop_areas_path(referential), class: 'list-group-item'      .panel.panel-default -      .panel-heading -        h3.panel-title -          = "Référentiels de lignes" -      .list-group -        - @dashboard.current_organisation.line_referentials.all.each do |referential| -          = link_to referential.name, line_referential_lines_path(referential), class: 'list-group-item' +      - @dashboard.current_organisation.line_referentials.all.each do |referential| +        .panel-heading +          h3.panel-title +            = referential.name +        .list-group +            = link_to Chouette::Line.model_name.human.pluralize.capitalize, line_referential_lines_path(referential), class: 'list-group-item' +            = link_to Chouette::Company.model_name.human.pluralize.capitalize, line_referential_companies_path(referential), class: 'list-group-item' +            = link_to "Réseaux", line_referential_networks_path(referential), class: 'list-group-item' diff --git a/app/views/purchase_windows/_date_value_fields.html.slim b/app/views/purchase_windows/_date_value_fields.html.slim new file mode 100644 index 000000000..7bde06a94 --- /dev/null +++ b/app/views/purchase_windows/_date_value_fields.html.slim @@ -0,0 +1,13 @@ +.nested-fields +  - if f.object.errors.has_key? :base +    .row +      .col-lg-12 +        .alert.alert-danger +          - f.object.errors[:base].each do |message| +            p.small = message + +  .wrapper +    div +      = f.input :value, as: :date, label: false, wrapper_html: { class: 'date smart_date' } +    div +      = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete') diff --git a/app/views/purchase_windows/_filters.html.slim b/app/views/purchase_windows/_filters.html.slim new file mode 100644 index 000000000..4d7c8ce26 --- /dev/null +++ b/app/views/purchase_windows/_filters.html.slim @@ -0,0 +1,15 @@ += search_form_for @q, url: referential_purchase_windows_path, builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f| +  .ffg-row +    .input-group.search_bar +      = f.search_field :name_cont, class: 'form-control', placeholder: t('purchase_windows.index.filter_placeholder') +      span.input-group-btn +        button.btn.btn-default#search_btn type='submit' +          span.fa.fa-search + +    .form-group +      = f.label Chouette::PurchaseWindow.human_attribute_name(:date), class: 'control-label' +      = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', default: @date, include_blank: @date ? false : true + +  .actions +    = link_to t('actions.erase'), referential_purchase_windows_path, class: 'btn btn-link' +    = f.submit t('actions.filter'), id: 'purchase_window_filter_btn', class: 'btn btn-default' diff --git a/app/views/purchase_windows/_form.html.slim b/app/views/purchase_windows/_form.html.slim new file mode 100644 index 000000000..8821ecc8a --- /dev/null +++ b/app/views/purchase_windows/_form.html.slim @@ -0,0 +1,37 @@ += simple_form_for [@referential, @purchase_window], html: { class: 'form-horizontal', id: 'purchase_window_form' }, wrapper: :horizontal_form do |f| +  .row +    .col-lg-12 +      = f.input :name +      // = f.input :color, as: :select, boolean_style: :inline, collection: Chouette::PurchaseWindow.color.values, input_html: {class: 'color_selector '} +      div +        .form-group +          label.select.optional.col-sm-4.col-xs-5.control-label +            = @purchase_window.class.human_attribute_name :color +          div.col-sm-8.col-xs-7 +            span.fa.fa-circle style="color:#7F551B" + +      = f.input :color, as: :hidden, input_html: { value: '#7F551B' } + +  .separator + +  .row +    .col-lg-12 +      .subform +        .nested-head +          .wrapper +            div +              .form-group +                label.control-label +                  = t('simple_form.labels.purchase_window.ranges.begin') +            div +              .form-group +                label.control-label +                  = t('simple_form.labels.purchase_window.ranges.end') +            div + +        = f.simple_fields_for :periods do |period| +          = render 'period_fields', f: period +        .links.nested-linker +          = link_to_add_association t('simple_form.labels.purchase_window.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary' + +  = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'purchase_window_form' diff --git a/app/views/purchase_windows/_period_fields.html.slim b/app/views/purchase_windows/_period_fields.html.slim new file mode 100644 index 000000000..95e204554 --- /dev/null +++ b/app/views/purchase_windows/_period_fields.html.slim @@ -0,0 +1,15 @@ +.nested-fields +  - if f.object.errors.has_key? :base +    .row +      .col-lg-12 +        .alert.alert-danger +          - f.object.errors[:base].each do |message| +            p.small = message + +  .wrapper +    div +      = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date smart_date' } +    div +      = f.input :end, as: :date, label: false, wrapper_html: { class: 'date smart_date' } +    div +      = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete') diff --git a/app/views/purchase_windows/edit.html.slim b/app/views/purchase_windows/edit.html.slim new file mode 100644 index 000000000..6354db853 --- /dev/null +++ b/app/views/purchase_windows/edit.html.slim @@ -0,0 +1,7 @@ +- breadcrumb :purchase_window, @referential, @purchase_window +- page_header_content_for @purchase_window +.page_content +  .container-fluid +    .row +      .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 +        = render 'form' diff --git a/app/views/purchase_windows/index.html.slim b/app/views/purchase_windows/index.html.slim new file mode 100644 index 000000000..04f9fb0a8 --- /dev/null +++ b/app/views/purchase_windows/index.html.slim @@ -0,0 +1,45 @@ +- breadcrumb :purchase_windows, @referential +- content_for :page_header_actions do +  - if policy(Chouette::PurchaseWindow).create? +    = link_to(t('actions.add'), new_referential_purchase_window_path(@referential), class: 'btn btn-default') + +.page_content +  .container-fluid +    - if params[:q].present? or @purchase_windows.any? +      .row +        .col-lg-12 +          = render 'filters' + +    - if @purchase_windows.any? +      .row +        .col-lg-12 +          = table_builder_2 @purchase_windows, +            [ \ +              TableBuilderHelper::Column.new( \ +                key: :name, \ +                attribute: 'name', \ +                link_to: lambda do |purchase_window| \ +                  referential_purchase_window_path(purchase_window.referential, purchase_window) \ +                end \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :color, \ +                attribute: Proc.new { |tt| tt.color ? content_tag(:span, '', class: 'fa fa-circle', style: "color:#{tt.color}") : '-' }\ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :bounding_dates, \ +                attribute: Proc.new {|w| w.bounding_dates.nil? ? '-' : t('validity_range', debut: l(w.bounding_dates.begin, format: :short), end: l(w.bounding_dates.end, format: :short))},  \ +                sortable: false \ +              ) \ +            ], +            links: [:show], +            cls: 'table has-filter' + +          = new_pagination @purchase_windows, 'pull-right' + +    - unless @purchase_windows.any? +      .row.mt-xs +        .col-lg-12 +          = replacement_msg t('purchase_windows.search_no_results') + += javascript_pack_tag 'date_filters' diff --git a/app/views/purchase_windows/new.html.slim b/app/views/purchase_windows/new.html.slim new file mode 100644 index 000000000..402084167 --- /dev/null +++ b/app/views/purchase_windows/new.html.slim @@ -0,0 +1,6 @@ +- breadcrumb :purchase_windows, @referential +.page_content +  .container-fluid +    .row +      .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 +        = render 'form' diff --git a/app/views/purchase_windows/show.html.slim b/app/views/purchase_windows/show.html.slim new file mode 100644 index 000000000..9f3abb267 --- /dev/null +++ b/app/views/purchase_windows/show.html.slim @@ -0,0 +1,20 @@ +- breadcrumb :purchase_window, @referential, @purchase_window +- page_header_content_for @purchase_window +- content_for :page_header_content do +  .row.mb-sm +    .col-lg-12.text-right +      - @purchase_window.action_links.each do |link| +        = link_to link.href, +            method: link.method, +            data: link.data, +            class: 'btn btn-primary' do +              = link.content + +.page_content +  .container-fluid +    .row +      .col-lg-6.col-md-6.col-sm-12.col-xs-12 +        = definition_list t('metadatas'), +          { Chouette::PurchaseWindow.human_attribute_name(:name) => @purchase_window.try(:name), +            'Organisation' => @purchase_window.referential.organisation.name, +            Chouette::PurchaseWindow.human_attribute_name(:date_ranges) => @purchase_window.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } diff --git a/app/views/referentials/_filters.html.slim b/app/views/referentials/_filters.html.slim index c5b6042f0..93fa679df 100644 --- a/app/views/referentials/_filters.html.slim +++ b/app/views/referentials/_filters.html.slim @@ -12,11 +12,11 @@        = f.input :transport_mode_eq_any, collection: @referential.lines.pluck(:transport_mode).uniq.compact, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + t("enumerize.transport_mode.#{l}") + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }      .form-group.togglable -      = f.label Chouette::Line.human_attribute_name(:network), required: false, class: 'control-label' +      = f.label  t('activerecord.attributes.referential.networks'), required: false, class: 'control-label'        = f.input :network_id_eq_any, collection: LineReferential.first.networks.order('name').pluck(:id), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.networks.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }      .form-group.togglable -      = f.label Chouette::Line.human_attribute_name(:company), required: false, class: 'control-label' +      = f.label t('activerecord.attributes.referential.companies'), required: false, class: 'control-label'        = f.input :company_id_eq_any, collection: LineReferential.first.companies.order('name').pluck(:id), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.companies.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }    .actions diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index 52c1a9728..4ad9d524d 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -1,5 +1,11 @@  - breadcrumb :vehicle_journeys, @referential, @route  - content_for :page_header_title, t('vehicle_journeys.index.title', route: @route.name) +- if @route.opposite_route.present? +  - content_for :page_header_content do +    .row.mb-sm +     .col-lg-12.text-right +       = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary') +  .page_content    .container-fluid diff --git a/config/breadcrumbs.rb b/config/breadcrumbs.rb index 9a748cb21..2cafc4419 100644 --- a/config/breadcrumbs.rb +++ b/config/breadcrumbs.rb @@ -167,6 +167,16 @@ crumb :line do |line|    parent :lines, line.line_referential  end +crumb :purchase_windows do |referential| +  link I18n.t('purchase_windows.index.title'), referential_purchase_windows_path(referential) +  parent :referential, referential +end + +crumb :purchase_window do |referential, purchase_window| +  link breadcrumb_name(purchase_window), referential_purchase_window_path(referential, purchase_window) +  parent :purchase_windows, referential +end +  crumb :calendars do    link I18n.t('calendars.index.title'), calendars_path  end diff --git a/config/initializers/apartment_null_db.rb b/config/initializers/apartment_null_db.rb new file mode 100644 index 000000000..438f1e58b --- /dev/null +++ b/config/initializers/apartment_null_db.rb @@ -0,0 +1,25 @@ +if ENV['RAILS_DB_ADAPTER'] == 'nulldb' +  require 'apartment/adapters/abstract_adapter' + +  module Apartment +    module Tenant +      def adapter +        Thread.current[:apartment_adapter] ||= nulldb_adapter(config) +      end + +      def self.nulldb_adapter(config) +        adapter = Adapters::NulldbAdapter +        adapter.new(config) +      end +    end + +    module Adapters +      # Default adapter when not using Postgresql Schemas +      class NulldbAdapter < AbstractAdapter +        def initialize config +          super +        end +      end +    end +  end +end diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml index d3cc57677..a2110d053 100644 --- a/config/locales/calendars.en.yml +++ b/config/locales/calendars.en.yml @@ -41,6 +41,8 @@ en:        date: Date      new:        title: Add a new calendar +    create: +      title: Add a new calendar      edit:        title: Update calendar %{name}      show: diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml index fc895bf89..88cb275ff 100644 --- a/config/locales/calendars.fr.yml +++ b/config/locales/calendars.fr.yml @@ -31,7 +31,7 @@ fr:        destroy_confirm: Etes vous sûr de supprimer cet calendrier ?      errors:        overlapped_periods: Une autre période chevauche cette période -      short_period: Une période doit être d'un duréé de deux jours minimum +      short_period: "Une période doit être d'une durée de deux jours minimum"      index:        title: Calendriers        all: Tous @@ -41,6 +41,8 @@ fr:        date: Date      new:        title: Ajouter un calendrier +    create: +      title: Ajouter un calendrier      edit:        title: Editer le calendrier %{name}      show: diff --git a/config/locales/compliance_controls.en.yml b/config/locales/compliance_controls.en.yml index f9d7d23d2..3392afab5 100644 --- a/config/locales/compliance_controls.en.yml +++ b/config/locales/compliance_controls.en.yml @@ -89,7 +89,7 @@ en:      vehicle_journey_control/waiting_time:        messages:          3_vehiclejourney_1: "On the following vehicle journey %{source_objectid}, the waiting time %{error_value} a this stop point %{target_0_label} (%{target_0_objectid}) is greater than the threshold (%{reference_value})" -      description: "The waiting time at a specific stop point cannot be too big" +      description: "The waiting time, in minutes, at a specific stop point cannot be too big"      vehicle_journey_control/speed:        messages:          3_vehiclejourney_2_1: "On the following vehicle journey %{source_objectid}, the computed speed %{error_value} between the stop points %{target_0_label} (%{target_0_objectid}) and %{target_1_label} (%{target_1_objectid}) is greater than the threshold (%{reference_value})" diff --git a/config/locales/compliance_controls.fr.yml b/config/locales/compliance_controls.fr.yml index b77b4e6d4..cde75aaf5 100644 --- a/config/locales/compliance_controls.fr.yml +++ b/config/locales/compliance_controls.fr.yml @@ -88,7 +88,7 @@ fr:      vehicle_journey_control/waiting_time:        messages:          3_vehiclejourney_1: "Sur la course %{source_objectid}, le temps d'attente %{error_value} à l'arrêt %{target_0_label} (%{target_0_objectid}) est supérieur au seuil toléré (%{reference_value})" -      description: "La durée d’attente à un arrêt ne doit pas être trop grande" +      description: "La durée d’attente, en minutes, à un arrêt ne doit pas être trop grande"      vehicle_journey_control/speed:        messages:          3_vehiclejourney_2_1: "Sur la course %{source_objectid}, la vitesse calculée %{error_value} entre les arrêts %{target_0_label} (%{target_0_objectid}) et %{target_1_label} (%{target_1_objectid}) est supérieur au seuil toléré (%{reference_value})" diff --git a/config/locales/dashboard.en.yml b/config/locales/dashboard.en.yml new file mode 100644 index 000000000..8d46ff7aa --- /dev/null +++ b/config/locales/dashboard.en.yml @@ -0,0 +1,16 @@ +en: +  dashboards: +    show: +      title: "Dashboard %{organisation}" +    calendars: +      title: Calendars +      none: No calendar created +    purchase_windows: +      title: Purchase windows +      none: No purchase window created +    line_referentials: +      title: Line referential +      none: No line referential created +    stop_area_referentials: +      title: Stop area referential +      none: No stop area referential created diff --git a/config/locales/dashboard.fr.yml b/config/locales/dashboard.fr.yml index fffb36cd1..d0aa36d61 100644 --- a/config/locales/dashboard.fr.yml +++ b/config/locales/dashboard.fr.yml @@ -2,3 +2,15 @@ fr:    dashboards:      show:        title: "Tableau de bord %{organisation}" +    calendars: +      title: Modèles de calendrier +      none: Aucun calendrier défini +    purchase_windows: +      title: Calendriers commerciaux +      none: Aucun calendrier commercial défini +    line_referentials: +      title: Référentiels de lignes +      none: Aucun référentiels de lignes défini +    stop_area_referentials: +      title: Référentiels d'arrêts +      none: Aucun référentiels d'arrêts défini diff --git a/config/locales/purchase_windows.en.yml b/config/locales/purchase_windows.en.yml new file mode 100644 index 000000000..f8f3eb83d --- /dev/null +++ b/config/locales/purchase_windows.en.yml @@ -0,0 +1,78 @@ +en: +  purchase_windows: +    search_no_results: 'No purchase window matching your query' +    days: +      monday: M +      tuesday: Tu +      wednesday: W +      thursday: Th +      friday: F +      saturday: Sa +      sunday: Su +    months: +      1: January +      2: February +      3: March +      4: April +      5: May +      6: June +      7: July +      8: August +      9: September +      10: October +      11: November +      12: December +    actions: +      new: Add a new purchase window +      edit: Edit this purchase window +      destroy: Remove this purchase window +      destroy_confirm: Are you sure you want destroy this purchase window? +    errors: +      overlapped_periods: Another period is overlapped with this period +      short_period: A period needs to last at least two days +    index: +      title: purchase windows +      all: All +      shared: Shared +      not_shared: Not shared +      search_no_results: No purchase window matching your query +      date: Date +      filter_placeholder: Put the name of a purchase window... +    create: +      title: Add a new purchase window +    new: +      title: Add a new purchase window +    edit: +      title: Update purchase window %{name} +    show: +      title: purchase window %{name} +  simple_form: +    labels: +      purchase_windows: +        date_value: Date +        add_a_date: Add a date +        add_a_date_range: Add a date range +        ranges: +          begin: Beginning +          end: End +  activerecord: +    models: +      purchase_window: +        zero: purchase window +        one: purchase window +        other: purchase windows +    attributes: +      purchase_windows: +        name: Name +        date_ranges: Date ranges +        referential: Referential +        color: Associated Color +        bounding_dates: Bounding Dates +    errors: +      models: +        purchase_windows: +          attributes: +            dates: +              date_in_date_ranges: A date can not be in Dates and in Date ranges. +              date_in_dates: A date can appear only once in the list of dates. +              illegal_date: The date %{date} does not exist. diff --git a/config/locales/purchase_windows.fr.yml b/config/locales/purchase_windows.fr.yml new file mode 100644 index 000000000..589546c32 --- /dev/null +++ b/config/locales/purchase_windows.fr.yml @@ -0,0 +1,79 @@ +fr: +  purchase_windows: +    search_no_results: 'Aucun calendrier commercial ne correspond à votre recherche' +    days: +      monday: L +      tuesday: Ma +      wednesday: Me +      thursday: J +      friday: V +      saturday: S +      sunday: D +    months: +      1: Janvier +      2: Février +      3: Mars +      4: Avril +      5: Mai +      6: Juin +      7: Juillet +      8: Août +      9: Septembre +      10: Octobre +      11: Novembre +      12: Décembre +    actions: +      new: Créer +      edit: Editer +      destroy: Supprimer +      destroy_confirm: Etes vous sûr de supprimer cet calendrier commercial ? +    errors: +      overlapped_periods: Une autre période chevauche cette période +      short_period: "Une période doit être d'une durée de deux jours minimum" +    index: +      title: Calendriers commerciaux +      all: Tous +      shared: Partagées +      not_shared: Non partagées +      search_no_results: Aucun calendrier commercial ne correspond à votre recherche +      date: Date +      filter_placeholder: Indiquez un nom de calendrier commercial... +    new: +      title: Ajouter un calendrier commercial +    create: +      title: Ajouter un calendrier commercial +    edit: +      title: Editer le calendrier commercial %{name} +    show: +      title: Calendrier commercial %{name} +  simple_form: +    labels: +      purchase_window: +        date_value: Date +        add_a_date: Ajouter une date +        add_a_date_range: Ajouter un intervalle de dates +        ranges: +          begin: Début +          end: Fin +  activerecord: +    models: +      purchase_window: +        zero: "calendrier commercial" +        one: "calendrier commercial" +        other: "calendriers commerciaux" +    attributes: +      purchase_window: +        name: Nom +        short_name: Nom court +        date_ranges: Intervalles de dates +        referential: Jeu de données +        color: Couleur associée +        bounding_dates: Période englobante +    errors: +      models: +        purchase_window: +          attributes: +            dates: +              date_in_date_ranges: Une même date ne peut pas être incluse à la fois dans la liste et dans les intervalles de dates. +              date_in_dates: Une même date ne peut pas être incluse plusieurs fois dans la liste. +              illegal_date: La date %{date} n'existe pas. diff --git a/config/locales/routes.en.yml b/config/locales/routes.en.yml index d82ba98dd..7b82e788b 100644 --- a/config/locales/routes.en.yml +++ b/config/locales/routes.en.yml @@ -13,6 +13,7 @@ en:        export_hub_all: "Export HUB routes"        add_stop_point: "Add stop point"        new_stop_point: "Create new stop" +      opposite_route_timetable: "Timetable back"      new:        title: "Add a new route"      edit: diff --git a/config/locales/routes.fr.yml b/config/locales/routes.fr.yml index 457345ae8..1d151e60b 100644 --- a/config/locales/routes.fr.yml +++ b/config/locales/routes.fr.yml @@ -13,6 +13,7 @@ fr:        export_hub_all: "Export HUB des itinéraires"        add_stop_point: "Ajouter un arrêt"        new_stop_point: "Créer un arrêt pour l'ajouter" +      opposite_route_timetable: "Horaires retour"      new:        title: "Ajouter un itinéraire"      edit: diff --git a/config/routes.rb b/config/routes.rb index 6668aa402..d097d2d71 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -173,6 +173,8 @@ ChouetteIhm::Application.routes.draw do      resources :companies, controller: "referential_companies" +    resources :purchase_windows +      resources :time_tables do        collection do          get :tags diff --git a/db/migrate/20171214131755_create_business_calendars.rb b/db/migrate/20171214131755_create_business_calendars.rb new file mode 100644 index 000000000..aa7c1ab12 --- /dev/null +++ b/db/migrate/20171214131755_create_business_calendars.rb @@ -0,0 +1,14 @@ +class CreateBusinessCalendars < ActiveRecord::Migration +  def change +    create_table :business_calendars do |t| +      t.string :name +      t.string :short_name +      t.string :color +      t.daterange :date_ranges, array: true +      t.date :dates, array: true +      t.belongs_to :organisation, index: true + +      t.timestamps null: false +    end +  end +end diff --git a/db/migrate/20171215144543_rename_business_calendars_to_purchase_windows.rb b/db/migrate/20171215144543_rename_business_calendars_to_purchase_windows.rb new file mode 100644 index 000000000..d4467d6a7 --- /dev/null +++ b/db/migrate/20171215144543_rename_business_calendars_to_purchase_windows.rb @@ -0,0 +1,5 @@ +class RenameBusinessCalendarsToPurchaseWindows < ActiveRecord::Migration +  def change +    rename_table :business_calendars, :purchase_windows +  end +end diff --git a/db/migrate/20171215145023_update_purchase_windows_attributes.rb b/db/migrate/20171215145023_update_purchase_windows_attributes.rb new file mode 100644 index 000000000..48dfb15bc --- /dev/null +++ b/db/migrate/20171215145023_update_purchase_windows_attributes.rb @@ -0,0 +1,13 @@ +class UpdatePurchaseWindowsAttributes < ActiveRecord::Migration +  def change +    add_column :purchase_windows, :objectid, :string +    add_column :purchase_windows, :checksum, :string +    add_column :purchase_windows, :checksum_source, :text + +    remove_column :purchase_windows, :short_name, :string +    remove_column :purchase_windows, :dates, :date +    remove_column :purchase_windows, :organisation_id, :integer + +    add_reference :purchase_windows, :referential, type: :bigint, index: true +  end +end diff --git a/db/schema.rb b/db/schema.rb index f5532ec57..81966575b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -281,22 +281,6 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_index "connection_links", ["objectid"], name: "connection_links_objectid_key", unique: true, using: :btree -  create_table "delayed_jobs", id: :bigserial, force: :cascade do |t| -    t.integer  "priority",               default: 0 -    t.integer  "attempts",               default: 0 -    t.text     "handler" -    t.text     "last_error" -    t.datetime "run_at" -    t.datetime "locked_at" -    t.datetime "failed_at" -    t.string   "locked_by",  limit: 255 -    t.string   "queue",      limit: 255 -    t.datetime "created_at" -    t.datetime "updated_at" -  end - -  add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree -    create_table "exports", id: :bigserial, force: :cascade do |t|      t.integer  "referential_id",  limit: 8      t.string   "status" @@ -416,11 +400,11 @@ ActiveRecord::Schema.define(version: 20171220164059) do      t.datetime "started_at"      t.datetime "ended_at"      t.string   "token_download" -    t.string   "type",                  limit: 255 +    t.string   "type"      t.integer  "parent_id",             limit: 8      t.string   "parent_type" -    t.integer  "current_step",                      default: 0 -    t.integer  "total_steps",                       default: 0 +    t.integer  "current_step",                    default: 0 +    t.integer  "total_steps",                     default: 0      t.datetime "notified_parent_at"      t.string   "creator"    end @@ -538,19 +522,6 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_index "lines", ["registration_number"], name: "lines_registration_number_key", using: :btree    add_index "lines", ["secondary_company_ids"], name: "index_lines_on_secondary_company_ids", using: :gin -  create_table "merges", id: :bigserial, force: :cascade do |t| -    t.integer  "workbench_id",    limit: 8 -    t.integer  "referential_ids", limit: 8,              array: true -    t.string   "creator" -    t.string   "status" -    t.datetime "started_at" -    t.datetime "ended_at" -    t.datetime "created_at",                null: false -    t.datetime "updated_at",                null: false -  end - -  add_index "merges", ["workbench_id"], name: "index_merges_on_workbench_id", using: :btree -    create_table "networks", id: :bigserial, force: :cascade do |t|      t.string   "objectid",                      null: false      t.integer  "object_version",      limit: 8 @@ -572,11 +543,6 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_index "networks", ["objectid"], name: "networks_objectid_key", unique: true, using: :btree    add_index "networks", ["registration_number"], name: "networks_registration_number_key", using: :btree -  create_table "object_id_factories", id: :bigserial, force: :cascade do |t| -    t.datetime "created_at", null: false -    t.datetime "updated_at", null: false -  end -    create_table "organisations", id: :bigserial, force: :cascade do |t|      t.string   "name"      t.datetime "created_at" @@ -606,6 +572,20 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_index "pt_links", ["objectid"], name: "pt_links_objectid_key", unique: true, using: :btree +  create_table "purchase_windows", id: :bigserial, force: :cascade do |t| +    t.string    "name" +    t.string    "color" +    t.daterange "date_ranges",                            array: true +    t.datetime  "created_at",                null: false +    t.datetime  "updated_at",                null: false +    t.string    "objectid" +    t.string    "checksum" +    t.text      "checksum_source" +    t.integer   "referential_id",  limit: 8 +  end + +  add_index "purchase_windows", ["referential_id"], name: "index_purchase_windows_on_referential_id", using: :btree +    create_table "referential_clonings", id: :bigserial, force: :cascade do |t|      t.string   "status"      t.datetime "started_at" @@ -744,7 +724,7 @@ ActiveRecord::Schema.define(version: 20171220164059) do    create_table "stop_areas", id: :bigserial, force: :cascade do |t|      t.integer  "parent_id",                       limit: 8 -    t.string   "objectid",                                                              null: false +    t.string   "objectid",                                                            null: false      t.integer  "object_version",                  limit: 8      t.string   "name"      t.string   "comment" @@ -752,8 +732,8 @@ ActiveRecord::Schema.define(version: 20171220164059) do      t.string   "registration_number"      t.string   "nearest_topic_name"      t.integer  "fare_code" -    t.decimal  "longitude",                                   precision: 19, scale: 16 -    t.decimal  "latitude",                                    precision: 19, scale: 16 +    t.decimal  "longitude",                                 precision: 19, scale: 16 +    t.decimal  "latitude",                                  precision: 19, scale: 16      t.string   "long_lat_type"      t.string   "country_code"      t.string   "street_name" @@ -771,7 +751,7 @@ ActiveRecord::Schema.define(version: 20171220164059) do      t.datetime "deleted_at"      t.datetime "created_at"      t.datetime "updated_at" -    t.string   "stif_type",                       limit: 255 +    t.string   "stif_type"      t.integer  "waiting_time"    end @@ -842,17 +822,17 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_index "time_table_periods", ["time_table_id"], name: "index_time_table_periods_on_time_table_id", using: :btree    create_table "time_tables", id: :bigserial, force: :cascade do |t| -    t.string   "objectid",                                null: false -    t.integer  "object_version",  limit: 8,   default: 1 +    t.string   "objectid",                              null: false +    t.integer  "object_version",  limit: 8, default: 1      t.string   "version"      t.string   "comment" -    t.integer  "int_day_types",               default: 0 +    t.integer  "int_day_types",             default: 0      t.date     "start_date"      t.date     "end_date"      t.integer  "calendar_id",     limit: 8      t.datetime "created_at"      t.datetime "updated_at" -    t.string   "color",           limit: 255 +    t.string   "color"      t.integer  "created_from_id", limit: 8      t.string   "checksum"      t.text     "checksum_source" @@ -1007,9 +987,7 @@ ActiveRecord::Schema.define(version: 20171220164059) do    add_foreign_key "compliance_controls", "compliance_control_blocks"    add_foreign_key "compliance_controls", "compliance_control_sets"    add_foreign_key "group_of_lines_lines", "group_of_lines", name: "groupofline_group_fkey", on_delete: :cascade -  add_foreign_key "journey_frequencies", "timebands", name: "journey_frequencies_timeband_id_fk", on_delete: :nullify    add_foreign_key "journey_frequencies", "timebands", on_delete: :nullify -  add_foreign_key "journey_frequencies", "vehicle_journeys", name: "journey_frequencies_vehicle_journey_id_fk", on_delete: :nullify    add_foreign_key "journey_frequencies", "vehicle_journeys", on_delete: :nullify    add_foreign_key "journey_patterns", "routes", name: "jp_route_fkey", on_delete: :cascade    add_foreign_key "journey_patterns", "stop_points", column: "arrival_stop_point_id", name: "arrival_point_fkey", on_delete: :nullify diff --git a/spec/factories/chouette_purchase_windows.rb b/spec/factories/chouette_purchase_windows.rb new file mode 100644 index 000000000..2e2faf4d8 --- /dev/null +++ b/spec/factories/chouette_purchase_windows.rb @@ -0,0 +1,12 @@ +FactoryGirl.define do +  factory :purchase_window, class: Chouette::PurchaseWindow do +    sequence(:name) { |n| "Purchase Window #{n}" } +    sequence(:objectid) { |n| "organisation:PurchaseWindow:#{n}:LOC" } +    date_ranges { [generate(:periods)] }     +  end + +  sequence :periods do |n| +    date = Date.today + 2*n +    date..(date+1) +  end +end diff --git a/spec/factories/chouette_routes.rb b/spec/factories/chouette_routes.rb index 4e20059fe..7443d08bc 100644 --- a/spec/factories/chouette_routes.rb +++ b/spec/factories/chouette_routes.rb @@ -31,6 +31,13 @@ FactoryGirl.define do          end        end + +      trait :with_opposite do +        after(:create) do |route| +          opposite = create :route +          route.opposite_route = opposite +        end +      end      end      factory :route_with_after_commit do diff --git a/spec/features/purchase_windows_permission_spec.rb b/spec/features/purchase_windows_permission_spec.rb new file mode 100644 index 000000000..9f155a1e8 --- /dev/null +++ b/spec/features/purchase_windows_permission_spec.rb @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +require 'spec_helper' + +describe "PurchaseWindows", :type => :feature do +  login_user + +  before do +    @user.organisation.update features: %w{purchase_windows} +  end + +  let(:purchase_window) { create :purchase_window, referential: first_referential} + +  describe 'permissions' do +    before do +      allow_any_instance_of(PurchaseWindowPolicy).to receive(:create?).and_return permission +      allow_any_instance_of(PurchaseWindowPolicy).to receive(:destroy?).and_return permission +      allow_any_instance_of(PurchaseWindowPolicy).to receive(:update?).and_return permission +      visit path +    end + +    context 'on show view' do +      let( :path ){ referential_purchase_window_path(first_referential, purchase_window) } + +      context 'if present → ' do +        let( :permission ){ true } +        it 'view shows the corresponding buttons' do +          expect(page).to have_content(I18n.t('purchase_windows.actions.edit')) +          expect(page).to have_content(I18n.t('purchase_windows.actions.destroy')) +        end +      end + +      context 'if absent → ' do +        let( :permission ){ false } +        it 'view does not show the corresponding buttons' do +          expect(page).not_to have_content(I18n.t('purchase_windows.actions.edit')) +          expect(page).not_to have_content(I18n.t('purchase_windows.actions.destroy')) +        end +      end +    end + +    context 'on index view' do +      let( :path ){ referential_purchase_windows_path(first_referential) } + +      context 'if present → ' do +        let( :permission ){ true } +        it 'index shows an edit button' do +          expect(page).to have_content(I18n.t('purchase_windows.actions.new')) +        end +      end + +      context 'if absent → ' do +        let( :permission ){ false } +        it 'index does not show any edit button' do +          expect(page).not_to have_content(I18n.t('purchase_windows.actions.new')) +        end +      end +    end +  end +end diff --git a/spec/models/chouette/purchase_window_spec.rb b/spec/models/chouette/purchase_window_spec.rb new file mode 100644 index 000000000..702a44eeb --- /dev/null +++ b/spec/models/chouette/purchase_window_spec.rb @@ -0,0 +1,27 @@ +RSpec.describe Chouette::PurchaseWindow, :type => :model do +  let(:referential) {create(:referential)} +  subject  { create(:purchase_window, referential: referential) } + +  it { should belong_to(:referential) } +  it { is_expected.to validate_presence_of(:name) } + +  describe 'validations' do +    it 'validates and date_ranges do not overlap' do +      expect(build(:purchase_window, referential: referential,date_ranges: [Date.today..Date.today + 10.day, Date.yesterday..Date.tomorrow])).to_not be_valid +      # expect(build(periods: [Date.today..Date.today + 10.day, Date.yesterday..Date.tomorrow ])).to_not be_valid +    end +  end + +  describe 'before_validation' do +    let(:purchase_window) { build(:purchase_window, referential: referential, date_ranges: []) } + +    it 'shoud fill date_ranges with date ranges' do +      expected_range = Date.today..Date.tomorrow +      purchase_window.date_ranges << expected_range +      purchase_window.valid? + +      expect(purchase_window.date_ranges.map { |period| period.begin..period.end }).to eq([expected_range]) +    end +  end + +end diff --git a/spec/policies/purchase_window_policy_spec.rb b/spec/policies/purchase_window_policy_spec.rb new file mode 100644 index 000000000..f078bf288 --- /dev/null +++ b/spec/policies/purchase_window_policy_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe PurchaseWindowPolicy, type: :policy do + +  let( :record ){ build_stubbed :purchase_window } +  before { stub_policy_scope(record) } + +  permissions :create? do +    it_behaves_like 'permitted policy and same organisation', "purchase_windows.create", archived: true +  end +  permissions :destroy? do +    it_behaves_like 'permitted policy and same organisation', "purchase_windows.destroy", archived: true +  end +  permissions :update? do +    it_behaves_like 'permitted policy and same organisation', "purchase_windows.update", archived: true +  end +end diff --git a/spec/support/integration_spec_helper.rb b/spec/support/integration_spec_helper.rb index 78efb9027..1bf211fe1 100644 --- a/spec/support/integration_spec_helper.rb +++ b/spec/support/integration_spec_helper.rb @@ -1,7 +1,11 @@  module IntegrationSpecHelper    def paginate_collection klass, decorator, page=1 -    ModelDecorator.decorate( klass.page(page), with: decorator ) +    coll = klass.page(page) +    if decorator +      coll = ModelDecorator.decorate( coll, with: decorator ) +    end +    coll    end    def build_paginated_collection factory, decorator, opts={} diff --git a/spec/views/vehicle_journeys/index.html.slim_spec.rb b/spec/views/vehicle_journeys/index.html.slim_spec.rb new file mode 100644 index 000000000..7f0a9c5aa --- /dev/null +++ b/spec/views/vehicle_journeys/index.html.slim_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe "/vehicle_journeys/index", :type => :view do + +  let!(:referential) { assign :referential, create(:referential) } +  let!(:line) { assign :line, create(:line) } +  let!(:route) { assign :route, create(:route, line: line) } +  let!(:vehicle_journeys) do +    assign :vehicle_journeys, build_paginated_collection(:vehicle_journey, nil, route: route) +  end + +  before :each do +    allow(view).to receive(:link_with_search).and_return("#") +    allow(view).to receive(:collection).and_return(vehicle_journeys) +    allow(view).to receive(:current_referential).and_return(referential) +    controller.request.path_parameters[:referential_id] = referential.id +    render +  end + +  context "with an opposite_route" do +    let!(:route) { assign :route, create(:route, :with_opposite, line: line) } + +    it "should have an 'oppposite route timetable' button" do +      href = view.referential_line_route_vehicle_journeys_path(referential, line, route.opposite_route) +      oppposite_button_selector = "a[href=\"#{href}\"]" + +      expect(view.content_for(:page_header_content)).to have_selector oppposite_button_selector +    end +  end +end | 
