diff --git a/.github/ISSUE_TEMPLATE/tech-debt-template.md b/.github/ISSUE_TEMPLATE/tech-debt-template.md new file mode 100644 index 0000000000..2bd9054af2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech-debt-template.md @@ -0,0 +1,21 @@ +--- +name: Tech debt template +about: File a refactoring task that aids future development +title: '' +labels: tech debt +assignees: '' + +--- + +### What we should change and why (this is tech debt) + + + + +### Context + + + + +### Impact and timeline + diff --git a/Gemfile b/Gemfile index e021395613..6eb959a3fb 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" } gem 'rails', '~> 3.2.22' gem 'rails-i18n', '~> 3.0.0' gem 'i18n', '~> 0.6.11' -gem 'i18n-js', '~> 3.1.0' +gem 'i18n-js', '~> 3.2.1' # Patched version. See http://rubysec.com/advisories/CVE-2015-5312/. gem 'nokogiri', '>= 1.6.7.1' diff --git a/Gemfile.lock b/Gemfile.lock index f42f7e1595..f602edc9ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -195,7 +195,7 @@ GEM bcrypt-ruby (3.1.5) bcrypt (>= 3.1.3) blockenspiel (0.5.0) - bugsnag (4.1.0) + bugsnag (5.5.0) builder (3.0.4) byebug (9.0.6) cancan (1.6.10) @@ -251,7 +251,7 @@ GEM css_parser (1.6.0) addressable daemons (1.3.1) - dalli (2.7.2) + dalli (2.7.9) database_cleaner (0.7.1) db2fog (0.9.0) activerecord (>= 3.2.0, < 5.0) @@ -293,7 +293,7 @@ GEM faraday (0.15.4) multipart-post (>= 1.2, < 3) ffaker (1.22.1) - ffi (1.9.25) + ffi (1.10.0) figaro (1.1.1) thor (~> 0.14) fission (0.5.0) @@ -489,8 +489,8 @@ GEM httparty (0.16.2) multi_xml (>= 0.5.2) i18n (0.6.11) - i18n-js (3.1.0) - i18n (>= 0.6.6, < 2) + i18n-js (3.2.1) + i18n (>= 0.6.6) immigrant (0.3.6) activerecord (>= 3.0) io-like (0.3.0) @@ -660,7 +660,7 @@ GEM rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) - rspec-rails (3.8.1) + rspec-rails (3.8.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -692,9 +692,9 @@ GEM select2-rails (3.4.9) sass-rails thor (~> 0.14) - selenium-webdriver (3.12.0) + selenium-webdriver (3.141.0) childprocess (~> 0.5) - rubyzip (~> 1.2) + rubyzip (~> 1.2, >= 1.2.2) shellany (0.0.1) shoulda-matchers (2.8.0) activesupport (>= 3.0.0) @@ -705,7 +705,7 @@ GEM simplecov-html (0.10.2) skylight (1.7.2) activesupport (>= 3.0.0) - spinjs-rails (1.3) + spinjs-rails (1.4) rails (>= 3.1) spreadsheet (1.1.7) ruby-ole (>= 1.0) @@ -812,7 +812,7 @@ DEPENDENCIES guard-rspec (~> 4.7.3) haml i18n (~> 0.6.11) - i18n-js (~> 3.1.0) + i18n-js (~> 3.2.1) immigrant jquery-migrate-rails jquery-rails (= 3.0.0) diff --git a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee index 9e2c3d5696..129f20b7aa 100644 --- a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee @@ -1 +1 @@ -angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown']) +angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown', 'admin.utils']) diff --git a/app/assets/javascripts/admin/utils/filters/localize_currency.js.coffee b/app/assets/javascripts/admin/utils/filters/localize_currency.js.coffee new file mode 100644 index 0000000000..25d97828b4 --- /dev/null +++ b/app/assets/javascripts/admin/utils/filters/localize_currency.js.coffee @@ -0,0 +1,14 @@ +angular.module("admin.utils").filter "localizeCurrency", (currencyConfig)-> + # Convert number to string currency using injected currency configuration. + (amount) -> + # Set country code (eg. "US"). + currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else "" + # Set decimal points, 2 or 0 if hide_cents. + decimals = if currencyConfig.hide_cents == "true" then 0 else 2 + # Set format if the currency symbol should come after the number, otherwise (default) use the locale setting. + format = if currencyConfig.symbol_position == "after" then "%n %u" else undefined + # We need to use parseFloat as the amount should come in as a string. + amount = parseFloat(amount) + + # Build the final price string. + I18n.toCurrency(amount, {precision: decimals, unit: currencyConfig.symbol, format: format}) + currency_code diff --git a/app/controllers/admin/product_import_controller.rb b/app/controllers/admin/product_import_controller.rb index 201d57033f..72a5a63f30 100644 --- a/app/controllers/admin/product_import_controller.rb +++ b/app/controllers/admin/product_import_controller.rb @@ -84,7 +84,8 @@ module Admin directory = 'tmp/product_import' Dir.mkdir(directory) unless File.exist?(directory) File.open(Rails.root.join(directory, filename + extension), 'wb') do |f| - f.write(upload.read) + data = UploadSanitizer.new(upload.read).call + f.write(data) f.path end end diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 9762c583ad..4e727d98f2 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -54,9 +54,7 @@ Spree::Admin::OrdersController.class_eval do end def invoice - pdf = render_to_string pdf: "invoice-#{@order.number}.pdf", - template: invoice_template, - formats: [:html], encoding: "UTF-8" + pdf = InvoiceRenderer.new.render_to_string(@order) Spree::OrderMailer.invoice_email(@order.id, pdf).deliver flash[:success] = t('admin.orders.invoice_email_sent') @@ -65,7 +63,7 @@ Spree::Admin::OrdersController.class_eval do end def print - render pdf: "invoice-#{@order.number}", template: invoice_template, encoding: "UTF-8" + render InvoiceRenderer.new.args(@order) end def print_ticket @@ -78,10 +76,6 @@ Spree::Admin::OrdersController.class_eval do private - def invoice_template - Spree::Config.invoice_style2? ? "spree/admin/orders/invoice2" : "spree/admin/orders/invoice" - end - def require_distributor_abn unless @order.distributor.abn.present? flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) diff --git a/app/controllers/spree/api/orders_controller_decorator.rb b/app/controllers/spree/api/orders_controller_decorator.rb deleted file mode 100644 index 21f64f51d8..0000000000 --- a/app/controllers/spree/api/orders_controller_decorator.rb +++ /dev/null @@ -1,7 +0,0 @@ -Spree::Api::OrdersController.class_eval do - - # We need to add expections for collection actions other than :index here - # because Spree's API controller causes authorize_read! to be called, which - # results in an ActiveRecord::NotFound Exception as the order object is not - # defined for collection actions -end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 0e93a4affc..022a27c7a2 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -65,6 +65,10 @@ module Admin admin_inject_json_ams_array opts[:module], "columns", column_preferences, Api::Admin::ColumnPreferenceSerializer end + def admin_inject_currency_config + admin_inject_json_ams 'admin.utils', "currencyConfig", {}, Api::CurrencyConfigSerializer + end + def admin_inject_enterprise_permissions permissions = {can_manage_shipping_methods: can?(:manage_shipping_methods, @enterprise), diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index bd9ef94d45..5f25287b2a 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -175,7 +175,7 @@ module ProductImport assign_errors product.errors.full_messages, entry.line_number end - @already_created[entry.enterprise_id] = { entry.name => product.id } + @already_created.deep_merge! entry.enterprise_id => { entry.name => product.id } end def save_variant(entry) @@ -212,12 +212,20 @@ module ProductImport case setting['mode'] when 'overwrite_all' object.assign_attributes(attribute => setting['value']) + # In case of new products, some attributes are saved on the variant. + # We write them to the entry here to be copied to the variant later. + if entry.respond_to? "#{attribute}=" + entry.public_send("#{attribute}=", setting['value']) + end when 'overwrite_empty' if object.public_send(attribute).blank? || ((attribute == 'on_hand' || attribute == 'count_on_hand') && entry.on_hand_nil) object.assign_attributes(attribute => setting['value']) + if entry.respond_to? "#{attribute}=" + entry.public_send("#{attribute}=", setting['value']) + end end end end diff --git a/app/overrides/spree/layouts/admin/add_currency_config.html.haml.deface b/app/overrides/spree/layouts/admin/add_currency_config.html.haml.deface new file mode 100644 index 0000000000..0ab8f96c55 --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_currency_config.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_before "div#wrapper" + += admin_inject_currency_config diff --git a/app/services/bulk_invoice_service.rb b/app/services/bulk_invoice_service.rb index 397020635e..18921bd413 100644 --- a/app/services/bulk_invoice_service.rb +++ b/app/services/bulk_invoice_service.rb @@ -1,5 +1,4 @@ class BulkInvoiceService - include WickedPdf::PdfHelper attr_reader :id def initialize @@ -11,10 +10,7 @@ class BulkInvoiceService orders = Spree::Order.where(id: order_ids) orders.each do |order| - invoice = renderer.render_to_string pdf: "invoice-#{order.number}.pdf", - template: invoice_template, - formats: [:html], encoding: "UTF-8", - locals: { :@order => order } + invoice = renderer.render_to_string(order) pdf << CombinePDF.parse(invoice) end @@ -42,11 +38,7 @@ class BulkInvoiceService end def renderer - ApplicationController.new - end - - def invoice_template - Spree::Config.invoice_style2? ? "spree/admin/orders/invoice2" : "spree/admin/orders/invoice" + @renderer ||= InvoiceRenderer.new end def file_directory diff --git a/app/services/invoice_renderer.rb b/app/services/invoice_renderer.rb new file mode 100644 index 0000000000..e3613dccbe --- /dev/null +++ b/app/services/invoice_renderer.rb @@ -0,0 +1,29 @@ +class InvoiceRenderer + def render_to_string(order) + renderer.render_to_string(args(order)) + end + + def args(order) + { + pdf: "invoice-#{order.number}.pdf", + template: invoice_template, + formats: [:html], + encoding: "UTF-8", + locals: { :@order => order } + } + end + + private + + def renderer + ApplicationController.new + end + + def invoice_template + if Spree::Config.invoice_style2? + "spree/admin/orders/invoice2" + else + "spree/admin/orders/invoice" + end + end +end diff --git a/app/services/upload_sanitizer.rb b/app/services/upload_sanitizer.rb new file mode 100644 index 0000000000..132669c884 --- /dev/null +++ b/app/services/upload_sanitizer.rb @@ -0,0 +1,18 @@ +# Formats uploaded files to UTF-8 encoding and strips unexpected BOM characters. +# Takes an open File object as input +class UploadSanitizer + def initialize(upload) + @data = upload + end + + def call + @data.force_encoding('UTF-8') + strip_bom_character + end + + private + + def strip_bom_character + @data.gsub("\xEF\xBB\xBF".force_encoding("UTF-8"), '') + end +end diff --git a/app/views/admin/subscriptions/_orders_panel.html.haml b/app/views/admin/subscriptions/_orders_panel.html.haml index 7314ee244d..d99ab196e8 100644 --- a/app/views/admin/subscriptions/_orders_panel.html.haml +++ b/app/views/admin/subscriptions/_orders_panel.html.haml @@ -19,7 +19,7 @@ %div{ ng: { bind: "::orderCycleCloses(proxyOrder.order_cycle_id)" } } %td.text-center %span.state{ ng: { class: "proxyOrder.state", bind: 'stateText(proxyOrder.state)' } } - %td.text-center{ ng: { bind: '(proxyOrder.total || subscription.estimatedTotal()) | currency' } } + %td.text-center{ ng: { bind: '(proxyOrder.total || subscription.estimatedTotal()) | localizeCurrency' } } %td.actions %a.edit-order.icon-edit.no-text{ href: '{{::proxyOrder.edit_path}}', target: '_blank', 'ofn-with-tip' => t(:edit_order), confirm_order_edit: true } %a.cancel-order.icon-remove.no-text{ href: 'javascript:void(0)', ng: { hide: "proxyOrder.state == 'canceled'", click: "cancelOrder(proxyOrder)" }, 'ofn-with-tip' => t(:cancel_order) } diff --git a/app/views/admin/subscriptions/_review.html.haml b/app/views/admin/subscriptions/_review.html.haml index 7e97bd02aa..4d06cb7859 100644 --- a/app/views/admin/subscriptions/_review.html.haml +++ b/app/views/admin/subscriptions/_review.html.haml @@ -75,9 +75,9 @@ .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 | currency }} + %td.price.align-center {{ item.price_estimate | localizeCurrency }} %td.quantity {{ item.quantity }} - %td.total.align-center {{ (item.price_estimate * item.quantity) | currency }} + %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"} @@ -85,7 +85,7 @@ = t(:subtotal) \: %td.total.align-center - %span {{ subscription.estimatedSubtotal() | currency }} + %span {{ subscription.estimatedSubtotal() | localizeCurrency }} %tbody#order-total.grand-total.no-border-top{"data-hook" => "admin_order_form_total"} %tr %td{:colspan => "3"} @@ -93,6 +93,6 @@ = t(:order_total_price) \: %td.total.align-center - %span#order_form_total {{ subscription.estimatedTotal() | currency }} + %span#order_form_total {{ subscription.estimatedTotal() | localizeCurrency }} %p.notice = t "this_is_an_estimate", scope: 'admin.subscriptions.subscription_line_items' diff --git a/app/views/admin/subscriptions/_subscription_line_items.html.haml b/app/views/admin/subscriptions/_subscription_line_items.html.haml index b80b95a85b..fdb5f10fdf 100644 --- a/app/views/admin/subscriptions/_subscription_line_items.html.haml +++ b/app/views/admin/subscriptions/_subscription_line_items.html.haml @@ -19,10 +19,10 @@ .description {{ item.description }} .not-in-open-and-upcoming-order-cycles-warning{ ng: { if: '!item.in_open_and_upcoming_order_cycles' } } = t(".not_in_open_and_upcoming_order_cycles_warning") - %td.price.align-center {{ item.price_estimate | currency }} + %td.price.align-center {{ item.price_estimate | localizeCurrency }} %td.quantity %input{ name: 'quantity', type: 'number', min: 0, ng: { model: 'item.quantity' } } - %td.total.align-center {{ (item.price_estimate * item.quantity) | currency }} + %td.total.align-center {{ (item.price_estimate * item.quantity) | localizeCurrency }} %td.actions %a.delete-item.icon-trash.no-text{ ng: { click: 'removeSubscriptionLineItem(item)'}, :href => "javascript:void(0)" } %tbody#subtotal.no-border-top{"data-hook" => "admin_order_form_subtotal"} @@ -32,7 +32,7 @@ = t(:subtotal) \: %td.total.align-center - %span#order_subtotal {{ subscription.estimatedSubtotal() | currency }} + %span#order_subtotal {{ subscription.estimatedSubtotal() | localizeCurrency }} %td.actions %tbody#fees.no-border-top{ ng: { show: "subscription.estimatedFees() > 0" } } %tr#fees-row @@ -41,7 +41,7 @@ = t(:fees) \: %td.total.align-center - %span#order_fees {{ subscription.estimatedFees() | currency }} + %span#order_fees {{ subscription.estimatedFees() | localizeCurrency }} %td.actions %tbody#order-total.grand-total.no-border-top{"data-hook" => "admin_order_form_total"} %tr @@ -50,7 +50,7 @@ = t(:order_total_price) \: %td.total.align-center - %span#order_form_total {{ subscription.estimatedTotal() | currency }} + %span#order_form_total {{ subscription.estimatedTotal() | localizeCurrency }} %td.actions %p.notice = t ".this_is_an_estimate" diff --git a/app/views/spree/admin/orders/_invoice_table.html.haml b/app/views/spree/admin/orders/_invoice_table.html.haml index 9375f9bb68..c96099edb6 100644 --- a/app/views/spree/admin/orders/_invoice_table.html.haml +++ b/app/views/spree/admin/orders/_invoice_table.html.haml @@ -14,6 +14,9 @@ %tr %td = render 'spree/shared/line_item_name', line_item: item + %br + %small + %em= raw(item.variant.product.supplier.name) %td{:align => "right"} = item.quantity %td{:align => "right"} diff --git a/app/views/spree/admin/orders/_invoice_table2.html.haml b/app/views/spree/admin/orders/_invoice_table2.html.haml index 0941904d8b..2d42591ddc 100644 --- a/app/views/spree/admin/orders/_invoice_table2.html.haml +++ b/app/views/spree/admin/orders/_invoice_table2.html.haml @@ -17,6 +17,9 @@ %tr %td = render 'spree/shared/line_item_name', line_item: item + %br + %small + %em= raw(item.variant.product.supplier.name) %td{:align => "right"} = item.quantity %td{:align => "right"} diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 4c2341161a..f340592af3 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -442,6 +442,7 @@ ca: tax_category: Categoria d'impostos inherits_properties?: Hereda propietats? available_on: Disponible el + av_on: "Disp. via" import_date: S'ha importat upload_an_image: Penja una imatge product_search_keywords: Paraules clau de cerca de producte @@ -475,6 +476,8 @@ ca: inventory_no_permission: no tens el permís per crear inventari per a aquesta productora none_saved: no s'han desat cap producte amb èxit line_number: "Línia %{number}:" + encoding_error: "Comproveu la configuració de l'idioma del vostre fitxer d'origen i assegureu-vos que es desa amb la codificació UTF-8" + unexpected_error: "La importació de productes ha detectat un error inesperat mentre obria el fitxer: %{error_message}" index: select_file: Selecciona un full de càlcul per pujar-lo spreadsheet: Full de càlcul @@ -966,6 +969,9 @@ ca: orders_and_distributors: name: Comandes i distribuïdores description: Comandes amb detalls de la distribuïdora + bulk_coop: + name: Compra grupal + description: Informes per a comandes de compra grupal payments: name: Informes de pagament description: Informes per a pagaments @@ -1023,6 +1029,7 @@ ca: this_is_an_estimate: | Els preus que es mostren són només una estimació i es calculen en el moment en què es canvia la subscripció. Si canvieu els preus o les comissions, les comandes s'actualitzaran, però la subscripció continuarà mostrant els valors anteriors. + not_in_open_and_upcoming_order_cycles_warning: "No hi ha cicles de comandes oberts o futurs per a aquest producte." details: details: Detalls invalid_error: Oops! Si us plau ompliu tots els camps obligatoris ... @@ -1037,6 +1044,7 @@ ca: details: Detalls address: Adreça products: 'Productes ' + no_open_or_upcoming_order_cycle: "No hi ha cap cicle de comandes proper" product_already_in_order: Aquest producte ja s'ha afegit a la comanda. Editeu-ne la quantitat directament. orders: number: Número @@ -1128,6 +1136,7 @@ ca: card_could_not_be_saved: no s'ha pogut desar la targeta spree_gateway_error_flash_for_checkout: "Hi ha hagut un problema amb la vostra informació de pagament: %{error}" invoice_billing_address: "Adreça de facturació:" + invoice_column_tax: "IVA" invoice_column_price: "Preu" invoice_column_item: "Article" invoice_column_qty: "Quantitat" @@ -1136,10 +1145,13 @@ ca: invoice_column_price_with_taxes: "Preu total (IVA inclòs)" invoice_column_price_without_taxes: "Preu total (sense impostos)" invoice_column_tax_rate: "Taxa d'impost" + invoice_tax_total: "Total IVA:" tax_invoice: "FACTURA D'IMPOSTOS" tax_total: "Impost total (%{rate}):" total_excl_tax: "Total (impostos exclòs):" total_incl_tax: "Total (impost inclòs):" + abn: "NIF:" + acn: "IVA intracomunitari:" invoice_issued_on: "Factura emesa el:" order_number: "Nombre de factura:" date_of_transaction: "Data de la transacció:" @@ -1233,6 +1245,7 @@ ca: card_securitycode: "Codi de seguretat" card_expiry_date: Data de caducitat card_masked_digit: "X" + card_expiry_abbreviation: "Exp" new_credit_card: "Nova targeta de crèdit" my_credit_cards: Les meves targetes de crèdit add_new_credit_card: 'Afegeix una nova targeta de crèdit ' @@ -1275,6 +1288,7 @@ ca: cookie_analytics_utmc_desc: "No s'utilitza a ga.js. Estableix la interoperabilitat amb urchin.js. Històricament, aquesta cookie funcionava juntament amb la cookie __utmb per determinar si l'usuari estava en una nova sessió / visita." cookie_analytics_utmz_desc: "Emmagatzema l'origen del codi o la campanya que explica com l'usuari ha arribat al vostre lloc. La cookie es crea quan s'executa i s'actualitza la biblioteca javascript cada vegada que s'envien dades a Google Analytics." cookie_matomo_basics_desc: "Les primeres cookies de Matomo per recollir estadístiques." + cookie_matomo_heatmap_desc: "Galeta d'enregistrament de sessió i heatmap de Matomo" cookie_matomo_ignore_desc: "La cookie usada per excloure l'usuari de ser seguit." disabling_cookies_header: "Advertència sobre la desactivació de cookies" disabling_cookies_desc: "Com a usuari sempre podeu permetre, bloquejar o eliminar les cookies Open Food Network o qualsevol altra pàgina web sempre que vulgueu mitjançant el control de configuració del vostre navegador. Cada navegador té una operativa diferent. Aquests són els enllaços:" @@ -1586,6 +1600,7 @@ ca: sell_hubs_detail: "Configura un perfil per a la teva organització alimentària a Katuma. En qualsevol moment, pots modificar i convertir el teu perfil en una botiga de diverses productores." sell_groups_detail: "Configura un directori personalitzat d'empreses (productores i altres empreses alimentàries) per a la teva regió o per a la teva xarxa, organització." sell_user_guide: "Troba més informació a la nostra Guia d'usuari." + sell_listing_price: "Aparèixer a OFN és gratuït. Obrir i executar una botiga a OFN és gratuït. La configuració d'un directori de grup d'OFN per a la vostra organització o xarxa regional és gratuït." sell_embed: "També podem incrustar una botiga OFN al vostre web personalitzat o crear un lloc web personalitzat de la xarxa alimentària per a la teva regió." sell_ask_services: "Pregunta'ns sobre els serveis d'OFN." shops_title: Botigues @@ -1608,6 +1623,7 @@ ca: orders_edit_continue: Continuar comprant orders_edit_checkout: Realitza la compra orders_form_empty_cart: "Cistella buida" + orders_form_subtotal: Subtotal orders_form_admin: Administració i manipulació orders_form_total: Total orders_oc_expired_headline: S'han tancat les comandes per a aquest cicle de comanda @@ -1672,6 +1688,7 @@ ca: error_not_found_in_database: "%{name} no s'ha trobat a la base de dades" error_not_primary_producer: "%{name} no està habilitat com a productora" error_no_permission_for_enterprise: "\"%{name}\": no tens permís per gestionar els productes d'aquesta organització" + item_handling_fees: "Comissions de gestió d'articles (incloses en els totals d'articles)" january: "Gener" february: "Febrer" march: "Març" @@ -1693,6 +1710,7 @@ ca: confirm_password: "Confirma la contrassenya" action_signup: "Registra't ara" welcome_to_ofn: "Benvingut a la xarxa Open Food Network!" + signup_or_login: "Comenceu registrant-vos (o iniciant sessió)" have_an_account: "Ja tens un compte?" action_login: "Inicia la sessió ara." forgot_password: "Has oblidat la contrasenya?" @@ -1806,6 +1824,7 @@ ca: enterprise_final_step: "Pas final!" enterprise_social_text: "Com poden la gent trobar %{enterprise}en línia?" website: "Lloc web" + website_placeholder: "per exemple: katuma.org" facebook: "Facebook" facebook_placeholder: "p .ex: www.facebook.com/NomDeLaPàgina" linkedin: "LinkedIn" @@ -1831,6 +1850,7 @@ ca: registration_finished_headline: "Acabat!" registration_finished_thanks: "Gràcies per omplir els detalls de%{enterprise}." registration_finished_login: "Pots canviar o actualitzar la teva organització en qualsevol moment accedint a Katuma i anant a Admin." + registration_finished_action: "Inici" registration_contact_name: 'Nom de contacte' registration_type_headline: "El darrer pas per afegir %{enterprise}!" registration_type_question: "Ets productora?" @@ -1865,11 +1885,14 @@ ca: accounts_and_billing_start_task_notice: "Tasca en cua" fees: "Tarifes" item_cost: "Cost de l'article" + bulk: "A granel" shop_variant_quantity_min: "min" shop_variant_quantity_max: "màx" follow: "Segueix" shop_for_products_html: "Compra productes de %{enterprise} a:" + change_shop: "Canvia la botiga a:" shop_at: "Compra ara a:" + price_breakdown: "Desglossament complet del preu" admin_fee: "Comissió d'administració" sales_fee: "Comissió de venda" packing_fee: "Comissió d'embalatge" @@ -1877,17 +1900,21 @@ ca: fundraising_fee: "Comissió d'autogestió" price_graph: "Gràfic de preus" included_tax: "Impostos incloses" + balance: "Balanç" transaction: "Transacció" transaction_date: "Data" payment_state: "Estat del pagament" shipping_state: "Estat d'enviament" value: "Valor" + balance_due: "A pagar" credit: "Crèdit" Paid: "Pagat" Ready: "Llest" ok: D'acord not_visible: no visible you_have_no_orders_yet: "Encara no tens comandes" + running_balance: "Balanç corrent" + outstanding_balance: "Balanç pendent" admin_enterprise_relationships: "Permisos de l'organització" admin_enterprise_relationships_everything: "Marcar tots" admin_enterprise_relationships_permits: "Permet" @@ -1896,6 +1923,7 @@ ca: admin_enterprise_groups: "Grups d'organització" admin_enterprise_groups_name: "Nom" admin_enterprise_groups_owner: "Propietària" + admin_enterprise_groups_on_front_page: "A la primera pàgina?" admin_enterprise_groups_enterprise: "Organitzacions" admin_enterprise_groups_data_powertip: "La usuària principal responsable d'aquest grup." admin_enterprise_groups_data_powertip_logo: "Això és el logotip del grup" @@ -1920,6 +1948,7 @@ ca: supplier: "Proveïdora" product_name: "Nom del producte" product_description: "Descripció del producte" + units: "Mida d'unitat" coordinator: "Coordinador" distributor: "Distribuïdora" enterprise_fees: "Honoraris de l'organització" @@ -1934,6 +1963,7 @@ ca: flat_rate_per_item: "Tarifa fixa (per article)" flat_rate_per_order: "Tarifa fixa (per comanda)" flexible_rate: "Tarifa Flexible" + price_sack: "Preu sac" new_order_cycles: "Nou cicle de comanda" new_order_cycle: "Nou cicle de comanda" select_a_coordinator_for_your_order_cycle: "Selecciona una coordinadora per al vostre cicle de comanda" @@ -1978,21 +2008,29 @@ ca: spree_admin_overview_check_your_inbox: "Si us plat comproveu la vostra safata d'entrada per obtenir més instruccions. Gràcies!" spree_admin_unit_value: Valor de la unitat spree_admin_unit_description: Descripció de la unitat + spree_admin_variant_unit: Unitat de variants + spree_admin_variant_unit_scale: Escala de unitat de variants spree_admin_supplier: Proveïdora spree_admin_product_category: Categoria del producte + spree_admin_variant_unit_name: Nom de unitat de variants change_package: "Canvia el perfil" spree_admin_single_enterprise_hint: "Suggeriment: per permetre que la gent us trobi, activeu la vostra visibilitat" spree_admin_eg_pickup_from_school: "p. ex: 'Recollida al local del grup de consum'" spree_admin_eg_collect_your_order: "p. ex: \"Recolliu la vostra comanda al c/Ample, n. 123'" + spree_classification_primary_taxon_error: "El taxó %{taxon} és el principal taxó d'%{product} i no es pot eliminar" spree_order_availability_error: "La distribuïdora o el cicle de comanda no pot subministrar els productes de la vostra cistella" spree_order_populator_error: "Aquesta distribuïdora o cicle de comanda no pot subministrar tots els productes de la vostra cistella. Si us plau trieu-ne d'altres." spree_order_populator_availability_error: "Aquest producte no està disponible des de la distribuïdora o cicle de comanda seleccionat." spree_distributors_error: "Cal seleccionar almenys un grup" spree_user_enterprise_limit_error: "^ %{email} no està autoritzat a tenir més organitzacions (el límit és %{enterprise_limit})." spree_variant_product_error: ha de tenir com a mínim una variant + your_profil_live: "El teu perfil online" on_ofn_map: "al mapa d'Open Food Network" + see: "Veure" + live: "online" manage: "Gestiona" resend: "Reenviar" + trial: Prova add_and_manage_products: "Afegeix & gestiona productes" add_and_manage_order_cycles: "Afegeix & gestiona cicles de comanda" manage_order_cycles: "Gestiona els cicles de comanda" @@ -2001,10 +2039,16 @@ ca: edit_profile_details_etc: "Canvia la descripció del perfil, les imatges, etc." order_cycle: "Cicle de Comanda" order_cycles: "Cicles de comanda" + enterprise_relationships: "Permisos d'organització" remove_tax: "Suprimeix comissions" + first_name_begins_with: "El nom comença amb" + last_name_begins_with: "El cognom comença amb" + enterprise_tos_link: "Enllaç a les condicions d'ús de l'organització" enterprise_tos_message: "Volem treballar amb persones que comparteixen els nostres objectius i valors. Com a tal, demanem a les noves organitzacions que acceptin la nostra" enterprise_tos_link_text: "Termes del servei." enterprise_tos_agree: "Accepto els Termes i condicions anteriors" + tax_settings: "Configuració fiscal" + products_require_tax_category: "els productes requereixen una categoria fiscal" admin_shared_address_1: "Adreça" admin_shared_address_2: "Adreça (cont.)" admin_share_city: "Municipi" @@ -2075,11 +2119,16 @@ ca: report_header_special_instructions: Instruccions especials report_header_order_number: Número de comanda report_header_date: Data + report_header_confirmation_date: Data de confirmació report_header_tags: Etiquetes report_header_items: Articles report_header_items_total: "Total d'articles %{currency_symbol}" + report_header_taxable_items_total: "Total d'articles subjectes a impostos (%{currency_symbol})" + report_header_sales_tax: "Impost sobre vendes (%{currency_symbol})" report_header_delivery_charge: "Càrrec de lliurament (%{currency_symbol})" report_header_tax_on_delivery: "Impost sobre el lliurament (%{currency_symbol})" + report_header_tax_on_fees: "Impost sobre les tarifes (%{currency_symbol})" + report_header_total_tax: "Impost total (%{currency_symbol})" report_header_enterprise: Organització report_header_customer: Consumidora report_header_customer_code: Codi de la consumidora @@ -2092,23 +2141,32 @@ ca: report_header_variant_unit: Unitat de la variant report_header_total_available: Total disponible report_header_unallocated: Sense assignar + report_header_max_quantity_excess: Quantitat màxima d'excés + report_header_taxons: Taxons report_header_supplier: Proveïdora report_header_producer: Productora + report_header_producer_suburb: barri de la productora report_header_unit: Unitat + report_header_group_buy_unit_quantity: Quantitat de la unitat de compra del grup report_header_cost: Cost report_header_shipping_cost: Despeses d'enviament + report_header_curr_cost_per_unit: Cost per unitat report_header_total_shipping_cost: Cost total d'enviament report_header_payment_method: Mètode de pagament report_header_sells: Ven report_header_visible: Visible report_header_price: Preu + report_header_unit_size: Mida d'unitat report_header_distributor: Distribuïdora report_header_distributor_address: Adreça de la distribuïdora report_header_distributor_city: Ciutat de la distribuïdora report_header_distributor_postcode: Codi postal de la distribuïdora report_header_delivery_address: Adreça de lliurament report_header_delivery_postcode: Codi postal de lliurament + report_header_bulk_unit_size: Mida de la unitat a granel report_header_weight: Pes + report_header_sum_total: Suma total + report_header_date_of_order: Data de la comanda report_header_amount_owing: Import adeutat report_header_amount_paid: Import pagat report_header_units_required: Unitats necessàries @@ -2124,20 +2182,47 @@ ca: report_header_payment_state: Estat del pagament report_header_payment_type: Tipus de pagament report_header_item_price: "Article (%{currency})" + report_header_item_fees_price: "Article + Comissions (%{currency})" + report_header_admin_handling_fees: "Administració i manipulació (%{currency})" + report_header_ship_price: "Enviar (%{currency})" + report_header_pay_fee_price: "Pagar comissió (%{currency})" report_header_total_price: "Total (%{currency})" report_header_product_total_price: "Total del producte (%{currency})" report_header_shipping_total_price: "Enviament total (%{currency})" + report_header_outstanding_balance_price: "Balanç pendent (%{currency})" + report_header_eft_price: "Transferència (%{currency})" report_header_paypal_price: "PayPal (%{currency})" report_header_sku: Número de referència (SKU) report_header_amount: Import + report_header_balance: Balanç + report_header_total_cost: "Cost total" + report_header_total_ordered: Total demanat + report_header_total_max: Máx Total + report_header_total_units: Unitats totals + report_header_sum_max_total: "Suma Max Total" report_header_total_excl_vat: "Total excl. impostos (%{currency_symbol})" report_header_total_incl_vat: "Total incl. impostos (%{currency_symbol})" report_header_temp_controlled: Control de temperatura? report_header_is_producer: Productor? + report_header_not_confirmed: No confirmat + report_header_gst_on_income: IVA sobre ingressos + report_header_gst_free_income: Ingressos sense IVA + report_header_total_untaxable_produce: Total de productes no tributables (sense impostos) + report_header_total_taxable_produce: Total de productes tributables (IVA inclòs) + report_header_total_untaxable_fees: Total de comissions no tributables (sense impostos) + report_header_total_taxable_fees: Total de les comissions imposables (IVA inclòs) + report_header_delivery_shipping_cost: Cost de l'enviament (impostos inclosos) + report_header_transaction_fee: Comissió de transacció (sense impostos) + report_header_total_untaxable_admin: Total d'ajustaments d'administració no imputables (sense impostos) + report_header_total_taxable_admin: Total d'ajustaments administratius imposables (impostos inclosos) initial_invoice_number: "Número de la comanda inicial:" invoice_date: "Data del comprovant de compra:" + due_date: "Data de venciment:" + account_code: "Codi del compte:" + equals: "És igual a" contains: "conté" discount: "Descompte" + filter_products: "Productes de filtre" delete_product_variant: "L'última variant no es pot esborrar!" progress: "progressió" saving: "Desant..." @@ -2168,12 +2253,19 @@ ca: tag_rules: "Regles d'etiqueta" shop_preferences: "Preferències de la botiga" enterprise_fee_whole_order: Comanda sencera + enterprise_fee_by: "Comissió %{type} per %{role} %{enterprise_name}" validation_msg_relationship_already_established: "^ Aquesta relació ja està establerta." validation_msg_at_least_one_hub: "^ Cal seleccionar com a mínim un grup" validation_msg_product_category_cant_be_blank: "^ La categoria de producte no pot estar en blanc" + validation_msg_tax: "^ La categoria d'impostos és obligatòria" + validation_msg_tax_category_cant_be_blank: "^ La categoria d'impostos no es pot deixar en blanc" validation_msg_is_associated_with_an_exising_customer: "Està associada amb una consumidora existent" + content_configuration_pricing_table: "(TODO: Taula de preus)" + content_configuration_case_studies: "(TODO: Casos d'estudi)" + content_configuration_detail: "(TODO: detall)" enterprise_name_error: "ja ha estat agafat. Si aquesta és la vostra organització i voleu reclamar-ne la propietat o si voleu comerciar amb aquesta organització, poseu-vos en contacte amb l'administradora actual d'aquest perfil %{email}." enterprise_owner_error: "^ %{email} no està autoritzat a tenir més organitzacions (el límit és %{enterprise_limit})." + enterprise_role_uniqueness_error: "^ Aquest rol ja està present." inventory_item_visibility_error: ha de ser veritable o falsa product_importer_file_error: "error: no s'ha carregat cap fitxer" product_importer_spreadsheet_error: "no s'ha pogut processar el fitxer: tipus de fitxer no vàlid" @@ -2182,7 +2274,17 @@ ca: product_import_no_data_in_spreadsheet_notice: 'No s''ha trobat cap dada al full de càlcul' order_choosing_hub_notice: El teu grup ha estat seleccionat. order_cycle_selecting_notice: S'ha seleccionat el teu cicle de comanda. + adjustments_tax_rate_error: "^ Si us plau, comproveu que el tipus impositiu d'aquest ajust és correcte." + active_distributors_not_ready_for_checkout_message_singular: >- + El grup %{distributor_names} apareix en un cicle de comanda actiu, però no té + mètodes de pagament i enviament vàlids. Fins que no ho configureu, els clients + no podran comprar en aquest grup. + active_distributors_not_ready_for_checkout_message_plural: >- + Els grups %{distributor_names} s'enumeren en un cicle de comandes actiu, però + no tenen mètodes de pagament i enviament vàlids. Fins que no ho configureu, + els clients no podran comprar en aquests grups. enterprise_fees_update_notice: S'han actualitzat les comissions de l'organització. + enterprise_fees_destroy_error: "Aquesta comissió d'organització no es pot eliminar perquè està referenciat per una distribució de producte: %{id} - %{name}." enterprise_register_package_error: "Si us plau, selecciona un perfil" enterprise_register_error: "No es pot completar el registre de %{enterprise}" enterprise_register_success_notice: "Enhorabona! El registre de %{enterprise} s'ha completat!" @@ -2221,6 +2323,7 @@ ca: choose: Escull resolve_errors: Si us plau, resol els errors següents more_items: "+ %{count} Més" + default_card_updated: Targeta predeterminada actualitzada admin: enterprise_limit_reached: "Has assolit el límit estàndard d'organitzacions per compte. Escriu a %{contact_email} si necessites augmentar-lo." modals: @@ -2337,6 +2440,8 @@ ca: non_producer_desc: Totes les altres organitzacions alimentàries non_producer_example: 'p. ex: botigues de queviures, cooperatives d''aliments, grups de compra...' enterprise_status: + status_title: "%{name} està configurat i llest!" + severity: Severitat description: Descripció resolve: Resoldre new_tag_rule_dialog: @@ -2344,6 +2449,11 @@ ca: orders: index: per_page: "%{results} per pàgina" + view_file: Mostra el fitxer + compiling_invoices: Generant factures + bulk_invoice_created: S'ha creat la factura conjunta + bulk_invoice_failed: No s'ha pogut crear la factura conjunta + please_wait: Espereu fins que el PDF estigui preparat abans de tancar aquesta finestra. resend_user_email_confirmation: resend: "Reenviar" sending: "Reenviar..." @@ -2368,6 +2478,9 @@ ca: hidden_products: "Productes ocults" new_products: "Nous productes" reset_stock_levels: Restablir els nivells d'existències a valors predeterminats + changes_to: Canvis a + one_override: una sobreescriptura + overrides: sobreescriptures remain_unsaved: romanen sense desar. no_changes_to_save: No hi ha canvis per desar. no_authorisation: "No he pogut obtenir l'autorització per guardar aquests canvis, de manera que romanen sense desar." @@ -2391,6 +2504,10 @@ ca: add_products_to_inventory: "afegeix productes a l'inventari" resources: could_not_delete_customer: 'No s''ha pogut eliminar la consumidora' + product_import: + confirmation: | + Això establirà el nivell d'estoc a zero en tots els productes per aquesta + organització que no estan presents al fitxer carregat. order_cycles: create_failure: "No s'ha pogut crear el cicle de comanda" update_success: 'S''ha actualitzat el cicle de comanda.' @@ -2420,8 +2537,12 @@ ca: header: store: Botiga admin: + product_properties: + index: + inherits_properties_checkbox_hint: "heredar propietats de %{supplier}? (llevat que es sobreescrigui a dalt)" orders: index: + listing_orders: "Llistant comandes" new_order: "Nova comanda" capture: "Captura" ship: "Enviament" @@ -2429,11 +2550,13 @@ ca: note: "Nota" first: "Primer" last: "Últim" + previous: "Anterior" next: "Següent" loading: "S'està carregant" no_orders_found: "No s'ha trobat cap comanda" results_found: "%{number} Resultats trobats." viewing: "Veient %{start} a %{end}." + print_invoices: "Imprimir factures" invoice: issued_on: Publicat a tax_invoice: FACTURA D'IMPOSTOS @@ -2467,6 +2590,7 @@ ca: stripe_disabled_msg: Els pagaments de Stripe han estat inhabilitat per l'administrador del sistema. request_failed_msg: Ho sentim. S'ha produït un error en provar de verificar els detalls del compte amb Stripe... account_missing_msg: No hi ha cap compte de Stripe per a aquesta organització. + connect_one: Connecta un access_revoked_msg: S'ha revocat l'accés a aquest compte de Stripe, si us plau torna a connectar el teu compte. status: Estat connected: Connectat @@ -2481,6 +2605,7 @@ ca: products: new: title: 'Nou producte' + unit_name_placeholder: 'per exemple: manat' index: header: title: Edició de productes en bloc @@ -2496,15 +2621,27 @@ ca: tax_category: Categoria d'impostos inherits_properties?: Hereda propietats? available_on: Disponible el + av_on: "Disp. via" + import_date: "Data d'importació" products_variant: + variant_has_n_overrides: "Aquesta variant té %{n} sobreescriptura/es" new_variant: "Nova variant" product_name: Nom del producte primary_taxon_form: product_category: Categoria del producte group_buy_form: group_buy: "Compra en grup?" + bulk_unit_size: Mida de la unitat a granel display_as: display_as: Mostra com + reports: + table: + select_and_search: "Seleccioneu els filtres i feu clic a %{option} per accedir a les vostres dades." + bulk_coop: + bulk_coop_supplier_report: 'Compra grupal - Totals per proveïdor' + bulk_coop_allocation: 'Compra grupal - Assignació' + bulk_coop_packing_sheets: 'Compra grupal - Fulls de preparació de cistelles' + bulk_coop_customer_payments: 'Compra grupal - Pagaments de les consumidores' users: index: listing_users: "Llistat d'usuàries" @@ -2530,10 +2667,13 @@ ca: producer_name: Productor general_settings: edit: + legal_settings: "Paràmetres legals" cookies_consent_banner_toggle: "Mostra banner de consentiment de cookies" privacy_policy_url: "URL de la política de privacitat " + enterprises_require_tos: "Les organitzacions han d'acceptar les condicions del servei" cookies_policy_matomo_section: "Mostra la secció de Matomo a la pàgina de política de cookies" cookies_policy_ga_section: "Mostra la secció de Google Analytics a la pàgina de política de cookies" + footer_tos_url: "URL de les condicions d'ús" checkout: payment: stripe: @@ -2542,6 +2682,9 @@ ca: used_saved_card: "Utilitza una targeta desada:" or_enter_new_card: "O bé introdueix els detalls d'una nova targeta:" remember_this_card: Recordar aquesta targeta? + date_picker: + format: '%d-% m-% Y' + js_format: 'dd-mm-yy' inventory: Inventari orders: edit: @@ -2574,7 +2717,9 @@ ca: paused: en pausa canceled: cancel·lat payment_states: + balance_due: a pagar completed: completat + checkout: fer comanda credit_owed: crèdit a deure failed: error paid: pagat diff --git a/config/locales/en.yml b/config/locales/en.yml index 3c4b35ebbd..d7afd039af 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2711,6 +2711,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using date_end_before_start_error: "must be after start" parameter_not_allowed_error: "You are not authorized to use one or more selected filters for this report." fee_calculated_on_transfer_through_all: "All" + fee_calculated_on_transfer_through_entire_orders: "Entire Orders through %{distributor}" + tax_category_various: "Various" fee_type: payment_method: "Payment Transaction" shipping_method: "Shipment" diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index bd4793017b..093c19f46b 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -31,6 +31,16 @@ en_GB: on_demand_but_count_on_hand_set: "must be blank if on demand" limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Start" + end_at: "End" + distributor_ids: "Hubs" + producer_ids: "Pricing" + order_cycle_ids: "Order Cycles" + enterprise_fee_ids: "Fees Names" + shipping_method_ids: "Shipping Methods" + payment_method_ids: "Payment Methods" errors: models: subscription_validator: @@ -74,6 +84,13 @@ en_GB: models: order_cycle: cloned_order_cycle_name: "COPY OF %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "must be a string" + invalid_format_error: "must be valid" + integer_array_validator: + not_array_error: "must be an array" + invalid_element_error: "must contain only valid integers" enterprise_mailer: confirmation_instructions: subject: "Please confirm the email address for %{enterprise}" @@ -994,6 +1011,9 @@ en_GB: description: Invoices for import into Xero packing: name: Packing Reports + enterprise_fee_summary: + name: "Enterprise Fee Summary" + description: "Summary of Enterprise Fees collected" subscriptions: subscriptions: Subscriptions new: New Subscription @@ -1027,6 +1047,7 @@ en_GB: this_is_an_estimate: | The displayed prices are only an estimate and calculated at the time the subscription is changed. If you change prices or fees, orders will be updated, but the subscription will still display the old values. + not_in_open_and_upcoming_order_cycles_warning: "There are no open or upcoming order cycles for this product." details: details: Details invalid_error: Oops! Please fill in all of the required fields... @@ -1041,6 +1062,7 @@ en_GB: details: Details address: Address products: Products + no_open_or_upcoming_order_cycle: "No Upcoming Order Cycle" product_already_in_order: This product has already been added to the order. Please edit the quantity directly. orders: number: Number @@ -1159,8 +1181,8 @@ en_GB: menu_1_url: "/shops" menu_2_title: "Map" menu_2_url: "/map" - menu_3_title: "Services" - menu_3_url: "https://about.openfoodnetwork.org.uk/services" + menu_3_title: "Pricing" + menu_3_url: "https://about.openfoodnetwork.org.uk/pricing-and-plans" menu_4_title: "Groups" menu_4_url: "/groups" menu_5_title: "About" @@ -1996,7 +2018,6 @@ en_GB: spree_admin_enterprises_none_text: "You don't have any enterprises yet" spree_admin_enterprises_tabs_hubs: "HUBS" spree_admin_enterprises_producers_manage_products: "MANAGE PRODUCTS" - spree_admin_enterprises_any_active_products_text: "You don't have any active products." spree_admin_enterprises_create_new_product: "CREATE A NEW PRODUCT" spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" @@ -2525,6 +2546,45 @@ en_GB: producers: signup: start_free_profile: "Start with a free profile, and expand when you're ready!" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "must be after start" + parameter_not_allowed_error: "You are not authorised to use one or more selected filters for this report." + fee_calculated_on_transfer_through_all: "All" + fee_calculated_on_transfer_through_entire_orders: "Entire Orders through %{distributor}" + tax_category_various: "Various" + fee_type: + payment_method: "Payment Transaction" + shipping_method: "Shipment" + fee_placements: + supplier: "Incoming" + distributor: "Outgoing" + coordinator: "Coordinator" + tax_category_name: + shipping_instance_rate: "Platform Rate" + formats: + csv: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + html: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + invalid_filter_parameters: "The filters you selected for this report are invalid." spree: email: Email account_updated: "Account updated!" @@ -2568,6 +2628,11 @@ en_GB: distributor: "Distributor:" order_cycle: "Order cycle:" overview: + products: + active_products: + zero: "You don't have any active products." + one: "You have one active product" + other: "You have %{count} active products" order_cycles: order_cycles: "Order Cycles" order_cycles_tip: "Order cycles determine when and where your products are available to customers." @@ -2641,6 +2706,14 @@ en_GB: bulk_coop_allocation: 'Bulk Co-op - Allocation' bulk_coop_packing_sheets: 'Bulk Co-op - Packing Sheets' bulk_coop_customer_payments: 'Bulk Co-op - Customer Payments' + enterprise_fee_summaries: + filters: + date_range: "Date Range" + report_format_csv: "Download as CSV" + generate_report: "Generate Report" + report: + none: "None" + select_and_search: "Select filters and click on GENERATE REPORT to access your data." users: index: listing_users: "Listing Users" diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index da6d82eddd..3028756fbe 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -31,6 +31,16 @@ en_US: on_demand_but_count_on_hand_set: "must be blank if on demand" limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Start" + end_at: "End" + distributor_ids: "Hubs" + producer_ids: "Producers" + order_cycle_ids: "Order Cycles" + enterprise_fee_ids: "Fee Names" + shipping_method_ids: "Shipping Methods" + payment_method_ids: "Payment Methods" errors: models: subscription_validator: @@ -74,6 +84,13 @@ en_US: models: order_cycle: cloned_order_cycle_name: "COPY OF %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "must be a string" + invalid_format_error: "must be valid" + integer_array_validator: + not_array_error: "must be an array" + invalid_element_error: "must contain only valid integers" enterprise_mailer: confirmation_instructions: subject: "Please confirm the email address for %{enterprise}" @@ -994,6 +1011,9 @@ en_US: description: Invoices for import into Xero packing: name: Packing Reports + enterprise_fee_summary: + name: "Enterprise Fee Summary" + description: "Summary of Enterprise Fees collected" subscriptions: subscriptions: Subscriptions new: New Subscription @@ -1027,6 +1047,7 @@ en_US: this_is_an_estimate: | The displayed prices are only an estimate and calculated at the time the subscription is changed. If you change prices or fees, orders will be updated, but the subscription will still display the old values. + not_in_open_and_upcoming_order_cycles_warning: "There are no open or upcoming order cycles for this product." details: details: Details invalid_error: Oops! Please fill in all of the required fields... @@ -1041,6 +1062,7 @@ en_US: details: Details address: Address products: Products + no_open_or_upcoming_order_cycle: "No Upcoming Order Cycles" product_already_in_order: This product has already been added to the order. Please edit the quantity directly. orders: number: Number @@ -1996,7 +2018,6 @@ en_US: spree_admin_enterprises_none_text: "You don't have any enterprises yet" spree_admin_enterprises_tabs_hubs: "HUBS" spree_admin_enterprises_producers_manage_products: "MANAGE PRODUCTS" - spree_admin_enterprises_any_active_products_text: "You don't have any active products." spree_admin_enterprises_create_new_product: "CREATE A NEW PRODUCT" spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" @@ -2519,6 +2540,45 @@ en_US: producers: signup: start_free_profile: "Start with a free profile, and expand when you're ready!" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "must be after start" + parameter_not_allowed_error: "You are not authorized to use one or more selected filters for this report." + fee_calculated_on_transfer_through_all: "All" + fee_calculated_on_transfer_through_entire_orders: "Entire Orders through %{distributor}" + tax_category_various: "Various" + fee_type: + payment_method: "Payment Transaction" + shipping_method: "Shipment" + fee_placements: + supplier: "Incoming" + distributor: "Outgoing" + coordinator: "Coordinator" + tax_category_name: + shipping_instance_rate: "Platform Rate" + formats: + csv: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + html: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + invalid_filter_parameters: "The filters you selected for this report are invalid." spree: email: Email account_updated: "Account updated!" @@ -2562,6 +2622,11 @@ en_US: distributor: "Distributor:" order_cycle: "Order cycle:" overview: + products: + active_products: + zero: "You don't have any active products." + one: "You have one active product" + other: "You have %{count} active products" order_cycles: order_cycles: "Order Cycles" order_cycles_tip: "Order cycles determine when and where your products are available to customers." @@ -2635,6 +2700,14 @@ en_US: bulk_coop_allocation: 'Bulk Co-op - Allocation' bulk_coop_packing_sheets: 'Bulk Co-op - Packing Sheets' bulk_coop_customer_payments: 'Bulk Co-op - Customer Payments' + enterprise_fee_summaries: + filters: + date_range: "Date Range" + report_format_csv: "Download as CSV" + generate_report: "Generate Report" + report: + none: "None" + select_and_search: "Select filters and click on GENERATE REPORT to access your data." users: index: listing_users: "Listing Users" diff --git a/config/locales/es.yml b/config/locales/es.yml index 489cb0684c..73bddfe067 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -476,6 +476,8 @@ es: inventory_no_permission: no tienes permiso para crear inventario para esta productora none_saved: No se guardó ningún producto con éxito line_number: "Línea %{number}:" + encoding_error: "Verifique la configuración de idioma de su archivo fuente y asegúrese de que esté guardado con la codificación UTF-8" + unexpected_error: "La importación de productos encontró un error inesperado al abrir el archivo: %{error_message}" index: select_file: Selecciona una hoja de cálculo para subir spreadsheet: Hoja de cálculo @@ -1027,6 +1029,7 @@ es: this_is_an_estimate: | Los precios mostrados son solo una estimación y se calculan en el momento en que se cambia la suscripción. Si cambias precios o tarifas, los pedidos se actualizarán pero la suscripción seguirá mostrando los valores anteriores. + not_in_open_and_upcoming_order_cycles_warning: "No hay ciclos de pedidos abiertos o próximos para este producto." details: details: Detalles invalid_error: Ups! Por favor complete todos los campos requeridos ... @@ -1041,6 +1044,7 @@ es: details: Detalles address: Dirección products: Productos + no_open_or_upcoming_order_cycle: "No hay ciclo de pedido próximo" product_already_in_order: Este producto ya ha sido agregado a la orden. Por favor edite la cantidad directamente. orders: number: Número @@ -2020,7 +2024,7 @@ es: spree_distributors_error: "Se debe seleccionar al menos un Grupo" spree_user_enterprise_limit_error: "^ %{email} no está autorizado a tener más organizaciones (el límite es %{enterprise_limit})." spree_variant_product_error: Debes tener al menos una variante - your_profil_live: "Tu perfil en directo" + your_profil_live: "Tu perfil online" on_ofn_map: "en el mapa Open Food Network" see: "Ver" live: "En directo" @@ -2037,6 +2041,8 @@ es: order_cycles: "Ciclos de Pedidos" enterprise_relationships: "Permisos de la organización" remove_tax: "Eliminar impuesto" + first_name_begins_with: "El nombre comienza con" + last_name_begins_with: "El apellido comienza con" enterprise_tos_link: "Enlace a los Términos del Servicio de la Organización" enterprise_tos_message: "Queremos trabajar con personas que compartan nuestros objetivos y valores. Por ello, pedimos a las nuevas organizaciones que acepten" enterprise_tos_link_text: "Términos del Servicio." @@ -2184,7 +2190,7 @@ es: report_header_product_total_price: "Producto Total (%{currency})" report_header_shipping_total_price: "Total envíos (%{currency})" report_header_outstanding_balance_price: "Saldo pendiente (%{currency})" - report_header_eft_price: "EFT (%{currency})" + report_header_eft_price: "Transferencia (%{currency})" report_header_paypal_price: "PayPal (%{currency})" report_header_sku: SKU report_header_amount: Cantidad @@ -2444,6 +2450,11 @@ es: orders: index: per_page: "%{results} por página" + view_file: Ver archivo + compiling_invoices: Generando facturas + bulk_invoice_created: Factura conjunta creada + bulk_invoice_failed: Error al crear la factura conjunta + please_wait: Por favor, espere hasta que el PDF esté listo antes de cerrar esta ventana. resend_user_email_confirmation: resend: "Reenviar" sending: "Reenviar..." @@ -2546,6 +2557,7 @@ es: no_orders_found: "No se encontraron pedidos" results_found: "%{number} Resultados encontrados." viewing: "Viendo de %{start}a %{end}." + print_invoices: "Imprimir facturas" invoice: issued_on: Emitido el tax_invoice: FACTURA DE IMPUESTOS @@ -2624,6 +2636,8 @@ es: display_as: display_as: Mostrar como reports: + table: + select_and_search: "Seleccione filtros y haga clic en %{option} para acceder a sus datos." bulk_coop: bulk_coop_supplier_report: 'Bulk Co-op - Totales por Proveedor' bulk_coop_allocation: 'Bulk Co-op - Asignación' diff --git a/config/locales/fr.yml b/config/locales/fr.yml index eb434d850e..cbc55cab18 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -31,6 +31,16 @@ fr: on_demand_but_count_on_hand_set: "doit être vide si \"à volonté\"" limited_stock_but_no_count_on_hand: "doit être spécifié car pas \"à volonté\"" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Début" + end_at: "Fin" + distributor_ids: "Hubs" + producer_ids: "Producteurs" + order_cycle_ids: "Cycles de vente" + enterprise_fee_ids: "Nom des marges et commissions" + shipping_method_ids: "Méthodes de livraison" + payment_method_ids: "Méthodes de paiement" errors: models: subscription_validator: @@ -74,6 +84,13 @@ fr: models: order_cycle: cloned_order_cycle_name: "Copie de %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "doit être une série" + invalid_format_error: "doit être valide" + integer_array_validator: + not_array_error: "doit être une séquence" + invalid_element_error: "doit contenir uniquement des nombres entiers" enterprise_mailer: confirmation_instructions: subject: "Confirmez l'adresse email pour %{enterprise}" @@ -995,6 +1012,9 @@ fr: description: Factures pour import dans Xero packing: name: Rapports de préparation des paniers + enterprise_fee_summary: + name: "Résumé des marges et commissions" + description: "Résumé des marges et commissions collectées" subscriptions: subscriptions: Abonnements new: Nouvel abonnement @@ -1028,6 +1048,7 @@ fr: this_is_an_estimate: | Les prix affichés ne sont qu'une estimation calculée au moment de la dernière modification de l'abonnement. Si vous modifiez les prix ou marges et commissions appliquées, les commandes seront mises à jour, mais l'abonnement affichera toujours les anciennes valeurs. + not_in_open_and_upcoming_order_cycles_warning: "Il n'y a pas de cycle de vente ouvert ou à venir pour ce produit." details: details: Informations invalid_error: Oups! Veuillez remplir tous les champs obligatoires... @@ -1042,6 +1063,7 @@ fr: details: Informations address: Adresse products: Produits + no_open_or_upcoming_order_cycle: "Pas de cycles de vente prévus." product_already_in_order: Ce produit a déjà été ajouté à la commande. Veuillez directement modifier la quantité. orders: number: Nombre @@ -1997,7 +2019,6 @@ fr: spree_admin_enterprises_none_text: "Vous n'avez pas encore d'entreprise" spree_admin_enterprises_tabs_hubs: "HUBS" spree_admin_enterprises_producers_manage_products: "GÉRER LES PRODUITS" - spree_admin_enterprises_any_active_products_text: "Vous n'avez aucun produit actif." spree_admin_enterprises_create_new_product: "CRÉER UN NOUVEAU PRODUIT" spree_admin_single_enterprise_alert_mail_confirmation: "Veuillez confirmer l'adresse mail pour" spree_admin_single_enterprise_alert_mail_sent: "Email envoyé à " @@ -2531,6 +2552,45 @@ fr: producers: signup: start_free_profile: "Commencez par créer votre profil entreprise, et changez de formule quand vous êtes prêt !" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "doit être après date début" + parameter_not_allowed_error: "Vous n'êtes pas authorisé.e à utiliser un ou plusieurs des filtres sélectionnés pour ce rapport." + fee_calculated_on_transfer_through_all: "Tous" + fee_calculated_on_transfer_through_entire_orders: "Commandes complètes via %{distributor}" + tax_category_various: "Multiples" + fee_type: + payment_method: "Méthodes de paiement" + shipping_method: "Livraison" + fee_placements: + supplier: "Produits entrants (pouvant être mis en vente par les hubs)" + distributor: "Produits sortants (mis en vente par/via un ou plusieurs hubs)" + coordinator: "Coordinateur" + tax_category_name: + shipping_instance_rate: "Marge de la plateforme" + formats: + csv: + header: + fee_type: "Type de marge" + enterprise_name: "Gestionnaire de l'entreprise" + fee_name: "Nom de la marge/commission" + customer_name: "Acheteur" + fee_placement: "Capture des frais" + fee_calculated_on_transfer_through_name: "Calc. des frais sur les transferts via" + tax_category_name: "TVA applicable" + total_amount: "€ total" + html: + header: + fee_type: "Type de marge" + enterprise_name: "Gestionnaire de l'entreprise" + fee_name: "Nom de la marge/commission" + customer_name: "Acheteur" + fee_placement: "Saisie de la marge" + fee_calculated_on_transfer_through_name: "Calc. des frais sur les transferts via" + tax_category_name: "TVA applicable" + total_amount: "€ total" + invalid_filter_parameters: "Les filtres sélectionnés pour ce rapport sont invalides." spree: email: Email account_updated: "Compte mis à jour!" @@ -2574,6 +2634,11 @@ fr: distributor: "Distributeur : " order_cycle: "Cycle de vente : " overview: + products: + active_products: + zero: "Vous n'avez aucun produit actif." + one: "Vous avez un produit actif" + other: "Vous avez %{count} produits actifs" order_cycles: order_cycles: "Cycles de vente" order_cycles_tip: "Les cycles de vente définissent quand et où vos produits peuvent être commandés par vos acheteurs." @@ -2647,6 +2712,14 @@ fr: bulk_coop_allocation: 'Achats groupés - Allocation' bulk_coop_packing_sheets: 'Achats groupés - Feuilles de préparation des paniers' bulk_coop_customer_payments: 'Achats groupés - Paiement des acheteurs' + enterprise_fee_summaries: + filters: + date_range: "Période" + report_format_csv: "Télécharger le CSV" + generate_report: "Générer le rapport" + report: + none: "Aucun" + select_and_search: "Sélectionnez les filtres et cliquez sur \"générer le rapport\" pour accéder aux données." users: index: listing_users: "Liste des utilisateurs" diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index cf2a74f12c..632c999f43 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -31,6 +31,16 @@ fr_CA: on_demand_but_count_on_hand_set: "doit être vide si \"à volonté\"" limited_stock_but_no_count_on_hand: "doit être spécifié car pas \"à volonté\"" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Début" + end_at: "Fin" + distributor_ids: "Hubs" + producer_ids: "Producteurs" + order_cycle_ids: "Cycles de Vente" + enterprise_fee_ids: "Nom des marges et commissions" + shipping_method_ids: "Méthodes de livraison" + payment_method_ids: "Méthodes de paiement" errors: models: subscription_validator: @@ -75,6 +85,13 @@ fr_CA: models: order_cycle: cloned_order_cycle_name: "Copie de %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "doit être une série" + invalid_format_error: "doit être valide" + integer_array_validator: + not_array_error: "doit être une séquence" + invalid_element_error: "doit contenir uniquement des nombres entiers" enterprise_mailer: confirmation_instructions: subject: "Confirmez l'adresse email pour %{enterprise}" @@ -996,6 +1013,9 @@ fr_CA: description: Factures pour import dans Xero packing: name: Rapports de préparation des paniers + enterprise_fee_summary: + name: "Résumé des marges et commissions" + description: "Résumé des marges et commissions collectées" subscriptions: subscriptions: Abonnements new: Nouvel abonnement @@ -1028,6 +1048,7 @@ fr_CA: subscription_line_items: this_is_an_estimate: | Les prix affichés ne sont qu'une estimation calculée au moment de la dernière modification de l'abonnement.Si vous modifiez les prix ou marges et commissions appliquées, les commandes seront mises à jour, mais l'abonnement affichera toujours les anciennes valeurs. + not_in_open_and_upcoming_order_cycles_warning: "Il n'y a pas de cycle de vente ouvert ou à venir pour ce produit." details: details: Informations invalid_error: Oups! Veuillez remplir tous les champs obligatoires... @@ -1042,6 +1063,7 @@ fr_CA: details: Informations address: Adresse products: Produits + no_open_or_upcoming_order_cycle: "Pas de cycles de vente prévus." product_already_in_order: Ce produit a déjà été ajouté à la commande. Veuillez directement modifier la quantité. orders: number: Nombre @@ -1997,7 +2019,6 @@ fr_CA: spree_admin_enterprises_none_text: "Vous n'avez pas encore d'entreprise" spree_admin_enterprises_tabs_hubs: "HUBS" spree_admin_enterprises_producers_manage_products: "GÉRER LES PRODUITS" - spree_admin_enterprises_any_active_products_text: "Vous n'avez aucun produit actif." spree_admin_enterprises_create_new_product: "CRÉER UN NOUVEAU PRODUIT" spree_admin_single_enterprise_alert_mail_confirmation: "Veuillez confirmer l'adresse mail pour" spree_admin_single_enterprise_alert_mail_sent: "Email envoyé à " @@ -2531,6 +2552,45 @@ fr_CA: producers: signup: start_free_profile: "Commencez par créer votre profil entreprise, c'est gratuit, et changez de formule quand vous êtes prêt !" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "doit être après date début" + parameter_not_allowed_error: "Vous n'êtes pas authorisé.e à utiliser un ou plusieurs des filtres sélectionnés pour ce rapport." + fee_calculated_on_transfer_through_all: "Tous" + fee_calculated_on_transfer_through_entire_orders: "Commandes complètes via %{distributor}" + tax_category_various: "Multiples" + fee_type: + payment_method: "Méthodes de paiement" + shipping_method: "Livraison" + fee_placements: + supplier: "Produits entrants (pouvant être mis en vente par les hubs)" + distributor: "Produits sortants (mis en vente par/via un ou plusieurs hubs)" + coordinator: "Coordinateur" + tax_category_name: + shipping_instance_rate: "Marge de la plateforme" + formats: + csv: + header: + fee_type: "Type de commissions" + enterprise_name: "Gestionnaire de l'entreprise" + fee_name: "Nom de la marge/commission" + customer_name: "Acheteur" + fee_placement: "Capture des frais" + fee_calculated_on_transfer_through_name: "Calc. des frais sur les transferts via" + tax_category_name: "Type de taxe" + total_amount: "$$ TOTAL" + html: + header: + fee_type: "Type de commissions" + enterprise_name: "Gestionnaire de l'entreprise" + fee_name: "Nom de la marge/commission" + customer_name: "Acheteur" + fee_placement: "Saisie de la marge" + fee_calculated_on_transfer_through_name: "Calc. des frais sur les transferts via" + tax_category_name: "Type de taxe" + total_amount: "$$ TOTAL" + invalid_filter_parameters: "Les filtres sélectionnés pour ce rapport sont invalides." spree: email: Email account_updated: "Compte mis à jour!" @@ -2574,6 +2634,11 @@ fr_CA: distributor: "Distributeur : " order_cycle: "Cycle de vente : " overview: + products: + active_products: + zero: "Vous n'avez aucun produit actif." + one: "Vous avez un produit actif" + other: "Vous avez %{count}produits actifs" order_cycles: order_cycles: "Cycles de vente" order_cycles_tip: "Les cycles de vente définissent quand et où vos produits peuvent être commandés par vos acheteurs." @@ -2647,6 +2712,14 @@ fr_CA: bulk_coop_allocation: 'Achats groupés - Allocation' bulk_coop_packing_sheets: 'Achats groupés - Feuilles de préparation des paniers' bulk_coop_customer_payments: 'Achats groupés - Paiement des acheteurs' + enterprise_fee_summaries: + filters: + date_range: "Période" + report_format_csv: "Télécharger en csv" + generate_report: "Générer le rapport" + report: + none: "Aucun" + select_and_search: "Sélectionnez les filtres et cliquez sur \"générer le rapport\" pour accéder aux données." users: index: listing_users: "Liste des utilisateurs" diff --git a/config/locales/nb.yml b/config/locales/nb.yml index 8490c91c8f..621427608a 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -31,6 +31,16 @@ nb: on_demand_but_count_on_hand_set: "må være tom hvis på forespørsel" limited_stock_but_no_count_on_hand: "må spesifiseres fordi det tvinger begrenset lager" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Start" + end_at: "Slutt" + distributor_ids: "Hubs" + producer_ids: "Produsenter" + order_cycle_ids: "Bestillingsrunder" + enterprise_fee_ids: "Avgiftsnavn" + shipping_method_ids: "Leveringsmetoder" + payment_method_ids: "Betalingsmetoder" errors: models: subscription_validator: @@ -74,6 +84,13 @@ nb: models: order_cycle: cloned_order_cycle_name: "KOPI AV %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "må være en streng" + invalid_format_error: "må være gyldig" + integer_array_validator: + not_array_error: "må være en tabell" + invalid_element_error: "må kun inneholde gyldige heltall" enterprise_mailer: confirmation_instructions: subject: "Vennligst bekreft epostadressen til %{enterprise}" @@ -256,7 +273,7 @@ nb: shop: Butikk sku: SKU status_state: Tilstand - tags: Merker + tags: Merkelapper variant: Variant weight: Vekt volume: Volum @@ -314,7 +331,7 @@ nb: embedded_shopfronts_whitelist: "Hviteliste for Eksterne Domener" number_localization: number_localization_settings: "Innstillinger for nummerlokalisering" - enable_localized_number: "Bruk den internasjonale tusen/desimal tegn-logikken" + enable_localized_number: "Bruk internasjonale tusen- og desimalskilletegn" business_model_configuration: edit: business_model_configuration: "Forretningsmodell" @@ -747,7 +764,7 @@ nb: no_rules_yet: Ingen regler gjelder for dette emnet ennå for_customers_tagged: 'For kunder merket:' add_new_rule: '+ Legg til En Ny Regel' - add_new_tag: '+ Legg til Et Nytt Merke' + add_new_tag: '+ Legg til ny Merkelapp' users: email_confirmation_notice_html: "Epostbekreftelse venter. Vi har sendt en bekreftelsesepost til %{email}." resend: Send på nytt @@ -875,7 +892,7 @@ nb: outgoing: Utgående distributor: Distributør products: Produkter - tags: Merker + tags: Merkelapper add_a_tag: Legg til en merkelapp delivery_details: Hente-/Leveringsdetaljer debug_info: Debuginformasjon @@ -994,6 +1011,9 @@ nb: description: Fakturaer for import til Xero packing: name: Pakkerapporter + enterprise_fee_summary: + name: "Sammendrag Bedriftsavgift" + description: "Sammendrag av bedriftsavgifter innsamlet" subscriptions: subscriptions: Abonnement new: Nytt abonnement @@ -1998,7 +2018,6 @@ nb: spree_admin_enterprises_none_text: "Du har ingen bedrifter ennå" spree_admin_enterprises_tabs_hubs: "HUBS" spree_admin_enterprises_producers_manage_products: "ADMINISTRER PRODUKTER" - spree_admin_enterprises_any_active_products_text: "Du har ingen aktive produkter." spree_admin_enterprises_create_new_product: "OPPRETT NYTT PRODUKT" spree_admin_single_enterprise_alert_mail_confirmation: "Vennligst bekreft epostadressen for" spree_admin_single_enterprise_alert_mail_sent: "Vi har sendt epost til" @@ -2520,6 +2539,45 @@ nb: producers: signup: start_free_profile: "Start med en gratis profil, og utvid når du er klar!" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "må være etter start" + parameter_not_allowed_error: "Du er ikke autorisert til å bruke ett eller flere utvalgte filtre for denne rapporten." + fee_calculated_on_transfer_through_all: "Alle" + fee_calculated_on_transfer_through_entire_orders: "Hele bestillinger gjennom %{distributor}" + tax_category_various: "Diverse" + fee_type: + payment_method: "Betalingstransaksjon" + shipping_method: "Leveranse" + fee_placements: + supplier: "Innkommende" + distributor: "Utgående" + coordinator: "Koordinator" + tax_category_name: + shipping_instance_rate: "Plattformrate" + formats: + csv: + header: + fee_type: "Avfgiftstype" + enterprise_name: "Bedriftseier" + fee_name: "Avgiftsnavn" + customer_name: "Kunde" + fee_placement: "Avgiftsplassering" + fee_calculated_on_transfer_through_name: "Avgift beregnes ved overføring gjennom" + tax_category_name: "Avgiftskategori" + total_amount: "$$ SUM" + html: + header: + fee_type: "Avfgiftstype" + enterprise_name: "Bedriftseier" + fee_name: "Avgiftsnavn" + customer_name: "Kunde" + fee_placement: "Avgiftsplassering" + fee_calculated_on_transfer_through_name: "Avgift beregnes ved overføring gjennom" + tax_category_name: "Avgiftskategori" + total_amount: "$$ SUM" + invalid_filter_parameters: "Filtrene du valgte for denne rapporten er ugyldige." spree: email: Epost account_updated: "Konto oppdatert!" @@ -2563,6 +2621,11 @@ nb: distributor: "Distributør:" order_cycle: "Bestillingsrunde:" overview: + products: + active_products: + zero: "Du har ingen aktive produkter." + one: "Du har ett aktivt produkt" + other: "Du har %{count} aktive produkter" order_cycles: order_cycles: "Bestillingsrunder" order_cycles_tip: "Bestillingsrundene bestemmer når og hvor produktene dine er tilgjengelige for kundene." @@ -2636,6 +2699,14 @@ nb: bulk_coop_allocation: 'Bulk Co-op - Allokering' bulk_coop_packing_sheets: 'Bulk Co-op - Pakkseddel' bulk_coop_customer_payments: 'Bulk Co-op - Kunde Betalinger' + enterprise_fee_summaries: + filters: + date_range: "Datointervall" + report_format_csv: "Last ned som CSV" + generate_report: "Generer rapport" + report: + none: "Ingen" + select_and_search: "Velg filtre og klikk på GENERER RAPPORT for å få tilgang til dataene dine." users: index: listing_users: "Lister opp brukere" diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/coordinator_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/coordinator_fee.rb new file mode 100644 index 0000000000..c186b9aae3 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/coordinator_fee.rb @@ -0,0 +1,27 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for coordinator fees +# in an order cycle. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class CoordinatorFee + include UsingEnterpriseFee + + def fee_calculated_on_transfer_through_name + i18n_translate("fee_calculated_on_transfer_through_all") + end + + def tax_category_name + return data["tax_category_name"] if data["tax_category_name"].present? + i18n_translate("tax_category_various") if inherits_tax_category? + end + + def inherits_tax_category? + data["enterprise_fee_inherits_tax_category"] == "t" + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/exchange_order_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/exchange_order_fee.rb new file mode 100644 index 0000000000..c92d81c361 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/exchange_order_fee.rb @@ -0,0 +1,23 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for exchange fees +# that use order-based calculators. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class ExchangeOrderFee + include UsingEnterpriseFee + + def fee_calculated_on_transfer_through_name + i18n_translate("fee_calculated_on_transfer_through_entire_orders", + distributor: data["adjustment_source_distributor_name"]) + end + + def tax_category_name + data["tax_category_name"] || i18n_translate("tax_category_various") + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/incoming_exchange_line_item_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/incoming_exchange_line_item_fee.rb new file mode 100644 index 0000000000..1bff07b5b0 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/incoming_exchange_line_item_fee.rb @@ -0,0 +1,22 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for incoming +# exchange fees that use line item -based calculators. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class IncomingExchangeLineItemFee + include UsingEnterpriseFee + + def fee_calculated_on_transfer_through_name + data["incoming_exchange_enterprise_name"] + end + + def tax_category_name + data["tax_category_name"] || data["product_tax_category_name"] + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/outgoing_exchange_line_item_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/outgoing_exchange_line_item_fee.rb new file mode 100644 index 0000000000..c4b997c774 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/outgoing_exchange_line_item_fee.rb @@ -0,0 +1,22 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for outgoing +# exchange fees that use line item -based calculators. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class OutgoingExchangeLineItemFee + include UsingEnterpriseFee + + def fee_calculated_on_transfer_through_name + data["outgoing_exchange_enterprise_name"] + end + + def tax_category_name + data["tax_category_name"] || data["product_tax_category_name"] + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/payment_method_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/payment_method_fee.rb new file mode 100644 index 0000000000..ed0d30af1f --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/payment_method_fee.rb @@ -0,0 +1,38 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for payment method +# fees. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class PaymentMethodFee + include WithI18n + + attr_reader :data + + def initialize(data) + @data = data + end + + def fee_type + i18n_translate("fee_type.payment_method") + end + + def enterprise_name + data["hub_name"] + end + + def fee_name + data["payment_method_name"] + end + + def fee_placement; end + + def fee_calculated_on_transfer_through_name; end + + def tax_category_name; end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/shipping_method_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/shipping_method_fee.rb new file mode 100644 index 0000000000..b96fd99e34 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/shipping_method_fee.rb @@ -0,0 +1,40 @@ +# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for shipping method +# fees. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + class ShippingMethodFee + include WithI18n + + attr_reader :data + + def initialize(data) + @data = data + end + + def fee_type + i18n_translate("fee_type.shipping_method") + end + + def enterprise_name + data["hub_name"] + end + + def fee_name + data["shipping_method_name"] + end + + def fee_placement; end + + def fee_calculated_on_transfer_through_name; end + + def tax_category_name + i18n_translate("tax_category_name.shipping_instance_rate") + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/using_enterprise_fee.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/using_enterprise_fee.rb new file mode 100644 index 0000000000..c9c04477df --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/using_enterprise_fee.rb @@ -0,0 +1,40 @@ +# Different EnterpriseFeeSummary::Scope DB result attributes are checked when dealing with +# enterprise fees that are attached to an order cycle in different ways. +# +# This module provides DB result to report mappings that are common among all rows for enterprise +# fees. These mappings are not complete and should be supplemented with mappings that are specific +# to the way that the enterprise fee is attached to the order cycle. + +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + module UsingEnterpriseFee + include WithI18n + + attr_reader :data + + def initialize(data) + @data = data + end + + def fee_type + data["fee_type"].try(:capitalize) + end + + def enterprise_name + data["enterprise_name"] + end + + def fee_name + data["fee_name"] + end + + def fee_placement + i18n_translate("fee_placements.#{data['placement_enterprise_role']}") + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/with_i18n.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/with_i18n.rb new file mode 100644 index 0000000000..8440886020 --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/data_representations/with_i18n.rb @@ -0,0 +1,15 @@ +module OrderManagement + module Reports + module EnterpriseFeeSummary + module DataRepresentations + module WithI18n + private + + def i18n_translate(translation_key, options = {}) + I18n.t("order_management.reports.enterprise_fee_summary.#{translation_key}", options) + end + end + end + end + end +end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/enterprise_fee_type_total_summarizer.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/enterprise_fee_type_total_summarizer.rb deleted file mode 100644 index cd7875f758..0000000000 --- a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/enterprise_fee_type_total_summarizer.rb +++ /dev/null @@ -1,87 +0,0 @@ -module OrderManagement - module Reports - module EnterpriseFeeSummary - class EnterpriseFeeTypeTotalSummarizer - attr_accessor :data - - def initialize(data) - @data = data - end - - def fee_type - if for_payment_method? - i18n_translate("fee_type.payment_method") - elsif for_shipping_method? - i18n_translate("fee_type.shipping_method") - else - data["fee_type"].try(:capitalize) - end - end - - def enterprise_name - if for_payment_method? - data["hub_name"] - elsif for_shipping_method? - data["hub_name"] - else - data["enterprise_name"] - end - end - - def fee_name - if for_payment_method? - data["payment_method_name"] - elsif for_shipping_method? - data["shipping_method_name"] - else - data["fee_name"] - end - end - - def customer_name - data["customer_name"] - end - - def fee_placement - return if for_payment_method? || for_shipping_method? - - i18n_translate("fee_placements.#{data['placement_enterprise_role']}") - end - - def fee_calculated_on_transfer_through_name - return if for_payment_method? || for_shipping_method? - - transfer_through_all_string = i18n_translate("fee_calculated_on_transfer_through_all") - - data["incoming_exchange_enterprise_name"] || data["outgoing_exchange_enterprise_name"] || - (transfer_through_all_string if data["placement_enterprise_role"] == "coordinator") - end - - def tax_category_name - return if for_payment_method? - return i18n_translate("tax_category_name.shipping_instance_rate") if for_shipping_method? - - data["tax_category_name"] || data["product_tax_category_name"] - end - - def total_amount - data["total_amount"] - end - - private - - def for_payment_method? - data["payment_method_name"].present? - end - - def for_shipping_method? - data["shipping_method_name"].present? - end - - def i18n_translate(translation_key) - I18n.t("order_management.reports.enterprise_fee_summary.#{translation_key}") - end - end - end - end -end diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/report_service.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/report_service.rb index 4780629a45..d0fd952161 100644 --- a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/report_service.rb +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/report_service.rb @@ -25,7 +25,7 @@ module OrderManagement def enterprise_fee_type_total_list enterprise_fees_by_customer.map do |total_data| - summarizer = EnterpriseFeeTypeTotalSummarizer.new(total_data) + summarizer = Summarizer.new(total_data) ReportData::EnterpriseFeeTypeTotal.new.tap do |total| enterprise_fee_type_summarizer_to_total_attributes.each do |attribute| diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb index b3f3168518..9ebec61417 100644 --- a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb @@ -31,7 +31,8 @@ module OrderManagement include_payment_fee_details include_shipping_fee_details include_enterprise_fee_details - include_line_item_details + include_order_source_details + include_line_item_source_details include_incoming_exchange_details include_outgoing_exchange_details @@ -169,14 +170,38 @@ module OrderManagement ) end - # If for line item - Use data only if spree_line_items.id is present + # If for order source + # + # Includes: + # * Source order + # * Distributor + def include_order_source_details + join_scope( + <<-JOIN_STRING.strip_heredoc + LEFT OUTER JOIN spree_orders AS adjustment_source_orders + ON ( + spree_adjustments.source_type = 'Spree::Order' + AND adjustment_source_orders.id = spree_adjustments.source_id + ) + JOIN_STRING + ) + + join_scope( + <<-JOIN_STRING.strip_heredoc + LEFT OUTER JOIN enterprises AS adjustment_source_distributors + ON (adjustment_source_distributors.id = adjustment_source_orders.distributor_id) + JOIN_STRING + ) + end + + # If for line item source - Use data only if spree_line_items.id is present # # Includes: # * Line item # * Variant # * Product # * Tax category of product, if enterprise fee tells to inherit - def include_line_item_details + def include_line_item_source_details join_scope( <<-JOIN_STRING.strip_heredoc LEFT OUTER JOIN spree_line_items @@ -316,7 +341,8 @@ module OrderManagement group("enterprise_fees.id", "enterprises.id", "customers.id", "hubs.id", "spree_payment_methods.id", "spree_shipping_methods.id", "adjustment_metadata.enterprise_role", "spree_tax_categories.id", - "product_tax_categories.id", "incoming_exchange_enterprises.id", + "product_tax_categories.id", "spree_adjustments.source_type", + "adjustment_source_distributors.id", "incoming_exchange_enterprises.id", "outgoing_exchange_enterprises.id") end end @@ -331,8 +357,11 @@ module OrderManagement enterprise_fees.fee_type AS fee_type, customers.name AS customer_name, customers.email AS customer_email, enterprise_fees.fee_type AS fee_type, enterprise_fees.name AS fee_name, spree_tax_categories.name AS tax_category_name, + enterprise_fees.inherits_tax_category AS enterprise_fee_inherits_tax_category, product_tax_categories.name AS product_tax_category_name, adjustment_metadata.enterprise_role AS placement_enterprise_role, + spree_adjustments.source_type AS adjustment_source_type, + adjustment_source_distributors.name AS adjustment_source_distributor_name, incoming_exchange_enterprises.name AS incoming_exchange_enterprise_name, outgoing_exchange_enterprises.name AS outgoing_exchange_enterprise_name JOIN_STRING diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/summarizer.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/summarizer.rb new file mode 100644 index 0000000000..ad678dc33b --- /dev/null +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/summarizer.rb @@ -0,0 +1,76 @@ +module OrderManagement + module Reports + module EnterpriseFeeSummary + class Summarizer + attr_reader :data + + delegate :fee_type, :enterprise_name, :fee_name, :fee_placement, + :fee_calculated_on_transfer_through_name, :tax_category_name, to: :representation + + def initialize(data) + @data = data + end + + def customer_name + data["customer_name"] + end + + def total_amount + data["total_amount"] + end + + private + + def representation + @representation ||= representation_klass.new(data) + end + + def representation_klass + return DataRepresentations::PaymentMethodFee if for_payment_method? + return DataRepresentations::ShippingMethodFee if for_shipping_method? + enterprise_fee_adjustment_presentation_klass if for_enterprise_fee? + end + + def enterprise_fee_adjustment_presentation_klass + return DataRepresentations::CoordinatorFee if for_coordinator_fee? + return DataRepresentations::ExchangeOrderFee if for_order_adjustment_source? + return unless for_line_item_adjustment_source? + return DataRepresentations::IncomingExchangeLineItemFee if for_incoming_exchange? + return DataRepresentations::OutgoingExchangeLineItemFee if for_outgoing_exchange? + end + + def for_payment_method? + data["payment_method_name"].present? + end + + def for_shipping_method? + data["shipping_method_name"].present? + end + + def for_enterprise_fee? + data["fee_name"].present? + end + + def for_coordinator_fee? + data["placement_enterprise_role"] == "coordinator" + end + + def for_incoming_exchange? + data["placement_enterprise_role"] == "supplier" + end + + def for_outgoing_exchange? + data["placement_enterprise_role"] == "distributor" + end + + def for_order_adjustment_source? + data["adjustment_source_type"] == "Spree::Order" + end + + def for_line_item_adjustment_source? + data["adjustment_source_type"] == "Spree::LineItem" + end + end + end + end +end diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index 83a3f77576..2161bbb4c6 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -124,9 +124,9 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", nil, nil, nil, "4.00"], ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Another Customer", - "Coordinator", "All", "Sample Product Tax", "1024.00"], + "Coordinator", "All", "Various", "1024.00"], ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Sample Customer", - "Coordinator", "All", "Sample Product Tax", "2048.00"], + "Coordinator", "All", "Various", "2048.00"], ["Sales", "Sample Distributor", "Distributor Fee 2", "Another Customer", "Outgoing", "Sample Distributor", "Sample Product Tax", "8.00"], ["Sales", "Sample Distributor", "Distributor Fee 2", "Sample Customer", @@ -206,6 +206,98 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end end + + context "with order-based enterprise fee calculator" do + let!(:producer_fee) do + tax_category = create(:tax_category, name: "Producer Tax A") + create(:enterprise_fee, :flat_rate, name: "Producer Fee A", enterprise: producer, + fee_type: "sales", tax_category: tax_category, + amount: 10) + end + let!(:coordinator_fee) do + tax_category = create(:tax_category, name: "Coordinator Tax A") + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee A", enterprise: coordinator, + fee_type: "admin", tax_category: tax_category, + amount: 15) + end + let!(:coordinator_fee_inheriting_product_tax_category) do + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee B", enterprise: coordinator, + fee_type: "admin", inherits_tax_category: true, + amount: 20) + end + let!(:coordinator_fee_without_tax) do + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee C", enterprise: coordinator, + fee_type: "admin", inherits_tax_category: false, + amount: 25) + end + let!(:distributor_fee) do + tax_category = create(:tax_category, name: "Distributor Tax A") + create(:enterprise_fee, :flat_rate, name: "Distributor Fee A", enterprise: distributor, + fee_type: "admin", inherits_tax_category: false, + amount: 30) + end + + let!(:coordinator_fees) do + [ + coordinator_fee, + coordinator_fee_inheriting_product_tax_category, + coordinator_fee_without_tax + ] + end + + let!(:order_cycle) do + create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: coordinator_fees) + end + + let!(:variant_incoming_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } + let!(:variant_outgoing_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } + + let!(:variant) do + prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, + outgoing_exchange_fees: variant_outgoing_exchange_fees) + end + + let!(:customer_order) { prepare_order(customer: customer) } + + it "fetches data correctly" do + totals = service.list + + expect(totals.length).to eq(11) + + entire_orders_text = i18n_translate("fee_calculated_on_transfer_through_entire_orders", + distributor: "Sample Distributor") + various_tax_categories_text = i18n_translate("tax_category_various") + + expected_result = [ + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Coordinator", "All", "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Incoming", entire_orders_text, "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Outgoing", entire_orders_text, "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee B", "Sample Customer", + "Coordinator", "All", various_tax_categories_text, "20.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee C", "Sample Customer", + "Coordinator", "All", nil, "25.00"], + ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", + "Incoming", entire_orders_text, various_tax_categories_text, "30.00"], + ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", + "Outgoing", entire_orders_text, various_tax_categories_text, "30.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", + "Incoming", entire_orders_text, "Producer Tax A", "10.00"], + ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", + "Outgoing", entire_orders_text, "Producer Tax A", "10.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end end describe "filtering results based on permissions" do @@ -488,6 +580,10 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do # Helper methods for example group + def i18n_translate(translation_key, options = {}) + I18n.t("order_management.reports.enterprise_fee_summary.#{translation_key}", options) + end + def expect_total_attributes(total, expected_attribute_list) actual_attribute_list = [total.fee_type, total.enterprise_name, total.fee_name, total.customer_name, total.fee_placement, diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index 9385d453f3..6d570bce86 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -695,7 +695,7 @@ describe Admin::SubscriptionsController, type: :controller do end context "when other payment methods exist" do - let!(:stripe) { create(:stripe_payment_method, distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:stripe) { create(:stripe_payment_method, distributors: [shop]) } let!(:paypal) { Spree::Gateway::PayPalExpress.create!(name: "PayPalExpress", distributor_ids: [shop.id]) } let!(:bogus) { create(:bogus_payment_method, distributors: [shop]) } diff --git a/spec/controllers/spree/admin/payments_controller_spec.rb b/spec/controllers/spree/admin/payments_controller_spec.rb index 8236c0f832..534c49c14e 100644 --- a/spec/controllers/spree/admin/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/payments_controller_spec.rb @@ -19,7 +19,7 @@ describe Spree::Admin::PaymentsController, type: :controller do before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) } context "that was processed by stripe" do - let!(:payment_method) { create(:stripe_payment_method, distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) } # let!(:credit_card) { create(:credit_card, gateway_customer_profile_id: "cus_1", gateway_payment_profile_id: 'card_2') } let!(:payment) { create(:payment, order: order, state: 'completed', payment_method: payment_method, response_code: 'ch_1a2b3c', amount: order.total) } @@ -76,7 +76,7 @@ describe Spree::Admin::PaymentsController, type: :controller do before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) } context "that was processed by stripe" do - let!(:payment_method) { create(:stripe_payment_method, distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) } let!(:payment) { create(:payment, order: order, state: 'completed', payment_method: payment_method, response_code: 'ch_1a2b3c', amount: order.total + 5) } diff --git a/spec/controllers/spree/api/orders_controller_spec.rb b/spec/controllers/spree/api/orders_controller_spec.rb deleted file mode 100644 index 53e6b880ac..0000000000 --- a/spec/controllers/spree/api/orders_controller_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'spec_helper' -require 'spree/api/testing_support/helpers' - -module Spree - describe Spree::Api::OrdersController, type: :controller do - - end -end diff --git a/spec/factories.rb b/spec/factories.rb index d21d1143fd..cbdbdab3a8 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -531,6 +531,8 @@ FactoryBot.define do factory :stripe_payment_method, :class => Spree::Gateway::StripeConnect do name 'Stripe' environment 'test' + distributors { [FactoryBot.create(:enterprise)] } + preferred_enterprise_id { distributors.first.id } end factory :stripe_account do diff --git a/spec/factories/calculated_adjustment_factory.rb b/spec/factories/calculated_adjustment_factory.rb index 50ae6f5fce..3e9c6f5cc2 100644 --- a/spec/factories/calculated_adjustment_factory.rb +++ b/spec/factories/calculated_adjustment_factory.rb @@ -1,12 +1,23 @@ -attach_per_item_trait = proc do - trait :per_item do - transient { amount 1 } - calculator { build(:calculator_per_item, preferred_amount: amount) } +FactoryBot.define do + factory :calculator_flat_rate, class: Spree::Calculator::FlatRate do + preferred_amount { generate(:calculator_amount) } end end FactoryBot.modify do - factory :payment_method, &attach_per_item_trait - factory :shipping_method, &attach_per_item_trait - factory :enterprise_fee, &attach_per_item_trait + attach_calculator_traits = proc do + trait :flat_rate do + transient { amount 1 } + calculator { build(:calculator_flat_rate, preferred_amount: amount) } + end + + trait :per_item do + transient { amount 1 } + calculator { build(:calculator_per_item, preferred_amount: amount) } + end + end + + factory :payment_method, &attach_calculator_traits + factory :shipping_method, &attach_calculator_traits + factory :enterprise_fee, &attach_calculator_traits end diff --git a/spec/features/admin/enterprise_roles_spec.rb b/spec/features/admin/enterprise_roles_spec.rb index ae83bae5ea..d0aa59c766 100644 --- a/spec/features/admin/enterprise_roles_spec.rb +++ b/spec/features/admin/enterprise_roles_spec.rb @@ -98,7 +98,7 @@ feature %q{ before do click_link 'Enterprises' click_link 'Test Enterprise' - within('.side_menu') { click_link 'Users' } + navigate_to_enterprise_users expect(page).to have_selector "table.managers" end @@ -131,7 +131,8 @@ feature %q{ it "shows changes to enterprise contact or owner" do select2_select user2.email, from: 'receives_notifications_dropdown' within('#save-bar') { click_button 'Update' } - within('.side_menu') { click_link 'Users' } + navigate_to_enterprise_users + expect(page).to have_selector "table.managers" within 'table.managers' do within "tr#manager-#{user1.id}" do @@ -156,6 +157,9 @@ feature %q{ click_button I18n.t('js.admin.modals.close') end + expect(page).not_to have_selector "#invite-manager-modal" + expect(page).to have_selector "table.managers" + new_user = Spree::User.find_by_email_and_confirmed_at(new_email, nil) expect(Enterprise.managed_by(new_user)).to include enterprise @@ -170,9 +174,14 @@ feature %q{ end end - private + def navigate_to_enterprise_users + within ".side_menu" do + click_link "Users" + end + end + def have_relationship(user, enterprise) have_table_row [user.email, 'manages', enterprise.name, ''] end diff --git a/spec/features/admin/subscriptions_spec.rb b/spec/features/admin/subscriptions_spec.rb index 175f6c8ca6..3f8dca93fe 100644 --- a/spec/features/admin/subscriptions_spec.rb +++ b/spec/features/admin/subscriptions_spec.rb @@ -153,7 +153,7 @@ xfeature 'Subscriptions' do let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [test_variant, shop_variant], enterprise_fees: [enterprise_fee]) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } - let!(:payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop]) } let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } before do @@ -300,7 +300,7 @@ xfeature 'Subscriptions' do let!(:variant3_oc) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } let!(:variant3_ex) { variant3_oc.exchanges.create(sender: shop, receiver: shop, variants: [variant3]) } let!(:payment_method) { create(:payment_method, distributors: [shop]) } - let!(:stripe_payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:stripe_payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop]) } let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } let!(:subscription) { create(:subscription, @@ -438,7 +438,7 @@ xfeature 'Subscriptions' do let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } - let!(:payment_method) { create(:stripe_payment_method, distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) } let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } before do diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 03237d6b98..18d3818030 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -159,10 +159,7 @@ feature "As a consumer I want to check out my cart", js: true, retry: 3 do context "with Stripe" do let!(:stripe_pm) do - create(:stripe_payment_method, - distributors: [distributor], - name: "Stripe", - preferred_enterprise_id: distributor.id) + create(:stripe_payment_method, distributors: [distributor]) end let!(:saved_card) do diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index 68e304c236..9c8ac6130a 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -249,6 +249,7 @@ xdescribe ProductImport::ProductImporter do 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 << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag", shipping_category_id_str] end File.write('/tmp/test-m.csv', csv_data) @@ -262,17 +263,17 @@ xdescribe ProductImport::ProductImporter do @importer.validate_entries entries = JSON.parse(@importer.entries_json) - expect(filter('valid', entries)).to eq 2 + expect(filter('valid', entries)).to eq 3 expect(filter('invalid', entries)).to eq 0 - expect(filter('create_product', entries)).to eq 2 + expect(filter('create_product', entries)).to eq 3 end it "saves and updates" do @importer.save_entries - expect(@importer.products_created_count).to eq 2 + expect(@importer.products_created_count).to eq 3 expect(@importer.updated_ids).to be_a(Array) - expect(@importer.updated_ids.count).to eq 2 + expect(@importer.updated_ids.count).to eq 3 small_bag = Spree::Variant.find_by_display_name('Small Bag') expect(small_bag.product.name).to eq 'Potatoes' diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 316c186787..305ba29138 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -794,7 +794,7 @@ describe Spree::Order do describe "determining checkout steps for an order" do let!(:enterprise) { create(:enterprise) } let!(:order) { create(:order, distributor: enterprise) } - let!(:payment_method) { create(:stripe_payment_method, distributor_ids: [enterprise.id], preferred_enterprise_id: enterprise.id) } + let!(:payment_method) { create(:stripe_payment_method, distributor_ids: [enterprise.id]) } let!(:payment) { create(:payment, order: order, payment_method: payment_method) } it "does not include the :confirm step" do diff --git a/spec/requests/checkout/stripe_connect_spec.rb b/spec/requests/checkout/stripe_connect_spec.rb index feaaacc7be..fa8fb703a5 100644 --- a/spec/requests/checkout/stripe_connect_spec.rb +++ b/spec/requests/checkout/stripe_connect_spec.rb @@ -8,7 +8,7 @@ describe "checking out an order with a Stripe Connect payment method", type: :re let!(:enterprise) { create(:distributor_enterprise) } let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: order_cycle.coordinator, receiver: enterprise, incoming: false, pickup_time: "Monday") } let!(:shipping_method) { create(:shipping_method, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 0), distributors: [enterprise]) } - let!(:payment_method) { create(:stripe_payment_method, distributors: [enterprise], preferred_enterprise_id: enterprise.id) } + let!(:payment_method) { create(:stripe_payment_method, distributors: [enterprise]) } let!(:stripe_account) { create(:stripe_account, enterprise: enterprise) } let!(:line_item) { create(:line_item, price: 12.34) } let!(:order) { line_item.order } diff --git a/spec/services/bulk_invoice_service_spec.rb b/spec/services/bulk_invoice_service_spec.rb index 4611a46846..4056a1b88f 100644 --- a/spec/services/bulk_invoice_service_spec.rb +++ b/spec/services/bulk_invoice_service_spec.rb @@ -11,6 +11,16 @@ describe BulkInvoiceService do expect(Delayed::Job.last.payload_object.method_name).to eq :start_pdf_job_without_delay end + + it "creates a PDF invoice" do + order = create(:completed_order_with_fees) + order.bill_address = order.ship_address + order.save! + + service.start_pdf_job_without_delay([order.id]) + + expect(service.invoice_created?(service.id)).to be_truthy + end end describe "#invoice_created?" do diff --git a/spec/services/invoice_renderer_spec.rb b/spec/services/invoice_renderer_spec.rb new file mode 100644 index 0000000000..7516b86492 --- /dev/null +++ b/spec/services/invoice_renderer_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe InvoiceRenderer do + let(:service) { described_class.new } + + it "creates a PDF invoice with two different templates" do + order = create(:completed_order_with_fees) + order.bill_address = order.ship_address + order.save! + + result = service.render_to_string(order) + expect(result).to match /^%PDF/ + + allow(Spree::Config).to receive(:invoice_style2?).and_return true + + alternative = service.render_to_string(order) + expect(alternative).to match /^%PDF/ + expect(alternative).to_not eq result + end +end diff --git a/spec/services/upload_sanitizer_spec.rb b/spec/services/upload_sanitizer_spec.rb new file mode 100644 index 0000000000..6d8e6f029c --- /dev/null +++ b/spec/services/upload_sanitizer_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe UploadSanitizer do + describe "#call" do + let(:upload) do + File.open("/tmp/unsanitized.csv", 'wb:ascii-8bit') do |f| + f << "\xEF\xBB\xBF" + f << "Test" + end + end + let(:service) { UploadSanitizer.new(File.open(upload).read) } + + it "sanitizes the uploaded file" do + sanitized_upload = service.call + + expect(sanitized_upload.encoding.name).to eq "UTF-8" + expect(sanitized_upload.to_s).to eq "Test" + end + end +end