API v1 group_order_articles endpoints
This commit is contained in:
parent
ed9192c47f
commit
e1d50e5b9c
11 changed files with 646 additions and 10 deletions
108
app/controllers/api/v1/user/group_order_articles_controller.rb
Normal file
108
app/controllers/api/v1/user/group_order_articles_controller.rb
Normal file
|
@ -0,0 +1,108 @@
|
|||
class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController
|
||||
include Concerns::CollectionScope
|
||||
|
||||
before_action ->{ doorkeeper_authorize! 'group_orders:user' }
|
||||
|
||||
before_action :require_ordergroup
|
||||
before_action :require_minimum_balance, only: [:create, :update] # destroy is ok
|
||||
before_action :require_enough_apples, only: [:create, :update] # destroy is ok
|
||||
# @todo allow decreasing amounts when minimum balance isn't met
|
||||
|
||||
def index
|
||||
render_collection search_scope
|
||||
end
|
||||
|
||||
def show
|
||||
goa = scope.find(params.require(:id))
|
||||
render_goa_with_oa(goa)
|
||||
end
|
||||
|
||||
def create
|
||||
goa = nil
|
||||
GroupOrderArticle.transaction do
|
||||
oa = order_articles_scope_for_create.find(create_params.require(:order_article_id))
|
||||
go = current_ordergroup.group_orders.find_or_create_by!(order_id: oa.order_id)
|
||||
goa = go.group_order_articles.create!(order_article: oa)
|
||||
goa.update_quantities((create_params[:quantity] || 0).to_i, (create_params[:tolerance] || 0).to_i)
|
||||
oa.update_results!
|
||||
go.update_price!
|
||||
go.update_attributes! updated_by: current_user
|
||||
end
|
||||
render_goa_with_oa(goa)
|
||||
end
|
||||
|
||||
def update
|
||||
goa = nil
|
||||
GroupOrderArticle.transaction do
|
||||
goa = scope_for_update.includes(:group_order_article_quantities).find(params.require(:id))
|
||||
goa.update_quantities((update_params[:quantity] || goa.quantity).to_i, (update_params[:tolerance] || goa.tolerance).to_i)
|
||||
goa.order_article.update_results!
|
||||
goa.group_order.update_price!
|
||||
goa.group_order.update_attributes! updated_by: current_user
|
||||
end
|
||||
render_goa_with_oa(goa)
|
||||
end
|
||||
|
||||
def destroy
|
||||
goa = nil
|
||||
GroupOrderArticle.transaction do
|
||||
goa = scope_for_update.find(params.require(:id))
|
||||
goa.destroy!
|
||||
goa.order_article.update_results!
|
||||
goa.group_order.update_price!
|
||||
goa.group_order.update_attributes! updated_by: current_user
|
||||
end
|
||||
render_goa_with_oa(nil, goa.order_article)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def max_per_page
|
||||
nil
|
||||
end
|
||||
|
||||
def scope
|
||||
GroupOrderArticle.
|
||||
joins(:group_order).
|
||||
includes(order_article: :article, group_order: :order).
|
||||
where(group_orders: { ordergroup_id: current_ordergroup.id })
|
||||
end
|
||||
|
||||
def scope_for_update
|
||||
scope.references(order_article: { group_order: :order }).merge(Order.open)
|
||||
end
|
||||
|
||||
def order_articles_scope_for_create
|
||||
OrderArticle.joins(:order).merge(Order.open)
|
||||
end
|
||||
|
||||
def create_params
|
||||
params.require(:group_order_article).permit(:order_article_id, :quantity, :tolerance)
|
||||
end
|
||||
|
||||
def update_params
|
||||
params.require(:group_order_article).permit(:quantity, :tolerance)
|
||||
end
|
||||
|
||||
def require_minimum_balance
|
||||
minimum_balance = FoodsoftConfig[:minimum_balance] or return
|
||||
if current_ordergroup.account_balance < minimum_balance
|
||||
raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance))
|
||||
end
|
||||
end
|
||||
|
||||
def require_enough_apples
|
||||
if current_ordergroup.not_enough_apples?
|
||||
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||
raise Api::Errors::PermissionRequired.new(s)
|
||||
end
|
||||
end
|
||||
|
||||
def render_goa_with_oa(goa, oa = goa.order_article)
|
||||
data = {}
|
||||
data[:group_order_article] = GroupOrderArticleSerializer.new(goa) if goa
|
||||
data[:order_article] = OrderArticleSerializer.new(oa) if oa
|
||||
|
||||
render json: data, root: nil
|
||||
end
|
||||
end
|
|
@ -21,6 +21,14 @@ class GroupOrder < ApplicationRecord
|
|||
|
||||
scope :ordered, -> { includes(:ordergroup).order('groups.name') }
|
||||
|
||||
def self.ransackable_attributes(auth_object = nil)
|
||||
%w(id price)
|
||||
end
|
||||
|
||||
def self.ransackable_associations(auth_object = nil)
|
||||
%w(order group_order_articles)
|
||||
end
|
||||
|
||||
# Generate some data for the javascript methods in ordering view
|
||||
def load_data
|
||||
data = {}
|
||||
|
|
|
@ -5,16 +5,25 @@ class GroupOrderArticle < ApplicationRecord
|
|||
|
||||
belongs_to :group_order
|
||||
belongs_to :order_article
|
||||
has_many :group_order_article_quantities, :dependent => :destroy
|
||||
has_many :group_order_article_quantities, dependent: :destroy
|
||||
|
||||
validates_presence_of :group_order, :order_article
|
||||
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
|
||||
validate :check_order_not_closed # don't allow changes to closed (aka settled) orders
|
||||
validates :quantity, :tolerance, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||
|
||||
scope :ordered, -> { includes(:group_order => :ordergroup).order('groups.name') }
|
||||
|
||||
localize_input_of :result
|
||||
|
||||
def self.ransackable_attributes(auth_object = nil)
|
||||
%w(id quantity tolerance result)
|
||||
end
|
||||
|
||||
def self.ransackable_associations(auth_object = nil)
|
||||
%w(order_article group_order)
|
||||
end
|
||||
|
||||
# Setter used in group_order_article#new
|
||||
# We have to create an group_order, if the ordergroup wasn't involved in the order yet
|
||||
def ordergroup_id=(id)
|
||||
|
@ -86,7 +95,7 @@ class GroupOrderArticle < ApplicationRecord
|
|||
|
||||
# Check if something went terribly wrong and quantites have not been adjusted as desired.
|
||||
if (self.quantity != quantity || self.tolerance != tolerance)
|
||||
raise 'Invalid state: unable to update GroupOrderArticle/-Quantities to desired quantities!'
|
||||
raise ActiveRecord::RecordNotSaved.new('Unable to update GroupOrderArticle/-Quantities to desired quantities!', self)
|
||||
end
|
||||
|
||||
# Remove zero-only items.
|
||||
|
|
|
@ -30,9 +30,9 @@ class Order < ApplicationRecord
|
|||
|
||||
# Finders
|
||||
scope :started, -> { where('starts <= ?', Time.now) }
|
||||
scope :closed, -> { where(state: 'closed').order('ends DESC') }
|
||||
scope :stockit, -> { where(supplier_id: nil).order('ends DESC') }
|
||||
scope :recent, -> { order('starts DESC').limit(10) }
|
||||
scope :closed, -> { where(state: 'closed').order(ends: :desc) }
|
||||
scope :stockit, -> { where(supplier_id: nil).order(ends: :desc) }
|
||||
scope :recent, -> { order(starts: :desc).limit(10) }
|
||||
scope :stock_group_order, -> { group_orders.where(ordergroup_id: nil).first }
|
||||
scope :with_invoice, -> { where.not(invoice: nil) }
|
||||
|
||||
|
@ -42,9 +42,9 @@ class Order < ApplicationRecord
|
|||
# So orders can
|
||||
# 1. ...only transition in one direction (e.g. an order that has been `finished` currently cannot be reopened)
|
||||
# 2. ...be set to `closed` when having the `finished` state. (`received` is optional)
|
||||
scope :open, -> { where(state: 'open').order('ends DESC') }
|
||||
scope :finished, -> { where(state: %w[finished received closed]).order('ends DESC') }
|
||||
scope :finished_not_closed, -> { where(state: %w[finished received]).order('ends DESC') }
|
||||
scope :open, -> { where(state: 'open').order(ends: :desc) }
|
||||
scope :finished, -> { where(state: %w[finished received closed]).order(ends: :desc) }
|
||||
scope :finished_not_closed, -> { where(state: %w[finished received]).order(ends: :desc) }
|
||||
|
||||
# Allow separate inputs for date and time
|
||||
# with workaround for https://github.com/einzige/date_time_attribute/issues/14
|
||||
|
|
8
app/serializers/group_order_article_serializer.rb
Normal file
8
app/serializers/group_order_article_serializer.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class GroupOrderArticleSerializer < ActiveModel::Serializer
|
||||
attributes :id, :order_article_id
|
||||
attributes :quantity, :tolerance, :result, :total_price
|
||||
|
||||
def total_price
|
||||
object.total_price.to_f
|
||||
end
|
||||
end
|
|
@ -49,7 +49,7 @@ Doorkeeper.configure do
|
|||
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
|
||||
|
||||
# default is a collection of read-only scopes
|
||||
default_scopes 'config:user', 'finance:user', 'user:read', 'orders:read'
|
||||
default_scopes 'config:user', 'finance:user', 'user:read', 'orders:read', 'group_orders:user'
|
||||
|
||||
optional_scopes 'config:read', 'config:write',
|
||||
'finance:read', 'finance:write',
|
||||
|
|
|
@ -418,6 +418,7 @@ en:
|
|||
error_denied_sign_in: sign in as another user
|
||||
error_feature_disabled: This feature is currently disabled.
|
||||
error_members_only: This action is only available to members of the group!
|
||||
error_minimum_balance: Sorry, your account balance is below the minimum of %{min}.
|
||||
error_token: Access denied (invalid token)!
|
||||
article_categories:
|
||||
create:
|
||||
|
|
|
@ -266,6 +266,7 @@ Rails.application.routes.draw do
|
|||
namespace :user do
|
||||
root to: 'users#show'
|
||||
resources :financial_transactions, only: [:index, :show]
|
||||
resources :group_order_articles
|
||||
end
|
||||
|
||||
resources :financial_transaction_classes, only: [:index, :show]
|
||||
|
@ -273,6 +274,7 @@ Rails.application.routes.draw do
|
|||
resources :financial_transactions, only: [:index, :show]
|
||||
resources :orders, only: [:index, :show]
|
||||
resources :order_articles, only: [:index, :show]
|
||||
resources :group_order_articles
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -120,6 +120,177 @@ paths:
|
|||
security:
|
||||
- foodsoft_auth: ['finance:user']
|
||||
|
||||
/user/group_order_articles:
|
||||
get:
|
||||
summary: group order articles
|
||||
tags:
|
||||
- 1. User
|
||||
- 2. Order
|
||||
parameters:
|
||||
- $ref: '#/parameters/page'
|
||||
- $ref: '#/parameters/per_page'
|
||||
- $ref: '#/parameters/q_ordered'
|
||||
responses:
|
||||
200:
|
||||
description: success
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
group_order_articles:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/GroupOrderArticle'
|
||||
meta:
|
||||
$ref: '#/definitions/Meta'
|
||||
401:
|
||||
description: not logged-in
|
||||
schema:
|
||||
$ref: '#/definitions/Error401'
|
||||
403:
|
||||
description: user has no ordergroup or missing scope
|
||||
schema:
|
||||
$ref: '#/definitions/Error403'
|
||||
security:
|
||||
- foodsoft_auth: ['group_orders:user']
|
||||
post:
|
||||
summary: create new group order article
|
||||
tags:
|
||||
- 1. User
|
||||
- 2. Order
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: group order article to create
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/GroupOrderArticleForCreate'
|
||||
responses:
|
||||
200:
|
||||
description: success
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
group_order_article:
|
||||
$ref: '#/definitions/GroupOrderArticle'
|
||||
order_article:
|
||||
$ref: '#/definitions/OrderArticle'
|
||||
401:
|
||||
description: not logged-in
|
||||
schema:
|
||||
$ref: '#/definitions/Error401'
|
||||
403:
|
||||
description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope
|
||||
schema:
|
||||
$ref: '#/definitions/Error403'
|
||||
404:
|
||||
description: order article not found in open orders
|
||||
schema:
|
||||
$ref: '#/definitions/Error404'
|
||||
422:
|
||||
description: invalid parameter value or group order article already exists
|
||||
schema:
|
||||
$ref: '#/definitions/Error422'
|
||||
/user/group_order_articles/{id}:
|
||||
parameters:
|
||||
- $ref: '#/parameters/idInUrl'
|
||||
get:
|
||||
summary: find group order article by id
|
||||
tags:
|
||||
- 1. User
|
||||
- 2. Order
|
||||
responses:
|
||||
200:
|
||||
description: success
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
group_order_article:
|
||||
$ref: '#/definitions/GroupOrderArticle'
|
||||
order_article:
|
||||
$ref: '#/definitions/OrderArticle'
|
||||
401:
|
||||
description: not logged-in
|
||||
schema:
|
||||
$ref: '#/definitions/Error401'
|
||||
403:
|
||||
description: user has no ordergroup or missing scope
|
||||
schema:
|
||||
$ref: '#/definitions/Error403'
|
||||
404:
|
||||
description: not found
|
||||
schema:
|
||||
$ref: '#/definitions/Error404'
|
||||
security:
|
||||
- foodsoft_auth: ['group_orders:user']
|
||||
patch:
|
||||
summary: update a group order article (but delete if quantity and tolerance are zero)
|
||||
tags:
|
||||
- 1. User
|
||||
- 2. Order
|
||||
parameters:
|
||||
- $ref: '#/parameters/idInUrl'
|
||||
- in: body
|
||||
name: body
|
||||
description: group order article update
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/GroupOrderArticleForUpdate'
|
||||
responses:
|
||||
200:
|
||||
description: success
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
group_order_article:
|
||||
$ref: '#/definitions/GroupOrderArticle'
|
||||
order_article:
|
||||
$ref: '#/definitions/OrderArticle'
|
||||
401:
|
||||
description: not logged-in
|
||||
schema:
|
||||
$ref: '#/definitions/Error401'
|
||||
403:
|
||||
description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope
|
||||
schema:
|
||||
$ref: '#/definitions/Error403'
|
||||
404:
|
||||
description: order article not found in open orders
|
||||
schema:
|
||||
$ref: '#/definitions/Error404'
|
||||
422:
|
||||
description: invalid parameter value
|
||||
schema:
|
||||
$ref: '#/definitions/Error422'
|
||||
delete:
|
||||
summary: remove group order article
|
||||
tags:
|
||||
- 1. User
|
||||
- 2. Order
|
||||
parameters:
|
||||
- $ref: '#/parameters/idInUrl'
|
||||
responses:
|
||||
200:
|
||||
description: success
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
group_order_article:
|
||||
$ref: '#/definitions/GroupOrderArticle'
|
||||
order_article:
|
||||
$ref: '#/definitions/OrderArticle'
|
||||
401:
|
||||
description: not logged-in
|
||||
schema:
|
||||
$ref: '#/definitions/Error401'
|
||||
403:
|
||||
description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope
|
||||
schema:
|
||||
$ref: '#/definitions/Error403'
|
||||
404:
|
||||
description: order article not found in open orders
|
||||
schema:
|
||||
$ref: '#/definitions/Error404'
|
||||
|
||||
/financial_transactions:
|
||||
get:
|
||||
summary: financial transactions
|
||||
|
@ -654,6 +825,39 @@ definitions:
|
|||
article:
|
||||
$ref: '#/definitions/Article'
|
||||
|
||||
GroupOrderArticleForUpdate:
|
||||
type: object
|
||||
properties:
|
||||
quantity:
|
||||
type: integer
|
||||
description: number of units ordered by the user's ordergroup
|
||||
tolerance:
|
||||
type: integer
|
||||
description: number of extra units the user's ordergroup is willing to buy for filling a box
|
||||
GroupOrderArticleForCreate:
|
||||
allOf:
|
||||
- $ref: '#/definitions/GroupOrderArticleForUpdate'
|
||||
- type: object
|
||||
properties:
|
||||
order_article_id:
|
||||
type: integer
|
||||
description: id of order article
|
||||
GroupOrderArticle:
|
||||
allOf:
|
||||
- $ref: '#/definitions/GroupOrderArticleForCreate'
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
result:
|
||||
type: number
|
||||
format: float
|
||||
description: number of units the user's ordergroup will receive or has received
|
||||
total_price:
|
||||
type: number
|
||||
format: float
|
||||
description: total price of this group order article
|
||||
|
||||
Navigation:
|
||||
type: array
|
||||
items:
|
||||
|
@ -721,6 +925,15 @@ definitions:
|
|||
description: '<tt>forbidden</tt> or <tt>invalid_scope</tt>'
|
||||
error_description:
|
||||
$ref: '#/definitions/Error/properties/error_description'
|
||||
Error422:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: unprocessable entity
|
||||
error_description:
|
||||
$ref: '#/definitions/Error/properties/error_description'
|
||||
|
||||
|
||||
securityDefinitions:
|
||||
foodsoft_auth:
|
||||
|
|
|
@ -27,7 +27,7 @@ describe 'API v1', type: :apivore, order: :defined do
|
|||
it_handles_invalid_token_and_scope(:get, '/user')
|
||||
end
|
||||
|
||||
context 'financial_transactions' do
|
||||
context 'user/financial_transactions' do
|
||||
let(:api_scopes) { ['finance:user'] }
|
||||
let(:other_user) { create :user, :ordergroup }
|
||||
let!(:other_ft_1) { create :financial_transaction, ordergroup: other_user.ordergroup }
|
||||
|
@ -53,6 +53,82 @@ describe 'API v1', type: :apivore, order: :defined do
|
|||
end
|
||||
end
|
||||
|
||||
context 'user/group_order_articles' do
|
||||
let(:api_scopes) { ['group_orders:user'] }
|
||||
let(:order) { create(:order, article_count: 2) }
|
||||
|
||||
let(:user_2) { create :user, :ordergroup }
|
||||
let(:group_order_2) { create(:group_order, order: order, ordergroup: user_2.ordergroup) }
|
||||
let!(:goa_2) { create :group_order_article, order_article: order.order_articles[0], group_order: group_order_2 }
|
||||
before { group_order_2.update_price!; user_2.ordergroup.update_stats! }
|
||||
|
||||
context 'without ordergroup' do
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles', 403, api_auth) }
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 403, api_auth({'id' => goa_2.id})) }
|
||||
end
|
||||
|
||||
context 'with ordergroup' do
|
||||
let(:user) { create :user, :ordergroup }
|
||||
let(:group_order) { create(:group_order, order: order, ordergroup: user.ordergroup) }
|
||||
let!(:goa) { create :group_order_article, order_article: order.order_articles[0], group_order: group_order }
|
||||
before { group_order.update_price!; user.ordergroup.update_stats! }
|
||||
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles', 200, api_auth) }
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 200, api_auth({'id' => goa.id})) }
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 404, api_auth({'id' => goa_2.id})) }
|
||||
it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 404, api_auth({'id' => GroupOrderArticle.last.id + 1})) }
|
||||
|
||||
let(:create_params) { {'_data' => {group_order_article: {order_article_id: order.order_articles[1].id, quantity: 1}}} }
|
||||
let(:update_params) { {'id' => goa.id, '_data' => {group_order_article: {quantity: goa.quantity + 1, tolerance: 0}}} }
|
||||
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 200, api_auth(create_params)) }
|
||||
it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 200, api_auth(update_params)) }
|
||||
it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({'id' => goa.id})) }
|
||||
|
||||
context 'with an existing group_order_article' do
|
||||
let(:create_params) { {'_data' => {group_order_article: {order_article_id: order.order_articles[0].id, quantity: 1}}} }
|
||||
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 422, api_auth(create_params)) }
|
||||
end
|
||||
|
||||
context 'with invalid parameter values' do
|
||||
let(:create_params) { {'_data' => {group_order_article: {order_article_id: order.order_articles[0].id, quantity: -1}}} }
|
||||
let(:update_params) { {'id' => goa.id, '_data' => {group_order_article: {quantity: -1, tolerance: 0}}} }
|
||||
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 422, api_auth(create_params)) }
|
||||
it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 422, api_auth(update_params)) }
|
||||
end
|
||||
|
||||
context 'with a closed order' do
|
||||
let(:order) { create(:order, article_count: 2, state: :finished) }
|
||||
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 404, api_auth(create_params)) }
|
||||
it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 404, api_auth(update_params)) }
|
||||
it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 404, api_auth({'id' => goa.id})) }
|
||||
end
|
||||
|
||||
context 'without enough balance' do
|
||||
before { FoodsoftConfig[:minimum_balance] = 1000 }
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 403, api_auth(create_params)) }
|
||||
it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 403, api_auth(update_params)) }
|
||||
it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({'id' => goa.id})) }
|
||||
end
|
||||
|
||||
context 'without enough apple points' do
|
||||
before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) }
|
||||
it { is_expected.to validate(:post, '/user/group_order_articles', 403, api_auth(create_params)) }
|
||||
it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 403, api_auth(update_params)) }
|
||||
it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({'id' => goa.id})) }
|
||||
end
|
||||
|
||||
it_handles_invalid_token_and_scope(:get, '/user/group_order_articles')
|
||||
it_handles_invalid_token_and_scope(:post, '/user/group_order_articles', ->{ api_auth(create_params) })
|
||||
it_handles_invalid_token_and_scope(:get, '/user/group_order_articles/{id}', ->{ api_auth({'id' => goa.id}) })
|
||||
it_handles_invalid_token_and_scope(:patch, '/user/group_order_articles/{id}', ->{ api_auth(update_params) })
|
||||
it_handles_invalid_token_and_scope(:delete, '/user/group_order_articles/{id}', ->{ api_auth({'id' => goa.id}) })
|
||||
end
|
||||
end
|
||||
|
||||
context 'config' do
|
||||
let(:api_scopes) { ['config:user'] }
|
||||
|
||||
|
|
211
spec/api/v1/user/group_order_articles_spec.rb
Normal file
211
spec/api/v1/user/group_order_articles_spec.rb
Normal file
|
@ -0,0 +1,211 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# Most routes are tested in the swagger_spec, this tests endpoints that change data.
|
||||
describe Api::V1::User::GroupOrderArticlesController, type: :controller do
|
||||
include ApiOAuth
|
||||
let(:user) { create(:user, :ordergroup) }
|
||||
let(:api_scopes) { ['group_orders:user'] }
|
||||
|
||||
let(:order) { create(:order, article_count: 1) }
|
||||
let(:oa_1) { order.order_articles.first }
|
||||
|
||||
let(:other_quantity) { rand(1..10) }
|
||||
let(:other_tolerance) { rand(1..10) }
|
||||
let(:user_other) { create(:user, :ordergroup) }
|
||||
let!(:go_other) { create(:group_order, order: order, ordergroup: user_other.ordergroup ) }
|
||||
let!(:goa_other) { create(:group_order_article, group_order: go_other, order_article: oa_1, quantity: other_quantity, tolerance: other_tolerance) }
|
||||
before { go_other.update_price!; user_other.ordergroup.update_stats! }
|
||||
|
||||
let(:json_goa) { json_response['group_order_article'] }
|
||||
let(:json_oa) { json_response['order_article'] }
|
||||
|
||||
|
||||
shared_examples "group_order_articles endpoint success" do
|
||||
before { request }
|
||||
|
||||
it "returns status 200" do
|
||||
expect(response.status).to eq 200
|
||||
end
|
||||
|
||||
it "returns the order_article" do
|
||||
expect(json_oa['id']).to eq oa_1.id
|
||||
expect(json_oa['quantity']).to eq new_quantity + other_quantity
|
||||
expect(json_oa['tolerance']).to eq new_tolerance + other_tolerance
|
||||
end
|
||||
|
||||
it "updates the group_order" do
|
||||
go = nil
|
||||
expect {
|
||||
request
|
||||
go = user.ordergroup.group_orders.where(order: order).last
|
||||
}.to change { go&.updated_by }.to(user)
|
||||
.and change { go&.price }
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "group_order_articles create/update success" do
|
||||
include_examples "group_order_articles endpoint success"
|
||||
|
||||
it "returns the group_order_article" do
|
||||
expect(json_goa['id']).to be_present
|
||||
expect(json_goa['order_article_id']).to eq oa_1.id
|
||||
expect(json_goa['quantity']).to eq new_quantity
|
||||
expect(json_goa['tolerance']).to eq new_tolerance
|
||||
end
|
||||
|
||||
it "updates the group_order_article" do
|
||||
resulting_goa = GroupOrderArticle.where(id: json_goa['id']).first
|
||||
expect(resulting_goa).to be_present
|
||||
expect(resulting_goa.quantity).to eq new_quantity
|
||||
expect(resulting_goa.tolerance).to eq new_tolerance
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
shared_examples "group_order_articles endpoint failure" do |status|
|
||||
it "returns status #{status}" do
|
||||
request
|
||||
expect(response.status).to eq status
|
||||
end
|
||||
|
||||
it "does not change the group_order" do
|
||||
expect { request }.to_not change {
|
||||
go = user.ordergroup.group_orders.where(order: order).last
|
||||
go&.attributes
|
||||
}
|
||||
end
|
||||
|
||||
it "does not change the group_order_article" do
|
||||
expect { request }.to_not change {
|
||||
goa = GroupOrderArticle.joins(:group_order)
|
||||
.where(order_article_id: oa_1.id, group_orders: { ordergroup: user.ordergroup }).last
|
||||
goa&.attributes
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "POST :create" do
|
||||
let(:new_quantity) { rand(1..10) }
|
||||
let(:new_tolerance) { rand(1..10) }
|
||||
|
||||
let(:goa_params) { { order_article_id: oa_1.id, quantity: new_quantity, tolerance: new_tolerance } }
|
||||
let(:request) { post :create, params: { group_order_article: goa_params, foodcoop: 'f' } }
|
||||
|
||||
context "with no existing group_order" do
|
||||
include_examples "group_order_articles create/update success"
|
||||
end
|
||||
|
||||
context "with an existing group_order" do
|
||||
let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) }
|
||||
include_examples "group_order_articles create/update success"
|
||||
end
|
||||
|
||||
context "with an existing group_order_article" do
|
||||
let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) }
|
||||
let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1, quantity: 0, tolerance: 1) }
|
||||
before { go.update_price!; user.ordergroup.update_stats! }
|
||||
include_examples "group_order_articles endpoint failure", 422
|
||||
end
|
||||
|
||||
context "with invalid parameter values" do
|
||||
let(:goa_params) { { order_article_id: oa_1.id, quantity: -1, tolerance: new_tolerance} }
|
||||
include_examples "group_order_articles endpoint failure", 422
|
||||
end
|
||||
|
||||
context 'with a closed order' do
|
||||
let(:order) { create(:order, article_count: 1, state: :finished) }
|
||||
include_examples "group_order_articles endpoint failure", 404
|
||||
end
|
||||
|
||||
context 'without enough balance' do
|
||||
before { FoodsoftConfig[:minimum_balance] = 1000 }
|
||||
include_examples "group_order_articles endpoint failure", 403
|
||||
end
|
||||
|
||||
context 'without enough apple points' do
|
||||
before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) }
|
||||
include_examples "group_order_articles endpoint failure", 403
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH :update" do
|
||||
let(:new_quantity) { rand(2..10) }
|
||||
let(:new_tolerance) { rand(2..10) }
|
||||
|
||||
let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) }
|
||||
let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1, quantity: 1, tolerance: 0) }
|
||||
before { go.update_price!; user.ordergroup.update_stats! }
|
||||
|
||||
let(:goa_params) { { quantity: new_quantity, tolerance: new_tolerance } }
|
||||
let(:request) { patch :update, params: { id: goa.id, group_order_article: goa_params, foodcoop: 'f' } }
|
||||
|
||||
context "happy flow" do
|
||||
include_examples "group_order_articles create/update success"
|
||||
end
|
||||
|
||||
context "with invalid parameter values" do
|
||||
let(:goa_params) { { order_article_id: oa_1.id, quantity: -1, tolerance: new_tolerance} }
|
||||
include_examples "group_order_articles endpoint failure", 422
|
||||
end
|
||||
|
||||
context 'with a closed order' do
|
||||
let(:order) { create(:order, article_count: 1, state: :finished) }
|
||||
include_examples "group_order_articles endpoint failure", 404
|
||||
end
|
||||
|
||||
context 'without enough balance' do
|
||||
before { FoodsoftConfig[:minimum_balance] = 1000 }
|
||||
include_examples "group_order_articles endpoint failure", 403
|
||||
end
|
||||
|
||||
context 'without enough apple points' do
|
||||
before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) }
|
||||
include_examples "group_order_articles endpoint failure", 403
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE :destroy" do
|
||||
let(:new_quantity) { 0 }
|
||||
let(:new_tolerance) { 0 }
|
||||
|
||||
let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) }
|
||||
let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1) }
|
||||
before { go.update_price!; user.ordergroup.update_stats! }
|
||||
|
||||
let(:request) { delete :destroy, params: { id: goa.id, foodcoop: 'f' } }
|
||||
|
||||
|
||||
shared_examples "group_order_articles destroy success" do
|
||||
include_examples "group_order_articles endpoint success"
|
||||
|
||||
it "does not return the group_order_article" do
|
||||
expect(json_goa).to be_nil
|
||||
end
|
||||
|
||||
it "deletes the group_order_article" do
|
||||
expect(GroupOrderArticle.where(id: goa.id)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "happy flow" do
|
||||
include_examples "group_order_articles destroy success"
|
||||
end
|
||||
|
||||
context 'with a closed order' do
|
||||
let(:order) { create(:order, article_count: 1, state: :finished) }
|
||||
include_examples "group_order_articles endpoint failure", 404
|
||||
end
|
||||
|
||||
context 'without enough balance' do
|
||||
before { FoodsoftConfig[:minimum_balance] = 1000 }
|
||||
include_examples "group_order_articles destroy success"
|
||||
end
|
||||
|
||||
context 'without enough apple points' do
|
||||
before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) }
|
||||
include_examples "group_order_articles destroy success"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue