Compare commits

...

55 Commits

Author SHA1 Message Date
Luis Ramos
97ef93b840 Merge pull request #4516 from openfoodfoundation/transifex
Transifex
2019-12-02 17:50:26 +00:00
Luis Ramos
6db15a0a20 Merge pull request #4524 from Matt-Yorkley/packing_reports
Packing reports performance
2019-12-02 17:32:52 +00:00
Luis Ramos
c8395a487a Merge pull request #4523 from luisramos0/permissions_improve
Replace pluck with select in permissions to avoid extra queries and extract Permissions::Orders from Permissions
2019-12-02 16:31:04 +00:00
luisramos0
1e948735fb Fix major performance problem by inverting the logic, instead of looking for line_items that are hidden, it looks for line items that are not editable using a merge statement that performs much better
Also, remove unnecessary if clause, merge will return an empty relation if no items are found, no need to test for empty.

The test report runs in a little over one minute instead of 8minutes
2019-11-30 22:38:03 +00:00
luisramos0
0ef4247914 Convert Report::LineItems to class and memoize orders so it's only executed once (this improves the report in 3secs for the case I am testing) 2019-11-29 21:51:54 +00:00
Matt-Yorkley
6d1fb63a21 Eager-load option_values on line_item objects instead of variants in packing reports. 2019-11-29 20:16:48 +01:00
Matt-Yorkley
9bcd303f4f Remove shipping_category N+1 from packing reports 2019-11-29 20:16:48 +01:00
Matt-Yorkley
38c327dae0 Improve N+1 issues around #suppliers_of_products_distributed_by
There's still some real mess here with repeating queries, but resolving it is out of scope for this quick PR
2019-11-29 20:16:03 +01:00
Matt-Yorkley
51177b833e Remove customer_code N+1 from packing reports 2019-11-29 17:09:12 +01:00
luisramos0
cc3368704a Fix rubocop issues in reports_controller_decorator and in report line_items 2019-11-29 13:54:30 +00:00
Pau Pérez Fabregat
2d53fbbe8c Merge pull request #4520 from kristinalim/fix/4238-flaky_spec_in_api_taxons_index
4238 Do not assume order in spec for taxons list
2019-11-29 13:23:51 +01:00
luisramos0
3959f16d65 Switch some more references from Permissions to Permissions::Order 2019-11-29 12:22:50 +00:00
Pau Pérez Fabregat
fb28826d92 Merge pull request #4522 from kristinalim/fix/4239-flaky_spec_in_bulk_product_clone
4239 Do one thing at a time in feature spec for product cloning
2019-11-29 13:22:42 +01:00
luisramos0
beaa8ffa27 Use more specific selector to avoid ambigous column error 2019-11-29 11:45:22 +00:00
luisramos0
da6d035a1d Rename some reports permissions to order_permissions 2019-11-29 11:23:17 +00:00
luisramos0
5cb77c443b Fix rubocop issues 2019-11-29 10:53:40 +00:00
luisramos0
8d16f496f4 Move Permissions::Order specs to its specific spec file 2019-11-29 10:49:59 +00:00
luisramos0
82b274e522 Make selector more specific to avoid sql error 'ambiguos column' 2019-11-29 10:49:58 +00:00
luisramos0
484cdd1e07 Make managed_and_related_enterprises public so they can be used by other permissions classes 2019-11-29 10:49:27 +00:00
luisramos0
bb2e6324bd Rename order permissions to just order 2019-11-29 10:49:27 +00:00
luisramos0
89056e13ed Extract order permissions to a separate class 2019-11-29 10:48:58 +00:00
luisramos0
df0458743b Replace pluck with select in permissions to avoid extra queries 2019-11-28 23:37:49 +00:00
Kristina Lim
1476859c83 Do one thing at a time in feature spec for product cloning 2019-11-28 22:05:17 +08:00
Kristina Lim
c6fb7dafec Do not assume order in test for taxons list 2019-11-28 20:33:42 +08:00
Transifex-Openfoodnetwork
80069731ed Updating translations for config/locales/en_NZ.yml 2019-11-28 20:52:48 +11:00
Maikel Linke
dfa3d40665 Create release task template recognised by Github 2019-11-28 15:58:14 +11:00
Maikel
00c2b95a0e Add issue template for release tasks 2019-11-28 15:53:34 +11:00
Maikel Linke
4a82a26830 Update all locales with the latest Transifex translations 2019-11-28 15:17:38 +11:00
Transifex-Openfoodnetwork
f1831fc6bb Updating translations for config/locales/en_AU.yml 2019-11-28 13:47:28 +11:00
Transifex-Openfoodnetwork
4c91a5571a Updating translations for config/locales/en_AU.yml 2019-11-28 13:47:08 +11:00
Transifex-Openfoodnetwork
ccb7a305bc Updating translations for config/locales/de_DE.yml 2019-11-28 13:45:01 +11:00
Transifex-Openfoodnetwork
0c87afefce Updating translations for config/locales/de_DE.yml 2019-11-28 13:41:53 +11:00
Maikel
d546817f0a Merge pull request #4486 from openfoodfoundation/transifex
Transifex
2019-11-28 10:36:06 +11:00
Luis Ramos
e0e833b2f3 Merge pull request #4454 from luisramos0/sort_products
Sort products alphabetically in OC edit page
2019-11-27 22:27:42 +00:00
Luis Ramos
7cc034c2bc Merge pull request #4136 from mkllnk/4018-synchronise-checkout
Lock variants during checkout to avoid race condition
2019-11-27 15:40:28 +00:00
luisramos0
15bcde36cb Remove order by filter on outgoing exchanges list of products
The products are now coming from the server already sorted
2019-11-26 17:59:30 +00:00
Transifex-Openfoodnetwork
6e69960ee9 Updating translations for config/locales/nb.yml 2019-11-25 22:54:56 +11:00
Transifex-Openfoodnetwork
a7a03b04a9 Updating translations for config/locales/nb.yml 2019-11-25 22:51:50 +11:00
Pau Pérez Fabregat
5759dcee48 Merge pull request #4463 from jonleighton/docker-ruby-version
Use .ruby-version when building Docker container
2019-11-25 10:43:55 +01:00
Pau Pérez Fabregat
a9672011a5 Merge pull request #4479 from openfoodfoundation/dependabot/bundler/ddtrace-0.29.0
Bump ddtrace from 0.28.0 to 0.29.0
2019-11-25 09:42:48 +01:00
Pau Pérez Fabregat
808aa188ab Merge pull request #4478 from openfoodfoundation/dependabot/bundler/rspec-retry-0.6.2
Bump rspec-retry from 0.6.1 to 0.6.2
2019-11-25 09:42:06 +01:00
Transifex-Openfoodnetwork
ad7fc61228 Updating translations for config/locales/en_GB.yml 2019-11-24 05:29:07 +11:00
Transifex-Openfoodnetwork
3e32e5c16e Updating translations for config/locales/ar.yml 2019-11-24 03:48:31 +11:00
Transifex-Openfoodnetwork
f67a2120f4 Updating translations for config/locales/ar.yml 2019-11-24 03:45:25 +11:00
Matt-Yorkley
a4ee562387 Update all locales with the latest Transifex translations 2019-11-23 12:57:35 +01:00
dependabot-preview[bot]
6df7ec9dbd Bump ddtrace from 0.28.0 to 0.29.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.28.0 to 0.29.0.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.28.0...v0.29.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-21 19:20:49 +00:00
dependabot-preview[bot]
0f7357166d Bump rspec-retry from 0.6.1 to 0.6.2
Bumps [rspec-retry](https://github.com/noredink/rspec-retry) from 0.6.1 to 0.6.2.
- [Release notes](https://github.com/noredink/rspec-retry/releases)
- [Changelog](https://github.com/NoRedInk/rspec-retry/blob/master/changelog.md)
- [Commits](https://github.com/noredink/rspec-retry/compare/v0.6.1...v0.6.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-21 19:17:49 +00:00
Maikel Linke
50093c325a Move checkout locking to its own service
It gives this complex logic more space and allows for better structure
and more comments at the right places.
2019-11-19 18:18:01 +11:00
Maikel Linke
4288428c70 Separating concurrency spec as it's entirely different 2019-11-19 18:18:01 +11:00
Maikel Linke
dc122a9450 Fix infinite loop in spec
The spec was setting the order's state to "complete" but didn't save
that state to the database. The new locking mechanism is was reloading
the order which loaded the cart state again. And since the order.next
method was mocked to just return true, the controller was trying to do
that in an infinite loop.
2019-11-19 18:18:01 +11:00
Maikel Linke
ec1b5a7a92 Test concurrent checkouts
When two people tried to buy the same item at the same time, it was
possible to oversell the item and end up with negative stock.

Parallel checkouts could also lead to other random failures. This spec
is testing that scenario by starting two threads which would run into a
race condition unless they use effective synchronisation. The added spec
fails if the synchronisation is removed from the CheckoutController.
2019-11-19 18:18:01 +11:00
Maikel Linke
df2306cf82 Lock variants during checkout to avoid race condition
It was possible that several people bought the same variant even though
there wasn't enough stock for everybody. That resulted in negative
stock.
2019-11-19 18:18:01 +11:00
Jon Leighton
172a79acc7 Use .ruby-version when building Docker container
The Ruby version was updated, but the Dockerfile wasn’t. This meant that
the Docker environment was broken. This change should prevent similar
breakage in the future by making .ruby-version the source of truth about
the correct version.
2019-11-14 15:44:49 +11:00
luisramos0
6e51be095b Add order to supplied products in enterprise serializer so that products in exchanges are sorted alphabetically 2019-11-12 12:04:42 +00:00
luisramos0
1c7237869a Refactor products_scope to make it more simple 2019-11-12 12:04:42 +00:00
55 changed files with 1522 additions and 398 deletions

View File

@@ -1,10 +0,0 @@
Draft: x
Target commit: x
Build: x
- [ ] Draft
- [ ] Test
- [ ] Publish
- [ ] Deploy

19
.github/ISSUE_TEMPLATE/release.md vendored Normal file
View File

@@ -0,0 +1,19 @@
---
name: Release task
about: Track the process of a new release
title: ''
labels: ''
assignees: ''
---
Steps:
- [ ] Include translations
- [ ] Draft: https://github.com/openfoodfoundation/openfoodnetwork/releases/new <!-- replace the URL -->
- [ ] Test: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master <!-- replace the URL -->
- [ ] Publish and notify #global-community
- [ ] Deploy and notify #instance-managers
- [ ] Nudge next release manager
The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing.

View File

@@ -45,7 +45,6 @@ Metrics/LineLength:
- app/controllers/spree/admin/base_controller_decorator.rb
- app/controllers/spree/admin/orders_controller_decorator.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/reports_controller_decorator.rb
- app/controllers/spree/credit_cards_controller.rb
- app/controllers/spree/paypal_controller_decorator.rb
- app/controllers/stripe/callbacks_controller.rb
@@ -121,7 +120,6 @@ Metrics/LineLength:
- lib/open_food_network/order_cycle_management_report.rb
- lib/open_food_network/payments_report.rb
- lib/open_food_network/reports/bulk_coop_allocation_report.rb
- lib/open_food_network/reports/line_items.rb
- lib/open_food_network/sales_tax_report.rb
- lib/open_food_network/variant_and_line_item_naming.rb
- lib/open_food_network/xero_invoices_report.rb

View File

@@ -6,16 +6,19 @@ RUN apt-get update && apt-get install -y curl git build-essential software-prope
# Setup ENV variables
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
ENV RBENV_ROOT /usr/local/src/rbenv
ENV RUBY_VERSION 2.1.9
ENV CONFIGURE_OPTS --disable-install-doc
ENV BUNDLE_PATH /bundles
WORKDIR /usr/src/app
COPY .ruby-version .
# Rbenv & Ruby part
RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
${RBENV_ROOT}/plugins/ruby-build/install.sh && \
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
rbenv install $RUBY_VERSION && \
rbenv global $RUBY_VERSION && \
rbenv install $(cat .ruby-version) && \
rbenv global $(cat .ruby-version) && \
gem install bundler --version=1.17.2
# Postgres
@@ -24,8 +27,4 @@ RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main'
apt-get update && \
apt-get install -yqq --no-install-recommends postgresql-client-9.5 libpq-dev
ENV BUNDLE_PATH /bundles
COPY . /usr/src/app/
WORKDIR /usr/src/app

View File

@@ -225,7 +225,7 @@ GEM
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.28.0)
ddtrace (0.29.0)
msgpack
debugger-linecache (1.2.0)
deface (1.0.2)
@@ -615,7 +615,7 @@ GEM
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-support (~> 3.9.0)
rspec-retry (0.6.1)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.9.0)
rubocop (0.68.1)

View File

@@ -11,7 +11,7 @@
.exchange-products
-# Scope product list based on permissions the current user has to view variants in this exchange
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' }
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
.exchange-product-details
%label
%img{'ng-src' => '{{ product.image_url }}'}

View File

@@ -5,10 +5,11 @@ module Admin
def index
order_params = params[:q].andand.delete :order
orders = OpenFoodNetwork::Permissions.new(spree_current_user).
order_permissions = ::Permissions::Order.new(spree_current_user)
orders = order_permissions.
editable_orders.ransack(order_params).result
line_items = OpenFoodNetwork::Permissions.new(spree_current_user).
line_items = order_permissions.
editable_line_items.where(order_id: orders).
includes(variant: { option_values: :option_type }).
ransack(params[:q]).result.

View File

@@ -3,6 +3,10 @@ require 'open_food_network/address_finder'
class CheckoutController < Spree::CheckoutController
layout 'darkswarm'
# We need pessimistic locking to avoid race conditions.
# Otherwise we fail on duplicate indexes or end up with negative stock.
prepend_around_filter CurrentOrderLocker, only: :update
prepend_before_filter :check_hub_ready_for_checkout
prepend_before_filter :check_order_cycle_expiry
prepend_before_filter :require_order_cycle

View File

@@ -23,7 +23,8 @@ Spree::Admin::ReportsController.class_eval do
before_filter :cache_search_state
# Fetches user's distributors, suppliers and order_cycles
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
before_filter :load_data,
only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
def report_types
OpenFoodNetwork::Reports::List.all
@@ -49,7 +50,9 @@ Spree::Admin::ReportsController.class_eval do
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params, render_content?
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
params,
render_content?
@table = @report.table_items
render_report(@report.header, @table, params[:csv], "order_cycle_management_#{timestamp}.csv")
@@ -69,7 +72,9 @@ Spree::Admin::ReportsController.class_eval do
end
def orders_and_distributors
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user, params, render_content?
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
params,
render_content?
@search = @report.search
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
render_report(@report.header, @report.table, params[:csv], csv_file_name)
@@ -126,8 +131,9 @@ Spree::Admin::ReportsController.class_eval do
@include_blank = I18n.t(:all)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions,
params, render_content?)
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
params,
render_content?
@table = order_grouper_table
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
@@ -137,16 +143,24 @@ Spree::Admin::ReportsController.class_eval do
def products_and_inventory
@report_types = report_types[:products_and_inventory]
@report = if params[:report_type] != 'lettuce_share'
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params, render_content?
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
params,
render_content?
else
OpenFoodNetwork::LettuceShareReport.new spree_current_user, params, render_content?
end
render_report(@report.header, @report.table, params[:csv], "products_and_inventory_#{timestamp}.csv")
render_report(@report.header,
@report.table,
params[:csv],
"products_and_inventory_#{timestamp}.csv")
end
def users_and_enterprises
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content?
render_report(@report.header, @report.table, params[:csv], "users_and_enterprises_#{timestamp}.csv")
render_report(@report.header,
@report.table,
params[:csv],
"users_and_enterprises_#{timestamp}.csv")
end
def xero_invoices
@@ -222,7 +236,8 @@ Spree::Admin::ReportsController.class_eval do
end
def suppliers_of_products_distributed_by(distributors)
distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq
distributors.map { |d| Spree::Product.in_distributor(d).includes(:supplier).all }.
flatten.map(&:supplier).uniq
end
# Load order cycles the current user has access to

View File

@@ -34,11 +34,11 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
private
def products_scope
products_relation = object.supplied_products
if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
object.supplied_products.visible_for(order_cycle.coordinator)
else
object.supplied_products
products_relation = products_relation.visible_for(order_cycle.coordinator)
end
products_relation.order(:name)
end
def products

View File

@@ -0,0 +1,40 @@
# Locks a controller's current order including its variants.
#
# It should be used when making major changes like checking out the order.
# It can keep stock checking in sync and prevent overselling of an item.
class CurrentOrderLocker
# This interface follows the ActionController filters convention:
#
# https://guides.rubyonrails.org/action_controller_overview.html#filters
#
def self.around(controller)
lock_order_and_variants(controller.current_order) { yield }
end
# Locking will not prevent all access to these rows. Other processes are
# only waiting if they try to lock one of these rows as well.
#
# https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html
#
def self.lock_order_and_variants(order)
return yield if order.nil?
order.with_lock do
lock_variants_of(order)
yield
end
end
private_class_method :lock_order_and_variants
# There are many places in which stock is stored in the database. Row locking
# on variant level ensures that there are no conflicts even when an item is
# sold through multiple shops.
def self.lock_variants_of(order)
variant_ids = order.line_items.select(:variant_id)
# Ordering the variants by id prevents deadlocks. Plucking the ids sends
# the locking query without building Spree::Variant objects.
Spree::Variant.where(id: variant_ids).order(:id).lock.pluck(:id)
end
private_class_method :lock_variants_of
end

View File

@@ -0,0 +1,80 @@
module Permissions
class Order
def initialize(user)
@user = user
@permissions = OpenFoodNetwork::Permissions.new(@user)
end
# Find orders that the user can see
def visible_orders
Spree::Order.where(id:
managed_orders.select(:id) |
coordinated_orders.select(:id) |
produced_orders.select("spree_orders.id"))
end
# Any orders that the user can edit
def editable_orders
Spree::Order.where(id:
managed_orders.select(:id) |
coordinated_orders.select(:id) )
end
def visible_line_items
Spree::LineItem.where(id:
editable_line_items.select(:id) |
produced_line_items.select("spree_line_items.id"))
end
# Any line items that I can edit
def editable_line_items
Spree::LineItem.where(order_id: editable_orders)
end
private
# Any orders placed through any hub that I manage
def managed_orders
Spree::Order.where(distributor_id: @permissions.managed_enterprises.select("enterprises.id"))
end
# Any order that is placed through an order cycle one of my managed enterprises coordinates
def coordinated_orders
Spree::Order.where(order_cycle_id: @permissions.coordinated_order_cycles.select(:id))
end
def produced_orders
Spree::Order.with_line_items_variants_and_products_outer.
where(
distributor_id: granted_distributor_ids,
spree_products: { supplier_id: enterprises_with_associated_orders }
)
end
def enterprises_with_associated_orders
# Any orders placed through hubs that my producers have granted P-OC,
# and which contain their products. This is pretty complicated but it's looking for order
# where at least one of my producers has granted P-OC to the distributor
# AND the order contains products of at least one of THE SAME producers
@permissions.related_enterprises_granting(:add_to_order_cycle, to: granted_distributor_ids).
merge(@permissions.managed_enterprises.is_primary_producer)
end
def granted_distributor_ids
@granted_distributor_ids ||= @permissions.related_enterprises_granted(
:add_to_order_cycle,
by: @permissions.managed_enterprises.is_primary_producer.select("enterprises.id")
).select("enterprises.id")
end
# Any from visible orders, where the product is produced by one of my managed producers
def produced_line_items
Spree::LineItem.where(order_id: visible_orders.select(:id)).
joins(:product).
where(spree_products:
{
supplier_id: @permissions.managed_enterprises.is_primary_producer.select("enterprises.id")
})
end
end
end

View File

@@ -24,7 +24,7 @@ class SearchOrders
attr_reader :params, :current_user
def fetch_orders
@search = OpenFoodNetwork::Permissions.new(current_user).editable_orders.ransack(params[:q])
@search = ::Permissions::Order.new(current_user).editable_orders.ransack(params[:q])
return paginated_results if using_pagination?

View File

@@ -262,6 +262,7 @@ ar:
cancel: "إلغاء"
save: "حفظ"
edit: "تعديل"
update: "تحديث"
delete: "حذف"
admin:
begins_at: يبدأ عند
@@ -2872,6 +2873,37 @@ ar:
form:
name: "الاسم"
presentation: "عرض"
return_authorizations:
index:
new_return_authorization: "عودة الترخيص جديد"
return_authorizations: "عودة التراخيص"
back_to_orders_list: "العودة إلى قائمة الطلبات"
rma_number: "رقم RMA"
status: "الحالة"
amount: "القيمة"
cannot_create_returns: "لا يمكن إنشاء عوائد لأن هذا الطلب لا يحتوي على وحدات مشحونة."
continue: "تابع"
new:
new_return_authorization: "عودة الترخيص جديد"
back_to_return_authorizations_list: "العودة إلى قائمة عودة الترخيص"
continue: "تابع"
edit:
receive: "استلام"
are_you_sure: "هل أنت واثق؟"
return_authorization: "عودة التراخيص"
form:
product: "المنتج"
quantity_shipped: "الكمية التي تم شحنها"
quantity_returned: "الكمية المرتجعة"
return_quantity: "عودة الكمية"
amount: "القيمة"
rma_value: "قيمة RMA"
reason: "السبب"
stock_location: "موقع المخزن"
states:
authorized: "مخول"
received: "تم الاستلام"
canceled: "ألغيت"
orders:
index:
listing_orders: "لائحة الطلبات"

View File

@@ -262,6 +262,7 @@ ca:
cancel: "Cancel·lar"
save: "Desa"
edit: "Editar"
update: "Actualitzar"
delete: "Suprimir"
admin:
begins_at: Comença a
@@ -832,10 +833,24 @@ ca:
loading_flash:
loading_order_cycles: CÀRREGUES CICLES DE COMANDA
loading: CARREGANT...
new:
create: "Crear"
cancel: "Cancel·lar"
edit:
advanced_settings: Configuració avançada
update_and_close: Actualitza i tanca
choose_products_from: 'Trieu Productes des de:'
advanced_settings: "Configuració avançada"
save: "Desa"
next: "Següent"
cancel: "Cancel·lar"
choose_products_from: "Trieu Productes des de:"
incoming:
previous: "Anterior"
save: "Desa"
next: "Següent"
cancel: "Cancel·lar"
outgoing:
previous: "Anterior"
save: "Desa"
cancel: "Cancel·lar"
exchange_form:
pickup_time_tip: Quan les comandes d'aquest cicle de comandes estiguin preparades per a les consumidores
pickup_instructions_placeholder: "Instruccions de recollida"
@@ -862,6 +877,7 @@ ca:
any_enterprise: "Qualsevol organització"
any_schedule: "Qualsevol programació"
form:
general_settings: "Configuració general"
incoming: Entrant
supplier: Proveïdora
receival_details: Detalls de recepció
@@ -2824,6 +2840,27 @@ ca:
product_properties:
index:
inherits_properties_checkbox_hint: "heredar propietats de %{supplier}? (llevat que es sobreescrigui a dalt)"
properties:
index:
properties: "Propietats"
name: "Nom"
form:
name: "Nom"
return_authorizations:
index:
return_authorizations: "Autoritzacions de devolució"
back_to_orders_list: "Tornar a la llista de comandes"
status: "Estat"
amount: "Quantitat"
cannot_create_returns: "No es poden crear devolucions ja que aquesta comanda no té cap unitat enviada."
continue: "Continua"
new:
continue: "Continua"
edit:
are_you_sure: "Estàs segur?"
form:
product: "Producte"
amount: "Quantitat"
orders:
index:
listing_orders: "Llistat comandes"

View File

@@ -113,6 +113,10 @@ de_DE:
subject: "%{enterprise} ist jetzt auf %{sitename}"
email_welcome: "Willkommen"
email_registered: "ist jetzt Teil von"
email_userguide_html: "Das Benutzerhandbuch mit detaillierter Unterstützung für die Einrichtung Ihres Produzenten oder Hubs finden Sie hier: %{link}"
userguide: "Öffnen Sie das Food Network-Benutzerhandbuch"
email_admin_html: "Sie können Ihr Konto verwalten, indem Sie sich bei %{link} anmelden oder auf das Zahnrad oben rechts auf der Startseite klicken und Administration auswählen."
admin_panel: "Administrationsmenü"
email_community_html: "Wir haben auch ein Online-Forum für Community-Diskussionen in Bezug auf OFN-Software und die einzigartigen Herausforderungen eines Lebensmittelunternehmens. Reden Sie doch mit. Wir entwickeln uns ständig weiter und Ihr Beitrag in diesem Forum prägt, was als nächstes passiert. %{link}"
join_community: "Treten Sie der Community bei"
invite_manager:
@@ -258,6 +262,7 @@ de_DE:
cancel: "Abbrechen"
save: "Speichern"
edit: "Bearbeiten"
update: "Aktualisieren"
delete: "Löschen"
admin:
begins_at: Beginnt um
@@ -429,9 +434,12 @@ de_DE:
infinity: "Unendlichkeit"
to_order_tip: "Artikel, die auf Bestellung hergestellt werden, haben keinen festgelegten Lagerbestand."
back_to_products_list: "Zurück zur Produktliste"
editing_product: "Produkt bearbeiten"
tabs:
product_details: "Produktdetails"
group_buy_options: "Gruppenkaufoptionen"
images: "Bilder"
variants: "Varianten"
product_properties: "Produkteigenschaften"
product_import:
title: Produkte importieren
@@ -534,6 +542,7 @@ de_DE:
title: Katalog
description: Verwenden Sie diese Seite, um Bestände für Ihre Unternehmen zu verwalten. Alle hier eingestellten Produktdetails überschreiben diejenigen, die auf der Seite "Produkte" eingestellt sind
enable_reset?: Lagerbestand zurücksetzbar?
default_stock: "Standardbestand"
inherit?: Übernehmen?
add: Hinzufügen
hide: Verbergen
@@ -827,10 +836,36 @@ de_DE:
loading_flash:
loading_order_cycles: LADEN VON AUFTRAGSZYKLEN
loading: WIRD GELADEN...
new:
create: "Neu"
cancel: "Abbrechen"
back_to_list: "Zurück zur Liste"
edit:
advanced_settings: Erweiterte Einstellungen
update_and_close: Aktualisieren und schließen
choose_products_from: 'Wählen Sie Produkte von:'
advanced_settings: "Erweiterte Einstellungen"
save: "Speichern"
save_and_next: "Speichern und weiter"
next: "Weiter"
cancel: "Abbrechen"
back_to_list: "Zurück zur Liste"
save_and_back_to_list: "Speichern und zurück zur Liste"
choose_products_from: "Wählen Sie Produkte von:"
incoming:
previous: "Bisherige"
save: "Speichern"
save_and_next: "Speichern und weiter"
next: "Weiter"
cancel: "Abbrechen"
back_to_list: "Zurück zur Liste"
outgoing:
previous: "Bisherige"
save: "Speichern"
save_and_back_to_list: "Speichern und zurück zur Liste"
cancel: "Abbrechen"
back_to_list: "Zurück zur Liste"
wizard_progress:
edit: "1. Allgemeine Einstellungen"
incoming: "2. Eingehende Produkte"
outgoing: "3. Ausgehende Produkte"
exchange_form:
pickup_time_tip: Wenn Bestellungen von diesem OC für den Kunden bereit sind
pickup_instructions_placeholder: "Abholungsinformationen"
@@ -857,6 +892,7 @@ de_DE:
any_enterprise: "Alle Unternehmen"
any_schedule: "Alle Zeitpläne"
form:
general_settings: "Allgemeine Einstellungen"
incoming: Eingehend
supplier: Anbieter
receival_details: Lieferinformation
@@ -2777,6 +2813,12 @@ de_DE:
minimal_amount: "Minimale Menge"
normal_amount: "Normaler Betrag"
discount_amount: "Rabattbetrag"
no_images_found: "Keine Bilder gefunden"
new_image: "Neues Bild"
filename: "Dateiname"
alt_text: "alternativer Text"
thumbnail: "Miniaturansicht"
back_to_images_list: "Zurück zur Bilderliste"
email: Email
account_updated: "Konto aktualisiert!"
email_updated: "Das Konto wird aktualisiert, sobald die neue E-Mail bestätigt wurde."
@@ -2788,6 +2830,7 @@ de_DE:
zipcode: Postleitzahl
weight: Gewicht (pro kg)
error_user_destroy_with_orders: "Benutzer mit abgeschlossenen Bestellungen dürfen nicht gelöscht werden"
options: "Optionen"
actions:
update: "Aktualisieren"
errors:
@@ -2819,6 +2862,53 @@ de_DE:
product_properties:
index:
inherits_properties_checkbox_hint: "Vererben Eigenschaften von %{supplier}? (außer oben aufgehoben)"
add_product_properties: "Produkteigenschaften hinzufügen"
select_from_prototype: "Wählen Sie Aus Prototyp"
properties:
index:
properties: "Eigenschaften"
new_property: "Neues Eigentum"
name: "Name"
presentation: "Präsentation"
new:
new_property: "Neues Eigentum"
edit:
editing_property: "Eigenschaft bearbeiten"
back_to_properties_list: "Zurück zur Eigenschaftenliste"
form:
name: "Name"
presentation: "Präsentation"
return_authorizations:
index:
new_return_authorization: "Neue Rücksendegenehmigung"
return_authorizations: "Rückgabeberechtigungen"
back_to_orders_list: "Zurück zur Bestellliste"
rma_number: "RMA-Nummer"
status: "Status"
amount: "Betrag"
cannot_create_returns: "Retouren können nicht erstellt werden, da für diese Bestellung keine Versandeinheiten vorhanden sind."
continue: "Fortsetzen"
new:
new_return_authorization: "Neue Rücksendegenehmigung"
back_to_return_authorizations_list: "Zurück zur Autorisierungsliste"
continue: "Fortsetzen"
edit:
receive: "erhalten"
are_you_sure: "Bist du sicher?"
return_authorization: "Rücksendegenehmigung"
form:
product: "Produkt"
quantity_shipped: "Menge ausgeliefert"
quantity_returned: "Menge zurückgegeben"
return_quantity: "Rückgabemenge"
amount: "Betrag"
rma_value: "RMA-Wert"
reason: "Grund"
stock_location: "Lagerort"
states:
authorized: "Autorisiert"
received: "Empfangen"
canceled: "Abgesagt"
orders:
index:
listing_orders: "Bestellungen auflisten"
@@ -3009,10 +3099,23 @@ de_DE:
index:
sku: "Artikelnummer"
price: "Preis"
options: "Optionen"
no_results: "Keine Ergebnisse"
to_add_variants_you_must_first_define: "Um Varianten hinzuzufügen, müssen Sie zuerst definieren"
option_types: "Optionstypen"
option_values: "Optionswerte"
and: "und"
new_variant: "Neue Variante"
show_active: "Aktiv anzeigen"
show_deleted: "Show gelöscht"
new:
new_variant: "Neue Variante"
form:
cost_price: "Selbstkostenpreis"
sku: "Artikelnummer"
price: "Preis"
display_as: "Angezeigt als"
display_name: "Anzeigename"
autocomplete:
producer_name: "Produzent"
unit: "Einheit"
@@ -3152,3 +3255,19 @@ de_DE:
allow_charges?: "Gebühren erlauben?"
localized_number:
invalid_format: hat ein ungültiges Format. Bitte Ziffern eingeben.
api:
invalid_api_key: "Ungültiger API-Schlüssel (%{key}) angegeben."
unauthorized: "Sie sind nicht berechtigt, diese Aktion auszuführen."
invalid_resource: "Ungültige Ressource. Bitte beheben Sie die Fehler und versuchen Sie es erneut."
resource_not_found: "Die gesuchte Ressource wurde nicht gefunden."
access: "API-Zugriff"
key: "Schlüssel"
clear_key: "Schlüssel löschen"
regenerate_key: "Schlüssel neu generieren"
no_key: "Kein Schlüssel"
generate_key: "API-Schlüssel generieren"
key_generated: "Schlüssel generiert"
key_cleared: "Schlüssel gelöscht"
shipment:
cannot_ready: "Versand nicht möglich."
invalid_taxonomy_id: "Ungültige Taxonomie-ID"

View File

@@ -114,7 +114,9 @@ en_AU:
email_welcome: "Welcome"
email_registered: "is now part of"
email_userguide_html: "The User Guide with detailed support for setting up your Producer or Hub is here: %{link}"
userguide: "Open Food Network User Guide"
email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration."
admin_panel: "Admin Panel"
email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. %{link}"
join_community: "Join the community"
invite_manager:
@@ -251,6 +253,7 @@ en_AU:
expired: has expired, please request a new one
back_to_payments_list: "Back to Payments List"
maestro_or_solo_cards: "Maestro/Solo cards"
backordered: "Backordered"
on hand: "On Hand"
ship: "Ship"
actions:
@@ -259,6 +262,7 @@ en_AU:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -430,9 +434,12 @@ en_AU:
infinity: "Infinity"
to_order_tip: "Items made to order do not have a set stock level, such as loaves of bread made fresh to order."
back_to_products_list: "Back to products list"
editing_product: "Editing Product"
tabs:
product_details: "Product Details"
group_buy_options: "Group Buy Options"
images: "Images"
variants: "Variants"
product_properties: "Product Properties"
product_import:
title: Product Import
@@ -535,6 +542,7 @@ en_AU:
title: Inventory
description: Use this page to manage inventories for your enterprises. Any product details set here will override those set on the 'Products' page
enable_reset?: Enable Stock Reset?
default_stock: "Default stock"
inherit?: Inherit?
add: Add
hide: Hide
@@ -826,10 +834,36 @@ en_AU:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
back_to_list: "Back To List"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
save_and_back_to_list: "Save and Back to List"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
cancel: "Cancel"
back_to_list: "Back To List"
wizard_progress:
edit: "1. General Settings"
incoming: "2. Incoming Products"
outgoing: "3. Outgoing Products"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -856,6 +890,7 @@ en_AU:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2623,6 +2658,8 @@ en_AU:
name_or_sku: "Name or SKU (enter at least first 4 characters of product name)"
resend: Resend
back_to_orders_list: Back To Orders List
return_authorizations: Return Authorizations
cannot_create_returns: Cannot create returns as this order has no shipped units.
select_stock: "Select stock"
location: "Location"
count_on_hand: "Count On Hand"
@@ -2771,8 +2808,15 @@ en_AU:
minimal_amount: "Minimal Amount"
normal_amount: "Normal Amount"
discount_amount: "Discount Amount"
no_images_found: "No Images Found"
new_image: "New Image"
filename: "Filename"
alt_text: "Alternative Text"
thumbnail: "Thumbnail"
back_to_images_list: "Back To Images List"
email: Email
account_updated: "Account updated!"
email_updated: "The account will be updated once the new email is confirmed."
my_account: "My account"
date: "Date"
time: "Time"
@@ -2781,6 +2825,7 @@ en_AU:
zipcode: Postcode
weight: Weight (per kg)
error_user_destroy_with_orders: "Users with completed orders may not be deleted"
options: "Options"
actions:
update: "Update"
errors:
@@ -2812,6 +2857,53 @@ en_AU:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
add_product_properties: "Add Product Properties"
select_from_prototype: "Select From Prototype"
properties:
index:
properties: "Properties"
new_property: "New Property"
name: "Name"
presentation: "Presentation"
new:
new_property: "New Property"
edit:
editing_property: "Editing Property"
back_to_properties_list: "Back To Properties List"
form:
name: "Name"
presentation: "Presentation"
return_authorizations:
index:
new_return_authorization: "New Return Authorization"
return_authorizations: "Return Authorizations"
back_to_orders_list: "Back To Orders List"
rma_number: "RMA Number"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
new_return_authorization: "New Return Authorization"
back_to_return_authorizations_list: "Back To Return Authorization List"
continue: "Continue"
edit:
receive: "receive"
are_you_sure: "Are you sure?"
return_authorization: "Return Authorization"
form:
product: "Product"
quantity_shipped: "Quantity Shipped"
quantity_returned: "Quantity Returned"
return_quantity: "Return Quantity"
amount: "Amount"
rma_value: "RMA Value"
reason: "Reason"
stock_location: "Stock Location"
states:
authorized: "Authorized"
received: "Received"
canceled: "Canceled"
orders:
index:
listing_orders: "Listing Orders"
@@ -3002,12 +3094,23 @@ en_AU:
index:
sku: "SKU"
price: "Price"
options: "Options"
no_results: "No results"
to_add_variants_you_must_first_define: "To add variants, you must first define"
option_types: "Option Types"
option_values: "Option Values"
and: "and"
new_variant: "New Variant"
show_active: "Show Active"
show_deleted: "Show Deleted"
new:
new_variant: "New Variant"
form:
cost_price: "Cost Price"
sku: "SKU"
price: "Price"
display_as: "Display As"
display_name: "Display Name"
autocomplete:
producer_name: "Producer"
unit: "Unit"
@@ -3147,3 +3250,19 @@ en_AU:
allow_charges?: "Allow Charges?"
localized_number:
invalid_format: has an invalid format. Please enter a number.
api:
invalid_api_key: "Invalid API key (%{key}) specified."
unauthorized: "You are not authorized to perform that action."
invalid_resource: "Invalid resource. Please fix errors and try again."
resource_not_found: "The resource you were looking for could not be found."
access: "API Access"
key: "Key"
clear_key: "Clear key"
regenerate_key: "Regenerate Key"
no_key: "No key"
generate_key: "Generate API key"
key_generated: "Key generated"
key_cleared: "Key cleared"
shipment:
cannot_ready: "Cannot ready shipment."
invalid_taxonomy_id: "Invalid taxonomy id."

View File

@@ -257,6 +257,7 @@ en_BE:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -822,10 +823,24 @@ en_BE:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
next: "Next"
cancel: "Cancel"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
next: "Next"
cancel: "Cancel"
outgoing:
previous: "Previous"
save: "Save"
cancel: "Cancel"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -852,6 +867,7 @@ en_BE:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2805,6 +2821,25 @@ en_BE:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
properties:
index:
properties: "Properties"
name: "Name"
form:
name: "Name"
return_authorizations:
index:
back_to_orders_list: "Back To Orders List"
status: "Status"
amount: "Amount"
continue: "Continue"
new:
continue: "Continue"
edit:
are_you_sure: "Are you sure?"
form:
product: "Product"
amount: "Amount"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -262,6 +262,7 @@ en_CA:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -2872,6 +2873,21 @@ en_CA:
form:
name: "Name"
presentation: "Presentation"
return_authorizations:
index:
return_authorizations: "Return Authoriations"
back_to_orders_list: "Back to Orders List"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units"
continue: "Continue"
new:
continue: "Continue"
edit:
are_you_sure: "Are you sure?"
form:
product: "Product"
amount: "Amount"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -262,6 +262,7 @@ en_DE:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -830,10 +831,24 @@ en_DE:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
next: "Next"
cancel: "Cancel"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
next: "Next"
cancel: "Cancel"
outgoing:
previous: "Previous"
save: "Save"
cancel: "Cancel"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -860,6 +875,7 @@ en_DE:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2819,6 +2835,27 @@ en_DE:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
properties:
index:
properties: "Properties"
name: "Name"
form:
name: "Name"
return_authorizations:
index:
return_authorizations: "Return Authorizations"
back_to_orders_list: "Back To Orders List"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
continue: "Continue"
edit:
are_you_sure: "Are you sure?"
form:
product: "Product"
amount: "Amount"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -262,6 +262,7 @@ en_GB:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -833,10 +834,36 @@ en_GB:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
back_to_list: "Back To List"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
save_and_back_to_list: "Save and Back to List"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
cancel: "Cancel"
back_to_list: "Back To List"
wizard_progress:
edit: "1. General Settings"
incoming: "2. Incoming Products"
outgoing: "3. Outgoing Products"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -863,6 +890,7 @@ en_GB:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2837,6 +2865,51 @@ en_GB:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
add_product_properties: "Add Product Properties"
select_from_prototype: "Select From Prototype"
properties:
index:
properties: "Properties"
new_property: "New Property"
name: "Name"
presentation: "Presentation"
new:
new_property: "New Property"
edit:
editing_property: "Editing Property"
back_to_properties_list: "Back To Properties List"
form:
name: "Name"
presentation: "Presentation"
return_authorizations:
index:
new_return_authorization: "New Return Authorisation"
return_authorizations: "Return Authorisations"
back_to_orders_list: "Back To Orders List"
rma_number: "RMA Number"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
new_return_authorization: "New Return Authorisation"
back_to_return_authorizations_list: "Back To Return Authorisation List"
continue: "Continue"
edit:
receive: "receive"
are_you_sure: "Are you sure?"
return_authorization: "Return Authorisation"
form:
product: "Product"
quantity_shipped: "Quantity Shipped"
quantity_returned: "Quantity Returned"
return_quantity: "Return Quantity"
amount: "Amount"
rma_value: "RMA Value"
reason: "Reason"
stock_location: "Stock Location"
states:
authorized: "Authorised"
received: "Received"
canceled: "Canceled"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -262,6 +262,7 @@ en_NZ:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -833,10 +834,36 @@ en_NZ:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
back_to_list: "Back To List"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
save_and_back_to_list: "Save and Back to List"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
save_and_next: "Save and Next"
next: "Next"
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
cancel: "Cancel"
back_to_list: "Back To List"
wizard_progress:
edit: "1. General Settings"
incoming: "2. Incoming Products"
outgoing: "3. Outgoing Products"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -863,6 +890,7 @@ en_NZ:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2845,6 +2873,37 @@ en_NZ:
form:
name: "Name"
presentation: "Presentation"
return_authorizations:
index:
new_return_authorization: "New Return Authorization"
return_authorizations: "Return Authorizations"
back_to_orders_list: "Back To Orders List"
rma_number: "RMA Number"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
new_return_authorization: "New Return Authorization"
back_to_return_authorizations_list: "Back To Return Authorization List"
continue: "Continue"
edit:
receive: "receive"
are_you_sure: "Are you sure?"
return_authorization: "Return Authorization"
form:
product: "Product"
quantity_shipped: "Quantity Shipped"
quantity_returned: "Quantity Returned"
return_quantity: "Return Quantity"
amount: "Amount"
rma_value: "RMA Value"
reason: "Reason"
stock_location: "Stock Location"
states:
authorized: "Authorized"
received: "Received"
canceled: "Canceled"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -260,6 +260,7 @@ en_US:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -827,10 +828,24 @@ en_US:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
next: "Next"
cancel: "Cancel"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
next: "Next"
cancel: "Cancel"
outgoing:
previous: "Previous"
save: "Save"
cancel: "Cancel"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -857,6 +872,7 @@ en_US:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2816,6 +2832,27 @@ en_US:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
properties:
index:
properties: "Properties"
name: "Name"
form:
name: "Name"
return_authorizations:
index:
return_authorizations: "Return Authorizations"
back_to_orders_list: "Back To Orders List"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
continue: "Continue"
edit:
are_you_sure: "Are you sure?"
form:
product: "Product"
amount: "Amount"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -259,6 +259,7 @@ en_ZA:
cancel: "Cancel"
save: "Save"
edit: "Edit"
update: "Update"
delete: "Delete"
admin:
begins_at: Begins At
@@ -826,10 +827,24 @@ en_ZA:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: LOADING...
new:
create: "Create"
cancel: "Cancel"
edit:
advanced_settings: Advanced Settings
update_and_close: Update and Close
choose_products_from: 'Choose Products From:'
advanced_settings: "Advanced Settings"
save: "Save"
next: "Next"
cancel: "Cancel"
choose_products_from: "Choose Products From:"
incoming:
previous: "Previous"
save: "Save"
next: "Next"
cancel: "Cancel"
outgoing:
previous: "Previous"
save: "Save"
cancel: "Cancel"
exchange_form:
pickup_time_tip: When orders from this OC will be ready for the customer
pickup_instructions_placeholder: "Pick-up instructions"
@@ -856,6 +871,7 @@ en_ZA:
any_enterprise: "Any Enterprise"
any_schedule: "Any Schedule"
form:
general_settings: "General Settings"
incoming: Incoming
supplier: Supplier
receival_details: Receival details
@@ -2819,6 +2835,25 @@ en_ZA:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
properties:
index:
properties: "Properties"
name: "Name"
form:
name: "Name"
return_authorizations:
index:
back_to_orders_list: "Back To Orders List"
status: "Status"
amount: "Amount"
continue: "Continue"
new:
continue: "Continue"
edit:
are_you_sure: "Are you sure?"
form:
product: "Product"
amount: "Amount"
orders:
index:
listing_orders: "Listing Orders"

View File

@@ -262,6 +262,7 @@ es:
cancel: "Cancelar"
save: "Guardar"
edit: "Editar"
update: "Actualizar"
delete: "Borrar"
admin:
begins_at: Empieza en
@@ -832,10 +833,24 @@ es:
loading_flash:
loading_order_cycles: Cargando ciclos de pedido
loading: Cargando...
new:
create: "Crear"
cancel: "Cancelar"
edit:
advanced_settings: Configuración Avanzada
update_and_close: Actualizar y Cerrar
choose_products_from: 'Escoger Productos desde:'
advanced_settings: "Configuración Avanzada"
save: "Guardar"
next: "Siguiente"
cancel: "Cancelar"
choose_products_from: "Escoger Productos desde:"
incoming:
previous: "Anterior"
save: "Guardar"
next: "Siguiente"
cancel: "Cancelar"
outgoing:
previous: "Anterior"
save: "Guardar"
cancel: "Cancelar"
exchange_form:
pickup_time_tip: Cuando los pedidos de este ciclo de pedido estarán listos para la consumidora
pickup_instructions_placeholder: "Instrucciones de recogida"
@@ -862,6 +877,7 @@ es:
any_enterprise: "Cualquier organización"
any_schedule: "Cualquier programación"
form:
general_settings: "Configuración general"
incoming: Entrante
supplier: Proveedora
receival_details: Detalles de la recepción
@@ -2825,6 +2841,27 @@ es:
product_properties:
index:
inherits_properties_checkbox_hint: "¿Heredar propiedades desde %{supplier}? (a menos que sea anulado arriba)"
properties:
index:
properties: "Propiedades"
name: "Nombre"
form:
name: "Nombre"
return_authorizations:
index:
return_authorizations: "Autorizaciones de devolución"
back_to_orders_list: "Volver a la lista de pedidos"
status: "Estado"
amount: "Cantidad"
cannot_create_returns: "No se pueden crear devoluciones ya que este pedido no tiene unidades enviadas."
continue: "Continuar"
new:
continue: "Continuar"
edit:
are_you_sure: "¿Está seguro?"
form:
product: "Producto"
amount: "Cantidad"
orders:
index:
listing_orders: "Pedidos de listado"

View File

@@ -257,6 +257,7 @@ fr_BE:
cancel: "Annuler"
save: "Sauvergarder"
edit: "Modifier"
update: "Mettre à jour"
delete: "Supprimer"
admin:
begins_at: Commence
@@ -823,10 +824,24 @@ fr_BE:
loading_flash:
loading_order_cycles: CHARGEMENT DES CYCLES DE VENTES
loading: CHARGEMENT...
new:
create: "Créer"
cancel: "Annuler"
edit:
advanced_settings: Paramétrages avancés
update_and_close: Mettre à jour et fermer
choose_products_from: 'Choisir produits depuis :'
advanced_settings: "Paramétrages avancés"
save: "Sauvergarder"
next: "Suivant"
cancel: "Annuler"
choose_products_from: "Choisir produits depuis :"
incoming:
previous: "Précédent"
save: "Sauvergarder"
next: "Suivant"
cancel: "Annuler"
outgoing:
previous: "Précédent"
save: "Sauvergarder"
cancel: "Annuler"
exchange_form:
pickup_time_tip: Quand des commandes liées à ce cycle de vente seront prêtes à être soumises à l'acheteur
pickup_instructions_placeholder: "Modalités de retrait/livraison"
@@ -853,6 +868,7 @@ fr_BE:
any_enterprise: "Toutes les entreprises"
any_schedule: "Tous"
form:
general_settings: "Réglages Généraux"
incoming: Produits entrants (pouvant être mis en vente par les hubs)
supplier: Fournisseur
receival_details: Détails livraison produits
@@ -2819,6 +2835,25 @@ fr_BE:
product_properties:
index:
inherits_properties_checkbox_hint: "Hériter des propriétés de %{supplier} ? (non applicable si information de remplacement déjà saisie)"
properties:
index:
properties: "Labels / propriétés"
name: "Nom"
form:
name: "Nom"
return_authorizations:
index:
back_to_orders_list: "Retour vers la liste des commandes "
status: "Statut"
amount: "Quantité"
continue: "Suivant"
new:
continue: "Suivant"
edit:
are_you_sure: "Confirmer?"
form:
product: "Produit"
amount: "Quantité"
orders:
index:
listing_orders: "Liste des commandes"

View File

@@ -261,6 +261,7 @@ fr_CA:
cancel: "Annuler"
save: "Enregistrer"
edit: "Modifier"
update: "Mettre à jour"
delete: "Supprimer"
admin:
begins_at: Commence à
@@ -829,10 +830,24 @@ fr_CA:
loading_flash:
loading_order_cycles: Cycles de vente en cours de chargement
loading: Chargement en cours...
new:
create: "Créer"
cancel: "Annuler"
edit:
advanced_settings: Paramétrages avancés
update_and_close: Mettre à jour et fermer
choose_products_from: 'Choisir produits depuis :'
advanced_settings: "Paramétrages avancés"
save: "Enregistrer"
next: "Suivant"
cancel: "Annuler"
choose_products_from: "Choisir produits depuis :"
incoming:
previous: "Précédent"
save: "Enregistrer"
next: "Suivant"
cancel: "Annuler"
outgoing:
previous: "Précédent"
save: "Enregistrer"
cancel: "Annuler"
exchange_form:
pickup_time_tip: Quand des commandes liées à ce cycle de vente seront prêtes à être soumises à l'acheteur
pickup_instructions_placeholder: "Modalités de retrait/livraison"
@@ -859,6 +874,7 @@ fr_CA:
any_enterprise: "Toutes les entreprises"
any_schedule: "Tous"
form:
general_settings: "Configurations générales"
incoming: Produits entrants (pouvant être mis en vente par les hubs)
supplier: Fournisseur
receival_details: Détails livraison produits
@@ -2827,6 +2843,27 @@ fr_CA:
product_properties:
index:
inherits_properties_checkbox_hint: "Hériter des propriétés de %{supplier}? (non applicable si information de remplacement déjà saisie)"
properties:
index:
properties: "Labels / propriétés"
name: "Nom"
form:
name: "Nom"
return_authorizations:
index:
return_authorizations: "Autorisations de retours"
back_to_orders_list: "Retour à la liste des commandes"
status: "Statut"
amount: "Montant"
cannot_create_returns: "Impossible de créer une autorisation de retour car aucun produit n'a été livré pour cette commande."
continue: "Suivant"
new:
continue: "Suivant"
edit:
are_you_sure: "Confirmer?"
form:
product: "Produit"
amount: "Montant"
orders:
index:
listing_orders: "Liste des commandes"

View File

@@ -251,6 +251,7 @@ it:
cancel: "Annulla"
save: "Salva"
edit: "Modifica"
update: "Aggiorna"
delete: "Annulla"
admin:
begins_at: Inizia a
@@ -808,10 +809,24 @@ it:
user_already_exists: "L'Utente esiste già"
error: "Qualcosa è andato storto"
order_cycles:
new:
create: "Crea"
cancel: "Annulla"
edit:
advanced_settings: Impostazioni avanzate
update_and_close: Aggiorna e chiudi
choose_products_from: 'Scegli i prodotti da:'
advanced_settings: "Impostazioni avanzate"
save: "Salva"
next: "Prossimo"
cancel: "Annulla"
choose_products_from: "Scegli i prodotti da:"
incoming:
previous: "Precedente"
save: "Salva"
next: "Prossimo"
cancel: "Annulla"
outgoing:
previous: "Precedente"
save: "Salva"
cancel: "Annulla"
exchange_form:
pickup_time_tip: Quando gli ordini di questa Lista d'ordine saranno pronti per il cliente
pickup_instructions_placeholder: "Istruzioni per la consegna"
@@ -837,6 +852,7 @@ it:
any_enterprise: "Qualsiasi Impresa"
any_schedule: "Qualunque Orario"
form:
general_settings: "Impostazioni generali"
incoming: In arrivo
supplier: Fornitore
receival_details: Dettagli ritiro
@@ -2753,6 +2769,25 @@ it:
product_properties:
index:
inherits_properties_checkbox_hint: "Eredita le proprietà da %{supplier}? (se non sovrascritto)"
properties:
index:
properties: "Proprietà"
name: "Nome"
form:
name: "Nome"
return_authorizations:
index:
back_to_orders_list: "Torna alla lista delle richieste"
status: "Stato"
amount: "Quantità"
continue: "Continua"
new:
continue: "Continua"
edit:
are_you_sure: "Sei sicuro?"
form:
product: "Prodotto"
amount: "Quantità"
orders:
index:
listing_orders: "Listino Ordini"

View File

@@ -262,6 +262,7 @@ nb:
cancel: "Avbryt"
save: "Lagre"
edit: "Rediger"
update: "Oppdater"
delete: "Slett"
admin:
begins_at: Begynner på
@@ -2871,6 +2872,37 @@ nb:
form:
name: "Navn"
presentation: "Presentasjon"
return_authorizations:
index:
new_return_authorization: "Ny Returautorisasjon"
return_authorizations: "Returautorisasjoner"
back_to_orders_list: "Tilbake til Bestillingsliste"
rma_number: "RMA-nummer"
status: "Status"
amount: "Antall"
cannot_create_returns: "Kan ikke opprette retur da denne bestillingen ikke har noen sendte elementer."
continue: "Fortsett"
new:
new_return_authorization: "Ny Returautorisasjon"
back_to_return_authorizations_list: "Tilbake til liste over Returautorisasjoner"
continue: "Fortsett"
edit:
receive: "motta"
are_you_sure: "Er du sikker?"
return_authorization: "Returautorisasjon"
form:
product: "Produkt"
quantity_shipped: "Mengde Sendt"
quantity_returned: "Mengde Returnert"
return_quantity: "Returneringsmengde"
amount: "Antall"
rma_value: "RMA-verdi"
reason: "Grunn"
stock_location: "Lagerplassering"
states:
authorized: "Autorisert"
received: "Mottatt"
canceled: "Avbrutt"
orders:
index:
listing_orders: "Lister opp bestillinger"

View File

@@ -257,6 +257,7 @@ nl_BE:
cancel: "Annuleren"
save: "Save"
edit: "bewerking"
update: "Update"
delete: "Uitwissen "
admin:
begins_at: Begint Bij
@@ -825,10 +826,24 @@ nl_BE:
loading_flash:
loading_order_cycles: LOADING ORDER CYCLES
loading: BEZIG MET LADEN...
new:
create: "Maak"
cancel: "Annuleren"
edit:
advanced_settings: Geavanceerde Instellingen
update_and_close: Update en Sluit
choose_products_from: 'Kies Producten Uit:'
advanced_settings: "Geavanceerde Instellingen"
save: "Save"
next: "Volgende"
cancel: "Annuleren"
choose_products_from: "Kies Producten Uit:"
incoming:
previous: "Voorgaande"
save: "Save"
next: "Volgende"
cancel: "Annuleren"
outgoing:
previous: "Voorgaande"
save: "Save"
cancel: "Annuleren"
exchange_form:
pickup_time_tip: Wanneer bestellingen van deze OC klaar staan voor de klant
pickup_instructions_placeholder: "Aanwijzingen voor ophaling"
@@ -855,6 +870,7 @@ nl_BE:
any_enterprise: "Een Onderneming"
any_schedule: "Een Planning"
form:
general_settings: "Algemene instellingen"
incoming: Binnenkomend
supplier: Leverancier
receival_details: Ontvangstspecificaties
@@ -2814,6 +2830,25 @@ nl_BE:
product_properties:
index:
inherits_properties_checkbox_hint: "De kenmerken van 1%{supplier} krijgen ? (niet ter toepassing in geval van invoer van de vervanging)"
properties:
index:
properties: "Eigenschappen"
name: "Naam"
form:
name: "Naam"
return_authorizations:
index:
back_to_orders_list: "Terug naar de bestellingslijst"
status: "Status"
amount: "Bedrag"
continue: "Ga verder"
new:
continue: "Ga verder"
edit:
are_you_sure: "Ben je zeker?"
form:
product: "Product"
amount: "Bedrag"
orders:
index:
listing_orders: "Lijst van de bestellingen"

View File

@@ -251,6 +251,7 @@ pt:
cancel: "Cancelar"
save: "Guardar"
edit: "Editar"
update: "Atualizar"
delete: "Apagar"
admin:
begins_at: Começa às
@@ -813,10 +814,24 @@ pt:
loading_flash:
loading_order_cycles: A carregar ciclos de encomenda
loading: A carregar...
new:
create: "Criar"
cancel: "Cancelar"
edit:
advanced_settings: Configurações Avançadas
update_and_close: Atualizar e fechar
choose_products_from: 'Escolha produtos de:'
advanced_settings: "Configurações Avançadas"
save: "Guardar"
next: "Seguinte"
cancel: "Cancelar"
choose_products_from: "Escolha produtos de:"
incoming:
previous: "Anterior"
save: "Guardar"
next: "Seguinte"
cancel: "Cancelar"
outgoing:
previous: "Anterior"
save: "Guardar"
cancel: "Cancelar"
exchange_form:
pickup_time_tip: Quando as encomendas deste ciclo ficam prontas para o consumidor
pickup_instructions_placeholder: "Instruções para levantamento"
@@ -842,6 +857,7 @@ pt:
any_enterprise: "Qualquer Organização"
any_schedule: "Qualquer Horário"
form:
general_settings: "Configurações Gerais"
incoming: Entrada
supplier: Fornecedor
receival_details: Detalhes de recebimento
@@ -2761,6 +2777,25 @@ pt:
product_properties:
index:
inherits_properties_checkbox_hint: "Herdar propriedades de %{supplier}? (excepto se já alterado em cima)"
properties:
index:
properties: "Propriedades"
name: "Nome"
form:
name: "Nome"
return_authorizations:
index:
back_to_orders_list: "Voltar à Lista de Encomendas"
status: "Estado"
amount: "Quantia"
continue: "Continuar"
new:
continue: "Continuar"
edit:
are_you_sure: "Tem a certeza?"
form:
product: "Produto"
amount: "Quantia"
orders:
index:
listing_orders: "Lista de Encomendas"

View File

@@ -260,6 +260,7 @@ pt_BR:
cancel: "Cancelar"
save: "Salvar"
edit: "Editar"
update: "Atualizar"
delete: "Deletar"
admin:
begins_at: Começa em
@@ -827,10 +828,24 @@ pt_BR:
loading_flash:
loading_order_cycles: CARREGANDO CICLOS DE PEDIDOS
loading: CARREGANDO...
new:
create: "Criar"
cancel: "Cancelar"
edit:
advanced_settings: Configurações avançadas
update_and_close: Atualizar e fechar
choose_products_from: 'Escolha produtos de:'
advanced_settings: "Configurações avançadas"
save: "Salvar"
next: "Próximo"
cancel: "Cancelar"
choose_products_from: "Escolha produtos de:"
incoming:
previous: "Anterior"
save: "Salvar"
next: "Próximo"
cancel: "Cancelar"
outgoing:
previous: "Anterior"
save: "Salvar"
cancel: "Cancelar"
exchange_form:
pickup_time_tip: Quando os pedidos desse ciclo de pedidos estarão prontos para os consumidores
pickup_instructions_placeholder: "Instruções para retirada"
@@ -857,6 +872,7 @@ pt_BR:
any_enterprise: "Qualquer empresa"
any_schedule: "Qualquer cronograma"
form:
general_settings: "Configurações Gerais"
incoming: Entrada
supplier: Fornecedor
receival_details: Detalhes de recebimento
@@ -2817,6 +2833,24 @@ pt_BR:
product_properties:
index:
inherits_properties_checkbox_hint: "Herdar propriedades de %{supplier}? (a menos que substituído acima)"
properties:
index:
properties: "Propriedades"
name: "Nome"
form:
name: "Nome"
return_authorizations:
index:
status: "Status"
amount: "Montante"
continue: "Continuar"
new:
continue: "Continuar"
edit:
are_you_sure: "Tem certeza?"
form:
product: "Produto"
amount: "Montante"
orders:
index:
listing_orders: "Listagem de Pedidos"

View File

@@ -111,6 +111,7 @@ sv:
create: "Skapa"
cancel: "Avbryt"
edit: "Redigera"
update: "Uppdatera"
admin:
date: Datum
email: epost
@@ -483,10 +484,19 @@ sv:
profile: 'Profil'
producer_profile: 'Producentprofil'
order_cycles:
new:
create: "Skapa"
cancel: "Avbryt"
edit:
advanced_settings: Avancerade inställningar
update_and_close: Uppdatera och Stäng
choose_products_from: 'Välj produkter från:'
advanced_settings: "Avancerade inställningar"
next: "Näst"
cancel: "Avbryt"
choose_products_from: "Välj produkter från:"
incoming:
next: "Näst"
cancel: "Avbryt"
outgoing:
cancel: "Avbryt"
exchange_form:
pickup_instructions_placeholder: "Instruktioner för upphämtning"
pickup_time_placeholder: "Klar för (dvs. Datum/Tid)"
@@ -1941,6 +1951,24 @@ sv:
enterprises: "Företag"
customers: "Kunder"
groups: "Grupper"
properties:
index:
properties: "Egenskaper"
name: "Namn"
form:
name: "Namn"
return_authorizations:
index:
status: "Status"
amount: "Belopp"
continue: "Fortsätt"
new:
continue: "Fortsätt"
edit:
are_you_sure: "Är du säker?"
form:
product: "Produkt"
amount: "Belopp"
orders:
index:
capture: "Fånga"

View File

@@ -46,12 +46,12 @@ module OpenFoodNetwork
end
def search
Reports::LineItems.search_orders(permissions, params)
report_line_items.orders
end
def table_items
return [] unless @render_table
Reports::LineItems.list(permissions, report_options)
report_line_items.list(line_item_includes)
end
def rules
@@ -121,18 +121,18 @@ module OpenFoodNetwork
private
def report_options
@params.merge(line_item_includes: line_item_includes)
end
def line_item_includes
[{ order: [:bill_address],
variant: [{ option_values: :option_type }, { product: :supplier }] }]
end
def permissions
return @permissions unless @permissions.nil?
@permissions = OpenFoodNetwork::Permissions.new(@user)
def order_permissions
return @order_permissions unless @order_permissions.nil?
@order_permissions = ::Permissions::Order.new(@user)
end
def report_line_items
@report_line_items ||= Reports::LineItems.new(order_permissions, @params)
end
end
end

View File

@@ -5,7 +5,7 @@ module OpenFoodNetwork
@user = user
@render_table = render_table
@permissions = OpenFoodNetwork::Permissions.new(user)
@permissions = ::Permissions::Order.new(user)
end
def header

View File

@@ -13,28 +13,24 @@ module OpenFoodNetwork
delegate :header, :rules, :columns, to: :report
def initialize(permissions, options = {}, render_table = false)
def initialize(user, options = {}, render_table = false)
@user = user
@options = options
@report_type = options[:report_type]
@permissions = permissions
@render_table = render_table
end
def search
Reports::LineItems.search_orders(permissions, options)
report_line_items.orders
end
def table_items
return [] unless @render_table
list_options = options.merge(line_item_includes: report.line_item_includes)
Reports::LineItems.list(permissions, list_options)
report_line_items.list(report.line_item_includes)
end
private
attr_reader :permissions
def report
@report ||= report_klass.new(self)
end
@@ -84,5 +80,14 @@ module OpenFoodNetwork
def scale_factor(product)
product.variant_unit == 'weight' ? 1000 : 1
end
def order_permissions
return @order_permissions unless @order_permissions.nil?
@order_permissions = ::Permissions::Order.new(@user)
end
def report_line_items
@report_line_items ||= Reports::LineItems.new(order_permissions, options)
end
end
end

View File

@@ -38,12 +38,12 @@ module OpenFoodNetwork
end
def search
Reports::LineItems.search_orders(permissions, params)
report_line_items.orders
end
def table_items
return [] unless @render_table
Reports::LineItems.list(permissions, report_options)
report_line_items.list(line_item_includes)
end
def rules
@@ -95,7 +95,7 @@ module OpenFoodNetwork
def columns
if is_by_customer?
[proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| customer_code(line_items.first.order.email) },
proc { |line_items| customer_code(line_items.first.order) },
proc { |line_items| line_items.first.order.bill_address.firstname },
proc { |line_items| line_items.first.order.bill_address.lastname },
proc { |line_items| line_items.first.product.supplier.name },
@@ -107,7 +107,7 @@ module OpenFoodNetwork
[
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.product.supplier.name },
proc { |line_items| customer_code(line_items.first.order.email) },
proc { |line_items| customer_code(line_items.first.order) },
proc { |line_items| line_items.first.order.bill_address.firstname },
proc { |line_items| line_items.first.order.bill_address.lastname },
proc { |line_items| line_items.first.product.name },
@@ -120,18 +120,15 @@ module OpenFoodNetwork
private
def report_options
@params.merge(line_item_includes: line_item_includes)
end
def line_item_includes
[{ order: [:bill_address, :distributor],
variant: [{ option_values: :option_type }, { product: :supplier }] }]
[{ option_values: :option_type,
order: [:bill_address, :distributor, :customer],
variant: { product: [:supplier, :shipping_category] } }]
end
def permissions
return @permissions unless @permissions.nil?
@permissions = OpenFoodNetwork::Permissions.new(@user)
def order_permissions
return @order_permissions unless @order_permissions.nil?
@order_permissions = ::Permissions::Order.new(@user)
end
def is_temperature_controlled?(line_item)
@@ -146,9 +143,13 @@ module OpenFoodNetwork
params[:report_type] == "pack_by_customer"
end
def customer_code(email)
customer = Customer.where(email: email).first
def customer_code(order)
customer = order.customer
customer.nil? ? "" : customer.code
end
def report_line_items
@report_line_items ||= Reports::LineItems.new(order_permissions, @params)
end
end
end

View File

@@ -58,55 +58,12 @@ module OpenFoodNetwork
permissions
end
# Find enterprises that an admin is allowed to add to an order cycle
def visible_orders
# Any orders that I can edit
editable = editable_orders.pluck(:id)
produced = Spree::Order.with_line_items_variants_and_products_outer.
where(
distributor_id: granted_distributor_ids,
spree_products: { supplier_id: enterprises_with_associated_orders }
).pluck(:id)
Spree::Order.where(id: editable | produced)
end
# Find enterprises that an admin is allowed to add to an order cycle
def editable_orders
# Any orders placed through any hub that I manage
managed = Spree::Order.where(distributor_id: managed_enterprises.pluck(:id)).pluck(:id)
# Any order that is placed through an order cycle one of my managed enterprises coordinates
coordinated = Spree::Order.
where(order_cycle_id: coordinated_order_cycles.pluck(:id)).
pluck(:id)
Spree::Order.where(id: managed | coordinated )
end
def visible_line_items
# Any line items that I can edit
editable = editable_line_items.pluck(:id)
# Any from visible orders, where the product is produced by one of my managed producers
produced = Spree::LineItem.where(order_id: visible_orders.pluck(:id)).
joins(:product).
where(spree_products: { supplier_id: managed_enterprises.is_primary_producer.pluck(:id) })
Spree::LineItem.where(id: editable | produced)
end
def editable_line_items
Spree::LineItem.where(order_id: editable_orders)
end
def editable_products
permitted_enterprise_products_ids = product_ids_supplied_by(
related_enterprises_granting(:manage_products)
)
Spree::Product.where(
id: managed_enterprise_products.pluck(:id) | permitted_enterprise_products_ids
id: managed_enterprise_products.select(:id) | permitted_enterprise_products_ids
)
end
@@ -116,10 +73,14 @@ module OpenFoodNetwork
related_enterprises_granting(:add_to_order_cycle)
)
Spree::Product.where(
id: managed_enterprise_products.pluck(:id) | permitted_enterprise_products_ids
id: managed_enterprise_products.select(:id) | permitted_enterprise_products_ids
)
end
def product_ids_supplied_by(supplier_ids)
Spree::Product.where(supplier_id: supplier_ids).select(:id)
end
def managed_product_enterprises
managed_and_related_enterprises_granting :manage_products
end
@@ -129,14 +90,16 @@ module OpenFoodNetwork
end
def editable_schedules
Schedule.joins(:order_cycles).
where(order_cycles: { id: OrderCycle.managed_by(@user).pluck(:id) }).
Schedule.
joins(:order_cycles).
where(order_cycles: { id: OrderCycle.managed_by(@user).select("order_cycles.id") }).
select("DISTINCT schedules.*")
end
def visible_schedules
Schedule.joins(:order_cycles).
where(order_cycles: { id: OrderCycle.accessible_by(@user).pluck(:id) }).
Schedule.
joins(:order_cycles).
where(order_cycles: { id: OrderCycle.managed_by(@user).select("order_cycles.id") }).
select("DISTINCT schedules.*")
end
@@ -148,53 +111,6 @@ module OpenFoodNetwork
editable_subscriptions
end
private
def admin?
@user.admin?
end
def granted_distributor_ids
@granted_distributor_ids ||= related_enterprises_granted(
:add_to_order_cycle,
by: managed_enterprises.is_primary_producer.pluck(:id)
).pluck(:id)
end
def enterprises_with_associated_orders
# Any orders placed through hubs that my producers have granted P-OC,
# and which contain their products. This is pretty complicated but it's looking for order
# where at least one of my producers has granted P-OC to the distributor
# AND the order contains products of at least one of THE SAME producers
related_enterprises_granting(:add_to_order_cycle, to: granted_distributor_ids).
merge(managed_enterprises.is_primary_producer)
end
def managed_and_related_enterprises_granting(permission)
if admin?
Enterprise.scoped
else
Enterprise.where(
id: managed_enterprises.pluck(:id) | related_enterprises_granting(permission)
)
end
end
def managed_and_related_enterprises_with(permission)
if admin?
Enterprise.scoped
else
managed_enterprise_ids = managed_enterprises.pluck(:id)
granting_enterprise_ids = related_enterprises_granting(permission)
granted_enterprise_ids = related_enterprises_granted(permission)
Enterprise.where(
id: managed_enterprise_ids | granting_enterprise_ids | granted_enterprise_ids
)
end
end
def managed_enterprises
@managed_enterprises ||= Enterprise.managed_by(@user)
end
@@ -209,7 +125,7 @@ module OpenFoodNetwork
parent_ids = EnterpriseRelationship.
permitting(options[:to] || managed_enterprises.select("enterprises.id")).
with_permission(permission).
pluck(:parent_id)
select(:parent_id)
(options[:scope] || Enterprise).where(id: parent_ids).select("enterprises.id")
end
@@ -218,17 +134,44 @@ module OpenFoodNetwork
child_ids = EnterpriseRelationship.
permitted_by(options[:by] || managed_enterprises.select("enterprises.id")).
with_permission(permission).
pluck(:child_id)
select(:child_id)
(options[:scope] || Enterprise).where(id: child_ids).select("enterprises.id")
end
private
def admin?
@user.admin?
end
def managed_and_related_enterprises_granting(permission)
if admin?
Enterprise.scoped
else
Enterprise.where(
id: managed_enterprises.select("enterprises.id") |
related_enterprises_granting(permission)
)
end
end
def managed_and_related_enterprises_with(permission)
if admin?
Enterprise.scoped
else
managed_enterprise_ids = managed_enterprises.select("enterprises.id")
granting_enterprise_ids = related_enterprises_granting(permission)
granted_enterprise_ids = related_enterprises_granted(permission)
Enterprise.where(
id: managed_enterprise_ids | granting_enterprise_ids | granted_enterprise_ids
)
end
end
def managed_enterprise_products
Spree::Product.managed_by(@user)
end
def product_ids_supplied_by(supplier_ids)
Spree::Product.where(supplier_id: supplier_ids).pluck(:id)
end
end
end

