Merge branch 'simple-order-cycles'

Conflicts:
	app/views/admin/order_cycles/_row.html.haml
This commit is contained in:
Rohan Mitchell
2014-10-23 12:11:34 +11:00
19 changed files with 656 additions and 271 deletions

View File

@@ -1,4 +1,4 @@
angular.module('order_cycle', ['ngResource'])
angular.module('admin.order_cycles', ['ngResource'])
.controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, OrderCycle, Enterprise, EnterpriseFee) ->
$scope.enterprises = Enterprise.index()
$scope.supplied_products = Enterprise.supplied_products
@@ -162,235 +162,6 @@ angular.module('order_cycle', ['ngResource'])
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
])
.factory('OrderCycle', ['$resource', '$window', ($resource, $window) ->
OrderCycle = $resource '/admin/order_cycles/:order_cycle_id.json', {}, {
'index': { method: 'GET', isArray: true}
'create': { method: 'POST'}
'update': { method: 'PUT'}}
{
order_cycle:
incoming_exchanges: []
outgoing_exchanges: []
coordinator_fees: []
loaded: false
exchangeSelectedVariants: (exchange) ->
numActiveVariants = 0
numActiveVariants++ for id, active of exchange.variants when active
numActiveVariants
exchangeDirection: (exchange) ->
if this.order_cycle.incoming_exchanges.indexOf(exchange) == -1 then 'outgoing' else 'incoming'
toggleProducts: (exchange) ->
exchange.showProducts = !exchange.showProducts
setExchangeVariants: (exchange, variants, selected) ->
exchange.variants[variant] = selected for variant in variants
addSupplier: (new_supplier_id) ->
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
addDistributor: (new_distributor_id) ->
this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []})
removeExchange: (exchange) ->
if exchange.incoming
incoming_index = this.order_cycle.incoming_exchanges.indexOf exchange
this.order_cycle.incoming_exchanges.splice(incoming_index, 1)
this.removeDistributionOfVariant(variant_id) for variant_id, active of exchange.variants when active
else
outgoing_index = this.order_cycle.outgoing_exchanges.indexOf exchange
this.order_cycle.outgoing_exchanges.splice(outgoing_index, 1) if outgoing_index > -1
addCoordinatorFee: ->
this.order_cycle.coordinator_fees.push({})
removeCoordinatorFee: (index) ->
this.order_cycle.coordinator_fees.splice(index, 1)
addExchangeFee: (exchange) ->
exchange.enterprise_fees.push({})
removeExchangeFee: (exchange, index) ->
exchange.enterprise_fees.splice(index, 1)
productSuppliedToOrderCycle: (product) ->
product_variant_ids = (variant.id for variant in product.variants)
variant_ids = [product.master_id].concat(product_variant_ids)
incomingExchangesVariants = this.incomingExchangesVariants()
# TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
# Use a better algorithm if needed.
# Also, incomingExchangesVariants is called every time, when it only needs to be
# called once per change to incoming variants. Some sort of caching?
ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
ids.length > 0
variantSuppliedToOrderCycle: (variant) ->
this.incomingExchangesVariants().indexOf(variant.id) != -1
incomingExchangesVariants: ->
variant_ids = []
for exchange in this.order_cycle.incoming_exchanges
variant_ids.push(parseInt(id)) for id, active of exchange.variants when active
variant_ids
participatingEnterpriseIds: ->
suppliers = (exchange.enterprise_id for exchange in this.order_cycle.incoming_exchanges)
distributors = (exchange.enterprise_id for exchange in this.order_cycle.outgoing_exchanges)
jQuery.unique(suppliers.concat(distributors)).sort()
removeDistributionOfVariant: (variant_id) ->
for exchange in this.order_cycle.outgoing_exchanges
exchange.variants[variant_id] = false
load: (order_cycle_id) ->
service = this
OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) ->
angular.extend(service.order_cycle, oc)
service.order_cycle.incoming_exchanges = []
service.order_cycle.outgoing_exchanges = []
for exchange in service.order_cycle.exchanges
if exchange.incoming
angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true})
delete(exchange.receiver_id)
service.order_cycle.incoming_exchanges.push(exchange)
else
angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true})
delete(exchange.sender_id)
service.order_cycle.outgoing_exchanges.push(exchange)
delete(service.order_cycle.exchanges)
service.loaded = true
this.order_cycle
create: ->
oc = new OrderCycle({order_cycle: this.dataForSubmit()})
oc.$create (data) ->
if data['success']
$window.location = '/admin/order_cycles'
else
console.log('Failed to create order cycle')
update: ->
oc = new OrderCycle({order_cycle: this.dataForSubmit()})
oc.$update {order_cycle_id: this.order_cycle.id}, (data) ->
if data['success']
$window.location = '/admin/order_cycles'
else
console.log('Failed to update order cycle')
dataForSubmit: ->
data = this.deepCopy()
data = this.removeInactiveExchanges(data)
data = this.translateCoordinatorFees(data)
data = this.translateExchangeFees(data)
data
deepCopy: ->
data = angular.extend({}, this.order_cycle)
# Copy exchanges
data.incoming_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.incoming_exchanges) if this.order_cycle.incoming_exchanges?
data.outgoing_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.outgoing_exchanges) if this.order_cycle.outgoing_exchanges?
# Copy exchange fees
all_exchanges = (data.incoming_exchanges || []) + (data.outgoing_exchanges || [])
for exchange in all_exchanges
if exchange.enterprise_fees?
exchange.enterprise_fees = (angular.extend {}, fee for fee in exchange.enterprise_fees)
data
removeInactiveExchanges: (order_cycle) ->
order_cycle.incoming_exchanges =
(exchange for exchange in order_cycle.incoming_exchanges when exchange.active)
order_cycle.outgoing_exchanges =
(exchange for exchange in order_cycle.outgoing_exchanges when exchange.active)
order_cycle
translateCoordinatorFees: (order_cycle) ->
order_cycle.coordinator_fee_ids = (fee.id for fee in order_cycle.coordinator_fees)
delete order_cycle.coordinator_fees
order_cycle
translateExchangeFees: (order_cycle) ->
for exchange in order_cycle.incoming_exchanges
exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees)
delete exchange.enterprise_fees
for exchange in order_cycle.outgoing_exchanges
exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees)
delete exchange.enterprise_fees
order_cycle
}])
.factory('Enterprise', ['$resource', ($resource) ->
Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}})
{
Enterprise: Enterprise
enterprises: {}
supplied_products: []
loaded: false
index: ->
service = this
Enterprise.index (data) ->
for enterprise in data
service.enterprises[enterprise.id] = enterprise
for product in enterprise.supplied_products
service.supplied_products.push(product)
service.loaded = true
this.enterprises
suppliedVariants: (enterprise_id) ->
vs = (this.variantsOf(product) for product in this.enterprises[enterprise_id].supplied_products)
[].concat vs...
variantsOf: (product) ->
if product.variants.length > 0
variant.id for variant in product.variants
else
[product.master_id]
totalVariants: (enterprise) ->
numVariants = 0
if enterprise
counts = for product in enterprise.supplied_products
numVariants += if product.variants.length == 0 then 1 else product.variants.length
numVariants
}])
.factory('EnterpriseFee', ['$resource', ($resource) ->
EnterpriseFee = $resource('/admin/enterprise_fees/:enterprise_fee_id.json', {}, {'index': {method: 'GET', isArray: true}})
{
EnterpriseFee: EnterpriseFee
enterprise_fees: {}
loaded: false
index: ->
service = this
EnterpriseFee.index (data) ->
service.enterprise_fees = data
service.loaded = true
forEnterprise: (enterprise_id) ->
enterprise_fee for enterprise_fee in this.enterprise_fees when enterprise_fee.enterprise_id == enterprise_id
}])
.directive('datetimepicker', ['$parse', ($parse) ->
(scope, element, attrs) ->
# using $parse instead of scope[attrs.datetimepicker] for cases

View File

@@ -0,0 +1,43 @@
angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee) ->
$scope.enterprises = Enterprise.index (enterprises) =>
$scope.init(enterprises)
$scope.enterprise_fees = EnterpriseFee.index()
$scope.order_cycle = OrderCycle.order_cycle
$scope.init = (enterprises) ->
enterprise = enterprises[Object.keys(enterprises)[0]]
OrderCycle.addSupplier enterprise.id
OrderCycle.addDistributor enterprise.id
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
# All variants start as checked
OrderCycle.setExchangeVariants(OrderCycle.order_cycle.incoming_exchanges[0],
Enterprise.suppliedVariants(enterprise.id), true)
OrderCycle.order_cycle.coordinator_id = enterprise.id
$scope.loaded = ->
Enterprise.loaded && EnterpriseFee.loaded
$scope.removeDistributionOfVariant = angular.noop
$scope.setExchangeVariants = (exchange, variants, selected) ->
OrderCycle.setExchangeVariants(exchange, variants, selected)
$scope.suppliedVariants = (enterprise_id) ->
Enterprise.suppliedVariants(enterprise_id)
$scope.addCoordinatorFee = ($event) ->
$event.preventDefault()
OrderCycle.addCoordinatorFee()
$scope.removeCoordinatorFee = ($event, index) ->
$event.preventDefault()
OrderCycle.removeCoordinatorFee(index)
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
$scope.submit = ->
OrderCycle.mirrorIncomingToOutgoingProducts()
OrderCycle.create()

View File

@@ -0,0 +1,31 @@
angular.module('admin.order_cycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, OrderCycle, Enterprise, EnterpriseFee) ->
$scope.orderCycleId = ->
$location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
$scope.enterprises = Enterprise.index()
$scope.enterprise_fees = EnterpriseFee.index()
$scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) =>
$scope.init()
$scope.loaded = ->
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded
$scope.init = ->
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
$scope.removeDistributionOfVariant = angular.noop
$scope.addCoordinatorFee = ($event) ->
$event.preventDefault()
OrderCycle.addCoordinatorFee()
$scope.removeCoordinatorFee = ($event, index) ->
$event.preventDefault()
OrderCycle.removeCoordinatorFee(index)
$scope.submit = ->
OrderCycle.mirrorIncomingToOutgoingProducts()
OrderCycle.update()

View File

@@ -0,0 +1,43 @@
angular.module('admin.order_cycles').factory('Enterprise', ($resource) ->
Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}})
{
Enterprise: Enterprise
enterprises: {}
supplied_products: []
loaded: false
index: (callback=null) ->
service = this
Enterprise.index (data) ->
for enterprise in data
service.enterprises[enterprise.id] = enterprise
for product in enterprise.supplied_products
service.supplied_products.push(product)
service.loaded = true
(callback || angular.noop)(service.enterprises)
this.enterprises
suppliedVariants: (enterprise_id) ->
vs = (this.variantsOf(product) for product in this.enterprises[enterprise_id].supplied_products)
[].concat vs...
variantsOf: (product) ->
if product.variants.length > 0
variant.id for variant in product.variants
else
[product.master_id]
totalVariants: (enterprise) ->
numVariants = 0
if enterprise
counts = for product in enterprise.supplied_products
numVariants += if product.variants.length == 0 then 1 else product.variants.length
numVariants
})

