diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb new file mode 100644 index 00000000..8eb2a066 --- /dev/null +++ b/app/controllers/api/v1/orders_controller.rb @@ -0,0 +1,19 @@ +class Api::V1::OrdersController < Api::V1::BaseController + include Concerns::CollectionScope + + before_action ->{ doorkeeper_authorize! 'orders:read', 'orders:write' } + + def index + render_collection search_scope + end + + def show + render json: scope.find(params.require(:id)) + end + + private + + def scope + Order.includes(:supplier) + end +end diff --git a/app/controllers/concerns/auth_api.rb b/app/controllers/concerns/auth_api.rb index be3389ec..fe93af97 100644 --- a/app/controllers/concerns/auth_api.rb +++ b/app/controllers/concerns/auth_api.rb @@ -53,14 +53,19 @@ module Concerns::AuthApi end case scope_parts.first - when 'user' then true # access to the current user's own profile - when 'config' then current_user.role_admin? - when 'users' then current_user.role_admin? - when 'workgroups' then current_user.role_admin? - when 'suppliers' then current_user.role_suppliers? - when 'group_orders' then current_user.role_orders? - when 'finance' then current_user.role_finance? + when 'user' then return true # access to the current user's own profile + when 'config' then return current_user.role_admin? + when 'users' then return current_user.role_admin? + when 'workgroups' then return current_user.role_admin? + when 'suppliers' then return current_user.role_suppliers? + when 'group_orders' then return current_user.role_orders? + when 'finance' then return current_user.role_finance? # please note that offline_access does not belong here, since it is not used for permission checking end + + case scope + when 'orders:read' then return true + when 'orders:write' then return current_user.role_orders? + end end end diff --git a/app/models/order.rb b/app/models/order.rb index 20db85ce..82c22481 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -51,6 +51,14 @@ class Order < ApplicationRecord include DateTimeAttributeValidate date_time_attribute :starts, :boxfill, :ends + def self.ransackable_attributes(auth_object = nil) + %w(id state supplier_id starts boxfill ends pickup) + end + + def self.ransackable_associations(auth_object = nil) + %w(supplier articles order_articles) + end + def stockit? supplier_id.nil? end @@ -111,11 +119,11 @@ class Order < ApplicationRecord end def boxfill? - FoodsoftConfig[:use_boxfill] && open? && boxfill.present? && boxfill < Time.now + !!FoodsoftConfig[:use_boxfill] && open? && boxfill.present? && boxfill < Time.now end def is_boxfill_useful? - FoodsoftConfig[:use_boxfill] && supplier.try(:has_tolerance?) + !!FoodsoftConfig[:use_boxfill] && !!supplier.try(:has_tolerance?) end def expired? diff --git a/app/models/supplier.rb b/app/models/supplier.rb index 6a9b3f1d..d6ebbb00 100644 --- a/app/models/supplier.rb +++ b/app/models/supplier.rb @@ -23,6 +23,14 @@ class Supplier < ApplicationRecord scope :undeleted, -> { where(deleted_at: nil) } scope :having_articles, -> { where(id: Article.undeleted.select(:supplier_id).distinct) } + def self.ransackable_attributes(auth_object = nil) + %w(id name) + end + + def self.ransackable_associations(auth_object = nil) + %w(articles stock_articles orders) + end + # sync all articles with the external database # returns an array with articles(and prices), which should be updated (to use in a form) # also returns an array with outlisted_articles, which should be deleted diff --git a/app/serializers/order_serializer.rb b/app/serializers/order_serializer.rb new file mode 100644 index 00000000..19555a24 --- /dev/null +++ b/app/serializers/order_serializer.rb @@ -0,0 +1,11 @@ +class OrderSerializer < ActiveModel::Serializer + attributes :id, :name, :starts, :ends, :boxfill, :pickup, :is_open, :is_boxfill + + def is_open + object.open? + end + + def is_boxfill + object.boxfill? + end +end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index ee9fc644..aa44583e 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -49,10 +49,11 @@ 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' + default_scopes 'config:user', 'finance:user', 'user:read', 'orders:read' optional_scopes 'config:read', 'config:write', 'finance:read', 'finance:write', + 'orders:write', 'user:write', 'offline_access' diff --git a/config/routes.rb b/config/routes.rb index f5d6505d..332f99d2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -271,6 +271,7 @@ Rails.application.routes.draw do resources :financial_transaction_classes, only: [:index, :show] resources :financial_transaction_types, only: [:index, :show] resources :financial_transactions, only: [:index, :show] + resources :orders, only: [:index, :show] end end diff --git a/doc/swagger.v1.yml b/doc/swagger.v1.yml index e3f99de2..7f44faa0 100644 --- a/doc/swagger.v1.yml +++ b/doc/swagger.v1.yml @@ -179,6 +179,65 @@ paths: $ref: '#/definitions/Error404' security: - foodsoft_auth: ['finance:read', 'finance:write'] + /orders: + get: + summary: orders + tags: + - 2. Orders + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/per_page' + responses: + 200: + description: success + schema: + type: object + properties: + orders: + type: array + items: + $ref: '#/definitions/Order' + meta: + $ref: '#/definitions/Meta' + 401: + description: not logged-in + schema: + $ref: '#/definitions/Error401' + 403: + description: missing scope or no permission + schema: + $ref: '#/definitions/Error403' + security: + - foodsoft_auth: ['orders:read', 'orders:write'] + /orders/{id}: + parameters: + - $ref: '#/parameters/idInUrl' + get: + summary: find order by id + tags: + - 2. Order + responses: + 200: + description: success + schema: + type: object + properties: + order: + $ref: '#/definitions/Order' + 401: + description: not logged-in + schema: + $ref: '#/definitions/Error401' + 403: + description: missing scope or no permission + schema: + $ref: '#/definitions/Error403' + 404: + description: not found + schema: + $ref: '#/definitions/Error404' + security: + - foodsoft_auth: ['orders:read', 'orders:write'] /financial_transaction_classes: get: @@ -436,6 +495,37 @@ definitions: description: name of the class of the transaction required: ['id', 'name', 'financial_transaction_class_id', 'financial_transaction_class_name'] + Order: + type: object + properties: + id: + type: integer + name: + type: string + description: name of the order's supplier (or stock) + starts: + type: string + format: date-time + description: when the order was opened + ends: + type: ['string', 'null'] + format: date-time + description: when the order will close or was closed + boxfill: + type: ['string', 'null'] + format: date-time + description: when the order will enter or entered the boxfill phase + pickup: + type: ['string', 'null'] + format: date + description: pickup date + is_open: + type: boolean + description: if the order is currently open or not + is_boxfill: + type: boolean + description: if the order is currently in the boxfill phase or not + Navigation: type: array items: diff --git a/spec/api/v1/swagger_spec.rb b/spec/api/v1/swagger_spec.rb index 6b9cd918..4a0dc370 100644 --- a/spec/api/v1/swagger_spec.rb +++ b/spec/api/v1/swagger_spec.rb @@ -113,6 +113,18 @@ describe 'API v1', type: :apivore, order: :defined do it_handles_invalid_token(:get, '/financial_transaction_types') it_handles_invalid_token(:get, '/financial_transaction_types/{id}', ->{ api_auth({'id' => tpy_1.id }) }) end + + context 'orders' do + let(:api_scopes) { ['orders:read'] } + let!(:order) { create :order } + + it { is_expected.to validate(:get, '/orders', 200, api_auth) } + it { is_expected.to validate(:get, '/orders/{id}', 200, api_auth({'id' => order.id})) } + it { is_expected.to validate(:get, '/orders/{id}', 404, api_auth({'id' => Order.last.id + 1})) } + + it_handles_invalid_token_and_scope(:get, '/orders') + it_handles_invalid_token_and_scope(:get, '/orders/{id}', ->{ api_auth({'id' => order.id}) }) + end end # needs to be last context so it is always run at the end