From 0f59b9c1d0133393f68194a366aafc27ca23392b Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Tue, 23 Jan 2018 22:46:16 -0800
Subject: First draft for including calendars into workgroup for having
 appropriate scoping
---
 app/controllers/calendars_controller.rb                           | 4 +++-
 app/decorators/calendar_decorator.rb                              | 2 +-
 app/helpers/table_builder_helper.rb                               | 8 +++++++-
 app/helpers/table_builder_helper/custom_links.rb                  | 7 ++++---
 app/helpers/table_builder_helper/url.rb                           | 5 +++--
 app/models/calendar.rb                                            | 1 +
 app/models/workgroup.rb                                           | 1 +
 app/views/calendars/_filters.html.slim                            | 4 ++--
 app/views/calendars/index.html.slim                               | 4 ++--
 app/views/dashboards/_dashboard.html.slim                         | 6 +++---
 .../layouts/navigation/_main_nav_left_content_stif.html.slim      | 2 +-
 app/views/stif/dashboards/_dashboard.html.slim                    | 4 ++--
 config/routes.rb                                                  | 7 ++++---
 db/migrate/20180123174450_add_workgroup_id_to_calendars.rb        | 6 ++++++
 db/schema.rb                                                      | 6 ++++--
 lib/stif/dashboard.rb                                             | 2 +-
 16 files changed, 45 insertions(+), 24 deletions(-)
 create mode 100644 db/migrate/20180123174450_add_workgroup_id_to_calendars.rb
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 4a752f2b9..72d213953 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -7,7 +7,9 @@ class CalendarsController < ChouetteController
 
   def index
     index! do
-      @calendars = ModelDecorator.decorate(@calendars, with: CalendarDecorator)
+      @calendars = ModelDecorator.decorate(@calendars, with: CalendarDecorator, context: {
+        workgroup: current_workgroup
+      })
     end
   end
 
diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb
index 37e2cfe80..ce2c1a2dd 100644
--- a/app/decorators/calendar_decorator.rb
+++ b/app/decorators/calendar_decorator.rb
@@ -7,7 +7,7 @@ class CalendarDecorator < Draper::Decorator
     if h.policy(object).destroy?
       links << Link.new(
         content: h.destroy_link_content,
-        href: h.calendar_path(object),
+        href: h.workgroup_calendar_path(context[:workgroup], object),
         method: :delete,
         data: { confirm: h.t('calendars.actions.destroy_confirm') }
       )
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index dede51920..9ead7180a 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -308,7 +308,7 @@ module TableBuilderHelper
 
     menu = content_tag :ul, class: 'dropdown-menu' do
       (
-        CustomLinks.new(item, pundit_user, links, referential).links +
+        CustomLinks.new(item, pundit_user, links, referential, workgroup).links +
         item.action_links.select { |link| link.is_a?(Link) }
       ).map do |link|
         gear_menu_link(link)
@@ -391,4 +391,10 @@ module TableBuilderHelper
     # cases, avoid a `NoMethodError`.
     @__referential__ ||= try(:current_referential)
   end
+
+  def workgroup
+    # Certain controllers don't define a `#current_referential`. In these
+    # cases, avoid a `NoMethodError`.
+    @__workgroup__ ||= try(:current_workgroup)
+  end
 end
diff --git a/app/helpers/table_builder_helper/custom_links.rb b/app/helpers/table_builder_helper/custom_links.rb
index b1bb11f10..e09078be0 100644
--- a/app/helpers/table_builder_helper/custom_links.rb
+++ b/app/helpers/table_builder_helper/custom_links.rb
@@ -8,13 +8,14 @@ module TableBuilderHelper
       unarchive: :put
     }
 
-    attr_reader :actions, :object, :user_context, :referential
+    attr_reader :actions, :object, :user_context, :referential, :workgroup
 
-    def initialize(object, user_context, actions, referential = nil)
+    def initialize(object, user_context, actions, referential = nil, workgroup = nil)
       @object       = object
       @user_context = user_context
       @actions      = actions
       @referential  = referential
+      @workgroup  = workgroup
     end
 
     def links
@@ -34,7 +35,7 @@ module TableBuilderHelper
         polymorph_url << action
       end
 
-      polymorph_url += URL.polymorphic_url_parts(object, referential)
+      polymorph_url += URL.polymorphic_url_parts(object, referential, workgroup)
     end
 
     def method_for_action(action)
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index 28f1ade76..40e1f839f 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -1,9 +1,9 @@
 module TableBuilderHelper
   class URL
-    def self.polymorphic_url_parts(item, referential)
+    def self.polymorphic_url_parts(item, referential, workgroup)
       polymorph_url = []
 
-      unless item.is_a?(Calendar) || item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
+      unless item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
         if referential
           polymorph_url << referential
           polymorph_url << item.line if item.respond_to? :line
@@ -20,6 +20,7 @@ module TableBuilderHelper
           end
         end
       else
+        polymorph_url << item.workgroup if item.is_a?(Calendar)
         polymorph_url << item
       end
 
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index a7fd9220c..236223892 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -8,6 +8,7 @@ class Calendar < ActiveRecord::Base
 
   has_paper_trail class_name: 'PublicVersion'
   belongs_to :organisation
+  belongs_to :workgroup
 
   validates_presence_of :name, :short_name, :organisation
   validates_uniqueness_of :short_name
diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb
index 3d761e81f..3af20ae23 100644
--- a/app/models/workgroup.rb
+++ b/app/models/workgroup.rb
@@ -3,6 +3,7 @@ class Workgroup < ActiveRecord::Base
   belongs_to :stop_area_referential
 
   has_many :workbenches
+  has_many :calendars
   has_many :organisations, through: :workbenches
   has_many :referentials, through: :workbenches
 
diff --git a/app/views/calendars/_filters.html.slim b/app/views/calendars/_filters.html.slim
index b5283c1e8..c1d8c47f7 100644
--- a/app/views/calendars/_filters.html.slim
+++ b/app/views/calendars/_filters.html.slim
@@ -1,4 +1,4 @@
-= search_form_for @q, url: calendars_path, builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f|
+= search_form_for @q, url: workgroup_calendars_path(current_workgroup), builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f|
   .ffg-row
     .input-group.search_bar
       = f.search_field :name_or_short_name_cont, class: 'form-control', placeholder: 'Indiquez un nom/nom court de calendrier...'
@@ -18,5 +18,5 @@
       = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', include_blank: true
 
   .actions
-    = link_to 'Effacer', calendars_path, class: 'btn btn-link'
+    = link_to 'Effacer', workgroup_calendars_path(current_workgroup), class: 'btn btn-link'
     = f.submit 'Filtrer', id: 'calendar_filter_btn', class: 'btn btn-default'
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index 77478a624..d0544856d 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,7 +1,7 @@
 - breadcrumb :calendars
 - content_for :page_header_actions do
   - if policy(Calendar).create?
-    = link_to(t('actions.add'), new_calendar_path, class: 'btn btn-default')
+    = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
 
 .page_content
   .container-fluid
@@ -19,7 +19,7 @@
                 key: :name, \
                 attribute: 'name', \
                 link_to: lambda do |calendar| \
-                  calendar_path(calendar) \
+                  workgroup_calendar_path(current_workgroup, calendar) \
                 end \
               ), \
               TableBuilderHelper::Column.new( \
diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim
index 075b94ddc..58cfcc542 100644
--- a/app/views/dashboards/_dashboard.html.slim
+++ b/app/views/dashboards/_dashboard.html.slim
@@ -22,13 +22,13 @@
     .panel.panel-default
       .panel-heading
         h3.panel-title.with_actions
-          = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), calendars_path
+          = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), workgroup_calendars_path(current_workgroup)
           div
-            = link_to '', calendars_path, class: ' fa fa-chevron-right pull-right'
+            = link_to '', workgroup_calendars_path(current_workgroup), class: ' fa fa-chevron-right pull-right'
       - if @dashboard.current_organisation.calendars.present?
         .list-group
           - @dashboard.current_organisation.calendars.order("updated_at desc").limit(5).each do |calendar|
-            = link_to calendar.name, calendar_path(calendar), class: 'list-group-item'
+            = link_to calendar.name, workgroup_calendars_path(current_workgroup, calendar), class: 'list-group-item'
       - else
         .panel-body
           em.small.text-muted
diff --git a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim
index 3963d4cd4..1b7293d21 100644
--- a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim
+++ b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim
@@ -29,7 +29,7 @@
             span Jeux de données
           = link_to workbench_imports_path(current_offer_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do
             span Import
-          = link_to calendars_path, class: 'list-group-item' do
+          = link_to workgroup_calendars_path(current_workgroup), class: 'list-group-item' do
             span Modèles de calendrier
           = link_to workbench_compliance_check_sets_path(current_offer_workbench), class: 'list-group-item' do
             span Rapport de contrôle
diff --git a/app/views/stif/dashboards/_dashboard.html.slim b/app/views/stif/dashboards/_dashboard.html.slim
index 64e7d4f96..c28696a94 100644
--- a/app/views/stif/dashboards/_dashboard.html.slim
+++ b/app/views/stif/dashboards/_dashboard.html.slim
@@ -60,12 +60,12 @@
           span.badge.ml-xs = @dashboard.calendars.count if @dashboard.calendars.present?
 
           div
-            = link_to '', calendars_path, class: ' fa fa-chevron-right pull-right', title: t('.see')
+            = link_to '', workgroup_calendars_path(current_workgroup), class: ' fa fa-chevron-right pull-right', title: t('.see')
 
       - if @dashboard.calendars.present?
         .list-group
           - @dashboard.calendars.first(5).each_with_index do |calendar, i|
-            = link_to calendar.name, calendar_path(calendar), class: 'list-group-item' if i < 6
+            = link_to calendar.name, workgroup_calendar_path(current_workgroup, calendar), class: 'list-group-item' if i < 6
 
       - else
         .panel-body
diff --git a/config/routes.rb b/config/routes.rb
index 0f60733af..6a6a7cb07 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -111,9 +111,10 @@ ChouetteIhm::Application.routes.draw do
     resources :companies
     resources :networks
   end
-
-  resources :calendars do
-    get :autocomplete, on: :collection, controller: 'autocomplete_calendars'
+  resources :workgroups do
+    resources :calendars do
+      get :autocomplete, on: :collection, controller: 'autocomplete_calendars'
+    end
   end
 
   resources :referentials, except: :index do
diff --git a/db/migrate/20180123174450_add_workgroup_id_to_calendars.rb b/db/migrate/20180123174450_add_workgroup_id_to_calendars.rb
new file mode 100644
index 000000000..64ad1a752
--- /dev/null
+++ b/db/migrate/20180123174450_add_workgroup_id_to_calendars.rb
@@ -0,0 +1,6 @@
+class AddWorkgroupIdToCalendars < ActiveRecord::Migration
+   def change
+    add_column :calendars, :workgroup_id, :integer, limit: 8
+    add_index :calendars, :workgroup_id
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2c5520110..11fb96ee3 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20180111200406) do
+ActiveRecord::Schema.define(version: 20180123174450) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -90,10 +90,12 @@ ActiveRecord::Schema.define(version: 20180111200406) do
     t.integer   "organisation_id", limit: 8
     t.datetime  "created_at"
     t.datetime  "updated_at"
+    t.integer   "workgroup_id",    limit: 8
   end
 
   add_index "calendars", ["organisation_id"], name: "index_calendars_on_organisation_id", using: :btree
   add_index "calendars", ["short_name"], name: "index_calendars_on_short_name", unique: true, using: :btree
+  add_index "calendars", ["workgroup_id"], name: "index_calendars_on_workgroup_id", using: :btree
 
   create_table "clean_up_results", id: :bigserial, force: :cascade do |t|
     t.string   "message_key"
@@ -416,9 +418,9 @@ ActiveRecord::Schema.define(version: 20180111200406) do
     t.string   "type"
     t.integer  "parent_id",             limit: 8
     t.string   "parent_type"
-    t.datetime "notified_parent_at"
     t.integer  "current_step",                    default: 0
     t.integer  "total_steps",                     default: 0
+    t.datetime "notified_parent_at"
     t.string   "creator"
   end
 
diff --git a/lib/stif/dashboard.rb b/lib/stif/dashboard.rb
index b6b6b8284..7dd83efbc 100644
--- a/lib/stif/dashboard.rb
+++ b/lib/stif/dashboard.rb
@@ -9,7 +9,7 @@ module Stif
     end
 
     def calendars
-      @calendars ||= Calendar.where('organisation_id = ? OR shared = ?', current_organisation.id, true)
+      @calendars ||= Calendar.where('workgroup_id = ? OR shared = ?', @workbench.workgroup_id, true)
     end
   end
 end
-- 
cgit v1.2.3
From f32d869cc3f34a939764cc3fa4d612a5d6544d08 Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Wed, 24 Jan 2018 19:55:56 -0800
Subject: update calendar build_links for table builder
---
 app/controllers/calendars_controller.rb       | 25 +++++++++++++++++++------
 app/controllers/concerns/workgroup_support.rb | 12 ++++++++++++
 app/helpers/table_builder_helper/url.rb       |  4 ++--
 app/models/calendar.rb                        |  2 +-
 app/views/calendars/_form.html.slim           |  2 +-
 app/views/calendars/edit.html.slim            |  2 +-
 app/views/calendars/index.html.slim           |  2 +-
 app/views/calendars/new.html.slim             |  2 +-
 app/views/calendars/show.html.slim            |  4 ++--
 config/breadcrumbs.rb                         | 10 +++++-----
 lib/stif/dashboard.rb                         |  6 +++++-
 11 files changed, 50 insertions(+), 21 deletions(-)
 create mode 100644 app/controllers/concerns/workgroup_support.rb
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 72d213953..27c944f5f 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,4 +1,5 @@
 class CalendarsController < ChouetteController
+  include WorkgroupSupport
   include PolicyChecker
   defaults resource_class: Calendar
   before_action :ransack_contains_date, only: [:index]
@@ -7,19 +8,30 @@ class CalendarsController < ChouetteController
 
   def index
     index! do
-      @calendars = ModelDecorator.decorate(@calendars, with: CalendarDecorator, context: {
-        workgroup: current_workgroup
-      })
+      @calendars = decorate_calendars(@calendars)
     end
   end
 
   def show
     show! do
-      @calendar = @calendar.decorate
+      @calendar = @calendar.decorate(context: {
+        workgroup: current_workgroup
+      })
     end
   end
 
   private
+
+  def decorate_calendars(calendars)
+    ModelDecorator.decorate(
+      calendars,
+      with: CalendarDecorator,
+      context: {
+        workgroup: current_workgroup
+      }
+    )
+  end
+
   def calendar_params
     permitted_params = [:id, :name, :short_name, :shared, periods_attributes: [:id, :begin, :end, :_destroy], date_values_attributes: [:id, :value, :_destroy]]
     permitted_params << :shared if policy(Calendar).share?
@@ -36,18 +48,19 @@ class CalendarsController < ChouetteController
 
   protected
   def resource
-    @calendar = Calendar.where('organisation_id = ? OR shared = true', current_organisation.id).find_by_id(params[:id])
+    @calendar = Calendar.where('(organisation_id = ? OR shared = ?) AND workgroup_id = ?', current_organisation.id).find_by_id(params[:id], true, @workgroup.id)
   end
 
   def build_resource
     super.tap do |calendar|
+      calendar.workgroup = current_workgroup
       calendar.organisation = current_organisation
     end
   end
 
   def collection
     return @calendars if @calendars
-    scope = Calendar.where('organisation_id = ? OR shared = ?', current_organisation.id, true)
+    scope = Calendar.where('(organisation_id = ? OR shared = ?) AND workgroup_id = ?', current_organisation.id, true, @workgroup.id)
     scope = shared_scope(scope)
     @q = scope.ransack(params[:q])
 
diff --git a/app/controllers/concerns/workgroup_support.rb b/app/controllers/concerns/workgroup_support.rb
new file mode 100644
index 000000000..a3b49bb12
--- /dev/null
+++ b/app/controllers/concerns/workgroup_support.rb
@@ -0,0 +1,12 @@
+module WorkgroupSupport
+  extend ActiveSupport::Concern
+
+  included do
+    before_action :find_workgroup
+  end
+
+  def find_workgroup
+   @workgroup ||= Workgroup.find params[:workgroup_id]
+  end
+
+end
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index 40e1f839f..0e3dce0aa 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -3,7 +3,7 @@ module TableBuilderHelper
     def self.polymorphic_url_parts(item, referential, workgroup)
       polymorph_url = []
 
-      unless item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
+      unless item.is_a?(Calendar) || item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
         if referential
           polymorph_url << referential
           polymorph_url << item.line if item.respond_to? :line
@@ -20,7 +20,7 @@ module TableBuilderHelper
           end
         end
       else
-        polymorph_url << item.workgroup if item.is_a?(Calendar)
+        polymorph_url << item.workgroup if item.respond_to? :workgroup
         polymorph_url << item
       end
 
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index 236223892..d93532908 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -10,7 +10,7 @@ class Calendar < ActiveRecord::Base
   belongs_to :organisation
   belongs_to :workgroup
 
-  validates_presence_of :name, :short_name, :organisation
+  validates_presence_of :name, :short_name, :organisation, :workgroup
   validates_uniqueness_of :short_name
 
   has_many :time_tables
diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim
index 3c152c61d..bf9f4f3a7 100644
--- a/app/views/calendars/_form.html.slim
+++ b/app/views/calendars/_form.html.slim
@@ -1,4 +1,4 @@
-= simple_form_for @calendar, html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f|
+= simple_form_for [@workgroup, @calendar], html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f|
   .row
     .col-lg-12
       = f.input :name
diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim
index e806fc94b..a1af5e257 100644
--- a/app/views/calendars/edit.html.slim
+++ b/app/views/calendars/edit.html.slim
@@ -1,4 +1,4 @@
-- breadcrumb :calendar, @calendar
+- breadcrumb :calendar, @workgroup, @calendar
 - page_header_content_for @calendar
 .page_content
   .container-fluid
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index d0544856d..60bc9793d 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,4 +1,4 @@
-- breadcrumb :calendars
+- breadcrumb :calendars, current_workgroup
 - content_for :page_header_actions do
   - if policy(Calendar).create?
     = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim
index ce8b6a036..c1e084913 100644
--- a/app/views/calendars/new.html.slim
+++ b/app/views/calendars/new.html.slim
@@ -1,4 +1,4 @@
-- breadcrumb :calendars
+- breadcrumb :calendars, @workgroup
 .page_content
   .container-fluid
     .row
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
index da4afa3e6..9f7512173 100644
--- a/app/views/calendars/show.html.slim
+++ b/app/views/calendars/show.html.slim
@@ -1,4 +1,4 @@
-- breadcrumb :calendar, @calendar
+- breadcrumb :calendar, @workgroup, @calendar
 - page_header_content_for @calendar
 - content_for :page_header_content do
   .row.mb-sm
@@ -11,7 +11,7 @@
               = link.content
 - if policy(@calendar).edit?
   - content_for :page_header_actions do
-      = link_to(t('actions.edit'), edit_calendar_path(@calendar), class: 'btn btn-default')
+      = link_to(t('actions.edit'), edit_workgroup_calendar_path(@workgroup, @calendar), class: 'btn btn-default')
 
 .page_content
   .container-fluid
diff --git a/config/breadcrumbs.rb b/config/breadcrumbs.rb
index 6da96d73a..2772895fe 100644
--- a/config/breadcrumbs.rb
+++ b/config/breadcrumbs.rb
@@ -202,13 +202,13 @@ crumb :purchase_window do |referential, purchase_window|
   parent :purchase_windows, referential
 end
 
-crumb :calendars do
-  link I18n.t('calendars.index.title'), calendars_path
+crumb :calendars do |workgroup|
+  link I18n.t('calendars.index.title'), workgroup_calendars_path(workgroup)
 end
 
-crumb :calendar do |calendar|
-  link breadcrumb_name(calendar), calendar_path(calendar)
-  parent :calendars
+crumb :calendar do |workgroup, calendar|
+  link breadcrumb_name(calendar), workgroup_calendar_path(workgroup, calendar)
+  parent :calendars, workgroup
 end
 
 crumb :referential_line do |referential, line|
diff --git a/lib/stif/dashboard.rb b/lib/stif/dashboard.rb
index 7dd83efbc..46c635091 100644
--- a/lib/stif/dashboard.rb
+++ b/lib/stif/dashboard.rb
@@ -4,12 +4,16 @@ module Stif
       @workbench ||= current_organisation.workbenches.find_by(name: "Gestion de l'offre")
     end
 
+    def workgroup
+      workbench.workgroup
+    end
+
     def referentials
       @referentials ||= self.workbench.all_referentials
     end
 
     def calendars
-      @calendars ||= Calendar.where('workgroup_id = ? OR shared = ?', @workbench.workgroup_id, true)
+      @calendars ||= Calendar.where('(organisation_id = ? OR shared = ?) AND workgroup_id = ?', current_organisation.id, true, workgroup.id)
     end
   end
 end
-- 
cgit v1.2.3
From 4023ea52097a47458ac2fcad36d343aba0c8e68b Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Tue, 30 Jan 2018 23:07:07 -0800
Subject: Make some changes to avoid unnacessaty lines of code
---
 app/controllers/calendars_controller.rb     | 33 +++++++++++++++++------------
 app/views/calendars/index.html.slim         |  4 ++--
 spec/factories/calendars.rb                 |  1 +
 spec/features/calendars_permissions_spec.rb |  7 +++---
 4 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 27c944f5f..193680342 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,10 +1,11 @@
 class CalendarsController < ChouetteController
-  include WorkgroupSupport
   include PolicyChecker
   defaults resource_class: Calendar
   before_action :ransack_contains_date, only: [:index]
   respond_to :html
   respond_to :js, only: :index
+  
+  belongs_to :workgroup
 
   def index
     index! do
@@ -15,7 +16,7 @@ class CalendarsController < ChouetteController
   def show
     show! do
       @calendar = @calendar.decorate(context: {
-        workgroup: current_workgroup
+        workgroup: workgroup
       })
     end
   end
@@ -27,7 +28,7 @@ class CalendarsController < ChouetteController
       calendars,
       with: CalendarDecorator,
       context: {
-        workgroup: current_workgroup
+        workgroup: workgroup
       }
     )
   end
@@ -47,26 +48,30 @@ class CalendarsController < ChouetteController
   end
 
   protected
+
+  alias_method :workgroup, :parent
+  helper_method :workgroup
+
   def resource
-    @calendar = Calendar.where('(organisation_id = ? OR shared = ?) AND workgroup_id = ?', current_organisation.id).find_by_id(params[:id], true, @workgroup.id)
+    @calendar ||= workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true).find_by_id(params[:id])
   end
 
   def build_resource
     super.tap do |calendar|
-      calendar.workgroup = current_workgroup
+      calendar.workgroup = workgroup
       calendar.organisation = current_organisation
     end
   end
 
   def collection
-    return @calendars if @calendars
-    scope = Calendar.where('(organisation_id = ? OR shared = ?) AND workgroup_id = ?', current_organisation.id, true, @workgroup.id)
-    scope = shared_scope(scope)
-    @q = scope.ransack(params[:q])
-
-    calendars = @q.result
-    calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction
-    @calendars = calendars.paginate(page: params[:page])
+    @calendars ||= begin
+		    scope = workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true)
+		    scope = shared_scope(scope)
+		    @q = scope.ransack(params[:q])
+		    calendars = @q.result
+		    calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction
+		    calendars = calendars.paginate(page: params[:page])
+                  end 
   end
 
   def ransack_contains_date
@@ -91,4 +96,4 @@ class CalendarsController < ChouetteController
     scope
   end
 
-end
+end
\ No newline at end of file
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index 309f0e6f9..0b58c0c72 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,4 +1,4 @@
-- breadcrumb :calendars, current_workgroup
+- breadcrumb :calendars, workgroup
 
 .page_content
   .container-fluid
@@ -16,7 +16,7 @@
                 key: :name, \
                 attribute: 'name', \
                 link_to: lambda do |calendar| \
-                  workgroup_calendar_path(current_workgroup, calendar) \
+                  workgroup_calendar_path(workgroup, calendar) \
                 end \
               ), \
               TableBuilderHelper::Column.new( \
diff --git a/spec/factories/calendars.rb b/spec/factories/calendars.rb
index 5f3188bee..d9fd242d1 100644
--- a/spec/factories/calendars.rb
+++ b/spec/factories/calendars.rb
@@ -6,6 +6,7 @@ FactoryGirl.define do
     sequence(:dates) { |n| [ Date.yesterday - n, Date.yesterday - 2*n ] }
     shared false
     organisation
+    workgroup
   end
 
   sequence :date_range do |n|
diff --git a/spec/features/calendars_permissions_spec.rb b/spec/features/calendars_permissions_spec.rb
index 9b47ab2bb..4857592d5 100644
--- a/spec/features/calendars_permissions_spec.rb
+++ b/spec/features/calendars_permissions_spec.rb
@@ -2,6 +2,7 @@ RSpec.describe 'Calendars', type: :feature do
   login_user
 
   let(:calendar) { create :calendar, organisation_id: 1 }
+  let(:workgroup) { calendar.workgroup }
 
   describe 'permissions' do
     before do
@@ -13,7 +14,7 @@ RSpec.describe 'Calendars', type: :feature do
     end
 
     context 'on show view' do
-      let( :path ){ calendar_path(calendar) }
+      let( :path ){ workgroup_calendar_path(workgroup, calendar) }
 
       context 'if present → ' do
         let( :permission ){ true }
@@ -33,7 +34,7 @@ RSpec.describe 'Calendars', type: :feature do
     end
 
     context 'on edit view' do
-      let( :path ){ edit_calendar_path(calendar) }
+      let( :path ){ edit_workgroup_calendar_path(workgroup, calendar) }
 
       context 'if present → ' do
         let( :permission ){ true }
@@ -51,7 +52,7 @@ RSpec.describe 'Calendars', type: :feature do
     end
 
     context 'on index view' do
-      let( :path ){ calendars_path }
+      let( :path ){ workgroup_calendars_path(workgroup) }
 
       context 'if present → ' do
         let( :permission ){ true }
-- 
cgit v1.2.3
From fe7915ffac359db41c7737a3847f31a728f502e6 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 11:39:39 +0100
Subject: Refs #5683 @2H; Fix specs and refactor action_links
Note: Did not fix the missing workgroup in the calendar mailer
---
 app/controllers/calendars_controller.rb            | 21 ++++----
 app/decorators/calendar_decorator.rb               |  2 +
 app/decorators/company_decorator.rb                | 19 ++-----
 app/decorators/compliance_control_decorator.rb     | 18 ++-----
 app/decorators/import_decorator.rb                 |  7 ++-
 app/decorators/line_decorator.rb                   | 10 ++--
 app/decorators/network_decorator.rb                | 14 +----
 app/decorators/purchase_window_decorator.rb        | 18 ++-----
 app/decorators/referential_line_decorator.rb       | 10 ++--
 app/decorators/referential_network_decorator.rb    | 21 ++------
 app/decorators/route_decorator.rb                  | 29 ++--------
 .../routing_constraint_zone_decorator.rb           | 39 ++------------
 app/decorators/stop_area_decorator.rb              | 22 +-------
 app/helpers/table_builder_helper.rb                |  1 -
 lib/af83/decorator.rb                              | 63 ++++++++++++----------
 lib/af83/decorator/enhanced_decorator.rb           | 22 ++++++--
 lib/af83/decorator/link.rb                         |  3 +-
 spec/support/pundit/pundit_view_policy.rb          |  5 +-
 spec/support/referential.rb                        |  4 +-
 spec/views/referentials/show.html.erb_spec.rb      |  6 ++-
 20 files changed, 114 insertions(+), 220 deletions(-)
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 193680342..6f1522428 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -4,7 +4,7 @@ class CalendarsController < ChouetteController
   before_action :ransack_contains_date, only: [:index]
   respond_to :html
   respond_to :js, only: :index
-  
+
   belongs_to :workgroup
 
   def index
@@ -24,9 +24,8 @@ class CalendarsController < ChouetteController
   private
 
   def decorate_calendars(calendars)
-    ModelDecorator.decorate(
+    CalendarDecorator.decorate(
       calendars,
-      with: CalendarDecorator,
       context: {
         workgroup: workgroup
       }
@@ -65,13 +64,13 @@ class CalendarsController < ChouetteController
 
   def collection
     @calendars ||= begin
-		    scope = workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true)
-		    scope = shared_scope(scope)
-		    @q = scope.ransack(params[:q])
-		    calendars = @q.result
-		    calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction
-		    calendars = calendars.paginate(page: params[:page])
-                  end 
+      scope = workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true)
+      scope = shared_scope(scope)
+      @q = scope.ransack(params[:q])
+      calendars = @q.result
+      calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction
+      calendars = calendars.paginate(page: params[:page])
+    end
   end
 
   def ransack_contains_date
@@ -96,4 +95,4 @@ class CalendarsController < ChouetteController
     scope
   end
 
-end
\ No newline at end of file
+end
diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb
index be1f9e3bf..0a7cbc312 100644
--- a/app/decorators/calendar_decorator.rb
+++ b/app/decorators/calendar_decorator.rb
@@ -1,6 +1,8 @@
 class CalendarDecorator < AF83::Decorator
   decorates Calendar
 
+  set_scope { context[:workgroup] }
+
   create_action_link
 
   with_instance_decorator do |instance_decorator|
diff --git a/app/decorators/company_decorator.rb b/app/decorators/company_decorator.rb
index aadce68bb..5580e0d4a 100644
--- a/app/decorators/company_decorator.rb
+++ b/app/decorators/company_decorator.rb
@@ -1,34 +1,21 @@
 class CompanyDecorator < AF83::Decorator
   decorates Chouette::Company
 
+  set_scope { context[:referential] }
+
   create_action_link do |l|
     l.content { h.t('companies.actions.new') }
-    l.href    { [:new, context[:referential], :company] }
   end
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href { [context[:referential], object] }
-    end
+    instance_decorator.show_action_link
 
     instance_decorator.edit_action_link do |l|
       l.content {|l| l.action == "show" ? h.t('actions.edit') : h.t('companies.actions.edit') }
-      l.href {
-        h.edit_line_referential_company_path(
-          context[:referential],
-          object
-        )
-      }
     end
 
     instance_decorator.destroy_action_link do |l|
       l.content { h.destroy_link_content('companies.actions.destroy') }
-      l.href {
-        h.edit_line_referential_company_path(
-          context[:referential],
-          object
-        )
-      }
       l.data {{ confirm: h.t('companies.actions.destroy_confirm') }}
     end
   end
diff --git a/app/decorators/compliance_control_decorator.rb b/app/decorators/compliance_control_decorator.rb
index c57a7ccc7..fd2dbd9ce 100644
--- a/app/decorators/compliance_control_decorator.rb
+++ b/app/decorators/compliance_control_decorator.rb
@@ -1,6 +1,8 @@
 class ComplianceControlDecorator < AF83::Decorator
   decorates ComplianceControl
 
+  set_scope { object.compliance_control_set }
+
   with_instance_decorator do |instance_decorator|
     instance_decorator.show_action_link do |l|
       l.content h.t('compliance_control_sets.actions.show')
@@ -12,23 +14,9 @@ class ComplianceControlDecorator < AF83::Decorator
       end
     end
 
-    instance_decorator.edit_action_link do |l|
-      l.href do
-        h.edit_compliance_control_set_compliance_control_path(
-          object.compliance_control_set_id,
-          object.id
-        )
-      end
-    end
+    instance_decorator.edit_action_link
 
     instance_decorator.destroy_action_link do |l|
-      l.content h.destroy_link_content
-      l.href do
-        h.compliance_control_set_compliance_control_path(
-          object.compliance_control_set.id,
-          object.id
-        )
-      end
       l.data confirm: h.t('compliance_controls.actions.destroy_confirm')
     end
   end
diff --git a/app/decorators/import_decorator.rb b/app/decorators/import_decorator.rb
index c6b1f2349..1964365ae 100644
--- a/app/decorators/import_decorator.rb
+++ b/app/decorators/import_decorator.rb
@@ -1,6 +1,8 @@
 class ImportDecorator < AF83::Decorator
   decorates Import
 
+  set_scope { context[:workbench] }
+
   define_instance_method :import_status_css_class do
     cls =''
     cls = 'overheaded-success' if object.status == 'successful'
@@ -11,13 +13,10 @@ class ImportDecorator < AF83::Decorator
 
   create_action_link do |l|
     l.content t('imports.actions.new')
-    l.href { h.new_workbench_import_path(workbench_id: context[:workbench]) }
   end
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href { h.workbench_import_path(context[:workbench], object) }
-    end
+    instance_decorator.show_action_link
 
     instance_decorator.action_link secondary: :show do |l|
       l.content t('imports.actions.download')
diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb
index 9171a6310..039ad90a3 100644
--- a/app/decorators/line_decorator.rb
+++ b/app/decorators/line_decorator.rb
@@ -1,9 +1,10 @@
 class LineDecorator < AF83::Decorator
   decorates Chouette::Line
 
+  set_scope { context[:line_referential] }
+
   create_action_link do |l|
     l.content t('lines.actions.new')
-    l.href    { h.new_line_referential_line_path(context[:line_referential]) }
   end
 
   with_instance_decorator do |instance_decorator|
@@ -14,17 +15,16 @@ class LineDecorator < AF83::Decorator
 
     instance_decorator.show_action_link do |l|
       l.content t('lines.actions.show')
-      l.href   { [context[:line_referential], object] }
     end
 
     instance_decorator.action_link secondary: :show do |l|
       l.content t('lines.actions.show_network')
-      l.href   { [context[:line_referential], object.network] }
+      l.href   { [scope, object.network] }
     end
 
     instance_decorator.action_link secondary: :show do |l|
       l.content  t('lines.actions.show_company')
-      l.href     { [context[:line_referential], object.company] }
+      l.href     { [scope, object.company] }
       l.disabled { object.company.nil? }
     end
 
@@ -33,7 +33,6 @@ class LineDecorator < AF83::Decorator
     instance_decorator.with_condition can_edit_line do
       edit_action_link do |l|
         l.content {|l| l.primary? ? h.t('actions.edit') : h.t('lines.actions.edit') }
-        l.href    { h.edit_line_referential_line_path(context[:line_referential], object.id) }
       end
 
       action_link on: :index, secondary: :index do |l|
@@ -63,7 +62,6 @@ class LineDecorator < AF83::Decorator
 
     instance_decorator.destroy_action_link do |l|
       l.content  { h.destroy_link_content('lines.actions.destroy') }
-      l.href     { h.line_referential_line_path(context[:line_referential], object) }
       l.data     confirm: h.t('lines.actions.destroy_confirm')
       l.add_class "delete-action"
     end
diff --git a/app/decorators/network_decorator.rb b/app/decorators/network_decorator.rb
index 90f0d0e82..ea0f73dc2 100644
--- a/app/decorators/network_decorator.rb
+++ b/app/decorators/network_decorator.rb
@@ -1,6 +1,7 @@
 class NetworkDecorator < AF83::Decorator
   decorates Chouette::Network
 
+  set_scope { context[:line_referential] }
   # Action links require:
   #   context: {
   #     line_referential: ,
@@ -8,15 +9,10 @@ class NetworkDecorator < AF83::Decorator
 
   create_action_link do |l|
     l.content t('networks.actions.new')
-    l.href { h.new_line_referential_network_path(context[:line_referential]) }
   end
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href do
-        h.line_referential_network_path(context[:line_referential], object)
-      end
-    end
+    instance_decorator.show_action_link
 
     instance_decorator.action_link secondary: true, policy: :edit do |l|
       l.content t('networks.actions.edit')
@@ -30,12 +26,6 @@ class NetworkDecorator < AF83::Decorator
 
     instance_decorator.destroy_action_link do |l|
       l.content h.destroy_link_content('networks.actions.destroy')
-      l.href do
-        h.line_referential_network_path(
-          context[:line_referential],
-          object
-        )
-      end
       l.data confirm: h.t('networks.actions.destroy_confirm')
     end
   end
diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb
index 54b241173..9b58577b2 100644
--- a/app/decorators/purchase_window_decorator.rb
+++ b/app/decorators/purchase_window_decorator.rb
@@ -1,32 +1,20 @@
 class PurchaseWindowDecorator < AF83::Decorator
   decorates Chouette::PurchaseWindow
 
+  set_scope { context[:referential] }
+
   create_action_link do |l|
     l.content t('purchase_windows.actions.new')
-    l.href { h.new_referential_purchase_window_path(context[:referential]) }
   end
 
   with_instance_decorator do |instance_decorator|
     instance_decorator.show_action_link do |l|
       l.content t('purchase_windows.actions.show')
-      l.href do
-        h.referential_purchase_window_path(
-          context[:referential],
-          object
-        )
-      end
     end
 
-    instance_decorator.edit_action_link do |l|
-      l.href do
-        h.edit_referential_purchase_window_path(context[:referential].id, object)
-      end
-    end
+    instance_decorator.edit_action_link
 
     instance_decorator.destroy_action_link do |l|
-      l.href do
-        h.referential_purchase_window_path(context[:referential].id, object)
-      end
       l.data confirm: h.t('purchase_windows.actions.destroy_confirm')
     end
   end
diff --git a/app/decorators/referential_line_decorator.rb b/app/decorators/referential_line_decorator.rb
index 8f884a8e0..3ac846d76 100644
--- a/app/decorators/referential_line_decorator.rb
+++ b/app/decorators/referential_line_decorator.rb
@@ -1,6 +1,8 @@
 class ReferentialLineDecorator < AF83::Decorator
   decorates Chouette::Line
 
+  set_scope { context[:referential] }
+
   # Action links require:
   #   context: {
   #     referential: ,
@@ -8,9 +10,7 @@ class ReferentialLineDecorator < AF83::Decorator
   #   }
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href { h.referential_line_path(context[:referential], object) }
-    end
+    instance_decorator.show_action_link
 
     instance_decorator.action_link secondary: true do |l|
       l.content Chouette::Line.human_attribute_name(:footnotes)
@@ -21,7 +21,7 @@ class ReferentialLineDecorator < AF83::Decorator
       l.content h.t('routing_constraint_zones.index.title')
       l.href do
         h.referential_line_routing_constraint_zones_path(
-          context[:referential],
+          scope,
           object
         )
       end
@@ -37,7 +37,7 @@ class ReferentialLineDecorator < AF83::Decorator
       secondary: true
     ) do |l|
       l.content h.t('routes.actions.new')
