mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-06 02:51:34 +00:00
Auto-merged master into uk/trial-length on deployment.
This commit is contained in:
@@ -730,6 +730,3 @@ DEPENDENCIES
|
||||
whenever
|
||||
wicked_pdf
|
||||
wkhtmltopdf-binary
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.6
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
//= require ./customers/customers
|
||||
//= require ./dropdown/dropdown
|
||||
//= require ./enterprises/enterprises
|
||||
//= require ./enterprise_fees/enterprise_fees
|
||||
//= require ./enterprise_groups/enterprise_groups
|
||||
//= require ./index_utils/index_utils
|
||||
//= require ./inventory_items/inventory_items
|
||||
//= require ./line_items/line_items
|
||||
//= require ./orders/orders
|
||||
//= require ./order_cycles/order_cycles
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
angular.module("admin.dropdown").controller "DropDownCtrl", ($scope) ->
|
||||
$scope.expanded = false
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.dropdown").directive "ofnCloseOnClick", ($document) ->
|
||||
angular.module("admin.dropdown").directive "closeOnClick", () ->
|
||||
link: (scope, element, attrs) ->
|
||||
element.click (event) ->
|
||||
event.stopPropagation()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
angular.module("admin.dropdown").directive "ofnDropDown", ($document) ->
|
||||
restrict: 'C'
|
||||
scope: true
|
||||
link: (scope, element, attrs) ->
|
||||
scope.expanded = false
|
||||
|
||||
outsideClickListener = (event) ->
|
||||
unless $(event.target).is("div.ofn-drop-down##{attrs.id} div.menu") ||
|
||||
$(event.target).parents("div.ofn-drop-down##{attrs.id} div.menu").length > 0
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
angular.module('enterprise_fees', [])
|
||||
.controller('AdminEnterpriseFeesCtrl', ['$scope', '$http', '$window', function($scope, $http, $window) {
|
||||
$scope.enterpriseFeesUrl = function() {
|
||||
var url = '/admin/enterprise_fees.json?include_calculators=1';
|
||||
|
||||
var match = $window.location.search.match(/enterprise_id=(\d+)/);
|
||||
if(match) {
|
||||
url += "&"+match[0];
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
$http.get($scope.enterpriseFeesUrl()).success(function(data) {
|
||||
$scope.enterprise_fees = data;
|
||||
|
||||
// TODO: Angular 1.1.0 will have a means to reset a form to its pristine state, which
|
||||
// would avoid the need to save off original calculator types for comparison.
|
||||
for(i in $scope.enterprise_fees) {
|
||||
$scope.enterprise_fees[i].orig_calculator_type = $scope.enterprise_fees[i].calculator_type;
|
||||
}
|
||||
});
|
||||
}])
|
||||
|
||||
.directive('ngBindHtmlUnsafeCompiled', ['$compile', function($compile) {
|
||||
return function(scope, element, attrs) {
|
||||
scope.$watch(attrs.ngBindHtmlUnsafeCompiled, function(value) {
|
||||
element.html($compile(value)(scope));
|
||||
});
|
||||
}
|
||||
}])
|
||||
|
||||
.directive('spreeDeleteResource', function() {
|
||||
return function(scope, element, attrs) {
|
||||
if(scope.enterprise_fee.id) {
|
||||
var url = "/admin/enterprise_fees/" + scope.enterprise_fee.id
|
||||
var html = '<a href="'+url+'" class="delete-resource icon_link icon-trash no-text" data-action="remove" data-confirm="Are you sure?" url="'+url+'"></a>';
|
||||
//var html = '<a href="'+url+'" class="delete-resource" data-confirm="Are you sure?"><img alt="Delete" src="/assets/admin/icons/delete.png" /> Delete</a>';
|
||||
element.append(html);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.directive('spreeEnsureCalculatorPreferencesMatchType', function() {
|
||||
// Hide calculator preference fields when calculator type changed
|
||||
// Fixes 'Enterprise fee is not found' error when changing calculator type
|
||||
// See spree/core/app/assets/javascripts/admin/calculator.js
|
||||
|
||||
// Note: For some reason, DOM --> model bindings aren't working here, so
|
||||
// we use element.val() instead of querying the model itself.
|
||||
|
||||
return function(scope, element, attrs) {
|
||||
scope.$watch(function(scope) {
|
||||
//return scope.enterprise_fee.calculator_type;
|
||||
return element.val();
|
||||
}, function(value) {
|
||||
var settings = element.parent().parent().find("div.calculator-settings");
|
||||
|
||||
// scope.enterprise_fee.calculator_type == scope.enterprise_fee.orig_calculator_type
|
||||
if(element.val() == scope.enterprise_fee.orig_calculator_type) {
|
||||
settings.show();
|
||||
settings.find("input").prop("disabled", false);
|
||||
} else {
|
||||
settings.hide();
|
||||
settings.find("input").prop("disabled", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
angular.module('admin.enterpriseFees').controller 'enterpriseFeesCtrl', ($scope, $http, $window, enterprises, tax_categories, calculators) ->
|
||||
$scope.enterprises = enterprises
|
||||
$scope.tax_categories = [{id: -1, name: "Inherit From Product"}].concat tax_categories
|
||||
$scope.calculators = calculators
|
||||
|
||||
$scope.enterpriseFeesUrl = ->
|
||||
url = '/admin/enterprise_fees.json?include_calculators=1'
|
||||
match = $window.location.search.match(/enterprise_id=(\d+)/)
|
||||
if match
|
||||
url += '&' + match[0]
|
||||
url
|
||||
|
||||
$http.get($scope.enterpriseFeesUrl()).success (data) ->
|
||||
$scope.enterprise_fees = data
|
||||
@@ -0,0 +1,6 @@
|
||||
angular.module("admin.enterpriseFees").directive 'ngBindHtmlUnsafeCompiled', ($compile) ->
|
||||
(scope, element, attrs) ->
|
||||
scope.$watch attrs.ngBindHtmlUnsafeCompiled, (value) ->
|
||||
element.html $compile(value)(scope)
|
||||
return
|
||||
return
|
||||
@@ -0,0 +1,8 @@
|
||||
angular.module('admin.enterpriseFees').directive 'spreeDeleteResource', ->
|
||||
(scope, element, attrs) ->
|
||||
if scope.enterprise_fee.id
|
||||
url = '/admin/enterprise_fees/' + scope.enterprise_fee.id
|
||||
html = '<a href="' + url + '" class="delete-resource icon_link icon-trash no-text" data-action="remove" data-confirm="Are you sure?" url="' + url + '"></a>'
|
||||
#var html = '<a href="'+url+'" class="delete-resource" data-confirm="Are you sure?"><img alt="Delete" src="/assets/admin/icons/delete.png" /> Delete</a>';
|
||||
element.append html
|
||||
return
|
||||
@@ -0,0 +1,17 @@
|
||||
angular.module("admin.enterpriseFees").directive 'spreeEnsureCalculatorPreferencesMatchType', ->
|
||||
# Hide calculator preference fields when calculator type changed
|
||||
# Fixes 'Enterprise fee is not found' error when changing calculator type
|
||||
# See spree/core/app/assets/javascripts/admin/calculator.js
|
||||
(scope, element, attrs) ->
|
||||
orig_calculator_type = scope.enterprise_fee.calculator_type
|
||||
|
||||
scope.$watch "enterprise_fee.calculator_type", (value) ->
|
||||
settings = element.parent().parent().find('div.calculator-settings')
|
||||
if value == orig_calculator_type
|
||||
settings.show()
|
||||
settings.find('input').prop 'disabled', false
|
||||
else
|
||||
settings.hide()
|
||||
settings.find('input').prop 'disabled', true
|
||||
return
|
||||
return
|
||||
@@ -0,0 +1,15 @@
|
||||
angular.module("admin.enterpriseFees").directive 'watchTaxCategory', ->
|
||||
# In order to have a nice user experience on this page, we're modelling tax_category
|
||||
# inheritance using tax_category_id = -1.
|
||||
# This directive acts as a parser for tax_category_id, storing the value the form as "" when
|
||||
# tax_category is to be inherited and setting inherits_tax_category as appropriate.
|
||||
(scope, element, attrs) ->
|
||||
scope.$watch 'enterprise_fee.tax_category_id', (value) ->
|
||||
if value == -1
|
||||
scope.enterprise_fee.inherits_tax_category = true
|
||||
element.val("")
|
||||
else
|
||||
scope.enterprise_fee.inherits_tax_category = false
|
||||
element.val(value)
|
||||
|
||||
scope.enterprise_fee.tax_category_id = -1 if scope.enterprise_fee.inherits_tax_category
|
||||
@@ -0,0 +1 @@
|
||||
angular.module("admin.enterpriseFees", ['admin.indexUtils'])
|
||||
@@ -17,6 +17,7 @@ angular.module("admin.enterprises")
|
||||
{ name: "Shipping Methods", icon_class: "icon-truck", show: "showShippingMethods()" }
|
||||
{ name: "Payment Methods", icon_class: "icon-money", show: "showPaymentMethods()" }
|
||||
{ name: "Enterprise Fees", icon_class: "icon-tasks", show: "showEnterpriseFees()" }
|
||||
{ name: "Inventory Settings", icon_class: "icon-list-ol", show: "showInventorySettings()" }
|
||||
{ name: "Shop Preferences", icon_class: "icon-shopping-cart", show: "showShopPreferences()" }
|
||||
]
|
||||
|
||||
@@ -41,5 +42,8 @@ angular.module("admin.enterprises")
|
||||
$scope.showEnterpriseFees = ->
|
||||
enterprisePermissions.can_manage_enterprise_fees && ($scope.Enterprise.sells != "none" || $scope.Enterprise.is_primary_producer)
|
||||
|
||||
$scope.showInventorySettings = ->
|
||||
$scope.Enterprise.sells != "none"
|
||||
|
||||
$scope.showShopPreferences = ->
|
||||
$scope.Enterprise.sells != "none"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Mainly useful for adding a blank option that works with AngularJS
|
||||
# Angular doesn't seem to understand the blank option generated by rails
|
||||
# using the include_blank flag on select helper.
|
||||
angular.module("admin.indexUtils").directive "ofnSelect", ->
|
||||
restrict: 'E'
|
||||
scope:
|
||||
data: "="
|
||||
replace: true
|
||||
template: (element, attrs) ->
|
||||
valueAttr = attrs.valueAttr || 'id'
|
||||
textAttr = attrs.textAttr || 'name'
|
||||
blank = if attrs.includeBlank? then "<option value=''>#{attrs.includeBlank}</option>" else ""
|
||||
return "<select ng-options='e.#{valueAttr} as e.#{textAttr} for e in data'>#{blank}</select>"
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.indexUtils").directive "ofnToggleColumn", (Columns) ->
|
||||
angular.module("admin.indexUtils").directive "toggleColumn", (Columns) ->
|
||||
link: (scope, element, attrs) ->
|
||||
element.addClass "selected" if scope.column.visible
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
angular.module("admin.indexUtils").directive "toggleView", (Views) ->
|
||||
link: (scope, element, attrs) ->
|
||||
Views.register
|
||||
element.addClass "selected" if scope.view.visible
|
||||
|
||||
element.click "click", ->
|
||||
scope.$apply ->
|
||||
Views.selectView(scope.viewKey)
|
||||
|
||||
scope.$watch "view.visible", (newValue, oldValue) ->
|
||||
element.toggleClass "selected", scope.view.visible
|
||||
@@ -1,12 +1,9 @@
|
||||
angular.module("admin.indexUtils").factory "dataFetcher", [
|
||||
"$http", "$q"
|
||||
($http, $q) ->
|
||||
return (dataLocation) ->
|
||||
deferred = $q.defer()
|
||||
$http.get(dataLocation).success((data) ->
|
||||
deferred.resolve data
|
||||
).error ->
|
||||
deferred.reject()
|
||||
angular.module("admin.indexUtils").factory "dataFetcher", ($http, $q, RequestMonitor) ->
|
||||
return (dataLocation) ->
|
||||
deferred = $q.defer()
|
||||
RequestMonitor.load $http.get(dataLocation).success((data) ->
|
||||
deferred.resolve data
|
||||
).error ->
|
||||
deferred.reject()
|
||||
|
||||
deferred.promise
|
||||
]
|
||||
deferred.promise
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
angular.module("admin.indexUtils").factory 'Views', ($rootScope) ->
|
||||
new class Views
|
||||
views: {}
|
||||
currentView: null
|
||||
|
||||
setViews: (views) =>
|
||||
@views = {}
|
||||
for key, view of views
|
||||
@views[key] = view
|
||||
@selectView(key) if view.visible
|
||||
@views
|
||||
|
||||
selectView: (selectedKey) =>
|
||||
@currentView = @views[selectedKey]
|
||||
for key, view of @views
|
||||
view.visible = (key == selectedKey)
|
||||
@@ -0,0 +1 @@
|
||||
angular.module("admin.inventoryItems", ['ngResource'])
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module("admin.inventoryItems").factory 'InventoryItemResource', ($resource) ->
|
||||
$resource('/admin/inventory_items/:id/:action.json', {}, {
|
||||
'update':
|
||||
method: 'PUT'
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
angular.module("admin.inventoryItems").factory "InventoryItems", (inventoryItems, InventoryItemResource) ->
|
||||
new class InventoryItems
|
||||
inventoryItems: {}
|
||||
errors: {}
|
||||
|
||||
constructor: ->
|
||||
for ii in inventoryItems
|
||||
@inventoryItems[ii.enterprise_id] ||= {}
|
||||
@inventoryItems[ii.enterprise_id][ii.variant_id] = new InventoryItemResource(ii)
|
||||
|
||||
setVisibility: (hub_id, variant_id, visible) ->
|
||||
if @inventoryItems[hub_id] && @inventoryItems[hub_id][variant_id]
|
||||
inventory_item = angular.extend(angular.copy(@inventoryItems[hub_id][variant_id]), {visible: visible})
|
||||
InventoryItemResource.update {id: inventory_item.id}, inventory_item, (data) =>
|
||||
@inventoryItems[hub_id][variant_id] = data
|
||||
, (response) =>
|
||||
@errors[hub_id] ||= {}
|
||||
@errors[hub_id][variant_id] = response.data.errors
|
||||
else
|
||||
InventoryItemResource.save {enterprise_id: hub_id, variant_id: variant_id, visible: visible}, (data) =>
|
||||
@inventoryItems[hub_id] ||= {}
|
||||
@inventoryItems[hub_id][variant_id] = data
|
||||
, (response) =>
|
||||
@errors[hub_id] ||= {}
|
||||
@errors[hub_id][variant_id] = response.data.errors
|
||||
@@ -1,4 +0,0 @@
|
||||
angular.module("admin.orderCycles").filter "visibleProductVariants", ->
|
||||
return (product, exchange, rules) ->
|
||||
variants = product.variants.concat( [{ "id": product.master_id}] )
|
||||
return (variant for variant in variants when variant.id in rules[exchange.enterprise_id])
|
||||
@@ -1,3 +1,3 @@
|
||||
angular.module("admin.orderCycles").filter "visibleProducts", ($filter) ->
|
||||
return (products, exchange, rules) ->
|
||||
return (product for product in products when $filter('visibleProductVariants')(product, exchange, rules).length > 0)
|
||||
return (product for product in products when $filter('visibleVariants')(product.variants, exchange, rules).length > 0)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
angular.module("admin.orderCycles").filter "visibleVariants", ->
|
||||
return (variants, exchange, rules) ->
|
||||
return (variant for variant in variants when variant.id in rules[exchange.enterprise_id])
|
||||
@@ -29,4 +29,4 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris
|
||||
when "add_to_order_cycle" then "add to order cycle"
|
||||
when "manage_products" then "manage products"
|
||||
when "edit_profile" then "edit profile"
|
||||
when "create_variant_overrides" then "override variant details"
|
||||
when "create_variant_overrides" then "add products to inventory"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
angular.module("admin.utils").directive "alertRow", ->
|
||||
restrict: "E"
|
||||
replace: true
|
||||
scope:
|
||||
message: '@'
|
||||
buttonText: '@?'
|
||||
buttonAction: '&?'
|
||||
dismissed: '=?'
|
||||
close: "&?"
|
||||
transclude: true
|
||||
templateUrl: "admin/alert_row.html"
|
||||
link: (scope, element, attrs) ->
|
||||
scope.dismissed = false
|
||||
|
||||
scope.dismiss = ->
|
||||
scope.dismissed = true
|
||||
scope.close() if scope.close?
|
||||
return false
|
||||
@@ -1,12 +1,23 @@
|
||||
angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) ->
|
||||
angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, Views, SpreeApiAuth, PagedFetcher, StatusMessage, RequestMonitor, hubs, producers, hubPermissions, InventoryItems, VariantOverrides, DirtyVariantOverrides) ->
|
||||
$scope.hubs = Indexer.index hubs
|
||||
$scope.hub = null
|
||||
$scope.hub_id = if hubs.length == 1 then hubs[0].id else null
|
||||
$scope.products = []
|
||||
$scope.producers = producers
|
||||
$scope.producersByID = Indexer.index producers
|
||||
$scope.hubPermissions = hubPermissions
|
||||
$scope.productLimit = 10
|
||||
$scope.variantOverrides = VariantOverrides.variantOverrides
|
||||
$scope.inventoryItems = InventoryItems.inventoryItems
|
||||
$scope.setVisibility = InventoryItems.setVisibility
|
||||
$scope.StatusMessage = StatusMessage
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.selectView = Views.selectView
|
||||
$scope.currentView = -> Views.currentView
|
||||
|
||||
$scope.views = Views.setViews
|
||||
inventory: { name: "Inventory Products", visible: true }
|
||||
hidden: { name: "Hidden Products", visible: false }
|
||||
new: { name: "New Products", visible: false }
|
||||
|
||||
$scope.columns = Columns.setColumns
|
||||
producer: { name: "Producer", visible: true }
|
||||
@@ -17,6 +28,9 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
on_demand: { name: "On Demand", visible: false }
|
||||
reset: { name: "Reset Stock Level", visible: false }
|
||||
inheritance: { name: "Inheritance", visible: false }
|
||||
visibility: { name: "Hide", visible: false }
|
||||
|
||||
$scope.bulkActions = [ name: "Reset Stock Levels To Defaults", callback: 'resetStock' ]
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.producerFilter = 0
|
||||
@@ -24,6 +38,9 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
|
||||
$scope.resetSelectFilters()
|
||||
|
||||
$scope.filtersApplied = ->
|
||||
$scope.producerFilter != 0 || $scope.query != ''
|
||||
|
||||
$scope.initialise = ->
|
||||
SpreeApiAuth.authorise()
|
||||
.then ->
|
||||
@@ -42,10 +59,6 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
$scope.products = $scope.products.concat products
|
||||
VariantOverrides.ensureDataFor hubs, products
|
||||
|
||||
|
||||
$scope.selectHub = ->
|
||||
$scope.hub = $scope.hubs[$scope.hub_id]
|
||||
|
||||
$scope.displayDirty = ->
|
||||
if DirtyVariantOverrides.count() > 0
|
||||
num = if DirtyVariantOverrides.count() == 1 then "one override" else "#{DirtyVariantOverrides.count()} overrides"
|
||||
|
||||
@@ -2,11 +2,11 @@ angular.module("admin.variantOverrides").directive "trackInheritance", (VariantO
|
||||
require: "ngModel"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
# This is a bit hacky, but it allows us to load the inherit property on the VO, but then not submit it
|
||||
scope.inherit = angular.equals scope.variantOverrides[scope.hub.id][scope.variant.id], VariantOverrides.newFor scope.hub.id, scope.variant.id
|
||||
scope.inherit = angular.equals scope.variantOverrides[scope.hub_id][scope.variant.id], VariantOverrides.newFor scope.hub_id, scope.variant.id
|
||||
|
||||
ngModel.$parsers.push (viewValue) ->
|
||||
if ngModel.$dirty && viewValue
|
||||
variantOverride = VariantOverrides.inherit(scope.hub.id, scope.variant.id)
|
||||
variantOverride = VariantOverrides.inherit(scope.hub_id, scope.variant.id)
|
||||
DirtyVariantOverrides.add variantOverride
|
||||
scope.displayDirty()
|
||||
viewValue
|
||||
|
||||
@@ -3,7 +3,7 @@ angular.module("admin.variantOverrides").directive "ofnTrackVariantOverride", (D
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
ngModel.$parsers.push (viewValue) ->
|
||||
if ngModel.$dirty
|
||||
variantOverride = scope.variantOverrides[scope.hub.id][scope.variant.id]
|
||||
variantOverride = scope.variantOverrides[scope.hub_id][scope.variant.id]
|
||||
scope.inherit = false
|
||||
DirtyVariantOverrides.add variantOverride
|
||||
scope.displayDirty()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
angular.module("admin.variantOverrides").filter "inventoryProducts", ($filter, InventoryItems) ->
|
||||
return (products, hub_id, views) ->
|
||||
return [] if !hub_id
|
||||
return $filter('filter')(products, (product) ->
|
||||
for variant in product.variants
|
||||
if InventoryItems.inventoryItems.hasOwnProperty(hub_id) && InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id)
|
||||
if InventoryItems.inventoryItems[hub_id][variant.id].visible
|
||||
# Important to only return if true, as other variants for this product might be visible
|
||||
return true if views.inventory.visible
|
||||
else
|
||||
# Important to only return if true, as other variants for this product might be visible
|
||||
return true if views.hidden.visible
|
||||
else
|
||||
# Important to only return if true, as other variants for this product might be visible
|
||||
return true if views.new.visible
|
||||
false
|
||||
, true)
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module("admin.variantOverrides").filter "inventoryVariants", ($filter, InventoryItems) ->
|
||||
return (variants, hub_id, views) ->
|
||||
return [] if !hub_id
|
||||
return $filter('filter')(variants, (variant) ->
|
||||
if InventoryItems.inventoryItems.hasOwnProperty(hub_id) && InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id)
|
||||
if InventoryItems.inventoryItems[hub_id][variant.id].visible
|
||||
return views.inventory.visible
|
||||
else
|
||||
return views.hidden.visible
|
||||
else
|
||||
return views.new.visible
|
||||
, true)
|
||||
@@ -0,0 +1,9 @@
|
||||
angular.module("admin.variantOverrides").filter "newInventoryProducts", ($filter, InventoryItems) ->
|
||||
return (products, hub_id) ->
|
||||
return [] if !hub_id
|
||||
return products unless InventoryItems.inventoryItems.hasOwnProperty(hub_id)
|
||||
return $filter('filter')(products, (product) ->
|
||||
for variant in product.variants
|
||||
return true if !InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id)
|
||||
false
|
||||
, true)
|
||||
@@ -1 +1 @@
|
||||
angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown"])
|
||||
angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown", "admin.inventoryItems"])
|
||||
|
||||
@@ -2,7 +2,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
new class EnterpriseRegistrationService
|
||||
enterprise:
|
||||
user_ids: [CurrentUser.id]
|
||||
email: CurrentUser.email
|
||||
email_address: CurrentUser.email
|
||||
address: {}
|
||||
country: availableCountries[0]
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.sixteen.columns.alpha.omega.alert-row{ ng: { show: '!dismissed' } }
|
||||
.fifteen.columns.pad.alpha
|
||||
%span.message.text-big{ ng: { bind: 'message'} }
|
||||
|
||||
%input{ type: 'button', ng: { value: "buttonText", show: 'buttonText && buttonAction', click: "buttonAction()" } }
|
||||
.one.column.omega.pad.text-center
|
||||
%a.close{ href: "#", ng: { click: "dismiss()" } }
|
||||
×
|
||||
@@ -1,11 +1,11 @@
|
||||
%div.contact-container{bindonce: true}
|
||||
%div.modal-centered{"bo-if" => "enterprise.email || enterprise.website || enterprise.phone"}
|
||||
%div.modal-centered{"bo-if" => "enterprise.email_address || enterprise.website || enterprise.phone"}
|
||||
%p.modal-header {{'contact' | t}}
|
||||
%p{"bo-if" => "enterprise.phone", "bo-text" => "enterprise.phone"}
|
||||
|
||||
%p.word-wrap{"ng-if" => "enterprise.email"}
|
||||
%a{"bo-href" => "enterprise.email | stripUrl", target: "_blank", mailto: true}
|
||||
%span.email{"bo-bind" => "enterprise.email | stripUrl"}
|
||||
%p.word-wrap{"ng-if" => "enterprise.email_address"}
|
||||
%a{"bo-href" => "enterprise.email_address | stripUrl", target: "_blank", mailto: true}
|
||||
%span.email{"bo-bind" => "enterprise.email_address | stripUrl"}
|
||||
|
||||
%p.word-wrap{"ng-if" => "enterprise.website"}
|
||||
%a{"bo-href-i" => "http://{{enterprise.website | stripUrl}}", target: "_blank", "bo-bind" => "enterprise.website | stripUrl"}
|
||||
|
||||
@@ -17,28 +17,13 @@
|
||||
{{'enterprise_contact_required' | t}}
|
||||
.row
|
||||
.small-12.columns.field
|
||||
%label{ for: 'enterprise_email' } {{'enterprise_email' | t}}:
|
||||
%input.chunky.small-12.columns{ id: 'enterprise_email', name: 'email', type: 'email', required: true, placeholder: "eg. charlie@thefarm.com", ng: { model: 'enterprise.email' } }
|
||||
%span.error.small-12.columns{ ng: { show: "(contact.email.$error.email || contact.email.$error.required) && submitted" } }
|
||||
{{'enterprise_email_required' | t}}
|
||||
%label{ for: 'enterprise_email_address' } {{'enterprise_email_address' | t}}:
|
||||
%input.chunky.small-12.columns{ id: 'enterprise_email_address', name: 'email_address', type: 'email', placeholder: "eg. charlie@thefarm.com", ng: { model: 'enterprise.email_address' } }
|
||||
.row
|
||||
.small-12.columns.field
|
||||
%label{ for: 'enterprise_phone' } {{'enterprise_phone' | t}}:
|
||||
%input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "eg. (03) 1234 5678", ng: { model: 'enterprise.phone' } }
|
||||
.small-12.medium-12.large-5.hide-for-small-only
|
||||
/ %h6
|
||||
/ Contact display
|
||||
/ %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your contact details on the Open Food Network."}
|
||||
/ .row
|
||||
/ .small-12.columns
|
||||
/ %label.indent-checkbox
|
||||
/ %input{ type: 'checkbox', id: 'contact_name_profile', ng: { model: 'enterprise.name_in_profile' } } Display name in profile
|
||||
/ .small-12.columns
|
||||
/ %label.indent-checkbox
|
||||
/ %input{ type: 'checkbox', id: 'contact_email_profile', ng: { model: 'enterprise.email_in_profile' } } Display email in profile
|
||||
/ .small-12.columns
|
||||
/ %label.indent-checkbox
|
||||
/ %input{ type: 'checkbox', id: 'contact_phone_profile', ng: { model: 'enterprise.phone_in_profile' } } Display phone in profile
|
||||
|
||||
.row.buttons
|
||||
.small-12.columns
|
||||
|
||||
19
app/assets/stylesheets/admin/advanced_settings.css.scss
Normal file
19
app/assets/stylesheets/admin/advanced_settings.css.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
#advanced_settings {
|
||||
background-color: #eff5fc;
|
||||
border: 1px solid #cee1f4;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.row{
|
||||
margin: 0px -4px;
|
||||
|
||||
padding: 20px 0px;
|
||||
|
||||
.column.alpha, .columns.alpha {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.column.omega, .columns.omega {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
app/assets/stylesheets/admin/components/alert_row.css.scss
Normal file
21
app/assets/stylesheets/admin/components/alert_row.css.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.alert-row{
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
background-color: #eff5fc;
|
||||
|
||||
.column, .columns {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
&.alpha { padding-left: 10px; }
|
||||
&.omega { padding-right: 10px; }
|
||||
}
|
||||
|
||||
span {
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
a.close {
|
||||
line-height: 3rem;
|
||||
font-size: 2.0rem;
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,12 @@
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
margin-right: 0px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:hover, &.expanded {
|
||||
@@ -55,12 +58,35 @@
|
||||
background-color: #ffffff;
|
||||
box-shadow: 1px 3px 10px #888888;
|
||||
z-index: 100;
|
||||
white-space: nowrap;
|
||||
|
||||
|
||||
.menu_item {
|
||||
margin: 0px;
|
||||
padding: 2px 0px;
|
||||
padding: 2px 10px;
|
||||
color: #454545;
|
||||
text-align: left;
|
||||
display: block;
|
||||
|
||||
.check {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
&:before {
|
||||
content: "\00a0";
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
display: inline-block;
|
||||
padding: 0px 15px 0px 0px;
|
||||
}
|
||||
|
||||
&.selected{
|
||||
.check:before {
|
||||
content: "\2713";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu_item:hover {
|
||||
|
||||
7
app/assets/stylesheets/admin/offsets.css.scss
Normal file
7
app/assets/stylesheets/admin/offsets.css.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.margin-bottom-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.margin-bottom-50 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
@@ -166,11 +166,16 @@ table#listing_enterprise_groups {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this, use class below
|
||||
#no_results {
|
||||
font-weight:bold;
|
||||
color: #DA5354;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
font-weight:bold;
|
||||
color: #DA5354;
|
||||
}
|
||||
|
||||
#loading {
|
||||
text-align: center;
|
||||
|
||||
9
app/assets/stylesheets/admin/typography.css.scss
Normal file
9
app/assets/stylesheets/admin/typography.css.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.text-normal {
|
||||
font-size: 1.0rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.text-big {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
.variant-override-unit
|
||||
float: right
|
||||
font-style: italic
|
||||
|
||||
button.hide:hover
|
||||
background-color: #DA5354
|
||||
|
||||
@@ -62,10 +62,8 @@
|
||||
// ABOUT Enterprise
|
||||
|
||||
.about-container
|
||||
max-height: 200px
|
||||
min-height: 20px
|
||||
margin-bottom: 0.5rem
|
||||
overflow-y: scroll
|
||||
overflow-x: hidden
|
||||
border-bottom: 1px solid $light-grey
|
||||
@include box-shadow(0 2px 2px -2px $light-grey)
|
||||
|
||||
@@ -16,18 +16,15 @@ module Admin
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { @presented_collection = @collection.each_with_index.map { |ef, i| EnterpriseFeePresenter.new(self, ef, i) } }
|
||||
format.json { render_as_json @collection, controller: self, include_calculators: @include_calculators }
|
||||
# format.json { @presented_collection = @collection.each_with_index.map { |ef, i| EnterpriseFeePresenter.new(self, ef, i) } }
|
||||
end
|
||||
end
|
||||
|
||||
def for_order_cycle
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: ActiveModel::ArraySerializer.new( @collection,
|
||||
each_serializer: Api::Admin::EnterpriseFeeSerializer, controller: self
|
||||
).to_json
|
||||
end
|
||||
format.json { render_as_json @collection, controller: self }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ module Admin
|
||||
def for_order_cycle
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user
|
||||
render json: @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, order_cycle: @order_cycle, spree_current_user: spree_current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -138,10 +138,10 @@ module Admin
|
||||
def collection
|
||||
case action
|
||||
when :for_order_cycle
|
||||
order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id]
|
||||
@order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id]
|
||||
coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id]
|
||||
order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present?
|
||||
return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises
|
||||
@order_cycle = OrderCycle.new(coordinator: coordinator) if @order_cycle.nil? && coordinator.present?
|
||||
return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle).visible_enterprises
|
||||
when :index
|
||||
if spree_current_user.admin?
|
||||
OpenFoodNetwork::Permissions.new(spree_current_user).
|
||||
|
||||
28
app/controllers/admin/inventory_items_controller.rb
Normal file
28
app/controllers/admin/inventory_items_controller.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module Admin
|
||||
class InventoryItemsController < ResourceController
|
||||
|
||||
respond_to :json
|
||||
|
||||
respond_override update: { json: {
|
||||
success: lambda { render_as_json @inventory_item },
|
||||
failure: lambda { render json: { errors: @inventory_item.errors.full_messages }, status: :unprocessable_entity }
|
||||
} }
|
||||
|
||||
respond_override create: { json: {
|
||||
success: lambda { render_as_json @inventory_item },
|
||||
failure: lambda { render json: { errors: @inventory_item.errors.full_messages }, status: :unprocessable_entity }
|
||||
} }
|
||||
|
||||
private
|
||||
|
||||
# Overriding Spree method to load data from params here so that
|
||||
# we can authorise #create using an object with required attributes
|
||||
def build_resource
|
||||
if parent_data.present?
|
||||
parent.send(controller_name).build
|
||||
else
|
||||
model_class.new(params[object_name]) # This line changed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -60,8 +60,12 @@ module Admin
|
||||
|
||||
respond_to do |format|
|
||||
if @order_cycle.update_attributes(params[:order_cycle])
|
||||
OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go!
|
||||
unless params[:order_cycle][:incoming_exchanges].nil? && params[:order_cycle][:outgoing_exchanges].nil?
|
||||
# Only update apply exchange information if it is actually submmitted
|
||||
OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go!
|
||||
end
|
||||
flash[:notice] = 'Your order cycle has been updated.' if params[:reloading] == '1'
|
||||
format.html { redirect_to main_app.edit_admin_order_cycle_path(@order_cycle) }
|
||||
format.json { render :json => {:success => true} }
|
||||
else
|
||||
format.json { render :json => {:success => false} }
|
||||
|
||||
@@ -54,6 +54,8 @@ module Admin
|
||||
|
||||
@hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user).
|
||||
variant_override_enterprises_per_hub
|
||||
|
||||
@inventory_items = InventoryItem.where(enterprise_id: @hubs)
|
||||
end
|
||||
|
||||
def load_collection
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'open_food_network/scope_product_to_hub'
|
||||
require 'open_food_network/products_renderer'
|
||||
|
||||
class ShopController < BaseController
|
||||
layout "darkswarm"
|
||||
@@ -10,22 +10,13 @@ class ShopController < BaseController
|
||||
end
|
||||
|
||||
def products
|
||||
if @products = products_for_shop
|
||||
begin
|
||||
products_json = OpenFoodNetwork::ProductsRenderer.new(current_distributor, current_order_cycle).products
|
||||
|
||||
enterprise_fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new current_distributor, current_order_cycle
|
||||
render json: products_json
|
||||
|
||||
render status: 200,
|
||||
json: ActiveModel::ArraySerializer.new(@products,
|
||||
each_serializer: Api::ProductSerializer,
|
||||
current_order_cycle: current_order_cycle,
|
||||
current_distributor: current_distributor,
|
||||
variants: variants_for_shop_by_id,
|
||||
master_variants: master_variants_for_shop_by_id,
|
||||
enterprise_fee_calculator: enterprise_fee_calculator,
|
||||
).to_json
|
||||
|
||||
else
|
||||
render json: "", status: 404
|
||||
rescue OpenFoodNetwork::ProductsRenderer::NoProducts
|
||||
render status: 404, json: ''
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,55 +33,4 @@ class ShopController < BaseController
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def products_for_shop
|
||||
if current_order_cycle
|
||||
scoper = OpenFoodNetwork::ScopeProductToHub.new(current_distributor)
|
||||
|
||||
current_order_cycle.
|
||||
valid_products_distributed_by(current_distributor).
|
||||
order(taxon_order).
|
||||
each { |p| scoper.scope(p) }.
|
||||
select { |p| !p.deleted? && p.has_stock_for_distribution?(current_order_cycle, current_distributor) }
|
||||
end
|
||||
end
|
||||
|
||||
def taxon_order
|
||||
if current_distributor.preferred_shopfront_taxon_order.present?
|
||||
current_distributor
|
||||
.preferred_shopfront_taxon_order
|
||||
.split(",").map { |id| "primary_taxon_id=#{id} DESC" }
|
||||
.join(",") + ", name ASC"
|
||||
else
|
||||
"name ASC"
|
||||
end
|
||||
end
|
||||
|
||||
def all_variants_for_shop
|
||||
# We use the in_stock? method here instead of the in_stock scope because we need to
|
||||
# look up the stock as overridden by VariantOverrides, and the scope method is not affected
|
||||
# by them.
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(current_distributor)
|
||||
Spree::Variant.
|
||||
for_distribution(current_order_cycle, current_distributor).
|
||||
each { |v| scoper.scope(v) }.
|
||||
select(&:in_stock?)
|
||||
end
|
||||
|
||||
def variants_for_shop_by_id
|
||||
index_by_product_id all_variants_for_shop.reject(&:is_master)
|
||||
end
|
||||
|
||||
def master_variants_for_shop_by_id
|
||||
index_by_product_id all_variants_for_shop.select(&:is_master)
|
||||
end
|
||||
|
||||
def index_by_product_id(variants)
|
||||
variants.inject({}) do |vs, v|
|
||||
vs[v.product_id] ||= []
|
||||
vs[v.product_id] << v
|
||||
vs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,6 +39,10 @@ module Admin
|
||||
admin_inject_json_ams_array opts[:module], "producers", @producers, Api::Admin::IdNameSerializer
|
||||
end
|
||||
|
||||
def admin_inject_inventory_items(opts={module: 'ofn.admin'})
|
||||
admin_inject_json_ams_array opts[:module], "inventoryItems", @inventory_items, Api::Admin::InventoryItemSerializer
|
||||
end
|
||||
|
||||
def admin_inject_enterprise_permissions
|
||||
permissions =
|
||||
{can_manage_shipping_methods: can?(:manage_shipping_methods, @enterprise),
|
||||
@@ -56,8 +60,8 @@ module Admin
|
||||
admin_inject_json_ams_array "ofn.admin", "products", @products, Api::Admin::ProductSerializer
|
||||
end
|
||||
|
||||
def admin_inject_tax_categories
|
||||
admin_inject_json_ams_array "ofn.admin", "tax_categories", @tax_categories, Api::Admin::TaxCategorySerializer
|
||||
def admin_inject_tax_categories(opts={module: 'ofn.admin'})
|
||||
admin_inject_json_ams_array opts[:module], "tax_categories", @tax_categories, Api::Admin::TaxCategorySerializer
|
||||
end
|
||||
|
||||
def admin_inject_taxons
|
||||
|
||||
@@ -11,26 +11,26 @@ class AngularFormBuilder < ActionView::Helpers::FormBuilder
|
||||
# @fields_for_record_name --> :collection
|
||||
# @object.send(@fields_for_record_name).first.class.to_s.underscore --> enterprise_fee
|
||||
|
||||
value = "{{ #{@object.send(@fields_for_record_name).first.class.to_s.underscore}.#{method} }}"
|
||||
value = "{{ #{angular_model(method)} }}"
|
||||
options.reverse_merge!({'id' => angular_id(method)})
|
||||
|
||||
@template.text_field_tag angular_name(method), value, options
|
||||
end
|
||||
|
||||
def ng_hidden_field(method, options = {})
|
||||
value = "{{ #{@object.send(@fields_for_record_name).first.class.to_s.underscore}.#{method} }}"
|
||||
value = "{{ #{angular_model(method)} }}"
|
||||
|
||||
@template.hidden_field_tag angular_name(method), value, :id => angular_id(method)
|
||||
end
|
||||
|
||||
def ng_select(method, choices, angular_field, options = {})
|
||||
options.reverse_merge!({'id' => angular_id(method)})
|
||||
options.reverse_merge!({'id' => angular_id(method), 'ng-model' => "#{angular_model(method)}"})
|
||||
|
||||
@template.select_tag angular_name(method), @template.ng_options_for_select(choices, angular_field), options
|
||||
end
|
||||
|
||||
def ng_collection_select(method, collection, value_method, text_method, angular_field, options = {})
|
||||
options.reverse_merge!({'id' => angular_id(method)})
|
||||
options.reverse_merge!({'id' => angular_id(method), 'ng-model' => "#{angular_model(method)}"})
|
||||
|
||||
@template.select_tag angular_name(method), @template.ng_options_from_collection_for_select(collection, value_method, text_method, angular_field), options
|
||||
end
|
||||
@@ -43,4 +43,8 @@ class AngularFormBuilder < ActionView::Helpers::FormBuilder
|
||||
def angular_id(method)
|
||||
"#{@object_name}_#{@fields_for_record_name}_attributes_{{ $index }}_#{method}"
|
||||
end
|
||||
|
||||
def angular_model(method)
|
||||
"#{@object.send(@fields_for_record_name).first.class.to_s.underscore}.#{method}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,8 +5,7 @@ module AngularFormHelper
|
||||
container.map do |element|
|
||||
html_attributes = option_html_attributes(element)
|
||||
text, value = option_text_and_value(element).map { |item| item.to_s }
|
||||
selected_attribute = %Q( ng-selected="#{angular_field} == '#{value}'") if angular_field
|
||||
%(<option value="#{ERB::Util.html_escape(value)}"#{selected_attribute}#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
|
||||
%(<option value="#{ERB::Util.html_escape(value)}"#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
|
||||
end.join("\n").html_safe
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
module EnterpriseFeesHelper
|
||||
def angular_name(method)
|
||||
"enterprise_fee_set[collection_attributes][{{ $index }}][#{method}]"
|
||||
end
|
||||
|
||||
def angular_id(method)
|
||||
"enterprise_fee_set_collection_attributes_{{ $index }}_#{method}"
|
||||
end
|
||||
|
||||
def enterprise_fee_type_options
|
||||
EnterpriseFee::FEE_TYPES.map { |f| [f.capitalize, f] }
|
||||
end
|
||||
|
||||
@@ -7,6 +7,11 @@ class Enterprise < ActiveRecord::Base
|
||||
preference :shopfront_taxon_order, :string, default: ""
|
||||
preference :shopfront_order_cycle_order, :string, default: "orders_close_at"
|
||||
|
||||
# This is hopefully a temporary measure, pending the arrival of multiple named inventories
|
||||
# for shops. We need this here to allow hubs to restrict visible variants to only those in
|
||||
# their inventory if they so choose
|
||||
preference :product_selection_from_inventory_only, :boolean, default: false
|
||||
|
||||
devise :confirmable, reconfirmable: true, confirmation_keys: [ :id, :email ]
|
||||
handle_asynchronously :send_confirmation_instructions
|
||||
handle_asynchronously :send_on_create_confirmation_instructions
|
||||
@@ -35,6 +40,7 @@ class Enterprise < ActiveRecord::Base
|
||||
has_many :shipping_methods, through: :distributor_shipping_methods
|
||||
has_many :customers
|
||||
has_many :billable_periods
|
||||
has_many :inventory_items
|
||||
|
||||
delegate :latitude, :longitude, :city, :state_name, :to => :address
|
||||
|
||||
@@ -74,6 +80,7 @@ class Enterprise < ActiveRecord::Base
|
||||
|
||||
before_validation :initialize_permalink, if: lambda { permalink.nil? }
|
||||
before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? && !owner_id.nil? }
|
||||
before_validation :ensure_email_set
|
||||
before_validation :set_unused_address_fields
|
||||
after_validation :geocode_address
|
||||
|
||||
@@ -164,7 +171,7 @@ class Enterprise < ActiveRecord::Base
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
joins(:enterprise_roles).where('enterprise_roles.user_id = ?', user.id).select("DISTINCT enterprises.*")
|
||||
joins(:enterprise_roles).where('enterprise_roles.user_id = ?', user.id)
|
||||
end
|
||||
}
|
||||
|
||||
@@ -246,6 +253,14 @@ class Enterprise < ActiveRecord::Base
|
||||
strip_url read_attribute(:linkedin)
|
||||
end
|
||||
|
||||
def inventory_variants
|
||||
if prefers_product_selection_from_inventory_only?
|
||||
Spree::Variant.visible_for(self)
|
||||
else
|
||||
Spree::Variant.not_hidden_for(self)
|
||||
end
|
||||
end
|
||||
|
||||
def distributed_variants
|
||||
Spree::Variant.joins(:product).merge(Spree::Product.in_distributor(self)).select('spree_variants.*')
|
||||
end
|
||||
@@ -394,6 +409,10 @@ class Enterprise < ActiveRecord::Base
|
||||
users << owner unless users.include?(owner) || owner.admin?
|
||||
end
|
||||
|
||||
def ensure_email_set
|
||||
self.email = owner.email if email.blank? && owner.present?
|
||||
end
|
||||
|
||||
def enforce_ownership_limit
|
||||
unless owner.can_own_more_enterprises?
|
||||
errors.add(:owner, "^#{owner.email} is not permitted to own any more enterprises (limit is #{owner.enterprise_limit}).")
|
||||
|
||||
@@ -9,7 +9,7 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
|
||||
calculated_adjustments
|
||||
|
||||
attr_accessible :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type
|
||||
attr_accessible :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type, :inherits_tax_category
|
||||
|
||||
FEE_TYPES = %w(packing transport admin sales fundraising)
|
||||
PER_ORDER_CALCULATORS = ['Spree::Calculator::FlatRate', 'Spree::Calculator::FlexiRate']
|
||||
@@ -18,6 +18,7 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
validates_inclusion_of :fee_type, :in => FEE_TYPES
|
||||
validates_presence_of :name
|
||||
|
||||
before_save :ensure_valid_tax_category_settings
|
||||
|
||||
scope :for_enterprise, lambda { |enterprise| where(enterprise_id: enterprise) }
|
||||
scope :for_enterprises, lambda { |enterprises| where(enterprise_id: enterprises) }
|
||||
@@ -57,4 +58,18 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
:mandatory => mandatory,
|
||||
:locked => true}, :without_protection => true)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_valid_tax_category_settings
|
||||
# Setting an explicit tax_category removes any inheritance behaviour
|
||||
# In the absence of any current changes to tax_category, setting
|
||||
# inherits_tax_category to true will clear the tax_category
|
||||
if tax_category_id_changed?
|
||||
self.inherits_tax_category = false if tax_category.present?
|
||||
elsif inherits_tax_category_changed?
|
||||
self.tax_category_id = nil if inherits_tax_category?
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,6 +6,8 @@ class EnterpriseRelationship < ActiveRecord::Base
|
||||
validates_presence_of :parent_id, :child_id
|
||||
validates_uniqueness_of :child_id, scope: :parent_id, message: "^That relationship is already established."
|
||||
|
||||
after_save :apply_variant_override_permissions
|
||||
|
||||
scope :with_enterprises,
|
||||
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')
|
||||
@@ -61,10 +63,28 @@ class EnterpriseRelationship < ActiveRecord::Base
|
||||
|
||||
|
||||
def permissions_list=(perms)
|
||||
perms.andand.each { |name| permissions.build name: name }
|
||||
if perms.nil?
|
||||
permissions.destroy_all
|
||||
else
|
||||
permissions.where('name NOT IN (?)', perms).destroy_all
|
||||
perms.map { |name| permissions.find_or_initialize_by_name name }
|
||||
end
|
||||
end
|
||||
|
||||
def has_permission?(name)
|
||||
permissions.map(&:name).map(&:to_sym).include? name.to_sym
|
||||
permissions(:reload).map(&:name).map(&:to_sym).include? name.to_sym
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_variant_override_permissions
|
||||
variant_overrides = VariantOverride.unscoped.for_hubs(child)
|
||||
.joins(variant: :product).where("spree_products.supplier_id IN (?)", parent)
|
||||
|
||||
if has_permission?(:create_variant_overrides)
|
||||
variant_overrides.update_all(permission_revoked_at: nil)
|
||||
else
|
||||
variant_overrides.update_all(permission_revoked_at: Time.now)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
14
app/models/inventory_item.rb
Normal file
14
app/models/inventory_item.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
class InventoryItem < ActiveRecord::Base
|
||||
attr_accessible :enterprise, :enterprise_id, :variant, :variant_id, :visible
|
||||
|
||||
belongs_to :enterprise
|
||||
belongs_to :variant, class_name: "Spree::Variant"
|
||||
|
||||
validates :variant_id, uniqueness: { scope: :enterprise_id }
|
||||
validates :enterprise_id, presence: true
|
||||
validates :variant_id, presence: true
|
||||
validates :visible, inclusion: { in: [true, false], message: "must be true or false" }
|
||||
|
||||
scope :visible, where(visible: true)
|
||||
scope :hidden, where(visible: false)
|
||||
end
|
||||
@@ -11,6 +11,8 @@ class OrderCycle < ActiveRecord::Base
|
||||
|
||||
validates_presence_of :name, :coordinator_id
|
||||
|
||||
preference :product_selection_from_coordinator_inventory_only, :boolean, default: false
|
||||
|
||||
scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.zone.now, Time.zone.now) }
|
||||
scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) }
|
||||
scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) }
|
||||
@@ -113,6 +115,7 @@ class OrderCycle < ActiveRecord::Base
|
||||
oc.name = "COPY OF #{oc.name}"
|
||||
oc.orders_open_at = oc.orders_close_at = nil
|
||||
oc.coordinator_fee_ids = self.coordinator_fee_ids
|
||||
oc.preferred_product_selection_from_coordinator_inventory_only = self.preferred_product_selection_from_coordinator_inventory_only
|
||||
oc.save!
|
||||
self.exchanges.each { |e| e.clone!(oc) }
|
||||
oc.reload
|
||||
@@ -142,12 +145,15 @@ class OrderCycle < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def distributed_variants
|
||||
# TODO: only used in DistributionChangeValidator, can we remove?
|
||||
self.exchanges.outgoing.map(&:variants).flatten.uniq.reject(&:deleted?)
|
||||
end
|
||||
|
||||
def variants_distributed_by(distributor)
|
||||
return Spree::Variant.where("1=0") unless distributor.present?
|
||||
Spree::Variant.
|
||||
not_deleted.
|
||||
merge(distributor.inventory_variants).
|
||||
joins(:exchanges).
|
||||
merge(Exchange.in_order_cycle(self)).
|
||||
merge(Exchange.outgoing).
|
||||
|
||||
@@ -125,6 +125,20 @@ class AbilityDecorator
|
||||
hub_auth && producer_auth
|
||||
end
|
||||
|
||||
can [:admin, :create, :update], InventoryItem do |ii|
|
||||
next false unless ii.enterprise.present? && ii.variant.andand.product.andand.supplier.present?
|
||||
|
||||
hub_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_hubs.
|
||||
include? ii.enterprise
|
||||
|
||||
producer_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_producers.
|
||||
include? ii.variant.product.supplier
|
||||
|
||||
hub_auth && producer_auth
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], Spree::ProductProperty
|
||||
can [:admin, :index, :read, :create, :edit, :update, :destroy], Spree::Image
|
||||
|
||||
|
||||
40
app/models/spree/calculator/default_tax_decorator.rb
Normal file
40
app/models/spree/calculator/default_tax_decorator.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
require 'open_food_network/enterprise_fee_calculator'
|
||||
|
||||
Spree::Calculator::DefaultTax.class_eval do
|
||||
|
||||
private
|
||||
|
||||
# Override this method to enable calculation of tax for
|
||||
# enterprise fees with tax rates where included_in_price = false
|
||||
def compute_order(order)
|
||||
matched_line_items = order.line_items.select do |line_item|
|
||||
line_item.product.tax_category == rate.tax_category
|
||||
end
|
||||
|
||||
line_items_total = matched_line_items.sum(&:total)
|
||||
|
||||
# Added this line
|
||||
calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(order.distributor, order.order_cycle)
|
||||
|
||||
# Added this block, finds relevant fees for each line_item, calculates the tax on them, and returns the total tax
|
||||
per_item_fees_total = order.line_items.sum do |line_item|
|
||||
calculator.send(:per_item_enterprise_fee_applicators_for, line_item.variant)
|
||||
.select { |applicator| (!applicator.enterprise_fee.inherits_tax_category && applicator.enterprise_fee.tax_category == rate.tax_category) ||
|
||||
(applicator.enterprise_fee.inherits_tax_category && line_item.product.tax_category == rate.tax_category) }
|
||||
.sum { |applicator| applicator.enterprise_fee.compute_amount(line_item) }
|
||||
end
|
||||
|
||||
# Added this block, finds relevant fees for whole order, calculates the tax on them, and returns the total tax
|
||||
per_order_fees_total = calculator.send(:per_order_enterprise_fee_applicators_for, order)
|
||||
.select { |applicator| applicator.enterprise_fee.tax_category == rate.tax_category }
|
||||
.sum { |applicator| applicator.enterprise_fee.compute_amount(order) }
|
||||
|
||||
# round_to_two_places(line_items_total * rate.amount) # Removed this line
|
||||
|
||||
# Added this block
|
||||
[line_items_total, per_item_fees_total, per_order_fees_total].sum do |total|
|
||||
round_to_two_places(total * rate.amount)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -52,6 +52,13 @@ Spree::Product.class_eval do
|
||||
|
||||
scope :with_order_cycles_inner, joins(:variants_including_master => {:exchanges => :order_cycle})
|
||||
|
||||
scope :visible_for, lambda { |enterprise|
|
||||
joins('LEFT OUTER JOIN spree_variants AS o_spree_variants ON (o_spree_variants.product_id = spree_products.id)').
|
||||
joins('LEFT OUTER JOIN inventory_items AS o_inventory_items ON (o_spree_variants.id = o_inventory_items.variant_id)').
|
||||
where('o_inventory_items.enterprise_id = (?) AND visible = (?)', enterprise, true).
|
||||
select('DISTINCT spree_products.*')
|
||||
}
|
||||
|
||||
|
||||
# -- Scopes
|
||||
scope :in_supplier, lambda { |supplier| where(:supplier_id => supplier) }
|
||||
|
||||
@@ -12,6 +12,7 @@ Spree::Variant.class_eval do
|
||||
has_many :exchange_variants, dependent: :destroy
|
||||
has_many :exchanges, through: :exchange_variants
|
||||
has_many :variant_overrides
|
||||
has_many :inventory_items
|
||||
|
||||
attr_accessible :unit_value, :unit_description, :images_attributes, :display_as, :display_name
|
||||
accepts_nested_attributes_for :images
|
||||
@@ -40,6 +41,16 @@ Spree::Variant.class_eval do
|
||||
where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor))
|
||||
}
|
||||
|
||||
scope :visible_for, lambda { |enterprise|
|
||||
joins(:inventory_items).where('inventory_items.enterprise_id = (?) AND inventory_items.visible = (?)', enterprise, true)
|
||||
}
|
||||
|
||||
scope :not_hidden_for, lambda { |enterprise|
|
||||
return where("1=0") unless enterprise.present?
|
||||
joins("LEFT OUTER JOIN (SELECT * from inventory_items WHERE enterprise_id = #{sanitize enterprise.andand.id}) AS o_inventory_items ON o_inventory_items.variant_id = spree_variants.id")
|
||||
.where("o_inventory_items.id IS NULL OR o_inventory_items.visible = (?)", true)
|
||||
}
|
||||
|
||||
# Define sope as class method to allow chaining with other scopes filtering id.
|
||||
# In Rails 3, merging two scopes on the same column will consider only the last scope.
|
||||
def self.in_distributor(distributor)
|
||||
|
||||
@@ -6,6 +6,8 @@ class VariantOverride < ActiveRecord::Base
|
||||
# Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user.
|
||||
validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
||||
|
||||
default_scope where(permission_revoked_at: nil)
|
||||
|
||||
scope :for_hubs, lambda { |hubs|
|
||||
where(hub_id: hubs)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/ replace_contents "title"
|
||||
|
||||
= t(controller.controller_name, :default => controller.controller_name.titleize)
|
||||
= " - OFN #{t(:administration)}"
|
||||
- if content_for? :html_title
|
||||
= yield :html_title
|
||||
- else
|
||||
= t(controller.controller_name, :default => controller.controller_name.titleize)
|
||||
= " - OFN #{t(:administration)}"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/ insert_bottom "[data-hook='admin_product_sub_tabs']"
|
||||
|
||||
= tab :variant_overrides, label: "Overrides", url: main_app.admin_variant_overrides_path, match_path: '/variant_overrides'
|
||||
= tab :variant_overrides, label: "Inventory", url: main_app.admin_inventory_path, match_path: '/inventory'
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
class EnterpriseFeePresenter
|
||||
def initialize(controller, enterprise_fee, index)
|
||||
@controller, @enterprise_fee, @index = controller, enterprise_fee, index
|
||||
end
|
||||
|
||||
delegate :id, :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type, :to => :enterprise_fee
|
||||
|
||||
def enterprise_fee
|
||||
@enterprise_fee
|
||||
end
|
||||
|
||||
|
||||
def enterprise_name
|
||||
@enterprise_fee.enterprise.andand.name
|
||||
end
|
||||
|
||||
def calculator_description
|
||||
@enterprise_fee.calculator.andand.description
|
||||
end
|
||||
|
||||
def calculator_settings
|
||||
result = nil
|
||||
|
||||
@controller.send(:with_format, :html) do
|
||||
result = @controller.render_to_string :partial => 'admin/enterprise_fees/calculator_settings', :locals => {:enterprise_fee => @enterprise_fee, :index => @index}
|
||||
end
|
||||
|
||||
result.gsub('[0]', '[{{ $index }}]').gsub('_0_', '_{{ $index }}_')
|
||||
end
|
||||
|
||||
end
|
||||
11
app/serializers/api/admin/calculator_serializer.rb
Normal file
11
app/serializers/api/admin/calculator_serializer.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Api::Admin::CalculatorSerializer < ActiveModel::Serializer
|
||||
attributes :name, :description
|
||||
|
||||
def name
|
||||
object.name
|
||||
end
|
||||
|
||||
def description
|
||||
object.description
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
|
||||
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type
|
||||
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :inherits_tax_category, :calculator_type
|
||||
attributes :enterprise_name, :calculator_description, :calculator_settings
|
||||
|
||||
def enterprise_name
|
||||
@@ -11,6 +11,8 @@ class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def calculator_settings
|
||||
return nil unless options[:include_calculators]
|
||||
|
||||
result = nil
|
||||
|
||||
options[:controller].send(:with_format, :html) do
|
||||
|
||||
@@ -2,6 +2,7 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids
|
||||
attributes :producer_profile_only, :email, :long_description, :permalink
|
||||
attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order
|
||||
attributes :preferred_product_selection_from_inventory_only
|
||||
attributes :owner, :users
|
||||
|
||||
has_one :owner, serializer: Api::Admin::UserSerializer
|
||||
|
||||
@@ -4,14 +4,35 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer
|
||||
has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer
|
||||
|
||||
def variants
|
||||
permitted = Spree::Variant.where("1=0")
|
||||
if object.incoming
|
||||
permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle).
|
||||
visible_variants_for_incoming_exchanges_from(object.sender)
|
||||
variants = object.incoming? ? visible_incoming_variants : visible_outgoing_variants
|
||||
Hash[ object.variants.merge(variants).map { |v| [v.id, true] } ]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def visible_incoming_variants
|
||||
if object.order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
permitted_incoming_variants.visible_for(object.order_cycle.coordinator)
|
||||
else
|
||||
permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle).
|
||||
visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
permitted_incoming_variants
|
||||
end
|
||||
Hash[ object.variants.merge(permitted).map { |v| [v.id, true] } ]
|
||||
end
|
||||
|
||||
def visible_outgoing_variants
|
||||
if object.receiver.prefers_product_selection_from_inventory_only?
|
||||
permitted_outgoing_variants.visible_for(object.receiver)
|
||||
else
|
||||
permitted_outgoing_variants.not_hidden_for(object.receiver)
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_incoming_variants
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle).
|
||||
visible_variants_for_incoming_exchanges_from(object.sender)
|
||||
end
|
||||
|
||||
def permitted_outgoing_variants
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle)
|
||||
.visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,11 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :is_primary_producer, :is_distributor, :sells
|
||||
|
||||
def issues_summary_supplier
|
||||
OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
|
||||
issues = OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
|
||||
if issues.nil? && products.empty?
|
||||
issues = "no products in inventory"
|
||||
end
|
||||
issues
|
||||
end
|
||||
|
||||
def issues_summary_distributor
|
||||
@@ -18,8 +22,22 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def supplied_products
|
||||
objects = object.supplied_products.not_deleted
|
||||
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
|
||||
ActiveModel::ArraySerializer.new(objects, each_serializer: serializer)
|
||||
ActiveModel::ArraySerializer.new(products, each_serializer: serializer, order_cycle: order_cycle)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def products
|
||||
return @products unless @products.nil?
|
||||
@products = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.supplied_products.not_deleted.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.supplied_products.not_deleted
|
||||
end
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
options[:order_cycle]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,8 +14,17 @@ class Api::Admin::ForOrderCycle::SuppliedProductSerializer < ActiveModel::Serial
|
||||
end
|
||||
|
||||
def variants
|
||||
object.variants.map do |variant|
|
||||
{ id: variant.id, label: variant.full_name }
|
||||
variants = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.variants.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.variants
|
||||
end
|
||||
variants.map { |variant| { id: variant.id, label: variant.full_name } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_cycle
|
||||
options[:order_cycle]
|
||||
end
|
||||
end
|
||||
|
||||
3
app/serializers/api/admin/inventory_item_serializer.rb
Normal file
3
app/serializers/api/admin/inventory_item_serializer.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Api::Admin::InventoryItemSerializer < ActiveModel::Serializer
|
||||
attributes :id, :enterprise_id, :variant_id, :visible
|
||||
end
|
||||
@@ -58,7 +58,14 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
|
||||
permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object)
|
||||
enterprises = permissions.visible_enterprises
|
||||
enterprises.each do |enterprise|
|
||||
variants = permissions.visible_variants_for_outgoing_exchanges_to(enterprise).pluck(:id)
|
||||
# This is hopefully a temporary measure, pending the arrival of multiple named inventories
|
||||
# for shops. We need this here to allow hubs to restrict visible variants to only those in
|
||||
# their inventory if they so choose
|
||||
variants = if enterprise.prefers_product_selection_from_inventory_only?
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).visible_for(enterprise)
|
||||
else
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).not_hidden_for(enterprise)
|
||||
end.pluck(:id)
|
||||
visible[enterprise.id] = variants if variants.any?
|
||||
end
|
||||
visible
|
||||
|
||||
@@ -41,7 +41,7 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :description, :latitude, :longitude,
|
||||
:long_description, :website, :instagram, :linkedin, :twitter,
|
||||
:facebook, :is_primary_producer, :is_distributor, :phone, :visible,
|
||||
:email, :hash, :logo, :promo_image, :path, :pickup, :delivery,
|
||||
:email_address, :hash, :logo, :promo_image, :path, :pickup, :delivery,
|
||||
:icon, :icon_font, :producer_icon_font, :category, :producers, :hubs
|
||||
|
||||
attributes :taxons, :supplied_taxons
|
||||
@@ -67,8 +67,8 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
|
||||
services ? services[:delivery] : false
|
||||
end
|
||||
|
||||
def email
|
||||
object.email.to_s.reverse
|
||||
def email_address
|
||||
object.email_address.to_s.reverse
|
||||
end
|
||||
|
||||
def hash
|
||||
|
||||
@@ -34,6 +34,7 @@ end
|
||||
class Api::CachedProductSerializer < ActiveModel::Serializer
|
||||
#cached
|
||||
#delegate :cache_key, to: :object
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
attributes :id, :name, :permalink, :count_on_hand
|
||||
attributes :on_demand, :group_buy, :notes, :description
|
||||
@@ -48,6 +49,10 @@ class Api::CachedProductSerializer < ActiveModel::Serializer
|
||||
has_many :images, serializer: Api::ImageSerializer
|
||||
has_one :supplier, serializer: Api::IdSerializer
|
||||
|
||||
def description
|
||||
strip_tags object.description
|
||||
end
|
||||
|
||||
def properties_with_values
|
||||
object.properties_including_inherited
|
||||
end
|
||||
|
||||
3
app/views/admin/enterprise_fees/_data.html.haml
Normal file
3
app/views/admin/enterprise_fees/_data.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
= admin_inject_json_ams_array "admin.enterpriseFees", "enterprises", @enterprises, Api::Admin::IdNameSerializer
|
||||
= admin_inject_tax_categories(module: "admin.enterpriseFees")
|
||||
= admin_inject_json_ams_array "admin.enterpriseFees", "calculators", @calculators, Api::Admin::CalculatorSerializer
|
||||
@@ -1,8 +1,9 @@
|
||||
= content_for :page_title do
|
||||
Enterprise Fees
|
||||
|
||||
= ng_form_for @enterprise_fee_set, :url => main_app.bulk_update_admin_enterprise_fees_path, :html => {'ng-app' => 'enterprise_fees', 'ng-controller' => 'AdminEnterpriseFeesCtrl'} do |enterprise_fee_set_form|
|
||||
= ng_form_for @enterprise_fee_set, :url => main_app.bulk_update_admin_enterprise_fees_path, :html => {'ng-app' => 'admin.enterpriseFees', 'ng-controller' => 'enterpriseFeesCtrl'} do |enterprise_fee_set_form|
|
||||
= hidden_field_tag 'enterprise_id', @enterprise.id if @enterprise
|
||||
= render "admin/enterprise_fees/data"
|
||||
= render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise_fee_set }
|
||||
|
||||
%input.search{'ng-model' => 'query', 'placeholder' => 'Search'}
|
||||
@@ -19,14 +20,20 @@
|
||||
%th.actions
|
||||
%tbody
|
||||
= enterprise_fee_set_form.ng_fields_for :collection do |f|
|
||||
%tr{'ng-repeat' => 'enterprise_fee in enterprise_fees | filter:query'}
|
||||
%tr{ ng: { repeat: 'enterprise_fee in enterprise_fees | filter:query' } }
|
||||
%td
|
||||
= f.ng_hidden_field :id
|
||||
= f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', include_blank: true
|
||||
%ofn-select{ :id => angular_id(:enterprise_id), data: 'enterprises', include_blank: true, ng: { model: 'enterprise_fee.enterprise_id' } }
|
||||
%input{ type: "hidden", name: angular_name(:enterprise_id), ng: { value: "enterprise_fee.enterprise_id" } }
|
||||
%td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type'
|
||||
%td= f.ng_text_field :name, { placeholder: 'e.g. packing fee' }
|
||||
%td= f.ng_collection_select :tax_category_id, @tax_categories, :id, :name, 'enterprise_fee.tax_category_id', include_blank: true
|
||||
%td= f.ng_collection_select :calculator_type, @calculators, :name, :description, 'enterprise_fee.calculator_type', {'class' => 'calculator_type', 'ng-model' => 'calculatorType', 'spree-ensure-calculator-preferences-match-type' => "1"}
|
||||
%td
|
||||
%ofn-select{ :id => angular_id(:tax_category_id), data: 'tax_categories', include_blank: true, ng: { model: 'enterprise_fee.tax_category_id' } }
|
||||
%input{ type: "hidden", name: angular_name(:tax_category_id), 'watch-tax-category' => true }
|
||||
%input{ type: "hidden", name: angular_name(:inherits_tax_category), ng: { value: "enterprise_fee.inherits_tax_category" } }
|
||||
%td
|
||||
%ofn-select.calculator_type{ :id => angular_id(:calculator_type), ng: { model: 'enterprise_fee.calculator_type' }, value_attr: 'name', text_attr: 'description', data: 'calculators', 'spree-ensure-calculator-preferences-match-type' => true }
|
||||
%input{ type: "hidden", name: angular_name(:calculator_type), ng: { value: "enterprise_fee.calculator_type" } }
|
||||
%td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'}
|
||||
%td.actions{'spree-delete-resource' => "1"}
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
r.list_of :enterprise_fees, @presented_collection do
|
||||
r.element :id
|
||||
r.element :enterprise_id
|
||||
r.element :enterprise_name
|
||||
r.element :fee_type
|
||||
r.element :name
|
||||
r.element :tax_category_id
|
||||
r.element :calculator_type
|
||||
r.element :calculator_description
|
||||
r.element :calculator_settings if @include_calculators
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
= content_for :page_title do
|
||||
Enterprise Groups
|
||||
|
||||
= content_for :page_actions do
|
||||
%li#new_enterprise_group_link
|
||||
= button_link_to "New Enterprise Group", main_app.new_admin_enterprise_group_path, :icon => 'add', :id => 'admin_new_enterprise_group_link'
|
||||
- if admin_user?
|
||||
= content_for :page_actions do
|
||||
%li= button_link_to "New Enterprise Group", main_app.new_admin_enterprise_group_path
|
||||
|
||||
%table.index#listing_enterprise_groups
|
||||
%thead
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%tr
|
||||
%td
|
||||
%select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"}
|
||||
%select.select2.fullwidth{id: "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_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"}
|
||||
%select.select2.fullwidth{id: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"}
|
||||
%td
|
||||
%label
|
||||
%input{type: "checkbox", ng: {checked: "allPermissionsChecked()", click: "checkAllPermissions()"}}
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
= render 'search_input'
|
||||
|
||||
%table#enterprise-relationships
|
||||
%colgroup
|
||||
%col{ style: "width: 30%" }
|
||||
%col{ style: "width: 5%" }
|
||||
%col{ style: "width: 30%" }
|
||||
%col{ style: "width: 30%" }
|
||||
%col{ style: "width: 5%" }
|
||||
%tbody
|
||||
= render 'form'
|
||||
= render 'enterprise_relationship'
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
%legend Enterprise Fees
|
||||
= render 'admin/enterprises/form/enterprise_fees', f: f
|
||||
|
||||
%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Inventory Settings'" } }
|
||||
%legend Inventory Settings
|
||||
= render 'admin/enterprises/form/inventory_settings', f: f
|
||||
|
||||
%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Shop Preferences'" } }
|
||||
%legend Shop Preferences
|
||||
= render 'admin/enterprises/form/shop_preferences', f: f
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= f.label :name
|
||||
%span.required *
|
||||
.nine.columns.omega
|
||||
= f.text_field :name, { placeholder: "eg. Professor Plum's Biodynamic Truffles", class: "fullwidth" }
|
||||
|
||||
@@ -8,6 +9,7 @@
|
||||
.row
|
||||
.three.columns.alpha
|
||||
=f.label :owner_id, 'Owner'
|
||||
%span.required *
|
||||
%div{'ofn-with-tip' => "The primary user responsible for this enterprise."}
|
||||
%a What's this?
|
||||
.nine.columns.omega
|
||||
@@ -50,13 +52,9 @@
|
||||
= f.text_field :contact, { placeholder: "eg. Gustav Plum"}
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :email
|
||||
= f.label :email_address
|
||||
.omega.nine.columns
|
||||
= f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" }
|
||||
.alert-box
|
||||
%i.icon-info-sign
|
||||
If we don't recognise this email address we'll send you a confirmation email to make sure it belongs to you. You'll need to use the link in the email we send to fully activate your new enterprise.
|
||||
%a.close{ href: "" } ×
|
||||
= f.text_field :email_address, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email_address" }
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :phone
|
||||
@@ -72,6 +70,7 @@
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= af.label :address1
|
||||
%span.required *
|
||||
.nine.columns.omega
|
||||
= af.text_field :address1, { placeholder: "eg. 123 High Street"}
|
||||
.row
|
||||
@@ -84,6 +83,7 @@
|
||||
= af.label :city, 'Suburb'
|
||||
\/
|
||||
= af.label :zipcode, 'Postcode'
|
||||
%span.required *
|
||||
.four.columns
|
||||
= af.text_field :city, { placeholder: "eg. Northcote"}
|
||||
.five.columns.omega
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
-if @enterprise.pending_any_confirmation?
|
||||
.alert-box
|
||||
- email = @enterprise.confirmed? ? @enterprise.unconfirmed_email : @enterprise.email
|
||||
Email confirmation is pending.
|
||||
We've sent a confirmation email to
|
||||
%strong= "#{email}."
|
||||
= link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: email } ), method: :post)
|
||||
%a.close{ href: "#" } ×
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :contact, 'Name'
|
||||
@@ -13,15 +5,9 @@
|
||||
= f.text_field :contact, { placeholder: "eg. Gustav Plum"}
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :email
|
||||
%span.required *
|
||||
= f.label :email_address
|
||||
.omega.eight.columns
|
||||
= f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" }
|
||||
.row{ ng: { hide: "pristineEmail == null || pristineEmail == Enterprise.email"} }
|
||||
.alpha.three.columns
|
||||
|
||||
.omega.eight.columns
|
||||
Note: A new email address may need to be confirmed prior to use
|
||||
= f.text_field :email_address, { placeholder: "eg. gustav@truffles.com" }
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :phone
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-# This is hopefully a temporary measure, pending the arrival of multiple named inventories
|
||||
-# for shops. We need this here to allow hubs to restrict visible variants to only those in
|
||||
-# their inventory if they so choose
|
||||
.row
|
||||
.alpha.eleven.columns
|
||||
You may opt to manage stock levels and prices in via your
|
||||
= succeed "." do
|
||||
%strong
|
||||
%a{href: main_app.admin_inventory_path } inventory
|
||||
If you are using the inventory tool, you can select whether new products added by your suppliers need to be added to your inventory before they can be stocked. If you are not using your inventory to manage your products you should select the 'recommended' option below:
|
||||
.row
|
||||
.alpha.eleven.columns
|
||||
.five.columns.alpha
|
||||
= radio_button :enterprise, :preferred_product_selection_from_inventory_only, "0", { 'ng-model' => 'Enterprise.preferred_product_selection_from_inventory_only' }
|
||||
= label :enterprise, :preferred_product_selection_from_inventory_only, "New products can be put into my shopfront (recommended)"
|
||||
.six.columns.omega
|
||||
= radio_button :enterprise, :preferred_product_selection_from_inventory_only, "1", { 'ng-model' => 'Enterprise.preferred_product_selection_from_inventory_only' }
|
||||
= label :enterprise, :preferred_product_selection_from_inventory_only, "New products must be added to my inventory before they can be put into my shopfront"
|
||||
@@ -1,6 +1,15 @@
|
||||
- owner_email = @enterprise.andand.owner.andand.email || ""
|
||||
- full_permissions = (spree_current_user.admin? || spree_current_user == @enterprise.andand.owner)
|
||||
|
||||
-if @enterprise.pending_any_confirmation?
|
||||
.alert-box
|
||||
- email = @enterprise.confirmed? ? @enterprise.unconfirmed_email : @enterprise.email
|
||||
Email confirmation is pending.
|
||||
We've sent a confirmation email to
|
||||
%strong= "#{email}."
|
||||
= link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: email } ), method: :post)
|
||||
%a.close{ href: "#" } ×
|
||||
|
||||
.row
|
||||
.three.columns.alpha
|
||||
=f.label :owner_id, 'Owner'
|
||||
@@ -14,6 +23,24 @@
|
||||
- else
|
||||
= owner_email
|
||||
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= f.label :email, 'Notifications'
|
||||
- if full_permissions
|
||||
%span.required *
|
||||
.with-tip{'data-powertip' => "Notifications about orders will be send to this email address."}
|
||||
%a What's this?
|
||||
.eight.columns.omega
|
||||
- if full_permissions
|
||||
= f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" }
|
||||
- else
|
||||
= @enterprise.email
|
||||
.row{ ng: { hide: "pristineEmail == null || pristineEmail == Enterprise.email"} }
|
||||
.alpha.three.columns
|
||||
|
||||
.omega.eight.columns
|
||||
Note: A new email address may need to be confirmed prior to use
|
||||
|
||||
.row
|
||||
.three.columns.alpha
|
||||
=f.label :user_ids, 'Managers'
|
||||
|
||||
22
app/views/admin/order_cycles/_advanced_settings.html.haml
Normal file
22
app/views/admin/order_cycles/_advanced_settings.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
.row
|
||||
.alpha.omega.sixteen.columns
|
||||
%h3 Advanced Settings
|
||||
|
||||
= form_for [main_app, :admin, @order_cycle] do |f|
|
||||
.row
|
||||
.six.columns.alpha
|
||||
= f.label "enterprise_preferred_product_selection_from_coordinator_inventory_only", t('admin.order_cycle.choose_products_from')
|
||||
.with-tip{'data-powertip' => "You can opt to restrict all available products (both incoming and outgoing), to only those in #{@order_cycle.coordinator.name}'s inventory."}
|
||||
%a What's this?
|
||||
.four.columns
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, true
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, "Coordinator's Inventory Only"
|
||||
.six.columns.omega
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, false
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, "All Available Products"
|
||||
|
||||
.row
|
||||
.sixteen.columns.alpha.omega.text-center
|
||||
%input{ type: 'submit', value: 'Save and Reload Page' }
|
||||
or
|
||||
%a{ href: "#", onClick: "toggleSettings()" } Close
|
||||
@@ -9,14 +9,14 @@
|
||||
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' }
|
||||
.exchange-product-details
|
||||
%label
|
||||
= check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}',
|
||||
'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0'
|
||||
-# MASTER_VARIANTS: No longer required
|
||||
-# = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}',
|
||||
-# 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0'
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
.name {{ product.name }}
|
||||
.supplier {{ product.supplier_name }}
|
||||
|
||||
-# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'}
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges | filter:variantSuppliedToOrderCycle'}
|
||||
%label
|
||||
= check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0'
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
- if can? :notify_producers, @order_cycle
|
||||
= content_for :page_actions do
|
||||
- content_for :page_actions do
|
||||
:javascript
|
||||
function toggleSettings(){
|
||||
if( $('#advanced_settings').is(":visible") ){
|
||||
$('button#toggle_settings i').switchClass("icon-chevron-up","icon-chevron-down")
|
||||
}
|
||||
else {
|
||||
$('button#toggle_settings i').switchClass("icon-chevron-down","icon-chevron-up")
|
||||
}
|
||||
$("#advanced_settings").slideToggle()
|
||||
}
|
||||
|
||||
- if can? :notify_producers, @order_cycle
|
||||
%li
|
||||
= button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers', :confirm => 'Are you sure?'
|
||||
%li
|
||||
%button#toggle_settings{ onClick: 'toggleSettings()' }
|
||||
Advanced Settings
|
||||
%i.icon-chevron-down
|
||||
|
||||
|
||||
#advanced_settings{ hidden: true }
|
||||
= render partial: "/admin/order_cycles/advanced_settings"
|
||||
|
||||
%h1 Edit Order Cycle
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.three.columns
|
||||
.ofn-drop-down#bulk-actions-dropdown{ 'ng-controller' => "DropDownCtrl" }
|
||||
%span.icon-check Actions
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
.three.columns.alpha.menu_item{ 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'ofn-close-on-click' => true }
|
||||
%span.three.columns.omega {{action.name }}
|
||||
.ofn-drop-down#bulk-actions-dropdown
|
||||
%span.icon-check= " #{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
.menu{ 'ng-show' => "expanded" }
|
||||
.menu_item{ 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'close-on-click' => true }
|
||||
%span.name {{ action.name }}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
%div.three.columns.omega
|
||||
%div.ofn-drop-down.right#columns-dropdown{ 'ng-controller' => "DropDownCtrl" }
|
||||
%span{ :class => 'icon-reorder' } Columns
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item.three.columns.alpha.omega{ 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true }
|
||||
%span.one.column.alpha.text-center {{ column.visible && "✓" || !column.visible && " " }}
|
||||
%span.two.columns.omega {{column.name }}
|
||||
.ofn-drop-down.right#columns-dropdown
|
||||
%span{ :class => 'icon-reorder' }= " #{t('admin.columns')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ ng: { repeat: "column in columns" }, toggle: { column: true } }
|
||||
%span.check
|
||||
%span.name {{column.name }}
|
||||
|
||||
7
app/views/admin/shared/_views_dropdown.html.haml
Normal file
7
app/views/admin/shared/_views_dropdown.html.haml
Normal file
@@ -0,0 +1,7 @@
|
||||
.ofn-drop-down#views-dropdown
|
||||
%span{ :class => 'icon-eye-open' }= " #{t('admin.viewing', current_view_name: '{{ currentView().name }}')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ ng: { repeat: "(viewKey, view) in views" }, toggle: { view: true }, 'close-on-click' => true }
|
||||
%span.check
|
||||
%span.name {{ view.name }}
|
||||
15
app/views/admin/variant_overrides/_controls.html.haml
Normal file
15
app/views/admin/variant_overrides/_controls.html.haml
Normal file
@@ -0,0 +1,15 @@
|
||||
%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'hub_id && products.length > 0' } }
|
||||
.controls.sixteen.columns.alpha.omega{ ng: { show: 'hub_id && products.length > 0' } }
|
||||
.eight.columns.alpha
|
||||
= render 'admin/shared/bulk_actions_dropdown'
|
||||
= render 'admin/shared/views_dropdown'
|
||||
%span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.inventory.visible' } , data: { powertip: "#{t('admin.inventory.inventory_powertip')}" } }
|
||||
%span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.hidden.visible' } , data: { powertip: "#{t('admin.inventory.hidden_powertip')}" } }
|
||||
%span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.new.visible' } , data: { powertip: "#{t('admin.inventory.new_powertip')}" } }
|
||||
.four.columns
|
||||
.four.columns.omega{ ng: { show: 'views.new.visible' } }
|
||||
%button.fullwidth{ type: 'button', ng: { click: "selectView('inventory')" } }
|
||||
%i.icon-chevron-left
|
||||
Back to my inventory
|
||||
.four.columns.omega{ng: { show: 'views.inventory.visible' } }
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
@@ -3,3 +3,4 @@
|
||||
= admin_inject_hub_permissions
|
||||
= admin_inject_producers module: 'admin.variantOverrides'
|
||||
= admin_inject_variant_overrides
|
||||
= admin_inject_inventory_items module: 'admin.variantOverrides'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
.filters.sixteen.columns.alpha
|
||||
.filter.four.columns.alpha
|
||||
%label{ :for => 'query', ng: {class: '{disabled: !hub.id}'} }Quick Search
|
||||
%label{ :for => 'query', ng: {class: '{disabled: !hub_id}'} }=t('admin.quick_search')
|
||||
%br
|
||||
%input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!hub.id'} }
|
||||
%input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!hub_id'} }
|
||||
.two.columns
|
||||
.filter_select.four.columns
|
||||
%label{ :for => 'hub_id', ng: { bind: 'hub_id ? "Shop" : "Select a shop"' } }
|
||||
%label{ :for => 'hub_id', ng: { bind: "hub_id ? '#{t('admin.shop')}' : '#{t('admin.inventory.select_a_shop')}'" } }
|
||||
%br
|
||||
%select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', ng: { options: 'hub.id as hub.name for (id, hub) in hubs', change: 'selectHub()' } }
|
||||
%select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', ng: { options: 'hub.id as hub.name for (id, hub) in hubs' } }
|
||||
.filter_select.four.columns
|
||||
%label{ :for => 'producer_filter', ng: {class: '{disabled: !hub.id}'} }Producer
|
||||
%label{ :for => 'producer_filter', ng: {class: '{disabled: !hub_id}'} }=t('admin.producer')
|
||||
%br
|
||||
%input.ofn-select2.fullwidth{ :id => 'producer_filter', type: 'number', data: 'producers', blank: "{id: 0, name: 'All'}", ng: { model: 'producerFilter', disabled: '!hub.id' } }
|
||||
%input.ofn-select2.fullwidth{ :id => 'producer_filter', type: 'number', data: 'producers', blank: "{id: 0, name: 'All'}", ng: { model: 'producerFilter', disabled: '!hub_id' } }
|
||||
-# .filter_select{ :class => "three columns" }
|
||||
-# %label{ :for => 'distributor_filter' }Hub
|
||||
-# %br
|
||||
@@ -23,4 +23,4 @@
|
||||
.filter_clear.two.columns.omega
|
||||
%label{ :for => 'clear_all_filters' }
|
||||
%br
|
||||
%input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear All", ng: { click: "resetSelectFilters()", disabled: '!hub.id'} }
|
||||
%input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "#{t('admin.clear_all')}", ng: { click: "resetSelectFilters()", disabled: '!hub_id'} }
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
- content_for :html_title do
|
||||
= t("admin.inventory.title")
|
||||
|
||||
- content_for :page_title do
|
||||
Override Product Details
|
||||
%h1.page-title= t("admin.inventory.title")
|
||||
%a.with-tip{ 'data-powertip' => "#{t("admin.inventory.description")}" }=t('admin.whats_this')
|
||||
|
||||
= render :partial => 'spree/admin/shared/product_sub_menu'
|
||||
|
||||
22
app/views/admin/variant_overrides/_hidden_products.html.haml
Normal file
22
app/views/admin/variant_overrides/_hidden_products.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
%div{ ng: { show: 'views.hidden.visible' } }
|
||||
%table#hidden-products{ ng: { show: 'filteredProducts.length > 0' } }
|
||||
%col.producer{ width: "20%" }
|
||||
%col.product{ width: "20%" }
|
||||
%col.variant{ width: "30%" }
|
||||
%col.add{ width: "15%" }
|
||||
%thead
|
||||
%tr
|
||||
%th.producer=t('admin.producer')
|
||||
%th.product=t('admin.product')
|
||||
%th.variant=t('(admin.variant')
|
||||
%th.add=t('admin.inventory.add')
|
||||
%tbody{ bindonce: true, ng: { repeat: 'product in filteredProducts | limitTo:productLimit' } }
|
||||
%tr{ id: "v_{{variant.id}}", ng: { repeat: 'variant in product.variants | inventoryVariants:hub_id:views' } }
|
||||
%td.producer{ bo: { bind: 'producersByID[product.producer_id].name'} }
|
||||
%td.product{ bo: { bind: 'product.name'} }
|
||||
%td.variant
|
||||
%span{ bo: { bind: 'variant.display_name || ""'} }
|
||||
.variant-override-unit{ bo: { bind: 'variant.unit_to_display'} }
|
||||
%td.add
|
||||
%button.fullwidth.icon-plus{ ng: { click: "setVisibility(hub_id,variant.id,true)" } }
|
||||
= t('admin.inventory.add')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user