View File

@@ -0,0 +1,18 @@
angular.module('admin.order_cycles').factory('EnterpriseFee', ($resource) ->
EnterpriseFee = $resource('/admin/enterprise_fees/:enterprise_fee_id.json', {}, {'index': {method: 'GET', isArray: true}})
{
EnterpriseFee: EnterpriseFee
enterprise_fees: {}
loaded: false
index: ->
service = this
EnterpriseFee.index (data) ->
service.enterprise_fees = data
service.loaded = true
forEnterprise: (enterprise_id) ->
enterprise_fee for enterprise_fee in this.enterprise_fees when enterprise_fee.enterprise_id == enterprise_id
})

View File

@@ -0,0 +1,179 @@
angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) ->
OrderCycle = $resource '/admin/order_cycles/:order_cycle_id.json', {}, {
'index': { method: 'GET', isArray: true}
'create': { method: 'POST'}
'update': { method: 'PUT'}}
{
order_cycle:
incoming_exchanges: []
outgoing_exchanges: []
coordinator_fees: []
loaded: false
exchangeSelectedVariants: (exchange) ->
numActiveVariants = 0
numActiveVariants++ for id, active of exchange.variants when active
numActiveVariants
exchangeDirection: (exchange) ->
if this.order_cycle.incoming_exchanges.indexOf(exchange) == -1 then 'outgoing' else 'incoming'
toggleProducts: (exchange) ->
exchange.showProducts = !exchange.showProducts
setExchangeVariants: (exchange, variants, selected) ->
exchange.variants[variant] = selected for variant in variants
addSupplier: (new_supplier_id) ->
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
addDistributor: (new_distributor_id) ->
this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []})
removeExchange: (exchange) ->
if exchange.incoming
incoming_index = this.order_cycle.incoming_exchanges.indexOf exchange
this.order_cycle.incoming_exchanges.splice(incoming_index, 1)
this.removeDistributionOfVariant(variant_id) for variant_id, active of exchange.variants when active
else
outgoing_index = this.order_cycle.outgoing_exchanges.indexOf exchange
this.order_cycle.outgoing_exchanges.splice(outgoing_index, 1) if outgoing_index > -1
addCoordinatorFee: ->
this.order_cycle.coordinator_fees.push({})
removeCoordinatorFee: (index) ->
this.order_cycle.coordinator_fees.splice(index, 1)
addExchangeFee: (exchange) ->
exchange.enterprise_fees.push({})
removeExchangeFee: (exchange, index) ->
exchange.enterprise_fees.splice(index, 1)
productSuppliedToOrderCycle: (product) ->
product_variant_ids = (variant.id for variant in product.variants)
variant_ids = [product.master_id].concat(product_variant_ids)
incomingExchangesVariants = this.incomingExchangesVariants()
# TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
# Use a better algorithm if needed.
# Also, incomingExchangesVariants is called every time, when it only needs to be
# called once per change to incoming variants. Some sort of caching?
ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
ids.length > 0
variantSuppliedToOrderCycle: (variant) ->
this.incomingExchangesVariants().indexOf(variant.id) != -1
incomingExchangesVariants: ->
variant_ids = []
for exchange in this.order_cycle.incoming_exchanges
variant_ids.push(parseInt(id)) for id, active of exchange.variants when active
variant_ids
participatingEnterpriseIds: ->
suppliers = (exchange.enterprise_id for exchange in this.order_cycle.incoming_exchanges)
distributors = (exchange.enterprise_id for exchange in this.order_cycle.outgoing_exchanges)
jQuery.unique(suppliers.concat(distributors)).sort()
removeDistributionOfVariant: (variant_id) ->
for exchange in this.order_cycle.outgoing_exchanges
exchange.variants[variant_id] = false
load: (order_cycle_id, callback=null) ->
service = this
OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) ->
angular.extend(service.order_cycle, oc)
service.order_cycle.incoming_exchanges = []
service.order_cycle.outgoing_exchanges = []
for exchange in service.order_cycle.exchanges
if exchange.incoming
angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true})
delete(exchange.receiver_id)
service.order_cycle.incoming_exchanges.push(exchange)
else
angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true})
delete(exchange.sender_id)
service.order_cycle.outgoing_exchanges.push(exchange)
delete(service.order_cycle.exchanges)
service.loaded = true
(callback || angular.noop)(service.order_cycle)
this.order_cycle
create: ->
oc = new OrderCycle({order_cycle: this.dataForSubmit()})
oc.$create (data) ->
if data['success']
$window.location = '/admin/order_cycles'
else
console.log('Failed to create order cycle')
update: ->
oc = new OrderCycle({order_cycle: this.dataForSubmit()})
oc.$update {order_cycle_id: this.order_cycle.id}, (data) ->
if data['success']
$window.location = '/admin/order_cycles'
else
console.log('Failed to update order cycle')
dataForSubmit: ->
data = this.deepCopy()
data = this.removeInactiveExchanges(data)
data = this.translateCoordinatorFees(data)
data = this.translateExchangeFees(data)
data
deepCopy: ->
data = angular.extend({}, this.order_cycle)
# Copy exchanges
data.incoming_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.incoming_exchanges) if this.order_cycle.incoming_exchanges?
data.outgoing_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.outgoing_exchanges) if this.order_cycle.outgoing_exchanges?
# Copy exchange fees
all_exchanges = (data.incoming_exchanges || []) + (data.outgoing_exchanges || [])
for exchange in all_exchanges
if exchange.enterprise_fees?
exchange.enterprise_fees = (angular.extend {}, fee for fee in exchange.enterprise_fees)
data
removeInactiveExchanges: (order_cycle) ->
order_cycle.incoming_exchanges =
(exchange for exchange in order_cycle.incoming_exchanges when exchange.active)
order_cycle.outgoing_exchanges =
(exchange for exchange in order_cycle.outgoing_exchanges when exchange.active)
order_cycle
translateCoordinatorFees: (order_cycle) ->
order_cycle.coordinator_fee_ids = (fee.id for fee in order_cycle.coordinator_fees)
delete order_cycle.coordinator_fees
order_cycle
translateExchangeFees: (order_cycle) ->
for exchange in order_cycle.incoming_exchanges
exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees)
delete exchange.enterprise_fees
for exchange in order_cycle.outgoing_exchanges
exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees)
delete exchange.enterprise_fees
order_cycle
# In the simple UI, we don't list outgoing products. Instead, all products are considered
# part of both incoming and outgoing enterprises. This method mirrors the former to the
# latter **for order cycles with a single incoming and outgoing exchange only**.
mirrorIncomingToOutgoingProducts: ->
incoming = this.order_cycle.incoming_exchanges[0]
outgoing = this.order_cycle.outgoing_exchanges[0]
for id, active of incoming.variants
outgoing.variants[id] = active
})

