Compare commits

..

8 Commits

Author SHA1 Message Date
Jean-Baptiste Bellet
b17d3e447c Avoid using exception but simply errors attribute contained in object 2023-03-14 15:56:16 +01:00
Jean-Baptiste Bellet
2ae4234015 Sort each array to ensure the order
and then expect the right values.
2023-03-14 09:38:31 +01:00
binarygit
20874dec98 Replace angular for when adding a new unregistered manager to an enterprise
Co-Authored-By: David Cook <david@redcliffs.net>
2023-03-14 09:37:57 +01:00
Jean-Baptiste Bellet
288cd367bc Add I18N to all reflexes 2023-03-13 21:12:00 +01:00
Jean-Baptiste Bellet
68902021ab Delete manager invitation controller 2023-03-13 21:12:00 +01:00
Jean-Baptiste Bellet
7f9c578fca Create a concern for manager invitations
Can be used elsewhere
2023-03-13 21:12:00 +01:00
Jean-Baptiste Bellet
782c9150a2 Move $locationProvider configuration to another file
Actually the `config()` method of `admin_ofn` file did not run on `/admin/enterprises/*` pages for an unknown reason

Now those two files have the same configuration
2023-03-13 21:12:00 +01:00
binarygit
447b040020 Replace what's this tooltips
There are tooltips here that don't have a what's this?
There are many angular directives/methods being used that I haven't
looked into
Every select box is using select2
2023-03-13 21:12:00 +01:00
128 changed files with 1188 additions and 2624 deletions

View File

@@ -1 +1 @@
17.9.1
14.21.2

View File

@@ -6,7 +6,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
gem 'rails'
gem 'rails', '>= 6.1.4'
# Active Storage
gem "active_storage_validations"
@@ -27,7 +27,7 @@ gem 'rails-i18n'
gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-7"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
gem "fog-aws", "~> 2.0" # db2fog does not support v3
gem "mime-types" # required by fog
@@ -136,9 +136,6 @@ gem 'view_component_reflex', '3.1.14.pre9'
gem 'mini_portile2', '~> 2.8'
gem "faraday"
gem "private_address_check"
group :production, :staging do
gem 'ddtrace'
gem 'rack-timeout'

View File

@@ -1,12 +1,12 @@
GIT
remote: https://github.com/openfoodfoundation/db2fog.git
revision: 6e88c0ab9eeb23d7ad9964b27becb1c04a83141f
branch: rails-7
revision: 5b63343847452f52aa42f7fc169d6ab3af57cda3
branch: rails-6
specs:
db2fog (0.9.2)
activerecord (>= 3.2.0)
activerecord (>= 3.2.0, < 7.0)
fog-core (~> 1.0)
rails (>= 3.2.0)
rails (>= 3.2.0, < 7.0)
GIT
remote: https://github.com/openfoodfoundation/select2-rails.git
@@ -44,49 +44,42 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
actioncable (7.0.4.3)
actionpack (= 7.0.4.3)
activesupport (= 7.0.4.3)
actioncable (6.1.7.2)
actionpack (= 6.1.7.2)
activesupport (= 6.1.7.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.4.3)
actionpack (= 7.0.4.3)
activejob (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
actionmailbox (6.1.7.2)
actionpack (= 6.1.7.2)
activejob (= 6.1.7.2)
activerecord (= 6.1.7.2)
activestorage (= 6.1.7.2)
activesupport (= 6.1.7.2)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.4.3)
actionpack (= 7.0.4.3)
actionview (= 7.0.4.3)
activejob (= 7.0.4.3)
activesupport (= 7.0.4.3)
actionmailer (6.1.7.2)
actionpack (= 6.1.7.2)
actionview (= 6.1.7.2)
activejob (= 6.1.7.2)
activesupport (= 6.1.7.2)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.4.3)
actionview (= 7.0.4.3)
activesupport (= 7.0.4.3)
rack (~> 2.0, >= 2.2.0)
actionpack (6.1.7.2)
actionview (= 6.1.7.2)
activesupport (= 6.1.7.2)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionpack-action_caching (1.2.2)
actionpack (>= 4.0.0)
actiontext (7.0.4.3)
actionpack (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
globalid (>= 0.6.0)
actiontext (6.1.7.2)
actionpack (= 6.1.7.2)
activerecord (= 6.1.7.2)
activestorage (= 6.1.7.2)
activesupport (= 6.1.7.2)
nokogiri (>= 1.8.5)
actionview (7.0.4.3)
activesupport (= 7.0.4.3)
actionview (6.1.7.2)
activesupport (= 6.1.7.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@@ -98,19 +91,19 @@ GEM
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
activesupport (>= 5.2.0)
activejob (7.0.4.3)
activesupport (= 7.0.4.3)
activejob (6.1.7.2)
activesupport (= 6.1.7.2)
globalid (>= 0.3.6)
activemerchant (1.123.0)
activesupport (>= 4.2)
builder (>= 2.1.2, < 4.0.0)
i18n (>= 0.6.9)
nokogiri (~> 1.4)
activemodel (7.0.4.3)
activesupport (= 7.0.4.3)
activerecord (7.0.4.3)
activemodel (= 7.0.4.3)
activesupport (= 7.0.4.3)
activemodel (6.1.7.2)
activesupport (= 6.1.7.2)
activerecord (6.1.7.2)
activemodel (= 6.1.7.2)
activesupport (= 6.1.7.2)
activerecord-import (1.4.1)
activerecord (>= 4.2)
activerecord-postgresql-adapter (0.0.1)
@@ -121,18 +114,19 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 2.0.8, < 3)
railties (>= 5.2.4.1)
activestorage (7.0.4.3)
actionpack (= 7.0.4.3)
activejob (= 7.0.4.3)
activerecord (= 7.0.4.3)
activesupport (= 7.0.4.3)
activestorage (6.1.7.2)
actionpack (= 6.1.7.2)
activejob (= 6.1.7.2)
activerecord (= 6.1.7.2)
activesupport (= 6.1.7.2)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.4.3)
activesupport (6.1.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
acts-as-taggable-on (9.0.1)
activerecord (>= 6.0, < 7.1)
acts_as_list (1.0.4)
@@ -230,9 +224,9 @@ GEM
cuprite (0.14.3)
capybara (~> 3.0)
ferrum (~> 0.13.0)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
database_cleaner-active_record (2.0.0)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
@@ -247,7 +241,7 @@ GEM
irb (>= 1.5.0)
reline (>= 0.3.1)
debugger-linecache (1.2.0)
devise (4.9.0)
devise (4.8.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@@ -289,17 +283,15 @@ GEM
websocket-driver (>= 0.6, < 0.8)
ffaker (2.21.0)
ffi (1.15.5)
flipper (0.26.2)
concurrent-ruby (< 2)
flipper-active_record (0.26.2)
activerecord (>= 4.2, < 8)
flipper (~> 0.26.2)
flipper-ui (0.26.2)
flipper (0.20.4)
flipper-active_record (0.20.4)
activerecord (>= 5.0, < 7)
flipper (~> 0.20.4)
flipper-ui (0.20.4)
erubi (>= 1.0.0, < 2.0.0)
flipper (~> 0.26.2)
flipper (~> 0.20.4)
rack (>= 1.4, < 3)
rack-protection (>= 1.5.3, <= 4.0.0)
sanitize (< 7)
rack-protection (>= 1.5.3, < 2.2.0)
fog-aws (2.0.1)
fog-core (~> 1.38)
fog-json (~> 1.0)
@@ -410,7 +402,7 @@ GEM
mini_portile2 (2.8.1)
mini_racer (0.6.3)
libv8-node (~> 16.10.0.0)
minitest (5.18.0)
minitest (5.17.0)
monetize (1.12.0)
money (~> 6.12)
money (6.16.0)
@@ -482,7 +474,6 @@ GEM
ttfunk
pg (1.2.3)
power_assert (2.0.2)
private_address_check (0.5.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
@@ -491,7 +482,7 @@ GEM
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.6.2)
rack (2.2.6.4)
rack (2.2.6.3)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (1.21.3)
@@ -500,7 +491,7 @@ GEM
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.0.5)
rack-protection (2.1.0)
rack
rack-proxy (0.7.6)
rack
@@ -508,20 +499,21 @@ GEM
rack-test (2.0.2)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (7.0.4.3)
actioncable (= 7.0.4.3)
actionmailbox (= 7.0.4.3)
actionmailer (= 7.0.4.3)
actionpack (= 7.0.4.3)
actiontext (= 7.0.4.3)
actionview (= 7.0.4.3)
activejob (= 7.0.4.3)
activemodel (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
rails (6.1.7.2)
actioncable (= 6.1.7.2)
actionmailbox (= 6.1.7.2)
actionmailer (= 6.1.7.2)
actionpack (= 6.1.7.2)
actiontext (= 6.1.7.2)
actionview (= 6.1.7.2)
activejob (= 6.1.7.2)
activemodel (= 6.1.7.2)
activerecord (= 6.1.7.2)
activestorage (= 6.1.7.2)
activesupport (= 6.1.7.2)
bundler (>= 1.15.0)
railties (= 7.0.4.3)
railties (= 6.1.7.2)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@@ -540,13 +532,12 @@ GEM
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
railties (7.0.4.3)
actionpack (= 7.0.4.3)
activesupport (= 7.0.4.3)
railties (6.1.7.2)
actionpack (= 6.1.7.2)
activesupport (= 6.1.7.2)
method_source
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.0.6)
ransack (2.6.0)
@@ -558,7 +549,7 @@ GEM
ffi (~> 1.0)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.14.0)
redis-client (0.13.0)
connection_pool
regexp_parser (2.7.0)
reline (0.3.2)
@@ -640,9 +631,6 @@ GEM
rubyzip (2.3.2)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
sanitize (6.0.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
sass (3.4.25)
sass-rails (5.0.8)
railties (>= 5.2.0)
@@ -654,7 +642,7 @@ GEM
semantic_range (3.0.0)
shoulda-matchers (5.3.0)
activesupport (>= 5.2.0)
sidekiq (7.0.7)
sidekiq (7.0.6)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
@@ -813,7 +801,6 @@ DEPENDENCIES
digest
dotenv-rails
factory_bot_rails (= 6.2.0)
faraday
ffaker
flipper
flipper-active_record
@@ -856,13 +843,12 @@ DEPENDENCIES
paypal-sdk-merchant (= 1.117.2)
pdf-reader
pg (~> 1.2.3)
private_address_check
pry (~> 0.13.0)
puma
rack-mini-profiler (< 3.0.0)
rack-rewrite
rack-timeout
rails
rails (>= 6.1.4)
rails-controller-testing
rails-erd
rails-i18n

View File

@@ -61,6 +61,9 @@ angular.module("admin.enterprises")
$scope.removePromoImage = ->
$scope.performEnterpriseAction("removePromoImage", "immediate_promo_image_removal_warning", "removed_promo_image_successfully")
$scope.removeTermsAndConditions = ->
$scope.performEnterpriseAction("removeTermsAndConditions", "immediate_terms_and_conditions_removal_warning", "removed_terms_and_conditions_successfully")
$scope.performEnterpriseAction = (enterpriseActionName, warning_message_key, success_message_key) ->
return unless confirm($scope.translation(warning_message_key))

View File

@@ -0,0 +1,32 @@
angular.module("admin.enterprises").directive 'termsAndConditionsWarning', ($rootScope, $compile, $templateCache, DialogDefaults, $timeout) ->
restrict: 'A'
scope: true
link: (scope, element, attr) ->
# This file input click handler will hold the browser file input dialog and show a warning modal
scope.hold_file_input_and_show_warning_modal = (event) ->
event.preventDefault()
scope.template = $compile($templateCache.get('admin/modals/terms_and_conditions_warning.html'))(scope)
if scope.template.dialog
scope.template.dialog(DialogDefaults)
scope.template.dialog('open')
$rootScope.$evalAsync()
element.bind 'click', scope.hold_file_input_and_show_warning_modal
# When the user presses continue in the warning modal, we open the browser file input dialog
scope.continue = ->
scope.template.dialog('close')
$rootScope.$evalAsync()
# unbind warning modal handler and click file input again to open the browser file input dialog
element.unbind('click').trigger('click')
# afterwards, bind warning modal handler again so that the warning is shown the next time
$timeout ->
element.bind 'click', scope.hold_file_input_and_show_warning_modal
return
scope.close = ->
scope.template.dialog('close')
$rootScope.$evalAsync()
return

View File

@@ -17,14 +17,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
]
$scope.page = 1
$scope.per_page = $scope.per_page_options[0].id
searchThrough = ["order_distributor_name",
"order_bill_address_phone",
"order_bill_address_firstname",
"order_bill_address_lastname",
"variant_product_supplier_name",
"order_email",
"order_number",
"product_name"].join("_or_") + "_cont"
$scope.confirmRefresh = ->
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
@@ -33,7 +26,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.distributorFilter = ''
$scope.supplierFilter = ''
$scope.orderCycleFilter = ''
$scope.query = ''
$scope.quickSearch = ''
$scope.startDate = undefined
$scope.endDate = undefined
event = new CustomEvent('flatpickr:clear')
@@ -67,7 +60,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
RequestMonitor.load LineItems.index(
"q[#{searchThrough}]": $scope.query,
"q[order_state_not_eq]": "canceled",
"q[order_shipment_state_not_eq]": "shipped",
"q[order_completed_at_not_null]": "true",

View File

@@ -14,4 +14,7 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
'removePromoImage':
url: '/api/v0/enterprises/:id/promo_image.json'
method: 'DELETE'
'removeTermsAndConditions':
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
method: 'DELETE'
})