View File

@@ -1,43 +1,66 @@
module OpenFoodNetwork
module Reports
# shared code to search and list line items
module LineItems
def self.search_orders(permissions, params)
permissions.visible_orders.complete.not_state(:canceled).search(params[:q])
class LineItems
def initialize(order_permissions, params)
@order_permissions = order_permissions
@params = params
end
def self.list(permissions, params)
orders = search_orders(permissions, params).result
def orders
@orders ||= search_orders
end
line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders))
line_items = line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
def list(line_item_includes = nil)
line_items = @order_permissions.
visible_line_items.
merge(Spree::LineItem.where(order_id: orders.result))
if params[:line_item_includes].present?
line_items = line_items.includes(*params[:line_item_includes])
if @params[:supplier_id_in].present?
line_items = line_items.supplied_by_any(@params[:supplier_id_in])
end
hidden_line_items = line_items_with_hidden_details(permissions, line_items)
if line_item_includes.present?
line_items = line_items.includes(*line_item_includes)
end
editable_line_items = editable_line_items(line_items)
line_items.select{ |li|
hidden_line_items.include? li
!editable_line_items.include? li
}.each do |line_item|
# TODO We should really be hiding customer code here too, but until we
# have an actual association between order and customer, it's a bit tricky
line_item.order.bill_address.andand.assign_attributes(firstname: I18n.t('admin.reports.hidden'), lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
line_item.order.ship_address.andand.assign_attributes(firstname: I18n.t('admin.reports.hidden'), lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
line_item.order.bill_address.andand.
assign_attributes(firstname: I18n.t('admin.reports.hidden'),
lastname: "", phone: "", address1: "", address2: "",
city: "", zipcode: "", state: nil)
line_item.order.ship_address.andand.
assign_attributes(firstname: I18n.t('admin.reports.hidden'),
lastname: "", phone: "", address1: "", address2: "",
city: "", zipcode: "", state: nil)
line_item.order.assign_attributes(email: I18n.t('admin.reports.hidden'))
end
line_items
end
def self.line_items_with_hidden_details(permissions, line_items)
editable_line_items = permissions.editable_line_items.pluck(:id)
private
if editable_line_items.empty?
line_items
else
line_items.where('"spree_line_items"."id" NOT IN (?)', editable_line_items)
end
def search_orders
@order_permissions.visible_orders.complete.not_state(:canceled).search(@params[:q])
end
# From the line_items given, returns the ones that are editable by the user
def editable_line_items(line_items)
editable_line_items_ids = @order_permissions.editable_line_items.select(:id)
# Although merge could take a relation, here we convert line_items to array
# because, if we pass a relation, merge will overwrite the conditions on the same field
# In this case: the IN clause on spree_line_items.order_id from line_items
# overwrites the IN clause on spree_line_items.order_id on editable_line_items_ids
# We convert to array the relation with less elements: line_items
editable_line_items_ids.merge(line_items.to_a)
end
end
end

View File

@@ -34,7 +34,7 @@ module OpenFoodNetwork
end
def search
permissions = OpenFoodNetwork::Permissions.new(user)
permissions = ::Permissions::Order.new(user)
permissions.editable_orders.complete.not_state(:canceled).search(params[:q])
end

View File

@@ -19,7 +19,7 @@ module OpenFoodNetwork
end
def search
permissions = OpenFoodNetwork::Permissions.new(@user)
permissions = ::Permissions::Order.new(@user)
permissions.editable_orders.complete.not_state(:canceled).search(@opts[:q])
end

View File

@@ -30,7 +30,8 @@ describe Api::TaxonsController do
it "gets all taxons" do
api_get :index
expect(json_response.first['name']).to eq taxonomy.root.name
json_names = json_response.map { |taxon_data| taxon_data["name"] }
expect(json_names).to include(taxon.name, taxon2.name)
end
it "can search for a single taxon" do

View File

@@ -0,0 +1,89 @@
require 'spec_helper'
# This is the first example of testing concurrency in the Open Food Network.
# If we want to do this more often, we should look at:
#
# https://github.com/forkbreak/fork_break
#
# The concurrency flag enables multiple threads to see the same database
# without isolated transactions.
describe CheckoutController, concurrency: true, type: :controller do
let(:order_cycle) { create(:order_cycle) }
let(:distributor) { order_cycle.distributors.first }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) }
let(:address) { create(:address) }
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
let(:breakpoint) { Mutex.new }
before do
# Create a valid order ready for checkout:
create(:shipping_method, distributors: [distributor])
variant = order_cycle.variants_distributed_by(distributor).first
order.line_items << create(:line_item, variant: variant)
# Set up controller environment:
session[:order_id] = order.id
allow(controller).to receive(:spree_current_user).and_return(order.user)
allow(controller).to receive(:current_distributor).and_return(order.distributor)
allow(controller).to receive(:current_order_cycle).and_return(order.order_cycle)
# New threads start running straight away. The breakpoint is after loading
# the order and before advancing the order's state and making payments.
breakpoint.lock
allow(controller).to receive(:check_order_for_phantom_fees) do
breakpoint.synchronize {}
end
end
it "waits for concurrent checkouts" do
# Basic data the user submits during checkout:
address_params = address.attributes.except("id")
order_params = {
"payments_attributes" => [
{
"payment_method_id" => payment_method.id,
"amount" => order.total
}
],
"bill_address_attributes" => address_params,
"ship_address_attributes" => address_params,
}
# Starting two checkout threads. The controller code will determine if
# these two threads are synchronised correctly or run into a race condition.
#
# 1. If the controller synchronises correctly:
# The first thread locks required resources and then waits at the
# breakpoint. The second thread waits for the first one.
# 2. If the controller fails to prevent the race condition:
# Both threads load required resources and wait at the breakpoint to do
# the same checkout action. This will lead to (random) errors.
#
# I observed:
# ActiveRecord::RecordNotUnique: duplicate key value violates unique
# constraint "index_spree_shipments_on_order_id"
# on `INSERT INTO "spree_shipments" ...`.
#
# Or:
# ActiveRecord::InvalidForeignKey: insert or update on table
# "spree_orders" violates foreign key constraint
# "spree_orders_customer_id_fk"
threads = [
Thread.new { spree_post :update, format: :json, order: order_params },
Thread.new { spree_post :update, format: :json, order: order_params },
]
# Let the threads run again. They should not be in a race condition.
breakpoint.unlock
# Wait for both threads to finish.
threads.each(&:join)
order.reload
# When the spec passes, both threads have the same result. The user should
# see the order page. This is basically verifying a "double click"
# scenario.
expect(response.status).to eq(200)
expect(response.body).to eq({ path: spree.order_path(order) }.to_json)
expect(order.payments.count).to eq 1
expect(order.completed?).to be true
end
end

