diff --git a/.gitignore b/.gitignore index 21ff15e1472..88a12db1be4 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ public/images public/spree config/abr.yml config/initializers/feature_toggle.rb -config/initializers/db2fog.rb NERD_tree* coverage libpeerconnection.log diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 65369bb8f42..29af82c21cc 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -301,7 +301,6 @@ Metrics/LineLength: - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb - spec/lib/open_food_network/order_cycle_permissions_spec.rb - spec/lib/open_food_network/order_grouper_spec.rb - - spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb - spec/lib/open_food_network/packing_report_spec.rb - spec/lib/open_food_network/permissions_spec.rb - spec/lib/open_food_network/products_and_inventory_report_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f8fc80cde19..eeeda4a552c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,6 @@ Layout/AlignArray: Exclude: - 'lib/open_food_network/bulk_coop_report.rb' - 'lib/open_food_network/customers_report.rb' - - 'lib/open_food_network/order_and_distributor_report.rb' - 'lib/open_food_network/orders_and_fulfillments_report.rb' - 'lib/open_food_network/packing_report.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' @@ -703,7 +702,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/helpers/spree/orders_helper_spec.rb' - 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - - 'spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb' - 'spec/lib/open_food_network/products_renderer_spec.rb' - 'spec/lib/open_food_network/tag_rule_applicator_spec.rb' - 'spec/models/column_preference_spec.rb' @@ -1033,7 +1031,6 @@ Lint/Void: - 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb' - 'spec/lib/open_food_network/enterprise_issue_validator_spec.rb' - 'spec/lib/open_food_network/group_buy_report_spec.rb' - - 'spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb' - 'spec/lib/open_food_network/packing_report_spec.rb' - 'spec/lib/open_food_network/reports/report_spec.rb' - 'spec/lib/open_food_network/reports/rule_spec.rb' @@ -1923,7 +1920,6 @@ Style/MixinUsage: - 'spec/features/admin/orders_spec.rb' - 'spec/lib/open_food_network/bulk_coop_report_spec.rb' - 'spec/lib/open_food_network/order_cycle_management_report_spec.rb' - - 'spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb' - 'spec/lib/open_food_network/packing_report_spec.rb' # Offense count: 4 diff --git a/Gemfile b/Gemfile index 58d4a868278..fe95d3e65ee 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '2-0-stable' # - Pass customer email and phone number to PayPal (merged to upstream master) # - Change type of password from string to password to hide it in the form gem 'spree_paypal_express', github: "openfoodfoundation/better_spree_paypal_express", branch: "2-0-stable" -gem 'stripe', '~> 4.9.0' +gem 'stripe' # We need at least this version to have Digicert's root certificate # which is needed for Pin Payments (and possibly others). diff --git a/Gemfile.lock b/Gemfile.lock index 77c8eca7cee..0d86913a2c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -553,7 +553,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - oj (3.7.10) + oj (3.7.11) orm_adapter (0.5.0) paper_trail (5.2.3) activerecord (>= 3.0, < 6.0) @@ -721,7 +721,7 @@ GEM tilt (~> 1.1, != 1.3.0) state_machine (1.2.0) stringex (1.5.1) - stripe (4.9.0) + stripe (4.11.0) faraday (~> 0.13) net-http-persistent (~> 3.0) therubyracer (0.12.0) @@ -860,7 +860,7 @@ DEPENDENCIES spree_paypal_express! spring (= 1.7.2) spring-commands-rspec - stripe (~> 4.9.0) + stripe therubyracer (= 0.12.0) timecop truncate_html diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb index c422ae8b44c..bb277a387ad 100644 --- a/app/controllers/spree/admin/variants_controller_decorator.rb +++ b/app/controllers/spree/admin/variants_controller_decorator.rb @@ -30,7 +30,7 @@ def destroy respond_with(@variant) do |format| format.html { redirect_to admin_product_variants_url(params[:product_id]) } - format.js { render_js_for_destroy } + format.js { render_js_for_destroy } end end diff --git a/app/models/concerns/address_display.rb b/app/models/concerns/address_display.rb new file mode 100644 index 00000000000..c585087e18d --- /dev/null +++ b/app/models/concerns/address_display.rb @@ -0,0 +1,5 @@ +module AddressDisplay + def full_name_reverse + [lastname, firstname].reject(&:blank?).join(" ") + end +end diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 39e3d761f92..50d8879d772 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -258,6 +258,7 @@ def mark_as_new_product(entry) new_product = Spree::Product.new new_product.assign_attributes(entry.attributes.except('id')) new_product.supplier_id = entry.producer_id + entry.on_hand = 0 if entry.on_hand.nil? if new_product.valid? entry.validates_as = 'new_product' unless entry.errors? diff --git a/app/models/spree/address_decorator.rb b/app/models/spree/address_decorator.rb index ff7c1f06659..603423e8d65 100644 --- a/app/models/spree/address_decorator.rb +++ b/app/models/spree/address_decorator.rb @@ -1,4 +1,6 @@ Spree::Address.class_eval do + include AddressDisplay + has_one :enterprise, dependent: :restrict belongs_to :country, class_name: "Spree::Country" diff --git a/app/views/admin/enterprises/welcome.html.haml b/app/views/admin/enterprises/welcome.html.haml index 8f70211e88d..b386191640c 100644 --- a/app/views/admin/enterprises/welcome.html.haml +++ b/app/views/admin/enterprises/welcome.html.haml @@ -4,7 +4,8 @@ %p = t('.welcome_text') %strong - = "#{"producer " if @enterprise.is_primary_producer}profile" + - profile_translation_key = @enterprise.is_primary_producer ? '.producer_profile' : '.profile' + = t(profile_translation_key) %section %h2= t('.next_step') diff --git a/config/initializers/0_depricated_db2fog.rb b/config/initializers/0_depricated_db2fog.rb deleted file mode 100644 index f2a2e76bc9d..00000000000 --- a/config/initializers/0_depricated_db2fog.rb +++ /dev/null @@ -1,15 +0,0 @@ -# Depricated: this initializer contains an invalid bucket name. -# Users of DB2fog should be able to configure DB2fog without changing the code. -# -# Name your configuration file `db2fog.rb`. It will be ignored by git. -# And it will overwrite this depricated configuration. -# -# See: https://github.com/yob/db2fog -# -# TODO: Remove this file in a future release. -DB2Fog.config = { - :aws_access_key_id => Spree::Config[:s3_access_key], - :aws_secret_access_key => Spree::Config[:s3_secret], - :directory => "db-backup_#{Spree::Config[:s3_bucket]}", - :provider => 'AWS' -} diff --git a/config/initializers/db2fog.rb b/config/initializers/db2fog.rb new file mode 100644 index 00000000000..7c7a2d6d396 --- /dev/null +++ b/config/initializers/db2fog.rb @@ -0,0 +1,7 @@ +# See: https://github.com/yob/db2fog +DB2Fog.config = { + :aws_access_key_id => Spree::Config[:s3_access_key], + :aws_secret_access_key => Spree::Config[:s3_secret], + :directory => Spree::Config[:s3_bucket], + :provider => 'AWS' +} diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index f937db86951..6f25d908eeb 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -2558,63 +2558,216 @@ de_DE: reports: enterprise_fee_summary: date_end_before_start_error: "muss nach dem start sein" + parameter_not_allowed_error: "Sie sind nicht berechtigt, einen oder mehrere ausgewählte Filter für diesen Bericht zu verwenden." fee_calculated_on_transfer_through_all: "Alle" + fee_calculated_on_transfer_through_entire_orders: "Gesamte Bestellungen über %{distributor}" + tax_category_various: "Verschiedene" + fee_type: + payment_method: "Zahlungsvorgang" + shipping_method: "Sendung" fee_placements: supplier: "Eingehend" distributor: "Ausgehend" coordinator: "Koordinator" + tax_category_name: + shipping_instance_rate: "Plattformpreis" formats: csv: header: fee_type: "Art der Gebühr" + enterprise_name: "Unternehmensinhaber" + fee_name: "Name der Gebühr" customer_name: "Kunde" + fee_placement: "Gebührenplatzierung" + fee_calculated_on_transfer_through_name: "Gebührenberechnung bei Übergabe" tax_category_name: "Steuerkategorie" + total_amount: "$$ SUMME" html: header: fee_type: "Art der Gebühr" + enterprise_name: "Unternehmensinhaber" + fee_name: "Name der Gebühr" customer_name: "Kunde" + fee_placement: "Gebührenplatzierung" + fee_calculated_on_transfer_through_name: "Gebührenberechnung bei Übergabe" tax_category_name: "Steuerkategorie" + total_amount: "$$ SUMME" + invalid_filter_parameters: "Die für diesen Bericht ausgewählten Filter sind ungültig." order: "Bestellung" distribution: "Verteilung" + order_details: "Bestelldetails" + customer_details: "Kundendetails" + adjustments: "Anpassungen" + payments: "Zahlungen" payment: "Zahlung" payment_method: "Zahlungsart" + shipment: "Sendung" + shipment_inc_vat: "Versand inklusive Mehrwertsteuer" + shipping_tax_rate: "Versandsteuersatz" category: "Kategorie" delivery: "Lieferung" + temperature_controlled: "Temperaturgesteuert" + new_product: "Neues Produkt" administration: "Verwaltung" + logged_in_as: "Eingeloggt als" account: "Konto" logout: "Ausloggen" + date_range: "Datumsbereich" + status: "Status" + new: "Neu" start: "Start" + stop: "Halt" + first: "Zuerst" + previous: "Bisherige" + last: "Zuletzt" spree: + your_order_is_empty_add_product: "Ihre Bestellung ist leer. Suchen Sie oben ein Produkt und fügen Sie es hinzu" + add_product: "Produkt hinzufügen" + name_or_sku: "Name oder SKU (geben Sie mindestens die ersten 4 Zeichen des Produktnamens ein)" resend: Erneut senden + back_to_orders_list: Zurück zur Bestellliste + select_stock: "Bestand auswählen" + location: "Ort" + count_on_hand: "Zählen Sie zur Hand" quantity: "Menge" + package_from: "Paket von" + item_description: "Artikelbeschreibung" price: "Preis" total: "Gesamt" edit: "Bearbeiten" + split: "Teilt" + delete: "Löschen" + cannot_set_shipping_method_without_address: "Versandart kann nicht festgelegt werden, bis Kundendaten angegeben werden." + no_tracking_present: "Keine Tracking-Details angegeben." + order_total: "Auftrag insgesamt" + customer_details: "Kundendetails" + customer_search: "Kundensuche" + choose_a_customer: "Wählen Sie einen Kunden aus" account: "Konto" billing_address: "Rechnungsadresse" shipping_address: "Lieferanschrift" + first_name: "Vorname" + last_name: "Nachname" + street_address: "Adresse" + street_address_2: "Straße (Fortsetzung)" city: "Ort" + zip: "Postleitzahl" country: "Land" state: "Status" phone: "Telefon" update: "Aktualisieren" + use_billing_address: "Rechnungsadresse verwenden" + adjustments: "Anpassungen" + continue: "Fortsetzen" + fill_in_customer_info: "Bitte geben Sie Ihre Kundeninformationen ein" + new_payment: "Neue Zahlung" + configurations: "Konfigurationen" + general_settings: "Allgemeine Einstellungen" + site_name: "Site-Name" + site_url: "Seiten-URL" + default_seo_title: "Standard-Seo-Titel" + default_meta_description: "Standard-Meta-Beschreibung" + default_meta_keywords: "Standard-Metaschlüsselwörter" + security_settings: "Sicherheitseinstellungen" + allow_ssl_in_development_and_test: "Erlauben Sie SSL in Entwicklungs- und Testmodi" + allow_ssl_in_production: "Zulassen, dass SSL im Produktionsmodus verwendet wird" + allow_ssl_in_staging: "Zulassen, dass SSL im Staging-Modus verwendet wird" + check_for_spree_alerts: "Suchen Sie nach Spree-Benachrichtigungen" + currency_decimal_mark: "Dezimalzeichen der Währung" + currency_settings: "Währungseinstellungen" + currency_symbol_position: Setzen Sie "Währungssymbol vor oder nach dem Dollarbetrag?" + currency_thousands_separator: "Tausendertrennzeichen der Währung" + hide_cents: "Cent ausblenden" + display_currency: "Währung anzeigen" + choose_currency: "Währung wählen" + mail_method_settings: "E-Mail-Methodeneinstellungen" + general: "Allgemeines" + enable_mail_delivery: "Aktivieren Sie die E-Mail-Zustellung" + send_mails_as: "Mails senden als" + smtp_send_all_emails_as_from_following_address: "Senden Sie alle E-Mails von der folgenden Adresse aus." + send_copy_of_all_mails_to: "Kopie aller Mails an senden" + smtp_send_copy_to_this_addresses: "Sendet eine Kopie aller ausgehenden Mails an diese Adresse. Bei mehreren Adressen trennen Sie diese durch Kommas." + intercept_email_address: "E-Mail-Adresse abfangen" + intercept_email_instructions: "Überschreiben Sie den E-Mail-Empfänger und ersetzen Sie ihn mit dieser Adresse." + smtp: "SMTP" + smtp_domain: "SMTP-Domäne" + smtp_mail_host: "SMTP-Mail-Host" + smtp_port: "SMTP-Port" + secure_connection_type: "Sicherer Verbindungstyp" + smtp_authentication_type: "SMTP-Authentifizierungstyp" + smtp_username: "SMTP-Benutzername" + smtp_password: "SMTP-Passwort" + image_settings: "Bildeinstellungen" + image_settings_warning: "Sie müssen die Miniaturbilder neu erstellen, wenn Sie die Büroklammerformate aktualisieren. Verwenden Sie dazu die Rake-Büroklammer: Refresh: thumbnails CLASS = Spree :: Image." + attachment_default_style: Anhänge-Stil + attachment_default_url: "Anhänge Standard-URL" + attachment_path: "Pfad der Anhänge" + attachment_styles: "Büroklammer-Stile" + attachment_url: "Anhänge-URL" + add_new_style: "Neuen Stil hinzufügen" + image_settings_updated: "Bildeinstellungen erfolgreich aktualisiert." tax_categories: "Steuerkategorien" + listing_tax_categories: "Steuerkategorien auflisten" + back_to_tax_categories_list: "Zurück zur Liste der Steuerkategorien" tax rate: "Steuersätze" + new_tax_rate: "Neuer Steuersatz" tax_category: "Steuerkategorie" + rate: "Bewertung" + tax_rate_amount_explanation: "Steuersätze sind ein Dezimalbetrag für Berechnungshilfen (d. H. Wenn der Steuersatz 5% beträgt, geben Sie 0,05 ein)." + included_in_price: "Im Preis inbegriffen" + show_rate_in_label: "Rate im Etikett anzeigen" + back_to_tax_rates_list: "Zurück zur Liste der Steuersätze" + tax_settings: "Steuereinstellungen" + zones: "Zonen" + new_zone: "Neue Zone" + default_tax: "Standardsteuer" + default_tax_zone: "Standardsteuerzone" + country_based: "Land basiert" + state_based: "State Based" + countries: "Länder" + listing_countries: "Länder auflisten" + iso_name: "ISO-Name" + states_required: "Staaten erforderlich" + editing_country: "Land bearbeiten" + back_to_countries_list: "Zurück zur Länderliste" + states: "Zustände" + abbreviation: "Abkürzung" + new_state: "Neuer Staat" payment_methods: "Zahlungsarten" + new_payment_method: "Neue Zahlungsart" + provider: "Anbieter" + taxonomies: "Taxonomien" + new_taxonomy: "Neue Taxonomie" + back_to_taxonomies_list: "Zurück zur Taxonomieliste" shipping_methods: "Lieferart" shipping_categories: "Versandkategorien" + new_shipping_category: "Neue Versandkategorie" + back_to_shipping_categories: "Zurück zu den Versandkategorien" + analytics_trackers: "Analytics-Tracker" + no_trackers_found: "Keine Tracker gefunden" + new_tracker: "Neuer Tracker" + add_one: "Füge eins hinzu" + google_analytics_id: "Analytics-ID" + back_to_trackers_list: "Zurück zur Trackers-Liste" name: "Name" description: "Beschreibung" type: "Art" + default: "Standard" calculator: "Rechner" + zone: "Zone" + display: "Anzeige" + environment: "Umgebung" + active: "Aktiv" nore: "Mehr" + no_results: "Keine Ergebnisse" create: "Neu" + loading: "Wird geladen" email: Email account_updated: "Konto aktualisiert!" my_account: "Mein Konto" date: "Datum" time: "Zeit" + inventory_error_flash_for_insufficient_quantity: "Ein Artikel in Ihrem Warenkorb ist nicht mehr verfügbar." inventory: Katalog zipcode: Postleitzahl weight: Gewicht (pro kg) @@ -2634,12 +2787,17 @@ de_DE: bulk_order_management: "Massenbearbeitung von Bestellungen" subscriptions: "Abonnements" products: "Produkte" + option_types: "Optionstypen" properties: "Eigenschaften" + prototypes: "Prototypen" variant_overrides: "Katalog" reports: "Berichte" + configuration: "Aufbau" + users: "Benutzer" roles: "Rollen" order_cycles: "Bestellrunden" enterprises: "Unternehmen" + enterprise_relationships: "Berechtigungen" customers: "Kunden" groups: "Gruppen" product_properties: @@ -2681,6 +2839,11 @@ de_DE: distributor: "Verteiler:" order_cycle: "Bestellzyklus:" overview: + products: + active_products: + zero: "Sie haben keine aktiven Produkte." + one: "Sie haben ein aktives Produkt" + other: "Sie haben %{count} aktive Produkte" order_cycles: order_cycles: "Bestellrunden" order_cycles_tip: "Bestellzyklen bestimmen, wann und wo Ihre Produkte für Kunden verfügbar sind." @@ -2755,8 +2918,13 @@ de_DE: bulk_coop_packing_sheets: 'Massenkoop - Verpackungsblätter' bulk_coop_customer_payments: 'Massenkoop - Kundenzahlungen' enterprise_fee_summaries: + filters: + date_range: "Datumsbereich" + report_format_csv: "Als CSV herunterladen" + generate_report: "Bericht generieren" report: none: "Keine" + select_and_search: "Wählen Sie Filter aus und klicken Sie auf GENERATE REPORT, um auf Ihre Daten zuzugreifen." users: index: listing_users: "Benutzer auflisten" @@ -2765,10 +2933,15 @@ de_DE: enterprise_limit: "Enterprise Limit" search: "Suche" email: "E-Mail" + edit: + editing_user: "Benutzer bearbeiten" + back_to_users_list: "Zurück zur Benutzerliste" + general_settings: "Allgemeine Einstellungen" form: email: "E-Mail" roles: "Rollen" enterprise_limit: "Enterprise Limit" + confirm_password: "Passwort bestätigen" password: "Passwort" email_confirmation: confirmation_pending: "E-Mail-Bestätigung steht aus. Wir haben eine Bestätigungs-E-Mail an %{address} gesendet." @@ -2802,6 +2975,7 @@ de_DE: item: "Bereits in dieser Reihenfolge bestellt" line_item: insufficient_stock: "Nicht genügend Lagerbestand verfügbar, nur noch %{on_hand} verfügbar" + out_of_stock: "Ausverkauft" shipment_states: backorder: Lieferrückstand partial: teilweise diff --git a/config/locales/en.yml b/config/locales/en.yml index 37e4eac9493..6d54e04df91 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -855,6 +855,8 @@ en: welcome_text: You have successfully created a next_step: Next step choose_starting_point: 'Choose your package:' + profile: 'Profile' + producer_profile: 'Producer Profile' invite_manager: user_already_exists: "User already exists" error: "Something went wrong" @@ -2531,6 +2533,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using pending: "pending" ready: "ready" shipped: "shipped" + canceled: "cancelled" payment_states: balance_due: "balance due" completed: "completed" @@ -2932,6 +2935,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using title: "Distribution" distributor: "Distributor:" order_cycle: "Order cycle:" + line_item_adjustments: "Line Item Adjustments" + order_adjustments: "Order Adjustments" + order_total: "Order Total" overview: products: active_products: diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index e22f2020a67..a80f3c254e6 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -242,6 +242,8 @@ en_CA: quantity: Quantity pick_up: Pick up copy: Copy + change_my_password: "Change my password" + update_password: "Update passord" password_confirmation: Password Confirmation reset_password_token: Reset password token expired: has expired, please request a new one @@ -371,6 +373,7 @@ en_CA: status: "Status" diff: "Diff" error: "Error" + enable_products_cache: "Enable Products Cache?" invoice_settings: edit: title: Invoice Settings @@ -1234,6 +1237,7 @@ en_CA: no_shipping_or_payment: no shipping or payment methods unconfirmed: unconfirmed days: days + authorization_failure: "Authorization Failure" label_shop: "Shop" label_shops: "Shops" label_map: "Map" @@ -1609,6 +1613,7 @@ en_CA: products_available: Available? products_producer: "Producer" products_price: "Price" + name_or_sku: "NAME OR SKU" register_title: Register sell_title: "Register" sell_headline: "Get on the Open Food Network!" @@ -1666,6 +1671,7 @@ en_CA: orders_bought_edit_button: Edit confirmed items orders_bought_already_confirmed: "* already confirmed" orders_confirm_cancel: Are you sure you want to cancel this order? + order_processed_successfully: "Your order has been processed successfully" products_cart_distributor_choice: "Distributor for your order:" products_cart_distributor_change: "Your distributor for this order will be changed to %{name} if you add this product to your cart." products_cart_distributor_is: "Your distributor for this order is %{name}." @@ -1746,11 +1752,13 @@ en_CA: registration_about_us: "'About Us' text" registration_outcome_headline: "What do I get?" registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." + registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. " registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." registration_action: "Let's get started!" details: title: "Details" headline: "Let's Get Started" + enterprise: "Great! First we need to know a little bit about your enterprise:" producer: "Woot! First we need to know a little bit about your farm:" enterprise_name_field: "Enterprise Name:" producer_name_field: "Farm Name:" @@ -1792,6 +1800,7 @@ en_CA: title: "About" headline: "Nice one!" message: "Now let's flesh out the details about" + success: "Success! %{enterprise} added to the Open Food Network " registration_exit_message: "If you exit this wizard at any stage, you can continue to create your profile by going to the admin interface." enterprise_description: "Short Description" enterprise_description_placeholder: "A short sentence describing your enterprise" @@ -1887,6 +1896,10 @@ en_CA: ok: OK not_visible: not visible you_have_no_orders_yet: "You have no orders yet" + show_only_complete_orders: "Only show completed orders" + successfully_created: '%{resource}has been successfully created!' + successfully_removed: '%{resource}has been successfully removed!' + successfully_updated: '%{resource}has been successfully updated!' running_balance: "Running balance" outstanding_balance: "Outstanding balance" admin_enterprise_relationships: "Enterprise Permissions" @@ -2222,6 +2235,8 @@ en_CA: shipping_methods: "Shipping Methods" payment_methods: "Payment Methods" payment_method_fee: "Transaction fee" + payment_processing_failed: "Payment could not be processed, please check the details you entered." + payment_updated: "Payment Updated" inventory_settings: "Inventory Settings" tag_rules: "Tag Rules" shop_preferences: "Shop Preferences" @@ -2458,6 +2473,7 @@ en_CA: sending: "Resend..." done: "Resend done ✓" failed: "Resend failed ✗" + insufficient_stock: "Insufficient stock available, only %{on_hand} remaining" out_of_stock: reduced_stock_available: Reduced stock available out_of_stock_text: > @@ -2574,50 +2590,171 @@ en_CA: invalid_filter_parameters: "The filters you selected for this report are invalid." order: "Order" distribution: "Distribution" + order_details: "Order Details" + customer_details: "Customer Details" + adjustments: "Adjustments" + payments: "Payments" payment: "Payment" payment_method: "Payment Method" shipment: "Shipment" + shipment_inc_vat: "Shipment including taxes" + shipping_tax_rate: "Shipping Tax Rate" category: "Category" delivery: "Delivery" + temperature_controlled: "Temperature Controlled" new_product: "New Product" administration: "Administration" + logged_in_as: "Logged in as" account: "Account" logout: "Logout" date_range: "Date Range" + status: "status" + new: "New" start: "Start" + stop: "Stop" first: "First" previous: "Previous" last: "Last" spree: + your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" + add_product: "Add Product" + 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 + select_stock: "Select stock" + location: "Location" + count_on_hand: "Count On Hand" quantity: "Quantity" + package_from: "package from" + item_description: "Item Description" price: "Price" total: "Total" edit: "Edit" + split: "Split" delete: "Delete" + cannot_set_shipping_method_without_address: "Cannot set shipping method until customer details are provided." + no_tracking_present: "No tracking details provided." + order_total: "Order Total" + customer_details: "Customer Details" + customer_search: "Customer Search" + choose_a_customer: "Choose a customer" account: "Account" billing_address: "Billing Address" shipping_address: "Shipping Address" + first_name: "First name" + last_name: "Last name" + street_address: "Street Address" + street_address_2: "Street Address (cont'd)" city: "City" + zip: "Postal Code" country: "Country" state: "Province" phone: "Phone" update: "Update" + use_billing_address: "Use Billing Address" + adjustments: "Adjustments" continue: "Continue" + fill_in_customer_info: "Please fill in customer info" + new_payment: "New Payment" + configurations: "Configurations" general_settings: "General Settings" + site_name: "Site Name" + site_url: "Site URL" + default_seo_title: "Default Seo Title" + default_meta_description: "Default Meta Description" + default_meta_keywords: "Default Meta Keywords" + security_settings: "Security Settings" + allow_ssl_in_development_and_test: "Allow SSL to be used when in development and test modes" + allow_ssl_in_production: "Allow SSL to be used in production mode" + allow_ssl_in_staging: "Allow SSL to be used in staging mode" + check_for_spree_alerts: "Check for Spree alerts" + currency_decimal_mark: "Currency decimal mark" + currency_settings: "Currency Settings" + currency_symbol_position: Put "currency symbol before or after dollar amount?" + currency_thousands_separator: "Currency thousands separator" + hide_cents: "Hide cents" + display_currency: "Display currency" + choose_currency: "Choose Currency" + mail_method_settings: "Mail Method Settings" + general: "General" + enable_mail_delivery: "Enable Mail Delivery" + send_mails_as: "Send Mails As" + smtp_send_all_emails_as_from_following_address: "Send all mails from the following address" + send_copy_of_all_mails_to: "Send copy of all mails to" + smtp_send_copy_to_this_addresses: "Sends a copy of all outgoing mails to this address. For multiple addresses, separate with commas." + intercept_email_address: "Intercept Email Address" + intercept_email_instructions: "Override email recipient and replace with this address." + smtp: "SMTP" + smtp_domain: "SMTP Domain" + smtp_mail_host: "SMTP Mail Host" + smtp_port: "SMTP Port" + secure_connection_type: "Secure Connection Type" + smtp_authentication_type: "SMTP Authentication Type" + smtp_username: "SMTP Username" + smtp_password: "SMTP Password" + image_settings: "Image Settings" + image_settings_warning: "You will need to regenerate thumbnails if you update the paperclip styles. Use rake paperclip:refresh:thumbnails CLASS=Spree::Image to do this." + attachment_default_style: Attachments Style + attachment_default_url: "Attachments Default URL" + attachment_path: "Attachments Path" + attachment_styles: "Paperclip Styles" + attachment_url: "Attachments URL" + add_new_style: "Add New Style" + image_settings_updated: "Image settings successfully updated" tax_categories: "Tax Categories" + listing_tax_categories: "Listing Tax Categories" + back_to_tax_categories_list: "Back to Tax Categories List" tax rate: "Tax Rates" + new_tax_rate: "New Tax Rate" tax_category: "Tax Category" + rate: "Rate" + tax_rate_amount_explanation: "Tax rates are a decimal amount to aid in calcuations, (i.e. if the tax rate is 5% then enter 0.05)" + included_in_price: "Included in Price" + show_rate_in_label: "Show rate in label" + back_to_tax_rates_list: "Back to Tax Rates List" tax_settings: "Tax Settings" + zones: "Zones" + new_zone: "New Zone" + default_tax: "Default Tax" + default_tax_zone: "Default Tax Zone" + country_based: "Country Based" + state_based: "State/Provinces Based" + countries: "Countries" + listing_countries: "Listing Countries" + iso_name: "ISO Name" + states_required: "Provinces Required" + editing_country: "Editing Country" + back_to_countries_list: "Back to Countries List" + states: "Provinces" + abbreviation: "Abbreviation" + new_state: "New Province" payment_methods: "Payment Methods" new_payment_method: "New Payment Method" + provider: "Provider" + taxonomies: "Taxonomies" + new_taxonomy: "New Taxonomy" + back_to_taxonomies_list: "Back to Taxonomies List" shipping_methods: "Delivery/Pick-up Methods" shipping_categories: "Shipping Categories" + new_shipping_category: "New Shipping Category" + back_to_shipping_categories: "Back to Shipping Categories" + analytics_trackers: "Analytics Trackers" + no_trackers_found: "No Trackers Found" + new_tracker: "New Tracker" + add_one: "Add One" + google_analytics_id: "Analytics ID" + back_to_trackers_list: "Back to Trackers List" name: "Name" description: "Description" type: "Type" + default: "default" calculator: "Calculator" + zone: "Zone" + display: "Display" + environment: "Environment" + active: "Active" nore: "More" + no_results: "No results" create: "Create" loading: "Loading" email: Email @@ -2625,6 +2762,7 @@ en_CA: my_account: "My account" date: "Date" time: "Time" + inventory_error_flash_for_insufficient_quantity: "An item in your cart has become unavailable" inventory: Inventory zipcode: Postal Code weight: Weight (per kg) @@ -2644,13 +2782,17 @@ en_CA: bulk_order_management: "Bulk Order Management" subscriptions: "Subscriptions" products: "Products" + option_types: "Option Types" properties: "Properties" + prototypes: "Prototypes" variant_overrides: "Inventory" reports: "Reports" + configuration: "Configuration" users: "Users" roles: "Roles" order_cycles: "Order Cycles" enterprises: "Enterprises" + enterprise_relationships: "Permissions" customers: "Customers" groups: "Groups" product_properties: @@ -2826,6 +2968,9 @@ en_CA: login_to_view_order: "Please log in to view your order." bought: item: "Already ordered in this order cycle" + line_item: + insufficient_stock: "Insufficient stock available, only %{on_hand} remaining" + out_of_stock: "Out of Stock" shipment_states: backorder: backorder partial: partial diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index 79844d5fe17..7c5fdfc024f 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -2736,6 +2736,7 @@ en_US: back_to_taxonomies_list: "Back to Taxonomies List" shipping_methods: "Shipping Methods" shipping_categories: "Shipping Categories" + new_shipping_category: "New Shipping Category" back_to_shipping_categories: "Back To Shipping Categories" analytics_trackers: "Analytics Trackers" no_trackers_found: "No Trackers Found" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 69bcc7773db..b53e81a4f61 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2748,6 +2748,7 @@ fr: back_to_taxonomies_list: "Retour à la liste des taxonomies" shipping_methods: "Méthodes de livraison" shipping_categories: "Condition de transport" + new_shipping_category: "Nouvelle catégorie d'expédition" back_to_shipping_categories: "Retour aux catégories d'expédition" analytics_trackers: "Traqueurs analyse navigation" no_trackers_found: "Pas de traqueur trouvé" diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 86b6c6b7d7a..1d285296c27 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -243,6 +243,8 @@ fr_CA: quantity: Quantité pick_up: Retrait copy: Copier + change_my_password: "Changer mon mot de passe" + update_password: "Mettre à jour le mot de passe" password_confirmation: Confirmation de mot de passe reset_password_token: La demande de réinitialisation du mot de passe expired: a expiré, veuillez faire une nouvelle demande. @@ -372,6 +374,7 @@ fr_CA: status: "Statut" diff: "Diff" error: "Erreur" + enable_products_cache: "Autoriser Cache Produits ?" invoice_settings: edit: title: Paramètres de facturation @@ -1235,6 +1238,7 @@ fr_CA: no_shipping_or_payment: pas de méthode de livraison ou de paiement unconfirmed: non confirmé days: jours + authorization_failure: "Echec de l'autorisation" label_shop: "Boutique" label_shops: "Boutiques" label_map: "Carte" @@ -1610,6 +1614,7 @@ fr_CA: products_available: Disponible? products_producer: "Producteur" products_price: "Prix" + name_or_sku: "NOM OU REF PRODUIT" register_title: S'inscrire sell_title: "S'inscrire" sell_headline: "Aller sur Open Food France!" @@ -1667,6 +1672,7 @@ fr_CA: orders_bought_edit_button: Modifier les produits confirmés orders_bought_already_confirmed: "* déjà confirmé" orders_confirm_cancel: Voulez-vous vraiment annuler cette commande ? + order_processed_successfully: "Votre commande a été traitée avec succès" products_cart_distributor_choice: "Distributeur pour votre commande:" products_cart_distributor_change: "Vore distributeur pour cette commande sera dorénavant %{name} si vous ajoutez ce produit à votre panier." products_cart_distributor_is: "Votre distributeur pour cette commande est %{name}." @@ -1747,11 +1753,13 @@ fr_CA: registration_about_us: "Un texte \"A propos\"" registration_outcome_headline: "Qu'est-ce que ça m'apporte?" registration_outcome1_html: "Votre profil permet aux gens de vous trouver et de vous contacter via Open Food Network." + registration_outcome2: "Utilisez cet espace pour raconter l'histoire de votre entreprise, et stimuler les visites vers vos points de présence en ligne." registration_outcome3: "C'est aussi le premier pas vers la vente via Open Food Network, ou l'ouverture de votre boutique en ligne." registration_action: "Démarrons!" details: title: "Détails" headline: "Commençons !" + enterprise: "Hey ! Nous avons d'abord besoin de quelques informations sur votre entreprise :" producer: "Woohoo! Dites-nous déjà quelques mots à propos de votre ferme:" enterprise_name_field: "Nom de l'entreprise:" producer_name_field: "Nom de la ferme:" @@ -1793,6 +1801,7 @@ fr_CA: title: "A propos" headline: "Bien joué!" message: "A présent, allons un peu plus dans les détails concernant" + success: "Opération réussie ! %{enterprise}a été ajoutée à Open Food Network " registration_exit_message: "Si vous quittez ce module, vous pourrez continuer la création de votre profile via l'interface d'administration.\n\n " enterprise_description: "Description courte" enterprise_description_placeholder: "Une phrase pour décrire votre organisation" @@ -1888,6 +1897,10 @@ fr_CA: ok: OK not_visible: invisible you_have_no_orders_yet: "Vous n'avez pas encore de commande" + show_only_complete_orders: "Ne montrer que les commandes finalisées" + successfully_created: '%{resource} a été créé avec succès !' + successfully_removed: ' %{resource}a été supprimé avec succès !' + successfully_updated: ' %{resource}été mis à jour avec succès !' running_balance: "Solde courant" outstanding_balance: "Solde restant" admin_enterprise_relationships: "Permissions Inter-entreprises" @@ -2223,6 +2236,8 @@ fr_CA: shipping_methods: "Méthodes de livraison" payment_methods: "Méthodes de paiement" payment_method_fee: "Frais de transaction" + payment_processing_failed: "Le paiement n'a pas pu être traité, veuillez vérifier les informations saisies" + payment_updated: "Paiement mis à jour" inventory_settings: "Paramètres catalogue boutique" tag_rules: "Règles de tag" shop_preferences: "Préférences boutique" @@ -2469,6 +2484,7 @@ fr_CA: sending: "Renvoyer" done: "Renvoi effectué ✓" failed: "Renvoi échoué ✗" + insufficient_stock: "Stock disponible insuffisant, il n'en reste que %{on_hand}" out_of_stock: reduced_stock_available: Stock disponible out_of_stock_text: > @@ -2586,50 +2602,171 @@ fr_CA: invalid_filter_parameters: "Les filtres sélectionnés pour ce rapport sont invalides." order: "Commander" distribution: "Distribution" + order_details: "Détails de la commande" + customer_details: "Informations acheteur" + adjustments: "ajustements" + payments: "Paiements" payment: "Paiement" payment_method: "Méthode de paiement" shipment: "Livraison" + shipment_inc_vat: "Livraison incluant taxes" + shipping_tax_rate: "Taux taxe livraison" category: "Catégorie" delivery: "Livraison" + temperature_controlled: "Température contrôlée" new_product: "Nouveau Produit" administration: "Administration" + logged_in_as: "Connecté en tant que" account: "Compte" logout: "Déconnexion" date_range: "Période" + status: "Statut" + new: "Nouveau" start: "Début" + stop: "Arrêter" first: "Début" previous: "Précédent" last: "Fin" spree: + your_order_is_empty_add_product: "Votre commande est vide, veuillez ajouter des produits" + add_product: "Ajouter un produit" + name_or_sku: "Nom ou Ref Produit (entrer au moins les 4 premiers caractères du nom du produit)" resend: Renvoyer + back_to_orders_list: Retour à la liste des commandes + select_stock: "Sélectionner le stock" + location: "Localisation" + count_on_hand: "Quantité en stock" quantity: "Quantité" + package_from: "conditionnement par" + item_description: "Description de la pièce" price: "Prix" total: "Total" edit: "Modifier" + split: "Découper" delete: "Supprimer" + cannot_set_shipping_method_without_address: "Impossible de choisir une méthode de livraison tant que les informations acheteur ne sont pas fournies." + no_tracking_present: "Pas d'informations de traçabilité fournies." + order_total: "Total Commande:" + customer_details: "Informations acheteur" + customer_search: "Recherche Acheteur" + choose_a_customer: "Choisir un acheteur" account: "Compte" billing_address: "Adresse de facturation" shipping_address: "Adresse de livraison" + first_name: "Prénom" + last_name: "Nom de famille" + street_address: "Adresse - nom de la rue" + street_address_2: "Adresse - nom de la rue (cont.)" city: "Ville" + zip: "Code postal" country: "Pays" state: "Département" phone: "n° tel" update: "Mettre à jour" + use_billing_address: "Utiliser l'adresse de facturation" + adjustments: "ajustements" continue: "Suivant" + fill_in_customer_info: "Veuillez saisir les informations acheteur" + new_payment: "Nouveau paiement" + configurations: "Configurations" general_settings: "Configurations générales" + site_name: "Nom du site" + site_url: "URL du site" + default_seo_title: "Titre référencement web (SEO) par défaut" + default_meta_description: "Meta description par défaut" + default_meta_keywords: "Meta mots clés par défaut" + security_settings: "Paramètres de sécurité" + allow_ssl_in_development_and_test: "Autoriser l'utilisation SSL dans les environnements de développement et de test" + allow_ssl_in_production: "Autoriser l'utilisation SSL dans l'environnement de production" + allow_ssl_in_staging: "Autoriser l'utilisation SSL dans l'environnement de staging" + check_for_spree_alerts: "Vérifier les alertes Spree" + currency_decimal_mark: "Symbole du séparateur de décimales" + currency_settings: "Paramètres devise" + currency_symbol_position: Mettre le symbole de la devise avant ou après le montant ? + currency_thousands_separator: "Symbole du séparateur de milliers" + hide_cents: "Masquer les centimes" + display_currency: "Afficher la devise" + choose_currency: "Choisir la devise" + mail_method_settings: "Paramètre méthode mail" + general: "Général" + enable_mail_delivery: "Permettre distribution des mails" + send_mails_as: "Envoyer les mails en tant que" + smtp_send_all_emails_as_from_following_address: "Envoyer tous les mails depuis l'adresse suivante." + send_copy_of_all_mails_to: "Envoyer une copie de tous les mails à" + smtp_send_copy_to_this_addresses: "Envoyer une copie de tous les mails sortants à cette adresse. Si plusieurs adresses, les séparer par une virgule." + intercept_email_address: "Adresse email d'interception" + intercept_email_instructions: "Modifier l'email destinataire et le remplacer avec cette adresse." + smtp: "SMTP" + smtp_domain: "Domaine SMTP" + smtp_mail_host: "Hôte de messagerie SMTP" + smtp_port: "Port SMTP" + secure_connection_type: "Type de connexion sécurisée" + smtp_authentication_type: "Type d'authentification SMTP" + smtp_username: "Nom d'utilisateur SMTP" + smtp_password: "Mot de passe SMTP" + image_settings: "Paramètres des images" + image_settings_warning: "Vous devrez générer de nouvelles vignettes si vous mettez à jour les styles paperclip. Utilisez rake paperclip:refresh:thumbnails CLASS=Spree::Image pour le faire." + attachment_default_style: Style des pièces jointes + attachment_default_url: "URL par défaut des pièces jointes" + attachment_path: "Chemin des pièces jointes" + attachment_styles: "Styles paperclip" + attachment_url: "URL des pièces jointes" + add_new_style: "Ajouter nouveau style" + image_settings_updated: "Paramètre des images mis à jour avec succès." tax_categories: "Taxe applicable" + listing_tax_categories: "Catégories de taxe en cours d'affichage" + back_to_tax_categories_list: "Retour à la liste des catégories de taxe" tax rate: "Taux de taxe" + new_tax_rate: "Nouveau taux de taxe" tax_category: "Type de taxe" + rate: "Taux" + tax_rate_amount_explanation: "Les taux de taxe sont présentés en nombres décimaux pour faciliter les calculs (ex : si le taux est 5% saisissez 0.05)" + included_in_price: "Inclus dans le prix" + show_rate_in_label: "Montrer le taux dans le nom" + back_to_tax_rates_list: "Retour à la liste des taux de taxe" tax_settings: "Paramètres taxe" + zones: "Zones" + new_zone: "Nouvelle zone" + default_tax: "Taxe par défaut" + default_tax_zone: "Zone de taxe par défaut" + country_based: "Basé sur un pays" + state_based: "Basé sur un État/département" + countries: "Pays" + listing_countries: "Pays en cours d'affichage" + iso_name: "Noms ISO" + states_required: "États/Départements requis" + editing_country: "Pays en cours de mise à jour" + back_to_countries_list: "Retour à la liste des pays" + states: "États/Départements requis" + abbreviation: "Code" + new_state: "Nouveau Région:" payment_methods: "Méthodes de paiement" new_payment_method: "Nouvelle méthode de paiement" + provider: "Fournisseur" + taxonomies: "Taxonomies" + new_taxonomy: "Nouvelle taxonomie" + back_to_taxonomies_list: "Retour à la liste des taxonomies" shipping_methods: "Méthodes de livraison" shipping_categories: "Condition de transport" + new_shipping_category: "Créer nouvelle méthode de livraison" + back_to_shipping_categories: "Retour aux catégories d'expédition" + analytics_trackers: "Traqueurs analyse navigation" + no_trackers_found: "Pas de traqueur trouvé" + new_tracker: "Nouveau traqueur" + add_one: "En ajouter un" + google_analytics_id: "ID analytique" + back_to_trackers_list: "Retour à la liste des traqueurs" name: "Nom" description: "Description" type: "Catégorie" + default: "par défaut" calculator: "Calculateur" + zone: "Zone" + display: "Afficher" + environment: "Environnement" + active: "Actif" nore: "Plus" + no_results: "Pas de résultats" create: "Créer" loading: "Chargement en cours" email: Email @@ -2637,6 +2774,7 @@ fr_CA: my_account: "Mon compte" date: "Date" time: "Heure" + inventory_error_flash_for_insufficient_quantity: "Un produit de votre panier n'est plus disponible." inventory: Catalogue boutique zipcode: Code postal weight: Poids (au kg) @@ -2656,13 +2794,17 @@ fr_CA: bulk_order_management: "Gestion des commandes" subscriptions: "Abonnements" products: "Produits" + option_types: "Types d'options" properties: "Labels / propriétés" + prototypes: "Prototypes" variant_overrides: "Catalogue boutique" reports: "Rapports" + configuration: "Configuration" users: "Utilisateurs" roles: "Roles" order_cycles: "Cycles de Vente" enterprises: "Entreprises" + enterprise_relationships: "Permissions" customers: "Acheteurs" groups: "Groupes" product_properties: @@ -2838,6 +2980,9 @@ fr_CA: login_to_view_order: "Veuillez vous connecter pour voir votre commande." bought: item: "Déjà commandé dans ce cycle de vente" + line_item: + insufficient_stock: "Stock disponible insuffisant, il n'en reste que %{on_hand}" + out_of_stock: "En rupture de stock" shipment_states: backorder: réapprovisionnement partial: partiel diff --git a/config/locales/nb.yml b/config/locales/nb.yml index 0e7cc717a71..f4344af9ec7 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -2735,6 +2735,7 @@ nb: back_to_taxonomies_list: "Tilbake til Taksonomiliste" shipping_methods: "Leveringsmetoder" shipping_categories: "Fraktkategorier" + new_shipping_category: "Ny Fraktkategori" back_to_shipping_categories: "Tilbake til Leveringskategorier" analytics_trackers: "Analyseverktøy" no_trackers_found: "Ingen sporingsverktøy funnet" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index a6c12d9cb3f..ba45be4fbae 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -21,16 +21,26 @@ pt: attributes: email: taken: "Já existe uma conta associada a este email. Por favor faça login ou defina uma nova palavra-passe." + spree/order: + no_card: Não há cartões autorizados disponíveis para serem cobrados order_cycle: attributes: orders_close_at: after_orders_open_at: tem de ser após data de abertura + variant_override: + count_on_hand: + using_producer_stock_settings_but_count_on_hand_set: "deve ser vazio dado estar a usar as configurações do produtor" + on_demand_but_count_on_hand_set: "deve ser vazio dado estar sob encomenda" + limited_stock_but_no_count_on_hand: "deve ser definido dado não estar sob encomenda" activemodel: attributes: order_management/reports/enterprise_fee_summary/parameters: + start_at: "Começar" + end_at: "Terminar" distributor_ids: "Centrais" producer_ids: "Produtores" order_cycle_ids: "Ciclos de Encomendas" + enterprise_fee_ids: "Nomes das Taxas" shipping_method_ids: "Métodos de Envio" payment_method_ids: "Métodos de pagamento" errors: @@ -49,6 +59,8 @@ pt: payment_method: not_available_to_shop: "não está disponível para %{shop}" invalid_type: "tem de ser em dinheiro ou método Stripe" + charges_not_allowed: "^Cobranças no Cartão de Crédito não são autorizadas por este/a consumidor/a" + no_default_card: "^Nenhum cartão por defeito para este/a consumidor/a" shipping_method: not_available_to_shop: "não está disponível para %{shop}" devise: @@ -61,6 +73,7 @@ pt: user_registrations: spree_user: signed_up_but_unconfirmed: "Foi enviada uma mensagem para o seu endereço de email com um link de confirmação. Por favor clique nesse link para activar a sua conta." + unknown_error: "Algo correu mal ao criar a sua conta. Verifique o seu email e tente outra vez." failure: invalid: | Email ou palavra-passe incorrectos. @@ -73,6 +86,13 @@ pt: models: order_cycle: cloned_order_cycle_name: "CÓPIA DE%{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "deve ser texto" + invalid_format_error: "deve ser válido" + integer_array_validator: + not_array_error: "deve ser uma lista" + invalid_element_error: "deve conter apenas números válidos" enterprise_mailer: confirmation_instructions: subject: "Por favor confirme o endereço de email de %{enterprise}" @@ -222,6 +242,8 @@ pt: quantity: Quantidade pick_up: Levantamento copy: Copiar + change_my_password: "Mudar a minha palavra-passe" + update_password: "Redefinir palavra-passe" password_confirmation: Confirmação de Palavra-passe reset_password_token: Redefinir palavra-passe expired: expirou, por favor peça uma nova @@ -476,14 +498,21 @@ pt: inventory_no_permission: não tem permissões para criar inventário para este produtor none_saved: não gravou nenhum produto com sucesso line_number: "Linha %{number}:" + encoding_error: "Por favor verifique as configurações de linguagem do ficheiro fonte e verifique que é gravado em UTF-8" + unexpected_error: "Error inesperado ao abrir o ficheiro: %{error_message}" index: select_file: Selecione uma folha de cálculo para carregar spreadsheet: Folha de cálculo + choose_import_type: Selecione tipo de importação import_into: Tipo de importação product_list: Lista de produtos inventories: Inventários import: Importar upload: Carregar + csv_templates: Templates de CSV + product_list_template: Descarregar templates de Lista de Produtos + inventory_template: Descarregar template de Inventário + category_values: Valores de Categoria disponíveis product_categories: Categorias de Produtos tax_categories: Categorias de Impostos shipping_categories: Categorias de Envio @@ -495,6 +524,8 @@ pt: save_imported: Guardar produtos importados no_valid_entries: Não foram encontradas entradas válidas none_to_save: Não foram encontradas entradas que possam ser guardadas + some_invalid_entries: Ficheiro importado contém entradas inválidas + fix_before_import: Por favor corrija estes erros e tente importar novamente save_valid?: Guardar entradas válidas e descartar as outras? no_errors: Nenhum erro detectado. save_all_imported?: Guardar todos os produtos importados? @@ -502,6 +533,9 @@ pt: no_permission: não tem permissões para gerir esta organização not_found: organização não encontrada no_name: Sem nome + blank_enterprise: alguns produtos não têm uma organização definida + reset_absent?: Restabelecer productos ausentes + reset_absent_tip: Restabelecer stock a zero para todos os produtos não presentes no ficheiro overwrite_all: Substituir todos overwrite_empty: Substituir se vazio default_stock: Definir nível de stock @@ -519,6 +553,11 @@ pt: inventory_to_reset: Itens de inventário existentes terão o nível de stock restabelecido a zero line: Linha item_line: Linha de item + import_review: + not_updatable_tip: "Os campos seguintes não podem ser actualizados para produtos existentes através da funcionalidade de importação em massa:" + fields_ignored: Estes campos vão ser ignorados quando os produtos importados forem gravados. + entries_table: + not_updatable: Este campo não é actualizável para produtos existentes através da funcionalidade de importação em massa save_results: final_results: Importar resultados finais products_created: Produtos criados @@ -530,6 +569,9 @@ pt: all_saved: "Todos os itens guardados com sucesso" some_saved: "itens guardados com sucesso" save_errors: Erros a guardar + import_again: Carregar outro ficheiro + view_products: Ir para a Página de Produtos + view_inventory: Ir para a Página de Inventário variant_overrides: loading_flash: loading_inventory: A CARREGAR INVENTÁRIO... @@ -562,6 +604,7 @@ pt: tip: "Use esta página para alterar a quantidade de produtos entre múltiplas encomendas. Os produtos podem ser removidos completamente das encomendas, se necessário" shared: "Recurso Compartilhado?" order_no: "Encomenda Nº" + order_date: "Concluído Em" max: "Máximo" product_unit: "Produto: Unidade" weight_volume: "Peso/Volume" @@ -703,7 +746,9 @@ pt: close_date: Data de fecho social: twitter_placeholder: 'ex: @o_prof' + instagram_placeholder: ex. o_agricultor facebook_placeholder: ex. www.facebook.com/asuapagina + linkedin_placeholder: ex. www.linkedin.com/in/OSeuNomeAqui stripe_connect: connect_with_stripe: "Conectar com o Stripe" stripe_connect_intro: "Para aceitar pagamentos com cartão de crédito, vai ser necessário ligar a sua conta Stripe à Open Food Network. Use o botão à direita para começar." @@ -795,10 +840,19 @@ pt: i_am_producer: Sou um produtor contact_name: Nome do contacto edit: + editing: 'Configurações' back_link: Voltar à lista de organizações new: title: Nova Organização back_link: Voltar à lista de organizações + remove_logo: + remove: "Remover Imagem" + removed_successfully: "Logotipo removido com sucesso" + immediate_removal_warning: "O logotipo será removido imediatamente após a confirmação." + remove_promo_image: + remove: "Remover Imagem" + removed_successfully: "Imagem promocional removida com sucesso" + immediate_removal_warning: "A imagem promocional será removida imediatamente após confirmação." welcome: welcome_title: Bem-vindo à Open Food Network! welcome_text: Você criou com sucesso uma @@ -832,7 +886,10 @@ pt: coordinator_fees: add: Adicionar taxa de coordenador filters: + search_by_order_cycle_name: "Procurar por nome do Ciclo de Encomenda..." involving: "Envolvendo" + any_enterprise: "Qualquer Organização" + any_schedule: "Qualquer Horário" form: incoming: Entrada supplier: Fornecedor @@ -882,6 +939,7 @@ pt: bulk_update: no_data: Hmmm, algo correu mal. Não foram encontrados dados do ciclo de encomendas. date_warning: + msg: Este ciclo de encomendas está ligado a %{n} encomendas de subscrições abertas. Mudar esta data não afetará as encomendas que já foram colocadas, mas deve ser evitado se possível. Tem a certeza que quer continuar? cancel: Cancelar proceed: Continuar producer_properties: @@ -959,6 +1017,9 @@ pt: description: Facturas para importar para o Xero packing: name: Relatórios de Embalamento + enterprise_fee_summary: + name: "Sumário das Taxas de Organização" + description: "Sumário das Taxas de Organização cobradas" subscriptions: subscriptions: Subscrições new: Nova Subscrição @@ -988,17 +1049,26 @@ pt: address: 2. Morada products: 3. Adicionar Produtos review: 4. Rever & Guardar + subscription_line_items: + this_is_an_estimate: | + Os preços mostrados são apenas estimativas e são calculados no momento em que a subscrição é mudada. + Se mudar preços ou taxas, as encomendas serão atualizadas, mas a subscrição ainda mostrará os preços anteriores. + not_in_open_and_upcoming_order_cycles_warning: "Não há ciclos de encomenda abertos ou que estejam para abrir para este produto." details: details: Detalhes invalid_error: Ooops! Por favor preencha todos os campos obrigatórios... allowed_payment_method_types_tip: De momento, só podem ser usados métodos de pagamento em Dinheiro ou Stripe credit_card: Cartão de Crédito + charges_not_allowed: Cobranças não são autorizadas por este/a consumidor/a + no_default_card: O/a consumidor/a não tem cartões disponíveis para cobrança + card_ok: O/a consumidor/a tem um cartão disponível para cobrança loading_flash: loading: A CARREGAR SUBSCRIÇÕES review: details: Detalhes address: Morada products: Produtos + no_open_or_upcoming_order_cycle: "Nenhum Ciclo de Encomendas aberto ou que abrirá em breve" product_already_in_order: Este produto já foi adicionado à encomenda. Por favor edite a quantidade directamente. orders: number: Número @@ -1029,6 +1099,11 @@ pt: stripe_connect_fail: Pedimos desculpa, mas a ligação à sua conta Stripe falhou stripe_connect_settings: resource: Configuração Stripe Connect + api: + enterprise_logo: + destroy_attachment_does_not_exist: "Logotipo não existe" + enterprise_promo_image: + destroy_attachment_does_not_exist: "Imagem promocional não existe" checkout: already_ordered: cart: "carrinho" @@ -1068,6 +1143,10 @@ pt: footer_legal_tos: "Termos e condições" footer_legal_visit: "Encontre-nos no" footer_legal_text_html: "A Open Food Network é uma plataforma livre e de código aberto. O nosso conteúdo tem uma licença %{content_license} e o nosso código %{code_license}." + footer_data_text_with_privacy_policy_html: "Nós tratamos com cuidado dos nosso dados. Veja a nossa %{privacy_policy} e a nossa %{cookies_policy}" + footer_data_text_without_privacy_policy_html: "Nós tratamos com cuidado dos nosso dados. Veja a nossa %{cookies_policy}" + footer_data_privacy_policy: "politíca de privacidade" + footer_data_cookies_policy: "política de cookies" footer_skylight_dashboard_html: Dados de performance disponível em %{dashboard}. shop: messages: @@ -1133,6 +1212,7 @@ pt: footer_email: "Email" footer_links_md: "Links" footer_about_url: "URL Sobre" + user_guide_link: "Link para o Guia de Utilizador/a" name: Nome first_name: Primeiro Nome last_name: Último Nome @@ -1158,6 +1238,7 @@ pt: no_shipping_or_payment: sem métodos de envio nem pagamento unconfirmed: não confirmado days: dias + authorization_failure: "Falha na Autorização" label_shop: "Loja" label_shops: "Lojas" label_map: "Mapa" @@ -1206,6 +1287,48 @@ pt: ie_warning_firefox: Descarregar Firefox ie_warning_ie: Actualizar Internet Explorer ie_warning_other: "Não consegue actualizar o navegador? Tente aceder à OFN pelo smartphone :-)" + legal: + cookies_policy: + header: "Como usámos as Cookies" + desc_part_1: "Cookies são pequenos ficheiros de texto que são guardados no seu computador quando visita um website." + desc_part_2: "No OFN nós respeitamos totalmente a sua privacidade. Só usamos Cookies que são essenciais para o funcionamento do website." + desc_part_3: "Usamos Cookies essencialmente para lembrar se fez login no website e que items é que tem no seu carrinho. Se continuar a navegar no website sem aceitar a política de cookies, nós assumimos que nos está a dar consentimento para guardar as cookies que são essenciais para o funcionamento do website. Aqui está a lista de cookies que usamos!" + essential_cookies: "Cookies Essenciais" + essential_cookies_desc: "As seguintes Cookies são estritamente necessárias para o funcionamento do website." + essential_cookies_note: "A maioria das cookies contém apenas um identificador e portanto o seu email e password nunca são guardados nas cookies." + cookie_domain: "Definida por:" + cookie_session_desc: "Usada para identificar o utilizador entre páginas, por exemplo, items contidos no carrinho de compras." + cookie_consent_desc: "Usado para manter o estado do consentimento sobre a utilização de cookies." + cookie_remember_me_desc: "Usado no caso em que o/a utilizador/a pediu ao website para se lembrar. Esta cookie é automaticamente apagada passados 12 dias." + cookie_openstreemap_desc: "Usado pelo nosso fornecedor amigável e de código aberto (OpenStreetMap) para garantir que não recebe demasiados pedidos durante um certo período de tempo, para evitar que os seus servidores sejam abusados." + cookie_stripe_desc: "Cookie definida pelo nosso fornecedor de serviços de pagamento Stripe. Para mais informações veja https://stripe.com/cookies-policy/legal e https://stripe.com/privacy." + statistics_cookies: "Cookies de Estatísticas" + statistics_cookies_desc: "As seguintes cookies não são estritamente necessárias mas ajudam a uma melhor experiência do/a utilizador/a." + statistics_cookies_analytics_desc_html: "Para coletar e analisar dados de utilização da plataforma nós usamos Google Analytics e Matomo (ex Piwik, uma ferramenta de analytics de código aberto que está confirme do RGPD e protege a sua privacidade)." + statistics_cookies_matomo_desc_html: "Para coletar e analisar dados de utilização da plataforma nós usamos Matomo (ex Piwik, uma ferramenta de analytics de código aberto que está confirme do RGPD e protege a sua privacidade)." + statistics_cookies_matomo_optout: "Quer optar por não usar a cookie Matomo?" + cookie_analytics_utma_desc: "Usado para distinguir utilizadores e sessões." + cookie_analytics_utmt_desc: "Usado para evitar demasiados pedidos aos servidores." + cookie_analytics_utmb_desc: "Usado para determinar novas visitas/sessões." + cookie_analytics_utmc_desc: "Versão anterior usada para determinar novas visitas/sessões." + cookie_analytics_utmz_desc: "Usado para guarda a origem do trafego." + cookie_matomo_basics_desc: "Cookies Matomo para recolher estatísticas." + cookie_matomo_heatmap_desc: "Cookie Matomo para registo de sessões." + cookie_matomo_ignore_desc: "Cookie Matomo para registar opt-out de utilizadores." + disabling_cookies_header: "Aviso sobre cookies desactivadas" + disabling_cookies_desc: "Como utilizador pode sempre autorizar, bloquear ou apagar as cookies da OFN e de outros websites. Aqui estão os links para o fazer em diferentes browsers:" + disabling_cookies_firefox_link: "https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences" + disabling_cookies_chrome_link: "https://support.google.com/chrome/answer/95647" + disabling_cookies_ie_link: "https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/en-ww/cookies/" + disabling_cookies_note: "Repare que se desactivar as cookies totalmente o website deixará de funcionar." + cookies_banner: + cookies_usage: "Este website usa cookies para melhorar a experiência do utilizador." + cookies_definition: "Cookies são pequenos ficheiros de texto que são guardados no seu computador quando visita um website." + cookies_desc: "Usamos Cookies essencialmente para lembrar se fez login no website e que items é que tem no seu carrinho. Se continuar a navegar no website sem aceitar a política de cookies, nós assumimos que nos está a dar consentimento para guardar as cookies que são essenciais para o funcionamento do website. Aqui está a lista de cookies que usamos!" + cookies_policy_link_desc: "Se quer saber mais, veja a nossa" + cookies_policy_link: "politíca de cookies" + cookies_accept_button: "Aceitar Cookies" home_shop: Ir às compras brandstory_headline: "Para quem consome com princípios" brandstory_intro: "Às vezes a melhor forma de consertar o sistema é construir um novo..." @@ -1295,6 +1418,7 @@ pt: email_confirmation_click_link: "Clique no link abaixo para confirmar o seu email e continuar a criar o seu perfil." email_confirmation_link_label: "Confirme este endereço de email »" email_confirmation_help_html: "Depois de confirmar o seu email, pode aceder à conta de administração desta organização. Veja o %{link} para saber mais sobre a %{sitename} e para começar a utilizar o seu perfil ou loja online." + email_confirmation_notice_unexpected: "Está a receber esta mensagem por se registou em %{sitename}, ou foi convidado para se registar por alguém que provavelmente conhece. Se não entende porque está a receber esta mensagem, por favor escreva para %{contact}." email_social: "Conecte-se connosco:" email_contact: "Envie-nos um email:" email_signoff: "Obrigado," @@ -1325,6 +1449,7 @@ pt: email_so_edit_true_html: "Pode fazer alterações até ao fecho das encomendas a %{orders_close_at}." email_so_edit_false_html: "Pode ver detalhes desta encomenda em qualquer momento. " email_so_contact_distributor_html: "Se tiver alguma questão pode contactar %{distributor}através de %{email}." + email_so_contact_distributor_to_change_order_html: "Esta encomenda foi criada automaticamente para si. Pode fazer alterações até as encomendas fecharem em %{orders_close_at} contactando %{distributor} através do email %{email}." email_so_confirmation_intro_html: "A sua encomenda com %{distributor} está agora confirmada" email_so_confirmation_explainer_html: "Esta encomenda foi feita automaticamente para si, e está agora finalizada." email_so_confirmation_details_html: "Aqui está tudo o que precisa de saber sobre a sua encomenda com %{distributor}:" @@ -1489,6 +1614,7 @@ pt: products_available: Disponível? products_producer: "Produtor" products_price: "Preço" + name_or_sku: "NOME OU CÓDIGO" register_title: Registo sell_title: "\bRegistar" sell_headline: "Junte-se à Open Food Network!" @@ -1546,6 +1672,7 @@ pt: orders_bought_edit_button: Editar itens confirmados orders_bought_already_confirmed: "* já confirmado" orders_confirm_cancel: Tem a certeza que quer cancelar esta encomenda? + order_processed_successfully: "A sua encomenda foi processada com sucesso" products_cart_distributor_choice: "Distribuidor para a sua encomenda:" products_cart_distributor_change: "O distribuidor para esta encomenda será alterado para %{name} se adicionar este produto ao carrinho." products_cart_distributor_is: "O distribuidor para esta encomenda é %{name}." @@ -1585,6 +1712,7 @@ pt: error_number: "precisa ser um número" error_email: "precisa ser um endereço de email" error_not_found_in_database: "%{name} não foi encontrado na base de dados" + error_not_primary_producer: "%{name} não está definido como produtor" error_no_permission_for_enterprise: "\"%{name}\": não tem permissão para gerir produtos desta organização" item_handling_fees: "Taxas de Manejo do Produto (incluídas no total do produto)" january: "Janeiro" @@ -1625,11 +1753,13 @@ pt: registration_about_us: "Texto 'Sobre Nós'" registration_outcome_headline: "O que ganho?" registration_outcome1_html: "O seu perfil ajuda as pessoas a o encontrarem e entrarem em contacto consigo na Open Food Network" + registration_outcome2: "Use este espaço para contar a história da sua organização, de forma a gerar ligações à sua presença social e online. " registration_outcome3: "É também o primeiro passo para comercializar na Open Food Network, ou abrir uma loja online." registration_action: "Vamos começar!" details: title: "Detalhes" headline: "Vamos começar" + enterprise: "Primeiro precisamos de saber um pouco sobre a sua organização:" producer: "Primeiro precisamos de saber um pouco sobre a sua quinta:" enterprise_name_field: "Nome da Organização:" producer_name_field: "Nome da Quinta" @@ -1671,6 +1801,8 @@ pt: title: "Sobre" headline: "Boa!" message: "Agora vamos inserir os detalhes sobre" + success: "Sucesso! %{enterprise} foi adicionada à Open Food Network" + registration_exit_message: "Se sair deste processo em qualquer passo, pode continuar a criar o seu perfil na área de administração." enterprise_description: "Descrição Curta" enterprise_description_placeholder: "Uma frase curta que descreva a sua organização " enterprise_long_desc: "Descrição Longa" @@ -1765,6 +1897,10 @@ pt: ok: OK not_visible: não visível you_have_no_orders_yet: "Ainda não tem encomendas" + show_only_complete_orders: "Mostrar só encomendas completas" + successfully_created: '%{resource} foi criado/a com sucesso!' + successfully_removed: '%{resource} foi removido/a com sucesso!' + successfully_updated: '%{resource} foi actualizado/a com sucesso!' running_balance: "Saldo corrente" outstanding_balance: "Saldo pendente" admin_enterprise_relationships: "Permissões das Organizações" @@ -1892,6 +2028,8 @@ pt: order_cycles: "Ciclos de Encomendas" enterprise_relationships: "Permissões das organizações" remove_tax: "Remover imposto" + first_name_begins_with: "Primeiro nome começa com" + last_name_begins_with: "Último nome começa com" enterprise_tos_link: "Ligação para Termos de Serviço da Organização" enterprise_tos_message: "Queremos trabalhar com pessoas que partilham os nossos objectivos e valores. Por isso pedimos às organizações novas que concordem com os nossos" enterprise_tos_link_text: "Termos de Serviço." @@ -2098,6 +2236,8 @@ pt: shipping_methods: "Métodos de Envio" payment_methods: "Métodos de Pagamento" payment_method_fee: "Taxa de transação" + payment_processing_failed: "O pagamento não pode ser processado, por favor verifique os detalhes que introduziu" + payment_updated: "Pagamento atualizado" inventory_settings: "Configurações de Inventário" tag_rules: "Regras para Etiquetas" shop_preferences: "Preferências da Loja" @@ -2292,6 +2432,13 @@ pt: new_tag_rule_dialog: select_rule_type: "Selecionar um tipo de regra:" orders: + index: + per_page: "%{results} por página" + view_file: "Ver Ficheiro" + compiling_invoices: "Preparando Faturas" + bulk_invoice_created: "Grupo de Faturas criado" + bulk_invoice_failed: "Falhou a criação de Grupo de Faturas" + please_wait: "Por favor espere até que o PDF esteja pronto antes de fechar a janela." order_state: address: "morada" adjustments: "ajustes" @@ -2326,6 +2473,10 @@ pt: invalid: "inválido" resend_user_email_confirmation: resend: "Reenviar" + sending: "Reenviar..." + done: "Reenvio feito ✓" + failed: "Reenvio falhou ✗" + insufficient_stock: "Stock disponível insuficiente, apenas %{on_hand} disponíveis" out_of_stock: reduced_stock_available: Stock reduzido disponível out_of_stock_text: > @@ -2338,6 +2489,7 @@ pt: 'yes': "Sob encomenda" variant_overrides: on_demand: + use_producer_settings: "Usar configurações do produtor" 'yes': "Sim" 'no': "Não" inventory_products: "Produtos de Inventário" @@ -2374,7 +2526,9 @@ pt: confirmation: | Isto colocará a zero o stock de todos os produtos desta organização que não estejam presentes no ficheiro carregado. order_cycles: + create_failure: "Falhou a criação do ciclo de encomendas" update_success: 'O seu ciclo de encomendas foi actualizado.' + update_failure: "Falhou a atualização do ciclo de encomendas" no_distributors: Não existem distribuidores neste ciclo de encomendas. Este ciclo de encomendas só ficará visível para os consumidores quando um distribuidor for adicionado. Gostaria de continuar a guardar este ciclo de encomendas? enterprises: producer: "Produtor" @@ -2400,55 +2554,163 @@ pt: order_management: reports: enterprise_fee_summary: + date_end_before_start_error: "deve ser depois do início" + parameter_not_allowed_error: "Não está autorizado a usar um ou mais filtros selecionados neste relatório." fee_calculated_on_transfer_through_all: "Tudo" + fee_calculated_on_transfer_through_entire_orders: "Encomendas Completas pelo %{distributor}" + tax_category_various: "Vários" + fee_type: + payment_method: "Método de Pagamento" + shipping_method: "Envio" fee_placements: supplier: "Entrada" distributor: "Saída" coordinator: "Coordenador" + tax_category_name: + shipping_instance_rate: "Taxa da Plataforma" formats: csv: header: fee_type: "Tipo de Taxa" + enterprise_name: "Organização" + fee_name: "Nome da Taxa" customer_name: "Consumidor" + fee_placement: "Taxa" + fee_calculated_on_transfer_through_name: "Calculo da Taxa na Transferência" tax_category_name: "Categoria de Imposto" + total_amount: "Soma Total" html: header: fee_type: "Tipo de Taxa" + enterprise_name: "Organização" + fee_name: "Nome da Taxa" customer_name: "Consumidor" + fee_placement: "Taxa" + fee_calculated_on_transfer_through_name: "Calculo da Taxa na Transferência" tax_category_name: "Categoria de Imposto" + total_amount: "Soma Total" + invalid_filter_parameters: "Os filtros que selecionou para este relatório são inválidos." order: "Encomenda" distribution: "Distribuição" + order_details: "Detalhes da Encomenda" + customer_details: "Detalhes do/a Consumidor/a" + adjustments: "Ajustes" + payments: "Pagamentos" payment: "Pagamento" payment_method: "Método de Pagamento" + shipment: "Envio" + shipment_inc_vat: "Envio incluíndo IVA" + shipping_tax_rate: "Taxa de Imposto sobre o Envio" category: "Categoria" delivery: "Entrega" + temperature_controlled: "Temperatura Controlada" new_product: "Novo Produto" administration: "Administração" + logged_in_as: "Ligado/a como" account: "Conta" logout: "Terminar sessão" + date_range: "Intervalo de Datas" + status: "estado" + new: "Novo" + start: "Começar" + stop: "Parar" first: "Primeiro" previous: "Anterior" last: "Último" spree: + your_order_is_empty_add_product: "A sua encomenda está vazia, por favor procure e adicione um produto em cima" + add_product: "Adicionar Produto" + name_or_sku: "Nome ou Código (insira pelo menos 4 caracteres)" resend: Reenviar + back_to_orders_list: Voltar à Lista de Encomendas + select_stock: "Selecione Quantidade" + location: "Localização" + count_on_hand: "Disponível" quantity: "Quantidade" + package_from: "Embalagem de" + item_description: "Descrição do Item" price: "Preço" total: "Total" edit: "Editar" + split: "Separar" delete: "Apagar" + cannot_set_shipping_method_without_address: "Não pode definir Método de Envio até os detalhes do/a consumidor/a estiverem definidos." + no_tracking_present: "Detalhes de tracking não definidos." + order_total: "Total da Encomenda" + customer_details: "Detalhes do/a Consumidor/a" + customer_search: "Pesquisa de Consumidor/a" + choose_a_customer: "Escolha um/a consumidor/a" account: "Conta" billing_address: "Morada de faturação" shipping_address: "Morada de Envio" + first_name: "Primeiro nome" + last_name: "Último nome" + street_address: "Morada" + street_address_2: "Morada (continuação)" city: "Cidade" + zip: "Código Postal" country: "País" state: "Região" phone: "Telefone" update: "Atualizar" + use_billing_address: "Utilizar Morada de Faturação" + adjustments: "Ajustes" continue: "Continuar" + fill_in_customer_info: "Por favor preencha informação do/a consumidor/a" + new_payment: "Novo Pagamento" + configurations: "Configurações" general_settings: "Configurações Gerais" + site_name: "Nome do Site" + site_url: "URL do Site" + default_seo_title: "Título SEO por defeito" + default_meta_description: "Meta Description por defeito" + default_meta_keywords: "Meta keywords por defeito" + security_settings: "Configurações de Segurança" + allow_ssl_in_development_and_test: "Permitir que SSL seja utilizado quando em desenvolvimento e testes" + allow_ssl_in_production: "Permitir que SSL seja usado em produção" + allow_ssl_in_staging: "Permitir que SSL seja usado em pre-produção" + check_for_spree_alerts: "Veja Alertas do Spree" + currency_decimal_mark: "Separador Decimal da Moeda" + currency_settings: "Configurações de Moeda" + currency_symbol_position: Colocar o símbolo de moeda antes ou depois do valor? + currency_thousands_separator: "Separador de milhares na moeda" + hide_cents: "Esconder cêntimos" + display_currency: "Mostrar moeda" + choose_currency: "Escolher Moeda" + mail_method_settings: "Configurações de Email" + general: "Geral" + enable_mail_delivery: "Ativar Envios de Email" + send_mails_as: "Enviar Emails como" + smtp_send_all_emails_as_from_following_address: "Enviar todos os emails a partir do seguinte endereço." + send_copy_of_all_mails_to: "Enviar cópia de todos os emails para" + smtp_send_copy_to_this_addresses: "Envia uma cópia de todos os email enviados para este endereço. Para múltiplos endereços, separar por vírgulas." + intercept_email_address: "Interceptar Endereço de Email" + intercept_email_instructions: "Substituir destinatário de todos os email por este endereço." + smtp: "SMTP" + smtp_domain: "Domínio SMTP" + smtp_mail_host: "Servidor de Email SMTP" + smtp_port: "Porta SMTP" + secure_connection_type: "Tipo de Ligação Segura" + smtp_authentication_type: "Tipo de Autenticação SMTP" + smtp_username: "Utilizador SMTP" + smtp_password: "Password SMTP" + image_settings: "Configurações de Imagens" + image_settings_warning: "Terá de regenerar os ícones se alterar is estilos. Para tal execute rake paperclip:refresh:thumbnails CLASS=Spree::Image" + attachment_default_style: Estilo dos Anexos + attachment_default_url: "URL por defeito dos Anexos" + attachment_path: "Localização dos Anexos" + attachment_styles: "Estilos do Anexo" + attachment_url: "URL do Anexo" + add_new_style: "Adicionar Novo Estilo" + image_settings_updated: "Configurações de Imagens actualizadas com sucesso." tax_categories: "Categorias de Impostos" + listing_tax_categories: "Lista de Categorias de Impostos" + back_to_tax_categories_list: "Voltar à Lista de Categorias de Impostos" tax rate: "Taxas de Imposto" + new_tax_rate: "Nova Taxa de Imposto" tax_category: "Categoria de Imposto" + rate: "Taxa" + tax_rate_amount_explanation: "Taxas de Imposto são uma quantidade decimal que suportam o cálculo (por exemplo, se a taxa de imposto for 5%, insira 0.05)" included_in_price: "Incluído no Preço" show_rate_in_label: "Mostrar taxa na etiqueta" back_to_tax_rates_list: "Voltar à lista de Taxas de Imposto" @@ -2464,14 +2726,37 @@ pt: iso_name: "Nome ISO" states_required: "Regiões obrigatórias" editing_country: "Editar País" + back_to_countries_list: "Voltar à Lista de Países" + states: "Regiões" + abbreviation: "Abreviatura" + new_state: "Nova Região" payment_methods: "Métodos de pagamento" + new_payment_method: "Novo Método de Pagamento" + provider: "Fornecedor" + taxonomies: "Taxonomias" + new_taxonomy: "Nova Taxonomia" + back_to_taxonomies_list: "Voltar à Lista de Taxonomias" shipping_methods: "Métodos de Envio" shipping_categories: "Categorias de Envio" + new_shipping_category: "Nova Categoria de Envio" + back_to_shipping_categories: "Voltar à Lista de Categorias de Envio" + analytics_trackers: "Trackers do Analytics" + no_trackers_found: "Nenhum Tracker encontrado" + new_tracker: "Novo Tracker" + add_one: "Adicionar um" + google_analytics_id: "ID do Analytics" + back_to_trackers_list: "Voltar à Lista de Trackers" name: "Nome" description: "Descrição" type: "Tipo" + default: "por defeito" calculator: "Calculadora" + zone: "Zona" + display: "Mostrar" + environment: "Ambiente" + active: "Ativo" nore: "Mais" + no_results: "Nenhum resultado" create: "Criar" loading: "A carregar" email: Email @@ -2479,6 +2764,7 @@ pt: my_account: "A minha conta" date: "Data" time: "Hora" + inventory_error_flash_for_insufficient_quantity: "Um item do seu carrinho ficou indisponível." inventory: Inventário zipcode: Código postal weight: Peso (por kg) @@ -2487,6 +2773,10 @@ pt: errors: messages: blank: "não pode ser vazio" + layouts: + admin: + header: + store: Loja admin: tab: dashboard: "Painel de controlo" @@ -2494,15 +2784,22 @@ pt: bulk_order_management: "Gestão de Encomendas por Atacado" subscriptions: "Subscrições" products: "Produtos" + option_types: "Tipos de Opções" properties: "Propriedades" + prototypes: "Protótipos" variant_overrides: "Inventário" reports: "Relatórios" + configuration: "Configuração" users: "Utilizadores" roles: "Papeis" order_cycles: "Ciclos de Encomendas" enterprises: "Organizações" + enterprise_relationships: "Permissões" customers: "Consumidores/as" groups: "Grupos" + product_properties: + index: + inherits_properties_checkbox_hint: "Herdar propriedades de %{supplier}? (excepto se já alterado em cima)" orders: index: listing_orders: "Lista de Encomendas" @@ -2517,7 +2814,9 @@ pt: next: "Seguinte" loading: "A carregar" no_orders_found: "Nenhuma encomenda encontrada" + results_found: "%{number} resultados encontrados." viewing: "A ver de %{start}a %{end}." + print_invoices: "Imprimir Faturas" sortable_header: payment_state: "Estado do Pagamento" shipment_state: "Estado do envio" @@ -2540,6 +2839,8 @@ pt: products: active_products: zero: "Não tem nenhum produto ativo." + one: "Tem um produto ativo" + other: "Tem %{count} produtos ativos" order_cycles: order_cycles: "Ciclos de Encomendas" order_cycles_tip: "Os ciclos de encomendas determinam quando e onde é que os seus produtos estão disponíveis para os consumidores." @@ -2549,6 +2850,12 @@ pt: other: "Tem %{count}ciclos de encomendas activos." manage_order_cycles: "GERIR CICLOS DE ENCOMENDAS" payment_methods: + new: + new_payment_method: "Novo Método de Pagamento" + back_to_payment_methods_list: "Voltar à Lista de Métodos de Pagamento" + edit: + editing_payment_method: "Editar Método de Pagamento" + back_to_payment_methods_list: "Voltar à Lista de Métodos de Pagamento" stripe_connect: enterprise_select_placeholder: Escolher... loading_account_information_msg: A carregar informação de conta do Stripe, por favor aguarde... @@ -2600,14 +2907,21 @@ pt: display_as: display_as: Mostrar como reports: + table: + select_and_search: "Selecione os filtros e carregue em %{option} para ver os seus dados." bulk_coop: bulk_coop_supplier_report: 'Cooperativa por Atacado - Totais por Fornecedor' bulk_coop_allocation: 'Cooperativa por Atacado - Alocação' bulk_coop_packing_sheets: 'Cooperativa por Atacado - Folhas de Empacotamento' bulk_coop_customer_payments: 'Cooperativa por Atacado - Pagamentos do Consumidor' enterprise_fee_summaries: + filters: + date_range: "Intervalo de Datas" + report_format_csv: "Descarregar como csv" + generate_report: "Gerar Relatório" report: none: "Nenhum" + select_and_search: "Selecione filtros e carregue em Gerar Relatório para ver os seus dados." users: index: listing_users: "Lista de Utilizadores" @@ -2634,8 +2948,11 @@ pt: general_settings: edit: legal_settings: "Configurações Legais" + cookies_consent_banner_toggle: "Mostrar o Banner de consentimento de Cookies" privacy_policy_url: "URL da Politíca de Privacidade" enterprises_require_tos: "As organizações têm de aceitar os Termos de Serviço" + cookies_policy_matomo_section: "Mostrar a secção do Matomo na página da política de Cookies" + cookies_policy_ga_section: "Mostrar a secção do Google Analytics na página de política de Cookies" footer_tos_url: "URL dos Termos de Serviço" checkout: payment: @@ -2653,6 +2970,9 @@ pt: login_to_view_order: "Por favor faça login para ver a sua encomenda." bought: item: "Já encomendou neste ciclo" + line_item: + insufficient_stock: "Stock disponível insuficiente, apenas %{on_hand} disponíveis" + out_of_stock: "Sem Stock" shipment_states: backorder: rutura de stock partial: parcial @@ -2744,6 +3064,8 @@ pt: delete?: Apagar? cards: authorised_shops: Lojas Autorizadas + authorised_shops_popover: Esta é uma lista de organizações que está autorizada a cobrar o seu cartão de crédito por defeito para as subscrições que tem. Os detalhes do seu cartão não serão partilhados com os vendedores. Será sempre notificado quando houver alguma cobrança. + saved_cards_popover: Esta é a lista de cartões que optou por guardar para uso futuro. O seu cartão por defeito será selecionado quando fizer checkout e pode ser cobrado pelas organizações que autorizou (veja à direita). authorised_shops: shop_name: "Nome da Loja" allow_charges?: "Permitir Taxas?" diff --git a/lib/open_food_network/order_and_distributor_report.rb b/lib/open_food_network/order_and_distributor_report.rb index 1663d483a48..29b9772c960 100644 --- a/lib/open_food_network/order_and_distributor_report.rb +++ b/lib/open_food_network/order_and_distributor_report.rb @@ -10,7 +10,8 @@ def initialize(user, params = {}, render_table = false) end def header - [I18n.t(:report_header_order_date), + [ + I18n.t(:report_header_order_date), I18n.t(:report_header_order_id), I18n.t(:report_header_customer_name), I18n.t(:report_header_customer_email), @@ -28,7 +29,9 @@ def header I18n.t(:report_header_distributor_address), I18n.t(:report_header_distributor_city), I18n.t(:report_header_distributor_postcode), - I18n.t(:report_header_shipping_instructions)] + I18n.t(:report_header_shipping_method), + I18n.t(:report_header_shipping_instructions) + ] end def search @@ -95,6 +98,7 @@ def row_for(line_item, order) order.distributor.address.address1, order.distributor.address.city, order.distributor.address.zipcode, + order.shipping_method.name, order.special_instructions ] end diff --git a/lib/open_food_network/orders_and_fulfillments_report.rb b/lib/open_food_network/orders_and_fulfillments_report.rb index eabdbc6c5f9..2f766bd640d 100644 --- a/lib/open_food_network/orders_and_fulfillments_report.rb +++ b/lib/open_food_network/orders_and_fulfillments_report.rb @@ -102,55 +102,57 @@ def rules sort_by: proc { |full_name| full_name } } ] when "order_cycle_customer_totals" [ { group_by: proc { |line_item| line_item.order.distributor }, - sort_by: proc { |distributor| distributor.name } }, + sort_by: proc { |distributor| distributor.name } }, { group_by: proc { |line_item| line_item.order }, - sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname }, - summary_columns: [ - proc { |line_items| line_items.first.order.distributor.name }, - proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| I18n.t('admin.reports.total') }, - proc { |line_items| "" }, + sort_by: proc { |order| order.bill_address.full_name_reverse }, + summary_columns: [ + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.order.bill_address.full_name }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| I18n.t('admin.reports.total') }, + proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.payment_fee } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } }, - proc { |line_items| line_items.all? { |li| li.order.paid? } ? I18n.t(:yes) : I18n.t(:no) }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum(&:amount) }, + proc { |line_items| line_items.sum(&:amount_with_adjustments) }, + proc { |line_items| line_items.first.order.admin_and_handling_total }, + proc { |line_items| line_items.first.order.ship_total }, + proc { |line_items| line_items.first.order.payment_fee }, + proc { |line_items| line_items.first.order.total }, + proc { |line_items| line_items.first.order.paid? ? I18n.t(:yes) : I18n.t(:no) }, - proc { |line_items| "" }, - proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.special_instructions } , - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.order_cycle.andand.name }, - proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, - proc { |line_items| "" }, - proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" } - ] }, + proc { |line_items| line_items.first.order.special_instructions } , + proc { |line_items| "" }, + proc { |line_items| line_items.first.order.order_cycle.andand.name }, + proc { |line_items| + line_items.first.order.payments.first.andand.payment_method.andand.name + }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" } + ] }, { group_by: proc { |line_item| line_item.product }, - sort_by: proc { |product| product.name } }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.variant }, + sort_by: proc { |variant| variant.full_name } }, { group_by: proc { |line_item| line_item.full_name }, - sort_by: proc { |full_name| full_name } } ] + sort_by: proc { |full_name| full_name } } ] else [ { group_by: proc { |line_item| line_item.product.supplier }, sort_by: proc { |supplier| supplier.name } }, @@ -222,7 +224,7 @@ def columns proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) }, proc { |line_items| "" }, - proc { |line_items| line_items.first.product.sku }, + proc { |line_items| line_items.first.variant.sku }, proc { |line_items| line_items.first.order.order_cycle.andand.name }, proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, diff --git a/spec/controllers/spree/admin/variants_controller_spec.rb b/spec/controllers/spree/admin/variants_controller_spec.rb index d8178fa3a1a..0e290014dcf 100644 --- a/spec/controllers/spree/admin/variants_controller_spec.rb +++ b/spec/controllers/spree/admin/variants_controller_spec.rb @@ -35,6 +35,84 @@ module Admin assigns(:variants).should match_array [v1, v2] end end + + describe '#destroy' do + let(:variant) { create(:variant) } + + context 'when requesting with js' do + before do + allow(Spree::Variant).to receive(:find).with(variant.id.to_s) { variant } + allow(variant).to receive(:destroy).and_call_original + end + + it 'destroys the variant' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + expect(variant).to have_received(:destroy) + end + + it 'shows a success flash message' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + expect(flash[:success]).to be + end + + it 'renders spree/admin/shared/destroy' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + expect(response).to render_template('spree/admin/shared/_destroy') + end + + it 'refreshes the cache' do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).with(variant) + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + end + + it 'destroys all its exchanges' do + exchange = create(:exchange) + variant.exchanges << exchange + + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + expect(variant.exchanges).to be_empty + end + end + + context 'when requesting with html' do + before do + allow(Spree::Variant).to receive(:find).with(variant.id.to_s) { variant } + allow(variant).to receive(:destroy).and_call_original + end + + it 'deletes the variant' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'html' + expect(variant).to have_received(:destroy) + end + + it 'shows a success flash message' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'html' + expect(flash[:success]).to be + end + + it 'redirects to admin_product_variants_url' do + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'html' + expect(response).to redirect_to( + controller: 'spree/admin/variants', + action: :index, + product_id: variant.product.permalink + ) + end + + it 'refreshes the cache' do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).with(variant) + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + end + + it 'destroys all its exchanges' do + exchange = create(:exchange) + variant.exchanges << exchange + + spree_delete :destroy, id: variant.id, product_id: variant.product.permalink, format: 'js' + expect(variant.exchanges).to be_empty + end + end + end end end end diff --git a/spec/lib/open_food_network/order_and_distributor_report_spec.rb b/spec/lib/open_food_network/order_and_distributor_report_spec.rb index f943aabdd0e..e2d00b14378 100644 --- a/spec/lib/open_food_network/order_and_distributor_report_spec.rb +++ b/spec/lib/open_food_network/order_and_distributor_report_spec.rb @@ -11,13 +11,14 @@ module OpenFoodNetwork 'Customer Name', 'Customer Email', 'Customer Phone', 'Customer City', 'SKU', 'Item name', 'Variant', 'Quantity', 'Max Quantity', 'Cost', 'Shipping Cost', 'Payment Method', - 'Distributor', 'Distributor address', 'Distributor city', 'Distributor postcode', 'Shipping instructions']) + 'Distributor', 'Distributor address', 'Distributor city', 'Distributor postcode', 'Shipping Method', 'Shipping instructions']) end context 'with completed order' do let(:bill_address) { create(:address) } let(:distributor) { create(:distributor_enterprise) } let(:product) { create(:product) } + let(:shipping_method) { create(:shipping_method) } let(:shipping_instructions) { 'pick up on thursday please!' } let(:order) { create(:order, state: 'complete', completed_at: Time.zone.now, distributor: distributor, bill_address: bill_address, special_instructions: shipping_instructions) } let(:payment_method) { create(:payment_method, distributors: [distributor]) } @@ -25,6 +26,7 @@ module OpenFoodNetwork let(:line_item) { create(:line_item_with_shipment, product: product, order: order) } before do + order.select_shipping_method(shipping_method.id) order.payments << payment order.line_items << line_item end @@ -53,6 +55,7 @@ module OpenFoodNetwork distributor.address.address1, distributor.address.city, distributor.address.zipcode, + shipping_method.name, shipping_instructions ]) end diff --git a/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb index 12df9373b17..35fa8accc69 100644 --- a/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb +++ b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb @@ -1,116 +1,246 @@ require 'spec_helper' -include AuthenticationWorkflow - -module OpenFoodNetwork - describe OrdersAndFulfillmentsReport do - describe "fetching orders" do - let(:d1) { create(:distributor_enterprise) } - let(:oc1) { create(:simple_order_cycle) } - let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } - let(:li1) { build(:line_item_with_shipment) } - - before { o1.line_items << li1 } - - context "as a site admin" do - let(:user) { create(:admin_user) } - subject { PackingReport.new user, {}, true } - - it "fetches completed orders" do - o2 = create(:order) - o2.line_items << build(:line_item) - subject.table_items.should == [li1] - end - - it "does not show cancelled orders" do - o2 = create(:order, state: "canceled", completed_at: 1.day.ago) - o2.line_items << build(:line_item_with_shipment) - subject.table_items.should == [li1] - end +describe OpenFoodNetwork::OrdersAndFulfillmentsReport do + include AuthenticationWorkflow + + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle) } + let(:address) { create(:address) } + let(:order) { + create( + :order, + completed_at: 1.day.ago, + order_cycle: order_cycle, + distributor: distributor, + bill_address: address + ) + } + let(:line_item) { build(:line_item_with_shipment) } + let(:user) { create(:user) } + let(:admin_user) { create(:admin_user) } + + describe "fetching orders" do + before { order.line_items << line_item } + + context "as a site admin" do + subject { described_class.new admin_user, {}, true } + + it "fetches completed orders" do + o2 = create(:order) + o2.line_items << build(:line_item) + expect(subject.table_items).to eq([line_item]) end - context "as a manager of a supplier" do - let!(:user) { create(:user) } - subject { OrdersAndFulfillmentsReport.new user, {}, true } + it "does not show cancelled orders" do + o2 = create(:order, state: "canceled", completed_at: 1.day.ago) + o2.line_items << build(:line_item_with_shipment) + expect(subject.table_items).to eq([line_item]) + end + end - let(:s1) { create(:supplier_enterprise) } + context "as a manager of a supplier" do + subject { described_class.new user, {}, true } - before do - s1.enterprise_roles.create!(user: user) - end + let(:s1) { create(:supplier_enterprise) } - context "that has granted P-OC to the distributor" do - let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } - let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) } + before do + s1.enterprise_roles.create!(user: user) + end - before do - o2.line_items << li2 - create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) - end + context "that has granted P-OC to the distributor" do + let(:o2) { + create( + :order, + distributor: distributor, + completed_at: 1.day.ago, + bill_address: create(:address), + ship_address: create(:address) + ) + } + let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) } - it "shows line items supplied by my producers, with names hidden" do - subject.table_items.should == [li2] - subject.table_items.first.order.bill_address.firstname.should == "HIDDEN" - end + before do + o2.line_items << li2 + create( + :enterprise_relationship, + parent: s1, + child: distributor, + permissions_list: [:add_to_order_cycle] + ) end - context "that has not granted P-OC to the distributor" do - let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } - let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) } - - before do - o2.line_items << li2 - end - - it "shows line items supplied by my producers, with names hidden" do - subject.table_items.should == [] - end + it "shows line items supplied by my producers, with names hidden" do + expect(subject.table_items).to eq([li2]) + expect(subject.table_items.first.order.bill_address.firstname).to eq("HIDDEN") end end - context "as a manager of a distributor" do - let!(:user) { create(:user) } - subject { OrdersAndFulfillmentsReport.new user, {}, true } + context "that has not granted P-OC to the distributor" do + let(:o2) { + create( + :order, + distributor: distributor, + completed_at: 1.day.ago, + bill_address: create(:address), + ship_address: create(:address) + ) + } + let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) } before do - d1.enterprise_roles.create!(user: user) + o2.line_items << li2 end - it "only shows line items distributed by enterprises managed by the current user" do - d2 = create(:distributor_enterprise) - d2.enterprise_roles.create!(user: create(:user)) - o2 = create(:order, distributor: d2, completed_at: 1.day.ago) - o2.line_items << build(:line_item_with_shipment) - subject.table_items.should == [li1] + it "shows line items supplied by my producers, with names hidden" do + expect(subject.table_items).to eq([]) end + end + end - it "only shows the selected order cycle" do - oc2 = create(:simple_order_cycle) - o2 = create(:order, distributor: d1, order_cycle: oc2) - o2.line_items << build(:line_item) - subject.stub(:params).and_return(order_cycle_id_in: oc1.id) - subject.table_items.should == [li1] - end + context "as a manager of a distributor" do + subject { described_class.new user, {}, true } + + before do + distributor.enterprise_roles.create!(user: user) + end + + it "only shows line items distributed by enterprises managed by the current user" do + d2 = create(:distributor_enterprise) + d2.enterprise_roles.create!(user: create(:user)) + o2 = create(:order, distributor: d2, completed_at: 1.day.ago) + o2.line_items << build(:line_item_with_shipment) + expect(subject.table_items).to eq([line_item]) + end + + it "only shows the selected order cycle" do + oc2 = create(:simple_order_cycle) + o2 = create(:order, distributor: distributor, order_cycle: oc2) + o2.line_items << build(:line_item) + allow(subject).to receive(:params).and_return(order_cycle_id_in: order_cycle.id) + expect(subject.table_items).to eq([line_item]) end end + end - describe "columns are aligned" do - let(:d1) { create(:distributor_enterprise) } - let(:oc1) { create(:simple_order_cycle) } - let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } - let(:li1) { build(:line_item_with_shipment) } - let(:user) { create(:admin_user)} + describe "columns are aligned" do + it 'has aligned columsn' do + report_types = [ + "", + "order_cycle_supplier_totals", + "order_cycle_supplier_totals_by_distributor", + "order_cycle_distributor_totals_by_supplier", + "order_cycle_customer_totals" + ] + + report_types.each do |report_type| + report = described_class.new admin_user, report_type: report_type + expect(report.header.size).to eq(report.columns.size) + end + end + end - before { o1.line_items << li1 } + describe "order_cycle_customer_totals" do + let!(:product) { line_item.product } + let!(:fuji) do + create(:variant, product: product, display_name: "Fuji", sku: "FUJI", on_hand: 100) + end + let!(:gala) do + create(:variant, product: product, display_name: "Gala", sku: "GALA", on_hand: 100) + end - it 'has aligned columsn' do - report_types = ["", "order_cycle_supplier_totals", "order_cycle_supplier_totals_by_distributor", "order_cycle_distributor_totals_by_supplier", "order_cycle_customer_totals"] + let(:items) { + report = described_class.new(admin_user, { report_type: "order_cycle_customer_totals" }, true) + OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items) + } - report_types.each do |report_type| - report = OrdersAndFulfillmentsReport.new user, report_type: report_type - report.header.size.should == report.columns.size - end + before do + # Clear price so it will be computed based on quantity and variant price. + order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil, quantity: 1) + order.line_items << build(:line_item_with_shipment, variant: gala, price: nil, quantity: 2) + end + + it "has a product row" do + product_name_field = items.first[5] + expect(product_name_field).to eq product.name + end + + it "has a summary row" do + product_name_field = items.last[5] + expect(product_name_field).to eq "TOTAL" + end + + # Expected Report for Scenario: + # + # Row 1: Armstrong Amari, Fuji Apple, price: 8 + # Row 2: SUMMARY + # Row 3: Bartoletti Brooklyn, Fuji Apple, price: 1 + 4 + # Row 4: Bartoletti Brooklyn, Gala Apple, price: 2 + # Row 5: SUMMARY + describe "grouping of line items" do + let!(:address) { create(:address, last_name: "Bartoletti", first_name: "Brooklyn") } + + let!(:second_address) { create(:address, last_name: "Armstrong", first_name: "Amari") } + let!(:second_order) do + create(:order, completed_at: 1.day.ago, order_cycle: order_cycle, distributor: distributor, + bill_address: second_address) + end + + before do + # Add a second line item for Fuji variant to the order, to test grouping in this edge case. + order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil, quantity: 4) + + second_order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil, quantity: 8) + end + + it "groups line items by variant and order" do + expect(items.length).to eq(5) + + # Row 1: Armstrong Amari, Fuji Apple, price: 8 + row_data = items[0] + expect(customer_name(row_data)).to eq(second_address.full_name) + expect(amount(row_data)).to eq(fuji.price * 8) + expect(variant_sku(row_data)).to eq(fuji.sku) + + # Row 2: SUMMARY + row_data = items[1] + expect(totals_row?(row_data)).to eq(true) + expect(customer_name(row_data)).to eq(second_address.full_name) + expect(amount(row_data)).to eq(fuji.price * 8) + + # Row 3: Bartoletti Brooklyn, Fuji Apple, price: 1 + 4 + row_data = items[2] + expect(customer_name(row_data)).to eq(address.full_name) + expect(amount(row_data)).to eq(fuji.price * 5) + expect(variant_sku(row_data)).to eq(fuji.sku) + + # Row 4: Bartoletti Brooklyn, Gala Apple, price: 2 + row_data = items[3] + expect(customer_name(row_data)).to eq(address.full_name) + expect(amount(row_data)).to eq(gala.price * 2) + expect(variant_sku(row_data)).to eq(gala.sku) + + # Row 5: SUMMARY + row_data = items[4] + expect(totals_row?(row_data)).to eq(true) + expect(customer_name(row_data)).to eq(address.full_name) + expect(amount(row_data)).to eq(fuji.price * 5 + gala.price * 2) end end + + def totals_row?(row_data) + row_data[5] == I18n.t("admin.reports.total") + end + + def customer_name(row_data) + row_data[1] + end + + def amount(row_data) + row_data[8] + end + + def variant_sku(row_data) + row_data[23] + end end end diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb index 1235c9e0018..b70d542d154 100644 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ b/spec/lib/open_food_network/products_and_inventory_report_spec.rb @@ -134,6 +134,32 @@ module OpenFoodNetwork subject.stub(:params).and_return(distributor_id: distributor.id) subject.filter(variants).should == [product2.variants.first] end + + it "ignores variant overrides without filter" do + distributor = create(:distributor_enterprise) + product = create(:simple_product, supplier: supplier, price: 5) + variant = product.variants.first + order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product.variants.first]) + create(:variant_override, hub: distributor, variant: variant, price: 2) + + result = subject.filter(variants) + + expect(result.first.price).to eq 5 + end + + it "considers variant overrides with distributor" do + distributor = create(:distributor_enterprise) + product = create(:simple_product, supplier: supplier, price: 5) + variant = product.variants.first + order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product.variants.first]) + create(:variant_override, hub: distributor, variant: variant, price: 2) + + allow(subject).to receive(:params).and_return(distributor_id: distributor.id) + result = subject.filter(variants) + + expect(result.first.price).to eq 2 + end + it "filters to a specific order cycle" do distributor = create(:distributor_enterprise) product1 = create(:simple_product, supplier: supplier) diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index 470d9d56823..1265be94fe2 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -52,7 +52,7 @@ csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "2", "kg", "", "", shipping_category_id_str] csv << ["Pea Soup", "User Enterprise", "Vegetables", "8", "5.50", "750", "ml", "", "0", shipping_category_id_str] csv << ["Salad", "User Enterprise", "Vegetables", "7", "4.50", "1", "", "bags", "", shipping_category_id_str] - csv << ["Hot Cross Buns", "User Enterprise", "Cake", "7", "3.50", "1", "", "buns", "1", shipping_category_id_str] + csv << ["Hot Cross Buns", "User Enterprise", "Cake", nil, "3.50", "1", "", "buns", "1", shipping_category_id_str] end File.write('/tmp/test-m.csv', csv_data) file = File.new('/tmp/test-m.csv') @@ -124,7 +124,7 @@ buns = Spree::Product.find_by_name('Hot Cross Buns') expect(buns.supplier).to eq enterprise - # buns.on_hand).to eq Infinity + expect(buns.on_hand).to eq 0 expect(buns.price).to eq 3.50 expect(buns.unit_value).to eq 1 expect(buns.variant_unit).to eq 'items' @@ -249,7 +249,7 @@ csv_data = CSV.generate do |csv| csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name", "shipping_category_id"] csv << ["Potatoes", "User Enterprise", "Vegetables", "5", "3.50", "500", "g", "Small Bag", shipping_category_id_str] - csv << ["Chives", "User Enterprise", "Vegetables", "6", "4.50", "500", "g", "Small Bag", shipping_category_id_str] + csv << ["Chives", "User Enterprise", "Vegetables", "6", "4.50", "500", "g", "Bag of Chives", shipping_category_id_str] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag", shipping_category_id_str] end File.write('/tmp/test-m.csv', csv_data) diff --git a/spec/models/spree/addresses_spec.rb b/spec/models/spree/addresses_spec.rb index 5d78cadcf2b..f895db91c70 100644 --- a/spec/models/spree/addresses_spec.rb +++ b/spec/models/spree/addresses_spec.rb @@ -24,6 +24,26 @@ end end + describe "#full_name_reverse" do + it "joins last name and first name" do + address.firstname = "Jane" + address.lastname = "Doe" + expect(address.full_name_reverse).to eq("Doe Jane") + end + + it "is last name when first name is blank" do + address.firstname = "" + address.lastname = "Doe" + expect(address.full_name_reverse).to eq("Doe") + end + + it "is first name when last name is blank" do + address.firstname = "Jane" + address.lastname = "" + expect(address.full_name_reverse).to eq("Jane") + end + end + describe "geocode address" do it "should include address1, address2, zipcode, city, state and country" do expect(address.geocode_address).to include(address.address1) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 7be1becbbd8..b5643e01091 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -188,7 +188,6 @@ module Spree end it "refreshes the products cache for the entire product on destroy" do - # Does this ever happen? expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).never master.destroy