Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More options for supplier emails (WIP - help welcome) #1065

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Metrics/BlockNesting:
# Offense count: 18
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 294
Max: 310

# Offense count: 51
# Configuration parameters: AllowedMethods, AllowedPatterns.
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/admin/configs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def update

# Set configuration tab names as `@tabs`
def get_tabs
@tabs = %w[foodcoop payment tasks messages layout language security others]
@tabs = %w[foodcoop payment tasks messages suppliers layout language security others]
# allow engines to modify this list
engines = Rails::Engine.subclasses.map(&:instance).select { |e| e.respond_to?(:configuration) }
engines.each { |e| e.configuration(@tabs, self) }
Expand Down
6 changes: 6 additions & 0 deletions app/lib/foodsoft_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ module DistributionStrategy
NO_AUTOMATIC_DISTRIBUTION = 'no_automatic_distribution'
end

module MailOrderResultCopyToUser
NO_COPY = 'no_copy'
CC = 'cc'
BCC = 'bcc'
end

class << self
# Load and initialize foodcoop configuration file.
# @param filename [String] Override configuration file
Expand Down
21 changes: 18 additions & 3 deletions app/mailers/mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,25 @@ def order_result_supplier(user, order, options = {})
@order = order
@supplier = order.supplier

associated_users =
if user == order.created_by
format_user_address(user)
else
[format_user_address(user), format_user_address(order.created_by)].join(', ')
end
reply_to_users = associated_users unless FoodsoftConfig[:order_result_email_reply_to] && !FoodsoftConfig[:order_result_email_reply_copy_to_user]
users_cc = associated_users if FoodsoftConfig[:mail_order_result_copy_to_user] == FoodsoftConfig::MailOrderResultCopyToUser::CC
users_bcc = associated_users if FoodsoftConfig[:mail_order_result_copy_to_user] == FoodsoftConfig::MailOrderResultCopyToUser::BCC

add_order_result_attachments order, options

subject = I18n.t('mailer.order_result_supplier.subject', name: order.supplier.name)
subject += " (#{I18n.t('activerecord.attributes.order.pickup')}: #{format_date(order.pickup)})" if order.pickup

mail to: order.supplier.email,
cc: user,
reply_to: user,
cc: users_cc,
bcc: users_bcc,
reply_to: [FoodsoftConfig[:order_result_email_reply_to], reply_to_users].join(', '),
subject: subject
end

Expand Down Expand Up @@ -119,7 +130,7 @@ def mail(args)

%i[bcc cc reply_to sender to].each do |k|
user = args[k]
args[k] = format_address(user.email, show_user(user)) if user.is_a? User
args[k] = format_user_address(user) if user.is_a? User
end

if contact_email = FoodsoftConfig[:contact][:email]
Expand Down Expand Up @@ -165,6 +176,10 @@ def additonal_welcome_text(user); end

private

def format_user_address(user)
format_address(user.email, show_user(user))
end

def format_address(email, name)
address = Mail::Address.new email
address.display_name = name
Expand Down
7 changes: 5 additions & 2 deletions app/models/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Order < ApplicationRecord
belongs_to :updated_by, class_name: 'User', foreign_key: 'updated_by_user_id'
belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id'

enum end_action: { no_end_action: 0, auto_close: 1, auto_close_and_send: 2, auto_close_and_send_min_quantity: 3 }
enum end_action: { no_end_action: 0, auto_close: 1, auto_close_and_send: 2, auto_close_and_send_unless_empty: 4, auto_close_and_send_min_quantity: 3 }
enum transport_distribution: { skip: 0, ordergroup: 1, price: 2, articles: 3 }

# Validations
Expand Down Expand Up @@ -316,7 +316,10 @@ def do_end_action!
send_to_supplier!(created_by)
elsif auto_close_and_send_min_quantity?
finish!(created_by)
send_to_supplier!(created_by) if sum >= supplier.min_order_quantity.to_r
send_to_supplier!(created_by) if sum >= supplier.min_order_quantity.to_r && !order_articles.ordered.empty?
elsif auto_close_and_send_unless_empty?
finish!(created_by)
send_to_supplier!(created_by) unless order_articles.ordered.empty?
end
end