View File

@@ -63,3 +63,4 @@ angular.module("admin.resources").factory 'Enterprises', ($q, $filter, Enterpris
removeLogo: performActionOnEnterpriseResource(EnterpriseResource.removeLogo)
removePromoImage: performActionOnEnterpriseResource(EnterpriseResource.removePromoImage)
removeTermsAndConditions: performActionOnEnterpriseResource(EnterpriseResource.removeTermsAndConditions)

View File

@@ -8,7 +8,7 @@ handle_move = (e, data) ->
node = data.rslt.o
new_parent = data.rslt.np
url = new URL(Spree.routes.admin_taxonomy_taxons)
url = new URL(base_url)
url.pathname = url.pathname + '/' + node.attr("id")
data = {
_method: "put",

View File

@@ -55,10 +55,6 @@ angular.module('Darkswarm').factory "EnterpriseRegistrationService", ($http, Reg
).catch((response) ->
Loading.clear()
alert(t('failed_to_update_enterprise_unknown'))
if response.data.errors.instagram
igErr = document.querySelector("#instagram-error")
igErr.style.display = 'block'
igErr.textContent = response.data.errors.instagram[0]
)
prepare: =>

View File

@@ -0,0 +1,14 @@
%div
.margin-bottom-30.text-center
.text-big
{{ 'js.admin.modals.terms_and_conditions_warning.title' | t }}
.margin-bottom-30
%p
{{ 'js.admin.modals.terms_and_conditions_warning.message_1' | t }}
.margin-bottom-30
%p
{{ 'js.admin.modals.terms_and_conditions_warning.message_2' | t }}
.text-center
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), ng: { click: 'close()' } }
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), ng: { click: 'continue()' } }

View File