View File

@@ -130,6 +130,7 @@ describe CheckoutController, type: :controller do
context 'when completing the order' do
before do
order.state = 'complete'
order.save!
allow(order).to receive(:update_attributes).and_return(true)
allow(order).to receive(:next).and_return(true)
allow(order).to receive(:set_distributor!).and_return(true)

View File

@@ -415,14 +415,17 @@ feature '
expect(page).to have_selector "a.clone-product", count: 1
find("a.clone-product").click
expect(page).to have_field "product_name", with: "COPY OF #{p.name}"
fill_in "product_name", with: "new product name"
within "#p_#{p.id}" do
fill_in "product_name", with: "new product name"
end
within "#save-bar" do
click_button 'Save Changes'
end
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
expect(p.name).to eq "new product name"
end

View File

@@ -62,7 +62,7 @@ module OpenFoodNetwork
o2.line_items << li2
end
it "shows line items supplied by my producers, with names hidden" do
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end

View File

@@ -4,11 +4,10 @@ RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::CustomerTotalsRepor
let!(:distributor) { create(:distributor_enterprise) }
let!(:customer) { create(:customer, enterprise: distributor) }
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(current_user, report_options, true)
end
let(:report_table) do

View File

@@ -8,11 +8,10 @@ RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::DistributorTotalsBy
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(current_user, report_options, true)
end
let(:report_table) do

