Compare commits

...

24 Commits

Author SHA1 Message Date
Maikel Linke
4a82a26830 Update all locales with the latest Transifex translations 2019-11-28 15:17:38 +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
29 changed files with 1077 additions and 64 deletions

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

@@ -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

@@ -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

@@ -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,24 @@ en_NZ:
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"
@@ -863,6 +878,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 +2861,21 @@ en_NZ:
form:
name: "Name"
presentation: "Presentation"
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

@@ -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

@@ -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

@@ -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