View File

@@ -62,6 +62,10 @@ module OrderCyclesHelper
OrderCycle.active.with_distributor(@distributor).present?
end
def order_cycles_simple_view
@order_cycles_simple_view ||= !OpenFoodNetwork::Permissions.new(spree_current_user).can_manage_complex_order_cycles?
end
def order_cycles_enabled?
OpenFoodNetwork::FeatureToggle.enabled? :order_cycles
end

View File

@@ -1,14 +1,4 @@
= f.label :name
= f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true
%br/
.date-field
= f.label :orders_open_at, 'Orders open'
= f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at'
.date-field
= f.label :orders_close_at, 'Orders close'
= f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at'
%br/
= render 'name_and_timing_form', f: f
%h2 Incoming

View File

@@ -0,0 +1,15 @@
.row
.alpha.two.columns
= f.label :name
.fourteen.columns.omega
= f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true
.row
.alpha.two.columns
= f.label :orders_open_at, 'Orders open'
.six.columns
= f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at'
.two.columns
= f.label :orders_close_at, 'Orders close'
.six.columns.omega
= f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at'

View File

@@ -4,15 +4,17 @@
%td= link_to order_cycle.name, main_app.edit_admin_order_cycle_path(order_cycle)
%td= order_cycle_form.text_field :orders_open_at, :class => 'datetimepicker', :value => order_cycle.orders_open_at
%td= order_cycle_form.text_field :orders_close_at, :class => 'datetimepicker', :value => order_cycle.orders_close_at
%td.suppliers
- order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s|
= s.name
%br/
%td= order_cycle.coordinator.name
%td.distributors
- order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |d|
= d.name
%br/
- unless order_cycles_simple_view
%td.suppliers
- order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s|
= s.name
%br/
%td= order_cycle.coordinator.name
%td.distributors
- order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |d|
= d.name
%br/
%td.products
- variant_images = capture do

