diff --git a/Gemfile.lock b/Gemfile.lock index 6ca2073502..3a1421aaf4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -598,17 +598,17 @@ GEM rspec-retry (0.6.2) rspec-core (> 3.3) rspec-support (3.10.1) - rswag (2.3.2) - rswag-api (= 2.3.2) - rswag-specs (= 2.3.2) - rswag-ui (= 2.3.2) - rswag-api (2.3.2) + rswag (2.3.3) + rswag-api (= 2.3.3) + rswag-specs (= 2.3.3) + rswag-ui (= 2.3.3) + rswag-api (2.3.3) railties (>= 3.1, < 7.0) - rswag-specs (2.3.2) + rswag-specs (2.3.3) activesupport (>= 3.1, < 7.0) json-schema (~> 2.2) railties (>= 3.1, < 7.0) - rswag-ui (2.3.2) + rswag-ui (2.3.3) actionpack (>= 3.1, < 7.0) railties (>= 3.1, < 7.0) rubocop (1.9.1) @@ -672,7 +672,7 @@ GEM activerecord (>= 4.1) state_machines-activemodel (>= 0.5.0) stringex (2.8.5) - stripe (5.29.0) + stripe (5.29.1) temple (0.8.2) test-prof (0.11.3) test-unit (3.4.0) @@ -682,7 +682,7 @@ GEM thor (0.20.3) thread_safe (0.3.6) tilt (1.4.1) - timecop (0.9.2) + timecop (0.9.4) tzinfo (1.2.9) thread_safe (~> 0.1) uglifier (4.2.0) diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index 76ecd388d9..453f05a2c3 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -13,7 +13,7 @@ window.Darkswarm = angular.module("Darkswarm", [ 'angularSlideables' ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) -> $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest' - $httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*" + $httpProvider.defaults.headers.common['Accept'] = "application/json, text/javascript, */*" # We manually handle our scrolling $anchorScrollProvider.disableAutoScrolling() diff --git a/app/assets/javascripts/templates/bulk_buy_modal.html.haml b/app/assets/javascripts/templates/bulk_buy_modal.html.haml index 5e26551419..05868dab77 100644 --- a/app/assets/javascripts/templates/bulk_buy_modal.html.haml +++ b/app/assets/javascripts/templates/bulk_buy_modal.html.haml @@ -16,10 +16,7 @@ %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}> -# U+FF0D Fullwidth Hyphen-Minus - - %input.bulk-buy.variant-quantity{ - type: "number", - min: "0", - max: "{{ available() }}", + %input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}", ng: {model: "variant.line_item.quantity", max: "Infinity"}}> %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}} -# U+FF0B Fullwidth Plus Sign @@ -31,10 +28,7 @@ %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(-1)", disabled: "!canAddMax(-1)"}}> -# U+FF0D Fullwidth Hyphen-Minus - - %input.bulk-buy.variant-quantity{ - type: "number", - min: "0", - max: "{{ available() }}", + %input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}", ng: {model: "variant.line_item.max_quantity", max: "Infinity"}}> %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(1)", disabled: "!canAddMax(1)"}} -# U+FF0B Fullwidth Plus Sign diff --git a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml index ad8abdf747..2157f548c7 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml @@ -8,12 +8,8 @@ %button.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}> -# U+FF0D Fullwidth Hyphen-Minus - - %input.variant-quantity{ - type: "number", - min: "0", - max: "{{ available() }}", - ng: {model: "variant.line_item.quantity", max: "Infinity"} - }> + %input.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", + ng: {model: "variant.line_item.quantity", max: "Infinity"}}> %button.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}} -# U+FF0B Fullwidth Plus Sign + diff --git a/app/controllers/spree/paypal_controller.rb b/app/controllers/spree/paypal_controller.rb index 5ca339f52f..f5ef6872e6 100644 --- a/app/controllers/spree/paypal_controller.rb +++ b/app/controllers/spree/paypal_controller.rb @@ -15,9 +15,7 @@ module Spree order = current_order || raise(ActiveRecord::RecordNotFound) items = order.line_items.map(&method(:line_item)) - tax_adjustments = order.adjustments.tax - # TODO: Remove in Spree 2.2 - tax_adjustments = tax_adjustments.additional if tax_adjustments.respond_to?(:additional) + tax_adjustments = order.adjustments.tax.additional shipping_adjustments = order.adjustments.shipping order.adjustments.eligible.each do |adjustment| @@ -175,12 +173,8 @@ module Spree def payment_details(items) item_sum = items.sum { |i| i[:Quantity] * i[:Amount][:value] } - # Would use tax_total here, but it can include "included" taxes as well. - # For instance, tax_total would include the 10% GST in Australian stores. - # A quick sum will get us around that little problem. - # TODO: Remove additional check in 2.2 - tax_adjustments = current_order.adjustments.tax - tax_adjustments = tax_adjustments.additional if tax_adjustments.respond_to?(:additional) + + tax_adjustments = current_order.adjustments.tax.additional tax_adjustments_total = tax_adjustments.sum(:amount) if item_sum.zero? diff --git a/app/models/spree/adjustment.rb b/app/models/spree/adjustment.rb index f256ab108e..3ffd9d8cac 100644 --- a/app/models/spree/adjustment.rb +++ b/app/models/spree/adjustment.rb @@ -64,12 +64,14 @@ module Spree end end - scope :tax, -> { where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::Order') } + scope :tax, -> { where(originator_type: 'Spree::TaxRate') } scope :price, -> { where(adjustable_type: 'Spree::LineItem') } scope :optional, -> { where(mandatory: false) } scope :charge, -> { where('amount >= 0') } scope :credit, -> { where('amount < 0') } scope :return_authorization, -> { where(source_type: "Spree::ReturnAuthorization") } + scope :inclusive, -> { where(included: true) } + scope :additional, -> { where(included: false) } scope :enterprise_fee, -> { where(originator_type: 'EnterpriseFee') } scope :admin, -> { where(source_type: nil, originator_type: nil) } diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index 784c9342d0..64278ed400 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -47,6 +47,7 @@ module Spree dependent: :destroy has_many :line_item_adjustments, through: :line_items, source: :adjustments + has_many :all_adjustments, class_name: 'Spree::Adjustment', dependent: :destroy has_many :shipments, dependent: :destroy do def states diff --git a/app/models/spree/shipment.rb b/app/models/spree/shipment.rb index 67d8e5e935..239430e07e 100644 --- a/app/models/spree/shipment.rb +++ b/app/models/spree/shipment.rb @@ -164,14 +164,6 @@ module Spree Spree::Money.new(item_cost, currency: currency) end - def total_cost - cost + item_cost - end - - def display_total_cost - Spree::Money.new(total_cost, currency: currency) - end - def editable_by?(_user) !shipped? end diff --git a/app/models/spree/tax_rate.rb b/app/models/spree/tax_rate.rb index eeee5673aa..0ea98c9cea 100644 --- a/app/models/spree/tax_rate.rb +++ b/app/models/spree/tax_rate.rb @@ -61,7 +61,7 @@ module Spree def adjust(order) label = create_label if included_in_price - if Zone.default_tax.contains? order.tax_zone + if default_zone_or_zone_match? order order.line_items.each { |line_item| create_adjustment(label, line_item, line_item) } else amount = -1 * calculator.compute(order) @@ -89,6 +89,10 @@ module Spree end end + def default_zone_or_zone_match?(order) + Zone.default_tax.contains?(order.tax_zone) || order.tax_zone == zone + end + # Manually apply a TaxRate to a particular amount. TaxRates normally compute against # LineItems or Orders, so we mock out a line item here to fit the interface # that our calculator (usually DefaultTax) expects. @@ -114,6 +118,8 @@ module Spree label = "" label << (name.presence || tax_category.name) + " " label << (show_rate_in_label? ? "#{amount * 100}%" : "") + label << " (#{I18n.t('models.tax_rate.included_in_price')})" if included_in_price? + label end def with_tax_included_in_price diff --git a/app/views/admin/subscriptions/_review.html.haml b/app/views/admin/subscriptions/_review.html.haml index 0bcaf2eab2..f7b716684a 100644 --- a/app/views/admin/subscriptions/_review.html.haml +++ b/app/views/admin/subscriptions/_review.html.haml @@ -3,47 +3,47 @@ .row .eight.columns.alpha .row - .six.columns.alpha + .five.columns.alpha %h3= t('.details') - .two.columns.omega.text-right + .eleven.columns.omega %input#edit-details{ type: "button", value: t(:edit), ng: { click: "setView('details')" } } .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.customer') - .five.columns.omega {{ subscription.customer().email }} + .eleven.columns.omega {{ subscription.customer().email }} .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.schedule') - .five.columns.omega {{ subscription.schedule().name }} + .eleven.columns.omega {{ subscription.schedule().name }} .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.payment_method') - .five.columns.omega {{ subscription.paymentMethod().name }} + .eleven.columns.omega {{ subscription.paymentMethod().name }} .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.shipping_method') - .five.columns.omega {{ subscription.shippingMethod().name }} + .eleven.columns.omega {{ subscription.shippingMethod().name }} .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.begins_at') - .five.columns.omega {{ subscription.begins_at }} + .eleven.columns.omega {{ subscription.begins_at }} .row.margin-bottom-30 - .three.columns.alpha + .five.columns.alpha %strong= t('admin.ends_at') - .five.columns.omega {{ subscription.ends_at || ('ongoing' | t) }} + .eleven.columns.omega {{ subscription.ends_at || ('ongoing' | t) }} .row - .six.columns.alpha + .five.columns.alpha %h3= t('.address') - .two.columns.omega.text-right + .eleven.columns.omega %input#edit-address{ type: "button", value: t(:edit), ng: { click: "setView('address')" } } .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.bill_address') - .five.columns.omega {{ formatAddress(subscription.bill_address) }} + .eleven.columns.omega {{ formatAddress(subscription.bill_address) }} .row - .three.columns.alpha + .five.columns.alpha %strong= t('admin.ship_address') - .five.columns.omega {{ formatAddress(subscription.ship_address) }} + .eleven.columns.omega {{ formatAddress(subscription.ship_address) }} .one.column   @@ -52,47 +52,46 @@ .row .five.columns.alpha %h3= t('.products') - .two.columns.omega.text-right + .eleven.columns.omega %input#edit-products{ type: "button", value: t(:edit), ng: { click: "setView('products')" } } .row - .seven.columns.alpha.omega - %table#subscription-line-items.admin-subscription-review-subscription-line-items - %colgroup - %col{:style => "width: 62%;"}/ - %col{:style => "width: 14%;"}/ - %col{:style => "width: 10%;"}/ - %col{:style => "width: 14%;"}/ - %thead - %tr - %th= t(:item_description) - %th.price= t(:price) - %th.quantity= t(:qty) - %th.total - %span= t(:total) - %tbody - %tr.item{ id: "sli_{{$index}}", ng: { repeat: "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", class: { even: 'even', odd: 'odd' } } } - %td - .description {{ item.description }} - .not-in-open-and-upcoming-order-cycles-warning{ ng: { if: '!item.in_open_and_upcoming_order_cycles' } } - = t(".no_open_or_upcoming_order_cycle") - %td.price.align-center {{ item.price_estimate | localizeCurrency }} - %td.quantity {{ item.quantity }} - %td.total.align-center {{ (item.price_estimate * item.quantity) | localizeCurrency }} - %tbody#subtotal.no-border-top{"data-hook" => "admin_order_form_subtotal"} - %tr#subtotal-row - %td{:colspan => "3"} - %b - = t(:subtotal) - \: - %td.total.align-center - %span {{ subscription.estimatedSubtotal() | localizeCurrency }} - %tbody#order-total.grand-total.no-border-top{"data-hook" => "admin_order_form_total"} - %tr - %td{:colspan => "3"} - %b - = t(:order_total_price) - \: - %td.total.align-center - %span#order_form_total {{ subscription.estimatedTotal() | localizeCurrency }} - %p.notice - = t "this_is_an_estimate", scope: 'admin.subscriptions.subscription_line_items' + %table#subscription-line-items.admin-subscription-review-subscription-line-items + %colgroup + %col{:style => "width: 62%;"}/ + %col{:style => "width: 14%;"}/ + %col{:style => "width: 10%;"}/ + %col{:style => "width: 14%;"}/ + %thead + %tr + %th= t(:item_description) + %th.price= t(:price) + %th.quantity= t(:qty) + %th.total + %span= t(:total) + %tbody + %tr.item{ id: "sli_{{$index}}", ng: { repeat: "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", class: { even: 'even', odd: 'odd' } } } + %td + .description {{ item.description }} + .not-in-open-and-upcoming-order-cycles-warning{ ng: { if: '!item.in_open_and_upcoming_order_cycles' } } + = t(".no_open_or_upcoming_order_cycle") + %td.price.align-center {{ item.price_estimate | localizeCurrency }} + %td.quantity {{ item.quantity }} + %td.total.align-center {{ (item.price_estimate * item.quantity) | localizeCurrency }} + %tbody#subtotal.no-border-top{"data-hook" => "admin_order_form_subtotal"} + %tr#subtotal-row + %td{:colspan => "3"} + %b + = t(:subtotal) + \: + %td.total.align-center + %span {{ subscription.estimatedSubtotal() | localizeCurrency }} + %tbody#order-total.grand-total.no-border-top{"data-hook" => "admin_order_form_total"} + %tr + %td{:colspan => "3"} + %b + = t(:order_total_price) + \: + %td.total.align-center + %span#order_form_total {{ subscription.estimatedTotal() | localizeCurrency }} + %p.notice + = t "this_is_an_estimate", scope: 'admin.subscriptions.subscription_line_items' diff --git a/config/initializers/feature_toggles.rb b/config/initializers/feature_toggles.rb index 13d083979c..9c31940462 100644 --- a/config/initializers/feature_toggles.rb +++ b/config/initializers/feature_toggles.rb @@ -1,5 +1,11 @@ require 'open_food_network/feature_toggle' -beta_testers = ENV['BETA_TESTERS']&.split(/[\s,]+/) +beta_testers = ENV['BETA_TESTERS']&.split(/[\s,]+/) || [] -OpenFoodNetwork::FeatureToggle.enable(:customer_balance, beta_testers) +OpenFoodNetwork::FeatureToggle.enable(:customer_balance) do |user| + if beta_testers == ['all'] + true + else + beta_testers.include?(user.email) + end +end diff --git a/config/locales/ca.yml b/config/locales/ca.yml index ed961ce0e5..bf51285cc4 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -117,6 +117,8 @@ ca: models: order_cycle: cloned_order_cycle_name: "CÒPIA DE %{order_cycle}" + tax_rate: + included_in_price: "Inclòs en el preu" validators: date_time_string_validator: not_string_error: "ha de ser una seqüència" diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 0adcac5365..8b970201b6 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -117,6 +117,8 @@ de_DE: models: order_cycle: cloned_order_cycle_name: "Kopie von %{order_cycle}" + tax_rate: + included_in_price: "im Preis inbegriffen" validators: date_time_string_validator: not_string_error: "muss eine Zeichenfolge sein" @@ -192,7 +194,7 @@ de_DE: explainer: Die automatische Verarbeitung dieser Bestellungen ist aus einem unbekannten Grund fehlgeschlagen. Dies sollte nicht geschehen, bitte kontaktieren Sie uns, wenn Sie dies sehen. home: "OFN" title: "Open Food Network" - welcome_to: "Willkommen bei" + welcome_to: "Willkommen beim" site_meta_description: "Wir wagen den Neustart. Mit Bauern und Züchtern, die bereit sind, ihre Geschichten stolz und wahrhaftig zu erzählen. Mit Händlern, die bereit sind, Menschen fair und ehrlich mit Produkten zu verbinden. Mit Käufern, die glauben, dass ihr Einkaufsverhalten die Welt wirklich verändern kann." search_by_name: Suche nach Ort oder Name des Ladens/Produzents... producers_join: 'Wir laden Produzenten ein, jetzt dem Open Food Network beizutreten. ' @@ -1263,7 +1265,7 @@ de_DE: ticket_column_item: "Artikel" ticket_column_unit_price: "Stückpreis" ticket_column_total_price: "Gesamtpreis" - menu_1_title: "Läden" + menu_1_title: "Einkaufen" menu_1_url: "/shops" menu_2_title: "Karte" menu_2_url: "/map" @@ -1271,12 +1273,12 @@ de_DE: menu_3_url: "/producers" menu_4_title: "Gruppen" menu_4_url: "/groups" - menu_5_title: "Über uns" - menu_5_url: "https://wp.openfoodnetwork.de/" - menu_6_title: "Support" - menu_6_url: "https://wp.openfoodnetwork.de/support/" - menu_7_title: "Mehr erfahren" - menu_7_url: "https://openfoodnetwork.org/au/learn/" + menu_5_title: "Verkaufen" + menu_5_url: "https://openfoodnetwork.de/sell" + menu_6_title: "Über uns" + menu_6_url: "https://wp.openfoodnetwork.de/" + menu_7_title: "Hilfe" + menu_7_url: "https://wp.openfoodnetwork.de/support/" logo: "Logo (640x130)" logo_mobile: "Mobile Logo (75x26)" logo_mobile_svg: "Mobile Logo (SVG)" @@ -1407,7 +1409,7 @@ de_DE: cookies_policy_link_desc: "Wenn Sie mehr erfahren möchten, besuchen Sie unsere" cookies_policy_link: "Hinweise zu Cookies" cookies_accept_button: "Cookies akzeptieren" - home_shop: Jetzt einkaufen + home_shop: Jetzt regional einkaufen brandstory_headline: "Lebensmittel Direktvermarktung" brandstory_intro: "Manchmal ist der beste Weg, das System zu reparieren, einen Neuanfang zu wagen ..." brandstory_part1: "Wir wagen den Neuanfang. Mit Bauern und Züchtern, die bereit sind, ihre Geschichten stolz und wahrhaftig zu erzählen. Mit Händlern, die bereit sind, Menschen fair und ehrlich mit Produkten zu verbinden. Mit Käufern, die glauben, dass ihr Einkaufsverhalten die Welt wirklich verändern kann." @@ -2960,6 +2962,7 @@ de_DE: display_currency: "Währung anzeigen" choose_currency: "Währung auswählen" mail_method_settings: "E-Mail-Methodeneinstellungen" + mail_settings_notice_html: "Einige der folgenden Einstellungen können nicht geändert werden und werden hier nur zu Zwecken der Fehlerbehebung aufgeführt. Änderungen können vorgenommen werden, indem die Einstellungen der deutschen Instanz aktualisiert und mithilfe von ofn-install bereitgestellt werden. Wenden Sie sich an das globale OFN-Team, um weitere Informationen zu erhalten." general: "Allgemeines" enable_mail_delivery: "E-Mail-Versand aktivieren" send_mails_as: "E-Mails senden als" diff --git a/config/locales/en.yml b/config/locales/en.yml index 6cb2b831c8..ba57144201 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -140,6 +140,8 @@ en: models: order_cycle: cloned_order_cycle_name: "COPY OF %{order_cycle}" + tax_rate: + included_in_price: "Included in price" validators: date_time_string_validator: diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index 1e12e3931f..5af9f594e3 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -2950,6 +2950,7 @@ en_CA: display_currency: "Display currency" choose_currency: "Choose Currency" mail_method_settings: "Mail Method Settings" + mail_settings_notice_html: "Some of the following settings can't be edited and are listed here just for debugging purposes. Changes can be made by updating the instance's secrets and provisioning them using ofn-install. Reach out to the OFN global team for further details." general: "General" enable_mail_delivery: "Enable Mail Delivery" send_mails_as: "Send Mails As" @@ -3466,6 +3467,8 @@ en_CA: cancel_email_for_shop: greeting: "Dear %{name}," subject: "Cancellation of Order" + intro: "A customer has cancelled ther order #%{number}." + view_cancelled_order: "View cancelled order" confirm_email: subject: "Order Confirmation" invoice_email: diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index 480eb47e27..262b3b380a 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -117,6 +117,8 @@ en_FR: models: order_cycle: cloned_order_cycle_name: "COPY OF %{order_cycle}" + tax_rate: + included_in_price: "Included in price" validators: date_time_string_validator: not_string_error: "must be a string" diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index 0768294f71..1a51d91705 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -117,6 +117,8 @@ en_GB: models: order_cycle: cloned_order_cycle_name: "COPY OF %{order_cycle}" + tax_rate: + included_in_price: "Included in price" validators: date_time_string_validator: not_string_error: "must be a string" @@ -2957,6 +2959,7 @@ en_GB: display_currency: "Display currency" choose_currency: "Choose Currency" mail_method_settings: "Mail Method Settings" + mail_settings_notice_html: "Some of the following settings can't be edited and are listed here just for debugging purposes. Changes can be made by updating the instance's secrets and provisioning them using ofn-install. Reach out to the OFN global team for further details." general: "General" enable_mail_delivery: "Enable Mail Delivery" send_mails_as: "Send Mails As" @@ -3473,6 +3476,8 @@ en_GB: cancel_email_for_shop: greeting: "Dear %{name}," subject: "Cancellation of Order" + intro: "A customer has cancelled their order #%{number}." + view_cancelled_order: "View cancelled order" confirm_email: subject: "Order Confirmation" invoice_email: diff --git a/config/locales/en_IE.yml b/config/locales/en_IE.yml index fc80e2ce82..10da03f91a 100644 --- a/config/locales/en_IE.yml +++ b/config/locales/en_IE.yml @@ -289,11 +289,16 @@ en_IE: create_and_add_another: "Create and Add Another" create: "Create" cancel: "Cancel" + resume: "Resume" save: "Save" edit: "Edit" update: "Update" delete: "Delete" add: "Add" + cut: "Cut" + paste: "Paste" + destroy: "Destroy" + rename: "Rename" admin: begins_at: Begins At begins_on: Begins On @@ -352,6 +357,7 @@ en_IE: has_n_rules: "has %{num} rules" unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" unsaved_changes: "You have unsaved changes" + available_units: "Available Units" shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -411,6 +417,7 @@ en_IE: search_by_email: "Search by email/code..." guest_label: "Guest checkout" credit_owed: "Credit Owed" + balance_due: "Balance Due" destroy: has_associated_orders: "Delete failed: customer has associated orders with this shop" contents: @@ -599,6 +606,8 @@ en_IE: controls: back_to_my_inventory: Back to my inventory orders: + edit: + order_sure_want_to: Are you sure you want to %{event} this order? invoice_email_sent: 'Invoice email has been sent' order_email_resent: 'Order email has been resent' bulk_management: @@ -647,6 +656,7 @@ en_IE: invoice_text: Add customized text at the end of invoices terms_and_conditions: "Terms and Conditions" remove_terms_and_conditions: "Remove File" + uploaded_on: "uploaded on" contact: name: Name name_placeholder: eg. Amanda Plum @@ -755,6 +765,7 @@ en_IE: is displayed on your shop only when you have no active order cycles (ie. shop is closed). shopfront_category_ordering: "Shopfront Category Ordering" + shopfront_category_ordering_note: "(top to bottom)" open_date: "Open Date" close_date: "Close Date" social: @@ -1164,6 +1175,7 @@ en_IE: cart: "cart" message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open." terms_and_conditions: + message_html: "I agree to the seller's %{terms_and_conditions_link}." link_text: "Terms and Conditions" failed: "The checkout failed. Please let us know so that we can process your order." shops: @@ -1557,6 +1569,7 @@ en_IE: shopping_groups_part_of: "is part of:" shopping_producers_of_hub: "%{hub}'s producers:" enterprises_next_closing: "Next order closing" + enterprises_currently_open: "Orders are currently open" enterprises_ready_for: "Ready for" enterprises_choose: "Choose when you want your order:" maps_open: "Open" @@ -1578,6 +1591,7 @@ en_IE: hubs_distance: Closest to hubs_distance_filter: "Show me shops near %{location}" shop_changeable_orders_alert_html: + one: Your order with %{shop} / %{order} is open for review. You can make changes until %{oc_close}. few: You have %{count} orders with %{shop} currently open for review. You can make changes until %{oc_close}. many: You have %{count} orders with %{shop} currently open for review. You can make changes until %{oc_close}. other: You have %{count} orders with %{shop} currently open for review. You can make changes until %{oc_close}. @@ -1725,6 +1739,7 @@ en_IE: orders_could_not_cancel: "Sorry, the order could not be cancelled" orders_cannot_remove_the_final_item: "Cannot remove the final item from an order, please cancel the order instead." orders_bought_items_notice: + one: An additional item is already confirmed for this order cycle few: "%{count} additional items already confirmed for this order cycle" many: "%{count} additional items already confirmed for this order cycle" other: "%{count} additional items already confirmed for this order cycle" @@ -2071,6 +2086,7 @@ en_IE: spree_classification_primary_taxon_error: "Taxon %{taxon} is the primary taxon of %{product} and cannot be deleted" spree_order_availability_error: "Distributor or order cycle cannot supply the products in your cart" spree_order_populator_error: "That distributor or order cycle can't supply all the products in your cart. Please choose another." + spree_order_cycle_error: "Please choose an order cycle for this order." spree_order_populator_availability_error: "That product is not available from the chosen distributor or order cycle." spree_distributors_error: "At least one hub must be selected" spree_user_enterprise_limit_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})." @@ -2303,6 +2319,8 @@ en_IE: payment_processing_failed: "Payment could not be processed, please check the details you entered" payment_method_not_supported: "That payment method is unsupported. Please choose another one." payment_updated: "Payment Updated" + cannot_perform_operation: "Could not update the payment" + action_required: "Action required" inventory_settings: "Inventory Settings" tag_rules: "Tag Rules" shop_preferences: "Shop Preferences" @@ -2363,6 +2381,7 @@ en_IE: js: saving: 'Saving...' changes_saved: 'Changes saved.' + authorising: "Authorising..." save_changes_first: Save changes first. all_changes_saved: All changes saved unsaved_changes: You have unsaved changes @@ -2378,6 +2397,7 @@ en_IE: resolve_errors: Please resolve the following errors more_items: "+ %{count} More" default_card_updated: Default Card Updated + default_card_voids_auth: Changing your default card will remove shops' existing authorizations to charge it. You can re-authorize shops after updating the default card. Do you wish to change the default card?" cart: add_to_cart_failed: > There was a problem adding this product to the cart. Perhaps it has become @@ -2407,6 +2427,14 @@ en_IE: By creating rules related to a specific customer tag, you can override the default behaviour (whether it be to show or to hide items) for customers with the specified tag. + terms_and_conditions_info: + title: "Uploading Terms and Conditions" + message_1: "Terms and Conditions are the contract between you, the seller, and the shopper. If you upload a file here shoppers must accept your Terms and Conditions in order to complete checkout. For the shopper this will appear as a checkbox at checkout that must be checked in order to proceed with checkout. We highly recommend you upload Terms and Conditions in alignment with national legislation." + message_2: "Shoppers will only be required to accept Terms and Conditions once. However if you change you Terms and Conditions shoppers will again be required to accept them before they can checkout." + terms_and_conditions_warning: + title: "Uploading Terms and Conditions" + message_1: "All your buyers will have to agree to them once at checkout. If you update the file, all your buyers will have to agree to them again at checkout." + message_2: "For buyers with subscriptions, you need to email them the Terms and Conditions (or the changes to them) for now, nothing will notify them about these new Terms and Conditions." panels: save: SAVE saved: SAVED @@ -2567,6 +2595,8 @@ en_IE: processing: "processing" void: "void" invalid: "invalid" + quantity_adjusted: "Insufficient stock available. Line item updated to maximum available quantity." + quantity_unchanged: "Quantity unchanged from previous amount." resend_user_email_confirmation: resend: "Resend" sending: "Resend..." @@ -2871,6 +2901,8 @@ en_IE: delete: "Delete" cannot_set_shipping_method_without_address: "Cannot set shipping method until customer details are provided." no_tracking_present: "No tracking details provided." + tracking: "Tracking" + tracking_number: "Tracking Number" order_total: "Order Total" customer_details: "Customer Details" customer_search: "Customer Search" @@ -2904,6 +2936,8 @@ en_IE: server: "Server" test_mode: "Test Mode" logourl: "Logourl" + are_you_sure_delete: "Are you sure you want to delete this record?" + confirm_delete: "Confirm Deletion" configurations: "Configurations" general_settings: "General Settings" site_name: "Site Name" @@ -2923,6 +2957,7 @@ en_IE: display_currency: "Display currency" choose_currency: "Choose Currency" mail_method_settings: "Mail Method Settings" + mail_settings_notice_html: "Some of the following settings can't be edited and are listed here just for debugging purposes. Changes can be made by updating the instance's secrets and provisioning them using ofn-install. Reach out to the OFN global team for further details." general: "General" enable_mail_delivery: "Enable Mail Delivery" send_mails_as: "Send Mails As" @@ -3025,6 +3060,7 @@ en_IE: shared: error_messages: errors_prohibited_this_record_from_being_saved: + one: "1 error prohibited this record from being saved:" few: "%{count} errors prohibited this record from being saved:" many: "%{count} errors prohibited this record from being saved:" other: "%{count} errors prohibited this record from being saved:" @@ -3036,6 +3072,7 @@ en_IE: payment_state: "Payment County" errors: messages: + included_price_validation: "cannot be selected unless you have set a Default Tax Zone" blank: "can't be blank" layouts: admin: @@ -3246,6 +3283,13 @@ en_IE: deactivation_warning: "De-activating a payment method can make the payment method disappear from your list. Alternatively, you can hide a payment method from the checkout page by setting the option 'Display' to 'back office only'." providers: provider: "Provider" + check: "Cash/EFT/etc. (payments for which automatic validation is not required)" + pin: "Pin Payments" + paypalexpress: "PayPal Express" + stripeconnect: "Stripe" + stripesca: "Stripe SCA" + bogus: "Bogus" + bogussimple: "BogusSimple" payments: source_forms: stripe: @@ -3430,6 +3474,8 @@ en_IE: cancel_email_for_shop: greeting: "Dear %{name}," subject: "Cancellation of Order" + intro: "A customer has cancelled their order #%{number}." + view_cancelled_order: "View cancelled order" confirm_email: subject: "Order Confirmation" invoice_email: @@ -3448,6 +3494,10 @@ en_IE: subject: "Reset password instructions" confirmation_instructions: subject: "Please confirm your OFN account" + payment_mailer: + authorize_payment: + subject: "Please authorize your payment to %{distributor} on OFN" + instructions: "Your payment of %{amount} to %{distributor} requires additional authentication. Please visit the following URL to authorize your payment:" shipment_mailer: shipped_email: dear_customer: "Dear Customer," @@ -3484,7 +3534,23 @@ en_IE: paused: paused canceled: cancelled paypal: + already_refunded: "This payment has been refunded and no further action can be taken on it." + no_payment_via_admin_backend: "You cannot charge PayPal accounts through the admin backend at this time." + transaction: "PayPal Transaction" + payer_id: "Payer ID" + transaction_id: "Transaction ID" + token: "Token" + refund: "Refund" refund_amount: "Amount" + original_amount: "Original amount: %{amount}" + refund_successful: "PayPal refund successful" + refund_unsuccessful: "PayPal refund unsuccessful" + actions: + refund: "Refund" + flash: + cancel: "Don't want to use PayPal? No problems." + connection_failed: "Could not connect to PayPal." + generic_error: "PayPal failed. %{reasons}" users: form: account_settings: Account Settings @@ -3523,9 +3589,11 @@ en_IE: delete?: Delete? cards: authorised_shops: Authorised Shops + authorised_shops_agreement: This is the list of shops which are permitted to charge your default credit card for any subscriptions (ie. repeating orders) you may have. Your card details will be kept secure and will not be shared with shop owners. You will always be notified when you are charged. By checking the box for a shop, you are agreeing to authorise that shop to send instructions to the financial institution that issued your card to take payments in accordance with the terms of any subscription you create with that shop. saved_cards_popover: This is the list of cards you have opted to save for later use. Your 'default' will be selected automatically when you checkout an order, and can be charged by any shops you have allowed to do so (see right). authorised_shops: shop_name: "Shop Name" + allow_charges?: "Allow Charges to Default Card?" localized_number: invalid_format: has an invalid format. Please enter a number. api: diff --git a/config/locales/es.yml b/config/locales/es.yml index d69a28a08f..3ace997748 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -117,6 +117,8 @@ es: models: order_cycle: cloned_order_cycle_name: "COPIA DE %{order_cycle}" + tax_rate: + included_in_price: "Incluido en el precio" validators: date_time_string_validator: not_string_error: "debe ser una cadena" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index d62630fa2d..87b28906da 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -117,6 +117,8 @@ fr: models: order_cycle: cloned_order_cycle_name: "Copie de %{order_cycle}" + tax_rate: + included_in_price: "Inclus dans le prix" validators: date_time_string_validator: not_string_error: "doit être une série" diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 3eced7a54c..a7af33b68d 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -2321,6 +2321,8 @@ fr_CA: payment_processing_failed: "Le paiement n'a pas pu être traité, veuillez vérifier les informations saisies" payment_method_not_supported: "Cette méthode de paiement n'est pas maintenue. Veuillez en sélectionner une autre." payment_updated: "Paiement mis à jour" + cannot_perform_operation: "Le paiement n'a pas pu être mis à jour." + action_required: "Une action est requise" inventory_settings: "Paramètres catalogue boutique" tag_rules: "Règles de tag" shop_preferences: "Préférences boutique" @@ -2599,6 +2601,8 @@ fr_CA: processing: "en traitement" void: "faire un avoir" invalid: "invalide" + quantity_adjusted: "Le stock disponible est insuffisant. La quantité a été mise à jour en fonction du stock restant." + quantity_unchanged: "La quantité n'a pas été modifiée." resend_user_email_confirmation: resend: "Renvoyer" sending: "Renvoyer" @@ -2960,6 +2964,7 @@ fr_CA: display_currency: "Afficher la devise" choose_currency: "Choisir la devise" mail_method_settings: "Paramètre méthode mail" + mail_settings_notice_html: "Certains champs ne peuvent pas être modifiés car ils sont utilisés uniquement pour déboggage. Les modifications peuvent être réalisées par un développeur via ofn-install." general: "Général" enable_mail_delivery: "Permettre distribution des mails" send_mails_as: "Envoyer les mails en tant que" @@ -3476,6 +3481,8 @@ fr_CA: cancel_email_for_shop: greeting: "Bonjour %{name}," subject: "Annulation de Commande" + intro: "Un acheteur a annulé sa commande # %{number}." + view_cancelled_order: "Voir la commande annulée" confirm_email: subject: "Confirmation de commande" invoice_email: @@ -3495,6 +3502,10 @@ fr_CA: subject: "Reprendre" confirmation_instructions: subject: "Veuillez confirmer votre compte" + payment_mailer: + authorize_payment: + subject: "Veuillez autoriser votre paiement à %{distributor} on OFN." + instructions: "Votre paiement de %{amount} to %{distributor} demande une autorisation additionnelle. Veuillez suivre ce lien afin d'autoriser votre paiement:" shipment_mailer: shipped_email: dear_customer: "Cher Acheteur," diff --git a/config/locales/it.yml b/config/locales/it.yml index 564b355612..dbec9ac539 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -117,6 +117,8 @@ it: models: order_cycle: cloned_order_cycle_name: "COPIA DI %{order_cycle}" + tax_rate: + included_in_price: "Incluso nel prezzo" validators: date_time_string_validator: not_string_error: "deve essere una stringa" @@ -2955,6 +2957,7 @@ it: display_currency: "Visualizza valuta" choose_currency: "Scegli Valuta" mail_method_settings: "Impostazioni del metodo di posta" + mail_settings_notice_html: "Alcune delle seguenti impostazioni non possono essere modificate e sono elencate qui solo per scopi di debug. È possibile apportare modifiche aggiornando i segreti dell'istanza ed utilizzando ofn-install. Contatta il team globale di OFN per maggiori dettagli. " general: "Generale" enable_mail_delivery: "Abilita recapito posta" send_mails_as: "Invia mail come" @@ -3471,6 +3474,8 @@ it: cancel_email_for_shop: greeting: "Caro %{name}," subject: "Cancellazione dell'ordine" + intro: "Un cliente ha cancellato la sua gentile richiesta #%{number}." + view_cancelled_order: "Visualizza richiesta eliminata" confirm_email: subject: "Conferma Ordine" invoice_email: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 0f760cd34d..9b1861f9a0 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -118,6 +118,8 @@ ru: models: order_cycle: cloned_order_cycle_name: "КОПИЯ %{order_cycle}" + tax_rate: + included_in_price: "Включено в цену" validators: date_time_string_validator: not_string_error: "должно быть строкой" @@ -3007,6 +3009,7 @@ ru: display_currency: "Показывать валюту" choose_currency: "Выбор валюты" mail_method_settings: "Почтовые Настройки" + mail_settings_notice_html: "Некоторые из следующих параметров нельзя редактировать, и они перечислены здесь только для целей отладки. Изменения можно внести, обновив секреты экземпляра и предоставив их с помощью ofn-install . За подробностями обращайтесь к глобальной команде ОСП." general: "Основные" enable_mail_delivery: "Включить отправку почты" send_mails_as: "Отправлять Почту Как" @@ -3523,6 +3526,8 @@ ru: cancel_email_for_shop: greeting: "Уважаемый %{name}!" subject: "Отмена заказа" + intro: "Покупатель отменил заказ № %{number}." + view_cancelled_order: "Посмотреть отмененный заказ" confirm_email: subject: "Подтверждение заказа" invoice_email: diff --git a/db/migrate/20201227122327_add_included_to_adjustments.rb b/db/migrate/20201227122327_add_included_to_adjustments.rb new file mode 100644 index 0000000000..2ccbeab2a3 --- /dev/null +++ b/db/migrate/20201227122327_add_included_to_adjustments.rb @@ -0,0 +1,22 @@ +class AddIncludedToAdjustments < ActiveRecord::Migration + class Spree::TaxRate < ActiveRecord::Base; end + + class Spree::Adjustment < ActiveRecord::Base + belongs_to :originator, polymorphic: true + end + + def up + add_column :spree_adjustments, :included, :boolean, default: false + Spree::Adjustment.reset_column_information + + inclusive_tax_rates = Spree::TaxRate.where(included_in_price: true) + + # Set included boolean to true on all adjustments based on price-inclusive tax rates + Spree::Adjustment.where(originator_type: 'Spree::TaxRate', originator_id: inclusive_tax_rates). + update_all(included: true) + end + + def down + remove_column :spree_adjustments, :included + end +end diff --git a/db/migrate/20210202052337_migrate_variant_unit_values.rb b/db/migrate/20210202052337_migrate_variant_unit_values.rb deleted file mode 100644 index 9c6cf46a2e..0000000000 --- a/db/migrate/20210202052337_migrate_variant_unit_values.rb +++ /dev/null @@ -1,27 +0,0 @@ -class MigrateVariantUnitValues < ActiveRecord::Migration - def up - Spree::Variant.where(unit_value: [nil, Float::NAN]).find_each do |variant| - variant.unit_value = 1 - variant.save - end - Spree::Variant.where(weight: [nil, Float::NAN]).find_each do |variant| - variant.weight = 0 - variant.save - end - change_column_null :spree_variants, :unit_value, false, 1 - change_column_null :spree_variants, :weight, false, 0.0 - change_column_default :spree_variants, :unit_value, 1 - change_column_default :spree_variants, :weight, 0.0 - execute "ALTER TABLE spree_variants ADD CONSTRAINT check_unit_value_for_nan CHECK (unit_value <> 'NaN')" - execute "ALTER TABLE spree_variants ADD CONSTRAINT check_weight_for_nan CHECK (weight <> 'NaN')" - end - - def down - change_column_null :spree_variants, :unit_value, true - change_column_null :spree_variants, :weight, true - change_column_default :spree_variants, :unit_value, nil - change_column_default :spree_variants, :weight, nil - execute "ALTER TABLE spree_variants DROP CONSTRAINT check_unit_value_for_nan" - execute "ALTER TABLE spree_variants DROP CONSTRAINT check_weight_for_nan" - end -end diff --git a/db/schema.rb b/db/schema.rb index c71d64ba0d..28b4383de8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -385,16 +385,17 @@ ActiveRecord::Schema.define(version: 20210203215049) do t.string "label", limit: 255 t.string "source_type", limit: 255 t.integer "adjustable_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "mandatory" t.integer "originator_id" t.string "originator_type", limit: 255 t.boolean "eligible", default: true t.string "adjustable_type", limit: 255 - t.decimal "included_tax", precision: 10, scale: 2, default: 0.0, null: false + t.decimal "included_tax", precision: 10, scale: 2, default: 0.0, null: false t.string "state", limit: 255 t.integer "order_id" + t.boolean "included", default: false end add_index "spree_adjustments", ["adjustable_id"], name: "index_adjustments_on_order_id", using: :btree diff --git a/lib/open_food_network/feature_toggle.rb b/lib/open_food_network/feature_toggle.rb index 5ee51a82f9..f57e740bc4 100644 --- a/lib/open_food_network/feature_toggle.rb +++ b/lib/open_food_network/feature_toggle.rb @@ -30,11 +30,9 @@ module OpenFoodNetwork new.enabled?(feature_name, user) end - def self.enable(feature_name, user_emails) - return unless user_emails.present? - + def self.enable(feature_name, &block) Thread.current[:features] ||= {} - Thread.current[:features][feature_name] = Feature.new(user_emails) + Thread.current[:features][feature_name] = Feature.new(block) end def initialize @@ -68,17 +66,17 @@ module OpenFoodNetwork end class Feature - def initialize(users = []) - @users = users + def initialize(block) + @block = block end def enabled?(user) - users.include?(user.email) + block.call(user) end private - attr_reader :users + attr_reader :block end class NullFeature diff --git a/lib/spree/core/calculated_adjustments.rb b/lib/spree/core/calculated_adjustments.rb index 6388369209..301d821642 100644 --- a/lib/spree/core/calculated_adjustments.rb +++ b/lib/spree/core/calculated_adjustments.rb @@ -43,7 +43,8 @@ module Spree order: order_object_for(target), label: label, mandatory: mandatory, - state: state + state: state, + included: tax_included?(self, target) ) end @@ -78,6 +79,15 @@ module Spree private + # Used for setting the #included boolean on tax adjustments. This will be removed in a + # later step, as the responsibility for creating all adjustments related to tax will be + # moved into the Spree::TaxRate class. + def tax_included?(originator, target) + originator.is_a?(Spree::TaxRate) && + originator.included_in_price && + originator.default_zone_or_zone_match?(order_object_for(target)) + end + def order_object_for(target) # Temporary method for adjustments transition. if target.is_a? Spree::Order diff --git a/lib/spree/i18n.rb b/lib/spree/i18n.rb index 39bd4590cd..a1ab621b0d 100644 --- a/lib/spree/i18n.rb +++ b/lib/spree/i18n.rb @@ -6,7 +6,7 @@ require 'spree/i18n/base' module Spree extend ActionView::Helpers::TranslationHelper - extend ActionView::Helpers::TagHelper if ENV['DEPENDENCIES_NEXT'] + extend ActionView::Helpers::TagHelper class << self # Add spree namespace and delegate to Rails TranslationHelper for some nice diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 507f840710..0a4e00b472 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -69,7 +69,7 @@ describe Admin::EnterprisesController, type: :controller do admin_user.enterprises << create(:distributor_enterprise) allow(controller).to receive_messages spree_current_user: admin_user - enterprise_params[:enterprise][:owner_id] = admin_user + enterprise_params[:enterprise][:owner_id] = admin_user.id enterprise_params[:enterprise][:sells] = 'none' spree_put :create, enterprise_params @@ -91,7 +91,7 @@ describe Admin::EnterprisesController, type: :controller do it "doesn't affect the hub status for super admins" do allow(controller).to receive_messages spree_current_user: admin_user - enterprise_params[:enterprise][:owner_id] = admin_user + enterprise_params[:enterprise][:owner_id] = admin_user.id enterprise_params[:enterprise][:sells] = 'any' spree_put :create, enterprise_params @@ -198,7 +198,7 @@ describe Admin::EnterprisesController, type: :controller do enterprise: { tag_rules_attributes: { '0' => { - id: tag_rule, + id: tag_rule.id, type: "TagRule::DiscountOrder", preferred_customer_tags: "some,new,tags", calculator_type: "Calculator::FlatPercentItemTotal", @@ -246,7 +246,7 @@ describe Admin::EnterprisesController, type: :controller do it "allows owner to be changed" do allow(controller).to receive_messages spree_current_user: distributor_owner - update_params = { id: distributor, enterprise: { owner_id: distributor_manager } } + update_params = { id: distributor, enterprise: { owner_id: distributor_manager.id } } spree_post :update, update_params distributor.reload @@ -275,7 +275,7 @@ describe Admin::EnterprisesController, type: :controller do it "allows owner to be changed" do allow(controller).to receive_messages spree_current_user: admin_user - update_params = { id: distributor, enterprise: { owner_id: distributor_manager } } + update_params = { id: distributor, enterprise: { owner_id: distributor_manager.id } } spree_post :update, update_params distributor.reload diff --git a/spec/controllers/api/exchange_products_controller_spec.rb b/spec/controllers/api/exchange_products_controller_spec.rb index d9aa7ce55c..a0ae941c8a 100644 --- a/spec/controllers/api/exchange_products_controller_spec.rb +++ b/spec/controllers/api/exchange_products_controller_spec.rb @@ -26,7 +26,7 @@ module Api let(:products_relation) { Spree::Product.where("1=0") } it "handles it gracefully" do - get :index, exchange_id: exchange.id + api_get :index, exchange_id: exchange.id expect(json_response["products"].length).to eq 0 end end @@ -36,14 +36,14 @@ module Api describe "when an exchange id param is provided" do it "uses exchange order_cycle, incoming and enterprise to fetch products" do - get :index, exchange_id: exchange.id, order_cycle_id: 666, enterprise_id: 666, incoming: false + api_get :index, exchange_id: exchange.id, order_cycle_id: 666, enterprise_id: 666, incoming: false expect(json_response["products"].first["supplier_name"]).to eq exchange.variants.first.product.supplier.name end end describe "when an exchange id param is not provided" do it "uses params order_cycle, incoming and enterprise to fetch products" do - spree_get :index, order_cycle_id: order_cycle.id, enterprise_id: exchange.sender_id, incoming: true + api_get :index, order_cycle_id: order_cycle.id, enterprise_id: exchange.sender_id, incoming: true expect(json_response["products"].first["supplier_name"]).to eq exchange.variants.first.product.supplier.name end end @@ -59,7 +59,7 @@ module Api describe "when a specific page is requested" do it "returns the requested page with paginated data" do - get :index, exchange_id: exchange.id, page: 1 + api_get :index, exchange_id: exchange.id, page: 1 expect(json_response["products"].size).to eq 1 expect(json_response["pagination"]["results"]).to eq 2 @@ -69,7 +69,7 @@ module Api describe "when no specific page is requested" do it "returns all results without paginating" do - get :index, exchange_id: exchange.id + api_get :index, exchange_id: exchange.id expect(json_response["products"].size).to eq 2 expect(json_response["pagination"]).to be nil diff --git a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb index 1e1437aee2..a6ed887d61 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -174,7 +174,7 @@ describe Spree::Admin::PaymentsController, type: :controller do let(:params) { { e: 'credit', order_id: order.number, id: payment.id } } before do - allow(request).to receive(:referer) { 'http://foo.com' } + request.env["HTTP_REFERER"] = "http://foo.com" allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } end @@ -210,7 +210,7 @@ describe Spree::Admin::PaymentsController, type: :controller do let(:params) { { e: 'refund', order_id: order.number, id: payment.id } } before do - allow(request).to receive(:referer) { 'http://foo.com' } + request.env["HTTP_REFERER"] = "http://foo.com" allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } end @@ -248,7 +248,7 @@ describe Spree::Admin::PaymentsController, type: :controller do before do allow(PaymentMailer).to receive(:authorize_payment) { mail_mock } - allow(request).to receive(:referer) { 'http://foo.com' } + request.env["HTTP_REFERER"] = "http://foo.com" allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } allow(payment).to receive(:cvv_response_message).and_return("https://www.stripe.com/authorize") end @@ -266,7 +266,7 @@ describe Spree::Admin::PaymentsController, type: :controller do let(:params) { { e: 'unrecognized_event', order_id: order.number, id: payment.id } } before do - allow(request).to receive(:referer) { 'http://foo.com' } + request.env["HTTP_REFERER"] = "http://foo.com" allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } end diff --git a/spec/controllers/spree/admin/tax_rates_controller_spec.rb b/spec/controllers/spree/admin/tax_rates_controller_spec.rb index 266edc9430..8bef1f2fb1 100644 --- a/spec/controllers/spree/admin/tax_rates_controller_spec.rb +++ b/spec/controllers/spree/admin/tax_rates_controller_spec.rb @@ -20,7 +20,7 @@ module Spree context "when the amount is not changed" do it "updates the record" do expect { - spree_put :update, id: tax_rate.id, tax_rate: { name: "Updated Rate", amount: 0.1 } + spree_put :update, id: tax_rate.id, tax_rate: { name: "Updated Rate", amount: "0.1" } }.to_not change{ Spree::TaxRate.with_deleted.count } expect(response).to redirect_to spree.admin_tax_rates_url @@ -32,7 +32,7 @@ module Spree context "when the amount is changed" do it "duplicates the record and soft-deletes the duplicate" do expect { - spree_put :update, id: tax_rate.id, tax_rate: { name: "Changed Rate", amount: 0.5 } + spree_put :update, id: tax_rate.id, tax_rate: { name: "Changed Rate", amount: "0.5" } }.to change{ Spree::TaxRate.with_deleted.count }.by(1) expect(response).to redirect_to spree.admin_tax_rates_url diff --git a/spec/features/admin/enterprise_roles_spec.rb b/spec/features/admin/enterprise_roles_spec.rb index 0e0cbd5237..4720e36334 100644 --- a/spec/features/admin/enterprise_roles_spec.rb +++ b/spec/features/admin/enterprise_roles_spec.rb @@ -117,7 +117,7 @@ feature ' end end - it "allows adding new managers" do + xit "allows adding new managers" do within 'table.managers' do select2_select user3.email, from: 'ignored', search: true @@ -129,7 +129,7 @@ feature ' end end - it "shows changes to enterprise contact or owner" do + xit "shows changes to enterprise contact or owner" do select2_select user2.email, from: 'receives_notifications_dropdown' within('#save-bar') { click_button 'Update' } navigate_to_enterprise_users @@ -146,7 +146,7 @@ feature ' end end - it "can invite unregistered users to be managers" do + xit "can invite unregistered users to be managers" do setup_email find('a.button.help-modal').click expect(page).to have_css '#invite-manager-modal' diff --git a/spec/initializers/feature_toggles_spec.rb b/spec/initializers/feature_toggles_spec.rb new file mode 100644 index 0000000000..0433d4508b --- /dev/null +++ b/spec/initializers/feature_toggles_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe 'config/initializers/feature_toggles.rb' do + # Executes the initializer's code block by reading the Ruby file. Note that `Kernel#require` would + # prevent this from happening twice. + subject(:execute_initializer) do + load Rails.root.join('config/initializers/feature_toggles.rb') + end + + let(:user) { build(:user) } + + around do |example| + original = ENV['BETA_TESTERS'] + example.run + ENV['BETA_TESTERS'] = original + end + + context 'when beta_testers is ["all"]' do + before { ENV['BETA_TESTERS'] = 'all' } + + it 'returns true' do + execute_initializer + + enabled = OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, user) + expect(enabled).to eq(true) + end + end + + context 'when beta_testers is a list of emails' do + let(:other_user) { build(:user) } + + context 'and the user is in the list' do + before { ENV['BETA_TESTERS'] = "#{user.email}, #{other_user.email}" } + + it 'enables the feature' do + execute_initializer + + enabled = OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, user) + expect(enabled).to eq(true) + end + end + + context 'and the user is not in the list' do + before { ENV['BETA_TESTERS'] = "#{other_user.email}" } + + it 'disables the feature' do + execute_initializer + + enabled = OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, user) + expect(enabled).to eq(false) + end + end + + context 'and the list is empty' do + before { ENV['BETA_TESTERS'] = '' } + + it 'disables the feature' do + execute_initializer + + enabled = OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, user) + expect(enabled).to eq(false) + end + end + end +end diff --git a/spec/lib/open_food_network/feature_toggle_spec.rb b/spec/lib/open_food_network/feature_toggle_spec.rb index 73407d671d..d9904b0fd6 100644 --- a/spec/lib/open_food_network/feature_toggle_spec.rb +++ b/spec/lib/open_food_network/feature_toggle_spec.rb @@ -32,19 +32,25 @@ module OpenFoodNetwork context 'when specifying users' do let(:user) { build(:user) } - context 'and the feature is enabled for them' do - before { FeatureToggle.enable(:foo, [user.email]) } + context 'and the block does not specify arguments' do + before do + FeatureToggle.enable(:foo) { 'return value' } + end - it 'returns true' do - expect(FeatureToggle.enabled?(:foo, user)).to eq(true) + it "returns the block's return value" do + expect(FeatureToggle.enabled?(:foo, user)).to eq('return value') end end - context 'and the feature is disabled for them' do - before { FeatureToggle.enable(:foo, []) } + context 'and the block specifies arguments' do + let(:users) { [user.email] } - it 'returns false' do - expect(FeatureToggle.enabled?(:foo, user)).to eq(false) + before do + FeatureToggle.enable(:foo) { |user| users.include?(user.email) } + end + + it "returns the block's return value" do + expect(FeatureToggle.enabled?(:foo, user)).to eq(true) end end end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index de6bdb1f98..0fab3d3cbd 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -460,5 +460,65 @@ module Spree context "extends LocalizedNumber" do it_behaves_like "a model using the LocalizedNumber module", [:amount] end + + describe "inclusive and additional taxes" do + let!(:zone) { create(:zone_with_member) } + let!(:tax_category) { create(:tax_category, name: "Tax Test") } + let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } + let(:order) { create(:order, distributor: distributor) } + let(:included_in_price) { true } + let(:tax_rate) { + create(:tax_rate, included_in_price: included_in_price, zone: zone, + calculator: ::Calculator::FlatRate.new(preferred_amount: 0.1)) + } + let(:product) { create(:product, tax_category: tax_category) } + let(:variant) { product.variants.first } + + describe "tax adjustment creation" do + before do + tax_category.tax_rates << tax_rate + allow(order).to receive(:tax_zone) { zone } + order.line_items << create(:line_item, variant: variant, quantity: 5) + end + + context "with included taxes" do + it "records the tax as included" do + expect(order.all_adjustments.tax.count).to eq 1 + expect(order.all_adjustments.tax.first.included).to be true + end + end + + context "with additional taxes" do + let(:included_in_price) { false } + + it "records the tax as additional" do + expect(order.all_adjustments.tax.count).to eq 1 + expect(order.all_adjustments.tax.first.included).to be false + end + end + end + + describe "inclusive and additional scopes" do + let(:included) { true } + let(:adjustment) { + create(:adjustment, adjustable: order, source: order, + originator: tax_rate, included: included) + } + + context "when tax is included in price" do + it "is returned by the #included scope" do + expect(Spree::Adjustment.inclusive).to eq [adjustment] + end + end + + context "when tax is additional to the price" do + let(:included) { false } + + it "is returned by the #additional scope" do + expect(Spree::Adjustment.additional).to eq [adjustment] + end + end + end + end end end diff --git a/spec/models/spree/shipment_spec.rb b/spec/models/spree/shipment_spec.rb index f5e846bca6..9fecb237b9 100644 --- a/spec/models/spree/shipment_spec.rb +++ b/spec/models/spree/shipment_spec.rb @@ -50,13 +50,6 @@ describe Spree::Shipment do end end - context "display_total_cost" do - it "retuns a Spree::Money" do - allow(shipment).to receive(:total_cost) { 21.22 } - expect(shipment.display_total_cost).to eq Spree::Money.new(21.22) - end - end - it "#item_cost" do shipment = Spree::Shipment.new( order: build_stubbed(:order_with_totals, line_items: [build_stubbed(:line_item)]) @@ -181,12 +174,6 @@ describe Spree::Shipment do end end - it '#total_cost' do - allow(shipment).to receive_messages cost: 5.0 - allow(shipment).to receive_messages item_cost: 50.0 - expect(shipment.total_cost).to eql(55.0) - end - context "#update!" do shared_examples_for "immutable once shipped" do it "should remain in shipped state once shipped" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62c67e9417..37fee324a5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -101,6 +101,10 @@ RSpec.configure do |config| # Retry config.verbose_retry = true + # Try twice (retry once) + config.default_retry_count = 2 + # Only retry when Selenium raises Net::ReadTimeout + config.exceptions_to_retry = [Net::ReadTimeout] # Force use of expect (over should) config.expect_with :rspec do |expectations|