From 7c7ec0aa4ceac6fec430506e412c495c30813356 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Fri, 4 Nov 2022 12:13:53 +0100 Subject: [PATCH] (feature) admin can buy product for himself --- app/controllers/api/cart_controller.rb | 2 +- app/frontend/src/javascript/api/cart.ts | 2 +- .../javascript/components/cart/store-cart.tsx | 17 +++++++++++++++-- .../src/javascript/controllers/machines.js.erb | 2 +- app/frontend/src/javascript/lib/user.ts | 4 +--- app/policies/cart_context.rb | 15 +++++++++++++++ app/policies/cart_policy.rb | 2 +- config/locales/app.public.en.yml | 1 + 8 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 app/policies/cart_context.rb diff --git a/app/controllers/api/cart_controller.rb b/app/controllers/api/cart_controller.rb index f338d99b7..5b150a70e 100644 --- a/app/controllers/api/cart_controller.rb +++ b/app/controllers/api/cart_controller.rb @@ -32,7 +32,7 @@ class API::CartController < API::ApiController end def set_offer - authorize @current_order, policy_class: CartPolicy + authorize CartContext.new(params[:customer_id], cart_params[:is_offered]) @order = Cart::SetOfferService.new.call(@current_order, orderable, cart_params[:is_offered]) render 'api/orders/show' end diff --git a/app/frontend/src/javascript/api/cart.ts b/app/frontend/src/javascript/api/cart.ts index a863de8ee..1f5627cd9 100644 --- a/app/frontend/src/javascript/api/cart.ts +++ b/app/frontend/src/javascript/api/cart.ts @@ -24,7 +24,7 @@ export default class CartAPI { } static async setOffer (order: Order, orderableId: number, isOffered: boolean): Promise { - const res: AxiosResponse = await apiClient.put('/api/cart/set_offer', { order_token: order.token, orderable_id: orderableId, is_offered: isOffered }); + const res: AxiosResponse = await apiClient.put('/api/cart/set_offer', { order_token: order.token, orderable_id: orderableId, is_offered: isOffered, customer_id: order.user?.id }); return res?.data; } diff --git a/app/frontend/src/javascript/components/cart/store-cart.tsx b/app/frontend/src/javascript/components/cart/store-cart.tsx index 308c81366..bae50718d 100644 --- a/app/frontend/src/javascript/components/cart/store-cart.tsx +++ b/app/frontend/src/javascript/components/cart/store-cart.tsx @@ -181,7 +181,14 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser, * Change cart's customer by admin/manger */ const handleChangeMember = (user: User): void => { - setCart({ ...cart, user: { id: user.id, role: 'member' } }); + // if the selected user is the operator, he cannot offer products to himself + if (user.id === currentUser.id) { + Promise.all(cart.order_items_attributes.filter(item => item.is_offered).map(item => { + return CartAPI.setOffer(cart, item.orderable_id, false); + })).then((data) => setCart({ ...data[data.length - 1], user: { id: user.id, role: user.role } })); + } else { + setCart({ ...cart, user: { id: user.id, role: 'member' } }); + } }; /** @@ -205,7 +212,13 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser, return (checked: boolean) => { CartAPI.setOffer(cart, item.orderable_id, checked).then(data => { setCart(data); - }).catch(onError); + }).catch(e => { + if (e.match(/code 403/)) { + onError(t('app.public.store_cart.errors.unauthorized_offering_product')); + } else { + onError(e); + } + }); }; }; diff --git a/app/frontend/src/javascript/controllers/machines.js.erb b/app/frontend/src/javascript/controllers/machines.js.erb index 480fc7245..2783f7c98 100644 --- a/app/frontend/src/javascript/controllers/machines.js.erb +++ b/app/frontend/src/javascript/controllers/machines.js.erb @@ -424,7 +424,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$tran // the moment when the slot selection changed for the last time, used to trigger changes in the cart $scope.selectionTime = null; - // the last clicked event in the calender + // the last clicked event in the calendar $scope.selectedEvent = null; // the application global settings diff --git a/app/frontend/src/javascript/lib/user.ts b/app/frontend/src/javascript/lib/user.ts index eaa54b5ea..a7fd30f69 100644 --- a/app/frontend/src/javascript/lib/user.ts +++ b/app/frontend/src/javascript/lib/user.ts @@ -13,9 +13,7 @@ export default class UserLib { * Check if the current user has privileged access for resources concerning the provided customer */ isPrivileged = (customer: User): boolean => { - if (this.user?.role === 'admin') return true; - - if (this.user?.role === 'manager') { + if (this.user?.role === 'admin' || this.user?.role === 'manager') { return (this.user?.id !== customer.id); } diff --git a/app/policies/cart_context.rb b/app/policies/cart_context.rb new file mode 100644 index 000000000..f962eb492 --- /dev/null +++ b/app/policies/cart_context.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Pundit Additional context for authorizing a product offering +class CartContext + attr_reader :customer_id, :is_offered + + def initialize(customer_id, is_offered) + @customer_id = customer_id + @is_offered = is_offered + end + + def policy_class + CartPolicy + end +end diff --git a/app/policies/cart_policy.rb b/app/policies/cart_policy.rb index bfdc32574..b63dcbcac 100644 --- a/app/policies/cart_policy.rb +++ b/app/policies/cart_policy.rb @@ -15,6 +15,6 @@ class CartPolicy < ApplicationPolicy end def set_offer? - user.privileged? + !record.is_offered || (user.privileged? && record.customer_id != user.id) end end diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index d494c1f82..b3f2d1c2f 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -451,6 +451,7 @@ en: stock_limit_QUANTITY: "Only {QUANTITY} {QUANTITY, plural, =1{unit} other{units}} left in stock, please adjust the quantity of items." quantity_min_QUANTITY: "Minimum number of product was changed to {QUANTITY}, please adjust the quantity of items." price_changed_PRICE: "The product price was modified to {PRICE}" + unauthorized_offering_product: "You can't offer anything to yourself" orders_dashboard: heading: "My orders" sort: