diff --git a/app/controllers/concerns/medical_release_context.rb b/app/controllers/concerns/medical_release_context.rb
new file mode 100644
index 0000000..6b15cd4
--- /dev/null
+++ b/app/controllers/concerns/medical_release_context.rb
@@ -0,0 +1,13 @@
+module MedicalReleaseContext
+ extend ActiveSupport::Concern
+
+ def medical_releases
+ policy_scope(MedicalRelease)
+ end
+
+ def set_medical_release
+ medical_release_id = params[:medical_release_id] || params[:id]
+
+ @medical_release = authorize medical_releases.find(medical_release_id)
+ end
+end
diff --git a/app/controllers/medical_releases_controller.rb b/app/controllers/medical_releases_controller.rb
new file mode 100644
index 0000000..9c48ce0
--- /dev/null
+++ b/app/controllers/medical_releases_controller.rb
@@ -0,0 +1,40 @@
+class MedicalReleasesController < ApplicationController
+ include ProjectContext, MedicalReleaseContext
+
+ before_action :set_project, only: [:index]
+ before_action :set_medical_release, only: [:destroy]
+
+ include ProjectLayout
+
+ def index
+ @medical_releases = filtered_medical_releases.order_by_recent.paginate(page: params[:page])
+ end
+
+ def destroy
+ @project = @medical_release.project
+
+ if @medical_release.destroy
+ redirect_to [@project, :medical_releases], alert: t(".alert")
+ end
+ end
+
+ private
+
+ def medical_releases
+ if @project
+ policy_scope(@project.medical_releases)
+ else
+ policy_scope(MedicalRelease)
+ end
+ end
+
+ def filtered_medical_releases
+ results = medical_releases
+
+ if params[:query].present?
+ results = results.search(params[:query])
+ end
+
+ results
+ end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 2b13b11..ad3fefa 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -60,7 +60,7 @@ class ProjectsController < ApplicationController
end
def features_settings_params
- %i(appearance_release location_release material_release acquired_media_release music_release talent_release video_analysis)
+ %i(appearance_release location_release material_release acquired_media_release music_release talent_release medical_release video_analysis)
end
def project_params_with_current_account
diff --git a/app/controllers/public/location_releases_controller.rb b/app/controllers/public/location_releases_controller.rb
index a88de75..536406d 100644
--- a/app/controllers/public/location_releases_controller.rb
+++ b/app/controllers/public/location_releases_controller.rb
@@ -64,7 +64,8 @@ class Public::LocationReleasesController < Public::BaseController
:person_address_country,
:signature_base64,
:locale, :contract_template, :filming_started_on, :filming_ended_on,
- :filming_hours
+ :filming_hours,
+ photos: []
)
end
diff --git a/app/controllers/public/material_releases_controller.rb b/app/controllers/public/material_releases_controller.rb
index f8e4846..5aef717 100644
--- a/app/controllers/public/material_releases_controller.rb
+++ b/app/controllers/public/material_releases_controller.rb
@@ -45,7 +45,7 @@ class Public::MaterialReleasesController < Public::BaseController
:person_first_name, :person_last_name, :person_title, :person_company, :person_phone, :person_email,
:person_address_street1, :person_address_street2, :person_address_city, :person_address_state, :person_address_zip, :person_address_country,
:signature_base64,
- :locale, :contract_template, :description
+ :locale, :contract_template, :description, photos: []
)
end
diff --git a/app/controllers/public/medical_releases_controller.rb b/app/controllers/public/medical_releases_controller.rb
new file mode 100644
index 0000000..597c522
--- /dev/null
+++ b/app/controllers/public/medical_releases_controller.rb
@@ -0,0 +1,81 @@
+class Public::MedicalReleasesController < Public::BaseController
+ before_action :set_account, :set_project, :set_contract_template
+
+ def new
+ @medical_release = build_medical_release
+ end
+
+ def create
+ @medical_release = build_medical_release(medical_release_params_with_locale_and_contract_template)
+
+ if @medical_release.save(context: :native)
+ if @medical_release.contract_template.present?
+ AttachContractToReleasableJob.perform_later(@medical_release)
+ end
+ log_create_analytics
+ else
+ render :new
+ end
+ end
+
+ private
+
+ def set_project
+ @project = @account.projects.find(params[:project_id])
+ end
+
+ def set_account
+ @account = Account.find_by(slug: params[:account_id])
+ end
+
+ def set_contract_template
+ @contract_template = @project.contract_templates.find(params[:contract_template_id])
+ end
+
+ def medical_releases
+ policy_scope(@project.medical_releases)
+ end
+
+ def build_medical_release(params = {})
+ authorize medical_releases.build(params)
+ end
+
+ def medical_release_params
+ params
+ .require(:medical_release)
+ .permit(
+ person_params,
+ :signature_base64,
+ :locale,
+ :contract_template,
+ photos: [],
+ )
+ end
+
+ def person_params
+ [
+ :person_first_name,
+ :person_last_name,
+ :person_phone,
+ :person_email,
+ :person_address_street1,
+ :person_address_street2,
+ :person_address_city,
+ :person_address_state,
+ :person_address_zip,
+ :person_address_country,
+ ]
+ end
+
+ def medical_release_params_with_locale
+ medical_release_params.merge(locale: I18n.locale)
+ end
+
+ def medical_release_params_with_locale_and_contract_template
+ medical_release_params_with_locale.merge(contract_template: @contract_template)
+ end
+
+ def log_create_analytics
+ TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: MedicalRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
+ end
+end
diff --git a/app/controllers/zoom_notifications_controller.rb b/app/controllers/zoom_notifications_controller.rb
index 9006c04..827af74 100644
--- a/app/controllers/zoom_notifications_controller.rb
+++ b/app/controllers/zoom_notifications_controller.rb
@@ -5,7 +5,7 @@ class ZoomNotificationsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize_zoom
- before_action :set_zoom_meeting, only: :create
+ before_action :set_zoom_meeting, only: [:create], if: :meeting_event?
def create
case notification_event
@@ -16,6 +16,12 @@ class ZoomNotificationsController < ApplicationController
when 'recording.completed'
recording = notification.dig(:payload, :object, :recording_files).first
AttachRecordingToZoomMeetingJob.perform_later(@zoom_meeting, recording, notification['download_token'])
+ when 'user.deleted'
+ zoom_user = ZoomUser.find_by(api_id: notification.dig(:payload, :object, :id))
+ if zoom_user.present?
+ zoom_user.api_id = nil
+ zoom_user.destroy
+ end
else
Rails.logger.info notification_event
Rails.logger.info notification
@@ -42,6 +48,10 @@ class ZoomNotificationsController < ApplicationController
notification.dig(:payload, :object, :host_id)
end
+ def meeting_event?
+ notification_event.split(".").first.to_s.in? %w(meeting recording)
+ end
+
def set_zoom_meeting
@zoom_meeting = ZoomMeeting.find_by!(api_meeting_id: notification_meeting_id)
end
diff --git a/app/helpers/dropzone_helper.rb b/app/helpers/dropzone_helper.rb
index 42dac83..bdd32f4 100644
--- a/app/helpers/dropzone_helper.rb
+++ b/app/helpers/dropzone_helper.rb
@@ -3,8 +3,12 @@ module DropzoneHelper
case releasable.model_name.param_key
when "acquired_media_release"
"To Add Photos & Videos to the release:
Drag & Drop Files
or
Click or Tap here to browse photos and connect to Camera"
+ when "material_release"
+ t 'material_releases.form.photos.dropzone_label'
when "music_release"
"To Add Audio Files to the release:
Drag & Drop Files
or
Click or Tap here to browse files"
+ when "location_release"
+ t 'location_releases.form.photos.dropzone_label'
when "directory"
"To Add Files to the Folder:
Drag & Drop Files
or
Click or Tap here to browse files"
else
diff --git a/app/jobs/attach_recording_to_zoom_meeting_job.rb b/app/jobs/attach_recording_to_zoom_meeting_job.rb
index 2a3017e..57bc321 100644
--- a/app/jobs/attach_recording_to_zoom_meeting_job.rb
+++ b/app/jobs/attach_recording_to_zoom_meeting_job.rb
@@ -1,4 +1,3 @@
-require 'zoom_gateway'
class AttachRecordingToZoomMeetingJob < ApplicationJob
queue_as :default
diff --git a/app/models/account.rb b/app/models/account.rb
index 9590bd6..188dd71 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -55,6 +55,7 @@ class Account < ApplicationRecord
User.joins(:project_memberships).where(project_memberships: { project: projects }),
Broadcast.where(project: projects),
ZoomMeeting.where(project: projects),
+ MedicalRelease.where(project: projects),
self
])).sum(:byte_size).to_f
end
diff --git a/app/models/contract_template.rb b/app/models/contract_template.rb
index 6e91a5e..16f87be 100644
--- a/app/models/contract_template.rb
+++ b/app/models/contract_template.rb
@@ -13,6 +13,7 @@ class ContractTemplate < ApplicationRecord
has_many :acquired_media_releases, dependent: :restrict_with_error
has_many :location_releases, dependent: :restrict_with_error
has_many :material_releases, dependent: :restrict_with_error
+ has_many :medical_releases, dependent: :restrict_with_error
monetize :fee_cents
has_rich_text :body
diff --git a/app/models/medical_release.rb b/app/models/medical_release.rb
new file mode 100644
index 0000000..512c75c
--- /dev/null
+++ b/app/models/medical_release.rb
@@ -0,0 +1,71 @@
+class MedicalRelease < ApplicationRecord
+ include Contractable
+ include Notable
+ include Photoable
+ include Releasable
+ include Searchable
+ include Signable
+ include Syncable
+ include PersonName
+
+ composed_of :person_address,
+ class_name: "Address",
+ mapping: [
+ %w(person_address_street1 street1),
+ %w(person_address_street2 street2),
+ %w(person_address_city city),
+ %w(person_address_state state),
+ %w(person_address_zip zip),
+ %w(person_address_country country)
+ ]
+
+ def self.face_photo_acceptable_content_types
+ ["image/png", "image/jpeg"]
+ end
+
+ # These validations apply to all releases
+ validates :person_first_name, :person_last_name, presence: true
+ validates :person_email, email: true, allow_blank: true
+
+ acts_as_taggable_on :internal_tags, :tags
+
+ # These validations apply to releases created natively by the system (i.e. not imported from elsewhere)
+ with_options on: :native do
+ validates :signature, attached: true
+ end
+
+ # These validations apply to releases imported to the system from an outside source
+ with_options on: :non_native do
+ validates :contract, attached: true
+ end
+
+ searchable_on %i[
+ person_first_name person_last_name person_email person_phone
+ person_address_street1 person_address_street2 person_address_city person_address_state person_address_zip person_address_country
+ ]
+
+ # All releases must respond to the following messages
+ def name
+ person_name
+ end
+
+ def filename_suffix
+ "#{person_last_name} #{person_first_name}"
+ end
+
+ def contact_person
+ @contact_person ||= Contact.new(person_name, person_address, person_email, person_phone)
+ end
+
+ def uses_edl?
+ false
+ end
+
+ def minor?
+ false
+ end
+
+ def contract_file_name
+ "#{project.name.parameterize}_#{contract_template.release_type}_#{(signed_at || created_at).strftime("%Y.%m.%d")}_#{release_number}_#{filename_suffix.parameterize}"
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 0001c1f..e37f081 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3,8 +3,8 @@ class Project < ApplicationRecord
include Filterable
include Syncable
- SIGNABLE_RELEASE_TYPES = %w(talent appearance acquired_media location material)
- AVAILABLE_RELEASE_TYPES = %w(appearance location material acquired_media talent music)
+ SIGNABLE_RELEASE_TYPES = %w(talent appearance acquired_media location material medical)
+ AVAILABLE_RELEASE_TYPES = %w(appearance location material acquired_media talent music medical)
belongs_to :account
has_many :acquired_media_releases, dependent: :destroy
@@ -13,6 +13,7 @@ class Project < ApplicationRecord
has_many :material_releases, dependent: :destroy
has_many :music_releases, dependent: :destroy
has_many :talent_releases, dependent: :destroy
+ has_many :medical_releases, dependent: :destroy
has_many :videos, dependent: :destroy
has_many :imports, dependent: :destroy
has_many :contract_templates, dependent: :destroy
@@ -33,6 +34,7 @@ class Project < ApplicationRecord
material_release: false,
music_release: false,
talent_release: false,
+ medical_release: false,
video_analysis: false,
}
end
@@ -65,6 +67,7 @@ class Project < ApplicationRecord
material_release: true,
music_release: true,
talent_release: true,
+ medical_release: true,
video_analysis: true,
}
when "nat_geo"
@@ -76,6 +79,7 @@ class Project < ApplicationRecord
material_release: true,
music_release: true,
talent_release: true,
+ medical_release: true,
video_analysis: true,
}
else
diff --git a/app/models/releasable_param.rb b/app/models/releasable_param.rb
index a8cff38..a3010be 100644
--- a/app/models/releasable_param.rb
+++ b/app/models/releasable_param.rb
@@ -1,5 +1,5 @@
class ReleasableParam
- TYPES = %w(talent appearance location material acquired_media music)
+ TYPES = %w(talent appearance location material acquired_media music medical)
def initialize(params)
@params = params
diff --git a/app/models/zoom_meeting.rb b/app/models/zoom_meeting.rb
index dadf30f..13e1e45 100644
--- a/app/models/zoom_meeting.rb
+++ b/app/models/zoom_meeting.rb
@@ -1,5 +1,3 @@
-require 'zoom_gateway'
-
class ZoomMeeting < ApplicationRecord
belongs_to :project, optional: true
belongs_to :zoom_user
diff --git a/app/models/zoom_user.rb b/app/models/zoom_user.rb
index e96f55b..95ca0eb 100644
--- a/app/models/zoom_user.rb
+++ b/app/models/zoom_user.rb
@@ -1,4 +1,3 @@
-require 'zoom_gateway'
require 'securerandom'
class ZoomUser < ApplicationRecord
has_many :zoom_meetings, dependent: :nullify
diff --git a/app/policies/medical_release_policy.rb b/app/policies/medical_release_policy.rb
new file mode 100644
index 0000000..126f992
--- /dev/null
+++ b/app/policies/medical_release_policy.rb
@@ -0,0 +1,37 @@
+class MedicalReleasePolicy < ReleasePolicy
+ def create?
+ true
+ end
+
+ def show?
+ true
+ end
+
+ def update?
+ !record.native?
+ end
+
+ def destroy?
+ true
+ end
+
+ def edit_photos?
+ true
+ end
+
+ def index?
+ true
+ end
+
+ def update_photos?
+ edit_photos?
+ end
+
+ def tag_multiple?
+ true
+ end
+
+ def download_multiple?
+ true
+ end
+end
diff --git a/app/views/medical_releases/_medical_release.html.erb b/app/views/medical_releases/_medical_release.html.erb
new file mode 100644
index 0000000..2b813e4
--- /dev/null
+++ b/app/views/medical_releases/_medical_release.html.erb
@@ -0,0 +1,49 @@
+
+ | <%= check_box_tag "medical_release_ids[]", medical_release.id, false %> |
+
+ <% if medical_release.photo.attached? %>
+ <%= image_tag medium_variant(medical_release.photo), class: "img-fluid" %>
+ <% end %>
+ |
+
+ <%= medical_release.name %>
+ |
+
+ <%= contact_info(
+ address: medical_release.person_address,
+ phone: medical_release.person_phone,
+ email: medical_release.person_email
+ ) %>
+ |
+
+ <%= notes_preview medical_release.notes.order_by_recent %>
+ |
+ ">
+ <%= tags_preview medical_release, medical_release.tags %>
+ |
+
+
+ <%= medical_release.signed_on %>
+ |
+
+
+
+
+ <%= button_tag t(".actions.manage"), class: "btn btn-light btn-sm dropdown-toggle border", data: { toggle: "dropdown", boundary: "window" }, aria: { haspopup: true, expanded: false } %>
+
+
+ |
+
diff --git a/app/views/medical_releases/index.html.erb b/app/views/medical_releases/index.html.erb
new file mode 100644
index 0000000..e056d0f
--- /dev/null
+++ b/app/views/medical_releases/index.html.erb
@@ -0,0 +1,48 @@
+
+
+
+ <% if @medical_releases.any? && policy(MedicalRelease).tag_multiple? %>
+ <%= button_to_bulk_tagging(@project) %>
+ <% end %>
+
+ <% if @medical_releases.any? && policy(MedicalRelease).download_multiple? %>
+ <%= link_to "Download All", [@project, :contract_downloads, release_type: @medical_releases.name], method: :post, remote: true, class: "btn btn-light border ml-auto mr-2 mb-2", data: {
+ disable_with: "Please wait..." } %>
+ <% end %>
+
+ <%= bootstrap_form_with url: [@project, :medical_releases], method: :get, remote: true, layout: :inline, id: "search" do |form| %>
+ <%= form.search_field :query, hide_label: true, placeholder: t(".actions.search"), class: "rounded-pill-right", value: params[:query], prepend: form.button(fa_icon("search"), class: "btn btn-light border mb-2 rounded-pill-left") %>
+ <% end %>
+
+
+
+
+
+
+
+
+ | <%= check_box_tag "medical_release_ids[]", false, false %> |
+ |
+ <%= MedicalRelease.human_attribute_name(:person_name) %> |
+ <%= MedicalRelease.human_attribute_name(:contact_info) %> |
+ <%= t(".table_headers.notes") %> |
+ <%= t(".table_headers.tags") %> |
+ <%= t(".table_headers.signed_at") %> |
+ |
+
+
+
+ <% if @medical_releases.any? %>
+ <%= render @medical_releases %>
+ <% else %>
+
+ | <%= t(".empty") %> |
+
+ <% end %>
+
+
+
+
+
diff --git a/app/views/medical_releases/index.js.erb b/app/views/medical_releases/index.js.erb
new file mode 100644
index 0000000..e720a01
--- /dev/null
+++ b/app/views/medical_releases/index.js.erb
@@ -0,0 +1,3 @@
+$("#medical_releases").html("<%= j render(@medical_releases) %>");
+$("form input[type='search']").val("<%= params[:query] %>");
+$("#medical_releases_pagination").html("<%= j will_paginate(@medical_releases) %>");
diff --git a/app/views/public/location_releases/new.html.erb b/app/views/public/location_releases/new.html.erb
index 0975f73..b93ffa0 100644
--- a/app/views/public/location_releases/new.html.erb
+++ b/app/views/public/location_releases/new.html.erb
@@ -45,6 +45,10 @@
<% end %>
+ <%= card_field_set_tag t(".photos.heading") do %>
+ <%= render "shared/photos_dropzone_fields", form: form, release: @location_release %>
+ <% end %>
+
<%= card_field_set_tag t(".signature.heading") do %>
<%= render "shared/signature_fields", form: form, instruction: 'An Authorized Signatory' %>
<% end %>
diff --git a/app/views/public/material_releases/new.html.erb b/app/views/public/material_releases/new.html.erb
index 6fa5c8f..bfd565f 100644
--- a/app/views/public/material_releases/new.html.erb
+++ b/app/views/public/material_releases/new.html.erb
@@ -34,6 +34,10 @@
<%= render "shared/address_fields", form: form, subject: "person" %>
<% end %>
+ <%= card_field_set_tag t(".photo.heading") do %>
+ <%= render "shared/photos_dropzone_fields", form: form, release: @material_release %>
+ <% end %>
+
<%= card_field_set_tag t(".signature.heading") do %>
diff --git a/app/views/public/medical_releases/create.html.erb b/app/views/public/medical_releases/create.html.erb
new file mode 100644
index 0000000..e9384ee
--- /dev/null
+++ b/app/views/public/medical_releases/create.html.erb
@@ -0,0 +1 @@
+Your release was successfully submitted. Thank you.
diff --git a/app/views/public/medical_releases/new.html.erb b/app/views/public/medical_releases/new.html.erb
new file mode 100644
index 0000000..93199d6
--- /dev/null
+++ b/app/views/public/medical_releases/new.html.erb
@@ -0,0 +1,43 @@
+
+
+ <%= errors_summary_for @medical_release %>
+ <%= bootstrap_form_with model: [@account, @project, @contract_template, @medical_release], local: true, validation_context: :native do |form| %>
+
<%= t ".instructions_html", name: @project.name %>
+ <%= card_field_set_tag t(".legal.heading") do %>
+
<%= @contract_template.body %>
+ <% if @contract_template.fee? %>
+
+ Fee <%= number_to_currency @contract_template.fee %>
+
+ <% end %>
+ <% end %>
+
+
+
+ <%= card_field_set_tag t(".personal_info.heading") do %>
+
<%= t ".personal_info.instructions" %>
+
+ <%= form.text_field :person_first_name, required: true, wrapper_class: "col-sm-6" %>
+ <%= form.text_field :person_last_name, required: true, wrapper_class: "col-sm-6" %>
+ <%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
+ <%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
+
+ <%= render "shared/address_fields", form: form, subject: "person" %>
+ <% end %>
+
+
+
+ <%= card_field_set_tag t(".photo.heading") do %>
+ <%= render "shared/photos_dropzone_fields", form: form, release: @medical_release %>
+ <% end %>
+
+ <%= card_field_set_tag t(".signature.heading") do %>
+ <%= render "shared/signature_fields", form: form %>
+ <% end %>
+
+
+ <%= form.button t("shared.submit_release_long"), class: "btn btn-block btn-lg btn-success", data: { disable_with: t("shared.disable_with") } %>
+
+ <% end %>
+
+
\ No newline at end of file
diff --git a/app/views/shared/_address_fields.html.erb b/app/views/shared/_address_fields.html.erb
index 56c08ee..6346517 100644
--- a/app/views/shared/_address_fields.html.erb
+++ b/app/views/shared/_address_fields.html.erb
@@ -11,6 +11,6 @@
<%= form.form_group "#{field_name_prefix}address_country" do %>
<%= form.label "#{field_name_prefix}address_country" %>
- <%= form.country_select "#{field_name_prefix}address_country", { priority: %w(US CA), prompt: true }, class: "form-control custom-select" %>
+ <%= form.country_select "#{field_name_prefix}address_country", { selected: 'US', priority: %w(US CA), prompt: true }, class: "form-control custom-select" %>
<% end %>
diff --git a/config/initializers/zoom.rb b/config/initializers/zoom.rb
index a07b9a1..b51101e 100644
--- a/config/initializers/zoom.rb
+++ b/config/initializers/zoom.rb
@@ -1,4 +1,6 @@
require 'zoom'
+require 'zoom_gateway'
+
unless Rails.env.test?
Zoom.configure do |c|
c.api_key = ENV['ZOOM_API_KEY']
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7d24079..19afb42 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -555,6 +555,7 @@ en:
location_details:
heading: 1 of 4 Location Details
photos:
+ dropzone_label: Tap to take a photo of the Property (optional)
heading: 4 of 4 Photos
signer_details:
heading: 2 of 4 Owner Details
@@ -589,6 +590,7 @@ en:
material_details:
heading: 1 of 3 Material Details
photos:
+ dropzone_label: Tap to take a photo of Licensed Material (optional)
heading: 4 of 4 Photos
signer_details:
heading: 2 of 4 Licensor/Owner Details
@@ -609,6 +611,20 @@ en:
heading: Import Material Release (Products / Logos)
update:
notice: The material release has been updated
+ medical_releases:
+ destroy:
+ alert: The medical release has been deleted
+ index:
+ actions:
+ search: Search
+ empty: Medical releases will appear here
+ table_headers:
+ notes: Notes
+ signed_at: Date Signed
+ tags: Tags
+ medical_release:
+ actions:
+ manage: Manage
music_releases:
create:
notice: The music release has been created
@@ -714,6 +730,7 @@ en:
label: Which release categories do you require for this project?
location_release: Location Releases
material_release: Material Releases (Products / Logos)
+ medical_release: Medical Releases
music_release: Music Releases (Original Music)
talent_release: Talent Releases
index:
@@ -740,6 +757,7 @@ en:
downloads: Downloads
location_release: Location Releases (%{count})
material_release: Material Releases (%{count})
+ medical_release: Medical Releases (%{count})
music_release: Music Releases (%{count})
report: Reports
talent_release: Talent Releases (%{count})
@@ -800,6 +818,8 @@ en:
heading: Legal
location_info:
heading: Location Information
+ photos:
+ heading: Photos
signature:
heading: Sign Below
material_releases:
@@ -811,10 +831,28 @@ en:
heading: Licensor/Owner Contact Information
legal:
heading: Legal
+ photo:
+ heading: Photos
release_info:
heading: Release Information
signature:
heading: Sign Below
+ medical_releases:
+ create:
+ notice: Your release has been signed. Thank you!
+ new:
+ cancel: Cancel
+ instructions_html: >
+ Below is the medical release form. After scrolling down and reading the medical release form, please enter your personal information, take a photo, and press the "Submit Release" button.
+ legal:
+ heading: Legal
+ personal_info:
+ heading: Personal Information
+ instructions: Now, enter your personal information.
+ photo:
+ heading: Photos
+ signature:
+ heading: Signature
talent_releases:
create:
notice: Your release has been signed. Thank you!
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 13abd12..370829e 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -132,6 +132,14 @@ es:
update: Save Changes (ES)
create: 'Crear %{model}'
update: 'Actualizar %{model}'
+ location_releases:
+ form:
+ photos:
+ dropzone_label: Tap to take a photo of the Property (optional) (ES)
+ material_releases:
+ form:
+ photos:
+ dropzone_label: Tap to take a photo of Licensed Material (optional) (ES)
public:
appearance_releases:
create:
@@ -160,6 +168,14 @@ es:
clear: Despejar
heading: Firma
instructions: 'Firma Abajo:'
+ location_releases:
+ new:
+ photos:
+ heading: Photos (ES)
+ material_releases:
+ new:
+ photo:
+ heading: Photos (ES)
talent_releases:
new:
guardian_clause:
diff --git a/config/routes.rb b/config/routes.rb
index a844490..742dc35 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -53,6 +53,7 @@ Rails.application.routes.draw do
resources :material_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
resources :music_releases, except: [:show], concerns: [:contractable, :notable]
resources :talent_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
+ resources :medical_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
resources :contract_templates, only: [:index, :new, :create, :destroy] do
resource :qr_codes, only: [:show], controller: "contract_templates/qr_codes"
resource :blank_contracts, only: [:show, :new, :create], controller: "contract_templates/blank_contracts"
@@ -117,6 +118,7 @@ Rails.application.routes.draw do
resources :acquired_media_releases, only: [:new, :create]
resources :location_releases, only: [:new, :create]
resources :material_releases, only: [:new, :create]
+ resources :medical_releases, only: [:new, :create]
end
end
end
@@ -126,7 +128,7 @@ Rails.application.routes.draw do
end
RELEASES = [:acquired_media_releases, :appearance_releases, :talent_releases, :material_releases, :location_releases]
- ALL_RELEASES = RELEASES + [:music_releases]
+ ALL_RELEASES = RELEASES + [:music_releases, :medical_releases]
ALL_RELEASES.each do |release|
resources release, only: [], concerns: :taggable
diff --git a/db/migrate/20200606044747_create_medical_releases.rb b/db/migrate/20200606044747_create_medical_releases.rb
new file mode 100644
index 0000000..950292d
--- /dev/null
+++ b/db/migrate/20200606044747_create_medical_releases.rb
@@ -0,0 +1,23 @@
+class CreateMedicalReleases < ActiveRecord::Migration[6.0]
+ def change
+ create_table :medical_releases do |t|
+ t.belongs_to :project, foreign_key: true
+ t.belongs_to :contract_template, foreign_key: true
+ t.string :person_first_name
+ t.string :person_last_name
+ t.string :person_address_street1
+ t.string :person_address_street2
+ t.string :person_address_city
+ t.string :person_address_state
+ t.string :person_address_zip
+ t.string :person_address_country
+ t.string :person_phone
+ t.string :person_email
+ t.string :locale
+ t.text :notes
+ t.datetime :signed_at
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index d4b1f92..2888215 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -9,6 +9,20 @@ SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
--
-- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: -
--
@@ -50,7 +64,7 @@ $_$;
SET default_tablespace = '';
-SET default_table_access_method = heap;
+SET default_with_oids = false;
--
-- Name: account_auths; Type: TABLE; Schema: public; Owner: -
@@ -615,15 +629,6 @@ CREATE SEQUENCE public.contract_templates_id_seq
ALTER SEQUENCE public.contract_templates_id_seq OWNED BY public.contract_templates.id;
---
--- Name: data_migrations; Type: TABLE; Schema: public; Owner: -
---
-
-CREATE TABLE public.data_migrations (
- version character varying NOT NULL
-);
-
-
--
-- Name: directories; Type: TABLE; Schema: public; Owner: -
--
@@ -846,7 +851,8 @@ CREATE TABLE public.location_releases (
filming_started_on date,
filming_ended_on date,
person_first_name character varying,
- person_last_name character varying
+ person_last_name character varying,
+ filming_hours text
);
@@ -927,6 +933,51 @@ CREATE SEQUENCE public.material_releases_id_seq
ALTER SEQUENCE public.material_releases_id_seq OWNED BY public.material_releases.id;
+--
+-- Name: medical_releases; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE public.medical_releases (
+ id bigint NOT NULL,
+ project_id bigint,
+ contract_template_id bigint,
+ person_first_name character varying,
+ person_last_name character varying,
+ person_address_street1 character varying,
+ person_address_street2 character varying,
+ person_address_city character varying,
+ person_address_state character varying,
+ person_address_zip character varying,
+ person_address_country character varying,
+ person_phone character varying,
+ person_email character varying,
+ locale character varying,
+ notes text,
+ signed_at timestamp without time zone,
+ created_at timestamp(6) without time zone NOT NULL,
+ updated_at timestamp(6) without time zone NOT NULL
+);
+
+
+--
+-- Name: medical_releases_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.medical_releases_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: medical_releases_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.medical_releases_id_seq OWNED BY public.medical_releases.id;
+
+
--
-- Name: music_releases; Type: TABLE; Schema: public; Owner: -
--
@@ -1181,6 +1232,7 @@ CREATE TABLE public.settings (
--
CREATE SEQUENCE public.settings_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1216,6 +1268,7 @@ CREATE TABLE public.taggings (
--
CREATE SEQUENCE public.taggings_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1246,6 +1299,7 @@ CREATE TABLE public.tags (
--
CREATE SEQUENCE public.tags_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1764,6 +1818,13 @@ ALTER TABLE ONLY public.location_releases ALTER COLUMN id SET DEFAULT nextval('p
ALTER TABLE ONLY public.material_releases ALTER COLUMN id SET DEFAULT nextval('public.material_releases_id_seq'::regclass);
+--
+-- Name: medical_releases id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.medical_releases ALTER COLUMN id SET DEFAULT nextval('public.medical_releases_id_seq'::regclass);
+
+
--
-- Name: music_releases id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -2010,14 +2071,6 @@ ALTER TABLE ONLY public.contract_templates
ADD CONSTRAINT contract_templates_pkey PRIMARY KEY (id);
---
--- Name: data_migrations data_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
---
-
-ALTER TABLE ONLY public.data_migrations
- ADD CONSTRAINT data_migrations_pkey PRIMARY KEY (version);
-
-
--
-- Name: directories directories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2074,6 +2127,14 @@ ALTER TABLE ONLY public.material_releases
ADD CONSTRAINT material_releases_pkey PRIMARY KEY (id);
+--
+-- Name: medical_releases medical_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.medical_releases
+ ADD CONSTRAINT medical_releases_pkey PRIMARY KEY (id);
+
+
--
-- Name: music_releases music_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2569,6 +2630,20 @@ CREATE INDEX index_material_releases_on_term_id ON public.material_releases USIN
CREATE INDEX index_material_releases_on_territory_id ON public.material_releases USING btree (territory_id);
+--
+-- Name: index_medical_releases_on_contract_template_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_medical_releases_on_contract_template_id ON public.medical_releases USING btree (contract_template_id);
+
+
+--
+-- Name: index_medical_releases_on_project_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_medical_releases_on_project_id ON public.medical_releases USING btree (project_id);
+
+
--
-- Name: index_music_releases_on_applicable_medium_id; Type: INDEX; Schema: public; Owner: -
--
@@ -3005,6 +3080,14 @@ ALTER TABLE ONLY public.video_release_confirmations
ADD CONSTRAINT fk_rails_2787252ceb FOREIGN KEY (file_info_id) REFERENCES public.file_infos(id);
+--
+-- Name: medical_releases fk_rails_325442c794; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.medical_releases
+ ADD CONSTRAINT fk_rails_325442c794 FOREIGN KEY (contract_template_id) REFERENCES public.contract_templates(id);
+
+
--
-- Name: music_releases fk_rails_3a2b4033ad; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3189,6 +3272,14 @@ ALTER TABLE ONLY public.zoom_meetings
ADD CONSTRAINT fk_rails_8d814ea729 FOREIGN KEY (broadcast_id) REFERENCES public.broadcasts(id);
+--
+-- Name: medical_releases fk_rails_98aa92daa9; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.medical_releases
+ ADD CONSTRAINT fk_rails_98aa92daa9 FOREIGN KEY (project_id) REFERENCES public.projects(id);
+
+
--
-- Name: contract_templates fk_rails_9b4d9d0e5a; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3502,6 +3593,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200428091105'),
('20200507110804'),
('20200512161738'),
-('20200526113516');
+('20200526113516'),
+('20200603090419'),
+('20200606044747');
diff --git a/lib/tasks/zoom.rake b/lib/tasks/zoom.rake
index 3ac0868..06e2902 100644
--- a/lib/tasks/zoom.rake
+++ b/lib/tasks/zoom.rake
@@ -1,4 +1,3 @@
-require 'zoom_gateway'
namespace :zoom do
desc "Setup necessary zoom roles and users"
task :setup => :environment do
@@ -17,4 +16,30 @@ namespace :zoom do
Rails.logger.info "Created role #{ZoomGateway.HOST_ROLE}."
end
end
+
+
+ desc "Synchronize ActiveRecord users with current account state"
+ task :sync => :environment do
+ zoom = Zoom.new
+
+ roles = zoom.roles_list["roles"]
+ ActiveRecord::Base.transaction do
+ ZoomUser.tiers.keys.each do |tier|
+ full_role_name = ZoomGateway.host_role_name(tier)
+ role_id = roles.select { |r| r["name"] == full_role_name }.first["id"]
+ user_ids = zoom.roles_members(role_id: role_id).dig("members").pluck("id")
+
+ # Invalid db users (not existing on the given Zoom account, but existing in the app db)
+ ZoomUser.current_account.public_send(tier).where.not(api_id: user_ids).each do |zu|
+ zu.api_id = nil
+ zu.destroy
+ end
+
+ # Missing zoom users (existing in given Zoom account, but not existing in the app db)
+ (user_ids - ZoomUser.current_account.public_send(tier).pluck(:api_id)).each do |api_user_id|
+ ZoomUser.current_account.public_send(tier).create(api_id: api_user_id)
+ end
+ end
+ end
+ end
end
\ No newline at end of file
diff --git a/lib/zoom_gateway.rb b/lib/zoom_gateway.rb
index f3c4e57..764c2f9 100644
--- a/lib/zoom_gateway.rb
+++ b/lib/zoom_gateway.rb
@@ -21,7 +21,7 @@ class ZoomGateway
end
def HOST_ROLE
- "#{self.USER_TYPE_NAME}-directme-host"
+ self.host_role_name(self.USER_TYPE_NAME)
end
def ACCOUNT_NUMBER
@@ -35,6 +35,10 @@ class ZoomGateway
def apply_limits?
self.USER_TYPE_NAME == 'pro'
end
+
+ def host_role_name(user_type_name)
+ "#{user_type_name}-directme-host"
+ end
end
def initialize
diff --git a/spec/controllers/medical_releases_controller_spec.rb b/spec/controllers/medical_releases_controller_spec.rb
new file mode 100644
index 0000000..e7dfa51
--- /dev/null
+++ b/spec/controllers/medical_releases_controller_spec.rb
@@ -0,0 +1,91 @@
+require "rails_helper"
+
+RSpec.describe MedicalReleasesController, type: :controller do
+ render_views
+
+ let(:user) { create(:user) }
+ let(:account) { user.primary_account }
+ let(:project) { create(:project, account: user.primary_account) }
+
+ before do
+ sign_in user
+ end
+
+ describe "#index" do
+ it "responds successfully" do
+ get :index, params: { project_id: project }
+
+ expect(response).to be_successful
+ end
+
+ it "renders content" do
+ release = create(:medical_release, project: project,
+ person_first_name: "My",
+ person_last_name: "Release",
+ person_phone: "5551234567",
+ person_email: "jane.doe@test.com")
+ create(:note, notable: release, content: "Some notes here")
+
+ get :index, params: { project_id: project }
+
+ expect(response.body).to have_content "My Release"
+ expect(response.body).to have_content "Some notes here"
+ expect(response.body).to have_content "Manage"
+ end
+
+ context "when there are no medical releases" do
+ it "renders an empty message" do
+ get :index, params: { project_id: project }
+
+ expect(response.body).to have_content("Medical releases will appear here")
+ end
+ end
+
+ context "when there are many records" do
+ it "paginates the table" do
+ create_list(:medical_release, 20, project: project)
+
+ get :index, params: { project_id: project }
+
+ expect(response.body).to have_link("2", href: project_medical_releases_path(project, page: 2))
+ end
+ end
+
+ context "for xhr request" do
+ it "filters the releases by a query param" do
+ medical_releases = [
+ create(:medical_release, person_name: "Adam Sandler", project: project),
+ create(:medical_release, person_name: "Zoe Perry", project: project),
+ ]
+
+ get :index, params: { project_id: project, query: "Zoe" }, xhr: true
+
+ expect(response.body).not_to have_content("Adam Sandler")
+ expect(response.body).to have_content("Zoe Perry")
+ end
+ end
+ end
+
+ describe "#destroy" do
+ let!(:medical_release) { create(:medical_release, project: project) }
+
+ it "responds with redirect" do
+ delete :destroy, params: { project_id: project, id: medical_release }
+
+ expect(response).to be_redirect
+ expect(response).to redirect_to [project, :medical_releases]
+ end
+
+ it "sets the flash" do
+ delete :destroy, params: { project_id: project, id: medical_release }
+
+ expect(flash.alert).not_to be_nil
+ end
+
+ it "destroys the record" do
+ expect {
+ delete :destroy, params: { project_id: project, id: medical_release }
+ }.to change(MedicalRelease, :count).by(-1)
+ end
+ end
+end
diff --git a/spec/controllers/public/location_releases_controller_spec.rb b/spec/controllers/public/location_releases_controller_spec.rb
index 6a6a261..af10c12 100644
--- a/spec/controllers/public/location_releases_controller_spec.rb
+++ b/spec/controllers/public/location_releases_controller_spec.rb
@@ -7,6 +7,15 @@ describe Public::LocationReleasesController do
render_views
describe "#create" do
+ it "allows photos param" do
+ contract_template = create(:contract_template, project: project)
+
+ post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, location_release: location_release_params_with_photos }
+
+ expect(response).to be_successful
+ expect(LocationRelease.last.photos.attached?).to eq true
+ end
+
it "logs analytics" do
contract_template = create(:contract_template, project: project)
@@ -67,6 +76,10 @@ describe Public::LocationReleasesController do
attributes_for(:location_release, :native).except(:signature).merge(signature_param).merge(person_address_params)
end
+ def location_release_params_with_photos
+ attributes_for(:location_release, :native, :with_photo).except(:signature).merge(signature_param)
+ end
+
def person_address_params
{
person_address_street1: "123 Broadway",
diff --git a/spec/controllers/public/material_releases_controller_spec.rb b/spec/controllers/public/material_releases_controller_spec.rb
index 1ada22f..6ab78be 100644
--- a/spec/controllers/public/material_releases_controller_spec.rb
+++ b/spec/controllers/public/material_releases_controller_spec.rb
@@ -7,6 +7,15 @@ describe Public::MaterialReleasesController do
render_views
describe "#create" do
+ it "allows photos param" do
+ contract_template = create(:contract_template, project: project)
+
+ post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, material_release: material_release_params_with_photos }
+
+ expect(response).to be_successful
+ expect(MaterialRelease.last.photos.attached?).to eq true
+ end
+
it "logs analytics" do
contract_template = create(:contract_template, project: project)
@@ -65,6 +74,11 @@ describe Public::MaterialReleasesController do
attributes_for(:material_release, :native).except(:signature).merge(signature_param)
end
+ def material_release_params_with_photos
+ attributes_for(:material_release, :native, :with_photo).except(:signature).merge(signature_param)
+ end
+
+
def signature_param
file = file_fixture("signature.png")
data_uri = Base64Image.from_image(file).data_uri
diff --git a/spec/controllers/public/medical_releases_controller_spec.rb b/spec/controllers/public/medical_releases_controller_spec.rb
new file mode 100644
index 0000000..794bdb2
--- /dev/null
+++ b/spec/controllers/public/medical_releases_controller_spec.rb
@@ -0,0 +1,71 @@
+require "rails_helper"
+
+RSpec.describe Public::MedicalReleasesController, type: :controller do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, account: user.primary_account) }
+
+ render_views
+
+ describe "#create" do
+ it "logs analytics" do
+ contract_template = create(:contract_template, project: project)
+
+ expect {
+ post :create, params: { account_id: project.account.to_param, project_id: project, contract_template_id: contract_template, medical_release: medical_release_params }
+ }.to(
+ have_enqueued_job(TrackAnalyticsJob)
+ .with(nil, nil, :track_create_native_release, release_type: "MedicalRelease", account: project.account, user_agent: "Rails Testing", user_ip: "0.0.0.0")
+ )
+ end
+
+ it "displays validation errors" do
+ contract_template = create(:contract_template, project: project)
+ sign_in(user)
+
+ post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, medical_release: { person_address_city: "Albuquerque" } }
+ body = CGI.unescape_html(response.body)
+ expect(body).to match /Person first name can't be blank/
+ expect(body).to match /Person last name can't be blank/
+ expect(body).to match />can't be blank
+ end
+
+ it "responds with success " do
+ contract_template = create(:contract_template, project: project)
+
+ post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, medical_release: medical_release_params }
+
+ expect(response).to be_successful
+ end
+
+ it "runs attach contract to releasable job" do
+ contract_template = create(:contract_template, project: project)
+
+ expect {
+ post :create, params: { account_id: project.account.to_param, project_id: project, contract_template_id: contract_template, medical_release: medical_release_params }
+ }.to(
+ have_enqueued_job(AttachContractToReleasableJob)
+ .with(MedicalRelease.last)
+ )
+ end
+ end
+
+ private
+
+ def medical_release_params
+ attributes_for(:medical_release, :native).except(:signature).merge(signature_param, photos_param)
+ end
+
+ def photos_param
+ path = Rails.root.join("spec", "fixtures", "files", "person_photo.png")
+ photo = Rack::Test::UploadedFile.new(path, "image/png")
+
+ { photos: [photo] }
+ end
+
+ def signature_param
+ file = file_fixture("signature.png")
+ data_uri = Base64Image.from_image(file).data_uri
+
+ { signature_base64: data_uri }
+ end
+end
diff --git a/spec/controllers/public/zoom_meetings_controller_spec.rb b/spec/controllers/public/zoom_meetings_controller_spec.rb
index d19c387..61f5a1c 100644
--- a/spec/controllers/public/zoom_meetings_controller_spec.rb
+++ b/spec/controllers/public/zoom_meetings_controller_spec.rb
@@ -1,5 +1,4 @@
require 'rails_helper'
-require 'zoom_gateway'
RSpec.describe Public::ZoomMeetingsController, type: :controller do
let(:user) { create(:user) }
diff --git a/spec/controllers/zoom_meetings_controller_spec.rb b/spec/controllers/zoom_meetings_controller_spec.rb
index 2048f4d..ca1ab04 100644
--- a/spec/controllers/zoom_meetings_controller_spec.rb
+++ b/spec/controllers/zoom_meetings_controller_spec.rb
@@ -1,5 +1,4 @@
require 'rails_helper'
-require 'zoom_gateway'
RSpec.describe ZoomMeetingsController, type: :controller do
let(:user) { create(:user) }
diff --git a/spec/controllers/zoom_notifications_controller_spec..rb b/spec/controllers/zoom_notifications_controller_spec..rb
deleted file mode 100644
index 2686957..0000000
--- a/spec/controllers/zoom_notifications_controller_spec..rb
+++ /dev/null
@@ -1,82 +0,0 @@
-require "rails_helper"
-
-RSpec.describe ZoomNotificationsController, type: :controller do
- render_views
-
- let!(:zoom_meeting) { create(:zoom_meeting, api_meeting_id: 'meeting_id') }
-
- let(:started_status) { {event: 'meeting.started', payload: {object: {id: 'meeting_id' }}} }
- let(:ended_status) { {event: 'meeting.ended', payload: {object: {id: 'meeting_id' }}} }
- let(:wrong_meeting_id) { {payload: {object: {id: 'wrong_id' }}} }
-
- let(:authorization_header) { {'Authorization' => 'xxx-xxx-xxx'} }
- let(:wrong_authorization_header) { {'Authorization' => 'yyy-yyy-yyy'} }
-
- before do
- allow(ENV).to receive(:[]).with('ZOOM_VERIFICATION_TOKEN').and_return('xxx-xxx-xxx')
- end
-
- describe '#create' do
- context 'with no authorization key' do
- it 'raises 403 response' do
- post :create, params: started_status
- expect(response).to have_http_status(403)
- end
- end
-
- context 'with wrong authorization key' do
- it 'raises 403 response' do
- request.headers.merge!(wrong_authorization_header)
- post :create, params: started_status
- expect(response).to have_http_status(403)
- end
- end
-
- context 'authorized' do
- before do
- request.headers.merge!(authorization_header)
- end
-
- context 'with wrong meeting id' do
- it 'raises RecordNotFound' do
- expect {
- post :create, params: wrong_meeting_id
- }.to raise_error(ActiveRecord::RecordNotFound)
- end
- end
-
- context 'with right meeting id' do
- it 'responds with 200' do
- post :create, params: started_status
-
- expect(response).to have_http_status(200)
- end
-
- it 'assigns the zoom meeting' do
- post :create, params: started_status
-
- expect(assigns(:zoom_meeting)).to eq(zoom_meeting)
- end
-
- it 'updates the zoom_meeting when started status is received in notification' do
- post :create, params: started_status
-
- expect(zoom_meeting.reload).to be_started
- end
-
- it 'updates the zoom_meeting when ended status is received in notification' do
- post :create, params: ended_status
-
- expect(zoom_meeting.reload).to be_ended
- end
-
- it 'updates the recording when recording complete notification is received' do
- expect {
- post :create, params: recording_complete
- }.to change { zoom_meeting.recording }
- end
-
- end
- end
- end
-end
diff --git a/spec/controllers/zoom_notifications_controller_spec.rb b/spec/controllers/zoom_notifications_controller_spec.rb
new file mode 100644
index 0000000..01eb0cb
--- /dev/null
+++ b/spec/controllers/zoom_notifications_controller_spec.rb
@@ -0,0 +1,101 @@
+require "rails_helper"
+
+RSpec.describe ZoomNotificationsController, type: :controller do
+ render_views
+
+ let!(:zoom_meeting) { create(:zoom_meeting, api_meeting_id: 'meeting_id') }
+
+ let(:started_status) { {event: 'meeting.started', payload: {object: {id: 'meeting_id' }}} }
+ let(:ended_status) { {event: 'meeting.ended', payload: {object: {id: 'meeting_id' }}} }
+ let(:wrong_meeting_id) { {event: 'meeting.started', payload: {object: {id: 'wrong_id' }}} }
+
+ let(:recording_complete) { {event: 'recording.completed', payload: {object: {id: 'meeting_id', recording_files: [Object.new]}}} }
+
+ let(:authorization_header) { {'Authorization' => 'xxx-xxx-xxx'} }
+ let(:wrong_authorization_header) { {'Authorization' => 'yyy-yyy-yyy'} }
+
+ before do
+ allow(ENV).to receive(:[]).with('ZOOM_VERIFICATION_TOKEN').and_return('xxx-xxx-xxx')
+ end
+
+ describe '#create' do
+ context 'with no authorization key' do
+ it 'raises 403 response' do
+ post :create, params: started_status
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'with wrong authorization key' do
+ it 'raises 403 response' do
+ request.headers.merge!(wrong_authorization_header)
+ post :create, params: started_status
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'authorized' do
+ before do
+ request.headers.merge!(authorization_header)
+ end
+
+ context 'user hooks' do
+ before(:each) { ZoomUser.create api_id: 'zoom_user_id' }
+
+ it 'deletes the user from db if user.deleted is passed with existing user id' do
+ expect {
+ post :create, params: {event: 'user.deleted', payload: {object: {id: 'zoom_user_id'}}}
+ }.to change { ZoomUser.count }.by(-1)
+ end
+
+ it 'does not do anything if user.deleted is passed with non-existing user' do
+ expect {
+ post :create, params: {event: 'user.deleted', payload: {object: {id: 'wrong-user-id'}}}
+ }.not_to change { ZoomUser.count }
+ end
+ end
+
+ context 'meeting hooks' do
+ context 'with wrong meeting id' do
+ it 'raises RecordNotFound' do
+ expect {
+ post :create, params: wrong_meeting_id
+ }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'with right meeting id' do
+ it 'responds with 200' do
+ post :create, params: started_status
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'assigns the zoom meeting' do
+ post :create, params: started_status
+
+ expect(assigns(:zoom_meeting)).to eq(zoom_meeting)
+ end
+
+ it 'updates the zoom_meeting when started status is received in notification' do
+ post :create, params: started_status
+
+ expect(zoom_meeting.reload).to be_started
+ end
+
+ it 'updates the zoom_meeting when ended status is received in notification' do
+ post :create, params: ended_status
+
+ expect(zoom_meeting.reload).to be_ended
+ end
+
+ it 'updates the recording when recording complete notification is received' do
+ expect(AttachRecordingToZoomMeetingJob).to receive(:perform_later)
+
+ post :create, params: recording_complete
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/factories/contract_templates.rb b/spec/factories/contract_templates.rb
index 782bea9..80c8772 100644
--- a/spec/factories/contract_templates.rb
+++ b/spec/factories/contract_templates.rb
@@ -16,6 +16,10 @@ FactoryBot.define do
release_type "talent"
end
+ factory :medical_release_contract_template do
+ release_type "medical"
+ end
+
factory :material_release_contract_template do
release_type "material"
end
diff --git a/spec/factories/location_releases.rb b/spec/factories/location_releases.rb
index 3431b77..727ea50 100644
--- a/spec/factories/location_releases.rb
+++ b/spec/factories/location_releases.rb
@@ -15,6 +15,13 @@ FactoryBot.define do
end
end
+ trait :with_photo do
+ photos do
+ path = Rails.root.join("spec", "fixtures", "files", "location_photo.png")
+ [Rack::Test::UploadedFile.new(path, "image/png")]
+ end
+ end
+
trait :non_native do
contract do
path = Rails.root.join("spec", "fixtures", "files", "contract.pdf")
diff --git a/spec/factories/material_releases.rb b/spec/factories/material_releases.rb
index 9f6339a..d4ac6d5 100644
--- a/spec/factories/material_releases.rb
+++ b/spec/factories/material_releases.rb
@@ -15,6 +15,14 @@ FactoryBot.define do
end
end
+ trait :with_photo do
+ photos do
+ path = Rails.root.join("spec", "fixtures", "files", "material_photo.png")
+ [Rack::Test::UploadedFile.new(path, "image/png")]
+ end
+ end
+
+
trait :non_native do
contract do
path = Rails.root.join("spec", "fixtures", "files", "contract.pdf")
diff --git a/spec/factories/medical_releases.rb b/spec/factories/medical_releases.rb
new file mode 100644
index 0000000..7109fac
--- /dev/null
+++ b/spec/factories/medical_releases.rb
@@ -0,0 +1,47 @@
+FactoryBot.define do
+ factory :medical_release do
+ association :project
+
+ person_first_name "Jane"
+ person_last_name "Doe"
+
+ photos [Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "person_photo.png"), "image/png")]
+
+ trait :native do
+ person_phone "123-555-6789"
+
+ signature do
+ path = Rails.root.join("spec", "fixtures", "files", "signature.png")
+ Rack::Test::UploadedFile.new(path, "image/png")
+ end
+ end
+
+ trait :non_native do
+ contract do
+ path = Rails.root.join("spec", "fixtures", "files", "contract.pdf")
+ Rack::Test::UploadedFile.new(path, "application/pdf")
+ end
+ end
+
+ factory :medical_release_with_contract_template do
+ after(:build) do |medical_release, _|
+ medical_release.contract_template = build(:medical_release_contract_template)
+ end
+ end
+
+ factory :medical_release_with_contract_template_and_photos do
+ after(:build) do |medical_release, _|
+ medical_release.contract_template = build(:medical_release_contract_template)
+ path = Rails.root.join("spec", "fixtures", "files", "person_photo.png")
+ medical_release.photos.attach Rack::Test::UploadedFile.new(path, "image/png")
+ end
+ end
+
+ factory :medical_release_with_photo do
+ after(:build) do |medical_release, _|
+ path = Rails.root.join("spec", "fixtures", "files", "person_photo.png")
+ medical_release.photos.attach Rack::Test::UploadedFile.new(path, "image/png")
+ end
+ end
+ end
+end
diff --git a/spec/factories/zoom_users.rb b/spec/factories/zoom_users.rb
index 9fc5e58..e3d569a 100644
--- a/spec/factories/zoom_users.rb
+++ b/spec/factories/zoom_users.rb
@@ -1,4 +1,3 @@
-require 'zoom_gateway'
FactoryBot.define do
factory :zoom_user do
account_number ZoomGateway.ACCOUNT_NUMBER
diff --git a/spec/features/user_creates_note_spec.rb b/spec/features/user_creates_note_spec.rb
index 9ab6ffa..9cfe11c 100644
--- a/spec/features/user_creates_note_spec.rb
+++ b/spec/features/user_creates_note_spec.rb
@@ -103,4 +103,10 @@ feature "User creates notes" do
it_behaves_like "a notable collection UI"
end
+
+ context "for medical releases" do
+ subject! { create(:medical_release, project: project, notes: []) }
+
+ it_behaves_like "a notable collection UI"
+ end
end
diff --git a/spec/features/user_creates_tags_spec.rb b/spec/features/user_creates_tags_spec.rb
index 028ee2c..b8ca14a 100644
--- a/spec/features/user_creates_tags_spec.rb
+++ b/spec/features/user_creates_tags_spec.rb
@@ -86,4 +86,10 @@ feature "User creates tags" do
it_behaves_like "a taggable collection UI"
end
+
+ context "for medical releases" do
+ subject! { create(:medical_release, project: project, tags: []) }
+
+ it_behaves_like "a taggable collection UI"
+ end
end
diff --git a/spec/features/user_managing_acquired_media_releases_spec.rb b/spec/features/user_managing_acquired_media_releases_spec.rb
index cc23c62..6018c3a 100644
--- a/spec/features/user_managing_acquired_media_releases_spec.rb
+++ b/spec/features/user_managing_acquired_media_releases_spec.rb
@@ -5,6 +5,13 @@ feature "User managing acquired_media releases" do
let(:project) { create(:project, members: current_user, account: current_user.primary_account) }
context "when signed out" do
+ scenario "United States is default country" do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_acquired_media_release_path(project.account, project, contract_template)
+ expect(country_field_value).to eq "US"
+ end
+
scenario "creating a release", js: true do
contract_template = create(:contract_template, project: project)
@@ -205,6 +212,10 @@ feature "User managing acquired_media releases" do
private
+ def country_field_value
+ find_field("acquired_media_release[person_address_country]").value
+ end
+
def acquired_media_name_field
"acquired_media_release[name]"
end
diff --git a/spec/features/user_managing_location_releases_spec.rb b/spec/features/user_managing_location_releases_spec.rb
index 9e8a32f..0f72f5a 100644
--- a/spec/features/user_managing_location_releases_spec.rb
+++ b/spec/features/user_managing_location_releases_spec.rb
@@ -5,7 +5,14 @@ feature "User managing location releases" do
let(:project) { create(:project, members: current_user, account: current_user.primary_account) }
context "when signed out" do
- scenario "creating a release", js: true do
+ scenario "United States is default country" do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_location_release_path(project.account, project, contract_template)
+ expect(country_field_value).to eq "US"
+ end
+
+ scenario "creating a release without photos", js: true do
contract_template = create(:contract_template, project: project)
visit new_account_project_contract_template_location_release_path(project.account, project, contract_template)
@@ -21,10 +28,31 @@ feature "User managing location releases" do
draw_signature file_fixture("signature.png"), "location_release_signature_base64"
end
- click_button "I have read and agree to the above"
+ click_button submit_release_button
expect(page).to have_content("Your release was successfully submitted. Thank you.")
end
+
+ scenario "creating a release with photos", js: true do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_location_release_path(project.account, project, contract_template)
+
+ fill_in location_name_field, with: "Benny's Burritos"
+ fill_in person_first_name_field, with: "Jane"
+ fill_in person_last_name_field, with: "Doe"
+ fill_in person_phone_field, with: "555-555-5555"
+ fill_in person_email_field, with: "jane.doe@test.com"
+ fill_in person_address_street1_field, with: "100 Broadway"
+ fill_in filming_hours_field, with: "04:00 - 22:00"
+ draw_signature file_fixture("signature.png"), "location_release_signature_base64"
+
+ drop_file Rails.root.join(file_fixture("location_photo.png")), type: :dropzone
+ click_button submit_release_button
+
+ expect(page).to have_content("Your release was successfully submitted. Thank you.")
+ expect(LocationRelease.last.photos.attached?).to eq true
+ end
end
context "when signed in" do
@@ -195,6 +223,10 @@ feature "User managing location releases" do
private
+ def country_field_value
+ find_field("location_release[person_address_country]").value
+ end
+
def photos_heading(photos_count = 1)
t 'contracts.photos.heading', count: photos_count
end
@@ -255,6 +287,10 @@ feature "User managing location releases" do
t "helpers.submit.location_release.create"
end
+ def submit_release_button
+ t("shared.submit_release_long")
+ end
+
def create_release_notice
t "location_releases.create.notice"
end
diff --git a/spec/features/user_managing_material_releases_spec.rb b/spec/features/user_managing_material_releases_spec.rb
index 6a6e486..a58a9eb 100644
--- a/spec/features/user_managing_material_releases_spec.rb
+++ b/spec/features/user_managing_material_releases_spec.rb
@@ -5,7 +5,14 @@ feature "User managing material releases" do
let(:project) { create(:project, members: current_user, account: current_user.primary_account) }
context "when signed out" do
- scenario "creating a release", js: true do
+ scenario "United States is default country" do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_material_release_path(project.account, project, contract_template)
+ expect(country_field_value).to eq "US"
+ end
+
+ scenario "creating a release without photos", js: true do
contract_template = create(:contract_template, project: project)
visit new_account_project_contract_template_material_release_path(project.account, project, contract_template)
@@ -17,10 +24,27 @@ feature "User managing material releases" do
draw_signature file_fixture("signature.png"), "material_release_signature_base64"
end
- click_button "I have read and agree to the above"
+ click_button submit_release_button
expect(page).to have_content("Your release was successfully submitted. Thank you.")
end
+
+ scenario "creating a release with photos", js: true do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_material_release_path(project.account, project, contract_template)
+
+ fill_in material_name_field, with: "Pepsi Logo"
+ fill_in person_first_name_field, with: "Jane"
+ fill_in person_last_name_field, with: "Doe"
+ draw_signature file_fixture("signature.png"), "material_release_signature_base64"
+
+ drop_file Rails.root.join(file_fixture("material_photo.png")), type: :dropzone
+ click_button submit_release_button
+
+ expect(page).to have_content("Your release was successfully submitted. Thank you.")
+ expect(MaterialRelease.last.photos.attached?).to eq true
+ end
end
context "when signed in" do
@@ -180,6 +204,10 @@ feature "User managing material releases" do
private
+ def country_field_value
+ find_field("material_release[person_address_country]").value
+ end
+
def photos_heading(photos_count = 1)
t 'contracts.photos.heading', count: photos_count
end
@@ -224,6 +252,10 @@ feature "User managing material releases" do
t "helpers.submit.material_release.create"
end
+ def submit_release_button
+ t 'shared.submit_release_long'
+ end
+
def create_release_notice
t "material_releases.create.notice"
end
diff --git a/spec/features/user_managing_talent_releases_spec.rb b/spec/features/user_managing_talent_releases_spec.rb
index 512cc85..2464c84 100644
--- a/spec/features/user_managing_talent_releases_spec.rb
+++ b/spec/features/user_managing_talent_releases_spec.rb
@@ -5,6 +5,13 @@ feature "User managing talent releases" do
let(:project) { create(:project, members: current_user, account: current_user.primary_account) }
context "when signed out" do
+ scenario "United States is default country" do
+ contract_template = create(:contract_template, project: project)
+
+ visit new_account_project_contract_template_talent_release_path(project.account, project, contract_template)
+ expect(country_field_value).to eq "US"
+ end
+
scenario "creating a release for an adult", js: true do
contract_template = create(:contract_template, project: project)
@@ -274,6 +281,10 @@ feature "User managing talent releases" do
private
+ def country_field_value
+ find_field("talent_release[person_address_country]").value
+ end
+
def photos_heading(photos_count = 1)
t 'contracts.photos.heading', count: photos_count
end
diff --git a/spec/lib/zoom_gateway_spec.rb b/spec/lib/zoom_gateway_spec.rb
index f9bfd3a..0b48a24 100644
--- a/spec/lib/zoom_gateway_spec.rb
+++ b/spec/lib/zoom_gateway_spec.rb
@@ -1,5 +1,4 @@
require 'rails_helper'
-require 'zoom_gateway'
RSpec.describe ZoomGateway do
let(:roles_list_response) { {"roles" => [{"name" => 'pro-directme-host', "id" => "host_role_id"}, {"name" => 'basic-directme-host', "id" => "host_role_id"}]} }
@@ -10,7 +9,7 @@ RSpec.describe ZoomGateway do
let(:meeting_hash) { {"id" => "meeting_id", "start_url" => "https://start_url", "join_url" => "https://join_url"} }
let(:gateway) { ZoomGateway.new }
- describe "constants" do
+ describe "pseudo-constants" do
context '.USER_TYPE_NAME' do
it 'defaults to "basic"' do
expect(ZoomGateway.USER_TYPE_NAME).to eq('basic')
@@ -65,6 +64,15 @@ RSpec.describe ZoomGateway do
end
end
+ context '.ACCOUNT_NUMBER' do
+ it 'depends on ENV["ZOOM_ACCOUNT_NUMBER"]' do
+ stub_env_variable('ZOOM_ACCOUNT_NUMBER', 'xxx-yyy-zzz')
+ expect(ZoomGateway.ACCOUNT_NUMBER).to eq('xxx-yyy-zzz')
+ end
+ end
+ end
+
+ describe 'static methods' do
context '.enable_recordings?' do
it 'is truthy when ZOOM_ENABLE_RECORDINGS is set to true' do
stub_env_variable('ZOOM_ENABLE_RECORDINGS', 'true')
@@ -80,10 +88,9 @@ RSpec.describe ZoomGateway do
end
end
- context '.ACCOUNT_NUMBER' do
- it 'depends on ENV["ZOOM_ACCOUNT_NUMBER"]' do
- stub_env_variable('ZOOM_ACCOUNT_NUMBER', 'xxx-yyy-zzz')
- expect(ZoomGateway.ACCOUNT_NUMBER).to eq('xxx-yyy-zzz')
+ context '.host_role_name' do
+ it 'returns given name with -directme-host prefix' do
+ expect(ZoomGateway.host_role_name('prefix')).to eq('prefix-directme-host')
end
end
end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 476eb8e..3fbd316 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -130,7 +130,8 @@ RSpec.describe Account do
User,
Broadcast,
Account,
- ZoomMeeting
+ ZoomMeeting,
+ MedicalRelease
]
Rails.application.eager_load!
ActiveRecord::Base.descendants.each do |model|
diff --git a/spec/models/contract_template_spec.rb b/spec/models/contract_template_spec.rb
index 0d9e4dc..3e801e9 100644
--- a/spec/models/contract_template_spec.rb
+++ b/spec/models/contract_template_spec.rb
@@ -13,6 +13,7 @@ describe ContractTemplate do
it { is_expected.to have_many(:appearance_releases).dependent(:restrict_with_error) }
it { is_expected.to have_many(:location_releases).dependent(:restrict_with_error) }
it { is_expected.to have_many(:material_releases).dependent(:restrict_with_error) }
+ it { is_expected.to have_many(:medical_releases).dependent(:restrict_with_error) }
end
describe 'validations' do
diff --git a/spec/models/medical_release_spec.rb b/spec/models/medical_release_spec.rb
new file mode 100644
index 0000000..917c3ef
--- /dev/null
+++ b/spec/models/medical_release_spec.rb
@@ -0,0 +1,53 @@
+require "rails_helper"
+
+RSpec.describe MedicalRelease do
+ it_behaves_like "a contractable"
+ it_behaves_like "a notable"
+ it_behaves_like "a photoable"
+ it_behaves_like "a releasable"
+
+ describe "validations" do
+ it { is_expected.to validate_presence_of(:person_first_name) }
+ it { is_expected.to validate_presence_of(:person_last_name) }
+
+ context "for #person_email" do
+ it { is_expected.to allow_value("test@test.com", nil).for(:person_email) }
+ it { is_expected.not_to allow_values("foo", "test@foo", "N/A").for(:person_email) }
+ end
+
+ context "for native releases" do
+ it { is_expected.to validate_attachment_of(:signature).on(:native) }
+ end
+
+ context "for non-native releases" do
+ it { is_expected.to validate_attachment_of(:contract).on(:non_native) }
+ end
+ end
+
+ describe "attachments" do
+ it { is_expected.to respond_to(:signature) }
+ end
+
+ describe "#uses_edl?" do
+ it { is_expected.not_to be_uses_edl }
+ end
+
+ describe "#contract_file_name" do
+ it "includes project name, signed at date, release type, release number and person name" do
+ release = create(:medical_release_with_contract_template, id: 100, signed_at: Date.new(2020, 2, 10), person_first_name: "John", person_last_name: "Doe")
+
+ expect(release.contract_file_name).to eq("my-video-project_medical_2020.02.10_1_doe-john")
+ end
+
+ context "when signed at is nil" do
+ it "uses the created at date" do
+ release = create(:medical_release_with_contract_template,
+ signed_at: nil,
+ created_at: DateTime.new(2020, 2, 10, 12, 0, 0),
+ person_first_name: "John", person_last_name: "Doe")
+
+ expect(release.contract_file_name).to eq("my-video-project_medical_2020.02.10_1_doe-john")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 4cad165..9230153 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1,5 +1,4 @@
require "rails_helper"
-require "zoom_gateway"
RSpec.describe Project, type: :model do
describe "associations" do
@@ -10,6 +9,7 @@ RSpec.describe Project, type: :model do
it { is_expected.to have_many(:material_releases).dependent(:destroy) }
it { is_expected.to have_many(:music_releases).dependent(:destroy) }
it { is_expected.to have_many(:talent_releases).dependent(:destroy) }
+ it { is_expected.to have_many(:medical_releases).dependent(:destroy) }
it { is_expected.to have_many(:videos).dependent(:destroy) }
it { is_expected.to have_many(:contract_templates).dependent(:destroy) }
it { is_expected.to have_many(:project_memberships).dependent(:destroy) }
diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb
index ae07a39..7b573db 100644
--- a/spec/models/zoom_meeting_spec.rb
+++ b/spec/models/zoom_meeting_spec.rb
@@ -1,5 +1,4 @@
require 'rails_helper'
-require 'zoom_gateway'
RSpec.describe ZoomMeeting, type: :model do
let(:zoom_meeting) { build(:zoom_meeting, api_meeting_id: nil) }
diff --git a/spec/models/zoom_user_spec.rb b/spec/models/zoom_user_spec.rb
index 2f79533..15732ae 100644
--- a/spec/models/zoom_user_spec.rb
+++ b/spec/models/zoom_user_spec.rb
@@ -1,4 +1,3 @@
-require 'zoom_gateway'
require 'rails_helper'
RSpec.describe ZoomUser, type: :model do
diff --git a/spec/policies/medical_release_policy_spec.rb b/spec/policies/medical_release_policy_spec.rb
new file mode 100644
index 0000000..8b79891
--- /dev/null
+++ b/spec/policies/medical_release_policy_spec.rb
@@ -0,0 +1,37 @@
+require "rails_helper"
+
+describe MedicalReleasePolicy do
+ let(:user_context) { build(:user_context) }
+
+ subject { described_class }
+
+ permissions :create? do
+ it { is_expected.to permit(:create) }
+ end
+
+ permissions :show? do
+ it { is_expected.to permit(:show) }
+ end
+
+ permissions :update? do
+ context "for a native release" do
+ it { is_expected.not_to permit(user_context, build(:medical_release, :native)) }
+ end
+
+ context "for a non-native release" do
+ it { is_expected.to permit(user_context, build(:medical_release, :non_native)) }
+ end
+ end
+
+ permissions :destroy? do
+ it { is_expected.to permit(:destroy) }
+ end
+
+ permissions :edit_photos? do
+ it { is_expected.to permit(:edit_photos) }
+ end
+
+ permissions :update_photos? do
+ it { is_expected.to permit(:update_photos) }
+ end
+end