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 %> +
+
+
+ +
+ + + + + + + + + + + + + + + <% if @medical_releases.any? %> + <%= render @medical_releases %> + <% else %> + + + + <% 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") %>
<%= t(".empty") %>
+
+ +
+ <%= will_paginate @medical_releases %> +
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 '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