2023-05-21 17:59:36 +03:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
|
#
|
|
|
|
|
# Table name: submissions
|
|
|
|
|
#
|
2023-07-26 09:58:50 +03:00
|
|
|
# id :bigint not null, primary key
|
2023-12-30 00:37:03 +02:00
|
|
|
# archived_at :datetime
|
2026-07-01 18:10:08 +03:00
|
|
|
# completed_at :datetime
|
2024-08-01 16:43:55 +03:00
|
|
|
# expire_at :datetime
|
2025-05-31 15:15:25 +03:00
|
|
|
# name :text
|
2023-11-23 20:03:52 +02:00
|
|
|
# preferences :text not null
|
2023-11-20 00:52:05 +02:00
|
|
|
# slug :string not null
|
2026-02-12 09:22:08 +02:00
|
|
|
# source :string not null
|
2023-09-10 02:10:01 +03:00
|
|
|
# submitters_order :string not null
|
2023-07-26 09:58:50 +03:00
|
|
|
# template_fields :text
|
|
|
|
|
# template_schema :text
|
|
|
|
|
# template_submitters :text
|
2025-09-12 12:17:26 +03:00
|
|
|
# variables :text
|
|
|
|
|
# variables_schema :text
|
2023-07-26 09:58:50 +03:00
|
|
|
# created_at :datetime not null
|
|
|
|
|
# updated_at :datetime not null
|
2024-02-03 14:39:28 +02:00
|
|
|
# account_id :bigint not null
|
2023-07-26 09:58:50 +03:00
|
|
|
# created_by_user_id :bigint
|
2025-05-31 15:15:25 +03:00
|
|
|
# template_id :bigint
|
2023-05-21 17:59:36 +03:00
|
|
|
#
|
|
|
|
|
# Indexes
|
|
|
|
|
#
|
2026-07-01 21:50:12 +03:00
|
|
|
# index_submissions_on_account_id_and_completed_at (account_id,completed_at) WHERE ((completed_at IS NOT NULL) AND (archived_at IS NULL))
|
2025-06-18 11:42:23 +03:00
|
|
|
# index_submissions_on_account_id_and_id (account_id,id)
|
2026-07-01 21:50:12 +03:00
|
|
|
# index_submissions_on_account_id_and_id_pending (account_id,id) WHERE ((completed_at IS NULL) AND (archived_at IS NULL))
|
2025-06-18 11:42:23 +03:00
|
|
|
# index_submissions_on_account_id_and_template_id_and_id (account_id,template_id,id) WHERE (archived_at IS NULL)
|
|
|
|
|
# index_submissions_on_account_id_and_template_id_and_id_archived (account_id,template_id,id) WHERE (archived_at IS NOT NULL)
|
2026-07-07 09:14:15 +03:00
|
|
|
# index_submissions_on_created_at (created_at)
|
2025-06-18 11:42:23 +03:00
|
|
|
# index_submissions_on_created_by_user_id (created_by_user_id)
|
|
|
|
|
# index_submissions_on_slug (slug) UNIQUE
|
|
|
|
|
# index_submissions_on_template_id (template_id)
|
2023-05-21 17:59:36 +03:00
|
|
|
#
|
|
|
|
|
# Foreign Keys
|
|
|
|
|
#
|
2023-06-29 02:04:17 +03:00
|
|
|
# fk_rails_... (created_by_user_id => users.id)
|
2023-05-31 23:19:20 +03:00
|
|
|
# fk_rails_... (template_id => templates.id)
|
2023-05-21 17:59:36 +03:00
|
|
|
#
|
|
|
|
|
class Submission < ApplicationRecord
|
2025-05-31 15:15:25 +03:00
|
|
|
belongs_to :template, optional: true
|
2024-02-03 14:39:28 +02:00
|
|
|
belongs_to :account
|
2023-06-29 02:04:17 +03:00
|
|
|
belongs_to :created_by_user, class_name: 'User', optional: true
|
2023-05-21 17:59:36 +03:00
|
|
|
|
2025-06-30 11:44:55 +03:00
|
|
|
has_one :search_entry, as: :record, inverse_of: :record, dependent: :destroy if SearchEntry.table_exists?
|
2025-06-05 13:16:55 +03:00
|
|
|
|
2023-06-13 00:29:07 +03:00
|
|
|
has_many :submitters, dependent: :destroy
|
2023-09-10 12:55:41 +03:00
|
|
|
has_many :submission_events, dependent: :destroy
|
2023-05-21 17:59:36 +03:00
|
|
|
|
2023-11-23 20:03:52 +02:00
|
|
|
attribute :preferences, :string, default: -> { {} }
|
|
|
|
|
|
2023-12-29 18:50:14 +00:00
|
|
|
serialize :template_fields, coder: JSON
|
|
|
|
|
serialize :template_schema, coder: JSON
|
|
|
|
|
serialize :template_submitters, coder: JSON
|
2025-09-12 12:17:26 +03:00
|
|
|
serialize :variables_schema, coder: JSON
|
|
|
|
|
serialize :variables, coder: JSON
|
2023-12-29 18:50:14 +00:00
|
|
|
serialize :preferences, coder: JSON
|
2023-07-26 09:58:50 +03:00
|
|
|
|
2023-08-05 19:20:06 +03:00
|
|
|
attribute :source, :string, default: 'link'
|
2023-09-10 02:10:01 +03:00
|
|
|
attribute :submitters_order, :string, default: 'random'
|
2023-08-05 19:20:06 +03:00
|
|
|
|
2023-11-20 00:52:05 +02:00
|
|
|
attribute :slug, :string, default: -> { SecureRandom.base58(14) }
|
|
|
|
|
|
2023-09-10 19:25:57 +03:00
|
|
|
has_one_attached :audit_trail
|
2024-05-25 20:57:36 +03:00
|
|
|
has_one_attached :combined_document
|
2026-01-11 11:01:20 +02:00
|
|
|
has_one_attached :merged_document
|
|
|
|
|
has_one_attached :preview_merged_document
|
2023-09-10 19:25:57 +03:00
|
|
|
|
2024-10-15 19:16:09 +03:00
|
|
|
has_many_attached :preview_documents
|
2025-05-31 15:15:25 +03:00
|
|
|
has_many_attached :documents
|
2024-10-15 19:16:09 +03:00
|
|
|
|
2024-12-08 01:37:20 +02:00
|
|
|
has_many :template_accesses, primary_key: :template_id, foreign_key: :template_id, dependent: nil, inverse_of: false
|
|
|
|
|
|
2023-09-01 01:27:13 +03:00
|
|
|
has_many :template_schema_documents,
|
|
|
|
|
->(e) { where(uuid: (e.template_schema.presence || e.template.schema).pluck('attachment_uuid')) },
|
|
|
|
|
through: :template, source: :documents_attachments
|
|
|
|
|
|
2026-02-12 09:22:08 +02:00
|
|
|
has_many :template_schema_static_documents,
|
|
|
|
|
->(e) { where(uuid: e.template_schema.reject { |s| s['dynamic'] }.pluck('attachment_uuid')) },
|
|
|
|
|
through: :template, source: :documents_attachments
|
|
|
|
|
|
|
|
|
|
has_many :template_schema_dynamic_document_versions,
|
|
|
|
|
->(e) { where(sha1: e.template_schema.select { |s| s['dynamic'] }.pluck('dynamic_document_sha1')) },
|
|
|
|
|
through: :template, source: :dynamic_document_versions
|
|
|
|
|
|
|
|
|
|
has_many :template_schema_dynamic_document_attachments,
|
|
|
|
|
through: :template_schema_dynamic_document_versions, source: :document_attachment
|
|
|
|
|
|
2023-12-30 00:37:03 +02:00
|
|
|
scope :active, -> { where(archived_at: nil) }
|
2025-04-01 10:12:54 +03:00
|
|
|
scope :archived, -> { where.not(archived_at: nil) }
|
2025-05-28 10:23:36 +03:00
|
|
|
scope :pending, lambda {
|
2026-07-01 21:50:12 +03:00
|
|
|
where(expire_at: nil).or(where(expire_at: Time.current..)).where(completed_at: nil)
|
2024-11-23 01:45:10 +02:00
|
|
|
}
|
2026-07-01 21:50:12 +03:00
|
|
|
scope :completed, -> { where.not(completed_at: nil) }
|
2025-05-28 10:23:36 +03:00
|
|
|
scope :declined, lambda {
|
2026-07-03 09:54:48 +03:00
|
|
|
where(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id]))
|
|
|
|
|
.where.not(declined_at: nil).limit(1).arel.exists)
|
2025-05-28 10:23:36 +03:00
|
|
|
}
|
2026-07-01 21:50:12 +03:00
|
|
|
scope :expired, -> { where(expire_at: ..Time.current).where(completed_at: nil) }
|
2023-08-05 19:20:06 +03:00
|
|
|
|
2026-06-28 10:52:54 +03:00
|
|
|
scope :select_for_list, lambda {
|
2026-07-01 21:50:12 +03:00
|
|
|
select(:id, :name, :created_by_user_id, :account_id, :completed_at,
|
2026-06-28 10:52:54 +03:00
|
|
|
:created_at, :archived_at, :expire_at, :template_id, :template_submitters)
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-05 19:20:06 +03:00
|
|
|
enum :source, {
|
|
|
|
|
invite: 'invite',
|
2024-04-12 17:20:00 +03:00
|
|
|
bulk: 'bulk',
|
2023-08-05 19:20:06 +03:00
|
|
|
api: 'api',
|
|
|
|
|
embed: 'embed',
|
2026-05-04 09:36:23 +03:00
|
|
|
mcp: 'mcp',
|
2023-08-05 19:20:06 +03:00
|
|
|
link: 'link'
|
|
|
|
|
}, scope: false, prefix: true
|
2023-09-10 02:10:01 +03:00
|
|
|
|
|
|
|
|
enum :submitters_order, {
|
|
|
|
|
random: 'random',
|
|
|
|
|
preserved: 'preserved'
|
|
|
|
|
}, scope: false, prefix: true
|
2023-11-01 12:17:35 +02:00
|
|
|
|
2024-08-01 16:43:55 +03:00
|
|
|
def expired?
|
|
|
|
|
expire_at && expire_at <= Time.current
|
|
|
|
|
end
|
|
|
|
|
|
2025-05-31 15:15:25 +03:00
|
|
|
def schema_documents
|
2026-02-12 09:22:08 +02:00
|
|
|
return documents_attachments unless template_id?
|
|
|
|
|
|
|
|
|
|
dynamic_count = template_schema&.count { |e| e['dynamic'] }.to_i
|
|
|
|
|
|
2026-03-15 15:02:46 +02:00
|
|
|
if variables_schema.blank?
|
2026-02-12 09:22:08 +02:00
|
|
|
if dynamic_count > 0
|
|
|
|
|
if dynamic_count == template_schema.size
|
|
|
|
|
template_schema_dynamic_document_attachments
|
|
|
|
|
else
|
|
|
|
|
template_schema_dynamic_and_static_document_attachments
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
template_schema_documents
|
|
|
|
|
end
|
|
|
|
|
elsif dynamic_count > 0 && dynamic_count != template_schema.size
|
|
|
|
|
template_schema_submission_dynamic_and_static_document_attachments
|
2025-05-31 15:15:25 +03:00
|
|
|
else
|
|
|
|
|
documents_attachments
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:22:08 +02:00
|
|
|
def template_schema_submission_dynamic_and_static_document_attachments
|
|
|
|
|
@template_schema_submission_dynamic_and_static_document_attachments ||=
|
|
|
|
|
ActiveStorage::Attachment.where(
|
|
|
|
|
ActiveStorage::Attachment.arel_table[:id].in(
|
|
|
|
|
template_schema_static_documents.select(:id).arel.union(
|
|
|
|
|
:all,
|
|
|
|
|
documents_attachments.select(:id).arel
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def template_schema_dynamic_and_static_document_attachments
|
|
|
|
|
@template_schema_dynamic_and_static_document_attachments ||=
|
|
|
|
|
ActiveStorage::Attachment.where(
|
|
|
|
|
ActiveStorage::Attachment.arel_table[:id].in(
|
|
|
|
|
template_schema_static_documents.select(:id).arel.union(
|
|
|
|
|
:all,
|
|
|
|
|
template_schema_dynamic_document_attachments.select(:id).arel
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
2025-04-25 12:14:49 +03:00
|
|
|
def fields_uuid_index
|
|
|
|
|
@fields_uuid_index ||= (template_fields || template.fields).index_by { |f| f['uuid'] }
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-31 16:40:18 +03:00
|
|
|
def audit_trail_url(expires_at: nil)
|
2023-12-03 13:03:36 +02:00
|
|
|
return if audit_trail.blank?
|
|
|
|
|
|
2025-08-31 16:40:18 +03:00
|
|
|
ActiveStorage::Blob.proxy_url(audit_trail.blob, expires_at:)
|
2023-11-01 12:17:35 +02:00
|
|
|
end
|
|
|
|
|
alias audit_log_url audit_trail_url
|
2024-08-23 23:46:22 +03:00
|
|
|
|
2025-08-31 16:40:18 +03:00
|
|
|
def combined_document_url(expires_at: nil)
|
2024-08-23 23:46:22 +03:00
|
|
|
return if combined_document.blank?
|
|
|
|
|
|
2025-08-31 16:40:18 +03:00
|
|
|
ActiveStorage::Blob.proxy_url(combined_document.blob, expires_at:)
|
2024-08-23 23:46:22 +03:00
|
|
|
end
|
2023-05-21 17:59:36 +03:00
|
|
|
end
|