Expand Down
8 changes: 8 additions & 0 deletions app/views/admin/configs/_tab_suppliers.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%fieldset
%label
%h4= t '.communication_with_suppliers'
- mail_order_result_copy_to_user_options = FoodsoftConfig::MailOrderResultCopyToUser.constants.map { |c| FoodsoftConfig::MailOrderResultCopyToUser.const_get(c) }
= config_input form, :mail_order_result_copy_to_user, as: :select, collection: mail_order_result_copy_to_user_options,
include_blank: false, input_html: {class: 'input-xxlarge'}, value_method: ->(s){ s }, label_method: ->(s){ t("config.keys.mail_order_result_copy_to_user_options.#{s}") }
= config_input form, :order_result_email_reply_to, as: :string, input_html: {class: 'input-xlarge', placeholder: "#{@cfg[:name]} <#{@cfg[:contact][:email]}>"}
= config_input form, :order_result_email_reply_copy_to_user, as: :boolean
13 changes: 13 additions & 0 deletions config/app_config.yml.SAMPLE
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ default: &defaults
# email address to be used as sender
email_sender: [email protected]


# Options for communication between suppliers, the foodcoop, and order-associated users:
# Associated users are a) the user who created the order and b) (unless it was auto-sent) the user who clicked the button "Send to supplier."

# Mail order results only to the supplier (no_copy), as copy to associated user(s) (cc), or as blind copy to associated user(s) (bcc).
mail_order_result_copy_to_user: bcc

# Enter an email address if you want to request your suppliers to send any replies to that address instead of the associated users':
# order_result_email_reply_to: Foodcoop <[email protected]>
# If you want replies to be sent to both the specified reply-to address and the associated users':
# order_result_email_reply_copy_to_user: true


# domain to be used for reply emails
#reply_email_domain: reply.foodcoop.test

