Permette di mettere in pausa un invio e riprenderlo senza perdere la coda.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
class MailingsController < ApplicationController
|
class MailingsController < ApplicationController
|
||||||
before_action :require_current_project!
|
before_action :require_current_project!
|
||||||
before_action :set_mailing, only: %i[show edit update destroy queue cancel test_send test_preview refresh_recipients update_recipients preview audience update_audience]
|
before_action :set_mailing, only: %i[show edit update destroy queue pause resume cancel test_send test_preview refresh_recipients update_recipients preview audience update_audience]
|
||||||
before_action :load_form_collections, only: %i[new create edit update]
|
before_action :load_form_collections, only: %i[new create edit update]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@@ -11,8 +11,9 @@ class MailingsController < ApplicationController
|
|||||||
@page_title = "Invii email"
|
@page_title = "Invii email"
|
||||||
@mailings = Mailing.for_project(current_project).includes(:mail_identity).recent
|
@mailings = Mailing.for_project(current_project).includes(:mail_identity).recent
|
||||||
@mail_identities_count = MailIdentity.active.count
|
@mail_identities_count = MailIdentity.active.count
|
||||||
@active_mailings = @mailings.select(&:sending?)
|
@active_mailings = @mailings.select { |mailing| mailing.sending? || mailing.paused? }
|
||||||
@paused_mailings = @active_mailings.select { |mailing| mailing.next_send_at.present? && mailing.next_send_at > Time.current }
|
@window_paused_mailings = @mailings.select(&:sending?).select { |mailing| mailing.next_send_at.present? && mailing.next_send_at > Time.current }
|
||||||
|
@paused_mailings = @window_paused_mailings
|
||||||
@completed_today = @mailings.count { |mailing| mailing.sent? && mailing.completed_at&.to_date == Time.zone.today }
|
@completed_today = @mailings.count { |mailing| mailing.sent? && mailing.completed_at&.to_date == Time.zone.today }
|
||||||
@failed_open = @mailings.sum(&:failed_count)
|
@failed_open = @mailings.sum(&:failed_count)
|
||||||
end
|
end
|
||||||
@@ -180,16 +181,42 @@ class MailingsController < ApplicationController
|
|||||||
redirect_to @mailing, alert: e.message
|
redirect_to @mailing, alert: e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel
|
def pause
|
||||||
unless @mailing.sending?
|
unless @mailing.sending?
|
||||||
redirect_to @mailing, alert: "Solo un invio in corso può essere fermato."
|
redirect_to @mailing, alert: "Solo un invio in corso può essere messo in pausa."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
remaining = @mailing.pending_count + @mailing.queued_count
|
||||||
|
@mailing.pause_send!
|
||||||
|
redirect_to dashboard_mailings_path,
|
||||||
|
notice: "Invio in pausa. #{@mailing.sent_count} già inviate, #{remaining} pronte per quando riprendi."
|
||||||
|
rescue StandardError => e
|
||||||
|
redirect_to @mailing, alert: e.message
|
||||||
|
end
|
||||||
|
|
||||||
|
def resume
|
||||||
|
unless @mailing.paused?
|
||||||
|
redirect_to @mailing, alert: "Solo un invio in pausa può essere ripreso."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@mailing.resume_send!
|
||||||
|
redirect_to dashboard_mailings_path, notice: "Invio ripreso da dove era rimasto."
|
||||||
|
rescue StandardError => e
|
||||||
|
redirect_to @mailing, alert: e.message
|
||||||
|
end
|
||||||
|
|
||||||
|
def cancel
|
||||||
|
unless @mailing.sending? || @mailing.paused?
|
||||||
|
redirect_to @mailing, alert: "Solo un invio in corso o in pausa può essere annullato."
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
pending = @mailing.pending_count + @mailing.queued_count
|
pending = @mailing.pending_count + @mailing.queued_count
|
||||||
@mailing.cancel_send!
|
@mailing.cancel_send!
|
||||||
redirect_to dashboard_mailings_path,
|
redirect_to dashboard_mailings_path,
|
||||||
notice: "Invio fermato. #{@mailing.sent_count} già inviate, #{pending} annullate in coda."
|
notice: "Invio annullato. #{@mailing.sent_count} già inviate, #{pending} in coda saltate."
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
redirect_to @mailing, alert: e.message
|
redirect_to @mailing, alert: e.message
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ module ApplicationHelper
|
|||||||
colors = {
|
colors = {
|
||||||
"draft" => "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200",
|
"draft" => "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200",
|
||||||
"sending" => "bg-amber-100 text-amber-800 dark:bg-amber-950 dark:text-amber-200",
|
"sending" => "bg-amber-100 text-amber-800 dark:bg-amber-950 dark:text-amber-200",
|
||||||
|
"paused" => "bg-sky-100 text-sky-800 dark:bg-sky-950 dark:text-sky-200",
|
||||||
"sent" => "bg-emerald-100 text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200",
|
"sent" => "bg-emerald-100 text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200",
|
||||||
"cancelled" => "bg-rose-100 text-rose-800 dark:bg-rose-950 dark:text-rose-200"
|
"cancelled" => "bg-rose-100 text-rose-800 dark:bg-rose-950 dark:text-rose-200"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ module Catalog
|
|||||||
MAILING_STATUSES = {
|
MAILING_STATUSES = {
|
||||||
"draft" => "Bozza",
|
"draft" => "Bozza",
|
||||||
"sending" => "Invio in corso",
|
"sending" => "Invio in corso",
|
||||||
|
"paused" => "In pausa",
|
||||||
"sent" => "Inviata",
|
"sent" => "Inviata",
|
||||||
"cancelled" => "Annullata"
|
"cancelled" => "Annullata"
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|||||||
+20
-1
@@ -77,6 +77,10 @@ class Mailing < ApplicationRecord
|
|||||||
status == "sending"
|
status == "sending"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def paused?
|
||||||
|
status == "paused"
|
||||||
|
end
|
||||||
|
|
||||||
def sent?
|
def sent?
|
||||||
status == "sent"
|
status == "sent"
|
||||||
end
|
end
|
||||||
@@ -215,8 +219,23 @@ class Mailing < ApplicationRecord
|
|||||||
update!(status: "sent", completed_at: Time.current, next_send_at: nil)
|
update!(status: "sent", completed_at: Time.current, next_send_at: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pause_send!
|
||||||
|
raise "Solo un invio in corso può essere messo in pausa" unless sending?
|
||||||
|
|
||||||
|
update!(status: "paused", next_send_at: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resume_send!
|
||||||
|
raise "Solo un invio in pausa può essere ripreso" unless paused?
|
||||||
|
raise "Nessun destinatario da inviare" if pending_count.zero? && queued_count.zero?
|
||||||
|
|
||||||
|
update!(status: "sending")
|
||||||
|
enqueue_next_recipient!(mailing_recipients.pending.order(:id).first, from: Time.current) if pending_count.positive?
|
||||||
|
DrainOutboundJob.perform_later if mailing_recipients.queued.exists?
|
||||||
|
end
|
||||||
|
|
||||||
def cancel_send!
|
def cancel_send!
|
||||||
raise "Solo un invio in corso può essere fermato" unless sending?
|
raise "Solo un invio in corso o in pausa può essere annullato" unless sending? || paused?
|
||||||
|
|
||||||
transaction do
|
transaction do
|
||||||
mailing_recipients.where(status: %w[pending queued]).find_each do |recipient|
|
mailing_recipients.where(status: %w[pending queued]).find_each do |recipient|
|
||||||
|
|||||||
@@ -31,11 +31,9 @@
|
|||||||
<% elsif mailing.pending_count.positive? %>
|
<% elsif mailing.pending_count.positive? %>
|
||||||
<p class="mt-3 text-xs text-zinc-500">Prossima email in lavorazione.</p>
|
<p class="mt-3 text-xs text-zinc-500">Prossima email in lavorazione.</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if mailing.sending? %>
|
<% if mailing.sending? || mailing.paused? %>
|
||||||
<div class="mt-4 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
<div class="mt-4 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
||||||
<%= button_to "Ferma invio", cancel_mailing_path(mailing), method: :post,
|
<%= render "mailings/send_controls", mailing: mailing %>
|
||||||
class: btn_danger,
|
|
||||||
form: { data: { turbo_confirm: "Fermare l'invio? Restano inviate #{mailing.sent_count} email, #{mailing.pending_count + mailing.queued_count} in coda verranno annullate." } } %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<% if mailing.sending? %>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<%= button_to "Metti in pausa", pause_mailing_path(mailing), method: :post,
|
||||||
|
class: btn_secondary,
|
||||||
|
form: { data: { turbo_confirm: "Mettere in pausa? Le #{mailing.sent_count} già inviate restano. Le #{mailing.pending_count + mailing.queued_count} non ancora partite si riprendono da qui." } } %>
|
||||||
|
<%= button_to "Annulla definitivamente", cancel_mailing_path(mailing), method: :post,
|
||||||
|
class: btn_danger,
|
||||||
|
form: { data: { turbo_confirm: "Annullare l'invio? Le #{mailing.sent_count} già inviate restano, le #{mailing.pending_count + mailing.queued_count} in coda verranno saltate e non si potranno riprendere." } } %>
|
||||||
|
</div>
|
||||||
|
<% elsif mailing.paused? %>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<%= button_to "Riprendi invio", resume_mailing_path(mailing), method: :post, class: btn_primary %>
|
||||||
|
<%= button_to "Annulla definitivamente", cancel_mailing_path(mailing), method: :post,
|
||||||
|
class: btn_danger,
|
||||||
|
form: { data: { turbo_confirm: "Annullare l'invio? Le #{mailing.sent_count} già inviate restano, le #{mailing.pending_count + mailing.queued_count} in coda verranno saltate e non si potranno riprendere." } } %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -77,6 +77,8 @@
|
|||||||
<%= mailing.sent_count %> inviate · <%= mailing.opened_count %> aperte · <%= mailing.pending_count %> in coda
|
<%= mailing.sent_count %> inviate · <%= mailing.opened_count %> aperte · <%= mailing.pending_count %> in coda
|
||||||
<% if mailing.sending? && mailing.next_send_at.present? && mailing.next_send_at > Time.current %>
|
<% if mailing.sending? && mailing.next_send_at.present? && mailing.next_send_at > Time.current %>
|
||||||
· Riparte <%= l(mailing.next_send_at, format: :short) %>
|
· Riparte <%= l(mailing.next_send_at, format: :short) %>
|
||||||
|
<% elsif mailing.paused? %>
|
||||||
|
· In pausa, si può riprendere
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -34,15 +34,13 @@
|
|||||||
<%= link_to "Raffina destinatari", audience_mailing_path(@mailing), class: btn_secondary %>
|
<%= link_to "Raffina destinatari", audience_mailing_path(@mailing), class: btn_secondary %>
|
||||||
<%= button_to "Rigenera lista", refresh_recipients_mailing_path(@mailing), method: :post, class: btn_secondary %>
|
<%= button_to "Rigenera lista", refresh_recipients_mailing_path(@mailing), method: :post, class: btn_secondary %>
|
||||||
<%= render "mailings/delete_draft_button", mailing: @mailing, extra_class: "flex-1 sm:flex-none" %>
|
<%= render "mailings/delete_draft_button", mailing: @mailing, extra_class: "flex-1 sm:flex-none" %>
|
||||||
<% elsif @mailing.sending? %>
|
<% elsif @mailing.sending? || @mailing.paused? %>
|
||||||
<%= button_to "Ferma invio", cancel_mailing_path(@mailing), method: :post,
|
<%= render "mailings/send_controls", mailing: @mailing %>
|
||||||
class: btn_danger,
|
|
||||||
form: { data: { turbo_confirm: "Fermare l'invio? Le #{@mailing.sent_count} già inviate restano, #{@mailing.pending_count + @mailing.queued_count} in coda verranno annullate." } } %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if @mailing.sending? || @mailing.sent? %>
|
<% if @mailing.sending? || @mailing.paused? || @mailing.sent? %>
|
||||||
<%= render "mailings/progress_card", mailing: @mailing %>
|
<%= render "mailings/progress_card", mailing: @mailing %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@@ -65,7 +63,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if @mailing.sending? || @mailing.sent? || @mailing.cancelled? %>
|
<% if @mailing.sending? || @mailing.paused? || @mailing.sent? || @mailing.cancelled? %>
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-2 gap-3">
|
||||||
<div class="<%= card_class %> p-4">
|
<div class="<%= card_class %> p-4">
|
||||||
<div class="text-xs uppercase tracking-wide text-zinc-500">Aperte</div>
|
<div class="text-xs uppercase tracking-wide text-zinc-500">Aperte</div>
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
post :queue
|
post :queue
|
||||||
|
post :pause
|
||||||
|
post :resume
|
||||||
post :cancel
|
post :cancel
|
||||||
post :test_send
|
post :test_send
|
||||||
get :test_preview
|
get :test_preview
|
||||||
|
|||||||
@@ -303,6 +303,32 @@ class MailingsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
assert mailing.reload.sending?
|
assert mailing.reload.sending?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "pause keeps remaining recipients and resume continues from there" do
|
||||||
|
login_as users(:admin)
|
||||||
|
follow_redirect! if response.redirect?
|
||||||
|
create_campaign_org(name: "Seconda societa", email: "seconda@example.com")
|
||||||
|
mailing = create_mailing(identity: @identity, audience: "to_send")
|
||||||
|
mailing.rebuild_recipients!
|
||||||
|
recipients = mailing.mailing_recipients.order(:id).to_a
|
||||||
|
assert recipients.size >= 2
|
||||||
|
recipients.first.update!(status: "sent", sent_at: Time.current)
|
||||||
|
recipients.second.update!(status: "queued")
|
||||||
|
mailing.update!(status: "sending", queued_at: Time.current, test_sent_at: Time.current, test_sent_to: "test@example.com")
|
||||||
|
|
||||||
|
post pause_mailing_path(mailing, project_code: @project.code)
|
||||||
|
|
||||||
|
assert_redirected_to dashboard_mailings_path(project_code: @project.code)
|
||||||
|
assert_equal "paused", mailing.reload.status
|
||||||
|
assert_equal "sent", recipients.first.reload.status
|
||||||
|
assert_equal "queued", recipients.second.reload.status
|
||||||
|
|
||||||
|
post resume_mailing_path(mailing, project_code: @project.code)
|
||||||
|
|
||||||
|
assert_redirected_to dashboard_mailings_path(project_code: @project.code)
|
||||||
|
assert_equal "sending", mailing.reload.status
|
||||||
|
assert_equal "queued", recipients.second.reload.status
|
||||||
|
end
|
||||||
|
|
||||||
test "cancel stops pending recipients and keeps sent ones" do
|
test "cancel stops pending recipients and keeps sent ones" do
|
||||||
login_as users(:admin)
|
login_as users(:admin)
|
||||||
follow_redirect! if response.redirect?
|
follow_redirect! if response.redirect?
|
||||||
|
|||||||
Reference in New Issue
Block a user