@@ -67,7 +67,7 @@ module Admin
def collection
if json_request? && params[:enterprise_id].present?
CustomersWithBalance.new(Customer.of(managed_enterprise_id)).query.
CustomersWithBalance.new(managed_enterprise_id).query.
includes(
:enterprise,
{ bill_address: [:state, :country] },

View File

@@ -106,9 +106,7 @@ module Api
end
def json_api_error(message, **options)
error_options = options.delete(:error_options) || {}
{ errors: [{ detail: message }.merge(error_options)] }.merge(options)
{ errors: [{ detail: message }] }.merge(options)
end
def json_api_invalid(message, errors)

View File

@@ -6,17 +6,11 @@ module Api
module V1
class CustomersController < Api::V1::BaseController
include AddressTransformation
include ExtraFields
skip_authorization_check only: :index
before_action :authorize_action, only: [:show, :update, :destroy]
# Query parameters
before_action only: [:index] do
@extra_customer_fields = extra_fields :customer, [:balance]
end
def index
@pagy, customers = pagy(search_customers, pagy_options)
@@ -57,11 +51,7 @@ module Api
private
def customer
@customer ||= if action_name == "show"
CustomersWithBalance.new(Customer.where(id: params[:id])).query.first!
else
Customer.find(params[:id])
end
@customer ||= Customer.find(params[:id])
end
def authorize_action
@@ -71,11 +61,6 @@ module Api
def search_customers
customers = visible_customers.includes(:bill_address, :ship_address)
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
if @extra_customer_fields.include?(:balance)
customers = CustomersWithBalance.new(customers).query
end
customers.ransack(params[:q]).result.order(:id)
end

View File

@@ -15,7 +15,9 @@ module CheckoutCallbacks
prepend_before_action :require_distributor_chosen
before_action :load_order, :associate_user, :load_saved_addresses, :load_saved_credit_cards
before_action :load_shipping_methods, if: -> { params[:step] == "details" }
before_action :allowed_shipping_methods, if: -> {
params[:step] == "details"
}
before_action :ensure_order_not_completed
before_action :ensure_checkout_allowed
@@ -46,8 +48,22 @@ module CheckoutCallbacks
@selected_card = nil
end
def load_shipping_methods
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
def allowed_shipping_methods
@allowed_shipping_methods ||= sorted_available_shipping_methods.filter(
&method(:supports_all_products_shipping_categories?)
)
end
def sorted_available_shipping_methods
available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
end
def supports_all_products_shipping_categories?(shipping_method)
(products_shipping_categories - shipping_method.shipping_categories.pluck(:id)).empty?
end
def products_shipping_categories
@products_shipping_categories ||= @order.products.pluck(:shipping_category_id).uniq
end
def redirect_to_shop?

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
# To be included in api controllers for handeling query params
module ExtraFields
extend ActiveSupport::Concern
def invalid_query_param(name, status, msg)
render status: status, json: json_api_error(msg, error_options:
{
title: I18n.t("api.query_param.error.title"),
source: { parameter: name },
status: status,
code: Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
})
end
def extra_fields(type, available_fields)
fields = params.dig(:extra_fields, type)&.split(',')&.compact&.map(&:to_sym)
return [] if fields.blank?
unknown_fields = fields - available_fields
if unknown_fields.present?
invalid_query_param(
"extra_fields[#{type}]", :unprocessable_entity,
I18n.t("api.query_param.error.extra_fields", fields: unknown_fields.join(', '))
)
end
fields
end
end

View File

@@ -9,10 +9,12 @@ module ManagerInvitations
new_user.reset_password_token = Devise.friendly_token
# Same time as used in Devise's lib/devise/models/recoverable.rb.
new_user.reset_password_sent_at = Time.now.utc
if new_user.save
enterprise.users << new_user
EnterpriseMailer.manager_invitation(enterprise, new_user).deliver_later
end
new_user.save
return new_user unless new_user.valid? # Return early if user is invalid.
enterprise.users << new_user
EnterpriseMailer.manager_invitation(@enterprise, new_user).deliver_later
new_user
end

View File

@@ -24,7 +24,7 @@ class SplitCheckoutController < ::BaseController
check_step if params[:step]
recalculate_tax if params[:step] == "summary"
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
flash_error_when_no_shipping_method_available if allowed_shipping_methods.none?
end
def update
@@ -168,7 +168,7 @@ class SplitCheckoutController < ::BaseController
end
def shipping_method_ship_address_not_required?
selected_shipping_method = available_shipping_methods&.select do |sm|
selected_shipping_method = allowed_shipping_methods&.select do |sm|
sm.id.to_s == params[:shipping_method_id]
end

View File

@@ -1,47 +0,0 @@
# frozen_string_literal: true
class WebhookEndpointsController < ::BaseController
before_action :load_resource, only: :destroy
def create
webhook_endpoint = spree_current_user.webhook_endpoints.new(webhook_endpoint_params)
if webhook_endpoint.save
flash[:success] = t('.success')
else
flash[:error] = t('.error')
end
redirect_to redirect_path
end
def destroy
if @webhook_endpoint.destroy
flash[:success] = t('.success')
else
flash[:error] = t('.error')
end
redirect_to redirect_path
end
def load_resource
@webhook_endpoint = spree_current_user.webhook_endpoints.find(params[:id])
end
def webhook_endpoint_params
params.require(:webhook_endpoint).permit(:url)
end
def redirect_path
if request.referer.blank? || request.referer.include?(spree.account_path)
developer_settings_path
else
request.referer
end
end
def developer_settings_path
"#{spree.account_path}#/developer_settings"
end
end

View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true
# Trigger jobs for any order cycles that recently opened
class OrderCycleOpenedJob < ApplicationJob
def perform
ActiveRecord::Base.transaction do
recently_opened_order_cycles.find_each do |order_cycle|
OrderCycleWebhookService.create_webhook_job(order_cycle, 'order_cycle.opened')
end
mark_as_opened(recently_opened_order_cycles)
end
end
private
def recently_opened_order_cycles
@recently_opened_order_cycles ||= OrderCycle
.where(opened_at: nil)
.where(orders_open_at: 1.hour.ago..Time.zone.now)
.lock.order(:id)
end
def mark_as_opened(order_cycles)
now = Time.zone.now
order_cycles.update_all(opened_at: now, updated_at: now)
end
end

View File

@@ -1,50 +0,0 @@
# frozen_string_literal: true
require "faraday"
require "private_address_check"
require "private_address_check/tcpsocket_ext"
# Deliver a webhook payload
# As a delayed job, it can run asynchronously and handle retries.
class WebhookDeliveryJob < ApplicationJob
# General failed request error that we're going to use to signal
# the job runner to retry our webhook worker.
class FailedWebhookRequestError < StandardError; end
queue_as :default
def perform(url, event, payload)
body = {
id: job_id,
at: Time.zone.now.to_s,
event: event,
data: payload,
}
# Request user-submitted url, preventing any private connections being made
# (SSRF).
# This method may allow the socket to open, but is necessary in order to
# protect from TOC/TOU.
# Note that private_address_check provides some methods for pre-validating,
# but they're not as comprehensive and so unnecessary here. Simply
# momentarily opening sockets probably can't cause DoS or other damage.
PrivateAddressCheck.only_public_connections do
notify_endpoint(url, body)
end
end
def notify_endpoint(url, body)
connection = Faraday.new(
request: { timeout: 30 },
headers: {
'User-Agent' => 'openfoodnetwork_webhook/1.0',
'Content-Type' => 'application/json',
}
)
response = connection.post(url, body.to_json)
# Raise a failed request error and let job runner handle retrying.
# In theory, only 5xx errors should be retried, but who knows.
raise FailedWebhookRequestError, response.status.to_s unless response.success?
end
end

View File

@@ -61,9 +61,4 @@ class CustomerSchema < JsonApiSchema
def self.relationships
[:enterprise]
end
# Optional attributes included with eg: CustomerSchema.schema(extra_fields: :balance)
def self.balance
{ balance: { type: :number, format: :double } }
end
end

View File

@@ -19,77 +19,84 @@ class JsonApiSchema
end
def schema(options = {})
Structure.schema(data_properties(**options))
{
type: :object,
properties: {
data: {
type: :object,
properties: data_properties(**options)
},
meta: { type: :object },
links: { type: :object }
},
required: [:data]
}
end
def collection(options)
Structure.collection(data_properties(**options))
{
type: :object,
properties: {
data: {
type: :array,
items: {
type: :object,
properties: data_properties(**options)
}
},
meta: {
type: :object,
properties: {
pagination: {
type: :object,
properties: {
results: { type: :integer, example: 250 },
pages: { type: :integer, example: 5 },
page: { type: :integer, example: 2 },
per_page: { type: :integer, example: 50 },
}
}
},
required: [:pagination]
},
links: {
type: :object,
properties: {
self: { type: :string },
first: { type: :string },
prev: { type: :string, nullable: true },
next: { type: :string, nullable: true },
last: { type: :string }
}
}
},
required: [:data, :meta, :links]
}
end
private
def data_properties(require_all: false, extra_fields: nil)
extra_fields_result = get_extra_fields(extra_fields)
attributes = get_attributes(extra_fields_result)
required = get_required(require_all, extra_fields, extra_fields_result)
Structure.data_properties(object_name, attributes, required, relationship_properties)
end
def relationship_properties
relationships.to_h { |name| [name, relationship_schema(name)] }
end
# Example
# MySchema.schema(extra_fields: :my_method)
# => extra_fields_result = MySchema.my_method
# => attributes = attributes.merge(extra_fields_result)
#
# MySchema.schema(extra_fields: {name: :my_method, required: true, opts: {method_opt: true}})
# => extra_fields_result = MySchema.my_method(method_opt: true)
# => attributes = attributes.merge(extra_fields_result)
# => required += extra_fields_result.keys
#
# MySchema.schema(extra_fields: [:my_method, :another_method])
# => extra_fields_result = MySchema.my_method.merge(another_method)
# => attributes = attribtues.merge(extra_fields_result)
#
# To test use eg:
# MySchema.schema(extra_fields: :my_method)
# .dig(:properties, :data, :properties, :attributes)
def get_extra_fields(extra_fields)
case extra_fields
when Symbol
public_send(extra_fields)
when Hash
public_send(extra_fields[:name], **extra_fields[:opts].to_h)
when Array
obj = {}
extra_fields.each do |w|
obj.merge!(get_extra_fields(w))
end
obj
end
end
def get_required(require_all, extra_fields, extra_fields_result)
def data_properties(require_all: false)
required = require_all ? all_attributes : required_attributes
if extra_fields.is_a?(Hash) && extra_fields[:required] == true && extra_fields_result.present?
required += extra_fields_result.keys
end
required
end
def get_attributes(extra_fields_result)
if [extra_fields_result, attributes].all?{ |obj| obj.respond_to?(:merge) }
attributes.merge(extra_fields_result)
else
attributes
end
{
id: { type: :string, example: "1" },
type: { type: :string, example: object_name },
attributes: {
type: :object,
properties: attributes,
required: required
},
relationships: {
type: :object,
properties: relationships.to_h do |name|
[
name,
relationship_schema(name)
]
end
}
}
end
def relationship_schema(name)

View File

@@ -1,83 +0,0 @@
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
class JsonApiSchema
module Structure
extend self
def schema(data_properties)
{
type: :object,
properties: {
data: {
type: :object,
properties: data_properties
},
meta: { type: :object },
links: { type: :object }
},
required: [:data]
}
end
def collection(data_properties)
{
type: :object,
properties: {
data: {
type: :array,
items: {
type: :object,
properties: data_properties
}
},
meta: {
type: :object,
properties: {
pagination: {
type: :object,
properties: {
results: { type: :integer, example: 250 },
pages: { type: :integer, example: 5 },
page: { type: :integer, example: 2 },
per_page: { type: :integer, example: 50 },
}
}
},
required: [:pagination]
},
links: {
type: :object,
properties: {
self: { type: :string },
first: { type: :string },
prev: { type: :string, nullable: true },
next: { type: :string, nullable: true },
last: { type: :string }
}
}
},
required: [:data, :meta, :links]
}
end
def data_properties(object_name, attributes, required, relationship_properties)
{
id: { type: :string, example: "1" },
type: { type: :string, example: object_name },
attributes: {
type: :object,
properties: attributes,
required: required
},
relationships: {
type: :object,
properties: relationship_properties
}
}
end
end
end
# rubocop:enable Metrics/MethodLength

View File

@@ -17,7 +17,7 @@ class Enterprise < ApplicationRecord
}.freeze
VALID_INSTAGRAM_REGEX = %r{\A[a-zA-Z0-9._]{1,30}([^/-]*)\z}
searchable_attributes :sells, :is_primary_producer, :name
searchable_attributes :sells, :is_primary_producer
searchable_associations :properties
searchable_scopes :is_primary_producer, :is_distributor, :is_hub, :activated, :visible,
:ready_for_checkout, :not_ready_for_checkout
@@ -450,8 +450,7 @@ class Enterprise < ApplicationRecord
end
def strip_url(url)
# Strip protocol and trailing slash
url&.sub(%r{(https?://)?}, '')&.sub(%r{/\z}, '')
url&.sub(%r{(https?://)?}, '')
end
def correct_whatsapp_url(phone_number)
@@ -459,11 +458,11 @@ class Enterprise < ApplicationRecord
end
def correct_instagram_url(url)
url && strip_url(url.downcase).sub(%r{(www\.)?instagram.com/}, '').delete("@")
url && strip_url(url.downcase).sub(%r{www.instagram.com/}, '').sub(%r{instagram.com/}, '').delete("@")
end
def correct_twitter_url(url)
url && strip_url(url).sub(%r{(www\.)?twitter.com/}, '').delete("@")
url && strip_url(url).sub(%r{www.twitter.com/}, '').delete("@")
end
def set_unused_address_fields

View File

@@ -34,7 +34,6 @@ class OrderCycle < ApplicationRecord
attr_accessor :incoming_exchanges, :outgoing_exchanges
before_update :reset_opened_at, if: :will_save_change_to_orders_open_at?
before_update :reset_processed_at, if: :will_save_change_to_orders_close_at?
after_save :sync_subscriptions, if: :opening?
@@ -334,14 +333,6 @@ class OrderCycle < ApplicationRecord
errors.add(:orders_close_at, :after_orders_open_at)
end
def reset_opened_at
# Reset only if order cycle is opening again at a later date
return unless orders_open_at.present? && orders_open_at_was.present?
return unless orders_open_at > orders_open_at_was
self.opened_at = nil
end
def reset_processed_at
return unless orders_close_at.present? && orders_close_at_was.present?
return unless orders_close_at > orders_close_at_was

View File

@@ -4,7 +4,7 @@ module Spree
class Address < ApplicationRecord
include AddressDisplay
searchable_attributes :firstname, :lastname, :phone
searchable_attributes :firstname, :lastname
searchable_associations :country, :state
belongs_to :country, class_name: "Spree::Country"

View File

@@ -14,7 +14,7 @@ module Spree
searchable_attributes :number, :state, :shipment_state, :payment_state, :distributor_id,
:order_cycle_id, :email, :total, :customer_id
searchable_associations :shipping_method, :bill_address, :distributor
searchable_associations :shipping_method, :bill_address
searchable_scopes :complete, :incomplete
checkout_flow do

View File

@@ -38,10 +38,7 @@ module Spree
has_many :customers
has_many :credit_cards
has_many :report_rendering_options, class_name: "::ReportRenderingOptions", dependent: :destroy
has_many :webhook_endpoints, dependent: :destroy
accepts_nested_attributes_for :enterprise_roles, allow_destroy: true
accepts_nested_attributes_for :webhook_endpoints
accepts_nested_attributes_for :bill_address
accepts_nested_attributes_for :ship_address
@@ -151,6 +148,10 @@ module Spree
spree_orders.incomplete.where(created_by_id: id).order('created_at DESC').first
end
def flipper_id
"#{self.class.name};#{id}"
end
def disabled
disabled_at.present?
end

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
# Records a webhook url to send notifications to
class WebhookEndpoint < ApplicationRecord
validates :url, presence: true
end

View File

@@ -1,22 +1,28 @@
# frozen_string_literal: true
# Adds an aggregated 'balance_value' to each customer based on their order history
#
# Fetches the customers of the specified enterprise including the aggregated balance across the
# customer's orders. That is, we get the total balance for each customer with this enterprise.
class CustomersWithBalance
def initialize(customers)
@customers = customers
def initialize(enterprise)
@enterprise = enterprise
end
def query
@customers.
Customer.of(enterprise).
joins(left_join_complete_orders).
group("customers.id").
select("customers.*").
select("#{outstanding_balance_sum} AS balance_value")
select(outstanding_balance_sum)
end
private
attr_reader :enterprise
def outstanding_balance_sum
"SUM(#{OutstandingBalance.new.statement}) AS balance_value"
end
# The resulting orders are in states that belong after the checkout. Only these can be considered
# for a customer's balance.
def left_join_complete_orders
@@ -30,8 +36,4 @@ class CustomersWithBalance
states = Spree::Order::FINALIZED_STATES.map { |state| Arel::Nodes.build_quoted(state) }
Arel::Nodes::In.new(Spree::Order.arel_table[:state], states)
end
def outstanding_balance_sum
"SUM(#{OutstandingBalance.new.statement})::float"
end
end

View File

@@ -1,35 +0,0 @@
# frozen_string_literal: true
class BulkActionsInOrdersListReflex < ApplicationReflex
def resend_confirmation_email(order_ids)
orders(order_ids).find_each do |o|
Spree::OrderMailer.confirm_email_for_customer(o.id, true).deliver_later if can? :resend, o
end
success("admin.resend_confirmation_emails_feedback", order_ids.count)
end
def send_invoice(order_ids)
count = 0
orders(order_ids).find_each do |o|
next unless o.distributor.can_invoice? && (o.resumed? || o.complete?)
Spree::OrderMailer.invoice_email(o.id).deliver_later
count += 1
end
success("admin.send_invoice_feedback", count)
end
private
def success(i18n_key, count)
flash[:success] = I18n.t(i18n_key, count: count)
cable_ready.dispatch_event(name: "modal:close")
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
end
def orders(order_ids)
Spree::Order.where(id: order_ids)
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: false
class EnterpriseEditReflex < ApplicationReflex
def remove_terms_and_conditions
@enterprise = Enterprise.find(element.dataset['enterprise-id'])
throw :forbidden unless can?(:remove_terms_and_conditions, @enterprise)
@enterprise.terms_and_conditions.purge_later
end
end

View File

@@ -22,10 +22,11 @@ class InviteManagerReflex < ApplicationReflex
new_user = create_new_manager(email, enterprise)
if new_user.errors.empty?
if new_user.valid?
locals[:success] = true
else
locals[:error] = new_user.errors.full_messages.to_sentence
locals[:error] = new_user.errors.full_messages.to_sentence ||
I18n.t('admin.enterprises.invite_manager.error')
end
return_morph(locals)

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
class ResendConfirmationEmailReflex < ApplicationReflex
def confirm(order_ids)
Spree::Order.where(id: order_ids).find_each do |o|
Spree::OrderMailer.confirm_email_for_customer(o.id, true).deliver_later if can? :resend, o
end
flash[:success] = I18n.t("admin.resend_confirmation_emails_feedback", count: order_ids.count)
cable_ready.dispatch_event(name: "modal:close")
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
end
end

View File

@@ -16,10 +16,6 @@ module Api
address(object.shipping_address)
end
attribute :balance, if: proc { |record|
record.respond_to?(:balance_value)
}, &:balance_value
belongs_to :enterprise, links: {
related: ->(object) {
url_helpers.api_v1_enterprise_url(id: object.enterprise_id)

View File

@@ -18,7 +18,9 @@ class OrderAvailablePaymentMethods
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor,
"FilterPaymentMethods", customer&.tag_list)
applicator.filter(payment_methods)
applicator.filter!(payment_methods)
payment_methods.uniq
end
private
@@ -30,7 +32,7 @@ class OrderAvailablePaymentMethods
distributor.payment_methods.where(
id: available_distributor_payment_methods_ids
)
end.available.select(&:configured?).uniq
end.available.select(&:configured?)
end
def available_distributor_payment_methods_ids

View File

@@ -14,32 +14,25 @@ class OrderAvailableShippingMethods
def to_a
return [] if distributor.blank?
filter_by_category(tag_rules.filter(shipping_methods))
shipping_methods = shipping_methods_before_tag_rules_applied
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor,
"FilterShippingMethods", customer&.tag_list)
applicator.filter!(shipping_methods)
shipping_methods.uniq
end
private
def filter_by_category(methods)
return methods unless OpenFoodNetwork::FeatureToggle.enabled?(:match_shipping_categories,
distributor&.owner)
required_category_ids = order.products.pluck(:shipping_category_id).to_set
return methods if required_category_ids.empty?
methods.select do |method|
provided_category_ids = method.shipping_categories.pluck(:id).to_set
required_category_ids.subset?(provided_category_ids)
end
end
def shipping_methods
def shipping_methods_before_tag_rules_applied
if order_cycle.nil? || order_cycle.simple?
distributor.shipping_methods
else
distributor.shipping_methods.where(
id: available_distributor_shipping_methods_ids
)
end.frontend.to_a.uniq
end.frontend.to_a
end
def available_distributor_shipping_methods_ids
@@ -47,10 +40,4 @@ class OrderAvailableShippingMethods
.where(distributor_id: distributor.id)
.select(:shipping_method_id)
end
def tag_rules
OpenFoodNetwork::TagRuleApplicator.new(
distributor, "FilterShippingMethods", customer&.tag_list
)
end
end

View File

@@ -1,21 +0,0 @@
# frozen_string_literal: true
# Create a webhook payload for an order cycle event.
# The payload will be delivered asynchronously.
class OrderCycleWebhookService
def self.create_webhook_job(order_cycle, event)
webhook_payload = order_cycle
.slice(:id, :name, :orders_open_at, :orders_close_at, :coordinator_id)
.merge(coordinator_name: order_cycle.coordinator.name)
# Endpoints for coordinator owner
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints
# Plus unique endpoints for distributor owners (ignore duplicates)
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints)
webhook_endpoints.each do |endpoint|
WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload)
end
end
end

View File

@@ -15,10 +15,7 @@ module PermittedAttributes
private
def permitted_attributes
[
:email, :password, :password_confirmation, :disabled,
{ webhook_endpoints_attributes: [:id, :url] },
]
[:email, :password, :password_confirmation, :disabled]
end
end
end

View File

@@ -29,11 +29,15 @@ module Shop
private
# order_cycles is a ActiveRecord::Relation that is modified with reject in the TagRuleApplicator
# If this relation is reloaded (for example by calling count on it), the modifications are lost
def apply_tag_rules!(order_cycles)
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor,
"FilterOrderCycles",
@customer&.tag_list)
applicator.filter(order_cycles)
applicator.filter!(order_cycles)
order_cycles
end
end
end

View File

@@ -31,39 +31,22 @@
= f.label :invoice_text, t('.invoice_text')
.omega.eight.columns
= f.text_area :invoice_text, style: "width: 100%; height: 100px;"
.row{ data: { controller: 'terms-and-conditions', "terms-and-conditions-message-value": t('js.admin.enterprises.form.images.immediate_terms_and_conditions_removal_warning') } }
.row
.alpha.three.columns
= f.label :terms_and_conditions, t('.terms_and_conditions')
%i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "terms_and_conditions_info_modal" }
.omega.eight.columns#terms_and_conditions{data: { 'reflex-root': '#terms_and_conditions' } }
- if @enterprise.terms_and_conditions.attached?
= link_to "#{@enterprise.terms_and_conditions.blob.filename} #{ t('.uploaded_on') } #{@enterprise.terms_and_conditions.blob.created_at}", url_for(@enterprise.terms_and_conditions), target: '_blank'
%div
%a.icon-trash{ href: '#', data: { action: 'click->terms-and-conditions#remove', "terms-and-conditions-message-value": t('js.admin.enterprises.form.images.immediate_terms_and_conditions_removal_warning'), 'enterprise-id': @enterprise.id}}
= t('.remove_terms_and_conditions')
.omega.eight.columns
%a{ href: '{{ Enterprise.terms_and_conditions }}', target: '_blank', ng: { if: 'Enterprise.terms_and_conditions' } }
= '{{ Enterprise.terms_and_conditions_file_name }}'
= t('.uploaded_on')
= '{{ Enterprise.terms_and_conditions_updated_at }}'
.pad-top
%div
.button.small{ data: { controller: 'help-modal-link', action: 'click->help-modal-link#open', "help-modal-link-target-value": "terms_and_conditions_warning_modal" } }
= t('.upload')
%span{ data: { "terms-and-conditions-target": "filename" } }
= f.file_field :terms_and_conditions, accept: 'application/pdf', style: 'display: none;', data: { "terms-and-conditions-target": "fileinput" }
= render HelpModalComponent.new(id: "terms_and_conditions_warning_modal", close_button: false ) do
%div
.margin-bottom-30.text-center
.text-big{ style: 'color: red'}
= t('js.admin.modals.terms_and_conditions_warning.title')
.margin-bottom-30
%p
= t('js.admin.modals.terms_and_conditions_warning.message_1')
.margin-bottom-30
%p
= t('js.admin.modals.terms_and_conditions_warning.message_2')
.text-center
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), "data-action": "click->help-modal#close"}
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), "data-action": "click->help-modal#close click->terms-and-conditions#add" }
= f.file_field :terms_and_conditions, accept: 'application/pdf', 'terms-and-conditions-warning' => 'true'
.pad-top
%a.button.red{ href: '', ng: {click: 'removeTermsAndConditions()', if: 'Enterprise.terms_and_conditions'} }
= t('.remove_terms_and_conditions')
= f.fields_for :business_address, @enterprise.business_address || @enterprise.build_business_address do |bf|
@@ -80,12 +63,13 @@
%legend= t('.invoice_item_sorting_legend')
.three.columns.alpha
%label= t('.sort_items_by_supplier?')
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.sort_items_by_supplier_tip')}
%div{'ofn-with-tip' => t('.sort_items_by_supplier_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :preferred_invoice_order_by_supplier, true
= f.radio_button :preferred_invoice_order_by_supplier, true, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'true'
= f.label :preffered_invoice_order_by_supplier, t('.enabled'), value: :true
.five.columns.omega
= f.radio_button :preferred_invoice_order_by_supplier, false
= f.radio_button :preferred_invoice_order_by_supplier, false, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'false'
= f.label :preferred_invoice_order_by_name, t('.disabled'), value: :false
= render HelpModalComponent.new(id: "terms_and_conditions_info_modal") do

View File

@@ -5,7 +5,7 @@
%fieldset.no-border-bottom.print-hidden
%legend{ align: 'center'}= t(:report_filters)
= render partial: "admin/reports/filters/#{@report_type}", locals: { f: f }
- if @report_subtype && lookup_context.exists?(@report_subtype, "admin/reports/filters", true)
- if @report_subtype && lookup_context.exists?(@report_subtype, "admin/reports/filters/", true)
= render partial: "admin/reports/filters/#{@report_subtype}", locals: { f: f }
%fieldset.print-hidden

View File

@@ -10,7 +10,7 @@
%span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } }
{{ enterprise.name }}
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('images', about)" } }
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('images',about)" } }
.row
.small-12.columns
.alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } }

View File

@@ -37,7 +37,6 @@
.field
%label{ for: 'enterprise_instagram' }= t(".instagram")+":"
%input.chunky{ id: 'enterprise_instagram', placeholder: "{{'registration.steps.social.instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } }
%span.error.small-12.columns#instagram-error{ style: "display: none" }
.row.buttons
.small-12.columns

View File

@@ -76,8 +76,8 @@
- display_ship_address = false
- ship_method_description = nil
- selected_shipping_method ||= @shipping_methods[0].id if @shipping_methods.length == 1
- @shipping_methods.each do |shipping_method|
- selected_shipping_method ||= @allowed_shipping_methods[0].id if @allowed_shipping_methods.length == 1
- @allowed_shipping_methods.each do |shipping_method|
- ship_method_is_selected = shipping_method.id == selected_shipping_method.to_i
%div.checkout-input.checkout-input-radio
= fields_for shipping_method do |shipping_method_form|

View File

@@ -18,28 +18,22 @@
%input.red{ type: "button", value: "Save Changes", ng: { click: "submit()", disabled: "!bulk_order_form.$dirty" } }
%legend{ align: 'center'}= t(:search)
%div{ :class => "sixteen columns alpha" }
.quick_search.three.columns.alpha
%label{ for: 'quick_filter' }
%br
%input.quick-search.fullwidth{ ng: {model: 'query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchResults()" }
.one.columns
&nbsp;
.filter_select{ :class => "three columns" }
.filter_select{ :class => "four columns" }
%label{ :for => 'supplier_filter' }
= t("admin.producer")
%br
%input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter' } }
.filter_select{ :class => "three columns" }
.filter_select{ :class => "four columns" }
%label{ :for => 'distributor_filter' }
= t("admin.shop")
%br
%input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter' } }
.filter_select{ :class => "three columns" }
.filter_select{ :class => "four columns" }
%label{ :for => 'order_cycle_filter' }
= t("admin.order_cycle")
%br
%input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter' } }
.date_filter{class: "three columns"}
.date_filter{class: "four columns"}
%label
= t("date_range")
%br
@@ -103,6 +97,8 @@
.clear
%div{ ng: { hide: 'RequestMonitor.loading || line_items.length == 0' }, style: "display: flex; justify-content: flex-start; column-gap: 10px; margin-bottom: 15px" }
%div{ style: "flex-grow: 1" }
%input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
-# This -20px is a hack to make the dropdowns align properly
%div{ style: "margin-right: -20px;" }
= render 'admin/shared/bulk_actions_dropdown'
@@ -166,7 +162,7 @@
= "#{t('admin.price')} (#{Spree::Money.currency_symbol})"
%th.actions
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | filter:quickSearch | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
%td.bulk
%input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true }
%td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }}

View File

@@ -33,9 +33,6 @@
%div.menu_item
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
= t('.resend_confirmation')
%div.menu_item
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" }
= t('.send_invoice')
%div.menu_item
%span.name.invoices-modal{'ng-controller' => 'bulkInvoiceCtrl', 'ng-click' => 'createBulkInvoice()' }
= t('.print_invoices')
@@ -127,11 +124,6 @@
= render ConfirmModalComponent.new(id: "resend_confirmation", confirm_actions: "click->resend-confirmation-email#confirm", controllers: "resend-confirmation-email") do
.margin-bottom-30
= t('.resend_confirmation_confirm_html')
= render ConfirmModalComponent.new(id: "send_invoice", confirm_actions: "click->send-invoice#confirm", controllers: "send-invoice") do
.margin-bottom-30
= t('.send_invoice_confirm_html')
= render ConfirmModalComponent.new(id: "cancel_orders", confirm_actions: "click->cancel-orders#confirm", controllers: "cancel-orders", message: "spree/admin/orders/messages/cancel_orders") do
.margin-bottom-30
= t("js.admin.orders.cancel_the_order_html")

View File

@@ -1,4 +1,3 @@
%script{ type: "text/ng-template", id: "account/developer_settings.html" }
%h3= t('.title')
= render partial: 'api_keys'
= render partial: 'webhook_endpoints'

View File

@@ -1,33 +0,0 @@
%section{ id: "webhook_endpoints" }
%hr
%h3= t('.title')
%p= t('.description')
%table{width: "100%"}
%thead
%tr
%th= t('.event_type.header')
%th= t('.url.header')
%th.actions
%tbody
-# Existing endpoints
- @user.webhook_endpoints.each do |webhook_endpoint|
%tr
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
%td= webhook_endpoint.url
%td.actions
- if webhook_endpoint.persisted?
= button_to account_webhook_endpoint_path(webhook_endpoint), method: :delete,
class: "tiny alert no-margin",
data: { confirm: I18n.t(:are_you_sure)} do
= I18n.t(:delete)
-# Create new
- if @user.webhook_endpoints.empty? # Only one allowed for now.
%tr
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
%td
= form_for(@user.webhook_endpoints.build, url: account_webhook_endpoints_path, id: 'new_webhook_endpoint') do |f|
= f.url_field :url, placeholder: t('.url.create_placeholder'), required: true, size: 64
%td.actions
= button_tag t(:create), class: 'button primary tiny no-margin', form: 'new_webhook_endpoint'

View File

@@ -1,20 +0,0 @@
import ApplicationController from "./application_controller";
export default class extends ApplicationController {
connect() {
super.connect();
}
// abstract
confirm(action) {
this.stimulate(action, this.getOrdersIds());
}
// private
getOrdersIds() {
const checkboxes = document.querySelectorAll(
"#listing_orders input[name='order_ids[]']:checked"
);
return Array.from(checkboxes).map((checkbox) => checkbox.value);
}
}

View File

@@ -1,11 +1,18 @@
import BulkActionsController from "./bulk_actions_controller";
import ApplicationController from "./application_controller";
export default class extends BulkActionsController {
export default class extends ApplicationController {
connect() {
super.connect();
}
confirm() {
super.confirm("BulkActionsInOrdersList#resend_confirmation_email");
const order_ids = [];
document
.querySelectorAll("#listing_orders input[name='order_ids[]']:checked")
.forEach((checkbox) => {
order_ids.push(checkbox.value);
});
this.stimulate("ResendConfirmationEmailReflex#confirm", order_ids);
}
}

View File

@@ -1,11 +0,0 @@
import BulkActionsController from "./bulk_actions_controller";
export default class extends BulkActionsController {
connect() {
super.connect();
}
confirm() {
super.confirm("BulkActionsInOrdersList#send_invoice");
}
}

View File

@@ -1,30 +0,0 @@
import ApplicationController from "./application_controller";
export default class extends ApplicationController {
static targets = ["filename", "fileinput"];
static values = {
message: String,
};
connect() {
super.connect();
this.fileinputTarget.addEventListener("change", (event) => {
this.filenameTarget.innerText = event.target.files[0].name;
});
}
remove(event) {
let confirmation = confirm(this.messageValue);
if (confirmation) {
location.hash = "";
this.stimulate(
"EnterpriseEdit#remove_terms_and_conditions",
event.target
);
}
}
add() {
this.fileinputTarget.click();
}
}

View File

@@ -2,7 +2,6 @@
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",

View File

@@ -2,7 +2,6 @@
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",

View File

@@ -249,5 +249,7 @@ module Openfoodnetwork
config.active_storage.variable_content_types += ["image/svg+xml"]
config.exceptions_app = self.routes
config.autoloader = :zeitwerk
end
end

View File

@@ -1,15 +0,0 @@
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#key-generator-digest-class-changing-to-use-sha256
Rails.application.config.after_initialize do
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
secret_key_base = Rails.application.secret_key_base
key_generator = ActiveSupport::KeyGenerator.new(
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
)
key_len = ActiveSupport::MessageEncryptor.key_len
secret = key_generator.generate_key(salt, key_len)
cookies.rotate :encrypted, secret
end
end

View File

@@ -1,5 +1,14 @@
require "flipper"
require "flipper/adapters/active_record"
require "flipper/instrumentation/log_subscriber"
Flipper.configure do |config|
config.default do
adapter = Flipper::Adapters::ActiveRecord.new
instrumented = Flipper::Adapters::Instrumented.new(adapter, instrumenter: ActiveSupport::Notifications)
Flipper.new(instrumented, instrumenter: ActiveSupport::Notifications)
end
end
if Rails.env.production?
Flipper::UI.configure do |config|
@@ -8,4 +17,6 @@ if Rails.env.production?
end
end
Rails.configuration.middleware.use Flipper::Middleware::Memoizer, preload_all: true
Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? }

View File