View File

@@ -0,0 +1,26 @@
= render 'name_and_timing_form', f: f
.row
.alpha.two.columns
= label_tag 'Pickup time'
.six.columns
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_time', 'placeholder' => 'Ready for (ie. Date / Time)', 'ng-model' => 'outgoing_exchange.pickup_time'
.two.columns
= label_tag 'Pickup instructions'
.six.columns.omega
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_instructions', 'placeholder' => 'Pick-up instructions', 'ng-model' => 'outgoing_exchange.pickup_instructions'
%table.exchanges
%tbody{ng: {repeat: "exchange in order_cycle.incoming_exchanges"}}
%tr.products
= render 'exchange_supplied_products_form'
= render 'coordinator_fees', f: f
.actions
= f.submit @order_cycle.new_record? ? 'Create' : 'Update', 'ng-disabled' => '!loaded()'
%span{'ng-show' => 'loaded()'}
or
= link_to 'Cancel', main_app.admin_order_cycles_path
%span{'ng-hide' => 'loaded()'} Loading...

View File

@@ -1,4 +1,9 @@
%h1 Edit Order Cycle
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'order_cycle', 'ng-controller' => 'AdminEditOrderCycleCtrl', 'ng-submit' => 'submit()'} do |f|
= render 'form', :f => f
- ng_controller = order_cycles_simple_view ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit()'} do |f|
- if order_cycles_simple_view
= render 'simple_form', f: f
- else
= render 'form', f: f