-      l.href { h.new_referential_line_route_path(context[:referential], object) }
+      l.href { h.new_referential_line_route_path(scope, object) }
     end
   end
 end
diff --git a/app/decorators/referential_network_decorator.rb b/app/decorators/referential_network_decorator.rb
index ff3467188..c508452c0 100644
--- a/app/decorators/referential_network_decorator.rb
+++ b/app/decorators/referential_network_decorator.rb
@@ -1,6 +1,8 @@
 class ReferentialNetworkDecorator < AF83::Decorator
   decorates Chouette::Network
 
+  set_scope { context[:referential] }
+
   # Action links require:
   #   context: {
   #     referential: ,
@@ -8,33 +10,18 @@ class ReferentialNetworkDecorator < AF83::Decorator
 
   create_action_link do |l|
     l.content t('networks.actions.new')
-    l.href { h.new_referential_network_path(context[:referential]) }
   end
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href { h.referential_network_path(context[:referential], object) }
-    end
+    instance_decorator.show_action_link
 
     instance_decorator.edit_action_link do |l|
       l.content t('networks.actions.edit')
-      l.href do
-        h.edit_referential_network_path(
-          context[:referential],
-          object
-        )
-      end
     end
 
     instance_decorator.destroy_action_link do |l|
       l.content h.destroy_link_content('networks.actions.destroy')
-      l.href do
-        h.referential_network_path(
-          context[:referential],
-          object
-        )
-      end
       l.data confirm: h.t('networks.actions.destroy_confirm')
     end
   end
-end
\ No newline at end of file
+end
diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb
index 7e3ea889f..75ef20d63 100644
--- a/app/decorators/route_decorator.rb
+++ b/app/decorators/route_decorator.rb
@@ -7,26 +7,12 @@ class RouteDecorator < AF83::Decorator
   #     line:
   #   }
 
+  set_scope { [context[:referential], context[:line]] }
+
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href do
-        h.referential_line_route_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
-    end
+    instance_decorator.show_action_link
 
-    instance_decorator.edit_action_link do |l|
-      l.href do
-        h.edit_referential_line_route_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
-    end
+    instance_decorator.edit_action_link
 
     instance_decorator.action_link(
       if: ->() { object.stop_points.any? },
@@ -85,13 +71,6 @@ class RouteDecorator < AF83::Decorator
     end
 
     instance_decorator.destroy_action_link do |l|
-      l.href do
-        h.referential_line_route_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
       l.data confirm: h.t('routes.actions.destroy_confirm')
     end
   end
diff --git a/app/decorators/routing_constraint_zone_decorator.rb b/app/decorators/routing_constraint_zone_decorator.rb
index 962625fa7..de73068be 100644
--- a/app/decorators/routing_constraint_zone_decorator.rb
+++ b/app/decorators/routing_constraint_zone_decorator.rb
@@ -1,6 +1,8 @@
 class RoutingConstraintZoneDecorator < AF83::Decorator
   decorates Chouette::RoutingConstraintZone
 
+  set_scope { [context[:referential], context[:line]] }
+
   # Action links require:
   #   context: {
   #     referential: ,
@@ -12,44 +14,13 @@ class RoutingConstraintZoneDecorator < AF83::Decorator
       h.policy(Chouette::RoutingConstraintZone).create? &&
         context[:referential].organisation == h.current_organisation
     }
-  ) do |l|
-    l.href do
-      h.new_referential_line_routing_constraint_zone_path(
-       context[:referential],
-       context[:line]
-     )
-    end
-  end
+  )
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href do
-        h.referential_line_routing_constraint_zone_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
-    end
-
-    instance_decorator.edit_action_link do |l|
-      l.href do
-        h.edit_referential_line_routing_constraint_zone_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
-    end
+    instance_decorator.show_action_link
+    instance_decorator.edit_action_link
 
     instance_decorator.destroy_action_link do |l|
-      l.href do
-        h.referential_line_routing_constraint_zone_path(
-          context[:referential],
-          context[:line],
-          object
-        )
-      end
       l.data confirm: h.t('routing_constraint_zones.actions.destroy_confirm')
     end
   end
diff --git a/app/decorators/stop_area_decorator.rb b/app/decorators/stop_area_decorator.rb
index 2e57da0e4..525681971 100644
--- a/app/decorators/stop_area_decorator.rb
+++ b/app/decorators/stop_area_decorator.rb
@@ -7,23 +7,11 @@ class StopAreaDecorator < AF83::Decorator
   end
 
   with_instance_decorator do |instance_decorator|
-    instance_decorator.show_action_link do |l|
-      l.href do
-        h.stop_area_referential_stop_area_path(
-          object.stop_area_referential,
-          object
-        )
-      end
-    end
+    set_scope { object.stop_area_referential }
+    instance_decorator.show_action_link
 
     instance_decorator.edit_action_link do |l|
       l.content h.t('stop_areas.actions.edit')
-      l.href do
-        h.edit_stop_area_referential_stop_area_path(
-          object.stop_area_referential,
-          object
-        )
-      end
     end
 
     instance_decorator.action_link policy: :deactivate, secondary: true do |l|
@@ -54,12 +42,6 @@ class StopAreaDecorator < AF83::Decorator
 
     instance_decorator.destroy_action_link do |l|
       l.content h.destroy_link_content('stop_areas.actions.destroy')
-      l.href do
-        h.stop_area_referential_stop_area_path(
-          object.stop_area_referential,
-          object
-        )
-      end
       l.data confirm: h.t('stop_areas.actions.destroy_confirm')
     end
   end
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index f48075ed9..2068dd23c 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -395,7 +395,6 @@ module TableBuilderHelper
     klass << link.extra_class if link.extra_class
     klass << 'delete-action' if link.method == :delete
     klass << 'disabled' if link.disabled
-
     content_tag(
       :li,
       link_to(
diff --git a/lib/af83/decorator.rb b/lib/af83/decorator.rb
index f990555fe..71cf1170d 100644
--- a/lib/af83/decorator.rb
+++ b/lib/af83/decorator.rb
@@ -2,40 +2,49 @@ class AF83::Decorator < ModelDecorator
   include AF83::Decorator::EnhancedDecorator
   extend AF83::Decorator::EnhancedDecorator::ClassMethods
 
-  def self.decorates klass
-    instance_decorator.decorates klass
-  end
+  class << self
+    def decorates klass
+      instance_decorator.decorates klass
+    end
 
-  def self.instance_decorator
-    @instance_decorator ||= begin
-      klass = Class.new(AF83::Decorator::InstanceDecorator)
-      klass.delegate_all
-      klass
+    def instance_decorator
+      @instance_decorator ||= begin
+        klass = Class.new(AF83::Decorator::InstanceDecorator)
+        klass.delegate_all
+        klass
+      end
     end
-  end
 
-  def self.with_instance_decorator
-    @_with_instance_decorator = true
-    yield instance_decorator
-    @_with_instance_decorator = false
-  end
+    def with_instance_decorator
+      @_with_instance_decorator = true
+      yield instance_decorator
+      @_with_instance_decorator = false
+    end
+
+    def decorate object, options = {}
+      if object.is_a?(ActiveRecord::Base)
+        return instance_decorator.decorate object, options
+      else
+        self.new object, options.update(with: instance_decorator)
+      end
+    end
 
-  def self.decorate object, options = {}
-    if object.is_a?(ActiveRecord::Base)
-      return instance_decorator.decorate object, options
-    else
-      self.new object, options.update(with: instance_decorator)
+    def define_instance_method method_name, &block
+      instance_decorator.send(:define_method, method_name, &block)
     end
-  end
 
-  def self.define_instance_method method_name, &block
-    instance_decorator.send(:define_method, method_name, &block)
-  end
+    # Defines a class method on the decorated object's class. These
+    # can be called like `object.class.my_method`.
+    def define_instance_class_method method_name, &block
+      instance_decorator.send(:define_singleton_method, method_name, &block)
+    end
+
+    def set_scope_with_instance_decorator value=nil, &block
+      set_scope_without_instance_decorator value, &block
+      instance_decorator.set_scope value, &block
+    end
 
-  # Defines a class method on the decorated object's class. These
-  # can be called like `object.class.my_method`.
-  def self.define_instance_class_method method_name, &block
-    instance_decorator.send(:define_singleton_method, method_name, &block)
+    alias_method_chain :set_scope, :instance_decorator
   end
 
   class ActionLinks
diff --git a/lib/af83/decorator/enhanced_decorator.rb b/lib/af83/decorator/enhanced_decorator.rb
index 904d1b2da..fff8bb8b3 100644
--- a/lib/af83/decorator/enhanced_decorator.rb
+++ b/lib/af83/decorator/enhanced_decorator.rb
@@ -25,7 +25,7 @@ module AF83::Decorator::EnhancedDecorator
         policy: :create,
         before_block: -> (l){
           l.content { h.t('actions.add') }
-          l.href    { [:new, object.klass.name.underscore.singularize] }
+          l.href    { [:new, scope, object.klass.model_name.singular] }
         }
       }
       action_link opts.update(args), &block
@@ -37,7 +37,7 @@ module AF83::Decorator::EnhancedDecorator
         primary: :index,
         before_block: -> (l){
           l.content { h.t('actions.show') }
-          l.href { [object] }
+          l.href { [scope, object] }
         }
       }
       action_link opts.update(args), &block
@@ -49,7 +49,7 @@ module AF83::Decorator::EnhancedDecorator
         policy: :edit,
         before_block: -> (l){
           l.content { h.t('actions.edit') }
-          l.href { [:edit, object] }
+          l.href { [:edit, scope, object] }
         }
       }
       action_link opts.update(args), &block
@@ -62,7 +62,7 @@ module AF83::Decorator::EnhancedDecorator
         secondary: :show,
         before_block: -> (l){
           l.content { h.destroy_link_content }
-          l.href { [object] }
+          l.href { [scope, object] }
           l.method :delete
           l.data {{ confirm: h.t('actions.destroy_confirm') }}
         }
@@ -70,6 +70,14 @@ module AF83::Decorator::EnhancedDecorator
       action_link opts.update(args), &block
     end
 
+    def set_scope value=nil, &block
+      @scope = value || block
+    end
+
+    def scope
+      @scope
+    end
+
     def t key
       eval  "-> (l){ h.t('#{key}') }"
     end
@@ -142,4 +150,10 @@ module AF83::Decorator::EnhancedDecorator
   def check_feature feature
     h.has_feature? feature
   end
+
+  def scope
+    scope = self.class.scope
+    scope = instance_exec &scope if scope.is_a? Proc
+    scope
+  end
 end
diff --git a/lib/af83/decorator/link.rb b/lib/af83/decorator/link.rb
index 7d2896e6a..de7106740 100644
--- a/lib/af83/decorator/link.rb
+++ b/lib/af83/decorator/link.rb
@@ -30,7 +30,8 @@ class AF83::Decorator::Link
       @options[name] = block
     elsif args.size == 0
       out = @options[name]
-      out = context.instance_exec(self, &out)  if out.is_a?(Proc)
+      out = context.instance_exec(self, &out) if out.is_a?(Proc)
+      out = out.flatten.compact if name.to_s == "href" && out.is_a?(Array)
       out
     else
       # we can use l.foo("bar") or l.foo = "bar"
diff --git a/spec/support/pundit/pundit_view_policy.rb b/spec/support/pundit/pundit_view_policy.rb
index 91be0624c..330209049 100644
--- a/spec/support/pundit/pundit_view_policy.rb
+++ b/spec/support/pundit/pundit_view_policy.rb
@@ -2,9 +2,8 @@ module Pundit
   module PunditViewPolicy
     def self.included into
       into.let(:permissions){ nil }
-      into.let(:organisation){ referential.try(:organisation) }
-      into.let(:current_referential){ referential || build_stubbed(:referential) }
-      into.let(:current_user){ build_stubbed :user, permissions: permissions, organisation: organisation }
+      into.let(:current_referential){ referential || build_stubbed(:referential, organisation: organisation) }
+      into.let(:current_user){ create :user, permissions: permissions, organisation: organisation }
       into.let(:pundit_user){ UserContext.new(current_user, referential: current_referential) }
       into.before do
         allow(view).to receive(:pundit_user) { pundit_user }
diff --git a/spec/support/referential.rb b/spec/support/referential.rb
index 497ff47a8..9acdce73a 100644
--- a/spec/support/referential.rb
+++ b/spec/support/referential.rb
@@ -11,8 +11,8 @@ module ReferentialHelper
   def self.included(base)
     base.class_eval do
       extend ClassMethods
-      alias_method :referential, :first_referential
-      alias_method :organisation, :first_organisation
+      base.let(:referential){ first_referential }
+      base.let(:organisation){ first_organisation }
     end
   end
 
diff --git a/spec/views/referentials/show.html.erb_spec.rb b/spec/views/referentials/show.html.erb_spec.rb
index 4a2afe2ca..6fd51949a 100644
--- a/spec/views/referentials/show.html.erb_spec.rb
+++ b/spec/views/referentials/show.html.erb_spec.rb
@@ -3,20 +3,22 @@ require 'spec_helper'
 describe "referentials/show", type: :view do
 
   let!(:referential) do
-    referential = create(:referential)
+    referential = create(:referential, organisation: organisation)
     assign :referential, referential.decorate(context: {
       current_organisation: referential.organisation
     })
   end
   let(:permissions){ [] }
   let(:current_organisation) { organisation }
-  let(:current_offer_workbench) { create :workbench, organisation: current_organisation}
+  let(:current_offer_workbench) { create :workbench, organisation: organisation}
+  let(:current_workgroup) { current_offer_workbench.workgroup }
   let(:readonly){ false }
 
   before :each do
     assign :reflines, []
     allow(view).to receive(:current_offer_workbench).and_return(current_offer_workbench)
     allow(view).to receive(:current_organisation).and_return(current_organisation)
+    allow(view).to receive(:current_workgroup).and_return(current_workgroup)
     allow(view).to receive(:current_user).and_return(current_user)
 
     allow(view).to receive(:resource).and_return(referential)
-- 
cgit v1.2.3
From d56aed95bcd9970bdc49477c2d5c59befbaeeefd Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 22 Jan 2018 11:05:09 +0100
Subject: Refs #5647; Scope CustomField validations on workgroup
---
 app/models/custom_field.rb       | 5 +++--
 spec/models/custom_field_spec.rb | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index 3f79ec62c..774c8b0f6 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -1,8 +1,9 @@
 class CustomField < ActiveRecord::Base
 
   extend Enumerize
+  belongs_to :workgroup
   enumerize :field_type, in: %i{list}
 
-  validates :name, uniqueness: {scope: :resource_type}
-  validates :code, uniqueness: {scope: :resource_type, case_sensitive: false}
+  validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]}
+  validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}
 end
diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb
index 8a6d0cb41..51128b0a2 100644
--- a/spec/models/custom_field_spec.rb
+++ b/spec/models/custom_field_spec.rb
@@ -4,8 +4,8 @@ RSpec.describe CustomField, type: :model do
   let( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} }
 
   context "validates" do
-    it { should validate_uniqueness_of(:name).scoped_to(:resource_type) }
-    it { should validate_uniqueness_of(:code).scoped_to(:resource_type).case_insensitive }
+    it { should validate_uniqueness_of(:name).scoped_to(:resource_type, :workgroup_id) }
+    it { should validate_uniqueness_of(:code).scoped_to(:resource_type, :workgroup_id).case_insensitive }
   end
 
   context "field access" do
-- 
cgit v1.2.3
From 87e0ea06cbc6bdfb281da8805b0221868341fee2 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 08:58:28 +0100
Subject: Add a toolbar for devs to easily manage permissions and features
---
 .gitignore                                         |  1 +
 app/assets/stylesheets/components/_toolbar.sass    | 47 +++++++++++++++++++++
 app/controllers/development_toolbar_controller.rb  | 11 +++++
 app/views/layouts/application.html.slim            |  3 ++
 .../layouts/navigation/_main_nav_top.html.slim     |  4 ++
 app/views/shared/_development_toolbar.html.slim    | 48 ++++++++++++++++++++++
 config/environments/development.rb                 |  7 ++++
 config/routes.rb                                   |  4 ++
 8 files changed, 125 insertions(+)
 create mode 100644 app/assets/stylesheets/components/_toolbar.sass
 create mode 100644 app/controllers/development_toolbar_controller.rb
 create mode 100644 app/views/shared/_development_toolbar.html.slim
diff --git a/.gitignore b/.gitignore
index ddd5fdda3..acdb5e230 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@ coverage
 /bin/spring
 
 spec/fixtures/target_*
+config/development_toolbar.rb
diff --git a/app/assets/stylesheets/components/_toolbar.sass b/app/assets/stylesheets/components/_toolbar.sass
new file mode 100644
index 000000000..47ae2ac0c
--- /dev/null
+++ b/app/assets/stylesheets/components/_toolbar.sass
@@ -0,0 +1,47 @@
+#development-toolbar
+  .inner
+    overflow: scroll
+    padding: 10px
+    max-height: 70vh
+    display: flex
+    flex-direction: row
+    .toggles
+      font-size: 0.7em
+      float: right
+      a:first-child
+        padding-right: 3px
+        margin-right: 3px
+        border-right: 1px solid $lightgrey
+
+    .col
+      flex: 1 1
+      padding-right: 10px
+      padding-left: 10px
+      border-right: 1px solid $lightgrey
+      &:last-child
+        padding-right: 0
+        border-right: none
+
+      ul
+        padding: 0
+      li
+        list-style-type: none
+      label
+        padding-left: 5px
+        font-weight: normal
+      h5
+        font-weight: bold
+      &.permissions
+        ul
+          overflow: hidden
+        li
+          float: left
+          width: 33%
+          label
+            font-size: 0.8em
+            max-width: calc(100% - 15px)
+            text-overflow: ellipsis
+            overflow: hidden
+            padding-top: 3px
+          input
+            vertical-align: top
diff --git a/app/controllers/development_toolbar_controller.rb b/app/controllers/development_toolbar_controller.rb
new file mode 100644
index 000000000..20349f7b8
--- /dev/null
+++ b/app/controllers/development_toolbar_controller.rb
@@ -0,0 +1,11 @@
+class DevelopmentToolbarController < ApplicationController
+  def update_settings
+    return unless Rails.application.config.development_toolbar.present?
+    organisation = current_user.organisation
+    organisation.features = params[:features].keys.select{|k| params[:features][k] == "true"}
+    organisation.save
+    current_user.permissions = params[:permissions].keys.select{|k| params[:permissions][k] == "true"}
+    current_user.save
+    redirect_to request.referrer || "/"
+  end
+end
diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim
index 567e14ef0..34b373295 100644
--- a/app/views/layouts/application.html.slim
+++ b/app/views/layouts/application.html.slim
@@ -21,3 +21,6 @@ html lang=I18n.locale
     = yield
     #sidebar
       = yield :sidebar
+
+    = render 'shared/development_toolbar'
+    = yield :javascript
diff --git a/app/views/layouts/navigation/_main_nav_top.html.slim b/app/views/layouts/navigation/_main_nav_top.html.slim
index 278249e09..f664d5416 100644
--- a/app/views/layouts/navigation/_main_nav_top.html.slim
+++ b/app/views/layouts/navigation/_main_nav_top.html.slim
@@ -14,9 +14,13 @@
           span = current_user.name
           span.fa.fa-lg.fa-user
 
+        - if Rails.application.config.development_toolbar
+          = link_to '#', data: { toggle: 'modal', target: '#development-toolbar' }, class: "toolbar-button menu-item" do
+            .fa.fa-cog
 
         = link_to destroy_user_session_path, method: :delete, class: 'menu-item', title: 'Se déconnecter' do
           span.fa.fa-lg.fa-sign-out
 
+
   = render 'layouts/navigation/nav_panel_operations'
   = render 'layouts/navigation/nav_panel_profile' if user_signed_in?
diff --git a/app/views/shared/_development_toolbar.html.slim b/app/views/shared/_development_toolbar.html.slim
new file mode 100644
index 000000000..b51bb3de6
--- /dev/null
+++ b/app/views/shared/_development_toolbar.html.slim
@@ -0,0 +1,48 @@
+- if Rails.application.config.development_toolbar
+  = modalbox 'development-toolbar' do
+    = form_tag development_toolbar_update_settings_path, authenticity_token: true do
+      .modal-header
+        h3= "Toolbar"
+
+      .inner
+          .col.features
+            h4
+              = "Features"
+              .toggles
+                = link_to 'all', '#', data: {mask: 'features', val: true}
+                = link_to 'none', '#', data: {mask: 'features', val: false}
+            ul
+              - Rails.application.config.development_toolbar.available_features.sort.each do |feature|
+                li
+                  = hidden_field_tag "features[#{feature}]", false, id: ""
+                  = check_box_tag "features[#{feature}]", true, has_feature?(feature)
+                  = label :features, feature
+          .col.permissions
+            h4
+              = "Permissions"
+              .toggles
+                = link_to 'all', '#', data: {mask: 'permissions', val: true}
+                = link_to 'none', '#', data: {mask: 'permissions', val: false}
+            - model = ""
+            - Rails.application.config.development_toolbar.available_permissions.sort.each do |permission|
+              - if permission.split('.').first != model
+                - model = permission.split('.').first
+                
+                h5
+                  = model
+                  .toggles
+                    = link_to 'all', '#', data: {mask: "permissions[#{model}", val: true}
+                    = link_to 'none', '#', data: {mask: "permissions[#{model}", val: false}
+                
+              li
+                = hidden_field_tag "permissions[#{permission}]", false, id: ""
+                = check_box_tag "permissions[#{permission}]", true, current_user.has_permission?(permission)
+                = label :permissions, permission, permission.split('.').last
+      .modal-footer= submit_tag t("actions.submit"), class: 'btn btn-primary'
+
+  - content_for :javascript do
+    coffee:
+      $('#development-toolbar .toggles a').click (e)->
+        $('#development-toolbar').find("[name^=\"#{e.currentTarget.dataset.mask}\"]").attr "checked", e.currentTarget.dataset.val == 'true'
+        e.preventDefault()
+        false
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 24a4ed6b8..925718e69 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -95,4 +95,11 @@ Rails.application.configure do
   config.i18n.available_locales = [:fr, :en]
 
   config.middleware.insert_after(ActionDispatch::Static, Rack::LiveReload) if ENV['LIVERELOAD']
+  config.development_toolbar = false
+  if ENV['TOOLBAR'] && File.exists?("config/development_toolbar.rb")
+    config.development_toolbar = OpenStruct.new
+    config.development_toolbar.tap do |toolbar|
+      eval File.read("config/development_toolbar.rb")
+    end
+  end
 end
diff --git a/config/routes.rb b/config/routes.rb
index 6a6a7cb07..07370ee6d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -239,6 +239,10 @@ ChouetteIhm::Application.routes.draw do
 
   get '/help/(*slug)' => 'help#show'
 
+  if Rails.application.config.development_toolbar
+    post "/development_toolbar" => "development_toolbar#update_settings", as: :development_toolbar_update_settings
+  end
+
   match '/404', to: 'errors#not_found', via: :all, as: 'not_found'
   match '/403', to: 'errors#forbidden', via: :all, as: 'forbidden'
   match '/422', to: 'errors#server_error', via: :all, as: 'unprocessable_entity'
-- 
cgit v1.2.3
From b0fbc01ba86730c93521cae2f29dbb858641a3da Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 09:00:34 +0100
Subject: Add a config file template
---
 config/development_toolbar.rb.tpl | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 config/development_toolbar.rb.tpl
diff --git a/config/development_toolbar.rb.tpl b/config/development_toolbar.rb.tpl
new file mode 100644
index 000000000..c278f07a7
--- /dev/null
+++ b/config/development_toolbar.rb.tpl
@@ -0,0 +1,2 @@
+toolbar.available_features = %w()
+toolbar.available_permissions = %w()
-- 
cgit v1.2.3
From 3a383e10f0b90e7367a6776bdf9ed1783ff739ac Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 10:00:05 +0100
Subject: - Add a default value for the config - Add a cancel button to dismiss
 the modal
---
 app/views/shared/_development_toolbar.html.slim | 4 +++-
 config/application.rb                           | 2 ++
 2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/views/shared/_development_toolbar.html.slim b/app/views/shared/_development_toolbar.html.slim
index b51bb3de6..4ad69f7ad 100644
--- a/app/views/shared/_development_toolbar.html.slim
+++ b/app/views/shared/_development_toolbar.html.slim
@@ -38,7 +38,9 @@
                 = hidden_field_tag "permissions[#{permission}]", false, id: ""
                 = check_box_tag "permissions[#{permission}]", true, current_user.has_permission?(permission)
                 = label :permissions, permission, permission.split('.').last
-      .modal-footer= submit_tag t("actions.submit"), class: 'btn btn-primary'
+      .modal-footer
+        button.btn.btn-link type='button' data-dismiss='modal' #{t('cancel')}
+        = submit_tag t("actions.submit"), class: 'btn btn-primary'
 
   - content_for :javascript do
     coffee:
diff --git a/config/application.rb b/config/application.rb
index bda582610..8da6a7428 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -39,6 +39,8 @@ module ChouetteIhm
       'FeatureChecker::NotAuthorizedError' => :unauthorized
     )
 
+    config.development_toolbar = false
+
     unless Rails.env.production?
         # Work around sprockets+teaspoon mismatch:
         Rails.application.config.assets.precompile += %w(spec_helper.js)
-- 
cgit v1.2.3
From e126e59759d2fcb8d5989cf154a5f56ff5268f02 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 10:39:38 +0100
Subject: Add links to doc
---
 app/assets/stylesheets/components/_toolbar.sass | 2 ++
 app/views/shared/_development_toolbar.html.slim | 3 +++
 config/environments/development.rb              | 3 +++
 3 files changed, 8 insertions(+)
diff --git a/app/assets/stylesheets/components/_toolbar.sass b/app/assets/stylesheets/components/_toolbar.sass
index 47ae2ac0c..86a32bd82 100644
--- a/app/assets/stylesheets/components/_toolbar.sass
+++ b/app/assets/stylesheets/components/_toolbar.sass
@@ -29,6 +29,8 @@
       label
         padding-left: 5px
         font-weight: normal
+        & + a
+          float: right
       h5
         font-weight: bold
       &.permissions
diff --git a/app/views/shared/_development_toolbar.html.slim b/app/views/shared/_development_toolbar.html.slim
index 4ad69f7ad..aafd37885 100644
--- a/app/views/shared/_development_toolbar.html.slim
+++ b/app/views/shared/_development_toolbar.html.slim
@@ -17,6 +17,9 @@
                   = hidden_field_tag "features[#{feature}]", false, id: ""
                   = check_box_tag "features[#{feature}]", true, has_feature?(feature)
                   = label :features, feature
+                  - if Rails.application.config.development_toolbar.features_doc_url
+                    = link_to "#{Rails.application.config.development_toolbar.features_doc_url}##{feature}", target: :blank do
+                      .fa.fa-question-circle
           .col.permissions
             h4
               = "Permissions"
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 925718e69..1d2fee44f 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -98,6 +98,9 @@ Rails.application.configure do
   config.development_toolbar = false
   if ENV['TOOLBAR'] && File.exists?("config/development_toolbar.rb")
     config.development_toolbar = OpenStruct.new
+    config.development_toolbar.features_doc_url = nil
+    config.development_toolbar.available_features = %w()
+    config.development_toolbar.available_permissions = %w()
     config.development_toolbar.tap do |toolbar|
       eval File.read("config/development_toolbar.rb")
     end
-- 
cgit v1.2.3
From 65db4fe0706d1c47f32ed51b219244693d3dbddf Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 25 Jan 2018 11:39:32 +0100
Subject: Refs #5598; Enable button to view a JourneyPattern when the user is
 not allowed to edit it
---
 app/javascript/journey_patterns/components/JourneyPattern.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js
index ecbebe2cc..01734f3a3 100644
--- a/app/javascript/journey_patterns/components/JourneyPattern.js
+++ b/app/javascript/journey_patterns/components/JourneyPattern.js
@@ -136,10 +136,9 @@ export default class JourneyPattern extends Component{
               
             
             
-              - 
+              
- 
                 
                   ))}