@@ -6,13 +6,9 @@ cy:
spree/shipping_method: Dull Anfon
attributes:
spree/order/ship_address:
address2: "Cyfeiriad danfon llinell 2"
city: "Tref y cyfeiriad danfon"
country: "Gwlad y cyfeiriad danfon"
phone: "Rhif ffôn"
firstname: "Enw cyntaf"
lastname: "Enw olaf"
zipcode: "Cod post y cyfeiriad danfon"
spree/user:
password: "Cyfrinair"
password_confirmation: "Cadarnhau'r cyfrinair"
@@ -1325,7 +1321,6 @@ cy:
addresses: Cyfeiriadau
payment_methods: Adroddiad Dulliau Talu
delivery: Adroddiad Cyflenwi
sales_tax_totals_by_producer: Cyfanswm Trethi Gwerthu fesul Cynhyrchwr
tax_types: Mathau o dreth
tax_rates: Cyfraddau Treth
pack_by_customer: Pecyn fesul Cwsmer
@@ -1815,7 +1810,6 @@ cy:
message_html: "Mae gennych chi archeb ar gyfer y cylch archebu hwn eisoes. Gwiriwch y %{cart} i weld yr eitemau a archebwyd o'r blaen. Gallwch hefyd ganslo eitemau cyn belled â bod y cylch archebu ar agor."
step1:
contact_information:
title: Manylion cyswllt
email:
label: E-bost
phone:
@@ -1869,13 +1863,10 @@ cy:
title: Manylion dosbarthu
edit: Golygu
address: Cyfeiriad dosbarthu
instructions: Cyfarwyddiadau
payment_method:
title: Dull talu
edit: Golygu
instructions: Cyfarwyddiadau
order:
title: Manylion yr archeb
edit: Golygu
terms_and_conditions:
message_html: "Rwy'n cytuno i %{terms_and_conditions_link} y gwerthwr."
@@ -1888,7 +1879,6 @@ cy:
submit: Cwblhau'r archeb
cancel: Nôl at y Dull Talu
errors:
saving_failed: "Wedi methu cadw, llenwch y meysydd a amlygwyd. %{messages}"
terms_not_accepted: Mae angen ichi dderbyn y Telerau ac Amodau
required: Ni chaniateir gadael maes yn wag
invalid_number: "Noder rhif ffôn dilys os gwelwch yn dda"
@@ -2671,8 +2661,6 @@ cy:
report_header_tax_on_delivery: "Treth ar Gyflenwi (%{currency_symbol})"
report_header_tax_on_fees: "Treth ar Ffioedd (%{currency_symbol})"
report_header_tax_category: "Categori Treth"
report_header_tax_rate_name: "Enwr Gyfradd Dreth"
report_header_tax_rate: "Cyfradd Dreth"
report_header_total_tax: "Cyfanswm Treth (%{currency_symbol})"
report_header_total_excl_tax: "Cyfanswm heb dreth (%{currency_symbol})"
report_header_total_incl_tax: "Cyfanswm gan gynnwys treth (%{currency_symbol})"
@@ -2695,7 +2683,6 @@ cy:
report_header_supplier: Cyflenwr
report_header_producer: Cynhyrchydd
report_header_producer_suburb: Maestref y Cynhyrchydd
report_header_producer_tax_status: Statws Treth y Cynhyrchwr
report_header_producer_charges_sales_tax?: Cofrestrwyd ar gyfer GST/TAW
report_header_unit: Uned
report_header_group_buy_unit_quantity: Nifer Unedau Grŵp Prynu
@@ -2712,12 +2699,10 @@ cy:
report_header_distributor_address: Cyfeiriad dosbarthwr
report_header_distributor_city: Dinas ddosbarthu
report_header_distributor_postcode: Cod post dosbarthwr
report_header_distributor_tax_status: Statws Treth y Dosbarthwr
report_header_delivery_address: Cyfeiriad Dosbarthu
report_header_delivery_postcode: Cod Post Cyflenwi
report_header_bulk_unit_size: Maint Uned Swmp
report_header_weight: Pwysau
report_header_final_weight_volume: Terfynol (Pwysau/Cyfaint)
report_header_height: Uchder
report_header_width: Lled
report_header_depth: Dyfnder
@@ -2894,7 +2879,6 @@ cy:
deleting_item_will_cancel_order: "Bydd y weithred hon yn golygu un neu fwy o archebion gwag, fydd yn cael eu canslo. Ydych chi'n siwr eich bod am wneud hyn?"
modals:
got_it: "Yn deall"
confirm: "Cadarnhau"
close: "Cau"
continue: "Parhau"
cancel: "Canslo"
@@ -3468,8 +3452,6 @@ cy:
server_error: "Nam gyda'r gweinydd"
shipping_method_names:
UPS Ground: "UPS dros y tir"
pick_up: "Casglu ar y fferm"
delivery: "Llofnodwyd, cadarnhawyd a chyflenwyd"
start_date: "Dyddiad cychwyn"
successfully_removed: "Llwyddwyd i ddileu"
taxonomy_edit: "Golygu tacsonomi"
@@ -3554,7 +3536,6 @@ cy:
display_currency: "Arddangos arian cyfred"
choose_currency: "Dewis Arian Cyfred"
mail_method_settings: "Gosodiadau Dull Postio"
mail_settings_notice_html: "<b>Bydd unrhyw newid a wneir fan hyn yn un dros dro at ddiben dadfygion unig</b>, a hwyrach y caiff ei droi yn ôl yn y dyfodol. <br>Gellir gwneud newidiadau parhaol trwy ddiweddaru cyfrinachaur achos au cyflenwi trwy ddefnyddio <a href='https://github.com/openfoodfoundation/ofn-install'>ofn-install</a>. Cysylltwch â thîm byd-eang OFN am fanylion pellach."
general: "Cyffredinol"
enable_mail_delivery: "Galluogi Dosbarthu trwy'r Post"
send_mails_as: "Anfon eitemau drwy'r Post fel"
@@ -3788,7 +3769,6 @@ cy:
print_invoices: "Argraffu anfonebau"
cancel_orders: "Canslo Archebion"
resend_confirmation: "Ailanfon Cadarnhad "
resend_confirmation_confirm_html: "Bydd hwn yn ail-anfon yr ebost cadarnhau at y cwsmer. <br />Ydych chin siŵr eich bod am wneud hyn?"
selected:
zero: "Ni ddewiswyd archeb."
one: "Dewiswyd 1 archeb"
@@ -3937,7 +3917,6 @@ cy:
title: "Cynnyrch Newydd"
new_product: "Cynnyrch Newydd"
supplier: "Cyflenwr"
supplier_select_placeholder: "Dewis cyflenwr"
product_name: "Enw Cynnyrch"
units: "Maint yr Uned"
value: "Gwerth"
@@ -4361,7 +4340,6 @@ cy:
search_input:
placeholder: Chwilio
selector_with_filter:
selected_items: "Dewiswyd %{count}"
search_placeholder: Chwilio
pagination:
next: Nesaf

View File

@@ -936,7 +936,6 @@ en:
legend: "Address"
business_details:
legend: "Business Details"
upload: 'upload'
abn: ABN
abn_placeholder: eg. 99 123 456 789
acn: ACN
@@ -1603,9 +1602,6 @@ en:
resend_confirmation_emails_feedback:
one: "Confirmation email sent for 1 order."
other: "Confirmation emails sent for %{count} orders."
send_invoice_feedback:
one: "Invoice email sent for 1 order."
other: "Invoice emails sent for %{count} orders."
# API
#
@@ -1625,11 +1621,6 @@ en:
destroy_attachment_does_not_exist: "Terms and Conditions file does not exist"
orders:
failed_to_update: "Failed to update order"
query_param:
error:
title: Invalid query parameter
extra_fields: "Unsupported fields: %{fields}"
# Frontend views
#
@@ -3574,14 +3565,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using
previous: "Previous"
last: "Last"
webhook_endpoints:
create:
success: Webhook endpoint successfully created
error: Webhook endpoint failed to create
destroy:
success: Webhook endpoint successfully deleted
error: Webhook endpoint failed to delete
spree:
order_updated: "Order Updated"
add_country: "Add country"
@@ -3962,8 +3945,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using
cancel_orders: "Cancel Orders"
resend_confirmation: "Resend Confirmation"
resend_confirmation_confirm_html: "This will resend the confirmation email to the customer.<br />Are you sure you want to proceed?"
send_invoice: "Send Invoices"
send_invoice_confirm_html: "This will email customer invoices for all selected complete orders. <br>Are you sure you want to proceed?"
selected:
zero: "No order selected"
one: "1 order selected"
@@ -4379,16 +4360,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using
api_keys:
regenerate_key: "Regenerate Key"
title: API key
webhook_endpoints:
title: Webhook Endpoints
description: Events in the system may trigger webhooks to external systems.
event_types:
order_cycle_opened: Order Cycle Opened
event_type:
header: Event type
url:
header: Endpoint URL
create_placeholder: Enter the URL of the remote webhook endpoint
developer_settings:
title: Developer Settings
form:

View File

@@ -6,22 +6,12 @@ en_CA:
spree/shipping_method: Shipping/Pick-up Method
attributes:
spree/order/ship_address:
address1: "Shipping address (House number + Street)"
address2: "Shipping address line 2"
city: "Shipping address city"
country: "Shipping address country"
phone: "Phone number"
firstname: "First name"
lastname: "Last name"
zipcode: "Shipping address postal code"
spree/order/bill_address:
address1: "Billing address (House number & Street)"
zipcode: "Billing address Postal Code"
city: "Billing address City"
country: "Billing address country"
firstname: "Billing address first name"
lastname: "Billing address last name"
phone: Customer phone
spree/user:
password: "Password"
password_confirmation: "Password confirmation"
@@ -1323,7 +1313,6 @@ en_CA:
total_by_customer: Total by Customer
total_by_supplier: Total By Supplier
supplier_totals: Order Cycle Supplier Totals
percentage: "%{value} %"
supplier_totals_by_distributor: Order Cycle Supplier Totals by Distributor
totals_by_supplier: Order Cycle Distributor Totals by Supplier
customer_totals: Order Cycle Customer Totals
@@ -1334,8 +1323,6 @@ en_CA:
addresses: Addresses
payment_methods: Payment Methods Report
delivery: Delivery Report
sales_tax_totals_by_producer: Sales Tax Totals by Producer
sales_tax_totals_by_order: Sales Tax Totals by Order
tax_types: Tax Types
tax_rates: Tax Rates
pack_by_customer: Pack By Customer
@@ -2684,7 +2671,6 @@ en_CA:
report_header_tax_on_delivery: "Tax on Delivery (%{currency_symbol})"
report_header_tax_on_fees: "Tax on Fees (%{currency_symbol})"
report_header_tax_category: "Tax Category"
report_header_tax_rate_name: "Tax Rate Name"
report_header_tax_rate: "Tax Rate"
report_header_total_tax: "Total Tax (%{currency_symbol})"
report_header_total_excl_tax: "Total excl. tax (%{currency_symbol})"
@@ -2708,7 +2694,6 @@ en_CA:
report_header_supplier: Supplier
report_header_producer: Producer
report_header_producer_suburb: Producer City/Town
report_header_producer_tax_status: Producer Tax Status
report_header_producer_charges_sales_tax?: Tax registered
report_header_unit: Unit
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
@@ -2725,7 +2710,6 @@ en_CA:
report_header_distributor_address: Distributor address
report_header_distributor_city: Distributor city
report_header_distributor_postcode: Distributor postal code
report_header_distributor_tax_status: Distributor Tax Status
report_header_delivery_address: Delivery Address
report_header_delivery_postcode: Delivery Postal Code
report_header_bulk_unit_size: Bulk Unit Size
@@ -3099,9 +3083,6 @@ en_CA:
cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer."
restock_item: "Restock Items: Return this item to stock"
restock_items: "Restock Items: Return all items to stock"
delete_line_items_html:
one: "This will delete one line item from the order. <br />Are you sure you want to proceed?"
other: "This will delete %{count} line items from the order.<br />Are you sure you want to proceed?"
resend_user_email_confirmation:
resend: "Resend"
sending: "Resend..."

View File

@@ -1520,10 +1520,6 @@ fr:
one: "Emails envoyés pour 1 commande."
many: "Emails envoyés pour %{count} commandes."
other: "Emails envoyés pour %{count} commandes."
send_invoice_feedback:
one: "Emails envoyés pour 1 commande."
many: "Emails envoyés pour %{count} commandes."
other: "Emails envoyés pour %{count} commandes."
api:
unknown_error: "Quelque chose n'a pas fonctionné. Notre équipe a été notifiée."
invalid_api_key: "La clé API (%{key}) n'est pas valide."
@@ -1540,10 +1536,6 @@ fr:
destroy_attachment_does_not_exist: "Aucun fichier"
orders:
failed_to_update: "La mise à jour de la commande échoué"
query_param:
error:
title: Paramètre de la requête invalide
extra_fields: "Champs invalides : %{fields}"
checkout:
already_ordered:
cart: "panier"
@@ -3136,10 +3128,6 @@ fr:
cancel_the_order_send_cancelation_email: "Envoyer un email d'annulation à l'acheteur"
restock_item: "Recharger le stock de ce produit"
restock_items: "Recharger le stock de tous les produits"
delete_line_items_html:
one: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
many: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
other: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
resend_user_email_confirmation:
resend: "Renvoyer"
sending: "Renvoi...."
@@ -3456,13 +3444,6 @@ fr:
first: "Début"
previous: "Précédent"
last: "Fin"
webhook_endpoints:
create:
success: Le webhook a bien été créé.
error: Le webhook n'a pas pu être créé.
destroy:
success: Le webhook a bien été supprimé.
error: Le webhook n'a pas pu être supprimé.
spree:
add_country: "Ajouter un pays"
add_state: "Ajouter un département"
@@ -3824,8 +3805,6 @@ fr:
cancel_orders: "Annuler les commandes"
resend_confirmation: "Renvoyer la confirmation"
resend_confirmation_confirm_html: "Cette action va renvoyer l'email de confirmation de commande. Etes-vous sûr de vouloir continuer ?"
send_invoice: "Envoyer les factures"
send_invoice_confirm_html: "Vous allez envoyer les factures clients pour toutes les commandes sélectionnées. <br>Souhaitez-vous continuer ?"
selected:
zero: "Aucune commande sélectionnée"
one: "1 commande sélectionnée"
@@ -4242,16 +4221,6 @@ fr:
api_keys:
regenerate_key: "Regénérer la clé"
title: Clé API
webhook_endpoints:
title: 'Webhook '
description: Les événements dans le système peuvent déclencher des webhooks vers des systèmes externes.
event_types:
order_cycle_opened: Cycle de vente ouvert
event_type:
header: Type d'évènement
url:
header: URL du point de terminaison
create_placeholder: Entrez l'URL du point de terminaison du webhook
developer_settings:
title: Paramètres développeurs
form:

