mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-18 12:54:27 +01:00
Merge branch 'dev' for release 4.5.4
This commit is contained in:
commit
27046492d0
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,5 +1,18 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
|
## v4.5.4 2020 July 29
|
||||||
|
|
||||||
|
- Display an asterisk on the phone input field, in the admin creation form, if the phone is configured as required
|
||||||
|
- Keep the history of footprints data for verification purposes
|
||||||
|
- Enhanced rake task to create fixtures for test cases
|
||||||
|
- Automated tests for exports
|
||||||
|
- Fix a bug: unable to export reservations
|
||||||
|
- Fix a bug: unable to export subscriptions
|
||||||
|
- Fix a bug: unable to receive mails in development
|
||||||
|
- Fix a security issue: updated json to 2.3.1 to fix [CVE-2020-10663](https://nvd.nist.gov/vuln/detail/CVE-2020-10663)
|
||||||
|
- [TODO DEPLOY] `rails db:migrate`
|
||||||
|
- [TODO DEPLOY] `rails fablab:maintenance:save_footprint_data`
|
||||||
|
|
||||||
## v4.5.3 2020 July 21
|
## v4.5.3 2020 July 21
|
||||||
|
|
||||||
- Documentation of the easy upgrade procedure
|
- Documentation of the easy upgrade procedure
|
||||||
|
4
Gemfile
4
Gemfile
@ -24,8 +24,7 @@ gem 'jquery-rails'
|
|||||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||||
gem 'jbuilder', '~> 2.5'
|
gem 'jbuilder', '~> 2.5'
|
||||||
gem 'jbuilder_cache_multi'
|
gem 'jbuilder_cache_multi'
|
||||||
# bundle exec rake doc:rails generates the API under doc/api.
|
gem "json", ">= 2.3.0"
|
||||||
gem 'sdoc', '~> 0.4.0', group: :doc # TODO, remove unused ?
|
|
||||||
|
|
||||||
gem 'forgery'
|
gem 'forgery'
|
||||||
gem 'responders', '~> 2.0'
|
gem 'responders', '~> 2.0'
|
||||||
@ -61,6 +60,7 @@ group :test do
|
|||||||
gem 'pdf-reader'
|
gem 'pdf-reader'
|
||||||
gem 'vcr', '3.0.1'
|
gem 'vcr', '3.0.1'
|
||||||
gem 'webmock'
|
gem 'webmock'
|
||||||
|
gem 'rubyXL'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production, :staging do
|
group :production, :staging do
|
||||||
|
12
Gemfile.lock
12
Gemfile.lock
@ -206,7 +206,7 @@ GEM
|
|||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
json (1.8.6)
|
json (2.3.1)
|
||||||
jwt (2.2.1)
|
jwt (2.2.1)
|
||||||
kaminari (1.2.1)
|
kaminari (1.2.1)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
@ -344,7 +344,6 @@ GEM
|
|||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rb-readline (0.5.5)
|
rb-readline (0.5.5)
|
||||||
rdoc (4.3.0)
|
|
||||||
recurrence (1.3.0)
|
recurrence (1.3.0)
|
||||||
activesupport
|
activesupport
|
||||||
i18n
|
i18n
|
||||||
@ -366,6 +365,9 @@ GEM
|
|||||||
ruby-rc4 (0.1.5)
|
ruby-rc4 (0.1.5)
|
||||||
ruby-vips (2.0.17)
|
ruby-vips (2.0.17)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
|
rubyXL (3.4.14)
|
||||||
|
nokogiri (>= 1.10.8)
|
||||||
|
rubyzip (>= 1.3.0)
|
||||||
rubyzip (1.3.0)
|
rubyzip (1.3.0)
|
||||||
safe_yaml (1.0.5)
|
safe_yaml (1.0.5)
|
||||||
sass (3.4.25)
|
sass (3.4.25)
|
||||||
@ -377,9 +379,6 @@ GEM
|
|||||||
tilt (>= 1.1, < 3)
|
tilt (>= 1.1, < 3)
|
||||||
sassc (2.2.1)
|
sassc (2.2.1)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
sdoc (0.4.2)
|
|
||||||
json (~> 1.7, >= 1.7.7)
|
|
||||||
rdoc (~> 4.0)
|
|
||||||
seed_dump (3.3.1)
|
seed_dump (3.3.1)
|
||||||
activerecord (>= 4)
|
activerecord (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
@ -490,6 +489,7 @@ DEPENDENCIES
|
|||||||
jbuilder (~> 2.5)
|
jbuilder (~> 2.5)
|
||||||
jbuilder_cache_multi
|
jbuilder_cache_multi
|
||||||
jquery-rails
|
jquery-rails
|
||||||
|
json (>= 2.3.0)
|
||||||
kaminari
|
kaminari
|
||||||
listen (~> 3.0.5)
|
listen (~> 3.0.5)
|
||||||
message_format
|
message_format
|
||||||
@ -518,9 +518,9 @@ DEPENDENCIES
|
|||||||
responders (~> 2.0)
|
responders (~> 2.0)
|
||||||
rolify
|
rolify
|
||||||
rubocop (~> 0.61.1)
|
rubocop (~> 0.61.1)
|
||||||
|
rubyXL
|
||||||
rubyzip (>= 1.3.0)
|
rubyzip (>= 1.3.0)
|
||||||
sass-rails (~> 5.0, >= 5.0.6)
|
sass-rails (~> 5.0, >= 5.0.6)
|
||||||
sdoc (~> 0.4.0)
|
|
||||||
seed_dump
|
seed_dump
|
||||||
sha3
|
sha3
|
||||||
sidekiq (>= 6.0.7)
|
sidekiq (>= 6.0.7)
|
||||||
|
@ -1070,7 +1070,8 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', '
|
|||||||
/**
|
/**
|
||||||
* Controller used in the admin's creation page (admin view)
|
* Controller used in the admin's creation page (admin view)
|
||||||
*/
|
*/
|
||||||
Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', function ($state, $scope, Admin, growl, _t) {
|
Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', 'phoneRequiredPromise',
|
||||||
|
function ($state, $scope, Admin, growl, _t, phoneRequiredPromise) {
|
||||||
// default admin profile
|
// default admin profile
|
||||||
let getGender;
|
let getGender;
|
||||||
$scope.admin = {
|
$scope.admin = {
|
||||||
@ -1090,6 +1091,9 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// is the phone number required in _admin_form?
|
||||||
|
$scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the birth day datepicker
|
* Shows the birth day datepicker
|
||||||
* @param $event {Object} jQuery event object
|
* @param $event {Object} jQuery event object
|
||||||
|
@ -947,6 +947,9 @@ angular.module('application.router', ['ui.router'])
|
|||||||
templateUrl: '<%= asset_path "admin/admins/new.html" %>',
|
templateUrl: '<%= asset_path "admin/admins/new.html" %>',
|
||||||
controller: 'NewAdminController'
|
controller: 'NewAdminController'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('app.admin.managers_new', {
|
.state('app.admin.managers_new', {
|
||||||
|
@ -110,12 +110,13 @@
|
|||||||
|
|
||||||
<div class="form-group" ng-class="{'has-error': adminForm['admin[profile_attributes][phone]'].$dirty && adminForm['admin[profile_attributes][phone]'].$invalid}">
|
<div class="form-group" ng-class="{'has-error': adminForm['admin[profile_attributes][phone]'].$dirty && adminForm['admin[profile_attributes][phone]'].$invalid}">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon"><i class="fa fa-phone"></i> </span>
|
<span class="input-group-addon"><i class="fa fa-phone"></i> <span class="exponent" ng-show="phoneRequired"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
|
||||||
<input ng-model="admin.profile_attributes.phone"
|
<input ng-model="admin.profile_attributes.phone"
|
||||||
type="text"
|
type="text"
|
||||||
name="admin[profile_attributes][phone]"
|
name="admin[profile_attributes][phone]"
|
||||||
class="form-control" id="user_phone"
|
class="form-control" id="user_phone"
|
||||||
placeholder="{{ 'app.admin.admins_new.phone_number' | translate }}">
|
placeholder="{{ 'app.admin.admins_new.phone_number' | translate }}"
|
||||||
|
ng-required="phoneRequired">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
5
app/models/footprint_debug.rb
Normal file
5
app/models/footprint_debug.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# When a footprint is generated, the associated data is kept to allow further verifications
|
||||||
|
class FootprintDebug < ApplicationRecord
|
||||||
|
end
|
@ -12,12 +12,21 @@ class HistoryValue < ApplicationRecord
|
|||||||
def chain_record
|
def chain_record
|
||||||
self.footprint = compute_footprint
|
self.footprint = compute_footprint
|
||||||
save!
|
save!
|
||||||
|
FootprintDebug.create!(
|
||||||
|
footprint: footprint,
|
||||||
|
data: FootprintService.footprint_data(HistoryValue, self, 'created_at'),
|
||||||
|
klass: HistoryValue.name
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def check_footprint
|
||||||
footprint == compute_footprint
|
footprint == compute_footprint
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def debug_footprint
|
||||||
|
FootprintService.debug_footprint(HistoryValue, self)
|
||||||
|
end
|
||||||
|
|
||||||
def user
|
def user
|
||||||
invoicing_profile.user
|
invoicing_profile.user
|
||||||
end
|
end
|
||||||
|
@ -174,12 +174,21 @@ class Invoice < ApplicationRecord
|
|||||||
def chain_record
|
def chain_record
|
||||||
self.footprint = compute_footprint
|
self.footprint = compute_footprint
|
||||||
save!
|
save!
|
||||||
|
FootprintDebug.create!(
|
||||||
|
footprint: footprint,
|
||||||
|
data: FootprintService.footprint_data(Invoice, self),
|
||||||
|
klass: Invoice.name
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def check_footprint
|
||||||
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def debug_footprint
|
||||||
|
FootprintService.debug_footprint(Invoice, self)
|
||||||
|
end
|
||||||
|
|
||||||
def set_wallet_transaction(amount, transaction_id)
|
def set_wallet_transaction(amount, transaction_id)
|
||||||
raise InvalidFootprintError unless check_footprint
|
raise InvalidFootprintError unless check_footprint
|
||||||
|
|
||||||
|
@ -15,12 +15,21 @@ class InvoiceItem < ApplicationRecord
|
|||||||
def chain_record
|
def chain_record
|
||||||
self.footprint = compute_footprint
|
self.footprint = compute_footprint
|
||||||
save!
|
save!
|
||||||
|
FootprintDebug.create!(
|
||||||
|
footprint: footprint,
|
||||||
|
data: FootprintService.footprint_data(InvoiceItem, self),
|
||||||
|
klass: InvoiceItem.name
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def check_footprint
|
||||||
footprint == compute_footprint
|
footprint == compute_footprint
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def debug_footprint
|
||||||
|
FootprintService.debug_footprint(InvoiceItem, self)
|
||||||
|
end
|
||||||
|
|
||||||
def amount_after_coupon
|
def amount_after_coupon
|
||||||
# deduct coupon discount
|
# deduct coupon discount
|
||||||
coupon_service = CouponService.new
|
coupon_service = CouponService.new
|
||||||
|
@ -3,19 +3,47 @@
|
|||||||
# Provides helper methods to compute footprints
|
# Provides helper methods to compute footprints
|
||||||
class FootprintService
|
class FootprintService
|
||||||
# Compute the footprint
|
# Compute the footprint
|
||||||
# @param class_name Invoice|InvoiceItem|HistoryValue
|
# @param klass Invoice|InvoiceItem|HistoryValue
|
||||||
# @param item an instance of the provided class
|
# @param item an instance of the provided class
|
||||||
# @param sort the items in database by the provided criterion, to find the previous one
|
# @param sort the items in database by the provided criterion, to find the previous one
|
||||||
def self.compute_footprint(klass, item, sort_on = 'id')
|
def self.compute_footprint(klass, item, sort_on = 'id')
|
||||||
|
Checksum.text(FootprintService.footprint_data(klass, item, sort_on))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the original data string used to compute the footprint
|
||||||
|
# @param klass Invoice|InvoiceItem|HistoryValue
|
||||||
|
# @param item an instance of the provided class
|
||||||
|
# @param sort the items in database by the provided criterion, to find the previous one
|
||||||
|
def self.footprint_data(klass, item, sort_on = 'id')
|
||||||
raise TypeError unless item.is_a? klass
|
raise TypeError unless item.is_a? klass
|
||||||
|
|
||||||
previous = klass.where("#{sort_on} < ?", item[sort_on])
|
previous = klass.where("#{sort_on} < ?", item[sort_on])
|
||||||
.order("#{sort_on} DESC")
|
.order("#{sort_on} DESC")
|
||||||
.limit(1)
|
.limit(1)
|
||||||
|
|
||||||
columns = klass.columns.map(&:name)
|
columns = FootprintService.footprint_columns(klass)
|
||||||
.delete_if { |c| %w[footprint updated_at].include? c }
|
|
||||||
|
|
||||||
Checksum.text("#{columns.map { |c| item[c] }.join}#{previous.first ? previous.first.footprint : ''}")
|
"#{columns.map { |c| item[c] }.join}#{previous.first ? previous.first.footprint : ''}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return an ordered array of the columns used in the footprint computation
|
||||||
|
# @param klass Invoice|InvoiceItem|HistoryValue
|
||||||
|
def self.footprint_columns(klass)
|
||||||
|
klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].include? c }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a debugging message to help finding why a footprint is invalid
|
||||||
|
# @param klass Invoice|InvoiceItem|HistoryValue
|
||||||
|
# @param item an instance of the provided class
|
||||||
|
def self.debug_footprint(klass, item)
|
||||||
|
columns = FootprintService.footprint_columns(klass)
|
||||||
|
current = FootprintService.footprint_data(klass, item)
|
||||||
|
saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass)
|
||||||
|
puts "Debug footprint for #{klass} [ id: #{item.id} ]"
|
||||||
|
puts '-----------------------------------------'
|
||||||
|
puts "columns: [ #{columns.join(', ')} ]"
|
||||||
|
puts "current footprint: #{current}"
|
||||||
|
puts " saved footprint: #{saved&.data}"
|
||||||
|
puts '-----------------------------------------'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,14 +15,14 @@ wb.add_worksheet(name: t('export_reservations.reservations')) do |sheet|
|
|||||||
# data rows
|
# data rows
|
||||||
@reservations.each do |resrv|
|
@reservations.each do |resrv|
|
||||||
data = [
|
data = [
|
||||||
resrv.user.id,
|
resrv.user&.id,
|
||||||
resrv.user.profile.full_name,
|
resrv.user&.profile&.full_name || t('export_reservations.deleted_user'),
|
||||||
resrv.user.email,
|
resrv.user&.email,
|
||||||
resrv.created_at.to_date,
|
resrv.created_at.to_date,
|
||||||
resrv.reservable_type,
|
resrv.reservable_type,
|
||||||
(resrv.reservable.nil? ? '' : resrv.reservable.name),
|
(resrv.reservable.nil? ? '' : resrv.reservable.name),
|
||||||
(resrv.reservable_type == 'Event') ? resrv.total_booked_seats: resrv.slots.count,
|
(resrv.reservable_type == 'Event') ? resrv.total_booked_seats: resrv.slots.count,
|
||||||
(resrv.invoice.paid_with_stripe?) ? t('export_reservations.local_payment') : t('export_reservations.online_payment')
|
(resrv.invoice&.paid_with_stripe?) ? t('export_reservations.online_payment') : t('export_reservations.local_payment')
|
||||||
]
|
]
|
||||||
styles = [nil, nil, nil, date, nil, nil, nil, nil]
|
styles = [nil, nil, nil, date, nil, nil, nil, nil]
|
||||||
types = [:integer, :string, :string, :date, :string, :string, :integer, :string]
|
types = [:integer, :string, :string, :date, :string, :string, :integer, :string]
|
||||||
|
@ -15,9 +15,9 @@ wb.add_worksheet(name: t('export_subscriptions.subscriptions')) do |sheet|
|
|||||||
# data rows
|
# data rows
|
||||||
@subscriptions.each do |sub|
|
@subscriptions.each do |sub|
|
||||||
data = [
|
data = [
|
||||||
sub.user.id,
|
sub.user&.id,
|
||||||
sub.user.profile.full_name,
|
sub.user&.profile&.full_name || t('export_subscriptions.deleted_user'),
|
||||||
sub.user.email,
|
sub.user&.email,
|
||||||
sub.plan.human_readable_name(group: true),
|
sub.plan.human_readable_name(group: true),
|
||||||
t("duration.#{sub.plan.interval}", count: sub.plan.interval_count),
|
t("duration.#{sub.plan.interval}", count: sub.plan.interval_count),
|
||||||
sub.created_at.to_date,
|
sub.created_at.to_date,
|
||||||
|
@ -188,6 +188,7 @@ en:
|
|||||||
payment_method: "Payment method"
|
payment_method: "Payment method"
|
||||||
local_payment: "Payment at the reception"
|
local_payment: "Payment at the reception"
|
||||||
online_payment: "Online payment"
|
online_payment: "Online payment"
|
||||||
|
deleted_user: "Deleted user"
|
||||||
#subscriptions list export to EXCEL format
|
#subscriptions list export to EXCEL format
|
||||||
export_subscriptions:
|
export_subscriptions:
|
||||||
subscriptions: "Subscriptions"
|
subscriptions: "Subscriptions"
|
||||||
@ -202,6 +203,7 @@ en:
|
|||||||
payment_method: "Payment method"
|
payment_method: "Payment method"
|
||||||
local_payment: "Payment at the reception"
|
local_payment: "Payment at the reception"
|
||||||
online_payment: "Online payment"
|
online_payment: "Online payment"
|
||||||
|
deleted_user: "Deleted user"
|
||||||
#reservation slots export, by type, to EXCEL format
|
#reservation slots export, by type, to EXCEL format
|
||||||
export_availabilities:
|
export_availabilities:
|
||||||
machines: "Machines"
|
machines: "Machines"
|
||||||
|
@ -188,6 +188,7 @@ es:
|
|||||||
payment_method: "Método de pago"
|
payment_method: "Método de pago"
|
||||||
local_payment: "Pago en recepción"
|
local_payment: "Pago en recepción"
|
||||||
online_payment: "Pago online"
|
online_payment: "Pago online"
|
||||||
|
deleted_user: "Usuario eliminado"
|
||||||
#subscriptions list export to EXCEL format
|
#subscriptions list export to EXCEL format
|
||||||
export_subscriptions:
|
export_subscriptions:
|
||||||
subscriptions: "Suscripciones"
|
subscriptions: "Suscripciones"
|
||||||
@ -202,6 +203,7 @@ es:
|
|||||||
payment_method: "Método de pago"
|
payment_method: "Método de pago"
|
||||||
local_payment: "Pago en recepción"
|
local_payment: "Pago en recepción"
|
||||||
online_payment: "Pago en línea"
|
online_payment: "Pago en línea"
|
||||||
|
deleted_user: "Usario eliminado"
|
||||||
#reservation slots export, by type, to EXCEL format
|
#reservation slots export, by type, to EXCEL format
|
||||||
export_availabilities:
|
export_availabilities:
|
||||||
machines: "Máquinas"
|
machines: "Máquinas"
|
||||||
|
@ -188,6 +188,7 @@ fr:
|
|||||||
payment_method: "Méthode de paiement"
|
payment_method: "Méthode de paiement"
|
||||||
local_payment: "Paiement à l'accueil"
|
local_payment: "Paiement à l'accueil"
|
||||||
online_payment: "Paiement en ligne"
|
online_payment: "Paiement en ligne"
|
||||||
|
deleted_user: "Utilisateur supprimé"
|
||||||
#subscriptions list export to EXCEL format
|
#subscriptions list export to EXCEL format
|
||||||
export_subscriptions:
|
export_subscriptions:
|
||||||
subscriptions: "Abonnements"
|
subscriptions: "Abonnements"
|
||||||
@ -202,6 +203,7 @@ fr:
|
|||||||
payment_method: "Méthode de paiement"
|
payment_method: "Méthode de paiement"
|
||||||
local_payment: "Paiement à l'accueil"
|
local_payment: "Paiement à l'accueil"
|
||||||
online_payment: "Paiement en ligne"
|
online_payment: "Paiement en ligne"
|
||||||
|
deleted_user: "Utilisateur supprimé"
|
||||||
#reservation slots export, by type, to EXCEL format
|
#reservation slots export, by type, to EXCEL format
|
||||||
export_availabilities:
|
export_availabilities:
|
||||||
machines: "Machines"
|
machines: "Machines"
|
||||||
|
@ -188,6 +188,7 @@ pt:
|
|||||||
payment_method: "Método de pagamento"
|
payment_method: "Método de pagamento"
|
||||||
local_payment: "Pagamento na recepção"
|
local_payment: "Pagamento na recepção"
|
||||||
online_payment: "Pagamento online"
|
online_payment: "Pagamento online"
|
||||||
|
deleted_user: "Usuário deletado"
|
||||||
#subscriptions list export to EXCEL format
|
#subscriptions list export to EXCEL format
|
||||||
export_subscriptions:
|
export_subscriptions:
|
||||||
subscriptions: "Assinaturas"
|
subscriptions: "Assinaturas"
|
||||||
@ -202,6 +203,7 @@ pt:
|
|||||||
payment_method: "Método de pagamento"
|
payment_method: "Método de pagamento"
|
||||||
local_payment: "Pagamento na recepção"
|
local_payment: "Pagamento na recepção"
|
||||||
online_payment: "Pagamento online"
|
online_payment: "Pagamento online"
|
||||||
|
deleted_user: "Usuário deletado"
|
||||||
#reservation slots export, by type, to EXCEL format
|
#reservation slots export, by type, to EXCEL format
|
||||||
export_availabilities:
|
export_availabilities:
|
||||||
machines: "Máquinas"
|
machines: "Máquinas"
|
||||||
|
@ -188,6 +188,7 @@ zu:
|
|||||||
payment_method: "crwdns3501:0crwdne3501:0"
|
payment_method: "crwdns3501:0crwdne3501:0"
|
||||||
local_payment: "crwdns3503:0crwdne3503:0"
|
local_payment: "crwdns3503:0crwdne3503:0"
|
||||||
online_payment: "crwdns3505:0crwdne3505:0"
|
online_payment: "crwdns3505:0crwdne3505:0"
|
||||||
|
deleted_user: "crwdns20886:0crwdne20886:0"
|
||||||
#subscriptions list export to EXCEL format
|
#subscriptions list export to EXCEL format
|
||||||
export_subscriptions:
|
export_subscriptions:
|
||||||
subscriptions: "crwdns3507:0crwdne3507:0"
|
subscriptions: "crwdns3507:0crwdne3507:0"
|
||||||
@ -202,6 +203,7 @@ zu:
|
|||||||
payment_method: "crwdns3525:0crwdne3525:0"
|
payment_method: "crwdns3525:0crwdne3525:0"
|
||||||
local_payment: "crwdns3527:0crwdne3527:0"
|
local_payment: "crwdns3527:0crwdne3527:0"
|
||||||
online_payment: "crwdns3529:0crwdne3529:0"
|
online_payment: "crwdns3529:0crwdne3529:0"
|
||||||
|
deleted_user: "crwdns20888:0crwdne20888:0"
|
||||||
#reservation slots export, by type, to EXCEL format
|
#reservation slots export, by type, to EXCEL format
|
||||||
export_availabilities:
|
export_availabilities:
|
||||||
machines: "crwdns3531:0crwdne3531:0"
|
machines: "crwdns3531:0crwdne3531:0"
|
||||||
|
@ -14,6 +14,8 @@ development:
|
|||||||
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
||||||
default_host: <%= ENV["DEFAULT_HOST"] %>
|
default_host: <%= ENV["DEFAULT_HOST"] %>
|
||||||
default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %>
|
default_protocol: <%= ENV["DEFAULT_PROTOCOL"] %>
|
||||||
|
smtp_address: <%= ENV["SMTP_ADDRESS"] %>
|
||||||
|
smtp_port: <%= ENV["SMTP_PORT"] %>
|
||||||
time_zone: <%= ENV["TIME_ZONE"] %>
|
time_zone: <%= ENV["TIME_ZONE"] %>
|
||||||
week_starting_day: <%= ENV["WEEK_STARTING_DAY"] %>
|
week_starting_day: <%= ENV["WEEK_STARTING_DAY"] %>
|
||||||
d3_date_format: <%= ENV["D3_DATE_FORMAT"].dump %> # .dump is needed as the value may start by a '%', see https://github.com/tenderlove/psych/issues/75
|
d3_date_format: <%= ENV["D3_DATE_FORMAT"].dump %> # .dump is needed as the value may start by a '%', see https://github.com/tenderlove/psych/issues/75
|
||||||
|
15
db/migrate/20200721162939_create_footprint_debugs.rb
Normal file
15
db/migrate/20200721162939_create_footprint_debugs.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This table saves the original data used to create footprints, this allows
|
||||||
|
# to debug invalid footprints
|
||||||
|
class CreateFootprintDebugs < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :footprint_debugs do |t|
|
||||||
|
t.string :footprint
|
||||||
|
t.string :data
|
||||||
|
t.string :klass
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -763,6 +763,39 @@ CREATE SEQUENCE public.exports_id_seq
|
|||||||
ALTER SEQUENCE public.exports_id_seq OWNED BY public.exports.id;
|
ALTER SEQUENCE public.exports_id_seq OWNED BY public.exports.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: footprint_debugs; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.footprint_debugs (
|
||||||
|
id bigint NOT NULL,
|
||||||
|
footprint character varying,
|
||||||
|
data character varying,
|
||||||
|
klass character varying,
|
||||||
|
created_at timestamp without time zone NOT NULL,
|
||||||
|
updated_at timestamp without time zone NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: footprint_debugs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.footprint_debugs_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: footprint_debugs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.footprint_debugs_id_seq OWNED BY public.footprint_debugs.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: friendly_id_slugs; Type: TABLE; Schema: public; Owner: -
|
-- Name: friendly_id_slugs; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -3020,6 +3053,13 @@ ALTER TABLE ONLY public.events_event_themes ALTER COLUMN id SET DEFAULT nextval(
|
|||||||
ALTER TABLE ONLY public.exports ALTER COLUMN id SET DEFAULT nextval('public.exports_id_seq'::regclass);
|
ALTER TABLE ONLY public.exports ALTER COLUMN id SET DEFAULT nextval('public.exports_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: footprint_debugs id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.footprint_debugs ALTER COLUMN id SET DEFAULT nextval('public.footprint_debugs_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: friendly_id_slugs id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: friendly_id_slugs id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -3607,6 +3647,14 @@ ALTER TABLE ONLY public.exports
|
|||||||
ADD CONSTRAINT exports_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT exports_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: footprint_debugs footprint_debugs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.footprint_debugs
|
||||||
|
ADD CONSTRAINT footprint_debugs_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: friendly_id_slugs friendly_id_slugs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: friendly_id_slugs friendly_id_slugs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -5598,6 +5646,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20200622135401'),
|
('20200622135401'),
|
||||||
('20200623134900'),
|
('20200623134900'),
|
||||||
('20200623141305'),
|
('20200623141305'),
|
||||||
('20200629123011');
|
('20200629123011'),
|
||||||
|
('20200721162939');
|
||||||
|
|
||||||
|
|
||||||
|
30
lib/tasks/db.rake
Normal file
30
lib/tasks/db.rake
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: false
|
||||||
|
|
||||||
|
namespace :db do
|
||||||
|
desc 'Convert development DB to Rails test fixtures'
|
||||||
|
task :to_fixtures, [:table] => :environment do |_task, args|
|
||||||
|
TABLES_TO_SKIP = %w[ar_internal_metadata delayed_jobs schema_info schema_migrations].freeze
|
||||||
|
|
||||||
|
begin
|
||||||
|
ActiveRecord::Base.establish_connection
|
||||||
|
ActiveRecord::Base.connection.tables.each do |table_name|
|
||||||
|
next if TABLES_TO_SKIP.include?(table_name)
|
||||||
|
next if args.table && args.table != table_name
|
||||||
|
|
||||||
|
conter = '000'
|
||||||
|
file_path = "#{Rails.root}/test/fixtures/test/#{table_name}.yml"
|
||||||
|
File.open(file_path, 'w') do |file|
|
||||||
|
rows = ActiveRecord::Base.connection.select_all("SELECT * FROM #{table_name}")
|
||||||
|
data = rows.each_with_object({}) do |record, hash|
|
||||||
|
suffix = record['id'].blank? ? conter.succ! : record['id']
|
||||||
|
hash["#{table_name.singularize}_#{suffix}"] = record
|
||||||
|
end
|
||||||
|
puts "Writing table '#{table_name}' to '#{file_path}'"
|
||||||
|
file.write(data.to_yaml)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
ActiveRecord::Base.connection&.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -35,15 +35,6 @@ namespace :fablab do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'generate fixtures from db'
|
|
||||||
task generate_fixtures: :environment do
|
|
||||||
Rails.application.eager_load!
|
|
||||||
ActiveRecord::Base.descendants.reject { |c| [ActiveRecord::SchemaMigration, PartnerPlan].include? c }.each do |ar_base|
|
|
||||||
p "========== #{ar_base} =============="
|
|
||||||
ar_base.dump_fixtures
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'generate current code checksum'
|
desc 'generate current code checksum'
|
||||||
task checksum: :environment do
|
task checksum: :environment do
|
||||||
require 'checksum'
|
require 'checksum'
|
||||||
@ -91,5 +82,18 @@ namespace :fablab do
|
|||||||
Sidekiq::Queue.new('default').clear
|
Sidekiq::Queue.new('default').clear
|
||||||
Sidekiq::DeadSet.new.clear
|
Sidekiq::DeadSet.new.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc 'save the footprint original data'
|
||||||
|
task save_footprint_data: :environment do
|
||||||
|
[Invoice, InvoiceItem, HistoryValue].each do |klass|
|
||||||
|
klass.all.each do |item|
|
||||||
|
FootprintDebug.create!(
|
||||||
|
footprint: item.footprint,
|
||||||
|
data: FootprintService.footprint_data(klass, item, klass == 'HistoryValue' ? 'created_at' : 'id'),
|
||||||
|
klass: klass
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fab-manager",
|
"name": "fab-manager",
|
||||||
"version": "4.5.3",
|
"version": "4.5.4",
|
||||||
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"fablab",
|
"fablab",
|
||||||
|
10
test/fixtures/README.md
vendored
Normal file
10
test/fixtures/README.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Test fixtures
|
||||||
|
|
||||||
|
Fixtures are test data.
|
||||||
|
Every time a new test is run, the database is filled with these data.
|
||||||
|
|
||||||
|
You can create fixtures manually or using the following task, to dump your current table/database to the YAML fixture files:
|
||||||
|
```bash
|
||||||
|
rails db:to_fixtures[table]
|
||||||
|
```
|
||||||
|
The parameter `table` is optional. If not specified, the whole database will be dumped.
|
645
test/fixtures/footprint_debugs.yml
vendored
Normal file
645
test/fixtures/footprint_debugs.yml
vendored
Normal file
File diff suppressed because one or more lines are too long
7
test/fixtures/trainings_availabilities.yml
vendored
7
test/fixtures/trainings_availabilities.yml
vendored
@ -19,3 +19,10 @@ trainings_availability_3:
|
|||||||
availability_id: 8
|
availability_id: 8
|
||||||
created_at: 2016-04-04 15:26:49.574308000 Z
|
created_at: 2016-04-04 15:26:49.574308000 Z
|
||||||
updated_at: 2016-04-04 15:26:49.574308000 Z
|
updated_at: 2016-04-04 15:26:49.574308000 Z
|
||||||
|
|
||||||
|
trainings_availability_4:
|
||||||
|
id: 4
|
||||||
|
training_id: 3
|
||||||
|
availability_id: 12
|
||||||
|
created_at: 2020-07-22 10:09:41.841162000 Z
|
||||||
|
updated_at: 2020-07-22 10:09:41.841162000 Z
|
||||||
|
7
test/fixtures/users.yml
vendored
7
test/fixtures/users.yml
vendored
@ -30,6 +30,7 @@ user_2:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: true
|
||||||
|
|
||||||
user_4:
|
user_4:
|
||||||
id: 4
|
id: 4
|
||||||
@ -62,6 +63,7 @@ user_4:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: false
|
||||||
|
|
||||||
user_6:
|
user_6:
|
||||||
id: 6
|
id: 6
|
||||||
@ -94,6 +96,7 @@ user_6:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: true
|
||||||
|
|
||||||
user_5:
|
user_5:
|
||||||
id: 5
|
id: 5
|
||||||
@ -126,6 +129,7 @@ user_5:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: true
|
||||||
|
|
||||||
user_3:
|
user_3:
|
||||||
id: 3
|
id: 3
|
||||||
@ -158,6 +162,7 @@ user_3:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: false
|
||||||
|
|
||||||
user_1:
|
user_1:
|
||||||
id: 1
|
id: 1
|
||||||
@ -190,6 +195,7 @@ user_1:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: true
|
||||||
|
|
||||||
|
|
||||||
user_7:
|
user_7:
|
||||||
@ -223,3 +229,4 @@ user_7:
|
|||||||
uid:
|
uid:
|
||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
|
is_allow_newsletter: false
|
||||||
|
@ -8,7 +8,7 @@ class Exports::AccountingExportTest < ActionDispatch::IntegrationTest
|
|||||||
login_as(admin, scope: :user)
|
login_as(admin, scope: :user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'creation modification reservation and re-modification scenario' do
|
test 'export accounting period to ACD software' do
|
||||||
# First, we create a new export
|
# First, we create a new export
|
||||||
post '/api/accounting/export',
|
post '/api/accounting/export',
|
||||||
params: {
|
params: {
|
||||||
|
60
test/integration/exports/availabilites_export_test.rb
Normal file
60
test/integration/exports/availabilites_export_test.rb
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
require 'rubyXL'
|
||||||
|
|
||||||
|
module Exports; end
|
||||||
|
|
||||||
|
class Exports::AvailabilitiesExportTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
admin = User.with_role(:admin).first
|
||||||
|
login_as(admin, scope: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'export availabilities to Excel' do
|
||||||
|
# First, we create a new export
|
||||||
|
get '/api/availabilities/export_index.xlsx'
|
||||||
|
|
||||||
|
# Check response format & status
|
||||||
|
assert_equal 200, response.status, response.body
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
# Check the export was created correctly
|
||||||
|
res = json_response(response.body)
|
||||||
|
e = Export.where(id: res[:export_id]).first
|
||||||
|
assert_not_nil e, 'Export was not created in database'
|
||||||
|
|
||||||
|
# Run the worker
|
||||||
|
worker = AvailabilitiesExportWorker.new
|
||||||
|
worker.perform(e.id)
|
||||||
|
|
||||||
|
# notification
|
||||||
|
assert_not_empty Notification.where(attached_object: e)
|
||||||
|
|
||||||
|
# resulting XLSX file
|
||||||
|
assert FileTest.exist?(e.file), 'XLSX file was not generated'
|
||||||
|
workbook = RubyXL::Parser.parse(e.file)
|
||||||
|
|
||||||
|
# test worksheets
|
||||||
|
assert_not_nil workbook[I18n.t('export_availabilities.machines')]
|
||||||
|
assert_not_nil workbook[I18n.t('export_availabilities.trainings')]
|
||||||
|
assert_not_nil workbook[I18n.t('export_availabilities.events')]
|
||||||
|
if Setting.get('spaces_module')
|
||||||
|
assert_not_nil workbook[I18n.t('export_availabilities.spaces')]
|
||||||
|
else
|
||||||
|
assert_nil workbook[I18n.t('export_availabilities.spaces')]
|
||||||
|
end
|
||||||
|
|
||||||
|
# test data
|
||||||
|
availability = Availability.find(13)
|
||||||
|
machines = workbook[I18n.t('export_availabilities.machines')]
|
||||||
|
assert_equal availability.start_at.to_date, machines.sheet_data[1][0].value.to_date
|
||||||
|
assert_equal I18n.l(availability.start_at, format: '%A').capitalize, machines.sheet_data[1][1].value
|
||||||
|
assert_match(/^#{availability.start_at.strftime('%H:%M')} - /, machines.sheet_data[1][2].value)
|
||||||
|
assert_includes availability.machines.map(&:name), machines.sheet_data[1][3].value
|
||||||
|
|
||||||
|
# Clean XLSX file
|
||||||
|
require 'fileutils'
|
||||||
|
FileUtils.rm(e.file)
|
||||||
|
end
|
||||||
|
end
|
51
test/integration/exports/members_export_test.rb
Normal file
51
test/integration/exports/members_export_test.rb
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
require 'rubyXL'
|
||||||
|
|
||||||
|
module Exports; end
|
||||||
|
|
||||||
|
class Exports::MembersExportTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
admin = User.with_role(:admin).first
|
||||||
|
login_as(admin, scope: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'export members to Excel' do
|
||||||
|
# First, we create a new export
|
||||||
|
get '/api/members/export_members.xlsx'
|
||||||
|
|
||||||
|
# Check response format & status
|
||||||
|
assert_equal 200, response.status, response.body
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
# Check the export was created correctly
|
||||||
|
res = json_response(response.body)
|
||||||
|
e = Export.where(id: res[:export_id]).first
|
||||||
|
assert_not_nil e, 'Export was not created in database'
|
||||||
|
|
||||||
|
# Run the worker
|
||||||
|
worker = UsersExportWorker.new
|
||||||
|
worker.perform(e.id)
|
||||||
|
|
||||||
|
# notification
|
||||||
|
assert_not_empty Notification.where(attached_object: e)
|
||||||
|
|
||||||
|
# resulting XLSX file
|
||||||
|
assert FileTest.exist?(e.file), 'XLSX file was not generated'
|
||||||
|
workbook = RubyXL::Parser.parse(e.file)
|
||||||
|
|
||||||
|
# test worksheet
|
||||||
|
assert_not_nil workbook[I18n.t('export_members.members')]
|
||||||
|
|
||||||
|
# test data
|
||||||
|
member = User.members.first
|
||||||
|
wb = workbook[I18n.t('export_members.members')]
|
||||||
|
assert_equal member.id, wb.sheet_data[1][0].value
|
||||||
|
assert_equal (member.is_allow_newsletter ? 1 : nil), wb.sheet_data[1][4].value
|
||||||
|
|
||||||
|
# Clean XLSX file
|
||||||
|
require 'fileutils'
|
||||||
|
FileUtils.rm(e.file)
|
||||||
|
end
|
||||||
|
end
|
52
test/integration/exports/reservations_export_test.rb
Normal file
52
test/integration/exports/reservations_export_test.rb
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
require 'rubyXL'
|
||||||
|
|
||||||
|
module Exports; end
|
||||||
|
|
||||||
|
class Exports::ReservationsExportTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
admin = User.with_role(:admin).first
|
||||||
|
login_as(admin, scope: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'export reservations to Excel' do
|
||||||
|
# First, we create a new export
|
||||||
|
get '/api/members/export_reservations.xlsx'
|
||||||
|
|
||||||
|
# Check response format & status
|
||||||
|
assert_equal 200, response.status, response.body
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
# Check the export was created correctly
|
||||||
|
res = json_response(response.body)
|
||||||
|
e = Export.where(id: res[:export_id]).first
|
||||||
|
assert_not_nil e, 'Export was not created in database'
|
||||||
|
|
||||||
|
# Run the worker
|
||||||
|
worker = UsersExportWorker.new
|
||||||
|
worker.perform(e.id)
|
||||||
|
|
||||||
|
# notification
|
||||||
|
assert_not_empty Notification.where(attached_object: e)
|
||||||
|
|
||||||
|
# resulting XLSX file
|
||||||
|
assert FileTest.exist?(e.file), 'XLSX file was not generated'
|
||||||
|
workbook = RubyXL::Parser.parse(e.file)
|
||||||
|
|
||||||
|
# test worksheet
|
||||||
|
assert_not_nil workbook[I18n.t('export_reservations.reservations')]
|
||||||
|
|
||||||
|
# test data
|
||||||
|
reservation = Reservation.find(1)
|
||||||
|
wb = workbook[I18n.t('export_reservations.reservations')]
|
||||||
|
assert_equal reservation.user.id, wb.sheet_data[1][0].value
|
||||||
|
assert_equal reservation.created_at.to_date, wb.sheet_data[1][3].value.to_date
|
||||||
|
assert_equal reservation.reservable_type, wb.sheet_data[1][4].value
|
||||||
|
|
||||||
|
# Clean XLSX file
|
||||||
|
require 'fileutils'
|
||||||
|
FileUtils.rm(e.file)
|
||||||
|
end
|
||||||
|
end
|
52
test/integration/exports/subscriptions_export_test.rb
Normal file
52
test/integration/exports/subscriptions_export_test.rb
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
require 'rubyXL'
|
||||||
|
|
||||||
|
module Exports; end
|
||||||
|
|
||||||
|
class Exports::SubscriptionsExportTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
admin = User.with_role(:admin).first
|
||||||
|
login_as(admin, scope: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'export subscriptions to Excel' do
|
||||||
|
# First, we create a new export
|
||||||
|
get '/api/members/export_subscriptions.xlsx'
|
||||||
|
|
||||||
|
# Check response format & status
|
||||||
|
assert_equal 200, response.status, response.body
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
# Check the export was created correctly
|
||||||
|
res = json_response(response.body)
|
||||||
|
e = Export.where(id: res[:export_id]).first
|
||||||
|
assert_not_nil e, 'Export was not created in database'
|
||||||
|
|
||||||
|
# Run the worker
|
||||||
|
worker = UsersExportWorker.new
|
||||||
|
worker.perform(e.id)
|
||||||
|
|
||||||
|
# notification
|
||||||
|
assert_not_empty Notification.where(attached_object: e)
|
||||||
|
|
||||||
|
# resulting XLSX file
|
||||||
|
assert FileTest.exist?(e.file), 'XLSX file was not generated'
|
||||||
|
workbook = RubyXL::Parser.parse(e.file)
|
||||||
|
|
||||||
|
# test worksheet
|
||||||
|
assert_not_nil workbook[I18n.t('export_subscriptions.subscriptions')]
|
||||||
|
|
||||||
|
# test data
|
||||||
|
subscription = Subscription.find(1)
|
||||||
|
wb = workbook[I18n.t('export_subscriptions.subscriptions')]
|
||||||
|
assert_equal subscription.user.id, wb.sheet_data[1][0].value
|
||||||
|
assert_equal subscription.plan.human_readable_name(group: true), wb.sheet_data[1][3].value
|
||||||
|
assert_equal subscription.created_at.to_date, wb.sheet_data[1][5].value.to_date
|
||||||
|
|
||||||
|
# Clean XLSX file
|
||||||
|
require 'fileutils'
|
||||||
|
FileUtils.rm(e.file)
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user