From e1ca747e3c7fcc658632f49de416e75bb56cb993 Mon Sep 17 00:00:00 2001 From: Matt Levy Date: Wed, 24 Jul 2024 16:21:20 -0700 Subject: [PATCH] event addons Update to mvc for event, event_addons Still WIP for views --- app/controllers/events_controller.rb | 57 ++++++-------- app/models/addon.rb | 8 ++ app/models/event.rb | 38 ++++++++- app/models/event_addon.rb | 11 +++ app/views/events/_form.html.haml | 42 +++------- db/seeds.rb | 19 +++-- spec/controllers/events_controller_spec.rb | 35 --------- spec/models/event_addon_spec.rb | 5 +- spec/models/event_spec.rb | 90 ++++++++++++++++++++++ 9 files changed, 193 insertions(+), 112 deletions(-) diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 25ad1f7a..4e447f01 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -24,20 +24,28 @@ def show def new @event = Event.new(Event::DEFAULT_ATTRIBUTES) + @event.build_default_event_addons end def edit params_symbolized_hash[:event]&.each_pair do |key, value| @event.send("#{key}=", value) if @event.respond_to?("#{key}=") end + + # build default event addons if event does not have persisted addons + @event.create_default_event_addons + render end def create create_params = params_symbolized_hash[:event].dup + Rails.logger.info("event_create: create_params: #{create_params}") TimeHelper.normalize_time_attributes(create_params) @event = Event.new(create_params) + @event.build_event_addons_from_params(create_params[:event_addons_attributes]) + Rails.logger.info("event_create: created event: #{@event.id} event_addons: #{@event.event_addons.inspect}") if @event.save redirect_to @event @@ -51,6 +59,8 @@ def update update_params = params_symbolized_hash[:event].dup TimeHelper.normalize_time_attributes(update_params) + Rails.logger.info("event_update: #{update_params}") + if @event.update(update_params) redirect_to @event, notice: 'The event has been updated.' else @@ -116,42 +126,21 @@ def params_symbolized_hash def set_event @event = Event.where(id: permitted_params[:id].to_i).first + if @event.event_addons.blank? + @event.create_default_event_addons + Rails.logger.info("event_set: event: #{@event.id} addons.size: #{@event.event_addons.size} event_addons: #{@event.event_addons.inspect}") + end end def permitted_params - params.permit( - :id, - :user_email, - :user_id, - :_method, - :authenticity_token, - :commit, - event: %i[ - start_time - end_time - ticket_sales_start_time - ticket_sales_end_time - ticket_requests_end_time - adult_ticket_price - allow_donations - allow_financial_assistance - cabin_price - early_arrival_price - end_time - kid_ticket_price - late_departure_price - max_adult_tickets_per_request - max_cabin_requests - max_cabins_per_request - max_kid_tickets_per_request - name - require_mailing_address - require_role - start_time - tickets_require_approval - ] - ) - .to_hash - .with_indifferent_access + params.permit(:id, :user_email, :user_id, :_method, :authenticity_token, :commit, + event: [:name, :start_time, :end_time, + :ticket_sales_start_time, :ticket_sales_end_time, :ticket_requests_end_time, + :adult_ticket_price, :kid_ticket_price, + :max_adult_tickets_per_request, :max_kid_tickets_per_request, + :allow_donations, :allow_financial_assistance, + :require_mailing_address, :require_role, :tickets_require_approval, + event_addons_attributes: [:id, :addon_id, :price]] + ).to_hash.with_indifferent_access end end diff --git a/app/models/addon.rb b/app/models/addon.rb index f8689e94..0b5663ab 100644 --- a/app/models/addon.rb +++ b/app/models/addon.rb @@ -12,6 +12,9 @@ # updated_at :datetime not null # class Addon < ApplicationRecord + has_many :event_addons, autosave: true + has_many :ticket_request_event_addon, autosave: true + CATEGORIES = [CATEGORY_PASS = 'pass', CATEGORY_CAMP = 'camp'].freeze @@ -23,6 +26,10 @@ def self.categories CATEGORIES end + def self.order_by_category + order(:category, :id) + end + def self.find_all_by_category(category) addons = [] @@ -32,4 +39,5 @@ def self.find_all_by_category(category) addons end + end diff --git a/app/models/event.rb b/app/models/event.rb index 1d69007d..3346e8fb 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -51,7 +51,9 @@ class Event < ApplicationRecord :photo, :photo_cache, :tickets_require_approval, :require_mailing_address, :require_role, :allow_financial_assistance, :allow_donations, :ticket_sales_start_time, :ticket_sales_end_time, - :ticket_requests_end_time + :ticket_requests_end_time, :event_addons_attributes + + accepts_nested_attributes_for :event_addons mount_uploader :photo, PhotoUploader @@ -206,8 +208,38 @@ def to_param [id, slug].join('-') # 1-summer-campout-xii end - def find_all_addons - Addon.all + def build_event_addons_from_params(build_params) + Rails.logger.debug { "build_event_addons_from_params: #{build_params}" } + + build_params.each do |key, value| + if Addon.exists?(id: value) + self.event_addons.build({event_id: id, addon_id: value["addon_id"], price: value["price"]}) + end + end + + Rails.logger.debug { "build_event_addons_from_params: save: #{event_addons.inspect}" } + event_addons + end + + # create default event addons from Addons + def create_default_event_addons + return if event_addons.present? + + build_default_event_addons + save + Rails.logger.debug { "create_default_event_addons: save: #{event_addons.inspect}" } + event_addons + end + + def build_default_event_addons + return if event_addons.present? + + Addon.order_by_category.each do |addon| + self.event_addons.build({event: self, addon: addon}).set_default_values + end + + Rails.logger.debug { "build_default_event_addons: #{event_addons.inspect}" } + event_addons end private diff --git a/app/models/event_addon.rb b/app/models/event_addon.rb index 53e115c7..df4ae42d 100644 --- a/app/models/event_addon.rb +++ b/app/models/event_addon.rb @@ -24,10 +24,21 @@ class EventAddon < ApplicationRecord belongs_to :addon belongs_to :event + attr_accessible :id, :event_id, :addon_id, :price, :event, :addon before_validation :set_default_values + validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } + def set_default_values self.price ||= addon.default_price end + + def name + addon.name + end + + def category + addon.category + end end diff --git a/app/views/events/_form.html.haml b/app/views/events/_form.html.haml index fda6ce0c..3f8261cb 100644 --- a/app/views/events/_form.html.haml +++ b/app/views/events/_form.html.haml @@ -16,7 +16,7 @@ .vertical-20 %fieldset - %legend Ticket Details + %legend Ticket Sale Details = f.label :ticket_sales_start_time do Ticket Sales Opening Time %p.muted @@ -75,7 +75,7 @@ .col-sm-12.col-md-6.col-lg-6 - %legend Event Pricing + %legend Ticket Pricing = f.label :adult_ticket_price, 'Adult Ticket Price ($)' = f.number_field :adult_ticket_price, class: 'input-small', required: true, min: 0 @@ -85,14 +85,6 @@ %p.muted Leave blank for no limit = f.number_field :max_adult_tickets_per_request, class: 'input-mini', min: 1 - = f.label :early_arrival_price, 'Early Arrival Price($)' - = f.number_field :early_arrival_price, class: 'input-small', required: true, - min: 0 - - = f.label :late_departure_price, 'Late Departure Price($)' - = f.number_field :late_departure_price, class: 'input-small', required: true, - min: 0 - = f.label :kid_ticket_price do Kid Ticket Price ($) %p.muted Keep blank if you want to sell only adult tickets @@ -103,25 +95,15 @@ %p.muted Leave blank for no limit = f.number_field :max_kid_tickets_per_request, class: 'input-mini', min: 1 - = f.label :max_cabin_requests do - Maximum number of cabins that can be requested in total - %p.muted - Once more than this number of cabins is requested, subsequent ticket - requesters will not have the option to request cabins. You'll probably - want to set this value to be a little bit higher than the actual number - of cabins at your site, and then manually email the few individuals who - requested a cabin but won't get one. Leave blank for no limit (probably - not a good idea). - = f.number_field :max_cabin_requests, class: 'input-mini', min: 1 - - = f.label :cabin_price do - Cabin Price ($) - %p.muted Keep blank if there are no cabins or you aren't charging separately for them - = f.number_field :cabin_price, class: 'input-small', min: 0 - - = f.label :max_cabins_per_request do - Maximum number of cabins allowed per ticket request? - %p.muted Leave blank for no limit - = f.number_field :max_cabins_per_request, class: 'input-mini', min: 1 + %legend Event Addons + %p.muted + Set price > 0 to include the addon in the event. + Setting the price to 0 removes the item from being sold. + + - @event.event_addons.each do |event_addon| + = f.fields_for :event_addons, event_addon do |event_addon_form| + = event_addon_form.hidden_field :addon_id, value: event_addon.addon_id + = event_addon_form.label :name, event_addon.name + = event_addon_form.number_field :price, class: 'input-small', min: 0 = f.submit " ▶︎ Update Event", class: 'btn btn-primary btn-large' diff --git a/db/seeds.rb b/db/seeds.rb index 1b13bac0..b3108c49 100755 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -165,6 +165,17 @@ def create_events end end + def create_addons + return if Addon.count > 0 + + Addon.create category: Addon::CATEGORY_PASS, name: 'Early Arrival', default_price: 20 + Addon.create category: Addon::CATEGORY_PASS, name: 'Late Departure', default_price: 30 + Addon.create category: Addon::CATEGORY_CAMP, name: 'Car Camping', default_price: 50 + Addon.create category: Addon::CATEGORY_CAMP, name: 'RV under 20ft', default_price: 100 + Addon.create category: Addon::CATEGORY_CAMP, name: 'RV under 25ft', default_price: 125 + Addon.create category: Addon::CATEGORY_CAMP, name: 'RV over 25ft', default_price: 150 + end + def print_event(event) puts puts " Name: #{event.name.to_s.colorize(color: :magenta, mode: :bold)}" @@ -184,14 +195,6 @@ def header(string) puts "\n#{format(" %-#{HEADER_WIDTH}.#{HEADER_WIDTH}s".colorize(background: :light_blue), string)}" end - def create_addons - Addon.create category: Addon::CATEGORY_PASS, name: 'Early Arrival', default_price: 20 - Addon.create category: Addon::CATEGORY_PASS, name: 'Late Departure', default_price: 30 - Addon.create category: Addon::CATEGORY_CAMP, name: 'Car Camping', default_price: 50 - Addon.create category: Addon::CATEGORY_CAMP, name: 'RV under 20ft', default_price: 100 - Addon.create category: Addon::CATEGORY_CAMP, name: 'RV under 25ft', default_price: 125 - Addon.create category: Addon::CATEGORY_CAMP, name: 'RV over 25ft', default_price: 150 - end end end diff --git a/spec/controllers/events_controller_spec.rb b/spec/controllers/events_controller_spec.rb index 20f5c132..2a948517 100644 --- a/spec/controllers/events_controller_spec.rb +++ b/spec/controllers/events_controller_spec.rb @@ -84,41 +84,6 @@ expect(new_event.require_role).to be_truthy end - # rubocop: enable RSpec/AnyInstance - - describe '#permitted_params' do - subject(:permitted_keys) { permitted_event_keys } - - let(:permitted_event_params) { described_class.new.send(:permitted_params)[:event].to_h.symbolize_keys } - let(:expected_keys) do - %i[ - adult_ticket_price - allow_donations - allow_financial_assistance - cabin_price - early_arrival_price - end_time - kid_ticket_price - late_departure_price - max_adult_tickets_per_request - max_cabin_requests - max_cabins_per_request - max_kid_tickets_per_request - name - require_mailing_address - require_role - start_time - ticket_requests_end_time - ticket_sales_end_time - ticket_sales_start_time - tickets_require_approval - ] - end - let(:permitted_event_keys) { permitted_event_params.keys.sort } - - it { expect(permitted_keys).to eql(expected_keys) } - end - context 'when the user is not signed in' do before { make_request[] } diff --git a/spec/models/event_addon_spec.rb b/spec/models/event_addon_spec.rb index 4b56851b..37a2618e 100644 --- a/spec/models/event_addon_spec.rb +++ b/spec/models/event_addon_spec.rb @@ -40,10 +40,11 @@ end context 'sets price' do - let(:event_addon) { build(:event_addon, price: 314_159) } + let(:price) { 314 } + let(:event_addon) { build(:event_addon, price: price) } it 'has price set not default price' do - expect(event_addon.price).to eq(314_159) + expect(event_addon.price).to eq(price) end end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index d68f7e49..0c02d31a 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -371,4 +371,94 @@ end end end + + describe '#build_event_addons_from_params' do + let(:price) { 314 } + let!(:addon) { create(:addon) } + let(:event_addons_attributes) { {"0"=>{"addon_id"=>addon.id, "price"=>price} }} + + before do + event.build_event_addons_from_params(event_addons_attributes) + end + + context "event addon is built for event" do + it 'has 1 event addon' do + expect(event.event_addons.size).to eq(1) + end + + it 'has price set from params' do + expect(event.event_addons.first&.price).to eq(price) + end + + it 'has addon id set from params' do + expect(event.event_addons.first&.addon_id).to eq(addon.id) + end + end + + context "event addon is not built for event" do + let(:event_addons_attributes) { {"0"=>{"addon_id"=>999, "price"=>price} }} + + it 'has an unknown addon id' do + expect(event.event_addons.size).to eq(0) + end + end + end + + describe '#create_default_event_addons' do + context 'event has no addons' do + it 'event has no addons' do + expect(event.event_addons.count).to eq(0) + end + end + + context 'event has event addons' do + let!(:addon) { create(:addon) } + + before do + event.create_default_event_addons + end + + it 'creates and persists an EventAddon' do + expect(EventAddon.count).to eq(1) + end + + it 'creates EventAddon with name' do + expect(EventAddon.first&.name).to eq(Addon.first.name) + end + + it 'event has event addons for each Addon' do + expect(event.event_addons.size).to eq(Addon.count) + end + + it 'event has default prices for addons' do + event_addon = event.event_addons.first + expect(event_addon&.price).to eq(event_addon&.addon&.default_price) + end + + end + end + + describe '#load_default_event_addons' do + context 'event has no addons' do + it 'event has no addons' do + expect(event.event_addons.count).to eq(0) + end + end + + context 'event has event addons' do + let!(:addon) { create(:addon) } + + before { + event.build_default_event_addons + } + + it 'event has event addons for each Addon' do + expect(event.event_addons.size).to eq(Addon.count) + end + + it 'event addons are not saved' do + expect(event.event_addons.first&.persisted?).to be_falsey + end + end + end end