View File

@@ -6,22 +6,9 @@ fr_CA:
spree/shipping_method: Option d'expédition
attributes:
spree/order/ship_address:
address1: "Adresse de livraison (Numéro et Rue)"
address2: "Adresse (Numéro et rue)"
city: "Adresse de livraison - Ville"
country: "Pays"
phone: "Numéro de téléphone"
firstname: "Prénom"
lastname: "Nom de famille"
zipcode: "Code postal"
spree/order/bill_address:
address1: "Adresse de facturation (Numéro et rue)"
zipcode: "Adresse de facturation - Code postal"
city: "Adresse de facturation - Ville"
country: "Adresse de facturation - Pays"
firstname: "Adresse de facturation - Prénom"
lastname: "Adresse de facturation - Nom"
phone: Téléphone
spree/user:
password: "Mot de passe"
password_confirmation: "Confirmation du mot de passe"
@@ -422,11 +409,8 @@ fr_CA:
filters:
categories:
title: Conditions de transport
selected_categories: "%{count} catégories sélectionnées"
producers:
title: Producteurs
selected_producers: "%{count} producteurs sélectionnés"
per_page: "%{count} éléments par page"
colums: Colonnes
columns:
name: Nom
@@ -682,7 +666,6 @@ fr_CA:
not_found: n'a pas été trouvé dans la base de donnée
category_not_found: n'est pas conforme aux catégories utilisées. Merci de modifier les catégories en utilisant celles listées sur la page d'import ou vérifier qu'il n'y ait pas de faute de frappe ou d'espace à fin du mot.
not_updatable: ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits
values_must_be_same: doit être identique pour les produits avec un nom identique
blank: Champ obligatoire
products_no_permission: vous n'avez pas les droits requis pour gérer les produits de cette entreprise
inventory_no_permission: Vous n'avez pas la permission de créer un catalogue boutique pour ce producteur
@@ -1326,7 +1309,6 @@ fr_CA:
total_by_customer: Total par acheteur
total_by_supplier: Total par producteur
supplier_totals: Totaux Cycle de Vente par Producteur
percentage: "%{value}%"
supplier_totals_by_distributor: Totaux Cycle de Vente par Producteur pour chaque Hub Distributeur
totals_by_supplier: Totaux Cycle de Vente par Hub Distributeur pour chaque Producteur
customer_totals: Totaux Cycle de Vente par Acheteur
@@ -1337,8 +1319,6 @@ fr_CA:
addresses: Adresses
payment_methods: Rapport Méthodes de Paiement
delivery: Rapport de Livraison
sales_tax_totals_by_producer: Détail des montants de taxes par producteur
sales_tax_totals_by_order: Détail des montants de taxes par commande
tax_types: Type de taxe
tax_rates: Taux de taxe
pack_by_customer: Préparation des commandes par Acheteur
@@ -1517,10 +1497,6 @@ fr_CA:
stripe_connect_fail: Désolé, la connexion de votre compte Stripe a échoué :-(
stripe_connect_settings:
resource: Configuration de Stripe Connect
resend_confirmation_emails_feedback:
one: "Emails envoyés pour 1 commande."
many: "Emails envoyés pour %{count} commandes"
other: "Emails envoyés pour %{count} commandes."
api:
unknown_error: "Quelque chose n'a pas fonctionné. Notre équipe a été notifiée."
invalid_api_key: " La clé API (%{key}) n'est pas valide."
@@ -1831,7 +1807,6 @@ fr_CA:
message_html: "Vous avez déjà passé une commande pour ce cycle de vente. Vérifiez votre %{cart} pour voir les produits commandés. Vous pouvez annuler ou modifier votre commande jusqu'à la fermeture du cycle de vente."
step1:
contact_information:
title: Contact
email:
label: Email
phone:
@@ -1885,13 +1860,10 @@ fr_CA:
title: Détails de livraison
edit: Modifier
address: Adresse de livraison
instructions: Instructions
payment_method:
title: Méthode de paiement
edit: Modifier
instructions: Instructions
order:
title: Total commande
edit: Modifier
terms_and_conditions:
message_html: "J'accepte les %{terms_and_conditions_link}"
@@ -1904,7 +1876,6 @@ fr_CA:
submit: Valider ma commande
cancel: Retour à la méthode de paiement
errors:
saving_failed: "La sauvegarde n'a pas fonctionné, veuillez mettre à jour les champs en rouge.%{messages}"
terms_not_accepted: Merci d'accepter les CGU & CGV.
required: Ce champ ne peut pas être vide
invalid_number: "Merci de renseigner un numéro de téléphone valide"
@@ -2605,7 +2576,6 @@ fr_CA:
report_customers_cycle: "Cycle de vente"
report_customers_type: "Type de rapport"
report_customers_csv: "Télécharger en csv"
report_customers: Acheteur
report_producers: "Producteurs"
report_type: "Type de rapport"
report_hubs: "Hubs"
@@ -2687,7 +2657,6 @@ fr_CA:
report_header_tax_on_delivery: "Taxe sur livraison (%{currency_symbol})"
report_header_tax_on_fees: "Taxe sur commission hub (%{currency_symbol})"
report_header_tax_category: "Type de taxe"
report_header_tax_rate_name: "Taxe"
report_header_tax_rate: "Taxe applicable"
report_header_total_tax: "Total Taxe (%{currency_symbol})"
report_header_total_excl_tax: "Total HT (%{currency_symbol})"
@@ -2698,7 +2667,6 @@ fr_CA:
report_header_customer_code: Code acheteur
report_header_product: Produit
report_header_product_properties: Propriétés / labels Produits
report_header_product_tax_category: Taxe applicable
report_header_quantity: Nb commandé
report_header_max_quantity: Quantité Max
report_header_variant: Variante
@@ -2711,8 +2679,6 @@ fr_CA:
report_header_supplier: Fournisseur
report_header_producer: Producteur
report_header_producer_suburb: Ville Producteur
report_header_producer_tax_status: Soumis à la taxe
report_header_producer_charges_sales_tax?: Soumis à la GST
report_header_unit: Unité
report_header_group_buy_unit_quantity: Nb d'unités achetées (vente par lots)
report_header_cost: Coût
@@ -2728,12 +2694,10 @@ fr_CA:
report_header_distributor_address: Adresse Hub Distributeur
report_header_distributor_city: Ville Distributeur
report_header_distributor_postcode: Code Postal Distributeur
report_header_distributor_tax_status: Statut de la boutique
report_header_delivery_address: Adresse Livraison
report_header_delivery_postcode: Code Postal Livraison
report_header_bulk_unit_size: Quantité totale du lot
report_header_weight: Poids
report_header_final_weight_volume: Poids ou volume livré
report_header_height: Hauteur
report_header_width: Largeur
report_header_depth: Profondeur
@@ -2912,7 +2876,6 @@ fr_CA:
deleting_item_will_cancel_order: "Cette opération va rendre une ou plusieurs commandes vides, sans aucun produit. Elles vont ainsi être annulées. Souhaitez-vous continuer ?"
modals:
got_it: "J'ai compris"
confirm: "Valider"
close: "Fermer"
continue: "Suivant"
cancel: "Annuler"
@@ -3469,8 +3432,6 @@ fr_CA:
server_error: "Erreur serveur"
shipping_method_names:
UPS Ground: "UPS Ground"
pick_up: "Retrait"
delivery: "Signé, scellé, livré"
start_date: "Date de début"
successfully_removed: "Supprimé avec succès"
taxonomy_edit: "Modifier la taxonomie"
@@ -3555,7 +3516,6 @@ fr_CA:
display_currency: "Afficher la devise"
choose_currency: "Choisir la devise"
mail_method_settings: "Paramètre méthode mail"
mail_settings_notice_html: "<b>Les modifications apportées ici seront temporaires</b>et peuvent changer à la prochaine mise à jour. <br> Si vous souhaitez réaliser des changements permanents, un administrateur système doit se charger de mettre à jour les informations et provisionner le serveur en utilisant<a href='https://github.com/openfoodfoundation/ofn-install'> ofn-install </a>."
general: "Général"
enable_mail_delivery: "Permettre distribution des mails"
send_mails_as: "Envoyer les mails en tant que"
@@ -3678,7 +3638,6 @@ fr_CA:
messages:
included_price_validation: "Ce cycle de vente a déjà été utilisé par un acheteur et ne peut être supprimé. Pour empêcher aux acheteurs d'y accéder, veuillez plutôt le fermer."
blank: "Champ obligatoire"
invalid_instagram_url: "Uniquement le nom d'utilisateur / identifiant par ex. le_prof"
layouts:
admin:
login_nav:
@@ -3789,7 +3748,6 @@ fr_CA:
print_invoices: "Imprimer les factures"
cancel_orders: "Annuler les commandes"
resend_confirmation: "Renvoyer la confirmation"
resend_confirmation_confirm_html: "Cette action va renvoyer l'email de confirmation de commande. Etes-vous sûr de vouloir continuer ?"
selected:
zero: "Aucune commande sélectionnée"
one: "1 commande sélectionnée"
@@ -3938,7 +3896,6 @@ fr_CA:
title: "Nouveau Produit"
new_product: "Nouveau Produit"
supplier: "Fournisseur"
supplier_select_placeholder: "Sélectionner un producteur"
product_name: "Nom du Produit"
units: "Unité de mesure"
value: "Nb unités"
@@ -3984,9 +3941,6 @@ fr_CA:
select_and_search: "Sélectionner les filtres et cliquez sur %{option} pour accéder aux données."
customer_names_message:
customer_names_tip: "Si les noms des acheteurs sont masqué, vous pouvez contacter le gestionnaire de la boutique. Il pourra vous donner accès à cette information."
products_and_inventory:
all_products:
message: "Attention les stocks correspondent aux stocks producteurs et non au stock du catalogue boutique."
users:
index:
listing_users: "Liste des utilisateurs"
@@ -4350,7 +4304,6 @@ fr_CA:
search_input:
placeholder: Chercher
selector_with_filter:
selected_items: "%{count} sélectionné"
search_placeholder: Chercher
pagination:
next: Suivant

View File

@@ -796,7 +796,7 @@ hu:
order_date: "Elkészült:"
max: "Max"
product_unit: "Termék: Egység"
weight_volume: "Súly/térfogat (kg)"
weight_volume: "Súly/térfogat (g)"
ask: "Kérdez?"
page_title: "Tömeges megrendelés kezelése"
actions_delete: "Kiválasztottak törlése"
@@ -1311,9 +1311,9 @@ hu:
delivery: Kézbesítési jelentés
tax_types: Adótípusok
tax_rates: Adókulcsok
pack_by_customer: Összekészítés Ügyfelenként
pack_by_supplier: Összekészítés Szállítónként
pack_by_product: Összekészítés Termékenként
pack_by_customer: Csomagolás Ügyfél által
pack_by_supplier: Csomagolás Szállító által
pack_by_product: Csomagolás Termékenként
revenues_by_hub:
name: Bevételek Átvételi pont szerint
description: Bevételek átvételi pont szerint
@@ -1355,7 +1355,7 @@ hu:
table:
select_and_search: "Válaszd ki a szűrőket, és kattintson a %{option} elemre az adatok eléréséhez."
headings:
hub: "Átadópont"
hub: "Kerékagy"
customer_code: "Kód"
first_name: "Keresztnév"
last_name: "Vezetéknév"
@@ -1363,8 +1363,8 @@ hu:
product: "Termék"
variant: "Változat"
quantity: "Mennyiség"
is_temperature_controlled: "Hűtést igényel?"
temp_controlled: "Hűtést igényel?"
is_temperature_controlled: "TempControlled?"
temp_controlled: "TempControlled?"
price: "Ár"
rendering_options:
generate_report: "Jelentést készít"
@@ -1376,7 +1376,7 @@ hu:
raw_data: Nyers adatok
formatted_data: Formázott adatok
packing:
name: "Összekészítési jelentések"
name: "Csomagolási jelentések"
subscriptions:
index:
title: "Előfizetések"
@@ -2469,7 +2469,7 @@ hu:
loading_customers: "Ügyfelek betöltése"
no_customers_found: "Nem található ügyfelek"
go: "Megy"
hub: "Átadópont"
hub: "Kerékagy"
producer: "Termelő"
product: "Termék"
price: "Ár"
@@ -2588,7 +2588,7 @@ hu:
report_header_address: Cím
report_header_billing_address: számlázási cím
report_header_relationship: Kapcsolat
report_header_hub: Átadópont
report_header_hub: Kerékagy
report_header_hub_address: Átvételi pont címe
report_header_to_hub: Átvételi ponthoz
report_header_hub_code: Átvételi pont kód
@@ -2706,7 +2706,7 @@ hu:
report_header_sum_max_total: "Összeg Max összesen"
report_header_total_excl_vat: "Összesen, kivéve adó (%{currency_symbol})"
report_header_total_incl_vat: "Összesen, beleértve adó (%{currency_symbol})"
report_header_temp_controlled: Hűtést igényel?
report_header_temp_controlled: TempControlled?
report_header_is_producer: Termelő?
report_header_not_confirmed: Nem megerősített
report_header_gst_on_income: GST a bevételekre
@@ -2831,7 +2831,7 @@ hu:
error: Hiba
unavailable: Nem érhető el
profile: Profil
hub: Átadópont
hub: Kerékagy
shop: Átadópont
choose: Választ
resolve_errors: Kérjük, oldja meg a következő hibákat

View File

@@ -32,9 +32,7 @@ Spree::Core::Engine.routes.draw do
put '/password/change' => 'user_passwords#update', :as => :update_password
end
resource :account, :controller => 'users' do
resources :webhook_endpoints, only: [:create, :destroy], controller: '/webhook_endpoints'
end
resource :account, :controller => 'users'
match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management", via: :get
match '/admin/payment_methods/show_provider_preferences' => 'admin/payment_methods#show_provider_preferences', :via => :get

View File

@@ -15,7 +15,5 @@
every: "5m"
SubscriptionConfirmJob:
every: "5m"
OrderCycleOpenedJob:
every: "5m"
OrderCycleClosingJob:
every: "5m"

View File

@@ -2,7 +2,4 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
const config = environment.toWebpackConfig();
config.output.filename = "js/[name]-[hash].js";
module.exports = environment.toWebpackConfig()

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
class AddOpenedAtToOrderCycle < ActiveRecord::Migration[6.1]
def change
add_column :order_cycles, :opened_at, :timestamp
end
end

View File

@@ -1,11 +0,0 @@
# frozen_string_literal: true
class CreateWebhookEndpoints < ActiveRecord::Migration[6.1]
def change
create_table :webhook_endpoints do |t|
t.string :url, null: false
t.timestamps null: false
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: true
class AddSpreeUserReferenceToWebhookEndpoint < ActiveRecord::Migration[6.1]
def change
add_column :webhook_endpoints, :user_id, :bigint, default: 0, null: false
add_index :webhook_endpoints, :user_id
add_foreign_key :webhook_endpoints, :spree_users, column: :user_id
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[6.1].define(version: 2023_02_13_160135) do
ActiveRecord::Schema.define(version: 2023_02_13_160135) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
@@ -310,7 +310,6 @@ ActiveRecord::Schema[6.1].define(version: 2023_02_13_160135) do
t.datetime "processed_at"
t.boolean "automatic_notifications", default: false
t.boolean "mails_sent", default: false
t.datetime "opened_at"
end
create_table "order_cycles_distributor_payment_methods", id: false, force: :cascade do |t|
@@ -1190,14 +1189,6 @@ ActiveRecord::Schema[6.1].define(version: 2023_02_13_160135) do
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
end
create_table "webhook_endpoints", force: :cascade do |t|
t.string "url", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.bigint "user_id", default: 0, null: false
t.index ["user_id"], name: "index_webhook_endpoints_on_user_id"
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk"
@@ -1302,5 +1293,4 @@ ActiveRecord::Schema[6.1].define(version: 2023_02_13_160135) do
add_foreign_key "subscriptions", "spree_shipping_methods", column: "shipping_method_id", name: "subscriptions_shipping_method_id_fk"
add_foreign_key "variant_overrides", "enterprises", column: "hub_id", name: "variant_overrides_hub_id_fk"
add_foreign_key "variant_overrides", "spree_variants", column: "variant_id", name: "variant_overrides_variant_id_fk"
add_foreign_key "webhook_endpoints", "spree_users", column: "user_id"
end

View File

@@ -42,7 +42,7 @@ module DfcProvider
end
def authorization_control
AuthorizationControl.new(request)
DfcProvider::AuthorizationControl.new(request)
end
def not_found

View File

@@ -22,7 +22,7 @@ module DfcProvider
def variant
@variant ||=
VariantFetcher.new(current_enterprise).scope.find(params[:id])
DfcProvider::VariantFetcher.new(current_enterprise).scope.find(params[:id])
end
end
end

View File

@@ -21,7 +21,7 @@ module DfcProvider
def variant
@variant ||=
VariantFetcher.new(current_enterprise).scope.find(params[:id])
DfcProvider::VariantFetcher.new(current_enterprise).scope.find(params[:id])
end
end
end

View File

@@ -32,11 +32,11 @@ module DfcProvider
end
def supplies
VariantFetcher.new(object).scope
DfcProvider::VariantFetcher.new(object).scope
end
def manages
VariantFetcher.new(object).scope
DfcProvider::VariantFetcher.new(object).scope
end
end
end

View File

@@ -1,54 +0,0 @@
# frozen_string_literal: true
# Service used to authorize the user on DCF Provider API
# It controls an OICD Access token and an enterprise.
class AuthorizationControl
# Copied from: https://login.lescommuns.org/auth/realms/data-food-consortium/
LES_COMMUNES_PUBLIC_KEY = <<~KEY
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl68JGqAILFzoi/1+6siXXp2vylu+7mPjYKjKelTtHFYXWVkbmVptCsamHlY3jRhqSQYe6M1SKfw8D+uXrrWsWficYvpdlV44Vm7uETZOr1/XBOjpWOi1vLmBVtX6jFeqN1BxfE1PxLROAiGn+MeMg90AJKShD2c5RoNv26e20dgPhshRVFPUGru+0T1RoKyIa64z/qcTcTVD2V7KX+ANMweRODdoPAzQFGGjTnL1uUqIdUwSfHSpXYnKxXOsnPC3Mowkv8UIGWWDxS/yzhWc7sOk1NmC7pb+Cg7G8NKj+Pp9qQZnXF39Dg95ZsxJrl6fyPFvTo3zf9CPG/fUM1CkkwIDAQAB
-----END PUBLIC KEY-----
KEY
def self.public_key
OpenSSL::PKey::RSA.new(LES_COMMUNES_PUBLIC_KEY)
end
def initialize(request)
@request = request
end
def user
oidc_user || ofn_user
rescue JWT::ExpiredSignature
nil
end
private
def oidc_user
find_ofn_user(decode_token) if access_token
end
def ofn_user
@request.env['warden']&.user
end
def decode_token
JWT.decode(
access_token,
self.class.public_key,
true, { algorithm: "RS256" }
).first
end
def access_token
@request.headers['Authorization'].to_s.split(' ').last
end
def find_ofn_user(payload)
return if payload["email"].blank?
Spree::User.find_by(uid: payload["email"])
end
end

View File

@@ -0,0 +1,56 @@
# frozen_string_literal: true
# Service used to authorize the user on DCF Provider API
# It controls an OICD Access token and an enterprise.
module DfcProvider
class AuthorizationControl
# Copied from: https://login.lescommuns.org/auth/realms/data-food-consortium/
LES_COMMUNES_PUBLIC_KEY = <<~KEY
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl68JGqAILFzoi/1+6siXXp2vylu+7mPjYKjKelTtHFYXWVkbmVptCsamHlY3jRhqSQYe6M1SKfw8D+uXrrWsWficYvpdlV44Vm7uETZOr1/XBOjpWOi1vLmBVtX6jFeqN1BxfE1PxLROAiGn+MeMg90AJKShD2c5RoNv26e20dgPhshRVFPUGru+0T1RoKyIa64z/qcTcTVD2V7KX+ANMweRODdoPAzQFGGjTnL1uUqIdUwSfHSpXYnKxXOsnPC3Mowkv8UIGWWDxS/yzhWc7sOk1NmC7pb+Cg7G8NKj+Pp9qQZnXF39Dg95ZsxJrl6fyPFvTo3zf9CPG/fUM1CkkwIDAQAB
-----END PUBLIC KEY-----
KEY
def self.public_key
OpenSSL::PKey::RSA.new(LES_COMMUNES_PUBLIC_KEY)
end
def initialize(request)
@request = request
end
def user
oidc_user || ofn_user
rescue JWT::ExpiredSignature
nil
end
private
def oidc_user
find_ofn_user(decode_token) if access_token
end
def ofn_user
@request.env['warden']&.user
end
def decode_token
JWT.decode(
access_token,
self.class.public_key,
true, { algorithm: "RS256" }
).first
end
def access_token
@request.headers['Authorization'].to_s.split(' ').last
end
def find_ofn_user(payload)
return if payload["email"].blank?
Spree::User.find_by(uid: payload["email"])
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
# Service used to fetch variants related to an entreprise.
# It improves maintenance as it is the central point requesting
# Spree::Varaint inside the DfcProvider engine.
module DfcProvider
class VariantFetcher
def initialize(enterprise)
@enterprise = enterprise
end
def scope
Spree::Variant.
joins(product: :supplier).
where('enterprises.id' => @enterprise.id)
end
end
end

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
# Service used to fetch variants related to an enterprise.
# It improves maintenance as it is the central point requesting
# Spree::Variant inside the DfcProvider engine.
class VariantFetcher
def initialize(enterprise)
@enterprise = enterprise
end
def scope
Spree::Variant.not_master.
joins(:product).
where(spree_products: { supplier: @enterprise })
end
end

View File

@@ -17,7 +17,7 @@ describe DfcProvider::EnterprisesController, type: :controller do
context 'with an authenticated user' do
before do
allow_any_instance_of(AuthorizationControl)
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:user)
.and_return(user)
end