@@ -86,4 +86,4 @@ Navigate.propTypes = {
   status: PropTypes.object.isRequired,
   pagination: PropTypes.object.isRequired,
   dispatch: PropTypes.func.isRequired
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3
From 33c7b132b1aa0ac061d82226a70dcb8034b2ec68 Mon Sep 17 00:00:00 2001
From: Zog
Date: Fri, 26 Jan 2018 10:01:37 +0100
Subject: Refs #5740; Add message in company filter when no value is available
---
 app/views/referential_vehicle_journeys/_filters.html.slim | 5 ++++-
 config/locales/companies.en.yml                           | 1 +
 config/locales/companies.fr.yml                           | 1 +
 3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index 609927615..f85ef1eb9 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -8,7 +8,10 @@
   .ffg-row
     .form-group.togglable
       = f.label Chouette::VehicleJourney.human_attribute_name(:company), required: false, class: 'control-label'
-      = f.input :company_id_eq_any, collection: @all_companies.select(:id, :name).order(name: :asc), as: :check_boxes, label: false, label_method: lambda{|l| ("" + l.name + "").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}
+      - if @all_companies.present?
+        = f.input :company_id_eq_any, collection: @all_companies.select(:id, :name).order(name: :asc), as: :check_boxes, label: false, label_method: lambda{|l| ("" + l.name + "").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}
+      - else
+        = f.input :company_id_eq_any, collection: [[I18n.t('companies.search_no_results_for_filter'), nil]], as: :check_boxes, label: false, disabled: true, required: false, wrapper_html: { class: 'checkbox_list disabled'}
     .form-group.togglable.name-filter
       = f.label Chouette::VehicleJourney.human_attribute_name(:published_journey_name), required: false, class: 'control-label'
       .inputs.form-inline.checkbox_list
diff --git a/config/locales/companies.en.yml b/config/locales/companies.en.yml
index a3cd520cb..29dc3911b 100644
--- a/config/locales/companies.en.yml
+++ b/config/locales/companies.en.yml
@@ -1,6 +1,7 @@
 en:
   companies: &en_companies
     search_no_results: "No company matching your query"
+    search_no_results_for_filter: "No company has been set for thess journeys"
     actions:
       new: "Add a new company"
       edit: "Edit this company"
diff --git a/config/locales/companies.fr.yml b/config/locales/companies.fr.yml
index 0cf729c35..3284115ab 100644
--- a/config/locales/companies.fr.yml
+++ b/config/locales/companies.fr.yml
@@ -1,6 +1,7 @@
 fr:
   companies: &fr_companies
     search_no_results: "Aucun transporteur ne correspond à votre recherche"
+    search_no_results_for_filter: "Aucun transporteur renseigné sur ces courses"
     actions:
       new: "Ajouter un transporteur"
       edit: "Editer ce transporteur"
-- 
cgit v1.2.3
From 2dd90cb38b4da66cdcc008beed6b15d073d1f4be Mon Sep 17 00:00:00 2001
From: Luc Donnet
Date: Tue, 30 Jan 2018 16:37:53 +0100
Subject: Update companies.en.yml
Fix translation in companies.en.yml---
 config/locales/companies.en.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/locales/companies.en.yml b/config/locales/companies.en.yml
index 29dc3911b..becb087b1 100644
--- a/config/locales/companies.en.yml
+++ b/config/locales/companies.en.yml
@@ -1,7 +1,7 @@
 en:
   companies: &en_companies
     search_no_results: "No company matching your query"
-    search_no_results_for_filter: "No company has been set for thess journeys"
+    search_no_results_for_filter: "No company has been set for these journeys"
     actions:
       new: "Add a new company"
       edit: "Edit this company"
-- 
cgit v1.2.3
From 6632217117a66bd09b2e8261e4351add9971d47c Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Tue, 23 Jan 2018 22:46:16 -0800
Subject: First draft for including calendars into workgroup for having
 appropriate scoping
---
 app/decorators/calendar_decorator.rb    | 10 ++++++++++
 app/helpers/table_builder_helper.rb     | 10 ++++++++++
 app/helpers/table_builder_helper/url.rb |  6 +++++-
 app/views/calendars/index.html.slim     |  4 ++++
 4 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb
index 0a7cbc312..1b2a085b7 100644
--- a/app/decorators/calendar_decorator.rb
+++ b/app/decorators/calendar_decorator.rb
@@ -5,11 +5,21 @@ class CalendarDecorator < AF83::Decorator
 
   create_action_link
 
+<<<<<<< HEAD
   with_instance_decorator do |instance_decorator|
     instance_decorator.show_action_link
     instance_decorator.edit_action_link
     instance_decorator.destroy_action_link do |l|
       l.data {{ confirm: h.t('calendars.actions.destroy_confirm') }}
+=======
+    if h.policy(object).destroy?
+      links << Link.new(
+        content: h.destroy_link_content,
+        href: h.workgroup_calendar_path(context[:workgroup], object),
+        method: :delete,
+        data: { confirm: h.t('calendars.actions.destroy_confirm') }
+      )
+>>>>>>> First draft for including calendars into workgroup for having appropriate scoping
     end
   end
 end
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index 2068dd23c..245843eab 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -316,6 +316,7 @@ module TableBuilderHelper
       content_tag :span, '', class: 'fa fa-cog'
     end
 
+<<<<<<< HEAD
     action_links = item.action_links
     if action_links.is_a?(AF83::Decorator::ActionLinks)
       menu = content_tag :div, class: 'dropdown-menu' do
@@ -336,6 +337,15 @@ module TableBuilderHelper
           gear_menu_link(link)
         end.join.html_safe
       end
+=======
+    menu = content_tag :ul, class: 'dropdown-menu' do
+      (
+        CustomLinks.new(item, pundit_user, links, referential, workgroup).links +
+        item.action_links.select { |link| link.is_a?(Link) }
+      ).map do |link|
+        gear_menu_link(link)
+      end.join.html_safe
+>>>>>>> First draft for including calendars into workgroup for having appropriate scoping
     end
 
     content_tag :div, trigger + menu, class: 'btn-group'
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index 0e3dce0aa..550b1e9d4 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -3,7 +3,7 @@ module TableBuilderHelper
     def self.polymorphic_url_parts(item, referential, workgroup)
       polymorph_url = []
 
-      unless item.is_a?(Calendar) || item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
+      unless item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
         if referential
           polymorph_url << referential
           polymorph_url << item.line if item.respond_to? :line
@@ -20,7 +20,11 @@ module TableBuilderHelper
           end
         end
       else
+<<<<<<< HEAD
         polymorph_url << item.workgroup if item.respond_to? :workgroup
+=======
+        polymorph_url << item.workgroup if item.is_a?(Calendar)
+>>>>>>> First draft for including calendars into workgroup for having appropriate scoping
         polymorph_url << item
       end
 
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index 0b58c0c72..a2d6b4731 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,4 +1,8 @@
 - breadcrumb :calendars, workgroup
+- content_for :page_header_actions do
+  - if policy(Calendar).create?
+    = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
+
 
 .page_content
   .container-fluid
-- 
cgit v1.2.3
From dd61691343892d502842341bc94ca8b355a716cc Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Wed, 24 Jan 2018 19:55:56 -0800
Subject: update calendar build_links for table builder
---
 app/controllers/calendars_controller.rb |  1 +
 app/helpers/table_builder_helper/url.rb |  6 +++++-
 app/views/calendars/index.html.slim     | 10 ++++++++++
 3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 6f1522428..61a62c285 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,4 +1,5 @@
 class CalendarsController < ChouetteController
+  include WorkgroupSupport
   include PolicyChecker
   defaults resource_class: Calendar
   before_action :ransack_contains_date, only: [:index]
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index 550b1e9d4..63a559162 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -3,7 +3,7 @@ module TableBuilderHelper
     def self.polymorphic_url_parts(item, referential, workgroup)
       polymorph_url = []
 
-      unless item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
+      unless item.is_a?(Calendar) || item.is_a?(Referential) || item.is_a?(ComplianceControlSet)
         if referential
           polymorph_url << referential
           polymorph_url << item.line if item.respond_to? :line
@@ -20,11 +20,15 @@ module TableBuilderHelper
           end
         end
       else
+<<<<<<< HEAD
 <<<<<<< HEAD
         polymorph_url << item.workgroup if item.respond_to? :workgroup
 =======
         polymorph_url << item.workgroup if item.is_a?(Calendar)
 >>>>>>> First draft for including calendars into workgroup for having appropriate scoping
+=======
+        polymorph_url << item.workgroup if item.respond_to? :workgroup
+>>>>>>> update calendar build_links for table builder
         polymorph_url << item
       end
 
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index a2d6b4731..f7192bc01 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,4 +1,14 @@
+<<<<<<< HEAD
 - breadcrumb :calendars, workgroup
+=======
+<<<<<<< HEAD
+- breadcrumb :calendars
+<<<<<<< HEAD
+=======
+=======
+- breadcrumb :calendars, current_workgroup
+>>>>>>> update calendar build_links for table builder
+>>>>>>> update calendar build_links for table builder
 - content_for :page_header_actions do
   - if policy(Calendar).create?
     = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
-- 
cgit v1.2.3
From 2ce236aea91bbe35a6b03353b4c5c7f33ded8eac Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Tue, 30 Jan 2018 23:07:07 -0800
Subject: Make some changes to avoid unnacessaty lines of code
---
 app/controllers/calendars_controller.rb |  3 +--
 app/views/calendars/index.html.slim     | 10 ++++++++++
 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 61a62c285..eaff3b0c6 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,5 +1,4 @@
 class CalendarsController < ChouetteController
-  include WorkgroupSupport
   include PolicyChecker
   defaults resource_class: Calendar
   before_action :ransack_contains_date, only: [:index]
@@ -96,4 +95,4 @@ class CalendarsController < ChouetteController
     scope
   end
 
-end
+end
\ No newline at end of file
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index f7192bc01..04ae8c758 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,6 +1,9 @@
 <<<<<<< HEAD
+<<<<<<< HEAD
 - breadcrumb :calendars, workgroup
 =======
+=======
+>>>>>>> Make some changes to avoid unnacessaty lines of code
 <<<<<<< HEAD
 - breadcrumb :calendars
 <<<<<<< HEAD
@@ -12,7 +15,14 @@
 - content_for :page_header_actions do
   - if policy(Calendar).create?
     = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
+<<<<<<< HEAD
 
+=======
+>>>>>>> First draft for including calendars into workgroup for having appropriate scoping
+=======
+- breadcrumb :calendars, workgroup
+>>>>>>> Make some changes to avoid unnacessaty lines of code
+>>>>>>> Make some changes to avoid unnacessaty lines of code
 
 .page_content
   .container-fluid
-- 
cgit v1.2.3
From f32ffd5b740583777634d0b58e3d50aa9d2414a3 Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Wed, 31 Jan 2018 09:00:34 -0800
Subject: Fix calendar mailer url
---
 app/decorators/calendar_decorator.rb        | 12 ------------
 app/helpers/table_builder_helper.rb         | 11 +----------
 app/helpers/table_builder_helper/url.rb     |  8 --------
 app/views/calendar_mailer/created.html.slim |  4 ++--
 app/views/calendar_mailer/updated.html.slim |  2 +-
 app/views/calendars/index.html.slim         | 24 ------------------------
 spec/mailers/calendar_mailer_spec.rb        |  2 +-
 7 files changed, 5 insertions(+), 58 deletions(-)
diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb
index 1b2a085b7..4c6088e8e 100644
--- a/app/decorators/calendar_decorator.rb
+++ b/app/decorators/calendar_decorator.rb
@@ -1,25 +1,13 @@
 class CalendarDecorator < AF83::Decorator
   decorates Calendar
-
   set_scope { context[:workgroup] }
-
   create_action_link
 
-<<<<<<< HEAD
   with_instance_decorator do |instance_decorator|
     instance_decorator.show_action_link
     instance_decorator.edit_action_link
     instance_decorator.destroy_action_link do |l|
       l.data {{ confirm: h.t('calendars.actions.destroy_confirm') }}
-=======
-    if h.policy(object).destroy?
-      links << Link.new(
-        content: h.destroy_link_content,
-        href: h.workgroup_calendar_path(context[:workgroup], object),
-        method: :delete,
-        data: { confirm: h.t('calendars.actions.destroy_confirm') }
-      )
->>>>>>> First draft for including calendars into workgroup for having appropriate scoping
     end
   end
 end
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index 245843eab..f48075ed9 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -316,7 +316,6 @@ module TableBuilderHelper
       content_tag :span, '', class: 'fa fa-cog'
     end
 
-<<<<<<< HEAD
     action_links = item.action_links
     if action_links.is_a?(AF83::Decorator::ActionLinks)
       menu = content_tag :div, class: 'dropdown-menu' do
@@ -337,15 +336,6 @@ module TableBuilderHelper
           gear_menu_link(link)
         end.join.html_safe
       end
-=======
-    menu = content_tag :ul, class: 'dropdown-menu' do
-      (
-        CustomLinks.new(item, pundit_user, links, referential, workgroup).links +
-        item.action_links.select { |link| link.is_a?(Link) }
-      ).map do |link|
-        gear_menu_link(link)
-      end.join.html_safe
->>>>>>> First draft for including calendars into workgroup for having appropriate scoping
     end
 
     content_tag :div, trigger + menu, class: 'btn-group'
@@ -405,6 +395,7 @@ module TableBuilderHelper
     klass << link.extra_class if link.extra_class
     klass << 'delete-action' if link.method == :delete
     klass << 'disabled' if link.disabled
+
     content_tag(
       :li,
       link_to(
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index 63a559162..0e3dce0aa 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -20,15 +20,7 @@ module TableBuilderHelper
           end
         end
       else
-<<<<<<< HEAD
-<<<<<<< HEAD
         polymorph_url << item.workgroup if item.respond_to? :workgroup
-=======
-        polymorph_url << item.workgroup if item.is_a?(Calendar)
->>>>>>> First draft for including calendars into workgroup for having appropriate scoping
-=======
-        polymorph_url << item.workgroup if item.respond_to? :workgroup
->>>>>>> update calendar build_links for table builder
         polymorph_url << item
       end
 
diff --git a/app/views/calendar_mailer/created.html.slim b/app/views/calendar_mailer/created.html.slim
index 37b2a86ea..bee071150 100644
--- a/app/views/calendar_mailer/created.html.slim
+++ b/app/views/calendar_mailer/created.html.slim
@@ -1,4 +1,4 @@
-div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url)
+div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup))
 
 table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif;width:550px;margin:0px auto;color:#333333;"
 
@@ -16,7 +16,7 @@ table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif
           = t('mailers.calendar_mailer.updated.subject')
 
         p style="font-size:14px;margin:0px 0px 10px 0px;"
-          = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe
+          = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup)).html_safe
 
     tr
       td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;"
diff --git a/app/views/calendar_mailer/updated.html.slim b/app/views/calendar_mailer/updated.html.slim
index bf128439a..0bdc2e7db 100644
--- a/app/views/calendar_mailer/updated.html.slim
+++ b/app/views/calendar_mailer/updated.html.slim
@@ -14,7 +14,7 @@ table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif
           = t('mailers.calendar_mailer.updated.subject')
 
         p style="font-size:14px;margin:0px 0px 10px 0px;"
-          = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe
+          = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup)).html_safe
 
     tr
       td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;"
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index 04ae8c758..0b58c0c72 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,28 +1,4 @@
-<<<<<<< HEAD
-<<<<<<< HEAD
 - breadcrumb :calendars, workgroup
-=======
-=======
->>>>>>> Make some changes to avoid unnacessaty lines of code
-<<<<<<< HEAD
-- breadcrumb :calendars
-<<<<<<< HEAD
-=======
-=======
-- breadcrumb :calendars, current_workgroup
->>>>>>> update calendar build_links for table builder
->>>>>>> update calendar build_links for table builder
-- content_for :page_header_actions do
-  - if policy(Calendar).create?
-    = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')
-<<<<<<< HEAD
-
-=======
->>>>>>> First draft for including calendars into workgroup for having appropriate scoping
-=======
-- breadcrumb :calendars, workgroup
->>>>>>> Make some changes to avoid unnacessaty lines of code
->>>>>>> Make some changes to avoid unnacessaty lines of code
 
 .page_content
   .container-fluid
diff --git a/spec/mailers/calendar_mailer_spec.rb b/spec/mailers/calendar_mailer_spec.rb
index 9a2076f64..00d73a58b 100644
--- a/spec/mailers/calendar_mailer_spec.rb
+++ b/spec/mailers/calendar_mailer_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe CalendarMailer, type: :mailer do
     end
 
     it 'should have correct body' do
-      key = I18n.t("mailers.calendar_mailer.#{type}.body", cal_name: calendar.name, cal_index_url: calendars_url)
+      key = I18n.t("mailers.calendar_mailer.#{type}.body", cal_name: calendar.name, cal_index_url: workgroup_calendars_url(calendar.workgroup))
       expect(email).to have_body_text /#{key}/
     end
   end
-- 
cgit v1.2.3
From f6a581980fc63dd84372161c2fd711c0b50bb2df Mon Sep 17 00:00:00 2001
From: Zog
Date: Fri, 12 Jan 2018 11:31:14 +0100
Subject: Refs #5571 @0.25h; Let the user select the number of items per page
on ReferentialVehicleJourneys#index
---
 app/assets/stylesheets/components/_forms.sass              | 4 ++++
 app/controllers/referential_vehicle_journeys_controller.rb | 2 +-
 app/views/referential_vehicle_journeys/_filters.html.slim  | 3 +++
 config/locales/simple_form.en.yml                          | 1 +
 config/locales/simple_form.fr.yml                          | 1 +
 5 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass
index b7f720963..1f0d2f59b 100644
--- a/app/assets/stylesheets/components/_forms.sass
+++ b/app/assets/stylesheets/components/_forms.sass
@@ -444,6 +444,10 @@ table, .table
     margin: 0
     min-height: 41px
     padding: 5px 15px
+    &.per-page-select
+      padding-top: 10px
+      .selected
+        font-weight: bold
 
     .control-label
       font-weight: 700
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index f93de29cc..89b3703a0 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -13,7 +13,7 @@ class ReferentialVehicleJourneysController < ChouetteController
     @q ||= end_of_association_chain
     @q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
     @q = @q.ransack(params[:q])
-    @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: 10
+    @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
     @all_stop_areas = Chouette::StopArea.where("id IN (#{@referential.vehicle_journeys.joins(:stop_areas).select("stop_areas.id").to_sql})").distinct
   end
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index f85ef1eb9..1301d3dab 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -6,6 +6,9 @@
         button.btn.btn-default#search-btn type='submit'
           span.fa.fa-search
   .ffg-row
+    .form-group.per-page-select
+      = I18n.t("simple_form.per_page")
+      = %w(10 50 100).each_with_index.map{ |v, i| (params[:per_page] == v || params[:per_page].nil? && i == 0) ? "#{v}" : link_to(v, referential_vehicle_journeys_path(@referential, q: params[:q], per_page: v)) }.join(' - ').html_safe
     .form-group.togglable
       = f.label Chouette::VehicleJourney.human_attribute_name(:company), required: false, class: 'control-label'
       - if @all_companies.present?
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index ad722312e..ed4ad2e95 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -1,5 +1,6 @@
 en:
   simple_form:
+    "per_page": "Per page: "
     "yes": 'Yes'
     "no": 'No'
     from: 'From'
diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml
index cd5dd1fbe..a1868eef7 100644
--- a/config/locales/simple_form.fr.yml
+++ b/config/locales/simple_form.fr.yml
@@ -1,5 +1,6 @@
 fr:
   simple_form:
+    "per_page": "Afficher par: "
     "yes": 'Oui'
     "no": 'Non'
     from: 'Du'
-- 
cgit v1.2.3
From 97ae053caa9e140dd3b3556dab542cc19d6ea8bd Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 25 Jan 2018 14:42:08 +0100
Subject: Refs #5718; Show times at stops in ReferentialVehicleJourneys#index
When 1 or 2 stops have been selected
---
 .../referential_vehicle_journeys_controller.rb     |  2 ++
 app/helpers/vehicle_journeys_helper.rb             | 24 ++++++++++++++--------
 .../referential_vehicle_journeys/index.html.slim   |  3 ++-
 3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index 89b3703a0..c3fcde0b1 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -16,6 +16,8 @@ class ReferentialVehicleJourneysController < ChouetteController
     @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
     @all_stop_areas = Chouette::StopArea.where("id IN (#{@referential.vehicle_journeys.joins(:stop_areas).select("stop_areas.id").to_sql})").distinct
+    stop_area_ids = params[:q][:stop_area_ids].select(&:present?)
+    @filters_stop_areas = Chouette::StopArea.find(stop_area_ids) if stop_area_ids.present? && stop_area_ids.size <= 2
   end
 
 end
diff --git a/app/helpers/vehicle_journeys_helper.rb b/app/helpers/vehicle_journeys_helper.rb
index 6877abd11..1cc865c62 100644
--- a/app/helpers/vehicle_journeys_helper.rb
+++ b/app/helpers/vehicle_journeys_helper.rb
@@ -1,5 +1,5 @@
 module VehicleJourneysHelper
-  
+
   def vehicle_name( vehicle)
     if !vehicle.published_journey_name.blank?
       vehicle.published_journey_name.first(8)
@@ -11,11 +11,11 @@ module VehicleJourneysHelper
       vehicle.id
     end
   end
-  
+
   def missing_time_check( is_present)
     return "missing" if (is_present && is_present.departure_time.nil?)
   end
-  
+
   def vehicle_departure(vehicle, departure_time=nil)
     unless departure_time
       first_vjas = vehicle.vehicle_journey_at_stops.first
@@ -24,7 +24,7 @@ module VehicleJourneysHelper
     end
     l(departure_time, :format => :hour).gsub( /  /, ' ')
   end
-  
+
   def vehicle_title(vehicle, journey_frequency=nil)
     return t("vehicle_journeys.vehicle_journey#{'_frequency' if vehicle.frequency?}.title_stopless", :name => vehicle_name( vehicle)) if vehicle.vehicle_journey_at_stops.empty?
     first_vjas = vehicle.vehicle_journey_at_stops.first
@@ -40,7 +40,7 @@ module VehicleJourneysHelper
             :time => vehicle_departure(vehicle, (journey_frequency ? journey_frequency.first_departure_time : nil )))
     end
   end
-  
+
   def route_journey_pattern_label_pairs route
     route
       .journey_patterns
@@ -50,7 +50,7 @@ module VehicleJourneysHelper
   def edit_vehicle_title( vehicle)
     return t('vehicle_journeys.edit.title_stopless', :name => vehicle_name( vehicle)) if vehicle.vehicle_journey_at_stops.empty?
     first_vjas = vehicle.vehicle_journey_at_stops.first
-    t('vehicle_journeys.edit.title', 
+    t('vehicle_journeys.edit.title',
           :name => vehicle_name( vehicle),
           :stop => first_vjas.stop_point.stop_area.name,
           :time => vehicle_departure(vehicle))
@@ -59,6 +59,14 @@ module VehicleJourneysHelper
   def exist_vehicle_journeys?(route)
     route.vehicle_journeys.count > 0
   end
-  
-end
 
+  def table_builder_column_for_stop_area stop_area
+    return nil unless stop_area
+    TableBuilderHelper::Column.new(
+      name: stop_area.name,
+      attribute: Proc.new {|v| v.vehicle_journey_at_stops.find{|vjas| vjas.stop_point.stop_area_id == stop_area.id}&.departure },
+      sortable: false
+    )
+  end
+
+end
diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim
index d29a78a6c..69e29597c 100644
--- a/app/views/referential_vehicle_journeys/index.html.slim
+++ b/app/views/referential_vehicle_journeys/index.html.slim
@@ -42,12 +42,13 @@
                   attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure }, \
                   sortable: false \
                 ), \
+                @filters_stop_areas&.map{|s| table_builder_column_for_stop_area(s)},
                 TableBuilderHelper::Column.new( \
                   key: :arrival_time, \
                   attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival }, \
                   sortable: false \
                 ), \
-              ],
+              ].flatten.compact,
               cls: 'table has-filter has-search'
 
           = new_pagination @vehicle_journeys, 'pull-right'
-- 
cgit v1.2.3
From 76607faf09c0dabd5282d4b6e5624c75d4625a63 Mon Sep 17 00:00:00 2001
From: Zog
Date: Fri, 26 Jan 2018 12:49:23 +0100
Subject: Refs #5741 @2h; Add a map of all routes on a line#show
---
 Gemfile.lock                                      |  22 +--
 app/assets/stylesheets/OpenLayers/custom.sass     |  21 +++
 app/helpers/routes_helper.rb                      |   8 +-
 app/javascript/helpers/routes_map.coffee          | 157 ++++++++++++++++++++++
 app/javascript/packs/referential_lines/show.js    |  10 ++
 app/javascript/packs/routes/show.js               | 123 +----------------
 app/javascript/routes/components/StopPointList.js |   4 +-
 app/views/referential_lines/show.html.slim        |   8 +-
 config/webpack/environment.js                     |   2 +
 config/webpack/loaders/coffee.js                  |   6 +
 package.json                                      |   3 +-
 yarn.lock                                         |  12 +-
 12 files changed, 238 insertions(+), 138 deletions(-)
 create mode 100644 app/javascript/helpers/routes_map.coffee
 create mode 100644 app/javascript/packs/referential_lines/show.js
 create mode 100644 config/webpack/loaders/coffee.js
diff --git a/Gemfile.lock b/Gemfile.lock
index ba1a90a5a..ba86a911f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -156,6 +156,7 @@ GEM
       sort_alphabetical (~> 1.0)
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
+    crass (1.0.3)
     cucumber (2.4.0)
       builder (>= 2.1.2)
       cucumber-core (~> 1.5.0)
@@ -265,7 +266,7 @@ GEM
     htmlbeautifier (1.3.1)
     httparty (0.14.0)
       multi_xml (>= 0.5.2)
-    i18n (0.9.0)
+    i18n (0.9.3)
       concurrent-ruby (~> 1.0)
     i18n-tasks (0.9.15)
       activesupport (>= 4.0.2)
@@ -302,7 +303,8 @@ GEM
       thor
       with_env (> 1.0)
       xml-simple
-    loofah (2.0.3)
+    loofah (2.1.1)
+      crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     mail (2.6.4)
       mime-types (>= 1.16, < 4)
@@ -313,7 +315,7 @@ GEM
     mime-types-data (3.2016.0521)
     mimemagic (0.3.2)
     mini_portile2 (2.3.0)
-    minitest (5.10.3)
+    minitest (5.11.2)
     money (6.10.1)
       i18n (>= 0.6.4, < 1.0)
     multi_json (1.12.1)
@@ -368,7 +370,7 @@ GEM
       rack
     rack-protection (1.5.3)
       rack
-    rack-proxy (0.6.2)
+    rack-proxy (0.6.3)
       rack
     rack-test (0.6.3)
       rack (>= 1.0)
@@ -396,8 +398,8 @@ GEM
       rails-assets-jquery (>= 1.0.0)
     rails-deprecated_sanitizer (1.0.3)
       activesupport (>= 4.2.0.alpha)
-    rails-dom-testing (1.0.8)
-      activesupport (>= 4.2.0.beta, < 5.0)
+    rails-dom-testing (1.0.9)
+      activesupport (>= 4.2.0, < 5.0)
       nokogiri (~> 1.6)
       rails-deprecated_sanitizer (>= 1.0.1)
     rails-erd (1.5.2)
@@ -419,7 +421,7 @@ GEM
       thor (>= 0.18.1, < 2.0)
     rainbow (2.2.2)
       rake
-    rake (12.0.0)
+    rake (12.3.0)
     ransack (1.8.3)
       actionpack (>= 3.0)
       activerecord (>= 3.0)
@@ -535,7 +537,7 @@ GEM
     therubyracer (0.12.3)
       libv8 (~> 3.16.14.15)
       ref
-    thor (0.19.4)
+    thor (0.20.0)
     thread (0.2.2)
     thread_safe (0.3.6)
     tilt (1.4.1)
@@ -547,7 +549,7 @@ GEM
       json (>= 1.8, < 3.0)
       parser (>= 2.3.0.7)
       rainbow (>= 1.99.1, < 3.0)
-    tzinfo (1.2.3)
+    tzinfo (1.2.4)
       thread_safe (~> 0.1)
     uglifier (2.7.2)
       execjs (>= 0.3.0)
@@ -560,7 +562,7 @@ GEM
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff
-    webpacker (3.0.2)
+    webpacker (3.2.1)
       activesupport (>= 4.2)
       rack-proxy (>= 0.6.1)
       railties (>= 4.2)
diff --git a/app/assets/stylesheets/OpenLayers/custom.sass b/app/assets/stylesheets/OpenLayers/custom.sass
index 7a5b4baf1..5a3612f99 100644
--- a/app/assets/stylesheets/OpenLayers/custom.sass
+++ b/app/assets/stylesheets/OpenLayers/custom.sass
@@ -2,6 +2,27 @@
   .list-group-item &
     margin-top: 15px
 
+  .routes-labels
+    padding: 0
+    display: flex
+    justify-content: space-between
+    flex-wrap: wrap
+    margin: 5px -5px
+    li
+      list-style-type: none
+      flex: 1 0 25%
+      border: 1px solid $lightgrey
+      padding: 5px
+      margin: 5px
+      border-radius: 5px
+      cursor: pointer
+      color: $blue
+      white-space: nowrap
+      max-width: 33%
+
+      &:hover
+        background: $orange
+        color: white
 
   .ol-scale-line
     background-color: transparent
diff --git a/app/helpers/routes_helper.rb b/app/helpers/routes_helper.rb
index 4bffa99d4..61714a066 100644
--- a/app/helpers/routes_helper.rb
+++ b/app/helpers/routes_helper.rb
@@ -19,13 +19,15 @@ module RoutesHelper
     css
   end
 
-  def route_json_for_edit(route)
-    route.stop_points.includes(:stop_area).order(:position).map do |stop_point|
+  def route_json_for_edit(route, serialize: true)
+    data = route.stop_points.includes(:stop_area).order(:position).map do |stop_point|
       stop_area_attributes = stop_point.stop_area.attributes.slice("name","city_name", "zip_code", "registration_number", "longitude", "latitude", "area_type", "comment")
       stop_area_attributes["short_name"] = truncate(stop_area_attributes["name"], :length => 30) || ""
       stop_point_attributes = stop_point.attributes.slice("for_boarding","for_alighting")
       stop_area_attributes.merge(stop_point_attributes).merge(stoppoint_id: stop_point.id, stoparea_id: stop_point.stop_area.id).merge(user_objectid: stop_point.stop_area.user_objectid)
-    end.to_json
+    end
+    data = data.to_json if serialize
+    data
   end
 
 end
diff --git a/app/javascript/helpers/routes_map.coffee b/app/javascript/helpers/routes_map.coffee
new file mode 100644
index 000000000..85def1390
--- /dev/null
+++ b/app/javascript/helpers/routes_map.coffee
@@ -0,0 +1,157 @@
+class RoutesMap
+  constructor: (@target)->
+    @initMap()
+    @area = []
+    @seenStopIds = []
+    @routes = {}
+
+  initMap: ->
+    @map = new ol.Map
+      target: @target,
+      layers:   [ new ol.layer.Tile(source: new ol.source.OSM()) ]
+      controls: [ new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider() ],
+      interactions: ol.interaction.defaults(zoom: true)
+      view: new ol.View()
+
+  addRoutes: (routes)->
+    for route in routes
+      @addRoute route
+
+  addRoute: (route)->
+    geoColPts = []
+    geoColLns = []
+    @routes[route.id] = route if route.id
+    stops = route.stops || route
+    geoColEdges = [
+      new ol.Feature({
+        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stops[0].longitude), parseFloat(stops[0].latitude)]))
+      }),
+      new ol.Feature({
+        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stops[stops.length - 1].longitude), parseFloat(stops[stops.length - 1].latitude)]))
+      })
+    ]
+    stops.forEach (stop, i) =>
+      if i < stops.length - 1
+        geoColLns.push new ol.Feature
+          geometry: new ol.geom.LineString([
+            ol.proj.fromLonLat([parseFloat(stops[i].longitude), parseFloat(stops[i].latitude)]),
+            ol.proj.fromLonLat([parseFloat(stops[i + 1].longitude), parseFloat(stops[i + 1].latitude)])
+          ])
+
+      geoColPts.push(new ol.Feature({
+        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)]))
+      }))
+      unless @seenStopIds.indexOf(stop.stoparea_id) > 0
+        @area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)]
+        @seenStopIds.push stop.stoparea_id
+
+    vectorPtsLayer = new ol.layer.Vector({
+      source: new ol.source.Vector({
+        features: geoColPts
+      }),
+      style: @defaultStyles(),
+      zIndex: 2
+    })
+    route.vectorPtsLayer = vectorPtsLayer if route.id
+    vectorEdgesLayer = new ol.layer.Vector({
+      source: new ol.source.Vector({
+        features: geoColEdges
+      }),
+      style: @edgeStyles(),
+      zIndex: 3
+    })
+    route.vectorEdgesLayer = vectorEdgesLayer if route.id
+    vectorLnsLayer = new ol.layer.Vector({
+      source: new ol.source.Vector({
+        features: geoColLns
+      }),
+      style: [@lineStyle()],
+      zIndex: 1
+    })
+    route.vectorLnsLayer = vectorLnsLayer if route.id
+    @map.addLayer vectorPtsLayer
+    @map.addLayer vectorEdgesLayer
+    @map.addLayer vectorLnsLayer
+
+  lineStyle: (highlighted=false)->
+    new ol.style.Style
+      stroke: new ol.style.Stroke
+        color: if highlighted then "#ed7f00" else '#007fbb'
+        width: 3
+
+  edgeStyles: (highlighted=false)->
+    new ol.style.Style
+      image: new ol.style.Circle
+        radius: 5
+        stroke: new ol.style.Stroke
+          color: if highlighted then "#ed7f00" else '#007fbb'
+          width: 2
+        fill: new ol.style.Fill
+          color: if highlighted then "#ed7f00" else '#007fbb'
+          width: 2
+
+  defaultStyles: (highlighted=false)->
+    new ol.style.Style
+      image: new ol.style.Circle
+        radius: 4
+        stroke: new ol.style.Stroke
+          color: if highlighted then "#ed7f00" else '#007fbb'
+          width: 2
+        fill: new ol.style.Fill
+          color: '#ffffff'
+          width: 2
+
+  addRoutesLabels: ->
+    labelsContainer = $("")
+    labelsContainer.appendTo $("##{@target}")
+    @vectorPtsLayer = null
+    @vectorEdgesLayer = null
+    @vectorLnsLayer = null
+    Object.keys(@routes).forEach (id)=>
+      route = @routes[id]
+      label = $("
- #{route.name}
")
+      label.appendTo labelsContainer
+      label.mouseleave =>
+        route.vectorPtsLayer.setStyle @defaultStyles(false)
+        route.vectorEdgesLayer.setStyle @edgeStyles(false)
+        route.vectorLnsLayer.setStyle @lineStyle(false)
+        route.vectorPtsLayer.setZIndex 2
+        route.vectorEdgesLayer.setZIndex 3
+        route.vectorLnsLayer.setZIndex 1
+        @fitZoom()
+      label.mouseenter =>
+        route.vectorPtsLayer.setStyle @defaultStyles(true)
+        route.vectorEdgesLayer.setStyle @edgeStyles(true)
+        route.vectorLnsLayer.setStyle @lineStyle(true)
+        route.vectorPtsLayer.setZIndex 11
+        route.vectorEdgesLayer.setZIndex 12
+        route.vectorLnsLayer.setZIndex 10
+        @fitZoom(route)
+
+  fitZoom: (route)->
+    if route
+      area = []
+      route.stops.forEach (stop, i) =>
+        area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)]
+    else
+      area = @area
+    boundaries = ol.extent.applyTransform(
+      ol.extent.boundingExtent(area), ol.proj.getTransform('EPSG:4326', 'EPSG:3857')
+    )
+    @map.getView().fit boundaries, @map.getSize()
+    tooCloseToBounds = false
+    mapBoundaries = @map.getView().calculateExtent @map.getSize()
+    mapWidth = mapBoundaries[2] - mapBoundaries[0]
+    mapHeight = mapBoundaries[3] - mapBoundaries[1]
+    marginSize = 0.1
+    heightMargin = marginSize * mapHeight
+    widthMargin = marginSize * mapWidth
+    tooCloseToBounds = tooCloseToBounds || (boundaries[0] - mapBoundaries[0]) < widthMargin
+    tooCloseToBounds = tooCloseToBounds || (mapBoundaries[2] - boundaries[2]) < widthMargin
+    tooCloseToBounds = tooCloseToBounds || (boundaries[1] - mapBoundaries[1]) < heightMargin
+    tooCloseToBounds = tooCloseToBounds || (mapBoundaries[3] - boundaries[3]) < heightMargin
+    if tooCloseToBounds
+      @map.getView().setZoom(@map.getView().getZoom() - 1)
+
+
+export default RoutesMap
diff --git a/app/javascript/packs/referential_lines/show.js b/app/javascript/packs/referential_lines/show.js
new file mode 100644
index 000000000..99c5072ef
--- /dev/null
+++ b/app/javascript/packs/referential_lines/show.js
@@ -0,0 +1,10 @@
+import clone from '../../helpers/clone'
+import RoutesMap from '../../helpers/routes_map'
+
+let routes = clone(window, "routes", true)
+routes = JSON.parse(decodeURIComponent(routes))
+
+var map = new RoutesMap('routes_map')
+map.addRoutes(routes)
+map.addRoutesLabels()
+map.fitZoom()
diff --git a/app/javascript/packs/routes/show.js b/app/javascript/packs/routes/show.js
index 71777c379..c20de0800 100644
--- a/app/javascript/packs/routes/show.js
+++ b/app/javascript/packs/routes/show.js
@@ -1,121 +1,8 @@
 import clone from '../../helpers/clone'
+import RoutesMap from '../../helpers/routes_map'
+
 let route = clone(window, "route", true)
 route = JSON.parse(decodeURIComponent(route))
-
-const geoColPts = []
-const geoColLns = []
-const area = []
-const geoColEdges = [
-  new ol.Feature({
-    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)]))
-  }),
-  new ol.Feature({
-    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[route.length - 1].longitude), parseFloat(route[route.length - 1].latitude)]))
-  })
-]
-route.forEach(function (stop, i) {
-  if (i < route.length - 1) {
-    geoColLns.push(new ol.Feature({
-      geometry: new ol.geom.LineString([
-        ol.proj.fromLonLat([parseFloat(route[i].longitude), parseFloat(route[i].latitude)]),
-        ol.proj.fromLonLat([parseFloat(route[i + 1].longitude), parseFloat(route[i + 1].latitude)])
-      ])
-    }))
-  }
-  geoColPts.push(new ol.Feature({
-    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)]))
-  }))
-  area.push([parseFloat(stop.longitude), parseFloat(stop.latitude)])
-})
-var edgeStyles = new ol.style.Style({
-  image: new ol.style.Circle(({
-    radius: 5,
-    stroke: new ol.style.Stroke({
-      color: '#007fbb',
-      width: 2
-    }),
-    fill: new ol.style.Fill({
-      color: '#007fbb',
-      width: 2
-    })
-  }))
-})
-var defaultStyles = new ol.style.Style({
-  image: new ol.style.Circle(({
-    radius: 4,
-    stroke: new ol.style.Stroke({
-      color: '#007fbb',
-      width: 2
-    }),
-    fill: new ol.style.Fill({
-      color: '#ffffff',
-      width: 2
-    })
-  }))
-})
-var lineStyle = new ol.style.Style({
-  stroke: new ol.style.Stroke({
-    color: '#007fbb',
-    width: 3
-  })
-})
-
-var vectorPtsLayer = new ol.layer.Vector({
-  source: new ol.source.Vector({
-    features: geoColPts
-  }),
-  style: defaultStyles,
-  zIndex: 2
-})
-var vectorEdgesLayer = new ol.layer.Vector({
-  source: new ol.source.Vector({
-    features: geoColEdges
-  }),
-  style: edgeStyles,
-  zIndex: 3
-})
-var vectorLnsLayer = new ol.layer.Vector({
-  source: new ol.source.Vector({
-    features: geoColLns
-  }),
-  style: [lineStyle],
-  zIndex: 1
-})
-
-var map = new ol.Map({
-  target: 'route_map',
-  layers: [
-    new ol.layer.Tile({
-      source: new ol.source.OSM()
-    }),
-    vectorPtsLayer,
-    vectorEdgesLayer,
-    vectorLnsLayer
-  ],
-  controls: [new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider()],
-  interactions: ol.interaction.defaults({
-    zoom: true
-  }),
-  view: new ol.View({
-    center: ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)]),
-    zoom: 13
-  })
-});
-const boundaries = ol.extent.applyTransform(
-  ol.extent.boundingExtent(area), ol.proj.getTransform('EPSG:4326', 'EPSG:3857')
-)
-map.getView().fit(boundaries, map.getSize());
-let tooCloseToBounds = false
-const mapBoundaries = map.getView().calculateExtent(map.getSize())
-const mapWidth = mapBoundaries[2] - mapBoundaries[0]
-const mapHeight = mapBoundaries[3] - mapBoundaries[1]
-const marginSize = 0.1
-const heightMargin = marginSize * mapHeight
-const widthMargin = marginSize * mapWidth
-tooCloseToBounds = tooCloseToBounds || (boundaries[0] - mapBoundaries[0]) < widthMargin
-tooCloseToBounds = tooCloseToBounds || (mapBoundaries[2] - boundaries[2]) < widthMargin
-tooCloseToBounds = tooCloseToBounds || (boundaries[1] - mapBoundaries[1]) < heightMargin
-tooCloseToBounds = tooCloseToBounds || (mapBoundaries[3] - boundaries[3]) < heightMargin
-if(tooCloseToBounds){
-  map.getView().setZoom(map.getView().getZoom() - 1)
-}
+var map = new RoutesMap('route_map')
+map.addRoute(route)
+map.fitZoom()
diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js
index 43a027084..b39fa0c9c 100644
--- a/app/javascript/routes/components/StopPointList.js
+++ b/app/javascript/routes/components/StopPointList.js
@@ -56,7 +56,7 @@ export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick
   )
 }
 
