Skip to content
Merged
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
4 changes: 3 additions & 1 deletion app/jobs/notification_mailer_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ def perform(notification_id, persist_delivered_email: true)
"event_registration_confirmation" => ->(n) { EventMailer.event_registration_confirmation(n.noticeable) },
"event_registration_confirmation_fyi" => ->(n) { NotificationMailer.event_registration_confirmation_fyi(n) },
"event_registration_cancelled" => ->(n) { EventMailer.event_registration_cancelled(n.noticeable) },
"event_registration_cancelled_fyi" => ->(n) { NotificationMailer.event_registration_cancelled_fyi(n) }
"event_registration_cancelled_fyi" => ->(n) { NotificationMailer.event_registration_cancelled_fyi(n) },
"bulk_payment_confirmation" => ->(n) { EventMailer.bulk_payment_confirmation(n.noticeable) },
"bulk_payment_confirmation_fyi" => ->(n) { NotificationMailer.bulk_payment_confirmation_fyi(n) }
}

mailer = mailer_map[notification.kind]&.call(notification)
Expand Down
20 changes: 20 additions & 0 deletions app/mailers/event_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ def event_registration_confirmation(event_registration)
)
end

def bulk_payment_confirmation(form_submission)
@submission = form_submission
@person = form_submission.person
@event = form_submission.event&.decorate
@answers = form_submission.answers_by_identifier
@attendees = form_submission.bulk_payment_attendees

@notification_type = "Bulk payment confirmation"

@submission_url = event_bulk_payment_url(@event, submission_id: @submission.id) if @event
@organization_name = ENV.fetch("ORGANIZATION_NAME", "AWBW")

mail(
to: @person.preferred_email,
from: ENV.fetch("REPLY_TO_EMAIL", "no-reply@awbw.org"),
reply_to: ENV.fetch("REPLY_TO_EMAIL", "programs@awbw.org"),
subject: "AWBW Portal: Bulk payment received for #{@event&.title}"
)
end

def event_registration_reminder(event_registration, days_until_event: nil)
@event_registration = event_registration
@event = event_registration.event.decorate
Expand Down
13 changes: 13 additions & 0 deletions app/mailers/notification_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ def event_registration_cancelled_fyi(notification)
)
end

def bulk_payment_confirmation_fyi(notification)
@submission = notification.noticeable
@person = @submission.person
@event = @submission.event&.decorate
@answers = @submission.answers_by_identifier
@attendees = @submission.bulk_payment_attendees
@notification_type = "Bulk payment"

mail(
subject: "#{FYI_PREFIX} New bulk payment by #{@person.full_name} for #{@event&.title}"
)
end

def idea_submitted(notification)
@notification = notification
@noticeable = notification.noticeable.decorate
Expand Down
28 changes: 28 additions & 0 deletions app/models/form_submission.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,32 @@ class FormSubmission < ApplicationRecord
has_many :payments

accepts_nested_attributes_for :form_answers

# The event this submission belongs to, resolved through the join role that
# matches the submission's own role (e.g. a "bulk_payment" submission maps to
# the event_form with role "bulk_payment").
def event
form.events.find_by(event_forms: { role: role })
end

# Answers keyed by their field's identifier. Bulk payment (and similar) forms
# address fields by identifier rather than position.
def answers_by_identifier
form_answers.includes(:form_field).each_with_object({}) do |answer, map|
identifier = answer.form_field&.field_identifier
map[identifier] = answer.submitted_answer if identifier.present?
end
end

# Attendees captured by the bulk payment form, stored as a JSON array under the
# "bulk_payment_attendees" field.
def bulk_payment_attendees
raw = answers_by_identifier["bulk_payment_attendees"]
return [] if raw.blank?

parsed = JSON.parse(raw)
parsed.is_a?(Array) ? parsed : []
rescue JSON::ParserError
[]
end
end
5 changes: 5 additions & 0 deletions app/models/notification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class Notification < ApplicationRecord
event_registration_confirmation_fyi
event_registration_cancelled
event_registration_cancelled_fyi
bulk_payment_confirmation
bulk_payment_confirmation_fyi
idea_submitted
idea_submitted_fyi
report_submitted
Expand Down Expand Up @@ -56,6 +58,7 @@ class Notification < ApplicationRecord