View File

@@ -15,7 +15,7 @@ describe DfcProvider::PersonsController, type: :controller do
context 'with an authenticated user' do
before do
allow_any_instance_of(AuthorizationControl)
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:user)
.and_return(user)
end

View File

@@ -20,7 +20,7 @@ describe DfcProvider::SuppliedProductsController, type: :controller do
context 'with an authenticated user' do
before do
allow_any_instance_of(AuthorizationControl)
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:user)
.and_return(user)
end

View File

@@ -2,7 +2,7 @@
require DfcProvider::Engine.root.join("spec/spec_helper")
describe AuthorizationControl do
describe DfcProvider::AuthorizationControl do
include AuthorizationHelper
let(:user) { create(:oidc_user) }

View File

@@ -1,26 +0,0 @@
# frozen_string_literal: true
require DfcProvider::Engine.root.join("spec/spec_helper")
describe VariantFetcher do
subject { VariantFetcher.new(enterprise) }
let(:enterprise) { build(:enterprise) }
let(:other_enterprise) { build(:enterprise) }
it "returns an empty set" do
expect(subject.scope).to eq []
end
it "returns the variants of a supplier" do
product = create(:product, supplier: enterprise)
expect(subject.scope.count).to eq 1
expect(subject.scope).to eq product.variants
end
it "ignores the variants of another enterprise" do
create(:product, supplier: other_enterprise)
expect(subject.scope).to eq []
end
end

View File

@@ -8,7 +8,7 @@ module AuthorizationHelper
def allow_token_for(payload)
private_key = OpenSSL::PKey::RSA.generate 2048
allow(AuthorizationControl).to receive(:public_key).
allow(DfcProvider::AuthorizationControl).to receive(:public_key).
and_return(private_key.public_key)
JWT.encode(payload, private_key, "RS256")

View File

@@ -4,10 +4,10 @@
%strong
= cookie_name
%span /
{{ 'legal.cookies_policy.cookie_domain' | t }}
= t 'legal.cookies_policy.cookie_domain'
- if cookie_domain
= cookie_domain
- else
= "{{ instance_hostname }}"
%p
= "{{ '#{cookie_desc}' | t }}"
= cookie_desc

View File

