From 4fb9637e2d42029cb3a0d2dff4321dd2bdbdc709 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Sun, 16 Mar 2025 21:56:30 +0200 Subject: [PATCH] add merge roles param --- app/controllers/api/submissions_controller.rb | 2 +- lib/submissions/create_from_submitters.rb | 99 +++++++++++++++++-- lib/submissions/normalize_param_utils.rb | 1 + lib/submitters/normalize_values.rb | 63 ++++++++---- 4 files changed, 135 insertions(+), 30 deletions(-) diff --git a/app/controllers/api/submissions_controller.rb b/app/controllers/api/submissions_controller.rb index d1c3fdb8..51a8e43e 100644 --- a/app/controllers/api/submissions_controller.rb +++ b/app/controllers/api/submissions_controller.rb @@ -179,7 +179,7 @@ module Api message: %i[subject body], submitters: [[:send_email, :send_sms, :completed_redirect_url, :uuid, :name, :email, :role, :completed, :phone, :application_key, :external_id, :reply_to, :go_to_last, - { metadata: {}, values: {}, readonly_fields: [], message: %i[subject body], + { metadata: {}, values: {}, roles: [], readonly_fields: [], message: %i[subject body], fields: [:name, :uuid, :default_value, :value, :title, :description, :readonly, :validation_pattern, :invalid_message, { default_value: [], value: [], preferences: {} }] }]] diff --git a/lib/submissions/create_from_submitters.rb b/lib/submissions/create_from_submitters.rb index f67f1142..6801aa13 100644 --- a/lib/submissions/create_from_submitters.rb +++ b/lib/submissions/create_from_submitters.rb @@ -23,15 +23,33 @@ module Submissions expire_at: attrs[:expire_at], template_submitters: [], submitters_order:) - maybe_set_template_fields(submission, attrs[:submitters]) + template_submitters = template.submitters.deep_dup attrs[:submitters].each_with_index do |submitter_attrs, index| - uuid = find_submitter_uuid(template, submitter_attrs, index) + if submitter_attrs[:roles].present? && submitter_attrs[:roles].size > 1 + template_submitter, template_submitters, submission.template_fields = + merge_submitters_and_fields(submitter_attrs, template_submitters, + submission.template_fields || submission.template.fields) + + submission.template_schema = submission.template.schema if submission.template_schema.blank? + + uuid = template_submitter['uuid'] + else + if submitter_attrs[:roles].present? && submitter_attrs[:roles].size == 1 + submitter_attrs[:role] = submitter_attrs[:roles].first + end + + uuid = find_submitter_uuid(template_submitters, submitter_attrs, index) - next if uuid.blank? - next if submitter_attrs.slice('email', 'phone', 'name').compact_blank.blank? + next if uuid.blank? + next if submitter_attrs.slice('email', 'phone', 'name').compact_blank.blank? + + submission.template_fields = submission.template.fields if submitter_attrs[:completed].present? && + submission.template_fields.blank? + + template_submitter = template_submitters.find { |e| e['uuid'] == uuid } + end - template_submitter = template.submitters.find { |e| e['uuid'] == uuid } submission.template_submitters << template_submitter.except('optional_invite_by_uuid', 'invite_by_uuid') is_order_sent = submitters_order == 'random' || index.zero? @@ -41,6 +59,8 @@ module Submissions preferences: preferences.merge(submission_preferences)) end + maybe_set_template_fields(submission, attrs[:submitters]) + if submission.submitters.size > template.submitters.size raise BaseError, 'Defined more signing parties than in template' end @@ -94,8 +114,10 @@ module Submissions def maybe_set_template_fields(submission, submitters_attrs, default_submitter_uuid: nil) template_fields = (submission.template_fields || submission.template.fields).deep_dup + submitters = submission.template_submitters || submission.template.submitters + submitters_attrs.each_with_index do |submitter_attrs, index| - submitter_uuid = default_submitter_uuid || find_submitter_uuid(submission.template, submitter_attrs, index) + submitter_uuid = default_submitter_uuid || find_submitter_uuid(submitters, submitter_attrs, index) process_readonly_fields_param(submitter_attrs[:readonly_fields], template_fields, submitter_uuid) process_field_values_param(submitter_attrs[:values], template_fields, submitter_uuid) @@ -112,6 +134,65 @@ module Submissions submission end + def merge_submitters_and_fields(submitter_attrs, template_submitters, template_fields) + selected_submitters = submitter_attrs[:roles].map do |role| + template_submitters.find { |e| e['name'].to_s.casecmp(role).zero? } || + raise(BaseError, "#{role} role doesn't exist") + end + + merge_role_uuids = selected_submitters.pluck('uuid') + old_role_uuids = template_submitters.pluck('uuid') + name = submitter_attrs[:role].presence || selected_submitters.pluck('name').join(' / ') + + merged_submitter, template_submitters = + build_merged_submitter(template_submitters, role_uuids: merge_role_uuids, name:) + + field_names_index = {} + + sorted_fields = template_fields.sort_by { |e| old_role_uuids.index(e['submitter_uuid']) } + + sorted_fields.each do |field| + next unless merge_role_uuids.include?(field['submitter_uuid']) + + if (existing_field = field_names_index[field['name']]) + existing_field['areas'] ||= [] + existing_field['areas'].push(*field['areas']) + template_fields.delete(field) + else + field['submitter_uuid'] = merged_submitter['uuid'] + field_names_index[field['name']] = field if field['name'].present? + end + end + + [merged_submitter, template_submitters, template_fields] + end + + def build_merged_submitter(submitters, role_uuids:, name:) + new_uuid = Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, role_uuids.sort.join(':')) + + merged_submitter = nil + + submitters = + submitters.filter_map do |submitter| + submitter['optional_invite_by_uuid'] = new_uuid if role_uuids.include?(submitter['optional_invite_by_uuid']) + submitter['invite_by_uuid'] = new_uuid if role_uuids.include?(submitter['invite_by_uuid']) + submitter['linked_to_uuid'] = new_uuid if role_uuids.include?(submitter['linked_to_uuid']) + + if role_uuids.include?(submitter['uuid']) + next if merged_submitter + + merged_submitter = submitter.deep_dup + merged_submitter['uuid'] = new_uuid + merged_submitter['name'] = name + merged_submitter.delete('linked_to_uuid') + end + + submitter + end + + [merged_submitter, submitters] + end + def process_readonly_fields_param(readonly_fields, template_fields, submitter_uuid) return if readonly_fields.blank? @@ -190,11 +271,11 @@ module Submissions field end - def find_submitter_uuid(template, attrs, index) + def find_submitter_uuid(submitters, attrs, index) uuid = attrs[:uuid].presence - uuid ||= template.submitters.find { |e| e['name'].to_s.casecmp(attrs[:role].to_s).zero? }&.dig('uuid') + uuid ||= submitters.find { |e| e['name'].to_s.casecmp(attrs[:role].to_s).zero? }&.dig('uuid') - uuid || template.submitters[index]&.dig('uuid') + uuid || submitters[index]&.dig('uuid') end def build_submitter(submission:, attrs:, uuid:, is_order_sent:, user:, preferences:, params:) diff --git a/lib/submissions/normalize_param_utils.rb b/lib/submissions/normalize_param_utils.rb index 07ded95c..388a55bc 100644 --- a/lib/submissions/normalize_param_utils.rb +++ b/lib/submissions/normalize_param_utils.rb @@ -33,6 +33,7 @@ module Submissions default_values, submitter_name: submitter_params[:role] || template.submitters.dig(index, 'name'), + role_names: submitter_params[:roles], for_submitter:, throw_errors: true) diff --git a/lib/submitters/normalize_values.rb b/lib/submitters/normalize_values.rb index 82e3eed6..08941916 100644 --- a/lib/submitters/normalize_values.rb +++ b/lib/submitters/normalize_values.rb @@ -17,38 +17,47 @@ module Submitters module_function - def call(template, values, submitter_name: nil, for_submitter: nil, throw_errors: false) - fields = fetch_fields(template, submitter_name:, for_submitter:) + def call(template, values, submitter_name: nil, role_names: nil, for_submitter: nil, throw_errors: false) + fields = + if role_names.present? + fetch_roles_fields(template, roles: role_names) + else + fetch_fields(template, submitter_name:, for_submitter:) + end fields_uuid_index = fields.index_by { |e| e['uuid'] } fields_name_index = build_fields_index(fields) attachments = [] - normalized_values = values.to_h.filter_map do |key, value| - if fields_uuid_index[key].blank? - original_key = key + normalized_values = values.to_h.each_with_object({}) do |(key, value), acc| + next if key.blank? + + uuid_field = fields_uuid_index[key] - key = fields_name_index[key]&.dig('uuid') || fields_name_index[key.to_s.downcase]&.dig('uuid') + value_fields = [uuid_field] if uuid_field - raise(UnknownFieldName, "Unknown field: #{original_key}") if key.blank? && throw_errors + if value_fields.blank? + value_fields = fields_name_index[key].presence || fields_name_index[key.to_s.downcase] + + raise(UnknownFieldName, "Unknown field: #{key}") if value_fields.blank? && throw_errors end - next if key.blank? + next if value_fields.blank? - field = fields_uuid_index[key] + value_fields.each do |field| + if field['type'].in?(%w[initials signature image file stamp]) && value.present? + new_value, new_attachments = + normalize_attachment_value(value, field, template.account, attachments, for_submitter) - if field['type'].in?(%w[initials signature image file stamp]) && value.present? - new_value, new_attachments = - normalize_attachment_value(value, field, template.account, attachments, for_submitter) + attachments.push(*new_attachments) - attachments.push(*new_attachments) + value = new_value + end - value = new_value + acc[field['uuid']] = normalize_value(field, value) end - - [key, normalize_value(field, value)] - end.to_h + end [normalized_values, attachments] end @@ -103,10 +112,24 @@ module Submitters end end + def fetch_roles_fields(template, roles:) + submitters = roles.map do |submitter_name| + template.submitters.find { |e| e['name'] == submitter_name } || + raise(UnknownSubmitterName, + "Unknown submitter role: #{submitter_name}. Template defines #{template.submitters.pluck('name')}") + end + + role_uuids = submitters.pluck('uuid') + + template.fields.select do |e| + role_uuids.include?(e['submitter_uuid']) + end + end + def build_fields_index(fields) - fields.index_by { |e| e['name'] } - .merge(fields.index_by { |e| e['name'].to_s.parameterize.underscore }) - .merge(fields.index_by { |e| e['name'].to_s.downcase }) + fields.group_by { |e| e['name'] } + .merge(fields.group_by { |e| e['name'].to_s.parameterize.underscore }) + .merge(fields.group_by { |e| e['name'].to_s.downcase }) end def normalize_attachment_value(value, field, account, attachments, for_submitter = nil)