-StopPointList.PropTypes = {
+StopPointList.propTypes = {
   stopPoints: PropTypes.array.isRequired,
   onDeleteClick: PropTypes.func.isRequired,
   onMoveUpClick: PropTypes.func.isRequired,
@@ -68,4 +68,4 @@ StopPointList.PropTypes = {
 
 StopPointList.contextTypes = {
   I18n: PropTypes.object
-}
\ No newline at end of file
+}
diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim
index 02d605d8c..5ea0e31bb 100644
--- a/app/views/referential_lines/show.html.slim
+++ b/app/views/referential_lines/show.html.slim
@@ -17,7 +17,8 @@
              @line.human_attribute_name(:transport_submode) => (@line.transport_submode.present? ? t("enumerize.transport_submode.#{@line.transport_submode}") : '-'),
              @line.human_attribute_name(:url) => (@line.url ? @line.url : '-'),
              @line.human_attribute_name(:seasonal) => (@line.seasonal? ? t('true') : t('false')),}
-
+      .col-lg-6.col-md-6.col-sm-12.col-xs-12
+        #routes_map.map.mb-lg
     .row
       .col-lg-12
         .h3 = t('lines.show.routes.title')
@@ -79,3 +80,8 @@
           .row.mt-xs
             .col-lg-12
               = replacement_msg t('routes.search_no_results')
+
+= javascript_tag do
+  | window.routes = "#{URI.escape(@routes.map{|r| {name: r.name, id: r.id, stops: route_json_for_edit(r, serialize: false)}}.to_json)}"
+
+= javascript_pack_tag 'referential_lines/show.js'
diff --git a/config/webpack/environment.js b/config/webpack/environment.js
index e7c879fb9..688bcbe8e 100644
--- a/config/webpack/environment.js
+++ b/config/webpack/environment.js
@@ -1,4 +1,5 @@
 const { environment } = require('@rails/webpacker')
+const coffee =  require('./loaders/coffee')
 const CleanWebpackPlugin = require('clean-webpack-plugin')
 
 let pathsToClean = [
@@ -24,4 +25,5 @@ environment.plugins.set(
 //   jquery: "jquery/src/jquery",
 // }
 
+environment.loaders.append('coffee', coffee)
 module.exports = environment
diff --git a/config/webpack/loaders/coffee.js b/config/webpack/loaders/coffee.js
new file mode 100644
index 000000000..4666716dc
--- /dev/null
+++ b/config/webpack/loaders/coffee.js
@@ -0,0 +1,6 @@
+module.exports = {
+  test: /\.coffee(\.erb)?$/,
+  use: [{
+    loader: 'coffee-loader'
+  }]
+}
diff --git a/package.json b/package.json
index 80ca22f83..802a2eef7 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
     "babel-preset-react": "6.24.1",
     "babelify": "8.0.0",
     "bootstrap": "3",
-    "coffeescript": "2.1.0",
+    "coffee-loader": "^0.9.0",
+    "coffeescript": "1.12.7",
     "jquery": "3.2.1",
     "lodash": "4.17.4",
     "promise-polyfill": "7.0.0",
diff --git a/yarn.lock b/yarn.lock
index e95ee9a63..d17ae1d52 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1404,13 +1404,19 @@ code-point-at@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
+coffee-loader@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/coffee-loader/-/coffee-loader-0.9.0.tgz#6deabd336062ddc6d773da4dfd16367fc7107bd6"
+  dependencies:
+    loader-utils "^1.0.2"
+
 coffee-script@~1.10.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.10.0.tgz#12938bcf9be1948fa006f92e0c4c9e81705108c0"
 
-coffeescript@2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.1.0.tgz#8cb7ce12021ab9f84d8c524f54edbd6141374606"
+coffeescript@1.12.7:
+  version "1.12.7"
+  resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27"
 
 color-convert@^1.3.0, color-convert@^1.8.2, color-convert@^1.9.0:
   version "1.9.0"
-- 
cgit v1.2.3
From 857e2f2319594f61436c2f2245136c303452d934 Mon Sep 17 00:00:00 2001
From: Zog
Date: Fri, 26 Jan 2018 16:17:22 +0100
Subject: Refs #5750 @1h; Add a "kind" attribute to StopAreas
This determines if the StopArea is commercial or not
The useless fields are hidden in the form for the non-commercials ones
---
 app/assets/stylesheets/components/_forms.sass      |  8 ++++++-
 app/javascript/helpers/master_slave.coffee         | 18 ++++++++++++++++
 app/javascript/packs/stop_areas/new.js             |  3 +++
 app/models/chouette/area_type.rb                   | 25 ++++++++++++++++------
 app/models/chouette/stop_area.rb                   |  9 ++++++++
 app/views/stop_areas/_form.html.slim               | 10 +++++++--
 app/views/stop_areas/show.html.slim                |  8 +++----
 config/locales/area_types.en.yml                   |  6 ++++++
 config/locales/area_types.fr.yml                   |  6 ++++++
 .../20180126134944_add_kind_to_stop_areas.rb       |  5 +++++
 db/schema.rb                                       | 11 +++++++++-
 spec/models/chouette/area_type_spec.rb             |  4 +++-
 12 files changed, 97 insertions(+), 16 deletions(-)
 create mode 100644 app/javascript/helpers/master_slave.coffee
 create mode 100644 app/javascript/packs/stop_areas/new.js
 create mode 100644 db/migrate/20180126134944_add_kind_to_stop_areas.rb
diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass
index 1f0d2f59b..998703ef0 100644
--- a/app/assets/stylesheets/components/_forms.sass
+++ b/app/assets/stylesheets/components/_forms.sass
@@ -85,9 +85,15 @@ input
 
 // BS horizontal form label positionning fix
 .form-horizontal
+  input[type="radio"].form-control
+    height: auto
+    width: auto
   .form-group
     position: relative
-
+    .radio-inline
+      padding-top: 4px
+      &:first-child
+        padding-left: 0
     > .control-label
       &[class*='col-sm-']
         float: none
diff --git a/app/javascript/helpers/master_slave.coffee b/app/javascript/helpers/master_slave.coffee
new file mode 100644
index 000000000..11f6bca7e
--- /dev/null
+++ b/app/javascript/helpers/master_slave.coffee
@@ -0,0 +1,18 @@
+class MasterSlave
+  constructor: (selector)->
+    $(selector).find('[data-master]').each (i, slave)->
+      $slave = $(slave)
+      master = $($slave.data().master)
+      console.log $slave.data().master
+      console.log master
+      toggle = ->
+        val = master.filter(":checked").val() if master.filter("[type=radio]").length > 0
+        val ||= master.val()
+        selected = val == $slave.data().value
+        $slave.toggle selected
+        $slave.find("input, select").attr "disabled", !selected
+      master.change toggle
+      toggle()
+      # $slave.toggle master.val() == $slave.data().value
+
+export default MasterSlave
diff --git a/app/javascript/packs/stop_areas/new.js b/app/javascript/packs/stop_areas/new.js
new file mode 100644
index 000000000..ffe702cdb
--- /dev/null
+++ b/app/javascript/packs/stop_areas/new.js
@@ -0,0 +1,3 @@
+import MasterSlave from "../../helpers/master_slave"
+
+new MasterSlave("form")
diff --git a/app/models/chouette/area_type.rb b/app/models/chouette/area_type.rb
index 4703ea646..e17d2ee8d 100644
--- a/app/models/chouette/area_type.rb
+++ b/app/models/chouette/area_type.rb
@@ -1,13 +1,22 @@
 class Chouette::AreaType
   include Comparable
 
-  ALL = %i(zdep zder zdlp zdlr lda gdl).freeze
+  COMMERCIAL = %i(zdep zder zdlp zdlr lda gdl).freeze
+  NON_COMMERCIAL = %i(deposit border service_area relief other).freeze
+  ALL = COMMERCIAL + NON_COMMERCIAL
 
+  @@commercial = COMMERCIAL
+  @@non_commercial = NON_COMMERCIAL
   @@all = ALL
-  mattr_accessor :all
+  mattr_accessor :all, :commercial, :non_commercial
 
-  def self.all=(values)
-    @@all = ALL & values
+  def self.commercial=(values)
+    @@commercial = COMMERCIAL & values
+    reset_caches!
+  end
+
+  def self.non_commercial=(values)
+    @@non_commercial = NON_COMMERCIAL & values
     reset_caches!
   end
 
@@ -20,12 +29,14 @@ class Chouette::AreaType
   end
 
   def self.reset_caches!
+    @@all = @@commercial + @@non_commercial
     @@instances = {}
-    @@options = nil
+    @@options = {}
   end
 
-  def self.options
-    @@options ||= all.map { |c| find(c) }.map { |t| [ t.label, t.code ] }
+  def self.options(kind=:all)
+    @@options ||= {}
+    @@options[kind] ||= self.send(kind).map { |c| find(c) }.map { |t| [ t.label, t.code ] }
   end
 
   attr_reader :code
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index ea1855ea8..d270a8696 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -10,6 +10,7 @@ module Chouette
 
     extend Enumerize
     enumerize :area_type, in: Chouette::AreaType::ALL
+    enumerize :kind, in: %i(commercial non_commercial)
 
     with_options dependent: :destroy do |assoc|
       assoc.has_many :stop_points
@@ -96,6 +97,10 @@ module Chouette
       end
     end
 
+    def local_id
+      id.to_s
+    end
+
     def children_in_depth
       return [] if self.children.empty?
 
@@ -374,5 +379,9 @@ module Chouette
       return nil unless time_zone.present?
       ActiveSupport::TimeZone[time_zone]&.formatted_offset
     end
+
+    def commercial?
+      kind == "commercial"
+    end
   end
 end
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index b2322f73a..699381d50 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -7,9 +7,13 @@
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
 
-        = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
+        = f.input :kind, as: :radio_buttons, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, item_class: "fooo"
 
-        = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options, :include_blank => false
+        .slave data-master="[name='stop_area[kind]']" data-value="commercial"
+          = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
+        - %i(commercial non_commercial).each do |kind|
+          .slave data-master="[name='stop_area[kind]']" data-value=kind
+            = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options(kind), :include_blank => false
 
         .location_info
           h3 = t("stop_areas.stop_area.localisation")
@@ -49,3 +53,5 @@
   .separator
 
   = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'stop_area_form'
+
+= javascript_pack_tag "stop_areas/new"
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index b5ec8ac00..b0896c1e0 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -6,11 +6,11 @@
   .container-fluid
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
-        - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id,
-            @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-",
-            @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
+        - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id }
+        - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial?
+        - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
             @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number,
-            }
+            })
         - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
         - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
             @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
diff --git a/config/locales/area_types.en.yml b/config/locales/area_types.en.yml
index 34ec3243d..5d23a6665 100644
--- a/config/locales/area_types.en.yml
+++ b/config/locales/area_types.en.yml
@@ -6,3 +6,9 @@ en:
       zdlp: ZDLp
       zdlr: ZDLr
       lda: LDA
+      gdl: GDL
+      deposit: Deposit
+      border: Border
+      service_area: Service Area
+      relief: Relief point
+      other: Other
diff --git a/config/locales/area_types.fr.yml b/config/locales/area_types.fr.yml
index fd4e1e741..bb249c235 100644
--- a/config/locales/area_types.fr.yml
+++ b/config/locales/area_types.fr.yml
@@ -6,3 +6,9 @@ fr:
       zdlp: ZDLp
       zdlr: ZDLr
       lda: LDA
+      gdl: GDL
+      deposit: Dépôt
+      border: Frontière
+      service_area: Aire de service / Pause
+      relief: Point de releve
+      other: Autre
diff --git a/db/migrate/20180126134944_add_kind_to_stop_areas.rb b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
new file mode 100644
index 000000000..3a4f0a0c8
--- /dev/null
+++ b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
@@ -0,0 +1,5 @@
+class AddKindToStopAreas < ActiveRecord::Migration
+  def change
+    add_column :stop_areas, :kind, :string
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 11fb96ee3..9be702fa3 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,13 +11,18 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
+<<<<<<< HEAD
 ActiveRecord::Schema.define(version: 20180123174450) do
+=======
+ActiveRecord::Schema.define(version: 20180126134944) do
+>>>>>>> Refs #5750 @1h; Add a "kind" attribute to StopAreas
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
-  enable_extension "postgis"
   enable_extension "hstore"
+  enable_extension "postgis"
   enable_extension "unaccent"
+  enable_extension "objectid"
 
   create_table "access_links", id: :bigserial, force: :cascade do |t|
     t.integer  "access_point_id",                        limit: 8
@@ -91,6 +96,8 @@ ActiveRecord::Schema.define(version: 20180123174450) do
     t.datetime  "created_at"
     t.datetime  "updated_at"
     t.integer   "workgroup_id",    limit: 8
+    t.integer   "int_day_types"
+    t.date      "excluded_dates",                            array: true
   end
 
   add_index "calendars", ["organisation_id"], name: "index_calendars_on_organisation_id", using: :btree
@@ -117,6 +124,7 @@ ActiveRecord::Schema.define(version: 20180123174450) do
     t.datetime "updated_at"
     t.date     "end_date"
     t.string   "date_type"
+    t.string   "mode"
   end
 
   add_index "clean_ups", ["referential_id"], name: "index_clean_ups_on_referential_id", using: :btree
@@ -788,6 +796,7 @@ ActiveRecord::Schema.define(version: 20180123174450) do
     t.datetime "updated_at"
     t.string   "stif_type"
     t.integer  "waiting_time"
+    t.string   "kind"
   end
 
   add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree
diff --git a/spec/models/chouette/area_type_spec.rb b/spec/models/chouette/area_type_spec.rb
index 67d218df8..28325dd0a 100644
--- a/spec/models/chouette/area_type_spec.rb
+++ b/spec/models/chouette/area_type_spec.rb
@@ -4,7 +4,9 @@ RSpec.describe Chouette::AreaType do
 
   describe "::ALL" do
     it "includes all supported types" do
-      expect(Chouette::AreaType::ALL).to match_array( %i(zdep zder zdlp zdlr lda gdl) )
+      expect(Chouette::AreaType::ALL).to match_array( %i(zdep zder zdlp zdlr lda gdl deposit border service_area relief other) )
+      expect(Chouette::AreaType::COMMERCIAL).to match_array( %i(zdep zder zdlp zdlr lda gdl) )
+      expect(Chouette::AreaType::NON_COMMERCIAL).to match_array( %i( deposit border service_area relief other) )
     end
   end
 
-- 
cgit v1.2.3
From a41d3b5e861929a75c6de23150d066ba366bb577 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 08:45:02 +0100
Subject: Refs #5750 @1h; Manage non-commercial StopAreas
- Add a `kind` attribute
- Hide irrelevant fields in the form
---
 app/assets/stylesheets/modules/_vj_collection.sass  |  3 +++
 app/controllers/stop_areas_controller.rb            |  1 +
 app/controllers/vehicle_journeys_controller.rb      |  2 ++
 app/javascript/helpers/master_slave.coffee          |  8 ++++----
 app/javascript/helpers/stop_area_header_manager.js  |  4 +++-
 app/models/chouette/stop_area.rb                    |  8 ++++++++
 app/policies/stop_area_policy.rb                    |  2 +-
 app/views/stop_areas/_form.html.slim                |  6 +++---
 config/locales/stop_areas.fr.yml                    |  2 ++
 db/migrate/20180126134944_add_kind_to_stop_areas.rb |  1 +
 spec/models/chouette/stop_area_spec.rb              | 10 ++++++++++
 11 files changed, 38 insertions(+), 9 deletions(-)
diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass
index 56769e52b..81c1fe43e 100644
--- a/app/assets/stylesheets/modules/_vj_collection.sass
+++ b/app/assets/stylesheets/modules/_vj_collection.sass
@@ -9,6 +9,9 @@
         position: relative
         padding-left: 25px
 
+        .fa
+          margin-left: 5px
+
         > .headlined
           &:before
             margin-left: -25px
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 79ffea72e..8e9df7157 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -203,6 +203,7 @@ class StopAreasController < ChouetteController
       :url,
       :waiting_time,
       :zip_code,
+      :kind,
     )
   end
 
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index c1762c13e..ed6ba6ed1 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -66,6 +66,8 @@ class VehicleJourneysController < ChouetteController
             :city_name => sp.stop_area.try(:city_name),
             :comment => sp.stop_area.try(:comment),
             :area_type => sp.stop_area.try(:area_type),
+            :area_type_i18n => I18n.t(sp.stop_area.try(:area_type), scope: 'area_types.label'),
+            :area_kind => sp.stop_area.try(:kind),
             :stop_area_id => sp.stop_area_id,
             :registration_number => sp.stop_area.try(:registration_number),
             :nearest_topic_name => sp.stop_area.try(:nearest_topic_name),
diff --git a/app/javascript/helpers/master_slave.coffee b/app/javascript/helpers/master_slave.coffee
index 11f6bca7e..4866a55e3 100644
--- a/app/javascript/helpers/master_slave.coffee
+++ b/app/javascript/helpers/master_slave.coffee
@@ -3,16 +3,16 @@ class MasterSlave
     $(selector).find('[data-master]').each (i, slave)->
       $slave = $(slave)
       master = $($slave.data().master)
-      console.log $slave.data().master
-      console.log master
+      console.log $slave
+      console.log $slave.find("input:disabled, select:disabled")
+      $slave.find("input:disabled, select:disabled").attr "data-slave-force-disabled", "true"
       toggle = ->
         val = master.filter(":checked").val() if master.filter("[type=radio]").length > 0
         val ||= master.val()
         selected = val == $slave.data().value
         $slave.toggle selected
-        $slave.find("input, select").attr "disabled", !selected
+        $slave.find("input, select").filter(":not([data-slave-force-disabled])").attr "disabled", !selected
       master.change toggle
       toggle()
-      # $slave.toggle master.val() == $slave.data().value
 
 export default MasterSlave
diff --git a/app/javascript/helpers/stop_area_header_manager.js b/app/javascript/helpers/stop_area_header_manager.js
index c9f397dee..2c820caf9 100644
--- a/app/javascript/helpers/stop_area_header_manager.js
+++ b/app/javascript/helpers/stop_area_header_manager.js
@@ -19,7 +19,7 @@ export default class StopAreaHeaderManager {
         
           
@@ -27,6 +27,8 @@ export default class StopAreaHeaderManager {
             {sp.time_zone_formatted_offset && 
                ({sp.time_zone_formatted_offset})
             }
+            {sp.area_kind == 'non_commercial' && 
+            }
           
         
       
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index d270a8696..75a4a34bb 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -32,6 +32,7 @@ module Chouette
 
     validates_format_of :registration_number, :with => %r{\A[\d\w_\-]+\Z}, :allow_blank => true
     validates_presence_of :name
+    validates_presence_of :kind
     validates_presence_of :latitude, :if => :longitude
     validates_presence_of :longitude, :if => :latitude
     validates_numericality_of :latitude, :less_than_or_equal_to => 90, :greater_than_or_equal_to => -90, :allow_nil => true
@@ -42,6 +43,7 @@ module Chouette
 
     validates_numericality_of :waiting_time, greater_than_or_equal_to: 0, only_integer: true, if: :waiting_time
     validate :parent_area_type_must_be_greater
+    validate :area_type_of_right_kind
 
     def self.nullable_attributes
       [:registration_number, :street_name, :country_code, :fare_code,
@@ -57,6 +59,12 @@ module Chouette
       end
     end
 
+    def area_type_of_right_kind
+      unless Chouette::AreaType.send(self.kind).include?(self.area_type)
+        errors.add(:area_type, I18n.t('stop_areas.errors.incorrect_kind_area_type'))
+      end
+    end
+
     after_update :clean_invalid_access_links
     before_save :coordinates_to_lat_lng
 
diff --git a/app/policies/stop_area_policy.rb b/app/policies/stop_area_policy.rb
index 6db48b702..fd73b7092 100644
--- a/app/policies/stop_area_policy.rb
+++ b/app/policies/stop_area_policy.rb
@@ -3,7 +3,7 @@ class StopAreaPolicy < ApplicationPolicy
     def search_scope scope_name
       scope = resolve
       if scope_name&.to_s == "route_editor"
-        scope = scope.where(area_type: 'zdep') unless user.organisation.has_feature?("route_stop_areas_all_types")
+        scope = scope.where("kind = ? OR area_type = ?", :non_commercial, 'zdep') unless user.organisation.has_feature?("route_stop_areas_all_types")
       end
       scope
     end
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 699381d50..6b75209b4 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -7,13 +7,13 @@
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
 
-        = f.input :kind, as: :radio_buttons, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, item_class: "fooo"
+        = f.input :kind, as: :radio_buttons, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, item_class: "fooo", disabled: !@stop_area.new_record?
 
         .slave data-master="[name='stop_area[kind]']" data-value="commercial"
           = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
-        - %i(commercial non_commercial).each do |kind|
+        - %i(non_commercial commercial).each do |kind|
           .slave data-master="[name='stop_area[kind]']" data-value=kind
-            = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options(kind), :include_blank => false
+            = f.input :area_type, as: :select, :input_html => {id: kind, :disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options(kind), :include_blank => false, disabled: !@stop_area.new_record?
 
         .location_info
           h3 = t("stop_areas.stop_area.localisation")
diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml
index 0095bbe6d..283000960 100644
--- a/config/locales/stop_areas.fr.yml
+++ b/config/locales/stop_areas.fr.yml
@@ -5,6 +5,7 @@ fr:
     errors:
       empty: Aucun stop_area_id
       parent_area_type: ne peut être de type %{area_type}
+      incorrect_kind_area_type: Ce type d'arrêt est invalide pour cette catégorie
     default_geometry_success: "%{count} arrêts édités"
     stop_area:
       no_position: "Pas de position"
@@ -97,6 +98,7 @@ fr:
     attributes:
       stop_area:
         name: "Nom"
+        kind: "Catégorie"
         registration_number: "Numéro d'enregistrement"
         published_name: "Nom public"
         deleted: "Supprimé"
diff --git a/db/migrate/20180126134944_add_kind_to_stop_areas.rb b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
index 3a4f0a0c8..7da227cd9 100644
--- a/db/migrate/20180126134944_add_kind_to_stop_areas.rb
+++ b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
@@ -1,5 +1,6 @@
 class AddKindToStopAreas < ActiveRecord::Migration
   def change
     add_column :stop_areas, :kind, :string
+    Chouette::StopArea.update_all kind: :commmercial
   end
 end
diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb
index a90e5d816..32ee5a3a6 100644
--- a/spec/models/chouette/stop_area_spec.rb
+++ b/spec/models/chouette/stop_area_spec.rb
@@ -10,10 +10,20 @@ describe Chouette::StopArea, :type => :model do
 
   it { should belong_to(:stop_area_referential) }
   it { should validate_presence_of :name }
+  it { should validate_presence_of :kind }
   it { should validate_numericality_of :latitude }
   it { should validate_numericality_of :longitude }
   it { is_expected.to be_versioned }
 
+  describe "#area_type" do
+    it "should validate the value is correct regarding to the kind" do
+      expect(build(:stop_area, kind: :commercial, area_type: :gdl)).to be_valid
+      expect(build(:stop_area, kind: :non_commercial, area_type: :relief)).to be_valid
+      expect(build(:stop_area, kind: :commercial, area_type: :relief)).to_not be_valid
+      expect(build(:stop_area, kind: :non_commercial, area_type: :gdl)).to_not be_valid
+    end
+  end
+
   # describe ".latitude" do
   #   it "should accept -90 value" do
   #     subject = create :stop_area, :area_type => "BoardingPosition"
-- 
cgit v1.2.3
From 755b07db36a10ffdbd315dff2b5315374eb3e58a Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 10:32:05 +0100
Subject: Refs #5750; Add a validation on VehicleJourneys
Ensure a time is set for all non-commercial stops
---
 app/assets/stylesheets/modules/_vj_collection.sass |  3 ++
 app/javascript/vehicle_journeys/actions/index.js   | 26 ++++++++++++++
 .../components/SaveVehicleJourneys.js              |  2 +-
 .../vehicle_journeys/components/VehicleJourney.js  |  3 ++
 .../vehicle_journeys/components/VehicleJourneys.js |  2 +-
 .../containers/SaveVehicleJourneys.js              |  3 ++
 .../vehicle_journeys/reducers/vehicleJourneys.js   |  2 ++
 app/views/vehicle_journeys/show.rabl               |  1 +
 spec/javascript/vehicle_journeys/actions_spec.js   | 41 ++++++++++++++++++++++
 9 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass
index 81c1fe43e..d99c67bd7 100644
--- a/app/assets/stylesheets/modules/_vj_collection.sass
+++ b/app/assets/stylesheets/modules/_vj_collection.sass
@@ -116,6 +116,9 @@
           margin-left: 5px
 
       &.has-error
+        .errors
+          color: $red
+          font-size: 0.8em
         &:before
           content: ''
           position: absolute
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index 2675328e3..b01158212 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -380,6 +380,32 @@ const actions = {
         }
       })
   },
+
+  validate : (dispatch, vehicleJourneys, next) => {
+    let valid = true
+    let vj, vjas
+    for (vj of vehicleJourneys){
+      vj.errors = false
+      for(vjas of vj.vehicle_journey_at_stops){
+        vjas.errors = null
+        if (vjas.area_kind == "non_commercial" && parseInt(vjas.departure_time.hour) == 0 && parseInt(vjas.departure_time.minute) == 0){
+          vjas.errors = "Champ requis"
+          vj.errors = true
+          valid = false
+        }
+      }
+    }
+    dispatch(actions.didValidateVehicleJourneys(vehicleJourneys))
+    if(valid){
+      actions.submitVehicleJourneys(dispatch, vehicleJourneys, next)
+    }
+  },
+
+  didValidateVehicleJourneys : (vehicleJourneys) => ({
+    type: 'DID_VALIDATE_VEHICLE_JOURNEYS',
+    vehicleJourneys
+  }),
+
   submitVehicleJourneys : (dispatch, state, next) => {
     dispatch(actions.fetchingApi())
     let urlJSON = window.location.pathname + "_collection.json"
diff --git a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
index 6e94b04a3..fb921df9c 100644
--- a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
@@ -13,7 +13,7 @@ export default class SaveVehicleJourneys extends SaveButton{
   }
 
   submitForm(){
-    this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys)
+    this.props.validate(this.props.vehicleJourneys, this.props.dispatch)
   }
 }
 
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js
index d240757a3..2b5783dda 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js
@@ -153,6 +153,9 @@ export default class VehicleJourney extends Component {
                       />
                   
                 
+                {vj.errors && 
+                  {vj.errors}
+                
}
             
           
         )}
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
index b188962c2..256ca81f9 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
@@ -89,7 +89,7 @@ export default class VehicleJourneys extends Component {
               
             )}
 
-            { _.some(this.props.vehicleJourneys, 'errors') && (
+            { this.props.vehicleJourneys.errors && this.props.vehicleJourneys.errors.length && _.some(this.props.vehicleJourneys, 'errors') && (
               
                 Erreur : 
                 {this.props.vehicleJourneys.map((vj, index) =>
diff --git a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
index f5f879ed8..3daf831f8 100644
--- a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
@@ -23,6 +23,9 @@ const mapDispatchToProps = (dispatch) => {
     },
     onSubmitVehicleJourneys: (next, state) => {
       actions.submitVehicleJourneys(dispatch, state, next)
+    },
+    validate: (state) =>{
+      actions.validate(dispatch, state)
     }
   }
 }
diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
index ae45993a8..1a15ec46d 100644
--- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
@@ -273,6 +273,8 @@ export default function vehicleJourneys(state = [], action) {
           return vj
         }
       })
+    case 'DID_VALIDATE_VEHICLE_JOURNEYS':
+      return [...action.vehicleJourneys]
     default:
       return state
   }
diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl
index fc65e6cb6..dca0866b3 100644
--- a/app/views/vehicle_journeys/show.rabl
+++ b/app/views/vehicle_journeys/show.rabl
@@ -45,6 +45,7 @@ child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops
     end
 
     node(:dummy) { vehicle_stop.dummy }
+    node(:area_kind) { vehicle_stop.stop_point.stop_area.kind }
 
     node(:stop_area_object_id) do
       vehicle_stop.stop_point.stop_area.objectid
diff --git a/spec/javascript/vehicle_journeys/actions_spec.js b/spec/javascript/vehicle_journeys/actions_spec.js
index 9515b57f2..d486c9af8 100644
--- a/spec/javascript/vehicle_journeys/actions_spec.js
+++ b/spec/javascript/vehicle_journeys/actions_spec.js
@@ -37,6 +37,47 @@ describe('when clicking on add button', () => {
     expect(actions.openCreateModal()).toEqual(expectedAction)
   })
 })