Expand Down
18 changes: 16 additions & 2 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ de:
end_action: Endeaktion
end_actions:
auto_close: Bestellung beenden
auto_close_and_send: Bestellung beenden und an Lieferantin schicken
auto_close_and_send_min_quantity: Bestellung beenden und an Lieferantin schicken sofern die Mindestbestellmenge erreicht wurde
auto_close_and_send: Bestellung beenden und an Lieferantin schicken (auch wenn nichts bestellt wurde)
auto_close_and_send_min_quantity: Bestellung beenden und an Lieferantin schicken, sofern die Mindestbestellmenge erreicht wurde (und mind. 1 Artikel bestellt)
auto_close_and_send_unless_empty: Bestellung beenden und an Lieferantin schicken, außer es wurde nichts bestellt
no_end_action: Keine automatische Aktion
ends: Endet am
name: Lieferant
Expand Down Expand Up @@ -316,6 +317,8 @@ de:
pdf_title: PDF-Dokumente
tab_messages:
emails_title: E-Mails versenden
tab_suppliers:
communication_with_suppliers: Kommunikation mit Lieferant:innen
tab_payment:
schedule_title: Bestellschema
tab_security:
Expand Down Expand Up @@ -603,6 +606,9 @@ de:
email_from: E-Mails werden so aussehen, als ob sie von dieser Adresse gesendet wurden. Kann leer gelassen werden, um die Kontaktadresse der Foodcoop zu benutzen.
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
mail_order_result_copy_to_user: Wenn eine Bestellung an eine Lieferant:in gesendet wird, wird eine (Blind-)Kopie der E-Mail an die zugehörige Benutzer:innen gesendet. Diese sind a) die Benutzer:in, die die Bestellung eröffnet hat und b) (außer bei automatischer Aussendung) die Benutzer:in, die auf den "An Lieferantin schicken"-Button geklickt hat.
order_result_email_reply_to: Gib eine E-Mail-Adresse ein, falls du die Lieferant:innen bitten möchtest, Antworten ggf. an jene Adresse zu schicken anstatt an die der zugehörigen Benutzer:innen (die die Bestellung erstellt bzw. auf den "An Lieferantin schicken"-Button geklickt haben)
order_result_email_reply_copy_to_user: Wenn aktiviert, werden die Lieferant:innen gebeten Antworten ggf. sowohl an die angegebene Adresse, als auch an die Benutzer:in, die die Bestellung erstellt hat, als auch ggf. an die Benutzer:in, die auf den "An Lieferantin schicken"-Button geklickt hat, zu schicken.
help_url: Link zur Dokumentationsseite
homepage: Webseite der Foodcoop
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
Expand Down Expand Up @@ -660,6 +666,13 @@ de:
email_from: Absenderadresse
email_replyto: Antwortadresse
email_sender: Senderadresse
mail_order_result_copy_to_user: E-Mail mit Bestellergebnis ...
mail_order_result_copy_to_user_options:
no_copy: nur an Lieferant:in senden
cc: als Kopie (CC) an zugehörige Benutzer:in(nen) senden
bcc: als Blindkopie (BCC) an zugehörige Benutzer:in(nen) senden
order_result_email_reply_to: Antwortadresse
order_result_email_reply_copy_to_user: Antwort auch an zugehörige Benutzer:in(nen)
help_url: URL Dokumentation
homepage: Webseite
ignore_browser_locale: Browsersprache ignorieren
Expand Down Expand Up @@ -701,6 +714,7 @@ de:
layout: Layout
list: Liste
messages: Nachrichten
suppliers: Lieferant:innen
others: Sonstiges
payment: Finanzen
security: Sicherheit
Expand Down
18 changes: 16 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ en:
end_action: End action
end_actions:
auto_close: Close the order
auto_close_and_send: Close the order and send it to the supplier
auto_close_and_send_min_quantity: Close the order and send it to the supplier if the minimum quantity has been reached
auto_close_and_send: Close the order and send it to the supplier (even if nothing has been ordered)
auto_close_and_send_min_quantity: Close the order and send it to the supplier if the minimum quantity has been reached (and min. 1 article ordered)
auto_close_and_send_unless_empty: Close the order and send it to the supplier unless nothing has been ordered
no_end_action: No automatic action
ends: Ends at
name: Supplier
Expand Down Expand Up @@ -316,6 +317,8 @@ en:
pdf_title: PDF documents
tab_messages:
emails_title: Sending email
tab_suppliers:
communication_with_suppliers: Communication with suppliers
tab_payment:
schedule_title: Ordering schedule
tab_security:
Expand Down Expand Up @@ -603,6 +606,9 @@ en:
email_from: Emails will appear to be from this email address. Leave empty to use the foodcoop's contact address.
email_replyto: Set this when you want to receive replies from emails sent by Foodsoft on a different address than the above.
email_sender: Emails will appear to be sent from this email address. To avoid emails sent being classified as spam, the webserver may need to be registered in the SPF record of the email address's domain.
mail_order_result_copy_to_user: When an order is sent to the supplier, a (blind) copy of the email will be sent to the associated users. Those are a) the user who created the order and b) (unless it was auto-sent) the user who clicked the button "Send to supplier."
order_result_email_reply_to: Enter an email address if you want to request your suppliers to send any replies to that address instead of the associated users' (who created the order / clicked the "Send to supplier" button.)
order_result_email_reply_copy_to_user: If enabled, your suppliers will be requested to send any replies both to the specified reply address, as to the user who created the order, as, if given, to the user who clicked the "Send to supplier" button.
help_url: Documentation website.
homepage: Website of your foodcoop.
ignore_browser_locale: Ignore the language of user's computer when the user has not chosen a language yet.
Expand Down Expand Up @@ -660,6 +666,13 @@ en:
email_from: From address
email_replyto: Reply-to address
email_sender: Sender address
mail_order_result_copy_to_user: Mail order result ...
mail_order_result_copy_to_user_options:
no_copy: only to the supplier
cc: as copy (CC) to associated user(s)
bcc: as blind copy (BCC) to associated user(s)
order_result_email_reply_to: Reply-to address
order_result_email_reply_copy_to_user: Send reply copy to associated user(s)
help_url: Documentation URL
homepage: Homepage
ignore_browser_locale: Ignore browser language
Expand Down Expand Up @@ -701,6 +714,7 @@ en:
layout: Layout
list: List
messages: Messages
suppliers: Suppliers
others: Other
payment: Finances
security: Security
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AddMailOrderResultCopyToUserSetting < ActiveRecord::Migration[7.0]
def up
FoodsoftConfig[:mail_order_result_copy_to_user] = FoodsoftConfig::MailOrderResultCopyToUser::CC
end

