mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'order-cycles-exchange-products'
Conflicts: Gemfile.lock
This commit is contained in:
10
Gemfile.lock
10
Gemfile.lock
@@ -145,13 +145,13 @@ GEM
|
||||
multi_json (~> 1.0)
|
||||
builder (3.0.4)
|
||||
cancan (1.6.7)
|
||||
capybara (1.1.3)
|
||||
capybara (2.0.2)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (~> 2.0)
|
||||
xpath (~> 0.1.4)
|
||||
xpath (~> 1.0.0)
|
||||
childprocess (0.3.6)
|
||||
ffi (~> 1.0, >= 1.0.6)
|
||||
cocaine (0.4.2)
|
||||
@@ -236,7 +236,7 @@ GEM
|
||||
nested_set (1.7.0)
|
||||
activerecord (>= 3.0.0)
|
||||
railties (>= 3.0.0)
|
||||
nokogiri (1.5.5)
|
||||
nokogiri (1.5.6)
|
||||
orm_adapter (0.0.7)
|
||||
paperclip (2.8.0)
|
||||
activerecord (>= 2.3.0)
|
||||
@@ -257,7 +257,7 @@ GEM
|
||||
rabl (0.6.5)
|
||||
activesupport (>= 2.3.14)
|
||||
multi_json (~> 1.0)
|
||||
rack (1.4.3)
|
||||
rack (1.4.4)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-ssl (1.3.2)
|
||||
@@ -352,7 +352,7 @@ GEM
|
||||
warden (1.1.1)
|
||||
rack (>= 1.0)
|
||||
websocket (1.0.6)
|
||||
xpath (0.1.4)
|
||||
xpath (1.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
function AdminCreateOrderCycleCtrl($scope, OrderCycle, Enterprise) {
|
||||
$scope.enterprises = Enterprise.index();
|
||||
|
||||
$scope.order_cycle = OrderCycle.order_cycle;
|
||||
|
||||
$scope.addSupplier = function($event) {
|
||||
OrderCycle.addSupplier($event, $scope.new_supplier_id);
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
OrderCycle.create();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function AdminEditOrderCycleCtrl($scope, OrderCycle, Enterprise) {
|
||||
$scope.enterprises = Enterprise.index();
|
||||
|
||||
var order_cycle_id = window.location.pathname.match(/\/admin\/order_cycles\/(\d+)/)[1];
|
||||
$scope.order_cycle = OrderCycle.load(order_cycle_id);
|
||||
|
||||
$scope.addSupplier = function($event) {
|
||||
OrderCycle.addSupplier($event, $scope.new_supplier_id);
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
OrderCycle.update();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
angular.module('order_cycle', ['ngResource']).
|
||||
config(function($httpProvider) {
|
||||
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
|
||||
}).
|
||||
factory('OrderCycle', function($resource) {
|
||||
var OrderCycle = $resource('/admin/order_cycles/:order_cycle_id.json', {},
|
||||
{'index': { method: 'GET', isArray: true},
|
||||
'create': { method: 'POST'},
|
||||
'update': { method: 'PUT'}});
|
||||
|
||||
|
||||
return {
|
||||
order_cycle: {incoming_exchanges: [],
|
||||
outgoing_exchanges: []},
|
||||
|
||||
addSupplier: function(event, new_supplier_id) {
|
||||
event.preventDefault();
|
||||
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, active: true});
|
||||
},
|
||||
|
||||
load: function(order_cycle_id) {
|
||||
var service = this;
|
||||
|
||||
OrderCycle.get({order_cycle_id: order_cycle_id}, function(oc) {
|
||||
$.extend(service.order_cycle, oc);
|
||||
service.order_cycle.incoming_exchanges = [];
|
||||
service.order_cycle.outgoing_exchanges = [];
|
||||
for(i in service.order_cycle.exchanges) {
|
||||
var exchange = service.order_cycle.exchanges[i];
|
||||
if(exchange.sender_id == service.order_cycle.coordinator_id) {
|
||||
service.order_cycle.outgoing_exchanges.push({enterprise_id: exchange.receiver_id, active: true});
|
||||
|
||||
} else if(exchange.receiver_id == service.order_cycle.coordinator_id) {
|
||||
service.order_cycle.incoming_exchanges.push({enterprise_id: exchange.sender_id, active: true});
|
||||
|
||||
} else {
|
||||
console.log('Exchange between two enterprises, neither of which is coordinator!');
|
||||
}
|
||||
}
|
||||
|
||||
delete(service.order_cycle.exchanges);
|
||||
});
|
||||
|
||||
return this.order_cycle;
|
||||
},
|
||||
|
||||
create: function() {
|
||||
this.removeInactiveExchanges();
|
||||
|
||||
var oc = new OrderCycle({order_cycle: this.order_cycle});
|
||||
oc.$create(function(data) {
|
||||
if(data['success']) {
|
||||
window.location = '/admin/order_cycles';
|
||||
} else {
|
||||
console.log('fail');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.removeInactiveExchanges();
|
||||
|
||||
var oc = new OrderCycle({order_cycle: this.order_cycle});
|
||||
oc.$update({order_cycle_id: this.order_cycle.id}, function(data) {
|
||||
if(data['success']) {
|
||||
window.location = '/admin/order_cycles';
|
||||
} else {
|
||||
console.log('fail');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeInactiveExchanges: function() {
|
||||
for(var i=0; i < this.order_cycle.incoming_exchanges.length; i++) {
|
||||
if(!this.order_cycle.incoming_exchanges[i].active) {
|
||||
this.order_cycle.incoming_exchanges.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}).
|
||||
factory('Enterprise', function($resource) {
|
||||
var Enterprise = $resource('/admin/enterprises/:enterprise_id.json', {},
|
||||
{'index': { method: 'GET', isArray: true}});
|
||||
|
||||
return {
|
||||
enterprises: {},
|
||||
|
||||
index: function() {
|
||||
var service = this;
|
||||
|
||||
Enterprise.index(function(data) {
|
||||
for(i in data) {
|
||||
service.enterprises[data[i]['id']] = data[i];
|
||||
}
|
||||
});
|
||||
|
||||
return this.enterprises;
|
||||
}
|
||||
};
|
||||
}).
|
||||
directive('datetimepicker', ['$parse', function($parse) {
|
||||
return function(scope, element, attrs) {
|
||||
// using $parse instead of scope[attrs.datetimepicker] for cases
|
||||
// where attrs.datetimepicker is 'foo.bar.lol'
|
||||
$(element).datetimepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
timeFormat: 'HH:mm:ss',
|
||||
showOn: "button",
|
||||
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>",
|
||||
buttonImageOnly: true,
|
||||
stepMinute: 15,
|
||||
onSelect: function(dateText, inst) {
|
||||
scope.$apply(function() {
|
||||
parsed = $parse(attrs.datetimepicker);
|
||||
parsed.assign(scope, dateText);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
||||
225
app/assets/javascripts/admin/order_cycle.js.erb.coffee
Normal file
225
app/assets/javascripts/admin/order_cycle.js.erb.coffee
Normal file
@@ -0,0 +1,225 @@
|
||||
app = angular.module('order_cycle', ['ngResource'])
|
||||
|
||||
app.controller 'AdminCreateOrderCycleCtrl', ($scope, OrderCycle, Enterprise) ->
|
||||
$scope.enterprises = Enterprise.index()
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangesVariants = ->
|
||||
OrderCycle.incomingExchangesVariants()
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.submit = ->
|
||||
OrderCycle.create()
|
||||
|
||||
|
||||
app.controller 'AdminEditOrderCycleCtrl', ($scope, $location, OrderCycle, Enterprise) ->
|
||||
$scope.enterprises = Enterprise.index()
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
|
||||
order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
|
||||
$scope.order_cycle = OrderCycle.load(order_cycle_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangesVariants = ->
|
||||
OrderCycle.incomingExchangesVariants()
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.submit = ->
|
||||
OrderCycle.update()
|
||||
|
||||
|
||||
app.config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
|
||||
|
||||
app.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: []
|
||||
|
||||
exchangeSelectedVariants: (exchange) ->
|
||||
numActiveVariants = 0
|
||||
numActiveVariants++ for id, active of exchange.variants when active
|
||||
numActiveVariants
|
||||
|
||||
toggleProducts: (exchange) ->
|
||||
exchange.showProducts = !exchange.showProducts
|
||||
|
||||
addSupplier: (new_supplier_id) ->
|
||||
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, active: true, variants: {}})
|
||||
|
||||
addDistributor: (new_distributor_id) ->
|
||||
this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, active: true, variants: {}})
|
||||
|
||||
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
|
||||
|
||||
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.sender_id == service.order_cycle.coordinator_id
|
||||
angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true})
|
||||
delete(exchange.sender_id)
|
||||
service.order_cycle.outgoing_exchanges.push(exchange)
|
||||
|
||||
else if exchange.receiver_id == service.order_cycle.coordinator_id
|
||||
angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true})
|
||||
delete(exchange.receiver_id)
|
||||
service.order_cycle.incoming_exchanges.push(exchange)
|
||||
|
||||
else
|
||||
console.log('Exchange between two enterprises, neither of which is coordinator!')
|
||||
|
||||
delete(service.order_cycle.exchanges)
|
||||
|
||||
this.order_cycle
|
||||
|
||||
create: ->
|
||||
this.removeInactiveExchanges()
|
||||
|
||||
oc = new OrderCycle({order_cycle: this.order_cycle})
|
||||
oc.$create (data) ->
|
||||
if data['success']
|
||||
$window.location = '/admin/order_cycles'
|
||||
else
|
||||
console.log('fail')
|
||||
|
||||
update: ->
|
||||
this.removeInactiveExchanges()
|
||||
|
||||
oc = new OrderCycle({order_cycle: this.order_cycle})
|
||||
oc.$update {order_cycle_id: this.order_cycle.id}, (data) ->
|
||||
if data['success']
|
||||
$window.location = '/admin/order_cycles'
|
||||
else
|
||||
console.log('fail')
|
||||
|
||||
removeInactiveExchanges: ->
|
||||
this.order_cycle.incoming_exchanges =
|
||||
(exchange for exchange in this.order_cycle.incoming_exchanges when exchange.active)
|
||||
this.order_cycle.outgoing_exchanges =
|
||||
(exchange for exchange in this.order_cycle.outgoing_exchanges when exchange.active)
|
||||
}
|
||||
|
||||
app.factory 'Enterprise', ($resource) ->
|
||||
Enterprise = $resource('/admin/enterprises/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}})
|
||||
|
||||
{
|
||||
Enterprise: Enterprise
|
||||
enterprises: {}
|
||||
supplied_products: []
|
||||
|
||||
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)
|
||||
|
||||
this.enterprises
|
||||
|
||||
totalVariants: (enterprise) ->
|
||||
numVariants = 0
|
||||
|
||||
counts = for product in enterprise.supplied_products
|
||||
numVariants += if product.variants.length == 0 then 1 else product.variants.length
|
||||
|
||||
numVariants
|
||||
}
|
||||
|
||||
|
||||
app.directive 'datetimepicker', ['$parse', ($parse) ->
|
||||
(scope, element, attrs) ->
|
||||
# using $parse instead of scope[attrs.datetimepicker] for cases
|
||||
# where attrs.datetimepicker is 'foo.bar.lol'
|
||||
$(element).datetimepicker
|
||||
dateFormat: 'yy-mm-dd'
|
||||
timeFormat: 'HH:mm:ss'
|
||||
showOn: "button"
|
||||
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
|
||||
buttonImageOnly: true
|
||||
stepMinute: 15
|
||||
onSelect: (dateText, inst) ->
|
||||
scope.$apply ->
|
||||
parsed = $parse(attrs.datetimepicker)
|
||||
parsed.assign(scope, dateText)
|
||||
]
|
||||
1741
app/assets/javascripts/shared/angular-mocks.js
vendored
Normal file
1741
app/assets/javascripts/shared/angular-mocks.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
9227
app/assets/javascripts/shared/jquery-1.8.0.js
vendored
Normal file
9227
app/assets/javascripts/shared/jquery-1.8.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -36,5 +36,36 @@ form.order_cycle {
|
||||
tr td.active {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
tr.supplier td {
|
||||
border-bottom: 2px solid #C3D9FF;
|
||||
}
|
||||
|
||||
.exchange-product {
|
||||
float: left;
|
||||
overflow: auto;
|
||||
width: 18%;
|
||||
min-height: 7.5em;
|
||||
margin: 0 1% 1% 0;
|
||||
border: 1px solid #DAE7FF;
|
||||
padding: 5px;
|
||||
|
||||
.exchange-product-details {
|
||||
clear: both;
|
||||
margin-bottom: 1em;
|
||||
|
||||
.supplier {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.exchange-product-variant {
|
||||
float: left;
|
||||
margin-right: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
margin-top: 3em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ module Admin
|
||||
if @order_cycle.update_attributes(params[:order_cycle])
|
||||
OpenFoodWeb::OrderCycleFormApplicator.new(@order_cycle).go!
|
||||
|
||||
flash[:notice] = 'Your order cycle has been saved.'
|
||||
flash[:notice] = 'Your order cycle has been updated.'
|
||||
format.html { redirect_to admin_order_cycles_path }
|
||||
format.json { render :json => {:success => true} }
|
||||
else
|
||||
|
||||
5
app/helpers/order_cycles_helper.rb
Normal file
5
app/helpers/order_cycles_helper.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
module OrderCyclesHelper
|
||||
def coordinating_enterprises
|
||||
Enterprise.is_distributor.order('name')
|
||||
end
|
||||
end
|
||||
@@ -12,4 +12,6 @@ class Exchange < ActiveRecord::Base
|
||||
|
||||
validates_presence_of :order_cycle, :sender, :receiver
|
||||
validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id]
|
||||
|
||||
accepts_nested_attributes_for :variants
|
||||
end
|
||||
|
||||
13
app/presenters/variant_presenter.rb
Normal file
13
app/presenters/variant_presenter.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class VariantPresenter
|
||||
attr_accessor :variant
|
||||
|
||||
def initialize(variant)
|
||||
@variant = variant
|
||||
end
|
||||
|
||||
delegate :id, :to => :variant
|
||||
|
||||
def image_url
|
||||
@variant.images.first.attachment.url :mini if @variant.images.present?
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,15 @@
|
||||
r.list_of :enterprises, @collection do
|
||||
r.element :id
|
||||
r.element :name
|
||||
|
||||
r.list_of :supplied_products do |product|
|
||||
r.element :name
|
||||
r.element :supplier_name, product.supplier.andand.name
|
||||
r.element :image_url, product.images.present? ? product.images.first.attachment.url(:mini) : nil
|
||||
r.element :master_id, product.master.id
|
||||
r.list_of :variants do |variant|
|
||||
r.element :id
|
||||
r.element :label, variant.options_text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
%td{:colspan => 3}
|
||||
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle'}
|
||||
.exchange-product-details
|
||||
.supplier {{ product.supplier_name }}
|
||||
= check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}'
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
{{ product.name }}
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'}
|
||||
= check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}'
|
||||
{{ variant.label }}
|
||||
15
app/views/admin/order_cycles/_exchange_form.html.haml
Normal file
15
app/views/admin/order_cycles/_exchange_form.html.haml
Normal file
@@ -0,0 +1,15 @@
|
||||
%td.active= check_box_tag 'order_cycle_exchange_{{ $index }}_active', 1, 1, 'ng-model' => 'exchange.active', 'id' => 'order_cycle_exchange_{{ $index }}_active'
|
||||
%td{:class => "#{type}_name"} {{ enterprises[exchange.enterprise_id].name }}
|
||||
%td.products
|
||||
= f.submit 'Products', 'ng-click' => 'toggleProducts($event, exchange)'
|
||||
{{ exchangeSelectedVariants(exchange) }} /
|
||||
- if type == 'supplier'
|
||||
{{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }}
|
||||
- else
|
||||
{{ incomingExchangesVariants().length }}
|
||||
selected
|
||||
- if type == 'distributor'
|
||||
%td.collection-details
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Pickup time / date', 'ng-model' => 'exchange.pickup_time'
|
||||
%br/
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => 'Delivery instructions', 'ng-model' => 'exchange.pickup_instructions'
|
||||
@@ -0,0 +1,10 @@
|
||||
/ TODO: Unify this with exchange_distributed_products_form
|
||||
%td{:colspan => 3}
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
|
||||
.exchange-product-details
|
||||
= check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}'
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
{{ product.name }}
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants'}
|
||||
= check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}'
|
||||
{{ variant.label }}
|
||||
@@ -13,9 +13,16 @@
|
||||
|
||||
%h2 Incoming
|
||||
%table.exchanges
|
||||
%tr{'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'}
|
||||
%td.active= check_box_tag 'order_cycle_exchange_{{ $index }}_active', 1, 1, 'ng-model' => 'exchange.active', 'id' => 'order_cycle_exchange_{{ $index }}_active'
|
||||
%td {{ enterprises[exchange.enterprise_id].name }}
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%th Supplier
|
||||
%th Products
|
||||
%tbody{'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'}
|
||||
%tr.supplier
|
||||
= render 'exchange_form', :f => f, :type => 'supplier'
|
||||
%tr.products{'ng-show' => 'exchange.showProducts'}
|
||||
= render 'exchange_supplied_products_form'
|
||||
|
||||
= select_tag :new_supplier_id, options_from_collection_for_select(Enterprise.is_primary_producer, :id, :name), {'ng-model' => 'new_supplier_id'}
|
||||
= f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)'
|
||||
@@ -23,17 +30,33 @@
|
||||
|
||||
%h2 Coordinator
|
||||
= f.label :coordinator_id, 'Coordinator'
|
||||
= f.collection_select :coordinator_id, Enterprise.all, :id, :name, {}, {'ng-model' => 'order_cycle.coordinator_id', 'required' => true}
|
||||
= f.collection_select :coordinator_id, coordinating_enterprises, :id, :name, {}, {'ng-model' => 'order_cycle.coordinator_id', 'required' => true}
|
||||
|
||||
%h2 Outgoing
|
||||
%p TODO
|
||||
%table.exchanges
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%th Distributor
|
||||
%th Products
|
||||
%th Collection details
|
||||
%tbody{'ng-repeat' => 'exchange in order_cycle.outgoing_exchanges'}
|
||||
%tr.distributor
|
||||
= render 'exchange_form', :f => f, :type => 'distributor'
|
||||
%tr.products{'ng-show' => 'exchange.showProducts'}
|
||||
= render 'exchange_distributed_products_form'
|
||||
|
||||
= f.submit @order_cycle.new_record? ? 'Create' : 'Update'
|
||||
or
|
||||
= link_to 'Cancel', main_app.admin_order_cycles_path
|
||||
= select_tag :new_distributor_id, options_from_collection_for_select(Enterprise.is_distributor, :id, :name), {'ng-model' => 'new_distributor_id'}
|
||||
= f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)'
|
||||
|
||||
.actions
|
||||
= f.submit @order_cycle.new_record? ? 'Create' : 'Update'
|
||||
or
|
||||
= link_to 'Cancel', main_app.admin_order_cycles_path
|
||||
|
||||
|
||||
<h2>Debug information</h2>
|
||||
%h2 Debug information
|
||||
|
||||
%pre order_cycle = {{ order_cycle | json }}
|
||||
%pre enterprises = {{ enterprises | json }}
|
||||
%pre supplied_products = {{ supplied_products | json }}
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
%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= order_cycle.coordinator.name
|
||||
%td
|
||||
%td.suppliers
|
||||
- order_cycle.suppliers.each do |s|
|
||||
= s.name
|
||||
%br/
|
||||
%td
|
||||
%td.distributors
|
||||
- order_cycle.distributors.each do |d|
|
||||
= d.name
|
||||
%br/
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
r.element :order_cycle, @order_cycle do
|
||||
r.element :id
|
||||
r.element :name
|
||||
r.element :orders_open_at
|
||||
r.element :orders_close_at
|
||||
r.element :orders_open_at, @order_cycle.orders_open_at.to_s
|
||||
r.element :orders_close_at, @order_cycle.orders_close_at.to_s
|
||||
r.element :coordinator_id
|
||||
r.list_of :exchanges do
|
||||
r.list_of :exchanges do |exchange|
|
||||
r.element :id
|
||||
r.element :sender_id
|
||||
r.element :receiver_id
|
||||
|
||||
r.element :variants, Hash[ exchange.variants.map { |v| [v.id, true] } ], {}
|
||||
|
||||
r.element :pickup_time
|
||||
r.element :pickup_instructions
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<script type="text/javascript">
|
||||
var uvOptions = {};
|
||||
(function() {
|
||||
var uv = document.createElement('script'); uv.type = 'text/javascript'; uv.async = true;
|
||||
uv.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'widget.uservoice.com/mCbT1cbjQZkPELXOix9Ag.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(uv, s);
|
||||
})();
|
||||
</script>
|
||||
10
app/views/layouts/_feedback_script.html.erb
Normal file
10
app/views/layouts/_feedback_script.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<% if Rails.env.production? %>
|
||||
<script type="text/javascript">
|
||||
var uvOptions = {};
|
||||
(function() {
|
||||
var uv = document.createElement('script'); uv.type = 'text/javascript'; uv.async = true;
|
||||
uv.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'widget.uservoice.com/mCbT1cbjQZkPELXOix9Ag.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(uv, s);
|
||||
})();
|
||||
</script>
|
||||
<% end %>
|
||||
39
app/views/spree/admin/overview/index.html.erb
Normal file
39
app/views/spree/admin/overview/index.html.erb
Normal file
@@ -0,0 +1,39 @@
|
||||
<% content_for :head do %>
|
||||
<% if Rails.env.production? %>
|
||||
<%= stylesheet_link_tag 'https://api.jirafe.com/dashboard/css/spree_ui.css', :media => 'all' %>
|
||||
<%= javascript_include_tag 'https://jirafe.com/dashboard/js/spree_namespaced_ui.js' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if Rails.env.production? %>
|
||||
<% if Spree::Dash::Config.configured? %>
|
||||
<h1><%= t(:overview) %></h1>
|
||||
<div id="jirafe"></div>
|
||||
<%= javascript_tag :defer => 'defer' do %>
|
||||
jirafe.jQuery('#jirafe').jirafe({
|
||||
api_url: 'https://api.jirafe.com/v1',
|
||||
api_token: '<%= Spree::Dash::Config.token %>',
|
||||
app_id: '<%= Spree::Dash::Config.app_id %>',
|
||||
version: 'spree-v0.1.0',
|
||||
locale: '<%= Spree::Dash::Config.locale %>' });
|
||||
setTimeout(function() {
|
||||
if ($('mod-jirafe') == undefined) {
|
||||
$('messages').insert ("<ul class=\"messages\"><li class=\"error-msg\">We're unable to connect with the Jirafe service for the moment. Please wait a few minutes and refresh this page later.</li></ul>");
|
||||
}
|
||||
}, 10000);
|
||||
<% end %>
|
||||
<div id="jirafe_locales">
|
||||
<%= raw jirafe_locale_links.join(' | ') %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="analytics_splash">
|
||||
<%= image_tag 'analytics_dashboard_preview.png', :alt => 'Spree Analytics' %>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="preview-buttons">
|
||||
<%= link_to content_tag(:span, t(:activate)), admin_analytics_sign_up_path, :class => 'button green' %>
|
||||
<%= t(:or) %>
|
||||
<%= link_to content_tag(:span, t(:learn_more)), "http://spreecommerce.com/blog/2012/01/31/introducing-spree-analytics/", :class => 'button', :target => '_blank' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
22
config-angular/testacular-e2e.conf.js
Normal file
22
config-angular/testacular-e2e.conf.js
Normal file
@@ -0,0 +1,22 @@
|
||||
basePath = '../';
|
||||
|
||||
files = [
|
||||
ANGULAR_SCENARIO,
|
||||
ANGULAR_SCENARIO_ADAPTER,
|
||||
'test/e2e/**/*.js'
|
||||
];
|
||||
|
||||
autoWatch = false;
|
||||
|
||||
browsers = ['Chrome'];
|
||||
|
||||
singleRun = true;
|
||||
|
||||
proxies = {
|
||||
'/': 'http://localhost:8000/'
|
||||
};
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'test_out/e2e.xml',
|
||||
suite: 'e2e'
|
||||
};
|
||||
24
config-angular/testacular.conf.js
Normal file
24
config-angular/testacular.conf.js
Normal file
@@ -0,0 +1,24 @@
|
||||
basePath = '../';
|
||||
|
||||
files = [
|
||||
JASMINE,
|
||||
JASMINE_ADAPTER,
|
||||
'app/assets/javascripts/shared/jquery-1.8.0.js', // TODO: Can we link to Rails' jquery?
|
||||
'app/assets/javascripts/shared/angular.js',
|
||||
'app/assets/javascripts/shared/angular-*.js',
|
||||
|
||||
'app/assets/javascripts/admin/order_cycle.js.erb.coffee',
|
||||
|
||||
'spec/javascripts/unit/**/*.js*'
|
||||
];
|
||||
|
||||
exclude = ['**/.#*']
|
||||
|
||||
autoWatch = true;
|
||||
|
||||
browsers = ['Chrome'];
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'log/testacular-unit.xml',
|
||||
suite: 'unit'
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
class ChangeExchangePickupTimeToString < ActiveRecord::Migration
|
||||
def up
|
||||
change_column :exchanges, :pickup_time, :string
|
||||
end
|
||||
|
||||
def down
|
||||
change_column :exchanges, :pickup_time, :datetime
|
||||
end
|
||||
end
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20121125232613) do
|
||||
ActiveRecord::Schema.define(:version => 20130118031610) do
|
||||
|
||||
create_table "cms_blocks", :force => true do |t|
|
||||
t.integer "page_id", :null => false
|
||||
@@ -177,7 +177,7 @@ ActiveRecord::Schema.define(:version => 20121125232613) do
|
||||
t.integer "sender_id"
|
||||
t.integer "receiver_id"
|
||||
t.integer "payment_enterprise_id"
|
||||
t.datetime "pickup_time"
|
||||
t.string "pickup_time"
|
||||
t.string "pickup_instructions"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
|
||||
@@ -8,29 +8,47 @@ module OpenFoodWeb
|
||||
@touched_exchanges = []
|
||||
|
||||
@order_cycle.incoming_exchanges.each do |exchange|
|
||||
variant_ids = exchange_variant_ids(exchange)
|
||||
|
||||
if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id)
|
||||
update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id)
|
||||
update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, {variant_ids: variant_ids})
|
||||
else
|
||||
add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id)
|
||||
add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, {variant_ids: variant_ids})
|
||||
end
|
||||
end
|
||||
|
||||
@order_cycle.outgoing_exchanges.each do |exchange|
|
||||
variant_ids = exchange_variant_ids(exchange)
|
||||
|
||||
if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id])
|
||||
update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], {variant_ids: variant_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]})
|
||||
else
|
||||
add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], {variant_ids: variant_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]})
|
||||
end
|
||||
end
|
||||
|
||||
destroy_untouched_exchanges
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :touched_exchanges
|
||||
|
||||
def exchange_exists?(sender_id, receiver_id)
|
||||
@order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id).present?
|
||||
end
|
||||
|
||||
def add_exchange(sender_id, receiver_id)
|
||||
exchange = @order_cycle.exchanges.create! :sender_id => sender_id, :receiver_id => receiver_id
|
||||
def add_exchange(sender_id, receiver_id, attrs={})
|
||||
attrs = attrs.reverse_merge(:sender_id => sender_id, :receiver_id => receiver_id)
|
||||
exchange = @order_cycle.exchanges.create! attrs
|
||||
@touched_exchanges << exchange
|
||||
end
|
||||
|
||||
def update_exchange(sender_id, receiver_id)
|
||||
# NOOP - when we're setting data on the exchange, we can do so here
|
||||
|
||||
def update_exchange(sender_id, receiver_id, attrs={})
|
||||
exchange = @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id).first
|
||||
exchange.update_attributes!(attrs)
|
||||
|
||||
@touched_exchanges << exchange
|
||||
end
|
||||
|
||||
@@ -42,5 +60,9 @@ module OpenFoodWeb
|
||||
@order_cycle.exchanges - @touched_exchanges
|
||||
end
|
||||
|
||||
|
||||
def exchange_variant_ids(exchange)
|
||||
exchange[:variants].select { |k, v| v }.keys.map { |k| k.to_i }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
11
script-angular/e2e-test.bat
Normal file
11
script-angular/e2e-test.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
|
||||
REM Windows script for running e2e tests
|
||||
REM You have to run server and capture some browser first
|
||||
REM
|
||||
REM Requirements:
|
||||
REM - NodeJS (http://nodejs.org/)
|
||||
REM - Testacular (npm install -g testacular)
|
||||
|
||||
set BASE_DIR=%~dp0
|
||||
testacular start "%BASE_DIR%\..\config\testacular-e2e.conf.js" %*
|
||||
9
script-angular/e2e-test.sh
Executable file
9
script-angular/e2e-test.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
|
||||
echo ""
|
||||
echo "Starting Testacular Server (http://vojtajina.github.com/testacular)"
|
||||
echo "-------------------------------------------------------------------"
|
||||
|
||||
testacular start $BASE_DIR/../config/testacular-e2e.conf.js $*
|
||||
19
script-angular/test-server.bat
Normal file
19
script-angular/test-server.bat
Normal file
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
|
||||
REM Windows script for starting JSTD server
|
||||
REM
|
||||
REM Requirements:
|
||||
REM - Java (http://www.java.com)
|
||||
|
||||
set BASE_DIR=%~dp0
|
||||
set PORT=9876
|
||||
|
||||
echo Starting JsTestDriver Server (http://code.google.com/p/js-test-driver/)
|
||||
echo Please open the following url and capture one or more browsers:
|
||||
echo http://localhost:%PORT%/
|
||||
|
||||
java -jar "%BASE_DIR%\..\test\lib\jstestdriver\JsTestDriver.jar" ^
|
||||
--port %PORT% ^
|
||||
--browserTimeout 20000 ^
|
||||
--config "%BASE_DIR%\..\config\jsTestDriver.conf" ^
|
||||
--basePath "%BASE_DIR%\.."
|
||||
14
script-angular/test-server.sh
Executable file
14
script-angular/test-server.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
PORT=9876
|
||||
|
||||
echo "Starting JsTestDriver Server (http://code.google.com/p/js-test-driver/)"
|
||||
echo "Please open the following url and capture one or more browsers:"
|
||||
echo "http://localhost:$PORT"
|
||||
|
||||
java -jar "$BASE_DIR/../test/lib/jstestdriver/JsTestDriver.jar" \
|
||||
--port $PORT \
|
||||
--browserTimeout 20000 \
|
||||
--config "$BASE_DIR/../config/jsTestDriver.conf" \
|
||||
--basePath "$BASE_DIR/.."
|
||||
11
script-angular/test.bat
Normal file
11
script-angular/test.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
|
||||
REM Windows script for running unit tests
|
||||
REM You have to run server and capture some browser first
|
||||
REM
|
||||
REM Requirements:
|
||||
REM - NodeJS (http://nodejs.org/)
|
||||
REM - Testacular (npm install -g testacular)
|
||||
|
||||
set BASE_DIR=%~dp0
|
||||
testacular start "%BASE_DIR%\..\config\testacular.conf.js" %*
|
||||
9
script-angular/test.sh
Executable file
9
script-angular/test.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
|
||||
echo ""
|
||||
echo "Starting Testacular Server (http://vojtajina.github.com/testacular)"
|
||||
echo "-------------------------------------------------------------------"
|
||||
|
||||
testacular start $BASE_DIR/../config-angular/testacular.conf.js $*
|
||||
13
script-angular/update-repo.sh
Executable file
13
script-angular/update-repo.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
###
|
||||
# This scripts updates the local repo with the latest changes from github.
|
||||
#
|
||||
# The master branch will be REPLACED with what's in github and all local changes
|
||||
# will be LOST.
|
||||
###
|
||||
|
||||
git checkout master
|
||||
git fetch -f origin
|
||||
git fetch --tags origin
|
||||
git reset --hard origin/master
|
||||
19
script-angular/watchr.rb
Executable file
19
script-angular/watchr.rb
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env watchr
|
||||
|
||||
# config file for watchr http://github.com/mynyml/watchr
|
||||
# install: gem install watchr
|
||||
# run: watch watchr.rb
|
||||
# note: make sure that you have jstd server running (server.sh) and a browser captured
|
||||
|
||||
log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log')
|
||||
|
||||
`cd ..`
|
||||
`touch #{log_file}`
|
||||
|
||||
puts "String watchr... log file: #{log_file}"
|
||||
|
||||
watch( '(app/js|test/unit)' ) do
|
||||
`echo "\n\ntest run started @ \`date\`" > #{log_file}`
|
||||
`scripts/test.sh &> #{log_file}`
|
||||
end
|
||||
|
||||
244
script-angular/web-server.js
Executable file
244
script-angular/web-server.js
Executable file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var util = require('util'),
|
||||
http = require('http'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
events = require('events');
|
||||
|
||||
var DEFAULT_PORT = 8000;
|
||||
|
||||
function main(argv) {
|
||||
new HttpServer({
|
||||
'GET': createServlet(StaticServlet),
|
||||
'HEAD': createServlet(StaticServlet)
|
||||
}).start(Number(argv[2]) || DEFAULT_PORT);
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return value.toString().
|
||||
replace('<', '<').
|
||||
replace('>', '>').
|
||||
replace('"', '"');
|
||||
}
|
||||
|
||||
function createServlet(Class) {
|
||||
var servlet = new Class();
|
||||
return servlet.handleRequest.bind(servlet);
|
||||
}
|
||||
|
||||
/**
|
||||
* An Http server implementation that uses a map of methods to decide
|
||||
* action routing.
|
||||
*
|
||||
* @param {Object} Map of method => Handler function
|
||||
*/
|
||||
function HttpServer(handlers) {
|
||||
this.handlers = handlers;
|
||||
this.server = http.createServer(this.handleRequest_.bind(this));
|
||||
}
|
||||
|
||||
HttpServer.prototype.start = function(port) {
|
||||
this.port = port;
|
||||
this.server.listen(port);
|
||||
util.puts('Http Server running at http://localhost:' + port + '/');
|
||||
};
|
||||
|
||||
HttpServer.prototype.parseUrl_ = function(urlString) {
|
||||
var parsed = url.parse(urlString);
|
||||
parsed.pathname = url.resolve('/', parsed.pathname);
|
||||
return url.parse(url.format(parsed), true);
|
||||
};
|
||||
|
||||
HttpServer.prototype.handleRequest_ = function(req, res) {
|
||||
var logEntry = req.method + ' ' + req.url;
|
||||
if (req.headers['user-agent']) {
|
||||
logEntry += ' ' + req.headers['user-agent'];
|
||||
}
|
||||
util.puts(logEntry);
|
||||
req.url = this.parseUrl_(req.url);
|
||||
var handler = this.handlers[req.method];
|
||||
if (!handler) {
|
||||
res.writeHead(501);
|
||||
res.end();
|
||||
} else {
|
||||
handler.call(this, req, res);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles static content.
|
||||
*/
|
||||
function StaticServlet() {}
|
||||
|
||||
StaticServlet.MimeMap = {
|
||||
'txt': 'text/plain',
|
||||
'html': 'text/html',
|
||||
'css': 'text/css',
|
||||
'xml': 'application/xml',
|
||||
'json': 'application/json',
|
||||
'js': 'application/javascript',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'gif': 'image/gif',
|
||||
'png': 'image/png',
|
||||
'svg': 'image/svg+xml'
|
||||
};
|
||||
|
||||
StaticServlet.prototype.handleRequest = function(req, res) {
|
||||
var self = this;
|
||||
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
});
|
||||
var parts = path.split('/');
|
||||
if (parts[parts.length-1].charAt(0) === '.')
|
||||
return self.sendForbidden_(req, res, path);
|
||||
fs.stat(path, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendMissing_(req, res, path);
|
||||
if (stat.isDirectory())
|
||||
return self.sendDirectory_(req, res, path);
|
||||
return self.sendFile_(req, res, path);
|
||||
});
|
||||
}
|
||||
|
||||
StaticServlet.prototype.sendError_ = function(req, res, error) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>Internal Server Error</title>\n');
|
||||
res.write('<h1>Internal Server Error</h1>');
|
||||
res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
|
||||
util.puts('500 Internal Server Error');
|
||||
util.puts(util.inspect(error));
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(404, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>404 Not Found</title>\n');
|
||||
res.write('<h1>Not Found</h1>');
|
||||
res.write(
|
||||
'<p>The requested URL ' +
|
||||
escapeHtml(path) +
|
||||
' was not found on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
util.puts('404 Not Found: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(403, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>403 Forbidden</title>\n');
|
||||
res.write('<h1>Forbidden</h1>');
|
||||
res.write(
|
||||
'<p>You do not have permission to access ' +
|
||||
escapeHtml(path) + ' on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
util.puts('403 Forbidden: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
|
||||
res.writeHead(301, {
|
||||
'Content-Type': 'text/html',
|
||||
'Location': redirectUrl
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>301 Moved Permanently</title>\n');
|
||||
res.write('<h1>Moved Permanently</h1>');
|
||||
res.write(
|
||||
'<p>The document has moved <a href="' +
|
||||
redirectUrl +
|
||||
'">here</a>.</p>'
|
||||
);
|
||||
res.end();
|
||||
util.puts('301 Moved Permanently: ' + redirectUrl);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendFile_ = function(req, res, path) {
|
||||
var self = this;
|
||||
var file = fs.createReadStream(path);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': StaticServlet.
|
||||
MimeMap[path.split('.').pop()] || 'text/plain'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
} else {
|
||||
file.on('data', res.write.bind(res));
|
||||
file.on('close', function() {
|
||||
res.end();
|
||||
});
|
||||
file.on('error', function(error) {
|
||||
self.sendError_(req, res, error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
|
||||
var self = this;
|
||||
if (path.match(/[^\/]$/)) {
|
||||
req.url.pathname += '/';
|
||||
var redirectUrl = url.format(url.parse(url.format(req.url)));
|
||||
return self.sendRedirect_(req, res, redirectUrl);
|
||||
}
|
||||
fs.readdir(path, function(err, files) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, error);
|
||||
|
||||
if (!files.length)
|
||||
return self.writeDirectoryIndex_(req, res, path, []);
|
||||
|
||||
var remaining = files.length;
|
||||
files.forEach(function(fileName, index) {
|
||||
fs.stat(path + '/' + fileName, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, err);
|
||||
if (stat.isDirectory()) {
|
||||
files[index] = fileName + '/';
|
||||
}
|
||||
if (!(--remaining))
|
||||
return self.writeDirectoryIndex_(req, res, path, files);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>' + escapeHtml(path) + '</title>\n');
|
||||
res.write('<style>\n');
|
||||
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
|
||||
res.write('</style>\n');
|
||||
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
|
||||
res.write('<ol>');
|
||||
files.forEach(function(fileName) {
|
||||
if (fileName.charAt(0) !== '.') {
|
||||
res.write('<li><a href="' +
|
||||
escapeHtml(fileName) + '">' +
|
||||
escapeHtml(fileName) + '</a></li>');
|
||||
}
|
||||
});
|
||||
res.write('</ol>');
|
||||
res.end();
|
||||
};
|
||||
|
||||
// Must be last,
|
||||
main(process.argv);
|
||||
@@ -5,22 +5,20 @@ FactoryGirl.define do
|
||||
factory :order_cycle, :parent => :simple_order_cycle do
|
||||
after(:create) do |oc|
|
||||
# Suppliers
|
||||
create(:exchange, :order_cycle => oc, :receiver => oc.coordinator)
|
||||
create(:exchange, :order_cycle => oc, :receiver => oc.coordinator)
|
||||
ex1 = create(:exchange, :order_cycle => oc, :receiver => oc.coordinator)
|
||||
ex2 = create(:exchange, :order_cycle => oc, :receiver => oc.coordinator)
|
||||
|
||||
# Distributors
|
||||
create(:exchange, :order_cycle => oc, :sender => oc.coordinator)
|
||||
create(:exchange, :order_cycle => oc, :sender => oc.coordinator)
|
||||
create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 0', :pickup_instructions => 'instructions 0')
|
||||
create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 1', :pickup_instructions => 'instructions 1')
|
||||
|
||||
# Products with images
|
||||
ex = oc.exchanges.first
|
||||
|
||||
2.times do
|
||||
product = create(:product)
|
||||
[ex1, ex2].each do |exchange|
|
||||
product = create(:product, supplier: exchange.sender)
|
||||
image = File.open(File.expand_path('../../app/assets/images/logo.jpg', __FILE__))
|
||||
Spree::Image.create({:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :alt => "position 1", :attachment => image, :position => 1})
|
||||
|
||||
ex.variants << product.master
|
||||
exchange.variants << product.master
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -31,7 +29,7 @@ FactoryGirl.define do
|
||||
orders_open_at { Time.zone.now - 1.day }
|
||||
orders_close_at { Time.zone.now + 1.week }
|
||||
|
||||
coordinator { Enterprise.first || FactoryGirl.create(:enterprise) }
|
||||
coordinator { Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise) }
|
||||
end
|
||||
|
||||
factory :exchange, :class => Exchange do
|
||||
|
||||
241
spec/features/admin/order_cycles_spec.rb
Normal file
241
spec/features/admin/order_cycles_spec.rb
Normal file
@@ -0,0 +1,241 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature %q{
|
||||
As an administrator
|
||||
I want to manage order cycles
|
||||
}, js: true do
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
scenario "listing order cycles" do
|
||||
# Given an order cycle
|
||||
oc = create(:order_cycle)
|
||||
|
||||
# When I go to the admin order cycles page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
|
||||
# Then I should see the basic fields
|
||||
page.should have_selector 'a', text: oc.name
|
||||
|
||||
page.should have_selector "input[value='#{oc.orders_open_at}']"
|
||||
page.should have_selector "input[value='#{oc.orders_close_at}']"
|
||||
page.should have_content oc.coordinator.name
|
||||
|
||||
# And I should see the suppliers and distributors
|
||||
oc.suppliers.each { |s| page.should have_content s.name }
|
||||
oc.distributors.each { |d| page.should have_content d.name }
|
||||
|
||||
# And I should see a thumbnail image for each product
|
||||
all('td.products img').count.should == 2
|
||||
end
|
||||
|
||||
scenario "creating an order cycle" do
|
||||
# Given coordinating, supplying and distributing enterprises with some products with variants
|
||||
create(:distributor_enterprise, name: 'My coordinator')
|
||||
supplier = create(:supplier_enterprise, name: 'My supplier')
|
||||
product = create(:product, supplier: supplier)
|
||||
create(:variant, product: product)
|
||||
create(:variant, product: product)
|
||||
distributor = create(:distributor_enterprise, name: 'My distributor')
|
||||
|
||||
# When I go to the new order cycle page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
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: '2012-11-06 06:00:00'
|
||||
fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00'
|
||||
select 'My coordinator', from: 'order_cycle_coordinator_id'
|
||||
|
||||
# And I add a supplier and some products
|
||||
select 'My supplier', from: 'new_supplier_id'
|
||||
click_button 'Add supplier'
|
||||
page.find('table.exchanges tr.supplier td.products input').click
|
||||
check 'order_cycle_incoming_exchange_0_variants_2'
|
||||
check 'order_cycle_incoming_exchange_0_variants_3'
|
||||
|
||||
# And I add a distributor with the same products
|
||||
select 'My distributor', from: 'new_distributor_id'
|
||||
click_button 'Add distributor'
|
||||
|
||||
fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'pickup time'
|
||||
fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions'
|
||||
|
||||
page.find('table.exchanges tr.distributor td.products input').click
|
||||
check 'order_cycle_outgoing_exchange_0_variants_2'
|
||||
check 'order_cycle_outgoing_exchange_0_variants_3'
|
||||
|
||||
# And I click Create
|
||||
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='2012-11-06 06:00:00 UTC']"
|
||||
page.should have_selector "input[value='2012-11-13 17:00:00 UTC']"
|
||||
page.should have_content 'My coordinator'
|
||||
|
||||
page.should have_selector 'td.suppliers', text: 'My supplier'
|
||||
page.should have_selector 'td.distributors', text: 'My distributor'
|
||||
|
||||
# And it should have some variants selected
|
||||
OrderCycle.last.exchanges.first.variants.count.should == 2
|
||||
OrderCycle.last.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'
|
||||
end
|
||||
|
||||
|
||||
scenario "editing an order cycle" do
|
||||
# Given an order cycle with all the settings
|
||||
oc = create(:order_cycle)
|
||||
|
||||
# When I edit it
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
click_link oc.name
|
||||
|
||||
# Then I should see the basic settings
|
||||
sleep(1)
|
||||
page.find('#order_cycle_name').value.should == oc.name
|
||||
page.find('#order_cycle_orders_open_at').value.should == oc.orders_open_at.to_s
|
||||
page.find('#order_cycle_orders_close_at').value.should == oc.orders_close_at.to_s
|
||||
page.find('#order_cycle_coordinator_id').value.to_i.should == oc.coordinator_id
|
||||
|
||||
# And I should see the suppliers with products
|
||||
page.should have_selector 'td.supplier_name', :text => oc.suppliers.first.name
|
||||
page.should have_selector 'td.supplier_name', :text => oc.suppliers.last.name
|
||||
|
||||
page.all('table.exchanges tbody tr.supplier').each do |row|
|
||||
row.find('td.products input').click
|
||||
|
||||
products_row = page.all('table.exchanges tr.products').select { |r| r.visible? }.first
|
||||
products_row.should have_selector "input[type='checkbox'][checked='checked']"
|
||||
|
||||
row.find('td.products input').click
|
||||
end
|
||||
|
||||
# And I should see the distributors with products
|
||||
page.should have_selector 'td.distributor_name', :text => oc.distributors.first.name
|
||||
page.should have_selector 'td.distributor_name', :text => oc.distributors.last.name
|
||||
|
||||
page.find('#order_cycle_outgoing_exchange_0_pickup_time').value.should == 'time 0'
|
||||
page.find('#order_cycle_outgoing_exchange_0_pickup_instructions').value.should == 'instructions 0'
|
||||
page.find('#order_cycle_outgoing_exchange_1_pickup_time').value.should == 'time 1'
|
||||
page.find('#order_cycle_outgoing_exchange_1_pickup_instructions').value.should == 'instructions 1'
|
||||
|
||||
page.all('table.exchanges tbody tr.distributor').each do |row|
|
||||
row.find('td.products input').click
|
||||
|
||||
products_row = page.all('table.exchanges tr.products').select { |r| r.visible? }.first
|
||||
products_row.should have_selector "input[type='checkbox'][checked='checked']"
|
||||
|
||||
row.find('td.products input').click
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
scenario "updating an order cycle" do
|
||||
# Given an order cycle with all the settings
|
||||
oc = create(:order_cycle)
|
||||
|
||||
# And a coordinating, supplying and distributing enterprise with some products with variants
|
||||
create(:distributor_enterprise, name: 'My coordinator')
|
||||
supplier = create(:supplier_enterprise, name: 'My supplier')
|
||||
distributor = create(:distributor_enterprise, name: 'My distributor')
|
||||
product = create(:product, supplier: supplier)
|
||||
v1 = create(:variant, product: product)
|
||||
v2 = create(:variant, product: product)
|
||||
|
||||
# When I go to its edit page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
click_link oc.name
|
||||
sleep 1
|
||||
|
||||
# And I update it
|
||||
fill_in 'order_cycle_name', with: 'Plums & Avos'
|
||||
fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00'
|
||||
fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00'
|
||||
select 'My coordinator', from: 'order_cycle_coordinator_id'
|
||||
|
||||
# And I add a supplier and some products
|
||||
select 'My supplier', from: 'new_supplier_id'
|
||||
click_button 'Add supplier'
|
||||
page.all("table.exchanges tr.supplier td.products input").each { |e| e.click }
|
||||
|
||||
uncheck "order_cycle_incoming_exchange_1_variants_2"
|
||||
check "order_cycle_incoming_exchange_2_variants_#{v1.id}"
|
||||
check "order_cycle_incoming_exchange_2_variants_#{v2.id}"
|
||||
|
||||
# And I add a distributor and some products
|
||||
select 'My distributor', from: 'new_distributor_id'
|
||||
click_button 'Add distributor'
|
||||
|
||||
fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'New time 0'
|
||||
fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'New instructions 0'
|
||||
fill_in 'order_cycle_outgoing_exchange_1_pickup_time', with: 'New time 1'
|
||||
fill_in 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'New instructions 1'
|
||||
|
||||
page.all("table.exchanges tr.distributor td.products input").each { |e| e.click }
|
||||
|
||||
uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}"
|
||||
check "order_cycle_outgoing_exchange_2_variants_#{v2.id}"
|
||||
|
||||
# And I click Update
|
||||
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='2012-11-06 06:00:00 UTC']"
|
||||
page.should have_selector "input[value='2012-11-13 17:00:00 UTC']"
|
||||
page.should have_content 'My coordinator'
|
||||
|
||||
page.should have_selector 'td.suppliers', text: 'My supplier'
|
||||
page.should have_selector 'td.distributors', text: 'My distributor'
|
||||
|
||||
# And it should have some variants selected
|
||||
OrderCycle.last.variants.map { |v| v.id }.sort.should == [1, v1.id, v2.id].sort
|
||||
|
||||
# And the collection details should have been updated
|
||||
OrderCycle.last.exchanges.where(pickup_time: 'New time 0', pickup_instructions: 'New instructions 0').should be_present
|
||||
OrderCycle.last.exchanges.where(pickup_time: 'New time 1', pickup_instructions: 'New instructions 1').should be_present
|
||||
end
|
||||
|
||||
|
||||
scenario "updating many order cycle opening/closing times at once" do
|
||||
# Given three order cycles
|
||||
3.times { create(:order_cycle) }
|
||||
|
||||
# When I go to the order cycles page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
|
||||
# And I fill in some new opening/closing times and save them
|
||||
fill_in 'order_cycle_set_collection_attributes_0_orders_open_at', :with => '2012-12-01 12:00:00'
|
||||
fill_in 'order_cycle_set_collection_attributes_0_orders_close_at', :with => '2012-12-01 12:00:01'
|
||||
fill_in 'order_cycle_set_collection_attributes_1_orders_open_at', :with => '2012-12-01 12:00:02'
|
||||
fill_in 'order_cycle_set_collection_attributes_1_orders_close_at', :with => '2012-12-01 12:00:03'
|
||||
fill_in 'order_cycle_set_collection_attributes_2_orders_open_at', :with => '2012-12-01 12:00:04'
|
||||
fill_in 'order_cycle_set_collection_attributes_2_orders_close_at', :with => '2012-12-01 12:00:05'
|
||||
click_button 'Update'
|
||||
|
||||
# Then my times should have been saved
|
||||
flash_message.should == 'Order cycles have been updated.'
|
||||
OrderCycle.all.map { |oc| oc.orders_open_at.sec }.should == [0, 2, 4]
|
||||
OrderCycle.all.map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5]
|
||||
end
|
||||
|
||||
end
|
||||
394
spec/javascripts/unit/order_cycle_spec.js.coffee
Normal file
394
spec/javascripts/unit/order_cycle_spec.js.coffee
Normal file
@@ -0,0 +1,394 @@
|
||||
describe 'OrderCycle controllers', ->
|
||||
|
||||
describe 'AdminCreateOrderCycleCtrl', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
OrderCycle = null
|
||||
Enterprise = null
|
||||
|
||||
beforeEach ->
|
||||
scope = {}
|
||||
event =
|
||||
preventDefault: jasmine.createSpy('preventDefault')
|
||||
OrderCycle =
|
||||
order_cycle: 'my order cycle'
|
||||
exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').andReturn('variants selected')
|
||||
productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').andReturn('product supplied')
|
||||
variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied')
|
||||
toggleProducts: jasmine.createSpy('toggleProducts')
|
||||
addSupplier: jasmine.createSpy('addSupplier')
|
||||
addDistributor: jasmine.createSpy('addDistributor')
|
||||
create: jasmine.createSpy('create')
|
||||
Enterprise =
|
||||
index: jasmine.createSpy('index').andReturn('enterprises list')
|
||||
supplied_products: 'supplied products'
|
||||
totalVariants: jasmine.createSpy('totalVariants').andReturn('variants total')
|
||||
|
||||
module('order_cycle')
|
||||
inject ($controller) ->
|
||||
ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise}
|
||||
|
||||
|
||||
it 'Loads enterprises and supplied products', ->
|
||||
expect(Enterprise.index).toHaveBeenCalled()
|
||||
expect(scope.enterprises).toEqual('enterprises list')
|
||||
expect(scope.supplied_products).toEqual('supplied products')
|
||||
|
||||
it 'Loads order cycles', ->
|
||||
expect(scope.order_cycle).toEqual('my order cycle')
|
||||
|
||||
it 'Delegates exchangeSelectedVariants to OrderCycle', ->
|
||||
expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected')
|
||||
expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange')
|
||||
|
||||
it 'Delegates enterpriseTotalVariants to Enterprise', ->
|
||||
expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total')
|
||||
expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise')
|
||||
|
||||
it 'Delegates productSuppliedToOrderCycle to OrderCycle', ->
|
||||
expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied')
|
||||
expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product')
|
||||
|
||||
it 'Delegates variantSuppliedToOrderCycle to OrderCycle', ->
|
||||
expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied')
|
||||
expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant')
|
||||
|
||||
it 'Delegates toggleProducts to OrderCycle', ->
|
||||
scope.toggleProducts(event, 'exchange')
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.toggleProducts).toHaveBeenCalledWith('exchange')
|
||||
|
||||
it 'Adds order cycle suppliers', ->
|
||||
scope.new_supplier_id = 'new supplier id'
|
||||
scope.addSupplier(event)
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id')
|
||||
|
||||
it 'Adds order cycle distributors', ->
|
||||
scope.new_distributor_id = 'new distributor id'
|
||||
scope.addDistributor(event)
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id')
|
||||
|
||||
it 'Submits the order cycle via OrderCycle create', ->
|
||||
scope.submit()
|
||||
expect(OrderCycle.create).toHaveBeenCalled()
|
||||
|
||||
describe 'AdminEditOrderCycleCtrl', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
location = null
|
||||
OrderCycle = null
|
||||
Enterprise = null
|
||||
|
||||
beforeEach ->
|
||||
scope = {}
|
||||
event =
|
||||
preventDefault: jasmine.createSpy('preventDefault')
|
||||
location =
|
||||
absUrl: ->
|
||||
'example.com/admin/order_cycles/27/edit'
|
||||
OrderCycle =
|
||||
load: jasmine.createSpy('load')
|
||||
exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').andReturn('variants selected')
|
||||
productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').andReturn('product supplied')
|
||||
variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied')
|
||||
toggleProducts: jasmine.createSpy('toggleProducts')
|
||||
addSupplier: jasmine.createSpy('addSupplier')
|
||||
addDistributor: jasmine.createSpy('addDistributor')
|
||||
update: jasmine.createSpy('update')
|
||||
Enterprise =
|
||||
index: jasmine.createSpy('index').andReturn('enterprises list')
|
||||
supplied_products: 'supplied products'
|
||||
totalVariants: jasmine.createSpy('totalVariants').andReturn('variants total')
|
||||
|
||||
module('order_cycle')
|
||||
inject ($controller) ->
|
||||
ctrl = $controller 'AdminEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise}
|
||||
|
||||
it 'Loads enterprises and supplied products', ->
|
||||
expect(Enterprise.index).toHaveBeenCalled()
|
||||
expect(scope.enterprises).toEqual('enterprises list')
|
||||
expect(scope.supplied_products).toEqual('supplied products')
|
||||
|
||||
it 'Loads order cycles', ->
|
||||
expect(OrderCycle.load).toHaveBeenCalledWith('27')
|
||||
|
||||
it 'Delegates exchangeSelectedVariants to OrderCycle', ->
|
||||
expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected')
|
||||
expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange')
|
||||
|
||||
it 'Delegates totalVariants to Enterprise', ->
|
||||
expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total')
|
||||
expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise')
|
||||
|
||||
it 'Delegates productSuppliedToOrderCycle to OrderCycle', ->
|
||||
expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied')
|
||||
expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product')
|
||||
|
||||
it 'Delegates variantSuppliedToOrderCycle to OrderCycle', ->
|
||||
expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied')
|
||||
expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant')
|
||||
|
||||
it 'Delegates toggleProducts to OrderCycle', ->
|
||||
scope.toggleProducts(event, 'exchange')
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.toggleProducts).toHaveBeenCalledWith('exchange')
|
||||
|
||||
it 'Adds order cycle suppliers', ->
|
||||
scope.new_supplier_id = 'new supplier id'
|
||||
scope.addSupplier(event)
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id')
|
||||
|
||||
it 'Adds order cycle distributors', ->
|
||||
scope.new_distributor_id = 'new distributor id'
|
||||
scope.addDistributor(event)
|
||||
expect(event.preventDefault).toHaveBeenCalled()
|
||||
expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id')
|
||||
|
||||
it 'Submits the order cycle via OrderCycle update', ->
|
||||
scope.submit()
|
||||
expect(OrderCycle.update).toHaveBeenCalled()
|
||||
|
||||
|
||||
describe 'OrderCycle services', ->
|
||||
describe 'Enterprise service', ->
|
||||
$httpBackend = null
|
||||
Enterprise = null
|
||||
|
||||
beforeEach ->
|
||||
module 'order_cycle'
|
||||
inject ($injector, _$httpBackend_)->
|
||||
Enterprise = $injector.get('Enterprise')
|
||||
$httpBackend = _$httpBackend_
|
||||
$httpBackend.whenGET('/admin/enterprises.json').respond [
|
||||
{id: 1, name: 'One', supplied_products: [1, 2]}
|
||||
{id: 2, name: 'Two', supplied_products: [3, 4]}
|
||||
{id: 3, name: 'Three', supplied_products: [5, 6]}
|
||||
]
|
||||
|
||||
it 'loads enterprises as a hash', ->
|
||||
enterprises = Enterprise.index()
|
||||
$httpBackend.flush()
|
||||
expect(enterprises).toEqual
|
||||
1: new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2]})
|
||||
2: new Enterprise.Enterprise({id: 2, name: 'Two', supplied_products: [3, 4]})
|
||||
3: new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6]})
|
||||
|
||||
it 'collates all supplied products', ->
|
||||
enterprises = Enterprise.index()
|
||||
$httpBackend.flush()
|
||||
expect(Enterprise.supplied_products).toEqual [1, 2, 3, 4, 5, 6]
|
||||
|
||||
it 'counts total variants supplied by an enterprise', ->
|
||||
enterprise =
|
||||
supplied_products: [
|
||||
{variants: []},
|
||||
{variants: []},
|
||||
{variants: [{}, {}, {}]}
|
||||
]
|
||||
|
||||
expect(Enterprise.totalVariants(enterprise)).toEqual(5)
|
||||
|
||||
|
||||
describe 'OrderCycle service', ->
|
||||
OrderCycle = null
|
||||
$httpBackend = null
|
||||
$window = null
|
||||
|
||||
beforeEach ->
|
||||
$window = {navigator: {userAgent: 'foo'}}
|
||||
|
||||
module 'order_cycle', ($provide)->
|
||||
$provide.value('$window', $window)
|
||||
null
|
||||
|
||||
inject ($injector, _$httpBackend_)->
|
||||
OrderCycle = $injector.get('OrderCycle')
|
||||
$httpBackend = _$httpBackend_
|
||||
$httpBackend.whenGET('/admin/order_cycles/123.json').respond
|
||||
id: 123
|
||||
name: 'Test Order Cycle'
|
||||
coordinator_id: 456
|
||||
exchanges: [
|
||||
{sender_id: 1, receiver_id: 456}
|
||||
{sender_id: 456, receiver_id: 2}
|
||||
]
|
||||
|
||||
it 'initialises order cycle', ->
|
||||
expect(OrderCycle.order_cycle).toEqual
|
||||
incoming_exchanges: []
|
||||
outgoing_exchanges: []
|
||||
|
||||
it 'counts selected variants in an exchange', ->
|
||||
result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}})
|
||||
expect(result).toEqual(2)
|
||||
|
||||
describe 'toggling products', ->
|
||||
exchange = null
|
||||
|
||||
beforeEach ->
|
||||
exchange = {}
|
||||
|
||||
it 'sets a blank value to true', ->
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
expect(exchange.showProducts).toEqual(true)
|
||||
|
||||
it 'sets a true value to false', ->
|
||||
exchange.showProducts = true
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
expect(exchange.showProducts).toEqual(false)
|
||||
|
||||
it 'sets a false value to true', ->
|
||||
exchange.showProducts = false
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
expect(exchange.showProducts).toEqual(true)
|
||||
|
||||
describe 'adding suppliers', ->
|
||||
exchange = null
|
||||
|
||||
it 'adds the supplier to incoming exchanges', ->
|
||||
OrderCycle.addSupplier('123')
|
||||
expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [
|
||||
{enterprise_id: '123', active: true, variants: {}}
|
||||
]
|
||||
|
||||
describe 'adding distributors', ->
|
||||
exchange = null
|
||||
|
||||
it 'adds the distributor to outgoing exchanges', ->
|
||||
OrderCycle.addDistributor('123')
|
||||
expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [
|
||||
{enterprise_id: '123', active: true, variants: {}}
|
||||
]
|
||||
|
||||
describe 'fetching all variants supplied on incoming exchanges', ->
|
||||
it 'collects variants from incoming exchanges', ->
|
||||
OrderCycle.order_cycle.incoming_exchanges = [
|
||||
{variants: {1: true, 2: false}}
|
||||
{variants: {3: false, 4: true}}
|
||||
{variants: {5: true, 6: false}}
|
||||
]
|
||||
expect(OrderCycle.incomingExchangesVariants()).toEqual [1, 4, 5]
|
||||
|
||||
describe 'checking whether a product is supplied to the order cycle', ->
|
||||
product_master_present = product_variant_present = product_master_absent = product_variant_absent = null
|
||||
|
||||
beforeEach ->
|
||||
product_master_present =
|
||||
name: "Linseed (500g)"
|
||||
master_id: 1
|
||||
variants: []
|
||||
product_variant_present =
|
||||
name: "Linseed (500g)"
|
||||
master_id: 2
|
||||
variants: [{id: 3}, {id: 4}]
|
||||
product_master_absent =
|
||||
name: "Linseed (500g)"
|
||||
master_id: 5
|
||||
variants: []
|
||||
product_variant_absent =
|
||||
name: "Linseed (500g)"
|
||||
master_id: 6
|
||||
variants: [{id: 7}, {id: 8}]
|
||||
|
||||
spyOn(OrderCycle, 'incomingExchangesVariants').andReturn([1, 3])
|
||||
|
||||
it 'returns true for products whose master is supplied', ->
|
||||
expect(OrderCycle.productSuppliedToOrderCycle(product_master_present)).toBeTruthy()
|
||||
|
||||
it 'returns true for products for whom a variant is supplied', ->
|
||||
expect(OrderCycle.productSuppliedToOrderCycle(product_variant_present)).toBeTruthy()
|
||||
|
||||
it 'returns false for products whose master is not supplied', ->
|
||||
expect(OrderCycle.productSuppliedToOrderCycle(product_master_absent)).toBeFalsy()
|
||||
|
||||
it 'returns false for products whose variants are not supplied', ->
|
||||
expect(OrderCycle.productSuppliedToOrderCycle(product_variant_absent)).toBeFalsy()
|
||||
|
||||
|
||||
describe 'checking whether a variant is supplied to the order cycle', ->
|
||||
beforeEach ->
|
||||
spyOn(OrderCycle, 'incomingExchangesVariants').andReturn([1, 3])
|
||||
|
||||
it 'returns true for variants that are supplied', ->
|
||||
expect(OrderCycle.variantSuppliedToOrderCycle({id: 1})).toBeTruthy()
|
||||
|
||||
it 'returns false for variants that are not supplied', ->
|
||||
expect(OrderCycle.variantSuppliedToOrderCycle({id: 999})).toBeFalsy()
|
||||
|
||||
|
||||
describe 'loading an order cycle', ->
|
||||
beforeEach ->
|
||||
OrderCycle.load('123')
|
||||
$httpBackend.flush()
|
||||
|
||||
it 'loads basic fields', ->
|
||||
expect(OrderCycle.order_cycle.id).toEqual(123)
|
||||
expect(OrderCycle.order_cycle.name).toEqual('Test Order Cycle')
|
||||
expect(OrderCycle.order_cycle.coordinator_id).toEqual(456)
|
||||
|
||||
it 'splits exchanges into incoming and outgoing', ->
|
||||
expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [
|
||||
sender_id: 1
|
||||
enterprise_id: 1
|
||||
active: true
|
||||
]
|
||||
|
||||
expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [
|
||||
receiver_id: 2
|
||||
enterprise_id: 2
|
||||
active: true
|
||||
]
|
||||
|
||||
it 'removes original exchanges array', ->
|
||||
expect(OrderCycle.order_cycle.exchanges).toEqual(undefined)
|
||||
|
||||
describe 'creating an order cycle', ->
|
||||
it 'redirects to the order cycles page on success', ->
|
||||
OrderCycle.order_cycle = 'this is the order cycle'
|
||||
spyOn(OrderCycle, 'removeInactiveExchanges')
|
||||
$httpBackend.expectPOST('/admin/order_cycles.json', {
|
||||
order_cycle: 'this is the order cycle'
|
||||
}).respond {success: true}
|
||||
|
||||
OrderCycle.create()
|
||||
$httpBackend.flush()
|
||||
expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled()
|
||||
expect($window.location).toEqual('/admin/order_cycles')
|
||||
|
||||
it 'does not redirect on error', ->
|
||||
OrderCycle.order_cycle = 'this is the order cycle'
|
||||
spyOn(OrderCycle, 'removeInactiveExchanges')
|
||||
$httpBackend.expectPOST('/admin/order_cycles.json', {
|
||||
order_cycle: 'this is the order cycle'
|
||||
}).respond {success: false}
|
||||
|
||||
OrderCycle.create()
|
||||
$httpBackend.flush()
|
||||
expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled()
|
||||
expect($window.location).toEqual(undefined)
|
||||
|
||||
it 'removes inactive exchanges', ->
|
||||
OrderCycle.order_cycle =
|
||||
incoming_exchanges: [
|
||||
{enterprise_id: "1", active: false}
|
||||
{enterprise_id: "2", active: true}
|
||||
{enterprise_id: "3", active: false}
|
||||
]
|
||||
outgoing_exchanges: [
|
||||
{enterprise_id: "4", active: true}
|
||||
{enterprise_id: "5", active: false}
|
||||
{enterprise_id: "6", active: true}
|
||||
]
|
||||
OrderCycle.removeInactiveExchanges()
|
||||
expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [
|
||||
{enterprise_id: "2", active: true}
|
||||
]
|
||||
expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [
|
||||
{enterprise_id: "4", active: true}
|
||||
{enterprise_id: "6", active: true}
|
||||
]
|
||||
@@ -1,56 +0,0 @@
|
||||
require 'open_food_web/order_cycle_form_applicator'
|
||||
|
||||
module OpenFoodWeb
|
||||
describe OrderCycleFormApplicator do
|
||||
it "creates new exchanges for incoming_exchanges" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
|
||||
oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [{:enterprise_id => supplier_id}])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_exists?).and_return(false)
|
||||
applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id)
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "updates existing exchanges for incoming_exchanges" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
|
||||
oc = double(:order_cycle,
|
||||
:coordinator_id => coordinator_id,
|
||||
:exchanges => [double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id)],
|
||||
:incoming_exchanges => [{:enterprise_id => supplier_id}])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_exists?).and_return(true)
|
||||
applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id)
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "removes exchanges that are no longer present in incoming_exchanges" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
exchange = double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id)
|
||||
|
||||
oc = double(:order_cycle,
|
||||
:coordinator_id => coordinator_id,
|
||||
:exchanges => [exchange],
|
||||
:incoming_exchanges => [])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
applicator.untouched_exchanges.should == [exchange]
|
||||
end
|
||||
end
|
||||
end
|
||||
168
spec/lib/open_food_web/order_cycle_form_applicator_spec.rb
Normal file
168
spec/lib/open_food_web/order_cycle_form_applicator_spec.rb
Normal file
@@ -0,0 +1,168 @@
|
||||
require 'open_food_web/order_cycle_form_applicator'
|
||||
|
||||
module OpenFoodWeb
|
||||
describe OrderCycleFormApplicator do
|
||||
context "unit specs" do
|
||||
it "creates new exchanges for incoming_exchanges" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
|
||||
incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}}
|
||||
|
||||
oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => [])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3])
|
||||
applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(false)
|
||||
applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3]})
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "creates new exchanges for outgoing_exchanges" do
|
||||
coordinator_id = 123
|
||||
distributor_id = 456
|
||||
|
||||
outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}
|
||||
|
||||
oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3])
|
||||
applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(false)
|
||||
applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'})
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "updates existing exchanges for incoming_exchanges" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
|
||||
incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}}
|
||||
|
||||
oc = double(:order_cycle,
|
||||
:coordinator_id => coordinator_id,
|
||||
:exchanges => [double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id)],
|
||||
:incoming_exchanges => [incoming_exchange],
|
||||
:outgoing_exchanges => [])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3])
|
||||
applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(true)
|
||||
applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3]})
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "updates existing exchanges for outgoing_exchanges" do
|
||||
coordinator_id = 123
|
||||
distributor_id = 456
|
||||
|
||||
outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}
|
||||
|
||||
oc = double(:order_cycle,
|
||||
:coordinator_id => coordinator_id,
|
||||
:exchanges => [double(:exchange, :sender_id => coordinator_id, :receiver_id => distributor_id)],
|
||||
:incoming_exchanges => [],
|
||||
:outgoing_exchanges => [outgoing_exchange])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3])
|
||||
applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(true)
|
||||
applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'})
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
end
|
||||
|
||||
it "removes exchanges that are no longer present" do
|
||||
coordinator_id = 123
|
||||
supplier_id = 456
|
||||
exchange = double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id)
|
||||
|
||||
oc = double(:order_cycle,
|
||||
:coordinator_id => coordinator_id,
|
||||
:exchanges => [exchange],
|
||||
:incoming_exchanges => [],
|
||||
:outgoing_exchanges => [])
|
||||
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.should_receive(:destroy_untouched_exchanges)
|
||||
|
||||
applicator.go!
|
||||
applicator.send(:untouched_exchanges).should == [exchange]
|
||||
end
|
||||
|
||||
it "converts exchange variant ids hash to an array of ids" do
|
||||
applicator = OrderCycleFormApplicator.new(nil)
|
||||
|
||||
applicator.send(:exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}).should == [1, 3]
|
||||
end
|
||||
end
|
||||
|
||||
context "integration specs" do
|
||||
before(:all) do
|
||||
require 'spec_helper'
|
||||
end
|
||||
|
||||
it "checks whether exchanges exist" do
|
||||
oc = FactoryGirl.create(:simple_order_cycle)
|
||||
exchange = FactoryGirl.create(:exchange, order_cycle: oc)
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
|
||||
applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id).should be_true
|
||||
applicator.send(:exchange_exists?, exchange.receiver_id, exchange.sender_id).should be_false
|
||||
applicator.send(:exchange_exists?, exchange.sender_id, 999).should be_false
|
||||
applicator.send(:exchange_exists?, 999, exchange.receiver_id).should be_false
|
||||
applicator.send(:exchange_exists?, 999, 888).should be_false
|
||||
end
|
||||
|
||||
it "adds exchanges" do
|
||||
oc = FactoryGirl.create(:simple_order_cycle)
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
sender = FactoryGirl.create(:enterprise)
|
||||
receiver = FactoryGirl.create(:enterprise)
|
||||
variant1 = FactoryGirl.create(:variant)
|
||||
variant2 = FactoryGirl.create(:variant)
|
||||
|
||||
applicator.send(:touched_exchanges=, [])
|
||||
applicator.send(:add_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant2.id]})
|
||||
|
||||
exchange = Exchange.last
|
||||
exchange.sender.should == sender
|
||||
exchange.receiver.should == receiver
|
||||
exchange.variants.sort.should == [variant1, variant2].sort
|
||||
|
||||
applicator.send(:touched_exchanges).should == [exchange]
|
||||
end
|
||||
|
||||
it "updates exchanges" do
|
||||
oc = FactoryGirl.create(:simple_order_cycle)
|
||||
applicator = OrderCycleFormApplicator.new(oc)
|
||||
sender = FactoryGirl.create(:enterprise)
|
||||
receiver = FactoryGirl.create(:enterprise)
|
||||
variant1 = FactoryGirl.create(:variant)
|
||||
variant2 = FactoryGirl.create(:variant)
|
||||
variant3 = FactoryGirl.create(:variant)
|
||||
|
||||
exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, variant_ids: [variant1, variant2])
|
||||
|
||||
applicator.send(:touched_exchanges=, [])
|
||||
applicator.send(:update_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant3.id]})
|
||||
|
||||
exchange.reload
|
||||
exchange.variant_ids.should == [variant1.id, variant3.id]
|
||||
applicator.send(:touched_exchanges).should == [exchange]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,82 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature %q{
|
||||
As an administrator
|
||||
I want to manage order cycles
|
||||
}, js: true do
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
scenario "listing order cycles" do
|
||||
# Given an order cycle
|
||||
oc = create(:order_cycle)
|
||||
|
||||
# When I go to the admin order cycles page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
|
||||
# Then I should see the basic fields
|
||||
page.should have_selector 'a', text: oc.name
|
||||
|
||||
page.should have_selector "input[value='#{oc.orders_open_at}']"
|
||||
page.should have_selector "input[value='#{oc.orders_close_at}']"
|
||||
page.should have_content oc.coordinator.name
|
||||
|
||||
# And I should see the suppliers and distributors
|
||||
oc.suppliers.each { |s| page.should have_content s.name }
|
||||
oc.distributors.each { |d| page.should have_content d.name }
|
||||
|
||||
# And I should see a thumbnail image for each product
|
||||
all('td.products img').count.should == 2
|
||||
end
|
||||
|
||||
scenario "creating an order cycle" do
|
||||
# Given a coordinating enterprise
|
||||
create(:enterprise, name: 'My coordinator')
|
||||
|
||||
# When I go to the new order cycle page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
click_link 'New Order Cycle'
|
||||
|
||||
# And I fill in the basic fields and click Create
|
||||
fill_in 'order_cycle_name', with: 'Plums & Avos'
|
||||
fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00'
|
||||
fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00'
|
||||
select 'My coordinator', from: 'order_cycle_coordinator_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='2012-11-06 06:00:00 UTC']"
|
||||
page.should have_selector "input[value='2012-11-13 17:00:00 UTC']"
|
||||
page.should have_content 'My coordinator'
|
||||
end
|
||||
|
||||
scenario "updating many order cycle opening/closing times at once" do
|
||||
# Given three order cycles
|
||||
3.times { create(:order_cycle) }
|
||||
|
||||
# When I go to the order cycles page
|
||||
login_to_admin_section
|
||||
click_link 'Order Cycles'
|
||||
|
||||
# And I fill in some new opening/closing times and save them
|
||||
fill_in 'order_cycle_set_collection_attributes_0_orders_open_at', :with => '2012-12-01 12:00:00'
|
||||
fill_in 'order_cycle_set_collection_attributes_0_orders_close_at', :with => '2012-12-01 12:00:01'
|
||||
fill_in 'order_cycle_set_collection_attributes_1_orders_open_at', :with => '2012-12-01 12:00:02'
|
||||
fill_in 'order_cycle_set_collection_attributes_1_orders_close_at', :with => '2012-12-01 12:00:03'
|
||||
fill_in 'order_cycle_set_collection_attributes_2_orders_open_at', :with => '2012-12-01 12:00:04'
|
||||
fill_in 'order_cycle_set_collection_attributes_2_orders_close_at', :with => '2012-12-01 12:00:05'
|
||||
click_button 'Update'
|
||||
|
||||
# Then my times should have been saved
|
||||
flash_message.should == 'Order cycles have been updated.'
|
||||
OrderCycle.all.map { |oc| oc.orders_open_at.sec }.should == [0, 2, 4]
|
||||
OrderCycle.all.map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5]
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user