View File

@@ -11,9 +11,10 @@
%col
%col{'style' => 'width: 20%;'}
%col{'style' => 'width: 20%;'}
%col
%col
%col
- unless order_cycles_simple_view
%col
%col
%col
%col
%col
%col
@@ -23,9 +24,10 @@
%th Name
%th Open
%th Close
%th Suppliers
%th Coordinator
%th Distributors
- unless order_cycles_simple_view
%th Suppliers
%th Coordinator
%th Distributors
%th Products
%th.actions
%th.actions

View File

@@ -1,4 +1,9 @@
%h1 New Order Cycle
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'order_cycle', 'ng-controller' => 'AdminCreateOrderCycleCtrl', 'ng-submit' => 'submit()'} do |f|
= render 'form', :f => f
- ng_controller = order_cycles_simple_view ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl'
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit()'} do |f|
- if order_cycles_simple_view
= render 'simple_form', f: f
- else
= render 'form', f: f

View File

@@ -4,6 +4,12 @@ module OpenFoodNetwork
@user = user
end
def can_manage_complex_order_cycles?
managed_and_related_enterprises_with(:add_to_order_cycle).any? do |e|
e.sells == 'any'
end
end
# Find enterprises that an admin is allowed to add to an order cycle
def order_cycle_enterprises
managed_and_related_enterprises_with :add_to_order_cycle