def down
FoodsoftConfig[:mail_order_result_copy_to_user] = nil
FoodsoftConfig[:order_result_email_reply_to] = nil
FoodsoftConfig[:order_result_email_reply_copy_to_user] = nil
end
end
2 changes: 1 addition & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2024_01_26_111615) do
ActiveRecord::Schema[7.0].define(version: 2024_04_24_015646) do
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
t.string "name", null: false
t.text "body", size: :long
Expand Down
62 changes: 53 additions & 9 deletions spec/models/order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,6 @@
end
end

it 'sends mail if min_order_quantity has been reached' do
create(:user, groups: [create(:ordergroup)])
create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago,
end_action: :auto_close_and_send_min_quantity)

Order.finish_ended!
expect(ActionMailer::Base.deliveries.count).to eq 1
end

it 'needs a supplier' do
expect(build(:order, supplier: nil)).to be_invalid
end
Expand Down Expand Up @@ -163,6 +154,59 @@
end
end

describe 'with end_action auto_close_and_send' do
let!(:order) { create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close_and_send) }

it 'sends mail even if nothing ordered' do
Order.finish_ended!
order.reload
expect(ActionMailer::Base.deliveries.count).to eq 1
end
end

describe 'with end_action auto_close_and_send_min_quantity' do
let!(:order) { create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close_and_send_min_quantity, article_count: 1) }
let!(:oa) { order.order_articles.first }
let!(:go) { create(:group_order, order: order) }
let!(:goa) { create(:group_order_article, group_order: go, order_article: oa, quantity: 0) }

it 'does not send mail if nothing ordered' do
# TODO: call go.reload, oa.update_results! if that proves to be correct
Order.finish_ended!
order.reload
expect(ActionMailer::Base.deliveries.count).to eq 0
end

it 'sends mail if min_order_quantity has been reached' do # I think there isn't actually a min_order_quantity that is checked?!
goa.update_quantities(1, 0)
go.reload
oa.update_results!
Order.finish_ended!
order.reload
expect(ActionMailer::Base.deliveries.count).to eq 1
end
end

describe 'with end_action auto_close_and_send_unless_empty' do
let!(:order) { create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close_and_send_unless_empty, article_count: 1) }
let!(:oa) { order.order_articles.first }
let!(:go) { create(:group_order, order: order) }
let!(:goa) { create(:group_order_article, group_order: go, order_article: oa, quantity: 0) }

it 'does not send mail if nothing ordered' do
Order.finish_ended!
order.reload
expect(ActionMailer::Base.deliveries.count).to eq 0
end

it 'sends mail if something ordered' do
goa.update_quantities(1, 0)
Order.finish_ended!
order.reload
expect(ActionMailer::Base.deliveries.count).to eq 1
end
end

describe 'balancing charges correct amounts' do
let!(:transport) { rand(0.1..26.0).round(2) }
let!(:order) { create(:order, article_count: 1) }
Expand Down
Loading