aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/assets/stylesheets/components/_main_nav.sass5
-rw-r--r--app/assets/stylesheets/components/_pagination.sass3
-rw-r--r--app/controllers/application_controller.rb6
-rw-r--r--app/controllers/calendars_controller.rb4
-rw-r--r--app/controllers/companies_controller.rb14
-rw-r--r--app/controllers/compliance_control_blocks_controller.rb13
-rw-r--r--app/controllers/compliance_controls_controller.rb2
-rw-r--r--app/controllers/exports_controller.rb3
-rw-r--r--app/controllers/referential_companies_controller.rb11
-rw-r--r--app/controllers/referentials_controller.rb2
-rw-r--r--app/controllers/stop_areas_controller.rb5
-rw-r--r--app/helpers/application_helper.rb6
-rw-r--r--app/helpers/routes_helper.rb2
-rw-r--r--app/helpers/stop_areas_helper.rb6
-rw-r--r--app/javascript/journey_patterns/components/ConfirmModal.js8
-rw-r--r--app/javascript/journey_patterns/components/CreateModal.js12
-rw-r--r--app/javascript/journey_patterns/components/EditModal.js16
-rw-r--r--app/javascript/journey_patterns/components/JourneyPattern.js8
-rw-r--r--app/javascript/journey_patterns/components/JourneyPatterns.js12
-rw-r--r--app/javascript/journey_patterns/components/Navigate.js3
-rw-r--r--app/javascript/packs/routes/edit.js1
-rw-r--r--app/javascript/routes/actions/index.js7
-rw-r--r--app/javascript/routes/components/BSelect2.js1
-rw-r--r--app/javascript/routes/components/StopPoint.js6
-rw-r--r--app/javascript/routes/index.js5
-rw-r--r--app/javascript/routes/reducers/stopPoints.js5
-rw-r--r--app/javascript/time_tables/actions/index.js1
-rw-r--r--app/javascript/time_tables/containers/App.js1
-rw-r--r--app/javascript/vehicle_journeys/components/ConfirmModal.js4
-rw-r--r--app/javascript/vehicle_journeys/components/Navigate.js3
-rw-r--r--app/javascript/vehicle_journeys/components/tools/CreateModal.js16
-rw-r--r--app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js13
-rw-r--r--app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js2
-rw-r--r--app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js12
-rw-r--r--app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js4
-rw-r--r--app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js14
-rw-r--r--app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js8
-rw-r--r--app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js12
-rw-r--r--app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js8
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js3
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js2
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js2
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js2
-rw-r--r--app/models/chouette/company.rb2
-rw-r--r--app/models/chouette/for_alighting_enumerations.rb2
-rw-r--r--app/models/chouette/for_boarding_enumerations.rb2
-rw-r--r--app/models/chouette/line.rb8
-rw-r--r--app/models/chouette/stop_area.rb5
-rw-r--r--app/models/chouette/stop_point.rb7
-rw-r--r--app/models/compliance_control.rb1
-rw-r--r--app/models/compliance_control_block.rb2
-rw-r--r--app/models/concerns/custom_fields_support.rb41
-rw-r--r--app/models/custom_field.rb202
-rw-r--r--app/models/export/base.rb20
-rw-r--r--app/models/export/simple_exporter/base.rb4
-rw-r--r--app/models/line_control/lines_scope.rb8
-rw-r--r--app/models/line_referential.rb4
-rw-r--r--app/models/line_referential_membership.rb2
-rw-r--r--app/models/organisation.rb4
-rw-r--r--app/models/referential.rb4
-rw-r--r--app/models/referential_metadata.rb4
-rw-r--r--app/models/simple_exporter.rb2
-rw-r--r--app/models/stop_area_referential.rb2
-rw-r--r--app/models/stop_area_referential_membership.rb2
-rw-r--r--app/services/route_way_cost_calculator.rb2
-rw-r--r--app/uploaders/custom_field_attachment_uploader.rb12
-rw-r--r--app/views/autocomplete_stop_areas/around.rabl1
-rw-r--r--app/views/autocomplete_stop_areas/index.rabl3
-rw-r--r--app/views/autocomplete_stop_areas/show.rabl3
-rw-r--r--app/views/calendars/show.html.slim7
-rw-r--r--app/views/companies/_form.html.slim4
-rw-r--r--app/views/companies/show.html.slim13
-rw-r--r--app/views/compliance_control_blocks/edit.html.slim1
-rw-r--r--app/views/compliance_control_blocks/new.html.slim2
-rw-r--r--app/views/dashboards/_dashboard.html.slim7
-rw-r--r--app/views/layouts/navigation/_nav_panel_operations.html.slim2
-rw-r--r--app/views/layouts/navigation/_nav_panel_profile.html.slim2
-rw-r--r--app/views/referential_companies/_form.html.slim37
-rw-r--r--app/views/referential_companies/edit.html.slim4
-rw-r--r--app/views/referential_companies/new.html.slim6
-rw-r--r--app/views/referential_companies/show.html.slim13
-rw-r--r--app/views/referentials/_form.html.slim2
-rw-r--r--app/views/referentials/select_compliance_control_set.html.slim2
-rw-r--r--app/views/shared/custom_fields/_attachment.html.slim4
-rw-r--r--app/views/shared/iev_interfaces/_messages.html.slim2
-rw-r--r--app/views/stif/dashboards/_dashboard.html.slim4
-rw-r--r--app/views/stop_areas/_form.html.slim8
-rw-r--r--app/views/stop_areas/autocomplete.rabl3
-rw-r--r--app/views/stop_areas/show.html.slim2
-rw-r--r--app/views/vehicle_journeys/index.html.slim1
-rw-r--r--config/initializers/simple_form_bootstrap.rb4
-rw-r--r--config/locales/compliance_check_messages.en.yml3
-rw-r--r--config/locales/compliance_check_messages.fr.yml3
-rw-r--r--config/locales/compliance_control_blocks.en.yml10
-rw-r--r--config/locales/compliance_control_blocks.fr.yml10
-rw-r--r--config/locales/compliance_controls.en.yml7
-rw-r--r--config/locales/compliance_controls.fr.yml7
-rw-r--r--config/locales/dashboard.en.yml2
-rw-r--r--config/locales/dashboard.fr.yml2
-rw-r--r--config/locales/journey_patterns.en.yml11
-rw-r--r--config/locales/journey_patterns.fr.yml10
-rw-r--r--config/locales/layouts.en.yml1
-rw-r--r--config/locales/layouts.fr.yml1
-rw-r--r--config/locales/stop_areas.en.yml6
-rw-r--r--config/locales/stop_areas.fr.yml6
-rw-r--r--config/locales/vehicle_journey_exports.en.yml2
-rw-r--r--config/locales/vehicle_journeys.en.yml35
-rw-r--r--config/locales/vehicle_journeys.fr.yml27
-rw-r--r--config/locales/will_paginate.en.yml8
-rw-r--r--db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb5
-rw-r--r--db/migrate/20180316115003_add_custom_field_values_to_companies.rb5
-rw-r--r--db/schema.rb5
-rw-r--r--db/seeds/seed_helpers.rb12
-rw-r--r--db/seeds/stif.seeds.rb53
-rw-r--r--lib/tom_tom.rb4
-rw-r--r--lib/tom_tom/matrix.rb114
-rw-r--r--lib/tom_tom/matrix/point.rb18
-rw-r--r--lib/tom_tom/matrix/request_json_serializer.rb25
-rw-r--r--spec/controllers/exports_controller_spec.rb2
-rw-r--r--spec/factories/custom_fields.rb2
-rw-r--r--spec/features/calendars_permissions_spec.rb4
-rw-r--r--spec/features/calendars_spec.rb16
-rw-r--r--spec/features/compliance_control_sets_spec.rb2
-rw-r--r--spec/fixtures/tom_tom_matrix.json123
-rw-r--r--spec/lib/compliance_control_set_cloner_spec.rb2
-rw-r--r--spec/lib/compliance_control_set_copier_spec.rb2
-rw-r--r--spec/lib/tom_tom/matrix/request_json_serializer_spec.rb39
-rw-r--r--spec/lib/tom_tom/matrix_spec.rb228
-rw-r--r--spec/models/chouette/route/route_stop_points_spec.rb18
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb1
-rw-r--r--spec/models/compliance_control_block_spec.rb12
-rw-r--r--spec/models/custom_field_spec.rb67
-rw-r--r--spec/models/referential_metadata_spec.rb19
-rw-r--r--spec/models/referential_spec.rb2
-rw-r--r--spec/services/route_way_cost_calculator_spec.rb10
-rw-r--r--spec/support/referential.rb12
-rw-r--r--spec/views/companies/edit.html.erb_spec.rb5
-rw-r--r--spec/views/companies/new.html.erb_spec.rb5
-rw-r--r--spec/views/stop_areas/edit.html.erb_spec.rb1
-rw-r--r--spec/views/stop_areas/new.html.erb_spec.rb1
140 files changed, 1419 insertions, 298 deletions
diff --git a/app/assets/stylesheets/components/_main_nav.sass b/app/assets/stylesheets/components/_main_nav.sass
index 8e164fa01..ef3f14762 100644
--- a/app/assets/stylesheets/components/_main_nav.sass
+++ b/app/assets/stylesheets/components/_main_nav.sass
@@ -346,6 +346,7 @@ $menuW: 300px
flex: 1 1
height: $menuH
position: relative
+ margin-right: 2em
h1
position: absolute
@@ -355,8 +356,8 @@ $menuW: 300px
line-height: 1.1
white-space: nowrap
max-height: 1.1em
- margin: 0 -1.4em 0 0
- padding: 0 1.4em 5px 0
+ margin: 0
+ padding: 0 0 5px 0
overflow: hidden
text-overflow: ellipsis
diff --git a/app/assets/stylesheets/components/_pagination.sass b/app/assets/stylesheets/components/_pagination.sass
index 88ba61c3c..b811a559c 100644
--- a/app/assets/stylesheets/components/_pagination.sass
+++ b/app/assets/stylesheets/components/_pagination.sass
@@ -7,6 +7,9 @@
border-radius: 0
line-height: 34px
+ &:first-letter
+ text-transform: capitalize
+
.page_links
display: inline-block
vertical-align: top
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c4961123d..9a83394e2 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -36,12 +36,6 @@ class ApplicationController < ActionController::Base
end
helper_method :current_organisation
- def current_functional_scope
- functional_scope = current_organisation.sso_attributes.try(:[], "functional_scope") if current_organisation
- JSON.parse(functional_scope) if functional_scope
- end
- helper_method :current_functional_scope
-
def collection_name
self.class.name.split("::").last.gsub('Controller', '').underscore
end
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 3d88e4910..adb3b4764 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -104,6 +104,10 @@ class CalendarsController < ChouetteController
end
end
+ def begin_of_association_chain
+ current_organisation
+ end
+
def ransack_contains_date
date =[]
if params[:q] && !params[:q]['contains_date(1i)'].empty?
diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb
index 4afd12be1..a09cab783 100644
--- a/app/controllers/companies_controller.rb
+++ b/app/controllers/companies_controller.rb
@@ -38,12 +38,14 @@ class CompaniesController < ChouetteController
protected
def collection
- @q = line_referential.companies.search(params[:q])
-
+ scope = line_referential.companies
+ @q = scope.search(params[:q])
+ ids = @q.result(:distinct => true).pluck(:id)
+ scope = scope.where(id: ids)
if sort_column && sort_direction
- @companies ||= @q.result(:distinct => true).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
+ @companies ||= scope.order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
else
- @companies ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page])
+ @companies ||= scope.order(:name).paginate(:page => params[:page])
end
end
@@ -69,7 +71,9 @@ class CompaniesController < ChouetteController
end
def company_params
- params.require(:company).permit( :objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone )
+ fields = [:objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone]
+ fields += permitted_custom_fields_params(Chouette::Company.custom_fields(line_referential.workgroup))
+ params.require(:company).permit( fields )
end
private
diff --git a/app/controllers/compliance_control_blocks_controller.rb b/app/controllers/compliance_control_blocks_controller.rb
index 9eee8dfaf..1173a548a 100644
--- a/app/controllers/compliance_control_blocks_controller.rb
+++ b/app/controllers/compliance_control_blocks_controller.rb
@@ -4,10 +4,23 @@ class ComplianceControlBlocksController < ChouetteController
belongs_to :compliance_control_set
actions :all, :except => [:show, :index]
+ after_action :display_errors, only: [:create, :update]
+
+ def display_errors
+ unless @compliance_control_block.errors[:condition_attributes].empty?
+ flash[:error] = @compliance_control_block.errors[:condition_attributes].join(', ')
+ end
+ end
+
private
def compliance_control_block_params
params.require(:compliance_control_block).permit(:transport_mode, :transport_submode)
end
+ protected
+
+ alias_method :compliance_control_set, :parent
+ helper_method :compliance_control_set
+
end
diff --git a/app/controllers/compliance_controls_controller.rb b/app/controllers/compliance_controls_controller.rb
index 73dc18f59..7df922d01 100644
--- a/app/controllers/compliance_controls_controller.rb
+++ b/app/controllers/compliance_controls_controller.rb
@@ -5,7 +5,7 @@ class ComplianceControlsController < ChouetteController
actions :all, :except => [:index]
def select_type
- @sti_subclasses = ComplianceControl.subclasses
+ @sti_subclasses = ComplianceControl.subclasses.sort_by {|compliance_control| compliance_control.default_code}
end
def show
diff --git a/app/controllers/exports_controller.rb b/app/controllers/exports_controller.rb
index a5282a514..c89da5000 100644
--- a/app/controllers/exports_controller.rb
+++ b/app/controllers/exports_controller.rb
@@ -3,13 +3,14 @@ class ExportsController < ChouetteController
include RansackDateFilter
include IevInterfaces
skip_before_action :authenticate_user!, only: [:upload]
+ skip_before_action :verify_authenticity_token, only: [:upload]
defaults resource_class: Export::Base, collection_name: 'exports', instance_name: 'export'
def upload
if params[:token] == resource.token_upload
resource.file = params[:file]
resource.save!
- redirect_to [resource.workbench, resource]
+ render json: {status: :ok}
else
user_not_authorized
end
diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb
index 806a70c8f..200e56a89 100644
--- a/app/controllers/referential_companies_controller.rb
+++ b/app/controllers/referential_companies_controller.rb
@@ -40,11 +40,12 @@ class ReferentialCompaniesController < ChouetteController
end
@q = scope.search(params[:q])
-
+ ids = @q.result(:distinct => true).pluck(:id)
+ scope = scope.where(id: ids)
if sort_column && sort_direction
- @companies ||= @q.result(:distinct => true).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
+ @companies ||= scope.order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
else
- @companies ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page])
+ @companies ||= scope.order(:name).paginate(:page => params[:page])
end
end
@@ -57,7 +58,9 @@ class ReferentialCompaniesController < ChouetteController
end
def company_params
- params.require(:company).permit( :objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone )
+ fields = [:objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone]
+ fields += permitted_custom_fields_params(Chouette::Company.custom_fields(@referential.workgroup))
+ params.require(:company).permit( fields )
end
private
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 6e3694547..fe661651e 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -143,7 +143,7 @@ class ReferentialsController < ChouetteController
def build_referential
if params[:from]
source_referential = Referential.find(params[:from])
- @referential = Referential.new_from(source_referential, current_functional_scope)
+ @referential = Referential.new_from(source_referential, current_organisation)
end
@referential.data_format = current_organisation.data_format
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index d0d9f652d..b2634467d 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -161,7 +161,7 @@ class StopAreasController < ChouetteController
helper_method :current_referential
def stop_area_params
- params.require(:stop_area).permit(
+ fields = [
:area_type,
:children_ids,
:city_name,
@@ -192,7 +192,8 @@ class StopAreasController < ChouetteController
:kind,
:status,
localized_names: Chouette::StopArea::AVAILABLE_LOCALIZATIONS
- )
+ ] + permitted_custom_fields_params(Chouette::StopArea.custom_fields) # XXX filter on the workgroup
+ params.require(:stop_area).permit(fields)
end
# Fake ransack filter
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 479b661c8..a0c6796ea 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -136,5 +136,9 @@ module ApplicationHelper
url_for(:controller => "/help", :action => "show") + '/' + target
end
-
+ def permitted_custom_fields_params custom_fields
+ [{
+ custom_field_values: custom_fields.map(&:code)
+ }]
+ end
end
diff --git a/app/helpers/routes_helper.rb b/app/helpers/routes_helper.rb
index 61714a066..84b4015a2 100644
--- a/app/helpers/routes_helper.rb
+++ b/app/helpers/routes_helper.rb
@@ -24,7 +24,7 @@ module RoutesHelper
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)
+ stop_area_attributes.merge(stop_point_attributes).merge(stoppoint_id: stop_point.id, stoparea_id: stop_point.stop_area.id, stoparea_kind: stop_point.stop_area.kind).merge(user_objectid: stop_point.stop_area.user_objectid)
end
data = data.to_json if serialize
data
diff --git a/app/helpers/stop_areas_helper.rb b/app/helpers/stop_areas_helper.rb
index 1c9d974a1..032f68494 100644
--- a/app/helpers/stop_areas_helper.rb
+++ b/app/helpers/stop_areas_helper.rb
@@ -68,7 +68,11 @@ module StopAreasHelper
end
def stop_area_registration_number_value stop_area
- stop_area&.registration_number || stop_area&.stop_area_referential&.generate_registration_number
+ stop_area&.registration_number
+ end
+
+ def stop_area_registration_number_hint
+ t "formtastic.hints.stop_area.registration_number"
end
def stop_area_status(stop_area)
diff --git a/app/javascript/journey_patterns/components/ConfirmModal.js b/app/javascript/journey_patterns/components/ConfirmModal.js
index ccd0a9384..fdf32649f 100644
--- a/app/javascript/journey_patterns/components/ConfirmModal.js
+++ b/app/javascript/journey_patterns/components/ConfirmModal.js
@@ -9,11 +9,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Confirmation</h4>
+ <h4 className='modal-title'>{I18n.t('journey_patterns.show.confirmation')}</h4>
</div>
<div className='modal-body'>
<div className='mt-md mb-md'>
- <p>Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?</p>
+ <p>{I18n.t('journey_patterns.show.confirm_page_change')}</p>
</div>
</div>
<div className='modal-footer'>
@@ -23,7 +23,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan
type='button'
onClick={() => { onModalCancel(modal.confirmModal.callback) }}
>
- Ne pas valider
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
@@ -31,7 +31,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan
type='button'
onClick={() => { onModalAccept(modal.confirmModal.callback, journeyPatterns) }}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</div>
diff --git a/app/javascript/journey_patterns/components/CreateModal.js b/app/javascript/journey_patterns/components/CreateModal.js
index a6c1b608a..36b5740dc 100644
--- a/app/javascript/journey_patterns/components/CreateModal.js
+++ b/app/javascript/journey_patterns/components/CreateModal.js
@@ -38,14 +38,14 @@ export default class CreateModal extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Ajouter une mission</h4>
+ <h4 className='modal-title'>{I18n.t('journey_patterns.actions.new')}</h4>
</div>
{(this.props.modal.type == 'create') && (
<form>
<div className='modal-body'>
<div className='form-group'>
- <label className='control-label is-required'>Nom</label>
+ <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'name')}</label>
<input
type='text'
ref='name'
@@ -57,7 +57,7 @@ export default class CreateModal extends Component {
<div className='row'>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
<div className='form-group'>
- <label className='control-label is-required'>Nom public</label>
+ <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}c</label>
<input
type='text'
ref='published_name'
@@ -69,7 +69,7 @@ export default class CreateModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
<div className='form-group'>
- <label className='control-label'>Code mission</label>
+ <label className='control-label'>{I18n.attribute_name('journey_pattern', 'registration_number')}</label>
<input
type='text'
ref='registration_number'
@@ -87,14 +87,14 @@ export default class CreateModal extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</form>
diff --git a/app/javascript/journey_patterns/components/EditModal.js b/app/javascript/journey_patterns/components/EditModal.js
index c960cb41c..1960849fb 100644
--- a/app/javascript/journey_patterns/components/EditModal.js
+++ b/app/javascript/journey_patterns/components/EditModal.js
@@ -18,12 +18,12 @@ export default class EditModal extends Component {
if (this.props.editMode) {
return (
<h4 className='modal-title'>
- Editer la mission
+ {I18n.t('journey_patterns.actions.edit')}
{this.props.modal.type == 'edit' && <em> "{this.props.modal.modalProps.journeyPattern.name}"</em>}
</h4>
)
} else {
- return <h4 className='modal-title'> Informations </h4>
+ return <h4 className='modal-title'> {I18n.t('journey_patterns.show.informations')} </h4>
}
}
@@ -41,7 +41,7 @@ export default class EditModal extends Component {
<form>
<div className='modal-body'>
<div className='form-group'>
- <label className='control-label is-required'>Nom</label>
+ <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'name')}</label>
<input
type='text'
ref='name'
@@ -57,7 +57,7 @@ export default class EditModal extends Component {
<div className='row'>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
<div className='form-group'>
- <label className='control-label is-required'>Nom public</label>
+ <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}</label>
<input
type='text'
ref='published_name'
@@ -72,7 +72,7 @@ export default class EditModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
<div className='form-group'>
- <label className='control-label'>Code mission</label>
+ <label className='control-label'>{I18n.attribute_name('journey_pattern', 'registration_number')}</label>
<input
type='text'
ref='registration_number'
@@ -86,7 +86,7 @@ export default class EditModal extends Component {
</div>
</div>
<div>
- <label className='control-label'>Signature métier</label>
+ <label className='control-label'>{I18n.attribute_name('journey_pattern', 'checksum')}</label>
<input
type='text'
ref='checksum'
@@ -105,14 +105,14 @@ export default class EditModal extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
}
diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js
index 00b5497f7..d381b0d50 100644
--- a/app/javascript/journey_patterns/components/JourneyPattern.js
+++ b/app/javascript/journey_patterns/components/JourneyPattern.js
@@ -23,7 +23,7 @@ export default class JourneyPattern extends Component{
let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid
return (
- <a href={vjURL}>Horaires des courses</a>
+ <a href={vjURL}>{I18n.t('journey_patterns.journey_pattern.vehicle_journey_at_stops')}</a>
)
}
@@ -139,7 +139,7 @@ export default class JourneyPattern extends Component{
<div className='th'>
<div className='strong mb-xs'>{this.props.value.object_id ? this.props.value.short_id : '-'}</div>
<div>{this.props.value.registration_number}</div>
- <div>{actions.getChecked(this.props.value.stop_points).length} arrêt(s)</div>
+ <div>{I18n.t('journey_patterns.show.stop_points_count', {count: actions.getChecked(this.props.value.stop_points).length})}</div>
{this.hasFeature('costs_in_journey_patterns') &&
<div className="small row totals">
<span className="col-md-6"><i className="fa fa-arrows-h"></i>{totalDistance}</span>
@@ -167,7 +167,7 @@ export default class JourneyPattern extends Component{
data-toggle='modal'
data-target='#JourneyPatternModal'
>
- {this.props.editMode ? 'Editer' : 'Consulter'}
+ {this.props.editMode ? I18n.t('actions.edit') : I18n.t('actions.show')}
</button>
</li>
<li className={this.props.value.object_id ? '' : 'disabled'}>
@@ -183,7 +183,7 @@ export default class JourneyPattern extends Component{
this.props.onDeleteJourneyPattern(this.props.index)}
}
>
- <span className='fa fa-trash'></span>Supprimer
+ <span className='fa fa-trash'></span>{I18n.t('actions.destroy')}
</button>
</li>
</ul>
diff --git a/app/javascript/journey_patterns/components/JourneyPatterns.js b/app/javascript/journey_patterns/components/JourneyPatterns.js
index 930acb390..91c783189 100644
--- a/app/javascript/journey_patterns/components/JourneyPatterns.js
+++ b/app/javascript/journey_patterns/components/JourneyPatterns.js
@@ -84,14 +84,14 @@ export default class JourneyPatterns extends Component {
<div className='col-lg-12'>
{(this.props.status.fetchSuccess == false) && (
<div className="alert alert-danger mt-sm">
- <strong>Erreur : </strong>
- la récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème
+ <strong>{I18n.t('error')} : </strong>
+ {I18n.t('journeys_patterns.journey_pattern.fetching_error')}
</div>
)}
{ _.some(this.props.journeyPatterns, 'errors') && (
<div className="alert alert-danger mt-sm">
- <strong>Erreur : </strong>
+ <strong> {I18n.t('error')} : </strong>
{this.props.journeyPatterns.map((jp, index) =>
jp.errors && jp.errors.map((err, i) => {
return (
@@ -107,9 +107,9 @@ export default class JourneyPatterns extends Component {
<div className={'table table-2entries mt-sm mb-sm' + ((this.props.journeyPatterns.length > 0) ? '' : ' no_result')}>
<div className='t2e-head w20'>
<div className='th'>
- <div className='strong mb-xs'>ID Mission</div>
- <div>Code mission</div>
- <div>Nb arrêts</div>
+ <div className='strong mb-xs'>{I18n.t('objectid')}</div>
+ <div>{I18n.attribute_name('journey_pattern', 'registration_number')}</div>
+ <div>{I18n.attribute_name('journey_pattern', 'stop_points')}</div>
{ this.hasFeature('costs_in_journey_patterns') &&
<div>
<div>{I18n.attribute_name('journey_pattern', 'full_journey_time')}</div>
diff --git a/app/javascript/journey_patterns/components/Navigate.js b/app/javascript/journey_patterns/components/Navigate.js
index 78f324a7d..9e454da5e 100644
--- a/app/javascript/journey_patterns/components/Navigate.js
+++ b/app/javascript/journey_patterns/components/Navigate.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
+import capitalize from 'lodash/capitalize'
import actions from '../actions'
export default function Navigate({ dispatch, journeyPatterns, pagination, status }) {
@@ -17,7 +18,7 @@ export default function Navigate({ dispatch, journeyPatterns, pagination, status
<div className='row'>
<div className='col-lg-12 text-right'>
<div className='pagination'>
- Liste des missions {firstItemOnPage} à {(lastItemOnPage < pagination.totalCount) ? lastItemOnPage : pagination.totalCount} sur {pagination.totalCount}
+ {I18n.t('will_paginate.page_entries_info.multi_page', { model: capitalize(I18n.model_name('journey_pattern', { plural: true })), from: firstItemOnPage, to: lastItemOnPage, count: pagination.totalCount})}
<form className='page_links' onSubmit={e => {
e.preventDefault()
}}>
diff --git a/app/javascript/packs/routes/edit.js b/app/javascript/packs/routes/edit.js
index 81745ad23..fc7aa203d 100644
--- a/app/javascript/packs/routes/edit.js
+++ b/app/javascript/packs/routes/edit.js
@@ -29,6 +29,7 @@ const getInitialState = () => {
state.push({
stoppoint_id: v.stoppoint_id,
stoparea_id: v.stoparea_id,
+ stoparea_kind: v.stoparea_kind,
user_objectid: v.user_objectid,
short_name: v.short_name ? v.short_name.replace("&#39;", "\'") : '',
area_type: v.area_type,
diff --git a/app/javascript/routes/actions/index.js b/app/javascript/routes/actions/index.js
index 13b2d60b2..5fbf5bce9 100644
--- a/app/javascript/routes/actions/index.js
+++ b/app/javascript/routes/actions/index.js
@@ -56,7 +56,12 @@ const actions = {
unselectMarker: (index) => ({
type: 'UNSELECT_MARKER',
index
- })
+ }),
+ defaultAttribute: (attribute, stopAreaKind) => {
+ if (attribute !== '') return attribute
+ if (stopAreaKind === undefined) return ''
+ return stopAreaKind === "commercial" ? "normal" : "forbidden"
+ }
}
module.exports = actions
diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js
index 035bce155..89e1b6cfa 100644
--- a/app/javascript/routes/components/BSelect2.js
+++ b/app/javascript/routes/components/BSelect2.js
@@ -17,6 +17,7 @@ export default class BSelect3 extends Component {
this.props.onChange(this.props.index, {
text: e.currentTarget.textContent,
stoparea_id: e.currentTarget.value,
+ stoparea_kind: e.params.data.kind,
user_objectid: e.params.data.user_objectid,
longitude: e.params.data.longitude,
latitude: e.params.data.latitude,
diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js
index af51a6bb4..368ec8261 100644
--- a/app/javascript/routes/components/StopPoint.js
+++ b/app/javascript/routes/components/StopPoint.js
@@ -4,6 +4,8 @@ import PropTypes from 'prop-types'
import BSelect2 from './BSelect2'
import OlMap from './OlMap'
+import { defaultAttribute } from '../actions'
+
export default function StopPoint(props, {I18n}) {
return (
<div className='nested-fields'>
@@ -17,14 +19,14 @@ export default function StopPoint(props, {I18n}) {
</div>
<div>
- <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}>
+ <select className='form-control' value={defaultAttribute(props.value.for_boarding, props.value.stoparea_kind)} id="for_boarding" onChange={props.onSelectChange}>
<option value="normal">{I18n.t('routes.edit.stop_point.boarding.normal')}</option>
<option value="forbidden">{I18n.t('routes.edit.stop_point.boarding.forbidden')}</option>
</select>
</div>
<div>
- <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}>
+ <select className='form-control' value={defaultAttribute(props.value.for_alighting, props.value.stoparea_kind)} id="for_alighting" onChange={props.onSelectChange}>
<option value="normal">{I18n.t('routes.edit.stop_point.alighting.normal')}</option>
<option value="forbidden">{I18n.t('routes.edit.stop_point.alighting.forbidden')}</option>
</select>
diff --git a/app/javascript/routes/index.js b/app/javascript/routes/index.js
index febae7d54..3c7322953 100644
--- a/app/javascript/routes/index.js
+++ b/app/javascript/routes/index.js
@@ -26,6 +26,7 @@ const getInitialState = () => {
state.push({
stoppoint_id: v.stoppoint_id,
stoparea_id: v.stoparea_id,
+ stoparea_kind: v.stoparea_kind,
user_objectid: v.user_objectid,
short_name: v.short_name ? v.short_name.replace("&#39;", "\'") : '',
area_type: v.area_type,
@@ -36,8 +37,8 @@ const getInitialState = () => {
name: v.name ? v.name.replace("&#39;", "\'") : '',
registration_number: v.registration_number,
text: fancyText,
- for_boarding: v.for_boarding || "normal",
- for_alighting: v.for_alighting || "normal",
+ for_boarding: v.for_boarding || '',
+ for_alighting: v.for_alighting || '',
longitude: v.longitude || 0,
latitude: v.latitude || 0,
comment: v.comment ? v.comment.replace("&#39;", "\'") : '',
diff --git a/app/javascript/routes/reducers/stopPoints.js b/app/javascript/routes/reducers/stopPoints.js
index 0b42b504f..ba183d002 100644
--- a/app/javascript/routes/reducers/stopPoints.js
+++ b/app/javascript/routes/reducers/stopPoints.js
@@ -8,8 +8,8 @@ const stopPoint = (state = {}, action, length) => {
text: '',
index: length,
edit: true,
- for_boarding: 'normal',
- for_alighting: 'normal',
+ for_boarding: '',
+ for_alighting: '',
olMap: {
isOpened: false,
json: {}
@@ -68,6 +68,7 @@ const stopPoints = (state = [], action) => {
stoppoint_id: t.stoppoint_id,
text: action.text.text,
stoparea_id: action.text.stoparea_id,
+ stoparea_kind: action.text.stoparea_kind,
user_objectid: action.text.user_objectid,
latitude: action.text.latitude,
longitude: action.text.longitude,
diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js
index 98b9eab4b..3127d11b8 100644
--- a/app/javascript/time_tables/actions/index.js
+++ b/app/javascript/time_tables/actions/index.js
@@ -4,7 +4,6 @@ import reject from 'lodash/reject'
import some from 'lodash/some'
import every from 'lodash/every'
import clone from '../../helpers/clone'
-const I18n = clone(window, "I18n")
const actions = {
weekDays: (index) => {
diff --git a/app/javascript/time_tables/containers/App.js b/app/javascript/time_tables/containers/App.js
index 5963f8f1d..c12e33ef5 100644
--- a/app/javascript/time_tables/containers/App.js
+++ b/app/javascript/time_tables/containers/App.js
@@ -10,7 +10,6 @@ import SaveTimetable from './SaveTimetable'
import ConfirmModal from './ConfirmModal'
import ErrorModal from './ErrorModal'
import clone from '../../helpers/clone'
-const I18n = clone(window, "I18n", true)
class App extends Component {
componentDidMount(){
diff --git a/app/javascript/vehicle_journeys/components/ConfirmModal.js b/app/javascript/vehicle_journeys/components/ConfirmModal.js
index 75e8a3932..330b4e02f 100644
--- a/app/javascript/vehicle_journeys/components/ConfirmModal.js
+++ b/app/javascript/vehicle_journeys/components/ConfirmModal.js
@@ -16,7 +16,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan
type='button'
onClick={() => { onModalCancel(modal.confirmModal.callback) }}
>
- Ne pas valider
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-danger'
@@ -24,7 +24,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan
type='button'
onClick={() => { onModalAccept(modal.confirmModal.callback, vehicleJourneys) }}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</div>
diff --git a/app/javascript/vehicle_journeys/components/Navigate.js b/app/javascript/vehicle_journeys/components/Navigate.js
index 24843babc..e79823e49 100644
--- a/app/javascript/vehicle_journeys/components/Navigate.js
+++ b/app/javascript/vehicle_journeys/components/Navigate.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
+import capitalize from 'lodash/capitalize'
import actions from'../actions'
export default function Navigate({ dispatch, vehicleJourneys, pagination, status, filters}) {
@@ -17,7 +18,7 @@ export default function Navigate({ dispatch, vehicleJourneys, pagination, status
if(status.fetchSuccess == true) {
return (
<div className="pagination">
- {I18n.t("vehicle_journeys.vehicle_journeys_matrix.pagination", {minVJ, maxVJ, total:pagination.totalCount})}
+ {I18n.t('will_paginate.page_entries_info.multi_page', { model: capitalize(I18n.model_name('vehicle_journey', { plural: true })), from: minVJ, to: maxVJ, count: pagination.totalCount })}
<form className='page_links' onSubmit={e => {e.preventDefault()}}>
<button
onClick={e => {
diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
index a60429765..f49b51f08 100644
--- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js
+++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
@@ -47,7 +47,7 @@ export default class CreateModal extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Ajouter une course</h4>
+ <h4 className='modal-title'>{I18n.t('vehicle_journeys.actions.new')}</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -57,7 +57,7 @@ export default class CreateModal extends Component {
<div className='row'>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>
<div className='form-group'>
- <label className='control-label'>Nom de la course</label>
+ <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_name')}</label>
<input
type='text'
ref='published_journey_name'
@@ -68,7 +68,7 @@ export default class CreateModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>
<div className='form-group'>
- <label className='control-label'>Nom du transporteur</label>
+ <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company_name')}</label>
<CompanySelect2
company = {this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company || undefined}
onSelect2Company = {(e) => this.props.onSelect2Company(e)}
@@ -78,7 +78,7 @@ export default class CreateModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>
<div className='form-group'>
- <label className='control-label is-required'>Nom public de la mission</label>
+ <label className='control-label is-required'>{I18n.attribute_name('vehicle_journey', 'journey_pattern_published_name')}</label>
<MissionSelect2
selection={this.props.modal.modalProps}
onSelect2JourneyPattern={this.props.onSelect2JourneyPattern}
@@ -89,7 +89,7 @@ export default class CreateModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>
<div className='form-group'>
- <label className='control-label'>Numéro de train</label>
+ <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'published_journey_identifier')}</label>
<input
type='text'
ref='published_journey_identifier'
@@ -105,7 +105,7 @@ export default class CreateModal extends Component {
/>
{ this.props.modal.modalProps.selectedJPModal && this.props.modal.modalProps.selectedJPModal.full_schedule && <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>
<div className='form-group'>
- <label className='control-label'>Heure de départ</label>
+ <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'start_time')}</label>
<div className='input-group time'>
<input
type='number'
@@ -142,14 +142,14 @@ export default class CreateModal extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</form>
diff --git a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js
index 90d72a801..827c36b76 100644
--- a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js
+++ b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js
@@ -27,6 +27,19 @@ export default class CustomFieldsInputs extends Component {
)
}
+ stringInput(cf){
+ return(
+ <input
+ type='text'
+ ref={'custom_fields.' + cf.code}
+ className='form-control'
+ disabled={this.props.disabled}
+ defaultValue={cf.value}
+ onChange={(e) => this.props.onUpdate(cf.code, e.target.value) }
+ />
+ )
+ }
+
render() {
return (
<div>
diff --git a/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js b/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js
index 4815003d3..b1ce3786b 100644
--- a/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js
@@ -13,7 +13,7 @@ export default function DeleteVehicleJourneys({onDeleteVehicleJourneys, vehicleJ
e.preventDefault()
onDeleteVehicleJourneys()
}}
- title='Supprimer'
+ title={ I18n.t('actions.delete') }
>
<span className='fa fa-trash'></span>
</button>
diff --git a/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
index 102a87d85..d7e48bf08 100644
--- a/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
@@ -93,7 +93,7 @@ export default class DuplicateVehicleJourney extends Component {
<div className='modal-content'>
<div className='modal-header'>
<h4 className='modal-title'>
- Dupliquer { actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'plusieurs courses' : 'une course' }
+ {I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate', { count: actions.getSelected(this.props.vehicleJourneys).length })}
</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -102,7 +102,7 @@ export default class DuplicateVehicleJourney extends Component {
<form className='form-horizontal'>
<div className='modal-body'>
<div className={'form-group ' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'hidden' : '' )}>
- <label className='control-label is-required col-sm-8'>Horaire de départ indicatif</label>
+ <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.start_time')}</label>
<span className="col-sm-4">
<span className={'input-group time' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? ' disabled' : '')}>
<input
@@ -133,7 +133,7 @@ export default class DuplicateVehicleJourney extends Component {
</div>
<div className='form-group'>
- <label className='control-label is-required col-sm-8'>Nombre de courses à créer et dupliquer</label>
+ <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.number')}</label>
<div className="col-sm-4">
<input
type='number'
@@ -152,7 +152,7 @@ export default class DuplicateVehicleJourney extends Component {
</div>
<div className='form-group'>
- <label className='control-label is-required col-sm-8'>Décalage à partir duquel on créé les courses</label>
+ <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.delta')}</label>
<span className="col-sm-4">
<input
type='number'
@@ -178,7 +178,7 @@ export default class DuplicateVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className={'btn btn-primary ' + (this.state.additional_time == 0 && this.state.originalDT.hour == this.state.duplicate_time_hh && this.state.originalDT.minute == this.state.duplicate_time_mm ? 'disabled' : '')}
@@ -186,7 +186,7 @@ export default class DuplicateVehicleJourney extends Component {
onClick={this.handleSubmit}
disabled={this.disableValidateButton()}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</form>
diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
index 795c2ecff..e4e266c79 100644
--- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -173,14 +173,14 @@ export default class EditVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
}
diff --git a/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
index 880542216..5d300f70c 100644
--- a/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
@@ -43,11 +43,11 @@ export default class NotesEditVehicleJourney extends Component {
renderAssociatedFN() {
if (this.footnotes().associated.length == 0) {
- return <h3>Aucune note associée</h3>
+ return <h3>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_footnotes')}</h3>
} else {
return (
<div>
- <h3>Notes associées :</h3>
+ <h3>{I18n.t('vehicle_journeys.form.purchase_windows')} :</h3>
{this.footnotes().associated.map((lf, i) =>
<div
key={i}
@@ -68,13 +68,13 @@ export default class NotesEditVehicleJourney extends Component {
}
renderToAssociateFN() {
- if (window.line_footnotes.length == 0) return <h3>La ligne ne possède pas de notes</h3>
+ if (window.line_footnotes.length == 0) return <h3>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_line_footnotes')}</h3>
if (this.footnotes().to_associate.length == 0) return false
return (
<div>
- <h3 className='mt-lg'>Sélectionnez les notes à associer à cette course :</h3>
+ <h3 className='mt-lg'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.select_footnotes')} :</h3>
{this.footnotes().to_associate.map((lf, i) =>
<div key={i} className='panel panel-default'>
<div className='panel-heading'>
@@ -111,7 +111,7 @@ export default class NotesEditVehicleJourney extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Notes</h4>
+ <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.footnotes')}</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -130,14 +130,14 @@ export default class NotesEditVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
}
diff --git a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
index ce9a4cde9..30c511302 100644
--- a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
@@ -44,7 +44,7 @@ export default class PurchaseWindowsEditVehicleJourney extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Calendriers commerciaux associés</h4>
+ <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.purchase_windows')}s</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -58,7 +58,7 @@ export default class PurchaseWindowsEditVehicleJourney extends Component {
<div className='wrapper'>
<div>
<div className='form-group'>
- <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? "Aucun calendrier commercial associé" : "Calendriers commerciaux associés"}</label>
+ <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_purchase_windows') : I18n.t('vehicle_journeys.form.purchase_windows')}</label>
</div>
</div>
<div></div>
@@ -117,14 +117,14 @@ export default class PurchaseWindowsEditVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
}
diff --git a/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js
index 6574bfa2d..bc3d8db34 100644
--- a/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js
@@ -27,6 +27,7 @@ export default class ShiftVehicleJourney extends Component {
}
render() {
+ let id = this.props.modal.type == 'shift' && actions.getSelected(this.props.vehicleJourneys)[0].short_id
if(this.props.status.isFetching == true) {
return false
}
@@ -48,10 +49,7 @@ export default class ShiftVehicleJourney extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Mettre à jour une course</h4>
- {(this.props.modal.type == 'shift') && (
- <em>Mettre à jour les horaires de la course {actions.getSelected(this.props.vehicleJourneys)[0].short_id}</em>
- )}
+ <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.slide_title', {id: id})}</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -61,7 +59,7 @@ export default class ShiftVehicleJourney extends Component {
<div className='row'>
<div className='col-lg-4 col-lg-offset-4 col-md-4 col-md-offset-4 col-sm-4 col-sm-offset-4 col-xs-12'>
<div className='form-group'>
- <label className='control-label is-required'>Avec un décalage de</label>
+ <label className='control-label is-required'>{I18n.t('vehicle_journeys.form.slide_delta')}</label>
<input
type='number'
style={{'width': 104}}
@@ -85,14 +83,14 @@ export default class ShiftVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className={'btn btn-primary ' + (this.state.additional_time == 0 ? 'disabled' : '')}
type='button'
onClick={this.handleSubmit.bind(this)}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
</form>
diff --git a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
index f21480563..9ee2e1849 100644
--- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
@@ -44,7 +44,7 @@ export default class TimetablesEditVehicleJourney extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Calendriers associés</h4>
+ <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.time_tables')}</h4>
<span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
</div>
@@ -58,7 +58,7 @@ export default class TimetablesEditVehicleJourney extends Component {
<div className='wrapper'>
<div>
<div className='form-group'>
- <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? "Aucun calendrier associé" : "Calendriers associés"}</label>
+ <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_timetables'): I18n.t('vehicle_journeys.form.time_tables')}</label>
</div>
</div>
<div></div>
@@ -119,14 +119,14 @@ export default class TimetablesEditVehicleJourney extends Component {
type='button'
onClick={this.props.onModalClose}
>
- Annuler
+ {I18n.t('cancel')}
</button>
<button
className='btn btn-primary'
type='button'
onClick={this.handleSubmit}
>
- Valider
+ {I18n.t('actions.submit')}
</button>
</div>
}
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
index 5c7f75d99..b7e9691c1 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
@@ -16,6 +16,7 @@ export default class BSelect4 extends Component {
}
render() {
+ let placeHolder = I18n.t('')
return (
<Select2
data={(this.props.company) ? [this.props.company.name] : undefined}
@@ -29,7 +30,7 @@ export default class BSelect4 extends Component {
allowClear: true,
theme: 'bootstrap',
width: '100%',
- placeholder: 'Filtrer par transporteur...',
+ placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.affect_company'),
language: require('./fr'),
ajax: {
url: origin + path + '/companies.json' + '?line_id=' + line,
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
index 72dbd0152..96b34125d 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
@@ -74,7 +74,7 @@ export default class BSelect4 extends Component {
width: '100%',
escapeMarkup: function (markup) { return markup; },
templateResult: formatRepo,
- placeholder: 'Filtrer par code, nom ou OID de mission...',
+ placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.journey_pattern'),
language: require('./fr'),
allowClear: false,
escapeMarkup: function (markup) { return markup; },
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
index 0339455ca..9a345b464 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
@@ -26,7 +26,7 @@ export default class BSelect4 extends Component {
allowClear: false,
theme: 'bootstrap',
width: '100%',
- placeholder: 'Filtrer par calendrier...',
+ placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.timetable'),
language: require('./fr'),
ajax: {
url: origin + path + this.props.chunkURL,
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
index ccb4c9595..f5881cef7 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
@@ -25,7 +25,7 @@ export default class BSelect4b extends Component {
options={{
allowClear: false,
theme: 'bootstrap',
- placeholder: 'Filtrer par ID course...',
+ placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.id'),
width: '100%',
language: require('./fr'),
ajax: {
diff --git a/app/models/chouette/company.rb b/app/models/chouette/company.rb
index 53e412600..8d6dbee92 100644
--- a/app/models/chouette/company.rb
+++ b/app/models/chouette/company.rb
@@ -3,6 +3,8 @@ module Chouette
include CompanyRestrictions
include LineReferentialSupport
include ObjectidSupport
+ include CustomFieldsSupport
+
has_paper_trail class_name: 'PublicVersion'
has_many :lines
diff --git a/app/models/chouette/for_alighting_enumerations.rb b/app/models/chouette/for_alighting_enumerations.rb
index ab07a670d..2e15fcb58 100644
--- a/app/models/chouette/for_alighting_enumerations.rb
+++ b/app/models/chouette/for_alighting_enumerations.rb
@@ -3,6 +3,6 @@ module Chouette
extend Enumerize
extend ActiveModel::Naming
- enumerize :for_alighting, in: %w[normal forbidden request_stop is_flexible]
+ enumerize :for_alighting, in: %w[normal forbidden request_stop is_flexible], default: :normal
end
end
diff --git a/app/models/chouette/for_boarding_enumerations.rb b/app/models/chouette/for_boarding_enumerations.rb
index 48f8762c2..0190bf805 100644
--- a/app/models/chouette/for_boarding_enumerations.rb
+++ b/app/models/chouette/for_boarding_enumerations.rb
@@ -3,6 +3,6 @@ module Chouette
extend Enumerize
extend ActiveModel::Naming
- enumerize :for_boarding, in: %w[normal forbidden request_stop is_flexible]
+ enumerize :for_boarding, in: %w[normal forbidden request_stop is_flexible], default: :normal
end
end
diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb
index ae7c25377..c8a02da1f 100644
--- a/app/models/chouette/line.rb
+++ b/app/models/chouette/line.rb
@@ -51,6 +51,14 @@ module Chouette
)
}
+ scope :for_organisation, ->(organisation){
+ if objectids = organisation&.lines_scope
+ where(objectid: objectids)
+ else
+ all
+ end
+ }
+
def self.nullable_attributes
[:published_name, :number, :comment, :url, :color, :text_color, :stable_id]
end
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index ccdff609f..c263fa987 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -7,6 +7,7 @@ module Chouette
include StopAreaRestrictions
include StopAreaReferentialSupport
include ObjectidSupport
+ include CustomFieldsSupport
extend Enumerize
enumerize :area_type, in: Chouette::AreaType::ALL
@@ -49,7 +50,7 @@ module Chouette
validate :registration_number_is_set
before_validation do
- self.registration_number ||= self.stop_area_referential.generate_registration_number
+ self.registration_number = self.stop_area_referential.generate_registration_number unless self.registration_number.present?
end
def self.nullable_attributes
@@ -90,7 +91,7 @@ module Chouette
end
unless self.stop_area_referential.validates_registration_number(self.registration_number)
- errors.add(:registration_number, I18n.t('stop_areas.errors.registration_number.invalid'))
+ errors.add(:registration_number, I18n.t('stop_areas.errors.registration_number.invalid', mask: self.stop_area_referential.registration_number_format))
end
end
diff --git a/app/models/chouette/stop_point.rb b/app/models/chouette/stop_point.rb
index 27407243b..da2da998a 100644
--- a/app/models/chouette/stop_point.rb
+++ b/app/models/chouette/stop_point.rb
@@ -30,13 +30,6 @@ module Chouette
delegate :name, to: :stop_area
- after_create :set_defaults
- def set_defaults
- value = stop_area.kind == 'commercial' ? 'normal' : 'forbidden'
- update_attribute :for_boarding, value
- update_attribute :for_alighting, value
- end
-
before_destroy :remove_dependent_journey_pattern_stop_points
def remove_dependent_journey_pattern_stop_points
route.journey_patterns.each do |jp|
diff --git a/app/models/compliance_control.rb b/app/models/compliance_control.rb
index 537343005..1cc06f927 100644
--- a/app/models/compliance_control.rb
+++ b/app/models/compliance_control.rb
@@ -76,6 +76,7 @@ require_dependency 'generic_attribute_control/uniqueness'
require_dependency 'journey_pattern_control/duplicates'
require_dependency 'journey_pattern_control/vehicle_journey'
require_dependency 'line_control/route'
+require_dependency 'line_control/lines_scope'
require_dependency 'route_control/duplicates'
require_dependency 'route_control/journey_pattern'
require_dependency 'route_control/minimum_length'
diff --git a/app/models/compliance_control_block.rb b/app/models/compliance_control_block.rb
index d7d84fd06..bc5d6fd4a 100644
--- a/app/models/compliance_control_block.rb
+++ b/app/models/compliance_control_block.rb
@@ -12,6 +12,8 @@ class ComplianceControlBlock < ActiveRecord::Base
validates :transport_mode, presence: true
validates :compliance_control_set, presence: true
+ validates_uniqueness_of :condition_attributes, scope: :compliance_control_set_id
+
def name
ApplicationController.helpers.transport_mode_text(self)
end
diff --git a/app/models/concerns/custom_fields_support.rb b/app/models/concerns/custom_fields_support.rb
index 6c76bd653..ef6b4f4af 100644
--- a/app/models/concerns/custom_fields_support.rb
+++ b/app/models/concerns/custom_fields_support.rb
@@ -3,22 +3,51 @@ module CustomFieldsSupport
included do
validate :custom_fields_values_are_valid
+ after_initialize :initialize_custom_fields
- def self.custom_fields
- CustomField.where(resource_type: self.name.split("::").last)
+ def self.custom_fields workgroup=:all
+ fields = CustomField.where(resource_type: self.name.split("::").last)
+ fields = fields.where(workgroup_id: workgroup&.id) if workgroup != :all
+ fields
end
- def custom_fields
- CustomField::Collection.new self
+ def method_missing method_name, *args
+ if method_name =~ /custom_field_*/ && !@custom_fields_initialized
+ initialize_custom_fields
+ send method_name, *args
+ else
+ super method_name, *args
+ end
+ end
+
+ def custom_fields workgroup=:all
+ CustomField::Collection.new self, workgroup
+ end
+
+ def custom_field_values= vals
+ out = {}
+ custom_fields.each do |code, field|
+ out[code] = field.preprocess_value_for_assignment(vals.symbolize_keys[code.to_sym])
+ end
+ write_attribute :custom_field_values, out
+ end
+
+ def initialize_custom_fields
+ self.custom_field_values ||= {}
+ custom_fields(:all).values.each &:initialize_custom_field
+ custom_fields(:all).each do |k, v|
+ custom_field_values[k] ||= v.default_value
+ end
+ @custom_fields_initialized = true
end
def custom_field_value key
- (custom_field_values || {})[key.to_s]
+ (custom_field_values&.stringify_keys || {})[key.to_s]
end
private
def custom_fields_values_are_valid
- custom_fields.values.all?{|cf| cf.valid?}
+ custom_fields(:all).values.all?{|cf| cf.valid?}
end
end
end
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index 402df7fa9..8347d84f9 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -2,15 +2,19 @@ class CustomField < ActiveRecord::Base
extend Enumerize
belongs_to :workgroup
- enumerize :field_type, in: %i{list}
+ enumerize :field_type, in: %i{list integer string attachment}
validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]}
- validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}
+ validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}, presence: true
+
+ scope :for_workgroup, ->(workgroup){ where workgroup_id: workgroup.id }
+
+ scope :for_workgroup, ->(workgroup){ where workgroup_id: workgroup.id }
class Collection < HashWithIndifferentAccess
- def initialize object
- vals = object.class.custom_fields.map do |v|
- [v.code, CustomField::Value.new(object, v, object.custom_field_value(v.code))]
+ def initialize object, workgroup=:all
+ vals = object.class.custom_fields(workgroup).map do |v|
+ [v.code, CustomField::Instance.new(object, v, object.custom_field_value(v.code))]
end
super Hash[*vals.flatten]
end
@@ -20,11 +24,11 @@ class CustomField < ActiveRecord::Base
end
end
- class Value
+ class Instance
def self.new owner, custom_field, value
- field_type = custom_field.options["field_type"]
- klass_name = field_type && "CustomField::Value::#{field_type.classify}"
- klass = klass_name && const_defined?(klass_name) ? klass_name.constantize : CustomField::Value::Base
+ field_type = custom_field.field_type
+ klass_name = field_type && "CustomField::Instance::#{field_type.classify}"
+ klass = klass_name.safe_constantize || CustomField::Instance::Base
klass.new owner, custom_field, value
end
@@ -38,8 +42,14 @@ class CustomField < ActiveRecord::Base
@valid = false
end
+ attr_accessor :owner, :custom_field
+
delegate :code, :name, :field_type, to: :@custom_field
+ def default_value
+ options["default"]
+ end
+
def options
@custom_field.options || {}
end
@@ -57,6 +67,14 @@ class CustomField < ActiveRecord::Base
@raw_value
end
+ def input form_helper
+ @input ||= begin
+ klass_name = field_type && "CustomField::Instance::#{field_type.classify}::Input"
+ klass = klass_name.safe_constantize || CustomField::Instance::Base::Input
+ klass.new self, form_helper
+ end
+ end
+
def errors_key
"custom_fields.#{code}"
end
@@ -64,22 +82,184 @@ class CustomField < ActiveRecord::Base
def to_hash
HashWithIndifferentAccess[*%w(code name field_type options value).map{|k| [k, send(k)]}.flatten(1)]
end
+
+ def display_value
+ value
+ end
+
+ def initialize_custom_field
+ end
+
+ def preprocess_value_for_assignment val
+ val
+ end
+
+ def render_partial
+ ActionView::Base.new(Rails.configuration.paths["app/views"].first).render(
+ :partial => "shared/custom_fields/#{field_type}",
+ :locals => { field: self}
+ )
+ end
+
+ class Input
+ def initialize instance, form_helper
+ @instance = instance
+ @form_helper = form_helper
+ end
+
+ def custom_field
+ @instance.custom_field
+ end
+
+ delegate :custom_field, :value, :options, to: :@instance
+ delegate :code, :name, :field_type, to: :custom_field
+
+ def to_s
+ out = form_input
+ out.html_safe
+ end
+
+ protected
+
+ def form_input_id
+ "custom_field_#{code}"
+ end
+
+ def form_input_name
+ "#{@form_helper.object_name}[custom_field_values][#{code}]"
+ end
+
+ def form_input_options
+ {
+ input_html: {value: value, name: form_input_name},
+ label: name
+ }
+ end
+
+ def form_input
+ @form_helper.input form_input_id, form_input_options
+ end
+ end
end
class Integer < Base
def value
- @raw_value.to_i
+ @raw_value&.to_i
end
def validate
@valid = true
- unless @raw_value =~ /\A\d*\Z/
+ return if @raw_value.is_a?(Fixnum) || @raw_value.is_a?(Float)
+ unless @raw_value.to_s =~ /\A\d*\Z/
@owner.errors.add errors_key, "'#{@raw_value}' is not a valid integer"
@valid = false
end
end
end
+ class List < Integer
+ def validate
+ super
+ return unless value.present?
+ unless value >= 0 && value < options["list_values"].size
+ @owner.errors.add errors_key, "'#{@raw_value}' is not a valid value"
+ @valid = false
+ end
+ end
+
+ def display_value
+ return unless value
+ k = options["list_values"].is_a?(Hash) ? value.to_s : value.to_i
+ options["list_values"][k]
+ end
+
+ class Input < Base::Input
+ def form_input_options
+ collection = options["list_values"]
+ collection = collection.each_with_index.to_a if collection.is_a?(Array)
+ collection = collection.map(&:reverse) if collection.is_a?(Hash)
+ super.update({
+ selected: value,
+ collection: collection
+ })
+ end
+ end
+ end
+
+ class Attachment < Base
+ def initialize_custom_field
+ custom_field_code = self.code
+ _attr_name = attr_name
+ _uploader_name = uploader_name
+ owner.send :define_singleton_method, "read_uploader" do |attr|
+ if attr.to_s == _attr_name
+ custom_field_values[custom_field_code]
+ else
+ read_attribute attr
+ end
+ end
+
+ owner.send :define_singleton_method, "write_uploader" do |attr, val|
+ if attr.to_s == _attr_name
+ custom_field_values[custom_field_code] = val
+ else
+ write_attribute attr, val
+ end
+ end
+
+ owner.send :define_singleton_method, "#{_attr_name}_will_change!" do
+ custom_field_values_will_change!
+ end
+
+ _extension_whitelist = options["extension_whitelist"]
+
+ owner.send :define_singleton_method, "#{_uploader_name}_extension_whitelist" do
+ _extension_whitelist
+ end
+
+ unless owner.class.uploaders.has_key? _uploader_name.to_sym
+ owner.class.mount_uploader _uploader_name, CustomFieldAttachmentUploader, mount_on: "custom_field_#{code}_raw_value"
+ end
+ end
+
+ def preprocess_value_for_assignment val
+ if val.present?
+ owner.send "#{uploader_name}=", val
+ else
+ @raw_value
+ end
+ end
+
+ def value
+ owner.send "custom_field_#{code}"
+ end
+
+ def raw_value
+ @raw_value
+ end
+
+ def attr_name
+ "custom_field_#{code}_raw_value"
+ end
+
+ def uploader_name
+ "custom_field_#{code}"
+ end
+
+ def display_value
+ render_partial
+ end
+
+ class Input < Base::Input
+ def form_input_options
+ super.update({
+ as: :file,
+ wrapper: :horizontal_file_input
+ })
+ end
+ end
+ end
+
class String < Base
def value
"#{@raw_value}"
diff --git a/app/models/export/base.rb b/app/models/export/base.rb
index 6cf4c6b02..c65539635 100644
--- a/app/models/export/base.rb
+++ b/app/models/export/base.rb
@@ -1,4 +1,8 @@
+require 'net/http/post/multipart'
+
class Export::Base < ActiveRecord::Base
+ include Rails.application.routes.url_helpers
+
self.table_name = "exports"
belongs_to :referential
@@ -21,6 +25,22 @@ class Export::Base < ActiveRecord::Base
%w(zip csv json)
end
+ def upload_file file
+ url = URI.parse upload_workbench_export_url(self.workbench_id, self.id, host: Rails.application.config.rails_host)
+ res = nil
+ filename = File.basename(file.path)
+ content_type = MIME::Types.type_for(filename).first&.content_type
+ File.open(file.path) do |file_content|
+ req = Net::HTTP::Post::Multipart.new url.path,
+ file: UploadIO.new(file_content, content_type, filename),
+ token: self.token_upload
+ res = Net::HTTP.start(url.host, url.port) do |http|
+ http.request(req)
+ end
+ end
+ res
+ end
+
if Rails.env.development?
def self.force_load_descendants
path = Rails.root.join 'app/models/export'
diff --git a/app/models/export/simple_exporter/base.rb b/app/models/export/simple_exporter/base.rb
index 4e6e8eba4..e77e23468 100644
--- a/app/models/export/simple_exporter/base.rb
+++ b/app/models/export/simple_exporter/base.rb
@@ -48,15 +48,15 @@ class Export::SimpleExporter::Base < Export::Base
exporter.export
set_status_from_exporter
convert_exporter_journal_to_messages
- self.file = tmp
self.save!
+ upload_file tmp
end
def set_status_from_exporter
if exporter.status.to_s == "error"
self.status = :failed
elsif exporter.status.to_s == "success"
- self.status = :successful
+ self.status = :successful
else
self.status = :warning
end
diff --git a/app/models/line_control/lines_scope.rb b/app/models/line_control/lines_scope.rb
new file mode 100644
index 000000000..4210a10dd
--- /dev/null
+++ b/app/models/line_control/lines_scope.rb
@@ -0,0 +1,8 @@
+module LineControl
+ class LinesScope < ComplianceControl
+
+ def self.default_code; "3-Line-2" end
+
+ def prerequisite; I18n.t("compliance_controls.#{self.class.name.underscore}.prerequisite") end
+ end
+end
diff --git a/app/models/line_referential.rb b/app/models/line_referential.rb
index 0d2ed39b1..89700c06f 100644
--- a/app/models/line_referential.rb
+++ b/app/models/line_referential.rb
@@ -1,7 +1,7 @@
class LineReferential < ActiveRecord::Base
include ObjectidFormatterSupport
extend StifTransportModeEnumerations
-
+
has_many :line_referential_memberships
has_many :organisations, through: :line_referential_memberships
has_many :lines, class_name: 'Chouette::Line'
@@ -14,7 +14,7 @@ class LineReferential < ActiveRecord::Base
def add_member(organisation, options = {})
attributes = options.merge organisation: organisation
- line_referential_memberships.build attributes
+ line_referential_memberships.build attributes unless organisations.include?(organisation)
end
validates :name, presence: true
diff --git a/app/models/line_referential_membership.rb b/app/models/line_referential_membership.rb
index b49d1b5b1..dcada25bf 100644
--- a/app/models/line_referential_membership.rb
+++ b/app/models/line_referential_membership.rb
@@ -1,4 +1,6 @@
class LineReferentialMembership < ActiveRecord::Base
belongs_to :organisation
belongs_to :line_referential
+
+ validates :organisation_id, presence: true, uniqueness: { scope: :line_referential }
end
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index 745bc0d22..5bef67941 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -86,4 +86,8 @@ class Organisation < ActiveRecord::Base
workbenches.default
end
+ def lines_scope
+ functional_scope = sso_attributes.try(:[], "functional_scope")
+ JSON.parse(functional_scope) if functional_scope
+ end
end
diff --git a/app/models/referential.rb b/app/models/referential.rb
index 91a88d02d..0e48be43f 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -233,7 +233,7 @@ class Referential < ActiveRecord::Base
end
end
- def self.new_from(from, functional_scope)
+ def self.new_from(from, organisation)
Referential.new(
name: I18n.t("activerecord.copy", name: from.name),
slug: "#{from.slug}_clone",
@@ -244,7 +244,7 @@ class Referential < ActiveRecord::Base
stop_area_referential: from.stop_area_referential,
created_from: from,
objectid_format: from.objectid_format,
- metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, functional_scope) }
+ metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, organisation) }
)
end
diff --git a/app/models/referential_metadata.rb b/app/models/referential_metadata.rb
index 393dc70d3..017eb1449 100644
--- a/app/models/referential_metadata.rb
+++ b/app/models/referential_metadata.rb
@@ -155,10 +155,10 @@ class ReferentialMetadata < ActiveRecord::Base
end
private :clear_periods
- def self.new_from(from, functional_scope)
+ def self.new_from(from, organisation)
from.dup.tap do |metadata|
metadata.referential_source_id = from.referential_id
- metadata.line_ids = from.referential.lines.where(id: metadata.line_ids, objectid: functional_scope).collect(&:id)
+ metadata.line_ids = from.referential.lines.where(id: metadata.line_ids).for_organisation(organisation).pluck(:id)
metadata.referential_id = nil
end
end
diff --git a/app/models/simple_exporter.rb b/app/models/simple_exporter.rb
index c267b5b8c..1fcb76a29 100644
--- a/app/models/simple_exporter.rb
+++ b/app/models/simple_exporter.rb
@@ -64,7 +64,7 @@ class SimpleExporter < SimpleInterface
def map_item_to_rows item
return [item] unless configuration.item_to_rows_mapping
- configuration.item_to_rows_mapping.call(item).map {|row| row.is_a?(ActiveRecord::Base) ? row : CustomRow.new(row) }
+ instance_exec(item, &configuration.item_to_rows_mapping).map {|row| row.is_a?(ActiveRecord::Base) ? row : CustomRow.new(row) }
end
def resolve_value item, col
diff --git a/app/models/stop_area_referential.rb b/app/models/stop_area_referential.rb
index a9d3cc9b1..4706cdd77 100644
--- a/app/models/stop_area_referential.rb
+++ b/app/models/stop_area_referential.rb
@@ -12,7 +12,7 @@ class StopAreaReferential < ActiveRecord::Base
def add_member(organisation, options = {})
attributes = options.merge organisation: organisation
- stop_area_referential_memberships.build attributes
+ stop_area_referential_memberships.build attributes unless organisations.include?(organisation)
end
def last_sync
diff --git a/app/models/stop_area_referential_membership.rb b/app/models/stop_area_referential_membership.rb
index 435970961..fbed1c004 100644
--- a/app/models/stop_area_referential_membership.rb
+++ b/app/models/stop_area_referential_membership.rb
@@ -1,4 +1,6 @@
class StopAreaReferentialMembership < ActiveRecord::Base
belongs_to :organisation
belongs_to :stop_area_referential
+
+ validates :organisation_id, presence: true, uniqueness: { scope: :stop_area_referential }
end
diff --git a/app/services/route_way_cost_calculator.rb b/app/services/route_way_cost_calculator.rb
index 2e30c94fc..d41a2e59a 100644
--- a/app/services/route_way_cost_calculator.rb
+++ b/app/services/route_way_cost_calculator.rb
@@ -5,7 +5,7 @@ class RouteWayCostCalculator
def calculate!
way_costs = StopAreasToWayCostsConverter.new(@route.stop_areas).convert
- way_costs = TomTom.batch(way_costs)
+ way_costs = TomTom.matrix(way_costs)
way_costs = WayCostCollectionJSONSerializer.dump(way_costs)
@route.update(costs: way_costs)
end
diff --git a/app/uploaders/custom_field_attachment_uploader.rb b/app/uploaders/custom_field_attachment_uploader.rb
new file mode 100644
index 000000000..411b65bc3
--- /dev/null
+++ b/app/uploaders/custom_field_attachment_uploader.rb
@@ -0,0 +1,12 @@
+class CustomFieldAttachmentUploader < CarrierWave::Uploader::Base
+
+ storage :file
+
+ def store_dir
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
+ end
+
+ def extension_whitelist
+ model.send "#{mounted_as}_extension_whitelist"
+ end
+end
diff --git a/app/views/autocomplete_stop_areas/around.rabl b/app/views/autocomplete_stop_areas/around.rabl
index d067dc4d0..116038639 100644
--- a/app/views/autocomplete_stop_areas/around.rabl
+++ b/app/views/autocomplete_stop_areas/around.rabl
@@ -15,6 +15,7 @@ child @stop_areas, root: :features, object_root: false do
area_type: Chouette::AreaType.find(s.area_type).label,
registration_number: s.registration_number,
stoparea_id: s.id,
+ stoparea_kind: s.kind,
text: "#{s.name}, #{s.zip_code} #{s.city_name}",
user_objectid: s.user_objectid,
zip_code: s.zip_code,
diff --git a/app/views/autocomplete_stop_areas/index.rabl b/app/views/autocomplete_stop_areas/index.rabl
index c92b708f4..786f942d6 100644
--- a/app/views/autocomplete_stop_areas/index.rabl
+++ b/app/views/autocomplete_stop_areas/index.rabl
@@ -15,7 +15,8 @@ node do |stop_area|
:latitude => stop_area.latitude,
:area_type => Chouette::AreaType.find(stop_area.area_type).label,
:comment => stop_area.comment,
- :text => stop_area.full_name
+ :text => stop_area.full_name,
+ :kind => stop_area.kind
}
end
diff --git a/app/views/autocomplete_stop_areas/show.rabl b/app/views/autocomplete_stop_areas/show.rabl
index 73ce277cf..6ebf38900 100644
--- a/app/views/autocomplete_stop_areas/show.rabl
+++ b/app/views/autocomplete_stop_areas/show.rabl
@@ -9,7 +9,8 @@ node do |stop_area|
:short_name => truncate(stop_area.name, :length => 30) || "",
:zip_code => stop_area.zip_code || "",
:city_name => stop_area.city_name || "",
- :short_city_name => truncate(stop_area.city_name, :length => 15) || ""
+ :short_city_name => truncate(stop_area.city_name, :length => 15) || "",
+ :kind => stop_area.kind
}
end
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
index 648c98928..880db99f6 100644
--- a/app/views/calendars/show.html.slim
+++ b/app/views/calendars/show.html.slim
@@ -6,8 +6,7 @@
.row
.col-lg-6.col-md-6.col-sm-12.col-xs-12
= definition_list t('metadatas'),
- { Calendar.tmf('short_name') => resource.try(:short_name),
- Calendar.tmf('shared') => t("#{resource.shared}"),
+ { Calendar.tmf('shared') => t("#{resource.shared}"),
Calendar.tmf('organisation') => resource.organisation.name,
Calendar.tmf('dates') => resource.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe,
Calendar.tmf('date_ranges') => resource.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe }
@@ -18,7 +17,7 @@
.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'
+ = link_to '', workgroup_calendar_path(@workgroup, @calendar, year: (@year - 1)), class: 'previous_page'
+ = link_to '', workgroup_calendar_path(@workgroup, @calendar, year: (@year + 1)), class: 'next_page'
= render 'time_tables/show_time_table', time_table: @calendar
diff --git a/app/views/companies/_form.html.slim b/app/views/companies/_form.html.slim
index 3979c5800..e8b3fcede 100644
--- a/app/views/companies/_form.html.slim
+++ b/app/views/companies/_form.html.slim
@@ -12,7 +12,9 @@
= f.input :time_zone, include_blank: true
= f.input :url
= f.input :registration_number, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.registration_number")}
-
+ - if resource.custom_fields(current_referential.workgroup).any?
+ - resource.custom_fields.each do |code, field|
+ = field.input(f).to_s
.separator
= f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'company_form'
diff --git a/app/views/companies/show.html.slim b/app/views/companies/show.html.slim
index ca0a410b3..8960b92dd 100644
--- a/app/views/companies/show.html.slim
+++ b/app/views/companies/show.html.slim
@@ -6,8 +6,11 @@
.container-fluid
.row
.col-lg-6.col-md-6.col-sm-12.col-xs-12
- = definition_list t('metadatas'),
- { 'ID Codif' => @company.try(:get_objectid).try(:short_id),
- Chouette::Company.human_attribute_name(:phone) => resource.phone,
- Chouette::Company.human_attribute_name(:email) => resource.email,
- Chouette::Company.human_attribute_name(:url) => resource.url }
+ - attributes = { t('id_codif') => @company.try(:objectid).try(:local_id),
+ Chouette::Company.human_attribute_name(:phone) => @company.phone,
+ Chouette::Company.human_attribute_name(:email) => @company.email,
+ Chouette::Company.human_attribute_name(:url) => @company.url }
+ - @company.custom_fields(current_referential.workgroup).each do |code, field|
+ - attributes.merge!(field.name => field.display_value)
+
+ = definition_list t('metadatas'), attributes
diff --git a/app/views/compliance_control_blocks/edit.html.slim b/app/views/compliance_control_blocks/edit.html.slim
index 49aee7705..1d32256b0 100644
--- a/app/views/compliance_control_blocks/edit.html.slim
+++ b/app/views/compliance_control_blocks/edit.html.slim
@@ -1,3 +1,4 @@
+- breadcrumb :compliance_control_set, compliance_control_set
- page_header_content_for @compliance_control_block
.page_content
diff --git a/app/views/compliance_control_blocks/new.html.slim b/app/views/compliance_control_blocks/new.html.slim
index 7d2551311..aab40572b 100644
--- a/app/views/compliance_control_blocks/new.html.slim
+++ b/app/views/compliance_control_blocks/new.html.slim
@@ -1,3 +1,5 @@
+- breadcrumb :compliance_control_set, compliance_control_set
+
.page_content
.container-fluid
.row
diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim
index 2f0791f50..e1be3df4a 100644
--- a/app/views/dashboards/_dashboard.html.slim
+++ b/app/views/dashboards/_dashboard.html.slim
@@ -5,7 +5,7 @@
.panel-heading
h3.panel-title.with_actions
div
- = link_to workbench.name, workbench_path(workbench)
+ = link_to t('dashboards.workbench.title', organisation: workbench.organisation.name), workbench_path(workbench)
span.badge.ml-xs = workbench.referentials.count if workbench.referentials.present?
div
@@ -23,6 +23,7 @@
.panel-heading
h3.panel-title.with_actions
= link_to I18n.t("activerecord.models.calendar", count: workbench.calendars.size), workgroup_calendars_path(workbench.workgroup)
+ span.badge.ml-xs = workbench.calendars.count if workbench.calendars.present?
div
= link_to '', workgroup_calendars_path(workbench.workgroup), class: ' fa fa-chevron-right pull-right'
- if workbench.calendars.present?
@@ -39,7 +40,7 @@
- @dashboard.current_organisation.stop_area_referentials.each do |referential|
.panel-heading
h3.panel-title
- = referential.name
+ = t('dashboards.stop_area_referentials.title')
.list-group
= link_to Chouette::StopArea.model_name.human.pluralize.capitalize, stop_area_referential_stop_areas_path(referential), class: 'list-group-item'
@@ -47,7 +48,7 @@
- @dashboard.current_organisation.line_referentials.all.each do |referential|
.panel-heading
h3.panel-title
- = referential.name
+ = t('dashboards.line_referentials.title')
.list-group
= link_to Chouette::Line.model_name.human.pluralize.capitalize, line_referential_lines_path(referential), class: 'list-group-item'
= link_to Chouette::Company.model_name.human.pluralize.capitalize, line_referential_companies_path(referential), class: 'list-group-item'
diff --git a/app/views/layouts/navigation/_nav_panel_operations.html.slim b/app/views/layouts/navigation/_nav_panel_operations.html.slim
index 8dce829cd..1c5a1f14b 100644
--- a/app/views/layouts/navigation/_nav_panel_operations.html.slim
+++ b/app/views/layouts/navigation/_nav_panel_operations.html.slim
@@ -1,5 +1,5 @@
#operations_panel.nav_panel
.panel-title
- h2 Opérations
+ h2 = t('layouts.operations')
.panel-body
p = "Lorem ipsum dolor sit amet..."
diff --git a/app/views/layouts/navigation/_nav_panel_profile.html.slim b/app/views/layouts/navigation/_nav_panel_profile.html.slim
index bcbf89e67..b0dee5d53 100644
--- a/app/views/layouts/navigation/_nav_panel_profile.html.slim
+++ b/app/views/layouts/navigation/_nav_panel_profile.html.slim
@@ -1,6 +1,6 @@
#profile_panel.nav_panel
.panel-title
- h2 Mon Profil
+ h2 = t('layouts.user.profile')
.panel-body
p = current_user.name
p = current_organisation.name
diff --git a/app/views/referential_companies/_form.html.slim b/app/views/referential_companies/_form.html.slim
index b02eab3f1..0e7b20af4 100644
--- a/app/views/referential_companies/_form.html.slim
+++ b/app/views/referential_companies/_form.html.slim
@@ -1,18 +1,19 @@
-= semantic_form_for [@referential, @company] do |form|
- = form.inputs do
- = form.input :name, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.name") }
- = form.input :short_name
- = form.input :organizational_unit
- = form.input :operating_department_name
- = form.input :code
- = form.input :phone, as: :phone
- = form.input :fax, as: :phone
- = form.input :email, as: :email
- = form.input :time_zone, include_blank: true
- = form.input :url
- = form.input :registration_number, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.registration_number") }
- = form.input :objectid, :required => !@company.new_record?, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.objectid") }
-
- = form.actions do
- = form.action :submit, as: :button
- = form.action :cancel, as: :link \ No newline at end of file
+= simple_form_for [@referential, @company], html: {class: 'form-horizontal', id: 'company_form'}, wrapper: :horizontal_form do |f|
+ .row
+ .col-lg-12
+ = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.name")}
+ = f.input :short_name
+ = f.input :organizational_unit
+ = f.input :operating_department_name
+ = f.input :code
+ = f.input :phone
+ = f.input :fax
+ = f.input :email, as: :email
+ = f.input :time_zone, include_blank: true
+ = f.input :url
+ = f.input :registration_number, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.registration_number")}
+ - if resource.custom_fields(@referential.workgroup).any?
+ - resource.custom_fields.each do |code, field|
+ = field.input(f).to_s
+ .separator
+ = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'company_form'
diff --git a/app/views/referential_companies/edit.html.slim b/app/views/referential_companies/edit.html.slim
index b3fcf6cd8..95be64aa1 100644
--- a/app/views/referential_companies/edit.html.slim
+++ b/app/views/referential_companies/edit.html.slim
@@ -1,3 +1,5 @@
- breadcrumb :referential_company, @referential, @company
- page_header_content_for @company
-= render 'form'
+.page_content
+ .container-fluid
+ = render 'form'
diff --git a/app/views/referential_companies/new.html.slim b/app/views/referential_companies/new.html.slim
index 5e59db139..1dfdc8eb5 100644
--- a/app/views/referential_companies/new.html.slim
+++ b/app/views/referential_companies/new.html.slim
@@ -1,2 +1,6 @@
- breadcrumb :referential_companies, @referential
-= render 'form'
+.page_content
+ .container-fluid
+ .row
+ .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1
+ = render 'form'
diff --git a/app/views/referential_companies/show.html.slim b/app/views/referential_companies/show.html.slim
index 1599145be..8ad011edf 100644
--- a/app/views/referential_companies/show.html.slim
+++ b/app/views/referential_companies/show.html.slim
@@ -17,8 +17,11 @@
.container-fluid
.row
.col-lg-6.col-md-6.col-sm-12.col-xs-12
- = definition_list t('metadatas'),
- { t('id_codif') => @company.try(:objectid).try(:local_id),
- Chouette::Company.human_attribute_name(:phone) => @company.phone,
- Chouette::Company.human_attribute_name(:email) => @company.email,
- Chouette::Company.human_attribute_name(:url) => @company.url }
+ - attributes = { t('id_codif') => @company.try(:objectid).try(:local_id),
+ Chouette::Company.human_attribute_name(:phone) => @company.phone,
+ Chouette::Company.human_attribute_name(:email) => @company.email,
+ Chouette::Company.human_attribute_name(:url) => @company.url }
+ - @company.custom_fields(@referential.workgroup).each do |code, field|
+ - attributes.merge!(field.name => field.display_value)
+
+ = definition_list t('metadatas'), attributes
diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim
index 96d847ec1..c378f871e 100644
--- a/app/views/referentials/_form.html.slim
+++ b/app/views/referentials/_form.html.slim
@@ -49,7 +49,7 @@
.separator
.row
.col-lg-11
- = subform.input :lines, as: :select, collection: Chouette::Line.includes(:company).order(:name).where(objectid: current_functional_scope), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('simple_form.labels.referential.placeholders.select_lines'), 'multiple': 'multiple', style: 'width: 100%' }
+ = subform.input :lines, as: :select, collection: Chouette::Line.includes(:company).order(:name).for_organisation(current_organisation), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('simple_form.labels.referential.placeholders.select_lines'), 'multiple': 'multiple', style: 'width: 100%' }
.col-lg-1
a.clear-lines.btn.btn-default href='#'
.fa.fa-trash
diff --git a/app/views/referentials/select_compliance_control_set.html.slim b/app/views/referentials/select_compliance_control_set.html.slim
index 69c87aab2..3550bb202 100644
--- a/app/views/referentials/select_compliance_control_set.html.slim
+++ b/app/views/referentials/select_compliance_control_set.html.slim
@@ -1,3 +1,5 @@
+- breadcrumb @referential
+
.page_content
.container-fluid
.row
diff --git a/app/views/shared/custom_fields/_attachment.html.slim b/app/views/shared/custom_fields/_attachment.html.slim
new file mode 100644
index 000000000..32d0fda4d
--- /dev/null
+++ b/app/views/shared/custom_fields/_attachment.html.slim
@@ -0,0 +1,4 @@
+- if field.value.present?
+ = link_to I18n.t("custom_fields.#{field.owner.class.name.demodulize.underscore}.#{field.code}.link"), field.value.url
+- else
+ = "-"
diff --git a/app/views/shared/iev_interfaces/_messages.html.slim b/app/views/shared/iev_interfaces/_messages.html.slim
index 022f4ee01..14157a88d 100644
--- a/app/views/shared/iev_interfaces/_messages.html.slim
+++ b/app/views/shared/iev_interfaces/_messages.html.slim
@@ -9,6 +9,6 @@
- else
.col-md-6= export_message_content message
.col-md-6
- - if message.criticity != "info"
+ - if message.resource_attributes
pre
= JSON.pretty_generate message.resource_attributes || {}
diff --git a/app/views/stif/dashboards/_dashboard.html.slim b/app/views/stif/dashboards/_dashboard.html.slim
index e0f754fd4..7538c7fc7 100644
--- a/app/views/stif/dashboards/_dashboard.html.slim
+++ b/app/views/stif/dashboards/_dashboard.html.slim
@@ -1,8 +1,4 @@
.row
- .col-lg-12
- h2.content_header = t('.subtitle')
-
-.row
.col-lg-6.col-md-6.col-sm-6.col-xs-12
.panel.panel-default
.panel-heading
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 1cba88f94..00f2ad8bb 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -48,7 +48,7 @@
- if has_feature?(:stop_area_waiting_time)
= f.input :waiting_time, input_html: { min: 0 }
- = f.input :registration_number, required: stop_area_registration_number_is_required(f.object), :input_html => {title: stop_area_registration_number_title(f.object), value: stop_area_registration_number_value(f.object)}
+ = f.input :registration_number, required: stop_area_registration_number_is_required(f.object), :input_html => {title: stop_area_registration_number_title(f.object), value: stop_area_registration_number_value(f.object)}, hint: stop_area_registration_number_hint
= f.input :fare_code
= f.input :nearest_topic_name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.nearest_topic_name")}
= f.input :comment, as: :text, :input_html => {:rows => 5, :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.comment")}
@@ -62,6 +62,12 @@
= f.input :stairs_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], :include_blank => true
= f.input :lift_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], :include_blank => true
+ - if resource.custom_fields(resource.stop_area_referential.workgroup).any?
+ .custom_fields
+ h3 = t("stop_areas.stop_area.custom_fields")
+ - resource.custom_fields(resource.stop_area_referential.workgroup).each do |code, field|
+ = field.input(f).to_s
+
.separator
= f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'stop_area_form'
diff --git a/app/views/stop_areas/autocomplete.rabl b/app/views/stop_areas/autocomplete.rabl
index a5f0bd5ec..26fca36b2 100644
--- a/app/views/stop_areas/autocomplete.rabl
+++ b/app/views/stop_areas/autocomplete.rabl
@@ -15,7 +15,8 @@ node do |stop_area|
:latitude => stop_area.latitude,
:area_type => stop_area.area_type,
:comment => stop_area.comment,
- :text => "<span class='small label label-info'>#{I18n.t("area_types.label.#{stop_area.area_type}")}</span>#{stop_area.full_name}"
+ :text => "<span class='small label label-info'>#{I18n.t("area_types.label.#{stop_area.area_type}")}</span>#{stop_area.full_name}",
+ :kind => stop_area.kind
}
end
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index a6147b86d..851bd9b82 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -23,4 +23,6 @@
t('activerecord.attributes.stop_area.state') => stop_area_status(@stop_area),
@stop_area.human_attribute_name(:comment) => @stop_area.try(:comment),
})
+ - @stop_area.custom_fields.each do |code, field|
+ - attributes.merge!(field.name => field.display_value)
= definition_list t('metadatas'), attributes
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index cd55d3ce6..c92fb7bae 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -33,7 +33,6 @@
| window.all_missions = #{(@all_missions.to_json).html_safe};
| window.custom_fields = #{(@custom_fields.to_json).html_safe};
| window.extra_headers = #{(@extra_headers.to_json).html_safe};
- // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};
- if has_feature?(:vehicle_journeys_return_route)
= javascript_tag do
diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb
index 4b9bd320d..8dbc3afee 100644
--- a/config/initializers/simple_form_bootstrap.rb
+++ b/config/initializers/simple_form_bootstrap.rb
@@ -72,9 +72,9 @@ SimpleForm.setup do |config|
b.use :placeholder
b.optional :maxlength
b.optional :readonly
- b.use :label, class: 'col-sm-3 control-label'
+ b.use :label, class: 'col-sm-4 col-xs-5 control-label'
- b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
+ b.wrapper tag: 'div', class: 'col-sm-8 col-xs-7' do |ba|
ba.use :input
ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block small' }
diff --git a/config/locales/compliance_check_messages.en.yml b/config/locales/compliance_check_messages.en.yml
index 216a363a3..88841f308 100644
--- a/config/locales/compliance_check_messages.en.yml
+++ b/config/locales/compliance_check_messages.en.yml
@@ -22,10 +22,11 @@ en:
3_routingconstraint_2: "The Routing Constraint Zone %{source_objectid} covers all the stop points of its related route : %{target_0_objectid}."
3_routingconstraint_3: "The Routing Constraint Zone %{source_objectid} has less than 2 stop points"
3_line_1: "On line :%{source_label} (%{source_objectid}), no route has an opposite route"
+ 3_line_2: "The line %{source_label} (%{source_objectid}) is not in the lines scope of the organization %{reference_value}"
3_generic_1: "%{source_objectid} : the %{source_attribute} attribute value (%{error_value}) does not respect the following pattern : %{reference_value}"
3_generic_2_1: "%{source_objectid} : the %{source_attribute} attributes's value (%{error_value}) is greater than the authorized maximum value : %{reference_value}"
3_generic_2_2: "%{source_objectid} : the %{source_attribute} attributes's value (%{error_value}) is smaller than the authorized minimum value %{reference_value}"
3_generic_3: "%{source_objectid} : the %{source_attribute} attribute (%{error_value}) has a value shared with : %{target_0_objectid}"
3_shape_1: "Tracé %{source_objectid} : le tracé passe trop loin de l'arrêt %{target_0_label} (%{target_0_objectid}) : %{error_value} > %{reference_value}"
3_shape_2: "Tracé %{source_objectid} : le tracé n'est pas défini entre les arrêts %{target_0_label} (%{target_0_objectid}) et %{target_1_label} (%{target_1_objectid})"
- 3_shape_3: "Le tracé de l'itinéraire %{source_objectid} est en écart avec la voirie sur %{error_value} sections" \ No newline at end of file
+ 3_shape_3: "Le tracé de l'itinéraire %{source_objectid} est en écart avec la voirie sur %{error_value} sections"
diff --git a/config/locales/compliance_check_messages.fr.yml b/config/locales/compliance_check_messages.fr.yml
index db127d236..167ef411a 100644
--- a/config/locales/compliance_check_messages.fr.yml
+++ b/config/locales/compliance_check_messages.fr.yml
@@ -22,10 +22,11 @@ fr:
3_routingconstraint_2: "L'ITL %{source_objectid} couvre tous les arrêts de l'itinéraire %{target_0_objectid}."
3_routingconstraint_3: "L'ITL %{source_objectid} n'a pas suffisament d'arrêts (minimum 2 arrêts requis)"
3_line_1: "Sur la ligne %{source_label} (%{source_objectid}), aucun itinéraire n'a d'itinéraire inverse"
+ 3_line_2: "La ligne %{source_label} (%{source_objectid}) ne fait pas partie du périmètre de lignes de l'organisation %{reference_value}"
3_generic_1: "%{source_objectid} : l'attribut %{source_attribute} a une valeur %{error_value} qui ne respecte pas le motif %{reference_value}"
3_generic_2_1: "%{source_objectid} : l'attribut %{source_attribute} a une valeur %{error_value} supérieure à la valeur maximale autorisée %{reference_value}"
3_generic_2_2: "%{source_objectid} : l'attribut %{source_attribute} a une valeur %{error_value} inférieure à la valeur minimale autorisée %{reference_value}"
3_generic_3: "%{source_objectid} : l'attribut %{source_attribute} a une valeur %{error_value} partagée avec %{target_0_objectid}"
3_shape_1: "Tracé %{source_objectid} : le tracé passe trop loin de l'arrêt %{target_0_label} (%{target_0_objectid}) : %{error_value} > %{reference_value}"
3_shape_2: "Tracé %{source_objectid} : le tracé n'est pas défini entre les arrêts %{target_0_label} (%{target_0_objectid}) et %{target_1_label} (%{target_1_objectid})"
- 3_shape_3: "Le tracé de l'itinéraire %{source_objectid} est en écart avec la voirie sur %{error_value} sections" \ No newline at end of file
+ 3_shape_3: "Le tracé de l'itinéraire %{source_objectid} est en écart avec la voirie sur %{error_value} sections"
diff --git a/config/locales/compliance_control_blocks.en.yml b/config/locales/compliance_control_blocks.en.yml
index 275f05106..0ec979549 100644
--- a/config/locales/compliance_control_blocks.en.yml
+++ b/config/locales/compliance_control_blocks.en.yml
@@ -9,6 +9,12 @@ en:
compliance_control_blocks:
transport_mode: Transport mode
sub_transport_mode: Transport submode
+ errors:
+ models:
+ compliance_control_block:
+ attributes:
+ condition_attributes:
+ taken: The same compliance control block already exists in this compliance control set
compliance_control_blocks:
clone:
prefix: 'Copy of'
@@ -16,8 +22,12 @@ en:
destroy_confirm: Are you sure you want to destroy this block ?
new:
title: Create a control block
+ create:
+ title: Create a control block
edit:
title: "Edit the control block : %{name}"
+ update:
+ title: "Edit the control block : %{name}"
metas:
control:
zero: "No controls"
diff --git a/config/locales/compliance_control_blocks.fr.yml b/config/locales/compliance_control_blocks.fr.yml
index a6720881f..5ce5b4729 100644
--- a/config/locales/compliance_control_blocks.fr.yml
+++ b/config/locales/compliance_control_blocks.fr.yml
@@ -9,6 +9,12 @@ fr:
compliance_control_blocks:
transport_mode: Mode de transport
transport_submode: Sous-mode de transport
+ errors:
+ models:
+ compliance_control_block:
+ attributes:
+ condition_attributes:
+ taken: Un groupe de contrôle identique existe déjà au sein de ce jeu de contrôles
compliance_control_blocks:
clone:
prefix: 'Copie de'
@@ -16,8 +22,12 @@ fr:
destroy_confirm: Etes vous sûr de supprimer ce bloc ?
new:
title: Créer un groupe de contrôle(s)
+ create:
+ title: Créer un groupe de contrôle(s)
edit:
title: "Editer le groupe de contrôle : %{name}"
+ update:
+ title: "Editer le groupe de contrôle : %{name}"
metas:
control:
zero: "Aucun contrôle"
diff --git a/config/locales/compliance_controls.en.yml b/config/locales/compliance_controls.en.yml
index d8dc44ecc..18069f2f7 100644
--- a/config/locales/compliance_controls.en.yml
+++ b/config/locales/compliance_controls.en.yml
@@ -142,6 +142,11 @@ en:
3_line_1: "On line :%{source_label} (%{source_objectid}), no route has an opposite route"
description: "The routes of a line must have an opposite route"
prerequisite: Line has multiple routes
+ line_control/lines_scope:
+ messages:
+ 3_line_2: "The line %{source_label} (%{source_objectid}) is not in the lines scope of the organization %{reference_value}"
+ description: "The line must be included in the lines scope of the organization"
+ prerequisite: "None"
generic_attribute_control/pattern:
messages:
3_generic_1: "%{source_objectid} : the %{source_attribute} attribute value (%{error_value}) does not respect the following pattern : %{reference_value}"
@@ -209,6 +214,8 @@ en:
one: "Unactivated stop points"
line_control/route:
one: "The routes of a line must have an opposite route"
+ line_control/lines_scope:
+ one: "Lines must be included in the lines scope of the organization"
generic_attribute_control/pattern:
one: "Attribute pattern of an object in a line"
generic_attribute_control/min_max:
diff --git a/config/locales/compliance_controls.fr.yml b/config/locales/compliance_controls.fr.yml
index 78b92451f..7dc6eeeb3 100644
--- a/config/locales/compliance_controls.fr.yml
+++ b/config/locales/compliance_controls.fr.yml
@@ -139,6 +139,11 @@ fr:
3_line_1: "Sur la ligne %{source_label} (%{source_objectid}), aucun itinéraire n'a d'itinéraire inverse"
description: "Les itinéraires d'une ligne doivent être associés en aller/retour"
prerequisite: Ligne disposant de plusieurs itinéraires
+ line_control/lines_scope:
+ messages:
+ 3_line_2: "La ligne %{source_label} (%{source_objectid}) ne fait pas partie du périmètre de lignes de l'organisation %{reference_value}"
+ description: "Les lignes doivent appartenir au périmètre de lignes de l'organisation"
+ prerequisite: "Aucun"
generic_attribute_control/pattern:
messages:
3_generic_1: "%{source_objectid} : l'attribut %{source_attribute} a une valeur %{error_value} qui ne respecte pas le motif %{reference_value}"
@@ -206,6 +211,8 @@ fr:
one: "ITL & arret désactivé"
line_control/route:
one: "Appariement des itinéraires"
+ line_control/lines_scope:
+ one: "Les lignes doivent appartenir au périmètre de lignes de l'organisation"
generic_attribute_control/pattern:
one: "Contrôle du contenu selon un pattern"
generic_attribute_control/min_max:
diff --git a/config/locales/dashboard.en.yml b/config/locales/dashboard.en.yml
index 8d46ff7aa..361a3cf2b 100644
--- a/config/locales/dashboard.en.yml
+++ b/config/locales/dashboard.en.yml
@@ -2,6 +2,8 @@ en:
dashboards:
show:
title: "Dashboard %{organisation}"
+ workbench:
+ title: Transport offer %{organisation}
calendars:
title: Calendars
none: No calendar created
diff --git a/config/locales/dashboard.fr.yml b/config/locales/dashboard.fr.yml
index d0aa36d61..1e1c095b1 100644
--- a/config/locales/dashboard.fr.yml
+++ b/config/locales/dashboard.fr.yml
@@ -2,6 +2,8 @@ fr:
dashboards:
show:
title: "Tableau de bord %{organisation}"
+ workbench:
+ title: Offre de transport %{organisation}
calendars:
title: Modèles de calendrier
none: Aucun calendrier défini
diff --git a/config/locales/journey_patterns.en.yml b/config/locales/journey_patterns.en.yml
index d480e144d..70ae94dd9 100644
--- a/config/locales/journey_patterns.en.yml
+++ b/config/locales/journey_patterns.en.yml
@@ -1,6 +1,7 @@
en:
journey_patterns:
journey_pattern:
+ fetching_error: "There has been a problem fetching the data. Please reload the page to try again."
from_to: "From '%{departure}' to '%{arrival}'"
stop_count: "%{count}/%{route_count} stops"
vehicle_journeys_count: "Vehicle journeys: %{count}"
@@ -19,6 +20,13 @@ en:
show:
title: "Journey Pattern %{journey_pattern}"
stop_points: "Stop point on journey pattern list"
+ stop_points_count:
+ none: '%{count} stop areas'
+ one: '%{count} stop area'
+ other: '%{count} stop areas'
+ informations: Informations
+ confirmation: Confimation
+ confirm_page_change: You are about to change page. Would you like to save your work before that ?
index:
title: "Journey Patterns of %{route}"
form:
@@ -50,7 +58,8 @@ en:
creator_id: "Created by"
full_journey_time: Full journey
commercial_journey_time: Commercial journey
-
+ stop_points: Nb stop areas
+ checksum: Checksum
formtastic:
titles:
journey_pattern:
diff --git a/config/locales/journey_patterns.fr.yml b/config/locales/journey_patterns.fr.yml
index 32c1f3f97..10653a02d 100644
--- a/config/locales/journey_patterns.fr.yml
+++ b/config/locales/journey_patterns.fr.yml
@@ -1,6 +1,7 @@
fr:
journey_patterns:
journey_pattern:
+ fetching_error: "La récupération des courses a rencontré un problème. Rechargez la page pour tenter de corriger le problème."
from_to: "De '%{departure}' à '%{arrival}'"
stop_count: "%{count}/%{route_count} arrêts"
vehicle_journeys_count: "Courses: %{count}"
@@ -19,6 +20,13 @@ fr:
show:
title: "Mission %{journey_pattern}"
stop_points: "Liste des arrêts de la mission"
+ stop_points_count:
+ none: '%{count} arrêt'
+ one: '%{count} arrêt'
+ other: '%{count} arrêts'
+ informations: Informations
+ confirmation: Confimation
+ confirm_page_change: Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?
index:
title: "Missions de %{route}"
form:
@@ -50,6 +58,8 @@ fr:
creator_id: "Créé par"
full_journey_time: Parcours complet
commercial_journey_time: Parcours commercial
+ stop_points: Nb arrêts
+ checksum: Signature métier
formtastic:
titles:
journey_pattern:
diff --git a/config/locales/layouts.en.yml b/config/locales/layouts.en.yml
index debff05e5..d5717b400 100644
--- a/config/locales/layouts.en.yml
+++ b/config/locales/layouts.en.yml
@@ -3,6 +3,7 @@ en:
back_to_dashboard: "Back to Dashboard"
help: "Help"
home: "Home"
+ operations: Operations
user:
profile: "My Profile"
sign_out: "Sign out"
diff --git a/config/locales/layouts.fr.yml b/config/locales/layouts.fr.yml
index 5e835bcf7..17d23c756 100644
--- a/config/locales/layouts.fr.yml
+++ b/config/locales/layouts.fr.yml
@@ -3,6 +3,7 @@ fr:
back_to_dashboard: "Retour au Tableau de Bord"
help: "Aide"
home: "Accueil"
+ operations: Opérations
user:
profile: "Mon Profil"
sign_out: "Déconnexion"
diff --git a/config/locales/stop_areas.en.yml b/config/locales/stop_areas.en.yml
index 37d39b76c..ddb2d940c 100644
--- a/config/locales/stop_areas.en.yml
+++ b/config/locales/stop_areas.en.yml
@@ -8,7 +8,7 @@ en:
registration_number:
already_taken: Already taken
cannot_be_empty: This field is mandatory
- invalid: Incorrect value
+ invalid: "Incorrect value (expected value: \"%{mask}\")"
default_geometry_success: "%{count} modified stop areas"
stop_area:
no_position: "No Position"
@@ -18,6 +18,7 @@ en:
general: "General"
localisation: "Localisation"
accessibility: "Accessibility"
+ custom_fields: "Custom fields"
actions:
new: "Add a new stop area"
create: "Add a new stop area"
@@ -205,6 +206,9 @@ en:
comment: "Maximum length = 255."
coordinates: "Coordinates are mandatory."
projection_xy: "x,y in secondary referential, dot for decimal separator"
+ hints:
+ stop_area:
+ registration_number: Leave empty for automatic value.
referential_stop_areas:
<<: *en_stop_areas
diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml
index aee112be7..6a5fbf24b 100644
--- a/config/locales/stop_areas.fr.yml
+++ b/config/locales/stop_areas.fr.yml
@@ -9,7 +9,7 @@ fr:
registration_number:
already_taken: Déjà utilisé
cannot_be_empty: Ce champ est requis
- invalid: Valeur invalide
+ invalid: "Valeur invalide (valeur attendue: \"%{mask}\")"
default_geometry_success: "%{count} arrêts édités"
stop_area:
no_position: "Pas de position"
@@ -19,6 +19,7 @@ fr:
general: "General"
localisation: "Localisation"
accessibility: "Accessibilité"
+ custom_fields: "Champs personnalisés"
actions:
new: "Ajouter un arrêt"
create: "Ajouter un arrêt"
@@ -208,6 +209,9 @@ fr:
comment: "Longueur maximale = 255."
coordinates: "Les coordonnées sont obligatoires."
projection_xy: "x,y dans le référentiel secondaire, le séparateur de décimales est 'point'"
+ hints:
+ stop_area:
+ registration_number: Laisser blanc pour assigner une valeur automatiquement.
referential_stop_areas:
<<: *fr_stop_areas
diff --git a/config/locales/vehicle_journey_exports.en.yml b/config/locales/vehicle_journey_exports.en.yml
index 93a782026..4e658353e 100644
--- a/config/locales/vehicle_journey_exports.en.yml
+++ b/config/locales/vehicle_journey_exports.en.yml
@@ -1,7 +1,7 @@
en:
vehicle_journey_exports:
new:
- title: "Export existing vehicle journey at stops"
+ title: Vehicle journeys export
basename: "vehicle_journeys"
label:
vehicle_journey_id: "vj id (empty for new vj)"
diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml
index f1ba96dc5..79e805ee8 100644
--- a/config/locales/vehicle_journeys.en.yml
+++ b/config/locales/vehicle_journeys.en.yml
@@ -1,14 +1,29 @@
en:
vehicle_journeys:
vehicle_journeys_matrix:
+ filters:
+ id: Filter by ID...
+ timetable: Filter by journey pattern...
+ timetable: Filter by timetable...
cancel_selection: "Cancel Selection"
fetching_error: "There has been a problem fetching the data. Please reload the page to try again."
line_routes: "Line's routes"
modal_confirm: 'Do you want to save mofications before moving on to the next page ?'
- pagination: "Schedules %{minVJ} to %{maxVJ} over %{total}"
selected_journeys: "%{count} selected journeys"
show_purchase_window: 'Show the purchase window'
show_timetable: 'Show calendar'
+ no_associated_timetables: No associated timetables
+ no_associated_purchase_windows: No associated purchase windows
+ no_associated_footnotes: No associated footnotes
+ duplicate:
+ one: Clone %{count} vehicle journey
+ other: Clone %{count} vehicle journeys
+ start_time: Indicative start time
+ number: Number of vehicle journeys to create and clone
+ delta: Delta from which vehicle journeys are created
+ affect_company: Affect company
+ no_line_footnotes: The line doesn't have any footnotes
+ select_footnotes: Select footnotes to associate with the vehicle journey
vehicle_journey:
title_stopless: "Vehicle journey %{name}"
title: "Vehicle journey leaving from %{stop} at %{time}"
@@ -17,10 +32,10 @@ en:
title: "Vehicle journey frequency leaving from %{stop} at %{time}"
title_frequency: "Vehicle journey frequency with %{interval}min leaving from %{stop} at %{time_first} to %{time_last}"
actions:
- index: "Vehicle time's board"
- new: "Add a new timed vehicle journey"
+ index: "Vehicle journeys"
+ new: "Add a new vehicle journey"
new_frequency: "Add a new frequency vehicle journey"
- edit: "Edit this timed vehicle journey"
+ edit: "Edit this vehicle journey"
edit_frequency: "Edit this frequency vehicle journey"
destroy: "Remove this vehicle journey"
destroy_confirm: "Are you sure you want destroy this vehicle journey?"
@@ -48,15 +63,18 @@ en:
show_journeys_without_schedule: "Show journeys without schedule"
slide_arrival: "arrival time at first stop"
slide_departure: "departure time at first stop"
- slide_title: "Shift all vehicle passing times"
+ slide_title: "Shift all the vehicle journey passing times : %{id}"
slide: "Shift"
+ slide_delta: "Shift of"
starting_stop: "Departure"
stop_title: "Stop"
submit_frequency_edit: "Edit frequency vehicle journey"
submit_frequency: "Create frequency vehicle journey"
submit_timed_edit: "Edit vehicle journey"
submit_timed: "Create vehicle journey"
- time_tables: "Associated calendars to vehicle journey"
+ time_tables: "Associated timetables"
+ purchase_windows: Associated purchase windows
+ footnotes: Associated footnotes
to_arrivals: "Copy departures to arrivals"
to_departures: "Copy arrivals to departures"
to: "at"
@@ -102,6 +120,7 @@ en:
checksum: "Checksum"
comment: "Comments"
company: "Company"
+ company_name: "Company name"
created_at: Created at
creator_id: "Created by"
departure_time: "Departure"
@@ -110,9 +129,10 @@ en:
footnote_ids: "Footnotes"
id: "Journey ID"
journey_frequency_ids: "Timeband"
- journey_name: "Name of the journey"
+ journey_name: "Name of the vehicle journey"
journey_pattern_id: "Pattern ID"
journey_pattern: "Journey Pattern"
+ journey_pattern_published_name: "Journey Pattern published name"
line: "Line"
mobility_restricted_suitability: "PRM accessibility"
name: "Journey Name"
@@ -137,6 +157,7 @@ en:
updated_at: Updated at
vehicle_journey_at_stop_ids: "Time list"
vehicle_type_identifier: "Vehicle Type Identifier"
+ start_time: Start time
errors:
models:
vehicle_journey:
diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml
index d144e580f..466eca684 100644
--- a/config/locales/vehicle_journeys.fr.yml
+++ b/config/locales/vehicle_journeys.fr.yml
@@ -1,14 +1,29 @@
fr:
vehicle_journeys:
vehicle_journeys_matrix:
+ filters:
+ id: Filtrer par ID course...
+ journey_pattern: 'Filtrer par code, nom ou OID de mission...'
+ timetable: Filtrer par calendrier...
cancel_selection: "Annuler la sélection"
fetching_error: "La récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème."
line_routes: "Séquences d'arrêts de la ligne"
modal_confirm: 'Voulez-vous valider vos modifications avant de changer de page?'
- pagination: "Liste des horaires %{minVJ} à %{maxVJ} sur %{total}"
selected_journeys: "%{count} course(s) sélectionnée(s)"
show_purchase_window: 'Voir le calendrier commercial'
show_timetable: 'Voir le calendrier'
+ no_associated_timetables: Aucun calendrier associé
+ no_associated_purchase_windows: Aucun calendrier commercial associé
+ no_associated_footnotes: Aucune note associée
+ duplicate:
+ one: Dupliquer %{count} course
+ other: Dupliquer %{count} courses
+ start_time: Horaire de départ indicatif
+ number: Nombre de courses à créer et dupliquer
+ delta: Décalage à partir duquel on créé les courses
+ affect_company: Indiquez un nom de transporteur...
+ no_line_footnotes: La ligne ne possède pas de notes
+ select_footnotes: Sélectionnez les notes à associer à cette course
vehicle_journey:
title_stopless: "Course %{name}"
title: "Course partant de %{stop} à %{time}"
@@ -48,8 +63,9 @@ fr:
show_journeys_without_schedule: "Afficher les courses sans horaires"
slide_arrival: "horaire d'arrivée au 1° arrêt à"
slide_departure: "horaire de départ au 1° arrêt à"
- slide_title: "Décaler l'ensemble des horaires de course"
+ slide_title: "Décaler l'ensemble des horaires de la course : %{id}"
slide: "Décaler"
+ slide_delta: "Avec un décalage de"
starting_stop: "Origine"
stop_title: "Arrêt"
submit_frequency_edit: "Editer course en fréquence"
@@ -57,6 +73,8 @@ fr:
submit_timed_edit: "Editer course"
submit_timed: "Créer course"
time_tables: "Calendriers associés à la course"
+ purchase_windows: "Calendriers commerciaux associés à la course"
+ footnotes: "Notes associés à la course"
to_arrivals: "Copie départs vers arrivées"
to_arrivals: "Copie départs vers arrivées"
to_departures: "Copie arrivées vers départs"
@@ -103,6 +121,7 @@ fr:
checksum: "Signature métier"
comment: "Commentaires"
company: "Transporteur"
+ company_name: "Nom du transporteur"
created_at: "Créé le"
creator_id: "Créé par"
departure_time: "Départ"
@@ -114,6 +133,7 @@ fr:
journey_name: "Nom de la course"
journey_pattern_id: "ID Mission"
journey_pattern: "Mission"
+ journey_pattern_published_name: "Nom public de la mission"
line: "Ligne"
mobility_restricted_suitability: "Accessibilité PMR"
name: "Nom Course"
@@ -129,7 +149,7 @@ fr:
route: "Itinéraire"
time_slot: "Fréquence"
time_table_ids: "Liste des calendriers"
- time_tables: "Calendriers"
+ time_tables: "Calendriers associés"
train_number: "Numéro de train"
transport_mode: "Mode de transport"
transport_submode: "Sous-mode de transport"
@@ -138,6 +158,7 @@ fr:
updated_at: "Edité le"
vehicle_journey_at_stop_ids: "Liste des horaires"
vehicle_type_identifier: "Type d'identifiant du véhicule"
+ start_time: Heure de départ
errors:
models:
vehicle_journey:
diff --git a/config/locales/will_paginate.en.yml b/config/locales/will_paginate.en.yml
index 29b8fe2bf..8f3189675 100644
--- a/config/locales/will_paginate.en.yml
+++ b/config/locales/will_paginate.en.yml
@@ -32,11 +32,11 @@ en:
single_page:
zero: "No item found"
one: "1 %{model} shown"
- other: "%{model} 1 to %{count} of %{count}"
+ other: "%{model} 1 to %{count} out of %{count}"
single_page_html:
zero: "No item found"
one: "1 %{model} shown"
- other: "%{model} 1 to %{count} of %{count}"
+ other: "%{model} 1 to %{count} out of %{count}"
- multi_page: "%{model} %{from} to %{to} of %{count}"
- multi_page_html: "%{model} %{from} to %{to} of %{count}"
+ multi_page: "%{model} list %{from} to %{to} out of %{count}"
+ multi_page_html: "%{model} list %{from} to %{to} out of %{count}"
diff --git a/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb b/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb
new file mode 100644
index 000000000..e49be7e40
--- /dev/null
+++ b/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb
@@ -0,0 +1,5 @@
+class AddCustomFieldValuesToStopAreas < ActiveRecord::Migration
+ def change
+ add_column :stop_areas, :custom_field_values, :jsonb
+ end
+end
diff --git a/db/migrate/20180316115003_add_custom_field_values_to_companies.rb b/db/migrate/20180316115003_add_custom_field_values_to_companies.rb
new file mode 100644
index 000000000..1791a6970
--- /dev/null
+++ b/db/migrate/20180316115003_add_custom_field_values_to_companies.rb
@@ -0,0 +1,5 @@
+class AddCustomFieldValuesToCompanies < ActiveRecord::Migration
+ def change
+ add_column :companies, :custom_field_values, :json
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d90bf7b6c..0f6f21b83 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -141,6 +141,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do
t.text "import_xml"
t.datetime "created_at"
t.datetime "updated_at"
+ t.json "custom_field_values"
end
add_index "companies", ["line_referential_id"], name: "index_companies_on_line_referential_id", using: :btree
@@ -842,10 +843,8 @@ ActiveRecord::Schema.define(version: 20180319043333) do
t.integer "waiting_time"
t.string "kind"
t.jsonb "localized_names"
-
t.datetime "confirmed_at"
- t.json "custom_field_values"
-
+ t.jsonb "custom_field_values"
end
add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree
diff --git a/db/seeds/seed_helpers.rb b/db/seeds/seed_helpers.rb
new file mode 100644
index 000000000..8e47e10bd
--- /dev/null
+++ b/db/seeds/seed_helpers.rb
@@ -0,0 +1,12 @@
+class ActiveRecord::Base
+ def self.seed_by(key_attribute, &block)
+ model = find_or_create_by! key_attribute
+ print "Seed #{name} #{key_attribute.inspect} "
+ yield model
+
+ puts "[#{(model.changed? ? 'updated' : 'no change')}]"
+ model.save!
+
+ model
+ end
+end
diff --git a/db/seeds/stif.seeds.rb b/db/seeds/stif.seeds.rb
index aa87b6f6c..bb73b0b9c 100644
--- a/db/seeds/stif.seeds.rb
+++ b/db/seeds/stif.seeds.rb
@@ -1,46 +1,25 @@
# coding: utf-8
-# This file should contain all the record creation needed to seed the database with its default values.
-# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
-stop_area_referential = StopAreaReferential.find_or_create_by!(name: "Reflex", objectid_format: "stif_netex")
-line_referential = LineReferential.find_or_create_by!(name: "CodifLigne", objectid_format: "stif_netex")
+require_relative 'seed_helpers'
-workgroup = Workgroup.find_or_create_by!(name: "Gestion de l'offre théorique IDFm") do |w|
- w.line_referential = line_referential
- w.stop_area_referential = stop_area_referential
- w.export_types = ["Export::Netex"]
+stif = Organisation.seed_by(code: "STIF") do |o|
+ o.name = 'STIF'
end
-Workbench.update_all workgroup_id: workgroup
-
-# Organisations
-stif = Organisation.find_or_create_by!(code: "STIF") do |org|
- org.name = 'STIF'
+stop_area_referential = StopAreaReferential.seed_by(name: "Reflex") do |r|
+ r.objectid_format = "stif_netex"
+ r.add_member stif, owner: true
end
-# operator = Organisation.find_or_create_by!(code: 'transporteur-a') do |organisation|
-# organisation.name = "Transporteur A"
-# end
-
-# Member
-line_referential.add_member stif, owner: true
-# line_referential.add_member operator
-stop_area_referential.add_member stif, owner: true
-# stop_area_referential.add_member operator
+line_referential = LineReferential.seed_by(name: "CodifLigne") do |r|
+ r.objectid_format = "stif_netex"
+ r.add_member stif, owner: true
+end
-# Users
-# stif.users.find_or_create_by!(username: "admin") do |user|
-# user.email = 'stif-boiv@af83.com'
-# user.password = "secret"
-# user.name = "STIF Administrateur"
-# end
-#
-# operator.users.find_or_create_by!(username: "transporteur") do |user|
-# user.email = 'stif-boiv+transporteur@af83.com'
-# user.password = "secret"
-# user.name = "Martin Lejeune"
-# end
+workgroup = Workgroup.seed_by(name: "Gestion de l'offre théorique IDFm") do |w|
+ w.line_referential = line_referential
+ w.stop_area_referential = stop_area_referential
+ w.export_types = ["Export::Netex"]
+end
-# Include all Lines in organisation functional_scope
-stif.update sso_attributes: { functional_scope: line_referential.lines.pluck(:objectid) }
-#operator.update sso_attributes: { functional_scope: line_referential.lines.limit(3).pluck(:objectid) }
+Workbench.update_all workgroup_id: workgroup
diff --git a/lib/tom_tom.rb b/lib/tom_tom.rb
index a1a2bda43..fcebcc7ac 100644
--- a/lib/tom_tom.rb
+++ b/lib/tom_tom.rb
@@ -19,4 +19,8 @@ module TomTom
def self.batch(way_costs)
TomTom::Batch.new(@connection).batch(way_costs)
end
+
+ def self.matrix(way_costs)
+ TomTom::Matrix.new(@connection).matrix(way_costs)
+ end
end
diff --git a/lib/tom_tom/matrix.rb b/lib/tom_tom/matrix.rb
new file mode 100644
index 000000000..b0c8cc335
--- /dev/null
+++ b/lib/tom_tom/matrix.rb
@@ -0,0 +1,114 @@
+module TomTom
+ class Matrix
+ def initialize(connection)
+ @connection = connection
+ end
+
+ def matrix(way_costs)
+ points_with_ids = points_from_way_costs(way_costs)
+ points = points_as_params(points_with_ids)
+
+ Rails.logger.info "Invoke TomTom for #{points.size} points"
+
+ response = @connection.post do |req|
+ req.url '/routing/1/matrix/json'
+ req.headers['Content-Type'] = 'application/json'
+
+ req.params[:routeType] = 'shortest'
+ req.params[:travelMode] = 'bus'
+
+ req.body = build_request_body(points)
+ end
+
+ extract_costs_to_way_costs!(
+ way_costs,
+ points_with_ids,
+ JSON.parse(response.body)
+ )
+ end
+
+ def points_from_way_costs(way_costs)
+ points = []
+
+ way_costs.each do |way_cost|
+ departure_id, arrival_id = way_cost.id.split('-')
+
+ departure = TomTom::Matrix::Point.new(
+ way_cost.departure,
+ departure_id
+ )
+ arrival = TomTom::Matrix::Point.new(
+ way_cost.arrival,
+ arrival_id
+ )
+
+ # Don't add duplicate coordinates. This assumes that
+ # `way_costs` consists of an ordered route of points where
+ # each departure coordinate is the same as the preceding
+ # arrival coordinate.
+ if points.empty? ||
+ points.last.coordinates != departure.coordinates
+ points << departure
+ end
+
+ points << arrival
+ end
+
+ points
+ end
+
+ def points_as_params(points)
+ points.map do |point|
+ {
+ point: {
+ latitude: point.coordinates.lat,
+ longitude: point.coordinates.lng
+ }
+ }
+ end
+ end
+
+ def build_request_body(points)
+ # Serialize `BigDecimal` values as floats to please the TomTom API
+ RequestJSONSerializer.dump({
+ origins: points,
+ destinations: points
+ })
+ end
+
+ def extract_costs_to_way_costs!(way_costs, points, matrix_json)
+ way_costs = []
+
+ # `row` and `column` order is the same as `points`
+ matrix_json['matrix'].each_with_index do |row, row_i|
+ row.each_with_index do |column, column_i|
+ next if column['statusCode'] != 200
+
+ distance = column['response']['routeSummary']['lengthInMeters']
+
+ # Ignore costs between a point and itself (e.g. from A to A)
+ next if distance == 0
+
+ departure = points[row_i]
+ arrival = points[column_i]
+
+ way_costs << WayCost.new(
+ departure: Geokit::LatLng.new(
+ departure.coordinates.lat,
+ departure.coordinates.lng
+ ),
+ arrival: Geokit::LatLng.new(
+ arrival.coordinates.lat,
+ arrival.coordinates.lng
+ ),
+ distance: distance,
+ time: column['response']['routeSummary']['travelTimeInSeconds'],
+ id: "#{departure.id}-#{arrival.id}"
+ )
+ end
+ end
+
+ way_costs
+ end
+ end
+end
diff --git a/lib/tom_tom/matrix/point.rb b/lib/tom_tom/matrix/point.rb
new file mode 100644
index 000000000..435b4d4b0
--- /dev/null
+++ b/lib/tom_tom/matrix/point.rb
@@ -0,0 +1,18 @@
+module TomTom
+ class Matrix
+ class Point
+ attr_reader :coordinates, :id
+
+ def initialize(coordinates, id)
+ @coordinates = coordinates
+ @id = id
+ end
+
+ def ==(other)
+ other.is_a?(self.class) &&
+ @coordinates == other.coordinates &&
+ @id == other.id
+ end
+ end
+ end
+end
diff --git a/lib/tom_tom/matrix/request_json_serializer.rb b/lib/tom_tom/matrix/request_json_serializer.rb
new file mode 100644
index 000000000..f4d12e482
--- /dev/null
+++ b/lib/tom_tom/matrix/request_json_serializer.rb
@@ -0,0 +1,25 @@
+module TomTom
+ class Matrix
+ class RequestJSONSerializer
+ def self.dump(hash)
+ hash[:origins].map! do |point|
+ point_to_f(point)
+ end
+ hash[:destinations].map! do |point|
+ point_to_f(point)
+ end
+
+ JSON.dump(hash)
+ end
+
+ private
+
+ def self.point_to_f(point)
+ point[:point][:latitude] = point[:point][:latitude].to_f
+ point[:point][:longitude] = point[:point][:longitude].to_f
+
+ point
+ end
+ end
+ end
+end
diff --git a/spec/controllers/exports_controller_spec.rb b/spec/controllers/exports_controller_spec.rb
index 9d8dde4ff..e2b89fc26 100644
--- a/spec/controllers/exports_controller_spec.rb
+++ b/spec/controllers/exports_controller_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe ExportsController, :type => :controller do
context "with the token" do
it 'should be successful' do
post :upload, workbench_id: workbench.id, id: export.id, token: export.token_upload
- expect(response).to be_redirect
+ expect(response).to be_success
end
end
diff --git a/spec/factories/custom_fields.rb b/spec/factories/custom_fields.rb
index 7c43a6147..db7a3823e 100644
--- a/spec/factories/custom_fields.rb
+++ b/spec/factories/custom_fields.rb
@@ -3,7 +3,7 @@ FactoryGirl.define do
code "code"
resource_type "VehicleJourney"
sequence(:name){|n| "custom field ##{n}"}
- field_type "list"
+ field_type "integer"
options( { capacity: "0" } )
end
end
diff --git a/spec/features/calendars_permissions_spec.rb b/spec/features/calendars_permissions_spec.rb
index 4857592d5..656c0dd78 100644
--- a/spec/features/calendars_permissions_spec.rb
+++ b/spec/features/calendars_permissions_spec.rb
@@ -1,8 +1,8 @@
RSpec.describe 'Calendars', type: :feature do
login_user
- let(:calendar) { create :calendar, organisation_id: 1 }
- let(:workgroup) { calendar.workgroup }
+ let(:calendar) { create :calendar, organisation: first_organisation, workgroup: first_workgroup }
+ let(:workgroup) { first_workgroup }
describe 'permissions' do
before do
diff --git a/spec/features/calendars_spec.rb b/spec/features/calendars_spec.rb
new file mode 100644
index 000000000..26220746b
--- /dev/null
+++ b/spec/features/calendars_spec.rb
@@ -0,0 +1,16 @@
+RSpec.describe 'Calendars', type: :feature do
+ login_user
+
+ let(:calendar1) { create(:calendar, workgroup: @user.organisation.workgroups.first, organisation: @user.organisation) }
+ let(:calendar2) { create(:calendar) }
+
+ describe "index" do
+ before(:each) do
+ visit workgroup_calendars_path(calendar1.workgroup)
+ end
+ it "should only display calendars from same workgroup" do
+ expect(page).to have_content calendar1.name
+ expect(page).to_not have_content calendar2.name
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/features/compliance_control_sets_spec.rb b/spec/features/compliance_control_sets_spec.rb
index 0f4597db3..306f363a5 100644
--- a/spec/features/compliance_control_sets_spec.rb
+++ b/spec/features/compliance_control_sets_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe "ComplianceControlSets", type: :feature do
let(:other_control_cset) { create :compliance_control_set, organisation: other_orga }
let(:blox){
- 2.times.map{ | _ | create :compliance_control_block, compliance_control_set: control_set }
+ 2.times.map{ |n| create :compliance_control_block, compliance_control_set: control_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] }
}
before do
diff --git a/spec/fixtures/tom_tom_matrix.json b/spec/fixtures/tom_tom_matrix.json
new file mode 100644
index 000000000..30048576c
--- /dev/null
+++ b/spec/fixtures/tom_tom_matrix.json
@@ -0,0 +1,123 @@
+{
+ "formatVersion": "0.0.1",
+ "matrix": [
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 0,
+ "travelTimeInSeconds": 0,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T11:20:17+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 117947,
+ "travelTimeInSeconds": 8356,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T13:39:32+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 999088,
+ "travelTimeInSeconds": 62653,
+ "trafficDelayInSeconds": 298,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-24T04:44:30+01:00"
+ }
+ }
+ }
+ ],
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 117231,
+ "travelTimeInSeconds": 9729,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T14:02:25+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 0,
+ "travelTimeInSeconds": 0,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T11:20:17+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 1114635,
+ "travelTimeInSeconds": 72079,
+ "trafficDelayInSeconds": 298,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T07:21:36+01:00"
+ }
+ }
+ }
+ ],
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 997232,
+ "travelTimeInSeconds": 63245,
+ "trafficDelayInSeconds": 179,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T04:54:23+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 1113108,
+ "travelTimeInSeconds": 68485,
+ "trafficDelayInSeconds": 52,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T06:21:43+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 344,
+ "travelTimeInSeconds": 109,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-23T11:22:07+01:00"
+ }
+ }
+ }
+ ]
+ ],
+ "summary": {
+ "successfulRoutes": 9,
+ "totalRoutes": 9
+ }
+}
diff --git a/spec/lib/compliance_control_set_cloner_spec.rb b/spec/lib/compliance_control_set_cloner_spec.rb
index 7efe27659..0d3561e0e 100644
--- a/spec/lib/compliance_control_set_cloner_spec.rb
+++ b/spec/lib/compliance_control_set_cloner_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe ComplianceControlSetCloner do
context 'Directed Acyclic Graph is copied correctly' do
let(:source_blox){
- 3.times.map{ |_| create :compliance_control_block, compliance_control_set: source_set }
+ 3.times.map{ |n| create :compliance_control_block, compliance_control_set: source_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] }
}
let(:direct_ccs){
3.times.map{ |n| create :generic_attribute_control_min_max, compliance_control_set: source_set, name: "direct #{n.succ}", code: "direct-#{n.succ}" }
diff --git a/spec/lib/compliance_control_set_copier_spec.rb b/spec/lib/compliance_control_set_copier_spec.rb
index 0f15d86d0..d1a56cd7f 100644
--- a/spec/lib/compliance_control_set_copier_spec.rb
+++ b/spec/lib/compliance_control_set_copier_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe ComplianceControlSetCopier do
context 'Directed Acyclic Graph is copied correctly' do
let(:cc_blox){
- 3.times.map{ |_| create :compliance_control_block, compliance_control_set: cc_set }
+ 3.times.map{ |n| create :compliance_control_block, compliance_control_set: cc_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] }
}
let!(:direct_ccs){
3.times.map{ |n| create :compliance_control, compliance_control_set: cc_set, name: "direct #{n.succ}", code: "direct-#{n.succ}" }
diff --git a/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb b/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb
new file mode 100644
index 000000000..1fafad302
--- /dev/null
+++ b/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb
@@ -0,0 +1,39 @@
+RSpec.describe TomTom::Matrix::RequestJSONSerializer do
+ describe ".dump" do
+ it "serializes BigDecimal values to floats" do
+ points = [{
+ point: {
+ latitude: 52.50867.to_d,
+ longitude: 13.42879.to_d
+ },
+ }]
+ data = {
+ origins: points,
+ destinations: points
+ }
+
+ expect(
+ TomTom::Matrix::RequestJSONSerializer.dump(data)
+ ).to eq(<<-JSON.delete(" \n"))
+ {
+ "origins": [
+ {
+ "point": {
+ "latitude": 52.50867,
+ "longitude": 13.42879
+ }
+ }
+ ],
+ "destinations": [
+ {
+ "point": {
+ "latitude": 52.50867,
+ "longitude": 13.42879
+ }
+ }
+ ]
+ }
+ JSON
+ end
+ end
+end
diff --git a/spec/lib/tom_tom/matrix_spec.rb b/spec/lib/tom_tom/matrix_spec.rb
new file mode 100644
index 000000000..605f1d254
--- /dev/null
+++ b/spec/lib/tom_tom/matrix_spec.rb
@@ -0,0 +1,228 @@
+RSpec.describe TomTom::Matrix do
+ let(:matrix) { TomTom::Matrix.new(nil) }
+
+ describe "#points_from_way_costs" do
+ it "extracts a set of lat/lng coordinates from a list of WayCosts" do
+ way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ id: '44-77'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ id: '77-88'
+ )
+ ]
+
+ expect(
+ matrix.points_from_way_costs(way_costs)
+ ).to eq([
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(48.85086, 2.36143),
+ '44'
+ ),
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(47.91231, 1.87606),
+ '77'
+ ),
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(52.50867, 13.42879),
+ '88'
+ )
+ ])
+ end
+ end
+
+ describe "#points_as_params" do
+ it "transforms a set of LatLng points into a hash for use by TomTom Matrix" do
+ points = [
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(48.85086, 2.36143),
+ '44'
+ ),
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(47.91231, 1.87606),
+ '77'
+ ),
+ TomTom::Matrix::Point.new(
+ Geokit::LatLng.new(52.50867, 13.42879),
+ '88'
+ )
+ ]
+
+ expect(
+ matrix.points_as_params(points)
+ ).to eq([
+ {
+ point: {
+ latitude: 48.85086,
+ longitude: 2.36143
+ },
+ },
+ {
+ point: {
+ latitude: 47.91231,
+ longitude: 1.87606
+ },
+ },
+ {
+ point: {
+ latitude: 52.50867,
+ longitude: 13.42879
+ },
+ }
+ ])
+ end
+ end
+
+ describe "#build_request_body" do
+ it "serializes BigDecimal coordinates to floats" do
+ points = [
+ {
+ point: {
+ latitude: 48.85086.to_d,
+ longitude: 2.36143.to_d
+ },
+ },
+ {
+ point: {
+ latitude: 47.91231.to_d,
+ longitude: 1.87606.to_d
+ },
+ },
+ {
+ point: {
+ latitude: 52.50867.to_d,
+ longitude: 13.42879.to_d
+ },
+ }
+ ]
+
+ expect(
+ matrix.build_request_body(points)
+ ).to eq(<<-JSON.delete(" \n"))
+ {
+ "origins": [
+ {
+ "point": {
+ "latitude": 48.85086,
+ "longitude": 2.36143
+ }
+ },
+ {
+ "point": {
+ "latitude": 47.91231,
+ "longitude": 1.87606
+ }
+ },
+ {
+ "point": {
+ "latitude": 52.50867,
+ "longitude": 13.42879
+ }
+ }
+ ],
+ "destinations": [
+ {
+ "point": {
+ "latitude": 48.85086,
+ "longitude": 2.36143
+ }
+ },
+ {
+ "point": {
+ "latitude": 47.91231,
+ "longitude": 1.87606
+ }
+ },
+ {
+ "point": {
+ "latitude": 52.50867,
+ "longitude": 13.42879
+ }
+ }
+ ]
+ }
+ JSON
+ end
+ end
+
+ describe "#extract_costs_to_way_costs!" do
+ it "puts distance & time costs in way_costs" do
+ way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ id: '55-99'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ id: '99-22'
+ )
+ ]
+
+ expected_way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ distance: 117947,
+ time: 8356,
+ id: '55-99'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 999088,
+ time: 62653,
+ id: '55-22'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(48.85086, 2.36143),
+ distance: 117231,
+ time: 9729,
+ id: '99-55'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 1114635,
+ time: 72079,
+ id: '99-22'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(48.85086, 2.36143),
+ distance: 997232,
+ time: 63245,
+ id: '22-55'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ distance: 1113108,
+ time: 68485,
+ id: '22-99'
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 344,
+ time: 109,
+ id: '22-22'
+ )
+ ]
+
+ matrix_response = JSON.parse(read_fixture('tom_tom_matrix.json'))
+
+ points = matrix.points_from_way_costs(way_costs)
+
+ expect(
+ matrix.extract_costs_to_way_costs!(way_costs, points, matrix_response)
+ ).to match_array(expected_way_costs)
+ end
+ end
+end
diff --git a/spec/models/chouette/route/route_stop_points_spec.rb b/spec/models/chouette/route/route_stop_points_spec.rb
index f8edadfee..af26f017a 100644
--- a/spec/models/chouette/route/route_stop_points_spec.rb
+++ b/spec/models/chouette/route/route_stop_points_spec.rb
@@ -86,24 +86,6 @@ RSpec.describe Chouette::Route, :type => :model do
end
end
end
-
- context 'defaults attributes' do
- let(:new_stop_area) { create :stop_area, kind: 'non_commercial', area_type: 'deposit' }
- let(:new_stop_point) { create :stop_point, stop_area_id: new_stop_area.id }
- it 'should have the correct default attributes' do
- subject.stop_points << new_stop_point
- subject.stop_points.each do |sp|
- # binding.pry
- if sp.stop_area.commercial?
- expect(sp.for_boarding).to eq('normal')
- expect(sp.for_alighting).to eq('normal')
- else
- expect(sp.for_boarding).to eq('forbidden')
- expect(sp.for_alighting).to eq('forbidden')
- end
- end
- end
- end
end
describe "#stop_areas" do
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index c69655bd4..7292f09f9 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -420,6 +420,7 @@ describe Chouette::VehicleJourney, :type => :model do
state['published_journey_name'] = 'edited_name'
state['published_journey_identifier'] = 'edited_identifier'
state['custom_fields'] = {energy: {value: 99}}
+ create :custom_field, field_type: :integer, code: :energy, name: :energy
Chouette::VehicleJourney.state_update(route, collection)
expect(state['errors']).to be_nil
diff --git a/spec/models/compliance_control_block_spec.rb b/spec/models/compliance_control_block_spec.rb
index 4abe0ed9c..089d78434 100644
--- a/spec/models/compliance_control_block_spec.rb
+++ b/spec/models/compliance_control_block_spec.rb
@@ -17,4 +17,16 @@ RSpec.describe ComplianceControlBlock, type: :model do
it { should_not allow_values( *%w{ demandResponseBus nightus irportLinkBus highrequencyBus expressBUs
Shuttle suburban regioalRail interregion4lRail })
.for(:transport_submode) }
+
+ context "transport mode & submode uniqueness" do
+ let(:cc_block) {create :compliance_control_block, transport_mode: 'bus', transport_submode: 'nightBus'}
+ let(:cc_set1) { cc_block.compliance_control_set }
+ let(:cc_set2) { create :compliance_control_set }
+
+ it "sould be unique in a compliance control set" do
+ expect( ComplianceControlBlock.new(transport_mode: 'bus', transport_submode: 'nightBus', compliance_control_set: cc_set1) ).not_to be_valid
+ expect( ComplianceControlBlock.new(transport_mode: 'bus', transport_submode: 'nightBus', compliance_control_set: cc_set2) ).to be_valid
+ end
+
+end
end
diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb
index b92bcfbdb..1dfe6d33c 100644
--- a/spec/models/custom_field_spec.rb
+++ b/spec/models/custom_field_spec.rb
@@ -1,6 +1,7 @@
require 'rails_helper'
RSpec.describe CustomField, type: :model do
+
let( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} }
context "validates" do
@@ -35,11 +36,43 @@ RSpec.describe CustomField, type: :model do
end
context "custom field_values for a resource" do
+ before do
+ create :custom_field, field_type: :integer, code: :energy, name: :energy
+ end
+
it { expect(vj.custom_field_value("energy")).to eq(99) }
end
+ context "with a 'list' field_type" do
+ let!(:field){ [create(:custom_field, code: :energy, field_type: 'list', options: {list_values: %w(foo bar baz)})] }
+ let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: "1"} }
+ it "should cast the value" do
+ p vj.custom_fields
+ expect(vj.custom_fields[:energy].value).to eq 1
+ expect(vj.custom_fields[:energy].display_value).to eq "bar"
+ end
+
+ it "should validate the value" do
+ {
+ "1" => true,
+ 1 => true,
+ "azerty" => false,
+ "10" => false,
+ 10 => false
+ }.each do |val, valid|
+ vj = build :vehicle_journey, custom_field_values: {energy: val}
+ if valid
+ expect(vj.validate).to be_truthy
+ else
+ expect(vj.validate).to be_falsy
+ expect(vj.errors.messages[:"custom_fields.energy"]).to be_present
+ end
+ end
+ end
+ end
+
context "with an 'integer' field_type" do
- let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'integer'})] }
+ let!(:field){ [create(:custom_field, code: :energy, field_type: 'integer')] }
let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: "99"} }
it "should cast the value" do
expect(vj.custom_fields[:energy].value).to eq 99
@@ -47,6 +80,7 @@ RSpec.describe CustomField, type: :model do
it "should validate the value" do
{
+ 99 => true,
"99" => true,
"azerty" => false,
"91a" => false,
@@ -64,10 +98,39 @@ RSpec.describe CustomField, type: :model do
end
context "with a 'string' field_type" do
- let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'string'})] }
+ let!(:field){ [create(:custom_field, code: :energy, field_type: 'string')] }
let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} }
it "should cast the value" do
expect(vj.custom_fields[:energy].value).to eq '99'
end
end
+
+ context "with a 'attachment' field_type" do
+ let!(:field){ [create(:custom_field, code: :energy, field_type: 'attachment')] }
+ let( :vj ){ create :vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'users.json'))} }
+ it "should cast the value" do
+ expect(vj.custom_fields[:energy].value.class).to be CustomFieldAttachmentUploader
+ path = vj.custom_fields[:energy].value.path
+ expect(File.exists?(path)).to be_truthy
+ expect(vj).to receive(:remove_custom_field_energy!).and_call_original
+ vj.destroy
+ vj.run_callbacks(:commit)
+ expect(File.exists?(path)).to be_falsy
+ end
+
+ it "should display a link" do
+ val = vj.custom_fields[:energy].value
+ out = vj.custom_fields[:energy].display_value
+ expect(out).to match(val.url)
+ expect(out).to match(/\<a.*\>/)
+ end
+
+ context "with a whitelist" do
+ let!(:field){ [create(:custom_field, code: :energy, field_type: 'attachment', options: {extension_whitelist: %w(zip)})] }
+ it "should validate extension" do
+ expect(build(:vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'users.json'))})).to_not be_valid
+ expect(build(:vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'nozip.zip'))})).to be_valid
+ end
+ end
+ end
end
diff --git a/spec/models/referential_metadata_spec.rb b/spec/models/referential_metadata_spec.rb
index 291ed974a..88a12b2bb 100644
--- a/spec/models/referential_metadata_spec.rb
+++ b/spec/models/referential_metadata_spec.rb
@@ -12,14 +12,19 @@ RSpec.describe ReferentialMetadata, :type => :model do
describe ".new_from" do
let(:referential_metadata) { create :referential_metadata, referential_source: create(:referential) }
- let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, []) }
+ let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, nil) }
+ before do
+ referential_metadata.line_ids.each do |id|
+ Chouette::Line.find(id).update_attribute :line_referential_id, referential_metadata.referential.line_referential_id
+ end
+ end
it "should not have an associated referential" do
expect(new_referential_metadata).to be_a_new(ReferentialMetadata)
end
- xit "should have the same lines" do
- expect(new_referential_metadata.lines).to eq(referential_metadata.lines)
+ it "should have the same lines" do
+ expect(new_referential_metadata.line_ids.sort).to eq(referential_metadata.line_ids.sort)
end
it "should have the same periods" do
@@ -34,6 +39,14 @@ RSpec.describe ReferentialMetadata, :type => :model do
expect(new_referential_metadata.referential_source).to eq(referential_metadata.referential)
end
+ context "with a functional scope" do
+ let(:organisation){ create :organisation, sso_attributes: {"functional_scope" => [referential_metadata.referential.lines.first.objectid]} }
+ let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, organisation) }
+
+ it "should scope the lines" do
+ expect(new_referential_metadata.line_ids).to eq [referential_metadata.referential.lines.first.id]
+ end
+ end
end
describe "Period" do
diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb
index 1d9b3d78a..ca2caf57f 100644
--- a/spec/models/referential_spec.rb
+++ b/spec/models/referential_spec.rb
@@ -55,7 +55,7 @@ describe Referential, :type => :model do
context "Cloning referential" do
let(:clone) do
- Referential.new_from(ref, [])
+ Referential.new_from(ref, nil)
end
let!(:workbench){ create :workbench }
diff --git a/spec/services/route_way_cost_calculator_spec.rb b/spec/services/route_way_cost_calculator_spec.rb
index d5358fcf6..79b81e34d 100644
--- a/spec/services/route_way_cost_calculator_spec.rb
+++ b/spec/services/route_way_cost_calculator_spec.rb
@@ -7,18 +7,20 @@ RSpec.describe RouteWayCostCalculator do
# things in the request or response. This is just to fake the request so
# we don't actually call their API in tests. The test doesn't test
# anything given in the response.
- stub_request(:post, "https://api.tomtom.com/routing/1/batch/json?key")
+ stub_request(
+ :post,
+ "https://api.tomtom.com/routing/1/matrix/json?key&routeType=shortest&travelMode=bus"
+ )
.with(
headers: {
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type'=>'application/json',
'User-Agent'=>'Faraday v0.9.2'
- }
- )
+ })
.to_return(
status: 200,
- body: "{\"formatVersion\":\"0.0.1\",\"batchItems\":[{\"statusCode\":200,\"response\":{\"routes\":[{\"summary\":{\"lengthInMeters\":117947,\"travelTimeInSeconds\":7969,\"trafficDelayInSeconds\":0,\"departureTime\":\"2018-03-12T12:32:26+01:00\",\"arrivalTime\":\"2018-03-12T14:45:14+01:00\"}}]}}]}",
+ body: "{\"formatVersion\":\"0.0.1\",\"matrix\":[[{\"statusCode\":200,\"response\":{\"routeSummary\":{\"lengthInMeters\":0,\"travelTimeInSeconds\":0,\"trafficDelayInSeconds\":0,\"departureTime\":\"2018-03-23T11:20:17+01:00\",\"arrivalTime\":\"2018-03-23T11:20:17+01:00\"}}}]]}",
headers: {}
)
diff --git a/spec/support/referential.rb b/spec/support/referential.rb
index 9acdce73a..b50844ae4 100644
--- a/spec/support/referential.rb
+++ b/spec/support/referential.rb
@@ -8,6 +8,10 @@ module ReferentialHelper
Organisation.find_by!(code: "first")
end
+ def first_workgroup
+ Workgroup.find_by_name('IDFM')
+ end
+
def self.included(base)
base.class_eval do
extend ClassMethods
@@ -53,10 +57,18 @@ RSpec.configure do |config|
referential.add_member organisation, owner: true
end
+ workgroup = FactoryGirl.create(
+ :workgroup,
+ name: "IDFM",
+ line_referential: line_referential,
+ stop_area_referential: stop_area_referential
+ )
+
workbench = FactoryGirl.create(
:workbench,
name: "Gestion de l'offre",
organisation: organisation,
+ workgroup: workgroup,
line_referential: line_referential,
stop_area_referential: stop_area_referential
)
diff --git a/spec/views/companies/edit.html.erb_spec.rb b/spec/views/companies/edit.html.erb_spec.rb
index 8aaf705ab..c72d84c0b 100644
--- a/spec/views/companies/edit.html.erb_spec.rb
+++ b/spec/views/companies/edit.html.erb_spec.rb
@@ -5,7 +5,10 @@ describe "/companies/edit", :type => :view do
let!(:company) { assign(:company, create(:company)) }
let!(:companies) { Array.new(2) { create(:company) } }
let!(:line_referential) { assign :line_referential, company.line_referential }
-
+ before do
+ allow(view).to receive(:resource){ company }
+ allow(view).to receive(:current_referential){ first_referential }
+ end
describe "form" do
it "should render input for name" do
render
diff --git a/spec/views/companies/new.html.erb_spec.rb b/spec/views/companies/new.html.erb_spec.rb
index ebb8c03c5..6c2163677 100644
--- a/spec/views/companies/new.html.erb_spec.rb
+++ b/spec/views/companies/new.html.erb_spec.rb
@@ -4,7 +4,10 @@ describe "/companies/new", :type => :view do
let!(:company) { assign(:company, build(:company)) }
let!(:line_referential) { assign :line_referential, company.line_referential }
-
+ before do
+ allow(view).to receive(:resource){company}
+ allow(view).to receive(:current_referential){ first_referential }
+ end
describe "form" do
it "should render input for name" do
diff --git a/spec/views/stop_areas/edit.html.erb_spec.rb b/spec/views/stop_areas/edit.html.erb_spec.rb
index bfbb0bb55..1bdb42817 100644
--- a/spec/views/stop_areas/edit.html.erb_spec.rb
+++ b/spec/views/stop_areas/edit.html.erb_spec.rb
@@ -8,6 +8,7 @@ describe "/stop_areas/edit", :type => :view do
before do
allow(view).to receive(:has_feature?)
+ allow(view).to receive(:resource){ stop_area }
end
describe "form" do
diff --git a/spec/views/stop_areas/new.html.erb_spec.rb b/spec/views/stop_areas/new.html.erb_spec.rb
index 23f7387fa..eced129e4 100644
--- a/spec/views/stop_areas/new.html.erb_spec.rb
+++ b/spec/views/stop_areas/new.html.erb_spec.rb
@@ -7,6 +7,7 @@ describe "/stop_areas/new", :type => :view do
before do
allow(view).to receive(:has_feature?)
+ allow(view).to receive(:resource){ stop_area }
end
describe "form" do