mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-13 18:46:49 +00:00
Compare commits
138 Commits
RachL-patc
...
v5.0.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d0bcfa06a | ||
|
|
9bfac66412 | ||
|
|
58c39166e1 | ||
|
|
bf41658d32 | ||
|
|
88837b55b9 | ||
|
|
9c0a15f431 | ||
|
|
fcbaefb2c8 | ||
|
|
9ca1b48d2e | ||
|
|
e76d6ad3df | ||
|
|
e1febc6e00 | ||
|
|
7d27f46d68 | ||
|
|
7d7253cf0e | ||
|
|
4fa4eb1b4e | ||
|
|
d16cd8c84e | ||
|
|
9870abfb1c | ||
|
|
141000f0df | ||
|
|
7302c2d161 | ||
|
|
604a47bd96 | ||
|
|
241a6d8128 | ||
|
|
bb70d21a35 | ||
|
|
626a269cf8 | ||
|
|
302336ab02 | ||
|
|
ee2a6bf2e6 | ||
|
|
7ca5927411 | ||
|
|
2926a9662c | ||
|
|
118ed915dc | ||
|
|
6e40e4da60 | ||
|
|
693bef1e7a | ||
|
|
42940f4729 | ||
|
|
f2da3bb11c | ||
|
|
f8003b00db | ||
|
|
521b72a6c9 | ||
|
|
aa4552aac4 | ||
|
|
93a3130851 | ||
|
|
f3a30f94db | ||
|
|
16cae2dbcc | ||
|
|
4c71ea3866 | ||
|
|
bc970927a5 | ||
|
|
c331d57cdb | ||
|
|
5845fee663 | ||
|
|
de938f6f10 | ||
|
|
697f430156 | ||
|
|
c41c15b895 | ||
|
|
af200ab4a0 | ||
|
|
4c6c1eedb1 | ||
|
|
bbdee7c0f3 | ||
|
|
11959515b8 | ||
|
|
cb781536b6 | ||
|
|
5719d0682d | ||
|
|
c4c95d472e | ||
|
|
3e7f61c4d1 | ||
|
|
db76cd1659 | ||
|
|
e791184468 | ||
|
|
23287573f4 | ||
|
|
925ac2ea6a | ||
|
|
355c9686e3 | ||
|
|
58d174fad9 | ||
|
|
d90c4f6aed | ||
|
|
f5b9ca361c | ||
|
|
16d6e1f935 | ||
|
|
1e6fbadd8b | ||
|
|
d102652c03 | ||
|
|
1b50217242 | ||
|
|
73819a4638 | ||
|
|
9ab2a3ae3d | ||
|
|
d413a142c9 | ||
|
|
48ad7ed8a0 | ||
|
|
a2c4c44eea | ||
|
|
e7ece294cc | ||
|
|
d7313ffec9 | ||
|
|
b42cba8c37 | ||
|
|
7726c7d129 | ||
|
|
d4d995851f | ||
|
|
12cf62c2ff | ||
|
|
0569b30e0d | ||
|
|
9f3da1af4f | ||
|
|
afb336d789 | ||
|
|
92c4cb9b7f | ||
|
|
724d5a2ca0 | ||
|
|
6251814152 | ||
|
|
cf13dc2ff6 | ||
|
|
4906c19c8e | ||
|
|
129ccc33f8 | ||
|
|
9399c7e129 | ||
|
|
c17eddd69b | ||
|
|
b30096317c | ||
|
|
c89b4fb86b | ||
|
|
3a367ceb6e | ||
|
|
f9fb7bf399 | ||
|
|
0f9b933117 | ||
|
|
7cbe77668a | ||
|
|
479eacc956 | ||
|
|
e7213dba68 | ||
|
|
078e191d26 | ||
|
|
b554eda7c7 | ||
|
|
477447ad92 | ||
|
|
039399ee37 | ||
|
|
d36438037a | ||
|
|
5b58f7b20e | ||
|
|
02e2214caa | ||
|
|
0ec8d13641 | ||
|
|
151fc7bf85 | ||
|
|
15c920c911 | ||
|
|
b4aaa0fae1 | ||
|
|
efe0a2a701 | ||
|
|
3f905cce16 | ||
|
|
5c5213e872 | ||
|
|
3a7aed154c | ||
|
|
60ace5d3ff | ||
|
|
1dec3debe1 | ||
|
|
711f37bce1 | ||
|
|
a493d70f5c | ||
|
|
c0887b1806 | ||
|
|
3d09ac01cc | ||
|
|
3a3d729dcb | ||
|
|
283706114e | ||
|
|
7ca544540b | ||
|
|
b1b4b10417 | ||
|
|
3b83200a14 | ||
|
|
7cd8311dcb | ||
|
|
14e7c57102 | ||
|
|
9f859f420d | ||
|
|
af33fc357e | ||
|
|
6a8cc410d2 | ||
|
|
61e7c1db07 | ||
|
|
0d8df5d2a8 | ||
|
|
73a1698aad | ||
|
|
97d41c230e | ||
|
|
1b4efd2164 | ||
|
|
1e13005fb5 | ||
|
|
7a5074cc90 | ||
|
|
41ffe848ed | ||
|
|
0797314360 | ||
|
|
3302f0e78d | ||
|
|
bafb881c46 | ||
|
|
32ab821839 | ||
|
|
008d764c34 | ||
|
|
9c51615b03 |
@@ -34,12 +34,6 @@ Lint/EmptyClass:
|
||||
Exclude:
|
||||
- 'spec/lib/reports/report_loader_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: AllowComments.
|
||||
Lint/EmptyFile:
|
||||
Exclude:
|
||||
- 'spec/lib/open_food_network/enterprise_injection_data_spec.rb'
|
||||
|
||||
# Offense count: 2
|
||||
Lint/FloatComparison:
|
||||
Exclude:
|
||||
@@ -92,7 +86,6 @@ Metrics/AbcSize:
|
||||
- 'app/controllers/admin/enterprises_controller.rb'
|
||||
- 'app/controllers/payment_gateways/paypal_controller.rb'
|
||||
- 'app/controllers/spree/admin/payments_controller.rb'
|
||||
- 'app/controllers/spree/admin/taxons_controller.rb'
|
||||
- 'app/controllers/spree/admin/variants_controller.rb'
|
||||
- 'app/controllers/spree/orders_controller.rb'
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
@@ -127,7 +120,7 @@ Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
|
||||
# Offense count: 46
|
||||
# Offense count: 47
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Exclude:
|
||||
@@ -137,6 +130,7 @@ Metrics/ClassLength:
|
||||
- 'app/controllers/admin/resource_controller.rb'
|
||||
- 'app/controllers/admin/subscriptions_controller.rb'
|
||||
- 'app/controllers/application_controller.rb'
|
||||
- 'app/controllers/checkout_controller.rb'
|
||||
- 'app/controllers/payment_gateways/paypal_controller.rb'
|
||||
- 'app/controllers/spree/admin/orders_controller.rb'
|
||||
- 'app/controllers/spree/admin/payment_methods_controller.rb'
|
||||
@@ -183,7 +177,7 @@ Metrics/ClassLength:
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/enterprises_controller.rb'
|
||||
- 'app/controllers/spree/admin/taxons_controller.rb'
|
||||
- 'app/controllers/spree/admin/payments_controller.rb'
|
||||
- 'app/controllers/spree/orders_controller.rb'
|
||||
- 'app/helpers/checkout_helper.rb'
|
||||
- 'app/helpers/order_cycles_helper.rb'
|
||||
@@ -208,13 +202,12 @@ Metrics/CyclomaticComplexity:
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'spec/models/product_importer_spec.rb'
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 23
|
||||
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||
Metrics/MethodLength:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/enterprises_controller.rb'
|
||||
- 'app/controllers/payment_gateways/paypal_controller.rb'
|
||||
- 'app/controllers/spree/admin/taxons_controller.rb'
|
||||
- 'app/controllers/spree/orders_controller.rb'
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
@@ -293,19 +286,17 @@ Metrics/ParameterLists:
|
||||
- 'spec/support/controller_requests_helper.rb'
|
||||
- 'spec/system/admin/reports_spec.rb'
|
||||
|
||||
# Offense count: 4
|
||||
# Offense count: 3
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
||||
Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
- 'app/controllers/spree/admin/taxons_controller.rb'
|
||||
- 'app/models/enterprise_relationship.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
|
||||
# Offense count: 8
|
||||
# Offense count: 7
|
||||
Naming/AccessorMethodName:
|
||||
Exclude:
|
||||
- 'app/controllers/spree/admin/taxonomies_controller.rb'
|
||||
- 'app/mailers/producer_mailer.rb'
|
||||
- 'app/models/spree/order.rb'
|
||||
- 'app/services/checkout/post_checkout_actions.rb'
|
||||
@@ -353,7 +344,7 @@ Naming/VariableNumber:
|
||||
- 'spec/models/spree/tax_rate_spec.rb'
|
||||
- 'spec/requests/api/orders_spec.rb'
|
||||
|
||||
# Offense count: 142
|
||||
# Offense count: 143
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: ResponseMethods.
|
||||
# ResponseMethods: response, last_response
|
||||
@@ -557,7 +548,7 @@ RSpecRails/InferredSpecType:
|
||||
- 'spec/requests/voucher_adjustments_spec.rb'
|
||||
- 'spec/routing/stripe_spec.rb'
|
||||
|
||||
# Offense count: 22
|
||||
# Offense count: 21
|
||||
# Configuration parameters: IgnoreScopes, Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/InverseOf:
|
||||
@@ -572,7 +563,6 @@ Rails/InverseOf:
|
||||
- 'app/models/spree/price.rb'
|
||||
- 'app/models/spree/product.rb'
|
||||
- 'app/models/spree/stock_item.rb'
|
||||
- 'app/models/spree/taxonomy.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/subscription_line_item.rb'
|
||||
|
||||
@@ -720,7 +710,7 @@ Style/GlobalStdStream:
|
||||
- 'lib/tasks/subscriptions/debug.rake'
|
||||
- 'lib/tasks/subscriptions/test.rake'
|
||||
|
||||
# Offense count: 12
|
||||
# Offense count: 10
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: AllowSplatArgument.
|
||||
Style/HashConversion:
|
||||
@@ -728,9 +718,7 @@ Style/HashConversion:
|
||||
- 'app/controllers/admin/column_preferences_controller.rb'
|
||||
- 'app/controllers/admin/variant_overrides_controller.rb'
|
||||
- 'app/controllers/spree/admin/products_controller.rb'
|
||||
- 'app/models/order_cycle.rb'
|
||||
- 'app/models/product_import/product_importer.rb'
|
||||
- 'app/models/spree/shipping_method.rb'
|
||||
- 'app/serializers/api/admin/exchange_serializer.rb'
|
||||
- 'app/services/variants_stock_levels.rb'
|
||||
- 'spec/controllers/admin/inventory_items_controller_spec.rb'
|
||||
|
||||
@@ -89,4 +89,4 @@ RUN ./script/install-bundler
|
||||
RUN yarn install
|
||||
|
||||
# Run bundler install in parallel with the amount of available CPUs
|
||||
RUN bundle install --jobs="$(nproc)"
|
||||
RUN bundle install --jobs="$(nproc)"
|
||||
2
Gemfile
2
Gemfile
@@ -86,7 +86,7 @@ gem "active_model_serializers", "0.8.4"
|
||||
gem 'activerecord-session_store'
|
||||
gem 'acts-as-taggable-on'
|
||||
gem 'angularjs-file-upload-rails', '~> 2.4.1'
|
||||
gem 'bigdecimal', '3.0.2'
|
||||
gem 'bigdecimal'
|
||||
gem 'bootsnap', require: false
|
||||
gem 'geocoder'
|
||||
gem 'gmaps4rails'
|
||||
|
||||
@@ -180,7 +180,7 @@ GEM
|
||||
base64 (0.2.0)
|
||||
bcp47_spec (0.2.1)
|
||||
bcrypt (3.1.20)
|
||||
bigdecimal (3.0.2)
|
||||
bigdecimal (3.1.8)
|
||||
bindata (2.5.0)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.18.3)
|
||||
@@ -865,7 +865,7 @@ DEPENDENCIES
|
||||
angularjs-rails (= 1.8.0)
|
||||
arel-helpers (~> 2.12)
|
||||
aws-sdk-s3
|
||||
bigdecimal (= 3.0.2)
|
||||
bigdecimal
|
||||
bootsnap
|
||||
bugsnag
|
||||
bullet
|
||||
|
||||
32
alpine.Dockerfile
Normal file
32
alpine.Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
||||
FROM ruby:3.1.4-alpine3.19 AS base
|
||||
ENV LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8 \
|
||||
TZ=Europe/London \
|
||||
RAILS_ROOT=/usr/src/app
|
||||
RUN apk --no-cache upgrade && \
|
||||
apk add --no-cache tzdata postgresql-client imagemagick imagemagick-jpeg && \
|
||||
apk add --no-cache --virtual wkhtmltopdf
|
||||
|
||||
WORKDIR $RAILS_ROOT
|
||||
|
||||
# Development dependencies
|
||||
FROM base AS development-base
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
build-base postgresql-dev git nodejs yarn && \
|
||||
apk add --no-cache --virtual .dev-utils \
|
||||
bash curl less vim chromium-chromedriver zlib-dev openssl-dev \
|
||||
readline-dev yaml-dev sqlite-dev libxml2-dev libxslt-dev libffi-dev vips-dev && \
|
||||
curl -o /usr/local/bin/wait-for-it https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && \
|
||||
chmod +x /usr/local/bin/wait-for-it
|
||||
|
||||
# Install yarn dependencies separately for caching
|
||||
FROM development-base AS yarn-dependencies
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Install Ruby gems
|
||||
FROM development-base
|
||||
COPY . $RAILS_ROOT
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
RUN bundle install --jobs "$(nproc)"
|
||||
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules
|
||||
@@ -20,10 +20,13 @@ angular.module('Darkswarm').directive 'mapSearch', ($timeout, Search) ->
|
||||
$timeout =>
|
||||
map = ctrl.getMap()
|
||||
|
||||
searchBox = scope.createSearchBox map
|
||||
scope.bindSearchResponse map, searchBox
|
||||
scope.biasResults map, searchBox
|
||||
scope.performUrlSearch map
|
||||
if !map
|
||||
alert(t('gmap_load_failure'))
|
||||
else
|
||||
searchBox = scope.createSearchBox map
|
||||
scope.bindSearchResponse map, searchBox
|
||||
scope.biasResults map, searchBox
|
||||
scope.performUrlSearch map
|
||||
|
||||
scope.createSearchBox = (map) ->
|
||||
map.controls[google.maps.ControlPosition.TOP_LEFT].push scope.input
|
||||
|
||||
@@ -44,9 +44,9 @@ module Admin
|
||||
|
||||
create_connected_app
|
||||
|
||||
jwt_service = VineJwtService.new(secret: connected_app_params[:vine_secret])
|
||||
vine_api = VineApiService.new(api_key: connected_app_params[:vine_api_key],
|
||||
jwt_generator: jwt_service)
|
||||
jwt_service = Vine::JwtService.new(secret: connected_app_params[:vine_secret])
|
||||
vine_api = Vine::ApiService.new(api_key: connected_app_params[:vine_api_key],
|
||||
jwt_generator: jwt_service)
|
||||
|
||||
if !@app.connect(api_key: connected_app_params[:vine_api_key],
|
||||
secret: connected_app_params[:vine_secret], vine_api:)
|
||||
@@ -77,7 +77,7 @@ module Admin
|
||||
|
||||
def log_and_notify_exception(exception)
|
||||
Rails.logger.error exception.inspect
|
||||
Bugsnag.notify(exception)
|
||||
Alert.raise(exception)
|
||||
end
|
||||
|
||||
def vine_params_empty?
|
||||
|
||||
@@ -30,13 +30,13 @@ module Admin
|
||||
def validate_data
|
||||
return unless process_data('validate')
|
||||
|
||||
render json: @importer.import_results, response: 200
|
||||
render json: @importer.import_results
|
||||
end
|
||||
|
||||
def save_data
|
||||
return unless process_data('save')
|
||||
|
||||
render json: @importer.save_results, response: 200
|
||||
render json: @importer.save_results
|
||||
end
|
||||
|
||||
def reset_absent_products
|
||||
@@ -76,7 +76,7 @@ module Admin
|
||||
begin
|
||||
@importer.public_send("#{method}_entries")
|
||||
rescue StandardError => e
|
||||
render json: e.message, response: 500
|
||||
render plain: e.message, status: :internal_server_error
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ module Api
|
||||
end
|
||||
|
||||
def error_during_processing(exception)
|
||||
Bugsnag.notify(exception)
|
||||
Alert.raise(exception)
|
||||
|
||||
render(json: { exception: exception.message },
|
||||
status: :unprocessable_entity) && return
|
||||
|
||||
@@ -24,6 +24,7 @@ module Api
|
||||
Orders::WorkflowService.new(@order).advance_to_payment if @order.line_items.any?
|
||||
|
||||
@order.recreate_all_fees!
|
||||
AmendBackorderJob.perform_later(@order) if @order.completed?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
@@ -73,6 +74,7 @@ module Api
|
||||
|
||||
@order.contents.add(variant, quantity, @shipment)
|
||||
@order.recreate_all_fees!
|
||||
AmendBackorderJob.perform_later(@order) if @order.completed?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
@@ -86,6 +88,7 @@ module Api
|
||||
@shipment.reload if @shipment.persisted?
|
||||
|
||||
@order.recreate_all_fees!
|
||||
AmendBackorderJob.perform_later(@order) if @order.completed?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ module Api
|
||||
end
|
||||
|
||||
def error_during_processing(exception)
|
||||
Bugsnag.notify(exception)
|
||||
Alert.raise(exception)
|
||||
|
||||
if Rails.env.development? || Rails.env.test?
|
||||
render status: :unprocessable_entity,
|
||||
|
||||
@@ -78,6 +78,18 @@ class CheckoutController < BaseController
|
||||
|
||||
return true if redirect_to_payment_gateway
|
||||
|
||||
# Redeem VINE voucher
|
||||
vine_voucher_redeemer = Vine::VoucherRedeemerService.new(order: @order)
|
||||
unless vine_voucher_redeemer.redeem
|
||||
# rubocop:disable Rails/DeprecatedActiveModelErrorsMethods
|
||||
flash[:error] = if vine_voucher_redeemer.errors.keys.include?(:redeeming_failed)
|
||||
vine_voucher_redeemer.errors[:redeeming_failed]
|
||||
else
|
||||
I18n.t('checkout.errors.voucher_redeeming_error')
|
||||
end
|
||||
return false
|
||||
# rubocop:enable Rails/DeprecatedActiveModelErrorsMethods
|
||||
end
|
||||
@order.process_payments!
|
||||
@order.confirm!
|
||||
order_completion_reset @order
|
||||
|
||||
@@ -50,9 +50,7 @@ module OrderCompletion
|
||||
end
|
||||
|
||||
def order_invalid!
|
||||
Bugsnag.notify("Notice: invalid order loaded during checkout") do |payload|
|
||||
payload.add_metadata :order, :order, @order
|
||||
end
|
||||
Alert.raise_with_record("Notice: invalid order loaded during checkout", @order)
|
||||
|
||||
flash[:error] = t('checkout.order_not_loaded')
|
||||
redirect_to main_app.shop_path
|
||||
@@ -92,9 +90,7 @@ module OrderCompletion
|
||||
end
|
||||
|
||||
def notify_failure(error = RuntimeError.new(order_processing_error))
|
||||
Bugsnag.notify(error) do |payload|
|
||||
payload.add_metadata :order, @order
|
||||
end
|
||||
Alert.raise_with_record(error, @order)
|
||||
flash[:error] = order_processing_error if flash.blank?
|
||||
end
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ module OrderStockCheck
|
||||
def check_order_cycle_expiry
|
||||
return unless current_order_cycle&.closed?
|
||||
|
||||
Bugsnag.notify("Notice: order cycle closed during checkout completion") do |payload|
|
||||
payload.add_metadata :order, :order, current_order
|
||||
end
|
||||
Alert.raise_with_record("Notice: order cycle closed during checkout completion", current_order)
|
||||
current_order.empty!
|
||||
current_order.set_order_cycle! nil
|
||||
|
||||
|
||||
@@ -4,11 +4,6 @@ class ErrorsController < ApplicationController
|
||||
layout "errors"
|
||||
|
||||
def not_found
|
||||
Bugsnag.notify("404") do |event|
|
||||
event.severity = "info"
|
||||
|
||||
event.add_metadata(:request, :env, request.env)
|
||||
end
|
||||
render status: :not_found, formats: :html
|
||||
end
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ module Spree
|
||||
@order.restock_items = params.fetch(:restock_items, "true") == "true"
|
||||
|
||||
if @order.public_send(event.to_s)
|
||||
AmendBackorderJob.perform_later(@order) if @order.completed?
|
||||
flash[:success] = Spree.t(:order_updated)
|
||||
else
|
||||
flash[:error] = Spree.t(:cannot_perform_operation)
|
||||
|
||||
@@ -24,9 +24,12 @@ module Spree
|
||||
end
|
||||
|
||||
def create
|
||||
# Try to redeem VINE voucher first as we don't want to create a payment and complete
|
||||
# the order if it fails
|
||||
return redirect_to spree.admin_order_payments_path(@order) unless redeem_vine_voucher
|
||||
|
||||
@payment = @order.payments.build(object_params)
|
||||
load_payment_source
|
||||
|
||||
begin
|
||||
unless @payment.save
|
||||
redirect_to spree.admin_order_payments_path(@order)
|
||||
@@ -51,6 +54,10 @@ module Spree
|
||||
event = params[:e]
|
||||
return unless event && @payment.payment_source
|
||||
|
||||
# capture_and_complete_order will complete the order, so we want to try to redeem VINE
|
||||
# voucher first and exit if it fails
|
||||
return if event == "capture_and_complete_order" && !redeem_vine_voucher
|
||||
|
||||
# Because we have a transition method also called void, we do this to avoid conflicts.
|
||||
event = "void_transaction" if event == "void"
|
||||
if allowed_events.include?(event) && @payment.public_send("#{event}!")
|
||||
@@ -60,7 +67,7 @@ module Spree
|
||||
end
|
||||
rescue StandardError => e
|
||||
logger.error e.message
|
||||
Bugsnag.notify(e)
|
||||
Alert.raise(e)
|
||||
flash[:error] = e.message
|
||||
ensure
|
||||
redirect_to request.referer
|
||||
@@ -182,6 +189,22 @@ module Spree
|
||||
%w{capture void_transaction credit refund resend_authorization_email
|
||||
capture_and_complete_order}
|
||||
end
|
||||
|
||||
def redeem_vine_voucher
|
||||
vine_voucher_redeemer = Vine::VoucherRedeemerService.new(order: @order)
|
||||
if vine_voucher_redeemer.redeem == false
|
||||
# rubocop:disable Rails/DeprecatedActiveModelErrorsMethods
|
||||
flash[:error] = if vine_voucher_redeemer.errors.keys.include?(:redeeming_failed)
|
||||
vine_voucher_redeemer.errors[:redeeming_failed]
|
||||
else
|
||||
I18n.t('checkout.errors.voucher_redeeming_error')
|
||||
end
|
||||
# rubocop:enable Rails/DeprecatedActiveModelErrorsMethods
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -213,7 +213,7 @@ module Spree
|
||||
end
|
||||
|
||||
def notify_bugsnag(error, product, variant)
|
||||
Bugsnag.notify(error) do |report|
|
||||
Alert.raise(error) do |report|
|
||||
report.add_metadata(:product,
|
||||
{ product: product.attributes, variant: variant.attributes })
|
||||
report.add_metadata(:product, :product_error, product.errors.first) unless product.valid?
|
||||
|
||||
@@ -70,14 +70,15 @@ module Spree
|
||||
@order.recreate_all_fees! # Enterprise fees on line items and on the order itself
|
||||
|
||||
# Re apply the voucher
|
||||
VoucherAdjustmentsService.new(@order).update
|
||||
@order.update_totals_and_states
|
||||
OrderManagement::Order::Updater.new(@order).update_voucher
|
||||
|
||||
if @order.complete?
|
||||
@order.update_payment_fees!
|
||||
@order.create_tax_charge!
|
||||
end
|
||||
|
||||
AmendBackorderJob.perform_later(@order) if @order.completed?
|
||||
|
||||
respond_with(@order) do |format|
|
||||
format.html do
|
||||
if params.key?(:checkout)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/error_logger'
|
||||
require "spree/core/controller_helpers/auth"
|
||||
require "spree/core/controller_helpers/common"
|
||||
require "spree/core/controller_helpers/order"
|
||||
@@ -37,7 +36,7 @@ class UserRegistrationsController < Devise::RegistrationsController
|
||||
end
|
||||
end
|
||||
rescue StandardError => e
|
||||
OpenFoodNetwork::ErrorLogger.notify(e)
|
||||
Alert.raise(e)
|
||||
render_error(message: I18n.t('unknown_error', scope: I18N_SCOPE))
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,16 @@ class VoucherAdjustmentsController < BaseController
|
||||
before_action :set_order
|
||||
|
||||
def create
|
||||
if add_voucher
|
||||
if voucher_params[:voucher_code].blank?
|
||||
@order.errors.add(:voucher_code, I18n.t('checkout.errors.voucher_not_found'))
|
||||
return render_error
|
||||
end
|
||||
|
||||
voucher = load_voucher
|
||||
|
||||
return render_error unless valid_voucher?(voucher)
|
||||
|
||||
if add_voucher_to_order(voucher)
|
||||
update_payment_section
|
||||
elsif @order.errors.present?
|
||||
render_error
|
||||
@@ -30,19 +39,28 @@ class VoucherAdjustmentsController < BaseController
|
||||
@order = current_order
|
||||
end
|
||||
|
||||
def add_voucher
|
||||
if voucher_params[:voucher_code].blank?
|
||||
@order.errors.add(:voucher_code, I18n.t('checkout.errors.voucher_not_found'))
|
||||
return false
|
||||
end
|
||||
|
||||
voucher = Voucher.find_by(code: voucher_params[:voucher_code], enterprise: @order.distributor)
|
||||
def valid_voucher?(voucher)
|
||||
return false if @order.errors.present?
|
||||
|
||||
if voucher.nil?
|
||||
@order.errors.add(:voucher_code, I18n.t('checkout.errors.voucher_not_found'))
|
||||
return false
|
||||
end
|
||||
|
||||
if !voucher.valid?
|
||||
@order.errors.add(
|
||||
:voucher_code,
|
||||
I18n.t(
|
||||
'checkout.errors.create_voucher_error', error: voucher.errors.full_messages.to_sentence
|
||||
)
|
||||
)
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def add_voucher_to_order(voucher)
|
||||
adjustment = voucher.create_adjustment(voucher.code, @order)
|
||||
|
||||
unless adjustment.persisted?
|
||||
@@ -51,14 +69,38 @@ class VoucherAdjustmentsController < BaseController
|
||||
return false
|
||||
end
|
||||
|
||||
# calculate_voucher_adjustment
|
||||
clear_payments
|
||||
|
||||
VoucherAdjustmentsService.new(@order).update
|
||||
@order.update_totals_and_states
|
||||
OrderManagement::Order::Updater.new(@order).update_voucher
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def load_voucher
|
||||
voucher = Voucher.find_by(code: voucher_params[:voucher_code],
|
||||
enterprise: @order.distributor)
|
||||
return voucher unless voucher.nil? || voucher.is_a?(Vouchers::Vine)
|
||||
|
||||
vine_voucher
|
||||
end
|
||||
|
||||
def vine_voucher
|
||||
vine_voucher_validator = Vine::VoucherValidatorService.new(
|
||||
voucher_code: voucher_params[:voucher_code], enterprise: @order.distributor
|
||||
)
|
||||
voucher = vine_voucher_validator.validate
|
||||
|
||||
return nil if vine_voucher_validator.errors[:not_found_voucher].present?
|
||||
|
||||
if vine_voucher_validator.errors.present?
|
||||
@order.errors.add(:voucher_code, I18n.t('checkout.errors.add_voucher_error'))
|
||||
return nil
|
||||
end
|
||||
|
||||
voucher
|
||||
end
|
||||
|
||||
def update_payment_section
|
||||
render cable_ready: cable_car.replace(
|
||||
selector: "#checkout-payment-methods",
|
||||
|
||||
@@ -1,98 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# When orders are cancelled, we need to amend
|
||||
# When orders are created, adjusted or cancelled, we need to amend
|
||||
# an existing backorder as well.
|
||||
# We're not dealing with line item changes just yet.
|
||||
class AmendBackorderJob < ApplicationJob
|
||||
sidekiq_options retry: 0
|
||||
|
||||
def self.schedule_bulk_update_for(orders)
|
||||
# We can have one backorder per order cycle and distributor.
|
||||
groups = orders.group_by { |order| [order.order_cycle, order.distributor] }
|
||||
groups.each_value do |orders_with_same_backorder|
|
||||
# We need to trigger only one update per backorder.
|
||||
perform_later(orders_with_same_backorder.first)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(order)
|
||||
OrderLocker.lock_order_and_variants(order) do
|
||||
amend_backorder(order)
|
||||
end
|
||||
end
|
||||
|
||||
# The following is a mix of the BackorderJob and the CompleteBackorderJob.
|
||||
# TODO: Move the common code into a re-usable service class.
|
||||
def amend_backorder(order)
|
||||
order_cycle = order.order_cycle
|
||||
distributor = order.distributor
|
||||
user = distributor.owner
|
||||
items = backorderable_items(order)
|
||||
backorder = BackorderUpdater.new.amend_backorder(order)
|
||||
|
||||
return if items.empty?
|
||||
|
||||
# We are assuming that all variants are linked to the same wholesale
|
||||
# shop and its catalog:
|
||||
reference_link = items[0].variant.semantic_links[0].semantic_id
|
||||
urls = FdcUrlBuilder.new(reference_link)
|
||||
orderer = FdcBackorderer.new(user, urls)
|
||||
|
||||
backorder = orderer.find_open_order(order)
|
||||
|
||||
variants = order_cycle.variants_distributed_by(distributor)
|
||||
adjust_quantities(order_cycle, user, backorder, urls, variants)
|
||||
|
||||
FdcBackorderer.new(user, urls).send_order(backorder)
|
||||
end
|
||||
|
||||
# Check if we have enough stock to reduce the backorder.
|
||||
#
|
||||
# Our local stock can increase when users cancel their orders.
|
||||
# But stock levels could also have been adjusted manually. So we review all
|
||||
# quantities before finalising the order.
|
||||
def adjust_quantities(order_cycle, user, order, urls, variants)
|
||||
broker = FdcOfferBroker.new(user, urls)
|
||||
|
||||
order.lines.each do |line|
|
||||
line.quantity = line.quantity.to_i
|
||||
wholesale_product_id = line.offer.offeredItem.semanticId
|
||||
transformation = broker.wholesale_to_retail(wholesale_product_id)
|
||||
linked_variant = variants.linked_to(transformation.retail_product_id)
|
||||
|
||||
# Assumption: If a transformation is present then we only sell the retail
|
||||
# variant. If that can't be found, it was deleted and we'll ignore that
|
||||
# for now.
|
||||
next if linked_variant.nil?
|
||||
|
||||
# Find all line items for this order cycle
|
||||
# Update quantity accordingly
|
||||
if linked_variant.on_demand
|
||||
release_superfluous_stock(line, linked_variant, transformation)
|
||||
else
|
||||
aggregate_final_quantities(order_cycle, line, linked_variant, transformation)
|
||||
end
|
||||
end
|
||||
|
||||
# Clean up empty lines:
|
||||
order.lines.reject! { |line| line.quantity.zero? }
|
||||
end
|
||||
|
||||
# We look at all linked variants.
|
||||
def backorderable_items(order)
|
||||
order.line_items.select do |item|
|
||||
# TODO: scope variants to hub.
|
||||
# We are only supporting producer stock at the moment.
|
||||
item.variant.semantic_links.present?
|
||||
end
|
||||
end
|
||||
|
||||
def release_superfluous_stock(line, linked_variant, transformation)
|
||||
# Note that a division of integers dismisses the remainder, like `floor`:
|
||||
wholesale_items_contained_in_stock = linked_variant.on_hand / transformation.factor
|
||||
|
||||
# But maybe we didn't actually order that much:
|
||||
deductable_quantity = [line.quantity, wholesale_items_contained_in_stock].min
|
||||
line.quantity -= deductable_quantity
|
||||
|
||||
retail_stock_changes = deductable_quantity * transformation.factor
|
||||
linked_variant.on_hand -= retail_stock_changes
|
||||
end
|
||||
|
||||
def aggregate_final_quantities(order_cycle, line, variant, transformation)
|
||||
orders = order_cycle.orders.invoiceable
|
||||
quantity = Spree::LineItem.where(order: orders, variant:).sum(:quantity)
|
||||
wholesale_quantity = (quantity.to_f / transformation.factor).ceil
|
||||
line.quantity = wholesale_quantity
|
||||
user = order.distributor.owner
|
||||
urls = nil # Not needed to send order. The backorder id is the URL.
|
||||
FdcBackorderer.new(user, urls).send_order(backorder) if backorder
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,9 +19,7 @@ class BackorderJob < ApplicationJob
|
||||
rescue StandardError => e
|
||||
# Errors here shouldn't affect the checkout. So let's report them
|
||||
# separately:
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata(:order, :order, order)
|
||||
end
|
||||
Alert.raise_with_record(e, order)
|
||||
end
|
||||
|
||||
def perform(order)
|
||||
|
||||
@@ -24,8 +24,7 @@ class CompleteBackorderJob < ApplicationJob
|
||||
|
||||
urls = FdcUrlBuilder.new(order.lines[0].offer.offeredItem.semanticId)
|
||||
|
||||
variants = order_cycle.variants_distributed_by(distributor)
|
||||
adjust_quantities(order_cycle, user, order, urls, variants)
|
||||
BackorderUpdater.new.update(order, user, distributor, order_cycle)
|
||||
|
||||
FdcBackorderer.new(user, urls).complete_order(order)
|
||||
|
||||
@@ -36,55 +35,4 @@ class CompleteBackorderJob < ApplicationJob
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
# Check if we have enough stock to reduce the backorder.
|
||||
#
|
||||
# Our local stock can increase when users cancel their orders.
|
||||
# But stock levels could also have been adjusted manually. So we review all
|
||||
# quantities before finalising the order.
|
||||
def adjust_quantities(order_cycle, user, order, urls, variants)
|
||||
broker = FdcOfferBroker.new(user, urls)
|
||||
|
||||
order.lines.each do |line|
|
||||
line.quantity = line.quantity.to_i
|
||||
wholesale_product_id = line.offer.offeredItem.semanticId
|
||||
transformation = broker.wholesale_to_retail(wholesale_product_id)
|
||||
linked_variant = variants.linked_to(transformation.retail_product_id)
|
||||
|
||||
# Assumption: If a transformation is present then we only sell the retail
|
||||
# variant. If that can't be found, it was deleted and we'll ignore that
|
||||
# for now.
|
||||
next if linked_variant.nil?
|
||||
|
||||
# Find all line items for this order cycle
|
||||
# Update quantity accordingly
|
||||
if linked_variant.on_demand
|
||||
release_superfluous_stock(line, linked_variant, transformation)
|
||||
else
|
||||
aggregate_final_quantities(order_cycle, line, linked_variant, transformation)
|
||||
end
|
||||
end
|
||||
|
||||
# Clean up empty lines:
|
||||
order.lines.reject! { |line| line.quantity.zero? }
|
||||
end
|
||||
|
||||
def release_superfluous_stock(line, linked_variant, transformation)
|
||||
# Note that a division of integers dismisses the remainder, like `floor`:
|
||||
wholesale_items_contained_in_stock = linked_variant.on_hand / transformation.factor
|
||||
|
||||
# But maybe we didn't actually order that much:
|
||||
deductable_quantity = [line.quantity, wholesale_items_contained_in_stock].min
|
||||
line.quantity -= deductable_quantity
|
||||
|
||||
retail_stock_changes = deductable_quantity * transformation.factor
|
||||
linked_variant.on_hand -= retail_stock_changes
|
||||
end
|
||||
|
||||
def aggregate_final_quantities(order_cycle, line, variant, transformation)
|
||||
orders = order_cycle.orders.invoiceable
|
||||
quantity = Spree::LineItem.where(order: orders, variant:).sum(:quantity)
|
||||
wholesale_quantity = (quantity.to_f / transformation.factor).ceil
|
||||
line.quantity = wholesale_quantity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,11 +22,7 @@ class ReportJob < ApplicationJob
|
||||
|
||||
broadcast_result(channel, format, blob) if channel
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata :report, {
|
||||
report_class:, user:, params:, format:
|
||||
}
|
||||
end
|
||||
Alert.raise(e, { report: { report_class:, user:, params:, format: } })
|
||||
|
||||
broadcast_error(channel)
|
||||
end
|
||||
|
||||
@@ -16,9 +16,7 @@ class StockSyncJob < ApplicationJob
|
||||
rescue StandardError => e
|
||||
# Errors here shouldn't affect the shopping. So let's report them
|
||||
# separately:
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata(:order, :order, order)
|
||||
end
|
||||
Alert.raise_with_record(e, order)
|
||||
end
|
||||
|
||||
def self.sync_linked_catalogs_now(order)
|
||||
@@ -29,9 +27,7 @@ class StockSyncJob < ApplicationJob
|
||||
rescue StandardError => e
|
||||
# Errors here shouldn't affect the shopping. So let's report them
|
||||
# separately:
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata(:order, :order, order)
|
||||
end
|
||||
Alert.raise_with_record(e, order)
|
||||
end
|
||||
|
||||
def self.catalog_ids(order)
|
||||
|
||||
@@ -55,9 +55,7 @@ class SubscriptionConfirmJob < ApplicationJob
|
||||
if order.errors.any?
|
||||
send_failed_payment_email(order)
|
||||
else
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata :order, :order, order
|
||||
end
|
||||
Alert.raise_with_record(e, order)
|
||||
send_failed_payment_email(order, e.message)
|
||||
end
|
||||
end
|
||||
@@ -108,8 +106,6 @@ class SubscriptionConfirmJob < ApplicationJob
|
||||
record_and_log_error(:failed_payment, order, error_message)
|
||||
SubscriptionMailer.failed_payment_email(order).deliver_now
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata :subscription_data, { order:, error_message: }
|
||||
end
|
||||
Alert.raise(e, { subscription_data: { order:, error_message: } })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -79,14 +79,19 @@ class ProducerMailer < ApplicationMailer
|
||||
def set_customer_data(line_items)
|
||||
return unless @coordinator.show_customer_names_to_suppliers?
|
||||
|
||||
@display_business_name = false
|
||||
line_items.map do |line_item|
|
||||
customer_code = line_item.order.customer&.code
|
||||
@display_business_name = true if customer_code.present?
|
||||
|
||||
{
|
||||
sku: line_item.variant.sku,
|
||||
supplier_name: line_item.variant.supplier.name,
|
||||
product_and_full_name: line_item.product_and_full_name,
|
||||
quantity: line_item.quantity,
|
||||
first_name: line_item.order.billing_address.first_name,
|
||||
last_name: line_item.order.billing_address.last_name
|
||||
last_name: line_item.order.billing_address.last_name,
|
||||
business_name: customer_code,
|
||||
}
|
||||
end.sort_by { |line_item| [line_item[:last_name].downcase, line_item[:first_name].downcase] }
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ module Calculator
|
||||
# In theory it should never be called any more after this has been deployed.
|
||||
# If the message below doesn't show up in Bugsnag, we can safely delete this method and all
|
||||
# the related methods below it.
|
||||
Bugsnag.notify("Calculator::DefaultTax was called with legacy tax calculations")
|
||||
Alert.raise("Calculator::DefaultTax was called with legacy tax calculations")
|
||||
|
||||
calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(order.distributor,
|
||||
order.order_cycle)
|
||||
|
||||
31
app/models/concerns/vouchers/flat_ratable.rb
Normal file
31
app/models/concerns/vouchers/flat_ratable.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "active_support/concern"
|
||||
|
||||
module Vouchers
|
||||
module FlatRatable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
validates :amount,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0 }
|
||||
end
|
||||
|
||||
def display_value
|
||||
Spree::Money.new(amount)
|
||||
end
|
||||
|
||||
# We limit adjustment to the maximum amount needed to cover the order, ie if the voucher
|
||||
# covers more than the order.total we only need to create an adjustment covering the order.total
|
||||
def compute_amount(order)
|
||||
-amount.clamp(0, order.pre_discount_total)
|
||||
end
|
||||
|
||||
def rate(order)
|
||||
amount = compute_amount(order)
|
||||
|
||||
amount / order.pre_discount_total
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -481,7 +481,7 @@ class Enterprise < ApplicationRecord
|
||||
|
||||
image_variant_url_for(image.variant(name))
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
|
||||
Alert.raise "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
|
||||
Rails.logger.error(e.message)
|
||||
|
||||
nil
|
||||
|
||||
@@ -79,10 +79,9 @@ module ProductImport
|
||||
if entry.attributes['on_hand'].present?
|
||||
new_variant.on_hand = entry.attributes['on_hand']
|
||||
end
|
||||
check_on_hand_nil(entry, new_variant)
|
||||
end
|
||||
|
||||
check_on_hand_nil(entry, new_variant)
|
||||
|
||||
if new_variant.valid?
|
||||
entry.product_object = new_variant
|
||||
entry.validates_as = 'new_variant' unless entry.errors?
|
||||
@@ -161,7 +160,7 @@ module ProductImport
|
||||
end
|
||||
|
||||
def unit_fields_validation(entry)
|
||||
unit_types = ['g', 'oz', 'lb', 'kg', 't', 'ml', 'l', 'kl', '']
|
||||
unit_types = ['mg', 'g', 'kg', 'oz', 'lb', 't', 'ml', 'cl', 'dl', 'l', 'kl', 'gal', '']
|
||||
|
||||
if entry.units.blank?
|
||||
mark_as_invalid(entry, attribute: 'units',
|
||||
@@ -297,7 +296,7 @@ module ProductImport
|
||||
unscaled_units = entry.unscaled_units.to_f || 0
|
||||
entry.unit_value = unscaled_units * unit_scale unless unit_scale.nil?
|
||||
|
||||
if entry.match_inventory_variant?(existing_variant)
|
||||
if entry.match_variant?(existing_variant)
|
||||
variant_override = create_inventory_item(entry, existing_variant)
|
||||
return validate_inventory_item(entry, variant_override)
|
||||
end
|
||||
|
||||
@@ -85,10 +85,6 @@ module ProductImport
|
||||
end
|
||||
|
||||
def match_variant?(variant)
|
||||
match_display_name?(variant) && variant.unit_value.to_d == unscaled_units.to_d
|
||||
end
|
||||
|
||||
def match_inventory_variant?(variant)
|
||||
match_display_name?(variant) && variant.unit_value.to_d == unit_value.to_d
|
||||
end
|
||||
|
||||
|
||||
@@ -32,14 +32,18 @@ module ProductImport
|
||||
|
||||
def unit_scales
|
||||
{
|
||||
'mg' => { scale: 0.001, unit: 'weight' },
|
||||
'g' => { scale: 1, unit: 'weight' },
|
||||
'kg' => { scale: 1000, unit: 'weight' },
|
||||
'oz' => { scale: 28.35, unit: 'weight' },
|
||||
'lb' => { scale: 453.6, unit: 'weight' },
|
||||
't' => { scale: 1_000_000, unit: 'weight' },
|
||||
'ml' => { scale: 0.001, unit: 'volume' },
|
||||
'cl' => { scale: 0.01, unit: 'volume' },
|
||||
'dl' => { scale: 0.1, unit: 'volume' },
|
||||
'l' => { scale: 1, unit: 'volume' },
|
||||
'kl' => { scale: 1000, unit: 'volume' }
|
||||
'kl' => { scale: 1000, unit: 'volume' },
|
||||
'gal' => { scale: 4.54609, unit: 'volume' },
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ module Spree
|
||||
|
||||
image_variant_url_for(variant(size))
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify "Product ##{viewable_id} Image ##{id} error: #{e.message}"
|
||||
Alert.raise "Product ##{viewable_id} Image ##{id} error: #{e.message}"
|
||||
Rails.logger.error(e.message)
|
||||
|
||||
self.class.default_image_url(size)
|
||||
|
||||
@@ -535,7 +535,7 @@ module Spree
|
||||
# because an outdated shipping fee is not as bad as a lost payment.
|
||||
# And the shipping fee is already up-to-date when this error occurs.
|
||||
# https://github.com/openfoodfoundation/openfoodnetwork/issues/3924
|
||||
Bugsnag.notify(e) do |report|
|
||||
Alert.raise(e) do |report|
|
||||
report.add_metadata(:order, attributes)
|
||||
report.add_metadata(:shipment, shipment.attributes)
|
||||
report.add_metadata(:shipment_in_db, Spree::Shipment.find_by(id: shipment.id).attributes)
|
||||
|
||||
@@ -142,8 +142,6 @@ module Spree
|
||||
|
||||
OrderMailer.cancel_email(id).deliver_later if send_cancellation_email
|
||||
update(payment_state: updater.update_payment_state)
|
||||
|
||||
AmendBackorderJob.perform_later(self)
|
||||
end
|
||||
|
||||
def after_resume
|
||||
|
||||
@@ -106,7 +106,7 @@ module Spree
|
||||
calculator.compute(item)
|
||||
else
|
||||
# Tax refund should not be possible with the way our production server are configured
|
||||
Bugsnag.notify(
|
||||
Alert.raise(
|
||||
"Notice: Tax refund should not be possible, please check the default zone and " \
|
||||
"the tax rate zone configuration"
|
||||
) do |payload|
|
||||
|
||||
@@ -293,7 +293,7 @@ module Spree
|
||||
end
|
||||
|
||||
def ensure_unit_value
|
||||
Bugsnag.notify("Trying to set unit_value to NaN") if unit_value&.nan?
|
||||
Alert.raise("Trying to set unit_value to NaN") if unit_value&.nan?
|
||||
return unless (variant_unit == "items" && unit_value.nil?) || unit_value&.nan?
|
||||
|
||||
self.unit_value = 1.0
|
||||
|
||||
@@ -15,7 +15,7 @@ class StripeAccount < ApplicationRecord
|
||||
|
||||
destroy && Stripe::OAuth.deauthorize(stripe_user_id:)
|
||||
rescue Stripe::OAuth::OAuthError => e
|
||||
Bugsnag.notify(
|
||||
Alert.raise(
|
||||
e,
|
||||
stripe_account: stripe_user_id,
|
||||
enterprise_id:
|
||||
|
||||
@@ -48,8 +48,8 @@ class VariantOverride < ApplicationRecord
|
||||
|
||||
def move_stock!(quantity)
|
||||
unless stock_overridden?
|
||||
Bugsnag.notify RuntimeError.new "Attempting to move stock of a VariantOverride " \
|
||||
"without a count_on_hand specified."
|
||||
Alert.raise "Attempting to move stock of a VariantOverride " \
|
||||
"without a count_on_hand specified."
|
||||
return
|
||||
end
|
||||
|
||||
@@ -73,8 +73,8 @@ class VariantOverride < ApplicationRecord
|
||||
self.attributes = { on_demand: false, count_on_hand: default_stock }
|
||||
save
|
||||
else
|
||||
Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant " \
|
||||
"with no default stock level."
|
||||
Alert.raise "Attempting to reset stock level for a variant " \
|
||||
"with no default stock level."
|
||||
end
|
||||
end
|
||||
self
|
||||
|
||||
@@ -14,7 +14,7 @@ class Voucher < ApplicationRecord
|
||||
class_name: 'Spree::Adjustment',
|
||||
dependent: nil
|
||||
|
||||
validates :code, presence: true, uniqueness: { scope: :enterprise_id }
|
||||
validates :code, presence: true
|
||||
|
||||
TYPES = ["Vouchers::FlatRate", "Vouchers::PercentageRate"].freeze
|
||||
|
||||
|
||||
@@ -2,24 +2,8 @@
|
||||
|
||||
module Vouchers
|
||||
class FlatRate < Voucher
|
||||
validates :amount,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0 }
|
||||
include FlatRatable
|
||||
|
||||
def display_value
|
||||
Spree::Money.new(amount)
|
||||
end
|
||||
|
||||
# We limit adjustment to the maximum amount needed to cover the order, ie if the voucher
|
||||
# covers more than the order.total we only need to create an adjustment covering the order.total
|
||||
def compute_amount(order)
|
||||
-amount.clamp(0, order.pre_discount_total)
|
||||
end
|
||||
|
||||
def rate(order)
|
||||
amount = compute_amount(order)
|
||||
|
||||
amount / order.pre_discount_total
|
||||
end
|
||||
validates_with ScopedUniquenessValidator
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,6 +5,7 @@ module Vouchers
|
||||
validates :amount,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0, less_than_or_equal_to: 100 }
|
||||
validates_with ScopedUniquenessValidator
|
||||
|
||||
def display_value
|
||||
ActionController::Base.helpers.number_to_percentage(amount, precision: 2)
|
||||
|
||||
13
app/models/vouchers/vine.rb
Normal file
13
app/models/vouchers/vine.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
module Vouchers
|
||||
class Vine < Voucher
|
||||
include FlatRatable
|
||||
|
||||
# a VINE voucher :
|
||||
# - can potentially be associated with mutiple enterprise
|
||||
# - code ( "short code" in VINE ) can be recycled, but they shouldn't be linked to the same
|
||||
# voucher_id
|
||||
validates :code, uniqueness: { scope: [:enterprise_id, :external_voucher_id] }
|
||||
end
|
||||
end
|
||||
38
app/services/alert.rb
Normal file
38
app/services/alert.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# A handy wrapper around error reporting libraries like Bugsnag.
|
||||
#
|
||||
# Bugsnag's API is great for general purpose but overly complex for our use.
|
||||
# It also changes over time and we often make mistakes using it. So this class
|
||||
# aims at:
|
||||
#
|
||||
# * Abstracting from Bugsnag, open for other services.
|
||||
# * Simpler interface to reduce user error.
|
||||
# * Central place to update Bugsnag API usage when it changes.
|
||||
#
|
||||
class Alert
|
||||
# Alert Bugsnag with additional metadata to appear in tabs.
|
||||
#
|
||||
# Alert.raise(
|
||||
# "Invalid order during checkout",
|
||||
# {
|
||||
# order: { number: "ABC123", state: "awaiting_return" },
|
||||
# env: { referer: "example.com" }
|
||||
# }
|
||||
# )
|
||||
def self.raise(error, metadata = {}, &block)
|
||||
Bugsnag.notify(error) do |payload|
|
||||
metadata.each do |name, data|
|
||||
payload.add_metadata(name, data)
|
||||
end
|
||||
block.call(payload)
|
||||
end
|
||||
end
|
||||
|
||||
def self.raise_with_record(error, record, &)
|
||||
metadata = {
|
||||
record.class.name => record&.attributes || { record_was_nil: true }
|
||||
}
|
||||
self.raise(error, metadata, &)
|
||||
end
|
||||
end
|
||||
147
app/services/backorder_updater.rb
Normal file
147
app/services/backorder_updater.rb
Normal file
@@ -0,0 +1,147 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/order_cycle_permissions'
|
||||
|
||||
# Update a backorder to reflect all local orders and stock levels
|
||||
# connected to the associated order cycle.
|
||||
class BackorderUpdater
|
||||
# Given an OFN order was created, changed or cancelled,
|
||||
# we re-calculate how much to order in for every variant.
|
||||
def amend_backorder(order)
|
||||
order_cycle = order.order_cycle
|
||||
distributor = order.distributor
|
||||
variants = distributed_linked_variants(order_cycle, distributor)
|
||||
|
||||
# Temporary code: once we don't need a variant link to look up the
|
||||
# backorder, we don't need this check anymore.
|
||||
# Then we can adjust the backorder even though there are no linked variants
|
||||
# in the order cycle right now. Some variants may have been in the order
|
||||
# cycle before and got ordered before being removed from the order cycle.
|
||||
return unless variants.any?
|
||||
|
||||
# We are assuming that all variants are linked to the same wholesale
|
||||
# shop and its catalog:
|
||||
reference_link = variants[0].semantic_links[0].semantic_id
|
||||
user = order.distributor.owner
|
||||
urls = FdcUrlBuilder.new(reference_link)
|
||||
orderer = FdcBackorderer.new(user, urls)
|
||||
|
||||
backorder = orderer.find_open_order(order)
|
||||
|
||||
update(backorder, user, distributor, order_cycle)
|
||||
end
|
||||
|
||||
# Update a given backorder according to a distributor's order cycle.
|
||||
def update(backorder, user, distributor, order_cycle)
|
||||
variants = distributed_linked_variants(order_cycle, distributor)
|
||||
|
||||
# We are assuming that all variants are linked to the same wholesale
|
||||
# shop and its catalog:
|
||||
reference_link = variants[0].semantic_links[0].semantic_id
|
||||
urls = FdcUrlBuilder.new(reference_link)
|
||||
orderer = FdcBackorderer.new(user, urls)
|
||||
broker = FdcOfferBroker.new(user, urls)
|
||||
|
||||
updated_lines = update_order_lines(backorder, order_cycle, variants, broker, orderer)
|
||||
unprocessed_lines = backorder.lines.to_set - updated_lines
|
||||
managed_variants = managed_linked_variants(user, order_cycle, distributor)
|
||||
cancel_stale_lines(unprocessed_lines, managed_variants, broker)
|
||||
|
||||
# Clean up empty lines:
|
||||
backorder.lines.reject! { |line| line.quantity.zero? }
|
||||
|
||||
backorder
|
||||
end
|
||||
|
||||
def update_order_lines(backorder, order_cycle, variants, broker, orderer)
|
||||
variants.map do |variant|
|
||||
link = variant.semantic_links[0].semantic_id
|
||||
solution = broker.best_offer(link)
|
||||
line = orderer.find_or_build_order_line(backorder, solution.offer)
|
||||
if variant.on_demand
|
||||
adjust_stock(variant, solution, line)
|
||||
else
|
||||
aggregate_final_quantities(order_cycle, line, variant, solution)
|
||||
end
|
||||
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_stale_lines(unprocessed_lines, managed_variants, broker)
|
||||
unprocessed_lines.each do |line|
|
||||
wholesale_quantity = line.quantity.to_i
|
||||
wholesale_product_id = line.offer.offeredItem.semanticId
|
||||
transformation = broker.wholesale_to_retail(wholesale_product_id)
|
||||
linked_variant = managed_variants.linked_to(transformation.retail_product_id)
|
||||
|
||||
if linked_variant.nil?
|
||||
transformation.factor = 1
|
||||
linked_variant = managed_variants.linked_to(wholesale_product_id)
|
||||
end
|
||||
|
||||
# Adjust stock level back, we're not going to order this one.
|
||||
if linked_variant&.on_demand
|
||||
retail_quantity = wholesale_quantity * transformation.factor
|
||||
linked_variant.on_hand -= retail_quantity
|
||||
end
|
||||
|
||||
# We don't have any active orders for this
|
||||
line.quantity = 0
|
||||
end
|
||||
end
|
||||
|
||||
def adjust_stock(variant, solution, line)
|
||||
line.quantity = line.quantity.to_i
|
||||
|
||||
if variant.on_hand.negative?
|
||||
needed_quantity = -1 * variant.on_hand # We need to replenish it.
|
||||
|
||||
# The number of wholesale packs we need to order to fulfill the
|
||||
# needed quantity.
|
||||
# For example, we order 2 packs of 12 cans if we need 15 cans.
|
||||
wholesale_quantity = (needed_quantity.to_f / solution.factor).ceil
|
||||
|
||||
# The number of individual retail items we get with the wholesale order.
|
||||
# For example, if we order 2 packs of 12 cans, we will get 24 cans
|
||||
# and we'll account for that in our stock levels.
|
||||
retail_quantity = wholesale_quantity * solution.factor
|
||||
|
||||
line.quantity += wholesale_quantity
|
||||
variant.on_hand += retail_quantity
|
||||
else
|
||||
# Note that a division of integers dismisses the remainder, like `floor`:
|
||||
wholesale_items_contained_in_stock = variant.on_hand / solution.factor
|
||||
|
||||
# But maybe we didn't actually order that much:
|
||||
deductable_quantity = [line.quantity, wholesale_items_contained_in_stock].min
|
||||
|
||||
if deductable_quantity.positive?
|
||||
line.quantity -= deductable_quantity
|
||||
|
||||
retail_stock_change = deductable_quantity * solution.factor
|
||||
variant.on_hand -= retail_stock_change
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def managed_linked_variants(user, order_cycle, distributor)
|
||||
# These permissions may be too complex. Here may be scope to optimise.
|
||||
permissions = OpenFoodNetwork::OrderCyclePermissions.new(user, order_cycle)
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(distributor)
|
||||
.where.associated(:semantic_links)
|
||||
end
|
||||
|
||||
def distributed_linked_variants(order_cycle, distributor)
|
||||
order_cycle.variants_distributed_by(distributor)
|
||||
.where.associated(:semantic_links)
|
||||
end
|
||||
|
||||
def aggregate_final_quantities(order_cycle, line, variant, transformation)
|
||||
# We may want to query all these quantities in one go instead of this n+1.
|
||||
orders = order_cycle.orders.invoiceable
|
||||
quantity = Spree::LineItem.where(order: orders, variant:).sum(:quantity)
|
||||
wholesale_quantity = (quantity.to_f / transformation.factor).ceil
|
||||
line.quantity = wholesale_quantity
|
||||
end
|
||||
end
|
||||
@@ -36,7 +36,7 @@ class FdcBackorderer
|
||||
.map { |id| find_order(id) }
|
||||
.compact
|
||||
# Just in case someone completed the order without updating our database:
|
||||
.select { |o| o.orderStatus[:path] == "Held" }
|
||||
.select { |o| o.orderStatus == order_status.HELD }
|
||||
.first
|
||||
# The DFC Connector doesn't recognise status values properly yet.
|
||||
# So we are overriding the value with something that can be exported.
|
||||
@@ -52,7 +52,7 @@ class FdcBackorderer
|
||||
def find_last_open_order
|
||||
graph = import(urls.orders_url)
|
||||
open_orders = graph&.select do |o|
|
||||
o.semanticType == "dfc-b:Order" && o.orderStatus[:path] == "Held"
|
||||
o.semanticType == "dfc-b:Order" && o.orderStatus == order_status.HELD
|
||||
end
|
||||
|
||||
return if open_orders.blank?
|
||||
@@ -152,7 +152,7 @@ class FdcBackorderer
|
||||
end
|
||||
|
||||
def new?(order)
|
||||
order.semanticId == urls.orders_url
|
||||
order.semanticId == urls&.orders_url
|
||||
end
|
||||
|
||||
def build_sale_session(order)
|
||||
@@ -160,4 +160,10 @@ class FdcBackorderer
|
||||
session.semanticId = urls.sale_session_url
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_status
|
||||
DfcLoader.vocabulary("vocabulary").STATES.ORDERSTATE
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,8 +33,7 @@ class FdcOfferBroker
|
||||
production_flow = catalog_item("#{product_id}/AsPlannedProductionFlow")
|
||||
|
||||
if production_flow
|
||||
wholesale_product_id = production_flow.product
|
||||
catalog_item(wholesale_product_id)
|
||||
production_flow.product
|
||||
else
|
||||
# We didn't find a wholesale variant, falling back to the given product.
|
||||
catalog_item(product_id)
|
||||
@@ -57,7 +56,7 @@ class FdcOfferBroker
|
||||
consumption_flow = catalog_item(
|
||||
production_flow.semanticId.sub("AsPlannedProductionFlow", "AsPlannedConsumptionFlow")
|
||||
)
|
||||
retail_product_id = consumption_flow.product
|
||||
retail_product_id = consumption_flow.product.semanticId
|
||||
|
||||
contained_quantity = consumption_flow.quantity.value.to_i
|
||||
|
||||
@@ -77,7 +76,7 @@ class FdcOfferBroker
|
||||
end
|
||||
|
||||
def flow_producing(wholesale_product_id)
|
||||
@production_flows_by_product_id ||= production_flows.index_by(&:product)
|
||||
@production_flows_by_product_id ||= production_flows.index_by { |flow| flow.product.semanticId }
|
||||
@production_flows_by_product_id[wholesale_product_id]
|
||||
end
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ module Orders
|
||||
order.send_cancellation_email = @send_cancellation_email
|
||||
order.restock_items = @restock_items
|
||||
order.cancel
|
||||
end
|
||||
end.tap { |orders| AmendBackorderJob.schedule_bulk_update_for(orders) }
|
||||
# rubocop:enable Rails/FindEach
|
||||
end
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ module Orders
|
||||
return unless order.cancel
|
||||
|
||||
Spree::OrderMailer.cancel_email_for_shop(order).deliver_later
|
||||
AmendBackorderJob.perform_later(order)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -24,9 +24,7 @@ class PlaceProxyOrder
|
||||
send_placement_email
|
||||
rescue StandardError => e
|
||||
summarizer.record_and_log_error(:processing, order, e.message)
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata :order, :order, order
|
||||
end
|
||||
Alert.raise_with_record(e, order)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -56,9 +54,7 @@ class PlaceProxyOrder
|
||||
|
||||
true
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata(:proxy_order, { subscription:, proxy_order: })
|
||||
end
|
||||
Alert.raise(e, { proxy_order: { subscription:, proxy_order: } })
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ module Sets
|
||||
end
|
||||
|
||||
def notify_bugsnag(error, product, variant, variant_attributes)
|
||||
Bugsnag.notify(error) do |report|
|
||||
Alert.raise(error) do |report|
|
||||
report.add_metadata( :product_set,
|
||||
{ product: product.attributes, variant_attributes:,
|
||||
variant: variant.attributes } )
|
||||
|
||||
@@ -58,7 +58,7 @@ module VariantUnits
|
||||
def option_value_value_unit_scaled
|
||||
unit_scale, unit_name = scale_for_unit_value
|
||||
|
||||
value = (@nameable.unit_value / unit_scale).to_d.truncate(2)
|
||||
value = (@nameable.unit_value.to_d / unit_scale).round(2)
|
||||
|
||||
[value, unit_name]
|
||||
end
|
||||
|
||||
81
app/services/vine/api_service.rb
Normal file
81
app/services/vine/api_service.rb
Normal file
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "faraday"
|
||||
|
||||
module Vine
|
||||
class ApiService
|
||||
attr_reader :api_key, :jwt_generator
|
||||
|
||||
def initialize(api_key:, jwt_generator:)
|
||||
@vine_api_url = ENV.fetch("VINE_API_URL")
|
||||
@api_key = api_key
|
||||
@jwt_generator = jwt_generator
|
||||
end
|
||||
|
||||
def my_team
|
||||
my_team_url = "#{@vine_api_url}/my-team"
|
||||
|
||||
call_with_logging do
|
||||
connection.get(my_team_url)
|
||||
end
|
||||
end
|
||||
|
||||
def voucher_validation(voucher_short_code)
|
||||
voucher_validation_url = "#{@vine_api_url}/voucher-validation"
|
||||
|
||||
call_with_logging do
|
||||
connection.post(
|
||||
voucher_validation_url,
|
||||
{ type: "voucher_code", value: voucher_short_code },
|
||||
'Content-Type': "application/json"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def voucher_redemptions(voucher_id, voucher_set_id, amount)
|
||||
voucher_redemptions_url = "#{@vine_api_url}/voucher-redemptions"
|
||||
|
||||
call_with_logging do
|
||||
connection.post(
|
||||
voucher_redemptions_url,
|
||||
{ voucher_id:, voucher_set_id:, amount: amount.to_i },
|
||||
'Content-Type': "application/json"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def connection
|
||||
jwt = jwt_generator.generate_token
|
||||
Faraday.new(
|
||||
request: { timeout: 30 },
|
||||
headers: {
|
||||
'X-Authorization': "JWT #{jwt}",
|
||||
Accept: "application/json"
|
||||
}
|
||||
) do |f|
|
||||
f.request :json
|
||||
f.response :json
|
||||
f.request :authorization, 'Bearer', api_key
|
||||
f.use Faraday::Response::RaiseError
|
||||
end
|
||||
end
|
||||
|
||||
def call_with_logging
|
||||
yield
|
||||
rescue Faraday::ClientError, Faraday::ServerError => e
|
||||
# caller_location(2,1) gets us the second entry in the stacktrace,
|
||||
# ie the method where `call_with_logging` is called from
|
||||
log_error("#{self.class}##{caller_locations(2, 1)[0].label}", e.response)
|
||||
|
||||
# Re raise the same exception
|
||||
raise
|
||||
end
|
||||
|
||||
def log_error(prefix, response)
|
||||
Rails.logger.error "#{prefix} -- response_status: #{response[:status]}"
|
||||
Rails.logger.error "#{prefix} -- response: #{response[:body]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
23
app/services/vine/jwt_service.rb
Normal file
23
app/services/vine/jwt_service.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Vine
|
||||
class JwtService
|
||||
ALGORITHM = "HS256"
|
||||
ISSUER = "openfoodnetwork"
|
||||
|
||||
def initialize(secret: )
|
||||
@secret = secret
|
||||
end
|
||||
|
||||
def generate_token
|
||||
generation_time = Time.zone.now
|
||||
payload = {
|
||||
iss: ISSUER,
|
||||
iat: generation_time.to_i,
|
||||
exp: (generation_time + 1.minute).to_i,
|
||||
}
|
||||
|
||||
JWT.encode(payload, @secret, ALGORITHM)
|
||||
end
|
||||
end
|
||||
end
|
||||
62
app/services/vine/voucher_redeemer_service.rb
Normal file
62
app/services/vine/voucher_redeemer_service.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Vine
|
||||
class VoucherRedeemerService
|
||||
attr_reader :order, :errors
|
||||
|
||||
def initialize(order: )
|
||||
@order = order
|
||||
@errors = {}
|
||||
end
|
||||
|
||||
def redeem
|
||||
# Do nothing if we don't have a vine voucher added to the order
|
||||
@voucher_adjustment = order.voucher_adjustments.first
|
||||
@voucher = @voucher_adjustment&.originator
|
||||
|
||||
return true if @voucher_adjustment.nil? || !@voucher.is_a?(Vouchers::Vine)
|
||||
|
||||
return false if vine_settings.nil?
|
||||
|
||||
call_vine_api
|
||||
|
||||
@voucher_adjustment.close
|
||||
|
||||
true
|
||||
rescue Faraday::ClientError => e
|
||||
handle_errors(e.response)
|
||||
false
|
||||
rescue Faraday::Error => e
|
||||
Rails.logger.error e.inspect
|
||||
Bugsnag.notify(e)
|
||||
|
||||
errors[:vine_api] = I18n.t("vine_voucher_validator_service.errors.vine_api")
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def vine_settings
|
||||
@vine_settings ||= ConnectedApps::Vine.find_by(enterprise: order.distributor)&.data
|
||||
end
|
||||
|
||||
def call_vine_api
|
||||
jwt_service = Vine::JwtService.new(secret: vine_settings["secret"])
|
||||
vine_api = Vine::ApiService.new(api_key: vine_settings["api_key"], jwt_generator: jwt_service)
|
||||
|
||||
# Voucher adjustment amount is stored in dollars and negative, VINE expect cents
|
||||
amount = -1 * @voucher_adjustment.amount * 100
|
||||
vine_api.voucher_redemptions(
|
||||
@voucher.external_voucher_id, @voucher.external_voucher_set_id, amount
|
||||
)
|
||||
end
|
||||
|
||||
def handle_errors(response)
|
||||
if response[:status] == 400
|
||||
errors[:redeeming_failed] = I18n.t("vine_voucher_redeemer_service.errors.redeeming_failed")
|
||||
else
|
||||
errors[:vine_api] = I18n.t("vine_voucher_redeemer_service.errors.vine_api")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
71
app/services/vine/voucher_validator_service.rb
Normal file
71
app/services/vine/voucher_validator_service.rb
Normal file
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Vine
|
||||
class VoucherValidatorService
|
||||
attr_reader :voucher_code, :errors
|
||||
|
||||
def initialize(voucher_code:, enterprise:)
|
||||
@voucher_code = voucher_code
|
||||
@enterprise = enterprise
|
||||
@errors = {}
|
||||
end
|
||||
|
||||
def validate
|
||||
return nil if vine_settings.nil?
|
||||
|
||||
response = call_vine_api
|
||||
|
||||
save_voucher(response)
|
||||
rescue Faraday::ClientError => e
|
||||
handle_errors(e.response)
|
||||
nil
|
||||
rescue Faraday::Error => e
|
||||
Rails.logger.error e.inspect
|
||||
Bugsnag.notify(e)
|
||||
|
||||
errors[:vine_api] = I18n.t("vine_voucher_validator_service.errors.vine_api")
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def vine_settings
|
||||
@vine_settings ||= ConnectedApps::Vine.find_by(enterprise: @enterprise)&.data
|
||||
end
|
||||
|
||||
def call_vine_api
|
||||
# Check voucher is valid
|
||||
jwt_service = Vine::JwtService.new(secret: vine_settings["secret"])
|
||||
vine_api = Vine::ApiService.new(api_key: vine_settings["api_key"], jwt_generator: jwt_service)
|
||||
|
||||
vine_api.voucher_validation(voucher_code)
|
||||
end
|
||||
|
||||
def handle_errors(response)
|
||||
if response[:status] == 400
|
||||
errors[:invalid_voucher] = I18n.t("vine_voucher_validator_service.errors.invalid_voucher")
|
||||
elsif response[:status] == 404
|
||||
errors[:not_found_voucher] =
|
||||
I18n.t("vine_voucher_validator_service.errors.not_found_voucher")
|
||||
else
|
||||
errors[:vine_api] = I18n.t("vine_voucher_validator_service.errors.vine_api")
|
||||
end
|
||||
end
|
||||
|
||||
def save_voucher(response)
|
||||
voucher_data = response.body["data"]
|
||||
|
||||
# Check if voucher already exist
|
||||
voucher = Vouchers::Vine.find_or_initialize_by(
|
||||
code: voucher_code,
|
||||
enterprise: @enterprise,
|
||||
external_voucher_id: voucher_data["id"],
|
||||
external_voucher_set_id: voucher_data["voucher_set_id"]
|
||||
)
|
||||
voucher.amount = voucher_data["voucher_value_remaining"].to_f / 100
|
||||
voucher.save
|
||||
|
||||
voucher
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,39 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "faraday"
|
||||
|
||||
class VineApiService
|
||||
attr_reader :api_key, :jwt_generator
|
||||
|
||||
def initialize(api_key:, jwt_generator:)
|
||||
@vine_api_url = ENV.fetch("VINE_API_URL")
|
||||
@api_key = api_key
|
||||
@jwt_generator = jwt_generator
|
||||
end
|
||||
|
||||
def my_team
|
||||
my_team_url = "#{@vine_api_url}/my-team"
|
||||
|
||||
jwt = jwt_generator.generate_token
|
||||
connection = Faraday.new(
|
||||
request: { timeout: 30 },
|
||||
headers: {
|
||||
'X-Authorization': "JWT #{jwt}",
|
||||
Accept: "application/json"
|
||||
}
|
||||
) do |f|
|
||||
f.request :json
|
||||
f.response :json
|
||||
f.request :authorization, 'Bearer', api_key
|
||||
end
|
||||
|
||||
response = connection.get(my_team_url)
|
||||
|
||||
if !response.success?
|
||||
Rails.logger.error "VineApiService#my_team -- response_status: #{response.status}"
|
||||
Rails.logger.error "VineApiService#my_team -- response: #{response.body}"
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VineJwtService
|
||||
ALGORITHM = "HS256"
|
||||
ISSUER = "openfoodnetwork"
|
||||
|
||||
def initialize(secret: )
|
||||
@secret = secret
|
||||
end
|
||||
|
||||
def generate_token
|
||||
generation_time = Time.zone.now
|
||||
payload = {
|
||||
iss: ISSUER,
|
||||
iat: generation_time.to_i,
|
||||
exp: (generation_time + 1.minute).to_i,
|
||||
}
|
||||
|
||||
JWT.encode(payload, @secret, ALGORITHM)
|
||||
end
|
||||
end
|
||||
25
app/validators/vouchers/scoped_uniqueness_validator.rb
Normal file
25
app/validators/vouchers/scoped_uniqueness_validator.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
# paranoia doesn't support unique validation including deleted records:
|
||||
# https://github.com/rubysherpas/paranoia/pull/333
|
||||
# We use a custom validator to fix the issue, so we don't need to fork/patch the gem
|
||||
module Vouchers
|
||||
class ScopedUniquenessValidator < ActiveModel::Validator
|
||||
def validate(record)
|
||||
@record = record
|
||||
|
||||
return unless unique_voucher_code_per_enterprise?
|
||||
|
||||
record.errors.add :code, :taken, value: @record.code
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unique_voucher_code_per_enterprise?
|
||||
query = Voucher.with_deleted.where(code: @record.code, enterprise_id: @record.enterprise_id)
|
||||
query = query.where.not(id: @record.id) unless @record.id.nil?
|
||||
|
||||
query.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@
|
||||
= t('.add_new')
|
||||
%br
|
||||
|
||||
- if @enterprise.vouchers.with_deleted.present?
|
||||
- if @enterprise.vouchers.where.not(type: "Vouchers::Vine").with_deleted.present?
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
@@ -17,7 +17,7 @@
|
||||
/%th= t('.customers')
|
||||
/%th= t('.net_value')
|
||||
%tbody
|
||||
- @enterprise.vouchers.with_deleted.order(deleted_at: :desc, code: :asc).each do |voucher|
|
||||
- @enterprise.vouchers.where.not(type: "Vouchers::Vine").with_deleted.order(deleted_at: :desc, code: :asc).each do |voucher|
|
||||
%tr
|
||||
%td= voucher.code
|
||||
%td= voucher.display_value
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
= f.hidden_field :variant_unit_scale
|
||||
= f.select :variant_unit_with_scale,
|
||||
options_for_select(WeightsAndMeasures.variant_unit_options, variant.variant_unit_with_scale),
|
||||
{ include_blank: true },
|
||||
{ include_blank: t('.select_unit_scale') },
|
||||
{ class: "fullwidth no-input", 'aria-label': t('admin.products_page.columns.unit_scale'), data: { "controller": "tom-select", "tom-select-options-value": '{ "plugins": [] }', action: "change->toggle-control#displayIfMatch" }, required: true }
|
||||
= error_message_on variant, :variant_unit, 'data-toggle-control-target': 'control'
|
||||
.field
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.medium-6#checkout-payment-methods
|
||||
- if @order.distributor.vouchers.present?
|
||||
- if @order.distributor.vouchers.present? || @order.distributor.connected_apps.vine.present?
|
||||
%div.checkout-substep
|
||||
= render partial: "checkout/voucher_section", locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
|
||||
|
||||
|
||||
@@ -81,6 +81,9 @@
|
||||
= t :first_name
|
||||
%th.text-right
|
||||
= t :last_name
|
||||
- if @display_business_name
|
||||
%th.text-right
|
||||
= t :business_name
|
||||
%tbody
|
||||
- @customer_line_items.each do |line_item|
|
||||
%tr
|
||||
@@ -97,6 +100,9 @@
|
||||
= line_item[:first_name]
|
||||
%td
|
||||
= line_item[:last_name]
|
||||
- if @display_business_name
|
||||
%td
|
||||
= line_item[:business_name]
|
||||
%p
|
||||
= t :producer_mail_text_after
|
||||
%p
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#{t :producer_mail_greeting} #{@producer.name},
|
||||
#{t :producer_mail_greeting} #{raw(@producer.name)},
|
||||
\
|
||||
= t :producer_mail_text_before
|
||||
\
|
||||
- @distributors_pickup_times.each do |distributor_name, pickup_time|
|
||||
\- #{distributor_name} (#{pickup_time})
|
||||
\- #{raw(distributor_name)} (#{pickup_time})
|
||||
\
|
||||
- if @receival_instructions
|
||||
= t :producer_mail_delivery_instructions
|
||||
= @receival_instructions
|
||||
= raw(@receival_instructions)
|
||||
\
|
||||
Orders summary
|
||||
================
|
||||
@@ -15,22 +15,22 @@ Orders summary
|
||||
= t :producer_mail_order_text
|
||||
\
|
||||
- @grouped_line_items.each_pair do |product_and_full_name, line_items|
|
||||
#{line_items.first.variant.sku} - #{raw(line_items.first.variant.supplier.name)} - #{raw(product_and_full_name)} (QTY: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)}
|
||||
#{line_items.first.variant.sku} - #{raw(line_items.first.variant.supplier.name)} - #{raw(product_and_full_name)} (#{t(:producer_mail_qty)}: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)} (#{t(:with_tax_incl, amount: Spree::Money.new(line_items.sum(&:included_tax), currency: line_items.first.currency))})
|
||||
\
|
||||
\
|
||||
#{t :total}: #{@total}
|
||||
#{t :total}: #{@total} (#{t(:with_tax_incl, amount: @tax_total)})
|
||||
\
|
||||
- if @customer_grouped_line_items
|
||||
- if @customer_line_items
|
||||
= t :producer_mail_order_customer_text
|
||||
\
|
||||
- @customer_line_items.each do |line_item|
|
||||
#{line_item[:sku]} - #{raw(line_item[:supplier_name])} - #{raw(line_item[:product_and_full_name])} (QTY: #{line_item[:quantity]}) - #{raw(line_item[:first_name])} #{raw(line_item[:last_name])}
|
||||
#{line_item[:sku]} - #{raw(line_item[:supplier_name])} - #{raw(line_item[:product_and_full_name])} (#{t(:producer_mail_qty)}: #{line_item[:quantity]}) - #{raw(line_item[:first_name])} #{raw(line_item[:last_name])} #{raw(line_item[:business_name])}
|
||||
\
|
||||
\
|
||||
= t :producer_mail_text_after
|
||||
|
||||
#{t :producer_mail_signoff},
|
||||
#{@coordinator.name}
|
||||
#{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode}
|
||||
#{raw(@coordinator.name)}
|
||||
#{raw(@coordinator.address.address1)}, #{raw(@coordinator.address.city)}, #{raw(@coordinator.address.zipcode)}
|
||||
#{@coordinator.phone}
|
||||
#{@coordinator.contact.email}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ru } from "flatpickr/dist/l10n/ru";
|
||||
import { sv } from "flatpickr/dist/l10n/sv";
|
||||
import { tr } from "flatpickr/dist/l10n/tr";
|
||||
import { en } from "flatpickr/dist/l10n/default.js";
|
||||
import { hu } from "flatpickr/dist/l10n/hu";
|
||||
import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr";
|
||||
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";
|
||||
|
||||
@@ -36,6 +37,7 @@ export default class extends Flatpickr {
|
||||
sv: sv,
|
||||
tr: tr,
|
||||
en: en,
|
||||
hu: hu,
|
||||
};
|
||||
|
||||
initialize() {
|
||||
|
||||
@@ -12,8 +12,8 @@ export const useOpenAndCloseAsAModal = (controller) => {
|
||||
}.bind(controller),
|
||||
|
||||
close: function (_event, remove = false) {
|
||||
// Only execute close if there is an open modal
|
||||
if (!document.querySelector("body").classList.contains('modal-open')) return;
|
||||
// Only execute close if the current modal is open
|
||||
if (!this.modalTarget.classList.contains('in')) return;
|
||||
|
||||
this.modalTarget.classList.remove("in");
|
||||
this.backgroundTarget.classList.remove("in");
|
||||
|
||||
@@ -10,7 +10,8 @@ export default class extends Controller {
|
||||
|
||||
changePage(event) {
|
||||
const productsForm = document.querySelector("#products-form");
|
||||
productsForm.scrollIntoView({ behavior: "smooth" });
|
||||
if (productsForm) productsForm.scrollIntoView({ behavior: "smooth" });
|
||||
|
||||
this.page.value = event.target.dataset.page;
|
||||
this.submitSearch();
|
||||
this.page.value = 1;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:10.19
|
||||
@@ -24,6 +22,8 @@ services:
|
||||
- .:/usr/src/app
|
||||
- gems:/bundles
|
||||
- ./config/database.yml:/usr/src/app/config/database.yml
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
@@ -32,6 +32,7 @@ services:
|
||||
OFN_DB_HOST: db
|
||||
OFN_REDIS_URL: redis://redis/
|
||||
OFN_REDIS_JOBS_URL: redis://redis
|
||||
OFN_REDIS_TEST_URL: redis://redis/3
|
||||
WEBPACKER_DEV_SERVER_HOST: webpack
|
||||
command: >
|
||||
bash -c "wait-for-it -t 30 db:5432 &&
|
||||
@@ -6,5 +6,9 @@ Bugsnag.configure do |config|
|
||||
config.logger = Logger.new(STDOUT)
|
||||
config.logger.level = Logger::ERROR
|
||||
end
|
||||
config.notify_release_stages = %w(production staging)
|
||||
|
||||
# If you want to notify Bugsnag in dev or test then set the env var:
|
||||
# spring stop
|
||||
# BUGSNAG=true ./bin/rails console
|
||||
config.notify_release_stages = %w(production staging) unless ENV["BUGSNAG"]
|
||||
end
|
||||
|
||||
@@ -351,6 +351,8 @@ en:
|
||||
sku: "SKU"
|
||||
subtotal: "Subtotal"
|
||||
tax_rate: "Tax rate"
|
||||
with_tax_incl: "%{amount} tax incl."
|
||||
producer_mail_qty: QTY
|
||||
validators:
|
||||
date_time_string_validator:
|
||||
not_string_error: "must be a string"
|
||||
@@ -574,7 +576,15 @@ en:
|
||||
depth: "Depth"
|
||||
payment_could_not_process: "The payment could not be processed"
|
||||
payment_could_not_complete: "The payment could not be completed"
|
||||
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API, please try again later."
|
||||
invalid_voucher: "The voucher is not valid"
|
||||
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
|
||||
vine_voucher_redeemer_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API"
|
||||
redeeming_failed: "Redeeming the voucher failed"
|
||||
actions:
|
||||
create_and_add_another: "Create and Add Another"
|
||||
create: "Create"
|
||||
@@ -973,6 +983,7 @@ en:
|
||||
category_field_name: "Category"
|
||||
tax_category_field_name: "Tax Category"
|
||||
producer_field_name: "Producer"
|
||||
select_unit_scale: Select unit scale
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
@@ -1412,7 +1423,7 @@ en:
|
||||
connected_apps:
|
||||
legend: "Connected apps"
|
||||
affiliate_sales_data:
|
||||
title: "INRAE Research"
|
||||
title: "INRAE / UFC QUE CHOISIR Research"
|
||||
tagline: "Allow this research project to access your orders data anonymously"
|
||||
enable: "Allow data sharing"
|
||||
disable: "Stop sharing"
|
||||
@@ -1420,10 +1431,10 @@ en:
|
||||
need_to_be_manager: "Only managers can connect apps."
|
||||
description_html: |
|
||||
<p>
|
||||
INRAE are studiying food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
|
||||
INRAE and UFC QUE CHOISIR are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://pepr-sams.fr/2024/03/12/plat4terfood/"
|
||||
<a href="https://apropos.coopcircuits.fr/"
|
||||
target="_blank"><b>Learn more about this research project</b>
|
||||
<i class="icon-external-link"></i></a>
|
||||
</p>
|
||||
@@ -2123,6 +2134,8 @@ en:
|
||||
no_shipping_methods_available: Checkout is not possible due to absence of shipping options. Please contact the shop owner.
|
||||
voucher_not_found: Not found
|
||||
add_voucher_error: There was an error while adding the voucher
|
||||
create_voucher_error: "There was an error while creating the voucher: %{error}"
|
||||
voucher_redeeming_error: There was an error while trying to redeem your voucher
|
||||
shops:
|
||||
hubs:
|
||||
show_closed_shops: "Show closed shops"
|
||||
@@ -2787,6 +2800,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart."
|
||||
confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart."
|
||||
location_placeholder: "Type in a location..."
|
||||
gmap_load_failure: "Unable to load map. Please check your browser settings and allow 3rd party cookies for this website."
|
||||
error_required: "can't be blank"
|
||||
error_number: "must be number"
|
||||
error_email: "must be email address"
|
||||
@@ -3447,6 +3461,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
ready: Ready
|
||||
pending: Pending
|
||||
shipped: Shipped
|
||||
business_name: Business Name
|
||||
js:
|
||||
saving: 'Saving...'
|
||||
changes_saved: 'Changes saved.'
|
||||
|
||||
@@ -107,6 +107,9 @@ en_CA:
|
||||
count_on_hand:
|
||||
using_producer_stock_settings_but_count_on_hand_set: "must be blank because you are using producer stock settings"
|
||||
limited_stock_but_no_count_on_hand: "must be specified because you are forcing limited stock"
|
||||
connected_apps:
|
||||
vine:
|
||||
api_request_error: "An error occured when connecting to Vine API"
|
||||
messages:
|
||||
confirmation: "doesn't match %{attribute}"
|
||||
blank: "can't be blank"
|
||||
@@ -435,7 +438,7 @@ en_CA:
|
||||
cancel_order: "Cancel Order"
|
||||
confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?"
|
||||
confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?"
|
||||
must_have_valid_business_number: "%{enterprise_name} must have a valid business number before invoices can be used."
|
||||
must_have_valid_business_number: "%{enterprise_name} must have a valid business number before invoices can be used. Enter a number in enterprise settings."
|
||||
invoice: "Invoice"
|
||||
invoices: "Invoices"
|
||||
file: "File"
|
||||
@@ -525,6 +528,15 @@ en_CA:
|
||||
depth: "Depth"
|
||||
payment_could_not_process: "The payment could not be processed"
|
||||
payment_could_not_complete: "The payment could not be completed"
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API, please try again later."
|
||||
invalid_voucher: "The voucher is not valid"
|
||||
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
|
||||
vine_voucher_redeemer_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API"
|
||||
redeeming_failed: "Redeeming the voucher failed"
|
||||
actions:
|
||||
create_and_add_another: "Create and Add Another"
|
||||
create: "Create"
|
||||
@@ -584,11 +596,11 @@ en_CA:
|
||||
clone: Clone
|
||||
delete: Delete
|
||||
remove: Remove
|
||||
preview: Prévisualisation
|
||||
preview: Preview
|
||||
image:
|
||||
edit: Edit
|
||||
product_preview:
|
||||
product_preview: Prévisualisation du produit
|
||||
product_preview: Product preview
|
||||
shop_tab: Shop
|
||||
product_details_tab: Product details
|
||||
adjustments:
|
||||
@@ -606,8 +618,8 @@ en_CA:
|
||||
first_name: First Name
|
||||
last_name: Last Name
|
||||
on_hand: On Hand
|
||||
on_demand: On Demand
|
||||
on_demand?: On Demand?
|
||||
on_demand: Unlimited
|
||||
on_demand?: Unlimited?
|
||||
order_cycle: Order Cycle
|
||||
payment: Payment
|
||||
payment_method: Payment Method
|
||||
@@ -717,6 +729,7 @@ en_CA:
|
||||
connected_apps_enabled:
|
||||
discover_regen: Discover Regenerative portal
|
||||
affiliate_sales_data: DFC anonymised orders API for research purposes
|
||||
vine: Voucher Integration Engine (VINE)
|
||||
update:
|
||||
resource: Connected app settings
|
||||
customers:
|
||||
@@ -896,6 +909,7 @@ en_CA:
|
||||
category_field_name: "Category"
|
||||
tax_category_field_name: "Tax Category"
|
||||
producer_field_name: "Producer"
|
||||
select_unit_scale: Select unit scale
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
@@ -1014,7 +1028,7 @@ en_CA:
|
||||
variant_unit_name: Variant Unit Name
|
||||
price: Price
|
||||
on_hand: On Hand
|
||||
on_demand: On Demand
|
||||
on_demand: Unlimited
|
||||
shipping_category: Shipping Category
|
||||
tax_category: Tax Category
|
||||
variant_overrides:
|
||||
@@ -1097,9 +1111,9 @@ en_CA:
|
||||
business_details:
|
||||
legend: "Business Details"
|
||||
upload: 'upload'
|
||||
abn: Business Number
|
||||
abn_placeholder: eg. 80781 2466
|
||||
acn: Busines Number
|
||||
abn: Business Number (if applicable)
|
||||
abn_placeholder: 1234 My Business
|
||||
acn: GST Number (if applicable)
|
||||
acn_placeholder: eg.80781 2466
|
||||
display_invoice_logo: Display logo in invoices
|
||||
invoice_text: Add customized text at the end of invoices
|
||||
@@ -1373,9 +1387,25 @@ en_CA:
|
||||
<i class="icon-external-link"></i></a>
|
||||
</p>
|
||||
vine:
|
||||
title: "Voucher Integration Engine (VINE)"
|
||||
tagline: "Allow redemption of VINE vouchers in your shopfront."
|
||||
enable: "Donate"
|
||||
disable: "Disconnect"
|
||||
need_to_be_manager: "Only managers can connect apps."
|
||||
vine_api_key: "VINE API Key"
|
||||
vine_secret: "VINE secret"
|
||||
description_html: |
|
||||
<p>
|
||||
To enable VINE for your enterprise, enter your API key and secret.
|
||||
</p>
|
||||
<p>
|
||||
<a href="#" target="_blank"><b>VINE</b>
|
||||
<i class="icon-external-link"></i></a>
|
||||
</p>
|
||||
api_parameters_empty: "Please enter an API key and a secret"
|
||||
api_parameters_error: "Check you entered your API key and secret correctly, contact your instance manager if the error persists"
|
||||
connection_error: "API connection error, please try again"
|
||||
setup_error: "VINE API is not configured, please contact your instance manager"
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1672,6 +1702,7 @@ en_CA:
|
||||
pack_by_customer: Pack By Customer
|
||||
pack_by_supplier: Pack By Supplier
|
||||
pack_by_product: Pack By Product
|
||||
pay_your_suppliers: Pay your suppliers
|
||||
display:
|
||||
report_is_big: "This report is big and may slow down your device."
|
||||
display_anyway: "Display anyway"
|
||||
@@ -1715,8 +1746,10 @@ en_CA:
|
||||
name: Xero Invoices
|
||||
description: Invoices for import into Xero
|
||||
enterprise_fee_summary:
|
||||
name: "Enterprise Fee Summary"
|
||||
name: "All Fees Summary"
|
||||
description: "Summary of Enterprise Fees collected"
|
||||
suppliers:
|
||||
name: Suppliers
|
||||
enterprise_fees_with_tax_report_by_order: "Enterprise Fees With Tax Report By Order"
|
||||
enterprise_fees_with_tax_report_by_producer: "Enterprise Fees With Tax Report By Producer"
|
||||
errors:
|
||||
@@ -2006,6 +2039,8 @@ en_CA:
|
||||
no_shipping_methods_available: Checkout is not possible because no shipping/pick-up options are available. Please contact the shop owner.
|
||||
voucher_not_found: Not found
|
||||
add_voucher_error: There was an error while adding the voucher
|
||||
create_voucher_error: "There was an error while creating the voucher: %{error}"
|
||||
voucher_redeeming_error: There was an error while trying to redeem your voucher
|
||||
shops:
|
||||
hubs:
|
||||
show_closed_shops: "Show closed shops"
|
||||
@@ -2619,6 +2654,7 @@ en_CA:
|
||||
confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart."
|
||||
confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart."
|
||||
location_placeholder: "Type in a location..."
|
||||
gmap_load_failure: "Unable to load map. Please check your browser settings and allow 3rd party cookies for this website."
|
||||
error_required: "can't be blank"
|
||||
error_number: "must be number"
|
||||
error_email: "must be email address"
|
||||
@@ -3003,6 +3039,8 @@ en_CA:
|
||||
report_render_options: Rendering Options
|
||||
report_header_ofn_uid: OFN UID
|
||||
report_header_order_cycle: Order Cycle
|
||||
report_header_order_cycle_start_date: OC Start Date
|
||||
report_header_order_cycle_end_date: OC End Date
|
||||
report_header_user: User
|
||||
report_header_email: Email
|
||||
report_header_status: Status
|
||||
@@ -3023,6 +3061,7 @@ en_CA:
|
||||
report_header_hub_legal_name: "Hub Legal Name"
|
||||
report_header_hub_contact_name: "Hub Contact Name"
|
||||
report_header_hub_email: "Hub Public Email"
|
||||
report_header_hub_contact_email: Hub Contact Email
|
||||
report_header_hub_owner_email: Hub Owner Email
|
||||
report_header_hub_phone: "Hub Phone Number"
|
||||
report_header_hub_address_line1: "Hub Address Line 1"
|
||||
@@ -3095,6 +3134,8 @@ en_CA:
|
||||
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_producer_abn_acn: Producer ABN/ACN
|
||||
report_header_producer_address: Producer Address
|
||||
report_header_unit: Unit
|
||||
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
|
||||
report_header_cost: Cost
|
||||
@@ -3155,7 +3196,11 @@ en_CA:
|
||||
report_header_total_units: Total Units
|
||||
report_header_sum_max_total: "Sum Max Total"
|
||||
report_header_total_excl_vat: "Total excl. tax (%{currency_symbol})"
|
||||
report_header_total_fees_excl_tax: "Total fees excl. tax (%{currency_symbol})"
|
||||
report_header_total_tax_on_fees: "Total tax on fees (%{currency_symbol})"
|
||||
report_header_total: "Total (%{currency_symbol})"
|
||||
report_header_total_incl_vat: "Total incl. tax (%{currency_symbol})"
|
||||
report_header_total_excl_fees_and_tax: "Total excl. fees and tax (%{currency_symbol})"
|
||||
report_header_temp_controlled: TempControlled?
|
||||
report_header_is_producer: Producer?
|
||||
report_header_not_confirmed: Not Confirmed
|
||||
|
||||
@@ -308,6 +308,8 @@ en_FR:
|
||||
sku: "SKU"
|
||||
subtotal: "Subtotal"
|
||||
tax_rate: "Tax rate"
|
||||
with_tax_incl: "%{amount} tax incl."
|
||||
producer_mail_qty: QTY
|
||||
validators:
|
||||
date_time_string_validator:
|
||||
not_string_error: "must be a string"
|
||||
@@ -528,6 +530,15 @@ en_FR:
|
||||
depth: "Depth"
|
||||
payment_could_not_process: "The payment could not be processed"
|
||||
payment_could_not_complete: "The payment could not be completed"
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API, please try again later."
|
||||
invalid_voucher: "The voucher is not valid"
|
||||
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
|
||||
vine_voucher_redeemer_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API"
|
||||
redeeming_failed: "Redeeming the voucher failed"
|
||||
actions:
|
||||
create_and_add_another: "Create and Add Another"
|
||||
create: "Create"
|
||||
@@ -900,6 +911,7 @@ en_FR:
|
||||
category_field_name: "Category"
|
||||
tax_category_field_name: "Tax Category"
|
||||
producer_field_name: "Producer"
|
||||
select_unit_scale: Select unit scale
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
@@ -1341,7 +1353,7 @@ en_FR:
|
||||
connected_apps:
|
||||
legend: "Connected apps"
|
||||
affiliate_sales_data:
|
||||
title: "INRAE / UFC QUE CHOISIR Research"
|
||||
title: "INRAE Research"
|
||||
tagline: "Allow this research project to access your orders data anonymously"
|
||||
enable: "Allow data sharing"
|
||||
disable: "Stop sharing"
|
||||
@@ -1349,7 +1361,7 @@ en_FR:
|
||||
need_to_be_manager: "Only managers can connect apps."
|
||||
description_html: |
|
||||
<p>
|
||||
INRAE and UFC QUE CHOISIR are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
|
||||
INRAE are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://apropos.coopcircuits.fr/"
|
||||
@@ -1382,7 +1394,7 @@ en_FR:
|
||||
vine:
|
||||
title: "Voucher Integration Engine (VINE)"
|
||||
tagline: "Allow redemption of VINE vouchers in your shopfront."
|
||||
enable: "About"
|
||||
enable: "Connect"
|
||||
disable: "Disconnect"
|
||||
need_to_be_manager: "Only managers can connect apps."
|
||||
vine_api_key: "VINE API Key"
|
||||
@@ -2032,6 +2044,8 @@ en_FR:
|
||||
no_shipping_methods_available: Checkout is not possible due to absence of shipping options. Please contact the shop owner.
|
||||
voucher_not_found: Not found
|
||||
add_voucher_error: There was an error while adding the voucher
|
||||
create_voucher_error: "There was an error while creating the voucher: %{error}"
|
||||
voucher_redeeming_error: There was an error while trying to redeem your voucher
|
||||
shops:
|
||||
hubs:
|
||||
show_closed_shops: "Show closed shops"
|
||||
@@ -2645,6 +2659,7 @@ en_FR:
|
||||
confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart."
|
||||
confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart."
|
||||
location_placeholder: "Type in a location..."
|
||||
gmap_load_failure: "Unable to load map. Please check your browser settings and allow 3rd party cookies for this website."
|
||||
error_required: "can't be blank"
|
||||
error_number: "must be number"
|
||||
error_email: "must be email address"
|
||||
|
||||
@@ -3124,7 +3124,7 @@ en_GB:
|
||||
report_header_producer_suburb: Producer Suburb
|
||||
report_header_producer_tax_status: Tax Rate Name
|
||||
report_header_producer_charges_sales_tax?: GST/VAT Registered
|
||||
report_header_producer_abn_acn: Producer ABN/ACN
|
||||
report_header_producer_abn_acn: Producer company number
|
||||
report_header_producer_address: Producer Address
|
||||
report_header_unit: Unit
|
||||
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
es:
|
||||
language_name: "Español"
|
||||
time:
|
||||
formats:
|
||||
long: "%B %d, %Y %-l:%M %p"
|
||||
activerecord:
|
||||
models:
|
||||
spree/product: Producto
|
||||
@@ -32,7 +35,7 @@ es:
|
||||
fee_type: Tipo de Comisión
|
||||
spree/order:
|
||||
payment_state: Estado del pago
|
||||
shipment_state: Estado del envío
|
||||
shipment_state: Provincia de envío
|
||||
completed_at: Completado en
|
||||
number: Número
|
||||
state: Estado
|
||||
@@ -50,6 +53,7 @@ es:
|
||||
primary_taxon: "categoría del producto"
|
||||
shipping_category_id: "Categoría de envío"
|
||||
supplier: "Proveedora"
|
||||
variant_unit: "Valor unidad"
|
||||
variant_unit_name: "Nombre de la unidad de la variante"
|
||||
unit_value: "Valor unidad"
|
||||
spree/credit_card:
|
||||
@@ -65,10 +69,24 @@ es:
|
||||
spree/payment_method/calculator:
|
||||
preferred_flat_percent: "Calculadora Porcentaje fijo:"
|
||||
preferred_amount: "Calculadora Importe:"
|
||||
preferred_first_item: "Calculadora Primer elemento:"
|
||||
preferred_additional_item: "Calculadora Coste del artículo adicional:"
|
||||
preferred_max_items: "Calculadora Max Items:"
|
||||
preferred_minimal_amount: "Calculadora Importe mínimo:"
|
||||
preferred_normal_amount: "Calculadora Cantidad normal:"
|
||||
preferred_discount_amount: "Calculadora Importe del descuento:"
|
||||
preferred_unit_from_list: "Calculadora Unidad De la Lista:"
|
||||
preferred_per_unit: "Calculadora por unidad:"
|
||||
enterprise:
|
||||
white_label_logo_link: "Enlace para el logo que se usara en el escaparate"
|
||||
errors:
|
||||
models:
|
||||
enterprise_fee:
|
||||
inherit_tax_requires_per_item_calculator: "Heredar la categoría fiscal requiere una calculadora por artículo."
|
||||
spree/image:
|
||||
attributes:
|
||||
attachment:
|
||||
integrity_error: "no se ha podido cargar. Compruebe que el archivo no esté dañado e inténtelo de nuevo."
|
||||
spree/user:
|
||||
attributes:
|
||||
email:
|
||||
@@ -93,21 +111,39 @@ es:
|
||||
vine:
|
||||
api_request_error: "Un error se ha producido al conectarse a Vine API"
|
||||
messages:
|
||||
confirmation: "No coincide"
|
||||
blank: "no puede estar vacío"
|
||||
too_short: "es demasiado corto (el mínimo es %{count}caracteres)"
|
||||
errors:
|
||||
messages:
|
||||
content_type_invalid: "Tiene un tipo de contenido invalido"
|
||||
file_size_out_of_range: "el tamaño %{file_size} no está en el rango requerido"
|
||||
limit_out_of_range: "El número total está fuera de rango"
|
||||
image_metadata_missing: "no es una imagen valida"
|
||||
dimension_min_inclusion: "debe ser mayor o igual a %{width} x %{height} píxeles."
|
||||
dimension_max_inclusion: "debe ser inferior o igual a %{width} x %{height} píxeles."
|
||||
dimension_width_inclusion: "la anchura no se incluye entre %{min} y %{max} píxel."
|
||||
dimension_height_inclusion: "la altura no se incluye entre %{min} y %{max} píxel."
|
||||
dimension_width_greater_than_or_equal_to: "La anchura debe ser mayor o igual a %{length} píxeles."
|
||||
dimension_height_greater_than_or_equal_to: "La altura debe ser mayor o igual que %{length} píxel."
|
||||
dimension_width_less_than_or_equal_to: "La anchura debe ser menor o igual a %{length} píxeles."
|
||||
dimension_height_less_than_or_equal_to: "La altura debe ser menor o igual a %{length} píxel."
|
||||
dimension_width_equal_to: "La anchura debe ser igual a %{length} píxeles."
|
||||
dimension_height_equal_to: "la altura debe ser igual a %{length} pixel."
|
||||
aspect_ratio_not_square: "la imagen tiene que ser cuadrada"
|
||||
aspect_ratio_not_portrait: "la imagen tiene que ser un retrato"
|
||||
aspect_ratio_not_landscape: "debe ser una imagen de paisaje"
|
||||
aspect_ratio_is_not: "debe tener una relación de aspecto de %{aspect_ratio}"
|
||||
aspect_ratio_unknown: "tiene una relación de aspecto desconocida"
|
||||
image_not_processable: "no es una imagen valida"
|
||||
not_found:
|
||||
title: "La página que estas buscando no existe (404)"
|
||||
internal_server_error:
|
||||
title: "Lo sentimos pero algo fue mal (500)"
|
||||
unprocessable_entity:
|
||||
title: "El cambio deseado ha sido rechazado (422)"
|
||||
general_error:
|
||||
message: "Lo sentimos, pero algo ha ido mal.\n\nPuede que se trate de un problema temporal, así que inténtalo de nuevo o recarga la página.\nRegistramos todos los errores y es posible que estemos trabajando en una solución.\nSi el problema persiste o es urgente, póngase en contacto con nosotras."
|
||||
stripe:
|
||||
error_code:
|
||||
incorrect_number: "El número de tarjeta es incorrecto."
|
||||
@@ -160,6 +196,7 @@ es:
|
||||
transaction_not_allowed: "La tarjeta ha sido denegada por una razón desconocida."
|
||||
try_again_later: "La tarjeta ha sido denegada por una razón desconocida."
|
||||
withdrawal_count_limit_exceeded: "El cliente ha superado el saldo o límite de crédito disponible en su tarjeta."
|
||||
disconnect_failure: "No hemos podido desconectar Stripe."
|
||||
success_code:
|
||||
disconnected: "Cuenta de Stripe desconectada."
|
||||
activemodel:
|
||||
@@ -187,11 +224,16 @@ es:
|
||||
not_available_to_shop: "no está disponible para %{shop}"
|
||||
card_details: "Detalles de tarjeta"
|
||||
card_type: "Tipo de tarjeta"
|
||||
card_type_is: "El tipo de tarjeta es"
|
||||
unrecognized_card_type: "Tipo de tarjeta no reconocido"
|
||||
use_new_cc: "Utilizar otra tarjeta"
|
||||
what_is_this: "¿Qué es esto?"
|
||||
cardholder_name: "Nombre del titular de la tarjeta"
|
||||
community_forum_url: "URL del foro de la comunidad"
|
||||
customer_instructions: "Instrucciones del Consumidor"
|
||||
additional_information: "Información adicional"
|
||||
connect_app:
|
||||
url: "https://n8n.openfoodnetwork.org/webhook/regen/connect-enterprise"
|
||||
devise:
|
||||
passwords:
|
||||
spree_user:
|
||||
@@ -228,6 +270,8 @@ es:
|
||||
updated_not_active: "Su contraseña ha sido restablecida, pero su correo electrónico aún no ha sido confirmado."
|
||||
updated: "Su contraseña ha sido cambiada con éxito. Ya tienes la sesión iniciada."
|
||||
send_instructions: "Recibirás un correo electrónico con instrucciones sobre cómo confirmar su cuenta en unos minutos."
|
||||
oidc:
|
||||
failure: "No se ha podido iniciar sesión: %{error}"
|
||||
home_page_alert_html: "HTML de alerta de página de inicio"
|
||||
hub_signup_case_studies_html: "HTML casos de estudio de registro de hub"
|
||||
hub_signup_detail_html: "HTML detalle de registro de hub"
|
||||
@@ -261,6 +305,8 @@ es:
|
||||
sku: "SKU"
|
||||
subtotal: "Subtotal"
|
||||
tax_rate: "% Impuestos"
|
||||
with_tax_incl: "%{amount}impuestos incluidos"
|
||||
producer_mail_qty: Cant.
|
||||
validators:
|
||||
date_time_string_validator:
|
||||
not_string_error: "debe ser una cadena"
|
||||
@@ -268,9 +314,40 @@ es:
|
||||
integer_array_validator:
|
||||
not_array_error: "debe ser una matriz"
|
||||
invalid_element_error: "debe contener solo enteros válidos"
|
||||
report_job:
|
||||
report_failed: |
|
||||
Este informe ha fallado. Puede que sea demasiado grande para procesarlo.
|
||||
Lo investigaremos, avísenos si el problema persiste.
|
||||
backorder_mailer:
|
||||
backorder_failed:
|
||||
subject: "Ha fallado un pedido pendiente automático"
|
||||
headline: "Fallo en el pedido pendiente"
|
||||
description: |
|
||||
Intentamos realizar o actualizar un pedido pendiente de artículos agotados, pero
|
||||
algo salió mal. Es posible que tenga existencias negativas y
|
||||
deba resolver el problema para pedir más existencias.
|
||||
hints: |
|
||||
Es posible que tenga que ir a la configuración de OIDC y volver a conectar su cuenta.
|
||||
Compruebe también que el catálogo de su proveedor no ha cambiado y siguen
|
||||
disponibles todos los productos que necesita. Póngase en contacto con nosotras si
|
||||
tiene alguna duda.
|
||||
order: "Pedido afectado: %{number}"
|
||||
stock: "Existencias"
|
||||
product: "Producto"
|
||||
backorder_incomplete:
|
||||
subject: "No se ha completado un pedido pendiente automático"
|
||||
headline: "Su pedido pendiente sigue siendo un borrador"
|
||||
description: |
|
||||
Intentamos completar un pedido pendiente de artículos
|
||||
agotados pero algo salió mal. Las cantidades de pedidos pendientes
|
||||
pueden ser demasiado altas si ha habido cancelaciones. Su pedido
|
||||
pendiente no se cumplirá mientras está en estado de borrador.
|
||||
hints: |
|
||||
Es posible que tenga que ir a la configuración de OIDC y volver a conectar su cuenta.
|
||||
Compruebe también que el catálogo de su proveedor no ha cambiado y siguen
|
||||
disponibles todos los productos que necesita. Póngase en contacto con nosotras si
|
||||
tiene alguna duda.
|
||||
affected: "%{enterprise}: %{order_cycle}"
|
||||
enterprise_mailer:
|
||||
confirmation_instructions:
|
||||
subject: "Confirma la dirección de correo electrónico de %{enterprise}"
|
||||
@@ -296,6 +373,7 @@ es:
|
||||
heading: "Informe listo para descargar"
|
||||
intro: |
|
||||
Este enlace caducara en una semana
|
||||
link_label: "%{name}"
|
||||
shipment_mailer:
|
||||
shipped_email:
|
||||
dear_customer: "Estimada consumidora,"
|
||||
@@ -449,6 +527,15 @@ es:
|
||||
depth: "Profundidad"
|
||||
payment_could_not_process: "No se pudo procesar el pago"
|
||||
payment_could_not_complete: "No se pudo completar el pago"
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "Se ha producido un error en la comunicación con la API, inténtelo de nuevo más tarde."
|
||||
invalid_voucher: "El bono no es válido"
|
||||
not_found_voucher: "Lo sentimos, no hemos podido encontrar ese vale, por favor, compruebe el código."
|
||||
vine_voucher_redeemer_service:
|
||||
errors:
|
||||
vine_api: "Ha habido un error en la comunicación con el API"
|
||||
redeeming_failed: "Error al canjear el bono"
|
||||
actions:
|
||||
create_and_add_another: "Crear y agregar otro"
|
||||
create: "Crear"
|
||||
@@ -469,12 +556,14 @@ es:
|
||||
filters:
|
||||
categories:
|
||||
title: Categorías
|
||||
selected_categories: "%{count} categorías seleccionadas"
|
||||
producers:
|
||||
title: Productoras
|
||||
colums: Columnas
|
||||
columns:
|
||||
image: Imagen
|
||||
name: Nombre
|
||||
unit_scale: Escala de unidades
|
||||
unit: Unidad
|
||||
unit_value: Valor unidad
|
||||
display_as: Mostrar unidad como
|
||||
@@ -504,10 +593,13 @@ es:
|
||||
clone: Duplicar
|
||||
delete: Borrar
|
||||
remove: Eliminar
|
||||
preview: Vista previa
|
||||
image:
|
||||
edit: Editar
|
||||
product_preview:
|
||||
product_preview: Vista previa del producto
|
||||
shop_tab: Tienda
|
||||
product_details_tab: Detalles del producto
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "No puede cambiar un pedido cancelado."
|
||||
begins_at: Empieza en
|
||||
@@ -572,6 +664,7 @@ es:
|
||||
has_n_rules: "Tiene %{num} reglas"
|
||||
unsaved_confirm_leave: "Has cambios sin guardar en esta página ¿Continuar sin guardar?"
|
||||
available_units: "Unidades disponibles"
|
||||
terms_of_service_have_been_updated_html: "Se han actualizado las condiciones de servicio de Open Food Network:%{tos_link}"
|
||||
terms_of_service: Leer los términos de servicio
|
||||
accept_terms_of_service: Aceptar los terminos de servicio
|
||||
shopfront_settings:
|
||||
@@ -624,6 +717,12 @@ es:
|
||||
info_html: "Matomo es una aplicación de análisis web y móvil. Puede alojar Matomo localmente o utilizar un servicio alojado en la nube. Ver <a href='http://matomo.org' target='_blank'>matomo.org</a> para más información."
|
||||
config_instructions_html: "Aquí puede configurar la integración de OFN Matomo. La siguiente URL de Matomo debe apuntar a la instancia de Matomo a la que se enviará la información de seguimiento del usuario; si se deja vacío, el seguimiento del usuario Matomo se desactivará. El campo ID del sitio no es obligatorio, pero es útil si está rastreando más de un sitio web en una sola instancia de Matomo; se puede encontrar en la consola de la instancia de Matomo."
|
||||
config_instructions_tag_manager_html: "La configuración de la URL de Matomo Tag Manager habilita Matomo Tag Manager. Esta herramienta le permite configurar eventos analíticos. La URL de Matomo Tag Manager se copia de la sección Código de instalación de Matomo Tag Manager. Asegúrese de seleccionar el contenedor y el entorno correctos, ya que estas opciones cambian la URL."
|
||||
connected_app_settings:
|
||||
edit:
|
||||
title: "Ajustes de la aplicación conectada"
|
||||
enabled_legend: "Aplicaciones conectadas habilitadas"
|
||||
update:
|
||||
resource: Ajustes de la aplicación conectada
|
||||
customers:
|
||||
index:
|
||||
new_customer: "Nuevo Consumidor"
|
||||
@@ -715,6 +814,7 @@ es:
|
||||
variants:
|
||||
infinity: "infinito"
|
||||
to_order_tip: "Los artículos hechos según demanda no tienen un nivel de stock, como por ejemplo panes hechos según demanda."
|
||||
back_to_products_list: "Volver a la lista de productos"
|
||||
editing_product: "Editando producto"
|
||||
tabs:
|
||||
product_details: "Detalles del Producto"
|
||||
@@ -742,6 +842,9 @@ es:
|
||||
search_for_producers: Buscar productoras
|
||||
select_producer: Seleccionar productora
|
||||
all_producers: Todas las productoras
|
||||
search_for_categories: Buscar por categorías
|
||||
select_category: Seleccionar categoría
|
||||
all_categories: Todas las categorías
|
||||
producers:
|
||||
label: Productoras
|
||||
categories:
|
||||
@@ -762,11 +865,20 @@ es:
|
||||
new_variant: Nueva variante
|
||||
bulk_update:
|
||||
success: Cambios guardados
|
||||
delete_product:
|
||||
success: ' El producto ha sido eliminado'
|
||||
error: No se puede eliminar el producto
|
||||
delete_variant:
|
||||
success: ' La variante ha sido eliminada'
|
||||
error: No se puede eliminar la variante
|
||||
variant_row:
|
||||
none_tax_category: Ninguno
|
||||
category_field_name: "Categoría"
|
||||
tax_category_field_name: "Categoría de impuestos"
|
||||
producer_field_name: "Productora"
|
||||
clone:
|
||||
success: Se ha clonado el producto
|
||||
error: No se ha podido clonar el producto
|
||||
product_import:
|
||||
title: Importación de productos
|
||||
file_not_found: Archivo no encontrado o no se pudo abrir
|
||||
@@ -779,7 +891,9 @@ es:
|
||||
conditional_blank: no puede estar en blanco si unit_type está en blanco
|
||||
no_product: no coincide con ningún producto en la base de datos
|
||||
not_found: no encontrado en la base de datos
|
||||
category_not_found: no coincide con las categorías permitidas. Consulta las categorías correctas para elegir en la página de importación de productos o comprueba que no haya ningún error ortográfico.
|
||||
not_updatable: No se puede actualizar sobre productos existentes a través de la importación de productos
|
||||
values_must_be_same: debe ser el mismo para los productos con el mismo nombre
|
||||
blank: no puede estar vacío
|
||||
products_no_permission: no tienes permiso para administrar productos para esta organización
|
||||
inventory_no_permission: no tienes permiso para crear inventario para esta productora
|
||||
@@ -809,6 +923,7 @@ es:
|
||||
dfc_import_form:
|
||||
title: "Importar del catálogo DFC"
|
||||
enterprise: "Organización"
|
||||
catalog_url: "URL del catálogo DFC"
|
||||
import: "Importar"
|
||||
import:
|
||||
review: Revisión
|
||||
@@ -911,6 +1026,7 @@ es:
|
||||
orders:
|
||||
edit:
|
||||
order_sure_want_to: ¿Está seguro de que desea %{event} este pedido?
|
||||
tax_on_fees: "Impuesto sobre las tasas"
|
||||
invoice_email_sent: 'Se ha enviado correo electrónico con la factura.'
|
||||
order_email_resent: 'El correo electrónico del pedido se ha reenviado'
|
||||
bulk_management:
|
||||
@@ -996,6 +1112,8 @@ es:
|
||||
phone: Teléfono
|
||||
phone_placeholder: ej. 98 7654 3210
|
||||
whatsapp_phone: Número de teléfono WhatsApp
|
||||
whatsapp_phone_placeholder: e. +34 666 666666
|
||||
whatsapp_phone_tip: "Este número se mostrará en tu perfil público para que se abra como un enlace de WhatsApp."
|
||||
website: Website
|
||||
website_placeholder: ej. www.truffles.com
|
||||
enterprise_fees:
|
||||
@@ -1062,7 +1180,7 @@ es:
|
||||
permalink:
|
||||
permalink: Permalink (sin espacios)
|
||||
permalink_tip: "Se usa para crear la URL de tu tienda: %{link}nombre-de-tu-tienda/shop"
|
||||
link_to_front: Link a la tienda
|
||||
link_to_front: Enlace a la tienda
|
||||
link_to_front_tip: Enlace directo a tu tienda en Open Food Networks
|
||||
ofn_uid: UID de OFN
|
||||
ofn_uid_tip: La identificación única utilizada para identificar la organización en Open Food Network.
|
||||
@@ -1119,6 +1237,7 @@ es:
|
||||
shopfront_sort_by_producer: "Por productora"
|
||||
shopfront_sort_by_category_placeholder: "Categoría"
|
||||
shopfront_sort_by_producer_placeholder: "Productora"
|
||||
display_remaining_stock: "Mostrar las existencias restantes en el escaparate si hay pocas disponibles"
|
||||
display_remaining_stock_tip: "Informe a los compradores cuando solo queden 3 artículos o menos."
|
||||
enabled: "Habilitado"
|
||||
disabled: "Deshabilitado"
|
||||
@@ -1175,25 +1294,42 @@ es:
|
||||
voucher_code: Código promocional
|
||||
rate: Impuesto
|
||||
label: Etiqueta
|
||||
purpose: Propósito
|
||||
expiry: Caducidad
|
||||
use_limit: Utilización/Límite
|
||||
customers: Consumidora
|
||||
net_value: Valor neto
|
||||
active: ¿Activo?
|
||||
add_new: Añadir nuevo
|
||||
no_voucher_yet: Todavía no hay bonos
|
||||
white_label:
|
||||
legend: "Etiqueta blanca"
|
||||
hide_ofn_navigation: "Ocultar la navegación de OFN"
|
||||
upload_logo: "Logo que se utilizará en el escaparate"
|
||||
remove_logo: "Eliminar el logo"
|
||||
remove_logo_confirm: "¿Está seguro de que desea eliminar este logo?"
|
||||
remove_logo_success: "Logo eliminado"
|
||||
white_label_logo_link_label: "Enlace para el log que se usara en el escaparate"
|
||||
hide_groups_tab: "Ocultar la pestaña de grupos en el escaparate"
|
||||
create_custom_tab: "Crear pestaña personalizada en el escaparate"
|
||||
custom_tab_title: "Título de la pestaña personalizada"
|
||||
custom_tab_content: "Contenido de la ficha personalizada"
|
||||
connected_apps:
|
||||
legend: "Aplicaciones connectadas"
|
||||
affiliate_sales_data:
|
||||
enable: "Permitir compartir datos"
|
||||
disable: "Dejar de compartir"
|
||||
loading: "Cargando"
|
||||
need_to_be_manager: "Sólo los administradores pueden conectar aplicaciones."
|
||||
discover_regen:
|
||||
enable: "Permitir compartir datos"
|
||||
disable: "Dejar de compartir"
|
||||
loading: "Cargando"
|
||||
need_to_be_manager: "Sólo los administradores pueden conectar aplicaciones."
|
||||
vine:
|
||||
enable: "Conectar"
|
||||
disable: "Desconectar"
|
||||
need_to_be_manager: "Sólo los administradores pueden conectar aplicaciones."
|
||||
actions:
|
||||
edit_profile: Configuración
|
||||
properties: Propiedades
|
||||
@@ -1246,8 +1382,10 @@ es:
|
||||
contact_name: Nombre de Contacto
|
||||
edit:
|
||||
editing: 'Configuración:'
|
||||
back_link: Volver a la lista de organizaciones
|
||||
new:
|
||||
title: Nueva Organización
|
||||
back_link: Volver a la lista de organizaciones
|
||||
welcome:
|
||||
welcome_title: Bienvenida a Open Food Network!
|
||||
welcome_text: Has creado correctamente un
|
||||
@@ -1285,6 +1423,7 @@ es:
|
||||
re_notify_producers: Re notificar a los productores
|
||||
notify_producers_tip: Esto le enviará un correo a cada productor con su lista de pedidos.
|
||||
date_time_warning_modal_content:
|
||||
proceed: 'Proceda de todos modos'
|
||||
cancel: 'Cancelar'
|
||||
incoming:
|
||||
incoming: "Entrante"
|
||||
@@ -1412,6 +1551,7 @@ es:
|
||||
could_not_resume_the_order: No se pudo reanudar el pedido
|
||||
select2:
|
||||
searching: Buscando...
|
||||
no_matches: No se han encontrado coincidencias
|
||||
shared:
|
||||
user_guide_link:
|
||||
user_guide: Manual de Usuario
|
||||
@@ -1419,6 +1559,8 @@ es:
|
||||
has_no_payment_methods: "%{enterprise} no tiene formas de pago"
|
||||
has_no_shipping_methods: "%{enterprise} no tiene ningún método de envío"
|
||||
has_no_enterprise_fees: "%{enterprise} no tiene comisiones de organización"
|
||||
flashes:
|
||||
dismiss: Desestimar
|
||||
side_menu:
|
||||
enterprise:
|
||||
primary_details: "Detalles Principales"
|
||||
@@ -1438,6 +1580,8 @@ es:
|
||||
shop_preferences: "Configuración de la tienda"
|
||||
users: "Usuarias"
|
||||
vouchers: Bonos
|
||||
white_label: "Etiqueta blanca"
|
||||
connected_apps: "Aplicaciones connectadas"
|
||||
enterprise_group:
|
||||
primary_details: "Detalles Principales"
|
||||
users: "Usuarias"
|
||||
@@ -1473,10 +1617,14 @@ es:
|
||||
pack_by_customer: Pack por Consumidor
|
||||
pack_by_supplier: Pack por proveedor
|
||||
pack_by_product: Empaquetar por producto
|
||||
display:
|
||||
display_anyway: "Mostrar de todos modos"
|
||||
download:
|
||||
button: "Descargar informe"
|
||||
show:
|
||||
report_link_label: Descargar informe (si está disponible)
|
||||
revenues_by_hub:
|
||||
description: Ingresos por grupo
|
||||
orders_and_distributors:
|
||||
name: Pedidos y Distribuidores
|
||||
description: Pedidos con detalles de distribuidor
|
||||
@@ -1505,6 +1653,8 @@ es:
|
||||
enterprise_fee_summary:
|
||||
name: "Resumen de las comisiones de la organización"
|
||||
description: "Resumen de las comisiones de la organización recolectadas"
|
||||
suppliers:
|
||||
name: Proveedoras
|
||||
errors:
|
||||
no_report_type: "Por favor especifique un tipo de reporte"
|
||||
report_not_found: "El reporte no fue encontrado"
|
||||
@@ -1540,6 +1690,7 @@ es:
|
||||
oidc_settings:
|
||||
index:
|
||||
connect: "Conecta tu cuenta"
|
||||
disconnect: "Desconectar"
|
||||
view_account: "Para ver tu cuenta, ver:"
|
||||
subscriptions:
|
||||
index:
|
||||
@@ -1810,7 +1961,7 @@ es:
|
||||
login: "login"
|
||||
contact: "contactar"
|
||||
require_customer_login: "Sólo las consumidoras aprobadas pueden acceder a esta tienda."
|
||||
require_login_2_html: "¿Quieres comprar en esta tienda? %{contact}%{enterprise}y solicita tu admisión."
|
||||
require_login_2_html: "¿Quieres comprar en esta tienda? %{contact} %{enterprise} y solicita tu admisión."
|
||||
require_customer_html: "Si desea comenzar a comprar aquí, por favor %{contact} %{enterprise} para preguntar acerca de incorporación."
|
||||
select_oc:
|
||||
select_oc_html: "Por favor <span class='highlighted'>debes elegir cuando quieres tu pedido</span>, para poder ver qué productos hay disponibles."
|
||||
@@ -2051,6 +2202,7 @@ es:
|
||||
order_back_to_store: Volver a la Tienda
|
||||
order_back_to_cart: Volver al Carrito
|
||||
order_back_to_website: Volver al sitio web
|
||||
checkout_summary_title: Resumen de caja
|
||||
bom_tip: "Usa esta página para alterar la cantidad del producto en varios pedidos a la vez. Los productos pueden ser eliminados de los pedidos si es necesario. "
|
||||
unsaved_changes_warning: "Hay cambios no guardados, se perderán si continúas."
|
||||
unsaved_changes_error: "Los campos con bordes rojos contienen errores."
|
||||
@@ -2330,6 +2482,7 @@ es:
|
||||
orders_bought_already_confirmed: "* ya confirmados"
|
||||
orders_confirm_cancel: "Seguro que desea cancelar este pedido?"
|
||||
order_processed_successfully: "Su pedido ha sido procesado con éxito."
|
||||
thank_you_for_your_order: "Gracias por su pedido"
|
||||
products_cart_distributor_choice: "Distribuidora para tu pedido:"
|
||||
products_cart_distributor_change: "Su distribuidora para este pedido se cambiará a %{name} si agregas este producto al carrito."
|
||||
products_cart_distributor_is: "Tu distribuidora para este pedido es %{name}."
|
||||
@@ -2447,6 +2600,7 @@ es:
|
||||
contact_field_required: "Debes introducir un contacto principal."
|
||||
phone_field: "Número de teléfono"
|
||||
whatsapp_phone_field: "Número de teléfono WhatsApp"
|
||||
whatsapp_phone_tooltip: "Este número se mostrará en tu perfil público para que se abra como un enlace de WhatsApp."
|
||||
phone_field_placeholder: "p.ej. 93 250 16 45"
|
||||
type:
|
||||
title: "Tipo"
|
||||
@@ -2765,6 +2919,10 @@ es:
|
||||
report_header_hub_contact_name: "Nombre de contacto del grupo"
|
||||
report_header_hub_email: "Correo electrónico público del grupo"
|
||||
report_header_hub_owner_email: Correo electrónico del responsable de grupo
|
||||
report_header_hub_address_line1: "Dirección 1 del grupo"
|
||||
report_header_hub_address_line2: "Dirección 2 del grupo"
|
||||
report_header_hub_address_zipcode: "Código postal del grupo"
|
||||
report_header_hub_address_state_name: "Región del grupo"
|
||||
report_header_code: Código
|
||||
report_header_paid: ¿Pagado?
|
||||
report_header_delivery: ¿Entregado?
|
||||
@@ -2775,7 +2933,7 @@ es:
|
||||
report_header_ship_street_2: Calle de envío 2
|
||||
report_header_ship_city: Ciudad de envío
|
||||
report_header_ship_postcode: Código postal de envío
|
||||
report_header_ship_state: Estado del envío
|
||||
report_header_ship_state: Provincia de envío
|
||||
report_header_billing_street: Calle de facturación
|
||||
report_header_billing_street_2: Calle de facturación 2
|
||||
report_header_billing_street_3: Calle de facturación 3
|
||||
@@ -2827,6 +2985,7 @@ es:
|
||||
report_header_supplier: Proveedor
|
||||
report_header_producer: Productora
|
||||
report_header_producer_suburb: Barrio Productora
|
||||
report_header_producer_address: Dirección de la productora
|
||||
report_header_unit: Unidad
|
||||
report_header_group_buy_unit_quantity: Agrupar por cantidad unidad
|
||||
report_header_cost: Coste
|
||||
@@ -2900,6 +3059,8 @@ es:
|
||||
report_header_transaction_fee: Comisión por transacción (sin impuestos)
|
||||
report_header_total_untaxable_admin: Total de ajustes administrativos no tributables (sin impuestos)
|
||||
report_header_total_taxable_admin: Total de ajustes tributarios de administración (impuestos incluidos)
|
||||
report_header_voucher_label: Etiqueta del bono
|
||||
report_header_voucher_amount: "Cantidad del bono (%{currency_symbol})"
|
||||
invoice_date: "Fecha de factura"
|
||||
due_date: "Fecha de vencimiento"
|
||||
account_code: "Código de cuenta"
|
||||
@@ -3209,6 +3370,7 @@ es:
|
||||
processing: "procesando"
|
||||
void: "pendiente"
|
||||
invalid: "inválido"
|
||||
quantity_unavailable: "Existencias insuficientes. ¡Artículo no guardado!"
|
||||
quantity_unchanged: "Cantidad sin cambios respecto a la cantidad anterior."
|
||||
resend_user_email_confirmation:
|
||||
resend: "Reenviar"
|
||||
@@ -3344,7 +3506,9 @@ es:
|
||||
link: "Enlace"
|
||||
numbers: "Numeros"
|
||||
redo: "Rehacer"
|
||||
strike: "Tachado"
|
||||
undo: "Deshacer"
|
||||
unlink: "Desenlazar"
|
||||
url: "URL"
|
||||
urlPlaceholder: "Por favor introduzca una URL para insertar"
|
||||
inflections:
|
||||
@@ -3584,6 +3748,7 @@ es:
|
||||
resend: "Reenviar"
|
||||
back_to_orders_list: "Volver a la lista de pedidos"
|
||||
back_to_payments_list: "Volver a la lista de pagos"
|
||||
back_to_states_list: "Volver a la lista de regiones"
|
||||
return_authorizations: "Autorizaciones de devolución"
|
||||
cannot_create_returns: "No se pueden crear devoluciones ya que este pedido no tiene unidades enviadas."
|
||||
select_stock: "Seleccionar stock"
|
||||
@@ -3628,6 +3793,7 @@ es:
|
||||
credit_card: "Tarjeta de crédito"
|
||||
new_payment: "Nuevo pago"
|
||||
capture: "Pagado"
|
||||
capture_and_complete_order: "Capturar y completar el pedido"
|
||||
void: "Pendiente"
|
||||
login: "Iniciar sesión"
|
||||
password: "Contraseña"
|
||||
@@ -3671,6 +3837,7 @@ es:
|
||||
tax_rate_amount_explanation: "Las tasas de impuestos son una cantidad decimal para ayudar en los cálculos (es decir, si la tasa de impuestos es del 5%, introduzca 0.05)"
|
||||
included_in_price: "Incluido en el precio"
|
||||
show_rate_in_label: "Mostrar impuesto en la etiqueta"
|
||||
back_to_tax_rates_list: "Volver a la lista de impuestos"
|
||||
tax_settings: "Configuración de Impuestos"
|
||||
zones: "Zonas"
|
||||
new_zone: "Nueva zona"
|
||||
@@ -3683,6 +3850,7 @@ es:
|
||||
iso_name: "Nombre ISO"
|
||||
states_required: "Estados requeridos"
|
||||
editing_country: "Editar país"
|
||||
back_to_countries_list: "Volver a la lista de países"
|
||||
states: "Estados"
|
||||
abbreviation: "Abreviatura"
|
||||
new_state: "Nuevo estado"
|
||||
@@ -3696,6 +3864,7 @@ es:
|
||||
shipping_categories: "Categorías de envío"
|
||||
new_shipping_category: "Nueva categoría de envío"
|
||||
back_to_shipping_categories: "Volver a las categorías de envío"
|
||||
editing_shipping_category: "Edición de la categoría de envío"
|
||||
name: "Nombre"
|
||||
description: "Descripción"
|
||||
type: "Tipo"
|
||||
@@ -3894,7 +4063,7 @@ es:
|
||||
one: "1 pedido seleccionado"
|
||||
sortable_header:
|
||||
payment_state: "Estado del pago"
|
||||
shipment_state: "Estado del envío"
|
||||
shipment_state: "Provincia de envío"
|
||||
completed_at: "Completado en"
|
||||
number: "Número"
|
||||
state: "Estado"
|
||||
@@ -3948,6 +4117,8 @@ es:
|
||||
many: "Tienes %{count} ciclos de pedido activos."
|
||||
other: "Tienes %{count} ciclos de pedido activos."
|
||||
manage_order_cycles: "GESTIONA LOS CICLOS DE PEDIDO"
|
||||
version:
|
||||
view_all_releases: Ver todas las publicaciones
|
||||
shipping_methods:
|
||||
index:
|
||||
shipping_methods: "Métodos de envío"
|
||||
@@ -4074,6 +4245,7 @@ es:
|
||||
product_name: nombre del producto
|
||||
primary_taxon_form:
|
||||
product_category: categoría del producto
|
||||
search_for_categories: "Buscar por categorias"
|
||||
group_buy_form:
|
||||
group_buy: "¿Agrupado por?"
|
||||
bulk_unit_size: Tamaño de la unidad a granel
|
||||
@@ -4097,11 +4269,13 @@ es:
|
||||
back_to_users_list: "Volver a la lista de usuarias"
|
||||
general_settings: "Configuración general"
|
||||
form:
|
||||
disabled: "¿Deshabilitado?"
|
||||
email: "Email"
|
||||
roles: "Roles"
|
||||
enterprise_limit: "Límite de la Organización"
|
||||
confirm_password: "Confirmar contraseña"
|
||||
password: "Contraseña"
|
||||
locale: "Idioma"
|
||||
email_confirmation:
|
||||
confirmation_pending: "La confirmación por correo electrónico está pendiente. Hemos enviado un correo electrónico de confirmación a %{address}."
|
||||
variants:
|
||||
@@ -4125,6 +4299,7 @@ es:
|
||||
display_name: "Nombre para mostrar"
|
||||
display_as_placeholder: 'p. ej. 2 Kg'
|
||||
display_name_placeholder: 'p. ej. Tomates'
|
||||
unit_scale: "Escala de unidades"
|
||||
unit: Unidad
|
||||
price: Precio
|
||||
unit_value: Valor unidad
|
||||
@@ -4142,13 +4317,19 @@ es:
|
||||
completed_at: "Completado en"
|
||||
state: "Provincia"
|
||||
payment_state: "Estado del pago"
|
||||
shipment_state: "Estado del envío"
|
||||
shipment_state: "Provincia de envío"
|
||||
email: "Email"
|
||||
total: "Total"
|
||||
billing_address_name: "Nombre"
|
||||
taxons:
|
||||
back_to_list: "Volver a la lista de categoría de producto"
|
||||
index:
|
||||
title: "Categorías de Producto"
|
||||
new_taxon: 'Nueva categoría de producto'
|
||||
new:
|
||||
title: "Nueva categoría de producto"
|
||||
edit:
|
||||
title: "Editar la categoría del producto"
|
||||
form:
|
||||
name: Nombre
|
||||
description: Descripción
|
||||
@@ -4261,6 +4442,7 @@ es:
|
||||
thanks: "Gracias por hacer negocios."
|
||||
track_information: "Información de seguimiento: %{tracking}"
|
||||
track_link: "Enlace de seguimiento: %{url}"
|
||||
picked_up_subject: "Notificación de recogida"
|
||||
test_mailer:
|
||||
test_email:
|
||||
greeting: "¡Felicidades!"
|
||||
@@ -4309,6 +4491,7 @@ es:
|
||||
api_keys:
|
||||
regenerate_key: "Regenerar llave"
|
||||
webhook_endpoints:
|
||||
description: Los eventos del sistema pueden desencadenar webhooks a sistemas externos.
|
||||
event_types:
|
||||
order_cycle_opened: Ciclo de pedidos abierto
|
||||
developer_settings:
|
||||
@@ -4448,3 +4631,4 @@ es:
|
||||
previous: Anterior
|
||||
invisible_captcha:
|
||||
sentence_for_humans: "Por favor, dejalo vacio"
|
||||
timestamp_error_message: "Por favor, inténtelo de nuevo después de 5 segundos."
|
||||
|
||||
@@ -308,6 +308,8 @@ fr:
|
||||
sku: "Référence Produit"
|
||||
subtotal: "Sous-total"
|
||||
tax_rate: "TVA applicable"
|
||||
with_tax_incl: "%{amount} taxes inclus"
|
||||
producer_mail_qty: Quantité
|
||||
validators:
|
||||
date_time_string_validator:
|
||||
not_string_error: "doit être une série"
|
||||
@@ -527,6 +529,15 @@ fr:
|
||||
depth: "Profondeur"
|
||||
payment_could_not_process: "Le paiement n'a pas pu être traité"
|
||||
payment_could_not_complete: "Le paiement n'a pas pu être finalisé"
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "Il y a eu une erreur de communication avec l'API, merci de réessayer."
|
||||
invalid_voucher: "Le bon de réduction n'est pas valide."
|
||||
not_found_voucher: "Désolé, nous n'avons pas trouvé ce bon de réduction. Merci de vérifier le code qui vous a été transmis."
|
||||
vine_voucher_redeemer_service:
|
||||
errors:
|
||||
vine_api: "Il y a eu une erreur de communication avec l'API"
|
||||
redeeming_failed: "Echec de la prise en compte du bon de réduction"
|
||||
actions:
|
||||
create_and_add_another: "Créer et ajouter nouveau"
|
||||
create: "Créer"
|
||||
@@ -718,7 +729,7 @@ fr:
|
||||
enabled_legend: "Applications connectées autorisées"
|
||||
connected_apps_enabled:
|
||||
discover_regen: Portail Discover Regenerative
|
||||
affiliate_sales_data: API de commandes anonymisées DFC à fins de recherche
|
||||
affiliate_sales_data: API DFC de commandes anonymisées à fins de recherche
|
||||
vine: Éditeur de bons de réduction (VINE)
|
||||
update:
|
||||
resource: Paramètres de l'application connectée
|
||||
@@ -901,6 +912,7 @@ fr:
|
||||
category_field_name: "Catégorie"
|
||||
tax_category_field_name: "TVA applicable"
|
||||
producer_field_name: "Producteur"
|
||||
select_unit_scale: Sélectionner l'échelle d'unité
|
||||
clone:
|
||||
success: Le produit a bien été dupliqué
|
||||
error: Impossible de dupliquer le produit
|
||||
@@ -1343,7 +1355,7 @@ fr:
|
||||
connected_apps:
|
||||
legend: "Applications connectées"
|
||||
affiliate_sales_data:
|
||||
title: "Programme de recherche INRAE / UFC QUE CHOISIR"
|
||||
title: "Programme de recherche INRAE"
|
||||
tagline: "Autoriser ce programme de recherche à accéder à vos données de commandes anonymisées"
|
||||
enable: "Autoriser le partage de données"
|
||||
disable: "Arrêter le partage"
|
||||
@@ -1351,7 +1363,7 @@ fr:
|
||||
need_to_be_manager: "Seuls les gestionnaires peuvent connecter des applications."
|
||||
description_html: |
|
||||
<p>
|
||||
L'INRAE et UFC QUE CHOISIR travaillent conjointement sur les prix des produits distribués en circuit court, comparés aux prix des mêmes produits vendus en supermarché, pour un échantillon de produits. Les données utilisées dans le cadre de ce programme de recherche sont une agrégation des données fournies par plusieurs plateformes de vente en circuit court en France. Aucun prix de produit d'une boutique en particulier ne sera transmis publiquement à travers ce programme.
|
||||
L'INRAE travaille sur les prix des produits distribués en circuit court, comparés aux prix des mêmes produits vendus en supermarché, pour un échantillon de produits. Les données utilisées dans le cadre de ce programme de recherche sont une agrégation des données fournies par plusieurs plateformes de vente en circuit court en France. Aucun prix de produit d'une boutique en particulier ne sera transmis publiquement à travers ce programme.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://apropos.coopcircuits.fr/"
|
||||
@@ -2038,6 +2050,8 @@ fr:
|
||||
no_shipping_methods_available: Il n'est pas possible de passer commande car il n'y a pas d'options de livraison / récupération de commande. Veuillez contacter le gestionnaire de la boutique.
|
||||
voucher_not_found: Non trouvé
|
||||
add_voucher_error: Il y a eu une erreur lors de l'ajout de votre réduction.
|
||||
create_voucher_error: "Il y a eu une erreur lors de la création du bon de réduction : %{error}"
|
||||
voucher_redeeming_error: Il y a eu une erreur lors de la tentative d'utilisation de votre bon de réduction.
|
||||
shops:
|
||||
hubs:
|
||||
show_closed_shops: "Afficher toutes les boutiques"
|
||||
@@ -2651,6 +2665,7 @@ fr:
|
||||
confirm_hub_change: "Confirmer? Cette action modifiera la boutique sélectionnée et tous les articles de votre panier seront effacés."
|
||||
confirm_oc_change: "Confirmer? Cette action modifiera le cycle de vente sélectionné et tous les articles de votre panier seront effacés."
|
||||
location_placeholder: "Saisissez une localisation..."
|
||||
gmap_load_failure: "Impossible de charger la carte. Veuillez vérifier les paramètres de votre navigateur et accepter les cookies pour ce site web."
|
||||
error_required: "Champ obligatoire"
|
||||
error_number: "saisir un nombre"
|
||||
error_email: "saisir une adresse email"
|
||||
@@ -3192,11 +3207,11 @@ fr:
|
||||
report_header_total_units: Vol. total
|
||||
report_header_sum_max_total: "Somme Max Total"
|
||||
report_header_total_excl_vat: "Total HT (%{currency_symbol})"
|
||||
report_header_total_fees_excl_tax: "Total commission boutique hors taxe (%{currency_symbol})"
|
||||
report_header_total_tax_on_fees: "Total taxe sur la commission boutique (%{currency_symbol})"
|
||||
report_header_total_fees_excl_tax: "Total commission fournisseur hors taxe (%{currency_symbol})"
|
||||
report_header_total_tax_on_fees: "Total taxe sur la commission fournisseur (%{currency_symbol})"
|
||||
report_header_total: "Total (%{currency_symbol})"
|
||||
report_header_total_incl_vat: "Total TTC (%{currency_symbol})"
|
||||
report_header_total_excl_fees_and_tax: "Total hors commission boutique et taxe (%{currency_symbol})"
|
||||
report_header_total_excl_fees_and_tax: "Total hors commission fournisseur et taxe (%{currency_symbol})"
|
||||
report_header_temp_controlled: Temp Contrôlée ?
|
||||
report_header_is_producer: Producteur ?
|
||||
report_header_not_confirmed: Non confirmé
|
||||
|
||||
@@ -107,6 +107,9 @@ fr_CA:
|
||||
count_on_hand:
|
||||
using_producer_stock_settings_but_count_on_hand_set: "doit être vide car utilise les informations de stock du producteur"
|
||||
limited_stock_but_no_count_on_hand: "doit être spécifié car pas \"à volonté\""
|
||||
connected_apps:
|
||||
vine:
|
||||
api_request_error: "Une erreur s'est produite lors de la connexion à l'API VINE"
|
||||
messages:
|
||||
confirmation: "ne correspond pas %{attribute}"
|
||||
blank: "Champ obligatoire"
|
||||
@@ -525,6 +528,10 @@ fr_CA:
|
||||
depth: "Profondeur"
|
||||
payment_could_not_process: "Le paiement n'a pas pu être traité"
|
||||
payment_could_not_complete: "Le paiement n'a pas pu être finalisé"
|
||||
vine_voucher_validator_service:
|
||||
errors:
|
||||
vine_api: "There was an error communicating with the API, please try again later."
|
||||
invalid_voucher: "The voucher is not valid"
|
||||
actions:
|
||||
create_and_add_another: "Créer et ajouter nouveau"
|
||||
create: "Créer"
|
||||
@@ -717,6 +724,7 @@ fr_CA:
|
||||
connected_apps_enabled:
|
||||
discover_regen: Portail Discover Regenerative
|
||||
affiliate_sales_data: API de commandes anonymisées DFC à fins de recherche
|
||||
vine: Éditeur de bons de réduction (VINE)
|
||||
update:
|
||||
resource: Paramètres de l'application connectée
|
||||
customers:
|
||||
@@ -898,6 +906,7 @@ fr_CA:
|
||||
category_field_name: "Catégorie"
|
||||
tax_category_field_name: "Type de taxe"
|
||||
producer_field_name: "Producteur"
|
||||
select_unit_scale: Select unit scale
|
||||
clone:
|
||||
success: Le produit a bien été dupliqué
|
||||
error: Impossible de dupliquer le produit
|
||||
@@ -1376,9 +1385,25 @@ fr_CA:
|
||||
<i class="icon-external-link"></i></a>
|
||||
</p>
|
||||
vine:
|
||||
title: "Éditeur de bons de réduction (VINE)"
|
||||
tagline: "Autoriser l'utilisation de bons de réduction VINE dans votre boutique"
|
||||
enable: "Se connecter"
|
||||
disable: "Déconnecter"
|
||||
need_to_be_manager: "Seuls les gestionnaires peuvent connecter des applications."
|
||||
vine_api_key: "clé API VINE"
|
||||
vine_secret: "VINE secret"
|
||||
description_html: |
|
||||
<p>
|
||||
Pour mettre en place l'éditeur de bons de réduction VINE pour votre entreprise, entrer votre clé API et secret.
|
||||
</p>
|
||||
<p>
|
||||
<a href="#" target="_blank"><b>VINE</b>
|
||||
<i class="icon-external-link"></i></a>
|
||||
</p>
|
||||
api_parameters_empty: "Entrer une clé API et un secret"
|
||||
api_parameters_error: "Vérifiez que vous avez entré la clé API et le secret correctement. Contactez l'équipe support de la plateforme si l'erreur persiste. "
|
||||
connection_error: "Erreur de connexion à l'API. Merci de réessayer."
|
||||
setup_error: "L'API VINE n'est pas configurée. Veuillez contacter l'équipe support de la plateforme."
|
||||
actions:
|
||||
edit_profile: Paramètres
|
||||
properties: Labels / propriétés
|
||||
@@ -1675,6 +1700,7 @@ fr_CA:
|
||||
pack_by_customer: Préparation des commandes par Acheteur
|
||||
pack_by_supplier: Préparation des commandes par Producteur
|
||||
pack_by_product: Préparation des commandes par Produit
|
||||
pay_your_suppliers: Payer vos fournisseurs
|
||||
display:
|
||||
report_is_big: "Ce rapport est volumineux et risque de ralentir l'appareil sur lequel vous êtes en train de le consulter."
|
||||
display_anyway: "Afficher quand même"
|
||||
@@ -1722,6 +1748,8 @@ fr_CA:
|
||||
enterprise_fee_summary:
|
||||
name: "Résumé des marges et commissions"
|
||||
description: "Résumé des marges et commissions collectées"
|
||||
suppliers:
|
||||
name: Fournisseurs
|
||||
enterprise_fees_with_tax_report_by_order: "Détail des montants de taxe par commande"
|
||||
enterprise_fees_with_tax_report_by_producer: "Détail des montants de taxe par producteur"
|
||||
errors:
|
||||
@@ -2625,6 +2653,7 @@ fr_CA:
|
||||
confirm_hub_change: "Confirmer? Cette action modifiera la boutique sélectionnée et tous les articles de votre panier seront effacés."
|
||||
confirm_oc_change: "Confirmer? Cette action modifiera le cycle de vente sélectionné et tous les articles de votre panier seront effacés."
|
||||
location_placeholder: "Saisissez une localisation..."
|
||||
gmap_load_failure: "Unable to load map. Please check your browser settings and allow 3rd party cookies for this website."
|
||||
error_required: "Champ obligatoire"
|
||||
error_number: "saisir un nombre"
|
||||
error_email: "saisir une adresse email"
|
||||
@@ -3009,6 +3038,8 @@ fr_CA:
|
||||
report_render_options: Mise en forme
|
||||
report_header_ofn_uid: ID OFN
|
||||
report_header_order_cycle: Cycle de Vente
|
||||
report_header_order_cycle_start_date: Date d'ouverture du cycle de vente
|
||||
report_header_order_cycle_end_date: Date de fermeture du cycle de vente
|
||||
report_header_user: Utilisateur
|
||||
report_header_email: Email
|
||||
report_header_status: Statut
|
||||
@@ -3029,6 +3060,7 @@ fr_CA:
|
||||
report_header_hub_legal_name: "Raison sociale"
|
||||
report_header_hub_contact_name: "Nom du contact"
|
||||
report_header_hub_email: "Email public"
|
||||
report_header_hub_contact_email: e-mail de contact de la boutique multi-producteurs
|
||||
report_header_hub_owner_email: Email gestionnaire principal
|
||||
report_header_hub_phone: "Numéro de téléphone"
|
||||
report_header_hub_address_line1: "Adresse ligne 1"
|
||||
@@ -3101,6 +3133,8 @@ fr_CA:
|
||||
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_producer_abn_acn: Numéro de SIRET/SIREN du producteur
|
||||
report_header_producer_address: Adresse du producteur
|
||||
report_header_unit: Unité
|
||||
report_header_group_buy_unit_quantity: Nb d'unités achetées (vente par lots)
|
||||
report_header_cost: Coût
|
||||
@@ -3161,7 +3195,11 @@ fr_CA:
|
||||
report_header_total_units: Vol. total
|
||||
report_header_sum_max_total: "Somme Max Total"
|
||||
report_header_total_excl_vat: "Total HT (%{currency_symbol})"
|
||||
report_header_total_fees_excl_tax: "Total commission boutique hors taxe (%{currency_symbol})"
|
||||
report_header_total_tax_on_fees: "Total taxe sur la commission boutique (%{currency_symbol})"
|
||||
report_header_total: "Total (%{currency_symbol})"
|
||||
report_header_total_incl_vat: "Total TTC (%{currency_symbol})"
|
||||
report_header_total_excl_fees_and_tax: "Total hors commission boutique et taxe (%{currency_symbol})"
|
||||
report_header_temp_controlled: Temp Contrôlée ?
|
||||
report_header_is_producer: Producteur ?
|
||||
report_header_not_confirmed: Non confirmé
|
||||
@@ -3739,7 +3777,7 @@ fr_CA:
|
||||
many: "pièces"
|
||||
other: "pièces"
|
||||
pot:
|
||||
one: "pots"
|
||||
one: "pot"
|
||||
many: "pots"
|
||||
other: "pots"
|
||||
bundle:
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
fr_CH:
|
||||
language_name: "Français"
|
||||
time:
|
||||
formats:
|
||||
long: "%e %b %Y, %-k:%M"
|
||||
activerecord:
|
||||
models:
|
||||
spree/product: Produit
|
||||
spree/shipping_method: Option d'expédition
|
||||
attributes:
|
||||
spree/image:
|
||||
attachment: Pièce jointe
|
||||
spree/order/ship_address:
|
||||
phone: "Numéro de téléphone"
|
||||
firstname: "Prénom"
|
||||
@@ -551,6 +556,7 @@ fr_CH:
|
||||
terms_of_service: "Conditions Générales d'Utilisation"
|
||||
delete: "Supprimer le fichier"
|
||||
confirm_delete: "Etes-vous certain de vouloir supprimer les CGU & CGV actuelles ?"
|
||||
attachment: "Pièce jointe"
|
||||
number_localization:
|
||||
number_localization_settings: "Gestion localisation des nombres"
|
||||
enable_localized_number: "Utiliser le traitement international des séparateurs de milliers/centimes"
|
||||
|
||||
@@ -53,6 +53,7 @@ hu:
|
||||
primary_taxon: "Termékkategória"
|
||||
shipping_category_id: "Szállítási mód"
|
||||
supplier: "Beszállító"
|
||||
variant_unit: "Mértékegység"
|
||||
variant_unit_name: "Változat egység neve"
|
||||
unit_value: "Egység értéke"
|
||||
spree/credit_card:
|
||||
@@ -297,7 +298,7 @@ hu:
|
||||
producer_signup_case_studies_html: "Termelői regisztrációs esettanulmányok HTML"
|
||||
producer_signup_detail_html: "Termelői regisztráció részletek HTML"
|
||||
producer_signup_pricing_table_html: "Termelői regisztrációs ártáblázat HTML"
|
||||
producers_social: "Termelői szövetkezet"
|
||||
producers_social: "Közösségi média"
|
||||
resume_order: "Rendelés folytatása"
|
||||
sku: "SKU"
|
||||
subtotal: "Részösszeg"
|
||||
@@ -534,7 +535,7 @@ hu:
|
||||
producer: Termelő
|
||||
category: Kategória
|
||||
sku: SKU kód
|
||||
on_hand: "Raktáron"
|
||||
on_hand: "Készlet"
|
||||
on_demand: "Igény szerint"
|
||||
tax_category: "Adókategória"
|
||||
inherits_properties: "Megörökli az tulajdonságokat?"
|
||||
@@ -546,7 +547,7 @@ hu:
|
||||
producer: Termelő
|
||||
category: Kategória
|
||||
sku: SKU kód
|
||||
on_hand: "Raktáron"
|
||||
on_hand: "Készlet"
|
||||
on_demand: "Igény szerint"
|
||||
tax_category: "Adókategória"
|
||||
inherits_properties: "Megörökli az tulajdonságokat?"
|
||||
@@ -680,7 +681,7 @@ hu:
|
||||
matomo_tag_manager_url: "Matomo Címkekezelő URL-je"
|
||||
info_html: "A Matomo egy webes és mobilelemző alkalmazás. A Matomo helyszíni üzemeltetését vagy felhőalapú szolgáltatást is használhatja. További információért lásd: <a href='http://matomo.org' target='_blank'>matomo.org</a>."
|
||||
config_instructions_html: "Itt konfigurálhatja az OFN Matomo integrációt. Az alábbi Matomo URL-nek arra a Matomo-példányra kell mutatnia, ahová a felhasználó követési információkat küldi; ha üresen marad, a Matomo felhasználókövetés le lesz tiltva. A Webhelyazonosító mező nem kötelező, de hasznos, ha egynél több webhelyet követ nyomon egyetlen Matomo-példányon; a Matomo példánykonzolon található."
|
||||
config_instructions_tag_manager_html: "A Matomo Címkekezelő URL-címének beállítása engedélyezi a Matomo Címkekezelőt. Ez az eszköz lehetővé teszi az elemzési események beállítását. A Matomo Címkekezelő URL-címe a Matomo Tag Manager Telepítési kód szakaszából lett kimásolva. Győződjön meg róla, hogy a megfelelő tárolót és környezetet választotta, mivel ezek a beállítások megváltoztatják az URL-t."
|
||||
config_instructions_tag_manager_html: "A Matomo Címkekezelő URL-címének beállítása engedélyezi a Matomo Címkekezelőt. Ez az eszköz lehetővé teszi az elemzési események beállítását. A Matomo Címkekezelő URL-címe a Matomo Tag Manager Telepítési kód szakaszából lett kimásolva. Győződj meg róla, hogy a megfelelő tárolót és környezetet választotta, mivel ezek a beállítások megváltoztatják az URL-t."
|
||||
connected_app_settings:
|
||||
edit:
|
||||
title: "Kapcsolt app beállítás"
|
||||
@@ -860,6 +861,7 @@ hu:
|
||||
category_field_name: "Kategória"
|
||||
tax_category_field_name: "Adókategória"
|
||||
producer_field_name: "Termelő"
|
||||
select_unit_scale: Válassz mértékegységet
|
||||
clone:
|
||||
success: Sikeresen klónozta a terméket
|
||||
error: A terméket nem sikerült klónozni
|
||||
@@ -1061,9 +1063,9 @@ hu:
|
||||
business_details:
|
||||
legend: "Vállalkozás adatai"
|
||||
upload: 'feltöltés'
|
||||
abn: LÉGI ÚTON SZÁLLÍTOTT
|
||||
abn: ADÓSZÁM
|
||||
abn_placeholder: pl. 99 123 456 789
|
||||
acn: Közösségi Adószám (EU)
|
||||
acn: Cégjegyzékszám
|
||||
acn_placeholder: pl. 123 456 789
|
||||
display_invoice_logo: Logó megjelenítése a vevői összesítőkön
|
||||
invoice_text: Adj testreszabott szöveget a vevői összesítők végéhez
|
||||
@@ -1197,7 +1199,7 @@ hu:
|
||||
enable_subscriptions_false: "Tiltva"
|
||||
enable_subscriptions_true: "Engedélyezve"
|
||||
customer_names_in_reports: "Ügyfélnevek a jelentésekben"
|
||||
customer_names_tip: "Engedélyezze beszállítóinak, hogy lássák az ügyfelek nevét a jelentésekben"
|
||||
customer_names_tip: "Engedélyezd a beszállítóidnak, hogy lássák az ügyfelek nevét a jelentésekben"
|
||||
customer_names_false: "Tiltva"
|
||||
customer_names_true: "Engedélyezve"
|
||||
shopfront_message: "Online Kirakat üzenete"
|
||||
@@ -1275,7 +1277,7 @@ hu:
|
||||
vouchers:
|
||||
legend: Kuponok
|
||||
voucher_code: Kupon kód
|
||||
rate: Mérték
|
||||
rate: Arány
|
||||
label: Címke
|
||||
purpose: Cél
|
||||
expiry: Lejárati idő
|
||||
@@ -1317,7 +1319,7 @@ hu:
|
||||
</p>
|
||||
discover_regen:
|
||||
title: "Discover Regenerative"
|
||||
tagline: "Engedélyezd a Discover Regenerative számára a vállalati adatok közzétételét."
|
||||
tagline: "Engedélyezd a Discover Regenerative számára a vállalkozási adatok közzétételét."
|
||||
enable: "Adatmegosztás engedélyezése"
|
||||
disable: "Megosztás leállítása"
|
||||
loading: "Betöltés"
|
||||
@@ -1338,6 +1340,7 @@ hu:
|
||||
enable: "Csatlakozás"
|
||||
disable: "Lecsatlakozás"
|
||||
need_to_be_manager: "Csak a megfelelő jogosultsággal rendelkező felhasználók csatlakoztathatnak alkalmazásokat."
|
||||
vine_api_key: "VINE API kulcs"
|
||||
connection_error: "API kapcsolódási hiba, kérjük próbáld újra"
|
||||
actions:
|
||||
edit_profile: Beállítások
|
||||
@@ -1391,8 +1394,10 @@ hu:
|
||||
contact_name: Kapcsolattartó neve
|
||||
edit:
|
||||
editing: 'Beállítások:'
|
||||
back_link: Vissza a válllalkozások listájához
|
||||
new:
|
||||
title: Új Vállalkozás
|
||||
back_link: Vissza a válllalkozások listájához
|
||||
welcome:
|
||||
welcome_title: Üdvözölünk az Open Food Network-ben!
|
||||
welcome_text: Sikeresen létrehoztad a
|
||||
@@ -1633,6 +1638,7 @@ hu:
|
||||
pack_by_customer: Összekészítés Ügyfelenként
|
||||
pack_by_supplier: Összekészítés Beszállítónként
|
||||
pack_by_product: Összekészítés Termékenként
|
||||
pay_your_suppliers: Fizetés a beszállítóknak
|
||||
display:
|
||||
report_is_big: "Ez a jelentés nagy méretű, így lelassíthatja az eszközödet."
|
||||
display_anyway: "Megjelenítés mindenképp"
|
||||
@@ -1678,6 +1684,8 @@ hu:
|
||||
enterprise_fee_summary:
|
||||
name: "Vállalkozási díjak összefoglalója"
|
||||
description: "Összefoglaló a beszedett vállalkozási díjakról"
|
||||
suppliers:
|
||||
name: Beszállítók
|
||||
enterprise_fees_with_tax_report_by_order: "Vállalkozási díjak adójelentéssei megrendelés alapján"
|
||||
enterprise_fees_with_tax_report_by_producer: "Vállalkozási díjak Jelentés termelőnként"
|
||||
errors:
|
||||
@@ -1688,7 +1696,7 @@ hu:
|
||||
summary_row:
|
||||
total: "ÖSSZESEN"
|
||||
table:
|
||||
select_and_search: "Válaszd ki a szűrőket, és kattintson a %{option} elemre az adatok eléréséhez."
|
||||
select_and_search: "Válaszd ki a szűrőket, és kattints a %{option} elemre az adatok eléréséhez."
|
||||
headings:
|
||||
hub: "Átvételi pont"
|
||||
customer_code: "Kód"
|
||||
@@ -1745,18 +1753,18 @@ hu:
|
||||
setup_explanation:
|
||||
title: "Előfizetések"
|
||||
just_a_few_more_steps: 'Még néhány lépés, mielőtt elkezdhetné:'''
|
||||
enable_subscriptions: "Engedélyezze a feliratkozásokat legalább egy oldalán"
|
||||
enable_subscriptions_step_1_html: 1. Nyissa meg a %{enterprises_link} oldalt, keresse meg az oldalát, és kattintson a "Kezelés" gombra
|
||||
enable_subscriptions_step_2: 2. Az "Oldalbeállítások" alatt", engedélyezze az Előfizetések opciót
|
||||
set_up_shipping_and_payment_methods_html: Állítson be %{shipping_link} és %{payment_link} metódust
|
||||
set_up_shipping_and_payment_methods_note_html: Ne feledje, hogy előfizetésekhez csak készpénzes és Stripe fizetési módok<br />használhatók
|
||||
ensure_at_least_one_customer_html: Győződjön meg arról, hogy legalább egy %{customer_link} létezik
|
||||
enable_subscriptions: "Engedélyezd a feliratkozásokat legalább egy oldalon"
|
||||
enable_subscriptions_step_1_html: 1. Nyisd meg a %{enterprises_link} oldalt, keresd meg az oldalad, és kattints a "Kezelés" gombra
|
||||
enable_subscriptions_step_2: 2. Az "Oldalbeállítások" alatt", engedélyezd az Előfizetések opciót
|
||||
set_up_shipping_and_payment_methods_html: Állíts be %{shipping_link} és %{payment_link} metódust
|
||||
set_up_shipping_and_payment_methods_note_html: Ne feledd, hogy előfizetésekhez csak készpénzes és Stripe fizetési módok<br />használhatók
|
||||
ensure_at_least_one_customer_html: Győződj meg arról, hogy legalább egy %{customer_link} létezik
|
||||
create_at_least_one_schedule: Hozz létre legalább egy ütemezést
|
||||
create_at_least_one_schedule_step_1_html: '1. Nyissa meg a következőt: %{order_cycles_link}'
|
||||
create_at_least_one_schedule_step_2: 2. Hozz létre egy rendelési ciklust, ha még nem tette meg
|
||||
create_at_least_one_schedule_step_3: 3. Kattintson a "+ Új ütemezés" gombra.", és töltse ki az űrlapot
|
||||
create_at_least_one_schedule_step_1_html: '1. Nyisd meg a következőt: %{order_cycles_link}'
|
||||
create_at_least_one_schedule_step_2: 2. Hozz létre egy rendelési ciklust, ha még nem tetted meg
|
||||
create_at_least_one_schedule_step_3: 3. Kattints a "+ Új ütemterv" gombra.", és töltsd ki az űrlapot
|
||||
once_you_are_done_you_can_html: Ha végeztél, %{reload_this_page_link}
|
||||
reload_this_page: töltse be újra ezt az oldalt
|
||||
reload_this_page: töltsd be újra ezt az oldalt
|
||||
form:
|
||||
create: "Előfizetés létrehozása"
|
||||
steps:
|
||||
@@ -1775,7 +1783,7 @@ hu:
|
||||
add: "Hozzáadás"
|
||||
details:
|
||||
details: Részletek
|
||||
invalid_error: Hoppá! Kérjük, töltse ki az összes kötelező mezőt...
|
||||
invalid_error: Hoppá! Kérjük, töltsd ki az összes kötelező mezőt...
|
||||
allowed_payment_method_types_tip: Jelenleg csak készpénzes és Stripe fizetési mód használhatók
|
||||
credit_card: Hitelkártya
|
||||
charges_not_allowed: Ez az ügyfél nem engedélyezi a díjakat
|
||||
@@ -1843,7 +1851,7 @@ hu:
|
||||
other: "%{count} megrendelés esetén visszaigazoló e-mailt küldtünk."
|
||||
send_invoice_feedback:
|
||||
one: "1 rendelés számláját elküldtük emailben."
|
||||
other: "%{count} rendelés számláját elküldtük emailben."
|
||||
other: "%{count} rendelés összegzését elküldtük emailben."
|
||||
api:
|
||||
unknown_error: "Valami elromlott. Csapatunk értesítést kapott."
|
||||
invalid_api_key: "Érvénytelen API-kulcs (%{key}) van megadva."
|
||||
@@ -2050,7 +2058,7 @@ hu:
|
||||
total_incl_tax: "Összesen (adóval együtt):"
|
||||
total_all_tax: "Összes adó:"
|
||||
abn: "Adószám:"
|
||||
acn: "Közösségi Adószám (EU):"
|
||||
acn: "Cégjegyzékszám:"
|
||||
invoice_issued_on: "Kiállítás dátuma:"
|
||||
order_number: "Rendelés sorszáma:"
|
||||
date_of_transaction: "A tranzakció dátuma:"
|
||||
@@ -2195,7 +2203,7 @@ hu:
|
||||
cookies_policy_link_desc: "Ha többet szeretne megtudni, nézze meg oldalunkat"
|
||||
cookies_policy_link: "cookie-kra vonatkozó szabályzat"
|
||||
cookies_accept_button: "Cookie-k elfogadása"
|
||||
home_shop: Tovább a termelőkhöz, átvételi pontokhoz.
|
||||
home_shop: Tovább a termelőkhöz, átvételi pontokhoz
|
||||
brandstory_headline: "Együtt, az élelem önrendelkezésért!"
|
||||
brandstory_intro: "Közösség által irányított élelmiszerrendszert építünk."
|
||||
brandstory_part1: "Az alapoktól kezdjük. \nA gazdálkodókkal, akiknek fontos, hogy közösen, egy fenntartható és mindenki számára átlátható, hazai élelemrendszert hozzunk létre. \nA bevásárlóközösségekkel, fogyasztói csoportokkal, más elosztókkal, akik összekapcsolják az embereket a termelőkkel és a termékekkel, és biztosítani szeretnék, hogy a termelők tisztességes díjazásban részesüljenek. \nAzokkal a fogyasztókkal, akik hisznek abban, hogy döntéseiknek hatása van."
|
||||
@@ -2212,7 +2220,7 @@ hu:
|
||||
system_step3: "3. Átvétel / kiszállítás"
|
||||
system_step3_text: "Válaszd a kiszállítást, vagy a személyesebb kapcsolat érdekében keresd fel a termelőt, vagy az átvételi pontot."
|
||||
cta_headline: "A jövőnk fenntartható élelmiszerhálózata."
|
||||
cta_label: "Tovább a termelőkhöz, átvételi pontokhoz."
|
||||
cta_label: "Tovább a termelőkhöz, átvételi pontokhoz"
|
||||
stats_headline: "Termelők és fogyasztók közösségét építjük."
|
||||
stats_producers: "élelmiszer-termelők"
|
||||
stats_shops: "átvételi pontok"
|
||||
@@ -2274,7 +2282,7 @@ hu:
|
||||
products_in: "itt: %{oc}"
|
||||
products_at: "itt: %{distributor}"
|
||||
products_elsewhere: "Máshol talált termékek"
|
||||
email_confirmed: "Köszönjük, hogy megerősítette e-mail címét."
|
||||
email_confirmed: "Köszönjük, hogy megerősítetted az e-mail címed."
|
||||
email_confirmation_activate_account: "Mielőtt aktiválhatnánk az új fiókodat, meg kell erősíteni az e-mail címed."
|
||||
email_confirmation_greeting: "Kedves %{contact}!"
|
||||
email_confirmation_profile_created: "A %{name} profilod sikeresen létrejött! Profilod aktiválásához meg kell erősíteni ezt az e-mail címet."
|
||||
@@ -2341,7 +2349,7 @@ hu:
|
||||
greeting: "Üdv!"
|
||||
invited_to_manage: "Meghívást kaptál, hogy kezeld a(z) %{enterprise} %{instance} szolgáltatást."
|
||||
confirm_your_email: "Egy megerősítő linket tartalmazó e-mailt kellett volna kapnia, vagy hamarosan kapni fog. Addig nem férhetsz hozzá %{enterprise} profiljához, amíg meg nem erősíted az e-mail címedet."
|
||||
set_a_password: "Ezután a rendszer kéri, hogy állítson be egy jelszót, mielőtt felügyelheti a vállalkozásot."
|
||||
set_a_password: "Ezután a rendszer kéri, hogy állítson be egy jelszót, mielőtt felügyelheti a vállalkozást."
|
||||
mistakenly_sent: "Nem tudod, miért kaptad ezt az e-mailt? További információért fordulj %{owner_email}-hoz."
|
||||
producer_mail_greeting: "Kedves"
|
||||
producer_mail_text_before: "Alább megtalálja a rendelési ciklusra vonatkozó frissítést:"
|
||||
@@ -2372,7 +2380,7 @@ hu:
|
||||
maps_open: "Nyitva"
|
||||
maps_closed: "Zárva"
|
||||
map_title: "Térkép"
|
||||
hubs_buy: "Kínálat:"
|
||||
hubs_buy: "Termékkínálat:"
|
||||
hubs_shopping_here: "Vásárlás itt"
|
||||
hubs_orders_closed: "A rendelések lezárva"
|
||||
hubs_profile_only: "Csak profil"
|
||||
@@ -2419,7 +2427,7 @@ hu:
|
||||
products_changes_saved: "Változtatások elmentve."
|
||||
products_no_results_html: "Sajnáljuk, nem található találat a következőre: %{query}"
|
||||
products_clear_search: "Keresés törlése"
|
||||
search_no_results_html: "Sajnáljuk, nem találtunk Átvételi pontot/Termelőt a következő szűrésre: %{query} Próbálkozz másik kereséssel!"
|
||||
search_no_results_html: "Sajnáljuk, nem találtunk Átvételi pontot/Termelőt a következő szűrésre: %{query} \nPróbálkozz másik kereséssel!"
|
||||
components_profiles_popover: "A Profiloknak nincs online kirakata az Open Food Network-ben, de lehet, hogy van saját fizikai vagy online átvételi pontjuk máshol."
|
||||
components_profiles_show: "Profilok megjelenítése"
|
||||
components_filters_nofilters: "Nincsenek szűrők"
|
||||
@@ -2458,7 +2466,7 @@ hu:
|
||||
groups_signup_detail: "Itt a részlet."
|
||||
login_invalid: "Rossz email cím vagy jelszó"
|
||||
producers_about: Rólunk
|
||||
producers_buy: Vásárolni valakinek
|
||||
producers_buy: 'Kínálat:'
|
||||
producers_contact: Kapcsolat
|
||||
producers_contact_phone: Hívás
|
||||
producers_contact_social: Kövesse
|
||||
@@ -2494,7 +2502,7 @@ hu:
|
||||
sell_hubs: "Átvételi Pontok"
|
||||
sell_groups: "Csoportok"
|
||||
sell_producers_detail: "Őstermelő, kistermelő, családi gazdálkodó, vagy épp kisválllalkozás vagy? Hozz létre termelői profilt az OFN-en percek alatt. Bármikor frissítheted profilod egy online áruházra, és közvetlenül ajánlhatod termékeidet a fogyasztóknak."
|
||||
sell_hubs_detail: "Bevásárlóközösséget indítanál? Vagy csak összefognál a termelőtársakkal és közösen szólítanátok meg a fogyasztókat? Hozz létre új profilt, vagy bármikor frissítheted a profilodat egy többtermelős átvételi pontra."
|
||||
sell_hubs_detail: "Bevásárlóközösséget indítanál, vagy termelői piacot működtetsz? Vagy csak összefognál a termelőtársakkal és közösen szólítanátok meg a fogyasztókat? Hozz létre új profilt, vagy bármikor frissítheted a profilodat egy többtermelős átvételi pontra."
|
||||
sell_groups_detail: "Hozz létre egy személyre szabott jegyzéket (termelők és egyéb élelmiszeripari vállalkozások) régiója vagy szervezete számára."
|
||||
sell_user_guide: "Tudj meg többet használati útmutatónkból."
|
||||
sell_listing_price: "A termelői regisztráció az OFN-en ingyenes, és az is marad. A platformon történő értékesítés volumenétől függően azonban közösségi hozzájárulást kérünk, a szoftver üzemeltetéshez és IT support biztosításához . \nRészletek hamarosan. "
|
||||
@@ -2690,7 +2698,7 @@ hu:
|
||||
enterprise_long_desc_length: "%{num} karakter / legfeljebb 600 ajánlott"
|
||||
enterprise_abn: "Adószám"
|
||||
enterprise_abn_placeholder: "például. 99 123 456 789"
|
||||
enterprise_acn: "Közösségi Adószám (EU)"
|
||||
enterprise_acn: "Cégjegyzékszám"
|
||||
enterprise_acn_placeholder: "például. 123 456 789"
|
||||
enterprise_tax_required: "Ki kell választani."
|
||||
images:
|
||||
@@ -2790,7 +2798,7 @@ hu:
|
||||
admin_enterprise_relationships_permits: "engedélyezi"
|
||||
admin_enterprise_relationships_seach_placeholder: "Keresés"
|
||||
admin_enterprise_relationships_button_create: "Létrehozás"
|
||||
admin_enterprise_relationships_to: "kapcsolat vele"
|
||||
admin_enterprise_relationships_to: "számára"
|
||||
admin_enterprise_groups: "Vállalkozási csoportok"
|
||||
admin_enterprise_groups_name: "Név"
|
||||
admin_enterprise_groups_owner: "Tulajdonos"
|
||||
@@ -2964,6 +2972,8 @@ hu:
|
||||
report_render_options: Rendezési beállítások
|
||||
report_header_ofn_uid: OFN Azonosító
|
||||
report_header_order_cycle: Rendelési Ciklus
|
||||
report_header_order_cycle_start_date: RC kezdő dátum
|
||||
report_header_order_cycle_end_date: RC záró dátum
|
||||
report_header_user: Felhasználó
|
||||
report_header_email: Email
|
||||
report_header_status: Státusz
|
||||
@@ -2984,6 +2994,7 @@ hu:
|
||||
report_header_hub_legal_name: "Átvételi Pont hivatalos neve"
|
||||
report_header_hub_contact_name: "Átvételi Pont kapcsolattartó neve"
|
||||
report_header_hub_email: "Átvételi Pont nyilvános email"
|
||||
report_header_hub_contact_email: Átvételi pont kapcsolattartói email
|
||||
report_header_hub_owner_email: Átvételi Pont üzemeltető email
|
||||
report_header_hub_phone: "Átvételi Pont telefonszám"
|
||||
report_header_hub_address_line1: "Átvételi Pont cím 1"
|
||||
@@ -3044,18 +3055,20 @@ hu:
|
||||
report_header_quantity: Mennyiség
|
||||
report_header_max_quantity: Max Mennyiség
|
||||
report_header_variant: Változat
|
||||
report_header_variant_unit_name: Változat egység neve
|
||||
report_header_variant_unit_name: Változat egysége
|
||||
report_header_variant_value: Változat értéke
|
||||
report_header_variant_unit: Változat egysége
|
||||
report_header_total_available: Összesen elérhető
|
||||
report_header_unallocated: Kiosztatlan
|
||||
report_header_max_quantity_excess: Maximális Túllépési Mennyiség
|
||||
report_header_taxons: Taxonok
|
||||
report_header_taxons: Termékkategóriák
|
||||
report_header_supplier: Beszállító
|
||||
report_header_producer: Termelő
|
||||
report_header_producer_suburb: Termelő kerület
|
||||
report_header_producer_tax_status: Termelő adókategóriája
|
||||
report_header_producer_charges_sales_tax?: ÁFA nyilvántartott
|
||||
report_header_producer_abn_acn: Termelő adószáma/cégjegyzékszáma
|
||||
report_header_producer_address: Termelő címe
|
||||
report_header_unit: Mértékegység
|
||||
report_header_group_buy_unit_quantity: Csoportos vásárlási egység mennyisége
|
||||
report_header_cost: Költség
|
||||
@@ -3115,8 +3128,12 @@ hu:
|
||||
report_header_total_max: Összesen max
|
||||
report_header_total_units: Összes egység
|
||||
report_header_sum_max_total: "Összeg Max összesen"
|
||||
report_header_total_excl_vat: "Összesen, kivéve adó (%{currency_symbol})"
|
||||
report_header_total_excl_vat: "Összesen, adó nélkül (%{currency_symbol})"
|
||||
report_header_total_fees_excl_tax: "Össz díjak adó nélkül (%{currency_symbol})"
|
||||
report_header_total_tax_on_fees: "Össz adó a díjakon (%{currency_symbol})"
|
||||
report_header_total: "Összesen (%{currency_symbol})"
|
||||
report_header_total_incl_vat: "Összesen, beleértve adó (%{currency_symbol})"
|
||||
report_header_total_excl_fees_and_tax: "Összesen díjak és adók nélkül (%{currency_symbol})"
|
||||
report_header_temp_controlled: Hűtést igényel?
|
||||
report_header_is_producer: Termelő?
|
||||
report_header_not_confirmed: Nem megerősített
|
||||
@@ -3256,7 +3273,7 @@ hu:
|
||||
enterprise_limit_reached: "Elérted a vállalkozások számlaszámonkénti standard korlátját. Írj a %{contact_email} címre, ha növelni szeretnéd."
|
||||
deleting_item_will_cancel_order: "Ez a művelet egy vagy több üres rendelést eredményez, amelyek törlésre kerülnek. Szeretné folytatni?"
|
||||
modals:
|
||||
got_it: "Megvan"
|
||||
got_it: "Értem"
|
||||
confirm: "Megerősítés"
|
||||
close: "Bezárás"
|
||||
continue: "Tovább"
|
||||
@@ -3830,7 +3847,7 @@ hu:
|
||||
cannot_create_returns: "Nem tudsz visszárut létrehozni, mivel a rendelésnek nincsenek kiszállított egységei."
|
||||
select_stock: "Válassz a készletről"
|
||||
location: "Elhelyezkedés"
|
||||
count_on_hand: "Count On Hand"
|
||||
count_on_hand: "Készleten lévő mennyiség"
|
||||
quantity: "Mennyiség"
|
||||
on_demand: "Igény szerint"
|
||||
on_hand: "Készlet"
|
||||
@@ -4066,7 +4083,7 @@ hu:
|
||||
product_properties:
|
||||
index:
|
||||
inherits_properties_checkbox_hint: "Tulajdonságokat örököl a következőtől: %{supplier}? (hacsak nincs felülírva)"
|
||||
add_product_properties: "Add hozzá a termék tulajdonságait"
|
||||
add_product_properties: "További termék tulajdonság hozzáadása"
|
||||
properties:
|
||||
index:
|
||||
properties: "Tulajdonságok"
|
||||
@@ -4482,7 +4499,7 @@ hu:
|
||||
out_of_stock: "Nincs raktáron"
|
||||
unavailable_item: "Jelenleg nem elérhető"
|
||||
shipment_states:
|
||||
backorder: utólagos rendelés
|
||||
backorder: visszamaradt rendelés/nem rendelhető
|
||||
partial: részleges
|
||||
pending: függőben levő
|
||||
ready: kész
|
||||
@@ -4537,7 +4554,7 @@ hu:
|
||||
subject: "Kérjük, erősítsd meg Open Food fiókod"
|
||||
payment_mailer:
|
||||
authorize_payment:
|
||||
subject: "Kérjük, engedélyezze fizetését %{distributor} részére az Open Food-on"
|
||||
subject: "Kérjük, engedélyezd fizetést %{distributor} részére az Open Food-on"
|
||||
instructions: "%{amount} összegű befizetésed %{distributor} részére további hitelesítést igényel. Kérjük, keresd fel a következő URL-t a fizetés engedélyezéséhez:"
|
||||
authorization_required:
|
||||
subject: "A fizetéshez az ügyfél engedélye szükséges"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class AddExternalVoucherIdExternalVoucherSetIdVoucherTypeToVouchers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :vouchers, :external_voucher_id, :uuid
|
||||
add_column :vouchers, :external_voucher_set_id, :uuid
|
||||
add_column :vouchers, :voucher_type, :string
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class UpdateIndexAndRemoveVoucherTypeFromVoucher < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
remove_column :vouchers, :voucher_type
|
||||
remove_index :vouchers, [:code, :enterprise_id], unique: true
|
||||
|
||||
add_index :vouchers, [:code, :enterprise_id]
|
||||
add_index :vouchers, [:code, :enterprise_id, :external_voucher_id], name: "index_vouchers_on_code_and_enterprise_id_and_ext_voucher_id"
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_10_30_033956) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_11_12_230401) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "plpgsql"
|
||||
@@ -1110,7 +1110,10 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_30_033956) do
|
||||
t.datetime "deleted_at", precision: nil
|
||||
t.decimal "amount", precision: 10, scale: 2, default: "0.0", null: false
|
||||
t.string "type", limit: 255, default: "Vouchers::FlatRate", null: false
|
||||
t.index ["code", "enterprise_id"], name: "index_vouchers_on_code_and_enterprise_id", unique: true
|
||||
t.uuid "external_voucher_id"
|
||||
t.uuid "external_voucher_set_id"
|
||||
t.index ["code", "enterprise_id", "external_voucher_id"], name: "index_vouchers_on_code_and_enterprise_id_and_ext_voucher_id"
|
||||
t.index ["code", "enterprise_id"], name: "index_vouchers_on_code_and_enterprise_id"
|
||||
t.index ["deleted_at"], name: "index_vouchers_on_deleted_at"
|
||||
t.index ["enterprise_id"], name: "index_vouchers_on_enterprise_id"
|
||||
end
|
||||
|
||||
@@ -21,7 +21,8 @@ Note: There is no need to install Docker Desktop on Linux.
|
||||
* You may have to deselect the option to use Docker Compose V2 in Docker settings to make our scripts work.
|
||||
|
||||
## Getting Started
|
||||
### Linux
|
||||
|
||||
1. **Clone the Repository**:
|
||||
* Open a terminal with a shell.
|
||||
* Clone the repository. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), begin by forking this repo with the Fork button in the top-right corner of this screen.
|
||||
* Use git clone to copy your fork onto your local machine.
|
||||
@@ -32,48 +33,50 @@ $ git clone https://github.com/YOUR_GITHUB_USERNAME_HERE/openfoodnetwork
|
||||
```sh
|
||||
$ git clone git@github.com:openfoodfoundation/openfoodnetwork.git
|
||||
```
|
||||
* Go at the root of the app:
|
||||
```sh
|
||||
$ cd openfoodnetwork
|
||||
```
|
||||
* Download the Docker images, build the Docker containers, seed the database with sample data, AND log the screen output from these tasks:
|
||||
```sh
|
||||
$ docker/build
|
||||
```
|
||||
* Run the Rails server and its required Docker containers:
|
||||
```sh
|
||||
$ docker/server
|
||||
```
|
||||
* The default admin user is 'ofn@example.com' with the password 'ofn123'.
|
||||
* View the app in the browser at `http://localhost:3000`.
|
||||
* You will then get the trace of the containers in the terminal. You can stop the containers using Ctrl-C in the terminal.
|
||||
* You can find some useful tips and commands [here](https://github.com/openfoodfoundation/openfoodnetwork/wiki/Docker:-useful-tips-and-commands).
|
||||
|
||||
### Windows
|
||||
* Prerequisite : don't forget to activate the execution of powershell scripts following the instruction on [this page chosing "Using RemoteSigned Execution Policy"](https://shellgeek.com/powershell-fix-running-scripts-is-disabled-on-this-system/)
|
||||
* Open a terminal with a shell command.
|
||||
* Clone the repository. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), begin by forking this repo with the Fork button in the top-right corner of this screen.
|
||||
* Use git clone to copy your fork onto your local machine.
|
||||
```command
|
||||
$ git clone https://github.com/YOUR_GITHUB_USERNAME_HERE/openfoodnetwork
|
||||
```
|
||||
* Otherwise, if you just want to get things running, clone from the OFN main repo:
|
||||
```command
|
||||
$ git clone git@github.com:openfoodfoundation/openfoodnetwork.git
|
||||
```
|
||||
* Go at the root of the app:
|
||||
```command
|
||||
2. **Navigate to the App Directory**:
|
||||
```sh
|
||||
$ cd openfoodnetwork
|
||||
```
|
||||
* Download the Docker images, build the Docker containers, seed the database with sample data, AND log the screen output from these tasks:
|
||||
```command
|
||||
$ docker/build.ps1
|
||||
```
|
||||
|
||||
3. **Choose and Build Docker Image**:
|
||||
* You have two choices of images you can build. The default Ubuntu image is most similar to the production servers but is larger in size and will take longer to build. Alternatively, you can use the Alpine image which is smaller, faster to setup, and should be compatible with the Ubuntu. However, it will need to be updated when ruby is updated. To download the Docker images, build the Docker containers, seed the database with sample data, AND log the screen output from these tasks:
|
||||
- **Ubuntu Image** (larger, production-like):
|
||||
- **Linux**:
|
||||
```sh
|
||||
$ docker/build
|
||||
```
|
||||
- **Windows**:
|
||||
```command
|
||||
$ docker/build.ps1
|
||||
```
|
||||
- **Alpine Image** (smaller, faster):
|
||||
- **Linux**:
|
||||
```sh
|
||||
$ docker/build alpine.Dockerfile
|
||||
```
|
||||
- **Windows**:
|
||||
```command
|
||||
$ docker/build.ps1 alpine.Dockerfile
|
||||
```
|
||||
|
||||
4. **Run the Rails Server**:
|
||||
* Run the Rails server and its required Docker containers:
|
||||
```command
|
||||
$ docker/server.ps1
|
||||
```
|
||||
You may need to wait several minutes before getting the server up and running properly.
|
||||
- **Linux**:
|
||||
```sh
|
||||
$ docker/server
|
||||
```
|
||||
- **Windows**:
|
||||
```command
|
||||
$ docker/server.ps1
|
||||
```
|
||||
* To run tests or access the server directly, you can use bash:
|
||||
```sh
|
||||
$ docker compose exec web bash
|
||||
```
|
||||
|
||||
> **Note**: Windows users may need to enable PowerShell script execution by setting the RemoteSigned policy. See instructions [here](https://shellgeek.com/powershell-fix-running-scripts-is-disabled-on-this-system/).
|
||||
|
||||
* The default admin user is 'ofn@example.com' with the password 'ofn123'.
|
||||
* View the app in the browser at `http://localhost:3000`.
|
||||
* You will then get the trace of the containers in the terminal. You can stop the containers using Ctrl-C in the terminal.
|
||||
|
||||
@@ -4,5 +4,5 @@ set -e
|
||||
# This script builds the Docker container, seeds the app with sample data, and logs the screen output.
|
||||
|
||||
DATE=`date +%Y%m%d-%H%M%S-%3N`
|
||||
docker/build-log 2>&1 | tee log/build-$DATE.log
|
||||
docker/build-log $1 2>&1 | tee log/build-$DATE.log
|
||||
docker/seed 2>&1 | tee log/seed-$DATE.log
|
||||
|
||||
@@ -6,7 +6,11 @@ wait
|
||||
echo '###########################'
|
||||
echo 'BEGIN: docker compose build'
|
||||
echo '###########################'
|
||||
docker compose build # Set up the Docker containers
|
||||
if [ -n "$1" ]; then
|
||||
docker build -f $1 . # Set up the Docker containers
|
||||
else
|
||||
docker compose build # Set up the Docker containers
|
||||
fi
|
||||
echo '##############################'
|
||||
echo 'FINISHED: docker compose build'
|
||||
echo '##############################'
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
Write-Host "Docker cleaning: remove old containers" -ForegroundColor Blue
|
||||
docker compose down -v --remove-orphans
|
||||
Write-Host "Docker build: set up the docker containers" -ForegroundColor Blue
|
||||
docker compose build
|
||||
|
||||
# Check if an argument is provided
|
||||
if ($args.Count -gt 0) {
|
||||
# Run the build command with the argument and log output
|
||||
docker build -f $1 .
|
||||
} else {
|
||||
docker compose build
|
||||
}
|
||||
Write-Host "Docker build finished"
|
||||
@@ -4,5 +4,11 @@
|
||||
|
||||
$DateTime=Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
|
||||
docker/build-log.ps1 > log/build-$DateTime.log 2>&1
|
||||
# Check if an argument is provided
|
||||
if ($args.Count -gt 0) {
|
||||
# Run the build command with the argument and log output
|
||||
docker/build-log.ps1 $args[0] > log/build-$DateTime.log 2>&1
|
||||
} else {
|
||||
docker/build-log.ps1 > log/build-$DateTime.log 2>&1
|
||||
}
|
||||
docker/seed.ps1 > log/seed-$DateTime.log 2>&1
|
||||
|
||||
@@ -12,7 +12,10 @@ class AffiliateSalesDataRowBuilder < DfcBuilder
|
||||
def build_supplier
|
||||
DataFoodConsortium::Connector::Enterprise.new(
|
||||
nil,
|
||||
localizations: [build_address(item[:supplier_postcode])],
|
||||
localizations: [build_address(
|
||||
item[:supplier_postcode],
|
||||
item[:supplier_country]
|
||||
)],
|
||||
suppliedProducts: [build_product],
|
||||
)
|
||||
end
|
||||
@@ -20,7 +23,10 @@ class AffiliateSalesDataRowBuilder < DfcBuilder
|
||||
def build_distributor
|
||||
DataFoodConsortium::Connector::Enterprise.new(
|
||||
nil,
|
||||
localizations: [build_address(item[:distributor_postcode])],
|
||||
localizations: [build_address(
|
||||
item[:distributor_postcode],
|
||||
item[:distributor_country]
|
||||
)],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -89,9 +95,10 @@ class AffiliateSalesDataRowBuilder < DfcBuilder
|
||||
)
|
||||
end
|
||||
|
||||
def build_address(postcode)
|
||||
def build_address(postcode, country)
|
||||
DataFoodConsortium::Connector::Address.new(
|
||||
nil,
|
||||
country:,
|
||||
postalCode: postcode,
|
||||
)
|
||||
end
|
||||
|
||||
@@ -38,9 +38,11 @@ class AffiliateSalesQuery
|
||||
JOIN spree_products ON spree_products.id = spree_variants.product_id
|
||||
JOIN enterprises AS suppliers ON suppliers.id = spree_variants.supplier_id
|
||||
JOIN spree_addresses AS supplier_addresses ON supplier_addresses.id = suppliers.address_id
|
||||
JOIN spree_countries AS supplier_countries ON supplier_countries.id = supplier_addresses.country_id
|
||||
JOIN spree_orders ON spree_orders.id = spree_line_items.order_id
|
||||
JOIN enterprises AS distributors ON distributors.id = spree_orders.distributor_id
|
||||
JOIN spree_addresses AS distributor_addresses ON distributor_addresses.id = distributors.address_id
|
||||
JOIN spree_countries AS distributor_countries ON distributor_countries.id = distributor_addresses.country_id
|
||||
SQL
|
||||
end
|
||||
|
||||
@@ -53,7 +55,9 @@ class AffiliateSalesQuery
|
||||
spree_variants.unit_presentation,
|
||||
spree_line_items.price,
|
||||
distributor_addresses.zipcode AS distributor_postcode,
|
||||
distributor_countries.name AS distributor_country,
|
||||
supplier_addresses.zipcode AS supplier_postcode,
|
||||
supplier_countries.name AS supplier_country,
|
||||
|
||||
SUM(spree_line_items.quantity) AS quantity_sold
|
||||
SQL
|
||||
@@ -68,7 +72,9 @@ class AffiliateSalesQuery
|
||||
spree_variants.unit_presentation,
|
||||
spree_line_items.price,
|
||||
distributor_postcode,
|
||||
supplier_postcode
|
||||
supplier_postcode,
|
||||
distributor_country,
|
||||
supplier_country
|
||||
SQL
|
||||
end
|
||||
|
||||
@@ -82,7 +88,9 @@ class AffiliateSalesQuery
|
||||
unit_presentation
|
||||
price
|
||||
distributor_postcode
|
||||
distributor_country
|
||||
supplier_postcode
|
||||
supplier_country
|
||||
quantity_sold
|
||||
]
|
||||
end
|
||||
|
||||
@@ -2,15 +2,31 @@
|
||||
|
||||
class DfcLoader
|
||||
def self.connector
|
||||
@connector ||= load_vocabularies
|
||||
unless @connector
|
||||
@connector = DataFoodConsortium::Connector::Connector.instance
|
||||
load_context
|
||||
load_vocabularies
|
||||
end
|
||||
|
||||
@connector
|
||||
end
|
||||
|
||||
def self.load_context
|
||||
JSON::LD::Context.add_preloaded("http://www.datafoodconsortium.org/") {
|
||||
JSON::LD::Context.parse(read_file("context_1.8.2")["@context"])
|
||||
}
|
||||
end
|
||||
|
||||
def self.vocabulary(name)
|
||||
@vocabs ||= {}
|
||||
@vocabs[name] ||= connector.__send__(:loadThesaurus, read_file(name))
|
||||
end
|
||||
|
||||
def self.load_vocabularies
|
||||
connector = DataFoodConsortium::Connector::Connector.instance
|
||||
connector.loadMeasures(read_file("measures"))
|
||||
connector.loadFacets(read_file("facets"))
|
||||
connector.loadProductTypes(read_file("productTypes"))
|
||||
connector
|
||||
vocabulary("vocabulary") # order states etc
|
||||
end
|
||||
|
||||
def self.read_file(name)
|
||||
|
||||
@@ -9,3 +9,4 @@ mkdir -p "$dst"
|
||||
curl --location "$src/facets.json" > "$dst/facets.json"
|
||||
curl --location "$src/measures.json" > "$dst/measures.json"
|
||||
curl --location "$src/productTypes.json" > "$dst/productTypes.json"
|
||||
curl --location "$src/vocabulary.json" > "$dst/vocabulary.json"
|
||||
|
||||
@@ -53,9 +53,15 @@ RSpec.describe AffiliateSalesQuery do
|
||||
it "converts an array to a hash" do
|
||||
row = [
|
||||
"Apples",
|
||||
"item", "item", nil, nil,
|
||||
"item",
|
||||
"item",
|
||||
nil,
|
||||
nil,
|
||||
15.50,
|
||||
"3210", "3211",
|
||||
"3210",
|
||||
"country1",
|
||||
"3211",
|
||||
"country2",
|
||||
3,
|
||||
]
|
||||
expect(query.label_row(row)).to eq(
|
||||
@@ -67,7 +73,9 @@ RSpec.describe AffiliateSalesQuery do
|
||||
unit_presentation: nil,
|
||||
price: 15.50,
|
||||
distributor_postcode: "3210",
|
||||
distributor_country: "country1",
|
||||
supplier_postcode: "3211",
|
||||
supplier_country: "country2",
|
||||
quantity_sold: 3,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -9,6 +9,12 @@ RSpec.describe DfcIo do
|
||||
let(:enterprise) do
|
||||
DataFoodConsortium::Connector::Enterprise.new("Pete's Pumpkins")
|
||||
end
|
||||
let(:order) do
|
||||
DataFoodConsortium::Connector::Order.new("https://example.net", orderStatus: orderstate.HELD)
|
||||
end
|
||||
let(:orderstate) do
|
||||
DfcLoader.vocabulary("vocabulary").STATES.ORDERSTATE
|
||||
end
|
||||
|
||||
describe ".export" do
|
||||
it "exports nothing" do
|
||||
@@ -33,5 +39,20 @@ RSpec.describe DfcIo do
|
||||
*%w(@id @type dfc-b:affiliates)
|
||||
)
|
||||
end
|
||||
|
||||
it "recognises loaded vocabularies" do
|
||||
json = DfcIo.export(order)
|
||||
result = JSON.parse(json)
|
||||
|
||||
expect(result["dfc-b:hasOrderStatus"]).to eq "dfc-v:Held"
|
||||
end
|
||||
end
|
||||
|
||||
describe ".import" do
|
||||
it "recognises loaded vocabularies" do
|
||||
result = DfcIo.import(DfcIo.export(order))
|
||||
|
||||
expect(result.orderStatus).to eq orderstate.HELD
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,4 +21,10 @@ RSpec.describe DfcLoader do
|
||||
)
|
||||
expect(result["dfc-b:name"]).to eq "Tomato"
|
||||
end
|
||||
|
||||
it "loads vocabularies" do
|
||||
terms = DfcLoader.vocabulary("vocabulary")
|
||||
expect(terms.STATES.ORDERSTATE.HELD.semanticId)
|
||||
.to eq "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Held"
|
||||
end
|
||||
end
|
||||
|
||||
450
engines/dfc_provider/vendor/context_1.8.2.json
vendored
Normal file
450
engines/dfc_provider/vendor/context_1.8.2.json
vendored
Normal file
@@ -0,0 +1,450 @@
|
||||
{
|
||||
"@context": {
|
||||
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
||||
"skos" : "http://www.w3.org/2004/02/skos/core#",
|
||||
"dfc": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_FullModel.owl#",
|
||||
"dc": "http://purl.org/dc/elements/1.1/#",
|
||||
"dfc-b": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_BusinessOntology.owl#",
|
||||
"dfc-t": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_TechnicalOntology.owl#",
|
||||
"dfc-m": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#",
|
||||
"dfc-pt": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#",
|
||||
"dfc-f": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#",
|
||||
"dfc-v": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#",
|
||||
"ontosec": "http://www.semanticweb.org/ontologies/2008/11/OntologySecurity.owl#",
|
||||
"dfc-b:DFC_BusinessOntology_ObjectProperty": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:DFC_Interface_Property": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:addressOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:affiliatedTo": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:affiliates": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:allergenCharacteristicOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:belongsTo": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:brandOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:certificateOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:characteristicOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:claimOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:composedOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:composes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:concernedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:concerns": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:constituedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:constitutes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:consumedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:consumes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:containerInformationOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:coordinatedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:coordinates": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:definedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:defines": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:deliveredAt": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:facetOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:from": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:geographicalOriginOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasAddress": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasAllergenCharacteristic": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasAllergenDimension": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasBrand": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasCertification": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasCharacteristic": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasClaim": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasContainerInformation": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasDimension": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasFacet": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasFulfilmentStatus": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasGeographicalOrigin": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasIngredient": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasInput": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasLabellingCharacteristic": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasLabellingDimension": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasMainContact": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasNatureOrigin": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasNutrientCharacteristic": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasNutrientDimension": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasObject": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasOffer": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasOption": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasOrderStatus": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasOutput": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPart": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPartOrigin": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPaymentMethod": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPaymentStatus": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPhoneNumber": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPhysicalCharacteristic": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPhysicalDimension": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasPrice": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasProcess": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasQuantity": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasReference": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasSocialMedia": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasStatus": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasTemperature": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasTransformationType": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasType": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hasUnit": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:holds": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hostedAt": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:hosts": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:identifiedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:identifies": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:industrializedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:industrializes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:inputOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:isIngredientOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:isPriceOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:isTemperatureOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:labellingCharacteristicOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:listedIn": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:lists": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:localizedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:localizes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:mainContactOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:maintainedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:maintains": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:managedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:manages": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:natureOriginOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:nutrientCharacteristicOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:objectOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:offeredThrough": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:offers": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:offersTo": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:optionOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:orderedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:orders": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:outputOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:ownedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:owns": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:paidWith": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:partOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:partOriginOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:phoneNumberOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:physicalCharacteristicOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:pickedUpAt": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:processOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:producedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:produces": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:proposedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:proposes": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:referenceOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:referencedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:references": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:refersTo": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:representedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:represents": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:requestedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:requests": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:satisfiedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:satisfies": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:selectedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:selects": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:sells": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:socialMediaOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:soldBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:storedIn": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:stores": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:suppliedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:supplies": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:suppliesTo": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:to": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:tracedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:traces": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:transformedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:transforms": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:typeOf": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-b:uses": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-t:represent": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-t:hasPivot": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-t:hostedBy": {
|
||||
"@type": "@id"
|
||||
},
|
||||
"dfc-t:owner": {
|
||||
"@type": "@id"
|
||||
}
|
||||
}
|
||||
}
|
||||
351
engines/dfc_provider/vendor/vocabulary.json
vendored
Normal file
351
engines/dfc_provider/vendor/vocabulary.json
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
[ {
|
||||
"@graph" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#",
|
||||
"@type" : [ "http://www.w3.org/2002/07/owl#Ontology" ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Cancelled",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#OrderState"
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#PaymentState"
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#FulfilmentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Cancelled"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Complete",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#OrderState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Complete"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#ConceptScheme" ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "DFC_Vocabulary"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Draft",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#OrderState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Draft"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Fulfilled",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#FulfilmentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Fulfilled"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#FulfilmentState",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#States"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Fulfilment state"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Held",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#OrderState"
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#FulfilmentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Held"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#OrderState",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#States"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Order state"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Paid",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#PaymentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Paid"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#PaymentState",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#States"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Payment state"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#States",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "States"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#topConceptOf" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Unfulfilled",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#FulfilmentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Unfulfilled"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#Unpaid",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#PaymentState"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Unpaid"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#accept",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "accept"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#c_734fc709",
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "FulfilmentState"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#combine",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "combine"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#consume",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "consume"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#dropoff",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "dropoff"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#lower",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "lower"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#modify",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "modify"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#move",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "move"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#pickup",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "pickup"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#produce",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "produce"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#raise",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "raise"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#separate",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "separate"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "Transformation type"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#topConceptOf" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ]
|
||||
}, {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#use",
|
||||
"@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ],
|
||||
"http://www.w3.org/2004/02/skos/core#broader" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#transformationType"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#inScheme" : [ {
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#DFC_Vocabulary"
|
||||
} ],
|
||||
"http://www.w3.org/2004/02/skos/core#prefLabel" : [ {
|
||||
"@language" : "en",
|
||||
"@value" : "use"
|
||||
} ]
|
||||
} ],
|
||||
"@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/vocabulary.rdf#"
|
||||
} ]
|
||||
@@ -161,6 +161,11 @@ module OrderManagement
|
||||
persist_totals
|
||||
end
|
||||
|
||||
def update_voucher
|
||||
VoucherAdjustmentsService.new(order).update
|
||||
update_totals_and_states
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cancel_payments_requiring_auth
|
||||
|
||||
@@ -460,6 +460,26 @@ module OrderManagement
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update_voucher" do
|
||||
let(:voucher_service) { instance_double(VoucherAdjustmentsService) }
|
||||
|
||||
it "calls VoucherAdjustmentsService" do
|
||||
expect(VoucherAdjustmentsService).to receive(:new).and_return(voucher_service)
|
||||
expect(voucher_service).to receive(:update)
|
||||
|
||||
updater.update_voucher
|
||||
end
|
||||
|
||||
it "calls update_totals_and_states" do
|
||||
allow(VoucherAdjustmentsService).to receive(:new).and_return(voucher_service)
|
||||
allow(voucher_service).to receive(:update)
|
||||
|
||||
expect(updater).to receive(:update_totals_and_states)
|
||||
|
||||
updater.update_voucher
|
||||
end
|
||||
end
|
||||
|
||||
def update_order_quantity(order)
|
||||
order.line_items.first.update_attribute(:quantity, 2)
|
||||
end
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Our error logging API currently wraps Bugsnag.
|
||||
# It makes us more flexible if we wanted to replace Bugsnag or change logging
|
||||
# behaviour.
|
||||
module OpenFoodNetwork
|
||||
module ErrorLogger
|
||||
# Tries to escalate the error to a developer.
|
||||
# If Bugsnag is configured, it will notify it. It would be nice to implement
|
||||
# some kind of fallback.
|
||||
def self.notify(error)
|
||||
Bugsnag.notify(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user