View File

@@ -131,16 +131,16 @@ feature %q{
page.should have_selector 'td.distributors', text: 'My distributor'
# And it should have some fees
OrderCycle.last.exchanges.incoming.first.enterprise_fees.should == [supplier_fee]
OrderCycle.last.coordinator_fees.should == [coordinator_fee]
OrderCycle.last.exchanges.outgoing.first.enterprise_fees.should == [distributor_fee]
oc = OrderCycle.last
oc.exchanges.incoming.first.enterprise_fees.should == [supplier_fee]
oc.coordinator_fees.should == [coordinator_fee]
oc.exchanges.outgoing.first.enterprise_fees.should == [distributor_fee]
# And it should have some variants selected
OrderCycle.last.exchanges.first.variants.count.should == 2
OrderCycle.last.exchanges.last.variants.count.should == 2
oc.exchanges.first.variants.count.should == 2
oc.exchanges.last.variants.count.should == 2
# And my pickup time and instructions should have been saved
oc = OrderCycle.last
exchange = oc.exchanges.where(:sender_id => oc.coordinator_id).first
exchange.pickup_time.should == 'pickup time'
exchange.pickup_instructions.should == 'pickup instructions'
@@ -576,7 +576,165 @@ feature %q{
occ = OrderCycle.last
occ.name.should == "COPY OF #{oc.name}"
end
end
describe "simplified interface for enterprise users selling only their own produce" do
let(:user) { create_enterprise_user }
let(:enterprise) { create(:enterprise, is_primary_producer: true, sells: 'own') }
let!(:p1) { create(:simple_product, supplier: enterprise) }
let!(:p2) { create(:simple_product, supplier: enterprise) }
let!(:p3) { create(:simple_product, supplier: enterprise) }
let!(:v) { create(:variant, product: p3) }
let!(:fee) { create(:enterprise_fee, enterprise: enterprise, name: 'Coord fee') }
use_short_wait
before do
user.enterprise_roles.create! enterprise: enterprise
login_to_admin_as user
end
it "shows me an index of order cycles without enterprise columns" do
create(:order_cycle, coordinator: enterprise)
visit admin_order_cycles_path
page.should_not have_selector 'th', text: 'SUPPLIERS'
page.should_not have_selector 'th', text: 'COORDINATOR'
page.should_not have_selector 'th', text: 'DISTRIBUTORS'
end
it "creates order cycles", js: true do
# When I go to the new order cycle page
visit admin_order_cycles_path
click_link 'New Order Cycle'
# And I fill in the basic fields
fill_in 'order_cycle_name', with: 'Plums & Avos'
fill_in 'order_cycle_orders_open_at', with: '2014-10-17 06:00:00'
fill_in 'order_cycle_orders_close_at', with: '2014-10-24 17:00:00'
fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'pickup time'
fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions'
# Then my products / variants should already be selected
page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p1.master.id}"
page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p2.master.id}"
page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v.id}"
# When I unselect a product
uncheck "order_cycle_incoming_exchange_0_variants_#{p2.master.id}"
# And I add a fee and save
click_button 'Add coordinator fee'
click_button 'Add coordinator fee'
click_link 'order_cycle_coordinator_fee_1_remove'
page.should have_select 'order_cycle_coordinator_fee_0_id'
page.should_not have_select 'order_cycle_coordinator_fee_1_id'
select 'Coord fee', from: 'order_cycle_coordinator_fee_0_id'
click_button 'Create'
# Then my order cycle should have been created
page.should have_content 'Your order cycle has been created.'
page.should have_selector 'a', text: 'Plums & Avos'
page.should have_selector "input[value='2014-10-17 06:00:00 +1100']"
page.should have_selector "input[value='2014-10-24 17:00:00 +1100']"
# And it should have some variants selected
oc = OrderCycle.last
oc.exchanges.incoming.first.variants.count.should == 2
oc.exchanges.outgoing.first.variants.count.should == 2
# And it should have the fee
oc.coordinator_fees.should == [fee]
# And my pickup time and instructions should have been saved
ex = oc.exchanges.outgoing.first
ex.pickup_time.should == 'pickup time'
ex.pickup_instructions.should == 'pickup instructions'
end
scenario "editing an order cycle" do
# Given an order cycle with pickup time and instructions
fee = create(:enterprise_fee, name: 'my fee', enterprise: enterprise)
oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [p1.master], coordinator_fees: [fee])
ex = oc.exchanges.outgoing.first
ex.update_attributes! pickup_time: 'pickup time', pickup_instructions: 'pickup instructions'
# When I edit it
login_to_admin_section
click_link 'Order Cycles'
click_link oc.name
wait_until { page.find('#order_cycle_name').value.present? }
# Then I should see the basic settings
page.should have_field 'order_cycle_name', with: oc.name
page.should have_field 'order_cycle_orders_open_at', with: oc.orders_open_at.to_s
page.should have_field 'order_cycle_orders_close_at', with: oc.orders_close_at.to_s
page.should have_field 'order_cycle_outgoing_exchange_0_pickup_time', with: 'pickup time'
page.should have_field 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions'
# And I should see the products
page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p1.master.id}"
page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{p2.master.id}"
page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{v.id}"
# And I should see the coordinator fees
page.should have_select 'order_cycle_coordinator_fee_0_id', selected: 'my fee'
end
scenario "updating an order cycle" do
# Given an order cycle with pickup time and instructions
fee1 = create(:enterprise_fee, name: 'my fee', enterprise: enterprise)
fee2 = create(:enterprise_fee, name: 'that fee', enterprise: enterprise)
oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [p1.master], coordinator_fees: [fee1])
ex = oc.exchanges.outgoing.first
ex.update_attributes! pickup_time: 'pickup time', pickup_instructions: 'pickup instructions'
# When I edit it
login_to_admin_section
visit edit_admin_order_cycle_path oc
wait_until { page.find('#order_cycle_name').value.present? }
# And I fill in the basic fields
fill_in 'order_cycle_name', with: 'Plums & Avos'
fill_in 'order_cycle_orders_open_at', with: '2014-10-17 06:00:00'
fill_in 'order_cycle_orders_close_at', with: '2014-10-24 17:00:00'
fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'xy'
fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy'
# And I make some product selections
uncheck "order_cycle_incoming_exchange_0_variants_#{p1.master.id}"
check "order_cycle_incoming_exchange_0_variants_#{p2.master.id}"
check "order_cycle_incoming_exchange_0_variants_#{v.id}"
uncheck "order_cycle_incoming_exchange_0_variants_#{v.id}"
# And I select some fees and update
click_link 'order_cycle_coordinator_fee_0_remove'
page.should_not have_select 'order_cycle_coordinator_fee_0_id'
click_button 'Add coordinator fee'
select 'that fee', from: 'order_cycle_coordinator_fee_0_id'
click_button 'Update'
# Then my order cycle should have been updated
page.should have_content 'Your order cycle has been updated.'
page.should have_selector 'a', text: 'Plums & Avos'
page.should have_selector "input[value='2014-10-17 06:00:00 +1100']"
page.should have_selector "input[value='2014-10-24 17:00:00 +1100']"
# And it should have a variant selected
oc = OrderCycle.last
oc.exchanges.incoming.first.variants.should == [p2.master]
oc.exchanges.outgoing.first.variants.should == [p2.master]
# And it should have the fee
oc.coordinator_fees.should == [fee2]
# And my pickup time and instructions should have been saved
ex = oc.exchanges.outgoing.first
ex.pickup_time.should == 'xy'
ex.pickup_instructions.should == 'zzy'
end
end

