From b0786580ad8a6b7832fe34b5ee2e8f13e1f51c58 Mon Sep 17 00:00:00 2001 From: pramodyallapu Date: Thu, 2 Apr 2026 17:29:15 +0530 Subject: [PATCH] feat:CST-986 --- app/controllers/account_configs_controller.rb | 3 +- app/jobs/process_submitter_completion_job.rb | 2 + app/jobs/send_signature_callback_job.rb | 78 +++++++++++++++++++ app/models/account_config.rb | 1 + .../_signature_callback_form.html.erb | 29 +++++++ .../personalization_settings/show.html.erb | 4 + app/views/templates/edit.html.erb | 5 +- 7 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 app/jobs/send_signature_callback_job.rb create mode 100644 app/views/personalization_settings/_signature_callback_form.html.erb diff --git a/app/controllers/account_configs_controller.rb b/app/controllers/account_configs_controller.rb index cf128ef9..e12d5c26 100644 --- a/app/controllers/account_configs_controller.rb +++ b/app/controllers/account_configs_controller.rb @@ -23,7 +23,8 @@ class AccountConfigsController < ApplicationController AccountConfig::WITH_SIGNATURE_ID, AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::REQUIRE_SIGNING_REASON_KEY, - AccountConfig::DOCUMENT_FILENAME_FORMAT_KEY + AccountConfig::DOCUMENT_FILENAME_FORMAT_KEY, + AccountConfig::SIGNATURE_CALLBACK_URL_KEY ].freeze InvalidKey = Class.new(StandardError) diff --git a/app/jobs/process_submitter_completion_job.rb b/app/jobs/process_submitter_completion_job.rb index 0976b3bf..2ada975a 100644 --- a/app/jobs/process_submitter_completion_job.rb +++ b/app/jobs/process_submitter_completion_job.rb @@ -29,6 +29,8 @@ class ProcessSubmitterCompletionJob end enqueue_completed_webhooks(submitter, is_all_completed:) + + SendSignatureCallbackJob.perform_async('submitter_id' => submitter.id) end def create_completed_submitter!(submitter) diff --git a/app/jobs/send_signature_callback_job.rb b/app/jobs/send_signature_callback_job.rb new file mode 100644 index 00000000..886d6db2 --- /dev/null +++ b/app/jobs/send_signature_callback_job.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +class SendSignatureCallbackJob + include Sidekiq::Job + + sidekiq_options queue: :webhooks + + TRACKED_FIELD_NAMES = %w[provider_signature client_caregiver_signature supervisor_signature].freeze + CALLBACK_ENDPOINT = 'https://app.therapypms.com/v1/note/signed'.freeze + + def perform(params = {}) + submitter = Submitter.find_by(id: params['submitter_id']) + + return unless submitter&.completed_at? + + callback_url = AccountConfig.find_by( + account_id: submitter.account_id, + key: AccountConfig::SIGNATURE_CALLBACK_URL_KEY + )&.value.presence || CALLBACK_ENDPOINT + + fields = submitter.submission.template_fields || submitter.submission.template&.fields + + return unless fields + + tracked_fields = fields.select do |f| + f['submitter_uuid'] == submitter.uuid && + f['type'] == 'signature' && + TRACKED_FIELD_NAMES.include?(f['name']) + end + + return if tracked_fields.empty? + + attachments_index = submitter.attachments.preload(:blob).index_by(&:uuid) + + signatures = tracked_fields.filter_map do |field| + attachment_uuid = submitter.values[field['uuid']] + next unless attachment_uuid.present? + + attachment = attachments_index[attachment_uuid] + next unless attachment + + { + field_name: field['name'], + field_uuid: field['uuid'], + signature: Base64.strict_encode64(attachment.download) + } + end + + return if signatures.empty? + + payload = { + admin_id: submitter.submission.created_by_user_id, + template_id: submitter.submission.template_id, + submission_id: submitter.submission_id, + embed_src: build_embed_src(submitter), + email: submitter.email, + signatures: signatures + } + + Faraday.post(callback_url) do |req| + req.headers['Content-Type'] = 'application/json' + req.body = payload.to_json + req.options.read_timeout = 10 + req.options.open_timeout = 10 + end + rescue Faraday::Error => e + Rails.logger.error("SendSignatureCallbackJob error for submitter #{params['submitter_id']}: #{e.message}") + end + + private + + def build_embed_src(submitter) + opts = Docuseal.default_url_options + port = opts[:port] ? ":#{opts[:port]}" : '' + + "#{opts[:protocol]}://#{opts[:host]}#{port}/submissions/#{submitter.submission_id}" + end +end diff --git a/app/models/account_config.rb b/app/models/account_config.rb index 3964e973..4d41eba6 100644 --- a/app/models/account_config.rb +++ b/app/models/account_config.rb @@ -54,6 +54,7 @@ class AccountConfig < ApplicationRecord COMBINE_PDF_RESULT_KEY = 'combine_pdf_result_key' DOCUMENT_FILENAME_FORMAT_KEY = 'document_filename_format' POLICY_LINKS_KEY = 'policy_links' + SIGNATURE_CALLBACK_URL_KEY = 'signature_callback_url' DEFAULT_VALUES = { SUBMITTER_INVITATION_EMAIL_KEY => lambda { diff --git a/app/views/personalization_settings/_signature_callback_form.html.erb b/app/views/personalization_settings/_signature_callback_form.html.erb new file mode 100644 index 00000000..8b4c21aa --- /dev/null +++ b/app/views/personalization_settings/_signature_callback_form.html.erb @@ -0,0 +1,29 @@ +<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::SIGNATURE_CALLBACK_URL_KEY) %> +<% if can?(:manage, account_config) %> +
+ > +
+ Signature Callback URL +
+
+

+ When a Provider, Client/Caregiver, or Supervisor signature is collected, DocuSeal will POST + { template_id, form_id, email, signatures } to this URL. + Leave blank to disable. +

+ <%= form_for account_config, url: account_configs_path, method: :post, + html: { autocomplete: 'off', class: 'space-y-3' } do |f| %> + <%= f.hidden_field :key %> +
+ <%= f.url_field :value, + class: 'base-input w-full font-mono', + placeholder: 'https://your-app.example.com/api/signature-callback', + value: account_config.value.presence %> +
+
+ <%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> +
+ <% end %> +
+
+<% end %> diff --git a/app/views/personalization_settings/show.html.erb b/app/views/personalization_settings/show.html.erb index 438da311..5e46347f 100644 --- a/app/views/personalization_settings/show.html.erb +++ b/app/views/personalization_settings/show.html.erb @@ -22,6 +22,10 @@ <%= render 'form_policy_links_form' %> <%= render 'form_customization_settings' %> +

+ Integrations +

+ <%= render 'signature_callback_form' %>
diff --git a/app/views/templates/edit.html.erb b/app/views/templates/edit.html.erb index a41a0ad6..4f492797 100644 --- a/app/views/templates/edit.html.erb +++ b/app/views/templates/edit.html.erb @@ -29,6 +29,9 @@ { name: 'patient_language', type: 'text', title: 'Patient Language' }, { name: 'patient_parent_name', type: 'text', title: 'Patient Parent Name' }, { name: 'patient_first_seen_date', type: 'date', title: 'Patient First Seen Date' }, - { name: 'payor_name', type: 'text', title: 'Payor Name' } + { name: 'payor_name', type: 'text', title: 'Payor Name' }, + { name: 'provider_signature', type: 'signature', title: 'Provider Signature' }, + { name: 'client_caregiver_signature', type: 'signature', title: 'Client/Caregiver Signature' }, + { name: 'supervisor_signature', type: 'signature', title: "Supervisor's Signature" } ] %> \ No newline at end of file