@@ -1,87 +1,87 @@
%h2
{{ 'legal.cookies_policy.header' | t }}
= t 'legal.cookies_policy.header'
%p
{{ 'legal.cookies_policy.desc_part_1' | t }}
= t 'legal.cookies_policy.desc_part_1'
%p
{{ 'legal.cookies_policy.desc_part_2' | t }}
= t 'legal.cookies_policy.desc_part_2'
%p
{{ 'legal.cookies_policy.desc_part_3' | t }}
= t 'legal.cookies_policy.desc_part_3'
%h2
{{ 'legal.cookies_policy.essential_cookies' | t }}
= t 'legal.cookies_policy.essential_cookies'
%p
{{ 'legal.cookies_policy.essential_cookies_desc' | t }}
= t 'legal.cookies_policy.essential_cookies_desc'
%table{ng: { controller:"CookiesPolicyModalCtrl"}}
= render_cookie_entry( "_ofn_session_id", "legal.cookies_policy.cookie_session_desc" )
= render_cookie_entry( "cookies_consent", "legal.cookies_policy.cookie_consent_desc" )
= render_cookie_entry( "remember_spree_user_token", "legal.cookies_policy.cookie_remember_me_desc" )
= render_cookie_entry( "qos_token", "legal.cookies_policy.cookie_openstreemap_desc", "openstreetmap.org" )
= render_cookie_entry( "_ofn_session_id", t( "legal.cookies_policy.cookie_session_desc" ) )
= render_cookie_entry( "cookies_consent", t( "legal.cookies_policy.cookie_consent_desc" ) )
= render_cookie_entry( "remember_spree_user_token", t( "legal.cookies_policy.cookie_remember_me_desc" ) )
= render_cookie_entry( "qos_token", t( "legal.cookies_policy.cookie_openstreemap_desc" ), "openstreetmap.org" )
%tr
%td
%p
%strong m
%span /
{{ 'legal.cookies_policy.cookie_domain' | t }}
= t 'legal.cookies_policy.cookie_domain'
= "m.stripe.com"
%p
%strong nsr
%span /
{{ 'legal.cookies_policy.cookie_domain' | t }}
= t 'legal.cookies_policy.cookie_domain'
= "m.stripe.network"
%p
%strong __stripe_sid
%span /
{{ 'legal.cookies_policy.cookie_domain' | t }}
= t 'legal.cookies_policy.cookie_domain'
= "{{ instance_hostname }}"
%p
%strong __stripe_mid
%span /
{{ 'legal.cookies_policy.cookie_domain' | t }}
= t 'legal.cookies_policy.cookie_domain'
= "{{ instance_hostname }}"
%p
{{ 'legal.cookies_policy.cookie_stripe_desc' | t }}
= t 'legal.cookies_policy.cookie_stripe_desc'
%p
{{ 'legal.cookies_policy.essential_cookies_note' | t }}
= t 'legal.cookies_policy.essential_cookies_note'
- if Spree::Config.cookies_policy_matomo_section
%h2
{{ 'legal.cookies_policy.statistics_cookies' | t }}
= t 'legal.cookies_policy.statistics_cookies'
%p
{{ 'legal.cookies_policy.statistics_cookies_desc' | t }}
= t 'legal.cookies_policy.statistics_cookies_desc'
%p
{{ 'legal.cookies_policy.statistics_cookies_matomo_desc_html' | t }}
= t 'legal.cookies_policy.statistics_cookies_matomo_desc_html'
%table{ng: { controller:"CookiesPolicyModalCtrl"}}
= render_cookie_entry( "_pk_ref, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_basics_desc" )
= render_cookie_entry( "_pk_hsr, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_heatmap_desc" )
= render_cookie_entry( "piwik_ignore, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_ignore_desc" )
= render_cookie_entry( "_pk_ref, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_basics_desc" ) )
= render_cookie_entry( "_pk_hsr, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_heatmap_desc" ) )
= render_cookie_entry( "piwik_ignore, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_ignore_desc" ) )
- if Spree::Config.matomo_url.present?
%p
{{ 'legal.cookies_policy.statistics_cookies_matomo_optout' | t }}
= t 'legal.cookies_policy.statistics_cookies_matomo_optout'
%p
%iframe{ src: matomo_iframe_src }
%h2
{{ 'legal.cookies_policy.disabling_cookies_header' | t }}
= t 'legal.cookies_policy.disabling_cookies_header'
%p
{{ 'legal.cookies_policy.disabling_cookies_desc' | t }}
= t 'legal.cookies_policy.disabling_cookies_desc'
%ul
%li
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_firefox_link' | t }}", target: "_blank" }
%a{ href: t( 'legal.cookies_policy.disabling_cookies_firefox_link' ), target: "_blank" }
Firefox
%li
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_chrome_link' | t }}", target: "_blank" }
%a{ href: t( 'legal.cookies_policy.disabling_cookies_chrome_link' ), target: "_blank" }
Chrome
%li
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_ie_link' | t }}", target: "_blank" }
%a{ href: t( 'legal.cookies_policy.disabling_cookies_ie_link' ), target: "_blank" }
Internet Explorer
%li
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_safari_link' | t }}", target: "_blank" }
%a{ href: t( 'legal.cookies_policy.disabling_cookies_safari_link' ), target: "_blank" }
Safari
%p
{{ 'legal.cookies_policy.disabling_cookies_note' | t }}
= t 'legal.cookies_policy.disabling_cookies_note'
%a.close-reveal-modal{"ng-click" => "$close()"}
%i.ofn-i_009-close

View File

@@ -13,8 +13,10 @@ module OpenFoodNetwork
@customer_tags = customer_tags || []
end
def filter(subject)
subject.to_a.reject do |element|
def filter!(subject)
return unless subject.respond_to?(:any?) && subject.any?
subject.to_a.reject! do |element|
if rule_class.respond_to?(:tagged_children_for)
children = rule_class.tagged_children_for(element)
children.reject! { |child| reject?(child) }

View File

@@ -10,13 +10,7 @@ module Reporting
@report = report
end
# Strip header and summary rows for these formats
def raw_render?
@report.params[:report_format].in?(['csv'])
end
# Do not format values for these output formats
def unformatted_render?
@report.params[:report_format].in?(['json', 'csv'])
end

View File

@@ -30,7 +30,7 @@ module Reporting
def slice_and_format_row(row)
result = row.to_h.select { |k, _v| k.in?(report.fields_to_show) }
unless report.unformatted_render?
unless report.raw_render?
result = result.map { |k, v| [k, format_cell(v, k)] }.to_h
end
OpenStruct.new(result)
@@ -47,17 +47,12 @@ module Reporting
proc_args = [group_value, datas.map(&:item), datas.map(&:full_row)]
row = rule[:summary_row].call(*proc_args)
row = add_summary_row_type(row)
row = slice_and_format_row(OpenStruct.new(row.reverse_merge!(blank_row)))
add_summary_row_label(row, rule, proc_args)
end
private
def add_summary_row_type(row)
row.reverse_merge!({ report_row_type: "summary" })
end
def add_summary_row_label(row, rule, proc_args)
previous_key = nil
label = rule[:summary_row_label]

View File

@@ -6,8 +6,7 @@ module Reporting
attr_accessor :user, :params, :ransack_params
delegate :render_as, :as_json, :to_html, :to_csv, :to_xlsx, :to_pdf, :to_json, to: :renderer
delegate :unformatted_render?, :html_render?, :display_header_row?, :display_summary_row?,
to: :renderer
delegate :raw_render?, :html_render?, :display_header_row?, :display_summary_row?, to: :renderer
delegate :rows, :table_rows, :grouped_data, to: :rows_builder
delegate :available_headers, :table_headers, :fields_to_hide, :fields_to_show,

View File

@@ -20,7 +20,7 @@
},
"dependencies": {
"@babel/preset-env": "^7.18.2",
"@floating-ui/dom": "^1.2.5",
"@floating-ui/dom": "^1.2.4",
"@hotwired/turbo": "^7.3.0",
"@rails/webpacker": "5.4.4",
"babel-loader": "^8.2.3",
@@ -38,19 +38,20 @@
"stimulus-flatpickr": "^1.4.0",
"stimulus_reflex": "3.5.0-pre9",
"tom-select": "^2.0.0",
"webpack": "~4",
"webpack-cli": "~4",
"webpack-dev-server": "~4"
"webpack": "~4"
},
"devDependencies": {
"@webpack-cli/serve": "*",
"husky": "^8.0.0",
"jasmine-core": "~4.6.0",
"jasmine-core": "~4.5.0",
"jest": "^27.4.7",
"karma": "~6.4.1",
"karma-chrome-launcher": "~3.1.0",
"karma-coffee-preprocessor": "~1.0.1",
"karma-jasmine": "~0.3.8",
"prettier": "2.8.6",
"pretty-quick": "^3.1.3"
"prettier": "2.8.4",
"pretty-quick": "^3.1.3",
"webpack-cli": "~3",
"webpack-dev-server": "~3"
}
}

View File

@@ -1,21 +0,0 @@
#!/bin/bash
#
# Install our selected Node version defined in the .node-version file.
#
# If our node-build version is outdated and it can't build the version we want
# then we try upgrading node-build and installing again.
if nodenv install --skip-existing; then
echo "Correct Node version is installed."
else
echo "Upgrading node-build:"
if command -v brew &> /dev/null; then
# Installation via Homebrew is recommended on macOS.
brew upgrade node-build
else
git -C "$(nodenv root)"/plugins/node-build pull
fi
nodenv install
fi

View File

@@ -45,7 +45,7 @@ module Admin
it 'calls CustomersWithBalance' do
customers_with_balance = instance_double(CustomersWithBalance)
allow(CustomersWithBalance)
.to receive(:new).with(Customer.of(enterprise)) { customers_with_balance }
.to receive(:new).with(enterprise) { customers_with_balance }
expect(customers_with_balance).to receive(:query) { Customer.none }

View File

@@ -122,8 +122,10 @@ describe Admin::SchedulesController, type: :controller do
spree_put :update, format: :json, id: coordinated_schedule.id,
order_cycle_ids: [coordinated_order_cycle.id, coordinated_order_cycle2.id]
reset_controller_environment
spree_put :update, format: :json, id: coordinated_schedule.id,
order_cycle_ids: [coordinated_order_cycle.id]
reset_controller_environment
spree_put :update, format: :json, id: coordinated_schedule.id,
order_cycle_ids: [coordinated_order_cycle.id]
end

View File

@@ -1,60 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe ExtraFields do
let(:dummy_controller) { Api::V1::BaseController.new.extend ExtraFields }
describe "#invalid_query_param" do
it "renders error" do
allow(dummy_controller).to receive(:render) {}
dummy_controller.invalid_query_param("param", :unprocessable_entity, "error message")
expect(dummy_controller).to have_received(:render).with(
json:
{
errors:
[{
code: 422,
detail: "error message",
source: { parameter: "param" },
status: :unprocessable_entity,
title: "Invalid query parameter"
}]
},
status: :unprocessable_entity
)
end
end
describe "#extra_fields" do
context "when fields present and available" do
it "returns extra fields" do
allow(dummy_controller).to receive(:params).
and_return({ extra_fields: { customer: "balance" } })
expect(dummy_controller.extra_fields(:customer, [:balance])).to eq([:balance])
end
end
context "when fields missing" do
it "returns empty arr" do
allow(dummy_controller).to receive(:params).and_return({})
expect(dummy_controller.extra_fields(:customer, [:balance])).to eq([])
end
end
context "when fields not in available fields" do
it "calls invalid_query_param" do
allow(dummy_controller).to receive(:invalid_query_param) {}
allow(dummy_controller).to receive(:params).
and_return({ extra_fields: { customer: "unknown" } })
dummy_controller.extra_fields(:customer, [:balance])
expect(dummy_controller).to have_received(:invalid_query_param).with(
"extra_fields[customer]",
:unprocessable_entity,
"Unsupported fields: unknown"
)
end
end
end
end

View File

@@ -23,9 +23,11 @@ module Spree
expect(return_authorization.amount.to_s).to eq "20.2"
expect(return_authorization.reason.to_s).to eq "broken"
# Reset the test controller between requests
reset_controller_environment
# Update return authorization
spree_put :update, order_id: order.number,
id: return_authorization.id,
spree_put :update, id: return_authorization.id,
return_authorization: { amount: "10.2", reason: "half broken" }
expect(response).to redirect_to spree.admin_order_return_authorizations_url(order.number)

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe Spree::Admin::TaxonsController do
render_views
let(:taxonomy) { create(:taxonomy) }
let(:taxon) { create(:taxon, name: "Ruby", taxonomy: taxonomy) }
let(:taxon2) { create(:taxon, name: "Rails", taxonomy: taxonomy) }
before do
allow(controller).to receive(:spree_current_user) { current_api_user }
taxonomy.root.children << taxon
taxonomy.root.children << taxon2
end
context "as an admin" do
let(:current_api_user) { build(:admin_user) }
it "can reorder taxons" do
spree_post :update,
taxonomy_id: taxonomy.id,
id: taxon2.id,
taxon: { parent_id: taxonomy.root.id, position: 0 }
expect(taxon2.reload.lft).to eq 2
expect(Spree::Taxonomy.find(taxonomy.id).root.children.first).to eq(taxon2)
end
end
end

View File

@@ -1,67 +0,0 @@
# frozen_string_literal: false
require 'spec_helper'
require 'open_food_network/order_cycle_permissions'
describe WebhookEndpointsController, type: :controller do
let(:user) { create(:admin_user) }
before { allow(controller).to receive(:spree_current_user) { user } }
describe "#create" do
it "creates a webhook_endpoint" do
expect {
spree_post :create, { url: "https://url" }
}.to change {
user.webhook_endpoints.count
}.by(1)
expect(flash[:success]).to be_present
expect(flash[:error]).to be_blank
expect(user.webhook_endpoints.first.url).to eq "https://url"
end
it "shows error if parameters not specified" do
expect {
spree_post :create, { url: "" }
}.to_not change {
user.webhook_endpoints.count
}
expect(flash[:success]).to be_blank
expect(flash[:error]).to be_present
end
it "redirects back to referrer" do
spree_post :create, { url: "https://url" }
expect(response).to redirect_to "/account#/developer_settings"
end
end
describe "#destroy" do
let!(:webhook_endpoint) { user.webhook_endpoints.create(url: "https://url") }
it "destroys a webhook_endpoint" do
webhook_endpoint2 = user.webhook_endpoints.create!(url: "https://url2")
expect {
spree_delete :destroy, { id: webhook_endpoint.id }
}.to change {
user.webhook_endpoints.count
}.by(-1)
expect(flash[:success]).to be_present
expect(flash[:error]).to be_blank
expect{ webhook_endpoint.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(webhook_endpoint2.reload).to be_present
end
it "redirects back to developer settings tab" do
spree_delete :destroy, id: webhook_endpoint.id
expect(response).to redirect_to "/account#/developer_settings"
end
end
end

Some files were not shown because too many files have changed in this diff Show More