View File

@@ -0,0 +1,49 @@
describe "AdminSimpleCreateOrderCycleCtrl", ->
ctrl = null
scope = {}
OrderCycle = {}
Enterprise = {}
EnterpriseFee = {}
incoming_exchange = {}
outgoing_exchange = {}
beforeEach ->
scope = {}
OrderCycle =
order_cycle:
incoming_exchanges: [incoming_exchange]
outgoing_exchanges: [outgoing_exchange]
addSupplier: jasmine.createSpy()
addDistributor: jasmine.createSpy()
setExchangeVariants: jasmine.createSpy()
Enterprise =
index: jasmine.createSpy()
suppliedVariants: jasmine.createSpy().andReturn('supplied variants')
EnterpriseFee =
index: jasmine.createSpy()
module('admin.order_cycles')
inject ($controller) ->
ctrl = $controller 'AdminSimpleCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee}
describe "initialisation", ->
enterprise = {id: 123}
enterprises = {123: enterprise}
beforeEach ->
scope.init(enterprises)
it "sets up an incoming and outgoing exchange", ->
expect(OrderCycle.addSupplier).toHaveBeenCalledWith(enterprise.id)
expect(OrderCycle.addDistributor).toHaveBeenCalledWith(enterprise.id)
expect(scope.outgoing_exchange).toEqual outgoing_exchange
it "selects all variants", ->
expect(Enterprise.suppliedVariants).
toHaveBeenCalledWith(enterprise.id)
expect(OrderCycle.setExchangeVariants).
toHaveBeenCalledWith(incoming_exchange, 'supplied variants', true)
it "sets the coordinator", ->
expect(OrderCycle.order_cycle.coordinator_id).toEqual enterprise.id