View File

@@ -8,11 +8,10 @@ RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::SupplierTotalsByDis
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(current_user, report_options, true)
end
let(:report_table) do

View File

@@ -8,11 +8,10 @@ RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::SupplierTotalsRepor
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(current_user, report_options, true)
end
let(:report_table) do

View File

@@ -23,7 +23,7 @@ describe OpenFoodNetwork::OrdersAndFulfillmentsReport do
before { order.line_items << line_item }
context "as a site admin" do
subject { described_class.new OpenFoodNetwork::Permissions.new(admin_user), {}, true }
subject { described_class.new(admin_user, {}, true) }
it "fetches completed orders" do
o2 = create(:order)
@@ -39,7 +39,7 @@ describe OpenFoodNetwork::OrdersAndFulfillmentsReport do
end
context "as a manager of a supplier" do
subject { described_class.new OpenFoodNetwork::Permissions.new(user), {}, true }
subject { described_class.new(user, {}, true) }
let(:s1) { create(:supplier_enterprise) }
@@ -91,14 +91,14 @@ describe OpenFoodNetwork::OrdersAndFulfillmentsReport do
o2.line_items << li2
end
it "shows line items supplied by my producers, with names hidden" do
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end
end
context "as a manager of a distributor" do
subject { described_class.new OpenFoodNetwork::Permissions.new(user), {}, true }
subject { described_class.new(user, {}, true) }
before do
distributor.enterprise_roles.create!(user: user)
@@ -133,8 +133,7 @@ describe OpenFoodNetwork::OrdersAndFulfillmentsReport do
]
report_types.each do |report_type|
report = described_class.new OpenFoodNetwork::Permissions.new(admin_user),
report_type: report_type
report = described_class.new(admin_user, report_type: report_type)
expect(report.header.size).to eq(report.columns.size)
end
end
@@ -150,9 +149,7 @@ describe OpenFoodNetwork::OrdersAndFulfillmentsReport do
end
let(:items) {
report = described_class.new(OpenFoodNetwork::Permissions.new(admin_user),
{ report_type: "order_cycle_customer_totals" },
true)
report = described_class.new(admin_user, { report_type: "order_cycle_customer_totals" }, true)
OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items)
}

