mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
check for slots restricted to subscribers at cart level
This commit is contained in:
parent
55b0e25ee9
commit
d0011a10f0
@ -5,6 +5,12 @@ module CartItem; end
|
||||
|
||||
# This is an abstract class implemented by classes that can be added to the shopping cart
|
||||
class CartItem::BaseItem
|
||||
attr_reader :errors
|
||||
|
||||
def initialize(*)
|
||||
@errors = {}
|
||||
end
|
||||
|
||||
def price
|
||||
{ elements: {}, amount: 0 }
|
||||
end
|
||||
@ -13,5 +19,11 @@ class CartItem::BaseItem
|
||||
''
|
||||
end
|
||||
|
||||
# This method run validations at cart-level, possibly using the other items in the cart, to validate the current one.
|
||||
# Other validations that may occurs at record-level (ActiveRecord validations) can't be related to other items.
|
||||
def valid?(_all_items)
|
||||
true
|
||||
end
|
||||
|
||||
def to_object; end
|
||||
end
|
||||
|
@ -12,6 +12,7 @@ class CartItem::Reservation < CartItem::BaseItem
|
||||
@operator = operator
|
||||
@reservable = reservable
|
||||
@slots = slots
|
||||
super
|
||||
end
|
||||
|
||||
def price
|
||||
@ -33,6 +34,23 @@ class CartItem::Reservation < CartItem::BaseItem
|
||||
@reservable.name
|
||||
end
|
||||
|
||||
def valid?(all_items)
|
||||
pending_subscription = all_items.find { |i| i.is_a?(CartItem::Subscription) }
|
||||
@slots.each do |slot|
|
||||
availability = Availability.find(slot[:availability_id])
|
||||
next if availability.plan_ids.empty?
|
||||
next if (@customer.subscribed_plan && availability.plan_ids.include?(@customer.subscribed_plan.id)) ||
|
||||
(pending_subscription && availability.plan_ids.include?(pending_subscription.plan.id)) ||
|
||||
(@operator.manager? && @customer.id != @operator.id) ||
|
||||
@operator.admin?
|
||||
|
||||
@errors[:slot] = 'slot is restricted for subscribers'
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def credits
|
||||
|
@ -7,6 +7,7 @@ class CartItem::Subscription < CartItem::BaseItem
|
||||
|
||||
@plan = plan
|
||||
@customer = customer
|
||||
super
|
||||
end
|
||||
|
||||
def plan
|
||||
|
@ -24,23 +24,12 @@ class Reservation < ApplicationRecord
|
||||
validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) }
|
||||
validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) }
|
||||
validate :slots_not_locked
|
||||
# validates_with ReservationSlotSubscriptionValidator
|
||||
|
||||
attr_accessor :plan_id, :subscription
|
||||
|
||||
after_commit :notify_member_create_reservation, on: :create
|
||||
after_commit :notify_admin_member_create_reservation, on: :create
|
||||
after_commit :extend_subscription, on: :create
|
||||
after_save :update_event_nb_free_places, if: proc { |reservation| reservation.reservable_type == 'Event' }
|
||||
|
||||
## Generate the subscription associated with for the current reservation
|
||||
def generate_subscription
|
||||
return unless plan_id
|
||||
|
||||
self.subscription = Subscription.new(plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil)
|
||||
subscription.init_save
|
||||
subscription
|
||||
end
|
||||
|
||||
# @param canceled if true, count the number of seats for this reservation, including canceled seats
|
||||
def total_booked_seats(canceled: false)
|
||||
|
@ -63,13 +63,17 @@ class ShoppingCart
|
||||
payment.post_save(payment_id)
|
||||
end
|
||||
|
||||
{ success: objects.map(&:errors).flatten.map(&:empty?).all?, payment: payment, errors: objects.map(&:errors).flatten }
|
||||
success = objects.map(&:errors).flatten.map(&:empty?).all? && items.map(&:errors).map(&:empty?).all?
|
||||
errors = objects.map(&:errors).flatten.concat(items.map(&:errors))
|
||||
{ success: success, payment: payment, errors: errors }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Save the object associated with the provided item or raise and Rollback if something wrong append.
|
||||
def save_item(item)
|
||||
raise ActiveRecord::Rollback unless item.valid?(@items)
|
||||
|
||||
object = item.to_object
|
||||
object.save
|
||||
raise ActiveRecord::Rollback unless object.errors.empty?
|
||||
|
@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'stripe/helper'
|
||||
require 'pay_zen/helper'
|
||||
|
||||
# create remote items on currently active payment gateway
|
||||
class PaymentGatewayService
|
||||
def initialize
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubscriptionExtensionAfterReservation
|
||||
attr_accessor :user, :reservation
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ReservationSlotSubscriptionValidator < ActiveModel::Validator
|
||||
def validate(record)
|
||||
record.slots.each do |s|
|
||||
unless s.availability.plan_ids.empty?
|
||||
if record.user.subscribed_plan && s.availability.plan_ids.include?(record.user.subscribed_plan.id)
|
||||
elsif s.availability.plan_ids.include?(record.plan_id)
|
||||
else
|
||||
# TODO, this validation requires to check if the operator is privileged.
|
||||
# Meanwhile we can't check this, we disable the validation
|
||||
record.errors[:slots] << 'slot is restrict for subscriptions'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
298
test/integration/reservations/restricted_test.rb
Normal file
298
test/integration/reservations/restricted_test.rb
Normal file
@ -0,0 +1,298 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module Reservations; end
|
||||
|
||||
class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = User.with_role(:admin).first
|
||||
@pdurand = User.find(3) # user with subscription to plan 2
|
||||
@jdupont = User.find(2) # user without subscription
|
||||
end
|
||||
|
||||
test 'reserve slot restricted to subscribers with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
availabilities_count = Availability.count
|
||||
invoices_count = Invoice.count
|
||||
slots_count = Slot.count
|
||||
|
||||
# first, create the restricted availability
|
||||
date = 4.days.from_now.utc.change(hour: 8, min: 0, sec: 0)
|
||||
post '/api/availabilities',
|
||||
params: {
|
||||
availability: {
|
||||
start_at: date.iso8601,
|
||||
end_at: (date + 6.hours).iso8601,
|
||||
available_type: 'machines',
|
||||
slot_duration: 60,
|
||||
machine_ids: [2],
|
||||
occurrences: [
|
||||
{ start_at: date.iso8601, end_at: (date + 6.hours).iso8601 }
|
||||
],
|
||||
plan_ids: [2]
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the id
|
||||
availability = json_response(response.body)
|
||||
assert_not_nil availability[:id], 'availability ID was unexpectedly nil'
|
||||
|
||||
assert_equal availabilities_count + 1, Availability.count
|
||||
|
||||
# change connected user
|
||||
login_as(@pdurand, scope: :user)
|
||||
|
||||
# book a reservation
|
||||
VCR.use_cassette('reservations_create_for_restricted_slot_success') do
|
||||
post '/api/stripe/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
items: [
|
||||
{
|
||||
reservation: {
|
||||
reservable_id: 2,
|
||||
reservable_type: 'Machine',
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability[:start_at],
|
||||
end_at: (DateTime.parse(availability[:start_at]) + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability[:id]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoices_count + 1, Invoice.count
|
||||
assert_equal slots_count + 1, Slot.count
|
||||
end
|
||||
|
||||
test 'unable to reserve slot restricted to subscribers' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
availabilities_count = Availability.count
|
||||
invoices_count = Invoice.count
|
||||
slots_count = Slot.count
|
||||
|
||||
# first, create the restricted availability
|
||||
date = 4.days.from_now.utc.change(hour: 8, min: 0, sec: 0)
|
||||
post '/api/availabilities',
|
||||
params: {
|
||||
availability: {
|
||||
start_at: date.iso8601,
|
||||
end_at: (date + 6.hours).iso8601,
|
||||
available_type: 'machines',
|
||||
slot_duration: 60,
|
||||
machine_ids: [2],
|
||||
occurrences: [
|
||||
{ start_at: date.iso8601, end_at: (date + 6.hours).iso8601 }
|
||||
],
|
||||
plan_ids: [2]
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the id
|
||||
availability = json_response(response.body)
|
||||
assert_not_nil availability[:id], 'availability ID was unexpectedly nil'
|
||||
|
||||
assert_equal availabilities_count + 1, Availability.count
|
||||
|
||||
# change connected user
|
||||
login_as(@jdupont, scope: :user)
|
||||
|
||||
# book a reservation
|
||||
VCR.use_cassette('reservations_create_for_restricted_slot_fails') do
|
||||
post '/api/stripe/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
items: [
|
||||
{
|
||||
reservation: {
|
||||
reservable_id: 2,
|
||||
reservable_type: 'Machine',
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability[:start_at],
|
||||
end_at: (DateTime.parse(availability[:start_at]) + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability[:id]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
assert_equal 422, response.status
|
||||
assert_match /slot is restricted for subscribers/, response.body
|
||||
|
||||
assert_equal reservations_count, Reservation.count
|
||||
assert_equal invoices_count, Invoice.count
|
||||
assert_equal slots_count, Slot.count
|
||||
end
|
||||
|
||||
test 'admin force reservation of a slot restricted to subscribers' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
availabilities_count = Availability.count
|
||||
invoices_count = Invoice.count
|
||||
slots_count = Slot.count
|
||||
|
||||
# first, create the restricted availability
|
||||
date = 4.days.from_now.utc.change(hour: 8, min: 0, sec: 0)
|
||||
post '/api/availabilities',
|
||||
params: {
|
||||
availability: {
|
||||
start_at: date.iso8601,
|
||||
end_at: (date + 6.hours).iso8601,
|
||||
available_type: 'machines',
|
||||
slot_duration: 60,
|
||||
machine_ids: [2],
|
||||
occurrences: [
|
||||
{ start_at: date.iso8601, end_at: (date + 6.hours).iso8601 }
|
||||
],
|
||||
plan_ids: [2]
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the id
|
||||
availability = json_response(response.body)
|
||||
assert_not_nil availability[:id], 'availability ID was unexpectedly nil'
|
||||
|
||||
assert_equal availabilities_count + 1, Availability.count
|
||||
|
||||
# book a reservation
|
||||
VCR.use_cassette('reservations_create_for_restricted_slot_fails') do
|
||||
post '/api/local_payment/confirm_payment',
|
||||
params: {
|
||||
customer_id: @jdupont.id,
|
||||
items: [
|
||||
{
|
||||
reservation: {
|
||||
reservable_id: 2,
|
||||
reservable_type: 'Machine',
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability[:start_at],
|
||||
end_at: (DateTime.parse(availability[:start_at]) + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability[:id]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the result
|
||||
result = json_response(response.body)
|
||||
assert_not_nil result[:id], 'invoice ID was unexpectedly nil'
|
||||
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoices_count + 1, Invoice.count
|
||||
assert_equal slots_count + 1, Slot.count
|
||||
end
|
||||
|
||||
test 'book a slot restricted to subscribers and a subscription at the same time' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
availabilities_count = Availability.count
|
||||
invoices_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
slots_count = Slot.count
|
||||
subscriptions_count = Subscription.count
|
||||
|
||||
# first, create the restricted availability
|
||||
date = 4.days.from_now.utc.change(hour: 8, min: 0, sec: 0)
|
||||
post '/api/availabilities',
|
||||
params: {
|
||||
availability: {
|
||||
start_at: date.iso8601,
|
||||
end_at: (date + 6.hours).iso8601,
|
||||
available_type: 'machines',
|
||||
slot_duration: 60,
|
||||
machine_ids: [2],
|
||||
occurrences: [
|
||||
{ start_at: date.iso8601, end_at: (date + 6.hours).iso8601 }
|
||||
],
|
||||
plan_ids: [2]
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the id
|
||||
availability = json_response(response.body)
|
||||
assert_not_nil availability[:id], 'availability ID was unexpectedly nil'
|
||||
|
||||
assert_equal availabilities_count + 1, Availability.count
|
||||
|
||||
# change connected user
|
||||
login_as(@jdupont, scope: :user)
|
||||
|
||||
# book a reservation
|
||||
VCR.use_cassette('reservations_create_for_restricted_slot_fails') do
|
||||
post '/api/stripe/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
items: [
|
||||
{
|
||||
reservation: {
|
||||
reservable_id: 2,
|
||||
reservable_type: 'Machine',
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability[:start_at],
|
||||
end_at: (DateTime.parse(availability[:start_at]) + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability[:id]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
subscription: {
|
||||
plan_id: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
assert_equal 201, response.status
|
||||
|
||||
# Check the result
|
||||
result = json_response(response.body)
|
||||
assert_not_nil result[:id], 'invoice ID was unexpectedly nil'
|
||||
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoices_count + 1, Invoice.count
|
||||
assert_equal slots_count + 1, Slot.count
|
||||
assert_equal subscriptions_count + 1, Subscription.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user