mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-29 18:52:22 +01:00
Merge branch 'release-2.1.0'
This commit is contained in:
commit
8864863343
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Changelog Fab Manager
|
||||||
|
|
||||||
|
## v2.1.0 2016 mai 2
|
||||||
|
- Add search feature on openlab projects : [Openlab-projects](https://github.com/LaCasemate/openlab-projects)
|
||||||
|
- Add integration tests for main features
|
||||||
|
- Credits logic has been extracted into microservice
|
||||||
|
- Improuve UI list of projects
|
||||||
|
- Refactor interface for SSO profile completion
|
||||||
|
- Change interface for SSO/email already used
|
||||||
|
- Fix bug custom asset favicon-file favicon file is not set
|
13
Gemfile
13
Gemfile
@ -34,12 +34,6 @@ group :development, :test do
|
|||||||
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
|
|
||||||
gem 'factory_girl_rails'
|
|
||||||
gem 'rspec-rails'
|
|
||||||
gem 'spring-commands-rspec'
|
|
||||||
|
|
||||||
gem 'guard-rspec', require: false
|
|
||||||
|
|
||||||
gem 'railroady'
|
gem 'railroady'
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -62,6 +56,11 @@ end
|
|||||||
group :test do
|
group :test do
|
||||||
gem 'database_cleaner'
|
gem 'database_cleaner'
|
||||||
gem 'faker'
|
gem 'faker'
|
||||||
|
gem 'test_after_commit'
|
||||||
|
gem 'minitest-reporters'
|
||||||
|
gem 'webmock'
|
||||||
|
gem 'vcr'
|
||||||
|
gem 'byebug'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
@ -138,3 +137,5 @@ gem 'chroma'
|
|||||||
gem 'protected_attributes'
|
gem 'protected_attributes'
|
||||||
|
|
||||||
gem 'message_format'
|
gem 'message_format'
|
||||||
|
|
||||||
|
gem 'openlab_ruby'
|
||||||
|
97
Gemfile.lock
97
Gemfile.lock
@ -41,6 +41,7 @@ GEM
|
|||||||
thread_safe (~> 0.3, >= 0.3.4)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.3.8)
|
addressable (2.3.8)
|
||||||
|
ansi (1.5.0)
|
||||||
arel (6.0.3)
|
arel (6.0.3)
|
||||||
autoprefixer-rails (5.1.8)
|
autoprefixer-rails (5.1.8)
|
||||||
execjs
|
execjs
|
||||||
@ -58,6 +59,7 @@ GEM
|
|||||||
sass (>= 3.2.19)
|
sass (>= 3.2.19)
|
||||||
buftok (0.2.0)
|
buftok (0.2.0)
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
|
byebug (8.2.3)
|
||||||
camertron-eprun (1.1.0)
|
camertron-eprun (1.1.0)
|
||||||
capistrano (2.15.5)
|
capistrano (2.15.5)
|
||||||
highline
|
highline
|
||||||
@ -80,7 +82,6 @@ GEM
|
|||||||
chroma (0.0.1)
|
chroma (0.0.1)
|
||||||
chunky_png (1.3.4)
|
chunky_png (1.3.4)
|
||||||
cldr-plurals-runtime-rb (1.0.1)
|
cldr-plurals-runtime-rb (1.0.1)
|
||||||
coderay (1.1.0)
|
|
||||||
coercible (1.0.0)
|
coercible (1.0.0)
|
||||||
descendants_tracker (~> 0.0.1)
|
descendants_tracker (~> 0.0.1)
|
||||||
coffee-rails (4.1.0)
|
coffee-rails (4.1.0)
|
||||||
@ -107,6 +108,8 @@ GEM
|
|||||||
sass-rails (<= 5.0.1)
|
sass-rails (<= 5.0.1)
|
||||||
sprockets (< 2.13)
|
sprockets (< 2.13)
|
||||||
connection_pool (2.2.0)
|
connection_pool (2.2.0)
|
||||||
|
crack (0.4.3)
|
||||||
|
safe_yaml (~> 1.0.0)
|
||||||
database_cleaner (1.4.1)
|
database_cleaner (1.4.1)
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.2)
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
@ -120,7 +123,6 @@ GEM
|
|||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
devise-async (0.9.0)
|
devise-async (0.9.0)
|
||||||
devise (~> 3.2)
|
devise (~> 3.2)
|
||||||
diff-lcs (1.2.5)
|
|
||||||
domain_name (0.5.25)
|
domain_name (0.5.25)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
elasticsearch (1.0.12)
|
elasticsearch (1.0.12)
|
||||||
@ -146,11 +148,6 @@ GEM
|
|||||||
equalizer (0.0.11)
|
equalizer (0.0.11)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (2.4.0)
|
execjs (2.4.0)
|
||||||
factory_girl (4.5.0)
|
|
||||||
activesupport (>= 3.0.0)
|
|
||||||
factory_girl_rails (4.5.0)
|
|
||||||
factory_girl (~> 4.5.0)
|
|
||||||
railties (>= 3.0.0)
|
|
||||||
faker (1.4.3)
|
faker (1.4.3)
|
||||||
i18n (~> 0.5)
|
i18n (~> 0.5)
|
||||||
faraday (0.9.1)
|
faraday (0.9.1)
|
||||||
@ -163,25 +160,11 @@ GEM
|
|||||||
foreman (0.78.0)
|
foreman (0.78.0)
|
||||||
thor (~> 0.19.1)
|
thor (~> 0.19.1)
|
||||||
forgery (0.6.0)
|
forgery (0.6.0)
|
||||||
formatador (0.2.5)
|
|
||||||
friendly_id (5.1.0)
|
friendly_id (5.1.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
globalid (0.3.6)
|
globalid (0.3.6)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
guard (2.12.5)
|
hashdiff (0.3.0)
|
||||||
formatador (>= 0.2.4)
|
|
||||||
listen (~> 2.7)
|
|
||||||
lumberjack (~> 1.0)
|
|
||||||
nenv (~> 0.1)
|
|
||||||
notiffany (~> 0.0)
|
|
||||||
pry (>= 0.9.12)
|
|
||||||
shellany (~> 0.0)
|
|
||||||
thor (>= 0.18.1)
|
|
||||||
guard-compat (1.2.1)
|
|
||||||
guard-rspec (4.5.0)
|
|
||||||
guard (~> 2.1)
|
|
||||||
guard-compat (~> 1.1)
|
|
||||||
rspec (>= 2.99.0, < 4.0)
|
|
||||||
hashie (3.4.2)
|
hashie (3.4.2)
|
||||||
highline (1.7.1)
|
highline (1.7.1)
|
||||||
hike (1.2.3)
|
hike (1.2.3)
|
||||||
@ -191,6 +174,9 @@ GEM
|
|||||||
http-cookie (1.0.2)
|
http-cookie (1.0.2)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
|
httparty (0.13.7)
|
||||||
|
json (~> 1.8)
|
||||||
|
multi_xml (>= 0.5.2)
|
||||||
i18n (0.7.0)
|
i18n (0.7.0)
|
||||||
ice_nine (0.11.1)
|
ice_nine (0.11.1)
|
||||||
jbuilder (2.2.12)
|
jbuilder (2.2.12)
|
||||||
@ -211,29 +197,27 @@ GEM
|
|||||||
letter_opener (1.3.0)
|
letter_opener (1.3.0)
|
||||||
launchy (~> 2.2)
|
launchy (~> 2.2)
|
||||||
libv8 (3.16.14.11)
|
libv8 (3.16.14.11)
|
||||||
listen (2.10.0)
|
|
||||||
celluloid (~> 0.16.0)
|
|
||||||
rb-fsevent (>= 0.9.3)
|
|
||||||
rb-inotify (>= 0.9)
|
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lumberjack (1.0.9)
|
|
||||||
mail (2.6.3)
|
mail (2.6.3)
|
||||||
mime-types (>= 1.16, < 3)
|
mime-types (>= 1.16, < 3)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
message_format (0.0.3)
|
message_format (0.0.3)
|
||||||
twitter_cldr (~> 3.1)
|
twitter_cldr (~> 3.1)
|
||||||
method_source (0.8.2)
|
|
||||||
mime-types (2.99)
|
mime-types (2.99)
|
||||||
mini_magick (4.2.0)
|
mini_magick (4.2.0)
|
||||||
mini_portile (0.6.2)
|
mini_portile (0.6.2)
|
||||||
minitest (5.8.3)
|
minitest (5.8.3)
|
||||||
|
minitest-reporters (1.1.8)
|
||||||
|
ansi
|
||||||
|
builder
|
||||||
|
minitest (>= 5.0)
|
||||||
|
ruby-progressbar
|
||||||
multi_json (1.11.2)
|
multi_json (1.11.2)
|
||||||
multi_xml (0.5.5)
|
multi_xml (0.5.5)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
naught (1.0.0)
|
naught (1.0.0)
|
||||||
nenv (0.2.0)
|
|
||||||
net-scp (1.2.1)
|
net-scp (1.2.1)
|
||||||
net-ssh (>= 2.6.5)
|
net-ssh (>= 2.6.5)
|
||||||
net-sftp (2.1.2)
|
net-sftp (2.1.2)
|
||||||
@ -244,9 +228,6 @@ GEM
|
|||||||
netrc (0.10.3)
|
netrc (0.10.3)
|
||||||
nokogiri (1.6.6.4)
|
nokogiri (1.6.6.4)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
notiffany (0.0.6)
|
|
||||||
nenv (~> 0.1)
|
|
||||||
shellany (~> 0.0)
|
|
||||||
notify_with (0.0.2)
|
notify_with (0.0.2)
|
||||||
jbuilder (~> 2.0)
|
jbuilder (~> 2.0)
|
||||||
rails (>= 4.2.0)
|
rails (>= 4.2.0)
|
||||||
@ -264,6 +245,8 @@ GEM
|
|||||||
omniauth-oauth2 (1.3.1)
|
omniauth-oauth2 (1.3.1)
|
||||||
oauth2 (~> 1.0)
|
oauth2 (~> 1.0)
|
||||||
omniauth (~> 1.2)
|
omniauth (~> 1.2)
|
||||||
|
openlab_ruby (0.0.3)
|
||||||
|
httparty (~> 0.13)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
pdf-core (0.5.1)
|
pdf-core (0.5.1)
|
||||||
pg (0.18.1)
|
pg (0.18.1)
|
||||||
@ -273,10 +256,6 @@ GEM
|
|||||||
prawn-table (0.2.1)
|
prawn-table (0.2.1)
|
||||||
protected_attributes (1.1.3)
|
protected_attributes (1.1.3)
|
||||||
activemodel (>= 4.0.1, < 5.0)
|
activemodel (>= 4.0.1, < 5.0)
|
||||||
pry (0.10.1)
|
|
||||||
coderay (~> 1.1.0)
|
|
||||||
method_source (~> 0.8.1)
|
|
||||||
slop (~> 3.4)
|
|
||||||
puma (2.11.1)
|
puma (2.11.1)
|
||||||
rack (>= 1.1, < 2.0)
|
rack (>= 1.1, < 2.0)
|
||||||
pundit (1.0.0)
|
pundit (1.0.0)
|
||||||
@ -338,31 +317,12 @@ GEM
|
|||||||
mime-types (>= 1.16, < 3.0)
|
mime-types (>= 1.16, < 3.0)
|
||||||
netrc (~> 0.7)
|
netrc (~> 0.7)
|
||||||
rolify (4.0.0)
|
rolify (4.0.0)
|
||||||
rspec (3.2.0)
|
ruby-progressbar (1.7.5)
|
||||||
rspec-core (~> 3.2.0)
|
|
||||||
rspec-expectations (~> 3.2.0)
|
|
||||||
rspec-mocks (~> 3.2.0)
|
|
||||||
rspec-core (3.2.2)
|
|
||||||
rspec-support (~> 3.2.0)
|
|
||||||
rspec-expectations (3.2.0)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.2.0)
|
|
||||||
rspec-mocks (3.2.1)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.2.0)
|
|
||||||
rspec-rails (3.2.1)
|
|
||||||
actionpack (>= 3.0, < 4.3)
|
|
||||||
activesupport (>= 3.0, < 4.3)
|
|
||||||
railties (>= 3.0, < 4.3)
|
|
||||||
rspec-core (~> 3.2.0)
|
|
||||||
rspec-expectations (~> 3.2.0)
|
|
||||||
rspec-mocks (~> 3.2.0)
|
|
||||||
rspec-support (~> 3.2.0)
|
|
||||||
rspec-support (3.2.2)
|
|
||||||
rufus-scheduler (3.0.9)
|
rufus-scheduler (3.0.9)
|
||||||
tzinfo
|
tzinfo
|
||||||
rvm-capistrano (1.5.6)
|
rvm-capistrano (1.5.6)
|
||||||
capistrano (~> 2.15.4)
|
capistrano (~> 2.15.4)
|
||||||
|
safe_yaml (1.0.4)
|
||||||
sass (3.4.13)
|
sass (3.4.13)
|
||||||
sass-rails (5.0.1)
|
sass-rails (5.0.1)
|
||||||
railties (>= 4.0.0, < 5.0)
|
railties (>= 4.0.0, < 5.0)
|
||||||
@ -376,7 +336,6 @@ GEM
|
|||||||
seed_dump (3.2.2)
|
seed_dump (3.2.2)
|
||||||
activerecord (~> 4)
|
activerecord (~> 4)
|
||||||
activesupport (~> 4)
|
activesupport (~> 4)
|
||||||
shellany (0.0.1)
|
|
||||||
sidekiq (3.3.4)
|
sidekiq (3.3.4)
|
||||||
celluloid (>= 0.16.0)
|
celluloid (>= 0.16.0)
|
||||||
connection_pool (>= 2.1.1)
|
connection_pool (>= 2.1.1)
|
||||||
@ -392,10 +351,7 @@ GEM
|
|||||||
rack (~> 1.4)
|
rack (~> 1.4)
|
||||||
rack-protection (~> 1.4)
|
rack-protection (~> 1.4)
|
||||||
tilt (>= 1.3, < 3)
|
tilt (>= 1.3, < 3)
|
||||||
slop (3.6.0)
|
|
||||||
spring (1.3.5)
|
spring (1.3.5)
|
||||||
spring-commands-rspec (1.0.4)
|
|
||||||
spring (>= 0.9.1)
|
|
||||||
sprockets (2.12.4)
|
sprockets (2.12.4)
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
@ -408,6 +364,8 @@ GEM
|
|||||||
stripe (1.30.2)
|
stripe (1.30.2)
|
||||||
json (~> 1.8.1)
|
json (~> 1.8.1)
|
||||||
rest-client (~> 1.4)
|
rest-client (~> 1.4)
|
||||||
|
test_after_commit (1.0.0)
|
||||||
|
activerecord (>= 3.2)
|
||||||
therubyracer (0.12.0)
|
therubyracer (0.12.0)
|
||||||
libv8 (~> 3.16.14.0)
|
libv8 (~> 3.16.14.0)
|
||||||
ref
|
ref
|
||||||
@ -447,6 +405,7 @@ GEM
|
|||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
rack
|
rack
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
|
vcr (3.0.1)
|
||||||
virtus (1.0.5)
|
virtus (1.0.5)
|
||||||
axiom-types (~> 0.1)
|
axiom-types (~> 0.1)
|
||||||
coercible (~> 1.0)
|
coercible (~> 1.0)
|
||||||
@ -459,6 +418,10 @@ GEM
|
|||||||
binding_of_caller (>= 0.7.2)
|
binding_of_caller (>= 0.7.2)
|
||||||
railties (>= 4.0)
|
railties (>= 4.0)
|
||||||
sprockets-rails (>= 2.0, < 4.0)
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
|
webmock (1.24.2)
|
||||||
|
addressable (>= 2.3.6)
|
||||||
|
crack (>= 0.3.2)
|
||||||
|
hashdiff
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@ -469,6 +432,7 @@ DEPENDENCIES
|
|||||||
active_record_query_trace
|
active_record_query_trace
|
||||||
awesome_print
|
awesome_print
|
||||||
bootstrap-sass
|
bootstrap-sass
|
||||||
|
byebug
|
||||||
capistrano
|
capistrano
|
||||||
capistrano-maintenance (= 0.0.5)
|
capistrano-maintenance (= 0.0.5)
|
||||||
capistrano-sidekiq
|
capistrano-sidekiq
|
||||||
@ -482,24 +446,24 @@ DEPENDENCIES
|
|||||||
elasticsearch-model
|
elasticsearch-model
|
||||||
elasticsearch-persistence
|
elasticsearch-persistence
|
||||||
elasticsearch-rails
|
elasticsearch-rails
|
||||||
factory_girl_rails
|
|
||||||
faker
|
faker
|
||||||
figaro
|
figaro
|
||||||
font-awesome-rails
|
font-awesome-rails
|
||||||
foreman
|
foreman
|
||||||
forgery
|
forgery
|
||||||
friendly_id (~> 5.1.0)
|
friendly_id (~> 5.1.0)
|
||||||
guard-rspec
|
|
||||||
jbuilder (~> 2.0)
|
jbuilder (~> 2.0)
|
||||||
jquery-rails
|
jquery-rails
|
||||||
kaminari
|
kaminari
|
||||||
letter_opener
|
letter_opener
|
||||||
message_format
|
message_format
|
||||||
mini_magick
|
mini_magick
|
||||||
|
minitest-reporters
|
||||||
notify_with
|
notify_with
|
||||||
oj
|
oj
|
||||||
omniauth
|
omniauth
|
||||||
omniauth-oauth2
|
omniauth-oauth2
|
||||||
|
openlab_ruby
|
||||||
pg
|
pg
|
||||||
prawn
|
prawn
|
||||||
prawn-table
|
prawn-table
|
||||||
@ -513,7 +477,6 @@ DEPENDENCIES
|
|||||||
recurrence
|
recurrence
|
||||||
responders (~> 2.0)
|
responders (~> 2.0)
|
||||||
rolify
|
rolify
|
||||||
rspec-rails
|
|
||||||
rvm-capistrano
|
rvm-capistrano
|
||||||
sass-rails (= 5.0.1)
|
sass-rails (= 5.0.1)
|
||||||
sdoc (~> 0.4.0)
|
sdoc (~> 0.4.0)
|
||||||
@ -522,14 +485,16 @@ DEPENDENCIES
|
|||||||
sidekiq-cron
|
sidekiq-cron
|
||||||
sinatra
|
sinatra
|
||||||
spring
|
spring
|
||||||
spring-commands-rspec
|
|
||||||
stripe (= 1.30.2)
|
stripe (= 1.30.2)
|
||||||
|
test_after_commit
|
||||||
therubyracer (= 0.12.0)
|
therubyracer (= 0.12.0)
|
||||||
twitter
|
twitter
|
||||||
twitter-text
|
twitter-text
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
unicorn
|
unicorn
|
||||||
|
vcr
|
||||||
web-console (~> 2.0)
|
web-console (~> 2.0)
|
||||||
|
webmock
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.10.6
|
1.11.2
|
||||||
|
22
README.md
22
README.md
@ -530,7 +530,7 @@ After modifying any values concerning the localisation, restart the application
|
|||||||
## Known issues
|
## Known issues
|
||||||
|
|
||||||
- When browsing a machine page, you may encounter an "InterceptError" in the console and the loading bar will stop loading before reaching its ending.
|
- When browsing a machine page, you may encounter an "InterceptError" in the console and the loading bar will stop loading before reaching its ending.
|
||||||
This may append if the machine was created through a seed file without any image.
|
This may happen if the machine was created through a seed file without any image.
|
||||||
To solve this, simply add an image to the machine's profile and refresh the web page.
|
To solve this, simply add an image to the machine's profile and refresh the web page.
|
||||||
|
|
||||||
- When starting the Ruby on Rails server (eg. `foreman s`) you may receive the following error:
|
- When starting the Ruby on Rails server (eg. `foreman s`) you may receive the following error:
|
||||||
@ -539,12 +539,30 @@ After modifying any values concerning the localisation, restart the application
|
|||||||
web.1 | Exiting
|
web.1 | Exiting
|
||||||
worker.1 | ...lib/redis/client.rb...:in `_parse_options'
|
worker.1 | ...lib/redis/client.rb...:in `_parse_options'
|
||||||
|
|
||||||
This may happens when the `application.yml` file is missing.
|
This may happen when the `application.yml` file is missing.
|
||||||
To solve this issue copy `config/application.yml.default` to `config/application.yml`.
|
To solve this issue copy `config/application.yml.default` to `config/application.yml`.
|
||||||
This is required before the first start.
|
This is required before the first start.
|
||||||
|
|
||||||
- Due to a stripe limitation, you won't be ble to create plans longer than one year.
|
- Due to a stripe limitation, you won't be ble to create plans longer than one year.
|
||||||
|
|
||||||
|
- When running the tests suite with `rake test`, all tests may fail with errors similar to the following:
|
||||||
|
|
||||||
|
Error:
|
||||||
|
...
|
||||||
|
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "..." violates foreign key constraint "fk_rails_..."
|
||||||
|
DETAIL: Key (group_id)=(1) is not present in table "groups".
|
||||||
|
: ...
|
||||||
|
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:11:in `block in transaction'
|
||||||
|
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:5:in `transaction'
|
||||||
|
|
||||||
|
This is due to an ActiveRecord behavior witch disable referential integrity in PostgreSQL to load the fixtures.
|
||||||
|
PostgreSQL will prevent any users to disable referential integrity on the fly if they doesn't have the `SUPERUSER` role.
|
||||||
|
To fix that, logon as the `postgres` user and run the PostgreSQL shell (see [Setup the FabManager database in PostgreSQL](#setup-fabmanager-in-postgresql) for an example).
|
||||||
|
Then, run the following command (replace `sleede` with your test database user, as specified in your database.yml):
|
||||||
|
|
||||||
|
ALTER ROLE sleede WITH SUPERUSER;
|
||||||
|
|
||||||
|
DO NOT do this in a production environment, as this would lead to a serious security issue.
|
||||||
|
|
||||||
<a name="related-documentation"></a>
|
<a name="related-documentation"></a>
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
Application.Controllers.controller "CompleteProfileController", ["$scope", "$rootScope", "$state", "_t", "growl", "CSRF", "Auth", "Member", "settingsPromise", "activeProviderPromise", "groupsPromise", "cguFile", "memberPromise"
|
Application.Controllers.controller "CompleteProfileController", ["$scope", "$rootScope", "$state", "_t", "growl", "CSRF", "Auth", "Member", "settingsPromise", "activeProviderPromise", "groupsPromise", "cguFile", "memberPromise", "Session"
|
||||||
, ($scope, $rootScope, $state, _t, growl, CSRF, Auth, Member, settingsPromise, activeProviderPromise, groupsPromise, cguFile, memberPromise) ->
|
, ($scope, $rootScope, $state, _t, growl, CSRF, Auth, Member, settingsPromise, activeProviderPromise, groupsPromise, cguFile, memberPromise, Session) ->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -83,6 +83,8 @@ Application.Controllers.controller "CompleteProfileController", ["$scope", "$roo
|
|||||||
$rootScope.currentUser = content
|
$rootScope.currentUser = content
|
||||||
$state.go('app.public.home')
|
$state.go('app.public.home')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||||
@ -112,6 +114,8 @@ Application.Controllers.controller "CompleteProfileController", ["$scope", "$roo
|
|||||||
growl.error(_t('an_unexpected_error_occurred_check_your_authentication_code'))
|
growl.error(_t('an_unexpected_error_occurred_check_your_authentication_code'))
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Return the email given by the SSO provider, parsed if needed
|
# Return the email given by the SSO provider, parsed if needed
|
||||||
# @return {String} E-mail of the current user
|
# @return {String} E-mail of the current user
|
||||||
@ -126,6 +130,30 @@ Application.Controllers.controller "CompleteProfileController", ["$scope", "$roo
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Test if the user's mail is marked as duplicate
|
||||||
|
# @return {boolean}
|
||||||
|
##
|
||||||
|
$scope.hasDuplicate = ->
|
||||||
|
email = memberPromise.email
|
||||||
|
if email
|
||||||
|
return !(email.match(/^<([^>]+)>.{20}-duplicate$/) == null)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Disconnect and re-connect the user to the SSO to force the synchronisation of the profile's data
|
||||||
|
##
|
||||||
|
$scope.syncProfile = ->
|
||||||
|
Auth.logout().then (oldUser) ->
|
||||||
|
Session.destroy()
|
||||||
|
$rootScope.currentUser = null
|
||||||
|
$rootScope.toCheckNotifications = false
|
||||||
|
$scope.notifications = []
|
||||||
|
$window.location.href = activeProviderPromise.link_to_sso_connect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### PRIVATE SCOPE ###
|
### PRIVATE SCOPE ###
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,16 +144,18 @@ class ProjectsController
|
|||||||
##
|
##
|
||||||
# Controller used on projects listing page
|
# Controller used on projects listing page
|
||||||
##
|
##
|
||||||
Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise'
|
Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout'
|
||||||
, ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise) ->
|
, ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout) ->
|
||||||
|
|
||||||
### PRIVATE STATIC CONSTANTS ###
|
### PRIVATE STATIC CONSTANTS ###
|
||||||
|
|
||||||
# Number of notifications added to the page when the user clicks on 'load next notifications'
|
# Number of projects added to the page when the user clicks on 'load more projects'
|
||||||
PROJECTS_PER_PAGE = 12
|
PROJECTS_PER_PAGE = 16
|
||||||
|
|
||||||
|
$scope.openlabAppId = Fablab.openlabAppId
|
||||||
|
|
||||||
### PUBLIC SCOPE ###
|
### PUBLIC SCOPE ###
|
||||||
$scope.search = { q: "", from: undefined, machine_id: undefined, component_id: undefined, theme_id: undefined }
|
$scope.search = { q: ($location.$$search.q || ""), from: ($location.$$search.from || undefined), machine_id: (parseInt($location.$$search.machine_id) || undefined), component_id: (parseInt($location.$$search.component_id) || undefined), theme_id: (parseInt($location.$$search.theme_id) || undefined) }
|
||||||
|
|
||||||
## list of projects to display
|
## list of projects to display
|
||||||
$scope.projects = []
|
$scope.projects = []
|
||||||
@ -167,47 +169,101 @@ Application.Controllers.controller "ProjectsController", ["$scope", "$state", 'P
|
|||||||
## list of components / used for filtering
|
## list of components / used for filtering
|
||||||
$scope.components = componentsPromise
|
$scope.components = componentsPromise
|
||||||
|
|
||||||
## By default, the pagination mode is activated to limit the page size
|
$scope.openlab = {}
|
||||||
$scope.paginateActive = true
|
$scope.openlab.projectsActive = Fablab.openlabProjectsActive
|
||||||
|
|
||||||
## The currently displayed page number
|
if $location.$$search.whole_network is 'f'
|
||||||
$scope.page = 1
|
$scope.openlab.searchOverWholeNetwork = false
|
||||||
|
else
|
||||||
|
$scope.openlab.searchOverWholeNetwork = $scope.openlab.projectsActive || false
|
||||||
|
|
||||||
$scope.resetFilters = ->
|
normalizeProjectsAttrs = (projects)->
|
||||||
|
projects.map((project)->
|
||||||
|
project.project_image = project.image_url
|
||||||
|
return project
|
||||||
|
)
|
||||||
|
|
||||||
|
$scope.searchOverWholeNetworkChanged = ->
|
||||||
|
setTimeout ->
|
||||||
|
$scope.resetFiltersAndTriggerSearch()
|
||||||
|
, 150
|
||||||
|
|
||||||
|
loadMoreCallback = (projectsPromise)->
|
||||||
|
$scope.projects = $scope.projects.concat(projectsPromise.projects)
|
||||||
|
updateUrlParam('page', $scope.projectsPagination.currentPage)
|
||||||
|
|
||||||
|
loadMoreOpenlabCallback = (projectsPromise)->
|
||||||
|
$scope.projects = $scope.projects.concat(normalizeProjectsAttrs(projectsPromise.projects))
|
||||||
|
updateUrlParam('page', $scope.projectsPagination.currentPage)
|
||||||
|
|
||||||
|
$scope.loadMore = ->
|
||||||
|
if $scope.openlab.searchOverWholeNetwork is true
|
||||||
|
$scope.projectsPagination.loadMore(q: $scope.search.q)
|
||||||
|
else
|
||||||
|
$scope.projectsPagination.loadMore(search: $scope.search)
|
||||||
|
|
||||||
|
|
||||||
|
$scope.resetFiltersAndTriggerSearch = ->
|
||||||
$scope.search.q = ""
|
$scope.search.q = ""
|
||||||
$scope.search.from = undefined
|
$scope.search.from = undefined
|
||||||
$scope.search.machine_id = undefined
|
$scope.search.machine_id = undefined
|
||||||
$scope.search.component_id = undefined
|
$scope.search.component_id = undefined
|
||||||
$scope.search.theme_id = undefined
|
$scope.search.theme_id = undefined
|
||||||
|
$scope.setUrlQueryParams($scope.search)
|
||||||
$scope.triggerSearch()
|
$scope.triggerSearch()
|
||||||
|
|
||||||
$scope.triggerSearch = ->
|
$scope.triggerSearch = ->
|
||||||
Project.search { search: $scope.search, page: 1 }, (projects)->
|
currentPage = parseInt($location.$$search.page) || 1
|
||||||
$scope.projects = projects
|
if $scope.openlab.searchOverWholeNetwork is true
|
||||||
if projects.length < PROJECTS_PER_PAGE
|
updateUrlParam('whole_network', 't')
|
||||||
$scope.paginateActive = false
|
$scope.projectsPagination = new paginationService.Instance(OpenlabProject, currentPage, PROJECTS_PER_PAGE, null, { }, loadMoreOpenlabCallback)
|
||||||
|
OpenlabProject.query { q: $scope.search.q, page: currentPage, per_page: PROJECTS_PER_PAGE }, (projectsPromise)->
|
||||||
|
if projectsPromise.errors?
|
||||||
|
growl.error(_t('openlab_search_not_available_at_the_moment'))
|
||||||
|
$scope.openlab.searchOverWholeNetwork = false
|
||||||
|
$scope.triggerSearch()
|
||||||
else
|
else
|
||||||
$scope.paginateActive = true
|
$scope.projectsPagination.totalCount = projectsPromise.meta.total
|
||||||
$scope.page = 2
|
$scope.projects = normalizeProjectsAttrs(projectsPromise.projects)
|
||||||
|
|
||||||
$scope.loadMoreProjects = ->
|
|
||||||
# Project.query {page: $scope.page}, (projects) ->
|
|
||||||
# $scope.projects = $scope.projects.concat projects
|
|
||||||
# $scope.paginateActive = false if projects.length < PROJECTS_PER_PAGE
|
|
||||||
Project.search { search: $scope.search, page: $scope.page }, (projects)->
|
|
||||||
$scope.projects = $scope.projects.concat projects
|
|
||||||
$scope.paginateActive = false if projects.length < PROJECTS_PER_PAGE
|
|
||||||
$scope.page += 1
|
|
||||||
|
|
||||||
|
else
|
||||||
|
updateUrlParam('whole_network', 'f')
|
||||||
|
$scope.projectsPagination = new paginationService.Instance(Project, currentPage, PROJECTS_PER_PAGE, null, { }, loadMoreCallback, 'search')
|
||||||
|
Project.search { search: $scope.search, page: currentPage, per_page: PROJECTS_PER_PAGE }, (projectsPromise)->
|
||||||
|
$scope.projectsPagination.totalCount = projectsPromise.meta.total
|
||||||
|
$scope.projects = projectsPromise.projects
|
||||||
|
|
||||||
##
|
##
|
||||||
# Callback to switch the user's view to the detailled project page
|
# Callback to switch the user's view to the detailled project page
|
||||||
# @param project {{slug:string}} The project to display
|
# @param project {{slug:string}} The project to display
|
||||||
##
|
##
|
||||||
$scope.showProject = (project) ->
|
$scope.showProject = (project) ->
|
||||||
|
if ($scope.openlab.searchOverWholeNetwork is true) and (project.app_id isnt Fablab.openlabAppId)
|
||||||
|
$window.open(project.project_url, '_blank')
|
||||||
|
return true
|
||||||
|
else
|
||||||
$state.go('app.public.projects_show', {id: project.slug})
|
$state.go('app.public.projects_show', {id: project.slug})
|
||||||
|
|
||||||
|
##
|
||||||
|
# function to set all url query search parameters from search object
|
||||||
|
##
|
||||||
|
$scope.setUrlQueryParams = (search)->
|
||||||
|
updateUrlParam('page', 1)
|
||||||
|
updateUrlParam('q', search.q)
|
||||||
|
updateUrlParam('from', search.from)
|
||||||
|
updateUrlParam('theme_id', search.theme_id)
|
||||||
|
updateUrlParam('component_id', search.component_id)
|
||||||
|
updateUrlParam('machine_id', search.machine_id)
|
||||||
|
|
||||||
|
##
|
||||||
|
# function to update url query param, little hack to turn off reloadOnSearch and re-enable it after setting the params
|
||||||
|
# params example: 'q' , 'presse-purée'
|
||||||
|
##
|
||||||
|
updateUrlParam = (name, value) ->
|
||||||
|
$state.current.reloadOnSearch = false
|
||||||
|
$location.search(name, value)
|
||||||
|
$timeout ->
|
||||||
|
$state.current.reloadOnSearch = undefined
|
||||||
|
|
||||||
## initialization
|
## initialization
|
||||||
$scope.triggerSearch()
|
$scope.triggerSearch()
|
||||||
|
@ -219,7 +219,7 @@ angular.module('application.router', ['ui.router']).
|
|||||||
|
|
||||||
# projects
|
# projects
|
||||||
.state 'app.public.projects_list',
|
.state 'app.public.projects_list',
|
||||||
url: '/projects'
|
url: '/projects?q&page&theme_id&component_id&machine_id&from&whole_network'
|
||||||
views:
|
views:
|
||||||
'main@':
|
'main@':
|
||||||
templateUrl: '<%= asset_path "projects/index.html" %>'
|
templateUrl: '<%= asset_path "projects/index.html" %>'
|
||||||
|
9
app/assets/javascripts/services/openlab_project.coffee
Normal file
9
app/assets/javascripts/services/openlab_project.coffee
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
Application.Services.factory 'OpenlabProject', ["$resource", ($resource)->
|
||||||
|
$resource "/api/openlab_projects/:id",
|
||||||
|
{id: "@id"},
|
||||||
|
query:
|
||||||
|
method: 'GET'
|
||||||
|
isArray: false
|
||||||
|
]
|
50
app/assets/javascripts/services/pagination_service.coffee
Normal file
50
app/assets/javascripts/services/pagination_service.coffee
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
Application.Services.factory("paginationService", [->
|
||||||
|
helpers = {}
|
||||||
|
|
||||||
|
helpers.pageCount = (totalCount, perPage)->
|
||||||
|
Math.ceil(totalCount/perPage)
|
||||||
|
|
||||||
|
helpers.hasNextPage = (currentPage, totalCount, perPage)->
|
||||||
|
_pageCount = helpers.pageCount(totalCount, perPage)
|
||||||
|
(_pageCount != currentPage) and (_pageCount != 0)
|
||||||
|
|
||||||
|
Instance = (resourceService, currentPage, perPage, totalCount, defaultQueryParams, callback, functionName)->
|
||||||
|
@resourceService = resourceService
|
||||||
|
@currentPage = currentPage
|
||||||
|
@perPage = perPage
|
||||||
|
@totalCount = totalCount
|
||||||
|
@defaultQueryParams = defaultQueryParams
|
||||||
|
@callback = callback
|
||||||
|
@functionName = functionName || 'query'
|
||||||
|
@loading = false
|
||||||
|
|
||||||
|
@pageCount = ->
|
||||||
|
helpers.pageCount(@totalCount, @perPage)
|
||||||
|
|
||||||
|
@hasNextPage = ->
|
||||||
|
helpers.hasNextPage(@currentPage, @totalCount, @perPage)
|
||||||
|
|
||||||
|
@loadMore = (queryParams)->
|
||||||
|
@currentPage += 1
|
||||||
|
@loading = true
|
||||||
|
|
||||||
|
_queryParams = { page: @currentPage, per_page: @perPage }
|
||||||
|
|
||||||
|
if queryParams
|
||||||
|
for k,v of queryParams
|
||||||
|
_queryParams[k] = v
|
||||||
|
|
||||||
|
for k,v of @defaultQueryParams
|
||||||
|
_queryParams[k] = v
|
||||||
|
|
||||||
|
@resourceService[@functionName](_queryParams, (dataPromise)=>
|
||||||
|
@callback(dataPromise)
|
||||||
|
@loading = false
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
return { Instance: Instance }
|
||||||
|
])
|
@ -10,5 +10,5 @@ Application.Services.factory 'Project', ["$resource", ($resource)->
|
|||||||
search:
|
search:
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
url: '/api/projects/search'
|
url: '/api/projects/search'
|
||||||
isArray: true
|
isArray: false
|
||||||
]
|
]
|
||||||
|
@ -65,49 +65,73 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-thumb {
|
// component card
|
||||||
opacity: 0.9;
|
.card {
|
||||||
&:hover { opacity: 1;}
|
|
||||||
&:hover .box-footer { opacity: 1; }
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: .25rem;
|
||||||
|
height: 325px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.card-overlay {
|
||||||
|
margin: 1.25rem;
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(43,46,56,0.8);
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: all 0.1s ease-out;
|
||||||
|
transition: all 0.1s ease-out;
|
||||||
|
height: 180px;
|
||||||
|
cursor: pointer;
|
||||||
|
.btn-group { margin-top: 70px;}
|
||||||
|
.btn {
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
border: 1px solid #fff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.card-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
padding: 1.25rem;
|
||||||
|
.card-header-bg {
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
border-radius: 8px;
|
background-position: center;
|
||||||
margin-bottom: 30px;
|
height: 180px;
|
||||||
cursor: pointer;
|
|
||||||
// todo
|
|
||||||
overflow: hidden;
|
|
||||||
height: 280px;
|
|
||||||
img {
|
|
||||||
opacity: 0.9;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.project-caption {
|
|
||||||
text-shadow: rgba(29, 29, 29, 0.5) 0 -1px, rgba(29, 29, 29, 0.5) -1px 0,
|
|
||||||
rgba(29, 29, 29, 0.5) 1px 0, rgba(29, 29, 29, 0.5) 0 1px;
|
|
||||||
}
|
}
|
||||||
.box-content {
|
.card-block {
|
||||||
position: absolute;
|
padding: 0 1.25rem 1.25rem 1.25rem;
|
||||||
top: 45px;
|
.card-title {
|
||||||
left: 0;
|
font-size: 1.5rem;
|
||||||
right: 0;
|
line-height: 2rem;
|
||||||
h1 {
|
margin: 0;
|
||||||
padding: 0 20px;
|
|
||||||
color: white;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: rem-calc(24);
|
|
||||||
font-weight: 900;
|
|
||||||
line-height: rem-calc(30);
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
.card-meta {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center
|
||||||
}
|
}
|
||||||
.box-footer {
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,3 +488,17 @@ padding: 10px;
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabling-overlay {
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(230, 230, 230, 0.7);
|
||||||
|
position: absolute;
|
||||||
|
z-index:10;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-t-xxl-on-md { margin-top: 60px; }
|
||||||
|
.m-t-3xl-on-md { margin-top: 80px; }
|
||||||
|
.m-t-4xl-on-md { margin-top: 100px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,12 +17,28 @@ p, .widget p {
|
|||||||
&.fleche-left {
|
&.fleche-left {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
|
span.or {
|
||||||
|
border: 2px solid $yellow;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 10px 17px 10px 17px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
img.fleche-left {
|
img.fleche-left {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -35px;
|
left: -35px;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
}
|
}
|
||||||
|
img.fleche-right {
|
||||||
|
position: absolute;
|
||||||
|
right: -35px;
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
img.fleche-left-from-top {
|
||||||
|
transform: rotate(90deg) scaleX(-1);
|
||||||
|
}
|
||||||
|
img.fleche-right-from-top {
|
||||||
|
transform: rotate(270deg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pos-rlt{position: relative;}
|
.pos-rlt{position: relative;}
|
||||||
@ -82,6 +98,8 @@ p, .widget p {
|
|||||||
.text-u-l, .underline {text-decoration: underline;}
|
.text-u-l, .underline {text-decoration: underline;}
|
||||||
.text-c { text-transform: capitalize; }
|
.text-c { text-transform: capitalize; }
|
||||||
|
|
||||||
|
.text-italic { font-style: italic; }
|
||||||
|
|
||||||
.text-center { text-align: center; }
|
.text-center { text-align: center; }
|
||||||
|
|
||||||
.text-active, .active > .text, .active > .auto .text{display: none !important;}
|
.text-active, .active > .text, .active > .auto .text{display: none !important;}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<section class="panel panel-default bg-token m-lg">
|
<section class="panel panel-default bg-token m-lg">
|
||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
<h3 translate>{{ 'do_you_already_have_an_account' }}</h3>
|
<h3 translate>{{ 'do_you_already_have_an_account' }}</h3>
|
||||||
<p translate>{{ 'do_not_fill_the_form_below_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access' }}</p>
|
<p ng-hide="hasDuplicate()" translate>{{ 'do_not_fill_the_form_beside_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access' }}</p>
|
||||||
|
<p ng-show="hasDuplicate()" translate>{{ 'just_specify_code_here_to_recover_access' }}</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-sm-offset-1"></div>
|
<div class="col-lg-3 col-lg-offset-1 hidden-md col-sm-3 col-sm-offset-1"></div>
|
||||||
<div class="col-sm-offset-1 col-sm-6">
|
<div class="col-lg-offset-1 col-lg-6 col-md-12 col-sm-offset-1 col-sm-6">
|
||||||
<div class="form-group" ng-class="{'has-error': userForm['user[auth_token]'].$dirty && userForm['user[auth_token]'].$invalid}">
|
<div class="form-group" ng-class="{'has-error': userForm['user[auth_token]'].$dirty && userForm['user[auth_token]'].$invalid}">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon"><i class="fa fa-key"></i></span>
|
<span class="input-group-addon"><i class="fa fa-key"></i></span>
|
||||||
|
@ -21,34 +21,45 @@
|
|||||||
|
|
||||||
<div class="row no-gutter">
|
<div class="row no-gutter">
|
||||||
<div class="col-sm-12 col-md-12 b-r">
|
<div class="col-sm-12 col-md-12 b-r">
|
||||||
<div class="col-md-8 m-t-md">
|
<div class="row">
|
||||||
|
<div class="col-md-offset-2 col-md-8 m-t-md">
|
||||||
<section class="panel panel-default bg-light m-lg">
|
<section class="panel panel-default bg-light m-lg">
|
||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
{{ 'you_ve_just_created_a_new_account_on_the_fablab_by_logging_from' | translate:{ GENDER: nameGenre, NAME: fablabName }:"messageformat" }}<br/>
|
{{ 'you_ve_just_created_a_new_account_on_the_fablab_by_logging_from' | translate:{ GENDER: nameGenre, NAME: fablabName }:"messageformat" }}<br/>
|
||||||
<img class="m-l v-middle" height="16" width="16" src='https://www.google.com/s2/favicons?domain={{activeProvider.domain}}' />
|
<img class="m-l v-middle" height="16" width="16" src='https://www.google.com/s2/favicons?domain={{activeProvider.domain}}' />
|
||||||
<strong class="v-middle">{{activeProvider.name}} <span ng-if="ssoEmail()">({{ssoEmail()}})</span></strong><br/>
|
<strong class="v-middle">{{activeProvider.name}} <span ng-if="ssoEmail()">({{ssoEmail()}})</span></strong><br/>
|
||||||
<p class="m-t-md">{{ 'before_letting_you_use_the_application_we_need_some_more_details' | translate }}.<br/>
|
<p class="m-t-md" ng-hide="hasDuplicate()" translate>{{ 'before_letting_you_use_the_application_we_need_some_more_details' }}.</p>
|
||||||
{{ 'please_fill_the_following_form' | translate }}.
|
<p class="m-t-md" ng-show="hasDuplicate()" translate>{{ 'your_email_is_already_used_by_another_account_on_the_platform' }}</p>
|
||||||
{{ 'some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified' | translate:{NAME:activeProvider.name} }}.
|
|
||||||
{{ 'then_click_on_' | translate }} <strong translate>{{ 'confirm_changes' }}</strong> {{ '_to_start_using_the_application' | translate }}.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-8" ng-hide="user.merged_at">
|
|
||||||
<ng-include src="'<%= asset_path 'profile/_token.html' %>'"></ng-include>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row col-md-2 col-md-offset-5 hidden-sm hidden-xs">
|
||||||
|
<p class="font-felt fleche-left text-lg upper text-center">
|
||||||
|
<%= image_tag("fleche-left.png", class: 'fleche-left visible-lg visible-md fleche-left-from-top') %>
|
||||||
|
<span class="or" translate>{{ 'or' }}</span>
|
||||||
|
<%= image_tag("fleche-left.png", class: 'fleche-right visible-lg visible-md fleche-right-from-top') %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="m-lg panel panel-default bg-light pos-rlt" ng-hide="hasDuplicate()">
|
||||||
|
<div ng-class="{'disabling-overlay' : !!user.auth_token}">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3 translate>{{ 'new_on_this_platform' }}</h3>
|
||||||
|
<p translate>{{ 'please_fill_the_following_form'}}.</p>
|
||||||
|
<p class="text-italic">{{ 'some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified' | translate:{NAME:activeProvider.name} }}.<br/>
|
||||||
|
{{ 'then_click_on_' | translate }} <strong translate>{{ 'confirm_changes' }}</strong> {{ '_to_start_using_the_application' | translate }}.</p>
|
||||||
|
</div>
|
||||||
<form role="form"
|
<form role="form"
|
||||||
name="userForm"
|
name="userForm"
|
||||||
class="form-horizontal col-md-8 m-t"
|
class="form-horizontal"
|
||||||
action="{{ actionUrl }}"
|
action="{{ actionUrl }}"
|
||||||
ng-upload="submited(content)"
|
ng-upload="submited(content)"
|
||||||
upload-options-enable-rails-csrf="true"
|
upload-options-enable-rails-csrf="true"
|
||||||
ng-if="!user.auth_token"
|
|
||||||
novalidate>
|
novalidate>
|
||||||
|
|
||||||
<section class="panel panel-default bg-light m-lg m-t-xs">
|
<section>
|
||||||
|
|
||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
<!-- common fields -->
|
<!-- common fields -->
|
||||||
@ -99,7 +110,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<section class="m-lg panel panel-default bg-light pos-rlt" ng-show="hasDuplicate()">
|
||||||
|
<div ng-class="{'disabling-overlay' : !!user.auth_token}">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3 translate>{{ 'new_on_this_platform' }}</h3>
|
||||||
|
<p class="text-italic">
|
||||||
|
{{ 'your_email_' | translate }} <strong>({{ssoEmail()}})</strong> {{ '_is_currently_associated_with_another_account_on_this_platform' | translate }}
|
||||||
|
{{ 'please_click_to_change_email_associated_with_your_PROVIDER_account' | translate:{PROVDER: activeProvider.name} }}
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||||
|
<a class="btn btn-default" ng-href="{{activeProvider.link_to_sso_profile}}" target="_blank">
|
||||||
|
<i class="fa fa-edit"></i> {{ 'change_my_data' | translate }}
|
||||||
|
</a>
|
||||||
|
<p class="text-italic">{{ 'once_your_data_are_up_to_date_' | translate }} <strong translate>{{ '_click_on_the_synchronization_button_opposite_' }}</strong> {{ 'or' | translate}} <strong translate>{{ '_disconnect_then_reconnect_' }}</strong> {{ '_for_your_changes_to_take_effect' | translate }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||||
|
<a class="btn btn-default" ng-click="syncProfile()">
|
||||||
|
<i class="fa fa-refresh"></i> {{ 'sync_my_profile' | translate }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="row col-xs-2 col-xs-offset-5 hidden-md hidden-lg">
|
||||||
|
<p class="font-felt fleche-left text-lg upper text-center">
|
||||||
|
<span class="or" translate>{{ 'or' }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 m-t-3xl-on-md" ng-hide="user.merged_at">
|
||||||
|
<ng-include src="'<%= asset_path 'profile/_token.html' %>'"></ng-include>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,25 +22,37 @@
|
|||||||
|
|
||||||
<section class="m-lg">
|
<section class="m-lg">
|
||||||
<div class="row m-b-md">
|
<div class="row m-b-md">
|
||||||
<div class="col-md-12"><h3 class="m-t-xs">{{ 'filter_projects' | translate }}
|
|
||||||
<a href="" class="text-sm pull-right" name="button" ng-click="resetFilters()"><i class="fa fa-refresh"></i> {{ 'reset_all_filters' | translate }}</a></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12 m-b">
|
<div class="col-md-12 m-b">
|
||||||
<form class="form-inline">
|
<a href="javascript:void(0);" class="text-sm pull-right" name="button" ng-click="resetFiltersAndTriggerSearch()" ng-show="!openlab.searchOverWholeNetwork"><i class="fa fa-refresh"></i> {{ 'reset_all_filters' | translate }}</a>
|
||||||
|
|
||||||
|
<span ng-if="openlab.projectsActive" uib-tooltip="{{ 'tooltip_openlab_projects_switch' | translate }}" tooltip-trigger="mouseenter">
|
||||||
|
<label for="searchOverWholeNetwork" class="control-label m-r text-sm" translate>{{ 'search_over_the_whole_network' }}</label>
|
||||||
|
<input bs-switch
|
||||||
|
ng-model="openlab.searchOverWholeNetwork"
|
||||||
|
type="checkbox"
|
||||||
|
class="form-control"
|
||||||
|
switch-on-text="{{ 'yes' | translate }}"
|
||||||
|
switch-off-text="{{ 'no' | translate }}"
|
||||||
|
switch-animate="true"
|
||||||
|
ng-change="searchOverWholeNetworkChanged()"
|
||||||
|
/>
|
||||||
|
{{ searchOverWholeNetwork }}
|
||||||
|
</span>
|
||||||
|
<form class="form-inline m-t text-center" role="form" ng-submit="setUrlQueryParams(search) && triggerSearch()">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
||||||
<input type="search" class="form-control" placeholder="Mots-clés" ng-model="search.q"/>
|
<input type="search" class="form-control" placeholder="Mots-clés" ng-model="search.q"/>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
<button ng-click="triggerSearch()" type="button" class="btn btn-warning" translate>{{ 'search' }}</button>
|
<button type="submit" class="btn btn-warning" translate>{{ 'search' }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<span ng-if="!openlab.searchOverWholeNetwork">
|
||||||
<div class="col-md-3 m-b" ng-show="isAuthenticated()">
|
<div class="col-md-3 m-b" ng-show="isAuthenticated()">
|
||||||
<select ng-model="search.from" ng-change="triggerSearch()" class="form-control">
|
<select ng-model="search.from" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control">
|
||||||
<option value="" translate>{{ 'all_projects' }}</option>
|
<option value="" translate>{{ 'all_projects' }}</option>
|
||||||
<option value="mine" translate>{{ 'my_projects' }}</option>
|
<option value="mine" translate>{{ 'my_projects' }}</option>
|
||||||
<option value="collaboration" translate>{{ 'projects_to_whom_i_take_part_in' }}</option>
|
<option value="collaboration" translate>{{ 'projects_to_whom_i_take_part_in' }}</option>
|
||||||
@ -48,51 +60,62 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 m-b">
|
<div class="col-md-3 m-b">
|
||||||
<select ng-model="search.machine_id" ng-change="triggerSearch()" class="form-control" ng-options="m.id as m.name for m in machines">
|
<select ng-model="search.machine_id" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control" ng-options="m.id as m.name for m in machines">
|
||||||
<option value="" translate>{{ 'all_machines' }}</option>
|
<option value="" translate>{{ 'all_machines' }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 m-b">
|
<div class="col-md-3 m-b">
|
||||||
<select ng-model="search.theme_id" ng-change="triggerSearch()" class="form-control" ng-options="t.id as t.name for t in themes">
|
<select ng-model="search.theme_id" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control" ng-options="t.id as t.name for t in themes">
|
||||||
<option value="" translate>{{ 'all_themes' }}</option>
|
<option value="" translate>{{ 'all_themes' }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 m-b">
|
<div class="col-md-3 m-b">
|
||||||
<select ng-model="search.component_id" ng-change="triggerSearch()" class="form-control" ng-options="t.id as t.name for t in components">
|
<select ng-model="search.component_id" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control" ng-options="t.id as t.name for t in components">
|
||||||
<option value="" translate>{{ 'all_materials' }}</option>
|
<option value="" translate>{{ 'all_materials' }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="project in projects" ng-click="showProject(project)">
|
<span class="col-md-12" ng-show="projects && (projects.length == 0)"> {{ 'project_search_result_is_empty' | translate }} </span>
|
||||||
<div class="box-thumb box-thumb-project" style="background-image: url({{project.project_image}});">
|
<div class="col-xs-12 col-sm-6 col-md-3" ng-repeat="project in projects" ng-click="showProject(project)">
|
||||||
|
|
||||||
|
<div class="card card-project">
|
||||||
|
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-header-bg" style="background-image: url({{project.project_image}});">
|
||||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!project.project_image">
|
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!project.project_image">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-content project-caption">
|
|
||||||
<h1>{{project.name}}</h1>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
|
||||||
|
<div class="card-block">
|
||||||
|
<h3 class="card-meta" ng-if="openlab.searchOverWholeNetwork && project.app_id != openlabAppId"><i class="fa fa-tag"></i> {{ project.app_name }}</h3>
|
||||||
|
<h1 class="card-title">{{project.name}}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-overlay">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<div class="btn btn-default" ui-sref="app.logged.projects_edit({id:project.id})" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')">
|
|
||||||
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
|
|
||||||
</div>
|
|
||||||
<div class="btn btn-default" ng-click="showProject(project)">
|
<div class="btn btn-default" ng-click="showProject(project)">
|
||||||
<i class="fa fa-eye"></i> {{ 'consult' | translate }}
|
<i ng-class="{'fa fa-external-link' : (openlab.searchOverWholeNetwork && project.app_id != openlabAppId) }"></i> {{ 'consult' | translate }}
|
||||||
|
</div>
|
||||||
|
<div class="btn btn-default" ui-sref="app.logged.projects_edit({id:project.id})" ng-if="isAuthorized('admin') && !openlab.searchOverWholeNetwork">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 text-center">
|
<div class="col-lg-12 text-center">
|
||||||
<a class="btn btn-warning" ng-click="loadMoreProjects()" ng-if="paginateActive" translate>{{ 'load_next_projects' }}</a>
|
<a class="btn btn-warning" ng-click="loadMore()" ng-if="projectsPagination.hasNextPage()" translate>{{ 'load_next_projects' }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
11
app/controllers/api/openlab_projects_controller.rb
Normal file
11
app/controllers/api/openlab_projects_controller.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class API::OpenlabProjectsController < API::ApiController
|
||||||
|
PROJECTS = Openlab::Projects.new
|
||||||
|
|
||||||
|
def index
|
||||||
|
begin
|
||||||
|
render json: PROJECTS.search(params[:q], page: params[:page], per_page: params[:per_page]).response.body
|
||||||
|
rescue StandardError
|
||||||
|
render json: { errors: ['service unavailable'] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -51,7 +51,9 @@ class API::ProjectsController < API::ApiController
|
|||||||
|
|
||||||
def search
|
def search
|
||||||
query_params = JSON.parse(params[:search])
|
query_params = JSON.parse(params[:search])
|
||||||
@projects = Project.search(query_params, current_user).page(params[:page]).records
|
records = Project.search(query_params, current_user).page(params[:page]).records
|
||||||
|
@total = records.total
|
||||||
|
@projects = records.includes(:users, :project_image)
|
||||||
render :index
|
render :index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,10 +28,8 @@ class API::ReservationsController < API::ApiController
|
|||||||
is_reserve = @reservation.save_with_payment
|
is_reserve = @reservation.save_with_payment
|
||||||
end
|
end
|
||||||
if is_reserve
|
if is_reserve
|
||||||
reservation_user = @reservation.user
|
SubscriptionExtensionAfterReservation.new(@reservation).extend_subscription_if_eligible
|
||||||
if @reservation.reservable_type == 'Training' and is_first_training_and_active_subscription(reservation_user)
|
|
||||||
reservation_user.subscription.update_expired_date_with_first_training(@reservation.slots.first.start_at)
|
|
||||||
end
|
|
||||||
render :show, status: :created, location: @reservation
|
render :show, status: :created, location: @reservation
|
||||||
else
|
else
|
||||||
render json: @reservation.errors, status: :unprocessable_entity
|
render json: @reservation.errors, status: :unprocessable_entity
|
||||||
@ -57,8 +55,4 @@ class API::ReservationsController < API::ApiController
|
|||||||
:nb_reserve_places, :nb_reserve_reduced_places,
|
:nb_reserve_places, :nb_reserve_reduced_places,
|
||||||
slots_attributes: [:id, :start_at, :end_at, :availability_id, :offered])
|
slots_attributes: [:id, :start_at, :end_at, :availability_id, :offered])
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_first_training_and_active_subscription(user)
|
|
||||||
user.reservations.where(reservable_type: 'Training').size == 1 and user.subscription and !user.subscription.is_expired?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -6,10 +6,7 @@ class API::SlotsController < API::ApiController
|
|||||||
def update
|
def update
|
||||||
authorize @slot
|
authorize @slot
|
||||||
if @slot.update(slot_params)
|
if @slot.update(slot_params)
|
||||||
reservation_user = @slot.reservation.user
|
SubscriptionExtensionAfterReservation.new(@slot.reservation).extend_subscription_if_eligible
|
||||||
if @slot.reservation.reservable_type == 'Training' and is_first_training_and_active_subscription(reservation_user)
|
|
||||||
reservation_user.subscription.update_expired_date_with_first_training(@slot.start_at)
|
|
||||||
end
|
|
||||||
render :show, status: :created, location: @slot
|
render :show, status: :created, location: @slot
|
||||||
else
|
else
|
||||||
render json: @slot.errors, status: :unprocessable_entity
|
render json: @slot.errors, status: :unprocessable_entity
|
||||||
@ -29,8 +26,4 @@ class API::SlotsController < API::ApiController
|
|||||||
def slot_params
|
def slot_params
|
||||||
params.require(:slot).permit(:start_at, :end_at, :availability_id)
|
params.require(:slot).permit(:start_at, :end_at, :availability_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_first_training_and_active_subscription(user)
|
|
||||||
user.reservations.where(reservable_type: 'Training').size == 1 and user.subscription and !user.subscription.is_expired?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -14,16 +14,18 @@ class API::SubscriptionsController < API::ApiController
|
|||||||
else
|
else
|
||||||
if current_user.is_admin?
|
if current_user.is_admin?
|
||||||
@subscription = Subscription.find_or_initialize_by(user_id: subscription_params[:user_id])
|
@subscription = Subscription.find_or_initialize_by(user_id: subscription_params[:user_id])
|
||||||
# @subscription.expired_at = nil
|
|
||||||
@subscription.update_column(:expired_at, nil) unless @subscription.new_record? # very important
|
@subscription.update_column(:expired_at, nil) unless @subscription.new_record? # very important
|
||||||
@subscription.attributes = subscription_params
|
@subscription.attributes = subscription_params
|
||||||
is_subscribe = @subscription.save_with_local_payment(!User.find(subscription_params[:user_id]).invoicing_disabled?)
|
is_subscribe = @subscription.save_with_local_payment(!User.find(subscription_params[:user_id]).invoicing_disabled?)
|
||||||
else
|
else
|
||||||
@subscription = Subscription.find_or_initialize_by(user_id: current_user.id)
|
@subscription = Subscription.find_or_initialize_by(user_id: current_user.id)
|
||||||
# @subscription.expired_at = nil
|
if valid_card_token?(subscription_params[:card_token])
|
||||||
@subscription.update_column(:expired_at, nil) unless @subscription.new_record? # very important
|
@subscription.update_column(:expired_at, nil) unless @subscription.new_record? # very important
|
||||||
@subscription.attributes = subscription_params.merge(user_id: current_user.id)
|
@subscription.attributes = subscription_params.merge(user_id: current_user.id)
|
||||||
is_subscribe = @subscription.save_with_payment
|
is_subscribe = @subscription.save_with_payment
|
||||||
|
else
|
||||||
|
is_subscribe = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if is_subscribe
|
if is_subscribe
|
||||||
render :show, status: :created, location: @subscription
|
render :show, status: :created, location: @subscription
|
||||||
@ -61,4 +63,14 @@ class API::SubscriptionsController < API::ApiController
|
|||||||
def subscription_update_params
|
def subscription_update_params
|
||||||
params.require(:subscription).permit(:expired_at)
|
params.require(:subscription).permit(:expired_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO refactor subscriptions logic and move this in model/validator
|
||||||
|
def valid_card_token?(token)
|
||||||
|
begin
|
||||||
|
Stripe::Token.retrieve(token)
|
||||||
|
rescue Stripe::InvalidRequestError => e
|
||||||
|
@subscription.errors[:card_token] << e.message
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,8 @@ class Abuse < ActiveRecord::Base
|
|||||||
|
|
||||||
after_create :notify_admins_abuse_reported
|
after_create :notify_admins_abuse_reported
|
||||||
|
|
||||||
|
validates :first_name, :last_name, :email, :message, :presence => true
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def notify_admins_abuse_reported
|
def notify_admins_abuse_reported
|
||||||
|
@ -20,7 +20,8 @@ class Availability < ActiveRecord::Base
|
|||||||
|
|
||||||
attr_accessor :is_reserved, :slot_id, :can_modify
|
attr_accessor :is_reserved, :slot_id, :can_modify
|
||||||
|
|
||||||
validate :length_must_be_1h_minimum
|
validates :start_at, :end_at, presence: true
|
||||||
|
validate :length_must_be_1h_minimum, unless: proc { end_at.blank? or start_at.blank? }
|
||||||
validate :should_be_associated
|
validate :should_be_associated
|
||||||
|
|
||||||
def safe_destroy
|
def safe_destroy
|
||||||
@ -64,13 +65,13 @@ class Availability < ActiveRecord::Base
|
|||||||
private
|
private
|
||||||
def length_must_be_1h_minimum
|
def length_must_be_1h_minimum
|
||||||
if end_at < (start_at + 1.hour)
|
if end_at < (start_at + 1.hour)
|
||||||
errors.add(:end_at, t('availabilities.must_be_at_least_1_hour_after_the_start_date'))
|
errors.add(:end_at, I18n.t('availabilities.must_be_at_least_1_hour_after_the_start_date'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_be_associated
|
def should_be_associated
|
||||||
if available_type == 'machines' and machine_ids.count == 0
|
if available_type == 'machines' and machine_ids.count == 0
|
||||||
errors.add(:machine_ids, t('availabilities.must_be_associated_with_at_least_1_machine'))
|
errors.add(:machine_ids, I18n.t('availabilities.must_be_associated_with_at_least_1_machine'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,5 +2,5 @@ class InvoiceItem < ActiveRecord::Base
|
|||||||
belongs_to :invoice
|
belongs_to :invoice
|
||||||
belongs_to :subscription
|
belongs_to :subscription
|
||||||
|
|
||||||
has_one :invoice_item
|
has_one :invoice_item # to associated invoice_items of an invoice to invoice_items of an avoir
|
||||||
end
|
end
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
##DEPRECATED, this class is not used anymore from the migration to the Pricing model
|
|
||||||
##TODO remove in future update, in conjunction with the following migrations:
|
|
||||||
# - 20140606133116_create_machines_pricings.rb
|
|
||||||
# - 20150520133409_migrate_data_from_machines_pricings_to_prices.rb
|
|
||||||
# - 20150603133050_drop_machines_pricings.rb
|
|
||||||
class MachinesPricing < ActiveRecord::Base
|
|
||||||
belongs_to :machine
|
|
||||||
belongs_to :group
|
|
||||||
end
|
|
@ -1,6 +1,7 @@
|
|||||||
class Project < ActiveRecord::Base
|
class Project < ActiveRecord::Base
|
||||||
include AASM
|
include AASM
|
||||||
include NotifyWith::NotificationAttachedObject
|
include NotifyWith::NotificationAttachedObject
|
||||||
|
include OpenlabSync
|
||||||
|
|
||||||
# elastic initialisations
|
# elastic initialisations
|
||||||
include Elasticsearch::Model
|
include Elasticsearch::Model
|
||||||
@ -32,6 +33,9 @@ class Project < ActiveRecord::Base
|
|||||||
has_many :project_steps, dependent: :destroy
|
has_many :project_steps, dependent: :destroy
|
||||||
accepts_nested_attributes_for :project_steps, allow_destroy: true
|
accepts_nested_attributes_for :project_steps, allow_destroy: true
|
||||||
|
|
||||||
|
# validations
|
||||||
|
validates :author, :name, presence: true
|
||||||
|
|
||||||
after_save :after_save_and_publish
|
after_save :after_save_and_publish
|
||||||
|
|
||||||
aasm :column => 'state' do
|
aasm :column => 'state' do
|
||||||
@ -104,7 +108,7 @@ class Project < ActiveRecord::Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if params['q'].empty? # we sort by created_at if there isn't a query
|
if params['q'].blank? # we sort by created_at if there isn't a query
|
||||||
search[:sort] = { created_at: { order: :desc } }
|
search[:sort] = { created_at: { order: :desc } }
|
||||||
else # otherwise we search for the word (q) in various fields
|
else # otherwise we search for the word (q) in various fields
|
||||||
search[:query][:filtered][:query] = {
|
search[:query][:filtered][:query] = {
|
||||||
|
65
app/models/project/openlab_sync.rb
Normal file
65
app/models/project/openlab_sync.rb
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
module Project::OpenlabSync
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
|
||||||
|
after_create :openlab_create, if: :openlab_sync_active?
|
||||||
|
after_update :openlab_update, if: :openlab_sync_active?
|
||||||
|
after_destroy :openlab_destroy, if: :openlab_sync_active?
|
||||||
|
|
||||||
|
def openlab_create
|
||||||
|
OpenlabWorker.delay_for(2.seconds).perform_async(:create, self.id) if self.published?
|
||||||
|
end
|
||||||
|
|
||||||
|
def openlab_update
|
||||||
|
if self.published?
|
||||||
|
if self.state_was == 'draft'
|
||||||
|
OpenlabWorker.perform_async(:create, self.id)
|
||||||
|
else
|
||||||
|
OpenlabWorker.perform_async(:update, self.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def openlab_destroy
|
||||||
|
OpenlabWorker.perform_async(:destroy, self.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def openlab_attributes
|
||||||
|
{
|
||||||
|
id: id, slug: slug, name: name, description: description, tags: tags,
|
||||||
|
machines: machines.map(&:name),
|
||||||
|
components: components.map(&:name),
|
||||||
|
themes: themes.map(&:name),
|
||||||
|
author: author&.profile&.full_name,
|
||||||
|
collaborators: users.map { |u| u.profile.full_name },
|
||||||
|
steps_body: steps_body,
|
||||||
|
image_path: project_image&.attachment&.medium&.url,
|
||||||
|
project_path: "/#!/projects/#{slug}",
|
||||||
|
updated_at: updated_at.to_s(:iso8601),
|
||||||
|
created_at: created_at.to_s(:iso8601),
|
||||||
|
published_at: published_at.to_s(:iso8601)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def steps_body
|
||||||
|
concatenated_steps = project_steps.map { |s| "#{s.title} #{s.description}" }
|
||||||
|
.join(' ').gsub('</p>', ' </p>')
|
||||||
|
.gsub("\r\n", ' ').gsub("\n\r", ' ')
|
||||||
|
.gsub("\n", ' ').gsub("\r", ' ').gsub("\t", ' ')
|
||||||
|
|
||||||
|
strip_tags(concatenated_steps).strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def openlab_sync_active?
|
||||||
|
self.class.openlab_sync_active?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def openlab_sync_active?
|
||||||
|
Rails.application.secrets.openlab_app_secret.present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -45,21 +45,20 @@ class Reservation < ActiveRecord::Base
|
|||||||
# === Machine reservation ===
|
# === Machine reservation ===
|
||||||
when Machine
|
when Machine
|
||||||
base_amount = reservable.prices.find_by(group_id: user.group_id, plan_id: plan.try(:id)).amount
|
base_amount = reservable.prices.find_by(group_id: user.group_id, plan_id: plan.try(:id)).amount
|
||||||
if plan
|
users_credits_manager = UsersCredits::Manager.new(reservation: self, plan: plan)
|
||||||
machine_credit = plan.machine_credits.select {|credit| credit.creditable_id == reservable_id}.first
|
|
||||||
if machine_credit
|
|
||||||
hours_available = machine_credit.hours
|
|
||||||
if !new_plan_being_bought
|
|
||||||
user_credit = user.users_credits.find_by_credit_id(machine_credit.id)
|
|
||||||
if user_credit
|
|
||||||
hours_available = machine_credit.hours - user_credit.hours_used
|
|
||||||
end
|
|
||||||
end
|
|
||||||
slots.each_with_index do |slot, index|
|
slots.each_with_index do |slot, index|
|
||||||
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||||
ii_amount = (index < hours_available ? 0 : base_amount)
|
|
||||||
ii_amount = 0 if (slot.offered and on_site)
|
ii_amount = base_amount # ii_amount default to base_amount
|
||||||
unless on_site
|
|
||||||
|
if users_credits_manager.will_use_credits?
|
||||||
|
ii_amount = (index < users_credits_manager.free_hours_count) ? 0 : base_amount
|
||||||
|
end
|
||||||
|
|
||||||
|
ii_amount = 0 if slot.offered and on_site # if it's a local payment and slot is offered free
|
||||||
|
|
||||||
|
unless on_site # if it's local payment then do not create Stripe::InvoiceItem
|
||||||
ii = Stripe::InvoiceItem.create(
|
ii = Stripe::InvoiceItem.create(
|
||||||
customer: user.stp_customer_id,
|
customer: user.stp_customer_id,
|
||||||
amount: ii_amount,
|
amount: ii_amount,
|
||||||
@ -70,63 +69,16 @@ class Reservation < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description)
|
self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
slots.each do |slot|
|
|
||||||
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
|
||||||
ii_amount = base_amount
|
|
||||||
ii_amount = 0 if (slot.offered and on_site)
|
|
||||||
unless on_site
|
|
||||||
ii = Stripe::InvoiceItem.create(
|
|
||||||
customer: user.stp_customer_id,
|
|
||||||
amount: ii_amount,
|
|
||||||
currency: Rails.application.secrets.stripe_currency,
|
|
||||||
description: description
|
|
||||||
)
|
|
||||||
invoice_items << ii
|
|
||||||
end
|
|
||||||
self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
slots.each do |slot|
|
|
||||||
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
|
||||||
ii_amount = base_amount
|
|
||||||
ii_amount = 0 if (slot.offered and on_site)
|
|
||||||
unless on_site
|
|
||||||
ii = Stripe::InvoiceItem.create(
|
|
||||||
customer: user.stp_customer_id,
|
|
||||||
amount: ii_amount,
|
|
||||||
currency: Rails.application.secrets.stripe_currency,
|
|
||||||
description: description
|
|
||||||
)
|
|
||||||
invoice_items << ii
|
|
||||||
end
|
|
||||||
self.invoice.invoice_items.push InvoiceItem.new(amount: ii_amount, stp_invoice_item_id: (ii.id if ii), description: description)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# === Training reservation ===
|
# === Training reservation ===
|
||||||
when Training
|
when Training
|
||||||
base_amount = reservable.amount_by_group(user.group_id).amount
|
base_amount = reservable.amount_by_group(user.group_id).amount
|
||||||
if plan
|
|
||||||
# Return True if the subscription link a training credit for training reserved by the user
|
|
||||||
training_is_creditable = plan.training_credits.select {|credit| credit.creditable_id == reservable.id}.size > 0
|
|
||||||
|
|
||||||
# Training reserved by the user is free when :
|
# be careful, variable plan can be the user's plan OR the plan user is currently purchasing
|
||||||
|
users_credits_manager = UsersCredits::Manager.new(reservation: self, plan: plan)
|
||||||
|
base_amount = 0 if users_credits_manager.will_use_credits?
|
||||||
|
|
||||||
# |-> the user already has a current subscription and if training_is_creditable is true and has at least one credit available.
|
|
||||||
if !new_plan_being_bought
|
|
||||||
if user.training_credits.size < plan.training_credit_nb and training_is_creditable
|
|
||||||
base_amount = 0
|
|
||||||
end
|
|
||||||
# |-> the user buys a new subscription and if training_is_creditable is true.
|
|
||||||
else
|
|
||||||
if training_is_creditable
|
|
||||||
base_amount = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
slots.each do |slot|
|
slots.each do |slot|
|
||||||
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||||
ii_amount = base_amount
|
ii_amount = base_amount
|
||||||
@ -176,32 +128,6 @@ class Reservation < ActiveRecord::Base
|
|||||||
invoice_items
|
invoice_items
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_users_credits
|
|
||||||
if user.subscribed_plan
|
|
||||||
if reservable_type == 'Machine'
|
|
||||||
machine_credit = user.subscribed_plan.machine_credits.select {|credit| credit.creditable_id == reservable_id}.first
|
|
||||||
if machine_credit
|
|
||||||
hours_available = machine_credit.hours
|
|
||||||
user_credit = user.users_credits.find_or_initialize_by(credit_id: machine_credit.id)
|
|
||||||
user_credit.hours_used ||= 0
|
|
||||||
hours_available = machine_credit.hours - user_credit.hours_used
|
|
||||||
if hours_available >= slots.size
|
|
||||||
user_credit.hours_used = user_credit.hours_used + slots.size
|
|
||||||
else
|
|
||||||
user_credit.hours_used = machine_credit.hours
|
|
||||||
end
|
|
||||||
user_credit.save
|
|
||||||
end
|
|
||||||
elsif reservable_type == 'Training'
|
|
||||||
training_credit = user.subscribed_plan.training_credits.select {|credit| credit.creditable_id == reservable_id}.first
|
|
||||||
if user.training_credits.size < user.subscribed_plan.training_credit_nb and training_credit
|
|
||||||
user.credits << training_credit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_with_payment
|
def save_with_payment
|
||||||
build_invoice(user: user)
|
build_invoice(user: user)
|
||||||
invoice_items = generate_invoice_items
|
invoice_items = generate_invoice_items
|
||||||
@ -218,6 +144,10 @@ class Reservation < ActiveRecord::Base
|
|||||||
total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+)
|
total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+)
|
||||||
self.invoice.total = total
|
self.invoice.total = total
|
||||||
save!
|
save!
|
||||||
|
#
|
||||||
|
# IMPORTANT NOTE: here, we don't have to create a stripe::invoice and pay it
|
||||||
|
# because subscription.create (in subscription.rb) will pay all waiting stripe invoice items
|
||||||
|
#
|
||||||
else
|
else
|
||||||
# error handling
|
# error handling
|
||||||
invoice_items.each(&:delete)
|
invoice_items.each(&:delete)
|
||||||
@ -237,6 +167,9 @@ class Reservation < ActiveRecord::Base
|
|||||||
customer.save
|
customer.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
#
|
||||||
|
# IMPORTANT NOTE: here, we have to create an invoice manually and pay it to pay all waiting stripe invoice items
|
||||||
|
#
|
||||||
invoice = Stripe::Invoice.create(
|
invoice = Stripe::Invoice.create(
|
||||||
customer: user.stp_customer_id,
|
customer: user.stp_customer_id,
|
||||||
)
|
)
|
||||||
@ -286,7 +219,8 @@ class Reservation < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
update_users_credits
|
UsersCredits::Manager.new(reservation: self).update_credits
|
||||||
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -318,7 +252,7 @@ class Reservation < ActiveRecord::Base
|
|||||||
if user.invoicing_disabled?
|
if user.invoicing_disabled?
|
||||||
if valid?
|
if valid?
|
||||||
save!
|
save!
|
||||||
update_users_credits
|
UsersCredits::Manager.new(reservation: self).update_credits
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -345,7 +279,8 @@ class Reservation < ActiveRecord::Base
|
|||||||
save!
|
save!
|
||||||
end
|
end
|
||||||
|
|
||||||
update_users_credits
|
UsersCredits::Manager.new(reservation: self).update_credits
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ class Subscription < ActiveRecord::Base
|
|||||||
has_many :offer_days, dependent: :destroy
|
has_many :offer_days, dependent: :destroy
|
||||||
|
|
||||||
validates_presence_of :plan_id
|
validates_presence_of :plan_id
|
||||||
|
validates_with SubscriptionGroupValidator
|
||||||
|
|
||||||
attr_accessor :card_token
|
attr_accessor :card_token
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ class Subscription < ActiveRecord::Base
|
|||||||
self.expired_at = Time.at(new_subscription.current_period_end)
|
self.expired_at = Time.at(new_subscription.current_period_end)
|
||||||
save!
|
save!
|
||||||
|
|
||||||
reset_users_credits if expired_date_changed
|
UsersCredits::Manager.new(user: self.user).reset_credits if expired_date_changed
|
||||||
|
|
||||||
# generate invoice
|
# generate invoice
|
||||||
stp_invoice = Stripe::Invoice.all(customer: user.stp_customer_id, limit: 1).data.first
|
stp_invoice = Stripe::Invoice.all(customer: user.stp_customer_id, limit: 1).data.first
|
||||||
@ -76,7 +77,7 @@ class Subscription < ActiveRecord::Base
|
|||||||
self.canceled_at = nil
|
self.canceled_at = nil
|
||||||
set_expired_at
|
set_expired_at
|
||||||
save!
|
save!
|
||||||
reset_users_credits if expired_date_changed
|
UsersCredits::Manager.new(user: self.user).reset_credits if expired_date_changed
|
||||||
generate_invoice.save if invoice
|
generate_invoice.save if invoice
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
@ -101,12 +102,6 @@ class Subscription < ActiveRecord::Base
|
|||||||
invoice.save
|
invoice.save
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_expired_date_with_first_training(training_start_at)
|
|
||||||
if plan.is_rolling?
|
|
||||||
update_columns(expired_at: training_start_at + plan.duration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def cancel
|
def cancel
|
||||||
if stp_subscription_id.present?
|
if stp_subscription_id.present?
|
||||||
stp_subscription = stripe_subscription
|
stp_subscription = stripe_subscription
|
||||||
@ -139,7 +134,7 @@ class Subscription < ActiveRecord::Base
|
|||||||
|
|
||||||
self.expired_at = expired_at
|
self.expired_at = expired_at
|
||||||
if save
|
if save
|
||||||
reset_users_credits if !free_days
|
UsersCredits::Manager.new(user: self.user).reset_credits if !free_days
|
||||||
notify_subscription_extended(free_days)
|
notify_subscription_extended(free_days)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -147,18 +142,6 @@ class Subscription < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def update_payment
|
|
||||||
if stp_subscription_id.present?
|
|
||||||
stp_subscription = stripe_subscription
|
|
||||||
stp_subscription.plan = plan.stp_plan_id
|
|
||||||
stp_subscription.prorate = false
|
|
||||||
stp_subscription.save
|
|
||||||
else
|
|
||||||
# TODO: implement stripe payment if member update subscription
|
|
||||||
# must have a card
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def notify_member_subscribed_plan
|
def notify_member_subscribed_plan
|
||||||
NotificationCenter.call type: 'notify_member_subscribed_plan',
|
NotificationCenter.call type: 'notify_member_subscribed_plan',
|
||||||
receiver: user,
|
receiver: user,
|
||||||
@ -214,10 +197,6 @@ class Subscription < ActiveRecord::Base
|
|||||||
p_value.to_date != expired_at.to_date and expired_at > p_value
|
p_value.to_date != expired_at.to_date and expired_at > p_value
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_users_credits
|
|
||||||
user.users_credits.destroy_all
|
|
||||||
end
|
|
||||||
|
|
||||||
# def is_being_extended?
|
# def is_being_extended?
|
||||||
# !expired_at_was.nil? and expired_at_changed?
|
# !expired_at_was.nil? and expired_at_changed?
|
||||||
# end
|
# end
|
||||||
|
@ -65,6 +65,8 @@ class User < ActiveRecord::Base
|
|||||||
validates :username, presence: true, uniqueness: true, length: { maximum: 30 }
|
validates :username, presence: true, uniqueness: true, length: { maximum: 30 }
|
||||||
|
|
||||||
scope :active, -> { where(is_active: true) }
|
scope :active, -> { where(is_active: true) }
|
||||||
|
scope :without_subscription, -> { includes(:subscriptions).where(subscriptions: { user_id: nil }) }
|
||||||
|
scope :with_subscription, -> { joins(:subscriptions) }
|
||||||
|
|
||||||
def to_builder
|
def to_builder
|
||||||
Jbuilder.new do |json|
|
Jbuilder.new do |json|
|
||||||
|
26
app/services/subscription_extension_after_reservation.rb
Normal file
26
app/services/subscription_extension_after_reservation.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
class SubscriptionExtensionAfterReservation
|
||||||
|
attr_accessor :user, :reservation
|
||||||
|
|
||||||
|
def initialize(reservation)
|
||||||
|
@user, @reservation = reservation.user, reservation
|
||||||
|
end
|
||||||
|
|
||||||
|
def extend_subscription_if_eligible
|
||||||
|
extend_subscription if eligible_to_extension?
|
||||||
|
end
|
||||||
|
|
||||||
|
def eligible_to_extension?
|
||||||
|
return false unless reservation.reservable_type == 'Training'
|
||||||
|
return false if user.reservations.where(reservable_type: 'Training').count != 1
|
||||||
|
return false unless user.subscription
|
||||||
|
return false if user.subscription.is_expired?
|
||||||
|
return false unless user.subscribed_plan.is_rolling
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def extend_subscription
|
||||||
|
user.subscription.update_columns(
|
||||||
|
expired_at: reservation.slots.first.start_at + user.subscribed_plan.duration
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
156
app/services/users_credits/manager.rb
Normal file
156
app/services/users_credits/manager.rb
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
require 'forwardable'
|
||||||
|
|
||||||
|
module UsersCredits
|
||||||
|
class AlreadyUpdatedError < StandardError;end;
|
||||||
|
|
||||||
|
class Manager
|
||||||
|
extend Forwardable
|
||||||
|
attr_reader :manager
|
||||||
|
|
||||||
|
def initialize(reservation: nil, user: nil, plan: nil)
|
||||||
|
if user
|
||||||
|
@manager = Managers::User.new(user)
|
||||||
|
elsif reservation
|
||||||
|
if reservation.reservable_type == "Training"
|
||||||
|
@manager = Managers::Training.new(reservation, plan)
|
||||||
|
elsif reservation.reservable_type == "Machine"
|
||||||
|
@manager = Managers::Machine.new(reservation, plan)
|
||||||
|
elsif reservation.reservable_type == "Event"
|
||||||
|
@manager = Managers::Event.new(reservation, plan)
|
||||||
|
else
|
||||||
|
raise ArgumentError, "reservation.reservable_type must be Training, Machine or Event"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise ArgumentError, "you have to pass either a reservation or a user to initialize a UsersCredits::Manager"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def_delegators :@manager, :will_use_credits?, :free_hours_count, :update_credits, :reset_credits
|
||||||
|
end
|
||||||
|
|
||||||
|
module Managers
|
||||||
|
class User # that class is responsible for reseting users_credits of a user
|
||||||
|
attr_reader :user
|
||||||
|
|
||||||
|
def initialize(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_credits
|
||||||
|
user.users_credits.destroy_all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Reservation
|
||||||
|
attr_reader :reservation
|
||||||
|
|
||||||
|
def initialize(reservation, plan) # a plan can be passed to do a simulation (if user didn't have a subscription YET)
|
||||||
|
@reservation = reservation
|
||||||
|
@already_updated = false
|
||||||
|
@plan = plan
|
||||||
|
end
|
||||||
|
|
||||||
|
def plan
|
||||||
|
@plan || user.subscribed_plan
|
||||||
|
end
|
||||||
|
|
||||||
|
def user
|
||||||
|
reservation.user
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_credits
|
||||||
|
if @already_updated
|
||||||
|
raise AlreadyUpdatedError, "update credit is not idempotent ! you can't invoke update_credits method twice."
|
||||||
|
else
|
||||||
|
@already_updated = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_constant :Reservation
|
||||||
|
|
||||||
|
|
||||||
|
class Machine < Reservation # that class is responsible for knowing how to update users_credit of a given user for a given reservation
|
||||||
|
def will_use_credits? # to known if a credit will be used in the context of the given reservation
|
||||||
|
_will_use_credits?[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def free_hours_count
|
||||||
|
_will_use_credits?[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_credits
|
||||||
|
super
|
||||||
|
|
||||||
|
will_use_credits, free_hours_count, machine_credit = _will_use_credits?
|
||||||
|
if will_use_credits
|
||||||
|
users_credit = user.users_credits.find_or_initialize_by(credit_id: machine_credit.id)
|
||||||
|
|
||||||
|
if users_credit.new_record?
|
||||||
|
users_credit.hours_used = free_hours_count
|
||||||
|
else
|
||||||
|
users_credit.hours_used += free_hours_count
|
||||||
|
end
|
||||||
|
users_credit.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def _will_use_credits?
|
||||||
|
return false, 0 unless plan
|
||||||
|
|
||||||
|
if machine_credit = plan.machine_credits.find_by(creditable_id: reservation.reservable_id)
|
||||||
|
users_credit = user.users_credits.find_by(credit_id: machine_credit.id)
|
||||||
|
already_used_hours = users_credit ? users_credit.hours_used : 0
|
||||||
|
|
||||||
|
remaining_hours = machine_credit.hours - already_used_hours
|
||||||
|
|
||||||
|
free_hours_count = [remaining_hours, reservation.slots.size].min
|
||||||
|
|
||||||
|
if free_hours_count > 0
|
||||||
|
return true, free_hours_count, machine_credit
|
||||||
|
else
|
||||||
|
return false, free_hours_count, machine_credit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Training < Reservation # same as class Machine but for Training reservation
|
||||||
|
def will_use_credits?
|
||||||
|
_will_use_credits?[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_credits
|
||||||
|
super
|
||||||
|
will_use_credits, training_credit = _will_use_credits?
|
||||||
|
if will_use_credits
|
||||||
|
user.credits << training_credit # we create a new UsersCredit object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def _will_use_credits?
|
||||||
|
return false, nil unless plan
|
||||||
|
|
||||||
|
# if there is a training_credit defined for this plan and this training
|
||||||
|
if training_credit = plan.training_credits.find_by(creditable_id: reservation.reservable_id)
|
||||||
|
# if user has not used all the plan credits
|
||||||
|
if user.training_credits.where(plan: plan).count < plan.training_credit_nb
|
||||||
|
return true, training_credit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Event < Reservation
|
||||||
|
def will_use_credits?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_credits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
app/validators/stripe_card_token_validator.rb
Normal file
15
app/validators/stripe_card_token_validator.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
class StripeCardTokenValidator
|
||||||
|
def validate(record)
|
||||||
|
if options[:token]
|
||||||
|
begin
|
||||||
|
res = Stripe::Token.retrieve(options[:token])
|
||||||
|
if res[:id] != options[:token]
|
||||||
|
record.errors[:card_token] << "A problem occurred while retrieving the card with the specified token: #{res.id}"
|
||||||
|
end
|
||||||
|
rescue Stripe::InvalidRequestError => e
|
||||||
|
record.errors[:card_token] << e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
app/validators/subscription_group_validator.rb
Normal file
7
app/validators/subscription_group_validator.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
class SubscriptionGroupValidator < ActiveModel::Validator
|
||||||
|
def validate(record)
|
||||||
|
if record.user.group != record.plan.group
|
||||||
|
record.errors[:plan_id] << "This plan is not compatible with the current user's group"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,3 +1,3 @@
|
|||||||
json.admin do
|
json.reporting do
|
||||||
json.extract! @abuse, :id, :signaled_id, :signaled_type
|
json.extract! @abuse, :id, :signaled_id, :signaled_type
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
json.array!(@projects) do |project|
|
json.projects @projects do |project|
|
||||||
json.extract! project, :id, :name, :description, :author_id, :licence_id, :slug
|
json.extract! project, :id, :name, :description, :author_id, :licence_id, :slug
|
||||||
json.url project_url(project, format: :json)
|
json.url project_url(project, format: :json)
|
||||||
json.project_image project.project_image.attachment.medium.url if project.project_image
|
json.project_image project.project_image.attachment.medium.url if project.project_image
|
||||||
json.machine_ids project.machine_ids
|
|
||||||
json.author_id project.author_id
|
json.author_id project.author_id
|
||||||
json.user_ids project.user_ids
|
json.user_ids project.user_ids
|
||||||
json.theme_ids project.theme_ids
|
end
|
||||||
json.component_ids project.component_ids
|
|
||||||
|
json.meta do
|
||||||
|
json.total @total if @total
|
||||||
end
|
end
|
||||||
|
@ -45,6 +45,12 @@
|
|||||||
Fablab.weekStartingDay = <%= Date.parse(Rails.application.secrets.week_starting_day).strftime('%w') %>;
|
Fablab.weekStartingDay = <%= Date.parse(Rails.application.secrets.week_starting_day).strftime('%w') %>;
|
||||||
Fablab.d3DateFormat = "<%= Rails.application.secrets.d3_date_format %>";
|
Fablab.d3DateFormat = "<%= Rails.application.secrets.d3_date_format %>";
|
||||||
Fablab.uibDateFormat = "<%= Rails.application.secrets.uib_date_format %>";
|
Fablab.uibDateFormat = "<%= Rails.application.secrets.uib_date_format %>";
|
||||||
|
Fablab.openlabProjectsActive = <%= Rails.application.secrets.openlab_app_secret.present? %>;
|
||||||
|
<% if Rails.application.secrets.openlab_app_id.present? %>
|
||||||
|
Fablab.openlabAppId = "<%= Rails.application.secrets.openlab_app_id %>";
|
||||||
|
<% else %>
|
||||||
|
Fablab.openlabAppId = null;
|
||||||
|
<% end %>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<%= stylesheet_link_tag 'application', media: 'all' %>
|
<%= stylesheet_link_tag 'application', media: 'all' %>
|
||||||
@ -54,8 +60,10 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<base href="/"></base>
|
<base href="/"></base>
|
||||||
|
|
||||||
|
<% if CustomAsset.get_url('favicon-file') %>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="<%= CustomAsset.get_url('favicon-file') %>">
|
<link rel="shortcut icon" type="image/x-icon" href="<%= CustomAsset.get_url('favicon-file') %>">
|
||||||
<link rel="shortcut icon" type="image/ico" href="<%= CustomAsset.get_url('favicon-file') %>">
|
<link rel="shortcut icon" type="image/ico" href="<%= CustomAsset.get_url('favicon-file') %>">
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
|
24
app/workers/openlab_worker.rb
Normal file
24
app/workers/openlab_worker.rb
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
class OpenlabWorker
|
||||||
|
include Sidekiq::Worker
|
||||||
|
sidekiq_options queue: 'default', retry: true
|
||||||
|
|
||||||
|
Logger = Sidekiq.logger.level == Logger::DEBUG ? Sidekiq.logger : nil
|
||||||
|
OPENLAB_CLIENT = Openlab::Projects.new
|
||||||
|
|
||||||
|
def perform(action, project_id)
|
||||||
|
logger.debug ["Openlab sync", action, "project ID: #{project_id}"]
|
||||||
|
|
||||||
|
case action.to_s
|
||||||
|
when /create/
|
||||||
|
project = Project.find(project_id)
|
||||||
|
response = OPENLAB_CLIENT.create(project.openlab_attributes)
|
||||||
|
when /update/
|
||||||
|
project = Project.find(project_id)
|
||||||
|
response = OPENLAB_CLIENT.update(project_id, project.openlab_attributes)
|
||||||
|
when /destroy/
|
||||||
|
response = OPENLAB_CLIENT.destroy(project_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
logger.debug ["Openlab sync", "RESPONSE ERROR", response.inspect] unless response.success?
|
||||||
|
end
|
||||||
|
end
|
@ -47,3 +47,7 @@ TIME_ZONE: 'Paris'
|
|||||||
WEEK_STARTING_DAY: 'monday'
|
WEEK_STARTING_DAY: 'monday'
|
||||||
D3_DATE_FORMAT: '%d/%m/%y'
|
D3_DATE_FORMAT: '%d/%m/%y'
|
||||||
UIB_DATE_FORMAT: 'dd/MM/yyyy'
|
UIB_DATE_FORMAT: 'dd/MM/yyyy'
|
||||||
|
|
||||||
|
OPENLAB_APP_SECRET:
|
||||||
|
OPENLAB_APP_ID:
|
||||||
|
OPENLAB_BASE_URI: 'https://openprojects.fab-manager.com'
|
||||||
|
@ -39,6 +39,6 @@ Rails.application.configure do
|
|||||||
# Raises error for missing translations
|
# Raises error for missing translations
|
||||||
# config.action_view.raise_on_missing_translations = true
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
|
||||||
|
config.active_support.test_order = :random
|
||||||
|
|
||||||
end
|
end
|
||||||
|
26
config/initializers/active_record_base.rb
Normal file
26
config/initializers/active_record_base.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
ActiveRecord::Base.class_eval do
|
||||||
|
def dump_fixture
|
||||||
|
fixture_file = "#{Rails.root}/test/fixtures/#{self.class.table_name}.yml"
|
||||||
|
File.open(fixture_file, "a") do |f|
|
||||||
|
f.puts({ "#{self.class.table_name.singularize}_#{id}" => attributes }.
|
||||||
|
to_yaml.sub!(/---\s?/, "\n"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dump_fixtures
|
||||||
|
fixture_file = "#{Rails.root}/test/fixtures/#{self.table_name}.yml"
|
||||||
|
mode = (File.exists?(fixture_file) ? 'a' : 'w')
|
||||||
|
File.open(fixture_file, mode) do |f|
|
||||||
|
|
||||||
|
if self.attribute_names.include?("id")
|
||||||
|
self.all.each do |instance|
|
||||||
|
f.puts({ "#{self.table_name.singularize}_#{instance.id}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.all.each_with_index do |instance, i|
|
||||||
|
f.puts({ "#{self.table_name.singularize}_#{i}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
config/initializers/openlab_ruby.rb
Normal file
4
config/initializers/openlab_ruby.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Openlab.configure do |config|
|
||||||
|
config.app_secret = Rails.application.secrets.openlab_app_secret
|
||||||
|
config.base_uri = Rails.application.secrets.openlab_base_uri
|
||||||
|
end
|
@ -6,12 +6,18 @@ en:
|
|||||||
confirm_your_new_account: "Confirm your new account"
|
confirm_your_new_account: "Confirm your new account"
|
||||||
you_ve_just_created_a_new_account_on_the_fablab_by_logging_from: "You've just created a new account on the {NAME}, by logging from" # messageFormat interpolation
|
you_ve_just_created_a_new_account_on_the_fablab_by_logging_from: "You've just created a new account on the {NAME}, by logging from" # messageFormat interpolation
|
||||||
before_letting_you_use_the_application_we_need_some_more_details: "Before letting you use the application, we need some more details"
|
before_letting_you_use_the_application_we_need_some_more_details: "Before letting you use the application, we need some more details"
|
||||||
|
your_email_is_already_used_by_another_account_on_the_platform: "But wait, there is a problem! Your email is already used by another account on the platform."
|
||||||
please_fill_the_following_form: "Please fill the following form"
|
please_fill_the_following_form: "Please fill the following form"
|
||||||
some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified: "Some data may have already been provided by {{NAME}} and cannot be modified" # angular interpolation
|
some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified: "Some data may have already been provided by {{NAME}} and cannot be modified" # angular interpolation
|
||||||
then_click_on_: "Then click on"
|
then_click_on_: "Then click on"
|
||||||
_to_start_using_the_application: "to start using the application"
|
_to_start_using_the_application: "to start using the application"
|
||||||
|
new_on_this_platform: "New on this platform?"
|
||||||
|
your_email_: "Your email"
|
||||||
|
_is_currently_associated_with_another_account_on_this_platform: "is currently associated with another account on this platform."
|
||||||
|
please_click_to_change_email_associated_with_your_PROVIDER_account: "If it is not yours, please click on the following button to change the email associated with your {{PROVIDER}} account." # angular interpolation
|
||||||
do_you_already_have_an_account: "Do you already have an account?"
|
do_you_already_have_an_account: "Do you already have an account?"
|
||||||
do_not_fill_the_form_below_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access: "Do not fill the form below but specify here the code you've received by email, to recover your access"
|
do_not_fill_the_form_beside_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access: "Do not fill the form beside but specify here the code you've received by email, to recover your access."
|
||||||
|
just_specify_code_here_to_recover_access: "Just specify here the code you've received by email to recover your access."
|
||||||
authentification_code: "Authentification code"
|
authentification_code: "Authentification code"
|
||||||
confirm_my_code: "Confirm my code"
|
confirm_my_code: "Confirm my code"
|
||||||
an_unexpected_error_occurred_check_your_authentication_code: "An unexpected error occurred, please check your authentication code."
|
an_unexpected_error_occurred_check_your_authentication_code: "An unexpected error occurred, please check your authentication code."
|
||||||
@ -32,13 +38,6 @@ en:
|
|||||||
no_labels: "No labels"
|
no_labels: "No labels"
|
||||||
delete_my_account: "Delete my account"
|
delete_my_account: "Delete my account"
|
||||||
edit_my_profile: "Edit my profile"
|
edit_my_profile: "Edit my profile"
|
||||||
change_my_data: "Change my data"
|
|
||||||
once_your_data_are_up_to_date_: "Once your data are up to date,"
|
|
||||||
_click_on_the_synchronization_button_opposite_: "click on the synchronization button opposite"
|
|
||||||
_or_: 'or'
|
|
||||||
_disconnect_then_reconnect_: "disconnect then reconnect"
|
|
||||||
_for_your_changes_to_take_effect: "for your changes to take effect."
|
|
||||||
sync_my_profile: "Sync my profile"
|
|
||||||
your_group_has_been_successfully_changed: "Your group has been successfully changed."
|
your_group_has_been_successfully_changed: "Your group has been successfully changed."
|
||||||
an_unexpected_error_prevented_your_group_from_being_changed: "An unexpected error prevented your group from being changed."
|
an_unexpected_error_prevented_your_group_from_being_changed: "An unexpected error prevented your group from being changed."
|
||||||
do_you_really_want_to_delete_your_account: "Do you really want to delete your account?"
|
do_you_really_want_to_delete_your_account: "Do you really want to delete your account?"
|
||||||
|
@ -6,12 +6,18 @@ fr:
|
|||||||
confirm_your_new_account: "Confirmez votre nouveau compte"
|
confirm_your_new_account: "Confirmez votre nouveau compte"
|
||||||
you_ve_just_created_a_new_account_on_the_fablab_by_logging_from: "Vous venez de créer un nouveau compte sur {GENDER, select, male{le} female{la} other{les}} {NAME}, en vous connectant depuis" # messageFormat interpolation
|
you_ve_just_created_a_new_account_on_the_fablab_by_logging_from: "Vous venez de créer un nouveau compte sur {GENDER, select, male{le} female{la} other{les}} {NAME}, en vous connectant depuis" # messageFormat interpolation
|
||||||
before_letting_you_use_the_application_we_need_some_more_details: "Avant de vous laisser utiliser l'application, nous avons besoin de quelques renseignements supplémentaires"
|
before_letting_you_use_the_application_we_need_some_more_details: "Avant de vous laisser utiliser l'application, nous avons besoin de quelques renseignements supplémentaires"
|
||||||
|
your_email_is_already_used_by_another_account_on_the_platform: "Mais attendez, il y a un problème ! Votre adresse de courriel est déjà utilisée par un autre compte sur cette plate-forme."
|
||||||
please_fill_the_following_form: "Merci de compléter le formulaire suivant"
|
please_fill_the_following_form: "Merci de compléter le formulaire suivant"
|
||||||
some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified: "Certaines informations peuvent nous avoir été déjà fournies par {{NAME}} et ne sont pas modifiables" # angular interpolation
|
some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified: "Certaines informations peuvent nous avoir été déjà fournies par {{NAME}} et ne sont pas modifiables" # angular interpolation
|
||||||
then_click_on_: "Cliquez ensuite sur"
|
then_click_on_: "Cliquez ensuite sur"
|
||||||
_to_start_using_the_application: "pour commencer à utiliser l'application"
|
_to_start_using_the_application: "pour commencer à utiliser l'application"
|
||||||
|
new_on_this_platform: "Nouveau sur cette plate-forme ?"
|
||||||
|
your_email_: "Votre adresse de courriel"
|
||||||
|
_is_currently_associated_with_another_account_on_this_platform: "est actuellement associée avec un autre compte sur cette plate-forme."
|
||||||
|
please_click_to_change_email_associated_with_your_PROVIDER_account: "Si ce n'est pas le vôtre, merci de cliquer sur le bouton ci-dessous pour changer l'adresse de courriel associée avec votre compte {{PROVIDER}}." # angular interpolation
|
||||||
do_you_already_have_an_account: "Vous possédez déjà un compte ?"
|
do_you_already_have_an_account: "Vous possédez déjà un compte ?"
|
||||||
do_not_fill_the_form_below_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access: "Ne remplissez pas le formulaire ci-dessous mais indiquez ici le code qui vous a été fourni par e-mail, cela vous permettra de récupérer l'accès à votre compte."
|
do_not_fill_the_form_beside_but_specify_here_the_code_you_ve_received_by_email_to_recover_your_access: "Ne remplissez pas le formulaire à gauche mais indiquez ici le code qui vous a été fourni par e-mail, cela vous permettra de récupérer l'accès à votre compte."
|
||||||
|
just_specify_code_here_to_recover_access: "Indiquez simplement ici le code que vous avez reçu par e-mail, cela vous permettra de récupérer l'accès à votre compte."
|
||||||
authentification_code: "Code d'authentification"
|
authentification_code: "Code d'authentification"
|
||||||
confirm_my_code: "Valider mon code"
|
confirm_my_code: "Valider mon code"
|
||||||
an_unexpected_error_occurred_check_your_authentication_code: "Une erreur inattendue est survenue, vérifiez votre code d'authentification."
|
an_unexpected_error_occurred_check_your_authentication_code: "Une erreur inattendue est survenue, vérifiez votre code d'authentification."
|
||||||
@ -32,13 +38,6 @@ fr:
|
|||||||
no_labels: "Aucune étiquette"
|
no_labels: "Aucune étiquette"
|
||||||
delete_my_account: "Supprimer mon compte"
|
delete_my_account: "Supprimer mon compte"
|
||||||
edit_my_profile: "Éditer votre profil"
|
edit_my_profile: "Éditer votre profil"
|
||||||
change_my_data: "Modifier mes données"
|
|
||||||
once_your_data_are_up_to_date_: "Une fois vos données à jour,"
|
|
||||||
_click_on_the_synchronization_button_opposite_: "cliquez sur le bouton de synchronisation ci-contre"
|
|
||||||
_or_: 'ou'
|
|
||||||
_disconnect_then_reconnect_: "déconnectez-vous puis re-connectez vous"
|
|
||||||
_for_your_changes_to_take_effect: "pour que les modifications soient prises en compte."
|
|
||||||
sync_my_profile: "Synchroniser mon profil"
|
|
||||||
your_group_has_been_successfully_changed: "Votre groupe a bien été changé."
|
your_group_has_been_successfully_changed: "Votre groupe a bien été changé."
|
||||||
an_unexpected_error_prevented_your_group_from_being_changed: "Une erreur inattendue a empêché votre changement de groupe."
|
an_unexpected_error_prevented_your_group_from_being_changed: "Une erreur inattendue a empêché votre changement de groupe."
|
||||||
do_you_really_want_to_delete_your_account: "Êtes-vous sûr de vouloir supprimer votre compte ?"
|
do_you_really_want_to_delete_your_account: "Êtes-vous sûr de vouloir supprimer votre compte ?"
|
||||||
|
@ -118,8 +118,11 @@ en:
|
|||||||
projects_list:
|
projects_list:
|
||||||
# projects gallery
|
# projects gallery
|
||||||
the_fablab_projects: "The Fab Lab projects"
|
the_fablab_projects: "The Fab Lab projects"
|
||||||
|
search_over_the_whole_network: "Search over the whole Fab Manager network"
|
||||||
|
tooltip_openlab_projects_switch: "The search over the whole network lets you search over the projects of every Fab-manager using this feature !"
|
||||||
|
openlab_search_not_available_at_the_moment: "Search over the whole network is not available at the moment. You still can search over the projects of this platform."
|
||||||
|
project_search_result_is_empty: "Sorry, we found no results matching your search criteria."
|
||||||
add_a_project: "Add a project"
|
add_a_project: "Add a project"
|
||||||
filter_projects: "Filter projects:"
|
|
||||||
reset_all_filters: "Reset all filters"
|
reset_all_filters: "Reset all filters"
|
||||||
search: "Search"
|
search: "Search"
|
||||||
all_projects: "All projects"
|
all_projects: "All projects"
|
||||||
@ -128,7 +131,7 @@ en:
|
|||||||
all_machines: "All machines"
|
all_machines: "All machines"
|
||||||
all_themes: "All themes"
|
all_themes: "All themes"
|
||||||
all_materials: "All materials"
|
all_materials: "All materials"
|
||||||
load_next_projects: "Load next projects..."
|
load_next_projects: "Load next projects"
|
||||||
|
|
||||||
projects_show:
|
projects_show:
|
||||||
# details of a projet
|
# details of a projet
|
||||||
|
@ -118,8 +118,11 @@ fr:
|
|||||||
projects_list:
|
projects_list:
|
||||||
# galerie des projets
|
# galerie des projets
|
||||||
the_fablab_projects: "Les projets du FabLab"
|
the_fablab_projects: "Les projets du FabLab"
|
||||||
|
search_over_the_whole_network: "Chercher sur tout le réseau Fab Manager"
|
||||||
|
tooltip_openlab_projects_switch: "La recherche sur tout le réseau vous permet de rechercher parmis les projets de tous les Fab-managers utilisant cette fonctionnalité !"
|
||||||
|
openlab_search_not_available_at_the_moment: "La recherche sur tout le réseau n'est pas disponible pour le moment. Vous pouvez cependant effectuer une recherche parmis les projets de cette plateforme."
|
||||||
|
project_search_result_is_empty: "Il n'y a pas de projets correspondant à vos critères de recherche."
|
||||||
add_a_project: "Ajouter un projet"
|
add_a_project: "Ajouter un projet"
|
||||||
filter_projects: "Filtrer les projets :"
|
|
||||||
reset_all_filters: "Réinitialiser tous les filtres"
|
reset_all_filters: "Réinitialiser tous les filtres"
|
||||||
search: "Rechercher"
|
search: "Rechercher"
|
||||||
all_projects: "Tous les projets"
|
all_projects: "Tous les projets"
|
||||||
@ -128,7 +131,7 @@ fr:
|
|||||||
all_machines: "Toutes les machines"
|
all_machines: "Toutes les machines"
|
||||||
all_themes: "Toutes les thématiques"
|
all_themes: "Toutes les thématiques"
|
||||||
all_materials: "Tous les matériaux"
|
all_materials: "Tous les matériaux"
|
||||||
load_next_projects: "Charger les projets suivants ..."
|
load_next_projects: "Charger les projets suivants"
|
||||||
|
|
||||||
projects_show:
|
projects_show:
|
||||||
# détails d'un projet
|
# détails d'un projet
|
||||||
|
@ -80,6 +80,12 @@ en:
|
|||||||
to_date: "to" # context: date. eg: "from 01/01 to 01/05"
|
to_date: "to" # context: date. eg: "from 01/01 to 01/05"
|
||||||
to_time: "to" # context: time. eg. "from 18:00 to 21:00"
|
to_time: "to" # context: time. eg. "from 18:00 to 21:00"
|
||||||
or: "or"
|
or: "or"
|
||||||
|
change_my_data: "Change my data"
|
||||||
|
sync_my_profile: "Sync my profile"
|
||||||
|
once_your_data_are_up_to_date_: "Once your data are up to date,"
|
||||||
|
_click_on_the_synchronization_button_opposite_: "click on the synchronization button opposite"
|
||||||
|
_disconnect_then_reconnect_: "disconnect then reconnect"
|
||||||
|
_for_your_changes_to_take_effect: "for your changes to take effect."
|
||||||
|
|
||||||
messages:
|
messages:
|
||||||
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "You will lose any unsaved modification if you quit this page"
|
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "You will lose any unsaved modification if you quit this page"
|
||||||
|
@ -80,6 +80,12 @@ fr:
|
|||||||
to_date: "au" # context: date. eg: "from 01/01 to 01/05"
|
to_date: "au" # context: date. eg: "from 01/01 to 01/05"
|
||||||
to_time: "à" # context: time. eg. "from 18:00 to 21:00"
|
to_time: "à" # context: time. eg. "from 18:00 to 21:00"
|
||||||
or: "ou"
|
or: "ou"
|
||||||
|
change_my_data: "Modifier mes données"
|
||||||
|
sync_my_profile: "Synchroniser mon profil"
|
||||||
|
once_your_data_are_up_to_date_: "Une fois vos données à jour,"
|
||||||
|
_click_on_the_synchronization_button_opposite_: "cliquez sur le bouton de synchronisation ci-contre"
|
||||||
|
_disconnect_then_reconnect_: "déconnectez-vous puis re-connectez vous"
|
||||||
|
_for_your_changes_to_take_effect: "pour que les modifications soient prises en compte."
|
||||||
|
|
||||||
messages:
|
messages:
|
||||||
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "Vous perdrez les modifications non enregistrées si vous quittez cette page"
|
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "Vous perdrez les modifications non enregistrées si vous quittez cette page"
|
||||||
|
@ -27,6 +27,7 @@ Rails.application.routes.draw do
|
|||||||
get :search
|
get :search
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
resources :openlab_projects, only: :index
|
||||||
resources :machines
|
resources :machines
|
||||||
resources :components
|
resources :components
|
||||||
resources :themes
|
resources :themes
|
||||||
|
@ -28,26 +28,31 @@ development:
|
|||||||
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
||||||
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
||||||
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
||||||
|
openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %>
|
||||||
|
openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %>
|
||||||
|
openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %>
|
||||||
|
|
||||||
test:
|
test:
|
||||||
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
secret_key_base: 83daf5e7b80d990f037407bab78dff9904aaf3c195a50f84fa8695a22287e707dfbd9524b403b1dcf116ae1d8c06844c3d7ed942564e5b46be6ae3ead93a9d30
|
||||||
stripe_api_key: <%= ENV["STRIPE_API_KEY"] %>
|
stripe_api_key: <%= ENV["STRIPE_API_KEY"] %>
|
||||||
stripe_publishable_key: <%= ENV["STRIPE_PUBLISHABLE_KEY"] %>
|
stripe_publishable_key: <%= ENV["STRIPE_PUBLISHABLE_KEY"] %>
|
||||||
stripe_currency: <%= ENV["STRIPE_CURRENCY"] %>
|
stripe_currency: usd
|
||||||
disqus_shortname: <%= ENV["DISQUS_SHORTNAME"] %>
|
disqus_shortname: fablab-sleede
|
||||||
fablab_without_plans: <%= ENV["FABLAB_WITHOUT_PLANS"] %>
|
fablab_without_plans: false
|
||||||
time_zone: <%= ENV["TIME_ZONE"] %>
|
time_zone: Paris
|
||||||
week_starting_day: <%= ENV["WEEK_STARTING_DAY"] %>
|
week_starting_day: monday
|
||||||
d3_date_format: <%= ENV["D3_DATE_FORMAT"].dump %>
|
d3_date_format: '%d/%m/%y'
|
||||||
uib_date_format: <%= ENV["UIB_DATE_FORMAT"] %>
|
uib_date_format: dd/MM/yyyy
|
||||||
rails_locale: <%= ENV["RAILS_LOCALE"] %>
|
rails_locale: en
|
||||||
moment_locale: <%= ENV["MOMENT_LOCALE"] %>
|
moment_locale: en
|
||||||
summernote_locale: <%= ENV["SUMMERNOTE_LOCALE"] %>
|
summernote_locale: en-US
|
||||||
angular_locale: <%= ENV["ANGULAR_LOCALE"] %>
|
angular_locale: en-us
|
||||||
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
messageformat_locale: en
|
||||||
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
fullcalendar_locale: en
|
||||||
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
elasticsearch_language_analyzer: french
|
||||||
|
openlab_app_secret:
|
||||||
|
openlab_app_id:
|
||||||
|
openlab_base_uri:
|
||||||
|
|
||||||
staging:
|
staging:
|
||||||
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
||||||
@ -74,6 +79,9 @@ staging:
|
|||||||
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
||||||
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
||||||
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
||||||
|
openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %>
|
||||||
|
openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %>
|
||||||
|
openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %>
|
||||||
|
|
||||||
# Do not keep production secrets in the repository,
|
# Do not keep production secrets in the repository,
|
||||||
# instead read values from the environment.
|
# instead read values from the environment.
|
||||||
@ -102,3 +110,6 @@ production:
|
|||||||
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
messageformat_locale: <%= ENV["MESSAGEFORMAT_LOCALE"] %>
|
||||||
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
fullcalendar_locale: <%= ENV["FULLCALENDAR_LOCALE"] %>
|
||||||
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
elasticsearch_language_analyzer: <%= ENV["ELASTICSEARCH_LANGUAGE_ANALYZER"] %>
|
||||||
|
openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %>
|
||||||
|
openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %>
|
||||||
|
openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
class CreateMachinesPricings < ActiveRecord::Migration
|
|
||||||
def change
|
|
||||||
create_table :machines_pricings do |t|
|
|
||||||
t.belongs_to :machine, index: true
|
|
||||||
t.belongs_to :group, index: true
|
|
||||||
t.integer :not_subscribe_amount
|
|
||||||
t.integer :month_amount
|
|
||||||
t.integer :year_amount
|
|
||||||
|
|
||||||
t.timestamps
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,35 +0,0 @@
|
|||||||
class MigrateDataFromMachinesPricingsToPrices < ActiveRecord::Migration
|
|
||||||
def up
|
|
||||||
insert <<-SQL
|
|
||||||
DELETE FROM machines_pricings WHERE machine_id NOT IN ( select distinct machines.id from machines )
|
|
||||||
SQL
|
|
||||||
|
|
||||||
machines_pricings = select_all("SELECT * FROM machines_pricings")
|
|
||||||
machines_pricings.each do |m_pricing|
|
|
||||||
time = "'#{Time.now.to_s(:db)}'"
|
|
||||||
|
|
||||||
plan_year = select_one("SELECT id FROM plans where plans.group_id = #{m_pricing['group_id']} and plans.interval = 'year'")
|
|
||||||
insert <<-SQL
|
|
||||||
INSERT INTO prices (group_id, plan_id, priceable_id, priceable_type, amount, created_at, updated_at)
|
|
||||||
VALUES (#{m_pricing['group_id']}, #{plan_year['id']}, #{m_pricing['machine_id']}, 'Machine', #{m_pricing['year_amount']}, #{time}, #{time})
|
|
||||||
SQL
|
|
||||||
|
|
||||||
plan_month = select_one("SELECT id FROM plans where plans.group_id = #{m_pricing['group_id']} and plans.interval = 'month'")
|
|
||||||
insert <<-SQL
|
|
||||||
INSERT INTO prices (group_id, plan_id, priceable_id, priceable_type, amount, created_at, updated_at)
|
|
||||||
VALUES (#{m_pricing['group_id']}, #{plan_month['id']}, #{m_pricing['machine_id']}, 'Machine', #{m_pricing['month_amount']}, #{time}, #{time})
|
|
||||||
SQL
|
|
||||||
|
|
||||||
insert <<-SQL
|
|
||||||
INSERT INTO prices (group_id, plan_id, priceable_id, priceable_type, amount, created_at, updated_at)
|
|
||||||
VALUES (#{m_pricing['group_id']}, NULL, #{m_pricing['machine_id']}, 'Machine', #{m_pricing['not_subscribe_amount']}, #{time}, #{time})
|
|
||||||
SQL
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def down
|
|
||||||
insert <<-SQL
|
|
||||||
DELETE FROM prices
|
|
||||||
SQL
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
class DropMachinesPricings < ActiveRecord::Migration
|
|
||||||
def up
|
|
||||||
drop_table :machines_pricings
|
|
||||||
end
|
|
||||||
end
|
|
144
db/schema.rb
144
db/schema.rb
@ -30,23 +30,23 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "abuses", ["signaled_type", "signaled_id"], name: "index_abuses_on_signaled_type_and_signaled_id", using: :btree
|
add_index "abuses", ["signaled_type", "signaled_id"], name: "index_abuses_on_signaled_type_and_signaled_id", using: :btree
|
||||||
|
|
||||||
create_table "addresses", force: :cascade do |t|
|
create_table "addresses", force: :cascade do |t|
|
||||||
t.string "address", limit: 255
|
t.string "address"
|
||||||
t.string "street_number", limit: 255
|
t.string "street_number"
|
||||||
t.string "route", limit: 255
|
t.string "route"
|
||||||
t.string "locality", limit: 255
|
t.string "locality"
|
||||||
t.string "country", limit: 255
|
t.string "country"
|
||||||
t.string "postal_code", limit: 255
|
t.string "postal_code"
|
||||||
t.integer "placeable_id"
|
t.integer "placeable_id"
|
||||||
t.string "placeable_type", limit: 255
|
t.string "placeable_type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "assets", force: :cascade do |t|
|
create_table "assets", force: :cascade do |t|
|
||||||
t.integer "viewable_id"
|
t.integer "viewable_id"
|
||||||
t.string "viewable_type", limit: 255
|
t.string "viewable_type"
|
||||||
t.string "attachment", limit: 255
|
t.string "attachment"
|
||||||
t.string "type", limit: 255
|
t.string "type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
@ -63,7 +63,7 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
create_table "availabilities", force: :cascade do |t|
|
create_table "availabilities", force: :cascade do |t|
|
||||||
t.datetime "start_at"
|
t.datetime "start_at"
|
||||||
t.datetime "end_at"
|
t.datetime "end_at"
|
||||||
t.string "available_type", limit: 255
|
t.string "available_type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "nb_total_places"
|
t.integer "nb_total_places"
|
||||||
@ -81,18 +81,18 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "availability_tags", ["tag_id"], name: "index_availability_tags_on_tag_id", using: :btree
|
add_index "availability_tags", ["tag_id"], name: "index_availability_tags_on_tag_id", using: :btree
|
||||||
|
|
||||||
create_table "categories", force: :cascade do |t|
|
create_table "categories", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "components", force: :cascade do |t|
|
create_table "components", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, null: false
|
t.string "name", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "credits", force: :cascade do |t|
|
create_table "credits", force: :cascade do |t|
|
||||||
t.integer "creditable_id"
|
t.integer "creditable_id"
|
||||||
t.string "creditable_type", limit: 255
|
t.string "creditable_type"
|
||||||
t.integer "plan_id"
|
t.integer "plan_id"
|
||||||
t.integer "hours"
|
t.integer "hours"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
@ -113,7 +113,7 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "events", force: :cascade do |t|
|
create_table "events", force: :cascade do |t|
|
||||||
t.string "title", limit: 255
|
t.string "title"
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
@ -139,10 +139,10 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "events_categories", ["event_id"], name: "index_events_categories_on_event_id", using: :btree
|
add_index "events_categories", ["event_id"], name: "index_events_categories_on_event_id", using: :btree
|
||||||
|
|
||||||
create_table "friendly_id_slugs", force: :cascade do |t|
|
create_table "friendly_id_slugs", force: :cascade do |t|
|
||||||
t.string "slug", limit: 255, null: false
|
t.string "slug", null: false
|
||||||
t.integer "sluggable_id", null: false
|
t.integer "sluggable_id", null: false
|
||||||
t.string "sluggable_type", limit: 50
|
t.string "sluggable_type", limit: 50
|
||||||
t.string "scope", limit: 255
|
t.string "scope"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -152,17 +152,17 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree
|
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree
|
||||||
|
|
||||||
create_table "groups", force: :cascade do |t|
|
create_table "groups", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "slug", limit: 255
|
t.string "slug"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "groups", ["slug"], name: "index_groups_on_slug", unique: true, using: :btree
|
add_index "groups", ["slug"], name: "index_groups_on_slug", unique: true, using: :btree
|
||||||
|
|
||||||
create_table "invoice_items", force: :cascade do |t|
|
create_table "invoice_items", force: :cascade do |t|
|
||||||
t.integer "invoice_id"
|
t.integer "invoice_id"
|
||||||
t.string "stp_invoice_item_id", limit: 255
|
t.string "stp_invoice_item_id"
|
||||||
t.integer "amount"
|
t.integer "amount"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
@ -175,17 +175,17 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
|
|
||||||
create_table "invoices", force: :cascade do |t|
|
create_table "invoices", force: :cascade do |t|
|
||||||
t.integer "invoiced_id"
|
t.integer "invoiced_id"
|
||||||
t.string "invoiced_type", limit: 255
|
t.string "invoiced_type"
|
||||||
t.string "stp_invoice_id", limit: 255
|
t.string "stp_invoice_id"
|
||||||
t.integer "total"
|
t.integer "total"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "reference", limit: 255
|
t.string "reference"
|
||||||
t.string "avoir_mode", limit: 255
|
t.string "avoir_mode"
|
||||||
t.datetime "avoir_date"
|
t.datetime "avoir_date"
|
||||||
t.integer "invoice_id"
|
t.integer "invoice_id"
|
||||||
t.string "type", limit: 255
|
t.string "type"
|
||||||
t.boolean "subscription_to_expire"
|
t.boolean "subscription_to_expire"
|
||||||
t.text "description"
|
t.text "description"
|
||||||
end
|
end
|
||||||
@ -194,17 +194,17 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "invoices", ["user_id"], name: "index_invoices_on_user_id", using: :btree
|
add_index "invoices", ["user_id"], name: "index_invoices_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "licences", force: :cascade do |t|
|
create_table "licences", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, null: false
|
t.string "name", null: false
|
||||||
t.text "description"
|
t.text "description"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "machines", force: :cascade do |t|
|
create_table "machines", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, null: false
|
t.string "name", null: false
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.text "spec"
|
t.text "spec"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "slug", limit: 255
|
t.string "slug"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "machines", ["slug"], name: "index_machines_on_slug", unique: true, using: :btree
|
add_index "machines", ["slug"], name: "index_machines_on_slug", unique: true, using: :btree
|
||||||
@ -220,12 +220,12 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
create_table "notifications", force: :cascade do |t|
|
create_table "notifications", force: :cascade do |t|
|
||||||
t.integer "receiver_id"
|
t.integer "receiver_id"
|
||||||
t.integer "attached_object_id"
|
t.integer "attached_object_id"
|
||||||
t.string "attached_object_type", limit: 255
|
t.string "attached_object_type"
|
||||||
t.integer "notification_type_id"
|
t.integer "notification_type_id"
|
||||||
t.boolean "is_read", default: false
|
t.boolean "is_read", default: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "receiver_type", limit: 255
|
t.string "receiver_type"
|
||||||
t.boolean "is_send", default: false
|
t.boolean "is_send", default: false
|
||||||
t.jsonb "meta_data", default: {}
|
t.jsonb "meta_data", default: {}
|
||||||
end
|
end
|
||||||
@ -268,11 +268,11 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "offer_days", ["subscription_id"], name: "index_offer_days_on_subscription_id", using: :btree
|
add_index "offer_days", ["subscription_id"], name: "index_offer_days_on_subscription_id", using: :btree
|
||||||
|
|
||||||
create_table "plans", force: :cascade do |t|
|
create_table "plans", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.integer "amount"
|
t.integer "amount"
|
||||||
t.string "interval", limit: 255
|
t.string "interval"
|
||||||
t.integer "group_id"
|
t.integer "group_id"
|
||||||
t.string "stp_plan_id", limit: 255
|
t.string "stp_plan_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "training_credit_nb", default: 0
|
t.integer "training_credit_nb", default: 0
|
||||||
@ -302,11 +302,11 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
|
|
||||||
create_table "profiles", force: :cascade do |t|
|
create_table "profiles", force: :cascade do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "first_name", limit: 255
|
t.string "first_name"
|
||||||
t.string "last_name", limit: 255
|
t.string "last_name"
|
||||||
t.boolean "gender"
|
t.boolean "gender"
|
||||||
t.date "birthday"
|
t.date "birthday"
|
||||||
t.string "phone", limit: 255
|
t.string "phone"
|
||||||
t.text "interest"
|
t.text "interest"
|
||||||
t.text "software_mastered"
|
t.text "software_mastered"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
@ -320,7 +320,7 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "title", limit: 255
|
t.string "title"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "project_steps", ["project_id"], name: "index_project_steps_on_project_id", using: :btree
|
add_index "project_steps", ["project_id"], name: "index_project_steps_on_project_id", using: :btree
|
||||||
@ -331,22 +331,22 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.boolean "is_valid", default: false
|
t.boolean "is_valid", default: false
|
||||||
t.string "valid_token", limit: 255
|
t.string "valid_token"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "project_users", ["project_id"], name: "index_project_users_on_project_id", using: :btree
|
add_index "project_users", ["project_id"], name: "index_project_users_on_project_id", using: :btree
|
||||||
add_index "project_users", ["user_id"], name: "index_project_users_on_user_id", using: :btree
|
add_index "project_users", ["user_id"], name: "index_project_users_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "projects", force: :cascade do |t|
|
create_table "projects", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "author_id"
|
t.integer "author_id"
|
||||||
t.text "tags"
|
t.text "tags"
|
||||||
t.integer "licence_id"
|
t.integer "licence_id"
|
||||||
t.string "state", limit: 255
|
t.string "state"
|
||||||
t.string "slug", limit: 255
|
t.string "slug"
|
||||||
t.datetime "published_at"
|
t.datetime "published_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -382,20 +382,20 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "reservable_id"
|
t.integer "reservable_id"
|
||||||
t.string "reservable_type", limit: 255
|
t.string "reservable_type"
|
||||||
t.string "stp_invoice_id", limit: 255
|
t.string "stp_invoice_id"
|
||||||
t.integer "nb_reserve_places"
|
t.integer "nb_reserve_places"
|
||||||
t.integer "nb_reserve_reduced_places"
|
t.integer "nb_reserve_reduced_places"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "reservations", ["reservable_id", "reservable_type"], name: "index_reservations_on_reservable_id_and_reservable_type", using: :btree
|
add_index "reservations", ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id", using: :btree
|
||||||
add_index "reservations", ["stp_invoice_id"], name: "index_reservations_on_stp_invoice_id", using: :btree
|
add_index "reservations", ["stp_invoice_id"], name: "index_reservations_on_stp_invoice_id", using: :btree
|
||||||
add_index "reservations", ["user_id"], name: "index_reservations_on_user_id", using: :btree
|
add_index "reservations", ["user_id"], name: "index_reservations_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "roles", force: :cascade do |t|
|
create_table "roles", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.integer "resource_id"
|
t.integer "resource_id"
|
||||||
t.string "resource_type", limit: 255
|
t.string "resource_type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
@ -420,8 +420,8 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "availability_id"
|
t.integer "availability_id"
|
||||||
t.datetime "ex_start_at"
|
t.datetime "ex_start_at"
|
||||||
t.datetime "canceled_at"
|
|
||||||
t.datetime "ex_end_at"
|
t.datetime "ex_end_at"
|
||||||
|
t.datetime "canceled_at"
|
||||||
t.boolean "offered", default: false
|
t.boolean "offered", default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -430,18 +430,18 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
|
|
||||||
create_table "statistic_fields", force: :cascade do |t|
|
create_table "statistic_fields", force: :cascade do |t|
|
||||||
t.integer "statistic_index_id"
|
t.integer "statistic_index_id"
|
||||||
t.string "key", limit: 255
|
t.string "key"
|
||||||
t.string "label", limit: 255
|
t.string "label"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "data_type", limit: 255
|
t.string "data_type"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "statistic_fields", ["statistic_index_id"], name: "index_statistic_fields_on_statistic_index_id", using: :btree
|
add_index "statistic_fields", ["statistic_index_id"], name: "index_statistic_fields_on_statistic_index_id", using: :btree
|
||||||
|
|
||||||
create_table "statistic_graphs", force: :cascade do |t|
|
create_table "statistic_graphs", force: :cascade do |t|
|
||||||
t.integer "statistic_index_id"
|
t.integer "statistic_index_id"
|
||||||
t.string "chart_type", limit: 255
|
t.string "chart_type"
|
||||||
t.integer "limit"
|
t.integer "limit"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
@ -450,8 +450,8 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "statistic_graphs", ["statistic_index_id"], name: "index_statistic_graphs_on_statistic_index_id", using: :btree
|
add_index "statistic_graphs", ["statistic_index_id"], name: "index_statistic_graphs_on_statistic_index_id", using: :btree
|
||||||
|
|
||||||
create_table "statistic_indices", force: :cascade do |t|
|
create_table "statistic_indices", force: :cascade do |t|
|
||||||
t.string "es_type_key", limit: 255
|
t.string "es_type_key"
|
||||||
t.string "label", limit: 255
|
t.string "label"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.boolean "table", default: true
|
t.boolean "table", default: true
|
||||||
@ -459,8 +459,8 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "statistic_sub_types", force: :cascade do |t|
|
create_table "statistic_sub_types", force: :cascade do |t|
|
||||||
t.string "key", limit: 255
|
t.string "key"
|
||||||
t.string "label", limit: 255
|
t.string "label"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
@ -477,8 +477,8 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
|
|
||||||
create_table "statistic_types", force: :cascade do |t|
|
create_table "statistic_types", force: :cascade do |t|
|
||||||
t.integer "statistic_index_id"
|
t.integer "statistic_index_id"
|
||||||
t.string "key", limit: 255
|
t.string "key"
|
||||||
t.string "label", limit: 255
|
t.string "label"
|
||||||
t.boolean "graph"
|
t.boolean "graph"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
@ -496,7 +496,7 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
create_table "subscriptions", force: :cascade do |t|
|
create_table "subscriptions", force: :cascade do |t|
|
||||||
t.integer "plan_id"
|
t.integer "plan_id"
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "stp_subscription_id", limit: 255
|
t.string "stp_subscription_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.datetime "expired_at"
|
t.datetime "expired_at"
|
||||||
@ -515,15 +515,15 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
|
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
|
||||||
|
|
||||||
create_table "themes", force: :cascade do |t|
|
create_table "themes", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, null: false
|
t.string "name", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "trainings", force: :cascade do |t|
|
create_table "trainings", force: :cascade do |t|
|
||||||
t.string "name", limit: 255
|
t.string "name"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "nb_total_places"
|
t.integer "nb_total_places"
|
||||||
t.string "slug", limit: 255
|
t.string "slug"
|
||||||
t.text "description"
|
t.text "description"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -579,30 +579,30 @@ ActiveRecord::Schema.define(version: 20160119131623) do
|
|||||||
add_index "user_trainings", ["user_id"], name: "index_user_trainings_on_user_id", using: :btree
|
add_index "user_trainings", ["user_id"], name: "index_user_trainings_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "users", force: :cascade do |t|
|
create_table "users", force: :cascade do |t|
|
||||||
t.string "username", limit: 255
|
t.string "email", default: "", null: false
|
||||||
t.string "email", limit: 255, default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
t.string "encrypted_password", limit: 255, default: "", null: false
|
t.string "reset_password_token"
|
||||||
t.string "reset_password_token", limit: 255
|
|
||||||
t.datetime "reset_password_sent_at"
|
t.datetime "reset_password_sent_at"
|
||||||
t.datetime "remember_created_at"
|
t.datetime "remember_created_at"
|
||||||
t.integer "sign_in_count", default: 0, null: false
|
t.integer "sign_in_count", default: 0, null: false
|
||||||
t.datetime "current_sign_in_at"
|
t.datetime "current_sign_in_at"
|
||||||
t.datetime "last_sign_in_at"
|
t.datetime "last_sign_in_at"
|
||||||
t.string "current_sign_in_ip", limit: 255
|
t.string "current_sign_in_ip"
|
||||||
t.string "last_sign_in_ip", limit: 255
|
t.string "last_sign_in_ip"
|
||||||
t.string "confirmation_token", limit: 255
|
t.string "confirmation_token"
|
||||||
t.datetime "confirmed_at"
|
t.datetime "confirmed_at"
|
||||||
t.datetime "confirmation_sent_at"
|
t.datetime "confirmation_sent_at"
|
||||||
t.string "unconfirmed_email", limit: 255
|
t.string "unconfirmed_email"
|
||||||
t.integer "failed_attempts", default: 0, null: false
|
t.integer "failed_attempts", default: 0, null: false
|
||||||
t.string "unlock_token", limit: 255
|
t.string "unlock_token"
|
||||||
t.datetime "locked_at"
|
t.datetime "locked_at"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.boolean "is_allow_contact", default: true
|
t.boolean "is_allow_contact", default: true
|
||||||
t.integer "group_id"
|
t.integer "group_id"
|
||||||
t.string "stp_customer_id", limit: 255
|
t.string "stp_customer_id"
|
||||||
t.string "slug", limit: 255
|
t.string "username"
|
||||||
|
t.string "slug"
|
||||||
t.boolean "is_active", default: true
|
t.boolean "is_active", default: true
|
||||||
t.boolean "invoicing_disabled", default: false
|
t.boolean "invoicing_disabled", default: false
|
||||||
t.string "provider"
|
t.string "provider"
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
Group.create!([
|
|
||||||
{name: "standard, association", slug: "standard"},
|
|
||||||
{name: "étudiant, - de 25 ans, enseignant, demandeur d'emploi", slug: "student"},
|
|
||||||
{name: "artisan, commerçant, chercheur, auto-entrepreneur", slug: "merchant"},
|
|
||||||
{name: "PME, PMI, SARL, SA", slug: "business"}
|
|
||||||
])
|
|
||||||
|
|
||||||
if Category.count == 0
|
|
||||||
Category.create!([
|
|
||||||
{name: "Stage"},
|
|
||||||
{name: "Atelier"}
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
if StatisticIndex.count == 0
|
|
||||||
StatisticIndex.create!([
|
|
||||||
{id:1, es_type_key:'subscription', label:'Abonnements'},
|
|
||||||
{id:2, es_type_key:'machine', label:'Heures machines'},
|
|
||||||
{id:3, es_type_key:'training', label:'Formations'},
|
|
||||||
{id:4, es_type_key:'event', label:'Ateliers/Stages'},
|
|
||||||
{id:5, es_type_key:'account', label:'Inscriptions', ca: false},
|
|
||||||
{id:6, es_type_key:'project', label:'Projets', ca: false},
|
|
||||||
{id:7, es_type_key:'user', label:'Utilisateurs', table: false, ca: false}
|
|
||||||
])
|
|
||||||
connection = ActiveRecord::Base.connection
|
|
||||||
if connection.instance_values["config"][:adapter] == 'postgresql'
|
|
||||||
connection.execute("SELECT setval('statistic_indices_id_seq', 7);")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if StatisticField.count == 0
|
|
||||||
StatisticField.create!([
|
|
||||||
# available data_types : index, number, date, text, list
|
|
||||||
{key:'trainingId', label:'ID Formation', statistic_index_id: 3, data_type: 'index'},
|
|
||||||
{key:'trainingDate', label:'Date Formation', statistic_index_id: 3, data_type: 'date'},
|
|
||||||
{key:'eventId', label:'ID Évènement', statistic_index_id: 4, data_type: 'index'},
|
|
||||||
{key:'eventDate', label:'Date Évènement', statistic_index_id: 4, data_type: 'date'},
|
|
||||||
{key:'themes', label:'Thèmes', statistic_index_id: 6, data_type: 'list'},
|
|
||||||
{key:'components', label:'Composants', statistic_index_id: 6, data_type: 'list'},
|
|
||||||
{key:'machines', label:'Machines', statistic_index_id: 6, data_type: 'list'},
|
|
||||||
{key:'name', label:'Nom Évènement', statistic_index_id: 4, data_type: 'text'},
|
|
||||||
{key:'userId', label:'ID Utilisateur', statistic_index_id: 7, data_type: 'index'}
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
if StatisticType.count == 0
|
|
||||||
StatisticType.create!([
|
|
||||||
{id:1, statistic_index_id: 1, key: 'month', label:'Abonnements mensuels', graph: true, simple: true},
|
|
||||||
{id:2, statistic_index_id: 1, key: 'year', label:'Abonnements annuels', graph: true, simple: true},
|
|
||||||
{id:3, statistic_index_id: 2, key: 'booking', label:'Réservations', graph: true, simple: true},
|
|
||||||
{id:4, statistic_index_id: 2, key: 'hour', label:"Nombre d'heures", graph: true, simple: false},
|
|
||||||
{id:5, statistic_index_id: 3, key: 'booking', label:'Réservations', graph: false, simple: true},
|
|
||||||
{id:6, statistic_index_id: 3, key: 'hour', label:"Nombre d'heures", graph: false, simple: false},
|
|
||||||
{id:7, statistic_index_id: 4, key: 'booking', label:'Nombre de places', graph: false, simple: false},
|
|
||||||
{id:8, statistic_index_id: 4, key: 'hour', label:"Nombre d'heures", graph: false, simple: false},
|
|
||||||
{id:9, statistic_index_id: 5, key: 'member', label:'Utilisateurs', graph: true, simple: true},
|
|
||||||
{id:10, statistic_index_id: 6, key: 'project', label:'Projets', graph: false, simple: true},
|
|
||||||
{id:11, statistic_index_id: 7, key: 'revenue', label:"Chiffre d'affaires", graph: false, simple: false}
|
|
||||||
])
|
|
||||||
connection = ActiveRecord::Base.connection
|
|
||||||
if connection.instance_values["config"][:adapter] == 'postgresql'
|
|
||||||
connection.execute("SELECT setval('statistic_types_id_seq', 11);")
|
|
||||||
end
|
|
||||||
end
|
|
@ -44,3 +44,6 @@ TIME_ZONE=Paris
|
|||||||
WEEK_STARTING_DAY=monday
|
WEEK_STARTING_DAY=monday
|
||||||
D3_DATE_FORMAT=%d/%m/%y
|
D3_DATE_FORMAT=%d/%m/%y
|
||||||
UIB_DATE_FORMAT=dd/MM/yyyy
|
UIB_DATE_FORMAT=dd/MM/yyyy
|
||||||
|
|
||||||
|
OPENLAB_APP_SECRET: 'fSF9jZEWxjHyqjAzzg34jd92'
|
||||||
|
OPENLAB_APP_ID: 'xLn9CmryyURNNHZiDRYVRXbv'
|
||||||
|
@ -31,7 +31,7 @@ namespace :fablab do
|
|||||||
puts "-> Done"
|
puts "-> Done"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Cancel strip subscriptions"
|
desc "Cancel stripe subscriptions"
|
||||||
task cancel_subscriptions: :environment do
|
task cancel_subscriptions: :environment do
|
||||||
Subscription.where("expired_at >= ?", Time.now.at_beginning_of_day).each do |s|
|
Subscription.where("expired_at >= ?", Time.now.at_beginning_of_day).each do |s|
|
||||||
puts "-> Start cancel subscription of #{s.user.email}"
|
puts "-> Start cancel subscription of #{s.user.email}"
|
||||||
@ -76,9 +76,10 @@ namespace :fablab do
|
|||||||
|
|
||||||
desc "sync all/one project in elastic search index"
|
desc "sync all/one project in elastic search index"
|
||||||
task :es_build_projects_index, [:id] => :environment do |task, args|
|
task :es_build_projects_index, [:id] => :environment do |task, args|
|
||||||
unless Project.__elasticsearch__.client.indices.exists? index: 'fablab'
|
if Project.__elasticsearch__.client.indices.exists? index: 'fablab'
|
||||||
Project.__elasticsearch__.client.indices.create index: Project.index_name, body: { settings: Project.settings.to_hash, mappings: Project.mappings.to_hash }
|
Project.__elasticsearch__.client.indices.delete index: 'fablab'
|
||||||
end
|
end
|
||||||
|
Project.__elasticsearch__.client.indices.create index: Project.index_name, body: { settings: Project.settings.to_hash, mappings: Project.mappings.to_hash }
|
||||||
if args.id
|
if args.id
|
||||||
IndexerWorker.perform_async(:index, id)
|
IndexerWorker.perform_async(:index, id)
|
||||||
else
|
else
|
||||||
@ -170,4 +171,24 @@ namespace :fablab do
|
|||||||
|
|
||||||
puts "\nUsers successfully notified\n\n"
|
puts "\nUsers successfully notified\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "generate fixtures from db"
|
||||||
|
task generate_fixtures: :environment do
|
||||||
|
Rails.application.eager_load!
|
||||||
|
ActiveRecord::Base.descendants.reject { |c| c == ActiveRecord::SchemaMigration or c == PartnerPlan }.each do |ar_base|
|
||||||
|
p "========== #{ar_base} =============="
|
||||||
|
ar_base.dump_fixtures
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'clean stripe secrets from VCR cassettes'
|
||||||
|
task clean_cassettes_secrets: :environment do
|
||||||
|
Dir['test/vcr_cassettes/*.yml'].each do |cassette_file|
|
||||||
|
cassette = File.read(cassette_file)
|
||||||
|
cassette.gsub!(Rails.application.secrets.stripe_api_key, 'sk_test_testfaketestfaketestfake')
|
||||||
|
cassette.gsub!(Rails.application.secrets.stripe_publishable_key, 'pk_test_faketestfaketestfaketest')
|
||||||
|
puts cassette
|
||||||
|
File.write(cassette_file, cassette)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
13
lib/tasks/fablab/openlab.rake
Normal file
13
lib/tasks/fablab/openlab.rake
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace :fablab do
|
||||||
|
namespace :openlab do
|
||||||
|
task bulk_export: :environment do
|
||||||
|
if Rails.application.secrets.openlab_app_secret.present?
|
||||||
|
Project.find_each do |project|
|
||||||
|
project.openlab_create
|
||||||
|
end
|
||||||
|
else
|
||||||
|
warn "Rails.application.secrets.openlab_app_secret not present. Export can't be done."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,10 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :abus, :class => 'Abuse' do
|
|
||||||
signaled nil
|
|
||||||
first_name "MyString"
|
|
||||||
last_name "MyString"
|
|
||||||
email ""
|
|
||||||
message "MyText"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :auth_provider do
|
|
||||||
name "MyString"
|
|
||||||
type ""
|
|
||||||
status "MyString"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :availability do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,7 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :availability_tag do
|
|
||||||
availability nil
|
|
||||||
tag nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :avoir do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :credit do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,6 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :custom_asset do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :database_provider do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :invoice do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :machine do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,10 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :o_auth2_mapping do
|
|
||||||
o_auth2_provider nil
|
|
||||||
resource_url "MyString"
|
|
||||||
local_field "MyString"
|
|
||||||
api_field "MyString"
|
|
||||||
data_type "MyString"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,11 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :o_auth2_provider do
|
|
||||||
base_url "MyString"
|
|
||||||
token_endpoint "MyString"
|
|
||||||
authorization_endpoint "MyString"
|
|
||||||
client_id "MyString"
|
|
||||||
client_secret "MyString"
|
|
||||||
auth_provider nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :plan do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,9 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :price do
|
|
||||||
group nil
|
|
||||||
plan nil
|
|
||||||
priceable nil
|
|
||||||
amount 1
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,11 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
sequence(:last_name) { |n| n }
|
|
||||||
|
|
||||||
factory :profile do
|
|
||||||
first_name 'member'
|
|
||||||
last_name { FactoryGirl.generate :last_name }
|
|
||||||
gender true
|
|
||||||
birthday { 30.years.ago }
|
|
||||||
phone '0606060606'
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
sequence(:name) { |n| "project#{n}" }
|
|
||||||
|
|
||||||
factory :project do
|
|
||||||
name { FactoryGirl.generate :name }
|
|
||||||
description { Faker::Lorem.paragraph }
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :reservation do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,6 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :stylesheet do
|
|
||||||
contents "MyString"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :subscription do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,6 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :tag do
|
|
||||||
name "MyString"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
#FactoryGirl.define do
|
|
||||||
#factory :training do
|
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#end
|
|
@ -1,7 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
factory :user_tag do
|
|
||||||
user nil
|
|
||||||
tag nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,14 +0,0 @@
|
|||||||
FactoryGirl.define do
|
|
||||||
sequence(:email) { |n| "member#{n}@sleede.com" }
|
|
||||||
sequence(:username) { |n| "member#{n}" }
|
|
||||||
|
|
||||||
factory :user do
|
|
||||||
email { FactoryGirl.generate :email }
|
|
||||||
username { FactoryGirl.generate :username }
|
|
||||||
password 'sleede22'
|
|
||||||
password_confirmation 'sleede22'
|
|
||||||
association :profile, strategy: :build
|
|
||||||
group { Group.first }
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,9 +0,0 @@
|
|||||||
class DeviseMailerPreview < ActionMailer::Preview
|
|
||||||
def confirmation_instructions
|
|
||||||
Devise::Mailer.confirmation_instructions(User.last, SecureRandom.hex)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reset_password_instructions
|
|
||||||
Devise::Mailer.reset_password_instructions(User.last, SecureRandom.hex)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,7 +0,0 @@
|
|||||||
class NotificationsMailerPreview < ActionMailer::Preview
|
|
||||||
NotificationType::NAMES.each do |name|
|
|
||||||
define_method name do
|
|
||||||
NotificationsMailer.send_mail_by(Notification.where(notification_type_id: NotificationType.find_by_name(name)).last)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
class UsersMailerPreview < ActionMailer::Preview
|
|
||||||
def notify_user_account_created
|
|
||||||
UsersMailer.notify_user_account_created(User.first, 'wfwwefwefsdfsdf')
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Abuse, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe AuthProvider, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,24 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Availability, type: :model do
|
|
||||||
|
|
||||||
describe 'create' do
|
|
||||||
it 'is success with start at, end at and type'
|
|
||||||
it 'is invalid without start at'
|
|
||||||
it 'is invalid without end at'
|
|
||||||
it 'is invalid without type'
|
|
||||||
it 'is invalid type isnt in [training, machines, event]'
|
|
||||||
it 'is invalid without training_ids when type training'
|
|
||||||
it 'is invalid without machine_ids when type machines'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should can associate one or many reservations'
|
|
||||||
|
|
||||||
it 'should can destroy if dont any reservations'
|
|
||||||
|
|
||||||
it 'should get a title'
|
|
||||||
|
|
||||||
it 'should set a number of places'
|
|
||||||
|
|
||||||
it 'should be completed when number of reservations equal number of places'
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe AvailabilityTag, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,9 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Avoir, type: :model do
|
|
||||||
it 'should generate a reference after create'
|
|
||||||
|
|
||||||
it 'is invoiced if avoir mode isnt in [stripe, cheque, transfer, none, cash]'
|
|
||||||
|
|
||||||
it 'should can expire user subscription if avoir indicate subscription to expire'
|
|
||||||
end
|
|
@ -1,11 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Credit, type: :model do
|
|
||||||
context 'training' do
|
|
||||||
it 'should is saved with plan'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'machine' do
|
|
||||||
it 'should is saved with plan and hours'
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe CustomAsset, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe DatabaseProvider, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,9 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Invoice, type: :model do
|
|
||||||
it 'should generate a reference after create'
|
|
||||||
|
|
||||||
it 'should generate a invoice pdf'
|
|
||||||
|
|
||||||
it 'should can build a avoir'
|
|
||||||
end
|
|
@ -1,21 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Machine, type: :model do
|
|
||||||
|
|
||||||
describe 'create' do
|
|
||||||
it 'is success with name, image, description and spec'
|
|
||||||
it 'is invalid without name'
|
|
||||||
it 'is invalid without image'
|
|
||||||
it 'is invalid without description'
|
|
||||||
it 'is invalid without spec'
|
|
||||||
it 'should auto generate slug by name'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can have many machine files'
|
|
||||||
|
|
||||||
it 'can have many projects'
|
|
||||||
|
|
||||||
it 'can have many trainings'
|
|
||||||
|
|
||||||
it 'should return an amount by user group'
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe OAuth2Mapping, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe OAuth2Provider, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,60 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Plan, type: :model do
|
|
||||||
let(:group){ Group.new(name: 'groupe test', slug: SecureRandom.hex) }
|
|
||||||
|
|
||||||
describe 'validations' do
|
|
||||||
it 'is success with amount and group' do
|
|
||||||
plan = Plan.new(amount: 500, group: group)
|
|
||||||
expect(plan).to be_valid
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'is invalid without amount' do
|
|
||||||
plan = Plan.new(group: group)
|
|
||||||
expect(plan).to be_invalid
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'is invalid without group' do
|
|
||||||
plan = Plan.new(amount: 500)
|
|
||||||
expect(plan).to be_invalid
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "on creation" do
|
|
||||||
before :each do
|
|
||||||
@plan_id = SecureRandom.hex
|
|
||||||
@plan_name = SecureRandom.hex
|
|
||||||
allow(Stripe::Plan).to receive(:create).and_return(double(id: @plan_id, name: @plan_name))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'calls Stripe::Plan create method' do
|
|
||||||
plan = Plan.create(amount: 500, interval: 'month', group: group)
|
|
||||||
expect(Stripe::Plan).to have_received :create
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'saves stripe_plan.id' do
|
|
||||||
plan = Plan.create(amount: 500, interval: 'month', group: group)
|
|
||||||
expect(plan.stp_plan_id).to eq(@plan_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'saves stripe_plan.name' do
|
|
||||||
plan = Plan.create(amount: 500, interval: 'month', group: group)
|
|
||||||
expect(plan.name).to eq(@plan_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "on update" do
|
|
||||||
before :each do
|
|
||||||
allow(Stripe::Plan).to receive(:create).and_return(double(id: SecureRandom.hex, name: SecureRandom.hex))
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:plan){ Plan.create(amount: 500, interval: 'month', group: group) }
|
|
||||||
|
|
||||||
describe "update_stripe_plan" do
|
|
||||||
it "should return false if plan already has subscriptions" do
|
|
||||||
allow(plan).to receive(:subscriptions).and_return([1,2])
|
|
||||||
expect(plan.send(:update_stripe_plan)).to eq(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Price, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
@ -1,30 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Project, type: :model do
|
|
||||||
|
|
||||||
describe 'create' do
|
|
||||||
it 'is success with author, name and description'
|
|
||||||
it 'is invalid without author'
|
|
||||||
it 'is invalid without name'
|
|
||||||
it 'is invalid without description'
|
|
||||||
it 'should auto generate slug by name'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'save as draft by default'
|
|
||||||
|
|
||||||
it 'should can published'
|
|
||||||
|
|
||||||
it 'should have a published time after published'
|
|
||||||
|
|
||||||
it 'can only add one project main image'
|
|
||||||
|
|
||||||
it 'can have many project caos'
|
|
||||||
|
|
||||||
it 'can have many machines'
|
|
||||||
|
|
||||||
it 'can have many components'
|
|
||||||
|
|
||||||
it 'can have many project steps'
|
|
||||||
|
|
||||||
it 'can add a licence'
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Reservation, type: :model do
|
|
||||||
describe 'create' do
|
|
||||||
it 'is success with user, slots and reservable'
|
|
||||||
it 'is invalid if reservable isnt in [Training, Machine, Event]'
|
|
||||||
|
|
||||||
it 'is success with a subscription'
|
|
||||||
|
|
||||||
context 'stripe' do
|
|
||||||
it 'should payment success'
|
|
||||||
it 'is invalid if payment info invalid'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'satori' do
|
|
||||||
it 'should success'
|
|
||||||
it 'is invalid if payment info invalid'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should update user credit after creation'
|
|
||||||
|
|
||||||
it 'should create a invoice'
|
|
||||||
|
|
||||||
it 'should can set a nb reserve places'
|
|
||||||
|
|
||||||
it 'should can set a nb reserve reduced places'
|
|
||||||
end
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user