From f63552ca3821677c3c97d490f23eb613b6e75dbd Mon Sep 17 00:00:00 2001 From: Konrad Date: Mon, 21 Apr 2025 20:05:06 +0200 Subject: [PATCH 1/3] Deletes remaining text versions of emails This is in preparation for advancing the white labelling feature of emails and adding more specs in that regard Related discussion on Slack: https://openfoodnetwork.slack.com/archives/C2GQ45KNU/p1722902361401869?thread_ts=1721485724.245519&cid=C2GQ45KNU --- .../authorization_required.text.haml | 3 -- .../authorize_payment.text.haml | 3 -- .../order_cycle_report.text.haml | 36 ------------------- .../confirmation_instructions.text.erb | 9 ----- .../reset_password_instructions.text.erb | 7 ---- .../signup_confirmation.en-GB.text.erb | 14 -------- 6 files changed, 72 deletions(-) delete mode 100644 app/views/payment_mailer/authorization_required.text.haml delete mode 100644 app/views/payment_mailer/authorize_payment.text.haml delete mode 100644 app/views/producer_mailer/order_cycle_report.text.haml delete mode 100644 app/views/spree/user_mailer/confirmation_instructions.text.erb delete mode 100644 app/views/spree/user_mailer/reset_password_instructions.text.erb delete mode 100644 app/views/spree/user_mailer/signup_confirmation.en-GB.text.erb diff --git a/app/views/payment_mailer/authorization_required.text.haml b/app/views/payment_mailer/authorization_required.text.haml deleted file mode 100644 index c494f054e3..0000000000 --- a/app/views/payment_mailer/authorization_required.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -= t('spree.payment_mailer.authorization_required.message', order_number: @order.number) - -= link_to spree.edit_admin_order_url(@order), spree.edit_admin_order_url(@order) diff --git a/app/views/payment_mailer/authorize_payment.text.haml b/app/views/payment_mailer/authorize_payment.text.haml deleted file mode 100644 index 39ca5e3dbc..0000000000 --- a/app/views/payment_mailer/authorize_payment.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -= t('spree.payment_mailer.authorize_payment.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount) - -= link_to main_app.authorize_payment_url(@payment), main_app.authorize_payment_url(@payment) diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml deleted file mode 100644 index 6b6b7112c8..0000000000 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ /dev/null @@ -1,36 +0,0 @@ -#{t :producer_mail_greeting} #{raw(@producer.name)}, -\ -= t :producer_mail_text_before -\ -- @distributors_pickup_times.each do |distributor_name, pickup_time| - \- #{raw(distributor_name)} (#{pickup_time}) -\ -- if @receival_instructions - = t :producer_mail_delivery_instructions - = raw(@receival_instructions) -\ -Orders summary -================ -\ -= t :producer_mail_order_text -\ -- @grouped_line_items.each_pair do |product_and_full_name, line_items| - #{line_items.first.variant.sku} - #{raw(line_items.first.variant.supplier.name)} - #{raw(product_and_full_name)} (#{t(:producer_mail_qty)}: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)} (#{t(:with_tax_incl, amount: Spree::Money.new(line_items.sum(&:included_tax), currency: line_items.first.currency))}) -\ -\ -#{t :total}: #{@total} (#{t(:with_tax_incl, amount: @tax_total)}) -\ -- if @customer_line_items - = t :producer_mail_order_customer_text - \ - - @customer_line_items.each do |line_item| - #{line_item[:sku]} - #{raw(line_item[:supplier_name])} - #{raw(line_item[:product_and_full_name])} (#{t(:producer_mail_qty)}: #{line_item[:quantity]}) - #{raw(line_item[:first_name])} #{raw(line_item[:last_name])} #{raw(line_item[:business_name])} -\ -\ -= t :producer_mail_text_after - -#{t :producer_mail_signoff}, -#{raw(@coordinator.name)} -#{raw(@coordinator.address.address1)}, #{raw(@coordinator.address.city)}, #{raw(@coordinator.address.zipcode)} -#{@coordinator.phone} -#{@coordinator.contact.email} diff --git a/app/views/spree/user_mailer/confirmation_instructions.text.erb b/app/views/spree/user_mailer/confirmation_instructions.text.erb deleted file mode 100644 index a7b47a5f3d..0000000000 --- a/app/views/spree/user_mailer/confirmation_instructions.text.erb +++ /dev/null @@ -1,9 +0,0 @@ -<%= t(:email_signup_greeting) %> - -<%= t(:email_signup_welcome, sitename: Spree::Config[:site_name]) %> - -<%= t(:email_confirmation_activate_account) %> - -<%= t(:email_confirmation_click_link) %> - -<%= link_to t(:email_confirmation_link_label), spree.spree_user_confirmation_url(confirmation_token: @user.confirmation_token) %> diff --git a/app/views/spree/user_mailer/reset_password_instructions.text.erb b/app/views/spree/user_mailer/reset_password_instructions.text.erb deleted file mode 100644 index 595b1cc6aa..0000000000 --- a/app/views/spree/user_mailer/reset_password_instructions.text.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= t('.request_sent_text') %> - -<%= t('.link_text') %> - -<%= @edit_password_reset_url %> - -<%= t('.issue_text') %> diff --git a/app/views/spree/user_mailer/signup_confirmation.en-GB.text.erb b/app/views/spree/user_mailer/signup_confirmation.en-GB.text.erb deleted file mode 100644 index 2af9144ddf..0000000000 --- a/app/views/spree/user_mailer/signup_confirmation.en-GB.text.erb +++ /dev/null @@ -1,14 +0,0 @@ -Hello, - -Welcome to Open Food Network UK! Your login email is <%= @user.email %> - -You can go online and start shopping through food hubs and local producers you like at http://openfoodnetwork.org.uk - -We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at hello@openfoodnetwork.org. - -Thanks for getting on board and we look forward to introducing you to many more great farmers, food hubs and food! - -Cheers, - -The Open Food Network UK team - From d97cb9ccb307bbd2e1f61db7c70b881d6ef85aa9 Mon Sep 17 00:00:00 2001 From: Konrad Date: Mon, 21 Apr 2025 20:07:55 +0200 Subject: [PATCH 2/3] Update specs --- spec/base_spec_helper.rb | 1 - spec/mailers/payment_mailer_spec.rb | 3 +- spec/mailers/producer_mailer_spec.rb | 103 +++++++-------------------- spec/mailers/user_mailer_spec.rb | 3 +- spec/support/mailers_helper.rb | 7 -- 5 files changed, 27 insertions(+), 90 deletions(-) delete mode 100644 spec/support/mailers_helper.rb diff --git a/spec/base_spec_helper.rb b/spec/base_spec_helper.rb index b44827a64e..7f7ef01a12 100644 --- a/spec/base_spec_helper.rb +++ b/spec/base_spec_helper.rb @@ -265,7 +265,6 @@ RSpec.configure do |config| config.include OpenFoodNetwork::PerformanceHelper config.include ActiveJob::TestHelper config.include ReportsHelper - config.include MailersHelper, type: :mailer config.include TomSelectHelper, type: :system config.include ViewComponent::TestHelpers, type: :component diff --git a/spec/mailers/payment_mailer_spec.rb b/spec/mailers/payment_mailer_spec.rb index cee814ef16..1c520fbc19 100644 --- a/spec/mailers/payment_mailer_spec.rb +++ b/spec/mailers/payment_mailer_spec.rb @@ -26,8 +26,7 @@ RSpec.describe PaymentMailer do it "includes a link to authorize the payment" do link = "http://test.host/payments/#{payment.id}/authorize" - expect(email.text_part.body).to match link - expect(html_body(email)).to have_link link, href: link + expect(email.body).to have_link link, href: link end end diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 387c42eea0..67ef14c86d 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -60,6 +60,7 @@ RSpec.describe ProducerMailer, type: :mailer do end let(:mail) { ProducerMailer.order_cycle_report(s1, order_cycle) } + let(:parsed_email) { Capybara::Node::Simple.new(mail.body.encoded) } it "sets a reply-to of the oc coordinator's email" do expect(mail.reply_to).to eq [order_cycle.coordinator.contact.email] @@ -78,22 +79,21 @@ RSpec.describe ProducerMailer, type: :mailer do end it "contains an aggregated list of produce in alphabetical order" do - expect(mail.body.encoded).to match(/coffee.+\n.+Zebra/) - body_lines_including(mail, p1.name).each do |line| - expect(line).to include 'QTY: 3' - expect(line).to include '@ $10.00 = $30.00' + rows = parsed_email.all('table.order-summary tbody tr:not(.total-row)') + actual = rows.map do |row| + row.all('td').map { |td| td.text.strip } end - expect(html_body(mail).find("table.order-summary tr", text: p1.name)) - .to have_selector("td", text: "$30.00") + expected = [ + ['', 'coffee - 1g', '2', '$10.00', '$20.00', '$0.00'], + ['', 'Zebra - 1g', '3', '$10.00', '$30.00', '$2.73'] + ] + expect(actual).to eq(expected) end it "displays tax totals for each product" do # Tax for p1 line items - expect(html_body(mail).find("table.order-summary tr", text: p1.name)) + expect(parsed_email.find("table.order-summary tr", text: p1.name)) .to have_selector("td.tax", text: "$2.73") - expect( - product_line_from_order_summary_text(mail, p1.name) - ).to include("($2.73 tax incl.)") end it "does not include incomplete orders" do @@ -121,9 +121,7 @@ RSpec.describe ProducerMailer, type: :mailer do end it "includes the total" do - expect(mail.body.encoded).to include 'Total: $50.00' - expect(html_body(mail).find("tr.total-row")) - .to have_selector("td", text: "$50.00") + expect(parsed_email.find("tr.total-row")).to have_selector("td", text: "$50.00") end it "sends no mail when the producer has no orders" do @@ -151,28 +149,16 @@ RSpec.describe ProducerMailer, type: :mailer do end it "adds customer names table" do - expect(html_body(mail).find(".order-summary.customer-order")).not_to be_nil - expect(customer_details_summary_text(mail)).to be_present + expect(parsed_email).to have_selector(".order-summary.customer-order") end - it "displays last name for each order" do + it "displays last name and first name for each order" do product_name = order.line_items.first.product.name last_name = order.billing_address.lastname - expect(html_body(mail).find("table.order-summary.customer-order tr", - text: product_name)).to have_selector("td", text: last_name) - expect( - product_line_from_details_summary_text(mail, product_name) - ).to include(last_name) - end - - it "displays first name for each order" do - product_name = order.line_items.first.product.name first_name = order.billing_address.firstname - expect(html_body(mail).find("table.order-summary.customer-order tr", - text: product_name)).to have_selector("td", text: first_name) - expect( - product_line_from_details_summary_text(mail, product_name) - ).to include(first_name) + row = parsed_email.find("table.order-summary.customer-order tbody tr") + expect(row).to have_selector("td", text: last_name) + expect(row).to have_selector("td", text: first_name) end it "it orders list via last name" do @@ -181,18 +167,16 @@ RSpec.describe ProducerMailer, type: :mailer do create(:order, :with_line_item, distributor: d1, order_cycle:, state: 'complete', bill_address: FactoryBot.create(:address, last_name: "smith")) expect(mail.body.encoded).to match(/.*Abby.*Doe.*smith/m) - expect(customer_details_summary_text(mail)).to include('Abby', 'Doe', 'smith') end context "validate business name" do let(:table_header) do - html_body(mail).find("table.order-summary.customer-order thead") + parsed_email.find("table.order-summary.customer-order thead") end context "when no customer has customer code" do - it 'should not displays business name column' do + it 'should not display business name column' do expect(table_header).not_to have_selector("th", text: 'Business Name') - expect(customer_details_summary_text(mail)).not_to include('Test Business Name') end end @@ -201,10 +185,8 @@ RSpec.describe ProducerMailer, type: :mailer do it 'displays business name for the customer' do expect(table_header).to have_selector("th", text: 'Business Name') - expect( - html_body(mail).find("table.order-summary.customer-order tbody tr") - ).to have_selector("td", text: 'Test Business Name') - expect(customer_details_summary_text(mail)).to include('Test Business Name') + expect(parsed_email.find("table.order-summary.customer-order tbody tr")) + .to have_selector("td", text: 'Test Business Name') end end end @@ -217,9 +199,8 @@ RSpec.describe ProducerMailer, type: :mailer do it "does not add customer names table" do expect { - html_body(mail).find(".order-summary.customer-order") + parsed_email.find(".order-summary.customer-order") }.to raise_error(Capybara::ElementNotFound) - expect(customer_details_summary_text(mail)).to be_nil end end @@ -236,7 +217,7 @@ RSpec.describe ProducerMailer, type: :mailer do end it "displays a supplier column" do - expect(html_body(mail).find(".order-summary")) + expect(parsed_email.find(".order-summary")) .to have_selector("th", text: "Supplier") end @@ -244,7 +225,7 @@ RSpec.describe ProducerMailer, type: :mailer do before { order_cycle.coordinator.update!(show_customer_names_to_suppliers: true) } it "displays a supplier column in the summary of orders grouped by customer" do - expect(html_body(mail).find(".customer-order")) + expect(parsed_email.find(".customer-order")) .to have_selector("th", text: "Supplier") end end @@ -252,7 +233,7 @@ RSpec.describe ProducerMailer, type: :mailer do context "products from only one supplier" do it "doesn't display a supplier column" do - expect(html_body(mail).find(".order-summary")) + expect(parsed_email.find(".order-summary")) .not_to have_selector("th", text: "Supplier") end @@ -260,43 +241,9 @@ RSpec.describe ProducerMailer, type: :mailer do before { order_cycle.coordinator.update!(show_customer_names_to_suppliers: true) } it "doesn't display a supplier column in the summary of orders grouped by customer" do - expect(html_body(mail).find(".customer-order")) + expect(parsed_email.find(".customer-order")) .not_to have_selector("th", text: "Supplier") end end end - - private - - def body_lines_including(mail, str) - mail.body.to_s.lines.select { |line| line.include? str } - end - - def body_as_text(mail) - mail.text_part.body.decoded - end - - def customer_details_summary_text(mail) - body_as_text(mail) - .split(I18n.t(:producer_mail_order_customer_text)) - .second - end - - def product_line_from_details_summary_text(mail, product_name) - summary = customer_details_summary_text(mail) - product_line_by_summary(summary, product_name) - end - - def product_line_from_order_summary_text(mail, product_name) - summary = body_as_text(mail) - .split(I18n.t(:producer_mail_order_customer_text)) - .first - product_line_by_summary(summary, product_name) - end - - def product_line_by_summary(summary, product_name) - return '' unless summary - - summary.lines.find { |line| line.include?(product_name) } || '' - end end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 9e9dc8e7ad..ff8aec0761 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -81,8 +81,7 @@ RSpec.describe Spree::UserMailer do context 'body includes' do it 'password reset url' do - expect(mail.text_part.body).to include spree.edit_spree_user_password_url - expect(mail.html_part.body).to include spree.edit_spree_user_password_url + expect(mail.body).to include spree.edit_spree_user_password_url end end diff --git a/spec/support/mailers_helper.rb b/spec/support/mailers_helper.rb deleted file mode 100644 index bc4688062e..0000000000 --- a/spec/support/mailers_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module MailersHelper - def html_body(mail) - Capybara.string(mail.html_part.body.to_s) - end -end From 4756078ab9b034464e6af2d7bd4ba0e6e263dd44 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 24 Apr 2025 13:53:46 +1000 Subject: [PATCH 3/3] Update all locales with the latest Transifex translations --- config/locales/en_FR.yml | 14 ++++++++++++++ config/locales/es.yml | 22 ++++++++++++++++++++++ config/locales/fr.yml | 14 ++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index 4103d105e2..a7052c926b 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -783,9 +783,18 @@ en_FR: connection_invalid_html: | Connecting with your OIDC account failed. Please refresh your OIDC connection at: %{oidc_settings_link} + absent_variant: + reset: "Reset stock" index: title: "DFC product catalog" catalog_url: "%{count} products to be imported from: %{catalog_url}" + absent_products: + one: | + One product is no longer in the catalog. + It will be marked as unavailable by resetting stock to zero. + other: | + %{count} products are no longer in the catalog. + They will be marked as unavailable by resetting stock to zero. enterprise: "Import to enterprise: %{enterprise_name}" select_all: "Select/deselect all" update: Update @@ -798,6 +807,8 @@ en_FR: invalid_url: This catalog URL is not valid. import: title: "DFC product catalog import" + imported_products: "Imported products: %{count}" + reset_products: "Stock reset for absent products: %{count}" enterprise_fees: index: title: "Enterprise Fees" @@ -1274,6 +1285,8 @@ en_FR: enable_subscriptions_true: "Enabled" customer_names_in_reports: "Customer Names in Reports" customer_names_tip: "Enable your suppliers to see your customers names in reports" + producers_to_edit_orders: "Ability for producers to edit orders" + producers_to_edit_orders_tip: "Enable your suppliers to see orders containing their products, and edit quantity and weight for their own products only." customer_names_false: "Disabled" customer_names_true: "Enabled" customer_contacts_in_reports: "Customer contact details in reports" @@ -1693,6 +1706,7 @@ en_FR: email_confirmation: "Email confirmation is pending. We've sent a confirmation email to %{email}." not_visible: "%{enterprise} is not visible and so cannot be found on the map or in searches" reports: + none: none deprecated: "This report is deprecated and will be removed in a future release." hidden_field: "< Hidden >" unitsize: UNITSIZE diff --git a/config/locales/es.yml b/config/locales/es.yml index 007e5720dd..eb144f043a 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -145,6 +145,10 @@ es: title: "El cambio deseado ha sido rechazado (422)" general_error: message: "Lo sentimos, pero algo ha ido mal.\n\nPuede que se trate de un problema temporal, así que inténtalo de nuevo o recarga la página.\nRegistramos todos los errores y es posible que estemos trabajando en una solución.\nSi el problema persiste o es urgente, póngase en contacto con nosotras." + unauthorized: + message: "No tiene autorización para realizar esta acción." + network_error: + message: "Error en la red: intentalo más tarde" stripe: error_code: incorrect_number: "El número de tarjeta es incorrecto." @@ -760,9 +764,15 @@ es: user_guide: Manual de Usuario map: Mapa dfc_product_imports: + absent_variant: + reset: "Restablecer stock" index: update: Actualizar new: Nuevo + selected: + zero: "0 seleccionados" + one: "1 seleccionados" + other: "%{count} seleccionados" import: Importar enterprise_fees: index: @@ -879,6 +889,7 @@ es: category_field_name: "Categoría" tax_category_field_name: "Categoría de impuestos" producer_field_name: "Productora" + select_unit_scale: Seleccionar escala de la unidad clone: success: Se ha clonado el producto error: No se ha podido clonar el producto @@ -1177,6 +1188,7 @@ es: visible: Pública not_visible: Oculto hidden: Ocultar todas las referencias + admin_only_legend: Solo administradores properties: legend: "Propiedades" permalink: @@ -3695,6 +3707,7 @@ es: last: "Último" spree: order_updated: "Pedido actualizado" + cannot_perform_operation: "No se puede realizar esta operación." add_country: "Agregar pais" add_state: "Agregar estado" adjustment: "Ajuste" @@ -4207,6 +4220,9 @@ es: title: "Nuevo producto" new_product: "Nuevo producto" supplier: "Proveedora" + supplier_select_placeholder: "Seleccionar proveedora" + search_for_suppliers: "Buscar proveedoras" + search_for_units: "Buscar unidades" product_name: "nombre del producto" units: "Unidad de medida" value: "Valor" @@ -4247,9 +4263,12 @@ es: bulk_unit_size: Tamaño de la unidad a granel display_as: display_as: Mostrar como + clone: + success: Producto clonado reports: table: select_and_search: "Seleccione filtros y haga clic en %{option} para acceder a sus datos." + hidden_customer_details_tip: "Si los nombres y/o los datos de contacto de las consumidoras están ocultos, puede solicitar a la distribuidora que actualice las preferencias de su tienda para permitir que sus proveedores vean los datos de las consumidoras en los informes." users: index: listing_users: "Listado de Usuarias" @@ -4408,6 +4427,7 @@ es: invoice_attached_text: Adjunta una factura para su pedido reciente de user_mailer: reset_password_instructions: + dear_customer: "Estimada consumidora," request_sent_text: | Se ha solicitado el cambio de tu contraseña. Si tu no lo has solicitado simplemente ignora este email. @@ -4483,6 +4503,7 @@ es: users: api_keys: regenerate_key: "Regenerar llave" + title: API key webhook_endpoints: description: Los eventos del sistema pueden desencadenar webhooks a sistemas externos. event_types: @@ -4618,6 +4639,7 @@ es: search_input: placeholder: Buscar selector_with_filter: + selected_items: "%{count} seleccionados" search_placeholder: Buscar pagination: next: Siguiente diff --git a/config/locales/fr.yml b/config/locales/fr.yml index b41e8faa2f..2f73e7858e 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -782,9 +782,18 @@ fr: connection_invalid_html: | La connexion avec votre compte OIDC a échoué. Merci de rafraîchir votre connexion OIDC à : %{oidc_settings_link} + absent_variant: + reset: "Réinitialiser le stock" index: title: "Catalogue produit DFC" catalog_url: "%{count} produits à importer de : %{catalog_url}" + absent_products: + one: | + Un produit n'est plus dans le catalogue produits. + Il sera marqué indisponible quand vous remettrez votre stock à zéro. + other: | + %{count} produits ne sont plus dans votre catalogue produits. + Ils seront marqués indisponibles quand vous remettrez votre stock à zéro. enterprise: "Import vers l'entreprise : %{enterprise_name}" select_all: "Sélectionner / désélectionner tout" update: Mettre à jour @@ -797,6 +806,8 @@ fr: invalid_url: L'URL de ce catalogue n'est pas valide. import: title: "Import du catalogue produit DFC" + imported_products: "Produits importés : %{count}" + reset_products: "Réinitialiser le stock pour les produits absents : %{count}" enterprise_fees: index: title: "Marges et Commissions" @@ -1276,6 +1287,8 @@ fr: enable_subscriptions_true: "Activé" customer_names_in_reports: "Affichage du nom des acheteurs" customer_names_tip: "Permettre aux producteurs de voir les noms de vos acheteurs" + producers_to_edit_orders: "Possibilité pour les producteurs de modifier les commandes" + producers_to_edit_orders_tip: "Permettre à vos fournisseurs de visualiser les commandes comprenant leurs produits et de modifier la quantité et le poids de leurs produits seulement." customer_names_false: "Désactivé" customer_names_true: "Activé" customer_contacts_in_reports: "Affichage des contacts clients" @@ -1695,6 +1708,7 @@ fr: email_confirmation: "L'adresse e-mail doit être confirmée. Nous avons envoyé un lien de confirmation à %{email}." not_visible: "%{enterprise} n'est pas visible et ne peut être trouvé sur la carte ou dans les recherches sur le site." reports: + none: aucun deprecated: "Ce rapport est obsolète et ne sera bientôt plus disponible (sera supprimé dans une prochaine mise à jour)." hidden_field: "< Masqué >" unitsize: Unité de mesure