NOTICEABLE_TYPES = %w[
EventRegistration
FormSubmission
Report
StoryIdea
User
Expand All @@ -69,13 +72,15 @@ class Notification < ApplicationRecord
[ "Admin FYI (all)", "[FYI]" ],
[ "Admin FYI: event registration confirmed", "[FYI] New event registration" ],
[ "Admin FYI: event registration cancelled", "[FYI] Event registration cancelled" ],
[ "Admin FYI: bulk payment", "[FYI] New bulk payment" ],
[ "Admin FYI: idea submitted", "submission by" ],
[ "Admin FYI: password reset", "[FYI] New password reset" ],
[ "Admin FYI: workshop log submission", "New WorkshopLog submission" ],
[ "Admin FYI: contact form submission", "contact form submission" ],
[ "Contact: form confirmation", "We received your message" ],
[ "Event: event registration cancelled", "Event registration cancelled" ],
[ "Event: event registration confirmed", "Event registration confirmed" ],
[ "Bulk payment: confirmation", "Bulk payment received" ],
[ "Idea: confirmation (all)", "has been received" ],
[ "Idea: confirmation: workshop log", "workshop log has been received" ],
[ "User: confirm new email", "Confirm your new email address" ],
Expand Down
23 changes: 23 additions & 0 deletions app/services/event_registration_services/bulk_payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def call
person = find_or_create_person
create_phone_contact(person) if field_value("payer_phone").present?
submission = create_form_submission(person)
send_notifications(submission, person)
Result.new(success?: true, form_submission: submission, errors: [])
end
rescue ActiveRecord::RecordInvalid => e
Expand All @@ -27,6 +28,28 @@ def call

private

# Emails the payer a confirmation and notifies staff with an FYI.
def send_notifications(submission, person)
payer_email = person.preferred_email.presence || field_value("payer_email")&.strip
if payer_email.present?
NotificationServices::CreateNotification.call(
noticeable: submission,
kind: :bulk_payment_confirmation,
recipient_role: :person,
recipient_email: payer_email,
notification_type: 0
)
end

NotificationServices::CreateNotification.call(
noticeable: submission,
kind: :bulk_payment_confirmation_fyi,
recipient_role: :admin,
recipient_email: ENV.fetch("REPLY_TO_EMAIL", "programs@awbw.org"),
notification_type: 0
)
end

def field_value(key)
field = @form.form_fields.find_by(field_identifier: key)
return nil unless field
Expand Down
88 changes: 88 additions & 0 deletions app/views/event_mailer/bulk_payment_confirmation.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<h1>Bulk payment received</h1>

<div style="margin-top: 36px; text-align: left;">
<p>
Hello <strong><%= @person.full_name %></strong>,
</p>

<p>
We've received your bulk payment submission<%= " for the following #{@organization_name} event" if @event %>:
</p>

<% if @event %>
<div style="text-align: center; background-color: #f3f4f6; border-radius: 6px; padding: 24px; margin: 16px 0;">
<% if @event.respond_to?(:pre_title) && @event.pre_title.present? %>
<p style="font-size: 14px; font-weight: 600; color: #374151; margin: 0 0 4px; font-family: 'Telefon Bold', sans-serif;">
<%= @event.pre_title %>
</p>
<% end %>

<h2 style="font-size: 28px; font-weight: bold; color: #166534; margin: 0 0 16px; font-family: Lato, sans-serif;">
<%= @event.title %>
</h2>

<p style="font-size: 22px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<%= @event.times(display_day: true, display_date: true) %>
</p>

<% if @event.labelled_cost.present? %>
<p style="font-size: 18px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<%= @event.labelled_cost %>
</p>
<% end %>

<% if @event.location.present? %>
<p style="font-size: 16px; color: #374151; margin: 0 0 8px;">
<%= @event.location.name %>
</p>
<% end %>
</div>
<% end %>

<table style="width: 100%; border-collapse: collapse; margin: 16px 0;">
<% if @answers["payer_organization"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Organization</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["payer_organization"] %></td>
</tr>
<% end %>
<% if @answers["number_of_attendees"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Number of attendees</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["number_of_attendees"] %></td>
</tr>
<% end %>
<% if @answers["payment_method"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Payment method</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["payment_method"] %></td>
</tr>
<% end %>
</table>

<% if @attendees.any? %>
<p style="margin-bottom: 4px;"><strong>Attendees</strong></p>
<table style="width: 100%; border-collapse: collapse; margin: 8px 0 16px;">
<thead>
<tr>
<th style="text-align: left; border-bottom: 1px solid #e5e7eb; padding: 6px; font-size: 13px; color: #6b7280;">Name</th>
<th style="text-align: left; border-bottom: 1px solid #e5e7eb; padding: 6px; font-size: 13px; color: #6b7280;">Email</th>
</tr>
</thead>
<tbody>
<% @attendees.each do |attendee| %>
<tr>
<td style="border-bottom: 1px solid #f3f4f6; padding: 6px; font-size: 14px;"><%= [ attendee["first_name"], attendee["last_name"] ].compact_blank.join(" ") %></td>
<td style="border-bottom: 1px solid #f3f4f6; padding: 6px; font-size: 14px;"><%= attendee["email"] %></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
</div>

<% if @submission_url %>
<p>
<a href="<%= @submission_url %>" class="button">View submission</a>
</p>
<% end %>
25 changes: 25 additions & 0 deletions app/views/event_mailer/bulk_payment_confirmation.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Bulk payment received

Hello <%= @person.full_name %>,

We've received your bulk payment submission<%= " for the following event" if @event %>:

<% if @event %><% if @event.respond_to?(:pre_title) && @event.pre_title.present? %><%= @event.pre_title %>
<% end %><%= @event.title %>
<%= @event.times(display_day: true, display_date: true) %>
<% if @event.location.present? %>Location: <%= @event.location.name %>
<% end %><% if @event.labelled_cost.present? %><%= @event.labelled_cost %>
<% end %><% end %>
<% if @answers["payer_organization"].present? %>Organization: <%= @answers["payer_organization"] %>
<% end %><% if @answers["number_of_attendees"].present? %>Number of attendees: <%= @answers["number_of_attendees"] %>
<% end %><% if @answers["payment_method"].present? %>Payment method: <%= @answers["payment_method"] %>
<% end %>
<% if @attendees.any? %>Attendees:
<% @attendees.each do |attendee| %>- <%= [ attendee["first_name"], attendee["last_name"] ].compact_blank.join(" ") %><%= " <#{attendee['email']}>" if attendee["email"].present? %>
<% end %><% end %>
<% if @submission_url %>View submission:
<%= @submission_url %>
<% end %>
--
This is an automated confirmation from <%= @organization_name %>.
If you did not expect this message, you may ignore it.
102 changes: 102 additions & 0 deletions app/views/notification_mailer/bulk_payment_confirmation_fyi.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<% profile_url = person_url(@person) %>

<p style="margin-bottom: 6px;">
New Bulk Payment
</p>

<h1 style="margin: 0;">
<strong><%= @person.full_name %></strong>
(<%= @person.preferred_email %>)
</h1>

<p style="margin: 4px 0 16px; font-size: 13px; color: #6b7280;">
Submitted on
<%= @submission.created_at
.in_time_zone("Pacific Time (US & Canada)")
.strftime("%B %-d, %Y at %-l:%M %p %Z") %>
</p>

<% if @event %>
<div style="text-align: center; background-color: #f3f4f6; border-radius: 6px; padding: 24px; margin: 16px 0;">
<% if @event.respond_to?(:pre_title) && @event.pre_title.present? %>
<p style="font-size: 14px; font-weight: 600; color: #374151; margin: 0 0 4px; font-family: 'Telefon Bold', sans-serif;">
<%= @event.pre_title %>
</p>
<% end %>

<h2 style="font-size: 28px; font-weight: bold; color: #166534; margin: 0 0 16px; font-family: Lato, sans-serif;">
<%= @event.title %>
</h2>

<p style="font-size: 22px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<%= @event.times(display_day: true, display_date: true) %>
</p>

<% if @event.labelled_cost.present? %>
<p style="font-size: 18px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<%= @event.labelled_cost %>
</p>
<% end %>
</div>
<% end %>

<table style="width: 100%; border-collapse: collapse; margin: 16px 0;">
<% if @answers["payer_organization"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Organization</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["payer_organization"] %></td>
</tr>
<% end %>
<% if @answers["payer_phone"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Phone</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["payer_phone"] %></td>
</tr>
<% end %>
<% if @answers["number_of_attendees"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Number of attendees</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["number_of_attendees"] %></td>
</tr>
<% end %>
<% if @answers["payment_method"].present? %>
<tr>
<td style="padding: 6px 0; color: #6b7280; font-size: 14px;">Payment method</td>
<td style="padding: 6px 0; text-align: right; font-weight: 600;"><%= @answers["payment_method"] %></td>
</tr>
<% end %>
</table>

<% if @attendees.any? %>
<p style="margin-bottom: 4px;"><strong>Attendees</strong></p>
<table style="width: 100%; border-collapse: collapse; margin: 8px 0 16px;">
<thead>
<tr>
<th style="text-align: left; border-bottom: 1px solid #e5e7eb; padding: 6px; font-size: 13px; color: #6b7280;">Name</th>
<th style="text-align: left; border-bottom: 1px solid #e5e7eb; padding: 6px; font-size: 13px; color: #6b7280;">Email</th>
</tr>
</thead>
<tbody>
<% @attendees.each do |attendee| %>
<tr>
<td style="border-bottom: 1px solid #f3f4f6; padding: 6px; font-size: 14px;"><%= [ attendee["first_name"], attendee["last_name"] ].compact_blank.join(" ") %></td>
<td style="border-bottom: 1px solid #f3f4f6; padding: 6px; font-size: 14px;"><%= attendee["email"] %></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>

<p>
<% if @event %>
<a href="<%= event_bulk_payment_url(@event, submission_id: @submission.id) %>"
style="display:inline-block;padding:10px 16px;background:#2563eb;color:#ffffff;text-decoration:none;border-radius:6px;font-weight:bold;">
View submission
</a>
<% end %>

<a href="<%= profile_url %>"
style="display:inline-block;background:#e5e7eb;color:#111827;text-decoration:none;padding:10px 16px;border-radius:6px;font-weight:bold;">
View profile
</a>
</p>
Loading