- <%= ff.label :request_email_body, t('email_body'), class: 'label' %>
-
- <%= svg_icon('info_circle', class: 'w-4 h-4') %>
-
+ <%= tag.input id: 'request_email_per_submitter', value: '1', name: 'request_email_per_submitter', class: 'peer', type: 'checkbox', hidden: true, checked: @template.preferences['submitters'].to_a.size > 1 %>
+
+ <%= f.fields_for :preferences, Struct.new(:request_email_subject, :request_email_body).new(*(@template.preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY).value.values_at('subject', 'body'))) do |ff| %>
+
-
- <%= ff.text_area :request_email_body, required: true, class: 'base-input w-full py-2', dir: 'auto' %>
-
+
+ <% end %>
+
+ <% if @template.submitters.size > 1 && @template.submitters.size < 5 %>
+
+ <% options = @template.submitters.map { |e| [e['name'], "request_email_#{e['uuid']}"] } %>
+
+
+ <% options.each_with_index do |(label, val), index| %>
+
+ <%= f.radio_button :selected, val, checked: index.zero?, id: "#{val}_radio", data: { action: 'click:toggle-visible#trigger' }, class: 'hidden peer' %>
+ <%= f.label :selected, label, value: val, for: "#{val}_radio", class: 'tab w-full tab-lifted peer-checked:tab-active' %>
+
+ <% end %>
+
+
+ <%= f.fields_for :preferences do |ff| %>
+ <% @template.submitters.each_with_index do |submitter, index| %>
+
+ <% submitter_preferences = f.object.preferences['submitters'].to_a.find { |e| e['uuid'] == submitter['uuid'] } || {} %>
+ <%= ff.fields_for :submitters, Struct.new(:request_email_subject, :request_email_body).new(*(submitter_preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || @template.preferences.values_at('request_email_subject', 'request_email_body').compact_blank.presence || AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY).value.values_at('subject', 'body'))), index: nil do |fff| %>
+ <%= fff.hidden_field :uuid, value: submitter['uuid'] %>
+
+ <%= fff.label :request_email_subject, t('email_subject'), class: 'label' %>
+ <%= fff.text_field :request_email_subject, required: true, class: 'base-input', dir: 'auto' %>
+
+
+ <% end %>
+
+ <% end %>
+ <% end %>
<% end %>
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml
index 484fe006e..924d46fde 100644
--- a/config/locales/i18n.yml
+++ b/config/locales/i18n.yml
@@ -20,6 +20,7 @@ en: &en
language_ko: 한국어
hi_there: Hi there
thanks: Thanks
+ edit_per_party: Edit per party
reply_to: Reply to
pending_by_me: Pending by me
partially_completed: Partially completed
@@ -732,6 +733,7 @@ en: &en
read: Read your data
es: &es
+ edit_per_party: Editar por parte
signed: Firmado
reply_to: Responder a
partially_completed: Parcialmente completado
@@ -1446,6 +1448,7 @@ es: &es
read: Leer tus datos
it: &it
+ edit_per_party: Modifica per partito
signed: Firmato
reply_to: Rispondi a
pending_by_me: In sospeso da me
@@ -2159,6 +2162,7 @@ it: &it
read: Leggi i tuoi dati
fr: &fr
+ edit_per_party: Éditer par partie
signed: Signé
reply_to: Répondre à
partially_completed: Partiellement complété
@@ -2874,6 +2878,7 @@ fr: &fr
read: Lire vos données
pt: &pt
+ edit_per_party: Edita por festa
signed: Assinado
reply_to: Responder a
partially_completed: Parcialmente concluído
@@ -3588,6 +3593,7 @@ pt: &pt
read: Ler seus dados
de: &de
+ edit_per_party: Bearbeiten pro Partei
signed: Unterschrieben
reply_to: Antworten auf
partially_completed: Teilweise abgeschlossen
diff --git a/config/routes.rb b/config/routes.rb
index 0223f2340..d4d792c6e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -145,9 +145,7 @@
get :completed
end
- resources :send_submission_email, only: %i[create] do
- get :success, on: :collection
- end
+ resources :send_submission_email, only: %i[create]
resources :submitters, only: %i[], param: 'slug' do
resources :download, only: %i[index], controller: 'submissions_download'
diff --git a/lib/pdf_icons.rb b/lib/pdf_icons.rb
index e9605f188..8f7671130 100644
--- a/lib/pdf_icons.rb
+++ b/lib/pdf_icons.rb
@@ -24,6 +24,10 @@ def logo_new_io
StringIO.new(logo_new_data)
end
+ def stamp_logo_io
+ StringIO.new(stamp_logo_data)
+ end
+
def check_data
@check_data ||= PATH.join('check.png').read
end
@@ -39,4 +43,8 @@ def logo_data
def logo_new_data
@logo_new_data ||= PATH.join('logo_new.png').read
end
+
+ def stamp_logo_data
+ @stamp_logo_data ||= PATH.join('stamp-logo.png').read
+ end
end
diff --git a/lib/pdf_icons/stamp-logo.png b/lib/pdf_icons/stamp-logo.png
new file mode 100644
index 000000000..c5a2738c4
Binary files /dev/null and b/lib/pdf_icons/stamp-logo.png differ
diff --git a/lib/submissions/create_from_submitters.rb b/lib/submissions/create_from_submitters.rb
index 421d431f6..c2144ad81 100644
--- a/lib/submissions/create_from_submitters.rb
+++ b/lib/submissions/create_from_submitters.rb
@@ -36,8 +36,8 @@ def call(template:, user:, submissions_attrs:, source:, submitters_order:, param
is_order_sent = submitters_order == 'random' || index.zero?
- build_submitter(submission:, attrs: submitter_attrs, uuid:,
- is_order_sent:, user:,
+ build_submitter(submission:, attrs: submitter_attrs,
+ uuid:, is_order_sent:, user:, params:,
preferences: preferences.merge(submission_preferences))
end
@@ -69,6 +69,16 @@ def maybe_add_invite_submitters(submission, template)
end
end
+ def submitter_message_preferences(uuid, params)
+ return {} if params[:request_email_per_submitter] != '1'
+ return {} if params[:is_custom_message] != '1'
+
+ {
+ 'subject' => params.dig('submitter_preferences', uuid, 'subject'),
+ 'body' => params.dig('submitter_preferences', uuid, 'body')
+ }.compact_blank
+ end
+
def maybe_set_template_fields(submission, submitters_attrs, default_submitter_uuid: nil)
template_fields = (submission.template_fields || submission.template.fields).deep_dup
@@ -175,9 +185,10 @@ def find_submitter_uuid(template, attrs, index)
uuid || template.submitters[index]&.dig('uuid')
end
- def build_submitter(submission:, attrs:, uuid:, is_order_sent:, user:, preferences:)
+ def build_submitter(submission:, attrs:, uuid:, is_order_sent:, user:, preferences:, params:)
email = Submissions.normalize_email(attrs[:email])
- submitter_preferences = Submitters.normalize_preferences(submission.account, user, attrs)
+ submitter_preferences = Submitters.normalize_preferences(submission.account, user,
+ attrs.merge(submitter_message_preferences(uuid, params)))
values = attrs[:values] || {}
phone_field_uuid = find_phone_field(submission, values)&.dig('uuid')
diff --git a/lib/submissions/filter.rb b/lib/submissions/filter.rb
index 11d40a17e..27a1c375f 100644
--- a/lib/submissions/filter.rb
+++ b/lib/submissions/filter.rb
@@ -37,30 +37,45 @@ def filter_by_author(submissions, filters, current_user)
submissions.where(created_by_user_id: user&.id || -1)
end
+ # rubocop:disable Metrics/MethodLength
def filter_by_status(submissions, filters)
- submissions = submissions.pending if filters[:status] == 'pending'
- submissions = submissions.completed if filters[:status] == 'completed'
- submissions = submissions.declined if filters[:status] == 'declined'
- submissions = submissions.expired if filters[:status] == 'expired'
-
- if filters[:status] == 'partially_completed'
- submissions =
- submissions.joins(:submitters)
- .group(:id)
- .having(Arel::Nodes::NamedFunction.new(
- 'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
- [Submitter.arel_table[:completed_at].eq(nil),
- Arel::Nodes.build_quoted(false)])]
- ).gt(0))
- .having(Arel::Nodes::NamedFunction.new(
- 'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
- [Submitter.arel_table[:completed_at].not_eq(nil),
- Arel::Nodes.build_quoted(false)])]
- ).gt(0))
+ case filters[:status]
+ when 'pending'
+ submissions.pending
+ when 'completed'
+ submissions.completed
+ when 'declined'
+ submissions.declined
+ when 'expired'
+ submissions.expired
+ when 'sent'
+ submissions.joins(:submitters)
+ .where(submitters: { opened_at: nil, completed_at: nil, declined_at: nil })
+ .where.not(submitters: { sent_at: nil })
+ .group(:id)
+ when 'opened'
+ submissions.joins(:submitters)
+ .where(submitters: { completed_at: nil, declined_at: nil })
+ .where.not(submitters: { opened_at: nil })
+ .group(:id)
+ when 'partially_completed'
+ submissions.joins(:submitters)
+ .group(:id)
+ .having(Arel::Nodes::NamedFunction.new(
+ 'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
+ [Submitter.arel_table[:completed_at].eq(nil),
+ Arel::Nodes.build_quoted(false)])]
+ ).gt(0))
+ .having(Arel::Nodes::NamedFunction.new(
+ 'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
+ [Submitter.arel_table[:completed_at].not_eq(nil),
+ Arel::Nodes.build_quoted(false)])]
+ ).gt(0))
+ else
+ submissions
end
-
- submissions
end
+ # rubocop:enable Metrics/MethodLength
def filter_by_created_at(submissions, filters)
submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb
index 1686d7eca..a5941863f 100644
--- a/lib/submissions/generate_result_attachments.rb
+++ b/lib/submissions/generate_result_attachments.rb
@@ -18,6 +18,8 @@ module GenerateResultAttachments
TEXT_TOP_MARGIN = 1
MAX_PAGE_ROTATE = 20
+ COURIER_FONT = 'Courier'
+
A4_SIZE = [595, 842].freeze
TESTING_FOOTER = 'Testing Document - NOT LEGALLY BINDING'
@@ -188,7 +190,8 @@ def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is
fill_color = field.dig('preferences', 'color').presence
- font = pdf.fonts.add(field.dig('preferences', 'font').presence || FONT_NAME)
+ font_name = field.dig('preferences', 'font').presence || FONT_NAME
+ font = pdf.fonts.add(font_name)
value = submitter.values[field['uuid']]
value = field['default_value'] if field['type'] == 'heading'
@@ -435,18 +438,19 @@ def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is
value = TextUtils.maybe_rtl_reverse(Array.wrap(value).join(', '))
- text = HexaPDF::Layout::TextFragment.create(value, font:,
- fill_color:,
- font_size:)
+ text_params = { font:, fill_color:, font_size: }
+ text_params[:line_height] = text_params[:font_size] * 1.6 if font_name == COURIER_FONT
+
+ text = HexaPDF::Layout::TextFragment.create(value, **text_params)
lines = layouter.fit([text], area['w'] * width, height).lines
box_height = lines.sum(&:height)
if preferences_font_size.blank? && box_height > (area['h'] * height) + 1
- text = HexaPDF::Layout::TextFragment.create(value,
- font:,
- fill_color:,
- font_size: (font_size / 1.4).to_i)
+ text_params[:font_size] = (font_size / 1.4).to_i
+ text_params[:line_height] = text_params[:font_size] * 1.6 if font_name == COURIER_FONT
+
+ text = HexaPDF::Layout::TextFragment.create(value, **text_params)
lines = layouter.fit([text], field['type'].in?(%w[date number]) ? width : area['w'] * width, height).lines
@@ -454,10 +458,10 @@ def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is
end
if preferences_font_size.blank? && box_height > (area['h'] * height) + 1
- text = HexaPDF::Layout::TextFragment.create(value,
- font:,
- fill_color:,
- font_size: (font_size / 1.9).to_i)
+ text_params[:font_size] = (font_size / 1.9).to_i
+ text_params[:line_height] = text_params[:font_size] * 1.6 if font_name == COURIER_FONT
+
+ text = HexaPDF::Layout::TextFragment.create(value, **text_params)
lines = layouter.fit([text], field['type'].in?(%w[date number]) ? width : area['w'] * width, height).lines
diff --git a/lib/submitters/create_stamp_attachment.rb b/lib/submitters/create_stamp_attachment.rb
index f18b013a2..5b5f1d64d 100644
--- a/lib/submitters/create_stamp_attachment.rb
+++ b/lib/submitters/create_stamp_attachment.rb
@@ -98,7 +98,7 @@ def build_text_image(submitter)
end
def load_logo(_submitter)
- PdfIcons.logo_io
+ PdfIcons.stamp_logo_io
end
end
end