mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-19 00:27:25 +00:00
Merge branch 'master' into 2-0-stable
* master: (206 commits) embedded groups layout changes embedded groups initial test Allow html requests for OrderCycleController#update Move applicator calls to OrderCycleForm Refactor OrderCycleForm to make logic clearer Extract schedule syncing logic into OrderCycleForm Add basic specs for OrderCyclesController#create Add basic OrderCycleForm to handle create/update logic Remove unnecessary respond_to blocks from OrderCyclesController Include admin users as managers on new enterprises Remove obsolete goWithoutHashFragments Simplify Navigation.go, not preserving hash fragments Only show change warning for open order cycles Use a SubscriptionsCount query object to provide counts to IndexOrderCycleSerializer Preload subscription counts for serialization in order cycle collection actions Request the subscription count for change warning each time, don't cache Ask user to confirm oc date change for open order cycles with subsciptions Fix ordering of Gemfile.lock Add rack-rewrite to handle redirects Renames product bulk edit action to index ...
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
//= require ./orders/orders
|
||||
//= require ./order_cycles/order_cycles
|
||||
//= require ./payment_methods/payment_methods
|
||||
//= require ./product_import/product_import
|
||||
//= require ./products/products
|
||||
//= require ./resources/resources
|
||||
//= require ./shipping_methods/shipping_methods
|
||||
|
||||
@@ -28,6 +28,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.filterTaxons = [{id: "0", name: ""}].concat $scope.taxons
|
||||
$scope.producerFilter = "0"
|
||||
$scope.categoryFilter = "0"
|
||||
$scope.importDateFilter = "0"
|
||||
$scope.products = BulkProducts.products
|
||||
$scope.filteredProducts = []
|
||||
$scope.currentFilters = []
|
||||
@@ -43,7 +44,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
.catch (message) ->
|
||||
$scope.api_error_msg = message
|
||||
|
||||
$scope.$watchCollection '[query, producerFilter, categoryFilter]', ->
|
||||
$scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter]', ->
|
||||
$scope.limit = 15 # Reset limit whenever searching
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
@@ -52,6 +53,9 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.resetProducts()
|
||||
$scope.loading = false
|
||||
|
||||
$timeout ->
|
||||
if $scope.showLatestImport
|
||||
$scope.importDateFilter = $scope.importDates[1].id
|
||||
|
||||
$scope.resetProducts = ->
|
||||
DirtyProducts.clear()
|
||||
@@ -91,6 +95,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.query = ""
|
||||
$scope.producerFilter = "0"
|
||||
$scope.categoryFilter = "0"
|
||||
$scope.importDateFilter = "0"
|
||||
|
||||
$scope.editWarn = (product, variant) ->
|
||||
if (DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
angular.module("ofn.admin").controller "enterprisesDashboardCtrl", ($scope) ->
|
||||
$scope.activeTab = "hubs"
|
||||
@@ -0,0 +1,6 @@
|
||||
angular.module("ofn.admin").directive "select2NoSearch", ($timeout) ->
|
||||
restrict: 'CA'
|
||||
link: (scope, element, attrs) ->
|
||||
$timeout ->
|
||||
element.select2
|
||||
minimumResultsForSearch: Infinity
|
||||
@@ -0,0 +1,18 @@
|
||||
# Used in enterprise new and edit forms to reset the state when the country is changed
|
||||
angular.module("admin.enterprises").controller 'countryCtrl', ($scope, availableCountries) ->
|
||||
$scope.countries = availableCountries
|
||||
|
||||
$scope.countriesById = $scope.countries.reduce (obj, country) ->
|
||||
obj[country.id] = country
|
||||
obj
|
||||
, {}
|
||||
|
||||
$scope.$watch 'Enterprise.address.country_id', (newID, oldID) ->
|
||||
$scope.clearState() unless $scope.addressStateMatchesCountry()
|
||||
|
||||
$scope.clearState = ->
|
||||
$scope.Enterprise.address.state_id = null
|
||||
|
||||
$scope.addressStateMatchesCountry = ->
|
||||
$scope.countriesById[$scope.Enterprise.address.country_id].states.some (state) ->
|
||||
state.id == $scope.Enterprise.address.state_id
|
||||
@@ -11,6 +11,9 @@ angular.module("admin.enterprises")
|
||||
$scope.$watch 'enterprise_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t('admin.unsaved_changes') if newValue
|
||||
|
||||
$scope.$watch 'newManager', (newValue) ->
|
||||
$scope.addManager($scope.newManager) if newValue
|
||||
|
||||
$scope.setFormDirty = ->
|
||||
$scope.$apply ->
|
||||
$scope.enterprise_form.$setDirty()
|
||||
@@ -47,7 +50,7 @@ angular.module("admin.enterprises")
|
||||
email: manager.email
|
||||
confirmed: manager.confirmed
|
||||
if (user for user in $scope.Enterprise.users when user.id == manager.id).length == 0
|
||||
$scope.Enterprise.users.push manager
|
||||
$scope.Enterprise.users.unshift(manager)
|
||||
$scope.enterprise_form?.$setDirty()
|
||||
else
|
||||
alert ("#{manager.email}" + " " + t("is_already_manager"))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module("admin.enterprises").controller 'NewEnterpriseController', ($scope, defaultCountryID) ->
|
||||
$scope.Enterprise =
|
||||
address:
|
||||
country_id: defaultCountryID
|
||||
state_id: null
|
||||
@@ -0,0 +1,4 @@
|
||||
angular.module("ofn.admin").filter "importDate", ($filter) ->
|
||||
return (products, importDate) ->
|
||||
return products if importDate == "0"
|
||||
$filter('filter')( products, { import_date: importDate } )
|
||||
@@ -0,0 +1,23 @@
|
||||
angular.module("admin.orderCycles").directive "changeWarning", (ConfirmDialog) ->
|
||||
restrict: "A"
|
||||
scope:
|
||||
orderCycle: '=changeWarning'
|
||||
link: (scope, element, attrs) ->
|
||||
acknowledged = false
|
||||
cancel = 'admin.order_cycles.date_warning.cancel'
|
||||
proceed = 'admin.order_cycles.date_warning.proceed'
|
||||
msg = 'admin.order_cycles.date_warning.msg'
|
||||
options = { cancel: t(cancel), confirm: t(proceed) }
|
||||
|
||||
isOpen = (orderCycle) ->
|
||||
moment(orderCycle.orders_open_at, "YYYY-MM-DD HH:mm:SS Z").isBefore() &&
|
||||
moment(orderCycle.orders_close_at, "YYYY-MM-DD HH:mm:SS Z").isAfter()
|
||||
|
||||
element.focus ->
|
||||
count = scope.orderCycle.subscriptions_count
|
||||
return if acknowledged
|
||||
return unless isOpen(scope.orderCycle)
|
||||
return if count < 1
|
||||
ConfirmDialog.open('info', t(msg, n: count), options).then ->
|
||||
acknowledged = true
|
||||
element.siblings('img').trigger('click')
|
||||
@@ -209,6 +209,7 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
delete order_cycle.editable_variants_for_incoming_exchanges
|
||||
delete order_cycle.editable_variants_for_outgoing_exchanges
|
||||
delete order_cycle.visible_variants_for_outgoing_exchanges
|
||||
delete order_cycle.subscriptions_count
|
||||
order_cycle
|
||||
|
||||
removeInactiveExchanges: (order_cycle) ->
|
||||
|
||||
@@ -56,7 +56,4 @@ angular.module("admin.orders").directive 'customerSearchOverride', ->
|
||||
return
|
||||
$('#order_email').val customer.email
|
||||
$('#user_id').val customer.user_id # modified
|
||||
$('#guest_checkout_true').prop 'checked', false
|
||||
$('#guest_checkout_false').prop 'checked', true
|
||||
$('#guest_checkout_false').prop 'disabled', false
|
||||
customer.email
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").controller "DropdownPanelsCtrl", ($scope) ->
|
||||
angular.module("admin.productImport").controller "DropdownPanelsCtrl", ($scope) ->
|
||||
$scope.active = false
|
||||
|
||||
$scope.togglePanel = ->
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module("admin.productImport").controller "ImportFeedbackCtrl", ($scope) ->
|
||||
|
||||
$scope.count = (items) ->
|
||||
total = 0
|
||||
angular.forEach items, (item) ->
|
||||
total++
|
||||
total
|
||||
|
||||
$scope.attribute_invalid = (attribute, line_number) ->
|
||||
$scope.entries[line_number]['errors'][attribute] != undefined
|
||||
|
||||
$scope.ignore_fields = ['variant_unit', 'variant_unit_scale', 'unit_description']
|
||||
@@ -0,0 +1,161 @@
|
||||
angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $http, $filter, ProductImportService, $timeout) ->
|
||||
|
||||
$scope.entries = {}
|
||||
$scope.update_counts = {}
|
||||
$scope.reset_counts = {}
|
||||
|
||||
$scope.updates = {}
|
||||
$scope.updated_total = 0
|
||||
$scope.updated_ids = []
|
||||
$scope.update_errors = []
|
||||
|
||||
$scope.chunks = 0
|
||||
$scope.completed = 0
|
||||
$scope.percentage = "0%"
|
||||
$scope.started = false
|
||||
$scope.finished = false
|
||||
|
||||
$scope.countResettable = () ->
|
||||
angular.forEach $scope.supplier_product_counts, (value, key) ->
|
||||
$scope.reset_counts[key] = value
|
||||
if $scope.update_counts[key]
|
||||
$scope.reset_counts[key] -= $scope.update_counts[key]
|
||||
|
||||
$scope.resetProgress = () ->
|
||||
$scope.chunks = 0
|
||||
$scope.completed = 0
|
||||
$scope.percentage = "0%"
|
||||
$scope.started = false
|
||||
$scope.finished = false
|
||||
|
||||
$scope.step = 'settings'
|
||||
|
||||
$scope.confirmSettings = () ->
|
||||
$scope.step = 'import'
|
||||
|
||||
$scope.viewResults = () ->
|
||||
$scope.countResettable()
|
||||
$scope.step = 'results'
|
||||
$scope.resetProgress()
|
||||
|
||||
$scope.acceptResults = () ->
|
||||
$scope.step = 'save'
|
||||
|
||||
$scope.finalResults = () ->
|
||||
$scope.step = 'complete'
|
||||
|
||||
$scope.start = () ->
|
||||
$scope.started = true
|
||||
$scope.percentage = "1%"
|
||||
total = $scope.item_count
|
||||
size = 100
|
||||
$scope.chunks = Math.ceil(total / size)
|
||||
|
||||
i = 0
|
||||
|
||||
while i < $scope.chunks
|
||||
start = (i*size)+1
|
||||
end = (i+1)*size
|
||||
if $scope.step == 'import'
|
||||
$scope.processImport(start, end)
|
||||
if $scope.step == 'save'
|
||||
$scope.processSave(start, end)
|
||||
i++
|
||||
|
||||
$scope.processImport = (start, end) ->
|
||||
$scope.getSettings() if $scope.importSettings == null
|
||||
$http(
|
||||
url: $scope.import_url
|
||||
method: 'POST'
|
||||
data:
|
||||
'start': start
|
||||
'end': end
|
||||
'filepath': $scope.filepath
|
||||
'settings': $scope.importSettings
|
||||
).success((data, status, headers, config) ->
|
||||
angular.merge($scope.entries, angular.fromJson(data['entries']))
|
||||
$scope.sortUpdates(data['reset_counts'])
|
||||
|
||||
$scope.updateProgress()
|
||||
).error((data, status, headers, config) ->
|
||||
$scope.exception = data
|
||||
console.error(data)
|
||||
)
|
||||
|
||||
$scope.importSettings = null
|
||||
|
||||
$scope.getSettings = () ->
|
||||
$scope.importSettings = ProductImportService.getSettings()
|
||||
|
||||
$scope.sortUpdates = (data) ->
|
||||
angular.forEach data, (value, key) ->
|
||||
if (key in $scope.update_counts)
|
||||
$scope.update_counts[key] += value['updates_count']
|
||||
else
|
||||
$scope.update_counts[key] = value['updates_count']
|
||||
|
||||
$scope.processSave = (start, end) ->
|
||||
$scope.getSettings() if $scope.importSettings == null
|
||||
$http(
|
||||
url: $scope.save_url
|
||||
method: 'POST'
|
||||
data:
|
||||
'start': start
|
||||
'end': end
|
||||
'filepath': $scope.filepath
|
||||
'settings': $scope.importSettings
|
||||
).success((data, status, headers, config) ->
|
||||
$scope.sortResults(data['results'])
|
||||
|
||||
angular.forEach data['updated_ids'], (id) ->
|
||||
$scope.updated_ids.push(id)
|
||||
|
||||
angular.forEach data['errors'], (error) ->
|
||||
$scope.update_errors.push(error)
|
||||
|
||||
$scope.updateProgress()
|
||||
).error((data, status, headers, config) ->
|
||||
$scope.exception = data
|
||||
console.error(data)
|
||||
)
|
||||
|
||||
$scope.sortResults = (results) ->
|
||||
angular.forEach results, (value, key) ->
|
||||
if ($scope.updates[key] != undefined)
|
||||
$scope.updates[key] += value
|
||||
else
|
||||
$scope.updates[key] = value
|
||||
|
||||
$scope.updated_total += value
|
||||
|
||||
$scope.resetAbsent = () ->
|
||||
enterprises_to_reset = []
|
||||
angular.forEach $scope.importSettings, (settings, enterprise) ->
|
||||
if settings['reset_all_absent']
|
||||
enterprises_to_reset.push(enterprise)
|
||||
|
||||
if enterprises_to_reset.length && $scope.updated_ids.length
|
||||
$http(
|
||||
url: $scope.reset_url
|
||||
method: 'POST'
|
||||
data:
|
||||
'filepath': $scope.filepath
|
||||
'settings': $scope.importSettings
|
||||
'reset_absent': true,
|
||||
'updated_ids': $scope.updated_ids,
|
||||
'enterprises_to_reset': enterprises_to_reset
|
||||
).success((data, status, headers, config) ->
|
||||
console.log(data)
|
||||
$scope.updates.products_reset = data
|
||||
|
||||
).error((data, status, headers, config) ->
|
||||
console.error(data)
|
||||
)
|
||||
|
||||
$scope.updateProgress = () ->
|
||||
$scope.completed++
|
||||
$scope.percentage = String(Math.round(($scope.completed / $scope.chunks) * 100)) + '%'
|
||||
|
||||
if $scope.completed == $scope.chunks
|
||||
$scope.finished = true
|
||||
$scope.resetAbsent() if $scope.step == 'save'
|
||||
@@ -1,12 +1,38 @@
|
||||
angular.module("ofn.admin").controller "ImportOptionsFormCtrl", ($scope, $rootScope, ProductImportService) ->
|
||||
angular.module("admin.productImport").controller "ImportOptionsFormCtrl", ($scope, $rootScope, ProductImportService) ->
|
||||
|
||||
$scope.toggleResetAbsent = () ->
|
||||
confirmed = confirm t('js.product_import.confirmation') if $scope.resetAbsent
|
||||
$scope.initForm = () ->
|
||||
$scope.settings = {} if $scope.settings == undefined
|
||||
$scope.settings[$scope.supplierId] = {
|
||||
import_into: 'product_list'
|
||||
defaults:
|
||||
count_on_hand:
|
||||
mode: 'overwrite_all'
|
||||
on_hand:
|
||||
mode: 'overwrite_all'
|
||||
tax_category_id:
|
||||
mode: 'overwrite_all'
|
||||
shipping_category_id:
|
||||
mode: 'overwrite_all'
|
||||
available_on:
|
||||
mode: 'overwrite_all'
|
||||
}
|
||||
$scope.import_into = 'product_list'
|
||||
|
||||
if confirmed or !$scope.resetAbsent
|
||||
ProductImportService.updateResetAbsent($scope.supplierId, $scope.resetCount, $scope.resetAbsent)
|
||||
$scope.updateImportInto = () ->
|
||||
$scope.import_into = $scope.settings[$scope.supplierId]['import_into']
|
||||
|
||||
$scope.$watch 'settings', (updated) ->
|
||||
ProductImportService.updateSettings(updated)
|
||||
, true
|
||||
|
||||
$scope.toggleResetAbsent = (id) ->
|
||||
checked = $scope.settings[id]['reset_all_absent']
|
||||
confirmed = confirm t('js.product_import.confirmation') if checked
|
||||
|
||||
if confirmed or !checked
|
||||
ProductImportService.updateResetAbsent($scope.supplierId, $scope.reset_counts[$scope.supplierId], checked)
|
||||
else
|
||||
$scope.resetAbsent = false
|
||||
$scope.settings[id]['reset_all_absent'] = false
|
||||
|
||||
$scope.resetTotal = ProductImportService.resetTotal
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
angular.module("admin.productImport").filter 'entriesFilterValid', ->
|
||||
(entries, type) ->
|
||||
if type == 'all'
|
||||
return entries
|
||||
|
||||
filtered = {}
|
||||
|
||||
angular.forEach entries, (entry, line_number) ->
|
||||
validates_as = entry.validates_as
|
||||
|
||||
if type == 'valid' and validates_as != '' \
|
||||
or type == 'invalid' and validates_as == '' \
|
||||
or type == 'create_product' and validates_as == 'new_product' or validates_as == 'new_variant' \
|
||||
or type == 'update_product' and validates_as == 'existing_variant' \
|
||||
or type == 'create_inventory' and validates_as == 'new_inventory_item' \
|
||||
or type == 'update_inventory' and validates_as == 'existing_inventory_item'
|
||||
filtered[line_number] = entry
|
||||
|
||||
filtered
|
||||
|
||||
angular.module("admin.productImport").filter 'entriesFilterSupplier', ->
|
||||
(entries, supplier) ->
|
||||
if supplier == 'all'
|
||||
return entries
|
||||
|
||||
filtered = {}
|
||||
|
||||
angular.forEach entries, (entry, line_number) ->
|
||||
if supplier == entry.attributes['supplier']
|
||||
filtered[line_number] = entry
|
||||
|
||||
filtered
|
||||
@@ -0,0 +1,3 @@
|
||||
angular.module("admin.productImport", ["ngResource"]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
@@ -1,7 +1,8 @@
|
||||
angular.module("ofn.admin").factory "ProductImportService", ($rootScope) ->
|
||||
angular.module("admin.productImport").factory "ProductImportService", ($rootScope) ->
|
||||
new class ProductImportService
|
||||
suppliers: {}
|
||||
resetTotal: 0
|
||||
settings: {}
|
||||
|
||||
updateResetAbsent: (supplierId, resetCount, resetAbsent) ->
|
||||
if resetAbsent
|
||||
@@ -13,3 +14,8 @@ angular.module("ofn.admin").factory "ProductImportService", ($rootScope) ->
|
||||
|
||||
$rootScope.resetTotal = @resetTotal
|
||||
|
||||
updateSettings: (updated) ->
|
||||
angular.merge(@settings, updated)
|
||||
|
||||
getSettings: () ->
|
||||
@settings
|
||||
@@ -2,5 +2,7 @@ angular.module("ofn.admin").controller "ProductImageCtrl", ($scope, ProductImage
|
||||
$scope.imageUploader = ProductImageService.imageUploader
|
||||
$scope.imagePreview = ProductImageService.imagePreview
|
||||
|
||||
$scope.$watch 'product.image_url', (newValue) ->
|
||||
$scope.imagePreview = newValue if newValue
|
||||
$scope.$watch 'product.image_url', (newValue, oldValue) ->
|
||||
if newValue != oldValue
|
||||
$scope.imagePreview = newValue
|
||||
$scope.uploadModal.close()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
angular.module("admin.utils").directive "datepicker", ->
|
||||
require: "ngModel"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
element.datepicker
|
||||
dateFormat: "yy-mm-dd"
|
||||
onSelect: (dateText, inst) ->
|
||||
scope.$apply (scope) ->
|
||||
# Fires ngModel.$parsers
|
||||
ngModel.$setViewValue dateText
|
||||
@@ -25,7 +25,8 @@ angular.module("admin.utils").directive "tagsWithTranslation", ($timeout) ->
|
||||
scope.object[scope.tagsAttr] ||= []
|
||||
compileTagList()
|
||||
|
||||
scope.tagAdded = ->
|
||||
scope.tagAdded = (tag)->
|
||||
tag.text = tag.text.toLowerCase()
|
||||
scope.onTagAdded()
|
||||
compileTagList()
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.producerFilter = 0
|
||||
$scope.importDateFilter = '0'
|
||||
$scope.query = ''
|
||||
|
||||
$scope.resetSelectFilters()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module("admin.variantOverrides").filter "importDate", ($filter, variantOverrides) ->
|
||||
return (products, hub_id, date) ->
|
||||
return [] if !hub_id
|
||||
return $filter('filter')(products, (product) ->
|
||||
return true if date == 0 or date == undefined or date == '0' or date == ''
|
||||
|
||||
angular.forEach product.variants (variant) ->
|
||||
angular.forEach variantOverrides (vo) ->
|
||||
if vo.variant_id == variant.id and vo.import_date == date
|
||||
return true
|
||||
false
|
||||
, true)
|
||||
@@ -1 +1 @@
|
||||
angular.module("admin.variantOverrides", ["admin.indexUtils", "admin.utils", "admin.dropdown", "admin.inventoryItems", 'ngTagsInput'])
|
||||
angular.module("admin.variantOverrides", ["ofn.admin", "admin.indexUtils", "admin.utils", "admin.dropdown", "admin.inventoryItems", 'ngTagsInput'])
|
||||
|
||||
Reference in New Issue
Block a user