mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Auto-merged master into delivery_email_updates on deployment.
This commit is contained in:
@@ -122,7 +122,7 @@ GEM
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.2.1)
|
||||
active_link_to (1.0.0)
|
||||
active_model_serializers (0.8.1)
|
||||
active_model_serializers (0.8.3)
|
||||
activemodel (>= 3.0)
|
||||
activemerchant (1.48.0)
|
||||
activesupport (>= 3.2.14, < 5.0.0)
|
||||
@@ -730,6 +730,3 @@ DEPENDENCIES
|
||||
whenever
|
||||
wicked_pdf
|
||||
wkhtmltopdf-binary
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.6
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
//= require ./taxons/taxons
|
||||
//= require ./utils/utils
|
||||
//= require ./users/users
|
||||
//= require ./variant_overrides/variant_overrides
|
||||
//= require textAngular.min.js
|
||||
//= require textAngular-sanitize.min.js
|
||||
//= require ../shared/bindonce.min.js
|
||||
|
||||
@@ -352,6 +352,9 @@ filterSubmitVariant = (variant) ->
|
||||
filteredVariant = {}
|
||||
if not variant.deleted_at? and variant.hasOwnProperty("id")
|
||||
filteredVariant.id = variant.id unless variant.id <= 0
|
||||
if variant.hasOwnProperty("sku")
|
||||
filteredVariant.sku = variant.sku
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("on_hand")
|
||||
filteredVariant.on_hand = variant.on_hand
|
||||
hasUpdatableProperty = true
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
angular.module("ofn.admin").controller "AdminVariantOverridesCtrl", ($scope, $timeout, Indexer, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) ->
|
||||
$scope.hubs = hubs
|
||||
$scope.hub = null
|
||||
$scope.products = []
|
||||
$scope.producers = Indexer.index producers
|
||||
$scope.hubPermissions = hubPermissions
|
||||
$scope.variantOverrides = VariantOverrides.variantOverrides
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.initialise = ->
|
||||
SpreeApiAuth.authorise()
|
||||
.then ->
|
||||
$scope.spree_api_key_ok = true
|
||||
$scope.fetchProducts()
|
||||
.catch (message) ->
|
||||
$scope.api_error_msg = message
|
||||
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
url = "/api/products/overridable?page=::page::;per_page=100"
|
||||
PagedFetcher.fetch url, (data) => $scope.addProducts data.products
|
||||
|
||||
|
||||
$scope.addProducts = (products) ->
|
||||
$scope.products = $scope.products.concat products
|
||||
VariantOverrides.ensureDataFor hubs, products
|
||||
|
||||
|
||||
$scope.selectHub = ->
|
||||
$scope.hub = (hub for hub in hubs when hub.id == $scope.hub_id)[0]
|
||||
|
||||
|
||||
$scope.displayDirty = ->
|
||||
if DirtyVariantOverrides.count() > 0
|
||||
num = if DirtyVariantOverrides.count() == 1 then "one override" else "#{DirtyVariantOverrides.count()} overrides"
|
||||
StatusMessage.display 'notice', "Changes to #{num} remain unsaved."
|
||||
else
|
||||
StatusMessage.clear()
|
||||
|
||||
|
||||
$scope.update = ->
|
||||
if DirtyVariantOverrides.count() == 0
|
||||
StatusMessage.display 'alert', 'No changes to save.'
|
||||
else
|
||||
StatusMessage.display 'progress', 'Saving...'
|
||||
DirtyVariantOverrides.save()
|
||||
.success (updatedVos) ->
|
||||
DirtyVariantOverrides.clear()
|
||||
VariantOverrides.updateIds updatedVos
|
||||
$timeout -> StatusMessage.display 'success', 'Changes saved.'
|
||||
.error (data, status) ->
|
||||
$timeout -> StatusMessage.display 'failure', $scope.updateError(data, status)
|
||||
|
||||
|
||||
$scope.updateError = (data, status) ->
|
||||
if status == 401
|
||||
"I couldn't get authorisation to save those changes, so they remain unsaved."
|
||||
|
||||
else if status == 400 && data.errors?
|
||||
errors = []
|
||||
for field, field_errors of data.errors
|
||||
errors = errors.concat field_errors
|
||||
errors = errors.join ', '
|
||||
"I had some trouble saving: #{errors}"
|
||||
|
||||
else
|
||||
"Oh no! I was unable to save your changes."
|
||||
@@ -1,8 +1,9 @@
|
||||
angular.module("admin.dropdown").directive "ofnDropDown", ($document) ->
|
||||
restrict: 'C'
|
||||
link: (scope, element, attrs) ->
|
||||
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
|
||||
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
|
||||
scope.$emit "offClick"
|
||||
|
||||
element.click (event) ->
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
angular.module("admin.indexUtils").directive "ofnSelect2", ($timeout, blankOption) ->
|
||||
require: 'ngModel'
|
||||
restrict: 'C'
|
||||
scope:
|
||||
data: "="
|
||||
minSearch: "@?"
|
||||
text: "@?"
|
||||
blank: "=?"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
$timeout ->
|
||||
scope.text ||= 'name'
|
||||
scope.data.unshift(scope.blank) if scope.blank? && typeof scope.blank is "object"
|
||||
element.select2
|
||||
minimumResultsForSearch: scope.minSearch || 0
|
||||
data: { results: scope.data, text: scope.text }
|
||||
initSelection: (element, callback) ->
|
||||
callback scope.data[0]
|
||||
formatSelection: (item) ->
|
||||
item[scope.text]
|
||||
formatResult: (item) ->
|
||||
item[scope.text]
|
||||
|
||||
attrs.$observe 'disabled', (value) ->
|
||||
element.select2('enable', !value)
|
||||
|
||||
ngModel.$formatters.push (value) ->
|
||||
element.select2('val', value)
|
||||
value
|
||||
@@ -1,7 +0,0 @@
|
||||
angular.module("admin.indexUtils").directive "saveBar", ->
|
||||
restrict: "E"
|
||||
scope:
|
||||
save: "&"
|
||||
saving: "&"
|
||||
dirty: "&"
|
||||
templateUrl: "admin/save_bar.html"
|
||||
@@ -0,0 +1,12 @@
|
||||
# Used like a regular angular filter where an object is passed
|
||||
# Adds the additional special case that a value of 0 for the filter
|
||||
# acts as a bypass for that particular attribute
|
||||
angular.module("admin.indexUtils").filter "attrFilter", ($filter) ->
|
||||
return (objects, filters) ->
|
||||
Object.keys(filters).reduce (filtered, attr) ->
|
||||
filter = filters[attr]
|
||||
return filtered if !filter? || filter == 0
|
||||
return $filter('filter')(filtered, (object) ->
|
||||
object[attr] == filter
|
||||
)
|
||||
, objects
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").factory "dataFetcher", [
|
||||
angular.module("admin.indexUtils").factory "dataFetcher", [
|
||||
"$http", "$q"
|
||||
($http, $q) ->
|
||||
return (dataLocation) ->
|
||||
@@ -9,4 +9,4 @@ angular.module("ofn.admin").factory "dataFetcher", [
|
||||
deferred.reject()
|
||||
|
||||
deferred.promise
|
||||
]
|
||||
]
|
||||
@@ -4,7 +4,7 @@
|
||||
# Indexer.index producers
|
||||
# -> {1: {id: 1, name: 'one'}, 2: {id: 2, name: 'two'}}
|
||||
|
||||
angular.module("ofn.admin").factory 'Indexer', ->
|
||||
angular.module("admin.indexUtils").factory 'Indexer', ->
|
||||
new class Indexer
|
||||
index: (data, key='id') ->
|
||||
index = {}
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").factory "PagedFetcher", (dataFetcher) ->
|
||||
angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) ->
|
||||
new class PagedFetcher
|
||||
# Given a URL like http://example.com/foo?page=::page::&per_page=20
|
||||
# And the response includes an attribute pages with the number of pages to fetch
|
||||
@@ -13,4 +13,4 @@ angular.module("ofn.admin").factory "PagedFetcher", (dataFetcher) ->
|
||||
processData data
|
||||
|
||||
urlForPage: (url, page) ->
|
||||
url.replace("::page::", page)
|
||||
url.replace("::page::", page)
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").factory "SpreeApiAuth", ($q, $http, SpreeApiKey) ->
|
||||
angular.module("admin.indexUtils").factory "SpreeApiAuth", ($q, $http, SpreeApiKey) ->
|
||||
new class SpreeApiAuth
|
||||
authorise: ->
|
||||
deferred = $q.defer()
|
||||
@@ -1,7 +1,6 @@
|
||||
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, blankOption, VariantUnitManager, RequestMonitor) ->
|
||||
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, blankOption, VariantUnitManager, RequestMonitor) ->
|
||||
$scope.initialized = false
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.saving = false
|
||||
$scope.filteredLineItems = []
|
||||
$scope.confirmDelete = true
|
||||
$scope.startDate = formatDate daysFromToday -7
|
||||
@@ -55,6 +54,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.enterprisesByID
|
||||
Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.ordersByID
|
||||
$scope.bulk_order_form.$setPristine()
|
||||
StatusMessage.clear()
|
||||
unless $scope.initialized
|
||||
$scope.initialized = true
|
||||
$timeout ->
|
||||
@@ -62,16 +62,20 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.submit = =>
|
||||
$scope.$watch 'bulk_order_form.$dirty', (newVal, oldVal) ->
|
||||
if newVal == true
|
||||
StatusMessage.display 'notice', "You have unsaved changes"
|
||||
|
||||
$scope.submit = ->
|
||||
if $scope.bulk_order_form.$valid
|
||||
$scope.saving = true
|
||||
StatusMessage.display 'progress', "Saving..."
|
||||
$q.all(LineItems.saveAll()).then(->
|
||||
StatusMessage.display 'success', "All changes saved"
|
||||
$scope.bulk_order_form.$setPristine()
|
||||
$scope.saving = false
|
||||
).catch ->
|
||||
alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors."
|
||||
StatusMessage.display 'failure', "Fields with red borders contain errors."
|
||||
else
|
||||
alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors."
|
||||
StatusMessage.display 'failure', "Fields with red borders contain errors."
|
||||
|
||||
$scope.deleteLineItem = (lineItem) ->
|
||||
if ($scope.confirmDelete && confirm("Are you sure?")) || !$scope.confirmDelete
|
||||
|
||||
@@ -1 +1 @@
|
||||
angular.module("admin.lineItems", ["admin.indexUtils", "admin.products", "admin.orders", "admin.enterprises", "admin.orderCycles"])
|
||||
angular.module("admin.lineItems", ["admin.indexUtils", "admin.utils", "admin.products", "admin.orders", "admin.enterprises", "admin.orderCycles"])
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminCreateOrderCycleCtrl', ($scope, $filter, OrderCycle, Enterprise, EnterpriseFee, ocInstance, StatusMessage) ->
|
||||
$scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id})
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.submit = (destination) ->
|
||||
OrderCycle.create(destination)
|
||||
@@ -0,0 +1,84 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) ->
|
||||
order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
|
||||
$scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id)
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.load(order_cycle_id)
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.submit = (destination) ->
|
||||
OrderCycle.update(destination)
|
||||
@@ -1,204 +0,0 @@
|
||||
angular.module('admin.orderCycles', ['ngResource', 'admin.utils'])
|
||||
.controller('AdminCreateOrderCycleCtrl', ['$scope', '$filter', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', 'StatusMessage', ($scope, $filter, OrderCycle, Enterprise, EnterpriseFee, ocInstance, StatusMessage) ->
|
||||
$scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id})
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.submit = (destination) ->
|
||||
OrderCycle.create(destination)
|
||||
])
|
||||
|
||||
.controller('AdminEditOrderCycleCtrl', ['$scope', '$filter', '$location', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'StatusMessage', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) ->
|
||||
order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
|
||||
$scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id)
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.load(order_cycle_id)
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.toggleProducts = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.toggleProducts(exchange)
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.submit = (destination) ->
|
||||
OrderCycle.update(destination)
|
||||
])
|
||||
|
||||
.config(['$httpProvider', ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
|
||||
])
|
||||
|
||||
.directive('datetimepicker', ['$parse', ($parse) ->
|
||||
(scope, element, attrs) ->
|
||||
# using $parse instead of scope[attrs.datetimepicker] for cases
|
||||
# where attrs.datetimepicker is 'foo.bar.lol'
|
||||
$(element).datetimepicker
|
||||
dateFormat: 'yy-mm-dd'
|
||||
timeFormat: 'HH:mm:ss'
|
||||
showOn: "button"
|
||||
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
|
||||
buttonImageOnly: true
|
||||
stepMinute: 15
|
||||
onSelect: (dateText, inst) ->
|
||||
scope.$apply ->
|
||||
parsed = $parse(attrs.datetimepicker)
|
||||
parsed.assign(scope, dateText)
|
||||
])
|
||||
|
||||
.directive('ofnOnChange', ->
|
||||
(scope, element, attrs) ->
|
||||
element.bind 'change', ->
|
||||
scope.$apply(attrs.ofnOnChange)
|
||||
)
|
||||
|
||||
.directive('ofnSyncDistributions', ->
|
||||
(scope, element, attrs) ->
|
||||
element.bind 'change', ->
|
||||
if !$(this).is(':checked')
|
||||
scope.$apply ->
|
||||
scope.removeDistributionOfVariant(attrs.ofnSyncDistributions)
|
||||
)
|
||||
@@ -1 +1,32 @@
|
||||
angular.module('admin.orderCycles', ['ngResource', 'admin.indexUtils'])
|
||||
angular.module('admin.orderCycles', ['ngResource', 'admin.utils', 'admin.indexUtils'])
|
||||
|
||||
.config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
|
||||
|
||||
.directive 'datetimepicker', ($parse) ->
|
||||
(scope, element, attrs) ->
|
||||
# using $parse instead of scope[attrs.datetimepicker] for cases
|
||||
# where attrs.datetimepicker is 'foo.bar.lol'
|
||||
$(element).datetimepicker
|
||||
dateFormat: 'yy-mm-dd'
|
||||
timeFormat: 'HH:mm:ss'
|
||||
showOn: "button"
|
||||
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
|
||||
buttonImageOnly: true
|
||||
stepMinute: 15
|
||||
onSelect: (dateText, inst) ->
|
||||
scope.$apply ->
|
||||
parsed = $parse(attrs.datetimepicker)
|
||||
parsed.assign(scope, dateText)
|
||||
|
||||
.directive 'ofnOnChange', ->
|
||||
(scope, element, attrs) ->
|
||||
element.bind 'change', ->
|
||||
scope.$apply(attrs.ofnOnChange)
|
||||
|
||||
.directive 'ofnSyncDistributions', ->
|
||||
(scope, element, attrs) ->
|
||||
element.bind 'change', ->
|
||||
if !$(this).is(':checked')
|
||||
scope.$apply ->
|
||||
scope.removeDistributionOfVariant(attrs.ofnSyncDistributions)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
angular.module("ofn.admin").factory "StatusMessage", ($timeout) ->
|
||||
new class StatusMessage
|
||||
types:
|
||||
progress: {timeout: false, style: {color: '#ff9906'}}
|
||||
alert: {timeout: 5000, style: {color: 'grey'}}
|
||||
notice: {timeout: false, style: {color: 'grey'}}
|
||||
success: {timeout: 5000, style: {color: '#9fc820'}}
|
||||
failure: {timeout: false, style: {color: '#da5354'}}
|
||||
|
||||
statusMessage:
|
||||
text: ""
|
||||
style: {}
|
||||
|
||||
display: (type, text) ->
|
||||
@statusMessage.text = text
|
||||
@statusMessage.style = @types[type].style
|
||||
$timeout.cancel @statusMessage.timeout if @statusMessage.timeout
|
||||
timeout = @types[type].timeout
|
||||
if timeout
|
||||
@statusMessage.timeout = $timeout =>
|
||||
@clear()
|
||||
, timeout, true
|
||||
|
||||
clear: ->
|
||||
@statusMessage.text = ''
|
||||
@statusMessage.style = {}
|
||||
@@ -1,23 +0,0 @@
|
||||
angular.module("ofn.admin").factory "VariantOverrides", (variantOverrides, Indexer) ->
|
||||
new class VariantOverrides
|
||||
variantOverrides: {}
|
||||
|
||||
constructor: ->
|
||||
for vo in variantOverrides
|
||||
@variantOverrides[vo.hub_id] ||= {}
|
||||
@variantOverrides[vo.hub_id][vo.variant_id] = vo
|
||||
|
||||
ensureDataFor: (hubs, products) ->
|
||||
for hub in hubs
|
||||
@variantOverrides[hub.id] ||= {}
|
||||
for product in products
|
||||
for variant in product.variants
|
||||
@variantOverrides[hub.id][variant.id] ||=
|
||||
variant_id: variant.id
|
||||
hub_id: hub.id
|
||||
price: ''
|
||||
count_on_hand: ''
|
||||
|
||||
updateIds: (updatedVos) ->
|
||||
for vo in updatedVos
|
||||
@variantOverrides[vo.hub_id][vo.variant_id].id = vo.id
|
||||
@@ -0,0 +1,8 @@
|
||||
angular.module("admin.utils").directive "saveBar", (StatusMessage) ->
|
||||
restrict: "E"
|
||||
scope:
|
||||
save: "&"
|
||||
form: "="
|
||||
templateUrl: "admin/save_bar.html"
|
||||
link: (scope, element, attrs) ->
|
||||
scope.StatusMessage = StatusMessage
|
||||
@@ -11,6 +11,9 @@ angular.module("admin.utils").factory "StatusMessage", ($timeout) ->
|
||||
text: ""
|
||||
style: {}
|
||||
|
||||
active: ->
|
||||
@statusMessage.text != ''
|
||||
|
||||
display: (type, text) ->
|
||||
@statusMessage.text = text
|
||||
@statusMessage.style = @types[type].style
|
||||
@@ -20,6 +23,7 @@ angular.module("admin.utils").factory "StatusMessage", ($timeout) ->
|
||||
@statusMessage.timeout = $timeout =>
|
||||
@clear()
|
||||
, timeout, true
|
||||
null # So we don't return weird timeouts
|
||||
|
||||
clear: ->
|
||||
@statusMessage.text = ''
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) ->
|
||||
$scope.hubs = Indexer.index hubs
|
||||
$scope.hub = null
|
||||
$scope.products = []
|
||||
$scope.producers = producers
|
||||
$scope.producersByID = Indexer.index producers
|
||||
$scope.hubPermissions = hubPermissions
|
||||
$scope.variantOverrides = VariantOverrides.variantOverrides
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.columns = Columns.setColumns
|
||||
producer: { name: "Producer", visible: true }
|
||||
product: { name: "Product", visible: true }
|
||||
sku: { name: "SKU", visible: false }
|
||||
price: { name: "Price", visible: true }
|
||||
on_hand: { name: "On Hand", visible: true }
|
||||
on_demand: { name: "On Demand", visible: false }
|
||||
reset: { name: "Reset Stock Level", visible: false }
|
||||
inheritance: { name: "Inheritance", visible: false }
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.producerFilter = 0
|
||||
$scope.query = ''
|
||||
|
||||
$scope.resetSelectFilters()
|
||||
|
||||
$scope.initialise = ->
|
||||
SpreeApiAuth.authorise()
|
||||
.then ->
|
||||
$scope.spree_api_key_ok = true
|
||||
$scope.fetchProducts()
|
||||
.catch (message) ->
|
||||
$scope.api_error_msg = message
|
||||
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
url = "/api/products/overridable?page=::page::;per_page=100"
|
||||
PagedFetcher.fetch url, (data) => $scope.addProducts data.products
|
||||
|
||||
|
||||
$scope.addProducts = (products) ->
|
||||
$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"
|
||||
StatusMessage.display 'notice', "Changes to #{num} remain unsaved."
|
||||
else
|
||||
StatusMessage.clear()
|
||||
|
||||
$scope.update = ->
|
||||
if DirtyVariantOverrides.count() == 0
|
||||
StatusMessage.display 'alert', 'No changes to save.'
|
||||
else
|
||||
StatusMessage.display 'progress', 'Saving...'
|
||||
DirtyVariantOverrides.save()
|
||||
.success (updatedVos) ->
|
||||
DirtyVariantOverrides.clear()
|
||||
VariantOverrides.updateIds updatedVos
|
||||
$scope.variant_overrides_form.$setPristine()
|
||||
StatusMessage.display 'success', 'Changes saved.'
|
||||
VariantOverrides.updateData updatedVos # Refresh page data
|
||||
.error (data, status) ->
|
||||
StatusMessage.display 'failure', $scope.updateError(data, status)
|
||||
|
||||
|
||||
$scope.updateError = (data, status) ->
|
||||
if status == 401
|
||||
"I couldn't get authorisation to save those changes, so they remain unsaved."
|
||||
|
||||
else if status == 400 && data.errors?
|
||||
errors = []
|
||||
for field, field_errors of data.errors
|
||||
errors = errors.concat field_errors
|
||||
errors = errors.join ', '
|
||||
"I had some trouble saving: #{errors}"
|
||||
else
|
||||
"Oh no! I was unable to save your changes."
|
||||
|
||||
$scope.resetStock = ->
|
||||
if DirtyVariantOverrides.count() > 0
|
||||
StatusMessage.display 'alert', 'Save changes first.'
|
||||
$timeout ->
|
||||
$scope.displayDirty()
|
||||
, 3000 # 3 second delay
|
||||
else
|
||||
return unless $scope.hub_id?
|
||||
StatusMessage.display 'progress', 'Changing on hand stock levels...'
|
||||
$http
|
||||
method: "POST"
|
||||
url: "/admin/variant_overrides/bulk_reset"
|
||||
data: { hub_id: $scope.hub_id }
|
||||
.success (updatedVos) ->
|
||||
VariantOverrides.updateData updatedVos
|
||||
StatusMessage.display 'success', 'Stocks reset to defaults.'
|
||||
.error (data, status) ->
|
||||
$timeout -> StatusMessage.display 'failure', $scope.updateError(data, status)
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module("admin.variantOverrides").directive "trackInheritance", (VariantOverrides, DirtyVariantOverrides) ->
|
||||
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
|
||||
|
||||
ngModel.$parsers.push (viewValue) ->
|
||||
if ngModel.$dirty && viewValue
|
||||
variantOverride = VariantOverrides.inherit(scope.hub.id, scope.variant.id)
|
||||
DirtyVariantOverrides.add variantOverride
|
||||
scope.displayDirty()
|
||||
viewValue
|
||||
@@ -1,9 +1,10 @@
|
||||
angular.module("ofn.admin").directive "ofnTrackVariantOverride", (DirtyVariantOverrides) ->
|
||||
angular.module("admin.variantOverrides").directive "ofnTrackVariantOverride", (DirtyVariantOverrides) ->
|
||||
require: "ngModel"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
ngModel.$parsers.push (viewValue) ->
|
||||
if ngModel.$dirty
|
||||
variantOverride = scope.variantOverrides[scope.hub.id][scope.variant.id]
|
||||
scope.inherit = false
|
||||
DirtyVariantOverrides.add variantOverride
|
||||
scope.displayDirty()
|
||||
viewValue
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").filter "hubPermissions", ($filter) ->
|
||||
angular.module("admin.variantOverrides").filter "hubPermissions", ($filter) ->
|
||||
return (products, hubPermissions, hub_id) ->
|
||||
return [] if !hub_id
|
||||
return $filter('filter')(products, ((product) -> hubPermissions[hub_id].indexOf(product.producer_id) > -1), true)
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").factory "DirtyVariantOverrides", ($http) ->
|
||||
angular.module("admin.variantOverrides").factory "DirtyVariantOverrides", ($http) ->
|
||||
new class DirtyVariantOverrides
|
||||
dirtyVariantOverrides: {}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
angular.module("admin.variantOverrides").factory "VariantOverrides", (variantOverrides) ->
|
||||
new class VariantOverrides
|
||||
variantOverrides: {}
|
||||
|
||||
constructor: ->
|
||||
for vo in variantOverrides
|
||||
@variantOverrides[vo.hub_id] ||= {}
|
||||
@variantOverrides[vo.hub_id][vo.variant_id] = vo
|
||||
|
||||
ensureDataFor: (hubs, products) ->
|
||||
for hub_id, hub of hubs
|
||||
@variantOverrides[hub.id] ||= {}
|
||||
for product in products
|
||||
for variant in product.variants
|
||||
@inherit(hub.id, variant.id) unless @variantOverrides[hub.id][variant.id]
|
||||
|
||||
inherit: (hub_id, variant_id) ->
|
||||
# This method is called from the trackInheritance directive, to reinstate inheritance
|
||||
@variantOverrides[hub_id][variant_id] ||= {}
|
||||
angular.extend @variantOverrides[hub_id][variant_id], @newFor hub_id, variant_id
|
||||
|
||||
newFor: (hub_id, variant_id) ->
|
||||
# These properties need to match those checked in VariantOverrideSet.deletable?
|
||||
hub_id: hub_id
|
||||
variant_id: variant_id
|
||||
sku: null
|
||||
price: null
|
||||
count_on_hand: null
|
||||
on_demand: null
|
||||
default_stock: null
|
||||
resettable: false
|
||||
|
||||
updateIds: (updatedVos) ->
|
||||
for vo in updatedVos
|
||||
@variantOverrides[vo.hub_id][vo.variant_id].id = vo.id
|
||||
|
||||
updateData: (updatedVos) ->
|
||||
for vo in updatedVos
|
||||
@variantOverrides[vo.hub_id][vo.variant_id] = vo
|
||||
@@ -0,0 +1 @@
|
||||
angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown"])
|
||||
@@ -1,4 +1,4 @@
|
||||
.ofn_drop_down{ "ofn-drop-down" => true }
|
||||
.ofn-drop-down
|
||||
%span
|
||||
%i.icon-check
|
||||
Actions
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#save-bar.animate-show{ ng: { show: 'dirty()' } }
|
||||
#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } }
|
||||
.twelve.columns.alpha
|
||||
%h5{ ng: { show: "dirty() && !saving()" } }
|
||||
You have unsaved changes
|
||||
%h5{ ng: { hide: "dirty() || saving()" } }
|
||||
All changes saved
|
||||
%h5{ ng: { show: "saving()" } }
|
||||
Saving...
|
||||
%h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } }
|
||||
{{ StatusMessage.statusMessage.text || " " }}
|
||||
.four.columns.omega.text-right
|
||||
%input.red{type: "button", value: "Save Changes", ng: { click: "save()" } }
|
||||
%input.red{type: "button", value: "Save Changes", ng: { disabled: '!form.$dirty', click: "save()" } }
|
||||
|
||||
13
app/assets/stylesheets/admin/disabled.css.scss
Normal file
13
app/assets/stylesheets/admin/disabled.css.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
label.disabled {
|
||||
color: #c3c3c3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
input[type='button']:disabled {
|
||||
background-color: #c3c3c3;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.select2-container-disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
70
app/assets/stylesheets/admin/dropdown.css.scss
Normal file
70
app/assets/stylesheets/admin/dropdown.css.scss
Normal file
@@ -0,0 +1,70 @@
|
||||
#content-header .ofn-drop-down {
|
||||
border: none;
|
||||
background-color: #5498da;
|
||||
color: #fff;
|
||||
float: none;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.ofn-drop-down:hover, .ofn-drop-down.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
.ofn-drop-down {
|
||||
padding: 7px 15px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d4d4d4;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
display: block;
|
||||
float: left;
|
||||
color: #828282;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&:hover, &.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
> span {
|
||||
width: auto;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu {
|
||||
margin-top: 1px;
|
||||
position: absolute;
|
||||
float: none;
|
||||
top:100%;
|
||||
left: 0px;
|
||||
padding: 5px 0px;
|
||||
border: 1px solid #adadad;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 1px 3px 10px #888888;
|
||||
z-index: 100;
|
||||
|
||||
.menu_item {
|
||||
margin: 0px;
|
||||
padding: 2px 0px;
|
||||
color: #454545;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.menu_item:hover {
|
||||
background-color: #ededed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.filters, .controls, .divider {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -184,68 +184,6 @@ table#listing_enterprise_groups {
|
||||
}
|
||||
}
|
||||
|
||||
#content-header .ofn_drop_down {
|
||||
border: none;
|
||||
background-color: #5498da;
|
||||
color: #fff;
|
||||
float: none;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.ofn_drop_down {
|
||||
padding: 6px 15px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d4d4d4;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
display: block;
|
||||
float: left;
|
||||
color: #828282;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
|
||||
> span {
|
||||
width: auto;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu {
|
||||
margin-top: 1px;
|
||||
position: absolute;
|
||||
float: none;
|
||||
top:100%;
|
||||
left: 0px;
|
||||
padding: 5px 0px;
|
||||
border: 1px solid #adadad;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 1px 3px 10px #888888;
|
||||
z-index: 100;
|
||||
|
||||
.menu_item {
|
||||
margin: 0px;
|
||||
padding: 2px 0px;
|
||||
color: #454545;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.menu_item:hover {
|
||||
background-color: #ededed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ofn_drop_down:hover, .ofn_drop_down.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
.field_with_errors > input {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.filter_select, .date_filter {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input, div {
|
||||
&.update-pending {
|
||||
border: solid 1px orange;
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
right: 10px
|
||||
top: 55px
|
||||
width: 480px
|
||||
|
||||
@media screen and (min-width: 641px)
|
||||
overflow-y: auto
|
||||
max-height: calc(95vh - 55px)
|
||||
|
||||
@media screen and (max-width: 640px)
|
||||
width: 96%
|
||||
|
||||
@@ -48,7 +53,7 @@
|
||||
.cart-item-delete
|
||||
a.delete
|
||||
font-size: 1.125em
|
||||
|
||||
|
||||
.item-thumb-image
|
||||
display: none
|
||||
@media screen and (min-width: 640px)
|
||||
|
||||
@@ -89,7 +89,7 @@ module Admin
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
[:index, :for_order_cycle]
|
||||
[:index, :for_order_cycle, :bulk_update]
|
||||
end
|
||||
|
||||
def current_enterprise
|
||||
|
||||
@@ -175,5 +175,9 @@ module Admin
|
||||
def ams_prefix_whitelist
|
||||
[:basic]
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
[:index, :bulk_update]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,32 +4,43 @@ module Admin
|
||||
class VariantOverridesController < ResourceController
|
||||
include OpenFoodNetwork::SpreeApiKeyLoader
|
||||
|
||||
prepend_before_filter :load_data
|
||||
before_filter :load_collection, only: [:bulk_update]
|
||||
before_filter :load_spree_api_key, only: :index
|
||||
before_filter :load_data
|
||||
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
|
||||
def bulk_update
|
||||
collection_hash = Hash[params[:variant_overrides].each_with_index.map { |vo, i| [i, vo] }]
|
||||
vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash
|
||||
|
||||
# Ensure we're authorised to update all variant overrides
|
||||
vo_set.collection.each { |vo| authorize! :update, vo }
|
||||
@vo_set.collection.each { |vo| authorize! :update, vo }
|
||||
|
||||
if vo_set.save
|
||||
if @vo_set.save
|
||||
# Return saved VOs with IDs
|
||||
render json: vo_set.collection, each_serializer: Api::Admin::VariantOverrideSerializer
|
||||
render json: @vo_set.collection, each_serializer: Api::Admin::VariantOverrideSerializer
|
||||
else
|
||||
if vo_set.errors.present?
|
||||
render json: { errors: vo_set.errors }, status: 400
|
||||
if @vo_set.errors.present?
|
||||
render json: { errors: @vo_set.errors }, status: 400
|
||||
else
|
||||
render nothing: true, status: 500
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_reset
|
||||
# Ensure we're authorised to update all variant overrides.
|
||||
@collection.each { |vo| authorize! :bulk_reset, vo }
|
||||
@collection.each(&:reset_stock!)
|
||||
|
||||
if collection_errors.present?
|
||||
render json: { errors: collection_errors }, status: 400
|
||||
else
|
||||
render json: @collection, each_serializer: Api::Admin::VariantOverrideSerializer
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
@@ -43,10 +54,28 @@ module Admin
|
||||
|
||||
@hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user).
|
||||
variant_override_enterprises_per_hub
|
||||
@variant_overrides = VariantOverride.for_hubs(@hubs)
|
||||
end
|
||||
|
||||
def load_collection
|
||||
collection_hash = Hash[params[:variant_overrides].each_with_index.map { |vo, i| [i, vo] }]
|
||||
@vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash
|
||||
end
|
||||
|
||||
def collection
|
||||
@variant_overrides = VariantOverride.for_hubs(params[:hub_id] || @hubs)
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
[:index, :bulk_update, :bulk_reset]
|
||||
end
|
||||
|
||||
# This has been pulled from ModelSet as it is useful for compiling a list of errors on any generic collection (not necessarily a ModelSet)
|
||||
# Could be pulled down into a lower level controller if it is useful in other high level controllers
|
||||
def collection_errors
|
||||
errors = ActiveModel::Errors.new self
|
||||
full_messages = @collection.map { |element| element.errors.full_messages }.flatten
|
||||
full_messages.each { |fm| errors.add(:base, fm) }
|
||||
errors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,12 +31,12 @@ module Admin
|
||||
admin_inject_json_ams_array ngModule, "shops", @shops, Api::Admin::IdNameSerializer
|
||||
end
|
||||
|
||||
def admin_inject_hubs
|
||||
admin_inject_json_ams_array "ofn.admin", "hubs", @hubs, Api::Admin::IdNameSerializer
|
||||
def admin_inject_hubs(opts={module: 'ofn.admin'})
|
||||
admin_inject_json_ams_array opts[:module], "hubs", @hubs, Api::Admin::IdNameSerializer
|
||||
end
|
||||
|
||||
def admin_inject_producers
|
||||
admin_inject_json_ams_array "ofn.admin", "producers", @producers, Api::Admin::IdNameSerializer
|
||||
def admin_inject_producers(opts={module: 'ofn.admin'})
|
||||
admin_inject_json_ams_array opts[:module], "producers", @producers, Api::Admin::IdNameSerializer
|
||||
end
|
||||
|
||||
def admin_inject_enterprise_permissions
|
||||
@@ -49,7 +49,7 @@ module Admin
|
||||
end
|
||||
|
||||
def admin_inject_hub_permissions
|
||||
render partial: "admin/json/injection_ams", locals: {ngModule: "ofn.admin", name: "hubPermissions", json: @hub_permissions.to_json}
|
||||
render partial: "admin/json/injection_ams", locals: {ngModule: "admin.variantOverrides", name: "hubPermissions", json: @hub_permissions.to_json}
|
||||
end
|
||||
|
||||
def admin_inject_products
|
||||
@@ -69,7 +69,7 @@ module Admin
|
||||
end
|
||||
|
||||
def admin_inject_variant_overrides
|
||||
admin_inject_json_ams_array "ofn.admin", "variantOverrides", @variant_overrides, Api::Admin::VariantOverrideSerializer
|
||||
admin_inject_json_ams_array "admin.variantOverrides", "variantOverrides", @variant_overrides, Api::Admin::VariantOverrideSerializer
|
||||
end
|
||||
|
||||
def admin_inject_order_cycle_instance
|
||||
@@ -85,7 +85,7 @@ module Admin
|
||||
end
|
||||
|
||||
def admin_inject_spree_api_key
|
||||
render partial: "admin/json/injection_ams", locals: {ngModule: 'ofn.admin', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"}
|
||||
render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.indexUtils', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"}
|
||||
end
|
||||
|
||||
def admin_inject_json_ams(ngModule, name, data, serializer, opts = {})
|
||||
|
||||
@@ -10,4 +10,4 @@ Spree::BaseMailer.class_eval do
|
||||
# This lets us specify assets using relative paths in email templates
|
||||
super.merge(url_options: {host: URI(spree.root_url).host })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,8 +16,8 @@ class ModelSet
|
||||
end
|
||||
end
|
||||
|
||||
def collection_attributes=(attributes)
|
||||
attributes.each do |k, attributes|
|
||||
def collection_attributes=(collection_attributes)
|
||||
collection_attributes.each do |k, attributes|
|
||||
# attributes == {:id => 123, :next_collection_at => '...'}
|
||||
e = @collection.detect { |e| e.id.to_s == attributes[:id].to_s && !e.id.nil? }
|
||||
if e.nil?
|
||||
@@ -41,7 +41,11 @@ class ModelSet
|
||||
end
|
||||
|
||||
def collection_to_delete
|
||||
collection.select { |e| @delete_if.andand.call(e.attributes) }
|
||||
# Remove all elements to be deleted from collection and return them
|
||||
# Allows us to render @model_set.collection without deleted elements
|
||||
deleted = []
|
||||
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) }
|
||||
deleted
|
||||
end
|
||||
|
||||
def collection_to_keep
|
||||
@@ -51,5 +55,4 @@ class ModelSet
|
||||
def persisted?
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ class AbilityDecorator
|
||||
def add_enterprise_management_abilities(user)
|
||||
# Spree performs authorize! on (:create, nil) when creating a new order from admin, and also (:search, nil)
|
||||
# when searching for variants to add to the order
|
||||
can [:create, :search, :bulk_update], nil
|
||||
can [:create, :search], nil
|
||||
|
||||
can [:admin, :index], :overview
|
||||
|
||||
@@ -111,7 +111,9 @@ class AbilityDecorator
|
||||
OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? variant.product.supplier
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :update, :bulk_update], VariantOverride do |vo|
|
||||
can [:admin, :index, :read, :update, :bulk_update, :bulk_reset], VariantOverride do |vo|
|
||||
next false unless vo.hub.present? && vo.variant.andand.product.andand.supplier.present?
|
||||
|
||||
hub_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_hubs.
|
||||
include? vo.hub
|
||||
|
||||
@@ -3,6 +3,8 @@ class VariantOverride < ActiveRecord::Base
|
||||
belongs_to :variant, class_name: 'Spree::Variant'
|
||||
|
||||
validates_presence_of :hub_id, :variant_id
|
||||
# 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
|
||||
|
||||
scope :for_hubs, lambda { |hubs|
|
||||
where(hub_id: hubs)
|
||||
@@ -49,6 +51,21 @@ class VariantOverride < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def default_stock?
|
||||
default_stock.present?
|
||||
end
|
||||
|
||||
def reset_stock!
|
||||
if resettable
|
||||
if default_stock?
|
||||
self.attributes = { count_on_hand: default_stock }
|
||||
self.save
|
||||
else
|
||||
Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant with no default stock level."
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
class VariantOverrideSet < ModelSet
|
||||
def initialize(collection, attributes={})
|
||||
super(VariantOverride, collection, attributes, nil,
|
||||
proc { |attrs| attrs['price'].blank? && attrs['count_on_hand'].blank? } )
|
||||
super(VariantOverride, collection, attributes, nil, proc { |attrs| deletable?(attrs) } )
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deletable?(attrs)
|
||||
attrs['price'].blank? &&
|
||||
attrs['count_on_hand'].blank? &&
|
||||
attrs['default_stock'].blank? &&
|
||||
attrs['resettable'].blank? &&
|
||||
attrs['sku'].nil? &&
|
||||
attrs['on_demand'].nil?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
class Api::Admin::VariantOverrideSerializer < ActiveModel::Serializer
|
||||
attributes :id, :hub_id, :variant_id, :price, :count_on_hand
|
||||
attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock, :resettable
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Api::Admin::VariantSerializer < ActiveModel::Serializer
|
||||
attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display
|
||||
attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display, :sku
|
||||
attributes :on_hand, :price
|
||||
has_many :variant_overrides
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Api::LineItemSerializer < ActiveModel::Serializer
|
||||
attributes :id, :quantity, :price
|
||||
attributes :id, :quantity, :max_quantity, :price
|
||||
|
||||
has_one :variant, serializer: Api::VariantSerializer
|
||||
end
|
||||
|
||||
@@ -12,25 +12,13 @@
|
||||
.seven.columns.omega
|
||||
|
||||
.row{ 'ng-hide' => '!loaded() || filteredCustomers.length == 0' }
|
||||
.controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" }
|
||||
.controls.sixteen.columns.alpha.omega
|
||||
.five.columns.alpha
|
||||
%input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
|
||||
%input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
|
||||
.five.columns
|
||||
-# %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true }
|
||||
-# %span{ :class => 'icon-check' } Actions
|
||||
-# %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
-# %div.menu{ 'ng-show' => "expanded" }
|
||||
-# %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "selectedBulkAction.callback(filteredCustomers)", 'ofn-close-on-click' => true }
|
||||
-# %span{ :class => 'three columns omega' } {{action.name }}
|
||||
-# =render 'admin/shared/bulk_actions_dropdown'
|
||||
.three.columns
|
||||
.three.columns.omega
|
||||
%div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' }
|
||||
%span{ :class => 'icon-reorder' } Columns
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true }
|
||||
%span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }}
|
||||
%span{ :class => 'two columns omega' } {{column.name }}
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
.row{ 'ng-if' => 'shop && !loaded()' }
|
||||
.sixteen.columns.alpha#loading
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
|
||||
@@ -4,21 +4,9 @@
|
||||
.four.columns.alpha
|
||||
%input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Search By Name' }
|
||||
.six.columns
|
||||
-# %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true }
|
||||
-# %span{ :class => 'icon-check' } Actions
|
||||
-# %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
-# %div.menu{ 'ng-show' => "expanded" }
|
||||
-# %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "selectedBulkAction.callback(filteredEnterprises)", 'ofn-close-on-click' => true }
|
||||
-# %span{ :class => 'three columns omega' } {{action.name }}
|
||||
-# = render 'admin/shared/bulk_actions_dropdown'
|
||||
.three.columns
|
||||
.three.columns.omega
|
||||
%div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' }
|
||||
%span{ :class => 'icon-reorder' } Columns
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true }
|
||||
%span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }}
|
||||
%span{ :class => 'two columns omega' } {{column.name }}
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
.row{ 'ng-if' => '!loaded' }
|
||||
.sixteen.columns.alpha#loading
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
|
||||
7
app/views/admin/shared/_bulk_actions_dropdown.html.haml
Normal file
7
app/views/admin/shared/_bulk_actions_dropdown.html.haml
Normal file
@@ -0,0 +1,7 @@
|
||||
.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 }}
|
||||
8
app/views/admin/shared/_columns_dropdown.html.haml
Normal file
8
app/views/admin/shared/_columns_dropdown.html.haml
Normal file
@@ -0,0 +1,8 @@
|
||||
%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 }}
|
||||
@@ -1,4 +0,0 @@
|
||||
.row
|
||||
%input.four.columns.alpha{type: 'button', value: 'Save Changes', 'ng-click' => 'update()'}
|
||||
.twelve.columns.omega
|
||||
= render 'spree/admin/shared/status_message'
|
||||
@@ -1,5 +1,5 @@
|
||||
= admin_inject_spree_api_key
|
||||
= admin_inject_hubs
|
||||
= admin_inject_hubs module: 'admin.variantOverrides'
|
||||
= admin_inject_hub_permissions
|
||||
= admin_inject_producers
|
||||
= admin_inject_producers module: 'admin.variantOverrides'
|
||||
= admin_inject_variant_overrides
|
||||
|
||||
26
app/views/admin/variant_overrides/_filters.html.haml
Normal file
26
app/views/admin/variant_overrides/_filters.html.haml
Normal file
@@ -0,0 +1,26 @@
|
||||
.filters.sixteen.columns.alpha
|
||||
.filter.four.columns.alpha
|
||||
%label{ :for => 'query', ng: {class: '{disabled: !hub.id}'} }Quick Search
|
||||
%br
|
||||
%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"' } }
|
||||
%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()' } }
|
||||
.filter_select.four.columns
|
||||
%label{ :for => 'producer_filter', ng: {class: '{disabled: !hub.id}'} }Producer
|
||||
%br
|
||||
%input.ofn-select2.fullwidth{ :id => 'producer_filter', type: 'number', style: 'display:none', data: 'producers', blank: "{id: 0, name: 'All'}", ng: { model: 'producerFilter', disabled: '!hub.id' } }
|
||||
-# .filter_select{ :class => "three columns" }
|
||||
-# %label{ :for => 'distributor_filter' }Hub
|
||||
-# %br
|
||||
-# %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'}
|
||||
-# .filter_select{ :class => "three columns" }
|
||||
-# %label{ :for => 'order_cycle_filter' }Order Cycle
|
||||
-# %br
|
||||
-# %select{ :class => "three columns alpha", :id => 'order_cycle_filter', 'select2-min-search' => 5, 'ng-model' => 'orderCycleFilter', 'ng-options' => 'oc.id as oc.name for oc in orderCycles', 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()'}
|
||||
.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'} }
|
||||
@@ -1,7 +0,0 @@
|
||||
.row
|
||||
.two.columns.alpha
|
||||
Hub
|
||||
.four.columns
|
||||
%select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', 'ng-options' => 'hub.id as hub.name for hub in hubs' }
|
||||
.ten.columns.omega
|
||||
%input{ type: 'button', value: 'Go', 'ng-click' => 'selectHub()' }
|
||||
@@ -1,10 +1,23 @@
|
||||
%table.index.bulk{ng: {show: 'hub'}}
|
||||
%table.index.bulk{ ng: {show: 'hub'}}
|
||||
%col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } }
|
||||
%col.product{ width: "20%", ng: { show: 'columns.product.visible' } }
|
||||
%col.sku{ width: "20%", ng: { show: 'columns.sku.visible' } }
|
||||
%col.price{ width: "10%", ng: { show: 'columns.price.visible' } }
|
||||
%col.on_hand{ width: "10%", ng: { show: 'columns.on_hand.visible' } }
|
||||
%col.on_demand{ width: "10%", ng: { show: 'columns.on_demand.visible' } }
|
||||
%col.reset{ width: "1%", ng: { show: 'columns.reset.visible' } }
|
||||
%col.reset{ width: "15%", ng: { show: 'columns.reset.visible' } }
|
||||
%col.inheritance{ width: "5%", ng: { show: 'columns.inheritance.visible' } }
|
||||
%thead
|
||||
%tr
|
||||
%th Producer
|
||||
%th Product
|
||||
%th Price
|
||||
%th On hand
|
||||
%tbody{ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id'}}
|
||||
%tr{ ng: { controller: "ColumnsCtrl" } }
|
||||
%th.producer{ ng: { show: 'columns.producer.visible' } } Producer
|
||||
%th.product{ ng: { show: 'columns.product.visible' } } Product
|
||||
%th.sku{ ng: { show: 'columns.sku.visible' } } SKU
|
||||
%th.price{ ng: { show: 'columns.price.visible' } } Price
|
||||
%th.on_hand{ ng: { show: 'columns.on_hand.visible' } } On hand
|
||||
%th.on_demand{ ng: { show: 'columns.on_demand.visible' } } On Demand?
|
||||
%th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } Enable Stock Level Reset?
|
||||
%th.inheritance{ ng: { show: 'columns.inheritance.visible' } } Inherit?
|
||||
%tbody{bindonce: true, ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id | attrFilter:{producer_id:producerFilter} | filter:query' } }
|
||||
= render 'admin/variant_overrides/products_product'
|
||||
= render 'admin/variant_overrides/products_variants'
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
%tr.product.even
|
||||
%td {{ producers[product.producer_id].name }}
|
||||
%td {{ product.name }}
|
||||
%td
|
||||
%td
|
||||
%td.producer{ ng: { show: 'columns.producer.visible' }, bo: { bind: 'producersByID[product.producer_id].name'} }
|
||||
%td.product{ ng: { show: 'columns.product.visible' }, bo: { bind: 'product.name'} }
|
||||
%td.sku{ ng: { show: 'columns.sku.visible' } }
|
||||
%td.price{ ng: { show: 'columns.price.visible' } }
|
||||
%td.on_hand{ ng: { show: 'columns.on_hand.visible' } }
|
||||
%td.on_demand{ ng: { show: 'columns.on_demand.visible' } }
|
||||
%td.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } }
|
||||
%td.inheritance{ ng: { show: 'columns.inheritance.visible' } }
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
%tr.variant{ng: {repeat: 'variant in product.variants'}}
|
||||
%td
|
||||
%td
|
||||
{{ variant.display_name }}
|
||||
.variant-override-unit {{ variant.unit_to_display }}
|
||||
%td
|
||||
%tr.variant{ id: "v_{{variant.id}}", ng: {repeat: 'variant in product.variants'}}
|
||||
%td.producer{ ng: { show: 'columns.producer.visible' } }
|
||||
%td.product{ ng: { show: 'columns.product.visible' } }
|
||||
%span{ bo: { bind: 'variant.display_name || ""'} }
|
||||
.variant-override-unit{ bo: { bind: 'variant.unit_to_display'} }
|
||||
%td.sku{ ng: { show: 'columns.sku.visible' } }
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-sku', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].sku'}, placeholder: '{{ variant.sku }}', 'ofn-track-variant-override' => 'sku'}
|
||||
%td.price{ ng: { show: 'columns.price.visible' } }
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-price', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].price'}, placeholder: '{{ variant.price }}', 'ofn-track-variant-override' => 'price'}
|
||||
|
||||
%td
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-count-on-hand', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].count_on_hand'}, placeholder: '{{ variant.on_hand }}', 'ofn-track-variant-override' => 'price'}
|
||||
%td.on_hand{ ng: { show: 'columns.on_hand.visible' } }
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].count_on_hand'}, placeholder: '{{ variant.on_hand }}', 'ofn-track-variant-override' => 'count_on_hand'}
|
||||
%td.on_demand{ ng: { show: 'columns.on_demand.visible' } }
|
||||
%input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-on_demand', ng: { model: 'variantOverrides[hub.id][variant.id].on_demand' }, 'ofn-track-variant-override' => 'on_demand' }
|
||||
%td.reset{ ng: { show: 'columns.reset.visible' } }
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-resettable', type: 'checkbox', ng: {model: 'variantOverrides[hub.id][variant.id].resettable'}, placeholder: '{{ variant.resettable }}', 'ofn-track-variant-override' => 'resettable'}
|
||||
%td.reset{ ng: { show: 'columns.reset.visible' } }
|
||||
%input{name: 'variant-overrides-{{ variant.id }}-default_stock', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].default_stock'}, placeholder: '{{ variant.default_stock ? variant.default_stock : "Default stock"}}', 'ofn-track-variant-override' => 'default_stock'}
|
||||
%td.inheritance{ ng: { show: 'columns.inheritance.visible' } }
|
||||
%input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-inherit', ng: { model: 'inherit' }, 'track-inheritance' => true }
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
= render 'admin/variant_overrides/header'
|
||||
= render 'admin/variant_overrides/data'
|
||||
|
||||
%div{ ng: { app: 'ofn.admin', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } }
|
||||
= render 'admin/variant_overrides/hub_choice'
|
||||
%div{ ng: { app: 'admin.variantOverrides', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } }
|
||||
= render 'admin/variant_overrides/filters'
|
||||
%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'hub' } }
|
||||
.controls.sixteen.columns.alpha.omega{ ng: { show: 'hub' } }
|
||||
%input.four.columns.alpha{ type: 'button', value: 'Reset Stock to Defaults', 'ng-click' => 'resetStock()' }
|
||||
%div.nine.columns.alpha
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
|
||||
%div{ng: {show: 'hub'}}
|
||||
%h2 {{ hub.name }}
|
||||
= render 'admin/variant_overrides/actions'
|
||||
|
||||
= render 'admin/variant_overrides/products'
|
||||
%form{ name: 'variant_overrides_form' }
|
||||
%save-bar{ save: "update()", form: "variant_overrides_form" }
|
||||
= render 'admin/variant_overrides/products'
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
- content_for(:title) do
|
||||
= current_distributor.name
|
||||
- content_for(:description) do
|
||||
= current_distributor.description
|
||||
- content_for(:image) do
|
||||
= current_distributor.logo.url
|
||||
|
||||
= inject_enterprises
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
- content_for(:title) do
|
||||
= @group.name
|
||||
- content_for(:description) do
|
||||
= @group.description
|
||||
- content_for(:image) do
|
||||
= @group.logo.url
|
||||
|
||||
-# inject all enterprises as "enterprises"
|
||||
-# it could be more efficient to inject only the enterprises that are related to the group
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
%head
|
||||
%meta{charset: 'utf-8'}/
|
||||
%meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/
|
||||
|
||||
%meta{property: "og:title", content: content_for?(:title) ? yield(:title) : t(:title)}
|
||||
%meta{property: "og:description", content: content_for?(:description) ? yield(:description) : t(:description)}
|
||||
%meta{property: "og:image", content: content_for?(:image) ? yield(:image) : ContentConfig.logo.url}
|
||||
%title= content_for?(:title) ? "#{yield(:title)} - #{t(:title)}".html_safe : "#{t(:welcome_to)} #{t(:title)}"
|
||||
- if Rails.env.production?
|
||||
= favicon_link_tag
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
= render :partial => 'spree/admin/shared/order_sub_menu'
|
||||
|
||||
%div{ ng: { app: 'admin.lineItems', controller: 'LineItemsCtrl' } }
|
||||
%save-bar{ save: "submit()", saving: 'saving', dirty: "bulk_order_form.$dirty" }
|
||||
%save-bar{ save: "submit()", form: "bulk_order_form" }
|
||||
.filters{ :class => "sixteen columns alpha" }
|
||||
.date_filter{ :class => "two columns alpha" }
|
||||
%label{ :for => 'start_date_filter' }Start Date
|
||||
@@ -32,8 +32,10 @@
|
||||
%label{ :for => 'clear_all_filters' }
|
||||
%br
|
||||
%input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear All", 'ng-click' => "resetSelectFilters()" }
|
||||
%hr{ :class => "sixteen columns alpha", 'ng-show' => 'unitsVariantSelected()' }
|
||||
%div#group_buy_calculation{ :class => "sixteen columns alpha", 'ng-show' => 'unitsVariantSelected()' }
|
||||
|
||||
%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'unitsVariantSelected()' } }
|
||||
|
||||
%div.sixteen.columns.alpha.omega#group_buy_calculation{ ng: { show: 'unitsVariantSelected()' } }
|
||||
%div.shared_resource{ :class => "four columns alpha" }
|
||||
%span{ :class => 'three columns alpha' }
|
||||
%input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'}
|
||||
@@ -70,32 +72,23 @@
|
||||
%div{ :class => "eight columns alpha", 'ng-hide' => 'allFinalWeightVolumesPresent()' }
|
||||
%span{ :class => "eight columns alpha", style: 'color:red' }
|
||||
WARNING: Some variants do not have a unit value
|
||||
%hr{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px" }
|
||||
%div{ 'ng-hide' => 'RequestMonitor.loading || lineItems.length == 0' }
|
||||
.controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" }
|
||||
%div{ :class => "three columns alpha" }
|
||||
%input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
|
||||
%div{ :class => "three columns" }
|
||||
%div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true }
|
||||
%span{ :class => 'icon-check' } Actions
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'ofn-close-on-click' => true }
|
||||
%span{ :class => 'three columns omega' } {{action.name }}
|
||||
%div{ :class => "seven columns" }
|
||||
%div{ :class => "three columns omega" }
|
||||
%div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' }
|
||||
%span{ :class => 'icon-reorder' } Columns
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true }
|
||||
%span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }}
|
||||
%span{ :class => 'two columns omega' } {{column.name }}
|
||||
|
||||
%hr.divider.sixteen.columns.alpha.omega
|
||||
|
||||
.controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || lineItems.length == 0' } }
|
||||
%div.three.columns.alpha
|
||||
%input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
|
||||
= render 'admin/shared/bulk_actions_dropdown'
|
||||
%div.seven.columns
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
|
||||
%div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' }
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
%h1 LOADING ORDERS
|
||||
|
||||
%div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'}
|
||||
%h1#no_results No orders found.
|
||||
|
||||
%div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' }
|
||||
%form{ name: 'bulk_order_form' }
|
||||
%table.index#listing_orders.bulk{ :class => "sixteen columns alpha" }
|
||||
@@ -129,6 +122,7 @@
|
||||
%th.actions
|
||||
Ask?
|
||||
%input{ :type => 'checkbox', 'ng-model' => "confirmDelete" }
|
||||
|
||||
%tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" }
|
||||
%td.bulk
|
||||
%input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true }
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
%div{ ng: { app: 'ofn.admin', controller: 'AdminProductEditCtrl', init: 'initialise()' } }
|
||||
|
||||
= render 'spree/admin/products/bulk_edit/filters'
|
||||
%hr.sixteen.columns.alpha
|
||||
%hr.divider.sixteen.columns.alpha.omega
|
||||
= render 'spree/admin/products/bulk_edit/actions'
|
||||
= render 'spree/admin/products/bulk_edit/indicators'
|
||||
= render 'spree/admin/products/bulk_edit/products'
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" }
|
||||
%div.four.columns.alpha
|
||||
.controls.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0' }
|
||||
.four.columns.alpha
|
||||
%input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'}
|
||||
%div.nine.columns
|
||||
.nine.columns
|
||||
= render 'spree/admin/shared/status_message'
|
||||
%div.three.columns.omega
|
||||
%div.ofn_drop_down.three.columns.omega{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' }
|
||||
%span{ :class => 'icon-reorder' } Columns
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true }
|
||||
%span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }}
|
||||
%span{ :class => 'two columns omega' } {{column.name }}
|
||||
= render 'admin/shared/columns_dropdown'
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
%div.sixteen.columns.alpha
|
||||
%div.quick_search{ :class => "four columns alpha" }
|
||||
.filters.sixteen.columns.alpha.omega
|
||||
.quick_search.four.columns.alpha
|
||||
%label{ :for => 'quick_filter' }
|
||||
%br
|
||||
%input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' }
|
||||
.filter_select{ :class => "four columns" }
|
||||
%input.quick-search.fullwidth{ 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' }
|
||||
.filter_select.four.columns
|
||||
%label{ :for => 'producer_filter' }Producer
|
||||
%br
|
||||
%select{ :class => "four columns alpha", :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' }
|
||||
.filter_select{ :class => "four columns" }
|
||||
%select.fullwidth{ :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' }
|
||||
.filter_select.four.columns
|
||||
%label{ :for => 'category_filter' }Category
|
||||
%br
|
||||
%select{ :class => "four columns alpha", :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'}
|
||||
%select.fullwidth{ :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'}
|
||||
%div{ :class => "one column" }
|
||||
.filter_clear{ :class => "three columns omega" }
|
||||
.filter_clear.three.columns.omega
|
||||
%label{ :for => 'clear_all_filters' }
|
||||
%br
|
||||
%input.fullwidth.red{ :type => 'button', :id => 'clear_all_filters', :value => "Clear Filters", 'ng-click' => "resetSelectFilters()" }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
%a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last" }
|
||||
%td{ 'ng-show' => 'columns.producer.visible' }
|
||||
%td{ 'ng-show' => 'columns.sku.visible' }
|
||||
%input{ 'ng-model' => "variant.sku", :name => 'variant_sku', 'ofn-track-variant' => 'sku', :type => 'text' }
|
||||
%td{ 'ng-show' => 'columns.name.visible' }
|
||||
%input{ 'ng-model' => 'variant.display_name', :name => 'variant_display_name', 'ofn-track-variant' => 'display_name', :type => 'text', placeholder: "{{ product.name }}" }
|
||||
%td.unit_value{ 'ng-show' => 'columns.unit.visible' }
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
%tr.total
|
||||
%td.text-right{colspan: "3"}
|
||||
%h5
|
||||
= t :order_produce
|
||||
= t :order_total_price
|
||||
%td.text-right.total
|
||||
%h5#order_total= order.display_total.to_html
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ en:
|
||||
home: "OFN"
|
||||
title: Open Food Network
|
||||
welcome_to: 'Welcome to '
|
||||
description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can…"
|
||||
search_by_name: Search by name or suburb...
|
||||
producers: Aussie Producers
|
||||
producers_join: Australian producers are now welcome to join the Open Food Network.
|
||||
@@ -52,6 +53,8 @@ en:
|
||||
free: "free"
|
||||
plus_tax: "plus GST"
|
||||
total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)"
|
||||
say_no: "No"
|
||||
say_yes: "Yes"
|
||||
|
||||
sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By"
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ Openfoodnetwork::Application.routes.draw do
|
||||
|
||||
resources :variant_overrides do
|
||||
post :bulk_update, on: :collection
|
||||
post :bulk_reset, on: :collection
|
||||
end
|
||||
|
||||
resources :customers, only: [:index, :update]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddDefaultStockToVariantOverrides < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :variant_overrides, :default_stock, :integer
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddEnableResetToVariantOverrides < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :variant_overrides, :enable_reset, :boolean
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
class AddOnDemandAndSkuToVariantOverrides < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :variant_overrides, :sku, :string, :default => nil, :after => :hub_id
|
||||
add_column :variant_overrides, :on_demand, :boolean, :default => nil, :after => :count_on_hand
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
class RenameEnableResetToResettable < ActiveRecord::Migration
|
||||
rename_column :variant_overrides, :enable_reset, :resettable
|
||||
end
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20151125051510) do
|
||||
ActiveRecord::Schema.define(:version => 20151128185900) do
|
||||
|
||||
create_table "account_invoices", :force => true do |t|
|
||||
t.integer "user_id", :null => false
|
||||
@@ -1159,6 +1159,10 @@ ActiveRecord::Schema.define(:version => 20151125051510) do
|
||||
t.integer "hub_id", :null => false
|
||||
t.decimal "price", :precision => 8, :scale => 2
|
||||
t.integer "count_on_hand"
|
||||
t.integer "default_stock"
|
||||
t.boolean "resettable"
|
||||
t.string "sku"
|
||||
t.boolean "on_demand"
|
||||
end
|
||||
|
||||
add_index "variant_overrides", ["variant_id", "hub_id"], :name => "index_variant_overrides_on_variant_id_and_hub_id"
|
||||
|
||||
@@ -26,12 +26,16 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
def on_demand
|
||||
if @variant_override.andand.count_on_hand.present?
|
||||
# If we're overriding the stock level of an on_demand variant, show it as not
|
||||
# on_demand, so our stock control can take effect.
|
||||
false
|
||||
if @variant_override.andand.on_demand.nil?
|
||||
if @variant_override.andand.count_on_hand.present?
|
||||
# If we're overriding the stock level of an on_demand variant, show it as not
|
||||
# on_demand, so our stock control can take effect.
|
||||
false
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
super
|
||||
@variant_override.andand.on_demand
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,7 +46,10 @@ module OpenFoodNetwork
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sku
|
||||
@variant_override.andand.sku || super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
135
spec/controllers/admin/variant_overrides_controller_spec.rb
Normal file
135
spec/controllers/admin/variant_overrides_controller_spec.rb
Normal file
@@ -0,0 +1,135 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Admin::VariantOverridesController, type: :controller do
|
||||
# include AuthenticationWorkflow
|
||||
|
||||
describe "bulk_update" do
|
||||
context "json" do
|
||||
let(:format) { :json }
|
||||
|
||||
let(:hub) { create(:distributor_enterprise) }
|
||||
let(:variant) { create(:variant) }
|
||||
let!(:variant_override) { create(:variant_override, hub: hub, variant: variant) }
|
||||
let(:variant_override_params) { [ { id: variant_override.id, price: 123.45, count_on_hand: 321, sku: "MySKU", on_demand: false } ] }
|
||||
|
||||
context "where I don't manage the variant override hub" do
|
||||
before do
|
||||
user = create(:user)
|
||||
user.owned_enterprises << create(:enterprise)
|
||||
allow(controller).to receive(:spree_current_user) { user }
|
||||
end
|
||||
|
||||
it "redirects to unauthorized" do
|
||||
spree_put :bulk_update, format: format, variant_overrides: variant_override_params
|
||||
expect(response).to redirect_to spree.unauthorized_path
|
||||
end
|
||||
end
|
||||
|
||||
context "where I manage the variant override hub" do
|
||||
before do
|
||||
allow(controller).to receive(:spree_current_user) { hub.owner }
|
||||
end
|
||||
|
||||
context "but the producer has not granted VO permission" do
|
||||
it "redirects to unauthorized" do
|
||||
spree_put :bulk_update, format: format, variant_overrides: variant_override_params
|
||||
expect(response).to redirect_to spree.unauthorized_path
|
||||
end
|
||||
end
|
||||
|
||||
context "and the producer has granted VO permission" do
|
||||
before do
|
||||
create(:enterprise_relationship, parent: variant.product.supplier, child: hub, permissions_list: [:create_variant_overrides])
|
||||
end
|
||||
|
||||
it "allows me to update the variant override" do
|
||||
spree_put :bulk_update, format: format, variant_overrides: variant_override_params
|
||||
variant_override.reload
|
||||
expect(variant_override.price).to eq 123.45
|
||||
expect(variant_override.count_on_hand).to eq 321
|
||||
expect(variant_override.sku).to eq "MySKU"
|
||||
expect(variant_override.on_demand).to eq false
|
||||
end
|
||||
|
||||
context "where params for a variant override are blank" do
|
||||
let(:variant_override_params) { [ { id: variant_override.id, price: "", count_on_hand: "", default_stock: nil, resettable: nil, sku: nil, on_demand: nil } ] }
|
||||
|
||||
it "destroys the variant override" do
|
||||
spree_put :bulk_update, format: format, variant_overrides: variant_override_params
|
||||
expect(VariantOverride.find_by_id(variant_override.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "bulk_reset" do
|
||||
context "json" do
|
||||
let(:format) { :json }
|
||||
|
||||
let(:hub) { create(:distributor_enterprise) }
|
||||
let(:producer) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:product, supplier: producer) }
|
||||
let(:variant1) { create(:variant, product: product) }
|
||||
let(:variant2) { create(:variant, product: product) }
|
||||
let!(:variant_override1) { create(:variant_override, hub: hub, variant: variant1, count_on_hand: 5, default_stock: 7, resettable: true) }
|
||||
let!(:variant_override2) { create(:variant_override, hub: hub, variant: variant2, count_on_hand: 2, default_stock: 1, resettable: false) }
|
||||
|
||||
let(:params) { { format: format, hub_id: hub.id } }
|
||||
|
||||
context "where I don't manage the variant override hub" do
|
||||
before do
|
||||
user = create(:user)
|
||||
user.owned_enterprises << create(:enterprise)
|
||||
allow(controller).to receive(:spree_current_user) { user }
|
||||
end
|
||||
|
||||
it "redirects to unauthorized" do
|
||||
spree_put :bulk_reset, params
|
||||
expect(response).to redirect_to spree.unauthorized_path
|
||||
end
|
||||
end
|
||||
|
||||
context "where I manage the variant override hub" do
|
||||
before do
|
||||
allow(controller).to receive(:spree_current_user) { hub.owner }
|
||||
end
|
||||
|
||||
context "where the producer has not granted create_variant_overrides permission to the hub" do
|
||||
it "restricts access" do
|
||||
spree_put :bulk_reset, params
|
||||
expect(response).to redirect_to spree.unauthorized_path
|
||||
end
|
||||
end
|
||||
|
||||
context "where the producer has granted create_variant_overrides permission to the hub" do
|
||||
let!(:er1) { create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:create_variant_overrides]) }
|
||||
|
||||
it "updates stock to default values where reset is enabled" do
|
||||
expect(variant_override1.reload.count_on_hand).to eq 5 # reset enabled
|
||||
expect(variant_override2.reload.count_on_hand).to eq 2 # reset disabled
|
||||
spree_put :bulk_reset, params
|
||||
expect(variant_override1.reload.count_on_hand).to eq 7 # reset enabled
|
||||
expect(variant_override2.reload.count_on_hand).to eq 2 # reset disabled
|
||||
end
|
||||
|
||||
context "and the producer has granted create_variant_overrides permission to another hub I manage" do
|
||||
before { hub.owner.update_attribute(:enterprise_limit, 2) }
|
||||
let(:hub2) { create(:distributor_enterprise, owner: hub.owner) }
|
||||
let(:product) { create(:product, supplier: producer) }
|
||||
let(:variant3) { create(:variant, product: product) }
|
||||
let!(:variant_override3) { create(:variant_override, hub: hub2, variant: variant3, count_on_hand: 1, default_stock: 13, resettable: true) }
|
||||
let!(:er2) { create(:enterprise_relationship, parent: producer, child: hub2, permissions_list: [:create_variant_overrides]) }
|
||||
|
||||
it "does not reset count_on_hand for variant_overrides not in params" do
|
||||
expect {
|
||||
spree_put :bulk_reset, params
|
||||
}.to_not change{variant_override3.reload.count_on_hand}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'spec_helper'
|
||||
|
||||
module Api
|
||||
describe EnterprisesController do
|
||||
describe EnterprisesController, :type => :controller do
|
||||
include AuthenticationWorkflow
|
||||
render_views
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ require 'spec_helper'
|
||||
require 'spree/api/testing_support/helpers'
|
||||
|
||||
module Api
|
||||
describe OrderCyclesController do
|
||||
describe OrderCyclesController, :type => :controller do
|
||||
include Spree::Api::TestingSupport::Helpers
|
||||
include AuthenticationWorkflow
|
||||
render_views
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe BaseController do
|
||||
describe BaseController, :type => :controller do
|
||||
let(:oc) { mock_model(OrderCycle) }
|
||||
let(:hub) { mock_model(Enterprise, ready_for_checkout?: true) }
|
||||
let(:order) { mock_model(Spree::Order, distributor: hub) }
|
||||
|
||||
@@ -94,6 +94,8 @@ FactoryGirl.define do
|
||||
factory :variant_override, :class => VariantOverride do
|
||||
price 77.77
|
||||
count_on_hand 11111
|
||||
default_stock 2000
|
||||
resettable false
|
||||
end
|
||||
|
||||
factory :enterprise, :class => Enterprise do
|
||||
|
||||
@@ -119,10 +119,9 @@ feature %q{
|
||||
expect(page).to_not have_selector "#save-bar"
|
||||
fill_in "quantity", :with => 2
|
||||
expect(page).to have_selector "input[name='quantity'].ng-dirty"
|
||||
expect(page).to have_selector "#save-bar"
|
||||
expect(page).to have_button "Save Changes"
|
||||
expect(page).to have_selector "#save-bar", text: "You have unsaved changes"
|
||||
click_button "Save Changes"
|
||||
expect(page).to_not have_selector "#save-bar"
|
||||
expect(page).to have_selector "#save-bar", text: "All changes saved"
|
||||
expect(page).to_not have_selector "input[name='quantity'].ng-dirty"
|
||||
end
|
||||
end
|
||||
@@ -132,10 +131,9 @@ feature %q{
|
||||
expect(page).to_not have_selector "#save-bar"
|
||||
fill_in "quantity", :with => li1.variant.on_hand + li1.quantity + 10
|
||||
expect(page).to have_selector "input[name='quantity'].ng-dirty"
|
||||
expect(page).to have_selector "#save-bar"
|
||||
expect(page).to have_button "Save Changes"
|
||||
expect(page).to have_selector "#save-bar", text: "You have unsaved changes"
|
||||
click_button "Save Changes"
|
||||
expect(page).to have_selector "#save-bar"
|
||||
expect(page).to have_selector "#save-bar", text: "Fields with red borders contain errors."
|
||||
expect(page).to have_selector "input[name='quantity'].ng-dirty.update-error"
|
||||
expect(page).to have_content "exceeds available stock. Please ensure line items have a valid quantity."
|
||||
end
|
||||
@@ -158,9 +156,9 @@ feature %q{
|
||||
context "modifying the weight/volume of a line item" do
|
||||
it "price is altered" do
|
||||
visit '/admin/orders/bulk_management'
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Weight/Volume").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Price").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Price").click
|
||||
within "tr#li_#{li1.id}" do
|
||||
expect(page).to have_field "price", with: "$50.00"
|
||||
fill_in "final_weight_volume", :with => 2000
|
||||
@@ -177,8 +175,8 @@ feature %q{
|
||||
context "modifying the quantity of a line item" do
|
||||
it "price is altered" do
|
||||
visit '/admin/orders/bulk_management'
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Price").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Price").click
|
||||
within "tr#li_#{li1.id}" do
|
||||
expect(page).to have_field "price", with: "$#{format("%.2f",li1.price * 5)}"
|
||||
fill_in "quantity", :with => 6
|
||||
@@ -190,8 +188,8 @@ feature %q{
|
||||
context "modifying the quantity of a line item" do
|
||||
it "weight/volume is altered" do
|
||||
visit '/admin/orders/bulk_management'
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Weight/Volume").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click
|
||||
within "tr#li_#{li1.id}" do
|
||||
expect(page).to have_field "final_weight_volume", with: "#{li1.final_weight_volume.round}"
|
||||
fill_in "quantity", :with => 6
|
||||
@@ -211,8 +209,8 @@ feature %q{
|
||||
expect(page).to have_selector "th", :text => "QUANTITY"
|
||||
expect(page).to have_selector "th", :text => "MAX"
|
||||
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Producer").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Producer").click
|
||||
|
||||
expect(page).to_not have_selector "th", :text => "PRODUCER"
|
||||
expect(page).to have_selector "th", :text => "NAME"
|
||||
@@ -501,8 +499,8 @@ feature %q{
|
||||
|
||||
it "displays a bulk action select box with a list of actions" do
|
||||
list_of_actions = ['Delete Selected']
|
||||
find("div#bulk_actions_dropdown").click
|
||||
within("div#bulk_actions_dropdown") do
|
||||
find("div#bulk-actions-dropdown").click
|
||||
within("div#bulk-actions-dropdown") do
|
||||
list_of_actions.each { |action_name| expect(page).to have_selector "div.menu_item", text: action_name }
|
||||
end
|
||||
end
|
||||
@@ -514,8 +512,8 @@ feature %q{
|
||||
within("tr#li_#{li2.id} td.bulk") do
|
||||
check "bulk"
|
||||
end
|
||||
find("div#bulk_actions_dropdown").click
|
||||
find("div#bulk_actions_dropdown div.menu_item", :text => "Delete Selected" ).click
|
||||
find("div#bulk-actions-dropdown").click
|
||||
find("div#bulk-actions-dropdown div.menu_item", :text => "Delete Selected" ).click
|
||||
expect(page).to have_selector "tr#li_#{li1.id}", visible: true
|
||||
expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true
|
||||
end
|
||||
@@ -534,8 +532,8 @@ feature %q{
|
||||
it "only applies the delete action to filteredLineItems" do
|
||||
check "toggle_bulk"
|
||||
fill_in "quick_search", with: o1.number
|
||||
find("div#bulk_actions_dropdown").click
|
||||
find("div#bulk_actions_dropdown div.menu_item", :text => "Delete Selected" ).click
|
||||
find("div#bulk-actions-dropdown").click
|
||||
find("div#bulk-actions-dropdown div.menu_item", :text => "Delete Selected" ).click
|
||||
fill_in "quick_search", with: ''
|
||||
expect(page).to_not have_selector "tr#li_#{li1.id}", visible: true
|
||||
expect(page).to have_selector "tr#li_#{li2.id}", visible: true
|
||||
|
||||
@@ -46,8 +46,8 @@ feature %q{
|
||||
p2 = FactoryGirl.create(:product, available_on: Date.current-1)
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click
|
||||
|
||||
expect(page).to have_field "available_on", with: p1.available_on.strftime("%F %T")
|
||||
expect(page).to have_field "available_on", with: p2.available_on.strftime("%F %T")
|
||||
@@ -243,11 +243,11 @@ feature %q{
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Category").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Inherits Properties?").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "SKU").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Category").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Inherits Properties?").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click
|
||||
|
||||
within "tr#p_#{p.id}" do
|
||||
expect(page).to have_field "product_name", with: p.name
|
||||
@@ -308,6 +308,7 @@ feature %q{
|
||||
p = FactoryGirl.create(:product, supplier: s1, available_on: Date.current, variant_unit: 'volume', variant_unit_scale: 0.001,
|
||||
price: 3.0, on_hand: 9, unit_value: 0.25, unit_description: '(bottle)' )
|
||||
v = p.variants.first
|
||||
v.update_column(:sku, "VARIANTSKU")
|
||||
|
||||
login_to_admin_section
|
||||
|
||||
@@ -315,12 +316,18 @@ feature %q{
|
||||
expect(page).to have_selector "a.view-variants"
|
||||
first("a.view-variants").trigger('click')
|
||||
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
|
||||
expect(page).to have_field "variant_sku", with: "VARIANTSKU"
|
||||
expect(page).to have_field "variant_price", with: "3.0"
|
||||
expect(page).to have_field "variant_unit_value_with_description", with: "250 (bottle)"
|
||||
expect(page).to have_field "variant_on_hand", with: "9"
|
||||
expect(page).to have_selector "span[name='on_hand']", "9"
|
||||
|
||||
select "Volume (L)", from: "variant_unit_with_scale"
|
||||
fill_in "variant_sku", with: "NEWSKU"
|
||||
fill_in "variant_price", with: "4.0"
|
||||
fill_in "variant_on_hand", with: "10"
|
||||
fill_in "variant_unit_value_with_description", with: "2 (8x250 mL bottles)"
|
||||
@@ -331,6 +338,7 @@ feature %q{
|
||||
expect(page.find("#status-message")).to have_content "Changes saved."
|
||||
|
||||
v.reload
|
||||
expect(v.sku).to eq "NEWSKU"
|
||||
expect(v.price).to eq 4.0
|
||||
expect(v.on_hand).to eq 10
|
||||
expect(v.unit_value).to eq 2 # 2L in L
|
||||
@@ -556,8 +564,8 @@ feature %q{
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click
|
||||
|
||||
expect(page).to have_selector "th", :text => "NAME"
|
||||
expect(page).to have_selector "th", :text => "PRODUCER"
|
||||
@@ -565,7 +573,7 @@ feature %q{
|
||||
expect(page).to have_selector "th", :text => "ON HAND"
|
||||
expect(page).to have_selector "th", :text => "AV. ON"
|
||||
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: /^.{0,1}Producer$/).click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: /^.{0,1}Producer$/).click
|
||||
|
||||
expect(page).to have_no_selector "th", :text => "PRODUCER"
|
||||
expect(page).to have_selector "th", :text => "NAME"
|
||||
@@ -688,8 +696,8 @@ feature %q{
|
||||
v = p.variants.first
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click
|
||||
|
||||
within "tr#p_#{p.id}" do
|
||||
expect(page).to have_field "product_name", with: p.name
|
||||
|
||||
@@ -39,8 +39,8 @@ feature 'Customers' do
|
||||
# Toggling columns
|
||||
expect(page).to have_selector "th.email"
|
||||
expect(page).to have_content customer1.email
|
||||
first("div#columns_dropdown", :text => "COLUMNS").click
|
||||
first("div#columns_dropdown div.menu div.menu_item", text: "Email").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Email").click
|
||||
expect(page).to_not have_selector "th.email"
|
||||
expect(page).to_not have_content customer1.email
|
||||
end
|
||||
|
||||
@@ -192,7 +192,7 @@ feature %q{
|
||||
order = create(:completed_order_with_totals, distributor: distributor1)
|
||||
visit spree.admin_order_path(order)
|
||||
|
||||
find("#links-dropdown .ofn_drop_down").click
|
||||
find("#links-dropdown .ofn-drop-down").click
|
||||
within "#links-dropdown" do
|
||||
expect(page).to have_link "Edit", href: spree.edit_admin_order_path(order)
|
||||
expect(page).to have_link "Resend Confirmation", href: spree.resend_admin_order_path(order)
|
||||
|
||||
@@ -26,14 +26,6 @@ feature %q{
|
||||
|
||||
page.should have_select2 'hub_id', options: ['', hub.name, hub2.name]
|
||||
end
|
||||
|
||||
it "displays the hub" do
|
||||
visit '/admin/variant_overrides'
|
||||
select2_select hub.name, from: 'hub_id'
|
||||
click_button 'Go'
|
||||
|
||||
page.should have_selector 'h2', text: hub.name
|
||||
end
|
||||
end
|
||||
|
||||
context "when a hub is selected" do
|
||||
@@ -59,29 +51,60 @@ feature %q{
|
||||
before do
|
||||
visit '/admin/variant_overrides'
|
||||
select2_select hub.name, from: 'hub_id'
|
||||
click_button 'Go'
|
||||
end
|
||||
|
||||
it "displays the list of products with variants" do
|
||||
page.should have_table_row ['PRODUCER', 'PRODUCT', 'PRICE', 'ON HAND']
|
||||
|
||||
page.should have_table_row [producer.name, product.name, '', '']
|
||||
page.should have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23'
|
||||
page.should have_input "variant-overrides-#{variant.id}-count-on-hand", placeholder: '12'
|
||||
page.should have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12'
|
||||
|
||||
page.should have_table_row [producer_related.name, product_related.name, '', '']
|
||||
page.should have_input "variant-overrides-#{variant_related.id}-price", placeholder: '2.34'
|
||||
page.should have_input "variant-overrides-#{variant_related.id}-count-on-hand", placeholder: '23'
|
||||
end
|
||||
page.should have_input "variant-overrides-#{variant_related.id}-count_on_hand", placeholder: '23'
|
||||
|
||||
it "filters the products to those the hub can override" do
|
||||
# filters the products to those the hub can override
|
||||
page.should_not have_content producer_unrelated.name
|
||||
page.should_not have_content product_unrelated.name
|
||||
|
||||
# Filters based on the producer select filter
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to have_selector "#v_#{variant_related.id}"
|
||||
select2_select producer.name, from: 'producer_filter'
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to_not have_selector "#v_#{variant_related.id}"
|
||||
select2_select 'All', from: 'producer_filter'
|
||||
|
||||
# Filters based on the quick search box
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to have_selector "#v_#{variant_related.id}"
|
||||
fill_in 'query', with: product.name
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to_not have_selector "#v_#{variant_related.id}"
|
||||
fill_in 'query', with: ''
|
||||
|
||||
# Clears the filters
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to have_selector "#v_#{variant_related.id}"
|
||||
select2_select producer.name, from: 'producer_filter'
|
||||
fill_in 'query', with: product_related.name
|
||||
expect(page).to_not have_selector "#v_#{variant.id}"
|
||||
expect(page).to_not have_selector "#v_#{variant_related.id}"
|
||||
click_button 'Clear All'
|
||||
expect(page).to have_selector "#v_#{variant.id}"
|
||||
expect(page).to have_selector "#v_#{variant_related.id}"
|
||||
end
|
||||
|
||||
it "creates new overrides" do
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
|
||||
fill_in "variant-overrides-#{variant.id}-sku", with: 'NEWSKU'
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '777.77'
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123'
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123'
|
||||
check "variant-overrides-#{variant.id}-on_demand"
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
expect do
|
||||
@@ -92,15 +115,17 @@ feature %q{
|
||||
vo = VariantOverride.last
|
||||
vo.variant_id.should == variant.id
|
||||
vo.hub_id.should == hub.id
|
||||
vo.sku.should == "NEWSKU"
|
||||
vo.price.should == 777.77
|
||||
vo.count_on_hand.should == 123
|
||||
vo.on_demand.should == true
|
||||
end
|
||||
|
||||
describe "creating and then updating the new override" do
|
||||
it "updates the same override instead of creating a duplicate" do
|
||||
# When I create a new override
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '777.77'
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123'
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123'
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
expect do
|
||||
@@ -110,7 +135,7 @@ feature %q{
|
||||
|
||||
# And I update its settings without reloading the page
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '111.11'
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '111'
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '111'
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
# Then I shouldn't see a new override
|
||||
@@ -130,7 +155,7 @@ feature %q{
|
||||
|
||||
it "displays an error when unauthorised to access the page" do
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '777.77'
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123'
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123'
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
user.enterprises.clear
|
||||
@@ -143,7 +168,7 @@ feature %q{
|
||||
|
||||
it "displays an error when unauthorised to update a particular override" do
|
||||
fill_in "variant-overrides-#{variant_related.id}-price", with: '777.77'
|
||||
fill_in "variant-overrides-#{variant_related.id}-count-on-hand", with: '123'
|
||||
fill_in "variant-overrides-#{variant_related.id}-count_on_hand", with: '123'
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
er2.destroy
|
||||
@@ -156,23 +181,27 @@ feature %q{
|
||||
end
|
||||
|
||||
context "with overrides" do
|
||||
let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111) }
|
||||
let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111, default_stock: 1000, resettable: true) }
|
||||
let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub3, price: 1, count_on_hand: 2) }
|
||||
let!(:product2) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) }
|
||||
let!(:variant2) { create(:variant, product: product2, unit_value: 8, price: 1.00, on_hand: 12) }
|
||||
let!(:vo_no_reset) { create(:variant_override, variant: variant2, hub: hub, price: 3.99, count_on_hand: 40, default_stock: 100, resettable: false) }
|
||||
let!(:variant3) { create(:variant, product: product, unit_value: 2, price: 5.00, on_hand: 6) }
|
||||
let!(:vo3) { create(:variant_override, variant: variant3, hub: hub, price: 6, count_on_hand: 7, sku: "SOMESKU", default_stock: 100, resettable: false) }
|
||||
|
||||
before do
|
||||
visit '/admin/variant_overrides'
|
||||
select2_select hub.name, from: 'hub_id'
|
||||
click_button 'Go'
|
||||
end
|
||||
|
||||
it "product values are affected by overrides" do
|
||||
page.should have_input "variant-overrides-#{variant.id}-price", with: '77.77', placeholder: '1.23'
|
||||
page.should have_input "variant-overrides-#{variant.id}-count-on-hand", with: '11111', placeholder: '12'
|
||||
page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '11111', placeholder: '12'
|
||||
end
|
||||
|
||||
it "updates existing overrides" do
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '22.22'
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '8888'
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '8888'
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
expect do
|
||||
@@ -187,17 +216,54 @@ feature %q{
|
||||
vo.count_on_hand.should == 8888
|
||||
end
|
||||
|
||||
# Any new fields added to the VO model need to be added to this test
|
||||
it "deletes overrides when values are cleared" do
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Reset Stock Level").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
|
||||
# Clearing values manually
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: ''
|
||||
fill_in "variant-overrides-#{variant.id}-count-on-hand", with: ''
|
||||
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: ''
|
||||
fill_in "variant-overrides-#{variant.id}-default_stock", with: ''
|
||||
page.uncheck "variant-overrides-#{variant.id}-resettable"
|
||||
page.should have_content "Changes to one override remain unsaved."
|
||||
|
||||
# Clearing values by 'inheriting'
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
first("div#columns-dropdown div.menu div.menu_item", text: "Inheritance").click
|
||||
first("div#columns-dropdown", :text => "COLUMNS").click
|
||||
page.check "variant-overrides-#{variant3.id}-inherit"
|
||||
|
||||
expect do
|
||||
click_button 'Save Changes'
|
||||
page.should have_content "Changes saved."
|
||||
end.to change(VariantOverride, :count).by(-1)
|
||||
end.to change(VariantOverride, :count).by(-2)
|
||||
|
||||
VariantOverride.where(id: vo.id).should be_empty
|
||||
VariantOverride.where(id: vo3.id).should be_empty
|
||||
end
|
||||
|
||||
it "resets stock to defaults" do
|
||||
click_button 'Reset Stock to Defaults'
|
||||
page.should have_content 'Stocks reset to defaults.'
|
||||
vo.reload
|
||||
page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '1000', placeholder: '12'
|
||||
vo.count_on_hand.should == 1000
|
||||
end
|
||||
|
||||
it "doesn't reset stock levels if the behaviour is disabled" do
|
||||
click_button 'Reset Stock to Defaults'
|
||||
vo_no_reset.reload
|
||||
page.should have_input "variant-overrides-#{variant2.id}-count_on_hand", with: '40', placeholder: '12'
|
||||
vo_no_reset.count_on_hand.should == 40
|
||||
end
|
||||
|
||||
it "prompts to save changes before reset if any are pending" do
|
||||
fill_in "variant-overrides-#{variant.id}-price", with: '200'
|
||||
click_button 'Reset Stock to Defaults'
|
||||
page.should have_content "Save changes first"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -187,7 +187,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
visit shop_path
|
||||
end
|
||||
|
||||
it "should save group buy data to the cart" do
|
||||
it "should save group buy data to the cart and display it on shopfront reload" do
|
||||
# -- Quantity
|
||||
fill_in "variants[#{variant.id}]", with: 6
|
||||
page.should have_in_cart product.name
|
||||
@@ -202,6 +202,11 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.max_quantity.should == 7
|
||||
|
||||
# -- Reload
|
||||
visit shop_path
|
||||
page.should have_field "variants[#{variant.id}]", with: 6
|
||||
page.should have_field "variant_attributes[#{variant.id}][max_quantity]", with: 7
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,12 +22,12 @@ feature "shopping with variant overrides defined", js: true do
|
||||
let(:v4) { create(:variant, product: p1, price: 44.44, unit_value: 4) }
|
||||
let(:v5) { create(:variant, product: p3, price: 55.55, unit_value: 5, on_demand: true) }
|
||||
let(:v6) { create(:variant, product: p3, price: 66.66, unit_value: 6, on_demand: true) }
|
||||
let!(:vo1) { create(:variant_override, hub: hub, variant: v1, price: 55.55, count_on_hand: nil) }
|
||||
let!(:vo2) { create(:variant_override, hub: hub, variant: v2, count_on_hand: 0) }
|
||||
let!(:vo3) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0) }
|
||||
let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3) }
|
||||
let!(:vo5) { create(:variant_override, hub: hub, variant: v5, count_on_hand: 0) }
|
||||
let!(:vo6) { create(:variant_override, hub: hub, variant: v6, count_on_hand: 6) }
|
||||
let!(:vo1) { create(:variant_override, hub: hub, variant: v1, price: 55.55, count_on_hand: nil, default_stock: nil, resettable: false) }
|
||||
let!(:vo2) { create(:variant_override, hub: hub, variant: v2, count_on_hand: 0, default_stock: nil, resettable: false) }
|
||||
let!(:vo3) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0, default_stock: nil, resettable: false) }
|
||||
let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3, default_stock: nil, resettable: false) }
|
||||
let!(:vo5) { create(:variant_override, hub: hub, variant: v5, count_on_hand: 0, default_stock: nil, resettable: false) }
|
||||
let!(:vo6) { create(:variant_override, hub: hub, variant: v6, count_on_hand: 6, default_stock: nil, resettable: false) }
|
||||
let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)) }
|
||||
|
||||
before do
|
||||
@@ -143,7 +143,6 @@ feature "shopping with variant overrides defined", js: true do
|
||||
it "does not subtract stock from overrides that do not override count_on_hand" do
|
||||
fill_in "variants[#{v1.id}]", with: "2"
|
||||
click_checkout
|
||||
|
||||
expect do
|
||||
complete_checkout
|
||||
end.to change { v1.reload.count_on_hand }.by(-2)
|
||||
@@ -192,7 +191,7 @@ feature "shopping with variant overrides defined", js: true do
|
||||
within "#payment" do
|
||||
choose pm.name
|
||||
end
|
||||
|
||||
|
||||
place_order
|
||||
page.should have_content "Your order has been processed successfully"
|
||||
end
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
describe "VariantOverridesCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
scope = {}
|
||||
hubs = [{id: 1, name: 'Hub'}]
|
||||
producers = [{id: 2, name: 'Producer'}]
|
||||
products = [{id: 1, name: 'Product'}]
|
||||
hubPermissions = {}
|
||||
VariantOverrides = null
|
||||
variantOverrides = {}
|
||||
DirtyVariantOverrides = null
|
||||
dirtyVariantOverrides = {}
|
||||
StatusMessage = null
|
||||
statusMessage = {}
|
||||
|
||||
beforeEach ->
|
||||
module 'ofn.admin'
|
||||
module 'admin.variantOverrides'
|
||||
module ($provide) ->
|
||||
$provide.value 'SpreeApiKey', 'API_KEY'
|
||||
$provide.value 'variantOverrides', variantOverrides
|
||||
$provide.value 'dirtyVariantOverrides', dirtyVariantOverrides
|
||||
null
|
||||
scope = {}
|
||||
|
||||
inject ($controller, Indexer, _VariantOverrides_) ->
|
||||
inject ($controller, _VariantOverrides_, _DirtyVariantOverrides_, _StatusMessage_) ->
|
||||
VariantOverrides = _VariantOverrides_
|
||||
ctrl = $controller 'AdminVariantOverridesCtrl', {$scope: scope, Indexer: Indexer, hubs: hubs, producers: producers, products: products, hubPermissions: hubPermissions, VariantOverrides: _VariantOverrides_}
|
||||
DirtyVariantOverrides = _DirtyVariantOverrides_
|
||||
StatusMessage = _StatusMessage_
|
||||
ctrl = $controller 'AdminVariantOverridesCtrl', { $scope: scope, hubs: hubs, producers: producers, products: products, hubPermissions: hubPermissions, VariantOverrides: VariantOverrides, DirtyVariantOverrides: DirtyVariantOverrides, StatusMessage: StatusMessage}
|
||||
|
||||
it "initialises the hub list and the chosen hub", ->
|
||||
expect(scope.hubs).toEqual hubs
|
||||
expect(scope.hubs).toEqual { 1: {id: 1, name: 'Hub'} }
|
||||
expect(scope.hub).toBeNull()
|
||||
|
||||
it "initialises select filters", ->
|
||||
expect(scope.producerFilter).toEqual 0
|
||||
expect(scope.query).toEqual ''
|
||||
|
||||
it "adds products", ->
|
||||
spyOn(VariantOverrides, "ensureDataFor")
|
||||
expect(scope.products).toEqual []
|
||||
@@ -54,4 +64,23 @@ describe "VariantOverridesCtrl", ->
|
||||
expect(scope.updateError(data, 400)).toEqual "I had some trouble saving: Hub can't be blank, Variant can't be blank"
|
||||
|
||||
it "returns a generic message otherwise", ->
|
||||
expect(scope.updateError({}, 500)).toEqual "Oh no! I was unable to save your changes."
|
||||
expect(scope.updateError({}, 500)).toEqual "Oh no! I was unable to save your changes."
|
||||
|
||||
describe "setting stock to defaults", ->
|
||||
it "prompts to save changes if there are any pending", ->
|
||||
spyOn(StatusMessage, "display")
|
||||
DirtyVariantOverrides.add {hub_id: 1, variant_id: 1}
|
||||
scope.resetStock()
|
||||
expect(StatusMessage.display).toHaveBeenCalledWith 'alert', 'Save changes first.'
|
||||
|
||||
it "updates and refreshes on hand value for variant overrides with a default stock level", inject ($httpBackend) ->
|
||||
scope.hub_id = 123
|
||||
variant_overrides_mock = "mock object"
|
||||
spyOn(StatusMessage, "display")
|
||||
spyOn(VariantOverrides, "updateData")
|
||||
$httpBackend.expectPOST("/admin/variant_overrides/bulk_reset", hub_id: 123).respond 200, variant_overrides_mock
|
||||
scope.resetStock()
|
||||
expect(StatusMessage.display).toHaveBeenCalledWith 'progress', 'Changing on hand stock levels...'
|
||||
$httpBackend.flush()
|
||||
expect(VariantOverrides.updateData).toHaveBeenCalledWith variant_overrides_mock
|
||||
expect(StatusMessage.display).toHaveBeenCalledWith 'success', 'Stocks reset to defaults.'
|
||||
|
||||
@@ -7,7 +7,7 @@ describe "maintaining a list of dirty variant overrides", ->
|
||||
count_on_hand: 4
|
||||
|
||||
beforeEach ->
|
||||
module "ofn.admin"
|
||||
module "admin.variantOverrides"
|
||||
|
||||
beforeEach inject (_DirtyVariantOverrides_) ->
|
||||
DirtyVariantOverrides = _DirtyVariantOverrides_
|
||||
|
||||
@@ -2,7 +2,7 @@ describe "indexer", ->
|
||||
Indexer = null
|
||||
|
||||
beforeEach ->
|
||||
module "ofn.admin"
|
||||
module "admin.indexUtils"
|
||||
|
||||
beforeEach inject (_Indexer_) ->
|
||||
Indexer = _Indexer_
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
describe "VariantOverrides service", ->
|
||||
VariantOverrides = null
|
||||
VariantOverrides = $httpBackend = null
|
||||
variantOverrides = [
|
||||
{id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1}
|
||||
{id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2}
|
||||
{id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3}
|
||||
{id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false }
|
||||
{id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false}
|
||||
{id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false}
|
||||
]
|
||||
|
||||
beforeEach ->
|
||||
module "ofn.admin"
|
||||
module "admin.variantOverrides"
|
||||
module ($provide) ->
|
||||
$provide.value "variantOverrides", variantOverrides
|
||||
null
|
||||
|
||||
beforeEach inject (_VariantOverrides_) ->
|
||||
beforeEach inject (_VariantOverrides_, _$httpBackend_) ->
|
||||
VariantOverrides = _VariantOverrides_
|
||||
$httpBackend = _$httpBackend_
|
||||
|
||||
it "indexes variant overrides by hub_id -> variant_id", ->
|
||||
expect(VariantOverrides.variantOverrides).toEqual
|
||||
10:
|
||||
100: {id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1}
|
||||
200: {id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2}
|
||||
100: {id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false }
|
||||
200: {id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false }
|
||||
20:
|
||||
300: {id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3}
|
||||
300: {id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false }
|
||||
|
||||
it "ensures blank data available for some products", ->
|
||||
hubs = [{id: 10}, {id: 20}, {id: 30}]
|
||||
@@ -32,37 +33,50 @@ describe "VariantOverrides service", ->
|
||||
}
|
||||
]
|
||||
VariantOverrides.ensureDataFor hubs, products
|
||||
expect(VariantOverrides.variantOverrides).toEqual
|
||||
10:
|
||||
100: {id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1}
|
||||
200: {id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2}
|
||||
300: { hub_id: 10, variant_id: 300, price: '', count_on_hand: ''}
|
||||
400: { hub_id: 10, variant_id: 400, price: '', count_on_hand: ''}
|
||||
500: { hub_id: 10, variant_id: 500, price: '', count_on_hand: ''}
|
||||
20:
|
||||
100: { hub_id: 20, variant_id: 100, price: '', count_on_hand: ''}
|
||||
200: { hub_id: 20, variant_id: 200, price: '', count_on_hand: ''}
|
||||
300: {id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3}
|
||||
400: { hub_id: 20, variant_id: 400, price: '', count_on_hand: ''}
|
||||
500: { hub_id: 20, variant_id: 500, price: '', count_on_hand: ''}
|
||||
30:
|
||||
100: { hub_id: 30, variant_id: 100, price: '', count_on_hand: ''}
|
||||
200: { hub_id: 30, variant_id: 200, price: '', count_on_hand: ''}
|
||||
300: { hub_id: 30, variant_id: 300, price: '', count_on_hand: ''}
|
||||
400: { hub_id: 30, variant_id: 400, price: '', count_on_hand: ''}
|
||||
500: { hub_id: 30, variant_id: 500, price: '', count_on_hand: ''}
|
||||
expect(VariantOverrides.variantOverrides[10]).toEqual
|
||||
100: { id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false }
|
||||
200: { id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false }
|
||||
300: { hub_id: 10, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
400: { hub_id: 10, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
500: { hub_id: 10, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
expect(VariantOverrides.variantOverrides[20]).toEqual
|
||||
100: { hub_id: 20, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
200: { hub_id: 20, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
300: { id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false }
|
||||
400: { hub_id: 20, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
500: { hub_id: 20, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
expect(VariantOverrides.variantOverrides[30]).toEqual
|
||||
100: { hub_id: 30, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
200: { hub_id: 30, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
300: { hub_id: 30, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
400: { hub_id: 30, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
500: { hub_id: 30, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false }
|
||||
|
||||
it "updates the IDs of variant overrides", ->
|
||||
VariantOverrides.variantOverrides[2] = {}
|
||||
VariantOverrides.variantOverrides[2][3] = {hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5}
|
||||
VariantOverrides.variantOverrides[2][8] = {hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10}
|
||||
VariantOverrides.variantOverrides[2][3] = {hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: '', resettable: false}
|
||||
VariantOverrides.variantOverrides[2][8] = {hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false}
|
||||
|
||||
updatedVos = [
|
||||
{id: 1, hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5}
|
||||
{id: 6, hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10}
|
||||
{id: 1, hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: '', resettable: false}
|
||||
{id: 6, hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false}
|
||||
]
|
||||
|
||||
VariantOverrides.updateIds updatedVos
|
||||
|
||||
expect(VariantOverrides.variantOverrides[2][3].id).toEqual 1
|
||||
expect(VariantOverrides.variantOverrides[2][8].id).toEqual 6
|
||||
|
||||
it "updates the variant overrides on the page with new data", ->
|
||||
VariantOverrides.variantOverrides[1] =
|
||||
3: {id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: 3, resettable: true}
|
||||
8: {id: 2, hub_id: 1, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false}
|
||||
# Updated count on hand to 3
|
||||
updatedVos = [
|
||||
{id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 3, default_stock: 3, resettable: true}
|
||||
]
|
||||
|
||||
VariantOverrides.updateData(updatedVos)
|
||||
expect(VariantOverrides.variantOverrides[1]).toEqual
|
||||
3: {id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 3, default_stock: 3, resettable: true}
|
||||
8: {id: 2, hub_id: 1, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false}
|
||||
|
||||
@@ -3,8 +3,8 @@ require 'open_food_network/scope_variant_to_hub'
|
||||
module OpenFoodNetwork
|
||||
describe ScopeVariantToHub do
|
||||
let(:hub) { create(:distributor_enterprise) }
|
||||
let(:v) { create(:variant, price: 11.11, count_on_hand: 1) }
|
||||
let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2) }
|
||||
let(:v) { create(:variant, price: 11.11, count_on_hand: 1, on_demand: true, sku: "VARIANTSKU") }
|
||||
let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2, on_demand: false, sku: "VOSKU") }
|
||||
let(:vo_price_only) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: nil) }
|
||||
let(:scoper) { ScopeVariantToHub.new(hub) }
|
||||
|
||||
@@ -66,6 +66,75 @@ module OpenFoodNetwork
|
||||
v.on_demand.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "overriding on_demand" do
|
||||
context "when an override exists" do
|
||||
before { vo }
|
||||
|
||||
context "with an on_demand set" do
|
||||
it "returns the overridden on_demand" do
|
||||
scoper.scope v
|
||||
expect(v.on_demand).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "without an on_demand set" do
|
||||
before { vo.update_column(:on_demand, nil) }
|
||||
|
||||
context "when count_on_hand is set" do
|
||||
it "returns false" do
|
||||
scoper.scope v
|
||||
expect(v.on_demand).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "when count_on_hand is not set" do
|
||||
before { vo.update_column(:count_on_hand, nil) }
|
||||
|
||||
it "returns the variant's on_demand" do
|
||||
scoper.scope v
|
||||
expect(v.on_demand).to be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when no override exists" do
|
||||
it "returns the variant's on_demand" do
|
||||
scoper.scope v
|
||||
expect(v.on_demand).to be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "overriding sku" do
|
||||
context "when an override exists" do
|
||||
before { vo }
|
||||
|
||||
context "with an sku set" do
|
||||
it "returns the overridden sku" do
|
||||
scoper.scope v
|
||||
expect(v.sku).to eq "VOSKU"
|
||||
end
|
||||
end
|
||||
|
||||
context "without an sku set" do
|
||||
before { vo.update_column(:sku, nil) }
|
||||
|
||||
it "returns the variant's sku" do
|
||||
scoper.scope v
|
||||
expect(v.sku).to eq "VARIANTSKU"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when no override exists" do
|
||||
it "returns the variant's sku" do
|
||||
scoper.scope v
|
||||
expect(v.sku).to eq "VARIANTSKU"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -323,7 +323,7 @@ module Spree
|
||||
let!(:er1) { create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:create_variant_overrides]) }
|
||||
|
||||
it "should be able to access variant overrides page" do
|
||||
should have_ability([:admin, :index, :bulk_update], for: VariantOverride)
|
||||
should have_ability([:admin, :index, :bulk_update, :bulk_reset], for: VariantOverride)
|
||||
end
|
||||
|
||||
it "should be able to read/write their own variant overrides" do
|
||||
|
||||
@@ -49,16 +49,16 @@ describe VariantOverride do
|
||||
describe "checking if stock levels have been overriden" do
|
||||
it "returns true when stock level has been overridden" do
|
||||
create(:variant_override, variant: variant, hub: hub, count_on_hand: 12)
|
||||
VariantOverride.stock_overridden?(hub, variant).should be_true
|
||||
VariantOverride.stock_overridden?(hub, variant).should be true
|
||||
end
|
||||
|
||||
it "returns false when the override has no stock level" do
|
||||
create(:variant_override, variant: variant, hub: hub, count_on_hand: nil)
|
||||
VariantOverride.stock_overridden?(hub, variant).should be_false
|
||||
VariantOverride.stock_overridden?(hub, variant).should be false
|
||||
end
|
||||
|
||||
it "returns false when there is no override for the hub/variant" do
|
||||
VariantOverride.stock_overridden?(hub, variant).should be_false
|
||||
VariantOverride.stock_overridden?(hub, variant).should be false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,16 +69,40 @@ describe VariantOverride do
|
||||
vo.reload.count_on_hand.should == 10
|
||||
end
|
||||
|
||||
it "silently logs an error if the variant override doesn't have a stock level" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: nil)
|
||||
Bugsnag.should_receive(:notify)
|
||||
VariantOverride.decrement_stock! hub, variant, 2
|
||||
vo.reload.count_on_hand.should be_nil
|
||||
end
|
||||
|
||||
it "silently logs an error if the variant override does not exist" do
|
||||
Bugsnag.should_receive(:notify)
|
||||
VariantOverride.decrement_stock! hub, variant, 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "checking default stock value is present" do
|
||||
it "returns true when a default stock level has been set" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20)
|
||||
vo.default_stock?.should be true
|
||||
end
|
||||
|
||||
it "returns false when the override has no default stock level" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock:nil)
|
||||
vo.default_stock?.should be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "resetting stock levels" do
|
||||
it "resets the on hand level to the value in the default_stock field" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20, resettable: true)
|
||||
vo.reset_stock!
|
||||
vo.reload.count_on_hand.should == 20
|
||||
end
|
||||
it "silently logs an error if the variant override doesn't have a default stock level" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock:nil, resettable: true)
|
||||
Bugsnag.should_receive(:notify)
|
||||
vo.reset_stock!
|
||||
vo.reload.count_on_hand.should == 12
|
||||
end
|
||||
it "doesn't reset the level if the behaviour is disabled" do
|
||||
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 10, resettable: false)
|
||||
vo.reset_stock!
|
||||
vo.reload.count_on_hand.should == 12
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user