diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 48b93ad8f..04e493c37 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -45,68 +45,36 @@ class Reservation < ActiveRecord::Base # === Machine reservation === when Machine base_amount = reservable.prices.find_by(group_id: user.group_id, plan_id: plan.try(:id)).amount - if plan - machine_credit = plan.machine_credits.select {|credit| credit.creditable_id == reservable_id}.first - if machine_credit - hours_available = machine_credit.hours - if !new_plan_being_bought - user_credit = user.users_credits.find_by_credit_id(machine_credit.id) - if user_credit - hours_available = machine_credit.hours - user_credit.hours_used - end - end - slots.each_with_index do |slot, index| - description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" - ii_amount = (index < hours_available ? 0 : base_amount) - ii_amount = 0 if (slot.offered and on_site) - unless on_site - ii = Stripe::InvoiceItem.create( - customer: user.stp_customer_id, - amount: ii_amount, - currency: Rails.application.secrets.stripe_currency, - description: description - ) - invoice_items << ii - end - self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description) - end - else - slots.each do |slot| - description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" - ii_amount = base_amount - ii_amount = 0 if (slot.offered and on_site) - unless on_site - ii = Stripe::InvoiceItem.create( - customer: user.stp_customer_id, - amount: ii_amount, - currency: Rails.application.secrets.stripe_currency, - description: description - ) - invoice_items << ii - end - self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description) - end + users_credits_manager = UsersCredits::Manager.new(reservation: self) + + slots.each_with_index do |slot, index| + description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" + + ii_amount = base_amount # ii_amount default to base_amount + + if users_credits_manager.will_use_credits? + ii_amount = (index < users_credits_manager.free_hours_count) ? 0 : base_amount end - else - slots.each do |slot| - description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" - ii_amount = base_amount - ii_amount = 0 if (slot.offered and on_site) - unless on_site - ii = Stripe::InvoiceItem.create( - customer: user.stp_customer_id, - amount: ii_amount, - currency: Rails.application.secrets.stripe_currency, - description: description - ) - invoice_items << ii - end - self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description) + + ii_amount = 0 if slot.offered and on_site # if it's a local payment and slot is offered free + + unless on_site # if it's local payment then do not create Stripe::InvoiceItem + ii = Stripe::InvoiceItem.create( + customer: user.stp_customer_id, + amount: ii_amount, + currency: Rails.application.secrets.stripe_currency, + description: description + ) + invoice_items << ii end + self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description) end + # === Training reservation === when Training + # TO BE REFACTORED WHEN PLAN PURCHASE AND RESERVATION PURCHASE WILL BE DECOUPLED + base_amount = reservable.amount_by_group(user.group_id).amount if plan # Return True if the subscription link a training credit for training reserved by the user @@ -176,32 +144,6 @@ class Reservation < ActiveRecord::Base invoice_items end - def update_users_credits - if user.subscribed_plan - if reservable_type == 'Machine' - machine_credit = user.subscribed_plan.machine_credits.select {|credit| credit.creditable_id == reservable_id}.first - if machine_credit - hours_available = machine_credit.hours - user_credit = user.users_credits.find_or_initialize_by(credit_id: machine_credit.id) - user_credit.hours_used ||= 0 - hours_available = machine_credit.hours - user_credit.hours_used - if hours_available >= slots.size - user_credit.hours_used = user_credit.hours_used + slots.size - else - user_credit.hours_used = machine_credit.hours - end - user_credit.save - end - elsif reservable_type == 'Training' - training_credit = user.subscribed_plan.training_credits.select {|credit| credit.creditable_id == reservable_id}.first - if user.training_credits.size < user.subscribed_plan.training_credit_nb and training_credit - user.credits << training_credit - end - end - end - return self - end - def save_with_payment build_invoice(user: user) invoice_items = generate_invoice_items @@ -286,7 +228,8 @@ class Reservation < ActiveRecord::Base end end - update_users_credits + UsersCredits::Manager.new(reservation: self).update_credits + return true end end @@ -318,7 +261,7 @@ class Reservation < ActiveRecord::Base if user.invoicing_disabled? if valid? save! - update_users_credits + UsersCredits::Manager.new(reservation: self).update_credits return true end else @@ -345,7 +288,8 @@ class Reservation < ActiveRecord::Base save! end - update_users_credits + UsersCredits::Manager.new(reservation: self).update_credits + return true end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index b41fac740..dfd0bd5e2 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -28,7 +28,7 @@ class Subscription < ActiveRecord::Base self.expired_at = Time.at(new_subscription.current_period_end) save! - reset_users_credits if expired_date_changed + UsersCredits::Manager.new(user: self.user).reset_credits if expired_date_changed # generate invoice stp_invoice = Stripe::Invoice.all(customer: user.stp_customer_id, limit: 1).data.first @@ -77,7 +77,7 @@ class Subscription < ActiveRecord::Base self.canceled_at = nil set_expired_at save! - reset_users_credits if expired_date_changed + UsersCredits::Manager.new(user: self.user).reset_credits if expired_date_changed generate_invoice.save if invoice return true else @@ -140,7 +140,7 @@ class Subscription < ActiveRecord::Base self.expired_at = expired_at if save - reset_users_credits if !free_days + UsersCredits::Manager.new(user: self.user).reset_credits if !free_days notify_subscription_extended(free_days) return true end @@ -215,10 +215,6 @@ class Subscription < ActiveRecord::Base p_value.to_date != expired_at.to_date and expired_at > p_value end - def reset_users_credits - user.users_credits.destroy_all - end - # def is_being_extended? # !expired_at_was.nil? and expired_at_changed? # end diff --git a/test/integration/reservations/create_as_admin_test.rb b/test/integration/reservations/create_as_admin_test.rb index 5d40a3d6b..6383c93fd 100644 --- a/test/integration/reservations/create_as_admin_test.rb +++ b/test/integration/reservations/create_as_admin_test.rb @@ -1,11 +1,188 @@ -# class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest -# setup do -# admin = User.with_role(:admin).first -# login_as(admin, scope: :user) -# end -# -# test "admin reserves a machine for a user" do -# availability = Availability.find(5) -# -# end -# end +module Reservations + class CreateAsAdminTest < ActionDispatch::IntegrationTest + setup do + @user_without_subscription = User.with_role(:member).without_subscription.first + @user_with_subscription = User.with_role(:member).with_subscription.second + @admin = User.with_role(:admin).first + login_as(@admin, scope: :user) + end + + test "user without subscription reserves a machine with success" do + machine = Machine.find(6) + availability = machine.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + users_credit_count = UsersCredit.count + + post reservations_path, { reservation: { + user_id: @user_without_subscription.id, + reservable_id: machine.id, + reservable_type: machine.class.name, + slots_attributes: [ + { start_at: availability.start_at.to_s(:iso8601), + end_at: (availability.start_at + 1.hour).to_s(:iso8601), + availability_id: availability.id + } + ] + }}.to_json, default_headers + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count + 1, Invoice.count + assert_equal invoice_items_count + 1, InvoiceItem.count + assert_equal users_credit_count, UsersCredit.count + + # reservation assertions + reservation = Reservation.last + + assert reservation.invoice + assert reservation.stp_invoice_id.blank? + assert_equal 1, reservation.invoice.invoice_items.count + + # invoice assertions + invoice = reservation.invoice + + assert invoice.stp_invoice_id.blank? + refute invoice.total.blank? + + # invoice_items assertions + invoice_item = InvoiceItem.last + + refute invoice_item.stp_invoice_item_id + assert_equal invoice_item.amount, machine.prices.find_by(group_id: @user_without_subscription.group_id).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) + end + + test "user without subscription reserves a training with success" do + training = Training.first + availability = training.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + + post reservations_path, { reservation: { + user_id: @user_without_subscription.id, + reservable_id: training.id, + reservable_type: training.class.name, + slots_attributes: [ + { start_at: availability.start_at.to_s(:iso8601), + end_at: (availability.start_at + 1.hour).to_s(:iso8601), + availability_id: availability.id + } + ] + }}.to_json, default_headers + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count + 1, Invoice.count + assert_equal invoice_items_count + 1, InvoiceItem.count + + # reservation assertions + reservation = Reservation.last + + assert reservation.invoice + assert reservation.stp_invoice_id.blank? + assert_equal 1, reservation.invoice.invoice_items.count + + # invoice assertions + invoice = reservation.invoice + + assert invoice.stp_invoice_id.blank? + refute invoice.total.blank? + # invoice_items + invoice_item = InvoiceItem.last + + refute invoice_item.stp_invoice_item_id + assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) + end + + test "user with subscription reserves a machine with success" do + plan = @user_with_subscription.subscribed_plan + machine = Machine.find(6) + availability = machine.availabilities.first + + reservations_count = Reservation.count + invoice_count = Invoice.count + invoice_items_count = InvoiceItem.count + users_credit_count = UsersCredit.count + + post reservations_path, { reservation: { + user_id: @user_with_subscription.id, + reservable_id: machine.id, + reservable_type: machine.class.name, + slots_attributes: [ + { start_at: availability.start_at.to_s(:iso8601), + end_at: (availability.start_at + 1.hour).to_s(:iso8601), + availability_id: availability.id + }, + { start_at: (availability.start_at + 1.hour).to_s(:iso8601), + end_at: (availability.start_at + 2.hours).to_s(:iso8601), + availability_id: availability.id + } + ] + }}.to_json, default_headers + + # general assertions + assert_equal 201, response.status + assert_equal reservations_count + 1, Reservation.count + assert_equal invoice_count + 1, Invoice.count + assert_equal invoice_items_count + 2, InvoiceItem.count + assert_equal users_credit_count + 1, UsersCredit.count + + # reservation assertions + reservation = Reservation.last + + assert reservation.invoice + assert reservation.stp_invoice_id.blank? + assert_equal 2, reservation.invoice.invoice_items.count + + # invoice assertions + invoice = reservation.invoice + + assert invoice.stp_invoice_id.blank? + refute invoice.total.blank? + + # invoice_items assertions + invoice_items = InvoiceItem.last(2) + machine_price = machine.prices.find_by(group_id: @user_with_subscription.group_id, plan_id: plan.id).amount + + assert invoice_items.any? { |invoice| invoice.amount == 0 } + assert invoice_items.any? { |invoice| invoice.amount == machine_price } + assert invoice_items.all? { |invoice| invoice.stp_invoice_item_id.blank? } + + # users_credits assertions + users_credit = UsersCredit.last + + assert_equal @user_with_subscription, users_credit.user + assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) + end + end +end diff --git a/test/integration/reservations/create_test.rb b/test/integration/reservations/create_test.rb index e1a5f0a97..e3c6dd29c 100644 --- a/test/integration/reservations/create_test.rb +++ b/test/integration/reservations/create_test.rb @@ -56,6 +56,14 @@ module Reservations assert invoice_item.stp_invoice_item_id assert_equal invoice_item.amount, machine.prices.find_by(group_id: @user_without_subscription.group_id).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) end test "user without subscription reserves a machine with error" do @@ -67,6 +75,7 @@ module Reservations reservations_count = Reservation.count invoice_count = Invoice.count invoice_items_count = InvoiceItem.count + notifications_count = Notification.count VCR.use_cassette("reservations_create_for_machine_without_subscription_error") do post reservations_path, { reservation: { @@ -88,6 +97,7 @@ module Reservations assert_equal reservations_count, Reservation.count assert_equal invoice_count, Invoice.count assert_equal invoice_items_count, InvoiceItem.count + assert_equal notifications_count, Notification.count end test "user without subscription reserves a training with success" do @@ -138,6 +148,14 @@ module Reservations assert invoice_item.stp_invoice_item_id assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) end test "user with subscription reserves a machine with success" do @@ -204,6 +222,14 @@ module Reservations assert_equal @user_with_subscription, users_credit.user assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used + + # invoice assertions + invoice = Invoice.find_by(invoiced: reservation) + assert invoice + assert File.exist?(invoice.file) + + # notification + assert_not_empty Notification.where(attached_object: reservation) end end end