1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

(feat) create cart items from the api

This commit is contained in:
Sylvain 2022-12-30 15:29:52 +01:00
parent 42d830b4f8
commit 48a44ff4f3
11 changed files with 311 additions and 23 deletions

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# API Controller for manage user's cart
# API Controller to manage user's cart
class API::CartController < API::ApiController
include API::OrderConcern
@ -13,6 +13,17 @@ class API::CartController < API::ApiController
render 'api/orders/show'
end
def create_item
authorize @current_order, policy_class: CartPolicy
service = Cart::CreateCartItemService.new(@current_order)
@item = service.create(params)
if @item.save({ context: @current_order.order_items })
render 'api/orders/item', status: :created
else
render json: @item.errors.full_messages, status: :unprocessable_entity
end
end
def add_item
authorize @current_order, policy_class: CartPolicy
@order = Cart::AddItemService.new.call(@current_order, orderable, cart_params[:quantity])

View File

@ -19,7 +19,7 @@ class CartItem::MachineReservation < CartItem::Reservation
'machine'
end
def valid?(all_items)
def valid?(all_items = [])
cart_item_reservation_slots.each do |slot|
same_hour_slots = SlotsReservation.joins(:reservation).where(
reservations: { reservable: reservable },

View File

@ -3,6 +3,7 @@
# A prepaid-pack added to the shopping cart
class CartItem::PrepaidPack < CartItem::BaseItem
belongs_to :prepaid_pack
belongs_to :customer_profile, class_name: 'InvoicingProfile'
def customer
customer_profile.user
@ -36,11 +37,11 @@ class CartItem::PrepaidPack < CartItem::BaseItem
def valid?(_all_items)
if pack.disabled
@errors[:item] = I18n.t('cart_item_validation.pack')
errors.add(:prepaid_pack, I18n.t('cart_item_validation.pack'))
return false
end
if pack.group_id != customer.group_id
@errors[:group] = "pack is reserved for members of group #{pack.group.name}"
errors.add(:group, "pack is reserved for members of group #{pack.group.name}")
return false
end
true

View File

@ -52,7 +52,7 @@ class CartItem::Reservation < CartItem::BaseItem
reservable&.name
end
def valid?(all_items)
def valid?(all_items = [])
pending_subscription = all_items.find { |i| i.is_a?(CartItem::Subscription) }
reservation_deadline_minutes = Setting.get('reservation_deadline').to_i

View File

@ -6,7 +6,7 @@ class CartPolicy < ApplicationPolicy
!Setting.get('store_hidden') || user&.privileged?
end
%w[add_item remove_item set_quantity refresh_item validate].each do |action|
%w[add_item remove_item set_quantity refresh_item validate create_item].each do |action|
define_method "#{action}?" do
return user.privileged? || (record.statistic_profile_id == user.statistic_profile.id) if user

View File

@ -8,8 +8,8 @@ class Cart::AddItemService
item = case orderable
when Product
add_product(order, orderable, quantity)
when Slot
add_slot(order, orderable)
when /^CartItem::/
add_cart_item(order, orderable)
else
raise Cart::UnknownItemError
end
@ -42,11 +42,7 @@ class Cart::AddItemService
item
end
def add_slot(order, orderable)
item = order.order_items.find_by(orderable: orderable)
item = order.order_items.new(quantity: 1, orderable: orderable, amount: orderable.amount || 0) if item.nil?
item
def add_cart_item(order, orderable)
order.order_items.new(quantity: 1, orderable: orderable, amount: orderable.price.amount || 0)
end
end

View File

@ -0,0 +1,110 @@
# frozen_string_literal: true
# Provides methods to create new cart items, based on an existing Order
class Cart::CreateCartItemService
def initialize(order)
@order = order
@customer = order.user
@operator = order.user.privileged? ? order.operator_profile.user : order.user
end
def create(item)
key = item.keys.filter { |k| %w[subscription reservation prepaid_pack free_extension].include?(k) }.first
case key
when 'subscription', :subscription
subscription = create_subscription(item.require(:subscription).permit!)
update_reservations(subscription)
subscription
when 'reservation', :reservation
create_reservation(item.require(:reservation).permit!)
when 'prepaid_pack', :prepaid_pack
create_prepaid_pack(item.require(:prepaid_pack).permit!)
when 'free_extension', :free_extension
create_free_extension(item.require(:free_extension).permit!)
else
raise NotImplementedError, "unknown item type #{item.keys.first}"
end
end
private
def create_subscription(cart_item)
CartItem::Subscription.new(
plan: Plan.find(cart_item[:plan_id]),
customer_profile: @customer.invoicing_profile,
start_at: cart_item[:start_at]
)
end
def update_reservations(new_subscription)
@order.order_items
.where(orderable_type: %w[CartItem::MachineReservation CartItem::SpaceReservation CartItem::TrainingReservation])
.find_each do |reserv|
reserv.update(plan: new_subscription.plan, new_subscription: true)
end
end
def create_reservation(cart_item)
plan_info = subscription_info
reservable = cart_item[:reservable_type]&.constantize&.find(cart_item[:reservable_id])
case reservable
when Machine
CartItem::MachineReservation.new(customer_profile: @customer.invoicing_profile,
operator_profile: @operator.invoicing_profile,
reservable: reservable,
cart_item_reservation_slots_attributes: cart_item[:slots_reservations_attributes],
plan: plan_info[:subscription]&.plan,
new_subscription: plan_info[:new_subscription])
when Training
CartItem::TrainingReservation.new(customer_profile: @customer.invoicing_profile,
operator_profile: @operator.invoicing_profile,
reservable: reservable,
cart_item_reservation_slots_attributes: cart_item[:slots_reservations_attributes],
plan: plan_info[:subscription]&.plan,
new_subscription: plan_info[:new_subscription])
when Event
CartItem::EventReservation.new(customer_profile: @customer.invoicing_profile,
operator_profile: @operator.invoicing_profile,
event: reservable,
cart_item_reservation_slots_attributes: cart_item[:slots_reservations_attributes],
normal_tickets: cart_item[:nb_reserve_places],
cart_item_event_reservation_tickets_attributes: cart_item[:tickets_attributes] || {})
when Space
CartItem::SpaceReservation.new(customer_profile: @customer.invoicing_profile,
operator_profile: @operator.invoicing_profile,
reservable: reservable,
cart_item_reservation_slots_attributes: cart_item[:slots_reservations_attributes],
plan: plan_info[:subscription]&.plan,
new_subscription: plan_info[:new_subscription])
else
raise NotImplementedError, "unknown reservable type #{reservable}"
end
end
def create_prepaid_pack(cart_item)
CartItem::PrepaidPack.new(
prepaid_pack: PrepaidPack.find(cart_item[:id]),
customer_profile: @customer.invoicing_profile
)
end
def create_free_extension(cart_item)
plan_info = subscription_info
CartItem::FreeExtension.new(
customer_profile: @customer.invoicing_profile,
subscription: plan_info[:subscription],
new_expiration_date: cart_item[:end_at]
)
end
def subscription_info
cart_subscription = @order.order_items.find_by(orderable_type: 'CartItem::Subscription')
if cart_subscription
{ subscription: cart_subscription, new_subscription: true }
elsif @customer.subscribed_plan
{ subscription: @customer.subscription, new_subscription: false } unless @customer.subscription.expired_at < DateTime.current
else
{ subscription: nil, new_subscription: false }
end
end
end

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
json.id @item.id
json.type @item.class.name

View File

@ -168,6 +168,7 @@ Rails.application.routes.draw do
put 'set_offer', on: :collection
put 'refresh_item', on: :collection
post 'validate', on: :collection
post 'create_item', on: :collection
end
resources :checkout, only: %i[] do
post 'payment', on: :collection

View File

@ -19,8 +19,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
enable_extension "unaccent"
create_table "abuses", id: :serial, force: :cascade do |t|
t.string "signaled_type"
t.integer "signaled_id"
t.string "signaled_type"
t.string "first_name"
t.string "last_name"
t.string "email"
@ -68,8 +68,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
t.string "locality"
t.string "country"
t.string "postal_code"
t.string "placeable_type"
t.integer "placeable_id"
t.string "placeable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
@ -93,8 +93,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
end
create_table "assets", id: :serial, force: :cascade do |t|
t.string "viewable_type"
t.integer "viewable_id"
t.string "viewable_type"
t.string "attachment"
t.string "type"
t.datetime "created_at"
@ -281,8 +281,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
end
create_table "credits", id: :serial, force: :cascade do |t|
t.string "creditable_type"
t.integer "creditable_id"
t.string "creditable_type"
t.integer "plan_id"
t.integer "hours"
t.datetime "created_at"
@ -524,15 +524,15 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
create_table "notifications", id: :serial, force: :cascade do |t|
t.integer "receiver_id"
t.string "attached_object_type"
t.integer "attached_object_id"
t.string "attached_object_type"
t.integer "notification_type_id"
t.boolean "is_read", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "receiver_type"
t.boolean "is_send", default: false
t.jsonb "meta_data", default: "{}"
t.jsonb "meta_data", default: {}
t.index ["notification_type_id"], name: "index_notifications_on_notification_type_id"
t.index ["receiver_id"], name: "index_notifications_on_receiver_id"
end
@ -772,8 +772,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
create_table "prices", id: :serial, force: :cascade do |t|
t.integer "group_id"
t.integer "plan_id"
t.string "priceable_type"
t.integer "priceable_id"
t.string "priceable_type"
t.integer "amount"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -976,8 +976,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
t.text "message"
t.datetime "created_at"
t.datetime "updated_at"
t.string "reservable_type"
t.integer "reservable_id"
t.string "reservable_type"
t.integer "nb_reserve_places"
t.integer "statistic_profile_id"
t.index ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id"
@ -986,8 +986,8 @@ ActiveRecord::Schema.define(version: 2023_01_31_104958) do
create_table "roles", id: :serial, force: :cascade do |t|
t.string "name"
t.string "resource_type"
t.integer "resource_id"
t.string "resource_type"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"

View File

@ -0,0 +1,165 @@
# frozen_string_literal: true
require 'test_helper'
class CreateCartItemTest < ActionDispatch::IntegrationTest
setup do
@user = User.find_by(username: 'pdurand')
login_as(@user, scope: :user)
@order = Cart::FindOrCreateService.new(@user).call(nil)
end
test 'create a subscription' do
plan = Plan.first
post '/api/cart/create_item',
params: {
order_token: @order.token,
subscription: {
plan_id: plan.id
}
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::Subscription
assert_equal plan, resource.plan
end
test 'create a machine reservation' do
machine = Machine.first
post '/api/cart/create_item',
params: {
order_token: @order.token,
reservation: {
reservable_id: machine.id,
reservable_type: 'Machine',
slots_reservations_attributes: [
{ slot_id: machine.availabilities.last&.slots&.last&.id }
]
}
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::MachineReservation
assert_equal machine, resource.reservable
end
test 'create a space reservation' do
space = Space.first
post '/api/cart/create_item',
params: {
order_token: @order.token,
reservation: {
reservable_id: space.id,
reservable_type: 'Space',
slots_reservations_attributes: [
{ slot_id: space.availabilities.last&.slots&.last&.id }
]
}
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::SpaceReservation
assert_equal space, resource.reservable
end
test 'create a training reservation' do
training = Training.first
post '/api/cart/create_item',
params: {
order_token: @order.token,
reservation: {
reservable_id: training.id,
reservable_type: 'Training',
slots_reservations_attributes: [
{ slot_id: training.availabilities.last&.slots&.last&.id }
]
}
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::TrainingReservation
assert_equal training, resource.reservable
end
test 'create an event reservation' do
event = Event.find(4)
post '/api/cart/create_item',
params: {
order_token: @order.token,
reservation: {
reservable_id: event.id,
reservable_type: 'Event',
slots_reservations_attributes: [
{ slot_id: event.availability.slots.last&.id }
],
nb_reserve_places: 2
}
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::EventReservation
assert_equal event, resource.event
assert_equal 2, resource.normal_tickets
end
test 'create a prepaid-pack' do
pack = PrepaidPack.first
post '/api/cart/create_item',
params: {
order_token: @order.token,
prepaid_pack: { id: pack.id }
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::PrepaidPack
assert_equal pack.id, resource.prepaid_pack_id
end
test 'create a free-extension for a subscription' do
subscription = @user.subscription
post '/api/cart/create_item',
params: {
order_token: @order.token,
free_extension: { end_at: subscription.expiration_date + 1.month }
}
# general assertions
assert_equal 201, response.status
assert_equal Mime[:json], response.content_type
# Check the cart item was created correctly
res = json_response(response.body)
resource = res[:type].classify.constantize.find(res[:id])
assert resource.is_a? CartItem::FreeExtension
assert_equal subscription.id, resource.subscription_id
end
end