API v1 group_order_articles endpoints

This commit is contained in:
wvengen 2018-10-13 16:27:24 +02:00 committed by wvengen
parent ed9192c47f
commit e1d50e5b9c
11 changed files with 646 additions and 10 deletions

View 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

View file

@ -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 = {}

View file

@ -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.

View file

@ -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

View 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

View file

@ -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',

View file

@ -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:

View file

@ -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

View file

@ -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:

View file

@ -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'] }

View 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