View File

@@ -62,7 +62,7 @@ module OpenFoodNetwork
o2.line_items << li2
end
it "shows line items supplied by my producers, with names hidden" do
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end

View File

@@ -269,137 +269,6 @@ module OpenFoodNetwork
end
end
describe "finding orders that are visible in reports" do
let(:distributor) { create(:distributor_enterprise) }
let(:coordinator) { create(:distributor_enterprise) }
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) }
let!(:line_item) { create(:line_item, order: order) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
context "as the hub through which the order was placed" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: distributor) }
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "as the coordinator of the order cycle through which the order was placed" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: coordinator) }
allow(permissions).to receive(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) }
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "as a producer which has granted P-OC to the distributor of an order" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: producer) }
create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle])
end
context "which contains my products" do
before do
line_item.product.supplier = producer
line_item.product.save
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "which does not contain my products" do
it "should not let me see the order" do
expect(permissions.visible_orders).to_not include order
end
end
end
context "as an enterprise that is a distributor in the order cycle, but not the distributor of the order" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: random_enterprise) }
end
it "should not let me see the order" do
expect(permissions.visible_orders).to_not include order
end
end
end
describe "finding line items that are visible in reports" do
let(:distributor) { create(:distributor_enterprise) }
let(:coordinator) { create(:distributor_enterprise) }
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) }
let!(:line_item1) { create(:line_item, order: order) }
let!(:line_item2) { create(:line_item, order: order) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
context "as the hub through which the parent order was placed" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: distributor) }
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the coordinator of the order cycle through which the parent order was placed" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: coordinator) }
allow(permissions).to receive(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) }
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the manager producer which has granted P-OC to the distributor of the parent order" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: producer) }
create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle])
line_item1.product.supplier = producer
line_item1.product.save
end
it "should let me see the line_items pertaining to variants I produce" do
ps = permissions.visible_line_items
expect(ps).to include line_item1
expect(ps).to_not include line_item2
end
end
context "as an enterprise that is a distributor in the order cycle, but not the distributor of the parent order" do
before do
allow(permissions).to receive(:managed_enterprises) { Enterprise.where(id: random_enterprise) }
end
it "should not let me see the line_items" do
expect(permissions.visible_line_items).to_not include line_item1, line_item2
end
end
end
describe "finding visible subscriptions" do
let!(:so1) { create(:subscription) }
let!(:so2) { create(:subscription) }