View File

@@ -0,0 +1,38 @@
describe "AdminSimpleEditOrderCycleCtrl", ->
ctrl = null
scope = {}
location = {}
OrderCycle = {}
Enterprise = {}
EnterpriseFee = {}
incoming_exchange = {}
outgoing_exchange = {}
beforeEach ->
scope = {}
location =
absUrl: ->
'example.com/admin/order_cycles/27/edit'
OrderCycle =
order_cycle:
incoming_exchanges: [incoming_exchange]
outgoing_exchanges: [outgoing_exchange]
load: jasmine.createSpy()
Enterprise =
index: jasmine.createSpy()
EnterpriseFee =
index: jasmine.createSpy()
module('admin.order_cycles')
inject ($controller) ->
ctrl = $controller 'AdminSimpleEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee}
describe "initialisation", ->
enterprise = {id: 123}
enterprises = {123: enterprise}
beforeEach ->
scope.init(enterprises)
it "sets the outgoing exchange", ->
expect(scope.outgoing_exchange).toEqual outgoing_exchange

View File

@@ -38,7 +38,7 @@ describe 'OrderCycle controllers', ->
index: jasmine.createSpy('index').andReturn('enterprise fees list')
forEnterprise: jasmine.createSpy('forEnterprise').andReturn('enterprise fees for enterprise')
module('order_cycle')
module('admin.order_cycles')
inject ($controller) ->
ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee}
@@ -198,7 +198,7 @@ describe 'OrderCycle controllers', ->
index: jasmine.createSpy('index').andReturn('enterprise fees list')
forEnterprise: jasmine.createSpy('forEnterprise').andReturn('enterprise fees for enterprise')
module('order_cycle')
module('admin.order_cycles')
inject ($controller) ->
ctrl = $controller 'AdminEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee}
@@ -323,7 +323,7 @@ describe 'OrderCycle services', ->
Enterprise = null
beforeEach ->
module 'order_cycle'
module 'admin.order_cycles'
inject ($injector, _$httpBackend_)->
Enterprise = $injector.get('Enterprise')
$httpBackend = _$httpBackend_
@@ -389,7 +389,7 @@ describe 'OrderCycle services', ->
EnterpriseFee = null
beforeEach ->
module 'order_cycle'
module 'admin.order_cycles'
inject ($injector, _$httpBackend_)->
EnterpriseFee = $injector.get('EnterpriseFee')
$httpBackend = _$httpBackend_
@@ -431,7 +431,7 @@ describe 'OrderCycle services', ->
beforeEach ->
$window = {navigator: {userAgent: 'foo'}}
module 'order_cycle', ($provide)->
module 'admin.order_cycles', ($provide)->
$provide.value('$window', $window)
null