<% (0..(area['w'] / area['cell_w']).ceil).each do |index| %>
<% if value[index] %>
<%= value[index] %>
diff --git a/app/views/submissions_filters/_applied_filters.html.erb b/app/views/submissions_filters/_applied_filters.html.erb
index ad988d71..f1cdb070 100644
--- a/app/views/submissions_filters/_applied_filters.html.erb
+++ b/app/views/submissions_filters/_applied_filters.html.erb
@@ -1,4 +1,26 @@
-<% query_params = params.permit(:q, :status).merge(filter_params) %>
+<% query_params = params.permit(:q).merge(filter_params) %>
+<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit' }[params[:status]] %>
+
+ <%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
+ <%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>
+ <%= t(params[:status]) %>
+ <% end %>
+ <%= link_to url_for(params.to_unsafe_h.except(:status)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
+ <%= svg_icon('x', class: 'w-5 h-5') %>
+ <% end %>
+
+<% end %>
+<% if params[:author].present? %>
+
+ <%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
+ <%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
+ <%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %>
+ <% end %>
+ <%= link_to url_for(params.to_unsafe_h.except(:author)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
+ <%= svg_icon('x', class: 'w-5 h-5') %>
+ <% end %>
+
+<% end %>
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
@@ -37,14 +59,3 @@
<% end %>
<% end %>
-<% if params[:author].present? %>
-
- <%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
- <%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
- <%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %>
- <% end %>
- <%= link_to url_for(params.to_unsafe_h.except(:author)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
- <%= svg_icon('x', class: 'w-5 h-5') %>
- <% end %>
-
-<% end %>
diff --git a/app/views/submissions_filters/_filter_button.html.erb b/app/views/submissions_filters/_filter_button.html.erb
index b840729d..3f24aaf6 100644
--- a/app/views/submissions_filters/_filter_button.html.erb
+++ b/app/views/submissions_filters/_filter_button.html.erb
@@ -1,8 +1,8 @@
-<% query_params = params.permit(:q, :status).merge(filter_params) %>
+<% query_params = params.permit(:q).merge(filter_params) %>
-
@@ -17,6 +17,12 @@
<%= t('created_at') %>
<% end %>
+ -
+ <%= link_to submissions_filter_path('status', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
+ <%= svg_icon('info_circle', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
+ <%= t('status') %>
+ <% end %>
+
-
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
<%= svg_icon('user', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
diff --git a/app/views/submissions_filters/_filter_modal.html.erb b/app/views/submissions_filters/_filter_modal.html.erb
index afb3916f..af4a9b86 100644
--- a/app/views/submissions_filters/_filter_modal.html.erb
+++ b/app/views/submissions_filters/_filter_modal.html.erb
@@ -1,6 +1,5 @@
<%= render 'shared/turbo_modal', title: local_assigns[:title] do %>
<%= form_for '', url: params[:path], method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
- <%= hidden_field_tag :status, params[:status] if params[:status].present? %>
<%= hidden_field_tag :q, params[:q] if params[:q].present? %>
<% local_assigns[:default_params].each do |key, value| %>
<%= hidden_field_tag(key, value) if value.present? %>
@@ -11,7 +10,7 @@
<% if params[:with_remove] %>
- <%= link_to t('remove_filter'), "#{params[:path]}?#{params.to_unsafe_h.slice(:q, :status).merge(local_assigns[:default_params]).to_query}", class: 'link', data: { turbo_frame: :_top } %>
+ <%= link_to t('remove_filter'), "#{params[:path]}?#{params.to_unsafe_h.slice(:q).merge(local_assigns[:default_params]).to_query}", class: 'link', data: { turbo_frame: :_top } %>
<% end %>
<% end %>
diff --git a/app/views/submissions_filters/status.html.erb b/app/views/submissions_filters/status.html.erb
new file mode 100644
index 00000000..d614b540
--- /dev/null
+++ b/app/views/submissions_filters/status.html.erb
@@ -0,0 +1,14 @@
+<%= render 'filter_modal', title: t('status'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[status])) do %>
+
+<% end %>
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml
index 87c69fcd..92ee1d8e 100644
--- a/config/locales/i18n.yml
+++ b/config/locales/i18n.yml
@@ -20,6 +20,8 @@ en: &en
language_ko: 한국어
hi_there: Hi there
thanks: Thanks
+ pending_by_me: Pending by me
+ partially_completed: Partially completed
unarchive: Unarchive
first_party: 'First Party'
remove_filter: Remove filter
@@ -660,6 +662,7 @@ en: &en
policy_links: Policy Links
markdown_content_e_g: Markdown content, e.g.
privacy_policy: Privacy Policy
+ use_the_edit_form_to_move_it_to_another_team: Use the edit form to move it to another team.
submission_event_names:
send_email_to_html: '
Email sent to %{submitter_name}'
send_reminder_email_to_html: '
Reminder email sent to %{submitter_name}'
@@ -698,6 +701,8 @@ en: &en
read: Read your data
es: &es
+ partially_completed: Parcialmente completado
+ pending_by_me: Pendiente por mi
add: Agregar
adding: Agregando
owner: Propietario
@@ -1340,6 +1345,7 @@ es: &es
policy_links: Enlaces de Políticas
markdown_content_e_g: Contenido Markdown, por ej.
privacy_policy: Política de Privacidad
+ use_the_edit_form_to_move_it_to_another_team: Usa el formulario de edición para moverlo a otro equipo.
submission_event_names:
send_email_to_html: '
Correo electrónico enviado a %{submitter_name}'
send_reminder_email_to_html: '
Correo de recordatorio enviado a %{submitter_name}'
@@ -1378,6 +1384,7 @@ es: &es
read: Leer tus datos
it: &it
+ pending_by_me: In sospeso da me
add: Aggiungi
adding: Aggiungendo
owner: Proprietario
@@ -2020,6 +2027,7 @@ it: &it
policy_links: Collegamenti alle Politiche
markdown_content_e_g: Contenuto Markdown, ad es.
privacy_policy: Politica sulla Privacy
+ use_the_edit_form_to_move_it_to_another_team: Usa il modulo di modifica per spostarlo in un altro team.
submission_event_names:
send_email_to_html: '
E-mail inviato a %{submitter_name}'
send_reminder_email_to_html: '
E-mail di promemoria inviato a %{submitter_name}'
@@ -2058,6 +2066,8 @@ it: &it
read: Leggi i tuoi dati
fr: &fr
+ partially_completed: Partiellement complété
+ pending_by_me: En attente par moi
add: Ajouter
adding: Ajout
owner: Propriétaire
@@ -2701,6 +2711,7 @@ fr: &fr
policy_links: Liens des Politiques
markdown_content_e_g: Contenu Markdown, par ex.
privacy_policy: Politique de Confidentialité
+ use_the_edit_form_to_move_it_to_another_team: Utilisez le formulaire de modification pour le déplacer vers une autre équipe.
submission_event_names:
send_email_to_html: '
E-mail envoyé à %{submitter_name}'
send_reminder_email_to_html: '
E-mail de rappel envoyé à %{submitter_name}'
@@ -2739,6 +2750,8 @@ fr: &fr
read: Lire vos données
pt: &pt
+ partially_completed: Parcialmente concluído
+ pending_by_me: Pendente por mim
add: Adicionar
adding: Adicionando
owner: Proprietário
@@ -3381,6 +3394,7 @@ pt: &pt
policy_links: Links de Políticas
markdown_content_e_g: Conteúdo Markdown, ex.
privacy_policy: Política de Privacidade
+ use_the_edit_form_to_move_it_to_another_team: Use o formulário de edição para movê-lo para outra equipe.
submission_event_names:
send_email_to_html: '
E-mail enviado para %{submitter_name}'
send_reminder_email_to_html: '
E-mail de lembrete enviado para %{submitter_name}'
@@ -3419,6 +3433,8 @@ pt: &pt
read: Ler seus dados
de: &de
+ partially_completed: Teilweise abgeschlossen
+ pending_by_me: Ausstehend von mir
add: Hinzufügen
adding: Hinzufügen
owner: Eigentümer
@@ -4061,6 +4077,7 @@ de: &de
policy_links: Richtlinien-Links
markdown_content_e_g: Markdown-Inhalt, z. B.
privacy_policy: Datenschutzrichtlinie
+ use_the_edit_form_to_move_it_to_another_team: Verwenden Sie das Bearbeitungsformular, um ihn in ein anderes Team zu verschieben.
submission_event_names:
send_email_to_html: '
E-Mail gesendet an %{submitter_name}'
send_reminder_email_to_html: '
Erinnerungs-E-Mail gesendet an %{submitter_name}'
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
index 2df9d73e..01ba85a5 100644
--- a/config/sidekiq.yml
+++ b/config/sidekiq.yml
@@ -1,6 +1,7 @@
queues:
- [default, 1]
- [webhooks, 1]
+ - [sms, 2]
- [images, 1]
- [mailers, 1]
- [recurrent, 1]
diff --git a/lib/params/base_validator.rb b/lib/params/base_validator.rb
index 29c1985f..8a5abcf1 100644
--- a/lib/params/base_validator.rb
+++ b/lib/params/base_validator.rb
@@ -89,12 +89,13 @@ module Params
raise_error(message || "#{key} must be unique")
end
- def in_path(params, path = [])
+ def in_path(params, path = [], skip_blank: false)
old_path = @current_path
@current_path = [old_path, *path].compact_blank.map(&:to_s).join('.')
param = params.dig(*path)
+ param = nil if skip_blank && param.blank?
yield params.dig(*path) if param
diff --git a/lib/params/submission_create_validator.rb b/lib/params/submission_create_validator.rb
index eef14391..5737e1dd 100644
--- a/lib/params/submission_create_validator.rb
+++ b/lib/params/submission_create_validator.rb
@@ -49,7 +49,7 @@ module Params
type(params, :message, Hash)
type(params, :submitters, Array)
- in_path(params, :message) do |message_params|
+ in_path(params, :message, skip_blank: true) do |message_params|
type(message_params, :subject, String)
type(message_params, :body, String)
diff --git a/lib/submissions/filter.rb b/lib/submissions/filter.rb
index f4a2271a..11d40a17 100644
--- a/lib/submissions/filter.rb
+++ b/lib/submissions/filter.rb
@@ -4,6 +4,7 @@ module Submissions
module Filter
ALLOWED_PARAMS = %w[
author
+ status
completed_at_from
completed_at_to
created_at_from
@@ -22,31 +23,68 @@ module Submissions
def call(submissions, current_user, params)
filters = normalize_filter_params(params, current_user)
- if filters[:author].present?
- user = current_user.account.users.find_by(email: filters[:author])
- submissions = submissions.where(created_by_user_id: user&.id || -1)
+ submissions = filter_by_author(submissions, filters, current_user)
+ submissions = filter_by_status(submissions, filters)
+ submissions = filter_by_created_at(submissions, filters)
+
+ filter_by_completed_at(submissions, filters)
+ end
+
+ def filter_by_author(submissions, filters, current_user)
+ return submissions if filters[:author].blank?
+
+ user = current_user.account.users.find_by(email: filters[:author])
+ submissions.where(created_by_user_id: user&.id || -1)
+ end
+
+ 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))
end
+ submissions
+ end
+
+ def filter_by_created_at(submissions, filters)
submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
if filters[:created_at_to].present?
submissions = submissions.where(created_at: ..filters[:created_at_to].end_of_day)
end
- if filters[:completed_at_from].present? || filters[:completed_at_to].present?
- completed_arel = Submitter.arel_table[:completed_at].maximum
- submissions = submissions.completed.joins(:submitters).group(:id)
+ submissions
+ end
- if filters[:completed_at_from].present?
- submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
- end
+ def filter_by_completed_at(submissions, filters)
+ return submissions unless filters[:completed_at_from].present? || filters[:completed_at_to].present?
- if filters[:completed_at_to].present?
- submissions = submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
- end
+ completed_arel = Submitter.arel_table[:completed_at].maximum
+ submissions = submissions.completed.joins(:submitters).group(:id)
+
+ if filters[:completed_at_from].present?
+ submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
end
- submissions
+ return submissions if filters[:completed_at_to].blank?
+
+ submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
end
def normalize_filter_params(params, current_user)
diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb
index 317fdc37..f9c66a89 100644
--- a/lib/submissions/generate_result_attachments.rb
+++ b/lib/submissions/generate_result_attachments.rb
@@ -236,7 +236,7 @@ module Submissions
reason_string =
I18n.with_locale(submitter.account.locale) do
- "#{I18n.t('reason')}: #{reason_value || I18n.t('digitally_signed_by')} " \
+ "#{reason_value ? "#{I18n.t('reason')}: " : ''}#{reason_value || I18n.t('digitally_signed_by')} " \
"#{submitter.name}#{submitter.email.present? ? " <#{submitter.email}>" : ''}\n" \
"#{I18n.l(attachment.created_at.in_time_zone(submitter.account.timezone), format: :long)} " \
"#{TimeUtils.timezone_abbr(submitter.account.timezone, attachment.created_at)}"
@@ -382,7 +382,10 @@ module Submissions
when ->(type) { type == 'cells' && !area['cell_w'].to_f.zero? }
cell_width = area['cell_w'] * width
- TextUtils.maybe_rtl_reverse(value).chars.each_with_index do |char, index|
+ chars = TextUtils.maybe_rtl_reverse(value).chars
+ chars = chars.reverse if field.dig('preferences', 'align') == 'right'
+
+ chars.each_with_index do |char, index|
next if char.blank?
text = HexaPDF::Layout::TextFragment.create(char, font:,
@@ -409,9 +412,15 @@ module Submissions
line_height = layouter.fit([text], cell_width, height).lines.first.height
end
+ x =
+ if field.dig('preferences', 'align') == 'right'
+ ((area['x'] + area['w']) * width) - (cell_width * (index + 1))
+ else
+ (area['x'] * width) + (cell_width * index)
+ end
+
cell_layouter.fit([text], cell_width, [line_height, area['h'] * height].max)
- .draw(canvas, ((area['x'] * width) + (cell_width * index)),
- height - (area['y'] * height))
+ .draw(canvas, x, height - (area['y'] * height))
end
else
if field['type'] == 'date'
diff --git a/lib/templates/clone.rb b/lib/templates/clone.rb
index 45bd5350..cd1118a3 100644
--- a/lib/templates/clone.rb
+++ b/lib/templates/clone.rb
@@ -10,24 +10,32 @@ module Templates
template.external_id = external_id
template.author = author
template.preferences = original_template.preferences.deep_dup
- template.name = name || "#{original_template.name} (#{I18n.t('clone')})"
+ template.name = name.presence || "#{original_template.name} (#{I18n.t('clone')})"
- template.assign_attributes(original_template.slice(:folder_id, :schema))
+ if folder_name.present?
+ template.folder = TemplateFolders.find_or_create_by_name(author, folder_name)
+ else
+ template.folder_id = original_template.folder_id
+ end
- template.folder = TemplateFolders.find_or_create_by_name(author, folder_name) if folder_name.present?
+ template.submitters, template.fields, template.schema =
+ update_submitters_and_fields_and_schema(original_template.submitters.deep_dup,
+ original_template.fields.deep_dup,
+ original_template.schema.deep_dup)
- template.submitters, template.fields = clone_submitters_and_fields(original_template)
+ if name.present? && template.schema.size == 1 &&
+ original_template.schema.first['name'] == original_template.name &&
+ template.name != "#{original_template.name} (#{I18n.t('clone')})"
+ template.schema.first['name'] = template.name
+ end
template
end
- def clone_submitters_and_fields(original_template)
+ def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema)
submitter_uuids_replacements = {}
field_uuids_replacements = {}
- cloned_submitters = original_template['submitters'].deep_dup
- cloned_fields = original_template['fields'].deep_dup
-
cloned_submitters.each do |submitter|
new_submitter_uuid = SecureRandom.uuid
@@ -44,20 +52,28 @@ module Templates
field['submitter_uuid'] = submitter_uuids_replacements[field['submitter_uuid']]
end
- replace_fields_regexp = Regexp.union(field_uuids_replacements.keys)
+ replace_fields_regexp = nil
cloned_fields.each do |field|
Array.wrap(field['conditions']).each do |condition|
condition['field_uuid'] = field_uuids_replacements[condition['field_uuid']]
end
- if field.dig('preferences', 'formula').present?
- field['preferences']['formula'] =
- field['preferences']['formula'].gsub(replace_fields_regexp, field_uuids_replacements)
+ next if field.dig('preferences', 'formula').blank?
+
+ replace_fields_regexp ||= Regexp.union(field_uuids_replacements.keys)
+
+ field['preferences']['formula'] =
+ field['preferences']['formula'].gsub(replace_fields_regexp, field_uuids_replacements)
+ end
+
+ cloned_schema.each do |field|
+ Array.wrap(field['conditions']).each do |condition|
+ condition['field_uuid'] = field_uuids_replacements[condition['field_uuid']]
end
end
- [cloned_submitters, cloned_fields]
+ [cloned_submitters, cloned_fields, cloned_schema]
end
end
end
diff --git a/lib/templates/clone_attachments.rb b/lib/templates/clone_attachments.rb
index 970214b3..8b08735d 100644
--- a/lib/templates/clone_attachments.rb
+++ b/lib/templates/clone_attachments.rb
@@ -4,20 +4,21 @@ module Templates
module CloneAttachments
module_function
- def call(template:, original_template:)
+ def call(template:, original_template:, documents: [])
schema_uuids_replacements = {}
- cloned_schema = original_template.schema.deep_dup
- cloned_fields = template.fields.deep_dup
-
- cloned_schema.each do |schema_item|
+ template.schema.each_with_index do |schema_item, index|
new_schema_item_uuid = SecureRandom.uuid
schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid
schema_item['attachment_uuid'] = new_schema_item_uuid
+
+ new_name = documents&.dig(index, 'name')
+
+ schema_item['name'] = new_name if new_name.present?
end
- cloned_fields.each do |field|
+ template.fields.each do |field|
next if field['areas'].blank?
field['areas'].each do |area|
@@ -25,7 +26,7 @@ module Templates
end
end
- template.update!(schema: cloned_schema, fields: cloned_fields)
+ template.save!
original_template.schema_documents.map do |document|
new_document =
diff --git a/spec/factories/accounts.rb b/spec/factories/accounts.rb
index b0551e06..6a5229bf 100644
--- a/spec/factories/accounts.rb
+++ b/spec/factories/accounts.rb
@@ -6,6 +6,10 @@ FactoryBot.define do
locale { 'en-US' }
timezone { 'UTC' }
+ transient do
+ teams_count { 2 }
+ end
+
trait :with_testing_account do
after(:create) do |account|
testing_account = account.dup.tap { |a| a.name = "Testing - #{account.name}" }
@@ -14,5 +18,16 @@ FactoryBot.define do
account.save!
end
end
+
+ trait :with_teams do
+ after(:create) do |account, evaluator|
+ Array.new(evaluator.teams_count) do |i|
+ Account.create!(
+ name: "Team #{i}",
+ linked_account_account: AccountLinkedAccount.new(account_type: :linked, account:)
+ )
+ end
+ end
+ end
end
end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 1157da61..e2195ceb 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -68,6 +68,10 @@ RSpec.configure do |config|
config.before do |example|
Sidekiq::Testing.inline! if example.metadata[:sidekiq] == :inline
end
+
+ config.before(multitenant: true) do
+ allow(Docuseal).to receive(:multitenant?).and_return(true)
+ end
end
ActiveSupport.run_load_hooks(:rails_specs, self)
diff --git a/spec/requests/submissions_spec.rb b/spec/requests/submissions_spec.rb
index c5d02f4b..502f6520 100644
--- a/spec/requests/submissions_spec.rb
+++ b/spec/requests/submissions_spec.rb
@@ -89,6 +89,21 @@ describe 'Submission API', type: :request do
expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
end
+ it 'creates a submission when the message is empty' do
+ post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+ template_id: templates[0].id,
+ send_email: true,
+ submitters: [{ role: 'First Party', email: 'john.doe@example.com' }],
+ message: {}
+ }.to_json
+
+ expect(response).to have_http_status(:ok)
+
+ submission = Submission.last
+
+ expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
+ end
+
it 'creates a submission when some submitter roles are not provided' do
post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
template_id: multiple_submitters_template.id,
@@ -168,6 +183,22 @@ describe 'Submission API', type: :request do
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body).to eq({ 'error' => 'Defined more signing parties than in template' })
end
+
+ it 'returns an error if the message has no body value' do
+ post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+ template_id: templates[0].id,
+ send_email: true,
+ submitters: [
+ { role: 'First Party', email: 'john.doe@example.com' }
+ ],
+ message: {
+ subject: 'Custom Email Subject'
+ }
+ }.to_json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ expect(response.parsed_body).to eq({ 'error' => 'body is required in `message`.' })
+ end
end
describe 'POST /api/submissions/emails' do
diff --git a/spec/system/team_settings_spec.rb b/spec/system/team_settings_spec.rb
index 3472917a..077c5104 100644
--- a/spec/system/team_settings_spec.rb
+++ b/spec/system/team_settings_spec.rb
@@ -4,6 +4,7 @@ require 'rails_helper'
RSpec.describe 'Team Settings' do
let(:account) { create(:account) }
+ let(:second_account) { create(:account) }
let(:current_user) { create(:user, account:) }
before do
@@ -56,6 +57,43 @@ RSpec.describe 'Team Settings' do
end
end
+ it "doesn't create a new user if a user already exists" do
+ click_link 'New User'
+
+ within '#modal' do
+ fill_in 'First name', with: 'Michael'
+ fill_in 'Last name', with: 'Jordan'
+ fill_in 'Email', with: users.first.email
+ fill_in 'Password', with: 'password'
+
+ expect do
+ click_button 'Submit'
+ end.not_to change(User, :count)
+ end
+
+ expect(page).to have_content('Email already exists')
+ end
+
+ it "doesn't create a new user if a user belongs to another account" do
+ user = create(:user, account: second_account)
+ visit settings_users_path
+
+ click_link 'New User'
+
+ within '#modal' do
+ fill_in 'First name', with: 'Michael'
+ fill_in 'Last name', with: 'Jordan'
+ fill_in 'Email', with: user.email
+ fill_in 'Password', with: 'password'
+
+ expect do
+ click_button 'Submit'
+ end.not_to change(User, :count)
+
+ expect(page).to have_content('Email has already been taken')
+ end
+ end
+
it 'updates a user' do
first(:link, 'Edit').click