+describe('when validating the form', () => {
+  it('should check that non-commercial stops have passing time', () => {
+    let state = [{
+      vehicle_journey_at_stops: [{
+        area_kind: "non_commercial",
+        departure_time: {
+          hour: "00",
+          minute: "00"
+        }
+      }]
+    }]
+
+    expect(actions.validate(dispatch, state)).toEqual(false)
+
+    state = [{
+      vehicle_journey_at_stops: [{
+        area_kind: "non_commercial",
+        departure_time: {
+          hour: "00",
+          minute: "01"
+        }
+      }]
+    }]
+
+    expect(actions.validate(dispatch, state)).toEqual(true)
+  })
+
+  it('should not check that commercial stops', () => {
+    let state = [{
+      vehicle_journey_at_stops: [{
+        area_kind: "commercial",
+        departure_time: {
+          hour: "00",
+          minute: "00"
+        }
+      }]
+    }]
+
+    expect(actions.validate(dispatch, state)).toEqual(true)
+  })
+})
 describe('when using select2 to pick a journey pattern', () => {
   it('should create an action to select a journey pattern inside modal', () => {
     let selectedJP = {
-- 
cgit v1.2.3
From fe3e3d7d8e623d8b172aa12496e24c5d91278166 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Tue, 30 Jan 2018 14:48:01 +0100
Subject: VehicleJourney: Allow deletion of associated company
If the state doesn't contain a company, that means it was deleted on the
frontend. In that case, the backend code should correctly delete the
associated company from the vehicle journey.
Refs #5574
---
 app/models/chouette/vehicle_journey.rb       | 8 ++++++--
 spec/models/chouette/vehicle_journey_spec.rb | 8 ++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 8a704d8c0..5cd87a66a 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -221,9 +221,13 @@ module Chouette
 
     def self.state_permited_attributes item
       attrs = item.slice('published_journey_identifier', 'published_journey_name', 'journey_pattern_id', 'company_id').to_hash
-      ['company', 'journey_pattern'].map do |association|
-        attrs["#{association}_id"] = item[association]['id'] if item[association]
+
+      if item['journey_pattern']
+        attrs['journey_pattern_id'] = item['journey_pattern']['id']
       end
+
+      attrs['company_id'] = item['company'] ? item['company']['id'] : nil
+
       attrs["custom_field_values"] = Hash[*(item["custom_fields"] || {}).map{|k, v| [k, v["value"]]}.flatten]
       attrs
     end
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 2a88ac3ce..21bbc1ba8 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -231,6 +231,14 @@ describe Chouette::VehicleJourney, :type => :model do
       expect(vehicle_journey.reload.company_id).to eq state['company']['id']
     end
 
+    it "handles vehicle journey company deletion" do
+      vehicle_journey.update(company: create(:company))
+      state.delete('company')
+      Chouette::VehicleJourney.state_update(route, collection)
+
+      expect(vehicle_journey.reload.company_id).to be_nil
+    end
+
     it 'should update vj attributes from state' do
       state['published_journey_name']       = 'edited_name'
       state['published_journey_identifier'] = 'edited_identifier'
-- 
cgit v1.2.3
From f6a00ca090d5048772ad1869eb6d39068aba16f2 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Tue, 30 Jan 2018 14:53:34 +0100
Subject: VehicleJourney.state_permited_attributes: Fix whitespace
Add line breaks to make this method more readable.
Refs #5574
---
 app/models/chouette/vehicle_journey.rb | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 5cd87a66a..d94b69271 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -220,7 +220,12 @@ module Chouette
     end
 
     def self.state_permited_attributes item
-      attrs = item.slice('published_journey_identifier', 'published_journey_name', 'journey_pattern_id', 'company_id').to_hash
+      attrs = item.slice(
+        'published_journey_identifier',
+        'published_journey_name',
+        'journey_pattern_id',
+        'company_id'
+      ).to_hash
 
       if item['journey_pattern']
         attrs['journey_pattern_id'] = item['journey_pattern']['id']
@@ -228,7 +233,12 @@ module Chouette
 
       attrs['company_id'] = item['company'] ? item['company']['id'] : nil
 
-      attrs["custom_field_values"] = Hash[*(item["custom_fields"] || {}).map{|k, v| [k, v["value"]]}.flatten]
+      attrs["custom_field_values"] = Hash[
+        *(item["custom_fields"] || {})
+          .map { |k, v| [k, v["value"]] }
+          .flatten
+      ]
+
       attrs
     end
 
-- 
cgit v1.2.3
From c539b09d834476a36804072a92f8c2c373aa487b Mon Sep 17 00:00:00 2001
From: cedricnjanga
Date: Tue, 30 Jan 2018 23:26:45 -0800
Subject: Refs ##5370 Set network action_link to disabled if line doesn't have
 one
---
 app/decorators/line_decorator.rb | 1 +
 1 file changed, 1 insertion(+)
diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb
index 039ad90a3..0e7b6b9ae 100644
--- a/app/decorators/line_decorator.rb
+++ b/app/decorators/line_decorator.rb
@@ -20,6 +20,7 @@ class LineDecorator < AF83::Decorator
     instance_decorator.action_link secondary: :show do |l|
       l.content t('lines.actions.show_network')
       l.href   { [scope, object.network] }
+      l.disabled { object.network.nil? }
     end
 
     instance_decorator.action_link secondary: :show do |l|
-- 
cgit v1.2.3
From aa4fa0970e35920e7e1553112fc2d1f792abe05d Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 10:46:02 +0100
Subject: Refs #5750; Fix validation
---
 app/controllers/stop_areas_controller.rb            | 2 +-
 app/models/chouette/stop_area.rb                    | 3 ++-
 app/views/stop_areas/_form.html.slim                | 3 +--
 db/migrate/20180126134944_add_kind_to_stop_areas.rb | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 8e9df7157..8d424b8d1 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -97,7 +97,7 @@ class StopAreasController < ChouetteController
     edit! do
       stop_area.position ||= stop_area.default_position
       map.editable = true
-   end
+    end
   end
 
   def destroy
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index 75a4a34bb..ad42d54ae 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -60,7 +60,8 @@ module Chouette
     end
 
     def area_type_of_right_kind
-      unless Chouette::AreaType.send(self.kind).include?(self.area_type)
+
+      unless Chouette::AreaType.send(self.kind).map(&:to_s).include?(self.area_type)
         errors.add(:area_type, I18n.t('stop_areas.errors.incorrect_kind_area_type'))
       end
     end
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 6b75209b4..aa156f7bd 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,8 +6,7 @@
         /= @map.to_html
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
-
-        = f.input :kind, as: :radio_buttons, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, item_class: "fooo", disabled: !@stop_area.new_record?
+        = f.input :kind, as: :radio_buttons, checked: @stop_area.kind, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, disabled: !@stop_area.new_record?
 
         .slave data-master="[name='stop_area[kind]']" data-value="commercial"
           = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
diff --git a/db/migrate/20180126134944_add_kind_to_stop_areas.rb b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
index 7da227cd9..08f54a6c5 100644
--- a/db/migrate/20180126134944_add_kind_to_stop_areas.rb
+++ b/db/migrate/20180126134944_add_kind_to_stop_areas.rb
@@ -1,6 +1,6 @@
 class AddKindToStopAreas < ActiveRecord::Migration
   def change
     add_column :stop_areas, :kind, :string
-    Chouette::StopArea.update_all kind: :commmercial
+    Chouette::StopArea.where.not(kind: :non_commercial).update_all kind: :commercial
   end
 end
-- 
cgit v1.2.3
From 19a25d3ebb1ce40483104c6cc19c8e1654413e65 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 11:14:23 +0100
Subject: Disable immature feature
---
 app/javascript/packs/referential_lines/show.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/javascript/packs/referential_lines/show.js b/app/javascript/packs/referential_lines/show.js
index 99c5072ef..542188018 100644
--- a/app/javascript/packs/referential_lines/show.js
+++ b/app/javascript/packs/referential_lines/show.js
@@ -6,5 +6,5 @@ routes = JSON.parse(decodeURIComponent(routes))
 
 var map = new RoutesMap('routes_map')
 map.addRoutes(routes)
-map.addRoutesLabels()
+// map.addRoutesLabels()
 map.fitZoom()
-- 
cgit v1.2.3
From 607cc587b2b2719feb3fafaf9a1b754cada143a9 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 11:17:02 +0100
Subject: Refs #5750; Remove useless validation
---
 app/javascript/vehicle_journeys/actions/index.js | 17 +----------------
 1 file changed, 1 insertion(+), 16 deletions(-)
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index b01158212..4a4ec371d 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -382,23 +382,8 @@ const actions = {
   },
 
   validate : (dispatch, vehicleJourneys, next) => {
-    let valid = true
-    let vj, vjas
-    for (vj of vehicleJourneys){
-      vj.errors = false
-      for(vjas of vj.vehicle_journey_at_stops){
-        vjas.errors = null
-        if (vjas.area_kind == "non_commercial" && parseInt(vjas.departure_time.hour) == 0 && parseInt(vjas.departure_time.minute) == 0){
-          vjas.errors = "Champ requis"
-          vj.errors = true
-          valid = false
-        }
-      }
-    }
     dispatch(actions.didValidateVehicleJourneys(vehicleJourneys))
-    if(valid){
-      actions.submitVehicleJourneys(dispatch, vehicleJourneys, next)
-    }
+    actions.submitVehicleJourneys(dispatch, vehicleJourneys, next)
   },
 
   didValidateVehicleJourneys : (vehicleJourneys) => ({
-- 
cgit v1.2.3
From 2a6b96b7b820afdf02c58cc277b31a0a6236a6ea Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Wed, 31 Jan 2018 13:23:24 +0100
Subject: VehicleJourneys#index: Allow company deletion in 'add' modal
While the 'x' button in the Select2 was available in the 'edit' modal
for a vehicle journey, it was not in the 'add' modal. Thus, once you
added an associated company, you couldn't remove it in the modal. You'd
either have to cancel and create your vehicle journey again, or create
it an delete the company in the 'edit' modal later.
This enables the 'x' button in the 'add' modal (it previously was only
enabled when `editMode` was active) and sets the proper action/reducer
to remove the company.
The `allowClear` attribute was changed to only work in the 'edit' modal
in 0079238842263768b88b0fa0fd977824b49eabd5. That commit appears to be
changing things so that certain functions work when not in 'edit' mode.
The case we're concerned about is that the 'edit' modal can be opened
when in 'view' mode (not 'edit' mode) in order to get more detailed
information about a vehicle journey. This means the company Select2 is
available in 'view' mode, and the 'x' button should not be visible in
that case. But, when I tested this, even with `allowClear: true`, the
'x' button wasn't visible. This, I can only assume, is because the
Select2 is in a 'disabled' state, so it's smart enough to know not to
show the 'x' button. Which works great for us, because this allows us to
not show the 'x' button here and still show it in the 'add' and 'edit'
modals.
Refs #5574
---
 app/javascript/vehicle_journeys/components/tools/CreateModal.js        | 1 +
 .../vehicle_journeys/components/tools/select2s/CompanySelect2.js       | 2 +-
 app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js  | 3 +++
 3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
index 90328458b..8536f66e6 100644
--- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js
+++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
@@ -66,6 +66,7 @@ export default class CreateModal extends Component {
                                this.props.onSelect2Company(e)}
+                                onUnselect2Company = {() => this.props.onUnselect2Company()}
                               />
                             
                           
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
index 28a092945..5c7f75d99 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
@@ -26,7 +26,7 @@ export default class BSelect4 extends Component {
         multiple={false}
         ref='company_id'
         options={{
-          allowClear: this.props.editMode,
+          allowClear: true,
           theme: 'bootstrap',
           width: '100%',
           placeholder: 'Filtrer par transporteur...',
diff --git a/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js
index 0db7628be..d982f5a5f 100644
--- a/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js
@@ -30,6 +30,9 @@ const mapDispatchToProps = (dispatch) => {
     },
     onSelect2Company: (e) => {
       dispatch(actions.select2Company(e.params.data))
+    },
+    onUnselect2Company: () => {
+      dispatch(actions.unselect2Company())
     }
   }
 }
-- 
cgit v1.2.3
From 0a2cf4363a0bd5c5ca3b971d8f609356016bc7a1 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 13:37:47 +0100
Subject: Try and fix jenkins builds
---
 package.json | 2 +-
 yarn.lock    | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/package.json b/package.json
index 802a2eef7..e80f5231e 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
     "babel-preset-react": "6.24.1",
     "babelify": "8.0.0",
     "bootstrap": "3",
+    "clean-webpack-plugin": "^0.1.18",
     "coffee-loader": "^0.9.0",
     "coffeescript": "1.12.7",
     "jquery": "3.2.1",
@@ -29,7 +30,6 @@
     "node": "~6.12.0"
   },
   "devDependencies": {
-    "clean-webpack-plugin": "0.1.17",
     "es6-object-assign": "1.1.0",
     "grunt": "^1.0.1",
     "grunt-contrib-watch": "^1.0.0",
diff --git a/yarn.lock b/yarn.lock
index d17ae1d52..b32d906dd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1347,9 +1347,9 @@ clap@^1.0.9:
   dependencies:
     chalk "^1.1.3"
 
-clean-webpack-plugin@0.1.17:
-  version "0.1.17"
-  resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-0.1.17.tgz#71c57242e6d47204d46f809413176e7bed28ec49"
+clean-webpack-plugin@^0.1.18:
+  version "0.1.18"
+  resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-0.1.18.tgz#2e2173897c76646031bff047c14b9c22c80d8c4a"
   dependencies:
     rimraf "^2.6.1"
 
-- 
cgit v1.2.3
From bde34143629e19273493fba8ba9e50bcd5f0c547 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 14:02:54 +0100
Subject: Fix StopArea validation breaking the specs
---
 app/models/chouette/stop_area.rb      | 2 +-
 spec/factories/chouette_stop_areas.rb | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index ad42d54ae..d7d5c2eb2 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -60,7 +60,7 @@ module Chouette
     end
 
     def area_type_of_right_kind
-
+      return unless self.kind
       unless Chouette::AreaType.send(self.kind).map(&:to_s).include?(self.area_type)
         errors.add(:area_type, I18n.t('stop_areas.errors.incorrect_kind_area_type'))
       end
diff --git a/spec/factories/chouette_stop_areas.rb b/spec/factories/chouette_stop_areas.rb
index 94517f856..9b4764781 100644
--- a/spec/factories/chouette_stop_areas.rb
+++ b/spec/factories/chouette_stop_areas.rb
@@ -3,9 +3,10 @@ FactoryGirl.define do
     sequence(:objectid) { |n| "FR:#{n}:ZDE:#{n}:STIF" }
     sequence(:name) { |n| "stop_area_#{n}" }
     sequence(:registration_number) { |n| "test-#{n}" }
-    area_type { Chouette::AreaType.all.sample }
+    area_type { Chouette::AreaType.commercial.sample }
     latitude {10.0 * rand}
     longitude {10.0 * rand}
+    kind "commercial"
 
     association :stop_area_referential
 
-- 
cgit v1.2.3
From 5e1b7a06a9958580fc18a0a4e33ff66ea6adcdf9 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 11:54:47 +0100
Subject: Refs #5754; Add a filter on purchase_windows on ReferentialVJs#index
---
 app/controllers/concerns/ransack_date_filter.rb    |  5 +--
 .../referential_vehicle_journeys_controller.rb     |  5 +++
 app/models/chouette/purchase_window.rb             |  1 +
 app/models/chouette/vehicle_journey.rb             |  7 ++++
 .../_filters.html.slim                             |  8 ++++-
 config/locales/vehicle_journeys.en.yml             |  3 ++
 config/locales/vehicle_journeys.fr.yml             |  3 ++
 spec/models/chouette/vehicle_journey_spec.rb       | 41 ++++++++++++++++++++++
 8 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/app/controllers/concerns/ransack_date_filter.rb b/app/controllers/concerns/ransack_date_filter.rb
index 0fbde91d3..99889294c 100644
--- a/app/controllers/concerns/ransack_date_filter.rb
+++ b/app/controllers/concerns/ransack_date_filter.rb
@@ -29,13 +29,14 @@ module RansackDateFilter
     def ransack_period_range **options
       return options[:scope] unless !!@begin_range && !!@end_range
 
+      scope = options[:scope]
       if @begin_range > @end_range
         flash.now[:error] = options[:error_message]
       else
-        scope = options[:scope].send options[:query], @begin_range..@end_range
+        scope = scope.send options[:query], @begin_range..@end_range
       end
       scope
     end
   end
 
-end
\ No newline at end of file
+end
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index c3fcde0b1..385405b84 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -3,6 +3,10 @@
 #
 class ReferentialVehicleJourneysController < ChouetteController
   include ReferentialSupport
+  include RansackDateFilter
+
+  before_action only: [:index] { set_date_time_params("purchase_window", Date) }
+
   defaults :resource_class => Chouette::VehicleJourney, collection_name: :vehicle_journeys
 
   requires_feature :referential_vehicle_journeys
@@ -12,6 +16,7 @@ class ReferentialVehicleJourneysController < ChouetteController
   def collection
     @q ||= end_of_association_chain
     @q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
+    @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window)
     @q = @q.ransack(params[:q])
     @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
index 22bcc1de1..334493015 100644
--- a/app/models/chouette/purchase_window.rb
+++ b/app/models/chouette/purchase_window.rb
@@ -18,6 +18,7 @@ module Chouette
     validates_presence_of :name, :referential
 
     scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) }
+    scope :overlap_dates, ->(date_range) { where('daterange(?, ?) && any (date_ranges)', date_range.first, date_range.last + 1.day) }
 
     def self.ransackable_scopes(auth_object = nil)
       [:contains_date]
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index d94b69271..a252d4519 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -52,8 +52,15 @@ module Chouette
       end
     }
 
+    scope :in_purchase_window, ->(range){
+      purchase_windows = Chouette::PurchaseWindow.overlap_dates(range)
+      sql = purchase_windows.joins(:vehicle_journeys).select('vehicle_journeys.id').uniq.to_sql
+      where("id IN (#{sql})")
+    }
+
     # We need this for the ransack object in the filters
     ransacker :stop_area_ids
+    ransacker :purchase_window_date_gt
 
     # TODO: Remove this validator
     # We've eliminated this validation because it prevented vehicle journeys
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index 1301d3dab..af8b2de3a 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -21,10 +21,16 @@
         = f.input :published_journey_name_gteq, label: false, wrapper_html: { class: 'w45'}
         .form-group.w10.to= I18n.t('vehicle_journeys.form.to')
         = f.input :published_journey_name_lteq, label: false, wrapper_html: { class: 'w45'}
-
     .form-group.togglable
       = f.label Chouette::StopArea.model_name.human.pluralize, required: false, class: 'control-label'
       = f.input :stop_area_ids, collection: @all_stop_areas.select(:id, :name).order(name: :asc), checked: params[:q] && params[:q][:stop_area_ids], as: :check_boxes, label: false, label_method: lambda{|l| ("" + l.name + "").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}, multiple: true
+    .form-group.togglable
+      = f.label Chouette::VehicleJourney.human_attribute_name(:purchase_window), class: 'control-label'
+      .filter_menu
+        = f.simple_fields_for :purchase_window do |p|
+          = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
+          = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
+
 
   .actions
     = link_to 'Effacer', referential_vehicle_journeys_path(@referential), class: 'btn btn-link'
diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml
index 1c1f6c6bd..08a9334cc 100644
--- a/config/locales/vehicle_journeys.en.yml
+++ b/config/locales/vehicle_journeys.en.yml
@@ -67,6 +67,8 @@ en:
       time_range_filter: "Filter"
     sidebar:
       timeless: "Timeless vehicle journeys"
+    errors:
+      purchase_window: Invalid purchase window
   activerecord:
     models:
       vehicle_journey:
@@ -108,6 +110,7 @@ en:
         footnote_ids: "Footnotes"
         departure_time: "Departure"
         arrival_time: "Arrival"
+        purchase_window: "Purchase availability"
     errors:
       models:
         vehicle_journey:
diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml
index 5749f8f10..8d85d877a 100644
--- a/config/locales/vehicle_journeys.fr.yml
+++ b/config/locales/vehicle_journeys.fr.yml
@@ -67,6 +67,8 @@ fr:
       time_range_filter: "Filtrer"
     sidebar:
       timeless: "Courses sans horaire"
+    errors:
+      purchase_window: Calendrier commercial invalide
   activerecord:
     models:
       vehicle_journey:
@@ -108,6 +110,7 @@ fr:
         footnote_ids: "Notes de bas de page"
         departure_time: "Départ"
         arrival_time: "Arrivée"
+        purchase_window: "Disponibilité commerciale"
     errors:
       models:
         vehicle_journey:
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 21bbc1ba8..06f392d65 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -24,6 +24,47 @@ describe Chouette::VehicleJourney, :type => :model do
     it_behaves_like 'checksum support', :vehicle_journey
   end
 
+  describe '#in_purchase_window' do
+    let(:start_date){2.month.ago.to_date}
+    let(:end_date){1.month.ago.to_date}
+
+    subject{Chouette::VehicleJourney.in_purchase_window start_date..end_date}
+
+    let!(:without_purchase_window){ create :vehicle_journey }
+    let!(:without_matching_purchase_window){
+      pw = create :purchase_window, referential: Referential.first, date_ranges: [(end_date+1.day..end_date+2.days)]
+      pw2 = create :purchase_window, referential: Referential.first, date_ranges: [(end_date+10.day..end_date+20.days)]
+      create :vehicle_journey, purchase_windows: [pw, pw2]
+    }
+    let!(:included_purchase_window){
+      pw = create :purchase_window, referential: Referential.first, date_ranges: [(start_date..end_date)]
+      pw2 = create :purchase_window, referential: Referential.first
+      create :vehicle_journey, purchase_windows: [pw, pw2]
+    }
+    let!(:overlapping_purchase_window){
+      pw = create :purchase_window, referential: Referential.first, date_ranges: [(end_date..end_date+1.day)]
+      pw2 = create :purchase_window, referential: Referential.first
+      create :vehicle_journey, purchase_windows: [pw, pw2]
+    }
+
+
+    it "should not include VJ with no purchase window" do
+      expect(subject).to_not include without_purchase_window
+    end
+
+    it "should not include VJ with no matching purchase window" do
+      expect(subject).to_not include without_matching_purchase_window
+    end
+
+    it "should include VJ with included purchase window" do
+      expect(subject).to include included_purchase_window
+    end
+
+    it "should include VJ with overlapping_purchase_window purchase window" do
+      expect(subject).to include overlapping_purchase_window
+    end
+  end
+
   describe "vjas_departure_time_must_be_before_next_stop_arrival_time",
       skip: "Validation currently commented out because it interferes with day offsets" do
 
-- 
cgit v1.2.3
From 9f583abe27b77e4a5af5e5428329a91c929f3254 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 13:17:14 +0100
Subject: Refs #5754; Add a filter on calendars for ReferentialVJs#index
---
 app/controllers/concerns/ransack_date_filter.rb    | 25 ++++---
 .../referential_vehicle_journeys_controller.rb     |  6 +-
 app/models/chouette/time_table.rb                  |  1 -
 app/models/chouette/vehicle_journey.rb             | 14 ++++
 .../_filters.html.slim                             | 10 ++-
 config/locales/vehicle_journeys.en.yml             |  1 +
 config/locales/vehicle_journeys.fr.yml             |  1 +
 spec/factories/chouette_time_table.rb              | 17 +++--
 spec/models/chouette/vehicle_journey_spec.rb       | 83 +++++++++++++++++++++-
 9 files changed, 137 insertions(+), 21 deletions(-)
diff --git a/app/controllers/concerns/ransack_date_filter.rb b/app/controllers/concerns/ransack_date_filter.rb
index 99889294c..055c01130 100644
--- a/app/controllers/concerns/ransack_date_filter.rb
+++ b/app/controllers/concerns/ransack_date_filter.rb
@@ -3,7 +3,15 @@ module RansackDateFilter
 
   included do
 
-    def set_date_time_params(param_name, klass)
+    def begin_range_var prefix
+      "@#{[prefix, "begin_range"].compact.join('_')}"
+    end
+
+    def end_range_var prefix
+      "@#{[prefix, "end_range"].compact.join('_')}"
+    end
+
+    def set_date_time_params(param_name, klass, prefix: nil)
       start_date = []
       end_date = []
 
@@ -16,24 +24,25 @@ module RansackDateFilter
         params[:q].delete([param_name])
 
         if klass == DateTime
-          @begin_range = klass.new(*start_date,0,0,0) rescue nil
-          @end_range = klass.new(*end_date,23,59,59) rescue nil
+          instance_variable_set begin_range_var(prefix), klass.new(*start_date,0,0,0) rescue nil
+          instance_variable_set end_range_var(prefix), klass.new(*end_date,23,59,59) rescue nil
         else
-          @begin_range = klass.new(*start_date) rescue nil
-          @end_range = klass.new(*end_date) rescue nil
+          instance_variable_set begin_range_var(prefix), klass.new(*start_date) rescue nil
+          instance_variable_set end_range_var(prefix), klass.new(*end_date) rescue nil
         end
       end
     end
 
     # Fake ransack filter
     def ransack_period_range **options
-      return options[:scope] unless !!@begin_range && !!@end_range
+      prefix = options[:prefix]
+      return options[:scope] unless !!instance_variable_get(begin_range_var(prefix)) && !!instance_variable_get(end_range_var(prefix))
 
       scope = options[:scope]
-      if @begin_range > @end_range
+      if instance_variable_get(begin_range_var(prefix)) > instance_variable_get(end_range_var(prefix))
         flash.now[:error] = options[:error_message]
       else
-        scope = scope.send options[:query], @begin_range..@end_range
+        scope = scope.send options[:query], instance_variable_get(begin_range_var(prefix))..instance_variable_get(end_range_var(prefix))
       end
       scope
     end
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index 385405b84..a199157dd 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -5,7 +5,8 @@ class ReferentialVehicleJourneysController < ChouetteController
   include ReferentialSupport
   include RansackDateFilter
 
-  before_action only: [:index] { set_date_time_params("purchase_window", Date) }
+  before_action only: [:index] { set_date_time_params("purchase_window", Date, prefix: :purchase_window) }
+  before_action only: [:index] { set_date_time_params("time_table", Date, prefix: :time_table) }
 
   defaults :resource_class => Chouette::VehicleJourney, collection_name: :vehicle_journeys
 
@@ -16,7 +17,8 @@ class ReferentialVehicleJourneysController < ChouetteController
   def collection
     @q ||= end_of_association_chain
     @q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
-    @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window)
+    @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window, prefix: :purchase_window)
+    @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.time_table'), query: :with_matching_timetable, prefix: :time_table)
     @q = @q.ransack(params[:q])
     @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index db97dd2fa..07bf35444 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -311,7 +311,6 @@ module Chouette
         bounding_max = periods_max_date if periods_max_date &&
             (bounding_max.nil? || (bounding_max < periods_max_date))
       end
-
       [bounding_min, bounding_max].compact
     end
 
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index a252d4519..1756a7098 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -62,6 +62,20 @@ module Chouette
     ransacker :stop_area_ids
     ransacker :purchase_window_date_gt
 
+    # returns VehicleJourneys with at least 1 day in their time_tables
+    # included in the given range
+    def self.with_matching_timetable date_range
+      out = []
+      time_tables = Chouette::TimeTable.where(id: self.joins("INNER JOIN time_tables_vehicle_journeys ON vehicle_journeys.id = time_tables_vehicle_journeys.vehicle_journey_id").pluck('time_tables_vehicle_journeys.time_table_id')).overlapping(date_range)
+      time_tables = time_tables.select do |time_table|
+        range = date_range
+        range = date_range & (time_table.start_date-1.day..time_table.end_date+1.day) || [] if time_table.start_date.present? && time_table.end_date.present?
+        range.any?{|d| time_table.include_day?(d) }
+      end
+      out += time_tables.map{|t| t.vehicle_journey_ids}.flatten
+      where(id: out)
+    end
+
     # TODO: Remove this validator
     # We've eliminated this validation because it prevented vehicle journeys
     # from being saved with at-stops having a day offset greater than 0,
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index af8b2de3a..bfb5b77dd 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -28,8 +28,14 @@
       = f.label Chouette::VehicleJourney.human_attribute_name(:purchase_window), class: 'control-label'
       .filter_menu
         = f.simple_fields_for :purchase_window do |p|
-          = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
-          = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
+          = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @purchase_window_begin_range, include_blank: @purchase_window_begin_range ? false : true
+          = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @purchase_window_end_range, include_blank: @purchase_window_end_range ? false : true
+    .form-group.togglable
+      = f.label Chouette::TimeTable.model_name.human, class: 'control-label'
+      .filter_menu
+        = f.simple_fields_for :time_table do |p|
+          = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @time_table_begin_range, include_blank: @time_table_begin_range ? false : true
+          = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @time_table_end_range, include_blank: @time_table_end_range ? false : true
 
 
   .actions
diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml
index 08a9334cc..abb1da530 100644
--- a/config/locales/vehicle_journeys.en.yml
+++ b/config/locales/vehicle_journeys.en.yml
@@ -69,6 +69,7 @@ en:
       timeless: "Timeless vehicle journeys"
     errors:
       purchase_window: Invalid purchase window
+      time_table: Invalid dates
   activerecord:
     models:
       vehicle_journey:
diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml
index 8d85d877a..ca8475812 100644
--- a/config/locales/vehicle_journeys.fr.yml
+++ b/config/locales/vehicle_journeys.fr.yml
@@ -69,6 +69,7 @@ fr:
       timeless: "Courses sans horaire"
     errors:
       purchase_window: Calendrier commercial invalide
+      time_table: Dates d'application invalides
   activerecord:
     models:
       vehicle_journey:
diff --git a/spec/factories/chouette_time_table.rb b/spec/factories/chouette_time_table.rb
index 81a08ca2a..af48e1b42 100644
--- a/spec/factories/chouette_time_table.rb
+++ b/spec/factories/chouette_time_table.rb
@@ -11,18 +11,21 @@ FactoryGirl.define do
     end
 
     after(:create) do |time_table, evaluator|
-
-      0.upto(4) do |i|
-        time_table.dates  << create(:time_table_date, :time_table => time_table, :date => i.days.since.to_date, :in_out => true)
+      unless time_table.dates.any?
+        evaluator.dates_count.times do |i|
+          time_table.dates  << create(:time_table_date, :time_table => time_table, :date => i.days.since.to_date, :in_out => true)
+        end
       end
 
       start_date = Date.today
       end_date = start_date + 10
 
-      0.upto(4) do |i|
-        time_table.periods << create(:time_table_period, :time_table => time_table, :period_start => start_date, :period_end => end_date)
-        start_date = start_date + 20
-        end_date = start_date + 10
+      unless time_table.periods.any?
+        evaluator.periods_count.times do |i|
+          time_table.periods << create(:time_table_period, :time_table => time_table, :period_start => start_date, :period_end => end_date)
+          start_date = start_date + 20
+          end_date = start_date + 10
+        end
       end
       time_table.save_shortcuts
       time_table.update_checksum!
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 06f392d65..70661bcc5 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -60,11 +60,92 @@ describe Chouette::VehicleJourney, :type => :model do
       expect(subject).to include included_purchase_window
     end
 
-    it "should include VJ with overlapping_purchase_window purchase window" do
+    it "should include VJ with overlapping purchase_window purchase window" do
       expect(subject).to include overlapping_purchase_window
     end
   end
 
+  describe '#in_time_table' do
+    let(:start_date){2.month.ago.to_date}
+    let(:end_date){1.month.ago.to_date}
+
+    subject{Chouette::VehicleJourney.with_matching_timetable start_date..end_date}
+
+    context "without time table" do
+      let!(:vehicle_journey){ create :vehicle_journey }
+      it "should not include VJ " do
+        expect(subject).to_not include vehicle_journey
+      end
+    end
+
+    context "without a time table matching on a regular day" do
+      let(:timetable){
+        period = create :time_table_period, period_start: start_date-2.day, period_end: start_date
+        create :time_table, periods: [period], dates_count: 0
+      }
+      let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+      it "should include VJ " do
+        expect(subject).to include vehicle_journey
+      end
+    end
+
+    context "without a time table matching on a regular day" do
+      let(:timetable){
+        period = create :time_table_period, period_start: end_date, period_end: end_date+1.day
+        create :time_table, periods: [period], dates_count: 0
+      }
+      let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+      it "should include VJ " do
+        expect(subject).to include vehicle_journey
+      end
+    end
+
+    context "with a time table with a matching period but not the right day" do
+      let(:start_date){end_date - 1.day}
+      let(:end_date){Time.now.end_of_week.to_date}
+
+      let(:timetable){
+        period = create :time_table_period, period_start: start_date-1.month, period_end: end_date+1.month
+        create :time_table, periods: [period], int_day_types: 4 + 8, dates_count: 0
+      }
+      let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+      it "should not include VJ " do
+        expect(subject).to_not include vehicle_journey
+      end
+    end
+
+    context "with a time table with a matching period but day opted-out" do
+      let(:start_date){end_date - 1.day}
+      let(:end_date){Time.now.end_of_week.to_date}
+
+      let(:timetable){
+        period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
+        date = create(:time_table_date, :date => start_date, in_out: false)
+        create :time_table, periods: [period], dates: [date]
+      }
+      let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+      it "should not include VJ " do
+        expect(subject).to_not include vehicle_journey
+      end
+    end
+
+    context "with a time table with no matching period but not the right extra day" do
+      let(:start_date){end_date - 1.day}
+      let(:end_date){Time.now.end_of_week.to_date}
+
+      let(:timetable){
+        period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
+        date = create(:time_table_date, :date => start_date, in_out: true)
+        create :time_table, periods: [period], dates: [date]
+      }
+      let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+      it "should include VJ " do
+        expect(subject).to include vehicle_journey
+      end
+    end
+
+  end
+
   describe "vjas_departure_time_must_be_before_next_stop_arrival_time",
       skip: "Validation currently commented out because it interferes with day offsets" do
 
-- 
cgit v1.2.3
From 32bb2b43d463a071164a8e87a3e995f6ddceb17e Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 14:34:48 +0100
Subject: Fix Reflex import
---
 lib/stif/reflex_synchronization.rb | 1 +
 1 file changed, 1 insertion(+)
diff --git a/lib/stif/reflex_synchronization.rb b/lib/stif/reflex_synchronization.rb
index 39a92bd1f..7570e4c49 100644
--- a/lib/stif/reflex_synchronization.rb
+++ b/lib/stif/reflex_synchronization.rb
@@ -151,6 +151,7 @@ module Stif
 
       def create_or_update_stop_area entry
         stop = Chouette::StopArea.find_or_create_by(objectid: entry['id'], stop_area_referential: self.defaut_referential )
+        stop.kind = :commercial
         stop.deleted_at            = nil
         {
           :comment        => 'Description',
-- 
cgit v1.2.3
From a477a97347792540426aad79b1709dbb12aeb3c2 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 15:10:40 +0100
Subject: Fix specs
---
 app/javascript/vehicle_journeys/actions/index.js |  1 +
 spec/javascript/vehicle_journeys/actions_spec.js |  3 ++-
 spec/models/chouette/vehicle_journey_spec.rb     | 14 ++++++++------
 3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index 4a4ec371d..8970c6025 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -384,6 +384,7 @@ const actions = {
   validate : (dispatch, vehicleJourneys, next) => {
     dispatch(actions.didValidateVehicleJourneys(vehicleJourneys))
     actions.submitVehicleJourneys(dispatch, vehicleJourneys, next)
+    return true
   },
 
   didValidateVehicleJourneys : (vehicleJourneys) => ({
diff --git a/spec/javascript/vehicle_journeys/actions_spec.js b/spec/javascript/vehicle_journeys/actions_spec.js
index d486c9af8..9710d833c 100644
--- a/spec/javascript/vehicle_journeys/actions_spec.js
+++ b/spec/javascript/vehicle_journeys/actions_spec.js
@@ -1,6 +1,7 @@
 import actions from '../../../app/javascript/vehicle_journeys/actions/index'
 
 const dispatch = function(){}
+window.fetch = function(){return Promise.resolve()}
 const currentPage = 1
 
 describe('when cannot fetch api', () => {
@@ -49,7 +50,7 @@ describe('when validating the form', () => {
       }]
     }]
 
-    expect(actions.validate(dispatch, state)).toEqual(false)
+    expect(actions.validate(dispatch, state)).toEqual(true)
 
     state = [{
       vehicle_journey_at_stops: [{
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 70661bcc5..e9ffddd2a 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -119,9 +119,10 @@ describe Chouette::VehicleJourney, :type => :model do
       let(:end_date){Time.now.end_of_week.to_date}
 
       let(:timetable){
-        period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
-        date = create(:time_table_date, :date => start_date, in_out: false)
-        create :time_table, periods: [period], dates: [date]
+        tt = create :time_table, dates_count: 0, periods_count: 0
+        create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day, time_table: tt
+        create(:time_table_date, :date => start_date, in_out: false, time_table: tt)
+        tt
       }
       let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
       it "should not include VJ " do
@@ -134,9 +135,10 @@ describe Chouette::VehicleJourney, :type => :model do
       let(:end_date){Time.now.end_of_week.to_date}
 
       let(:timetable){
-        period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
-        date = create(:time_table_date, :date => start_date, in_out: true)
-        create :time_table, periods: [period], dates: [date]
+        tt = create :time_table, dates_count: 0, periods_count: 0
+        create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day, time_table: tt
+        create(:time_table_date, :date => start_date, in_out: true, time_table: tt)
+        tt
       }
       let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
       it "should include VJ " do
-- 
cgit v1.2.3
From 57d1749d53f0ad8fc26f67b42b2b207bf0ab06ad Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 07:28:57 +0100
Subject: Refs #5682; Add application_days field to calendars
---
 app/models/calendar.rb                             |   3 +-
 app/models/chouette/time_table.rb                  |  95 +-----------------
 app/models/concerns/application_days_support.rb    | 108 +++++++++++++++++++++
 ...0180124061955_add_int_day_types_to_calendars.rb |   5 +
 db/schema.rb                                       |   5 +-
 spec/models/calendar_spec.rb                       |   3 +-
 spec/models/chouette/time_table_spec.rb            |   5 +-
 7 files changed, 121 insertions(+), 103 deletions(-)
 create mode 100644 app/models/concerns/application_days_support.rb
 create mode 100644 db/migrate/20180124061955_add_int_day_types_to_calendars.rb
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index d93532908..40dfa1210 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -5,6 +5,7 @@ require_relative 'calendar/period'
 class Calendar < ActiveRecord::Base
   include DateSupport
   include PeriodSupport
+  include ApplicationDaysSupport
 
   has_paper_trail class_name: 'PublicVersion'
   belongs_to :organisation
@@ -29,7 +30,7 @@ class Calendar < ActiveRecord::Base
       self.periods.each do |p|
         tt.periods << Chouette::TimeTablePeriod.new(period_start: p.begin, period_end: p.end)
       end
-      tt.int_day_types = 508
+      tt.int_day_types = self.int_day_types
     end
   end
 
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 07bf35444..1a1972113 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -4,11 +4,12 @@ module Chouette
     include ChecksumSupport
     include TimeTableRestrictions
     include ObjectidSupport
+    include ApplicationDaysSupport
+
     # FIXME http://jira.codehaus.org/browse/JRUBY-6358
     self.primary_key = "id"
     acts_as_taggable
 
-    attr_accessor :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
     attr_accessor :tag_search
 
     def self.ransackable_attributes auth_object = nil
@@ -314,98 +315,6 @@ module Chouette
       [bounding_min, bounding_max].compact
     end
 
-    def display_day_types
-      %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ')
-    end
-
-    def day_by_mask(flag)
-      int_day_types & flag == flag
-    end
-
-    def self.day_by_mask(int_day_types,flag)
-      int_day_types & flag == flag
-    end
-
-
-    def valid_days
-      # Build an array with day of calendar week (1-7, Monday is 1).
-      [].tap do |valid_days|
-        valid_days << 1  if monday
-        valid_days << 2  if tuesday
-        valid_days << 3  if wednesday
-        valid_days << 4  if thursday
-        valid_days << 5  if friday
-        valid_days << 6  if saturday
-        valid_days << 7  if sunday
-      end
-    end
-
-    def self.valid_days(int_day_types)
-      # Build an array with day of calendar week (1-7, Monday is 1).
-      [].tap do |valid_days|
-        valid_days << 1  if day_by_mask(int_day_types,4)
-        valid_days << 2  if day_by_mask(int_day_types,8)
-        valid_days << 3  if day_by_mask(int_day_types,16)
-        valid_days << 4  if day_by_mask(int_day_types,32)
-        valid_days << 5  if day_by_mask(int_day_types,64)
-        valid_days << 6  if day_by_mask(int_day_types,128)
-        valid_days << 7  if day_by_mask(int_day_types,256)
-      end
-    end
-
-    def monday
-      day_by_mask(4)
-    end
-    def tuesday
-      day_by_mask(8)
-    end
-    def wednesday
-      day_by_mask(16)
-    end
-    def thursday
-      day_by_mask(32)
-    end
-    def friday
-      day_by_mask(64)
-    end
-    def saturday
-      day_by_mask(128)
-    end
-    def sunday
-      day_by_mask(256)
-    end
-
-    def set_day(day,flag)
-      if day == '1' || day == true
-        self.int_day_types |= flag
-      else
-        self.int_day_types &= ~flag
-      end
-      shortcuts_update
-    end
-
-    def monday=(day)
-      set_day(day,4)
-    end
-    def tuesday=(day)
-      set_day(day,8)
-    end
-    def wednesday=(day)
-      set_day(day,16)
-    end
-    def thursday=(day)
-      set_day(day,32)
-    end
-    def friday=(day)
-      set_day(day,64)
-    end
-    def saturday=(day)
-      set_day(day,128)
-    end
-    def sunday=(day)
-      set_day(day,256)
-    end
-
     def effective_days_of_period(period,valid_days=self.valid_days)
       days = []
         period.period_start.upto(period.period_end) do |date|
diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb
new file mode 100644
index 000000000..927011acb
--- /dev/null
+++ b/app/models/concerns/application_days_support.rb
@@ -0,0 +1,108 @@
+module ApplicationDaysSupport
+  extend ActiveSupport::Concern
+
+  included do |into|
+    into.class_eval do
+      MONDAY    = 4
+      TUESDAY   = 8
+      WEDNESDAY = 16
+      THURSDAY  = 32
+      FRIDAY    = 64
+      SATURDAY  = 128
+      SUNDAY    = 256
+    end
+
+    # attr_accessor :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
+  end
+
+  def display_day_types
+    %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ')
+  end
+
+  def day_by_mask(flag)
+    int_day_types & flag == flag
+  end
+
+  def self.day_by_mask(int_day_types,flag)
+    int_day_types & flag == flag
+  end
+
+  def valid_days
+    # Build an array with day of calendar week (1-7, Monday is 1).
+    [].tap do |valid_days|
+      valid_days << 1  if monday
+      valid_days << 2  if tuesday
+      valid_days << 3  if wednesday
+      valid_days << 4  if thursday
+      valid_days << 5  if friday
+      valid_days << 6  if saturday
+      valid_days << 7  if sunday
+    end
+  end
+
+  def self.valid_days(int_day_types)
+    # Build an array with day of calendar week (1-7, Monday is 1).
+    [].tap do |valid_days|
+      valid_days << 1  if day_by_mask(int_day_types,MONDAY)
+      valid_days << 2  if day_by_mask(int_day_types,TUESDAY)
+      valid_days << 3  if day_by_mask(int_day_types,WEDNESDAY)
+      valid_days << 4  if day_by_mask(int_day_types,THURSDAY)
+      valid_days << 5  if day_by_mask(int_day_types,FRIDAY)
+      valid_days << 6  if day_by_mask(int_day_types,SATURDAY)
+      valid_days << 7  if day_by_mask(int_day_types,SUNDAY)
+    end
+  end
+
+  def monday
+    day_by_mask(MONDAY)
+  end
+  def tuesday
+    day_by_mask(TUESDAY)
+  end
+  def wednesday
+    day_by_mask(WEDNESDAY)
+  end
+  def thursday
+    day_by_mask(THURSDAY)
+  end
+  def friday
+    day_by_mask(FRIDAY)
+  end
+  def saturday
+    day_by_mask(SATURDAY)
+  end
+  def sunday
+    day_by_mask(SUNDAY)
+  end
+
+  def set_day(day,flag)
+    if day == '1' || day == true
+      self.int_day_types |= flag
+    else
+      self.int_day_types &= ~flag
+    end
+    shortcuts_update
+  end
+
+  def monday=(day)
+    set_day(day,4)
+  end
+  def tuesday=(day)
+    set_day(day,8)
+  end
+  def wednesday=(day)
+    set_day(day,16)
+  end
+  def thursday=(day)
+    set_day(day,32)
+  end
+  def friday=(day)
+    set_day(day,64)
+  end
+  def saturday=(day)
+    set_day(day,128)
+  end
+  def sunday=(day)
+    set_day(day,256)
+  end
+end
diff --git a/db/migrate/20180124061955_add_int_day_types_to_calendars.rb b/db/migrate/20180124061955_add_int_day_types_to_calendars.rb
new file mode 100644
index 000000000..6384c7177
--- /dev/null
+++ b/db/migrate/20180124061955_add_int_day_types_to_calendars.rb
@@ -0,0 +1,5 @@
+class AddIntDayTypesToCalendars < ActiveRecord::Migration
+  def change
+    add_column :calendars, :int_day_types, :integer, default: 0
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9be702fa3..614d9803e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,11 +11,8 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-<<<<<<< HEAD
-ActiveRecord::Schema.define(version: 20180123174450) do
-=======
+
 ActiveRecord::Schema.define(version: 20180126134944) do
->>>>>>> Refs #5750 @1h; Add a "kind" attribute to StopAreas
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb
index e71c2b081..4c65b9660 100644
--- a/spec/models/calendar_spec.rb
+++ b/spec/models/calendar_spec.rb
@@ -9,11 +9,12 @@ RSpec.describe Calendar, :type => :model do
   it { is_expected.to be_versioned }
 
   describe '#to_time_table' do
-    let(:calendar) { create(:calendar, date_ranges: [Date.today...(Date.today + 1.month)]) }
+    let(:calendar) { create(:calendar, int_day_types: Calendar::MONDAY | Calendar::SUNDAY, date_ranges: [Date.today...(Date.today + 1.month)]) }
 
     it 'should convert calendar to an instance of Chouette::TimeTable' do
       time_table = calendar.convert_to_time_table
       expect(time_table).to be_an_instance_of(Chouette::TimeTable)
+      expect(time_table.int_day_types).to eq calendar.int_day_types
       expect(time_table.periods[0].period_start).to eq(calendar.periods[0].begin)
       expect(time_table.periods[0].period_end).to eq(calendar.periods[0].end)
       expect(time_table.dates.map(&:date)).to match_array(calendar.dates)
diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb
index d4a726740..28197984e 100644
--- a/spec/models/chouette/time_table_spec.rb
+++ b/spec/models/chouette/time_table_spec.rb
@@ -926,6 +926,7 @@ end
       end
     end
   end
+  
   describe "#validity_out_between?" do
     let(:empty_tm) {build(:time_table)}
     it "should be false if empty calendar" do
@@ -1068,8 +1069,6 @@ end
       end
   end
 
-
-
   describe "#effective_days" do
       before do
         subject.periods.clear
@@ -1094,8 +1093,6 @@ end
       end
   end
 
-
-
   describe "#optimize_overlapping_periods" do
       before do
         subject.periods.clear
-- 
cgit v1.2.3
From b1fc1da5262d2b03c872829a7e1238059243e9f3 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 09:56:56 +0100
Subject: Refs #5682; Fix warning
---
 app/models/concerns/application_days_support.rb | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb
index 927011acb..f83e4a5c8 100644
--- a/app/models/concerns/application_days_support.rb
+++ b/app/models/concerns/application_days_support.rb
@@ -1,20 +1,14 @@
 module ApplicationDaysSupport
   extend ActiveSupport::Concern
 
-  included do |into|
-    into.class_eval do
-      MONDAY    = 4
-      TUESDAY   = 8
-      WEDNESDAY = 16
-      THURSDAY  = 32
-      FRIDAY    = 64
-      SATURDAY  = 128
-      SUNDAY    = 256
-    end
-
-    # attr_accessor :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
-  end
-
+  MONDAY    = 4
+  TUESDAY   = 8
+  WEDNESDAY = 16
+  THURSDAY  = 32
+  FRIDAY    = 64
+  SATURDAY  = 128
+  SUNDAY    = 256
+  
   def display_day_types
     %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ')
   end
-- 
cgit v1.2.3
From 5253b169da36a3de270f00038597b007035f09dd Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 10:15:48 +0100
Subject: Refs #5682; Set default value for application days
---
 app/models/calendar.rb                                      | 5 +++++
 app/models/concerns/application_days_support.rb             | 3 ++-
 db/migrate/20180124061955_add_int_day_types_to_calendars.rb | 2 +-
 db/schema.rb                                                | 4 +---
 spec/models/calendar_spec.rb                                | 9 +++++++++
 5 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index 40dfa1210..e0f0f03da 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -17,11 +17,16 @@ class Calendar < ActiveRecord::Base
   has_many :time_tables
 
   scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) }
+  before_create :set_default_days
 
   def self.ransackable_scopes(auth_object = nil)
     [:contains_date]
   end
 
+  def set_default_days
+    self.int_day_types ||= EVERYDAY
+  end
+
   def convert_to_time_table
     Chouette::TimeTable.new.tap do |tt|
       self.dates.each do |d|
diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb
index f83e4a5c8..425cba5bf 100644
--- a/app/models/concerns/application_days_support.rb
+++ b/app/models/concerns/application_days_support.rb
@@ -8,7 +8,8 @@ module ApplicationDaysSupport
   FRIDAY    = 64
   SATURDAY  = 128
   SUNDAY    = 256
-  
+  EVERYDAY  = MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY | SATURDAY | SUNDAY
+
   def display_day_types
     %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ')
   end
diff --git a/db/migrate/20180124061955_add_int_day_types_to_calendars.rb b/db/migrate/20180124061955_add_int_day_types_to_calendars.rb
index 6384c7177..5b1ff6fc1 100644
--- a/db/migrate/20180124061955_add_int_day_types_to_calendars.rb
+++ b/db/migrate/20180124061955_add_int_day_types_to_calendars.rb
@@ -1,5 +1,5 @@
 class AddIntDayTypesToCalendars < ActiveRecord::Migration
   def change
-    add_column :calendars, :int_day_types, :integer, default: 0
+    add_column :calendars, :int_day_types, :integer
   end
 end
diff --git a/db/schema.rb b/db/schema.rb
index 614d9803e..231bea9ba 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -16,10 +16,9 @@ ActiveRecord::Schema.define(version: 20180126134944) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
-  enable_extension "hstore"
   enable_extension "postgis"
+  enable_extension "hstore"
   enable_extension "unaccent"
-  enable_extension "objectid"
 
   create_table "access_links", id: :bigserial, force: :cascade do |t|
     t.integer  "access_point_id",                        limit: 8
@@ -121,7 +120,6 @@ ActiveRecord::Schema.define(version: 20180126134944) do
     t.datetime "updated_at"
     t.date     "end_date"
     t.string   "date_type"
-    t.string   "mode"
   end
 
   add_index "clean_ups", ["referential_id"], name: "index_clean_ups_on_referential_id", using: :btree
diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb
index 4c65b9660..86ce565cd 100644
--- a/spec/models/calendar_spec.rb
+++ b/spec/models/calendar_spec.rb
@@ -21,6 +21,15 @@ RSpec.describe Calendar, :type => :model do
     end
   end
 
+  describe 'application days' do
+    let(:calendar) { create(:calendar) }
+    it "should default to all days" do
+      %w(monday tuesday wednesday thursday friday saturday sunday).each do |day|
+        expect(calendar.send(day)).to be_truthy
+      end
+    end
+  end
+
   describe 'validations' do
     it 'validates that dates and date_ranges do not overlap' do
       expect(build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])).to_not be_valid
-- 
cgit v1.2.3
From f07802715562b31d15e31b5be90dbeac29cef227 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 16:56:16 +0100
Subject: Refs #5682 @3h; Use same UI as for timetables
---
 app/assets/stylesheets/modules/_timetables.sass    |   2 +-
 app/controllers/calendars_controller.rb            |  29 ++++
 app/helpers/time_tables_helper.rb                  |   2 +-
 app/javascript/packs/calendars/edit.js             |  74 ++++++++++
 app/javascript/time_tables/actions/index.js        |   5 +-
 app/javascript/time_tables/components/Metas.js     |  12 +-
 app/models/calendar.rb                             |  79 ++++++++++-
 app/models/calendar/period.rb                      |   5 +
 app/models/chouette/time_table.rb                  |  96 ++++---------
 app/models/concerns/application_days_support.rb    |   4 +
 app/models/concerns/date_support.rb                |  11 +-
 app/models/concerns/timetable_support.rb           | 149 +++++++++++++++++++++
 app/views/calendars/_form.html.slim                |  53 --------
 app/views/calendars/_form_advanced.html.slim       |   8 ++
 app/views/calendars/_form_simple.html.slim         |  56 ++++++++
 app/views/calendars/edit.html.slim                 |   7 +-
 app/views/calendars/month.rabl                     |   9 ++
 app/views/calendars/new.html.slim                  |   4 +-
 app/views/calendars/show.html.slim                 |  11 ++
 app/views/calendars/show.rabl                      |  22 +++
 app/views/time_tables/_show_time_table.html.slim   |  28 +---
 app/views/time_tables/show.html.slim               |   2 +-
 config/locales/calendars.en.yml                    |   7 +
 config/locales/calendars.fr.yml                    |   7 +
 config/routes.rb                                   |   6 +-
 ...180124124215_add_excluded_dates_to_calendars.rb |   5 +
 db/schema.rb                                       |   1 -
 spec/models/calendar_spec.rb                       | 110 ++++++++++++++-
 28 files changed, 637 insertions(+), 167 deletions(-)
 create mode 100644 app/javascript/packs/calendars/edit.js
 create mode 100644 app/models/concerns/timetable_support.rb
 delete mode 100644 app/views/calendars/_form.html.slim
 create mode 100644 app/views/calendars/_form_advanced.html.slim
 create mode 100644 app/views/calendars/_form_simple.html.slim
 create mode 100644 app/views/calendars/month.rabl
 create mode 100644 app/views/calendars/show.rabl
 create mode 100644 db/migrate/20180124124215_add_excluded_dates_to_calendars.rb
diff --git a/app/assets/stylesheets/modules/_timetables.sass b/app/assets/stylesheets/modules/_timetables.sass
index b06972ef9..8999456ec 100644
--- a/app/assets/stylesheets/modules/_timetables.sass
+++ b/app/assets/stylesheets/modules/_timetables.sass
@@ -30,7 +30,7 @@
 
   .t2e-item
     .th
-      padding: 6px 0 0 0
+      padding: 4px 0 0 0
       border-color: $darkgrey
       border-top-width: 2px
 
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index eaff3b0c6..3b7b6de15 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,8 +1,11 @@
 class CalendarsController < ChouetteController
   include PolicyChecker
+  include TimeTablesHelper
+
   defaults resource_class: Calendar
   before_action :ransack_contains_date, only: [:index]
   respond_to :html
+  respond_to :json, only: :show
   respond_to :js, only: :index
 
   belongs_to :workgroup
@@ -21,6 +24,32 @@ class CalendarsController < ChouetteController
     end
   end
 
+  def month
+    @date = params['date'] ? Date.parse(params['date']) : Date.today
+    @calendar = resource
+  end
+
+  def create
+    create! do
+      if @calendar.valid? && has_feature?('application_days_on_calendars')
+        redirect_to([:edit, @calendar])
+        return
+      end
+    end
+  end
+
+  def update
+    if params[:calendar]
+      super
+    else
+      state  = JSON.parse request.raw_post
+      resource.state_update state
+      respond_to do |format|
+        format.json { render json: state, status: state['errors'] ? :unprocessable_entity : :ok }
+      end
+    end
+  end
+
   private
 
   def decorate_calendars(calendars)
diff --git a/app/helpers/time_tables_helper.rb b/app/helpers/time_tables_helper.rb
index b380a2b0a..1be1fa5a1 100644
--- a/app/helpers/time_tables_helper.rb
+++ b/app/helpers/time_tables_helper.rb
@@ -84,7 +84,7 @@ module TimeTablesHelper
     end unless first.wday == first_weekday
 
     first.upto(last) do |cur|
-      cell_text, cell_attrs = block.call(cur)
+      cell_text, cell_attrs = yield cur
       cell_text  ||= cur.mday
       cell_attrs ||= {}
       cell_attrs[:headers] = th_id(cur, options[:table_id])
diff --git a/app/javascript/packs/calendars/edit.js b/app/javascript/packs/calendars/edit.js
new file mode 100644
index 000000000..bd09657ec
--- /dev/null
+++ b/app/javascript/packs/calendars/edit.js
@@ -0,0 +1,74 @@
+import React from 'react'
+import { render } from 'react-dom'
+import { Provider } from 'react-redux'
+import { createStore } from 'redux'
+import timeTablesApp from '../../time_tables/reducers'
+import App from '../../time_tables/containers/App'
+import clone from '../../helpers/clone'
+
+const actionType = clone(window, "actionType", true)
+
+// logger, DO NOT REMOVE
+// var applyMiddleware = require('redux').applyMiddleware
+// var createLogger = require('redux-logger')
+// var thunkMiddleware = require('redux-thunk').default
+// var promise = require('redux-promise')
+
+let initialState = {
+  status: {
+    actionType: actionType,
+    policy: window.perms,
+    fetchSuccess: true,
+    isFetching: false
+  },
+  timetable: {
+    current_month: [],
+    current_periode_range: '',
+    periode_range: [],
+    time_table_periods: [],
+    time_table_dates: []
+  },
+  metas: {
+    comment: '',
+    day_types: [],
+    initial_tags: []
+  },
+  pagination: {
+    stateChanged: false,
+    currentPage: '',
+    periode_range: []
+  },
+  modal: {
+    type: '',
+    modalProps: {
+      active: false,
+      begin: {
+        day: '01',
+        month: '01',
+        year: String(new Date().getFullYear())
+      },
+      end: {
+        day: '01',
+        month: '01',
+        year: String(new Date().getFullYear())
+      },
+      index: false,
+      error: ''
+    },
+    confirmModal: {}
+  }
+}
+// const loggerMiddleware = createLogger()
+
+let store = createStore(
+  timeTablesApp,
+  initialState,
+  // applyMiddleware(thunkMiddleware, promise, loggerMiddleware)
+)
+
+render(
+  
+    
+  ,
+  document.getElementById('periods')
+)
diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js
index 87c7a3e8d..70f548174 100644
--- a/app/javascript/time_tables/actions/index.js
+++ b/app/javascript/time_tables/actions/index.js
@@ -246,7 +246,8 @@ const actions = {
     return error
   },
   fetchTimeTables: (dispatch, nextPage) => {
-    let urlJSON = window.location.pathname.split('/', 5).join('/')
+    let urlJSON = window.timetablesUrl || window.location.pathname.split('/', 5).join('/')
+
     if(nextPage) {
       urlJSON += "/month.json?date=" + nextPage
     }else{
@@ -277,7 +278,7 @@ const actions = {
     let strDayTypes = actions.arrayToStrDayTypes(metas.day_types)
     metas.day_types = strDayTypes
     let sentState = assign({}, timetable, metas)
-    let urlJSON = window.location.pathname.split('/', 5).join('/')
+    let urlJSON = window.timetablesUrl || window.location.pathname.split('/', 5).join('/')
     let hasError = false
     fetch(urlJSON + '.json', {
       credentials: 'same-origin',
diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js
index 4170ba493..3c6848d27 100644
--- a/app/javascript/time_tables/components/Metas.js
+++ b/app/javascript/time_tables/components/Metas.js
@@ -27,7 +27,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat
           
 
           {/* color */}
-          
+          {metas.color !== undefined && 
             
             
               
@@ -69,10 +69,10 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat
                 
                
              
-          
+          {metas.tags !== undefined && 
             
             
                onUnselect2Tags(e)}
               />
             
-          
 
+          
+          {metas.calendar !== null && 
             
             
               {metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}
             
-          
 
+          
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index e0f0f03da..84b569ab4 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -6,6 +6,7 @@ class Calendar < ActiveRecord::Base
   include DateSupport
   include PeriodSupport
   include ApplicationDaysSupport
+  include TimetableSupport
 
   has_paper_trail class_name: 'PublicVersion'
   belongs_to :organisation
@@ -17,16 +18,29 @@ class Calendar < ActiveRecord::Base
   has_many :time_tables
 
   scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) }
-  before_create :set_default_days
+
+  after_initialize :set_defaults
 
   def self.ransackable_scopes(auth_object = nil)
     [:contains_date]
   end
 
-  def set_default_days
+  def self.state_permited_attributes item
+    {name: item["comment"]}
+  end
+
+  def set_defaults
+    self.excluded_dates ||= []
     self.int_day_types ||= EVERYDAY
   end
 
+  def human_attribute_name(*args)
+    self.class.human_attribute_name(*args)
+  end
+
+  def shortcuts_update(date=nil)
+  end
+
   def convert_to_time_table
     Chouette::TimeTable.new.tap do |tt|
       self.dates.each do |d|
@@ -39,4 +53,65 @@ class Calendar < ActiveRecord::Base
     end
   end
 
+  def include_in_dates?(day)
+    self.dates.include? day
+  end
+
+  def excluded_date?(day)
+    self.excluded_dates.include? day
+  end
+
+  def update_in_out date, in_out
+    if in_out
+      self.excluded_dates.delete date
+      self.dates << date unless include_in_dates?(date)
+    else
+      self.dates.delete date
+      self.excluded_dates << date unless excluded_date?(date)
+    end
+    date
+  end
+
+  def included_days
+    dates
+  end
+
+  def excluded_days
+    excluded_dates
+  end
+
+  def saved_dates
+    Hash[*self.dates.each_with_index.to_a.map(&:reverse).flatten]
+  end
+
+  def all_dates
+    (dates + excluded_dates).sort.each_with_index.map do |d, i|
+      OpenStruct.new(id: i, date: d, in_out: include_in_dates?(d))
+    end
+  end
+
+  def find_date_by_id id
+    self.dates[id]
+  end
+
+  def destroy_date date
+    self.dates -= [date]
+  end
+
+  def create_date in_out:, date:
+    update_in_out date, in_out
+  end
+
+  def find_period_by_id id
+    self.periods.find{|p| p.id == id}
+  end
+
+  def build_period
+    self.periods << Calendar::Period.new(id: self.periods.count + 1)
+    self.periods.last
+  end
+
+  def destroy_period period
+    @periods = self.periods.select{|p| p.end != period.end || p.begin != period.begin}
+  end
 end
diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb
index 56ab722fe..8b3e4109b 100644
--- a/app/models/calendar/period.rb
+++ b/app/models/calendar/period.rb
@@ -10,6 +10,11 @@ class Calendar < ActiveRecord::Base
     validates_presence_of :begin, :end
     validate :check_end_greather_than_begin
 
+    alias_method :period_start, :begin
+    alias_method :period_end, :end
+    alias_method :period_start=, :begin=
+    alias_method :period_end=, :end=
+
     def check_end_greather_than_begin
       if self.begin && self.end && self.begin >= self.end
         errors.add(:base, I18n.t('calendars.errors.short_period'))
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 1a1972113..8113457ec 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -5,6 +5,7 @@ module Chouette
     include TimeTableRestrictions
     include ObjectidSupport
     include ApplicationDaysSupport
+    include TimetableSupport
 
     # FIXME http://jira.codehaus.org/browse/JRUBY-6358
     self.primary_key = "id"
@@ -82,72 +83,36 @@ module Chouette
       end
     end
 
-    def state_update state
-      update_attributes(self.class.state_permited_attributes(state))
-      self.tag_list    = state['tags'].collect{|t| t['name']}.join(', ')
-      self.calendar_id = nil unless state['calendar']
-
-      days = state['day_types'].split(',')
-      Date::DAYNAMES.map(&:underscore).each do |name|
-        prefix = human_attribute_name(name).first(2)
-        send("#{name}=", days.include?(prefix))
-      end
-
-      saved_dates = Hash[self.dates.collect{ |d| [d.id, d.date]}]
-      cmonth = Date.parse(state['current_periode_range'])
-
-      state['current_month'].each do |d|
-        date    = Date.parse(d['date'])
-        checked = d['include_date'] || d['excluded_date']
-        in_out  = d['include_date'] ? true : false
-
-        date_id = saved_dates.key(date)
-        time_table_date = self.dates.find(date_id) if date_id
+    def find_date_by_id id
+      self.dates.find id
+    end
 
-        next if !checked && !time_table_date
-        # Destroy date if no longer checked
-        next if !checked && time_table_date.destroy
+    def destroy_date date
+      date.destroy
+    end
 
-        # Create new date
-        unless time_table_date
-          time_table_date = self.dates.create({in_out: in_out, date: date})
-        end
-        # Update in_out
-        if in_out != time_table_date.in_out
-          time_table_date.update_attributes({in_out: in_out})
-        end
+    def update_in_out date, in_out
+      if in_out != date.in_out
+        date.update_attributes({in_out: in_out})
       end
-
-      self.state_update_periods state['time_table_periods']
-      self.save
     end
 
-    def state_update_periods state_periods
-      state_periods.each do |item|
-        period = self.periods.find(item['id']) if item['id']
-        next if period && item['deleted'] && period.destroy
-        period ||= self.periods.build
-
-        period.period_start = Date.parse(item['period_start'])
-        period.period_end   = Date.parse(item['period_end'])
+    def find_period_by_id id
+      self.periods.find id
+    end
 
-        if period.changed?
-          period.save
-          item['id'] = period.id
-        end
-      end
+    def build_period
+      periods.build
+    end
 
-      state_periods.delete_if {|item| item['deleted']}
+    def destroy_period period
+      period.destroy
     end
 
     def self.state_permited_attributes item
       item.slice('comment', 'color').to_hash
     end
 
-    def presenter
-      @presenter ||= ::TimeTablePresenter.new( self)
-    end
-
     def self.start_validity_period
       [Chouette::TimeTable.minimum(:start_date)].compact.min
     end
@@ -168,20 +133,6 @@ module Chouette
       self.save
     end
 
-    def month_inspect(date)
-      (date.beginning_of_month..date.end_of_month).map do |d|
-        {
-          day: I18n.l(d, format: '%A'),
-          date: d.to_s,
-          wday: d.wday,
-          wnumber: d.strftime("%W").to_s,
-          mday: d.mday,
-          include_date: include_in_dates?(d),
-          excluded_date: excluded_date?(d)
-        }
-      end
-    end
-
     def save_shortcuts
         shortcuts_update
         self.update_column(:start_date, start_date)
@@ -361,6 +312,17 @@ module Chouette
       days.sort
     end
 
+    def create_date in_out:, date:
+      self.dates.create in_out: in_out, date: date
+    end
+
+    def saved_dates
+      Hash[self.dates.collect{ |d| [d.id, d.date]}]
+    end
+
+    def all_dates
+      dates
+    end
 
     # produce a copy of periods without anyone overlapping or including another
     def optimize_overlapping_periods
diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb
index 425cba5bf..348436aa4 100644
--- a/app/models/concerns/application_days_support.rb
+++ b/app/models/concerns/application_days_support.rb
@@ -35,6 +35,10 @@ module ApplicationDaysSupport
     end
   end
 
+  def valid_day? wday
+    valid_days.include?(wday)
+  end
+
   def self.valid_days(int_day_types)
     # Build an array with day of calendar week (1-7, Monday is 1).
     [].tap do |valid_days|
diff --git a/app/models/concerns/date_support.rb b/app/models/concerns/date_support.rb
index fbfe19af1..5c66cb1a9 100644
--- a/app/models/concerns/date_support.rb
+++ b/app/models/concerns/date_support.rb
@@ -38,9 +38,12 @@ module DateSupport
           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
+          if date_range.cover?(date_value.value)
+            excluded_day = self.respond_to?(:valid_day?) && !self.valid_day?(date_value.value.wday)
+            unless excluded_day
+              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
       end
@@ -77,4 +80,4 @@ module DateSupport
 
     private :clear_date_values
   end
-end
\ No newline at end of file
+end
diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb
new file mode 100644
index 000000000..8c49723fe
--- /dev/null
+++ b/app/models/concerns/timetable_support.rb
@@ -0,0 +1,149 @@
+module TimetableSupport
+  extend ActiveSupport::Concern
+
+  def presenter
+    @presenter ||= ::TimeTablePresenter.new( self)
+  end
+
+  def periods_max_date
+    return nil if self.periods.empty?
+
+    min_start = self.periods.map(&:period_start).compact.min
+    max_end = self.periods.map(&:period_end).compact.max
+    result = nil
+
+    if max_end && min_start
+      max_end.downto( min_start) do |date|
+        if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
+            result = date
+            break
+        end
+      end
+    end
+    result
+  end
+
+  def periods_min_date
+    return nil if self.periods.empty?
+
+    min_start = self.periods.map(&:period_start).compact.min
+    max_end = self.periods.map(&:period_end).compact.max
+    result = nil
+
+    if max_end && min_start
+      min_start.upto(max_end) do |date|
+        if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
+            result = date
+            break
+        end
+      end
+    end
+    result
+  end
+
+  def bounding_dates
+    bounding_min = self.all_dates.select{|d| d.in_out}.map(&:date).compact.min
+    bounding_max = self.all_dates.select{|d| d.in_out}.map(&:date).compact.max
+
+    unless self.periods.empty?
+      bounding_min = periods_min_date if periods_min_date &&
+          (bounding_min.nil? || (periods_min_date < bounding_min))
+
+      bounding_max = periods_max_date if periods_max_date &&
+          (bounding_max.nil? || (bounding_max < periods_max_date))
+    end
+
+    [bounding_min, bounding_max].compact
+  end
+
+  def month_inspect(date)
+    (date.beginning_of_month..date.end_of_month).map do |d|
+      {
+        day: I18n.l(d, format: '%A'),
+        date: d.to_s,
+        wday: d.wday,
+        wnumber: d.strftime("%W").to_s,
+        mday: d.mday,
+        include_date: include_in_dates?(d),
+        excluded_date: excluded_date?(d)
+      }
+    end
+  end
+
+  def include_in_dates?(day)
+    self.dates.any?{ |d| d.date === day && d.in_out == true }
+  end
+
+  def excluded_date?(day)
+    self.dates.any?{ |d| d.date === day && d.in_out == false }
+  end
+
+  def include_in_overlap_dates?(day)
+    return false if self.excluded_date?(day)
+
+    self.all_dates.any?{ |d| d.date === day} \
+    && self.periods.any?{ |period| period.period_start <= day && day <= period.period_end && valid_days.include?(day.cwday) }
+  end
+
+  def include_in_periods?(day)
+    self.periods.any?{ |period| period.period_start <= day &&
+                                day <= period.period_end &&
+                                valid_days.include?(day.cwday) &&
+                                ! excluded_date?(day) }
+  end
+
+  def state_update_periods state_periods
+    state_periods.each do |item|
+      period = self.find_period_by_id(item['id']) if item['id']
+      next if period && item['deleted'] && self.destroy_period(period)
+      period ||= self.build_period
+
+      period.period_start = Date.parse(item['period_start'])
+      period.period_end   = Date.parse(item['period_end'])
+
+      period.save if period === ActiveRecord::Base && period.changed?
+
+      item['id'] = period.id
+    end
+
+    state_periods.delete_if {|item| item['deleted']}
+  end
+
+  def state_update state
+    update_attributes(self.class.state_permited_attributes(state))
+    self.tag_list    = state['tags'].collect{|t| t['name']}.join(', ') if state['tags']
+    self.calendar_id = nil if self.respond_to?(:calendar_id) && !state['calendar']
+
+    days = state['day_types'].split(',')
+    Date::DAYNAMES.map(&:underscore).each do |name|
+      prefix = human_attribute_name(name).first(2)
+      send("#{name}=", days.include?(prefix))
+    end
+
+    cmonth = Date.parse(state['current_periode_range'])
+
+    state['current_month'].each do |d|
+      date    = Date.parse(d['date'])
+      checked = d['include_date'] || d['excluded_date']
+      in_out  = d['include_date'] ? true : false
+
+      date_id = saved_dates.key(date)
+      time_table_date = self.find_date_by_id(date_id) if date_id
+
+      next if !checked && !time_table_date
+      # Destroy date if no longer checked
+      next if !checked && destroy_date(time_table_date)
+
+      # Create new date
+      unless time_table_date
+        time_table_date = self.create_date in_out: in_out, date: date
+      end
+      # Update in_out
+      self.update_in_out time_table_date, in_out
+    end
+
+    self.state_update_periods state['time_table_periods']
+    self.save
+  end
+
+end
diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim
deleted file mode 100644
index bf9f4f3a7..000000000
--- a/app/views/calendars/_form.html.slim
+++ /dev/null
@@ -1,53 +0,0 @@
-= simple_form_for [@workgroup, @calendar], html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f|
-  .row
-    .col-lg-12
-      = f.input :name
-      = f.input :short_name
-
-      - if policy(@calendar).share?
-        .form-group.has_switch
-          = f.label :shared, class: 'col-sm-4 col-xs-5 control-label'
-          = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8 col-xs-7'}
-
-  .separator
-  
-  .row
-    .col-lg-12
-      .subform
-        .nested-head
-          .wrapper
-            div
-              .form-group
-                label.control-label
-                  = Calendar.human_attribute_name(:date)
-            div
-
-        = f.simple_fields_for :date_values do |date_value|
-          = render 'date_value_fields', f: date_value
-
-        .links.nested-linker
-          = link_to_add_association t('simple_form.labels.calendar.add_a_date'), f, :date_values, class: 'btn btn-outline-primary'
-  
-  .separator
-  
-  .row
-    .col-lg-12
-      .subform
-        .nested-head
-          .wrapper
-            div
-              .form-group
-                label.control-label
-                  = t('simple_form.labels.calendar.ranges.begin')
-            div
-              .form-group
-                label.control-label
-                  = t('simple_form.labels.calendar.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.calendar.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary'
-
-  = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'calendar_form'
diff --git a/app/views/calendars/_form_advanced.html.slim b/app/views/calendars/_form_advanced.html.slim
new file mode 100644
index 000000000..b4154166b
--- /dev/null
+++ b/app/views/calendars/_form_advanced.html.slim
@@ -0,0 +1,8 @@
+#periods
+
+= javascript_tag do
+  | window.actionType = "#{raw params[:action]}";
+  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};
+  | window.timetablesUrl = "#{calendar_url(@calendar).html_safe}";
+
+= javascript_pack_tag 'calendars/edit.js'
diff --git a/app/views/calendars/_form_simple.html.slim b/app/views/calendars/_form_simple.html.slim
new file mode 100644
index 000000000..2f469ada7
--- /dev/null
+++ b/app/views/calendars/_form_simple.html.slim
@@ -0,0 +1,56 @@
+.row
+  .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1
+    = simple_form_for @calendar, html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f|
+      .row
+        .col-lg-12
+          = f.input :name
+          = f.input :short_name
+
+          - if policy(@calendar).share?
+            .form-group.has_switch
+              = f.label :shared, class: 'col-sm-4 col-xs-5 control-label'
+              = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8 col-xs-7'}
+
+      .separator
+
+      - unless has_feature?('application_days_on_calendars')
+        .row
+          .col-lg-12
+            .subform
+              .nested-head
+                .wrapper
+                  div
+                    .form-group
+                      label.control-label
+                        = Calendar.human_attribute_name(:date)
+                  div
+
+              = f.simple_fields_for :date_values do |date_value|
+                = render 'date_value_fields', f: date_value
+
+              .links.nested-linker
+                = link_to_add_association t('simple_form.labels.calendar.add_a_date'), f, :date_values, class: 'btn btn-outline-primary'
+
+        .separator
+
+      .row
+        .col-lg-12
+          .subform
+            .nested-head
+              .wrapper
+                div
+                  .form-group
+                    label.control-label
+                      = t('simple_form.labels.calendar.ranges.begin')
+                div
+                  .form-group
+                    label.control-label
+                      = t('simple_form.labels.calendar.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.calendar.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary'
+
+      = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'calendar_form'
diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim
index a1af5e257..79ab1f5d0 100644
--- a/app/views/calendars/edit.html.slim
+++ b/app/views/calendars/edit.html.slim
@@ -2,6 +2,7 @@
 - page_header_content_for @calendar
 .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'
+    - if has_feature?('application_days_on_calendars')
+      = render 'form_advanced'
+    - else
+      = render 'form_simple'
diff --git a/app/views/calendars/month.rabl b/app/views/calendars/month.rabl
new file mode 100644
index 000000000..1584db44c
--- /dev/null
+++ b/app/views/calendars/month.rabl
@@ -0,0 +1,9 @@
+object @calendar
+
+node do |tt|
+  {
+    name: I18n.l(@date, format: '%B'),
+    days: tt.month_inspect(@date),
+    day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join('')
+  }
+end
diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim
index c1e084913..5657a0c55 100644
--- a/app/views/calendars/new.html.slim
+++ b/app/views/calendars/new.html.slim
@@ -1,6 +1,4 @@
 - breadcrumb :calendars, @workgroup
 .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'
+    = render 'form_simple'
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
index cd2be2bd1..cec4f66a5 100644
--- a/app/views/calendars/show.html.slim
+++ b/app/views/calendars/show.html.slim
@@ -11,3 +11,14 @@
             'Organisation' => resource.organisation.name,
             Calendar.human_attribute_name(:dates) =>  resource.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe,
             Calendar.human_attribute_name(:date_ranges) => resource.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('
').html_safe }
+
+    - if has_feature?('application_days_on_calendars')
+      .row
+        .col-lg-12.mb-sm
+          .pagination.pull-right
+            = @year
+            .page_links
+              = link_to '', calendar_path(@calendar, year: (@year - 1)), class: 'previous_page'
+              = link_to '', calendar_path(@calendar, year: (@year + 1)), class: 'next_page'
+
+      = render 'time_tables/show_time_table', time_table: @calendar
diff --git a/app/views/calendars/show.rabl b/app/views/calendars/show.rabl
new file mode 100644
index 000000000..295d97528
--- /dev/null
+++ b/app/views/calendars/show.rabl
@@ -0,0 +1,22 @@
+object @calendar
+
+attributes :id
+node do |tt|
+  {
+    comment: tt.name,
+    time_table_bounding: tt.presenter.time_table_bounding,
+    day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join(''),
+    current_month: tt.month_inspect(Date.today),
+    periode_range: month_periode_enum(3),
+    current_periode_range: Date.today.beginning_of_month,
+    short_id: tt.object_id,
+  }
+end
+
+child(:periods, object_root: false, root: :time_table_periods) do
+  attributes :id, :period_start, :period_end
+end
+
+child(:all_dates, object_root: false, root: :time_table_dates) do
+  attributes :id, :date, :in_out
+end
diff --git a/app/views/time_tables/_show_time_table.html.slim b/app/views/time_tables/_show_time_table.html.slim
index ebfe9d283..102dbfad7 100644
--- a/app/views/time_tables/_show_time_table.html.slim
+++ b/app/views/time_tables/_show_time_table.html.slim
@@ -2,24 +2,10 @@
   - (1..12).each do |month|
     .col-lg-3.col-md-4.col-sm-4.col-xs-6
       = new_alt_calendar(year: @year, month: month, first_day_of_week: 1, calendar_title: "#{I18n.t("date.month_names")[month]}", show_today: false) do |d|
-        / - if @time_table.excluded_date?(d)
-        /   - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day excluded_date"}]
-        - if @time_table.include_in_overlap_dates?(d)
-          - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day overlaped_date", title: 'Voir'}]
-        - elsif @time_table.include_in_dates?(d)
-          - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_date", title: 'Voir'}]
-        - elsif @time_table.include_in_periods?(d)
-          - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_period", title: 'Voir'}]
-
-/ .row
-/   .col-lg-12
-/     / wip
-/     - if @time_table.dates.where("in_out = true").present?
-/       h3.time_table_dates = @time_table.human_attribute_name("dates")
-/       .dates.content
-/         == render "time_tables/dates"
-/ 
-/     - if @time_table.dates.where("in_out = false").present?
-/       h3.time_table_dates = @time_table.human_attribute_name("excluded_dates")
-/       .excluded_dates.content
-/         == render "time_tables/excluded_dates"
+        - edit_url = [:edit, @referential, time_table].compact
+        - if time_table.include_in_overlap_dates?(d)
+          - [link_to(d.mday, edit_url), {class: "day overlaped_date", title: 'Voir'}]
+        - elsif time_table.include_in_dates?(d)
+          - [link_to(d.mday, edit_url), {class: "day selected_date", title: 'Voir'}]
+        - elsif time_table.include_in_periods?(d)
+          - [link_to(d.mday, edit_url), {class: "day selected_period", title: 'Voir'}]
diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim
index 6d15323f3..e038b73ca 100644
--- a/app/views/time_tables/show.html.slim
+++ b/app/views/time_tables/show.html.slim
@@ -24,4 +24,4 @@
             = link_to '', referential_time_table_path(@referential, @time_table, year: (@year - 1)), class: 'previous_page'
             = link_to '', referential_time_table_path(@referential, @time_table, year: (@year + 1)), class: 'next_page'
 
-    = render 'show_time_table'
+    = render 'show_time_table', time_table: @time_table
diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml
index a2110d053..c3df413af 100644
--- a/config/locales/calendars.en.yml
+++ b/config/locales/calendars.en.yml
@@ -69,6 +69,13 @@ en:
         dates: Dates
         shared: Shared
         organisation: Organisation
+        monday: "Monday"
+        tuesday: "Tuesday"
+        wednesday: "Wednesday"
+        thursday: "Thursday"
+        friday: "Friday"
+        saturday: "Saturday"
+        sunday: "Sunday"
     errors:
       models:
         calendar:
diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml
index f9eaf1be5..6fd265925 100644
--- a/config/locales/calendars.fr.yml
+++ b/config/locales/calendars.fr.yml
@@ -69,6 +69,13 @@ fr:
         dates: Dates
         shared: Partagé
         organisation: Organisation
+        monday: "Lundi"
+        tuesday: "Mardi"
+        wednesday: "Mercredi"
+        thursday: "Jeudi"
+        friday: "Vendredi"
+        saturday: "Samedi"
+        sunday: "Dimanche"
     errors:
       models:
         calendar:
diff --git a/config/routes.rb b/config/routes.rb
index 07370ee6d..5fc39ba92 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -111,9 +111,13 @@ ChouetteIhm::Application.routes.draw do
     resources :companies
     resources :networks
   end
+  
   resources :workgroups do
     resources :calendars do
       get :autocomplete, on: :collection, controller: 'autocomplete_calendars'
+      member do
+        get 'month', defaults: { format: :json }
+      end
     end
   end
 
@@ -121,7 +125,7 @@ ChouetteIhm::Application.routes.draw do
     resources :autocomplete_stop_areas, only: [:show, :index] do
       get 'around', on: :member
     end
-    resources :autocomplete_purchase_windows, only: [:index] 
+    resources :autocomplete_purchase_windows, only: [:index]
     get :select_compliance_control_set
     post :validate, on: :member
     resources :autocomplete_time_tables, only: [:index]
diff --git a/db/migrate/20180124124215_add_excluded_dates_to_calendars.rb b/db/migrate/20180124124215_add_excluded_dates_to_calendars.rb
new file mode 100644
index 000000000..b98b50717
--- /dev/null
+++ b/db/migrate/20180124124215_add_excluded_dates_to_calendars.rb
@@ -0,0 +1,5 @@
+class AddExcludedDatesToCalendars < ActiveRecord::Migration
+  def change
+    add_column :calendars, :excluded_dates, :date, array: true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 231bea9ba..b696cfc98 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,6 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-
 ActiveRecord::Schema.define(version: 20180126134944) do
 
   # These are extensions that must be enabled in order to support this database
diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb
index 86ce565cd..3cffd0f8a 100644
--- a/spec/models/calendar_spec.rb
+++ b/spec/models/calendar_spec.rb
@@ -32,7 +32,11 @@ RSpec.describe Calendar, :type => :model do
 
   describe 'validations' do
     it 'validates that dates and date_ranges do not overlap' do
-      expect(build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])).to_not be_valid
+      expect(build(:calendar, dates: [Date.today.beginning_of_week], date_ranges: [Date.today.beginning_of_week..Date.today])).to_not be_valid
+    end
+
+    it 'validates that dates and date_ranges do not overlap but allow for days not in the list' do
+      expect(build(:calendar, dates: [Date.today.beginning_of_week], date_ranges: [Date.today.beginning_of_week..Date.today], int_day_types: Calendar::THURSDAY)).to be_valid
     end
 
     it 'validates that there are no duplicates in dates' do
@@ -52,4 +56,108 @@ RSpec.describe Calendar, :type => :model do
     end
   end
 
+  describe "Update state" do
+    def calendar_to_state calendar
+      calendar.slice('id').tap do |item|
+        item['comment'] = calendar.name
+        item['day_types'] = "Di,Lu,Ma,Me,Je,Ve,Sa"
+        item['current_month'] = calendar.month_inspect(Date.today.beginning_of_month)
+        item['current_periode_range'] = Date.today.beginning_of_month.to_s
+        item['time_table_periods'] = calendar.periods.map{|p| {'id': p.id, 'period_start': p.period_start.to_s, 'period_end': p.period_end.to_s}}
+      end
+    end
+
+    subject(:calendar){ create :calendar }
+    let(:state) { calendar_to_state subject }
+
+    it 'should update time table periods association' do
+      period = state['time_table_periods'].first
+      period['period_start'] = (Date.today - 1.month).to_s
+      period['period_end']   = (Date.today + 1.month).to_s
+
+      subject.state_update_periods state['time_table_periods']
+      ['period_end', 'period_start'].each do |prop|
+        expect(subject.reload.periods.first.send(prop).to_s).to eq(period[prop])
+      end
+    end
+
+    it 'should create time table periods association' do
+      state['time_table_periods'] << {
+        'id' => false,
+        'period_start' => (Date.today + 1.year).to_s,
+        'period_end' => (Date.today + 2.year).to_s
+      }
+
+      expect {
+        subject.state_update_periods state['time_table_periods']
+      }.to change {subject.periods.count}.by(1)
+      expect(state['time_table_periods'].last['id']).to eq subject.reload.periods.last.id
+    end
+
+    it 'should delete time table periods association' do
+      state['time_table_periods'].first['deleted'] = true
+      expect {
+        subject.state_update_periods state['time_table_periods']
+      }.to change {subject.periods.count}.by(-1)
+    end
+
+    it 'should update name' do
+      state['comment'] = "Edited timetable name"
+      subject.state_update state
+      expect(subject.reload.name).to eq state['comment']
+    end
+
+    it 'should update day_types' do
+      state['day_types'] = "Di,Lu,Je,Ma"
+      subject.state_update state
+      expect(subject.reload.valid_days).to include(7, 1, 4, 2)
+      expect(subject.reload.valid_days).not_to include(3, 5, 6)
+    end
+
+    it 'should delete date if date is set to neither include or excluded date' do
+      updated = state['current_month'].map do |day|
+        day['include_date'] = false if day['include_date']
+      end
+
+      expect {
+        subject.state_update state
+      }.to change {subject.dates.count}.by(-updated.compact.count)
+    end
+
+    it 'should update date if date is set to excluded date' do
+        updated = state['current_month'].map do |day|
+          next unless day['include_date']
+          day['include_date']  = false
+          day['excluded_date'] = true
+        end
+
+        subject.state_update state
+        expect(subject.reload.excluded_days.count).to eq (updated.compact.count)
+    end
+
+    it 'should create new include date' do
+      day  = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date'] }
+      date = Date.parse(day['date'])
+      day['include_date'] = true
+      expect(subject.included_days).not_to include(date)
+
+      expect {
+        subject.state_update state
+      }.to change {subject.dates.count}.by(1)
+      expect(subject.reload.included_days).to include(date)
+    end
+
+    it 'should create new exclude date' do
+      day  = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date']}
+      date = Date.parse(day['date'])
+      day['excluded_date'] = true
+      expect(subject.excluded_days).not_to include(date)
+
+      expect {
+        subject.state_update state
+      }.to change {subject.all_dates.count}.by(1)
+      expect(subject.reload.excluded_days).to include(date)
+    end
+  end
+
 end
-- 
cgit v1.2.3
From 29656560af22c0a13daf2638717a75c695fc21a6 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 24 Jan 2018 17:18:31 +0100
Subject: Refs #5682; Fix timetables bulk update
---
 app/models/concerns/timetable_support.rb | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb
index 8c49723fe..d2bc99d51 100644
--- a/app/models/concerns/timetable_support.rb
+++ b/app/models/concerns/timetable_support.rb
@@ -100,8 +100,7 @@ module TimetableSupport
 
       period.period_start = Date.parse(item['period_start'])
       period.period_end   = Date.parse(item['period_end'])
-
-      period.save if period === ActiveRecord::Base && period.changed?
+      period.save if period.is_a?(ActiveRecord::Base) && period.changed?
 
       item['id'] = period.id
     end
-- 
cgit v1.2.3
From 1aa2965d049b6ec1c56a580bcb4dac1b50c72b4e Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 15:47:06 +0100
Subject: Refs #5796: Add some legroom in the JourneyPatterns editor
---
 app/assets/stylesheets/modules/_jp_collection.sass           | 7 ++++++-
 app/javascript/journey_patterns/components/JourneyPattern.js | 2 +-
 2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/assets/stylesheets/modules/_jp_collection.sass b/app/assets/stylesheets/modules/_jp_collection.sass
index b4370a5ac..46ea3fb6e 100644
--- a/app/assets/stylesheets/modules/_jp_collection.sass
+++ b/app/assets/stylesheets/modules/_jp_collection.sass
@@ -121,6 +121,11 @@
           .totals
             color: $blue
             padding-top: 4px
+            margin-left: -5px
+            margin-right: -5px
+            span
+              white-space: nowrap
+              padding: 0 5px
             i
               padding-right: 3px
 
@@ -219,7 +224,7 @@
 
               input
                 display: inline-block
-                width: 40px
+                width: 50px
                 border: none
                 margin-right: 5px
                 color: black
diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js
index 01734f3a3..35765b99a 100644
--- a/app/javascript/journey_patterns/components/JourneyPattern.js
+++ b/app/javascript/journey_patterns/components/JourneyPattern.js
@@ -109,7 +109,7 @@ export default class JourneyPattern extends Component{
     }
     else{
       let hours = parseInt(time/60)
-      return hours + " h " + (time - 60*hours) + " min"
+      return hours + " h " + (time - 60*hours)
     }
   }
 
-- 
cgit v1.2.3
From e0288495f2d30154ac3b9ba462aa23c0f07fd205 Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 31 Jan 2018 16:09:55 +0100
Subject: Refs #5796; Fix dropdown menu
---
 app/assets/stylesheets/components/_buttons.sass | 77 +++++++++++++------------
 app/assets/stylesheets/components/_tables.sass  |  2 +-
 2 files changed, 40 insertions(+), 39 deletions(-)
diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass
index 93f9907a5..2881cee3e 100644
--- a/app/assets/stylesheets/components/_buttons.sass
+++ b/app/assets/stylesheets/components/_buttons.sass
@@ -142,47 +142,48 @@ table, .table
         margin: 0
         border-radius: 0
         box-shadow: 0 0 3px rgba($darkgrey, 0.25)
-        > ul
-          padding: 0
-          margin: 0
-          > li > a, > li > button
-            padding: 5px 15px
-            white-space: nowrap
-            padding: 5px 15px
-            font-weight: normal
-            line-height: $line-height
-            display: block
-            font-size: 14px
-            &:hover, &:focus
-              text-decoration: none
-              background-color: whitesmoke
-              outline: none
 
-          &:not(:first-child)
+        & > ul:not(:first-child)
+          position: relative
+          margin-top: 11px
+          &:before
+            content: ''
+            display: block
+            position: absolute
+            left: 15px
+            right: 15px
+            top: -6px
+            height: 1px
+            background-color: $grey
+
+      ul.dropdown-menu, .dropdown-menu > ul
+        padding: 0
+        margin: 0
+        > li > a, > li > button
+          padding: 5px 15px
+          white-space: nowrap
+          padding: 5px 15px
+          font-weight: normal
+          line-height: $line-height
+          display: block
+          font-size: 14px
+          &:hover, &:focus
+            text-decoration: none
+            background-color: whitesmoke
+            outline: none
+
+        > li.delete-action
+          > a, > button
+            display: block
             position: relative
-            margin-top: 11px
-            &:before
-              content: ''
-              display: block
-              position: absolute
-              left: 15px
-              right: 15px
-              top: -6px
-              height: 1px
-              background-color: $grey
-
-          > li.delete-action
+            .fa:first-child
+              margin-right: 0.5em
+
+          & + li.delete-action
             > a, > button
-              display: block
-              position: relative
-              .fa:first-child
-                margin-right: 0.5em
-
-            & + li.delete-action
-              > a, > button
-                margin-top: 0
-                &:before
-                  display: none
+              margin-top: 0
+              &:before
+                display: none
 
 
   &.table-2entries .t2e-item
diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass
index 119a38c07..35e1122f3 100644
--- a/app/assets/stylesheets/components/_tables.sass
+++ b/app/assets/stylesheets/components/_tables.sass
@@ -372,7 +372,7 @@
       white-space: nowrap
       // border-right: 1px solid rgba($grey, 0.5)
       max-width: 100%
-      min-width: 280px
+      min-width: 330px
       padding-right: 1px
 
     .t2e-item
-- 
cgit v1.2.3
From 890d9997258f0e522c862b8443da12113126d1fe Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 1 Feb 2018 10:34:40 +0100
Subject: Refs #5798 @3h; Show return journeys on the journeys' editor.
---
 app/assets/stylesheets/components/_forms.sass      | 11 +++-
 app/controllers/vehicle_journeys_controller.rb     | 65 ++++++++++++----------
 app/javascript/helpers/stop_area_header_manager.js |  5 ++
 app/javascript/packs/vehicle_journeys/index.js     |  3 +-
 app/javascript/vehicle_journeys/actions/index.js   | 17 ++++--
 app/javascript/vehicle_journeys/components/App.js  |  1 +
 .../vehicle_journeys/components/VehicleJourney.js  |  6 +-
 .../vehicle_journeys/components/VehicleJourneys.js | 41 +++++++++++---
 .../components/tools/EditVehicleJourney.js         |  8 ++-
 .../containers/VehicleJourneysList.js              |  8 ++-
 .../containers/tools/EditVehicleJourney.js         |  1 +
 app/javascript/vehicle_journeys/reducers/index.js  |  3 +
 .../reducers/returnVehicleJourneys.js              | 11 ++++
 app/models/chouette/journey_pattern.rb             |  3 +-
 app/views/vehicle_journeys/index.html.slim         |  6 ++
 15 files changed, 134 insertions(+), 55 deletions(-)
 create mode 100644 app/javascript/vehicle_journeys/reducers/returnVehicleJourneys.js
diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass
index 998703ef0..ba5bbbde1 100644
--- a/app/assets/stylesheets/components/_forms.sass
+++ b/app/assets/stylesheets/components/_forms.sass
@@ -256,8 +256,15 @@ table, .table
   &.table-2entries .t2e-item
     > .th
       position: relative
-
-      > .checkbox
+      > .st_action
+        list-style-type: none
+        button
+          border-radius: 50%
+          background: $blue
+          color: white
+          border: none
+
+      > .checkbox, > .st_action
         position: absolute
         right: 0
         top: 0
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index ed6ba6ed1..e031e4952 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -50,36 +50,8 @@ class VehicleJourneysController < ChouetteController
       format.html do
         load_missions
         load_custom_fields
-        @stop_points_list = []
-        @stop_points_list = route.stop_points.includes(:stop_area).map do |sp|
-          {
-            :id => sp.stop_area.id,
-            :route_id => sp.try(:route_id),
-            :object_id => sp.try(:objectid),
-            :position => sp.try(:position),
-            :for_boarding => sp.try(:for_boarding),
-            :for_alighting => sp.try(:for_alighting),
-            :name => sp.stop_area.try(:name),
-            :time_zone_offset => sp.stop_area.try(:time_zone_offset),
-            :time_zone_formatted_offset => sp.stop_area.try(:time_zone_formatted_offset),
-            :zip_code => sp.stop_area.try(:zip_code),
-            :city_name => sp.stop_area.try(:city_name),
-            :comment => sp.stop_area.try(:comment),
-            :area_type => sp.stop_area.try(:area_type),
-            :area_type_i18n => I18n.t(sp.stop_area.try(:area_type), scope: 'area_types.label'),
-            :area_kind => sp.stop_area.try(:kind),
-            :stop_area_id => sp.stop_area_id,
-            :registration_number => sp.stop_area.try(:registration_number),
-            :nearest_topic_name => sp.stop_area.try(:nearest_topic_name),
-            :fare_code => sp.stop_area.try(:fare_code),
-            :longitude => sp.stop_area.try(:longitude),
-            :latitude => sp.stop_area.try(:latitude),
-            :long_lat_type => sp.stop_area.try(:long_lat_type),
-            :country_code => sp.stop_area.try(:country_code),
-            :country_name => sp.stop_area.try(:country_name),
-            :street_name => sp.stop_area.try(:street_name)
-          }
-        end
+        @stop_points_list = map_stop_points(route.stop_points)
+        @return_stop_points_list = map_stop_points(route.opposite_route&.stop_points) if has_feature?(:vehicle_journeys_return_route)
         @transport_mode = route.line['transport_mode']
         @transport_submode = route.line['transport_submode']
 
@@ -185,6 +157,39 @@ class VehicleJourneysController < ChouetteController
     @custom_fields = current_workgroup.custom_fields_definitions
   end
 
+  def map_stop_points points
+    (points&.includes(:stop_area) || []).map do |sp|
+      {
+        :id => sp.stop_area.id,
+        :route_id => sp.try(:route_id),
+        :object_id => sp.try(:objectid),
+        :area_object_id => sp.stop_area.try(:objectid),
+        :position => sp.try(:position),
+        :for_boarding => sp.try(:for_boarding),
+        :for_alighting => sp.try(:for_alighting),
+        :name => sp.stop_area.try(:name),
+        :time_zone_offset => sp.stop_area.try(:time_zone_offset),
+        :time_zone_formatted_offset => sp.stop_area.try(:time_zone_formatted_offset),
+        :zip_code => sp.stop_area.try(:zip_code),
+        :city_name => sp.stop_area.try(:city_name),
+        :comment => sp.stop_area.try(:comment),
+        :area_type => sp.stop_area.try(:area_type),
+        :area_type_i18n => I18n.t(sp.stop_area.try(:area_type), scope: 'area_types.label'),
+        :area_kind => sp.stop_area.try(:kind),
+        :stop_area_id => sp.stop_area_id,
+        :registration_number => sp.stop_area.try(:registration_number),
+        :nearest_topic_name => sp.stop_area.try(:nearest_topic_name),
+        :fare_code => sp.stop_area.try(:fare_code),
+        :longitude => sp.stop_area.try(:longitude),
+        :latitude => sp.stop_area.try(:latitude),
+        :long_lat_type => sp.stop_area.try(:long_lat_type),
+        :country_code => sp.stop_area.try(:country_code),
+        :country_name => sp.stop_area.try(:country_name),
+        :street_name => sp.stop_area.try(:street_name)
+      }
+    end
+  end
+
   def load_missions
     @all_missions = route.journey_patterns.count > 10 ? [] : route.journey_patterns.map do |item|
       {
diff --git a/app/javascript/helpers/stop_area_header_manager.js b/app/javascript/helpers/stop_area_header_manager.js
index 2c820caf9..5b18e2f63 100644
--- a/app/javascript/helpers/stop_area_header_manager.js
+++ b/app/javascript/helpers/stop_area_header_manager.js
@@ -42,6 +42,11 @@ export default class StopAreaHeaderManager {
     let index = this.ids_list.indexOf(object_id)
     let sp = this.stopPointsList[index]
     let previousBreakpoint = this.stopPointsList[index - 1]
+    if(sp == undefined){
+      console.log("STOP_POINT NOT FOUND: " + object_id)
+      console.log("AVAILABLE IDS:" + this.ids_list)
+      return
+    }
     if(index == 0 || (sp[attribute_to_check] != previousBreakpoint[attribute_to_check])){
       showHeadline = true
       headline = this.hasFeature('long_distance_routes') ? sp.country_name : sp.city_name
diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js
index aa5738d59..e6867cb17 100644
--- a/app/javascript/packs/vehicle_journeys/index.js
+++ b/app/javascript/packs/vehicle_journeys/index.js
@@ -60,6 +60,7 @@ var initialState = {
   },
   vehicleJourneys: [],
   stopPointsList: window.stopPoints,
+  returnStopPointsList: window.returnStopPoints,
   pagination: {
     page : 1,
     totalCount: 0,
@@ -99,7 +100,7 @@ let store = createStore(
 
 render(
   
-    
+    
   ,
   document.getElementById('vehicle_journeys_wip')
 )
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index 8970c6025..74a333b53 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -13,8 +13,8 @@ const actions = {
   exitEditMode: () => ({
     type: "EXIT_EDIT_MODE"
   }),
-  receiveVehicleJourneys : (json) => ({
-    type: "RECEIVE_VEHICLE_JOURNEYS",
+  receiveVehicleJourneys : (json, returnJourneys) => ({
+    type: (returnJourneys ? "RECEIVE_RETURN_VEHICLE_JOURNEYS" : "RECEIVE_VEHICLE_JOURNEYS"),
     json
   }),
   receiveErrors : (json) => ({
@@ -290,10 +290,17 @@ const actions = {
     type: 'RECEIVE_TOTAL_COUNT',
     total
   }),
-  fetchVehicleJourneys : (dispatch, currentPage, nextPage, queryString) => {
+  fetchVehicleJourneys : (dispatch, currentPage, nextPage, queryString, url) => {
+    let returnJourneys = false
     if(currentPage == undefined){
       currentPage = 1
     }
+    if(url == undefined){
+      url = window.location.pathname
+    }
+    else{
+      returnJourneys = true
+    }
     let vehicleJourneys = []
     let page
     switch (nextPage) {
@@ -315,7 +322,7 @@ const actions = {
       str = '.json?page=' + page.toString()
       sep = '&'
     }
-    let urlJSON = window.location.pathname + str
+    let urlJSON = url + str
     if (queryString){
       urlJSON = urlJSON + sep + queryString
     }
@@ -375,7 +382,7 @@ const actions = {
             )
           }
           window.currentItemsLength = vehicleJourneys.length
-          dispatch(actions.receiveVehicleJourneys(vehicleJourneys))
+          dispatch(actions.receiveVehicleJourneys(vehicleJourneys, returnJourneys))
           dispatch(actions.receiveTotalCount(json.total))
         }
       })
diff --git a/app/javascript/vehicle_journeys/components/App.js b/app/javascript/vehicle_journeys/components/App.js
index 44559c7c6..5ac284438 100644
--- a/app/javascript/vehicle_journeys/components/App.js
+++ b/app/javascript/vehicle_journeys/components/App.js
@@ -22,6 +22,7 @@ export default function App() {
 
       
       
+      {window.returnRouteUrl && 
}
 
       
         
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js
index 2b5783dda..e7d4b5b30 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js
@@ -1,6 +1,7 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import actions from '../actions'
+import EditVehicleJourney from '../containers/tools/EditVehicleJourney'
 
 export default class VehicleJourney extends Component {
   constructor(props) {
@@ -80,7 +81,7 @@ export default class VehicleJourney extends Component {
               {purchase_windows.length > 3 &&  + {purchase_windows.length - 3}}
             
           }
-          
+          {!this.props.disabled && 
             
             
-          
+          
}
+          {this.props.disabled && 
}
         
 
         {this.props.value.vehicle_journey_at_stops.map((vj, i) =>
           
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
index 256ca81f9..ae852b35a 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
@@ -8,14 +8,36 @@ export default class VehicleJourneys extends Component {
   constructor(props){
     super(props)
     this.headerManager = new StopAreaHeaderManager(
-      _.map(this.props.stopPointsList, (sp)=>{return sp.object_id}),
-      this.props.stopPointsList,
+      _.map(this.stopPoints(), (sp)=>{return sp.object_id}),
+      this.stopPoints(),
       this.props.filters.features
     )
   }
 
+  isReturn() {
+    return this.props.routeUrl != undefined
+  }
+
+  vehicleJourneysList() {
+    if(this.isReturn()){
+      return this.props.returnVehicleJourneys
+    }
+    else{
+      return this.props.vehicleJourneys
+    }
+  }
+
+  stopPoints() {
+    if(this.isReturn()){
+      return this.props.returnStopPointsList
+    }
+    else{
+      return this.props.stopPointsList
+    }
+  }
+
   componentDidMount() {
-    this.props.onLoadFirstPage(this.props.filters)
+    this.props.onLoadFirstPage(this.props.filters, this.props.routeUrl)
   }
 
   hasFeature(key) {
@@ -89,10 +111,10 @@ export default class VehicleJourneys extends Component {
               
             )}
 
-            { this.props.vehicleJourneys.errors && this.props.vehicleJourneys.errors.length && _.some(this.props.vehicleJourneys, 'errors') && (
+            { this.vehicleJourneysList().errors && this.vehicleJourneysList().errors.length && _.some(this.vehicleJourneysList(), 'errors') && (
               
                 Erreur : 
-                {this.props.vehicleJourneys.map((vj, index) =>
+                {this.vehicleJourneysList().map((vj, index) =>
                   vj.errors && vj.errors.map((err, i) => {
                     return (
                       
@@ -104,7 +126,7 @@ export default class VehicleJourneys extends Component {
               
 
             )}
 
-             0) ? '' : ' no_result')}>
+            
 0) ? '' : ' no_result')}>
               
                 
                   ID course
@@ -114,7 +136,7 @@ export default class VehicleJourneys extends Component {
                   
Calendriers
                   { this.hasFeature('purchase_windows') && 
Calendriers Commerciaux
 }
                 
 
-                {this.props.stopPointsList.map((sp, i) =>{
+                {this.stopPoints().map((sp, i) =>{
                   return (
                     
                       {this.headerManager.stopPointHeader(sp.object_id)}
@@ -125,17 +147,18 @@ export default class VehicleJourneys extends Component {
 
               
                 
-                  {this.props.vehicleJourneys.map((vj, index) =>
+                  {this.vehicleJourneysList().map((vj, index) =>
                     
                   )}
                 
diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
index 2893422f8..f814c0459 100644
--- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -23,6 +23,10 @@ export default class EditVehicleJourney extends Component {
     }
   }
 
+  getSelected() {
+    return this.props.vehicleJourney ? [this.props.vehicleJourney] : actions.getSelected(this.props.vehicleJourneys)
+  }
+
   render() {
     if(this.props.status.isFetching == true) {
       return false
@@ -35,10 +39,10 @@ export default class EditVehicleJourney extends Component {
         
- 
           
diff --git a/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js b/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js
index 38ab9f6d3..76d1c3a78 100644
--- a/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js
+++ b/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js
@@ -6,17 +6,19 @@ const mapStateToProps = (state) => {
   return {
     editMode: state.editMode,
     vehicleJourneys: state.vehicleJourneys,
+    returnVehicleJourneys: state.returnVehicleJourneys,
     status: state.status,
     filters: state.filters,
-    stopPointsList: state.stopPointsList
+    stopPointsList: state.stopPointsList,
+    returnStopPointsList: state.returnStopPointsList
   }
 }
 
 const mapDispatchToProps = (dispatch) => {
   return {
-    onLoadFirstPage: (filters) =>{
+    onLoadFirstPage: (filters, routeUrl) =>{
       dispatch(actions.fetchingApi())
-      actions.fetchVehicleJourneys(dispatch, undefined, undefined, filters.queryString)
+      actions.fetchVehicleJourneys(dispatch, undefined, undefined, filters.queryString, routeUrl)
     },
     onUpdateTime: (e, subIndex, index, timeUnit, isDeparture, isArrivalsToggled) => {
       dispatch(actions.updateTime(e.target.value, subIndex, index, timeUnit, isDeparture, isArrivalsToggled))
diff --git a/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
index c2eabcc10..a851b6e7d 100644
--- a/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
@@ -18,6 +18,7 @@ const mapDispatchToProps = (dispatch) => {
       dispatch(actions.closeModal())
     },
     onOpenEditModal: (vj) =>{
+      console.log({vj})
       dispatch(actions.openEditModal(vj))
     },
     onEditVehicleJourney: (data, selectedCompany) =>{
diff --git a/app/javascript/vehicle_journeys/reducers/index.js b/app/javascript/vehicle_journeys/reducers/index.js
index 1963f7c6d..95ac9c7e1 100644
--- a/app/javascript/vehicle_journeys/reducers/index.js
+++ b/app/javascript/vehicle_journeys/reducers/index.js
@@ -1,5 +1,6 @@
 import { combineReducers } from 'redux'
 import vehicleJourneys from './vehicleJourneys'
+import returnVehicleJourneys from './returnVehicleJourneys'
 import pagination from './pagination'
 import modal from './modal'
 import status from './status'
@@ -11,12 +12,14 @@ import custom_fields from './custom_fields'
 
 const vehicleJourneysApp = combineReducers({
   vehicleJourneys,
+  returnVehicleJourneys,
   pagination,
   modal,
   status,
   filters,
   editMode,
   stopPointsList,
+  returnStopPointsList: stopPointsList,
   missions,
   custom_fields
 })
diff --git a/app/javascript/vehicle_journeys/reducers/returnVehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/returnVehicleJourneys.js
new file mode 100644
index 000000000..db3c71d17
--- /dev/null
+++ b/app/javascript/vehicle_journeys/reducers/returnVehicleJourneys.js
@@ -0,0 +1,11 @@
+import _ from 'lodash'
+import actions from '../actions'
+
+export default function returnVehicleJourneys(state = [], action) {
+  switch (action.type) {
+    case 'RECEIVE_RETURN_VEHICLE_JOURNEYS':
+      return [...action.json]
+        default:
+      return state
+  }
+}
diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb
index 55faaf997..a81f4e9ce 100644
--- a/app/models/chouette/journey_pattern.rb
+++ b/app/models/chouette/journey_pattern.rb
@@ -160,12 +160,13 @@ module Chouette
 
     def full_schedule?
       full = true
-      stop_points.inject(nil) do |start, finish|
+      stop_points.order(:position).inject(nil) do |start, finish|
         next finish unless start.present?
         costs = costs_between(start, finish)
         full = false unless costs.present?
         full = false unless costs[:distance] && costs[:distance] > 0
         full = false unless costs[:time] && costs[:time] > 0
+        p "#{start.stop_area_id}-#{finish.stop_area_id}" unless full
         finish
       end
       full
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index ce56f9332..caa8450a0 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -17,6 +17,7 @@
 = javascript_tag do
   | window.route_id = #{params[:route_id]};
   | window.stopPoints = #{(@stop_points_list.to_json).html_safe};
+  | window.returnStopPoints = #{(@return_stop_points_list.to_json).html_safe};
   | window.jpOrigin = #{(@jp_origin.present? ? @jp_origin.attributes.update({full_schedule: @jp_origin.full_schedule?}).to_json  : "null").html_safe};
   | window.jpOriginStopPoints = #{(@jp_origin_stop_points.to_json).html_safe};
   | window.transportMode = #{(@transport_mode.to_json).html_safe};
@@ -30,4 +31,9 @@
   | window.custom_fields = #{(@custom_fields.to_json).html_safe};
   | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};
 
+- if has_feature?(:vehicle_journeys_return_route)
+  = javascript_tag do
+    | window.returnRouteUrl = "#{(@route.opposite_route && url_for([@referential, @route.line, @route.opposite_route, :vehicle_journeys]) || "").html_safe}";
+
+
 = javascript_pack_tag 'vehicle_journeys/index.js'
-- 
cgit v1.2.3
From f78b950171da76c9306a4bf927ca8e83308ce262 Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 1 Feb 2018 10:37:44 +0100
Subject: :fire: log
---
 app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js | 1 -
 app/models/chouette/journey_pattern.rb                                 | 1 -
 2 files changed, 2 deletions(-)
diff --git a/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
index a851b6e7d..c2eabcc10 100644
--- a/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/containers/tools/EditVehicleJourney.js
@@ -18,7 +18,6 @@ const mapDispatchToProps = (dispatch) => {
       dispatch(actions.closeModal())
     },
     onOpenEditModal: (vj) =>{
-      console.log({vj})
       dispatch(actions.openEditModal(vj))
     },
     onEditVehicleJourney: (data, selectedCompany) =>{
diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb
index a81f4e9ce..aa9fdb810 100644
--- a/app/models/chouette/journey_pattern.rb
+++ b/app/models/chouette/journey_pattern.rb
@@ -166,7 +166,6 @@ module Chouette
         full = false unless costs.present?
         full = false unless costs[:distance] && costs[:distance] > 0
         full = false unless costs[:time] && costs[:time] > 0
-        p "#{start.stop_area_id}-#{finish.stop_area_id}" unless full
         finish
       end
       full
-- 
cgit v1.2.3
From 3f538cbd57d42b47f7c19e22d3a7bedc980a69ca Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 11:00:52 +0100
Subject: Change path in capistrano default_env to make bundle available for
 webpacker compiler. Refs #5803
---
 config/deploy.rb | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/config/deploy.rb b/config/deploy.rb
index d541f2581..1ea1613de 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -7,10 +7,12 @@ set :scm, :git
 set :repository,  "git@github.com:AF83/stif-boiv.git"
 set :deploy_to, "/var/www/stif-boiv"
 set :use_sudo, false
+set :ruby_version, "2.3.0"
 default_run_options[:pty] = true
 set :group_writable, true
-set :bundle_cmd, "/var/lib/gems/2.3.0/bin/bundle"
-set :rake, "#{bundle_cmd} exec rake"
+set :bundle_cmd, "/var/lib/gems/#{ruby_version}/bin/bundle"
+oset :rake, "#{bundle_cmd} exec rake"
+set :default_env, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
 
 set :keep_releases, -> { fetch(:kept_releases, 5) }
 after "deploy:restart", "deploy:cleanup"
@@ -29,7 +31,7 @@ require 'whenever/capistrano'
 #after 'deploy:finalize_update', 'npm:install'
 
 # Whenever
-set :whenever_variables, ->{ "'environment=#{fetch :whenever_environment}&bundle_command=bin/bundle exec&additionnal_path=/var/lib/gems/2.3.0/bin'" } # invoke bin/bundle to use 'correct' ruby environment
+set :whenever_variables, ->{ "'environment=#{fetch :whenever_environment}&bundle_command=bin/bundle exec&additionnal_path=/var/lib/gems/#{ruby_version}/bin'" } # invoke bin/bundle to use 'correct' ruby environment
 
 set :whenever_command, "sudo /usr/local/sbin/whenever-sudo" # use sudo to change www-data crontab
 set :whenever_user, "www-data" # use www-data crontab
-- 
cgit v1.2.3
From 38cadb07b47648c3d9ea2424a4b62229d0f468d3 Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 11:02:46 +0100
Subject: Add env variable to disable ci:jest if needed. Refs #5802
---
 lib/tasks/ci.rake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake
index 3e73b7a3b..82619d04b 100644
--- a/lib/tasks/ci.rake
+++ b/lib/tasks/ci.rake
@@ -38,7 +38,7 @@ namespace :ci do
   end
 
   task :jest => "ci:assets" do
-    sh "node_modules/.bin/jest"
+    sh "node_modules/.bin/jest" unless ["CHOUETTE_JEST_DISABLED"]
   end
 
   desc "Deploy after CI"
-- 
cgit v1.2.3
From 5c45c5d9ab4a7a7d64c4a73c7908861824fd8ee4 Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 11:13:17 +0100
Subject: Fixes typo in deploy.rb. Refs #5803
---
 config/deploy.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/deploy.rb b/config/deploy.rb
index 1ea1613de..6a5d07d79 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -11,7 +11,7 @@ set :ruby_version, "2.3.0"
 default_run_options[:pty] = true
 set :group_writable, true
 set :bundle_cmd, "/var/lib/gems/#{ruby_version}/bin/bundle"
-oset :rake, "#{bundle_cmd} exec rake"
+set :rake, "#{bundle_cmd} exec rake"
 set :default_env, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
 
 set :keep_releases, -> { fetch(:kept_releases, 5) }
-- 
cgit v1.2.3
From 387450bd2a929d4a8ae6cc77182859f8c8587786 Mon Sep 17 00:00:00 2001
From: Luc Donnet
Date: Thu, 1 Feb 2018 11:31:56 +0100
Subject: Fix yarn installation with no production for ci and force yarn
 install before jest launch du to webpacker bug
---
 lib/tasks/ci.rake | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake
index 82619d04b..13d7b8d73 100644
--- a/lib/tasks/ci.rake
+++ b/lib/tasks/ci.rake
@@ -3,7 +3,7 @@ namespace :ci do
   task :setup do
     cp "config/database/jenkins.yml", "config/database.yml"
     sh "RAILS_ENV=test rake db:drop db:create db:migrate"
-    sh "yarn --production --no-progress install"
+    sh "yarn --no-progress install"
   end
 
   def git_branch
@@ -38,6 +38,7 @@ namespace :ci do
   end
 
   task :jest => "ci:assets" do
+    sh "yarn --no-progress install" # Hack to force install jest after webpack 
     sh "node_modules/.bin/jest" unless ["CHOUETTE_JEST_DISABLED"]
   end
 
-- 
cgit v1.2.3
From f9a4d3b6f0360071e8b15d18345a4b9bc279d12c Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 11:35:32 +0100
Subject: Use default_environment (for capistrano 2). Refs #5803
---
 config/deploy.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/deploy.rb b/config/deploy.rb
index 6a5d07d79..a67fbcd13 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -12,7 +12,7 @@ default_run_options[:pty] = true
 set :group_writable, true
 set :bundle_cmd, "/var/lib/gems/#{ruby_version}/bin/bundle"
 set :rake, "#{bundle_cmd} exec rake"
-set :default_env, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
+set :default_environment, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
 
 set :keep_releases, -> { fetch(:kept_releases, 5) }
 after "deploy:restart", "deploy:cleanup"
-- 
cgit v1.2.3
From 4329ed3aa9d64ecbed44d1b70f9101a8f4653748 Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 13:11:55 +0100
Subject: Use upcase PATH variable in :default_environment. Refs #5803
---
 config/deploy.rb | 4 ++++
 1 file changed, 4 insertions(+)
diff --git a/config/deploy.rb b/config/deploy.rb
index a67fbcd13..7e385cebc 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -14,6 +14,10 @@ set :bundle_cmd, "/var/lib/gems/#{ruby_version}/bin/bundle"
 set :rake, "#{bundle_cmd} exec rake"
 set :default_environment, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
 
+set :default_environment, {
+  'PATH' => "/var/lib/gems/#{ruby_version}/bin:$PATH"
+}
+
 set :keep_releases, -> { fetch(:kept_releases, 5) }
 after "deploy:restart", "deploy:cleanup"
 
-- 
cgit v1.2.3
From 3577ea93c9d0e4475da9cef47c86d2880715909c Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 13:12:24 +0100
Subject: Remove first :default_environment. Refs #5803
---
 config/deploy.rb | 2 --
 1 file changed, 2 deletions(-)
diff --git a/config/deploy.rb b/config/deploy.rb
index 7e385cebc..9be023adc 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -12,8 +12,6 @@ default_run_options[:pty] = true
 set :group_writable, true
 set :bundle_cmd, "/var/lib/gems/#{ruby_version}/bin/bundle"
 set :rake, "#{bundle_cmd} exec rake"
-set :default_environment, { path: "/var/lib/gems/#{ruby_version}/bin:$PATH" }
-
 set :default_environment, {
   'PATH' => "/var/lib/gems/#{ruby_version}/bin:$PATH"
 }
-- 
cgit v1.2.3
From 5a43a7f610395a6c4a09163959c4987c7a61bd28 Mon Sep 17 00:00:00 2001
From: Alban Peignier
Date: Thu, 1 Feb 2018 13:39:00 +0100
Subject: Revert webpacker to 3.0.2. Refs #5807
---
 Gemfile      | 3 ++-
 Gemfile.lock | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/Gemfile b/Gemfile
index c378820b3..e1943e659 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,4 @@
+# coding: utf-8
 source 'https://rubygems.org'
 
 # Use https for github
@@ -15,7 +16,7 @@ gem 'uglifier', '~> 2.7.2'
 gem 'coffee-rails', '~> 4.0.0'
 
 # Webpacker
-gem 'webpacker', '~> 3.0'
+gem 'webpacker', '3.0.2'
 
 # Use jquery as the JavaScript library
 gem 'jquery-rails', '~> 3.1.4' # Update to v4 for Rails 4.2
diff --git a/Gemfile.lock b/Gemfile.lock
index ba86a911f..3a2314857 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -562,7 +562,7 @@ GEM
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff
-    webpacker (3.2.1)
+    webpacker (3.0.2)
       activesupport (>= 4.2)
       rack-proxy (>= 0.6.1)
       railties (>= 4.2)
@@ -698,10 +698,10 @@ DEPENDENCIES
   transpec
   uglifier (~> 2.7.2)
   webmock
-  webpacker (~> 3.0)
+  webpacker (= 3.0.2)
   whenever!
   will_paginate
   will_paginate-bootstrap
 
 BUNDLED WITH
-   1.16.0
+   1.16.1
-- 
cgit v1.2.3
From 6fb07ac6da9c98a8643c40fbe38360153100be83 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 16:38:50 +0100
Subject: Refs #5762; Make coordinates optional on stop areas
---
 app/models/chouette/stop_area.rb     | 4 +---
 app/views/stop_areas/_form.html.slim | 2 +-
 2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index d7d5c2eb2..c6feaf940 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -106,9 +106,7 @@ module Chouette
       end
     end
 
-    def local_id
-      id.to_s
-    end
+    alias_method :local_id, :user_objectid
 
     def children_in_depth
       return [] if self.children.empty?
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index aa156f7bd..1bc3e77ef 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -23,7 +23,7 @@
           - unless @stop_area.projection.blank? or @stop_area.projection_type_label.empty?
             = f.input :projection_xy, :label => t("activerecord.attributes.stop_area.projection_xy", :projection => @referential.projection_type_label), :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.projection_xy")}
 
-          = f.input :coordinates, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.coordinates")}, required: true
+          = f.input :coordinates, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.coordinates")}
           = f.input :street_name
           = f.input :zip_code, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.zip_code")}
           = f.input :city_name, required: format_restriction_for_locales(@referential) == '.hub', :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.city_name")}
-- 
cgit v1.2.3
From ec5a557df43d16294f55a1c11e9a581b55126695 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 16:46:08 +0100
Subject: Refs #5762; Disable default position for stops
---
 app/controllers/stop_areas_controller.rb | 2 --
 1 file changed, 2 deletions(-)
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 8d424b8d1..076de922c 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -95,7 +95,6 @@ class StopAreasController < ChouetteController
   def edit
     authorize stop_area
     edit! do
-      stop_area.position ||= stop_area.default_position
       map.editable = true
     end
   end
@@ -107,7 +106,6 @@ class StopAreasController < ChouetteController
 
   def update
     authorize stop_area
-    stop_area.position ||= stop_area.default_position
     map.editable = true
 
     update!
-- 
cgit v1.2.3
From 827dd381446e850330d5a85d13979e40d06fe186 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 16:56:06 +0100
Subject: Refs #5762; Update JS to accomodate stops without location
---
 app/javascript/helpers/routes_map.coffee | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/app/javascript/helpers/routes_map.coffee b/app/javascript/helpers/routes_map.coffee
index 85def1390..6834406fc 100644
--- a/app/javascript/helpers/routes_map.coffee
+++ b/app/javascript/helpers/routes_map.coffee
@@ -30,20 +30,24 @@ class RoutesMap
         geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stops[stops.length - 1].longitude), parseFloat(stops[stops.length - 1].latitude)]))
       })
     ]
+
+    prevStop = null
     stops.forEach (stop, i) =>
-      if i < stops.length - 1
-        geoColLns.push new ol.Feature
-          geometry: new ol.geom.LineString([
-            ol.proj.fromLonLat([parseFloat(stops[i].longitude), parseFloat(stops[i].latitude)]),
-            ol.proj.fromLonLat([parseFloat(stops[i + 1].longitude), parseFloat(stops[i + 1].latitude)])
-          ])
+      if stop.longitude && stop.latitude
+        if prevStop
+          geoColLns.push new ol.Feature
+            geometry: new ol.geom.LineString([
+              ol.proj.fromLonLat([parseFloat(prevStop.longitude), parseFloat(prevStop.latitude)]),
+              ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)])
+            ])
+        prevStop = stop
 
-      geoColPts.push(new ol.Feature({
-        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)]))
-      }))
-      unless @seenStopIds.indexOf(stop.stoparea_id) > 0
-        @area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)]
-        @seenStopIds.push stop.stoparea_id
+        geoColPts.push(new ol.Feature({
+          geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)]))
+        }))
+        unless @seenStopIds.indexOf(stop.stoparea_id) > 0
+          @area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)]
+          @seenStopIds.push stop.stoparea_id
 
     vectorPtsLayer = new ol.layer.Vector({
       source: new ol.source.Vector({
-- 
cgit v1.2.3
From 3c4e0b97d2c2502412817b5c99989f98821fc207 Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 1 Feb 2018 13:55:21 +0100
Subject: Fix css
---
 app/assets/stylesheets/OpenLayers/custom.sass | 1 -
 1 file changed, 1 deletion(-)
diff --git a/app/assets/stylesheets/OpenLayers/custom.sass b/app/assets/stylesheets/OpenLayers/custom.sass
index 5a3612f99..fa874d924 100644
--- a/app/assets/stylesheets/OpenLayers/custom.sass
+++ b/app/assets/stylesheets/OpenLayers/custom.sass
@@ -18,7 +18,6 @@
       cursor: pointer
       color: $blue
       white-space: nowrap
-      max-width: 33%
 
       &:hover
         background: $orange
-- 
cgit v1.2.3
From 47520607ee5a91fbba206aff4e72dd25293e1541 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 16:25:08 +0100
Subject: Refs #5758 @1h; Add localized names to StopAreas
---
 Gemfile                                                        | 1 +
 Gemfile.lock                                                   | 3 +++
 app/assets/stylesheets/application.sass                        | 2 ++
 app/controllers/stop_areas_controller.rb                       | 1 +
 app/helpers/stop_areas_helper.rb                               | 6 ++++--
 app/models/chouette/stop_area.rb                               | 7 +++++++
 app/views/stop_areas/_form.html.slim                           | 6 +++++-
 app/views/stop_areas/show.html.slim                            | 6 ++++--
 config/initializers/countries.rb                               | 3 +++
 db/migrate/20180129141656_add_localized_names_to_stop_areas.rb | 5 +++++
 db/schema.rb                                                   | 1 +
 11 files changed, 36 insertions(+), 5 deletions(-)
 create mode 100644 config/initializers/countries.rb
 create mode 100644 db/migrate/20180129141656_add_localized_names_to_stop_areas.rb
diff --git a/Gemfile b/Gemfile
index e1943e659..bb1a42df0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -102,6 +102,7 @@ gem 'font-awesome-sass', '~> 4.7'
 gem 'will_paginate-bootstrap'
 gem 'gretel'
 gem 'country_select'
+gem 'flag-icons-rails'
 
 # Format Output
 gem 'json'
diff --git a/Gemfile.lock b/Gemfile.lock
index 3a2314857..ffa63b535 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -236,6 +236,8 @@ GEM
     ffi (1.9.18)
     ffi-geos (1.2.0)
       ffi (>= 1.0.0)
+    flag-icons-rails (2.5.0)
+      sass (~> 3.2)
     font-awesome-sass (4.7.0)
       sass (>= 3.2)
     formtastic (3.1.5)
@@ -625,6 +627,7 @@ DEPENDENCIES
   faraday (~> 0.9.1)
   faraday_middleware (~> 0.9.1)
   ffaker (~> 2.1.0)
+  flag-icons-rails
   font-awesome-sass (~> 4.7)
   formtastic (= 3.1.5)
   georuby (= 2.3.0)
diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass
index 50f8b64cb..3f8467efe 100644
--- a/app/assets/stylesheets/application.sass
+++ b/app/assets/stylesheets/application.sass
@@ -19,3 +19,5 @@
 @import 'modules/vj_collection'
 @import 'modules/timetables'
 @import 'modules/import_messages'
+
+@import 'flag-icon'
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 076de922c..41a1a8c6d 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -202,6 +202,7 @@ class StopAreasController < ChouetteController
       :waiting_time,
       :zip_code,
       :kind,
+      localized_names: Chouette::StopArea::AVAILABLE_LOCALIZATIONS
     )
   end
 
diff --git a/app/helpers/stop_areas_helper.rb b/app/helpers/stop_areas_helper.rb
index 3e04fac7d..05ae042f5 100644
--- a/app/helpers/stop_areas_helper.rb
+++ b/app/helpers/stop_areas_helper.rb
@@ -11,6 +11,10 @@ module StopAreasHelper
     ( "}) " + " " + name + " " + localization + "").html_safe
   end
 
+  def label_for_country country, txt=nil
+    "#{txt} ".html_safe
+  end
+
   def genealogical_title
     return t("stop_areas.genealogical.genealogical_routing") if @stop_area.stop_area_type == 'itl'
     t("stop_areas.genealogical.genealogical")
@@ -33,12 +37,10 @@ module StopAreasHelper
     @stop_area.stop_area_type == 'stop_place' || @stop_area.stop_area_type == 'commercial_stop_point'
   end
 
-
   def pair_key(access_link)
     "#{access_link.access_point.id}-#{access_link.stop_area.id}"
   end
 
-
   def geo_data(sa, sar)
     if sa.long_lat_type.nil?
       content_tag :span, '-'
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index c6feaf940..bb8747faa 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -12,6 +12,8 @@ module Chouette
     enumerize :area_type, in: Chouette::AreaType::ALL
     enumerize :kind, in: %i(commercial non_commercial)
 
+    AVAILABLE_LOCALIZATIONS = %i(gb nl de fr it es)
+
     with_options dependent: :destroy do |assoc|
       assoc.has_many :stop_points
       assoc.has_many :access_points
@@ -50,6 +52,11 @@ module Chouette
       :nearest_topic_name, :comment, :long_lat_type, :zip_code, :city_name, :url, :time_zone]
     end
 
+    def localized_names
+      val = read_attribute(:localized_names) || {}
+      Hash[*AVAILABLE_LOCALIZATIONS.map{|k| [k, val[k.to_s]]}.flatten]
+    end
+
     def parent_area_type_must_be_greater
       return unless self.parent
 
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 1bc3e77ef..c2b9a7f4f 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,8 +6,12 @@
         /= @map.to_html
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
+        .form-group
+          .col-sm-3.col-xs-5
+          .col-sm-9.col-xs-7
+            - f.object.localized_names.each do |k, v|
+              .col-md-6= f.input "localized_names[#{k}]", input_html: {value: v}, label: label_for_country(k)
         = f.input :kind, as: :radio_buttons, checked: @stop_area.kind, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, disabled: !@stop_area.new_record?
-
         .slave data-master="[name='stop_area[kind]']" data-value="commercial"
           = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
         - %i(non_commercial commercial).each do |kind|
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index b0896c1e0..67f309cef 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -7,10 +7,12 @@
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
         - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id }
+        - @stop_area.localized_names.each do |k, v|
+          - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
         - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial?
         - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
-            @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number,
-            })
+            @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number
+          })
         - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
         - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
             @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
diff --git a/config/initializers/countries.rb b/config/initializers/countries.rb
new file mode 100644
index 000000000..7f2b5c9db
--- /dev/null
+++ b/config/initializers/countries.rb
@@ -0,0 +1,3 @@
+ISO3166.configure  do |config|
+  config.locales = (I18n.available_locales + Chouette::StopArea::AVAILABLE_LOCALIZATIONS).uniq
+end
diff --git a/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb b/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb
new file mode 100644
index 000000000..320ce739a
--- /dev/null
+++ b/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb
@@ -0,0 +1,5 @@
+class AddLocalizedNamesToStopAreas < ActiveRecord::Migration
+  def change
+    add_column :stop_areas, :localized_names, :jsonb
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b696cfc98..51cb44d17 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -791,6 +791,7 @@ ActiveRecord::Schema.define(version: 20180126134944) do
     t.string   "stif_type"
     t.integer  "waiting_time"
     t.string   "kind"
+    t.jsonb    "localized_names"
   end
 
   add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree
-- 
cgit v1.2.3
From 9db4ffe5d56bd95444b03d62d0a138822424c4f2 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 20:48:04 +0100
Subject: Refs #5758; Add corresponding feature `stop_area_localized_names`
---
 app/views/stop_areas/_form.html.slim | 7 +++++++
 app/views/stop_areas/show.html.slim  | 7 +++++--
 2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index c2b9a7f4f..67af39dc6 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,6 +6,13 @@
         /= @map.to_html
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
+        - if has_feature?(:stop_area_localized_names)
+          .form-group
+            .col-sm-3.col-xs-5
+            .col-sm-9.col-xs-7
+              - f.object.localized_names.each do |k, v|
+                .col-md-6= f.input "localized_names[#{k}]", input_html: {value: v}, label: label_for_country(k)
+                
         .form-group
           .col-sm-3.col-xs-5
           .col-sm-9.col-xs-7
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index 67f309cef..300d714e8 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -7,12 +7,15 @@
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
         - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id }
-        - @stop_area.localized_names.each do |k, v|
-          - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
+
+        - if has_feature?(:stop_area_localized_names)
+          - @stop_area.localized_names.each do |k, v|
+            - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
         - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial?
         - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
             @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number
           })
+
         - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
         - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
             @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
-- 
cgit v1.2.3
From ea8c633d45b97e41894ea8c691a6b3ae434eb770 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 15:20:43 +0100
Subject: ReferentialVehicleJourneys#index: Fix `nil` error on `stop_area_ids`
This wasn't checking that `params[:q]` was present before trying to
access `[:stop_area_ids]`, and thus caused an error on the page when no
filters were applied.
Refs #5809
---
 app/controllers/referential_vehicle_journeys_controller.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index a199157dd..2ce28a5cc 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -23,7 +23,7 @@ class ReferentialVehicleJourneysController < ChouetteController
     @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
     @all_stop_areas = Chouette::StopArea.where("id IN (#{@referential.vehicle_journeys.joins(:stop_areas).select("stop_areas.id").to_sql})").distinct
-    stop_area_ids = params[:q][:stop_area_ids].select(&:present?)
+    stop_area_ids = params[:q].try(:[], :stop_area_ids).try(:select, &:present?)
     @filters_stop_areas = Chouette::StopArea.find(stop_area_ids) if stop_area_ids.present? && stop_area_ids.size <= 2
   end
 
-- 
cgit v1.2.3
From 7120ef13351868898d9c01e7f9d2264a04d4136a Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 1 Feb 2018 15:27:33 +0100
Subject: Fix broken TimeTableDecorator
---
 app/decorators/time_table_decorator.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb
index 9a56fc2ee..95b1fd959 100644
--- a/app/decorators/time_table_decorator.rb
+++ b/app/decorators/time_table_decorator.rb
@@ -11,7 +11,7 @@ class TimeTableDecorator < AF83::Decorator
     end
 
     instance_decorator.edit_action_link do |l|
-      l.href { [context[:referential], object] }
+      l.href { [:edit, context[:referential], object] }
     end
 
     instance_decorator.action_link if: ->{ object.calendar }, secondary: true do |l|
-- 
cgit v1.2.3
From 505e3b3e9cc69c79f9ce4359916449a06e082bea Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Wed, 31 Jan 2018 19:40:59 +0100
Subject: ReferentialVehicleJourneys#index: Add 'line' filter
Filter vehicle journeys by their associated route's line.
The `with_line_id` scope is modeled on `with_stop_area_ids`.
The HTML filter is modeled on the line filter from
`app/views/workbenches/_filters.html.slim`.
Refs #5576
---
 .../referential_vehicle_journeys_controller.rb          |  1 +
 app/models/chouette/vehicle_journey.rb                  | 11 ++++++++++-
 .../referential_vehicle_journeys/_filters.html.slim     | 17 +++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index 2ce28a5cc..935b52d93 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -16,6 +16,7 @@ class ReferentialVehicleJourneysController < ChouetteController
 
   def collection
     @q ||= end_of_association_chain
+    @q = @q.with_line_id(params[:q][:line_id]) if params[:q] && params[:q][:line_id]
     @q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
     @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window, prefix: :purchase_window)
     @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.time_table'), query: :with_matching_timetable, prefix: :time_table)
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 1756a7098..918ca07aa 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -52,6 +52,10 @@ module Chouette
       end
     }
 
+    scope :with_line_id, ->(id) {
+      joins(:route).where(routes: { line_id: id })
+    }
+
     scope :in_purchase_window, ->(range){
       purchase_windows = Chouette::PurchaseWindow.overlap_dates(range)
       sql = purchase_windows.joins(:vehicle_journeys).select('vehicle_journeys.id').uniq.to_sql
@@ -59,8 +63,9 @@ module Chouette
     }
 
     # We need this for the ransack object in the filters
-    ransacker :stop_area_ids
+    ransacker :line_id
     ransacker :purchase_window_date_gt
+    ransacker :stop_area_ids
 
     # returns VehicleJourneys with at least 1 day in their time_tables
     # included in the given range
@@ -375,5 +380,9 @@ module Chouette
         ')
         .where('"time_tables_vehicle_journeys"."vehicle_journey_id" IS NULL')
     end
+
+    def self.lines
+      Chouette::Line.joins(routes: :vehicle_journeys).distinct
+    end
   end
 end
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index bfb5b77dd..838150d2c 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -15,6 +15,23 @@
         = f.input :company_id_eq_any, collection: @all_companies.select(:id, :name).order(name: :asc), as: :check_boxes, label: false, label_method: lambda{|l| ("" + l.name + "").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}
       - else
         = f.input :company_id_eq_any, collection: [[I18n.t('companies.search_no_results_for_filter'), nil]], as: :check_boxes, label: false, disabled: true, required: false, wrapper_html: { class: 'checkbox_list disabled'}
+
+    .form-group.togglable
+      = f.label Chouette::Line.model_name.human,
+          required: false,
+          class: 'control-label'
+      .inputs.form-inline.checkbox_list
+        = f.input :line_id,
+            as: :select,
+            collection: @vehicle_journeys.lines,
+            input_html: { \
+              'data-select2ed': 'true',
+              'data-select2ed-placeholder': t('referentials.filters.line') \
+            },
+            label: false,
+            label_method: :display_name,
+            wrapper_html: { class: 'select2ed' }
+
     .form-group.togglable.name-filter
       = f.label Chouette::VehicleJourney.human_attribute_name(:published_journey_name), required: false, class: 'control-label'
       .inputs.form-inline.checkbox_list
-- 
cgit v1.2.3
From 4cf5dc7f1d1f2a0867e288918d08762d2e35c251 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 10:57:44 +0100
Subject: ReferentialVehicleJourneys#index: Add padding to line filter
Add some padding inside the drop-down so that the filter select box
doesn't run up against the sides of the drop-down.
Refs #5576
---
 app/views/referential_vehicle_journeys/_filters.html.slim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index 838150d2c..077c40de7 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -20,7 +20,7 @@
       = f.label Chouette::Line.model_name.human,
           required: false,
           class: 'control-label'
-      .inputs.form-inline.checkbox_list
+      .form-inline.filter_menu
         = f.input :line_id,
             as: :select,
             collection: @vehicle_journeys.lines,
@@ -30,7 +30,7 @@
             },
             label: false,
             label_method: :display_name,
-            wrapper_html: { class: 'select2ed' }
+            wrapper_html: { class: 'filter_menu-item select2ed' }
 
     .form-group.togglable.name-filter
       = f.label Chouette::VehicleJourney.human_attribute_name(:published_journey_name), required: false, class: 'control-label'
-- 
cgit v1.2.3
From 0cf9a848266fde870330ceff2bf312d04036a28b Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 11:52:48 +0100
Subject: ReferentialVehicleJourneys#index: Pre-fill line select filter
Pre-fill this box with the value from `params[:q]` so users can see
their selected line after filtering.
For some reason Select2 wants to keep it selected even after clicking
"Effacer". Not sure what that's about, but the HTML ` " + " " + name + " " + localization + "").html_safe
   end
 
+  def label_for_country country, txt=nil
+    "#{txt} ".html_safe
+  end
+
   def genealogical_title
     return t("stop_areas.genealogical.genealogical_routing") if @stop_area.stop_area_type == 'itl'
     t("stop_areas.genealogical.genealogical")
@@ -33,12 +37,10 @@ module StopAreasHelper
     @stop_area.stop_area_type == 'stop_place' || @stop_area.stop_area_type == 'commercial_stop_point'
   end
 
-
   def pair_key(access_link)
     "#{access_link.access_point.id}-#{access_link.stop_area.id}"
   end
 
-
   def geo_data(sa, sar)
     if sa.long_lat_type.nil?
       content_tag :span, '-'
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index c6feaf940..bb8747faa 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -12,6 +12,8 @@ module Chouette
     enumerize :area_type, in: Chouette::AreaType::ALL
     enumerize :kind, in: %i(commercial non_commercial)
 
+    AVAILABLE_LOCALIZATIONS = %i(gb nl de fr it es)
+
     with_options dependent: :destroy do |assoc|
       assoc.has_many :stop_points
       assoc.has_many :access_points
@@ -50,6 +52,11 @@ module Chouette
       :nearest_topic_name, :comment, :long_lat_type, :zip_code, :city_name, :url, :time_zone]
     end
 
+    def localized_names
+      val = read_attribute(:localized_names) || {}
+      Hash[*AVAILABLE_LOCALIZATIONS.map{|k| [k, val[k.to_s]]}.flatten]
+    end
+
     def parent_area_type_must_be_greater
       return unless self.parent
 
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 1bc3e77ef..c2b9a7f4f 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,8 +6,12 @@
         /= @map.to_html
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
+        .form-group
+          .col-sm-3.col-xs-5
+          .col-sm-9.col-xs-7
+            - f.object.localized_names.each do |k, v|
+              .col-md-6= f.input "localized_names[#{k}]", input_html: {value: v}, label: label_for_country(k)
         = f.input :kind, as: :radio_buttons, checked: @stop_area.kind, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, disabled: !@stop_area.new_record?
-
         .slave data-master="[name='stop_area[kind]']" data-value="commercial"
           = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
         - %i(non_commercial commercial).each do |kind|
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index b0896c1e0..67f309cef 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -7,10 +7,12 @@
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
         - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id }
+        - @stop_area.localized_names.each do |k, v|
+          - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
         - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial?
         - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
-            @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number,
-            })
+            @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number
+          })
         - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
         - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
             @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
diff --git a/config/initializers/countries.rb b/config/initializers/countries.rb
new file mode 100644
index 000000000..7f2b5c9db
--- /dev/null
+++ b/config/initializers/countries.rb
@@ -0,0 +1,3 @@
+ISO3166.configure  do |config|
+  config.locales = (I18n.available_locales + Chouette::StopArea::AVAILABLE_LOCALIZATIONS).uniq
+end
diff --git a/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb b/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb
new file mode 100644
index 000000000..320ce739a
--- /dev/null
+++ b/db/migrate/20180129141656_add_localized_names_to_stop_areas.rb
@@ -0,0 +1,5 @@
+class AddLocalizedNamesToStopAreas < ActiveRecord::Migration
+  def change
+    add_column :stop_areas, :localized_names, :jsonb
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b696cfc98..51cb44d17 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -791,6 +791,7 @@ ActiveRecord::Schema.define(version: 20180126134944) do
     t.string   "stif_type"
     t.integer  "waiting_time"
     t.string   "kind"
+    t.jsonb    "localized_names"
   end
 
   add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree
-- 
cgit v1.2.3
From 9db4ffe5d56bd95444b03d62d0a138822424c4f2 Mon Sep 17 00:00:00 2001
From: Zog
Date: Mon, 29 Jan 2018 20:48:04 +0100
Subject: Refs #5758; Add corresponding feature `stop_area_localized_names`
---
 app/views/stop_areas/_form.html.slim | 7 +++++++
 app/views/stop_areas/show.html.slim  | 7 +++++--
 2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index c2b9a7f4f..67af39dc6 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,6 +6,13 @@
         /= @map.to_html
         = f.input :id, as: :hidden
         = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
+        - if has_feature?(:stop_area_localized_names)
+          .form-group
+            .col-sm-3.col-xs-5
+            .col-sm-9.col-xs-7
+              - f.object.localized_names.each do |k, v|
+                .col-md-6= f.input "localized_names[#{k}]", input_html: {value: v}, label: label_for_country(k)
+                
         .form-group
           .col-sm-3.col-xs-5
           .col-sm-9.col-xs-7
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index 67f309cef..300d714e8 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -7,12 +7,15 @@
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
         - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id }
-        - @stop_area.localized_names.each do |k, v|
-          - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
+
+        - if has_feature?(:stop_area_localized_names)
+          - @stop_area.localized_names.each do |k, v|
+            - attributes.merge!({label_for_country(k, @stop_area.human_attribute_name(:name)) => v }) if v.present?
         - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial?
         - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
             @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number
           })
+
         - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
         - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
             @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
-- 
cgit v1.2.3
From ea8c633d45b97e41894ea8c691a6b3ae434eb770 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 15:20:43 +0100
Subject: ReferentialVehicleJourneys#index: Fix `nil` error on `stop_area_ids`
This wasn't checking that `params[:q]` was present before trying to
access `[:stop_area_ids]`, and thus caused an error on the page when no
filters were applied.
Refs #5809
---
 app/controllers/referential_vehicle_journeys_controller.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index a199157dd..2ce28a5cc 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -23,7 +23,7 @@ class ReferentialVehicleJourneysController < ChouetteController
     @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
     @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
     @all_stop_areas = Chouette::StopArea.where("id IN (#{@referential.vehicle_journeys.joins(:stop_areas).select("stop_areas.id").to_sql})").distinct
-    stop_area_ids = params[:q][:stop_area_ids].select(&:present?)
+    stop_area_ids = params[:q].try(:[], :stop_area_ids).try(:select, &:present?)
     @filters_stop_areas = Chouette::StopArea.find(stop_area_ids) if stop_area_ids.present? && stop_area_ids.size <= 2
   end
 
-- 
cgit v1.2.3
From 7120ef13351868898d9c01e7f9d2264a04d4136a Mon Sep 17 00:00:00 2001
From: Zog
Date: Thu, 1 Feb 2018 15:27:33 +0100
Subject: Fix broken TimeTableDecorator
---
 app/decorators/time_table_decorator.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb
index 9a56fc2ee..95b1fd959 100644
--- a/app/decorators/time_table_decorator.rb
+++ b/app/decorators/time_table_decorator.rb
@@ -11,7 +11,7 @@ class TimeTableDecorator < AF83::Decorator
     end
 
     instance_decorator.edit_action_link do |l|
-      l.href { [context[:referential], object] }
+      l.href { [:edit, context[:referential], object] }
     end
 
     instance_decorator.action_link if: ->{ object.calendar }, secondary: true do |l|
-- 
cgit v1.2.3
From 505e3b3e9cc69c79f9ce4359916449a06e082bea Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Wed, 31 Jan 2018 19:40:59 +0100
Subject: ReferentialVehicleJourneys#index: Add 'line' filter
Filter vehicle journeys by their associated route's line.
The `with_line_id` scope is modeled on `with_stop_area_ids`.
The HTML filter is modeled on the line filter from
`app/views/workbenches/_filters.html.slim`.
Refs #5576
---
 .../referential_vehicle_journeys_controller.rb          |  1 +
 app/models/chouette/vehicle_journey.rb                  | 11 ++++++++++-
 .../referential_vehicle_journeys/_filters.html.slim     | 17 +++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index 2ce28a5cc..935b52d93 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -16,6 +16,7 @@ class ReferentialVehicleJourneysController < ChouetteController
 
   def collection
     @q ||= end_of_association_chain
+    @q = @q.with_line_id(params[:q][:line_id]) if params[:q] && params[:q][:line_id]
     @q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
     @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window, prefix: :purchase_window)
     @q = ransack_period_range(scope: @q, error_message:  t('vehicle_journeys.errors.time_table'), query: :with_matching_timetable, prefix: :time_table)
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 1756a7098..918ca07aa 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -52,6 +52,10 @@ module Chouette
       end
     }
 
+    scope :with_line_id, ->(id) {
+      joins(:route).where(routes: { line_id: id })
+    }
+
     scope :in_purchase_window, ->(range){
       purchase_windows = Chouette::PurchaseWindow.overlap_dates(range)
       sql = purchase_windows.joins(:vehicle_journeys).select('vehicle_journeys.id').uniq.to_sql
@@ -59,8 +63,9 @@ module Chouette
     }
 
     # We need this for the ransack object in the filters
-    ransacker :stop_area_ids
+    ransacker :line_id
     ransacker :purchase_window_date_gt
+    ransacker :stop_area_ids
 
     # returns VehicleJourneys with at least 1 day in their time_tables
     # included in the given range
@@ -375,5 +380,9 @@ module Chouette
         ')
         .where('"time_tables_vehicle_journeys"."vehicle_journey_id" IS NULL')
     end
+
+    def self.lines
+      Chouette::Line.joins(routes: :vehicle_journeys).distinct
+    end
   end
 end
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index bfb5b77dd..838150d2c 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -15,6 +15,23 @@
         = f.input :company_id_eq_any, collection: @all_companies.select(:id, :name).order(name: :asc), as: :check_boxes, label: false, label_method: lambda{|l| ("" + l.name + "").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}
       - else
         = f.input :company_id_eq_any, collection: [[I18n.t('companies.search_no_results_for_filter'), nil]], as: :check_boxes, label: false, disabled: true, required: false, wrapper_html: { class: 'checkbox_list disabled'}
+
+    .form-group.togglable
+      = f.label Chouette::Line.model_name.human,
+          required: false,
+          class: 'control-label'
+      .inputs.form-inline.checkbox_list
+        = f.input :line_id,
+            as: :select,
+            collection: @vehicle_journeys.lines,
+            input_html: { \
+              'data-select2ed': 'true',
+              'data-select2ed-placeholder': t('referentials.filters.line') \
+            },
+            label: false,
+            label_method: :display_name,
+            wrapper_html: { class: 'select2ed' }
+
     .form-group.togglable.name-filter
       = f.label Chouette::VehicleJourney.human_attribute_name(:published_journey_name), required: false, class: 'control-label'
       .inputs.form-inline.checkbox_list
-- 
cgit v1.2.3
From 4cf5dc7f1d1f2a0867e288918d08762d2e35c251 Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 10:57:44 +0100
Subject: ReferentialVehicleJourneys#index: Add padding to line filter
Add some padding inside the drop-down so that the filter select box
doesn't run up against the sides of the drop-down.
Refs #5576
---
 app/views/referential_vehicle_journeys/_filters.html.slim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index 838150d2c..077c40de7 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -20,7 +20,7 @@
       = f.label Chouette::Line.model_name.human,
           required: false,
           class: 'control-label'
-      .inputs.form-inline.checkbox_list
+      .form-inline.filter_menu
         = f.input :line_id,
             as: :select,
             collection: @vehicle_journeys.lines,
@@ -30,7 +30,7 @@
             },
             label: false,
             label_method: :display_name,
-            wrapper_html: { class: 'select2ed' }
+            wrapper_html: { class: 'filter_menu-item select2ed' }
 
     .form-group.togglable.name-filter
       = f.label Chouette::VehicleJourney.human_attribute_name(:published_journey_name), required: false, class: 'control-label'
-- 
cgit v1.2.3
From 0cf9a848266fde870330ceff2bf312d04036a28b Mon Sep 17 00:00:00 2001
From: Teddy Wing
Date: Thu, 1 Feb 2018 11:52:48 +0100
Subject: ReferentialVehicleJourneys#index: Pre-fill line select filter
Pre-fill this box with the value from `params[:q]` so users can see
their selected line after filtering.
For some reason Select2 wants to keep it selected even after clicking
"Effacer". Not sure what that's about, but the HTML `
 
         {this.props.value.vehicle_journey_at_stops.map((vj, i) =>
           
diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
index f814c0459..f6a0e3c61 100644
--- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -7,6 +7,7 @@ import CustomFieldsInputs from './CustomFieldsInputs'
 export default class EditVehicleJourney extends Component {
   constructor(props) {
     super(props)
+    this.updateValue = this.updateValue.bind(this)
   }
 
   handleSubmit() {
@@ -23,8 +24,14 @@ export default class EditVehicleJourney extends Component {
     }
   }
 
-  getSelected() {
-    return this.props.vehicleJourney ? [this.props.vehicleJourney] : actions.getSelected(this.props.vehicleJourneys)
+  updateValue(attribute, e) {
+    this.props.modal.modalProps.vehicleJourney[attribute] = e.target.value
+    actions.resetValidation(e.currentTarget)
+    this.forceUpdate()
+  }
+
+  editMode() {
+    return !this.props.modal.modalProps.info && this.props.editMode
   }
 
   render() {
@@ -39,10 +46,10 @@ export default class EditVehicleJourney extends Component {
         
- 
           
@@ -67,9 +74,9 @@ export default class EditVehicleJourney extends Component {
                                   type='text'
                                   ref='published_journey_name'
                                   className='form-control'
-                                  disabled={!this.props.editMode}
-                                  defaultValue={this.props.modal.modalProps.vehicleJourney.published_journey_name}
-                                  onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+                                  disabled={!this.editMode()}
+                                  value={this.props.modal.modalProps.vehicleJourney.published_journey_name}
+                                  onChange={(e) => this.updateValue('published_journey_name', e)}
                                   />
                               
@@ -94,9 +101,9 @@ export default class EditVehicleJourney extends Component {
                                 type='text'
                                 ref='published_journey_identifier'
                                 className='form-control'
-                                disabled={!this.props.editMode}
-                                defaultValue={this.props.modal.modalProps.vehicleJourney.published_journey_identifier}
-                                onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+                                disabled={!this.editMode()}
+                                value={this.props.modal.modalProps.vehicleJourney.published_journey_identifier}
+                                onChange={(e) => this.updateValue('published_journey_identifier', e)}
                               />
                             
 
                           
@@ -105,7 +112,7 @@ export default class EditVehicleJourney extends Component {
                               
                                this.props.onSelect2Company(e)}
                                 onUnselect2Company = {() => this.props.onUnselect2Company()}
@@ -152,13 +159,13 @@ export default class EditVehicleJourney extends Component {
                            this.custom_fields[code]["value"] = value}
-                            disabled={!this.props.editMode}
+                            disabled={!this.editMode()}
                           />