mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
516 lines
18 KiB
Ruby
516 lines
18 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Admin::OrderCyclesController do
|
|
let!(:distributor_owner) { create(:user) }
|
|
let(:datetime_confirmation_attrs) {
|
|
{ confirm_datetime_change: nil,
|
|
error_class: Admin::OrderCyclesController::DateTimeChangeError }
|
|
}
|
|
|
|
before do
|
|
allow(controller).to receive_messages spree_current_user: distributor_owner
|
|
end
|
|
|
|
describe "#index" do
|
|
describe "when the user manages a coordinator" do
|
|
let!(:coordinator) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
let!(:oc1) {
|
|
create(:simple_order_cycle, orders_open_at: 70.days.ago, orders_close_at: 60.days.ago )
|
|
}
|
|
let!(:oc2) {
|
|
create(:simple_order_cycle, orders_open_at: 70.days.ago, orders_close_at: 40.days.ago )
|
|
}
|
|
let!(:oc3) {
|
|
create(:simple_order_cycle, orders_open_at: 70.days.ago, orders_close_at: 20.days.ago )
|
|
}
|
|
let!(:oc4) {
|
|
create(:simple_order_cycle, orders_open_at: 70.days.ago, orders_close_at: nil )
|
|
}
|
|
|
|
context "html" do
|
|
it "doesn't load any data" do
|
|
get :index, as: :html
|
|
expect(assigns(:collection)).to be_empty
|
|
end
|
|
end
|
|
|
|
context "json" do
|
|
context "where ransack conditions are specified" do
|
|
it "loads order cycles closed within past month, and orders w/o a close_at date" do
|
|
get :index, as: :json
|
|
expect(assigns(:collection)).not_to include oc1, oc2
|
|
expect(assigns(:collection)).to include oc3, oc4
|
|
end
|
|
end
|
|
|
|
context "where q[orders_close_at_gt] is set" do
|
|
let(:q) { { orders_close_at_gt: 45.days.ago } }
|
|
|
|
it "loads order cycles closed after specified date, and orders w/o a close_at date" do
|
|
get :index, as: :json, params: { q: }
|
|
expect(assigns(:collection)).not_to include oc1
|
|
expect(assigns(:collection)).to include oc2, oc3, oc4
|
|
end
|
|
|
|
context "and other conditions are specified" do
|
|
before { q.merge!(id_not_in: [oc2.id, oc4.id]) }
|
|
|
|
it "loads order cycles that meet all conditions" do
|
|
get :index, format: :json, params: { q: }
|
|
expect(assigns(:collection)).not_to include oc1, oc2, oc4
|
|
expect(assigns(:collection)).to include oc3
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "new" do
|
|
describe "when the user manages a single distributor enterprise suitable for coordinator" do
|
|
let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
|
|
it "renders the new template" do
|
|
get :new
|
|
expect(response).to render_template :new
|
|
end
|
|
end
|
|
|
|
describe "when a user manages multiple enterprises suitable for coordinator" do
|
|
let!(:distributor1) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
let!(:distributor2) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
let!(:distributor3) { create(:distributor_enterprise) }
|
|
|
|
it "renders the set_coordinator template" do
|
|
get :new
|
|
expect(response).to render_template :set_coordinator
|
|
end
|
|
|
|
describe "and a coordinator_id is submitted as part of the request" do
|
|
describe "when the user manages the enterprise" do
|
|
it "renders the new template" do
|
|
get :new, params: { coordinator_id: distributor1.id }
|
|
expect(response).to render_template :new
|
|
end
|
|
end
|
|
|
|
describe "when the user does not manage the enterprise" do
|
|
it "renders the set_coordinator template and sets a flash error" do
|
|
get :new, params: { coordinator_id: distributor3.id }
|
|
expect(response).to render_template :set_coordinator
|
|
expect(flash[:error])
|
|
.to eq "You don't have permission to create an order cycle " \
|
|
"coordinated by that enterprise"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "show" do
|
|
context 'a distributor manages an order cycle' do
|
|
let(:distributor) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
let(:oc) { create(:simple_order_cycle, coordinator: distributor) }
|
|
|
|
context "distributor navigates to order cycle show page" do
|
|
it 'redirects to edit page' do
|
|
get :show, params: { id: oc.id }
|
|
expect(response).to redirect_to edit_admin_order_cycle_path(oc.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "queries" do
|
|
context "as manager, when order cycle has multiple exchanges" do
|
|
let!(:distributor) { create(:distributor_enterprise) }
|
|
let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor) }
|
|
before do
|
|
order_cycle.exchanges.create! sender: distributor, receiver: distributor,
|
|
incoming: true, receival_instructions: 'A', tag_list: "A"
|
|
order_cycle.exchanges.create! sender: distributor, receiver: distributor,
|
|
incoming: false, pickup_instructions: 'B', tag_list: "B"
|
|
controller_login_as_enterprise_user([distributor])
|
|
end
|
|
|
|
it do
|
|
expect {
|
|
get :show, params: { id: order_cycle.id }, as: :json
|
|
}.to query_database(
|
|
{
|
|
select: {
|
|
enterprise_fees: 3,
|
|
enterprise_groups: 1,
|
|
enterprises: 19,
|
|
exchanges: 7,
|
|
order_cycles: 6,
|
|
proxy_orders: 1,
|
|
schedules: 1,
|
|
spree_variants: 8,
|
|
tags: 1
|
|
},
|
|
update: { spree_users: 1 }
|
|
}
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "create" do
|
|
let(:shop) { create(:distributor_enterprise) }
|
|
|
|
context "as a manager of a shop" do
|
|
let(:form_mock) { instance_double(OrderCycles::FormService) }
|
|
let(:params) { { as: :json, order_cycle: {} } }
|
|
|
|
before do
|
|
controller_login_as_enterprise_user([shop])
|
|
allow(OrderCycles::FormService).to receive(:new) { form_mock }
|
|
end
|
|
|
|
context "when creation is successful" do
|
|
before { allow(form_mock).to receive(:save) { true } }
|
|
|
|
# mock build_resource so that we can control the edit_path
|
|
Admin::OrderCyclesController.class_eval do
|
|
def build_resource
|
|
order_cycle = OrderCycle.new
|
|
order_cycle.id = 1
|
|
order_cycle
|
|
end
|
|
end
|
|
|
|
it "returns success: true and a valid edit path" do
|
|
spree_post :create, params
|
|
json_response = response.parsed_body
|
|
expect(json_response['success']).to be true
|
|
expect(json_response['edit_path']).to eq "/admin/order_cycles/1/incoming"
|
|
end
|
|
end
|
|
|
|
context "when an error occurs" do
|
|
before { allow(form_mock).to receive(:save) { false } }
|
|
|
|
it "returns an errors hash" do
|
|
spree_post :create, params
|
|
json_response = response.parsed_body
|
|
expect(json_response['errors']).to be
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "update" do
|
|
let(:order_cycle) { create(:simple_order_cycle) }
|
|
let(:coordinator) { order_cycle.coordinator }
|
|
let(:form_mock) { instance_double(OrderCycles::FormService) }
|
|
|
|
before do
|
|
allow(OrderCycles::FormService).to receive(:new) { form_mock }
|
|
end
|
|
|
|
context "as a manager of the coordinator" do
|
|
before { controller_login_as_enterprise_user([coordinator]) }
|
|
let(:params) { { format: :json, id: order_cycle.id, order_cycle: {} } }
|
|
|
|
context "when order cycle has subscriptions" do
|
|
let(:coordinator) { order_cycle.coordinator }
|
|
let(:producer) { create(:supplier_enterprise) }
|
|
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
|
|
let(:v) { create(:variant) }
|
|
let!(:incoming_exchange) {
|
|
create(:exchange, order_cycle:, sender: producer, receiver: coordinator,
|
|
incoming: true, variants: [v])
|
|
}
|
|
let!(:outgoing_exchange) {
|
|
create(:exchange, order_cycle:, sender: coordinator, receiver: coordinator,
|
|
incoming: false, variants: [v])
|
|
}
|
|
let!(:subscription) { create(:subscription, shop: coordinator, schedule:) }
|
|
let!(:subscription_line_item) {
|
|
create(:subscription_line_item, subscription:, variant: v)
|
|
}
|
|
|
|
before do
|
|
allow(form_mock).to receive(:save) { true }
|
|
v.destroy
|
|
end
|
|
|
|
it "can update order cycle even if the variant has been deleted" do
|
|
spree_put :update, { format: :json, id: order_cycle.id, order_cycle: {} }
|
|
expect(response).to have_http_status :ok
|
|
end
|
|
end
|
|
|
|
context "when updating succeeds" do
|
|
before { allow(form_mock).to receive(:save) { true } }
|
|
|
|
context "when the page is reloading" do
|
|
before { params[:reloading] = '1' }
|
|
|
|
it "sets flash message" do
|
|
spree_put :update, params
|
|
expect(flash[:success]).to eq('Your order cycle has been updated.')
|
|
end
|
|
end
|
|
|
|
context "when the page is not reloading" do
|
|
it "does not set flash message" do
|
|
spree_put :update, params
|
|
expect(flash[:success]).to be nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when a validation error occurs" do
|
|
before { allow(form_mock).to receive(:save) { false } }
|
|
|
|
it "returns an error message" do
|
|
spree_put :update, params
|
|
|
|
json_response = response.parsed_body
|
|
expect(json_response['errors']).to be
|
|
end
|
|
end
|
|
|
|
it "can update preference product_selection_from_coordinator_inventory_only" do
|
|
expect(OrderCycles::FormService).to receive(:new).
|
|
with(order_cycle,
|
|
{ "preferred_product_selection_from_coordinator_inventory_only" => true,
|
|
**datetime_confirmation_attrs },
|
|
anything) { form_mock }
|
|
allow(form_mock).to receive(:save) { true }
|
|
|
|
spree_put :update, params.
|
|
merge(order_cycle: {
|
|
preferred_product_selection_from_coordinator_inventory_only: true
|
|
})
|
|
end
|
|
|
|
it "can update preference automatic_notifications" do
|
|
expect(OrderCycles::FormService).to receive(:new).
|
|
with(order_cycle,
|
|
{ "automatic_notifications" => true, **datetime_confirmation_attrs },
|
|
anything) { form_mock }
|
|
allow(form_mock).to receive(:save) { true }
|
|
|
|
spree_put :update, params.
|
|
merge(order_cycle: { automatic_notifications: true })
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "limiting update scope" do
|
|
let(:order_cycle) { create(:simple_order_cycle) }
|
|
let(:producer) { create(:supplier_enterprise) }
|
|
let(:coordinator) { order_cycle.coordinator }
|
|
let(:hub) { create(:distributor_enterprise) }
|
|
let(:v) { create(:variant) }
|
|
let!(:incoming_exchange) {
|
|
create(:exchange, order_cycle:, sender: producer, receiver: coordinator,
|
|
incoming: true, variants: [v])
|
|
}
|
|
let!(:outgoing_exchange) {
|
|
create(:exchange, order_cycle:, sender: coordinator, receiver: hub,
|
|
incoming: false, variants: [v])
|
|
}
|
|
|
|
let(:allowed) { { incoming_exchanges: [], outgoing_exchanges: [] } }
|
|
let(:restricted) {
|
|
{ name: 'some name', orders_open_at: 1.day.from_now.to_s, orders_close_at: 1.day.ago.to_s }
|
|
}
|
|
let(:params) {
|
|
{
|
|
format: :json, id: order_cycle.id, order_cycle: allowed.merge(restricted)
|
|
}
|
|
}
|
|
let(:form_mock) {
|
|
instance_double(OrderCycles::FormService, save: true)
|
|
}
|
|
|
|
before { allow(controller).to receive(:spree_current_user) { user } }
|
|
|
|
context "as a manager of the coordinator" do
|
|
let(:user) { coordinator.owner }
|
|
let(:expected) {
|
|
[order_cycle, allowed.merge(restricted).merge(datetime_confirmation_attrs), user]
|
|
}
|
|
|
|
it "allows me to update exchange information for exchanges, name and dates" do
|
|
expect(OrderCycles::FormService).to receive(:new).with(*expected) { form_mock }
|
|
spree_put :update, params
|
|
end
|
|
end
|
|
|
|
context "as a producer supplying to an order cycle" do
|
|
let(:user) { producer.owner }
|
|
let(:expected) { [order_cycle, allowed.merge(datetime_confirmation_attrs), user] }
|
|
|
|
it "allows me to update exchange information for exchanges, but not name or dates" do
|
|
expect(OrderCycles::FormService).to receive(:new).with(*expected) { form_mock }
|
|
spree_put :update, params
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "bulk_update" do
|
|
let(:oc) { create(:simple_order_cycle) }
|
|
let!(:coordinator) { oc.coordinator }
|
|
|
|
context "when I manage the coordinator of an order cycle" do
|
|
let(:params) do
|
|
{ format: :json, order_cycle_set: { collection_attributes: { '0' => {
|
|
id: oc.id,
|
|
name: "Updated Order Cycle",
|
|
orders_open_at: Date.current - 21.days,
|
|
orders_close_at: Date.current + 21.days,
|
|
} } } }
|
|
end
|
|
|
|
before { create(:enterprise_role, user: distributor_owner, enterprise: coordinator) }
|
|
|
|
it "updates order cycle properties" do
|
|
spree_put :bulk_update, params
|
|
oc.reload
|
|
expect(oc.name).to eq "Updated Order Cycle"
|
|
expect(oc.orders_open_at.to_date).to eq Date.current - 21.days
|
|
expect(oc.orders_close_at.to_date).to eq Date.current + 21.days
|
|
end
|
|
|
|
it "does nothing when no data is supplied" do
|
|
expect do
|
|
spree_put :bulk_update, format: :json
|
|
end.to change { oc.orders_open_at }.by(0)
|
|
json_response = response.parsed_body
|
|
expect(json_response['errors'])
|
|
.to eq 'Hm, something went wrong. No order cycle data found.'
|
|
end
|
|
|
|
context "when a validation error occurs" do
|
|
let(:params) do
|
|
{ format: :json, order_cycle_set: { collection_attributes: { '0' => {
|
|
id: oc.id,
|
|
name: "Updated Order Cycle",
|
|
orders_open_at: Date.current + 25.days,
|
|
orders_close_at: Date.current + 21.days,
|
|
} } } }
|
|
end
|
|
|
|
it "returns an error message" do
|
|
spree_put :bulk_update, params
|
|
json_response = response.parsed_body
|
|
expect(json_response['errors']).to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when I do not manage the coordinator of an order cycle" do
|
|
# I need to manage a hub in order to access the bulk_update action
|
|
let!(:another_distributor) { create(:distributor_enterprise, users: [distributor_owner]) }
|
|
|
|
it "doesn't update order cycle properties" do
|
|
spree_put :bulk_update,
|
|
format: :json,
|
|
order_cycle_set: { collection_attributes: { '0' => {
|
|
id: oc.id,
|
|
name: "Updated Order Cycle",
|
|
orders_open_at: Date.current - 21.days,
|
|
orders_close_at: Date.current + 21.days,
|
|
} } }
|
|
|
|
oc.reload
|
|
expect(oc.name).not_to eq "Updated Order Cycle"
|
|
expect(oc.orders_open_at.to_date).not_to eq Date.current - 21.days
|
|
expect(oc.orders_close_at.to_date).not_to eq Date.current + 21.days
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "notifying producers" do
|
|
let(:user) { create(:user) }
|
|
let(:admin_user) { create(:admin_user) }
|
|
let(:order_cycle) { create(:simple_order_cycle) }
|
|
|
|
before do
|
|
allow(controller).to receive_messages spree_current_user: admin_user
|
|
end
|
|
|
|
it "enqueues a job" do
|
|
expect do
|
|
spree_post :notify_producers, id: order_cycle.id
|
|
end.to enqueue_job OrderCycleNotificationJob
|
|
end
|
|
|
|
it "redirects back to the order cycles path with a success message" do
|
|
spree_post :notify_producers, id: order_cycle.id
|
|
expect(response).to redirect_to admin_order_cycles_path
|
|
expect(flash[:success]).to eq(
|
|
'Emails to be sent to producers have been queued for sending.'
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "destroy" do
|
|
let(:distributor) { create(:distributor_enterprise, owner: distributor_owner) }
|
|
let(:oc) { create(:simple_order_cycle, coordinator: distributor) }
|
|
|
|
describe "when an order cycle is deleteable" do
|
|
it "allows the order_cycle to be destroyed" do
|
|
get :destroy, params: { id: oc.id }
|
|
expect(OrderCycle.find_by(id: oc.id)).to be nil
|
|
end
|
|
end
|
|
|
|
describe "when an order cycle becomes non-deletable due to the presence of an order" do
|
|
let!(:order) { create(:order, order_cycle: oc) }
|
|
|
|
it "displays an error message when we attempt to delete it" do
|
|
get :destroy, params: { id: oc.id }
|
|
expect(response).to redirect_to admin_order_cycles_path
|
|
expect(flash[:error])
|
|
.to eq 'That order cycle has been selected by a customer and cannot be deleted. ' \
|
|
'To prevent customers from accessing it, please close it instead.'
|
|
end
|
|
end
|
|
|
|
describe "when an order cycle becomes non-deletable because it is linked to a schedule" do
|
|
let!(:schedule) { create(:schedule, order_cycles: [oc]) }
|
|
|
|
it "displays an error message when we attempt to delete it" do
|
|
get :destroy, params: { id: oc.id }
|
|
expect(response).to redirect_to admin_order_cycles_path
|
|
expect(flash[:error])
|
|
.to eq 'That order cycle is linked to a schedule and cannot be deleted. ' \
|
|
'Please unlink or delete the schedule first.'
|
|
end
|
|
end
|
|
|
|
describe "when an order cycle has any coordinator_fees" do
|
|
let(:enterprise_fee1) { create(:enterprise_fee) }
|
|
|
|
before do
|
|
oc.coordinator_fees << enterprise_fee1
|
|
end
|
|
|
|
it "actually delete the order cycle" do
|
|
get :destroy, params: { id: oc.id }
|
|
expect(OrderCycle.find_by(id: oc.id)).to be nil
|
|
expect(response).to redirect_to admin_order_cycles_path
|
|
end
|
|
|
|
describe "when the order_cycle was previously cloned" do
|
|
let(:cloned) { oc.clone! }
|
|
|
|
it "actually delete the order cycle" do
|
|
get :destroy, params: { id: cloned.id }
|
|
|
|
expect(OrderCycle.find_by(id: cloned.id)).to be nil
|
|
expect(OrderCycle.find_by(id: oc.id)).not_to be nil
|
|
expect(EnterpriseFee.find_by(id: enterprise_fee1.id)).not_to be nil
|
|
expect(response).to redirect_to admin_order_cycles_path
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|