mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-04-04 07:09:14 +00:00
Enterprise User can create and update schedules via OC index
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, Columns, StatusMessage, RequestMonitor, OrderCycles, Enterprises, Schedules) ->
|
||||
angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, Columns, StatusMessage, RequestMonitor, OrderCycles, Enterprises, Schedules, Dereferencer) ->
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.columns = Columns.columns
|
||||
$scope.saveAll = -> OrderCycles.saveChanges($scope.order_cycles_form)
|
||||
@@ -7,10 +7,13 @@ angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, C
|
||||
|
||||
compileData = ->
|
||||
for schedule in $scope.schedules
|
||||
Schedules.linkToOrderCycles(schedule)
|
||||
Dereferencer.dereference(schedule.order_cycles, OrderCycles.orderCyclesByID)
|
||||
for orderCycle in $scope.orderCycles
|
||||
OrderCycles.linkToEnterprises(orderCycle)
|
||||
OrderCycles.linkToSchedules(orderCycle)
|
||||
coordinator = Enterprises.byID[orderCycle.coordinator.id]
|
||||
orderCycle.coordinator = coordinator if coordinator?
|
||||
Dereferencer.dereference(orderCycle.producers, Enterprises.byID)
|
||||
Dereferencer.dereference(orderCycle.shops, Enterprises.byID)
|
||||
Dereferencer.dereference(orderCycle.schedules, Schedules.byID)
|
||||
orderCycle.involvedEnterpriseIDs = [orderCycle.coordinator.id]
|
||||
orderCycle.producerNames = orderCycle.producers.map((producer) -> orderCycle.involvedEnterpriseIDs.push(producer.id); producer.name).join(", ")
|
||||
orderCycle.shopNames = orderCycle.shops.map((shop) -> orderCycle.involvedEnterpriseIDs.push(shop.id); shop.name).join(", ")
|
||||
@@ -29,8 +32,8 @@ angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, C
|
||||
existingIDs = Object.keys(OrderCycles.orderCyclesByID)
|
||||
RequestMonitor.load (orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}", "q[id_not_in][]": existingIDs)).$promise
|
||||
orderCycles.$promise.then ->
|
||||
compileData()
|
||||
$scope.orderCycles.push(orderCycle) for orderCycle in orderCycles
|
||||
compileData()
|
||||
|
||||
daysFromToday = (days) ->
|
||||
now = new Date
|
||||
|
||||
@@ -2,14 +2,8 @@ angular.module("admin.orderCycles").directive 'orderCyclesSelector', (OrderCycle
|
||||
restrict: 'C'
|
||||
templateUrl: 'admin/order_cycles_selector.html'
|
||||
link: (scope, element, attr) ->
|
||||
if scope.scheduleID?
|
||||
scope.selectedOrderCycles = Schedules.byID[scope.scheduleID].orderCycles
|
||||
scope.orderCycleIDs = scope.selectedOrderCycles.map (i, orderCycle) -> orderCycle.id
|
||||
else
|
||||
scope.selectedOrderCycles = []
|
||||
|
||||
scope.availableOrderCycles = (orderCycle for id, orderCycle of OrderCycles.orderCyclesByID when orderCycle not in scope.selectedOrderCycles)
|
||||
|
||||
scope.selectedOrderCycles = (orderCycle for id, orderCycle of OrderCycles.orderCyclesByID when orderCycle.id in scope.schedule.order_cycle_ids)
|
||||
scope.availableOrderCycles = (orderCycle for id, orderCycle of OrderCycles.orderCyclesByID when orderCycle.id not in scope.schedule.order_cycle_ids)
|
||||
|
||||
element.find('#available-order-cycles .order-cycles').sortable
|
||||
connectWith: '#selected-order-cycles .order-cycles'
|
||||
@@ -17,6 +11,6 @@ angular.module("admin.orderCycles").directive 'orderCyclesSelector', (OrderCycle
|
||||
element.find('#selected-order-cycles .order-cycles').sortable
|
||||
connectWith: '#available-order-cycles .order-cycles'
|
||||
receive: (event, ui) ->
|
||||
scope.orderCycleIDs = $('#selected-order-cycles .order-cycles').children('.order-cycle').map((i, element) -> $(element).scope().orderCycle.id).get()
|
||||
scope.schedule.order_cycle_ids = $('#selected-order-cycles .order-cycles').children('.order-cycle').map((i, element) -> $(element).scope().orderCycle.id).get()
|
||||
remove: (event, ui) ->
|
||||
scope.orderCycleIDs = $('#selected-order-cycles .order-cycles').children('.order-cycle').map((i, element) -> $(element).scope().orderCycle.id).get()
|
||||
scope.schedule.order_cycle_ids = $('#selected-order-cycles .order-cycles').children('.order-cycle').map((i, element) -> $(element).scope().orderCycle.id).get()
|
||||
|
||||
@@ -1,40 +1,43 @@
|
||||
angular.module("admin.orderCycles").directive 'scheduleDialog', ($compile, $injector, $templateCache, DialogDefaults, Schedules) ->
|
||||
angular.module("admin.orderCycles").directive 'scheduleDialog', ($window, $compile, $injector, $templateCache, DialogDefaults, Schedules) ->
|
||||
restrict: 'A'
|
||||
scope:
|
||||
scheduleID: '@'
|
||||
scheduleId: '@'
|
||||
link: (scope, element, attr) ->
|
||||
scope.submitted = false
|
||||
scope.name = ""
|
||||
scope.orderCycleIDs = []
|
||||
scope.errors = []
|
||||
# Link opening of dialog to click event on element
|
||||
element.bind 'click', (e) ->
|
||||
existing = Schedules.byID[scope.scheduleId]
|
||||
scope.schedule =
|
||||
id: existing?.id
|
||||
name: existing?.name || ''
|
||||
order_cycle_ids: existing?.order_cycle_ids || []
|
||||
scope.submitted = false
|
||||
scope.errors = []
|
||||
# Compile modal template
|
||||
scope.template = $compile($templateCache.get('admin/schedule_dialog.html'))(scope)
|
||||
# Set Dialog options
|
||||
settings = angular.copy(DialogDefaults)
|
||||
scope.template.dialog(angular.extend(settings,{width: $window.innerWidth * 0.6}))
|
||||
scope.template.dialog(close: -> scope.template.remove())
|
||||
scope.template.dialog('open')
|
||||
|
||||
scope.close = ->
|
||||
scope.template.dialog('close')
|
||||
return
|
||||
|
||||
scope.addSchedule = ->
|
||||
scope.submit = ->
|
||||
scope.schedule_form.$setPristine()
|
||||
scope.submitted = true
|
||||
scope.errors = []
|
||||
return scope.errors.push("Please select at least one order cycle") unless scope.schedule.order_cycle_ids.length > 0
|
||||
if scope.schedule_form.$valid
|
||||
Schedules.add({name: scope.name, order_cycle_ids: scope.orderCycleIDs}).$promise.then (data) ->
|
||||
method = if scope.schedule.id? then Schedules.update else Schedules.add
|
||||
method(scope.schedule).$promise.then (data) ->
|
||||
if data.id
|
||||
scope.name = ""
|
||||
scope.orderCycleIDs = ""
|
||||
scope.submitted = false
|
||||
template.dialog('close')
|
||||
scope.template.dialog('close')
|
||||
, (response) ->
|
||||
if response.data.errors
|
||||
scope.errors.push(error) for error in response.data.errors
|
||||
else
|
||||
scope.errors.push("Sorry! Could not create '#{scope.name}'")
|
||||
return
|
||||
|
||||
# Link opening of dialog to click event on element
|
||||
element.bind 'click', (e) ->
|
||||
# Compile modal template
|
||||
scope.template = $compile($templateCache.get('admin/schedule_dialog.html'))(scope)
|
||||
# Set Dialog options
|
||||
scope.template.dialog(DialogDefaults)
|
||||
scope.template.dialog(close: -> scope.template.remove())
|
||||
scope.template.dialog('open')
|
||||
|
||||
@@ -7,4 +7,6 @@ angular.module("admin.orderCycles").factory 'ScheduleResource', ($resource) ->
|
||||
method: 'POST'
|
||||
'update':
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
})
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
angular.module("admin.orderCycles").factory "Schedules", ($q, RequestMonitor, ScheduleResource) ->
|
||||
angular.module("admin.orderCycles").factory "Schedules", ($q, RequestMonitor, ScheduleResource, OrderCycles, Dereferencer) ->
|
||||
new class Schedules
|
||||
byID: {}
|
||||
# all: []
|
||||
|
||||
add: (params) ->
|
||||
add: (params) =>
|
||||
ScheduleResource.create params, (schedule) =>
|
||||
@byID[schedule.id] = schedule if schedule.id
|
||||
@byID[schedule.id] = schedule if schedule.id?
|
||||
Dereferencer.dereference(schedule.order_cycles, OrderCycles.orderCyclesByID)
|
||||
orderCycle.schedules.push(schedule) for orderCycle in schedule.order_cycles
|
||||
|
||||
update: (params) =>
|
||||
ScheduleResource.update params, (schedule) =>
|
||||
if schedule.id?
|
||||
Dereferencer.dereference(schedule.order_cycles, OrderCycles.orderCyclesByID)
|
||||
for orderCycle in @byID[schedule.id].order_cycles when orderCycle.id not in schedule.order_cycle_ids
|
||||
orderCycle.schedules.splice(i, 1) for s, i in orderCycle.schedules by -1 when s.id == schedule.id
|
||||
for orderCycle in schedule.order_cycles when orderCycle.id not in @byID[schedule.id].order_cycle_ids
|
||||
orderCycle.schedules.push(@byID[schedule.id])
|
||||
angular.extend(@byID[schedule.id], schedule)
|
||||
|
||||
# remove: (schedule) ->
|
||||
# params = id: schedule.id
|
||||
@@ -26,8 +38,3 @@ angular.module("admin.orderCycles").factory "Schedules", ($q, RequestMonitor, Sc
|
||||
# @all = data
|
||||
RequestMonitor.load(request.$promise)
|
||||
request
|
||||
|
||||
linkToOrderCycles: (schedule) ->
|
||||
for orderCycle, i in schedule.orderCycles
|
||||
orderCycle = OrderCycles.orderCyclesByID[orderCycle.id]
|
||||
schedule.orderCycles[i] = orderCycle if orderCycle?
|
||||
|
||||
@@ -66,22 +66,3 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy
|
||||
|
||||
resetAttribute: (order_cycle, attribute) ->
|
||||
order_cycle[attribute] = @pristineByID[order_cycle.id][attribute]
|
||||
|
||||
linkAllToEnterprises: ->
|
||||
for id, orderCycle of @orderCyclesByID
|
||||
@linkToEnterprises(orderCycle)
|
||||
|
||||
linkToEnterprises: (orderCycle) ->
|
||||
coordinator = Enterprises.enterprisesByID[orderCycle.coordinator.id]
|
||||
orderCycle.coordinator = coordinator if coordinator?
|
||||
for producer, i in orderCycle.producers
|
||||
producer = Enterprises.enterprisesByID[producer.id]
|
||||
orderCycle.producers[i] = producer if producer?
|
||||
for shop, i in orderCycle.shops
|
||||
shop = Enterprises.enterprisesByID[shop.id]
|
||||
orderCycle.shops[i] = shop if shop?
|
||||
|
||||
linkToSchedules: (orderCycle) ->
|
||||
for schedule, i in orderCycle.schedules
|
||||
schedule = Schedules.byID[schedule.id]
|
||||
orderCycle.schedules[i] = schedule if schedule?
|
||||
|
||||
@@ -3,7 +3,8 @@ angular.module("admin.utils").factory "DialogDefaults", ($window) ->
|
||||
hide: { effect: "fade", duration: 300 }
|
||||
autoOpen: false
|
||||
resizable: false
|
||||
width: $window.innerWidth * 0.4;
|
||||
width: $window.innerWidth * 0.4
|
||||
position: ['middle', 100]
|
||||
modal: true
|
||||
open: (event, ui) ->
|
||||
$('.ui-widget-overlay').bind 'click', ->
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
.order-cycles
|
||||
.order-cycle{ ng: { repeat: 'orderCycle in selectedOrderCycles' } }
|
||||
{{ orderCycle.name }}
|
||||
.error{ ng: { repeat: "error in errors", bind: "error" } }
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
#schedule-dialog
|
||||
.text-normal.margin-bottom-30.text-center
|
||||
= t('admin.order_cycles.index.add_a_new_schedule')
|
||||
%span{ ng: { hide: 'schedule.id' } }= t('admin.order_cycles.index.adding_a_new_schedule')
|
||||
%span{ ng: { show: 'schedule.id' } }= t('admin.order_cycles.index.updating_a_schedule')
|
||||
|
||||
%form{ name: 'schedule_form', novalidate: true, ng: { submit: "addSchedule()" }}
|
||||
%form{ name: 'schedule_form', novalidate: true, ng: { submit: "submit()" }}
|
||||
|
||||
.text-center.margin-bottom-20
|
||||
%input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: t('admin.order_cycles.index.schedule_name_placeholder'), ng: { model: "name" } }
|
||||
%div{ ng: { show: "submitted && new_schedule_form.$pristine" } }
|
||||
.error{ ng: { show: "(new_schedule_form.name.$error.required)" } }
|
||||
%input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: t('admin.order_cycles.index.schedule_name_placeholder'), ng: { model: "schedule.name" } }
|
||||
%div{ ng: { show: "submitted && schedule_form.$pristine" } }
|
||||
.error{ ng: { show: "(schedule_form.name.$error.required)" } }
|
||||
= t('admin.order_cycles.index.name_required_error')
|
||||
.error{ ng: { repeat: "error in errors", bind: "error" } }
|
||||
|
||||
.order-cycles-selector.text-center.margin-bottom-30
|
||||
|
||||
.text-center
|
||||
%input.button.icon-plus{ type: 'submit', value: t('admin.order_cycles.index.create_schedule') }
|
||||
%input.button.icon-plus{ type: 'submit', value: t('admin.order_cycles.index.create_schedule'), ng: { hide: 'schedule.id' } }
|
||||
%input.button.icon-plus{ type: 'submit', value: t('admin.order_cycles.index.update_schedule'), ng: { show: 'schedule.id' } }
|
||||
or
|
||||
%input.button.red.icon-remove{ type: 'button', value: t('actions.cancel'), ng: { click: 'close()' } }
|
||||
|
||||
@@ -66,6 +66,9 @@ input.search {
|
||||
background-color: #fa787e;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
form.order_cycle {
|
||||
h2 {
|
||||
|
||||
@@ -2,12 +2,16 @@ require 'open_food_network/permissions'
|
||||
|
||||
module Admin
|
||||
class SchedulesController < ResourceController
|
||||
before_filter :check_editable_order_cycle_ids, only: [:create, :update]
|
||||
|
||||
respond_to :json
|
||||
|
||||
respond_override create: { json: {
|
||||
success: lambda {
|
||||
binding.pry
|
||||
render_as_json @schedule, editable_schedule_ids: permissions.editable_schedules.pluck(:id)
|
||||
},
|
||||
success: lambda { render_as_json @schedule, editable_schedule_ids: permissions.editable_schedules.pluck(:id) },
|
||||
failure: lambda { render json: { errors: @schedule.errors.full_messages }, status: :unprocessable_entity }
|
||||
} }
|
||||
respond_override update: { json: {
|
||||
success: lambda { render_as_json @schedule, editable_schedule_ids: permissions.editable_schedules.pluck(:id) },
|
||||
failure: lambda { render json: { errors: @schedule.errors.full_messages }, status: :unprocessable_entity }
|
||||
} }
|
||||
|
||||
@@ -30,6 +34,15 @@ module Admin
|
||||
[:index]
|
||||
end
|
||||
|
||||
def check_editable_order_cycle_ids
|
||||
return unless params[:schedule][:order_cycle_ids]
|
||||
requested = params[:schedule][:order_cycle_ids]
|
||||
existing = @schedule.order_cycle_ids
|
||||
permitted = OrderCycle.where(id: params[:schedule][:order_cycle_ids] + existing).merge(OrderCycle.managed_by(spree_current_user)).pluck(:id)
|
||||
params[:schedule][:order_cycle_ids] |= (existing - permitted)
|
||||
params[:schedule][:order_cycle_ids] -= (requested - permitted)
|
||||
end
|
||||
|
||||
def permissions
|
||||
return @permissions unless @permission.nil?
|
||||
@permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
|
||||
@@ -2,4 +2,6 @@ class Schedule < ActiveRecord::Base
|
||||
has_and_belongs_to_many :order_cycles, join_table: 'order_cycle_schedules'
|
||||
|
||||
attr_accessible :name, :order_cycle_ids
|
||||
|
||||
validates :order_cycles, presence: true
|
||||
end
|
||||
|
||||
@@ -186,6 +186,9 @@ class AbilityDecorator
|
||||
OrderCycle.accessible_by(user).include? order_cycle
|
||||
end
|
||||
can [:admin, :index, :create], Schedule
|
||||
can [:admin, :update], Schedule do |schedule|
|
||||
OpenFoodNetwork::Permissions.new(user).editable_schedules.include? schedule
|
||||
end
|
||||
can [:bulk_update, :clone, :destroy, :notify_producers], OrderCycle do |order_cycle|
|
||||
user.enterprises.include? order_cycle.coordinator
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%colgroup
|
||||
%col{ ng: { show: 'columns.name.visible' } }
|
||||
%col{ ng: { show: 'columns.schedule.visible' } }
|
||||
%col{ ng: { show: 'columns.schedules.visible' } }
|
||||
%col{ ng: { show: 'columns.open.visible' }, style: 'width: 20%;' }
|
||||
%col{ ng: { show: 'columns.close.visible' }, style: 'width: 20%;' }
|
||||
- unless simple_index
|
||||
@@ -16,7 +16,7 @@
|
||||
%tr
|
||||
%th{ ng: { show: 'columns.name.visible' } }
|
||||
=t :name
|
||||
%th{ ng: { show: 'columns.schedule.visible' } }
|
||||
%th{ ng: { show: 'columns.schedules.visible' } }
|
||||
=t('admin.order_cycles.index.schedules')
|
||||
%th{ ng: { show: 'columns.open.visible' } }
|
||||
=t :open
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
%tr{ class: "order-cycle-{{orderCycle.id}} {{orderCycle.status}}", ng: { repeat: 'orderCycle in orderCycles | involving:involvingFilter | filter:{name: query} track by orderCycle.id' } }
|
||||
%td{ ng: { show: 'columns.name.visible' } }
|
||||
%td.name{ ng: { show: 'columns.name.visible' } }
|
||||
%a{ ng: { href: '{{orderCycle.edit_path}}' } }
|
||||
{{ orderCycle.name }}
|
||||
%td{ ng: { show: 'columns.schedule.visible' } }
|
||||
%a{ href: '#', 'schedule-dialog' => true, 'schedule-id' => 'schedule.id', ng: { repeat: 'schedule in orderCycle.schedules'} }
|
||||
{{ schedule.name }}
|
||||
%td{ ng: { show: 'columns.open.visible' } }
|
||||
%td.schedules{ ng: { show: 'columns.schedules.visible' } }
|
||||
%span{ ng: { repeat: 'schedule in orderCycle.schedules'} }
|
||||
%a{ 'schedule-dialog' => true, 'schedule-id' => '{{schedule.id}}' }
|
||||
{{ schedule.name }}
|
||||
%span{ ng: { show: 'orderCycle.schedules.length == 0'}} None
|
||||
%td.orders_open_at{ ng: { show: 'columns.open.visible' } }
|
||||
%input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at' }, datetimepicker: 'orderCycle.orders_open_at' }
|
||||
%input{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at'}, disabled: true }
|
||||
%td{ ng: { show: 'columns.close.visible' } }
|
||||
%td.orders_close_at{ ng: { show: 'columns.close.visible' } }
|
||||
%input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at' }, datetimepicker: 'orderCycle.orders_close_at' }
|
||||
%input{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at'}, disabled: true }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
= content_for :page_actions do
|
||||
%li
|
||||
%a.button.icon-plus#new-schedule{ href: "#", "schedule-dialog" => true }
|
||||
%a.button.icon-plus#new-schedule{ "schedule-dialog" => true }
|
||||
= t('admin.order_cycles.index.new_schedule')
|
||||
%li#new_order_cycle_link
|
||||
= button_link_to t(:new_order_cycle), main_app.new_admin_order_cycle_path, icon: 'icon-plus', id: 'admin_new_order_cycle_link'
|
||||
|
||||
@@ -677,8 +677,11 @@ en:
|
||||
index:
|
||||
involving: Involving
|
||||
schedules: Schedules
|
||||
add_a_new_schedule: Add a new schedule
|
||||
adding_a_new_schedule: Adding A New Schedule
|
||||
updating_a_schedule: Updating A Schedule
|
||||
new_schedule: New Schedule
|
||||
create_schedule: Create Schedule
|
||||
update_schedule: Update Schedule
|
||||
schedule_name_placeholder: Schedule Name
|
||||
name_required_error: Please enter a name for this schedule
|
||||
name_and_timing_form:
|
||||
|
||||
@@ -175,7 +175,7 @@ Openfoodnetwork::Application.routes.draw do
|
||||
get :status, on: :collection
|
||||
end
|
||||
|
||||
resources :schedules, only: [:index, :create], format: :json
|
||||
resources :schedules, only: [:index, :create, :update], format: :json
|
||||
end
|
||||
|
||||
namespace :api do
|
||||
|
||||
@@ -128,14 +128,11 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
def editable_schedules
|
||||
Schedule.joins(:order_cycles).where(coordinator_id: managed_enterprises.pluck(:id)).select("DISTINCT schedules.*")
|
||||
Schedule.joins(:order_cycles).where(order_cycles: { id: OrderCycle.managed_by(@user).pluck(:id) }).select("DISTINCT schedules.*")
|
||||
end
|
||||
|
||||
def visible_schedules
|
||||
managed_enterprise_ids = managed_enterprises.pluck(:id)
|
||||
Schedule.joins(order_cycles: :exchanges)
|
||||
.where('exchanges.sender_id IN (?) OR exchanges.receiver_id IN (?)', managed_enterprise_ids, managed_enterprise_ids)
|
||||
.select("DISTINCT schedules.*")
|
||||
Schedule.joins(:order_cycles).where(order_cycles: { id: OrderCycle.accessible_by(@user).pluck(:id) }).select("DISTINCT schedules.*")
|
||||
end
|
||||
|
||||
|
||||
|
||||
167
spec/controllers/admin/schedules_controller_spec.rb
Normal file
167
spec/controllers/admin/schedules_controller_spec.rb
Normal file
@@ -0,0 +1,167 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Admin::SchedulesController, type: :controller do
|
||||
include AuthenticationWorkflow
|
||||
|
||||
describe "index" do
|
||||
let!(:coordinated_order_cycle) { create(:simple_order_cycle) }
|
||||
let!(:managed_coordinator) { coordinated_order_cycle.coordinator }
|
||||
let!(:other_order_cycle) { create(:simple_order_cycle, coordinator: create(:enterprise)) }
|
||||
let!(:coordinated_schedule) { create(:schedule, order_cycles: [coordinated_order_cycle] ) }
|
||||
let!(:uncoordinated_schedule) { create(:schedule, order_cycles: [other_order_cycle] ) }
|
||||
|
||||
context "html" do
|
||||
context "where I manage an order cycle coordinator" do
|
||||
before do
|
||||
controller.stub spree_current_user: managed_coordinator.owner
|
||||
end
|
||||
|
||||
it "returns an empty @collection" do
|
||||
spree_get :index, format: :html
|
||||
expect(assigns(:collection)).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "json" do
|
||||
context "where I manage an order cycle coordinator" do
|
||||
before do
|
||||
controller.stub spree_current_user: managed_coordinator.owner
|
||||
end
|
||||
|
||||
let(:params) { { format: :json } }
|
||||
|
||||
it "scopes @collection to schedules containing order_cycles coordinated by enterprises I manage" do
|
||||
spree_get :index, params
|
||||
expect(assigns(:collection)).to eq [coordinated_schedule]
|
||||
end
|
||||
|
||||
it "serializes the data" do
|
||||
expect(ActiveModel::ArraySerializer).to receive(:new)
|
||||
spree_get :index, params
|
||||
end
|
||||
end
|
||||
|
||||
context "where I manage an order cycle coordinator" do
|
||||
it "returns an empty collection" do
|
||||
spree_get :index, format: :json
|
||||
expect(assigns(:collection)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "update" do
|
||||
let(:user) { create(:user, enterprise_limit: 10) }
|
||||
let!(:managed_coordinator) { create(:enterprise, owner: user) }
|
||||
let!(:managed_enterprise) { create(:enterprise, owner: user) }
|
||||
let!(:coordinated_order_cycle) { create(:simple_order_cycle, coordinator: managed_coordinator ) }
|
||||
let!(:coordinated_order_cycle2) { create(:simple_order_cycle, coordinator: managed_enterprise ) }
|
||||
let!(:uncoordinated_order_cycle) { create(:simple_order_cycle, coordinator: create(:enterprise) ) }
|
||||
let!(:uncoordinated_order_cycle2) { create(:simple_order_cycle, coordinator: create(:enterprise)) }
|
||||
let!(:coordinated_schedule) { create(:schedule, order_cycles: [coordinated_order_cycle, uncoordinated_order_cycle] ) }
|
||||
let!(:uncoordinated_schedule) { create(:schedule, order_cycles: [uncoordinated_order_cycle] ) }
|
||||
|
||||
context "json" do
|
||||
context "where I manage at least one of the schedule's coordinators" do
|
||||
render_views
|
||||
|
||||
before do
|
||||
controller.stub spree_current_user: user
|
||||
end
|
||||
|
||||
it "allows me to update basic information" do
|
||||
spree_put :update, format: :json, id: coordinated_schedule.id, schedule: { name: "my awesome schedule" }
|
||||
expect(JSON.parse(response.body)["id"]).to eq coordinated_schedule.id
|
||||
expect(JSON.parse(response.body)["name"]).to eq "my awesome schedule"
|
||||
expect(assigns(:schedule)).to eq coordinated_schedule
|
||||
expect(coordinated_schedule.reload.name).to eq 'my awesome schedule'
|
||||
end
|
||||
|
||||
it "allows me to add/remove only order cycles I coordinate to/from the schedule" do
|
||||
order_cycle_ids = [coordinated_order_cycle2.id, uncoordinated_order_cycle2.id ]
|
||||
spree_put :update, format: :json, id: coordinated_schedule.id, schedule: { order_cycle_ids: order_cycle_ids }
|
||||
expect(assigns(:schedule)).to eq coordinated_schedule
|
||||
# coordinated_order_cycle2 is added, uncoordinated_order_cycle is NOT removed
|
||||
expect(coordinated_schedule.reload.order_cycles).to include coordinated_order_cycle2, uncoordinated_order_cycle
|
||||
# coordinated_order_cycle is removed, uncoordinated_order_cycle2 is NOT added
|
||||
expect(coordinated_schedule.reload.order_cycles).to_not include coordinated_order_cycle, uncoordinated_order_cycle2
|
||||
end
|
||||
end
|
||||
|
||||
context "where I don't manage any of the schedule's coordinators" do
|
||||
before do
|
||||
controller.stub spree_current_user: uncoordinated_order_cycle2.coordinator.owner
|
||||
end
|
||||
|
||||
it "prevents me from updating the schedule" do
|
||||
spree_put :update, format: :json, id: coordinated_schedule.id, schedule: { name: "my awesome schedule" }
|
||||
expect(response).to redirect_to spree.unauthorized_path
|
||||
expect(assigns(:schedule)).to eq nil
|
||||
expect(coordinated_schedule.name).to_not eq "my awesome schedule"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "create" do
|
||||
let(:user) { create(:user) }
|
||||
let!(:managed_coordinator) { create(:enterprise, owner: user) }
|
||||
let!(:coordinated_order_cycle) { create(:simple_order_cycle, coordinator: managed_coordinator ) }
|
||||
let!(:uncoordinated_order_cycle) { create(:simple_order_cycle, coordinator: create(:enterprise)) }
|
||||
|
||||
def create_schedule(params)
|
||||
spree_put :create, params
|
||||
end
|
||||
|
||||
context "json" do
|
||||
let(:params) { { format: :json, schedule: { name: 'new schedule' } } }
|
||||
|
||||
context 'as an enterprise user' do
|
||||
before { allow(controller).to receive(:spree_current_user) { user } }
|
||||
|
||||
context "where no order cycles ids are provided" do
|
||||
it "does not allow me to create the schedule" do
|
||||
expect { create_schedule params }.to_not change(Schedule, :count)
|
||||
end
|
||||
end
|
||||
|
||||
context "where I manage at least one of the order cycles to be added to the schedules" do
|
||||
before do
|
||||
params[:schedule].merge!( order_cycle_ids: [coordinated_order_cycle.id, uncoordinated_order_cycle.id] )
|
||||
end
|
||||
|
||||
it "allows me to create the schedule, adding only order cycles that I manage" do
|
||||
expect { create_schedule params }.to change(Schedule, :count).by(1)
|
||||
schedule = Schedule.last
|
||||
expect(schedule.order_cycles).to include coordinated_order_cycle
|
||||
expect(schedule.order_cycles).to_not include uncoordinated_order_cycle
|
||||
end
|
||||
end
|
||||
|
||||
context "where I don't manage any of the order cycles to be added to the schedules" do
|
||||
before do
|
||||
params[:schedule].merge!( order_cycle_ids: [uncoordinated_order_cycle.id] )
|
||||
end
|
||||
|
||||
it "prevents me from creating the schedule" do
|
||||
expect { create_schedule params }.to_not change(Schedule, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as an admin user' do
|
||||
before do
|
||||
allow(controller).to receive(:spree_current_user) { create(:admin_user) }
|
||||
params[:schedule].merge!( order_cycle_ids: [coordinated_order_cycle.id, uncoordinated_order_cycle.id] )
|
||||
end
|
||||
|
||||
it "allows me to create a schedule" do
|
||||
expect { create_schedule params }.to change(Schedule, :count).by(1)
|
||||
schedule = Schedule.last
|
||||
expect(schedule.order_cycles).to include coordinated_order_cycle, uncoordinated_order_cycle
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -133,6 +133,10 @@ FactoryGirl.define do
|
||||
receiver { incoming ? order_cycle.coordinator : FactoryGirl.create(:enterprise) }
|
||||
end
|
||||
|
||||
factory :schedule, class: Schedule do
|
||||
sequence(:name) { |n| "Schedule #{n}" }
|
||||
end
|
||||
|
||||
factory :variant_override, :class => VariantOverride do
|
||||
price 77.77
|
||||
count_on_hand 11111
|
||||
|
||||
90
spec/features/admin/schedules_spec.rb
Normal file
90
spec/features/admin/schedules_spec.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature 'Schedules', js: true do
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
context "as an enterprise user" do
|
||||
let(:user) { create(:user) }
|
||||
let(:managed_enterprise) { create(:distributor_enterprise, owner: user) }
|
||||
let(:unmanaged_enterprise) { create(:distributor_enterprise) }
|
||||
let!(:weekly_schedule) { create(:schedule, name: 'Weekly') }
|
||||
let!(:fortnightly_schedule) { create(:schedule, name: 'Fortnightly') }
|
||||
let!(:oc1) { create(:order_cycle, coordinator: managed_enterprise, name: 'oc1', schedules: [weekly_schedule]) }
|
||||
let!(:oc2) { create(:order_cycle, coordinator: managed_enterprise, name: 'oc2', schedules: [weekly_schedule]) }
|
||||
let!(:oc3) { create(:order_cycle, coordinator: managed_enterprise, name: 'oc3', schedules: [weekly_schedule]) }
|
||||
let!(:oc4) { create(:order_cycle, coordinator: unmanaged_enterprise, name: 'oc4', schedules: [weekly_schedule]) }
|
||||
|
||||
before { login_to_admin_as user }
|
||||
|
||||
describe "Adding a new Schedule" do
|
||||
it "immediately shows the schedule in the order cycle list once created" do
|
||||
click_link 'Order Cycles'
|
||||
expect(page).to have_selector ".order-cycle-#{oc1.id}"
|
||||
find('a', text: 'NEW SCHEDULE').click
|
||||
|
||||
within "#schedule-dialog" do
|
||||
expect(page).to have_selector '#available-order-cycles .order-cycle', text: oc1.name
|
||||
expect(page).to have_selector '#available-order-cycles .order-cycle', text: oc2.name
|
||||
expect(page).to have_selector '#available-order-cycles .order-cycle', text: oc3.name
|
||||
expect(page).to have_no_selector '#available-order-cycles .order-cycle', text: oc4.name
|
||||
fill_in 'name', with: "Fortnightly"
|
||||
find("#available-order-cycles .order-cycle", text: oc1.name).drag_to find("#selected-order-cycles")
|
||||
find("#available-order-cycles .order-cycle", text: oc3.name).drag_to find("#selected-order-cycles")
|
||||
click_button "Create Schedule"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc1.id} td.schedules" do
|
||||
expect(page).to have_selector "a", text: "Weekly"
|
||||
expect(page).to have_selector "a", text: "Fortnightly"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc2.id} td.schedules" do
|
||||
expect(page).to have_selector "a", text: "Weekly"
|
||||
expect(page).to have_no_selector "a", text: "Fortnightly"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc3.id} td.schedules" do
|
||||
expect(page).to have_selector "a", text: "Weekly"
|
||||
expect(page).to have_selector "a", text: "Fortnightly"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "updating existing schedules" do
|
||||
use_short_wait
|
||||
before do
|
||||
oc1.update_attributes(schedule_ids: [weekly_schedule.id, fortnightly_schedule.id])
|
||||
oc3.update_attributes(schedule_ids: [weekly_schedule.id, fortnightly_schedule.id])
|
||||
end
|
||||
|
||||
it "immediately shows updated schedule lists for order cycles" do
|
||||
click_link 'Order Cycles'
|
||||
|
||||
within ".order-cycle-#{oc1.id} td.schedules" do
|
||||
find('a', text: "Weekly").click
|
||||
end
|
||||
|
||||
within "#schedule-dialog" do
|
||||
find("#selected-order-cycles .order-cycle", text: oc3.name).drag_to find("#available-order-cycles")
|
||||
click_button "Update Schedule"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc1.id} td.schedules" do
|
||||
expect(page).to have_selector "a", text: "Weekly"
|
||||
expect(page).to have_selector "a", text: "Fortnightly"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc2.id} td.schedules" do
|
||||
expect(page).to have_selector "a", text: "Weekly"
|
||||
expect(page).to have_no_selector "a", text: "Fortnightly"
|
||||
end
|
||||
|
||||
within ".order-cycle-#{oc3.id} td.schedules" do
|
||||
expect(page).to have_no_selector "a", text: "Weekly"
|
||||
expect(page).to have_selector "a", text: "Fortnightly"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
describe "OrderCyclesCtrl", ->
|
||||
ctrl = scope = httpBackend = Enterprises = OrderCycles = null
|
||||
coordinator = producer = shop = orderCycle = null
|
||||
ctrl = scope = httpBackend = Enterprises = OrderCycles = Schedules = null
|
||||
coordinator = producer = shop = orderCycle = schedule = null
|
||||
|
||||
beforeEach ->
|
||||
module "admin.orderCycles"
|
||||
@@ -13,48 +13,62 @@ describe "OrderCyclesCtrl", ->
|
||||
compare: (actual, expected) ->
|
||||
{ pass: angular.equals(actual, expected) }
|
||||
|
||||
beforeEach inject(($controller, $rootScope, $httpBackend, _OrderCycles_, _Enterprises_) ->
|
||||
beforeEach inject(($controller, $rootScope, $httpBackend, _OrderCycles_, _Enterprises_, _Schedules_) ->
|
||||
scope = $rootScope.$new()
|
||||
ctrl = $controller
|
||||
httpBackend = $httpBackend
|
||||
Enterprises = _Enterprises_
|
||||
OrderCycles = _OrderCycles_
|
||||
Schedules = _Schedules_
|
||||
spyOn(window, "daysFromToday").and.returnValue "SomeDate"
|
||||
|
||||
coordinator = { id: 3, name: "Coordinator" }
|
||||
producer = { id: 1, name: "Producer" }
|
||||
shop = { id: 5, name: "Shop" }
|
||||
orderCycle = { id: 4, name: "OC1", coordinator: {id: 3}, shops: [{id: 3},{id: 5}], producers: [{id: 1}] }
|
||||
schedule = { id: 7, name: 'Weekly', order_cycles: [{id: 4}]}
|
||||
orderCycle = { id: 4, schedules: [{id: 7}], name: "OC1", coordinator: {id: 3}, shops: [{id: 3},{id: 5}], producers: [{id: 1}] }
|
||||
|
||||
httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic").respond [coordinator, producer, shop]
|
||||
httpBackend.expectGET("/admin/schedules.json").respond [schedule]
|
||||
httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=index&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle]
|
||||
|
||||
ctrl "OrderCyclesCtrl", {$scope: scope, Enterprises: Enterprises, OrderCycles: OrderCycles}
|
||||
ctrl "OrderCyclesCtrl", {$scope: scope, Enterprises: Enterprises, OrderCycles: OrderCycles, Schedules: Schedules}
|
||||
)
|
||||
|
||||
describe "before data is returned", ->
|
||||
it "the RequestMonitor will have a state of loading", ->
|
||||
expect(scope.RequestMonitor.loading).toBe true
|
||||
|
||||
it "has not received/stored any data yet", ->
|
||||
expect(Enterprises.byID["5"]).toBeUndefined()
|
||||
expect(OrderCycles.orderCyclesByID["4"]).toBeUndefined()
|
||||
expect(Schedules.byID["7"]).toBeUndefined()
|
||||
|
||||
describe "after data is returned", ->
|
||||
beforeEach ->
|
||||
httpBackend.flush()
|
||||
|
||||
describe "initialisation", ->
|
||||
it "gets suppliers, adds a blank option as the first in the list", ->
|
||||
expect(scope.enterprises).toDeepEqual [ { id : '0', name : 'All' }, coordinator, producer, shop ]
|
||||
it "gets enterprises", ->
|
||||
expect(scope.enterprises).toDeepEqual [ coordinator, producer, shop ]
|
||||
|
||||
it "stores enterprises in an list that is accessible by id", ->
|
||||
expect(Enterprises.enterprisesByID["5"]).toDeepEqual shop
|
||||
it "stores enterprises, order cycle and schedules in a list that is accessible by id", ->
|
||||
expect(Enterprises.byID["5"]).toBeDefined()
|
||||
expect(OrderCycles.orderCyclesByID["4"]).toBeDefined()
|
||||
expect(Schedules.byID["7"]).toBeDefined()
|
||||
|
||||
it "gets order cycles, with dereferenced coordinator, shops and producers", ->
|
||||
it "gets order cycles, with dereferenced coordinator, shops and producers, schedules", ->
|
||||
oc = OrderCycles.orderCyclesByID["4"]
|
||||
s = Schedules.byID["7"]
|
||||
expect(scope.orderCycles).toDeepEqual [oc]
|
||||
expect(oc.coordinator).toDeepEqual coordinator
|
||||
expect(oc.shops).toDeepEqual [coordinator,shop]
|
||||
expect(oc.producers).toDeepEqual [producer]
|
||||
expect(oc.schedules).toEqual [s]
|
||||
expect(s.order_cycles).toEqual [oc]
|
||||
expect(oc.shopNames).toEqual "Coordinator, Shop"
|
||||
expect(oc.producerNames).toEqual "Producer"
|
||||
|
||||
|
||||
it "the RequestMonitor will not longer have a state of loading", ->
|
||||
expect(scope.RequestMonitor.loading).toBe false
|
||||
|
||||
Reference in New Issue
Block a user