View File

@@ -0,0 +1,142 @@
require 'spec_helper'
module Permissions
describe Order do
let(:user) { double(:user) }
let(:permissions) { Permissions::Order.new(user) }
let!(:basic_permissions) { OpenFoodNetwork::Permissions.new(user) }
before { allow(OpenFoodNetwork::Permissions).to receive(:new) { basic_permissions } }
describe "finding orders that are visible in reports" do
let(:distributor) { create(:distributor_enterprise) }
let(:coordinator) { create(:distributor_enterprise) }
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) }
let!(:line_item) { create(:line_item, order: order) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
context "as the hub through which the order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: distributor) }
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "as the coordinator of the order cycle through which the order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: coordinator) }
allow(basic_permissions).to receive(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) }
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "as a producer which has granted P-OC to the distributor of an order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: producer) }
create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle])
end
context "which contains my products" do
before do
line_item.product.supplier = producer
line_item.product.save
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "which does not contain my products" do
it "should not let me see the order" do
expect(permissions.visible_orders).to_not include order
end
end
end
context "as an enterprise that is a distributor in the order cycle, but not the distributor of the order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: random_enterprise) }
end
it "should not let me see the order" do
expect(permissions.visible_orders).to_not include order
end
end
end
describe "finding line items that are visible in reports" do
let(:distributor) { create(:distributor_enterprise) }
let(:coordinator) { create(:distributor_enterprise) }
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) }
let!(:line_item1) { create(:line_item, order: order) }
let!(:line_item2) { create(:line_item, order: order) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
context "as the hub through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: distributor) }
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the coordinator of the order cycle through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: coordinator) }
allow(basic_permissions).to receive(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) }
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the manager producer which has granted P-OC to the distributor of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: producer) }
create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle])
line_item1.product.supplier = producer
line_item1.product.save
end
it "should let me see the line_items pertaining to variants I produce" do
ps = permissions.visible_line_items
expect(ps).to include line_item1
expect(ps).to_not include line_item2
end
end
context "as an enterprise that is a distributor in the order cycle, but not the distributor of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) { Enterprise.where(id: random_enterprise) }
end
it "should not let me see the line_items" do
expect(permissions.visible_line_items).to_not include line_item1, line_item2
end
end
end
end
end

View File

@@ -100,6 +100,7 @@ RSpec.configure do |config|
config.before(:suite) { DatabaseCleaner.clean_with :deletion, except: ['spree_countries', 'spree_states'] }
config.before(:each) { DatabaseCleaner.strategy = :transaction }
config.before(:each, js: true) { DatabaseCleaner.strategy = :deletion, { except: ['spree_countries', 'spree_states'] } }
config.before(:each, concurrency: true) { DatabaseCleaner.strategy = :deletion, { except: ['spree_countries', 'spree_states'] } }
config.before(:each) { DatabaseCleaner.start }
config.after(:each) { DatabaseCleaner.clean }
config.after(:each, js: true) do