mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(feat) OpenAPI endpoint for accounting
Also: (bug) filter by array in openAPI = error
This commit is contained in:
parent
523529228c
commit
a55880a0ad
@ -1,5 +1,8 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
|
- Accounting data is now built each night and saved in database
|
||||||
|
- OpenAPI endpoint to fetch accounting data
|
||||||
|
- Fix a bug: providing an array of attributes to filter OpenApi data, results in error
|
||||||
- [TODO DEPLOY] `rails fablab:maintenance:build_accounting_lines`
|
- [TODO DEPLOY] `rails fablab:maintenance:build_accounting_lines`
|
||||||
|
|
||||||
- Add reservation deadline parameter (#414)
|
- Add reservation deadline parameter (#414)
|
||||||
|
@ -1,26 +1,29 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# OpenAPI controller for the accounting lines
|
# authorized 3rd party softwares can fetch the accounting lines through the OpenAPI
|
||||||
class OpenAPI::V1::AccountingController < OpenAPI::V1::BaseController
|
class OpenAPI::V1::AccountingController < OpenAPI::V1::BaseController
|
||||||
extend OpenAPI::ApiDoc
|
extend OpenAPI::ApiDoc
|
||||||
include Rails::Pagination
|
include Rails::Pagination
|
||||||
expose_doc
|
expose_doc
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@invoices = Invoice.order(created_at: :desc)
|
@lines = AccountingLine.order(date: :desc)
|
||||||
.includes(invoicing_profile: :user)
|
.includes(:invoice)
|
||||||
.references(:invoicing_profiles)
|
|
||||||
|
|
||||||
@invoices = @invoices.where(invoicing_profiles: { user_id: params[:user_id] }) if params[:user_id].present?
|
@lines = @lines.where('date >= ?', DateTime.parse(params[:after])) if params[:after].present?
|
||||||
|
@lines = @lines.where('date <= ?', DateTime.parse(params[:before])) if params[:before].present?
|
||||||
|
@lines = @lines.where(invoice_id: may_array(params[:invoice_id])) if params[:invoice_id].present?
|
||||||
|
|
||||||
return if params[:page].blank?
|
@lines = @lines.page(page).per(per_page)
|
||||||
|
paginate @lines, per_page: per_page
|
||||||
@invoices = @invoices.page(params[:page]).per(per_page)
|
|
||||||
paginate @invoices, per_page: per_page
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def page
|
||||||
|
params[:page] || 1
|
||||||
|
end
|
||||||
|
|
||||||
def per_page
|
def per_page
|
||||||
params[:per_page] || 20
|
params[:per_page] || 20
|
||||||
end
|
end
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
module OpenAPI::V1; end
|
module OpenAPI::V1; end
|
||||||
|
|
||||||
# Parameters for OpenAPI endpoints
|
# Parameters for OpenAPI endpoints
|
||||||
class OpenAPI::V1::BaseController < ActionController::Base
|
class OpenAPI::V1::BaseController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
||||||
|
include ApplicationHelper
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
protect_from_forgery with: :null_session
|
||||||
skip_before_action :verify_authenticity_token
|
skip_before_action :verify_authenticity_token
|
||||||
before_action :authenticate
|
before_action :authenticate
|
||||||
@ -49,7 +51,7 @@ class OpenAPI::V1::BaseController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_unauthorized
|
def render_unauthorized
|
||||||
render json: { errors: ['Bad credentials'] }, status: 401
|
render json: { errors: ['Bad credentials'] }, status: :unauthorized
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -16,10 +16,9 @@ class OpenAPI::V1::EventsController < OpenAPI::V1::BaseController
|
|||||||
@events.order(created_at: :desc)
|
@events.order(created_at: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@events = @events.where(id: may_array(params[:id])) if params[:id].present?
|
||||||
|
|
||||||
@events = @events.where(id: params[:id]) if params[:id].present?
|
return if params[:page].blank?
|
||||||
|
|
||||||
return unless params[:page].present?
|
|
||||||
|
|
||||||
@events = @events.page(params[:page]).per(per_page)
|
@events = @events.page(params[:page]).per(per_page)
|
||||||
paginate @events, per_page: per_page
|
paginate @events, per_page: per_page
|
||||||
|
@ -11,9 +11,9 @@ class OpenAPI::V1::InvoicesController < OpenAPI::V1::BaseController
|
|||||||
.includes(invoicing_profile: :user)
|
.includes(invoicing_profile: :user)
|
||||||
.references(:invoicing_profiles)
|
.references(:invoicing_profiles)
|
||||||
|
|
||||||
@invoices = @invoices.where(invoicing_profiles: { user_id: params[:user_id] }) if params[:user_id].present?
|
@invoices = @invoices.where(invoicing_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
|
||||||
|
|
||||||
return unless params[:page].present?
|
return if params[:page].blank?
|
||||||
|
|
||||||
@invoices = @invoices.page(params[:page]).per(per_page)
|
@invoices = @invoices.page(params[:page]).per(per_page)
|
||||||
paginate @invoices, per_page: per_page
|
paginate @invoices, per_page: per_page
|
||||||
@ -21,7 +21,7 @@ class OpenAPI::V1::InvoicesController < OpenAPI::V1::BaseController
|
|||||||
|
|
||||||
def download
|
def download
|
||||||
@invoice = Invoice.find(params[:id])
|
@invoice = Invoice.find(params[:id])
|
||||||
send_file File.join(Rails.root, @invoice.file), type: 'application/pdf', disposition: 'inline', filename: @invoice.filename
|
send_file Rails.root.join(@invoice.file), type: 'application/pdf', disposition: 'inline', filename: @invoice.filename
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -11,11 +11,11 @@ class OpenAPI::V1::ReservationsController < OpenAPI::V1::BaseController
|
|||||||
.includes(statistic_profile: :user)
|
.includes(statistic_profile: :user)
|
||||||
.references(:statistic_profiles)
|
.references(:statistic_profiles)
|
||||||
|
|
||||||
@reservations = @reservations.where(statistic_profiles: { user_id: params[:user_id] }) if params[:user_id].present?
|
@reservations = @reservations.where(statistic_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
|
||||||
@reservations = @reservations.where(reservable_type: format_type(params[:reservable_type])) if params[:reservable_type].present?
|
@reservations = @reservations.where(reservable_type: format_type(params[:reservable_type])) if params[:reservable_type].present?
|
||||||
@reservations = @reservations.where(reservable_id: params[:reservable_id]) if params[:reservable_id].present?
|
@reservations = @reservations.where(reservable_id: may_array(params[:reservable_id])) if params[:reservable_id].present?
|
||||||
|
|
||||||
return unless params[:page].present?
|
return if params[:page].blank?
|
||||||
|
|
||||||
@reservations = @reservations.page(params[:page]).per(per_page)
|
@reservations = @reservations.page(params[:page]).per(per_page)
|
||||||
paginate @reservations, per_page: per_page
|
paginate @reservations, per_page: per_page
|
||||||
|
@ -12,11 +12,10 @@ class OpenAPI::V1::UserTrainingsController < OpenAPI::V1::BaseController
|
|||||||
.references(:statistic_profiles)
|
.references(:statistic_profiles)
|
||||||
.order(created_at: :desc)
|
.order(created_at: :desc)
|
||||||
|
|
||||||
|
@user_trainings = @user_trainings.where(statistic_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
|
||||||
|
@user_trainings = @user_trainings.where(training_id: may_array(params[:training_id])) if params[:training_id].present?
|
||||||
|
|
||||||
@user_trainings = @user_trainings.where(statistic_profiles: { user_id: params[:user_id] }) if params[:user_id].present?
|
return if params[:page].blank?
|
||||||
@user_trainings = @user_trainings.where(training_id: params[:training_id]) if params[:training_id].present?
|
|
||||||
|
|
||||||
return unless params[:page].present?
|
|
||||||
|
|
||||||
@user_trainings = @user_trainings.page(params[:page]).per(per_page)
|
@user_trainings = @user_trainings.page(params[:page]).per(per_page)
|
||||||
paginate @user_trainings, per_page: per_page
|
paginate @user_trainings, per_page: per_page
|
||||||
|
@ -13,9 +13,9 @@ class OpenAPI::V1::UsersController < OpenAPI::V1::BaseController
|
|||||||
email_param = params[:email].is_a?(String) ? params[:email].downcase : params[:email].map(&:downcase)
|
email_param = params[:email].is_a?(String) ? params[:email].downcase : params[:email].map(&:downcase)
|
||||||
@users = @users.where(email: email_param)
|
@users = @users.where(email: email_param)
|
||||||
end
|
end
|
||||||
@users = @users.where(id: params[:user_id]) if params[:user_id].present?
|
@users = @users.where(id: may_array(params[:user_id])) if params[:user_id].present?
|
||||||
|
|
||||||
return unless params[:page].present?
|
return if params[:page].blank?
|
||||||
|
|
||||||
@users = @users.page(params[:page]).per(per_page)
|
@users = @users.page(params[:page]).per(per_page)
|
||||||
paginate @users, per_page: per_page
|
paginate @users, per_page: per_page
|
||||||
|
@ -13,61 +13,76 @@ class OpenAPI::V1::AccountingDoc < OpenAPI::V1::BaseDoc
|
|||||||
|
|
||||||
doc_for :index do
|
doc_for :index do
|
||||||
api :GET, "/#{API_VERSION}/accounting", 'Accounting lines'
|
api :GET, "/#{API_VERSION}/accounting", 'Accounting lines'
|
||||||
description 'All accounting lines, with optional pagination and dates filtering. Ordered by *date* descendant.'
|
description 'All accounting lines, paginated (necessarily becauce there is a lot of data) with optional dates filtering. ' \
|
||||||
|
'Ordered by *date* descendant.'
|
||||||
param_group :pagination
|
param_group :pagination
|
||||||
param :after, DateTime, optional: true, desc: 'Filter accounting lines to lines after the given date.'
|
param :after, DateTime, optional: true, desc: 'Filter accounting lines to lines after the given date.'
|
||||||
param :before, DateTime, optional: true, desc: 'Filter accounting lines to lines before the given date.'
|
param :before, DateTime, optional: true, desc: 'Filter accounting lines to lines before the given date.'
|
||||||
|
param :invoice_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various invoices.'
|
||||||
|
|
||||||
example <<-LINES
|
example <<-LINES
|
||||||
# /open_api/v1/accounting?after=2022-01-01T00:00:00+02:00&page=1&per_page=3
|
# /open_api/v1/accounting?after=2022-01-01T00:00:00+02:00&page=1&per_page=3
|
||||||
{
|
{
|
||||||
"lines": [
|
"lines": [
|
||||||
{
|
{
|
||||||
|
"id": 1,
|
||||||
|
"line_type": "client",
|
||||||
"journal_code": "VT01",
|
"journal_code": "VT01",
|
||||||
"date": "2022-01-02T18:14:21+01:00",
|
"date": "2022-01-02T18:14:21+01:00",
|
||||||
"account_code": "5802",
|
"account_code": "5802",
|
||||||
"account_label": "Wallet customers",
|
|
||||||
"analytical_code": "P3D71",
|
|
||||||
"invoice": {
|
|
||||||
"reference": "22010009/VL",
|
|
||||||
"id": 274,
|
|
||||||
"label": "Dupont Marcel, 22010009/VL, subscr.",
|
|
||||||
},
|
|
||||||
"user_id": 6512,
|
|
||||||
"amount": 200,
|
|
||||||
"currency": "EUR",
|
|
||||||
"invoice_url": "/open_api/v1/invoices/247/download"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"journal_code": "VT01",
|
|
||||||
"date": "2022-01-02T18:14:21+01:00",
|
|
||||||
"account_code": "5801",
|
|
||||||
"account_label": "Card customers",
|
"account_label": "Card customers",
|
||||||
"analytical_code": "P3D71",
|
"analytical_code": "",
|
||||||
"invoice": {
|
"invoice": {
|
||||||
"reference": "22010009/VL",
|
"reference": "22010009/VL",
|
||||||
"id": 274,
|
"id": 274,
|
||||||
"label": "Dupont Marcel, 22010009/VL, subscr.",
|
"label": "Subscription of Dupont Marcel for 1 month starting from 2022, january 2nd",
|
||||||
|
"url": "/open_api/v1/invoices/247/download"
|
||||||
},
|
},
|
||||||
"user_id": 6512,
|
"user_invoicing_profile_id": 6512,
|
||||||
"amount": 100,
|
"debit": 1400,
|
||||||
|
"credit": 0
|
||||||
"currency": "EUR",
|
"currency": "EUR",
|
||||||
"invoice_url": "/open_api/v1/invoices/247/download"
|
"summary": "Dupont Marcel, 22010009/VL, subscr."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": 2,
|
||||||
|
"line_type": "item",
|
||||||
"journal_code": "VT01",
|
"journal_code": "VT01",
|
||||||
"date": "2022-01-02T18:14:21+01:00",
|
"date": "2022-01-02T18:14:21+01:00",
|
||||||
"account_code": "5802",
|
"account_code": "7071",
|
||||||
"account_label": "Wallet customers",
|
"account_label": "Subscriptions",
|
||||||
"analytical_code": "P3D71",
|
"analytical_code": "P3D71",
|
||||||
"invoice": {
|
"invoice": {
|
||||||
"reference": "22010009/VL",
|
"reference": "22010009/VL",
|
||||||
"id": 274,
|
"id": 274,
|
||||||
"label": "Dupont Marcel, 22010009/VL, subscr.",
|
"label": "Subscription of Dupont Marcel for 1 month starting from 2022, january 2nd",
|
||||||
|
"url": "/open_api/v1/invoices/247/download"
|
||||||
},
|
},
|
||||||
"user_id": 6512,
|
"user_invoicing_profile_id": 6512,
|
||||||
"amount": 200,
|
"debit": 0,
|
||||||
|
"credit": 1167
|
||||||
"currency": "EUR",
|
"currency": "EUR",
|
||||||
"invoice_url": "/open_api/v1/invoices/247/download"
|
"summary": "Dupont Marcel, 22010009/VL, subscr."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"line_type": "vat",
|
||||||
|
"journal_code": "VT01",
|
||||||
|
"date": "2022-01-02T18:14:21+01:00",
|
||||||
|
"account_code": "4457",
|
||||||
|
"account_label": "Collected VAT",
|
||||||
|
"analytical_code": "P3D71",
|
||||||
|
"invoice": {
|
||||||
|
"reference": "22010009/VL",
|
||||||
|
"id": 274,
|
||||||
|
"label": "Subscription of Dupont Marcel for 1 month starting from 2022, january 2nd",
|
||||||
|
"url": "/open_api/v1/invoices/247/download"
|
||||||
|
},
|
||||||
|
"user_invoicing_profile_id": 6512,
|
||||||
|
"debit": 0,
|
||||||
|
"credit": 233
|
||||||
|
"currency": "EUR",
|
||||||
|
"summary": "Dupont Marcel, 22010009/VL, subscr."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ export const FormMultiFileUpload = <TFieldValues extends FieldValues, TContext e
|
|||||||
return (
|
return (
|
||||||
<div className={`form-multi-file-upload ${className || ''}`}>
|
<div className={`form-multi-file-upload ${className || ''}`}>
|
||||||
<div className="list">
|
<div className="list">
|
||||||
{output.map((field: FileType, index) => (
|
{output?.map((field: FileType, index) => (
|
||||||
<FormFileUpload key={index}
|
<FormFileUpload key={index}
|
||||||
defaultFile={field}
|
defaultFile={field}
|
||||||
id={`${id}.${index}`}
|
id={`${id}.${index}`}
|
||||||
|
@ -83,15 +83,22 @@ module ApplicationHelper
|
|||||||
(BigDecimal(amount.to_s) * 100.0).to_f
|
(BigDecimal(amount.to_s) * 100.0).to_f
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return the given parameter as it, or as an array if it can be parsed as an array
|
||||||
|
def may_array(param)
|
||||||
|
return param unless param&.chars&.first == '[' && param&.chars&.last == ']'
|
||||||
|
|
||||||
|
param.gsub(/[\[\]]/i, '').split(',')
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
## inspired by gems/actionview-4.2.5/lib/action_view/helpers/translation_helper.rb
|
## inspired by gems/actionview-4.2.5/lib/action_view/helpers/translation_helper.rb
|
||||||
# rubocop:disable Rails/HelperInstanceVariable
|
# rubocop:disable Rails/HelperInstanceVariable
|
||||||
def scope_key_by_partial(key)
|
def scope_key_by_partial(key)
|
||||||
if key.to_s.first == '.'
|
if key.to_s.first == '.'
|
||||||
raise "Cannot use t(#{key.inspect}) shortcut because path is not available" unless @virtual_path
|
raise "Cannot use t(#{key.inspect}) shortcut because path is not available" unless @virtual_path # rubocop:disable Rails/HelperInstanceVariable
|
||||||
|
|
||||||
@virtual_path.gsub(%r{/_?}, '.') + key.to_s
|
@virtual_path.gsub(%r{/_?}, '.') + key.to_s # rubocop:disable Rails/HelperInstanceVariable
|
||||||
else
|
else
|
||||||
key
|
key
|
||||||
end
|
end
|
||||||
|
@ -53,74 +53,19 @@ class PDF::Invoice < Prawn::Document
|
|||||||
end
|
end
|
||||||
|
|
||||||
# user/organization's information
|
# user/organization's information
|
||||||
if invoice.invoicing_profile.organization
|
name = Invoices::RecipientService.name(invoice)
|
||||||
name = invoice.invoicing_profile.organization.name
|
others = Invoices::RecipientService.organization_data(invoice)
|
||||||
full_name = "#{name} (#{invoice.invoicing_profile.full_name})"
|
address = Invoices::RecipientService.address(invoice)
|
||||||
others = invoice.invoicing_profile.user_profile_custom_fields&.joins(:profile_custom_field)
|
|
||||||
&.where('profile_custom_fields.actived' => true)
|
|
||||||
&.order('profile_custom_fields.id ASC')
|
|
||||||
&.select { |f| f.value.present? }
|
|
||||||
&.map do |f|
|
|
||||||
"#{f.profile_custom_field.label}: #{f.value}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
name = invoice.invoicing_profile.full_name
|
|
||||||
full_name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
address = if invoice&.invoicing_profile&.organization&.address
|
|
||||||
invoice.invoicing_profile.organization.address.address
|
|
||||||
elsif invoice&.invoicing_profile&.address
|
|
||||||
invoice.invoicing_profile.address.address
|
|
||||||
else
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
text_box "<b>#{name}</b>\n#{invoice.invoicing_profile.email}\n#{address}\n#{others&.join("\n")}",
|
text_box "<b>#{name}</b>\n#{invoice.invoicing_profile.email}\n#{address}\n#{others&.join("\n")}",
|
||||||
at: [bounds.width - 180, bounds.top - 49],
|
at: [bounds.width - 180, bounds.top - 49],
|
||||||
width: 180,
|
width: 180,
|
||||||
align: :right,
|
align: :right,
|
||||||
inline_format: true
|
inline_format: true
|
||||||
name = full_name
|
|
||||||
|
|
||||||
# object
|
# object
|
||||||
move_down 28
|
move_down 28
|
||||||
if invoice.is_a?(Avoir)
|
text "#{I18n.t('invoices.object')} #{Invoices::LabelService.build(invoice)}"
|
||||||
object = if invoice.main_item.object_type == WalletTransaction.name
|
|
||||||
I18n.t('invoices.wallet_credit')
|
|
||||||
else
|
|
||||||
I18n.t('invoices.cancellation_of_invoice_REF', REF: invoice.invoice.reference)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
case invoice.main_item.object_type
|
|
||||||
when 'Reservation'
|
|
||||||
object = I18n.t('invoices.reservation_of_USER_on_DATE_at_TIME',
|
|
||||||
USER: name,
|
|
||||||
DATE: I18n.l(invoice.main_item.object.slots[0].start_at.to_date),
|
|
||||||
TIME: I18n.l(invoice.main_item.object.slots[0].start_at, format: :hour_minute))
|
|
||||||
invoice.invoice_items.each do |item|
|
|
||||||
next unless item.object_type == Subscription.name
|
|
||||||
|
|
||||||
subscription = item.object
|
|
||||||
cancellation = invoice.is_a?(Avoir) ? "#{I18n.t('invoices.cancellation')} - " : ''
|
|
||||||
object = "\n- #{object}\n- #{cancellation + subscription_verbose(subscription, name)}"
|
|
||||||
break
|
|
||||||
end
|
|
||||||
when 'Subscription'
|
|
||||||
object = subscription_verbose(invoice.main_item.object, name)
|
|
||||||
when 'OfferDay'
|
|
||||||
object = offer_day_verbose(invoice.main_item.object, name)
|
|
||||||
when 'Error'
|
|
||||||
object = I18n.t('invoices.error_invoice')
|
|
||||||
when 'StatisticProfilePrepaidPack'
|
|
||||||
object = I18n.t('invoices.prepaid_pack')
|
|
||||||
when 'OrderItem'
|
|
||||||
object = I18n.t('invoices.order')
|
|
||||||
else
|
|
||||||
Rails.logger.error "specified main_item.object_type type (#{invoice.main_item.object_type}) is unknown"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
text "#{I18n.t('invoices.object')} #{object}"
|
|
||||||
|
|
||||||
# details table of the invoice's elements
|
# details table of the invoice's elements
|
||||||
move_down 20
|
move_down 20
|
||||||
@ -370,22 +315,6 @@ class PDF::Invoice < Prawn::Document
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscription_verbose(subscription, username)
|
|
||||||
subscription_start_at = subscription.expired_at - subscription.plan.duration
|
|
||||||
duration_verbose = I18n.t("duration.#{subscription.plan.interval}", count: subscription.plan.interval_count)
|
|
||||||
I18n.t('invoices.subscription_of_NAME_for_DURATION_starting_from_DATE',
|
|
||||||
NAME: username,
|
|
||||||
DURATION: duration_verbose,
|
|
||||||
DATE: I18n.l(subscription_start_at.to_date))
|
|
||||||
end
|
|
||||||
|
|
||||||
def offer_day_verbose(offer_day, username)
|
|
||||||
I18n.t('invoices.subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE',
|
|
||||||
NAME: username,
|
|
||||||
STARTDATE: I18n.l(offer_day.start_at.to_date),
|
|
||||||
ENDDATE: I18n.l(offer_day.end_at.to_date))
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
|
# Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
|
||||||
# The supported tags are <b>, <u>, <i> and <br>.
|
# The supported tags are <b>, <u>, <i> and <br>.
|
||||||
|
74
app/services/invoices/label_service.rb
Normal file
74
app/services/invoices/label_service.rb
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# module definition
|
||||||
|
module Invoices; end
|
||||||
|
|
||||||
|
# Build a label for the given invoice
|
||||||
|
class Invoices::LabelService
|
||||||
|
class << self
|
||||||
|
def build(invoice)
|
||||||
|
username = Invoices::RecipientService.name(invoice)
|
||||||
|
if invoice.is_a?(Avoir)
|
||||||
|
avoir_label(invoice)
|
||||||
|
else
|
||||||
|
case invoice.main_item.object_type
|
||||||
|
when 'Reservation'
|
||||||
|
reservation_invoice_label(invoice, username)
|
||||||
|
when 'Subscription'
|
||||||
|
subscription_label(invoice.main_item.object, username)
|
||||||
|
when 'OfferDay'
|
||||||
|
offer_day_label(invoice.main_item.object, username)
|
||||||
|
when 'Error'
|
||||||
|
I18n.t('invoices.error_invoice')
|
||||||
|
when 'StatisticProfilePrepaidPack'
|
||||||
|
I18n.t('invoices.prepaid_pack')
|
||||||
|
when 'OrderItem'
|
||||||
|
I18n.t('invoices.order')
|
||||||
|
else
|
||||||
|
Rails.logger.error "specified main_item.object_type type (#{invoice.main_item.object_type}) is unknown"
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def avoir_label(invoice)
|
||||||
|
return I18n.t('invoices.wallet_credit') if invoice.main_item.object_type == WalletTransaction.name
|
||||||
|
|
||||||
|
I18n.t('invoices.cancellation_of_invoice_REF', REF: invoice.invoice.reference)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reservation_invoice_label(invoice, username)
|
||||||
|
label = I18n.t('invoices.reservation_of_USER_on_DATE_at_TIME',
|
||||||
|
USER: username,
|
||||||
|
DATE: I18n.l(invoice.main_item.object.slots[0].start_at.to_date),
|
||||||
|
TIME: I18n.l(invoice.main_item.object.slots[0].start_at, format: :hour_minute))
|
||||||
|
invoice.invoice_items.each do |item|
|
||||||
|
next unless item.object_type == Subscription.name
|
||||||
|
|
||||||
|
subscription = item.object
|
||||||
|
cancellation = invoice.is_a?(Avoir) ? "#{I18n.t('invoices.cancellation')} - " : ''
|
||||||
|
label = "\n- #{label}\n- #{cancellation + subscription_label(subscription, username)}"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
label
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscription_label(subscription, username)
|
||||||
|
subscription_start_at = subscription.expired_at - subscription.plan.duration
|
||||||
|
duration_verbose = I18n.t("duration.#{subscription.plan.interval}", count: subscription.plan.interval_count)
|
||||||
|
I18n.t('invoices.subscription_of_NAME_for_DURATION_starting_from_DATE',
|
||||||
|
NAME: username,
|
||||||
|
DURATION: duration_verbose,
|
||||||
|
DATE: I18n.l(subscription_start_at.to_date))
|
||||||
|
end
|
||||||
|
|
||||||
|
def offer_day_label(offer_day, username)
|
||||||
|
I18n.t('invoices.subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE',
|
||||||
|
NAME: username,
|
||||||
|
STARTDATE: I18n.l(offer_day.start_at.to_date),
|
||||||
|
ENDDATE: I18n.l(offer_day.end_at.to_date))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
app/services/invoices/recipient_service.rb
Normal file
41
app/services/invoices/recipient_service.rb
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# module definition
|
||||||
|
module Invoices; end
|
||||||
|
|
||||||
|
# The recipient may be be an individual or an organization
|
||||||
|
class Invoices::RecipientService
|
||||||
|
class << self
|
||||||
|
# Get the full name of the recipient for the given invoice.
|
||||||
|
def name(invoice)
|
||||||
|
if invoice.invoicing_profile.organization
|
||||||
|
name = invoice.invoicing_profile.organization.name
|
||||||
|
"#{name} (#{invoice.invoicing_profile.full_name})"
|
||||||
|
else
|
||||||
|
invoice.invoicing_profile.full_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the street address of the recipient for the given invoice.
|
||||||
|
def address(invoice)
|
||||||
|
if invoice.invoicing_profile&.organization&.address
|
||||||
|
invoice.invoicing_profile.organization.address.address
|
||||||
|
elsif invoice.invoicing_profile&.address
|
||||||
|
invoice.invoicing_profile.address.address
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the optional data in profile_custom_fields, if the recipient is an organization
|
||||||
|
def organization_data(invoice)
|
||||||
|
return unless invoice.invoicing_profile.organization
|
||||||
|
|
||||||
|
invoice.invoicing_profile.user_profile_custom_fields&.joins(:profile_custom_field)
|
||||||
|
&.where('profile_custom_fields.actived' => true)
|
||||||
|
&.order('profile_custom_fields.id ASC')
|
||||||
|
&.select { |f| f.value.present? }
|
||||||
|
&.map { |f| "#{f.profile_custom_field.label}: #{f.value}" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -62,7 +62,7 @@ class InvoicesService
|
|||||||
##
|
##
|
||||||
# Create an Invoice with an associated array of InvoiceItem matching the given parameters
|
# Create an Invoice with an associated array of InvoiceItem matching the given parameters
|
||||||
# @param payment_details {Hash} as generated by ShoppingCart.total
|
# @param payment_details {Hash} as generated by ShoppingCart.total
|
||||||
# @param operator_profile_id {Number} ID of the user that operates the invoice generation (may be an admin, a manager or the customer himself)
|
# @param operator_profile_id {Number} ID of the user that operates the invoice generation (an admin, a manager or the customer himself)
|
||||||
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>} the booked reservation and/or subscription or pack
|
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>} the booked reservation and/or subscription or pack
|
||||||
# @param user {User} the customer
|
# @param user {User} the customer
|
||||||
# @param payment_id {String} ID of the payment, a returned by the gateway, if the current invoice is paid by card
|
# @param payment_id {String} ID of the payment, a returned by the gateway, if the current invoice is paid by card
|
||||||
@ -100,17 +100,17 @@ class InvoicesService
|
|||||||
def self.generate_invoice_items(invoice, payment_details, objects)
|
def self.generate_invoice_items(invoice, payment_details, objects)
|
||||||
objects.each_with_index do |object, index|
|
objects.each_with_index do |object, index|
|
||||||
if object.is_a?(Reservation) && object.reservable.is_a?(Event)
|
if object.is_a?(Reservation) && object.reservable.is_a?(Event)
|
||||||
InvoicesService.generate_event_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_event_item(invoice, object, payment_details, main: index.zero?)
|
||||||
elsif object.is_a?(Subscription)
|
elsif object.is_a?(Subscription)
|
||||||
InvoicesService.generate_subscription_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_subscription_item(invoice, object, payment_details, main: index.zero?)
|
||||||
elsif object.is_a?(Reservation)
|
elsif object.is_a?(Reservation)
|
||||||
InvoicesService.generate_reservation_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_reservation_item(invoice, object, payment_details, main: index.zero?)
|
||||||
elsif object.is_a?(StatisticProfilePrepaidPack)
|
elsif object.is_a?(StatisticProfilePrepaidPack)
|
||||||
InvoicesService.generate_prepaid_pack_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_prepaid_pack_item(invoice, object, payment_details, main: index.zero?)
|
||||||
elsif object.is_a?(OrderItem)
|
elsif object.is_a?(OrderItem)
|
||||||
InvoicesService.generate_order_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_order_item(invoice, object, payment_details, main: index.zero?)
|
||||||
else
|
else
|
||||||
InvoicesService.generate_generic_item(invoice, object, payment_details, index.zero?)
|
InvoicesService.generate_generic_item(invoice, object, payment_details, main: index.zero?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -119,7 +119,7 @@ class InvoicesService
|
|||||||
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
||||||
# This method must be called if reservation.reservable is an Event
|
# This method must be called if reservation.reservable is an Event
|
||||||
##
|
##
|
||||||
def self.generate_event_item(invoice, reservation, payment_details, main = false)
|
def self.generate_event_item(invoice, reservation, payment_details, main: false)
|
||||||
raise TypeError unless reservation.reservable.is_a? Event
|
raise TypeError unless reservation.reservable.is_a? Event
|
||||||
|
|
||||||
reservation.slots_reservations.map(&:slot).each do |slot|
|
reservation.slots_reservations.map(&:slot).each do |slot|
|
||||||
@ -128,12 +128,12 @@ class InvoicesService
|
|||||||
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute} " \
|
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute} " \
|
||||||
"- #{I18n.l slot.end_at, format: :hour_minute}"
|
"- #{I18n.l slot.end_at, format: :hour_minute}"
|
||||||
else
|
else
|
||||||
I18n.t('events.from_STARTDATE_to_ENDDATE',
|
"#{I18n.t('events.from_STARTDATE_to_ENDDATE',
|
||||||
STARTDATE: I18n.l(slot.start_at.to_date, format: :long),
|
STARTDATE: I18n.l(slot.start_at.to_date, format: :long),
|
||||||
ENDDATE: I18n.l(slot.end_at.to_date, format: :long)) + ' ' +
|
ENDDATE: I18n.l(slot.end_at.to_date, format: :long))} " \
|
||||||
I18n.t('events.from_STARTTIME_to_ENDTIME',
|
"#{I18n.t('events.from_STARTTIME_to_ENDTIME',
|
||||||
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
|
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
|
||||||
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))
|
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))}"
|
||||||
end
|
end
|
||||||
|
|
||||||
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
||||||
@ -150,7 +150,7 @@ class InvoicesService
|
|||||||
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
||||||
# This method must be called if reservation.reservable is a Space, a Machine or a Training
|
# This method must be called if reservation.reservable is a Space, a Machine or a Training
|
||||||
##
|
##
|
||||||
def self.generate_reservation_item(invoice, reservation, payment_details, main = false)
|
def self.generate_reservation_item(invoice, reservation, payment_details, main: false)
|
||||||
raise TypeError unless [Space, Machine, Training].include? reservation.reservable.class
|
raise TypeError unless [Space, Machine, Training].include? reservation.reservable.class
|
||||||
|
|
||||||
reservation.slots_reservations.map(&:slot).each do |slot|
|
reservation.slots_reservations.map(&:slot).each do |slot|
|
||||||
@ -171,7 +171,7 @@ class InvoicesService
|
|||||||
# Generate an InvoiceItem for the given subscription and save it in invoice.invoice_items.
|
# Generate an InvoiceItem for the given subscription and save it in invoice.invoice_items.
|
||||||
# This method must be called only with a valid subscription
|
# This method must be called only with a valid subscription
|
||||||
##
|
##
|
||||||
def self.generate_subscription_item(invoice, subscription, payment_details, main = false)
|
def self.generate_subscription_item(invoice, subscription, payment_details, main: false)
|
||||||
raise TypeError unless subscription
|
raise TypeError unless subscription
|
||||||
|
|
||||||
invoice.invoice_items.push InvoiceItem.new(
|
invoice.invoice_items.push InvoiceItem.new(
|
||||||
@ -186,7 +186,7 @@ class InvoicesService
|
|||||||
# Generate an InvoiceItem for the given StatisticProfilePrepaidPack and save it in invoice.invoice_items.
|
# Generate an InvoiceItem for the given StatisticProfilePrepaidPack and save it in invoice.invoice_items.
|
||||||
# This method must be called only with a valid pack-statistic_profile relation
|
# This method must be called only with a valid pack-statistic_profile relation
|
||||||
##
|
##
|
||||||
def self.generate_prepaid_pack_item(invoice, pack, payment_details, main = false)
|
def self.generate_prepaid_pack_item(invoice, pack, payment_details, main: false)
|
||||||
raise TypeError unless pack
|
raise TypeError unless pack
|
||||||
|
|
||||||
invoice.invoice_items.push InvoiceItem.new(
|
invoice.invoice_items.push InvoiceItem.new(
|
||||||
@ -201,7 +201,7 @@ class InvoicesService
|
|||||||
# Generate an InvoiceItem for given OrderItem and sva it in invoice.invoice_items
|
# Generate an InvoiceItem for given OrderItem and sva it in invoice.invoice_items
|
||||||
# This method must be called whith an order
|
# This method must be called whith an order
|
||||||
##
|
##
|
||||||
def self.generate_order_item(invoice, item, _payment_details, main = false)
|
def self.generate_order_item(invoice, item, _payment_details, main: false)
|
||||||
raise TypeError unless item
|
raise TypeError unless item
|
||||||
|
|
||||||
invoice.invoice_items.push InvoiceItem.new(
|
invoice.invoice_items.push InvoiceItem.new(
|
||||||
@ -212,7 +212,7 @@ class InvoicesService
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_generic_item(invoice, item, payment_details, main = false)
|
def self.generate_generic_item(invoice, item, payment_details, main: false)
|
||||||
invoice.invoice_items.push InvoiceItem.new(
|
invoice.invoice_items.push InvoiceItem.new(
|
||||||
amount: payment_details[:elements][item.class.name.to_sym],
|
amount: payment_details[:elements][item.class.name.to_sym],
|
||||||
description: item.class.name,
|
description: item.class.name,
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
# Provides methods for Prices
|
# Provides methods for Prices
|
||||||
class PriceService
|
class PriceService
|
||||||
|
extend ApplicationHelper
|
||||||
|
|
||||||
def self.list(filters)
|
def self.list(filters)
|
||||||
prices = Price.where(nil)
|
prices = Price.where(nil)
|
||||||
|
|
||||||
prices = prices.where(priceable_type: filters[:priceable_type]) if filters[:priceable_type].present?
|
prices = prices.where(priceable_type: filters[:priceable_type]) if filters[:priceable_type].present?
|
||||||
prices = prices.where(priceable_id: filters[:priceable_id]) if filters[:priceable_id].present?
|
prices = prices.where(priceable_id: may_array(filters[:priceable_id])) if filters[:priceable_id].present?
|
||||||
prices = prices.where(group_id: filters[:group_id]) if filters[:group_id].present?
|
prices = prices.where(group_id: may_array(filters[:group_id])) if filters[:group_id].present?
|
||||||
if filters[:plan_id].present?
|
if filters[:plan_id].present?
|
||||||
plan_id = /no|nil|null|undefined/i.match?(filters[:plan_id]) ? nil : filters[:plan_id]
|
plan_id = /no|nil|null|undefined/i.match?(filters[:plan_id]) ? nil : filters[:plan_id]
|
||||||
prices = prices.where(plan_id: plan_id)
|
prices = prices.where(plan_id: plan_id)
|
||||||
|
12
app/views/open_api/v1/accounting/index.json.jbuilder
Normal file
12
app/views/open_api/v1/accounting/index.json.jbuilder
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
|
||||||
|
json.lines @lines do |line|
|
||||||
|
json.extract! line, :id, :line_type, :journal_code, :date, :account_code, :account_label, :analytical_code, :debit, :credit, :currency, :summary
|
||||||
|
json.invoice do
|
||||||
|
json.extract! line.invoice, :reference, :id
|
||||||
|
json.label Invoices::LabelService.build(line.invoice)
|
||||||
|
json.url download_open_api_v1_invoice_path(line.invoice)
|
||||||
|
end
|
||||||
|
json.user_invoicing_profile_id line.invoicing_profile_id
|
||||||
|
end
|
@ -279,6 +279,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
resources :events
|
resources :events
|
||||||
resources :availabilities
|
resources :availabilities
|
||||||
|
resources :accounting
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,5 +12,58 @@ class OpenApi::AccountingTest < ActionDispatch::IntegrationTest
|
|||||||
test 'list all accounting lines' do
|
test 'list all accounting lines' do
|
||||||
get '/open_api/v1/accounting', headers: open_api_headers(@token)
|
get '/open_api/v1/accounting', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
lines = json_response(response.body)
|
||||||
|
assert_not_empty lines[:lines]
|
||||||
|
assert_not_nil lines[:lines][0][:id]
|
||||||
|
assert_not_empty lines[:lines][0][:line_type]
|
||||||
|
assert_not_empty lines[:lines][0][:journal_code]
|
||||||
|
assert_not_empty lines[:lines][0][:date]
|
||||||
|
assert_not_empty lines[:lines][0][:account_code]
|
||||||
|
assert_not_empty lines[:lines][0][:account_label]
|
||||||
|
assert_nil lines[:lines][0][:analytical_code]
|
||||||
|
assert_not_nil lines[:lines][0][:invoice]
|
||||||
|
assert_not_empty lines[:lines][0][:invoice][:reference]
|
||||||
|
assert_not_nil lines[:lines][0][:invoice][:id]
|
||||||
|
assert_not_empty lines[:lines][0][:invoice][:label]
|
||||||
|
assert_not_empty lines[:lines][0][:invoice][:url]
|
||||||
|
assert_not_nil lines[:lines][0][:user_invoicing_profile_id]
|
||||||
|
assert_not_nil lines[:lines][0][:debit]
|
||||||
|
assert_not_nil lines[:lines][0][:credit]
|
||||||
|
assert_not_empty lines[:lines][0][:currency]
|
||||||
|
assert_not_empty lines[:lines][0][:summary]
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'list all accounting lines with pagination' do
|
||||||
|
get '/open_api/v1/accounting?page=1&per_page=5', headers: open_api_headers(@token)
|
||||||
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
lines = json_response(response.body)
|
||||||
|
assert_equal 5, lines[:lines].count
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'list all accounting lines with dates filtering' do
|
||||||
|
get '/open_api/v1/accounting?after=2022-09-01T00:00:00+02:00&before=2022-09-30T23:59:59+02:00', headers: open_api_headers(@token)
|
||||||
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
lines = json_response(response.body)
|
||||||
|
assert lines[:lines].count.positive?
|
||||||
|
assert(lines[:lines].all? do |line|
|
||||||
|
date = DateTime.parse(line[:date])
|
||||||
|
date >= '2022-09-01'.to_date && date <= '2022-09-30'.to_date
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'list all accounting lines with invoices filtering' do
|
||||||
|
get '/open_api/v1/accounting?invoice_id=[1,2,3]', headers: open_api_headers(@token)
|
||||||
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
lines = json_response(response.body)
|
||||||
|
assert lines[:lines].count.positive?
|
||||||
|
assert(lines[:lines].all? { |line| [1, 2, 3].include?(line[:invoice][:id]) })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,4 +29,13 @@ class OpenApi::PricesTest < ActionDispatch::IntegrationTest
|
|||||||
|
|
||||||
assert_equal [1], json_response(response.body)[:prices].pluck(:priceable_id).uniq
|
assert_equal [1], json_response(response.body)[:prices].pluck(:priceable_id).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'list all prices for some groups' do
|
||||||
|
get '/open_api/v1/prices?group_id=[1,2]', headers: open_api_headers(@token)
|
||||||
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
prices = json_response(response.body)
|
||||||
|
assert_equal [1, 2], prices[:prices].pluck(:group_id).uniq.sort
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,6 +12,7 @@ class OpenApi::ReservationsTest < ActionDispatch::IntegrationTest
|
|||||||
test 'list all reservations' do
|
test 'list all reservations' do
|
||||||
get '/open_api/v1/reservations', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
assert_equal Reservation.count, json_response(response.body)[:reservations].length
|
assert_equal Reservation.count, json_response(response.body)[:reservations].length
|
||||||
end
|
end
|
||||||
@ -19,27 +20,50 @@ class OpenApi::ReservationsTest < ActionDispatch::IntegrationTest
|
|||||||
test 'list all reservations with pagination' do
|
test 'list all reservations with pagination' do
|
||||||
get '/open_api/v1/reservations?page=1&per_page=5', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations?page=1&per_page=5', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
assert json_response(response.body)[:reservations].length <= 5
|
reservations = json_response(response.body)
|
||||||
|
assert reservations[:reservations].count <= 5
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all reservations for a user' do
|
test 'list all reservations for a user' do
|
||||||
get '/open_api/v1/reservations?user_id=3', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations?user_id=3', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
reservations = json_response(response.body)
|
||||||
|
assert_not_empty reservations[:reservations]
|
||||||
|
assert_equal [3], reservations[:reservations].pluck(:user_id).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all reservations for a user with pagination' do
|
test 'list all reservations for a user with pagination' do
|
||||||
get '/open_api/v1/reservations?user_id=3&page=1&per_page=5', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations?user_id=3&page=1&per_page=5', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
reservations = json_response(response.body)
|
||||||
|
assert reservations[:reservations].count <= 5
|
||||||
|
assert_equal [3], reservations[:reservations].pluck(:user_id).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all machine reservations for a user' do
|
test 'list all machine reservations for a user' do
|
||||||
get '/open_api/v1/reservations?reservable_type=Machine&user_id=3', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations?reservable_type=Machine&user_id=3', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
reservations = json_response(response.body)
|
||||||
|
assert_not_empty reservations[:reservations]
|
||||||
|
assert_equal [3], reservations[:reservations].pluck(:user_id).uniq
|
||||||
|
assert_equal ['Machine'], reservations[:reservations].pluck(:reservable_type).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all machine 4 reservations' do
|
test 'list all machine 4 reservations' do
|
||||||
get '/open_api/v1/reservations?reservable_type=Machine&reservable_id=4', headers: open_api_headers(@token)
|
get '/open_api/v1/reservations?reservable_type=Machine&reservable_id=4', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
reservations = json_response(response.body)
|
||||||
|
assert_not_empty reservations[:reservations]
|
||||||
|
assert_equal [4], reservations[:reservations].pluck(:reservable_id).uniq
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -22,6 +22,21 @@ class OpenApi::UsersTest < ActionDispatch::IntegrationTest
|
|||||||
test 'list all users filtering by IDs' do
|
test 'list all users filtering by IDs' do
|
||||||
get '/open_api/v1/users?user_id=[3,4,5]', headers: open_api_headers(@token)
|
get '/open_api/v1/users?user_id=[3,4,5]', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
users = json_response(response.body)
|
||||||
|
assert users[:users].count.positive?
|
||||||
|
assert(users[:users].all? { |user| [3, 4, 5].include?(user[:id]) })
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'list a user filtering by ID' do
|
||||||
|
get '/open_api/v1/users?user_id=2', headers: open_api_headers(@token)
|
||||||
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
users = json_response(response.body)
|
||||||
|
assert_equal 1, users[:users].count
|
||||||
|
assert_equal 2, users[:users].first[:id]
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all users filtering by email' do
|
test 'list all users filtering by email' do
|
||||||
|
Loading…
x
Reference in New Issue
Block a user