diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin.js.coffee
index 76154e9e20..edebc019e5 100644
--- a/app/assets/javascripts/admin/admin.js.coffee
+++ b/app/assets/javascripts/admin/admin.js.coffee
@@ -1,3 +1,3 @@
-window.Admin = angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown"]).config ($httpProvider) ->
+angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js
index 99c936c4d7..3fdd45585a 100644
--- a/app/assets/javascripts/admin/all.js
+++ b/app/assets/javascripts/admin/all.js
@@ -16,5 +16,6 @@
//= require admin/spree_auth
//= require admin/spree_promo
//= require ./admin
+//= require ./products/products
//= require_tree .
diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee
index 7a1edcbca6..9ba34790a8 100644
--- a/app/assets/javascripts/admin/bulk_order_management.js.coffee
+++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee
@@ -1,4 +1,4 @@
-Admin.controller "AdminOrderMgmtCtrl", [
+angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [
"$scope", "$http", "dataFetcher", "blankOption", "pendingChanges"
($scope, $http, dataFetcher, blankOption, pendingChanges) ->
diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee
index 4df57d6689..8d50a3dc1f 100644
--- a/app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -1,4 +1,4 @@
-Admin.controller "AdminProductEditCtrl", [
+angular.module("ofn.admin").controller "AdminProductEditCtrl", [
"$scope", "$timeout", "$http", "dataFetcher", "DirtyProducts"
($scope, $timeout, $http, dataFetcher, DirtyProducts) ->
$scope.updateStatusMessage =
diff --git a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee
index c66e79c046..665753a522 100644
--- a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee
+++ b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee
@@ -1,4 +1,4 @@
-Admin.controller "AdminEnterpriseRelationshipsCtrl", ($scope, EnterpriseRelationships, Enterprises) ->
+angular.module("ofn.admin").controller "AdminEnterpriseRelationshipsCtrl", ($scope, EnterpriseRelationships, Enterprises) ->
$scope.EnterpriseRelationships = EnterpriseRelationships
$scope.Enterprises = Enterprises
diff --git a/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee
new file mode 100644
index 0000000000..ad72ff3529
--- /dev/null
+++ b/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee
@@ -0,0 +1,5 @@
+angular.module("ofn.admin").controller "enterprisesDashboardCtrl", [
+ "$scope"
+ ($scope) ->
+ $scope.activeTab = "hubs"
+]
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee b/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee
index e0e0511f0c..2c8dba4baf 100644
--- a/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee
+++ b/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnConfirmLinkPath", (ofnConfirmHandler) ->
+angular.module("ofn.admin").directive "ofnConfirmLinkPath", (ofnConfirmHandler) ->
restrict: "A"
scope:
path: "@ofnConfirmLinkPath"
diff --git a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee b/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee
index aba2cd919e..a0b5272981 100644
--- a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee
+++ b/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) ->
+angular.module("ofn.admin").directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) ->
restrict: "A"
link: (scope, element, attrs) ->
handler = ofnConfirmHandler scope, -> scope.fetchOrders()
diff --git a/app/assets/javascripts/admin/directives/date_picker.js.coffee b/app/assets/javascripts/admin/directives/date_picker.js.coffee
index 3a07bfd3f5..6b5cac3e1b 100644
--- a/app/assets/javascripts/admin/directives/date_picker.js.coffee
+++ b/app/assets/javascripts/admin/directives/date_picker.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "datepicker", ->
+angular.module("ofn.admin").directive "datepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datepicker
diff --git a/app/assets/javascripts/admin/directives/datetime_picker.js.coffee b/app/assets/javascripts/admin/directives/datetime_picker.js.coffee
index 232d958b99..b0d91e538a 100644
--- a/app/assets/javascripts/admin/directives/datetime_picker.js.coffee
+++ b/app/assets/javascripts/admin/directives/datetime_picker.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "datetimepicker", ->
+angular.module("ofn.admin").directive "datetimepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datetimepicker
diff --git a/app/assets/javascripts/admin/directives/decimal.js.coffee b/app/assets/javascripts/admin/directives/decimal.js.coffee
index 8077f90c06..f4bed8e371 100644
--- a/app/assets/javascripts/admin/directives/decimal.js.coffee
+++ b/app/assets/javascripts/admin/directives/decimal.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnDecimal", ->
+angular.module("ofn.admin").directive "ofnDecimal", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
numRegExp = /^\d+(\.\d+)?$/
diff --git a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee
index a71aa42997..c83d7fdc0f 100644
--- a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee
+++ b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnLineItemUpdAttr", [
+angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [
"switchClass", "pendingChanges"
(switchClass, pendingChanges) ->
require: "ngModel"
diff --git a/app/assets/javascripts/admin/directives/select2_min_search.js.coffee b/app/assets/javascripts/admin/directives/select2_min_search.js.coffee
index c33beb710e..1d55d886aa 100644
--- a/app/assets/javascripts/admin/directives/select2_min_search.js.coffee
+++ b/app/assets/javascripts/admin/directives/select2_min_search.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnSelect2MinSearch", ->
+angular.module("ofn.admin").directive "ofnSelect2MinSearch", ->
require: 'ngModel'
link: (scope, element, attrs, ngModel) ->
element.select2
diff --git a/app/assets/javascripts/admin/directives/taxon_autocomplete.js.coffee b/app/assets/javascripts/admin/directives/taxon_autocomplete.js.coffee
index 0705d04a23..e5713274ef 100644
--- a/app/assets/javascripts/admin/directives/taxon_autocomplete.js.coffee
+++ b/app/assets/javascripts/admin/directives/taxon_autocomplete.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnTaxonAutocomplete", (Taxons) ->
+angular.module("ofn.admin").directive "ofnTaxonAutocomplete", (Taxons) ->
# Adapted from Spree's existing taxon autocompletion
require: "ngModel"
link: (scope,element,attrs,ngModel) ->
diff --git a/app/assets/javascripts/admin/directives/toggle_column.js.coffee b/app/assets/javascripts/admin/directives/toggle_column.js.coffee
index 2337a0fb58..1b8487eeb1 100644
--- a/app/assets/javascripts/admin/directives/toggle_column.js.coffee
+++ b/app/assets/javascripts/admin/directives/toggle_column.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnToggleColumn", ->
+angular.module("ofn.admin").directive "ofnToggleColumn", ->
link: (scope, element, attrs) ->
element.addClass "selected" if scope.column.visible
element.click "click", ->
diff --git a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee
index 8c07490359..879942d698 100644
--- a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee
+++ b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnToggleVariants", ->
+angular.module("ofn.admin").directive "ofnToggleVariants", ->
link: (scope, element, attrs) ->
if scope.displayProperties[scope.product.id].showVariants
element.removeClass "icon-chevron-right"
diff --git a/app/assets/javascripts/admin/directives/track_product.js.coffee b/app/assets/javascripts/admin/directives/track_product.js.coffee
index 3667794712..a4ea3865c0 100644
--- a/app/assets/javascripts/admin/directives/track_product.js.coffee
+++ b/app/assets/javascripts/admin/directives/track_product.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnTrackProduct", ["DirtyProducts", (DirtyProducts) ->
+angular.module("ofn.admin").directive "ofnTrackProduct", ["DirtyProducts", (DirtyProducts) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
diff --git a/app/assets/javascripts/admin/directives/track_variant.js.coffee b/app/assets/javascripts/admin/directives/track_variant.js.coffee
index 189d5043fe..ab17ebcfa5 100644
--- a/app/assets/javascripts/admin/directives/track_variant.js.coffee
+++ b/app/assets/javascripts/admin/directives/track_variant.js.coffee
@@ -1,4 +1,4 @@
-Admin.directive "ofnTrackVariant", ["DirtyProducts", (DirtyProducts) ->
+angular.module("ofn.admin").directive "ofnTrackVariant", ["DirtyProducts", (DirtyProducts) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
diff --git a/app/assets/javascripts/admin/filters/range_array.js.coffee b/app/assets/javascripts/admin/filters/range_array.js.coffee
index 5056476f1b..be4a020442 100644
--- a/app/assets/javascripts/admin/filters/range_array.js.coffee
+++ b/app/assets/javascripts/admin/filters/range_array.js.coffee
@@ -1,4 +1,4 @@
-Admin.filter "rangeArray", ->
+angular.module("ofn.admin").filter "rangeArray", ->
return (input,start,end) ->
input.push(i) for i in [start..end]
input
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/filters/select_filter.js.coffee b/app/assets/javascripts/admin/filters/select_filter.js.coffee
index 562be954b2..2b03abd613 100644
--- a/app/assets/javascripts/admin/filters/select_filter.js.coffee
+++ b/app/assets/javascripts/admin/filters/select_filter.js.coffee
@@ -1,4 +1,4 @@
-Admin.filter "selectFilter", (blankOption) ->
+angular.module("ofn.admin").filter "selectFilter", (blankOption) ->
return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) &&
diff --git a/app/assets/javascripts/admin/filters/variant_filter.js.coffee b/app/assets/javascripts/admin/filters/variant_filter.js.coffee
index 039c4cc46f..ddbc0f2711 100644
--- a/app/assets/javascripts/admin/filters/variant_filter.js.coffee
+++ b/app/assets/javascripts/admin/filters/variant_filter.js.coffee
@@ -1,4 +1,4 @@
-Admin.filter "variantFilter", ->
+angular.module("ofn.admin").filter "variantFilter", ->
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
diff --git a/app/assets/javascripts/admin/products/products.js.coffee b/app/assets/javascripts/admin/products/products.js.coffee
new file mode 100644
index 0000000000..e922d40a34
--- /dev/null
+++ b/app/assets/javascripts/admin/products/products.js.coffee
@@ -0,0 +1 @@
+angular.module("admin.products", [])
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/products/units_controller.js.coffee b/app/assets/javascripts/admin/products/units_controller.js.coffee
new file mode 100644
index 0000000000..9070648e21
--- /dev/null
+++ b/app/assets/javascripts/admin/products/units_controller.js.coffee
@@ -0,0 +1,46 @@
+angular.module("admin.products")
+ .controller "unitsCtrl", ($scope) ->
+ $scope.product = { master: {} }
+
+ $scope.$watch ->
+ $scope.product.variant_unit_with_scale
+ , ->
+ if $scope.product.variant_unit_with_scale
+ match = $scope.product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/)
+ if match
+ $scope.product.variant_unit = match[1]
+ $scope.product.variant_unit_scale = parseFloat(match[2])
+ else
+ $scope.product.variant_unit = $scope.product.variant_unit_with_scale
+ $scope.product.variant_unit_scale = null
+ else
+ $scope.product.variant_unit = $scope.product.variant_unit_scale = null
+
+ $scope.$watch ->
+ $scope.product.master.unit_value_with_description
+ , ->
+ if $scope.product.master.hasOwnProperty("unit_value_with_description")
+ match = $scope.product.master.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/)
+ if match
+ $scope.product.master.unit_value = parseFloat(match[1])
+ $scope.product.master.unit_value = null if isNaN($scope.product.master.unit_value)
+ $scope.product.master.unit_value *= $scope.product.variant_unit_scale if $scope.product.master.unit_value && $scope.product.variant_unit_scale
+ $scope.product.master.unit_description = match[3]
+
+ $scope.variant_unit_options = [
+ ["Weight (g)", "weight_1"],
+ ["Weight (kg)", "weight_1000"],
+ ["Weight (T)", "weight_1000000"],
+ ["Volume (mL)", "volume_0.001"],
+ ["Volume (L)", "volume_1"],
+ ["Volume (ML)", "volume_1000000"],
+ ["Items", "items"]
+ ]
+
+ $scope.hasVariants = (product) ->
+ Object.keys(product.variants).length > 0
+
+ $scope.hasUnit = (product) ->
+ product.variant_unit_with_scale?
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/services/blank_option.js.coffee b/app/assets/javascripts/admin/services/blank_option.js.coffee
index 9913a6511d..42ff69a77b 100644
--- a/app/assets/javascripts/admin/services/blank_option.js.coffee
+++ b/app/assets/javascripts/admin/services/blank_option.js.coffee
@@ -1,2 +1,2 @@
-Admin.value "blankOption", ->
+angular.module("ofn.admin").value "blankOption", ->
{ id: "0", name: "All" }
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/services/confirm_handler.js.coffee b/app/assets/javascripts/admin/services/confirm_handler.js.coffee
index 51b92848dc..88655e34e3 100644
--- a/app/assets/javascripts/admin/services/confirm_handler.js.coffee
+++ b/app/assets/javascripts/admin/services/confirm_handler.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "ofnConfirmHandler", (pendingChanges, $compile, $q) ->
+angular.module("ofn.admin").factory "ofnConfirmHandler", (pendingChanges, $compile, $q) ->
return (scope, callback) ->
template = "
Unsaved changes currently exist, save now or ignore?
"
dialogDiv = $compile(template)(scope)
diff --git a/app/assets/javascripts/admin/services/data_fetcher.js.coffee b/app/assets/javascripts/admin/services/data_fetcher.js.coffee
index 0227d26fc7..735e4cc6bb 100644
--- a/app/assets/javascripts/admin/services/data_fetcher.js.coffee
+++ b/app/assets/javascripts/admin/services/data_fetcher.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "dataFetcher", [
+angular.module("ofn.admin").factory "dataFetcher", [
"$http", "$q"
($http, $q) ->
return (dataLocation) ->
diff --git a/app/assets/javascripts/admin/services/data_submitter.js.coffee b/app/assets/javascripts/admin/services/data_submitter.js.coffee
index 6a08ca2d33..7d121ec645 100644
--- a/app/assets/javascripts/admin/services/data_submitter.js.coffee
+++ b/app/assets/javascripts/admin/services/data_submitter.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "dataSubmitter", [
+angular.module("ofn.admin").factory "dataSubmitter", [
"$http", "$q", "switchClass"
($http, $q, switchClass) ->
return (changeObj) ->
diff --git a/app/assets/javascripts/admin/services/dirty_products.js.coffee b/app/assets/javascripts/admin/services/dirty_products.js.coffee
index bb8614205e..16c10e1e34 100644
--- a/app/assets/javascripts/admin/services/dirty_products.js.coffee
+++ b/app/assets/javascripts/admin/services/dirty_products.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "DirtyProducts", ($parse) ->
+angular.module("ofn.admin").factory "DirtyProducts", ($parse) ->
# Temporary service to track changes in products on admin bulk product edit
dirtyProducts = {}
diff --git a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee
index 0469aa5637..e07b992112 100644
--- a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee
+++ b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory 'EnterpriseRelationships', ($http, enterprise_relationships) ->
+angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterprise_relationships) ->
new class EnterpriseRelationships
create_errors: ""
diff --git a/app/assets/javascripts/admin/services/enterprises.js.coffee b/app/assets/javascripts/admin/services/enterprises.js.coffee
index 199b6294c3..5b5fd6a009 100644
--- a/app/assets/javascripts/admin/services/enterprises.js.coffee
+++ b/app/assets/javascripts/admin/services/enterprises.js.coffee
@@ -1,4 +1,5 @@
-Admin.factory 'Enterprises', (enterprises) ->
+angular.module("ofn.admin").factory 'Enterprises', (my_enterprises, all_enterprises) ->
new class Enterprises
constructor: ->
- @enterprises = enterprises
+ @my_enterprises = my_enterprises
+ @all_enterprises = all_enterprises
diff --git a/app/assets/javascripts/admin/services/pending_changes.js.coffee b/app/assets/javascripts/admin/services/pending_changes.js.coffee
index df66900aa7..d72a4ac7bc 100644
--- a/app/assets/javascripts/admin/services/pending_changes.js.coffee
+++ b/app/assets/javascripts/admin/services/pending_changes.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "pendingChanges",[
+angular.module("ofn.admin").factory "pendingChanges",[
"dataSubmitter"
(dataSubmitter) ->
pendingChanges: {}
diff --git a/app/assets/javascripts/admin/services/switch_class.js.coffee b/app/assets/javascripts/admin/services/switch_class.js.coffee
index bdd61dac2e..e39c52d1f6 100644
--- a/app/assets/javascripts/admin/services/switch_class.js.coffee
+++ b/app/assets/javascripts/admin/services/switch_class.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "switchClass", [
+angular.module("ofn.admin").factory "switchClass", [
"$timeout"
($timeout) ->
return (element,classToAdd,removeClasses,timeout) ->
diff --git a/app/assets/javascripts/admin/services/taxons.js.coffee b/app/assets/javascripts/admin/services/taxons.js.coffee
index 5e17dca657..1779ec3518 100644
--- a/app/assets/javascripts/admin/services/taxons.js.coffee
+++ b/app/assets/javascripts/admin/services/taxons.js.coffee
@@ -1,4 +1,4 @@
-Admin.factory "Taxons", ($resource) ->
+angular.module("ofn.admin").factory "Taxons", ($resource) ->
resource = $resource "/admin/taxons/search"
return {
diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee
index 20d849f60c..2c07d069c4 100644
--- a/app/assets/javascripts/darkswarm/all.js.coffee
+++ b/app/assets/javascripts/darkswarm/all.js.coffee
@@ -5,6 +5,7 @@
#
#= require angular
#= require angular-cookies
+#= require angular-sanitize
#= require angular-resource
#= require ../shared/mm-foundation-tpls-0.2.0-SNAPSHOT
#= require ../shared/bindonce.min.js
diff --git a/app/assets/javascripts/darkswarm/controllers/checkout/accordion_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout/accordion_controller.js.coffee
index b6f533e751..38f41c6f6b 100644
--- a/app/assets/javascripts/darkswarm/controllers/checkout/accordion_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/checkout/accordion_controller.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.controller "AccordionCtrl", ($scope, storage) ->
+Darkswarm.controller "AccordionCtrl", ($scope, storage, $timeout) ->
$scope.accordion =
details: true
shipping: false
@@ -9,3 +9,8 @@ Darkswarm.controller "AccordionCtrl", ($scope, storage) ->
$scope.show = (name)->
$scope.accordion[name] = true
+ $timeout =>
+ if $scope.checkout.$valid
+ for k, v of $scope.accordion
+ $scope.accordion[k] = false
+
diff --git a/app/assets/javascripts/darkswarm/controllers/checkout/details_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout/details_controller.js.coffee
index c9d559a0d0..8153c24f92 100644
--- a/app/assets/javascripts/darkswarm/controllers/checkout/details_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/checkout/details_controller.js.coffee
@@ -1,5 +1,6 @@
Darkswarm.controller "DetailsCtrl", ($scope) ->
angular.extend(this, new FieldsetMixin($scope))
+
$scope.name = "details"
$scope.nextPanel = "billing"
@@ -11,11 +12,3 @@ Darkswarm.controller "DetailsCtrl", ($scope) ->
$scope.fullName = ->
[$scope.order.bill_address.firstname ? null,
$scope.order.bill_address.lastname ? null].join(" ").trim()
-
-
- #$scope.$watch ->
- #$scope.detailsValid()
- #, (valid)->
- #if valid
- #$scope.show("billing")
-
diff --git a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee
index 8fd47c49f8..850f58b33e 100644
--- a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee
@@ -1,3 +1,6 @@
-Darkswarm.controller "GroupsCtrl", ($scope, Groups) ->
+Darkswarm.controller "GroupsCtrl", ($scope, Groups, $anchorScroll, $rootScope) ->
$scope.Groups = Groups
$scope.order = 'position'
+
+ $rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) ->
+ $anchorScroll()
diff --git a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee
index 29e81ae861..555e49c539 100644
--- a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee
@@ -1,15 +1,17 @@
-Darkswarm.controller "OrderCycleCtrl", ($scope, $rootScope, OrderCycle, $timeout) ->
+Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) ->
$scope.order_cycle = OrderCycle.order_cycle
$scope.OrderCycle = OrderCycle
- $scope.changeOrderCycle = ->
- OrderCycle.push_order_cycle()
- $timeout ->
- $("#order_cycle_id").trigger("closeTrigger")
-
# Timeout forces this to be evaluated after everything is loaded
# This is a hack. We should probably write our own "popover" directive
# That takes an expression instead of a trigger, and binds to that
$timeout =>
if !$scope.OrderCycle.selected()
$("#order_cycle_id").trigger("openTrigger")
+
+
+Darkswarm.controller "OrderCycleChangeCtrl", ($scope, OrderCycle, Product, $timeout) ->
+ $scope.changeOrderCycle = ->
+ OrderCycle.push_order_cycle Product.update
+ $timeout ->
+ $("#order_cycle_id").trigger("closeTrigger")
diff --git a/app/assets/javascripts/darkswarm/controllers/producer_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/producer_node_controller.js.coffee
index 2f3bf1ce00..d46106a830 100644
--- a/app/assets/javascripts/darkswarm/controllers/producer_node_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/producer_node_controller.js.coffee
@@ -1,6 +1,6 @@
Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll) ->
$scope.toggle = ->
- HashNavigation.navigate $scope.producer.hash
+ HashNavigation.toggle $scope.producer.hash
$scope.open = ->
HashNavigation.active($scope.producer.hash)
diff --git a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
index 959423e3ca..f016d2e43f 100644
--- a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
@@ -8,5 +8,4 @@ Darkswarm.controller "ProductNodeCtrl", ($scope) ->
$scope.product.price
$scope.producer = $scope.product.supplier
-
$scope.hasVariants = $scope.product.variants.length > 0
diff --git a/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee
new file mode 100644
index 0000000000..92cf8dedfc
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee
@@ -0,0 +1,2 @@
+Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub) ->
+ $scope.CurrentHub = CurrentHub
diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
index fe128b963a..8d26384c14 100644
--- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee
+++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
@@ -5,6 +5,7 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource",
'infinite-scroll',
'angular-flash.service',
'templates',
+ 'ngSanitize',
'backstretch']).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
$httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
diff --git a/app/assets/javascripts/darkswarm/directives/modal.js.coffee b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
index 6f62c7cb45..c8ed5df4af 100644
--- a/app/assets/javascripts/darkswarm/directives/modal.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
@@ -8,11 +8,10 @@ Darkswarm.directive "ofnModal", ($modal)->
link: (scope, elem, attrs, ctrl, transclude)->
scope.title = attrs.title
contents = null
-
# We're using an isolate scope, which is a child of the original scope
# We have to compile the transclude against the original scope, not the isolate
transclude scope.$parent, (clone)->
contents = clone
elem.on "click", =>
- scope.modalInstance = $modal.open(controller: ctrl, template: contents)
+ scope.modalInstance = $modal.open(controller: ctrl, template: contents, scope: scope.$parent)
diff --git a/app/assets/javascripts/darkswarm/filters/strip_url.js.coffee b/app/assets/javascripts/darkswarm/filters/strip_url.js.coffee
new file mode 100644
index 0000000000..dcc86b600a
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/filters/strip_url.js.coffee
@@ -0,0 +1,6 @@
+Darkswarm.filter "stripUrl", ->
+ stripper = /(https?:\/\/)?(www\.)?(.*)/
+ (url) ->
+ url.match(stripper).pop()
+
+
diff --git a/app/assets/javascripts/darkswarm/services/checkout_form_state.js.coffee b/app/assets/javascripts/darkswarm/services/checkout_form_state.js.coffee
index 9d68efba64..6fedb6b3c6 100644
--- a/app/assets/javascripts/darkswarm/services/checkout_form_state.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/checkout_form_state.js.coffee
@@ -1,3 +1,3 @@
Darkswarm.factory 'CheckoutFormState', ()->
- # Just a singleton place to store data about the form statr
+ # Just a singleton place to store data about the form state
new class CheckoutFormState
diff --git a/app/assets/javascripts/darkswarm/services/order.js.coffee b/app/assets/javascripts/darkswarm/services/order.js.coffee
index 2357fd42da..8078cf8fc3 100644
--- a/app/assets/javascripts/darkswarm/services/order.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/order.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory 'Order', ($resource, Product, order, $http, CheckoutFormState, flash, Navigation)->
+Darkswarm.factory 'Order', ($resource, order, $http, CheckoutFormState, flash, Navigation)->
new class Order
errors: {}
diff --git a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee
index 1bb23446a2..0c5176328b 100644
--- a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee
@@ -1,10 +1,10 @@
-Darkswarm.factory 'OrderCycle', ($resource, Product, orderCycleData) ->
+Darkswarm.factory 'OrderCycle', ($resource, orderCycleData) ->
class OrderCycle
@order_cycle = orderCycleData # Object or {} due to RABL
- @push_order_cycle: ->
+ @push_order_cycle: (callback) ->
new $resource("/shop/order_cycle").save {order_cycle_id: @order_cycle.order_cycle_id}, (order_data)->
OrderCycle.order_cycle.orders_close_at = order_data.orders_close_at
- Product.update()
+ callback()
@orders_close_at: ->
@order_cycle.orders_close_at if @selected()
diff --git a/app/assets/javascripts/darkswarm/services/product.js.coffee b/app/assets/javascripts/darkswarm/services/product.js.coffee
index e58b83bacc..406e122de1 100644
--- a/app/assets/javascripts/darkswarm/services/product.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/product.js.coffee
@@ -9,7 +9,7 @@ Darkswarm.factory 'Product', ($resource) ->
products: null
loading: true
- update: ->
+ update: =>
@data.products = $resource("/shop/products").query =>
@data.loading = false
@data
diff --git a/app/assets/stylesheets/admin/dashboard_item.css.sass b/app/assets/stylesheets/admin/dashboard_item.css.sass
index ca33dd99fb..e24d60b277 100644
--- a/app/assets/stylesheets/admin/dashboard_item.css.sass
+++ b/app/assets/stylesheets/admin/dashboard_item.css.sass
@@ -4,7 +4,23 @@ div.dashboard_item
.centered
text-align: center
+ .text-icon
+ margin-top: 8px
+ display: block
+ font-size: 16px
+ font-weight: bold
+ color: #fff
+ padding: 0px 6px
+ border-radius: 10px
+ &.green
+ background-color: #9fc820
+ &.red
+ background-color: #DA5354
+ &.orange
+ background-color: #DA7F52
+
div.header
+ height: 50px
border-radius: 6px 6px 0px 0px
border: 1px solid #5498da
position: relative
@@ -26,16 +42,41 @@ div.dashboard_item
h3
color: #DA7F52
- h3
- padding: 10px 5px 10px 3%
+ h3.alpha
+ height: 100%
+ padding: 10px 5px 0px 3%
a
border-radius: 0px 4px 0px 0px
margin-left: 8px
+ height: 100%
+ padding: 15px 2px 0px 2px
+
+ .tabs
+ height: 30px
+ border: solid #5498da
+ border-width: 0px 0px 1px 0px
+ margin-top: 3px
+ div.dashboard_tab
+ cursor: pointer
+ height: 30px
+ color: #fff
+ background-color: #5498da
+ padding: 5px 5px 0px 5px
+ text-align: center
+ font-weight: bold
+ border: solid #5498da
+ border-width: 1px 1px 0px 1px
+ &:hover
+ background-color: #9fc820
+ &.selected
+ color: #5498da
+ background-color: #fff
.list
max-height: 250px
overflow-y: auto
+ overflow-x: hidden
.list-title
border: solid #5498da
@@ -50,10 +91,11 @@ div.dashboard_item
.list-item
border: solid #5498da
border-width: 0px 1px 0px 1px
+ height: 38px
span.alpha
font-weight: bold
margin-left: -3px
- padding: 10px 2px 10px 5%
+ padding: 10px 2px 0px 5%
span.omega
padding-right: 13px
margin-right: -3px
@@ -94,10 +136,13 @@ div.dashboard_item
color: #fff
.icon-ok-sign
color: #fff
+ .text-icon
+ &.green
+ color: #9fc820
+ background-color: #fff
a.button
color: #fff
- padding: 15px 15px
font-size: 110%
font-weight: bold
text-align: center
@@ -110,4 +155,5 @@ div.dashboard_item
&:hover
background-color: #9fc820
&.bottom
- border-radius: 0px 0px 6px 6px
\ No newline at end of file
+ border-radius: 0px 0px 6px 6px
+ padding: 15px 15px
\ No newline at end of file
diff --git a/app/assets/stylesheets/admin/sidebar-item.css.sass b/app/assets/stylesheets/admin/sidebar-item.css.sass
index e3e23f85a4..fc19154e94 100644
--- a/app/assets/stylesheets/admin/sidebar-item.css.sass
+++ b/app/assets/stylesheets/admin/sidebar-item.css.sass
@@ -17,10 +17,22 @@ div.sidebar_item
.list
max-height: 400px
overflow-y: auto
-
- .list-item
+ overflow-x: hidden
border: solid #5498da
border-width: 0px 1px 0px 1px
+ &.red
+ color: #DA5354
+ border: solid #DA5354
+ border-width: 0px 3px 0px 3px
+ .list-item
+ &.odd
+ background-color: #fcf6ef
+ &:hover
+ background-color: #9fc820
+ a
+ color: #DA5354
+
+ .list-item
span.alpha
font-weight: bold
margin-left: -3px
@@ -29,16 +41,12 @@ div.sidebar_item
max-width: 160px
text-overflow: ellipsis
span.omega
- padding: 8px 13px 8px 0px
+ padding: 8px 18px 8px 0px
margin-right: -3px
text-align: right
.icon-remove-sign
color: #DA5354
font-size: 18px
- &.red
- color: #DA5354
- border: solid #DA5354
- border-width: 0px 3px 0px 3px
&.even
background-color: #fff
&.odd
diff --git a/app/assets/stylesheets/darkswarm/all.scss b/app/assets/stylesheets/darkswarm/all.scss
index a8ac8af078..80e43cdc8b 100644
--- a/app/assets/stylesheets/darkswarm/all.scss
+++ b/app/assets/stylesheets/darkswarm/all.scss
@@ -9,3 +9,6 @@
*/
@import 'foundation-icons';
+ofn-modal {
+ display: block;
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/darkswarm/images.css.sass b/app/assets/stylesheets/darkswarm/images.css.sass
deleted file mode 100644
index 927e906842..0000000000
--- a/app/assets/stylesheets/darkswarm/images.css.sass
+++ /dev/null
@@ -1,46 +0,0 @@
-@import mixins
-@import variables
-@import branding
-
-.product-img
- border-bottom: 40px white solid
- border-top: 20px white solid
- border-left: 20px white solid
- border-right: 20px white solid
- outline: 1px solid #ccc
- @include box-shadow(0 1px 2px 1px rgba(0,0,0,0.25))
-
-.producer-hero
- position: relative
- padding: 0
-
-.producer-hero-img
- background-color: #999
- width: 100%
- height: inherit
- max-height: 260px
- overflow: hidden
- margin-top: 2em
- margin-bottom: 1em
-
-
-h3.producer-name
- background-color: rgba(255,255,255,0.65)
- height: 2.5em
- width: 100%
- position: absolute
- bottom: 0
- padding: 0.5em
-
-.producer-logo
- max-width: 220px
-
-@media only screen and (max-width: 1024px)
- .product-img
- margin-top: 2em
- margin-bottom: 1em
-
-
-
-
-
diff --git a/app/assets/stylesheets/darkswarm/overrides.css.sass b/app/assets/stylesheets/darkswarm/overrides.css.sass
index fcec6b455d..917d6dd2e3 100644
--- a/app/assets/stylesheets/darkswarm/overrides.css.sass
+++ b/app/assets/stylesheets/darkswarm/overrides.css.sass
@@ -1,2 +1,5 @@
.row
max-width: 74em
+
+.reveal-modal
+ position: fixed
diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass
index e0030a7d7a..a23458b941 100644
--- a/app/assets/stylesheets/darkswarm/shop.css.sass
+++ b/app/assets/stylesheets/darkswarm/shop.css.sass
@@ -27,7 +27,6 @@
margin-right: 12px
location
font-family: "AvenirBla_IE", "AvenirBla"
- padding-right: 16px
@media all and (max-width: 768px)
location, location + small
display: block
@@ -58,21 +57,23 @@
@media all and (max-width: 768px)
font-size: 1.2em
select
- width: 280px
+ width: inherit
display: inline-block
vackground: transparent
- border-width: 2px
- border-color: #666666
+ border-width: 1px
+ border-color: #999
+ color: #666
font-size: 1em
margin-bottom: 0
- padding: 8px 0px 8px 12px
+ padding: 8px 20px 8px 12px
@media all and (max-width: 768px)
font-size: 1.2em
- width: 180px
+ // width: 180px
closing
- font-size: 0.875em
+ color: black
+ font-size: 1.5em
display: block
- padding-top: 14px
+ padding-bottom: 20px
products
display: block
@@ -81,7 +82,8 @@
padding-top: 1em
input.button.right
float: left
-
+
+
product:hover, product:focus, product:active
border-color: $clr-brick
@include box-shadow(0 0 3px 0 $clr-brick-bright)
diff --git a/app/controllers/admin/enterprise_relationships_controller.rb b/app/controllers/admin/enterprise_relationships_controller.rb
index 46ac45a3e5..212bf3849d 100644
--- a/app/controllers/admin/enterprise_relationships_controller.rb
+++ b/app/controllers/admin/enterprise_relationships_controller.rb
@@ -1,8 +1,9 @@
module Admin
class EnterpriseRelationshipsController < ResourceController
def index
- @enterprises = Enterprise.managed_by(spree_current_user).by_name
- @enterprise_relationships = EnterpriseRelationship.by_name
+ @my_enterprises = Enterprise.managed_by(spree_current_user).by_name
+ @all_enterprises = Enterprise.by_name
+ @enterprise_relationships = EnterpriseRelationship.by_name.involving_enterprises @my_enterprises
end
def create
diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb
index 46273d0eff..566c660112 100644
--- a/app/controllers/admin/order_cycles_controller.rb
+++ b/app/controllers/admin/order_cycles_controller.rb
@@ -23,7 +23,7 @@ module Admin
respond_to do |format|
if @order_cycle.save
- OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle).go!
+ OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, managed_enterprises).go!
flash[:notice] = 'Your order cycle has been created.'
format.html { redirect_to admin_order_cycles_path }
@@ -40,7 +40,7 @@ module Admin
respond_to do |format|
if @order_cycle.update_attributes(params[:order_cycle])
- OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle).go!
+ OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, managed_enterprises).go!
flash[:notice] = 'Your order cycle has been updated.'
format.html { redirect_to admin_order_cycles_path }
diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb
index 51c7acba09..27d9a36943 100644
--- a/app/controllers/spree/admin/orders_controller_decorator.rb
+++ b/app/controllers/spree/admin/orders_controller_decorator.rb
@@ -15,11 +15,10 @@ Spree::Admin::OrdersController.class_eval do
per(params[:per_page] || Spree::Config[:orders_per_page])
} } }
-
private
def load_spree_api_key
current_user.generate_spree_api_key! unless spree_current_user.spree_api_key
@spree_api_key = spree_current_user.spree_api_key
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/spree/store_controller_decorator.rb b/app/controllers/spree/store_controller_decorator.rb
new file mode 100644
index 0000000000..09c2efc7b4
--- /dev/null
+++ b/app/controllers/spree/store_controller_decorator.rb
@@ -0,0 +1,7 @@
+class Spree::StoreController
+ layout 'darkswarm'
+
+ def unauthorized
+ render 'shared/unauthorized', :status => 401
+ end
+end
diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb
index 4b0f2f4a93..ed22831589 100644
--- a/app/helpers/enterprises_helper.rb
+++ b/app/helpers/enterprises_helper.rb
@@ -2,6 +2,10 @@ module EnterprisesHelper
def current_distributor
@current_distributor ||= current_order(false).andand.distributor
end
+
+ def managed_enterprises
+ Enterprise.managed_by(spree_current_user)
+ end
def enterprises_options enterprises
enterprises.map { |enterprise| [enterprise.name + ": " + enterprise.address.address1 + ", " + enterprise.address.city, enterprise.id.to_i] }
diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb
index 828277cb57..f856a088d1 100644
--- a/app/helpers/shared_helper.rb
+++ b/app/helpers/shared_helper.rb
@@ -14,16 +14,6 @@ module SharedHelper
klass
end
- # all suppliers of current distributor's products
- def current_producers
- if current_distributor && current_order_cycle
- variants = current_order_cycle.variants_distributed_by(current_distributor)
- Enterprise.supplying_variant_in(variants)
- else
- []
- end
- end
-
def enterprise_user?
spree_current_user.andand.enterprises.andand.count.to_i > 0
end
diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb
index a72c6fdbd6..dd40744492 100644
--- a/app/models/enterprise.rb
+++ b/app/models/enterprise.rb
@@ -18,8 +18,8 @@ class Enterprise < ActiveRecord::Base
delegate :latitude, :longitude, :city, :state_name, :to => :address
accepts_nested_attributes_for :address
- has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
- has_attached_file :promo_image, :styles => { :large => "570x380>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
+ has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
+ has_attached_file :promo_image, :styles => { :large => "260x1200#", :thumb => "100x100>" }
validates_presence_of :name
validates_presence_of :address
@@ -123,6 +123,14 @@ class Enterprise < ActiveRecord::Base
self.supplied_products.where('count_on_hand > 0').present?
end
+ def supplied_and_active_products_on_hand
+ self.supplied_products.where('spree_products.count_on_hand > 0').active
+ end
+
+ def active_products_in_order_cycles
+ self.supplied_and_active_products_on_hand.in_an_active_order_cycle
+ end
+
def to_param
"#{id}-#{name.parameterize}"
end
diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb
index 6c163b087a..1cea76db72 100644
--- a/app/models/enterprise_group.rb
+++ b/app/models/enterprise_group.rb
@@ -9,7 +9,7 @@ class EnterpriseGroup < ActiveRecord::Base
attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids
attr_accessible :promo_image
- has_attached_file :promo_image, styles: {medium: "800>400"}
+ has_attached_file :promo_image, styles: {large: "260x1200#"}
validates_attachment_content_type :promo_image, :content_type => /\Aimage\/.*\Z/
attr_accessible :logo
diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb
index 09fff8ba03..26bf8fc012 100644
--- a/app/models/enterprise_relationship.rb
+++ b/app/models/enterprise_relationship.rb
@@ -9,4 +9,8 @@ class EnterpriseRelationship < ActiveRecord::Base
joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id').
joins('LEFT JOIN enterprises AS child_enterprises ON child_enterprises.id = enterprise_relationships.child_id')
scope :by_name, with_enterprises.order('parent_enterprises.name, child_enterprises.name')
+
+ scope :involving_enterprises, ->(enterprises) {
+ where('parent_id IN (?) OR child_id IN (?)', enterprises, enterprises)
+ }
end
diff --git a/app/models/exchange.rb b/app/models/exchange.rb
index 18c3f81447..c491748d5d 100644
--- a/app/models/exchange.rb
+++ b/app/models/exchange.rb
@@ -54,6 +54,10 @@ class Exchange < ActiveRecord::Base
incoming? ? 'supplier' : 'distributor'
end
+ def participant
+ incoming? ? sender : receiver
+ end
+
def to_h(core_only=false)
h = attributes.merge({ 'variant_ids' => variant_ids.sort, 'enterprise_fee_ids' => enterprise_fee_ids.sort })
h.reject! { |k| %w(id order_cycle_id created_at updated_at).include? k } if core_only
diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb
index ee83e85bf7..44e7d5e02e 100644
--- a/app/models/spree/ability_decorator.rb
+++ b/app/models/spree/ability_decorator.rb
@@ -1,6 +1,6 @@
-
class AbilityDecorator
include CanCan::Ability
+
def initialize(user)
if user.enterprises.count > 0
@@ -53,6 +53,11 @@ class AbilityDecorator
(user.enterprises & shipping_method.distributors).any?
end
+ can [:admin, :index, :create], EnterpriseRelationship
+ can [:destroy], EnterpriseRelationship do |enterprise_relationship|
+ user.enterprises.include? enterprise_relationship.parent
+ end
+
can [:create], OrderCycle
can [:admin, :index, :read, :edit, :update, :bulk_update, :clone], OrderCycle do |order_cycle|
user.enterprises.include? order_cycle.coordinator
diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb
index ba7cbc253d..0140586668 100644
--- a/app/models/spree/product_decorator.rb
+++ b/app/models/spree/product_decorator.rb
@@ -75,6 +75,12 @@ Spree::Product.class_eval do
scope :in_order_cycle, lambda { |order_cycle| with_order_cycles_inner.
merge(Exchange.outgoing).
where('order_cycles.id = ?', order_cycle) }
+
+ scope :in_an_active_order_cycle, lambda { with_order_cycles_inner.
+ merge(OrderCycle.active).
+ merge(Exchange.outgoing).
+ where('order_cycles.id IS NOT NULL') }
+
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
scoped
diff --git a/app/overrides/spree/admin/products/_form/add_primary_taxon_field.html.haml.deface b/app/overrides/spree/admin/products/_form/add_primary_taxon_field.html.haml.deface
new file mode 100644
index 0000000000..efd7ccbfaf
--- /dev/null
+++ b/app/overrides/spree/admin/products/_form/add_primary_taxon_field.html.haml.deface
@@ -0,0 +1,3 @@
+/ insert_top "[data-hook='admin_product_form_right']"
+
+= render 'spree/admin/products/primary_taxon_form', f: f
\ No newline at end of file
diff --git a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface
new file mode 100644
index 0000000000..e964e9a704
--- /dev/null
+++ b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface
@@ -0,0 +1,63 @@
+/ replace "fieldset[data-hook='new_product']"
+
+%fieldset{ id: "new_product" }
+ %legend{align: "center"}= t(:new_product)
+
+ .row
+ .alpha.eleven.columns
+ = f.field_container :name do
+ = f.label :name, t(:name)
+ %span.required *
+ %br/
+ = f.text_field :name, :class => 'fullwidth title'
+ = f.error_message_on :name
+ .five.columns.omega
+ = f.field_container :supplier do
+ = f.label :supplier
+ %span.required *
+ = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"})
+ = f.error_message_on :supplier
+
+ .row
+ .alpha.six.columns
+ = render 'spree/admin/products/primary_taxon_form', f: f
+ .five.columns
+ = f.field_container :price do
+ = f.label :price, t(:price)
+ %span.required *
+ %br/
+ = f.text_field :price, :class => 'fullwidth'
+ = f.error_message_on :price
+ .five.columns.omega
+ = f.field_container :on_hand do
+ = f.label :on_hand, t(:on_hand)
+ %span.required *
+ %br/
+ = f.text_field :on_hand, :class => 'fullwidth'
+ = f.error_message_on :on_hand
+
+ .row{ 'ng-controller' => 'unitsCtrl' }
+ .six.columns.alpha
+ = f.label :variant_unit_with_scale, :units
+ %select.select2.fullwidth{ id: 'product_variant_unit_with_scale', 'ng-model' => 'product.variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options' }
+ %option{'value' => '', 'ng-hide' => "hasUnit(product)"}
+ %input{ type: 'hidden', 'ng-value' => 'product.variant_unit', name: 'product[variant_unit]' }
+ %input{ type: 'hidden', 'ng-value' => 'product.variant_unit_scale', name: 'product[variant_unit_scale]' }
+ .five.columns
+ = f.label :product_unit_value_with_description, :value
+ %input.fullwidth{ id: 'product_unit_value_with_description', 'ng-model' => 'product.master.unit_value_with_description', :type => 'text', :placeholder => 'eg. 2', 'ng-disabled' => "!hasUnit(product)" }
+ %input{ type: 'hidden', 'ng-value' => 'product.master.unit_value', name: 'product[unit_value]' }
+ %input{ type: 'hidden', 'ng-value' => 'product.master.unit_description', name: 'product[unit_description]' }
+ .five.columns.omega{ 'ng-show' => "product.variant_unit_with_scale == 'items'" }
+ = f.label :product_variant_unit_name, :unit_name
+ %input.fullwidth{ id: 'product_variant_unit_name','ng-model' => 'product.variant_unit_name', :name => 'product[variant_unit_name]', :placeholder => 'eg. bunches', :type => 'text' }
+
+ #product-from-prototype.clearfix{"data-hook" => "product-from-prototype"}
+ = render :file => 'spree/admin/prototypes/show' if @prototype
+ = render :partial => 'spree/admin/shared/new_resource_links'
+
+
+:javascript
+ angular.element(document.getElementById("new_product")).ready(function() {
+ angular.bootstrap(document.getElementById("new_product"), ['admin.products']);
+ });
diff --git a/app/views/admin/enterprise_relationships/_data.html.haml b/app/views/admin/enterprise_relationships/_data.html.haml
index 2361755707..7c13978e90 100644
--- a/app/views/admin/enterprise_relationships/_data.html.haml
+++ b/app/views/admin/enterprise_relationships/_data.html.haml
@@ -1,3 +1,4 @@
:javascript
angular.module('ofn.admin').value('enterprise_relationships', #{render partial: "admin/json/enterprise_relationships", object: @enterprise_relationships});
- angular.module('ofn.admin').value('enterprises', #{render partial: "admin/json/enterprises", object: @enterprises});
+ angular.module('ofn.admin').value('my_enterprises', #{render partial: "admin/json/enterprises", object: @my_enterprises});
+ angular.module('ofn.admin').value('all_enterprises', #{render partial: "admin/json/enterprises", object: @all_enterprises});
diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml
index 5cc10b0fe1..1a737ec3a5 100644
--- a/app/views/admin/enterprise_relationships/_form.html.haml
+++ b/app/views/admin/enterprise_relationships/_form.html.haml
@@ -1,9 +1,9 @@
%tr
%td
- %select{name: "enterprise_relationship_parent_name", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.enterprises"}
+ %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"}
%td permits
%td
- %select{name: "enterprise_relationship_child_name", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.enterprises"}
+ %select{name: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"}
%td.actions
%input{type: "button", value: "Create", "ng-click" => "create()"}
.errors {{ EnterpriseRelationships.create_errors }}
diff --git a/app/views/admin/enterprises/_sidebar.html.haml b/app/views/admin/enterprises/_sidebar.html.haml
index 578612ec6f..0390cb289a 100644
--- a/app/views/admin/enterprises/_sidebar.html.haml
+++ b/app/views/admin/enterprises/_sidebar.html.haml
@@ -1,59 +1,63 @@
+- payment_methods_color = @payment_methods.count > 0 ? (@enterprise.payment_methods.count > 0 ? "blue" : "red") : "red"
.sidebar_item.four.columns.alpha#payment_methods
- .four.columns.alpha.header{ class: "#{@payment_methods.count > 0 ? "blue" : "red"}" }
+ .four.columns.alpha.header{ class: "#{payment_methods_color}" }
%span.four.columns.alpha.centered Payment Methods
- - if @payment_methods.count > 0
- .four.columns.alpha.list
+ .four.columns.alpha.list{ class: "#{payment_methods_color}" }
+ - if @payment_methods.count > 0
+ -# = hidden_field_tag "enterprise[payment_method_ids][]", []
- @payment_methods.each do |payment_method|
%a.four.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{edit_admin_payment_method_path(payment_method)}" }
%span.three.columns.alpha
= payment_method.name
%span.one.column.omega
- = f.check_box :payment_method_ids, { :multiple => true }, payment_method.id, nil
- - else
- .four.columns.alpha.list-item.red
- %span.three.columns.alpha None Available
- %span.one.column.omega
- %span.icon-remove-sign
- %a.four.columns.alpha.button{ href: "#{new_admin_payment_method_path}", class: "#{@payment_methods.count > 0 ? "blue" : "red"}" }
+ = f.check_box :payment_method_ids, { multiple: true }, payment_method.id, nil
+ - else
+ .four.columns.alpha.list-item
+ %span.three.columns.alpha None Available
+ %span.one.column.omega
+ %span.icon-remove-sign
+ %a.four.columns.alpha.button{ href: "#{new_admin_payment_method_path}", class: "#{payment_methods_color}" }
CREATE NEW
%span.icon-arrow-right
+- shipping_methods_color = @shipping_methods.count > 0 ? (@enterprise.shipping_methods.count > 0 ? "blue" : "red") : "red"
.sidebar_item.four.columns.alpha#shipping_methods
- .four.columns.alpha.header{ class: "#{@shipping_methods.count > 0 ? "blue" : "red"}" }
+ .four.columns.alpha.header{ class: "#{shipping_methods_color}" }
%span.four.columns.alpha.centered Shipping Methods
- - if @shipping_methods.count > 0
- .four.columns.alpha.list
+ .four.columns.alpha.list{ class: "#{shipping_methods_color}" }
+ - if @shipping_methods.count > 0
- @shipping_methods.each do |shipping_method|
%a.four.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{edit_admin_shipping_method_path(shipping_method)}" }
%span.three.columns.alpha
= shipping_method.name
%span.one.column.omega
= f.check_box :shipping_method_ids, { :multiple => true }, shipping_method.id, nil
- - else
- .four.columns.alpha.list-item.red
- %span.three.columns.alpha None Available
- %span.one.column.omega
- %span.icon-remove-sign
- %a.four.columns.alpha.button{ href: "#{new_admin_shipping_method_path}", class: "#{@payment_methods.count > 0 ? "blue" : "red"}" }
+ - else
+ .four.columns.alpha.list-item
+ %span.three.columns.alpha None Available
+ %span.one.column.omega
+ %span.icon-remove-sign
+ %a.four.columns.alpha.button{ href: "#{new_admin_shipping_method_path}", class: "#{shipping_methods_color}" }
CREATE NEW
%span.icon-arrow-right
+- enterprise_fees_color = @enterprise_fees.count > 0 ? "blue" : "red"
.sidebar_item.four.columns.alpha#enterprise_fees
- .four.columns.alpha.header{ class: "#{@enterprise_fees.count > 0 ? "blue" : "red"}" }
+ .four.columns.alpha.header{ class: "#{enterprise_fees_color}" }
%span.four.columns.alpha.centered Enterprise Fees
- - if @enterprise_fees.count > 0
- .four.columns.alpha.list
+ .four.columns.alpha.list{ class: "#{enterprise_fees_color}" }
+ - if @enterprise_fees.count > 0
- @enterprise_fees.each do |enterprise_fee|
%a.four.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.admin_enterprise_fees_path}" }
%span.three.columns.alpha
= enterprise_fee.name
%span.one.column.omega
- - else
- .four.columns.alpha.list-item.red
- %span.three.columns.alpha None Available
- %span.one.column.omega
- %span.icon-remove-sign
- %a.four.columns.alpha.button{ href: "#{main_app.admin_enterprise_fees_path}", class: "#{@enterprise_fees.count > 0 ? "blue" : "red"}" }
+ - else
+ .four.columns.alpha.list-item.red
+ %span.three.columns.alpha None Available
+ %span.one.column.omega
+ %span.icon-remove-sign
+ %a.four.columns.alpha.button{ href: "#{main_app.admin_enterprise_fees_path}", class: "#{enterprise_fees_color}" }
CREATE NEW
%span.icon-arrow-right
\ No newline at end of file
diff --git a/app/views/admin/enterprises/index.rabl b/app/views/admin/enterprises/index.rabl
new file mode 100644
index 0000000000..9e1ee893d1
--- /dev/null
+++ b/app/views/admin/enterprises/index.rabl
@@ -0,0 +1,14 @@
+collection @collection
+
+attributes :id, :name
+
+child supplied_products: :supplied_products do |product|
+ attributes :name
+ node(:supplier_name) { |p| p.supplier.andand.name }
+ node(:image_url) { |p| p.images.present? ? p.images.first.attachment.url(:mini) : nil }
+ node(:master_id) { |p| p.master.id }
+ child variants: :variants do |variant|
+ attributes :id
+ node(:label) { |v| v.options_text }
+ end
+end
diff --git a/app/views/admin/enterprises/index.rep b/app/views/admin/enterprises/index.rep
deleted file mode 100644
index beaceef8a4..0000000000
--- a/app/views/admin/enterprises/index.rep
+++ /dev/null
@@ -1,15 +0,0 @@
-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
diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml
index 3f7d7ba578..ae0b9b7349 100644
--- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml
+++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml
@@ -1,7 +1,7 @@
%td{:colspan => 3}
.exchange-select-all-variants
%label
- = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-click' => 'setExchangeVariants(exchange, incomingExchangesVariants(), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants'
+ = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, incomingExchangesVariants(), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants'
Select all
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle'}
diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml
index 6ad13a88a6..ac791d7fd2 100644
--- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml
+++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml
@@ -2,7 +2,7 @@
%td{:colspan => 3}
.exchange-select-all-variants
%label
- = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-click' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants'
+ = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants'
Select all
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
diff --git a/app/views/checkout/_billing.html.haml b/app/views/checkout/_billing.html.haml
index 6ab949e36e..27fc2b202f 100644
--- a/app/views/checkout/_billing.html.haml
+++ b/app/views/checkout/_billing.html.haml
@@ -1,7 +1,7 @@
%fieldset#billing
%ng-form{"ng-controller" => "BillingCtrl", name: "billing"}
- %h5{"ng-class" => "{valid: billing.$valid, dirty: details.$dirty}"}
+ %h5{"ng-class" => "{valid: billing.$valid, dirty: billing.$dirty}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
diff --git a/app/views/checkout/_payment.html.haml b/app/views/checkout/_payment.html.haml
index 39b40675bf..045882ade3 100644
--- a/app/views/checkout/_payment.html.haml
+++ b/app/views/checkout/_payment.html.haml
@@ -1,7 +1,7 @@
%fieldset#payment
%ng-form{"ng-controller" => "PaymentCtrl", name: "payment"}
- %h5{"ng-class" => "{valid: payment.$valid, dirty: details.$dirty}"}
+ %h5{"ng-class" => "{valid: payment.$valid, dirty: payment.$dirty}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml
index 3712b995ac..e5208d5f60 100644
--- a/app/views/checkout/_shipping.html.haml
+++ b/app/views/checkout/_shipping.html.haml
@@ -1,7 +1,7 @@
%fieldset#shipping
%ng-form{"ng-controller" => "ShippingCtrl", name: "shipping"}
- %h5{"ng-class" => "{valid: shipping.$valid, dirty: details.$dirty}"}
+ %h5{"ng-class" => "{valid: shipping.$valid, dirty: shipping.$dirty}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml
index 0e9ae1b185..56dfeacaad 100644
--- a/app/views/groups/index.html.haml
+++ b/app/views/groups/index.html.haml
@@ -17,18 +17,18 @@
"ng-debounce" => "150",
"ofn-disable-enter" => true}
- .group{"ng-repeat" => "group in Groups.groups | filter:query | orderBy:order"}
-
+ .group{"ng-repeat" => "group in Groups.groups | filter:query | orderBy:order",
+ name: "group{{group.id}}",
+ id: "group{{group.id}}"}
.row.pad-top{bindonce: true}
.small-12.columns
.group-hero
- %img.group-hero-img{"bo-src" => "{{group.promo_image}}"}
- %img.group-logo{"bo-src" => "group.logo"}
+ %img.group-hero-img{"bo-src" => "group.promo_image"}
+ %img.group-logo{"bo-src" => "group.logo", "bo-if" => "group.logo"}
%h3.group-name
%i.ofn-i_035-groups
{{ group.name }}
%h5.group-description {{ group.description }}
- / Will - scale large images down to 1200px wide, crop in to img aspect ratio 60W:13H
.row.pad-top{bindonce: true}
.small-6.columns
diff --git a/app/views/json/_current_hub.rabl b/app/views/json/_current_hub.rabl
index ab67cce3cc..7f615bb07a 100644
--- a/app/views/json/_current_hub.rabl
+++ b/app/views/json/_current_hub.rabl
@@ -1,2 +1,8 @@
object current_distributor
attributes :name, :id
+
+if current_distributor
+ child suppliers: :producers do
+ extends "json/producer"
+ end
+end
diff --git a/app/views/json/_groups.rabl b/app/views/json/_groups.rabl
index 9475b425c4..27325bb850 100644
--- a/app/views/json/_groups.rabl
+++ b/app/views/json/_groups.rabl
@@ -6,5 +6,9 @@ child enterprises: :enterprises do
end
node :logo do |group|
- group.logo(:original)
+ group.logo(:medium) if group.logo.exists?
+end
+
+node :promo_image do |group|
+ group.promo_image(:large) if group.promo_image.exists?
end
diff --git a/app/views/json/_hubs.rabl b/app/views/json/_hubs.rabl
index dc589b878b..c118b4504f 100644
--- a/app/views/json/_hubs.rabl
+++ b/app/views/json/_hubs.rabl
@@ -6,11 +6,7 @@ child distributed_taxons: :taxons do
end
child suppliers: :producers do
- attributes :name, :id, :description, :long_description
-
- node :promo_image do |producer|
- producer.promo_image.url
- end
+ extends "json/producer"
end
node :pickup do |hub|
diff --git a/app/views/json/_producer.rabl b/app/views/json/_producer.rabl
new file mode 100644
index 0000000000..4c0efae8e1
--- /dev/null
+++ b/app/views/json/_producer.rabl
@@ -0,0 +1,5 @@
+attributes :name, :id, :description, :long_description
+
+node :promo_image do |producer|
+ producer.promo_image.url
+end
diff --git a/app/views/modals/_producer.html.haml b/app/views/modals/_producer.html.haml
index 6031537089..13f8026195 100644
--- a/app/views/modals/_producer.html.haml
+++ b/app/views/modals/_producer.html.haml
@@ -5,39 +5,35 @@
/ Will - scale large images down to 1200px wide, crop in to img aspect ratio 60W:13H
%h3.producer-name {{ producer.name }}
.row
- .columns.small-12.large-6{"ng-bind-html" => "{{producer.long_description}}"}
- / Will to fix this
+ .columns.small-12.large-6{"ng-bind-html" => "producer.long_description"}
.columns.small-12.large-6
- / Will needs logic to add in following only if data is available for each type:
- / Will needs better formatting of URLs printed on-screen (minus http://www.)
- / Will needs formatting of URLs driving %a regardless of user input (plus http://www.)
- %img.producer-logo{"ng-src" => "{{producer.logo}}"}
+ %img.producer-logo{"ng-src" => "{{producer.logo}}", "ng-if" => "producer.logo"}
%h4 Stay in touch with {{ producer.name }}
- %ul.small-block-grid-1
- %li
- %a{href: "{{producer.website}}", target: "_blank" }
+ %ul.small-block-grid-1{bindonce: true}
+ %li{"ng-if" => "producer.website"}
+ %a{"ng-href" => "http://{{producer.website | stripUrl}}", target: "_blank" }
%i.fi-web
- {{ producer.website }}
- %li
- %a{href: "{{producer.twitter}}", target: "_blank"}
- / Will needs logic to allow user to input @twitterhandle to drive a logical URL
+ {{ producer.website | stripUrl }}
+
+ %li{"ng-if" => "producer.twitter"}
+ %a{"ng-href" => "http://twitter.com/{{producer.twitter}}", target: "_blank"}
%i.fi-social-twitter
{{ producer.twitter }}
- %li
- %a{href: "{{producer.facebook}}", target: "_blank"}
+
+ %li{"ng-if" => "producer.facebook"}
+ %a{"ng-href" => "http://{{producer.facebook | stripUrl}}", target: "_blank"}
%i.fi-social-facebook
- {{ producer.facebook }}
- %li
- %a{href: "{{producer.linkedin}}", target: "_blank"}
+ {{ producer.facebook | stripUrl }}
+
+ %li{"ng-if" => "producer.linkedin"}
+ %a{"ng-href" => "http://{{producer.linkedin | stripUrl}}", target: "_blank"}
%i.fi-social-linkedin
- {{ producer.linkedin }}
- %li
- %a{href: "{{producer.instagram}}", target: "_blank"}
- / Will needs logic to allow user to input @instagramhandle to drive a logical URL
+ {{ producer.linkedin | stripUrl }}
+
+ %li{"ng-if" => "producer.instagram"}
+ %a{"ng-href" => "http://instagram.com/{{producer.instagram}}", target: "_blank"}
%i.fi-social-instagram
{{ producer.instagram }}
- / %pre
- / {{ producer | json }}
- %a.close-reveal-modal{"ng-click" => "$close()"} ×
\ No newline at end of file
+ %a.close-reveal-modal{"ng-click" => "$close()"} ×
diff --git a/app/views/modals/_product.html.haml b/app/views/modals/_product.html.haml
index 70137ba987..9c071b51eb 100644
--- a/app/views/modals/_product.html.haml
+++ b/app/views/modals/_product.html.haml
@@ -1,14 +1,10 @@
%ofn-modal{title: "{{product.name}}"}
-
.row
.columns.small-12.large-6
- %img.product-img{"ng-src" => "{{product.master.images[0].large_url}}"}
+ %img.product-img{"ng-src" => "{{product.master.images[0].large_url}}", "ng-if" => "product.master.images[0]"}
.columns.small-12.large-6
%h2
%img{"ng-src" => "{{product.primary_taxon.icon}}"}
{{product.name}}
%p {{product.description}}
- / %pre
- / {{ product | json }}
%a.close-reveal-modal{"ng-click" => "$close()"} ×
-
\ No newline at end of file
diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml
index 45b3bf8dd0..3313db6552 100644
--- a/app/views/producers/_fat.html.haml
+++ b/app/views/producers/_fat.html.haml
@@ -5,7 +5,7 @@
{{ producer.taxons | printArrayOfObjects }}
.columns.small-8
%strong About us
- %p.trans-sentence
+ %p
{{ producer.description }}
.row.active_table_row.link{"ng-show" => "open()", "ng-repeat" => "hub in producer.distributors"}
diff --git a/app/views/shared/unauthorized.html.haml b/app/views/shared/unauthorized.html.haml
new file mode 100644
index 0000000000..fcf3f3bf27
--- /dev/null
+++ b/app/views/shared/unauthorized.html.haml
@@ -0,0 +1 @@
+Unauthorized
diff --git a/app/views/shop/products.rabl b/app/views/shop/products.rabl
index ee5e567285..b1567a311a 100644
--- a/app/views/shop/products.rabl
+++ b/app/views/shop/products.rabl
@@ -13,10 +13,10 @@ child :supplier => :supplier do
attributes :id, :name, :description, :long_description, :website, :instagram, :facebook, :linkedin, :twitter
node :logo do |supplier|
- supplier.logo.url
+ supplier.logo(:medium) if supplier.logo.exists?
end
node :promo_image do |supplier|
- supplier.promo_image.url
+ supplier.promo_image(:large) if supplier.promo_image.exists?
end
end
diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml
index 5a3af9684e..6212ef746d 100644
--- a/app/views/shop/products/_form.html.haml
+++ b/app/views/shop/products/_form.html.haml
@@ -21,7 +21,7 @@
%div{"bo-if" => "hasVariants"}
= render partial: "shop/products/variants"
- .variant.row{"bo-if" => "!hasVariants"}
+ .variants.row{"bo-if" => "!hasVariants"}
= render partial: "shop/products/master"
.row
diff --git a/app/views/shop/products/_master.html.haml b/app/views/shop/products/_master.html.haml
index 7398912bda..bd8a8cd3de 100644
--- a/app/views/shop/products/_master.html.haml
+++ b/app/views/shop/products/_master.html.haml
@@ -1,4 +1,4 @@
-.small-1.column
+.small-1.columns
%span.bulk{"bo-if" => "product.group_buy"} bulk
diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml
index e41c6ef440..22ce416336 100644
--- a/app/views/shop/products/_summary.html.haml
+++ b/app/views/shop/products/_summary.html.haml
@@ -1,5 +1,5 @@
.row.summary
- .small-1.column
+ .small-1.columns
%img{"bo-src" => "product.master.images[0].small_url"}
.small-4.columns.summary-header
diff --git a/app/views/shop/products/_variants.html.haml b/app/views/shop/products/_variants.html.haml
index 14ff8eff24..6751f8c62c 100644
--- a/app/views/shop/products/_variants.html.haml
+++ b/app/views/shop/products/_variants.html.haml
@@ -1,7 +1,7 @@
.row.variants{bindonce: true,
"ng-repeat" => "variant in product.variants"}
- .small-1.column
+ .small-1.columns
%span.bulk{"bo-if" => "product.group_buy"} bulk
diff --git a/app/views/shop/show.html.haml b/app/views/shop/show.html.haml
index b94bd59bf9..ad0080edae 100644
--- a/app/views/shop/show.html.haml
+++ b/app/views/shop/show.html.haml
@@ -1,19 +1,25 @@
%shop.darkswarm
- content_for :order_cycle_form do
- %strong.avenir Ready for
- %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id",
- "ng-change" => "changeOrderCycle()",
- "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}",
- "popover-placement" => "bottom", "popover" => "When do you want to get your order?", "popover-trigger" => "openTrigger"}
+ %div{"ng-controller" => "OrderCycleChangeCtrl"}
+ %closing{"ng-if" => "OrderCycle.selected()"}
+ Next order closing
+ %strong {{ OrderCycle.orders_close_at() | date_in_words }}
- %closing{"ng-if" => "OrderCycle.selected()"}
- Orders close
- %strong {{ OrderCycle.orders_close_at() | date_in_words }}
+ %span Ready for pickup
+
+ / Will this label should be a variable to reflect pickup or delivery as appropriate
+
+ %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id",
+ "ng-change" => "changeOrderCycle()",
+ "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}",
+ "popover-placement" => "bottom", "popover" => "When do you want to get your order?", "popover-trigger" => "openTrigger"}
+
+
= render partial: "shopping_shared/details"
.row
= render partial: "shop/products/form"
-= render partial: "shared/footer"
\ No newline at end of file
+= render partial: "shared/footer"
diff --git a/app/views/shopping_shared/_details.html.haml b/app/views/shopping_shared/_details.html.haml
index 37edfa3c16..2a8e1b10b1 100644
--- a/app/views/shopping_shared/_details.html.haml
+++ b/app/views/shopping_shared/_details.html.haml
@@ -1,14 +1,11 @@
-= render partial: "shopping_shared/modals"
-
%navigation
%distributor.details.row
#distributor_title
%img.left{src: current_distributor.logo.url(:thumb)}
- %h4
+ %h3
= current_distributor.name
%location= current_distributor.address.city
- %small
- %a{href: "/"} Change location
+ / Will this needs to be a drop-down to choose either pick-up point or delivery once shipping methods are implemented
= render partial: "shopping_shared/order_cycles"
diff --git a/app/views/shopping_shared/_groups.html.haml b/app/views/shopping_shared/_groups.html.haml
index a218d6aeb5..e9ebb219b3 100644
--- a/app/views/shopping_shared/_groups.html.haml
+++ b/app/views/shopping_shared/_groups.html.haml
@@ -1,10 +1,5 @@
-.content#groups
+.content
%ul
- for group in current_distributor.groups
%li
- %h4= group.name
- %ul
- - for sibling in group.enterprises.except(current_distributor)
- %li
- %a{"data-reveal-id" => "sibling_details_#{sibling.id}", "data-reveal" => ""}
- = sibling.name
+ %a{href: main_app.groups_path(anchor: "#/#group#{group.id}")}= group.name
diff --git a/app/views/shopping_shared/_modals.html.haml b/app/views/shopping_shared/_modals.html.haml
deleted file mode 100644
index ad92f1957f..0000000000
--- a/app/views/shopping_shared/_modals.html.haml
+++ /dev/null
@@ -1,36 +0,0 @@
-- for producer in current_producers
- .reveal-modal{id: "producer_details_#{producer.id}", "data-reveal" => ""}
- .row
- - if producer.logo.exists?
- .large-1.columns
- %img.left{src: producer.logo.url(:thumb)}
- .large-11.columns
- %h2
- = producer.name
- .row
- .large-8.columns
- = producer.long_description.andand.html_safe
-
- - if producer.promo_image.exists?
- .large-4.columns
- %img.about.right{src: producer.promo_image.url(:large)}
- %a.close-reveal-modal ×
-
-
-- for group in current_distributor.groups
- - for sibling in group.enterprises.except(current_distributor)
- .reveal-modal{id: "sibling_details_#{sibling.id}", "data-reveal" => ""}
- .row
- - if sibling.logo.exists?
- .large-1.columns
- %img.left{src: sibling.logo.url(:thumb)}
- .large-11.columns
- %h2
- = sibling.name
- .row
- .large-8.columns
- = sibling.long_description.andand.html_safe
- - if sibling.promo_image.exists?
- .large-4.columns
- %img.about.right{src: sibling.promo_image.url(:large)}
- %a.close-reveal-modal ×
diff --git a/app/views/shopping_shared/_order_cycles.html.haml b/app/views/shopping_shared/_order_cycles.html.haml
index 91517e0c1c..195389df91 100644
--- a/app/views/shopping_shared/_order_cycles.html.haml
+++ b/app/views/shopping_shared/_order_cycles.html.haml
@@ -14,5 +14,3 @@
- else
%form.custom
= yield :order_cycle_form
-
-
diff --git a/app/views/shopping_shared/_producers.html.haml b/app/views/shopping_shared/_producers.html.haml
index 32f09869e8..584d75ff35 100644
--- a/app/views/shopping_shared/_producers.html.haml
+++ b/app/views/shopping_shared/_producers.html.haml
@@ -1,6 +1,4 @@
-.content#producers
+.content#producers{"ng-controller" => "ProducersTabCtrl"}
%ul
- - for producer in current_producers
- %li
- %a{"data-reveal-id" => "producer_details_#{producer.id}", "data-reveal" => ""}
- = producer.name
+ %li{"ng-repeat" => "producer in CurrentHub.producers"}
+ = render partial: "modals/producer"
diff --git a/app/views/spree/admin/overview/_enterprises.html.haml b/app/views/spree/admin/overview/_enterprises.html.haml
index 4042381502..85dc93c2ee 100644
--- a/app/views/spree/admin/overview/_enterprises.html.haml
+++ b/app/views/spree/admin/overview/_enterprises.html.haml
@@ -1,11 +1,15 @@
-%div.dashboard_item.sixteen.columns.alpha#enterprises
+%div.dashboard_item.sixteen.columns.alpha#enterprises{ 'ng-app' => 'ofn.admin', 'ng-controller' => "enterprisesDashboardCtrl" }
%div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "red"}"}
%h3.thirteen.columns.alpha My Enterprises
- if @enterprises.any?
- %a.three.columns.omega.icon-plus.button.blue{ href: "#{main_app.new_admin_enterprise_path}" }
+ %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" }
CREATE NEW
- else
%a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this?
+ - if @enterprises.any?
+ %div.sixteen.columns.alpha.tabs
+ %div.dashboard_tab.eight.columns.alpha.blue{ ng: { class: "{selected: activeTab == 'hubs'}", click: "activeTab = 'hubs'" } } HUBS
+ %div.dashboard_tab.eight.columns.omega.blue{ ng: { class: "{selected: activeTab == 'producers'}", click: "activeTab = 'producers'" } } PRODUCERS
- if @enterprises.empty?
%div.sixteen.columns.alpha.list-item.red
%span.text.fifteen.columns.alpha You don't have any enterprises yet.
@@ -15,40 +19,69 @@
CREATE A NEW ENTERPRISE
%span.icon-arrow-right
- else
- %div.sixteen.columns.alpha.list-title
- %span.five.columns.alpha Name
- %span.centered.three.columns Payment Methods
- %span.centered.three.columns Shipping Methods
- %span.centered.three.columns Enterprise Fees
- %div.sixteen.columns.alpha.list
- - @enterprises.each do |enterprise|
- %a.sixteen.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(enterprise)}" }
- %span.five.columns.alpha
- = enterprise.name
- %span.symbol.three.columns.centered
- - payment_method_count = enterprise.payment_methods.count
- - if payment_method_count < 1 && enterprise.is_distributor
- %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no Payment Methods" }
- - elsif enterprise.is_primary_producer
- %span.icon-ok-sign.with-tip{ title: "Producers (like #{enterprise.name}) do not require Payment Methods." }
- - else
- %span.icon-ok-sign.with-tip{ title: "#{payment_method_count} Payment Method#{payment_method_count > 1 ? "s" : ""}" }
- %span.symbol.three.columns.centered
- - shipping_method_count = enterprise.shipping_methods.count
- - if shipping_method_count < 1 && enterprise.is_distributor
- %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no Shipping Methods" }
- - elsif enterprise.is_primary_producer
- %span.icon-ok-sign.with-tip{ title: "Producers (like #{enterprise.name}) do not require Shipping Methods." }
- -else
- %span.icon-ok-sign.with-tip{ title: "#{shipping_method_count} Shipping Method#{shipping_method_count > 1 ? "s" : ""}" }
- %span.symbol.three.columns.centered
- - fee_count = enterprise.enterprise_fees.count
- - if fee_count > 0
- %span.icon-ok-sign.with-tip{ title: "#{fee_count} Fee#{fee_count > 1 ? "s" : ""}" }
- - else
- %span.icon-warning-sign.with-tip{ title: "#{enterprise.name} has no Enterprise Fees" }
- %span.two.columns.omega.right
- %span.icon-arrow-right
+ %div.hubs_tab{ ng: { show: "activeTab == 'hubs'"} }
+ %div.sixteen.columns.alpha.list-title
+ %span.five.columns.alpha Name
+ %span.centered.three.columns Payment Methods
+ %span.centered.three.columns Shipping Methods
+ %span.centered.three.columns Enterprise Fees
+ %div.sixteen.columns.alpha.list
+ - @enterprises.is_distributor.each do |enterprise|
+ %a.sixteen.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(enterprise)}" }
+ %span.five.columns.alpha
+ = enterprise.name
+ %span.symbol.three.columns.centered
+ - payment_method_count = enterprise.payment_methods.count
+ - if payment_method_count < 1 && enterprise.is_distributor
+ %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no Payment Methods" }
+ - elsif enterprise.is_primary_producer
+ %span.icon-ok-sign.with-tip{ title: "Producers (like #{enterprise.name}) do not require Payment Methods." }
+ - else
+ %span.icon-ok-sign.with-tip{ title: "#{payment_method_count} Payment Method#{payment_method_count > 1 ? "s" : ""}" }
+ %span.symbol.three.columns.centered
+ - shipping_method_count = enterprise.shipping_methods.count
+ - if shipping_method_count < 1 && enterprise.is_distributor
+ %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no Shipping Methods" }
+ - elsif enterprise.is_primary_producer
+ %span.icon-ok-sign.with-tip{ title: "Producers (like #{enterprise.name}) do not require Shipping Methods." }
+ -else
+ %span.icon-ok-sign.with-tip{ title: "#{shipping_method_count} Shipping Method#{shipping_method_count > 1 ? "s" : ""}" }
+ %span.symbol.three.columns.centered
+ - fee_count = enterprise.enterprise_fees.count
+ - if fee_count > 0
+ %span.icon-ok-sign.with-tip{ title: "#{fee_count} Fee#{fee_count > 1 ? "s" : ""}" }
+ - else
+ %span.icon-warning-sign.with-tip{ title: "#{enterprise.name} has no Enterprise Fees" }
+ %span.two.columns.omega.right
+ %span.icon-arrow-right
+ %div.producers_tab{ ng: { show: "activeTab == 'producers'"} }
+ %div.list-title.sixteen.columns.alpha
+ %span.five.columns.alpha Name
+ %span.centered.three.columns Total Products
+ %span.centered.three.columns Active Products
+ %span.centered.three.columns Products in OCs
+ %div.sixteen.columns.alpha.list
+ - @enterprises.is_primary_producer.each do |enterprise|
+ %a.sixteen.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(enterprise)}" }
+ %span.five.columns.alpha
+ = enterprise.name
+ %span.symbol.three.columns.centered
+ %span.one.column.alpha
+ %span.text-icon.one.column.centered{ class: "#{enterprise.supplied_products.not_deleted.count > 0 ? "green" : "red" }" }
+ = enterprise.supplied_products.not_deleted.count
+ %span.one.column.omega
+ %span.symbol.three.columns.centered
+ %span.one.column.alpha
+ %span.text-icon.one.column.centered{ class: "#{enterprise.supplied_and_active_products_on_hand.count > 0 ? "green" : "red" }" }
+ = enterprise.supplied_and_active_products_on_hand.count
+ %span.one.column.omega
+ %span.symbol.three.columns.centered
+ %span.one.column.alpha
+ %span.text-icon.one.column.centered{ class: "#{enterprise.active_products_in_order_cycles.count > 0 ? "green" : "orange" }" }
+ = enterprise.active_products_in_order_cycles.count
+ %span.one.column.omega
+ %span.two.columns.omega.right
+ %span.icon-arrow-right
%a.sixteen.columns.alpha.button.bottom.blue{ href: "#{main_app.admin_enterprises_path}" }
MANAGE MY ENTERPRISES
%span.icon-arrow-right
\ No newline at end of file
diff --git a/app/views/spree/admin/products/_primary_taxon_form.html.haml b/app/views/spree/admin/products/_primary_taxon_form.html.haml
new file mode 100644
index 0000000000..a844f4f447
--- /dev/null
+++ b/app/views/spree/admin/products/_primary_taxon_form.html.haml
@@ -0,0 +1,5 @@
+= f.field_container :primary_taxon_id do
+ = f.label :primary_taxon_id
+ %br
+ = f.collection_select(:primary_taxon_id, Spree::Taxon.all, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"})
+ = f.error_message_on :primary_taxon_id
diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml
index ee74fd9662..fa295adb47 100644
--- a/app/views/spree/admin/products/bulk_edit.html.haml
+++ b/app/views/spree/admin/products/bulk_edit.html.haml
@@ -132,7 +132,7 @@
%td{ 'ng-show' => 'columns.on_hand.visible' }
%span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' }
%input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' }
- %td{ 'ng-show' => 'columns.taxons.visible' }
+ %td{ 'ng-if' => 'columns.taxons.visible' }
%input.fullwidth{ :type => 'text', 'ng-model' => 'product.taxon_ids', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'taxon_ids' }
%td{ 'ng-show' => 'columns.available_on.visible' }
%input{ 'ng-model' => 'product.available_on', :name => 'available_on', 'ofn-track-product' => 'available_on', 'datetimepicker' => 'product.available_on', type: "text" }
diff --git a/app/views/spree/admin/shared/_tabs.html.erb b/app/views/spree/admin/shared/_tabs.html.erb
index ab66dbf1de..2228d4ac7b 100644
--- a/app/views/spree/admin/shared/_tabs.html.erb
+++ b/app/views/spree/admin/shared/_tabs.html.erb
@@ -1,4 +1,4 @@
-<%= tab :overview, :route => :admin, :icon => 'icon-dashboard' %>
+<%= tab :dashboard, :route => :admin, :icon => 'icon-dashboard' %>
<%= tab :orders, :payments, :creditcard_payments, :shipments, :credit_cards, :return_authorizations, :url => admin_orders_path('q[s]' => 'completed_at desc'), :icon => 'icon-shopping-cart' %>
<%= tab :products , :option_types, :properties, :prototypes, :variants, :product_properties, :taxons, :url => bulk_edit_admin_products_path, :icon => 'icon-th-large' %>
<%= tab :reports, :icon => 'icon-file' %>
diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml
index 559a0227f1..b6ff00925d 100644
--- a/app/views/spree/shared/_order_details.html.haml
+++ b/app/views/spree/shared/_order_details.html.haml
@@ -16,8 +16,6 @@
.columns.large-2
%h6
= t(:shipping_method)
- \:
- = link_to "(#{t(:edit)})", checkout_state_path(:delivery) unless @order.completed?
.delivery
= order.shipping_method.name
.columns.large-4
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
new file mode 100644
index 0000000000..c40b9a4130
--- /dev/null
+++ b/config/initializers/paperclip.rb
@@ -0,0 +1,3 @@
+Paperclip::Attachment.default_options[:convert_options] = {
+ all: "-auto-orient"
+}
diff --git a/db/migrate/20140402033428_add_foreign_keys.rb b/db/migrate/20140402033428_add_foreign_keys.rb
index f072127158..fce1ffd009 100644
--- a/db/migrate/20140402033428_add_foreign_keys.rb
+++ b/db/migrate/20140402033428_add_foreign_keys.rb
@@ -32,6 +32,11 @@ class AddForeignKeys < ActiveRecord::Migration
say "Destroying #{orphaned_exchange_variants.count} orphaned ExchangeVariants (of total #{ExchangeVariant.count})"
orphaned_exchange_variants.destroy_all
+ # Remove orphaned ExchangeFee records
+ orphaned_exchange_fees = ExchangeFee.joins('LEFT OUTER JOIN enterprise_fees ON enterprise_fees.id=exchange_fees.enterprise_fee_id').where('enterprise_fees.id IS NULL')
+ say "Destroying #{orphaned_exchange_fees.count} orphaned ExchangeFees (of total #{ExchangeFee.count})"
+ orphaned_exchange_fees.destroy_all
+
# Remove orphaned Spree::InventoryUnits
orphaned_inventory_units = Spree::InventoryUnit.joins('LEFT OUTER JOIN spree_variants ON spree_variants.id=spree_inventory_units.variant_id').where('spree_variants.id IS NULL')
say "Destroying #{orphaned_inventory_units.count} orphaned InventoryUnits (of total #{Spree::InventoryUnit.count})"
diff --git a/db/migrate/20140522044009_add_primary_taxon_to_products.rb b/db/migrate/20140522044009_add_primary_taxon_to_products.rb
new file mode 100644
index 0000000000..f6ff3cfe96
--- /dev/null
+++ b/db/migrate/20140522044009_add_primary_taxon_to_products.rb
@@ -0,0 +1,7 @@
+class AddPrimaryTaxonToProducts < ActiveRecord::Migration
+ def change
+ add_column :spree_products, :primary_taxon_id, :integer
+ add_index :spree_products, :primary_taxon_id
+ add_foreign_key :spree_products, :spree_taxons, column: :primary_taxon_id
+ end
+end
diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb
index dc5056d53a..6556223dac 100644
--- a/lib/open_food_network/order_cycle_form_applicator.rb
+++ b/lib/open_food_network/order_cycle_form_applicator.rb
@@ -5,8 +5,10 @@ module OpenFoodNetwork
# translation is more a responsibility of Angular, so I'd be inclined to refactor this class to move
# as much as possible (if not all) of its logic into Angular.
class OrderCycleFormApplicator
- def initialize(order_cycle)
+ # The applicator will only touch exchanges where a permitted enterprise is the participant
+ def initialize(order_cycle, permitted_enterprises)
@order_cycle = order_cycle
+ @permitted_enterprises = permitted_enterprises
end
def go!
@@ -56,19 +58,25 @@ module OpenFoodNetwork
def add_exchange(sender_id, receiver_id, incoming, attrs={})
attrs = attrs.reverse_merge(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming)
- exchange = @order_cycle.exchanges.create! attrs
- @touched_exchanges << exchange
+ exchange = @order_cycle.exchanges.build attrs
+
+ if permission_for exchange
+ exchange.save!
+ @touched_exchanges << exchange
+ end
end
def update_exchange(sender_id, receiver_id, incoming, attrs={})
exchange = @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming).first
- exchange.update_attributes!(attrs)
- @touched_exchanges << exchange
+ if permission_for exchange
+ exchange.update_attributes!(attrs)
+ @touched_exchanges << exchange
+ end
end
def destroy_untouched_exchanges
- untouched_exchanges.each { |exchange| exchange.destroy }
+ with_permission(untouched_exchanges).each(&:destroy)
end
def untouched_exchanges
@@ -76,6 +84,14 @@ module OpenFoodNetwork
@order_cycle.exchanges.reject { |ex| touched_exchange_ids.include? ex.id }
end
+ def with_permission(exchanges)
+ exchanges.select { |ex| permission_for(ex) }
+ end
+
+ def permission_for(exchange)
+ @permitted_enterprises.include? exchange.participant
+ end
+
def exchange_variant_ids(exchange)
exchange[:variants].select { |k, v| v }.keys.map { |k| k.to_i }
diff --git a/lib/tasks/users.rake b/lib/tasks/users.rake
new file mode 100644
index 0000000000..323c679fb0
--- /dev/null
+++ b/lib/tasks/users.rake
@@ -0,0 +1,86 @@
+require 'csv'
+
+namespace :openfoodnetwork do
+
+ namespace :dev do
+ desc 'export users to CSV'
+ task export_users: :environment do
+ CSV.open('db/users.csv', 'wb') do |csv|
+ csv << header
+ users.each do |user|
+ csv << row(user)
+ end
+ end
+ end
+
+
+ desc 'import users from CSV'
+ task import_users: :environment do
+ ActionMailer::Base.delivery_method = :test
+
+ CSV.foreach('db/users.csv') do |row|
+ next if row[0] == 'encrypted_password'
+
+ create_user_from row
+ end
+ end
+
+
+ private
+
+ def users
+ # Skip some spambot users
+ Spree::User.all.reject { |u| u.email =~ /example.net/ }
+ end
+
+
+ def header
+ ["encrypted_password", "password_salt", "email", "remember_token", "persistence_token", "reset_password_token", "perishable_token", "sign_in_count", "failed_attempts", "last_request_at", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "login", "created_at", "updated_at", "authentication_token", "unlock_token", "locked_at", "remember_created_at", "reset_password_sent_at",
+
+ "role_name",
+
+ "ship_address_firstname", "ship_address_lastname", "ship_address_address1", "ship_address_address2", "ship_address_city", "ship_address_zipcode", "ship_address_phone", "ship_address_state", "ship_address_country", "ship_address_created_at", "ship_address_updated_at", "ship_address_company",
+
+ "bill_address_firstname", "bill_address_lastname", "bill_address_address1", "bill_address_address2", "bill_address_city", "bill_address_zipcode", "bill_address_phone", "bill_address_state", "bill_address_country", "bill_address_created_at", "bill_address_updated_at", "bill_address_company",]
+ end
+
+
+ def row(user)
+ sa = user.orders.last.andand.ship_address
+ ba = user.orders.last.andand.bill_address
+
+ [user.encrypted_password, user.password_salt, user.email, user.remember_token, user.persistence_token, user.reset_password_token, user.perishable_token, user.sign_in_count, user.failed_attempts, user.last_request_at, user.current_sign_in_at, user.last_sign_in_at, user.current_sign_in_ip, user.last_sign_in_ip, user.login, user.created_at, user.updated_at, user.authentication_token, user.unlock_token, user.locked_at, user.remember_created_at, user.reset_password_sent_at,
+
+ user.spree_roles.first.andand.name,
+
+ sa.andand.firstname, sa.andand.lastname, sa.andand.address1, sa.andand.address2, sa.andand.city, sa.andand.zipcode, sa.andand.phone, sa.andand.state, sa.andand.country, sa.andand.created_at, sa.andand.updated_at, sa.andand.company,
+
+ ba.andand.firstname, ba.andand.lastname, ba.andand.address1, ba.andand.address2, ba.andand.city, ba.andand.zipcode, ba.andand.phone, ba.andand.state, ba.andand.country, ba.andand.created_at, ba.andand.updated_at, ba.andand.company,]
+ end
+
+
+ def create_user_from(row)
+ user = Spree::User.create!({password: 'changeme123', password_confirmation: 'changeme123', email: row[2], remember_token: row[3], persistence_token: row[4], reset_password_token: row[5], perishable_token: row[6], sign_in_count: row[7], failed_attempts: row[8], last_request_at: row[9], current_sign_in_at: row[10], last_sign_in_at: row[11], current_sign_in_ip: row[12], last_sign_in_ip: row[13], login: row[14], created_at: row[15], updated_at: row[16], authentication_token: row[17], unlock_token: row[18], locked_at: row[19], remember_created_at: row[20], reset_password_sent_at: row[21]}, without_protection: true)
+
+ user.update_column :encrypted_password, row[0]
+ user.update_column :password_salt, row[1]
+
+ # Safer if we don't make new users into admins
+ #role = Spree::Role.find_by_name row[24]
+ #user.spree_roles << role if role
+
+ sa_state = Spree::State.find_by_name row[30]
+ sa_country = Spree::Country.find_by_name row[31]
+ sa = Spree::Address.create!({firstname: row[23], lastname: row[24], address1: row[25], address2: row[26], city: row[27], zipcode: row[28], phone: row[29], state: sa_state, country: sa_country, created_at: row[32], updated_at: row[33], company: row[34]}, without_protection: true)
+ user.update_column :ship_address_id, sa.id
+
+ ba_state = Spree::State.find_by_name row[42]
+ ba_country = Spree::Country.find_by_name row[43]
+ ba = Spree::Address.create!({firstname: row[35], lastname: row[36], address1: row[37], address2: row[38], city: row[39], zipcode: row[40], phone: row[41], state: ba_state, country: ba_country, created_at: row[44], updated_at: row[45], company: row[46]}, without_protection: true)
+ user.update_column :bill_address_id, ba.id
+
+ rescue ActiveRecord::RecordInvalid => e
+ puts "#{row[2]} - #{e.message}"
+ end
+ end
+end
diff --git a/spec/controllers/spree/store_controller_spec.rb b/spec/controllers/spree/store_controller_spec.rb
new file mode 100644
index 0000000000..e1b94de185
--- /dev/null
+++ b/spec/controllers/spree/store_controller_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe Spree::StoreController do
+ controller(Spree::StoreController) do
+ before_filter :unauthorized
+ def index
+ render text: ""
+ end
+ end
+ it "redirects to home when unauthorized" do
+ get :index
+ response.should render_template("shared/unauthorized", layout: 'darkswarm')
+ end
+end
diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb
index 79981bbd16..963cd04f8d 100644
--- a/spec/features/admin/bulk_order_management_spec.rb
+++ b/spec/features/admin/bulk_order_management_spec.rb
@@ -206,7 +206,7 @@ feature %q{
page.should have_selector "tr#li_#{li2.id}", visible: true
select2_select s1.name, from: "supplier_filter"
page.should have_selector "tr#li_#{li1.id}", visible: true
- page.should_not have_selector "tr#li_#{li2.id}", visible: true
+ page.should_not have_selector "tr#li_#{li2.id}"
end
it "displays all line items when 'All' is selected from supplier filter" do
diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb
index 7174da9758..d89280df80 100644
--- a/spec/features/admin/bulk_product_update_spec.rb
+++ b/spec/features/admin/bulk_product_update_spec.rb
@@ -236,9 +236,7 @@ feature %q{
fill_in 'product_name', :with => 'Big Bag Of Apples'
select(s.name, :from => 'product_supplier_id')
- choose('product_group_buy_0')
fill_in 'product_price', :with => '10.00'
- fill_in 'product_available_on', :with => Date.today.strftime("%Y/%m/%d")
click_button 'Create'
URI.parse(current_url).path.should == '/admin/products/bulk_edit'
diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb
index ea95f86ba4..1443cd4c34 100644
--- a/spec/features/admin/enterprise_relationships_spec.rb
+++ b/spec/features/admin/enterprise_relationships_spec.rb
@@ -7,70 +7,108 @@ feature %q{
include AuthenticationWorkflow
include WebHelper
- before { login_to_admin_section }
+
+ context "as a site administrator" do
+ before { login_to_admin_section }
+
+ scenario "listing relationships" do
+ # Given some enterprises with relationships
+ e1, e2, e3, e4 = create(:enterprise), create(:enterprise), create(:enterprise), create(:enterprise)
+ create(:enterprise_relationship, parent: e1, child: e2)
+ create(:enterprise_relationship, parent: e3, child: e4)
+
+ # When I go to the relationships page
+ click_link 'Enterprises'
+ click_link 'Relationships'
+
+ # Then I should see the relationships
+ within('table#enterprise-relationships') do
+ page.should have_relationship e1, e2
+ page.should have_relationship e3, e4
+ end
+ end
- scenario "listing relationships" do
- # Given some enterprises with relationships
- e1, e2, e3, e4 = create(:enterprise), create(:enterprise), create(:enterprise), create(:enterprise)
- create(:enterprise_relationship, parent: e1, child: e2)
- create(:enterprise_relationship, parent: e3, child: e4)
+ scenario "creating a relationship" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
- # When I go to the relationships page
- click_link 'Enterprises'
- click_link 'Relationships'
+ visit admin_enterprise_relationships_path
+ select 'One', from: 'enterprise_relationship_parent_id'
+ select 'Two', from: 'enterprise_relationship_child_id'
+ click_button 'Create'
- # Then I should see the relationships
- within('table#enterprise-relationships') do
- page.should have_table_row [e1.name, 'permits', e2.name, '']
- page.should have_table_row [e3.name, 'permits', e4.name, '']
+ page.should have_relationship e1, e2
+ EnterpriseRelationship.where(parent_id: e1, child_id: e2).should be_present
+ end
+
+
+ scenario "attempting to create a relationship with invalid data" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
+ create(:enterprise_relationship, parent: e1, child: e2)
+
+ expect do
+ # When I attempt to create a duplicate relationship
+ visit admin_enterprise_relationships_path
+ select 'One', from: 'enterprise_relationship_parent_id'
+ select 'Two', from: 'enterprise_relationship_child_id'
+ click_button 'Create'
+
+ # Then I should see an error message
+ page.should have_content "That relationship is already established."
+ end.to change(EnterpriseRelationship, :count).by(0)
+ end
+
+
+ scenario "deleting a relationship" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
+ er = create(:enterprise_relationship, parent: e1, child: e2)
+
+ visit admin_enterprise_relationships_path
+ page.should have_relationship e1, e2
+
+ first("a.delete-enterprise-relationship").click
+
+ page.should_not have_relationship e1, e2
+ EnterpriseRelationship.where(id: er.id).should be_empty
end
end
- scenario "creating a relationship" do
- e1 = create(:enterprise, name: 'One')
- e2 = create(:enterprise, name: 'Two')
+ context "as an enterprise user" do
+ let!(:d1) { create(:distributor_enterprise) }
+ let!(:d2) { create(:distributor_enterprise) }
+ let!(:d3) { create(:distributor_enterprise) }
+ let(:enterprise_user) { create_enterprise_user([d1]) }
- visit admin_enterprise_relationships_path
- select 'One', from: 'enterprise_relationship_parent_name'
- select 'Two', from: 'enterprise_relationship_child_name'
- click_button 'Create'
+ let!(:er1) { create(:enterprise_relationship, parent: d1, child: d2) }
+ let!(:er2) { create(:enterprise_relationship, parent: d2, child: d1) }
+ let!(:er3) { create(:enterprise_relationship, parent: d2, child: d3) }
- page.should have_table_row [e1.name, 'permits', e2.name, '']
- EnterpriseRelationship.where(parent_id: e1, child_id: e2).should be_present
- end
+ before { login_to_admin_as enterprise_user }
-
- scenario "attempting to create a relationship with invalid data" do
- e1 = create(:enterprise, name: 'One')
- e2 = create(:enterprise, name: 'Two')
- create(:enterprise_relationship, parent: e1, child: e2)
-
- expect do
- # When I attempt to create a duplicate relationship
+ scenario "enterprise user can only see relationships involving their enterprises" do
visit admin_enterprise_relationships_path
- select 'One', from: 'enterprise_relationship_parent_name'
- select 'Two', from: 'enterprise_relationship_child_name'
- click_button 'Create'
- # Then I should see an error message
- page.should have_content "That relationship is already established."
- end.to change(EnterpriseRelationship, :count).by(0)
+ page.should have_relationship d1, d2
+ page.should have_relationship d2, d1
+ page.should_not have_relationship d2, d3
+ end
+
+
+ scenario "enterprise user can only add their own enterprises as parent" do
+ visit admin_enterprise_relationships_path
+ page.should have_select 'enterprise_relationship_parent_id', options: ['', d1.name]
+ page.should have_select 'enterprise_relationship_child_id', options: ['', d1.name, d2.name, d3.name]
+ end
end
- scenario "deleting a relationship" do
- e1 = create(:enterprise, name: 'One')
- e2 = create(:enterprise, name: 'Two')
- er = create(:enterprise_relationship, parent: e1, child: e2)
+ private
- visit admin_enterprise_relationships_path
- page.should have_table_row [e1.name, 'permits', e2.name, '']
-
- first("a.delete-enterprise-relationship").click
-
- page.should_not have_table_row [e1.name, 'permits', e2.name, '']
- EnterpriseRelationship.where(id: er.id).should be_empty
+ def have_relationship(parent, child)
+ have_table_row [parent.name, 'permits', child.name, '']
end
end
diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb
index 6cb3a501a4..2423a6dc05 100644
--- a/spec/features/admin/order_cycles_spec.rb
+++ b/spec/features/admin/order_cycles_spec.rb
@@ -514,6 +514,15 @@ feature %q{
# I should not see exchanges for supplier2 or distributor2
page.all('tr.supplier').count.should == 1
page.all('tr.distributor').count.should == 1
+
+ # When I save, then those exchanges should remain
+ click_button 'Update'
+ page.should have_content "Your order cycle has been updated."
+
+ oc.reload
+ oc.suppliers.sort.should == [supplier1, supplier2]
+ oc.coordinator.should == supplier1
+ oc.distributors.sort.should == [distributor1, distributor2]
end
scenario "cloning an order cycle" do
diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb
index b9f9d7a206..efe531224d 100644
--- a/spec/features/admin/products_spec.rb
+++ b/spec/features/admin/products_spec.rb
@@ -46,23 +46,20 @@ feature %q{
product.product_distributions.map { |pd| pd.enterprise_fee }.sort.should == [@enterprise_fees[0], @enterprise_fees[2]].sort
end
+ scenario "making a product into a group buy product" do
+ product = create(:simple_product, name: 'group buy product')
- scenario "creating a group buy product" do
login_to_admin_section
- click_link 'Products'
- click_link 'New Product'
+ visit spree.edit_admin_product_path(product)
- fill_in 'product_name', :with => 'A new product !!!'
- fill_in 'product_price', :with => '19.99'
- select 'New supplier', :from => 'product_supplier_id'
choose 'product_group_buy_1'
fill_in 'Group buy unit size', :with => '10'
- click_button 'Create'
+ click_button 'Update'
- flash_message.should == 'Product "A new product !!!" has been successfully created!'
- product = Spree::Product.find_by_name('A new product !!!')
+ flash_message.should == 'Product "group buy product" has been successfully updated!'
+ product.reload
product.group_buy.should be_true
product.group_buy_unit_size.should == 10.0
end
diff --git a/spec/helpers/shared_helper_spec.rb b/spec/helpers/shared_helper_spec.rb
index d28a6eb7b3..92c688d1ae 100644
--- a/spec/helpers/shared_helper_spec.rb
+++ b/spec/helpers/shared_helper_spec.rb
@@ -23,37 +23,4 @@ describe SharedHelper do
helper.stub(:current_order) { order }
helper.distributor_link_class(d1).should =~ /empties-cart/
end
-
- describe "finding current producers" do
- it "finds producers for the current distribution" do
- s = create(:supplier_enterprise)
- d = create(:distributor_enterprise)
- p = create(:simple_product)
- oc = create(:simple_order_cycle, suppliers: [s], distributors: [d], variants: [p.master])
-
- helper.stub(:current_order_cycle) { oc }
- helper.stub(:current_distributor) { d }
-
- helper.current_producers.should == [s]
- end
-
- it "returns [] when no order cycle set" do
- d = double(:distributor)
-
- helper.stub(:current_order_cycle) { nil }
- helper.stub(:current_distributor) { d }
-
- helper.current_producers.should == []
- end
-
- it "returns [] when no distributor set" do
- oc = double(:order_cycle)
-
- helper.stub(:current_order_cycle) { oc }
- helper.stub(:current_distributor) { nil }
-
- helper.current_producers.should == []
-
- end
- end
end
diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js
index 9e821754de..df629c1c81 100644
--- a/spec/javascripts/application_spec.js
+++ b/spec/javascripts/application_spec.js
@@ -1,6 +1,7 @@
//= require angular
//= require angular-resource
//= require angular-animate
+//= require angular-sanitize
//= require angular-mocks
//= require angular-cookies
//= require angular-backstretch.js
diff --git a/spec/javascripts/unit/darkswarm/controllers/checkout/accordion_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/checkout/accordion_controller_spec.js.coffee
index 1c0c199351..d35fb93e86 100644
--- a/spec/javascripts/unit/darkswarm/controllers/checkout/accordion_controller_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/controllers/checkout/accordion_controller_spec.js.coffee
@@ -1,19 +1,40 @@
describe "AccordionCtrl", ->
ctrl = null
- scope = scope
+ scope = null
beforeEach ->
module "Darkswarm"
localStorage.clear()
- inject ($controller, $rootScope) ->
- scope = $rootScope.$new()
- scope.order =
- id: 129
- ctrl = $controller 'AccordionCtrl', {$scope: scope}
- it "defaults the details accordion to visible", ->
- expect(scope.accordion.details).toEqual true
+ describe "loading incomplete form", ->
+ beforeEach ->
+ inject ($controller, $rootScope) ->
+ scope = $rootScope.$new()
+ scope.order =
+ id: 129
+ ctrl = $controller 'AccordionCtrl', {$scope: scope}
- it "changes accordion", ->
- scope.show "shipping"
- expect(scope.accordion["shipping"]).toEqual true
+ it "defaults the details accordion to visible", ->
+ expect(scope.accordion.details).toEqual true
+
+ it "changes accordion", ->
+ scope.show "shipping"
+ expect(scope.accordion["shipping"]).toEqual true
+
+ describe "loading complete form", ->
+ beforeEach ->
+ inject ($controller, $rootScope) ->
+ scope = $rootScope.$new()
+ scope.checkout =
+ $valid: true
+ scope.order =
+ id: 129
+ ctrl = $controller 'AccordionCtrl', {$scope: scope}
+
+ it "automatically closes all sections if the entire form is valid", ->
+ waitsFor ->
+ (scope.accordion.details and
+ scope.accordion.shipping and
+ scope.accordion.payment and
+ scope.accordion.billing) == false
+ , "the accordion to close", 100
diff --git a/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee
new file mode 100644
index 0000000000..f34df5cc71
--- /dev/null
+++ b/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee
@@ -0,0 +1,16 @@
+describe 'filtering urls', ->
+ filter = null
+
+ beforeEach ->
+ module 'Darkswarm'
+ inject ($filter) ->
+ filter = $filter('stripUrl')
+
+ it "removes http and www", ->
+ expect(filter("http://www.footle.com")).toEqual "footle.com"
+
+ it "removes https and www", ->
+ expect(filter("https://www.footle.com")).toEqual "footle.com"
+
+ it "removes just www", ->
+ expect(filter("www.footle.com")).toEqual "footle.com"
diff --git a/spec/javascripts/unit/darkswarm/services/order_cycle_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/order_cycle_spec.js.coffee
index f7a65867e8..93dd7de79c 100644
--- a/spec/javascripts/unit/darkswarm/services/order_cycle_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/services/order_cycle_spec.js.coffee
@@ -21,14 +21,14 @@ describe 'OrderCycle service', ->
$httpBackend.expectPOST("/shop/order_cycle", {"order_cycle_id" : 10}).respond(200)
spyOn(mockProduct, "update")
OrderCycle.order_cycle.order_cycle_id = 10
- OrderCycle.push_order_cycle()
+ OrderCycle.push_order_cycle mockProduct.update
$httpBackend.flush()
expect(mockProduct.update).toHaveBeenCalled()
it "updates the orders_close_at attr after update", ->
datestring = "2013-12-20T00:00:00+11:00"
$httpBackend.expectPOST("/shop/order_cycle").respond({orders_close_at: datestring})
- OrderCycle.push_order_cycle()
+ OrderCycle.push_order_cycle mockProduct.update
$httpBackend.flush()
expect(OrderCycle.order_cycle.orders_close_at).toEqual(datestring)
diff --git a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
index fedcf23629..4f6c711f99 100644
--- a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
@@ -29,7 +29,6 @@ describe 'Order service', ->
inject ($injector, _$httpBackend_)->
$httpBackend = _$httpBackend_
- $httpBackend.expectGET("/shop/products").respond 200, []
Order = $injector.get("Order")
Navigation = $injector.get("Navigation")
flash = $injector.get("flash")
@@ -77,7 +76,6 @@ describe 'Order service', ->
$httpBackend.flush()
expect(Order.errors).toEqual {error: "frogs"}
-
it "Munges the order attributes to add _attributes as Rails needs", ->
expect(Order.preprocess().bill_address_attributes).not.toBe(undefined)
expect(Order.preprocess().bill_address).toBe(undefined)
diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb
index 81cd925569..c63d1eac25 100644
--- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb
+++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb
@@ -11,7 +11,7 @@ module OpenFoodNetwork
oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => [])
- applicator = OrderCycleFormApplicator.new(oc)
+ 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, true).and_return(false)
@@ -29,7 +29,7 @@ module OpenFoodNetwork
oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange])
- applicator = OrderCycleFormApplicator.new(oc)
+ 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, false).and_return(false)
@@ -51,7 +51,7 @@ module OpenFoodNetwork
:incoming_exchanges => [incoming_exchange],
:outgoing_exchanges => [])
- applicator = OrderCycleFormApplicator.new(oc)
+ 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, true).and_return(true)
@@ -73,7 +73,7 @@ module OpenFoodNetwork
:incoming_exchanges => [],
:outgoing_exchanges => [outgoing_exchange])
- applicator = OrderCycleFormApplicator.new(oc)
+ 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, false).and_return(true)
@@ -95,7 +95,7 @@ module OpenFoodNetwork
:incoming_exchanges => [],
:outgoing_exchanges => [])
- applicator = OrderCycleFormApplicator.new(oc)
+ applicator = OrderCycleFormApplicator.new(oc, [])
applicator.should_receive(:destroy_untouched_exchanges)
@@ -108,20 +108,61 @@ module OpenFoodNetwork
e2 = double(:exchange2, id: 1, foo: 2)
oc = double(:order_cycle, :exchanges => [e1])
- applicator = OrderCycleFormApplicator.new(oc)
+ applicator = OrderCycleFormApplicator.new(oc, [])
applicator.instance_eval do
@touched_exchanges = [e2]
end
applicator.send(:untouched_exchanges).should == []
end
+
+ it "does not destroy exchanges involving enterprises it does not have permission to touch" do
+ applicator = OrderCycleFormApplicator.new(nil, [])
+ exchanges = double(:exchanges)
+ permitted_exchanges = [double(:exchange), double(:exchange)]
+
+ applicator.should_receive(:with_permission).with(exchanges) { permitted_exchanges }
+ applicator.stub(:untouched_exchanges) { exchanges }
+ permitted_exchanges.each { |ex| ex.should_receive(:destroy) }
+
+ applicator.send(:destroy_untouched_exchanges)
+ end
end
it "converts exchange variant ids hash to an array of ids" do
- applicator = OrderCycleFormApplicator.new(nil)
+ applicator = OrderCycleFormApplicator.new(nil, [])
applicator.send(:exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}).should == [1, 3]
end
+
+ describe "filtering exchanges for permission" do
+ describe "checking permission on a single exchange" do
+ it "returns true when it has permission" do
+ e = double(:enterprise)
+ ex = double(:exchange, participant: e)
+
+ applicator = OrderCycleFormApplicator.new(nil, [e])
+ applicator.send(:permission_for, ex).should be_true
+ end
+
+ it "returns false otherwise" do
+ e = double(:enterprise)
+ ex = double(:exchange, participant: e)
+
+ applicator = OrderCycleFormApplicator.new(nil, [])
+ applicator.send(:permission_for, ex).should be_false
+ end
+ end
+
+ describe "filtering many exchanges" do
+ it "returns exchanges involving enterprises we have permission to touch" do
+ ex1, ex2 = double(:exchange), double(:exchange)
+ applicator = OrderCycleFormApplicator.new(nil, [])
+ applicator.stub(:permission_for).and_return(true, false)
+ applicator.send(:with_permission, [ex1, ex2]).should == [ex1]
+ end
+ end
+ end
end
context "integration specs" do
@@ -132,7 +173,7 @@ module OpenFoodNetwork
it "checks whether exchanges exist" do
oc = FactoryGirl.create(:simple_order_cycle)
exchange = FactoryGirl.create(:exchange, order_cycle: oc)
- applicator = OrderCycleFormApplicator.new(oc)
+ applicator = OrderCycleFormApplicator.new(oc, [])
applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, exchange.incoming).should be_true
applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, !exchange.incoming).should be_false
@@ -143,10 +184,10 @@ module OpenFoodNetwork
end
it "adds exchanges" do
- oc = FactoryGirl.create(:simple_order_cycle)
- applicator = OrderCycleFormApplicator.new(oc)
sender = FactoryGirl.create(:enterprise)
receiver = FactoryGirl.create(:enterprise)
+ oc = FactoryGirl.create(:simple_order_cycle)
+ applicator = OrderCycleFormApplicator.new(oc, [sender, receiver])
incoming = true
variant1 = FactoryGirl.create(:variant)
variant2 = FactoryGirl.create(:variant)
@@ -167,10 +208,11 @@ module OpenFoodNetwork
end
it "updates exchanges" do
- oc = FactoryGirl.create(:simple_order_cycle)
- applicator = OrderCycleFormApplicator.new(oc)
sender = FactoryGirl.create(:enterprise)
receiver = FactoryGirl.create(:enterprise)
+ oc = FactoryGirl.create(:simple_order_cycle)
+ applicator = OrderCycleFormApplicator.new(oc, [sender, receiver])
+
incoming = true
variant1 = FactoryGirl.create(:variant)
variant2 = FactoryGirl.create(:variant)
@@ -189,6 +231,34 @@ module OpenFoodNetwork
exchange.enterprise_fees.sort.should == [enterprise_fee2, enterprise_fee3]
applicator.send(:touched_exchanges).should == [exchange]
end
+
+ it "does not add exchanges it is not permitted to touch" do
+ sender = FactoryGirl.create(:enterprise)
+ receiver = FactoryGirl.create(:enterprise)
+ oc = FactoryGirl.create(:simple_order_cycle)
+ applicator = OrderCycleFormApplicator.new(oc, [])
+ incoming = true
+
+ expect do
+ applicator.send(:touched_exchanges=, [])
+ applicator.send(:add_exchange, sender.id, receiver.id, incoming)
+ end.to change(Exchange, :count).by(0)
+ end
+
+ it "does not update exchanges it is not permitted to touch" do
+ sender = FactoryGirl.create(:enterprise)
+ receiver = FactoryGirl.create(:enterprise)
+ oc = FactoryGirl.create(:simple_order_cycle)
+ applicator = OrderCycleFormApplicator.new(oc, [])
+ incoming = true
+ exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming)
+ variant1 = FactoryGirl.create(:variant)
+
+ applicator.send(:touched_exchanges=, [])
+ applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id]})
+
+ exchange.variants.should_not == [variant1]
+ end
end
end
end
diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb
index 7cca07472e..b715674594 100644
--- a/spec/models/enterprise_relationship_spec.rb
+++ b/spec/models/enterprise_relationship_spec.rb
@@ -2,15 +2,32 @@ require 'spec_helper'
describe EnterpriseRelationship do
describe "scopes" do
+ let(:e1) { create(:enterprise, name: 'A') }
+ let(:e2) { create(:enterprise, name: 'B') }
+ let(:e3) { create(:enterprise, name: 'C') }
+
it "sorts by parent, child enterprise name" do
- e1 = create(:enterprise, name: 'A')
- e2 = create(:enterprise, name: 'B')
- e3 = create(:enterprise, name: 'C')
er1 = create(:enterprise_relationship, parent: e1, child: e3)
er2 = create(:enterprise_relationship, parent: e2, child: e1)
er3 = create(:enterprise_relationship, parent: e1, child: e2)
EnterpriseRelationship.by_name.should == [er3, er1, er2]
end
+
+ describe "finding relationships involving some enterprises" do
+ let!(:er) { create(:enterprise_relationship, parent: e1, child: e2) }
+
+ it "returns relationships where an enterprise is the parent" do
+ EnterpriseRelationship.involving_enterprises([e1]).should == [er]
+ end
+
+ it "returns relationships where an enterprise is the child" do
+ EnterpriseRelationship.involving_enterprises([e2]).should == [er]
+ end
+
+ it "does not return other relationships" do
+ EnterpriseRelationship.involving_enterprises([e3]).should == []
+ end
+ end
end
end
diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb
index f375b8890a..d93bc78dd6 100644
--- a/spec/models/enterprise_spec.rb
+++ b/spec/models/enterprise_spec.rb
@@ -338,6 +338,16 @@ describe Enterprise do
end
end
+ describe "supplied_and_active_products_on_hand" do
+ it "find only active products which are in stock" do
+ supplier = create(:supplier_enterprise)
+ inactive_product = create(:product, supplier: supplier, on_hand: 1, available_on: Date.tomorrow)
+ out_of_stock_product = create(:product, supplier: supplier, on_hand: 0, available_on: Date.yesterday)
+ p1 = create(:product, supplier: supplier, on_hand: 1, available_on: Date.yesterday)
+ supplier.supplied_and_active_products_on_hand.should == [p1]
+ end
+ end
+
describe "finding variants distributed by the enterprise" do
it "finds the master variant" do
d = create(:distributor_enterprise)
diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb
index ed6cdbfc93..0dfceadbad 100644
--- a/spec/models/exchange_spec.rb
+++ b/spec/models/exchange_spec.rb
@@ -48,7 +48,7 @@ describe Exchange do
e.enterprise_fees.count.should == 1
end
- describe "reporting whether it is an incoming exchange" do
+ describe "exchange directionality" do
let(:supplier) { create(:supplier_enterprise) }
let(:coordinator) { create(:distributor_enterprise) }
let(:distributor) { create(:distributor_enterprise) }
@@ -56,12 +56,24 @@ describe Exchange do
let(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator, incoming: true }
let(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor, incoming: false }
- it "returns true for incoming exchanges" do
- incoming_exchange.should be_incoming
+ describe "reporting whether it is an incoming exchange" do
+ it "returns true for incoming exchanges" do
+ incoming_exchange.should be_incoming
+ end
+
+ it "returns false for outgoing exchanges" do
+ outgoing_exchange.should_not be_incoming
+ end
end
- it "returns false for outgoing exchanges" do
- outgoing_exchange.should_not be_incoming
+ describe "finding the exchange participant (the enterprise other than the coordinator)" do
+ it "returns the sender for incoming exchanges" do
+ incoming_exchange.participant.should == supplier
+ end
+
+ it "returns the receiver for outgoing exchanges" do
+ outgoing_exchange.participant.should == distributor
+ end
end
end
diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb
index fc13925bae..111c8e5196 100644
--- a/spec/models/spree/ability_spec.rb
+++ b/spec/models/spree/ability_spec.rb
@@ -17,6 +17,9 @@ module Spree
let(:p1) { create(:product, supplier: s1, distributors:[d1, d2]) }
let(:p2) { create(:product, supplier: s2, distributors:[d1, d2]) }
+ let(:er1) { create(:enterprise_relationship, parent: s1, child: d1) }
+ let(:er2) { create(:enterprise_relationship, parent: d1, child: s1) }
+
subject { user }
let(:user) { nil }
@@ -72,6 +75,18 @@ module Spree
should have_ability([:admin, :index, :read, :create, :edit], for: Spree::Classification)
end
+ it "should be able to read and create enterprise relationships" do
+ should have_ability([:admin, :index, :create], for: EnterpriseRelationship)
+ end
+
+ it "should be able to destroy enterprise relationships for its enterprises" do
+ should have_ability(:destroy, for: er1)
+ end
+
+ it "should not be able to destroy enterprise relationships for other enterprises" do
+ should_not have_ability(:destroy, for: er2)
+ end
+
end
context "when is a distributor enterprise user" do
@@ -146,6 +161,18 @@ module Spree
it "should be able to read/write ShippingMethods" do
should have_ability([:admin, :index, :create, :update, :destroy], for: Spree::ShippingMethod)
end
+
+ it "should be able to read and create enterprise relationships" do
+ should have_ability([:admin, :index, :create], for: EnterpriseRelationship)
+ end
+
+ it "should be able to destroy enterprise relationships for its enterprises" do
+ should have_ability(:destroy, for: er2)
+ end
+
+ it "should not be able to destroy enterprise relationships for other enterprises" do
+ should_not have_ability(:destroy, for: er1)
+ end
end
context 'Order Cycle co-ordinator' do
diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb
index 6d90b70761..a5333b6d11 100644
--- a/spec/models/spree/product_spec.rb
+++ b/spec/models/spree/product_spec.rb
@@ -230,6 +230,20 @@ module Spree
end
end
+ describe "in_an_active_order_cycle" do
+ it "shows products in order cycle distribution" do
+ s = create(:supplier_enterprise)
+ d2 = create(:distributor_enterprise)
+ d3 = create(:distributor_enterprise)
+ p1 = create(:product)
+ p2 = create(:product)
+ p3 = create(:product)
+ oc2 = create(:simple_order_cycle, suppliers: [s], distributors: [d2], variants: [p2.master], orders_close_at: 1.day.ago)
+ oc2 = create(:simple_order_cycle, suppliers: [s], distributors: [d3], variants: [p3.master], orders_close_at: Date.tomorrow)
+ Product.in_an_active_order_cycle.should == [p3]
+ end
+ end
+
describe "access roles" do
before(:each) do
@e1 = create(:enterprise)
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 49a7f39250..4f96a09070 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -71,26 +71,20 @@ RSpec.configure do |config|
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
- # ## Filters
- #
+ # Filters
config.filter_run_excluding :skip => true, :future => true, :to_figure_out => true
- config.before(:each) do
- Spree::Address.any_instance.stub(:geocode).and_return([1,1])
+ # DatabaseCleaner
+ config.before(:suite) { DatabaseCleaner.clean_with :deletion, {except: ['spree_countries', 'spree_states']} }
+ config.before(:each) { DatabaseCleaner.strategy = :transaction }
+ config.before(:each, js: true) { DatabaseCleaner.strategy = :deletion, {except: ['spree_countries', 'spree_states']} }
+ config.before(:each) { DatabaseCleaner.start }
+ config.after(:each) { DatabaseCleaner.clean }
- if example.metadata[:js]
- DatabaseCleaner.strategy = :deletion, { :except => ['spree_countries', 'spree_states'] }
- else
- DatabaseCleaner.strategy = :transaction
- end
-
- DatabaseCleaner.start
- end
-
- config.after(:each) do
- DatabaseCleaner.clean
- end
+ # Geocoding
+ config.before(:each) { Spree::Address.any_instance.stub(:geocode).and_return([1,1]) }
+ # Helpers
config.include Rails.application.routes.url_helpers
config.include Spree::UrlHelpers
config.include Spree::CheckoutHelpers
@@ -103,7 +97,7 @@ RSpec.configure do |config|
config.include OpenFoodNetwork::DistributionHelper
config.include ActionView::Helpers::DateHelper
- # Factory girl
+ # FactoryGirl
require 'factory_girl_rails'
config.include FactoryGirl::Syntax::Methods
diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb
index 6add44834e..3a2cb82128 100644
--- a/spec/support/request/web_helper.rb
+++ b/spec/support/request/web_helper.rb
@@ -1,4 +1,22 @@
module WebHelper
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # By default, Capybara uses a 30 s wait time, which is more reliable for CI, but too slow
+ # for TDD. Use this to make tests fail fast. Usage:
+ #
+ # describe "foo" do
+ # use_short_wait
+ # ...
+ # end
+ def use_short_wait
+ around { |example| Capybara.using_wait_time(2) { example.run } }
+ end
+ end
+
+
def current_path_should_be path
current_path = URI.parse(current_url).path
current_path.should == path