mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-13 18:46:49 +00:00
Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9f619c036 | ||
|
|
31ffeab4cd | ||
|
|
1673a18eb6 | ||
|
|
29cdadd563 | ||
|
|
8dffb38bf5 | ||
|
|
ecd5033efa | ||
|
|
a13227defa | ||
|
|
465a295dfa | ||
|
|
9c84a6936a | ||
|
|
809a57dbdb | ||
|
|
6b30a654a6 | ||
|
|
9b749ee2e0 | ||
|
|
c27974c5a3 | ||
|
|
6f41a4168d | ||
|
|
db7ac904e9 | ||
|
|
96eaec908d | ||
|
|
12de3ae584 | ||
|
|
75c3bf8b56 | ||
|
|
52c12ea896 | ||
|
|
2e4bbb7740 | ||
|
|
1b0880e7df | ||
|
|
84f319894a | ||
|
|
c4e58eab3c | ||
|
|
920e9e65bd | ||
|
|
78b978efba | ||
|
|
31db35675b | ||
|
|
6b25c36476 | ||
|
|
73fa6295ec | ||
|
|
b5f43b3c1c | ||
|
|
fcc68a0a19 | ||
|
|
4c2d7c0d1c | ||
|
|
eee5e8eee7 | ||
|
|
e227cb912b | ||
|
|
6f3a0e8812 | ||
|
|
d7e4962fba | ||
|
|
7ad5181026 | ||
|
|
c2133c8fa5 | ||
|
|
de5264138d | ||
|
|
bfbcdba98c | ||
|
|
0632553a4a | ||
|
|
a97d416bfd | ||
|
|
b306539163 | ||
|
|
caea53ab91 | ||
|
|
d7ee0ca434 | ||
|
|
7fa4aa62f9 | ||
|
|
6295ecc7cf | ||
|
|
b207d3e7fd | ||
|
|
b4f3655fae | ||
|
|
918e220557 | ||
|
|
a397ba1ca9 | ||
|
|
7cd235c84d | ||
|
|
e97cd0a1fd | ||
|
|
a56f061663 | ||
|
|
512cb44df5 | ||
|
|
0ab8e62749 | ||
|
|
008b12a75d | ||
|
|
2e51d9be4a | ||
|
|
bd20e299f3 | ||
|
|
90a92421f4 | ||
|
|
84daa046ca | ||
|
|
61259bcfc2 | ||
|
|
4f44d50943 | ||
|
|
2e426c701e | ||
|
|
99b3701e17 | ||
|
|
8bef0f9a43 | ||
|
|
d69aa04435 | ||
|
|
0b4a243d8e | ||
|
|
ced9784fb8 | ||
|
|
788457618f | ||
|
|
ef607da2c1 | ||
|
|
9ea6fa5c44 | ||
|
|
a945f8f72f | ||
|
|
0c3ee2e8fc | ||
|
|
f6458e91c2 | ||
|
|
4757b82a80 | ||
|
|
ad9e82e973 | ||
|
|
e8430eae6d | ||
|
|
584b013a49 | ||
|
|
f15cb01f2a | ||
|
|
f1a5c46685 | ||
|
|
00f2f92ce0 | ||
|
|
fdd71cff51 | ||
|
|
c9ca020f05 | ||
|
|
d59074dabd | ||
|
|
6a874b9527 | ||
|
|
1f08da207f | ||
|
|
9c3bdc6b9b | ||
|
|
2105c0d0ea | ||
|
|
6e514acc77 | ||
|
|
b8338fb9af | ||
|
|
687d4593fb | ||
|
|
b62f88512f | ||
|
|
8f67e9839d | ||
|
|
fdeeab5750 | ||
|
|
9e7e176cf9 | ||
|
|
40a0d8e08c | ||
|
|
5cbec5521c | ||
|
|
8f6fdf3e31 | ||
|
|
9d5ca2255b | ||
|
|
00a823b2fc | ||
|
|
3d81a6e280 | ||
|
|
739df4be01 | ||
|
|
b91cabc510 | ||
|
|
ba152f12ee | ||
|
|
778baba118 | ||
|
|
85c98c6d3e | ||
|
|
de9546587a | ||
|
|
9741935955 | ||
|
|
9d19f37fec | ||
|
|
718ac0ab80 | ||
|
|
d95c5ff8a8 | ||
|
|
797b98d686 | ||
|
|
3dc3ebe584 | ||
|
|
6d05d57846 | ||
|
|
ac739108a2 | ||
|
|
3f3ae97a40 | ||
|
|
ae166ae220 | ||
|
|
533f7048bf | ||
|
|
e399e27a7d | ||
|
|
63ba3defec | ||
|
|
fb01a6e244 | ||
|
|
50bc48c96f | ||
|
|
8ad532c41a | ||
|
|
ebd5d706c2 | ||
|
|
8658b1a743 | ||
|
|
5208094f05 | ||
|
|
8926a3f08d | ||
|
|
58ea3a10e8 | ||
|
|
5ca5c32da4 | ||
|
|
7a1a1286d2 | ||
|
|
5e61aa8a77 | ||
|
|
af92b9f464 | ||
|
|
ffaf4a837f | ||
|
|
d377300f32 | ||
|
|
b7a3f7263b | ||
|
|
f645f8fc79 | ||
|
|
3d06b75892 | ||
|
|
af23375b04 | ||
|
|
f15a4cc943 | ||
|
|
e95d08cae8 | ||
|
|
3989843a21 | ||
|
|
22b1dd3232 | ||
|
|
96c0057b03 | ||
|
|
1fb4e27064 | ||
|
|
9f2a17c3e9 | ||
|
|
ac639fa283 |
@@ -1 +1 @@
|
||||
14.21.2
|
||||
17.9.1
|
||||
|
||||
@@ -119,7 +119,6 @@ Layout/LineLength:
|
||||
- 'spec/controllers/admin/bulk_line_items_controller_spec.rb'
|
||||
- 'spec/controllers/admin/column_preferences_controller_spec.rb'
|
||||
- 'spec/controllers/admin/enterprises_controller_spec.rb'
|
||||
- 'spec/controllers/admin/manager_invitations_controller_spec.rb'
|
||||
- 'spec/controllers/admin/order_cycles_controller_spec.rb'
|
||||
- 'spec/controllers/admin/schedules_controller_spec.rb'
|
||||
- 'spec/controllers/admin/stripe_accounts_controller_spec.rb'
|
||||
|
||||
7
Gemfile
7
Gemfile
@@ -6,7 +6,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
|
||||
|
||||
gem 'rails', '>= 6.1.4'
|
||||
gem 'rails'
|
||||
|
||||
# Active Storage
|
||||
gem "active_storage_validations"
|
||||
@@ -27,7 +27,7 @@ gem 'rails-i18n'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
|
||||
gem "activerecord-import"
|
||||
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
|
||||
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-7"
|
||||
gem "fog-aws", "~> 2.0" # db2fog does not support v3
|
||||
gem "mime-types" # required by fog
|
||||
|
||||
@@ -136,6 +136,9 @@ gem 'view_component_reflex', '3.1.14.pre9'
|
||||
|
||||
gem 'mini_portile2', '~> 2.8'
|
||||
|
||||
gem "faraday"
|
||||
gem "private_address_check"
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'rack-timeout'
|
||||
|
||||
168
Gemfile.lock
168
Gemfile.lock
@@ -1,12 +1,12 @@
|
||||
GIT
|
||||
remote: https://github.com/openfoodfoundation/db2fog.git
|
||||
revision: 5b63343847452f52aa42f7fc169d6ab3af57cda3
|
||||
branch: rails-6
|
||||
revision: 6e88c0ab9eeb23d7ad9964b27becb1c04a83141f
|
||||
branch: rails-7
|
||||
specs:
|
||||
db2fog (0.9.2)
|
||||
activerecord (>= 3.2.0, < 7.0)
|
||||
activerecord (>= 3.2.0)
|
||||
fog-core (~> 1.0)
|
||||
rails (>= 3.2.0, < 7.0)
|
||||
rails (>= 3.2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/openfoodfoundation/select2-rails.git
|
||||
@@ -44,42 +44,49 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.1.0)
|
||||
actioncable (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
actioncable (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
activejob (= 6.1.7.2)
|
||||
activerecord (= 6.1.7.2)
|
||||
activestorage (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
actionmailbox (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
actionview (= 6.1.7.2)
|
||||
activejob (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.7.2)
|
||||
actionview (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
actionpack (7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
rack (~> 2.0, >= 2.2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actionpack-action_caching (1.2.2)
|
||||
actionpack (>= 4.0.0)
|
||||
actiontext (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
activerecord (= 6.1.7.2)
|
||||
activestorage (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
actiontext (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
actionview (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
@@ -91,19 +98,19 @@ GEM
|
||||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
activejob (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemerchant (1.123.0)
|
||||
activesupport (>= 4.2)
|
||||
builder (>= 2.1.2, < 4.0.0)
|
||||
i18n (>= 0.6.9)
|
||||
nokogiri (~> 1.4)
|
||||
activemodel (6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
activerecord (6.1.7.2)
|
||||
activemodel (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
activemodel (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
activerecord (7.0.4.3)
|
||||
activemodel (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
activerecord-import (1.4.1)
|
||||
activerecord (>= 4.2)
|
||||
activerecord-postgresql-adapter (0.0.1)
|
||||
@@ -114,19 +121,18 @@ GEM
|
||||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 2.0.8, < 3)
|
||||
railties (>= 5.2.4.1)
|
||||
activestorage (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
activejob (= 6.1.7.2)
|
||||
activerecord (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
activestorage (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.7.2)
|
||||
activesupport (7.0.4.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
acts-as-taggable-on (9.0.1)
|
||||
activerecord (>= 6.0, < 7.1)
|
||||
acts_as_list (1.0.4)
|
||||
@@ -224,9 +230,9 @@ GEM
|
||||
cuprite (0.14.3)
|
||||
capybara (~> 3.0)
|
||||
ferrum (~> 0.13.0)
|
||||
database_cleaner (2.0.1)
|
||||
database_cleaner-active_record (~> 2.0.0)
|
||||
database_cleaner-active_record (2.0.0)
|
||||
database_cleaner (2.0.2)
|
||||
database_cleaner-active_record (>= 2, < 3)
|
||||
database_cleaner-active_record (2.1.0)
|
||||
activerecord (>= 5.a)
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
@@ -241,7 +247,7 @@ GEM
|
||||
irb (>= 1.5.0)
|
||||
reline (>= 0.3.1)
|
||||
debugger-linecache (1.2.0)
|
||||
devise (4.8.1)
|
||||
devise (4.9.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
@@ -283,15 +289,17 @@ GEM
|
||||
websocket-driver (>= 0.6, < 0.8)
|
||||
ffaker (2.21.0)
|
||||
ffi (1.15.5)
|
||||
flipper (0.20.4)
|
||||
flipper-active_record (0.20.4)
|
||||
activerecord (>= 5.0, < 7)
|
||||
flipper (~> 0.20.4)
|
||||
flipper-ui (0.20.4)
|
||||
flipper (0.26.2)
|
||||
concurrent-ruby (< 2)
|
||||
flipper-active_record (0.26.2)
|
||||
activerecord (>= 4.2, < 8)
|
||||
flipper (~> 0.26.2)
|
||||
flipper-ui (0.26.2)
|
||||
erubi (>= 1.0.0, < 2.0.0)
|
||||
flipper (~> 0.20.4)
|
||||
flipper (~> 0.26.2)
|
||||
rack (>= 1.4, < 3)
|
||||
rack-protection (>= 1.5.3, < 2.2.0)
|
||||
rack-protection (>= 1.5.3, <= 4.0.0)
|
||||
sanitize (< 7)
|
||||
fog-aws (2.0.1)
|
||||
fog-core (~> 1.38)
|
||||
fog-json (~> 1.0)
|
||||
@@ -402,7 +410,7 @@ GEM
|
||||
mini_portile2 (2.8.1)
|
||||
mini_racer (0.6.3)
|
||||
libv8-node (~> 16.10.0.0)
|
||||
minitest (5.17.0)
|
||||
minitest (5.18.0)
|
||||
monetize (1.12.0)
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
@@ -474,6 +482,7 @@ GEM
|
||||
ttfunk
|
||||
pg (1.2.3)
|
||||
power_assert (2.0.2)
|
||||
private_address_check (0.5.0)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
@@ -482,7 +491,7 @@ GEM
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.6.2)
|
||||
rack (2.2.6.3)
|
||||
rack (2.2.6.4)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
rack-oauth2 (1.21.3)
|
||||
@@ -491,7 +500,7 @@ GEM
|
||||
httpclient
|
||||
json-jwt (>= 1.11.0)
|
||||
rack (>= 2.1.0)
|
||||
rack-protection (2.1.0)
|
||||
rack-protection (3.0.5)
|
||||
rack
|
||||
rack-proxy (0.7.6)
|
||||
rack
|
||||
@@ -499,21 +508,20 @@ GEM
|
||||
rack-test (2.0.2)
|
||||
rack (>= 1.3)
|
||||
rack-timeout (0.6.3)
|
||||
rails (6.1.7.2)
|
||||
actioncable (= 6.1.7.2)
|
||||
actionmailbox (= 6.1.7.2)
|
||||
actionmailer (= 6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
actiontext (= 6.1.7.2)
|
||||
actionview (= 6.1.7.2)
|
||||
activejob (= 6.1.7.2)
|
||||
activemodel (= 6.1.7.2)
|
||||
activerecord (= 6.1.7.2)
|
||||
activestorage (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
rails (7.0.4.3)
|
||||
actioncable (= 7.0.4.3)
|
||||
actionmailbox (= 7.0.4.3)
|
||||
actionmailer (= 7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
actiontext (= 7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activemodel (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.7.2)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
railties (= 7.0.4.3)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
@@ -532,12 +540,13 @@ GEM
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
rails_safe_tasks (1.0.0)
|
||||
railties (6.1.7.2)
|
||||
actionpack (= 6.1.7.2)
|
||||
activesupport (= 6.1.7.2)
|
||||
railties (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
ransack (2.6.0)
|
||||
@@ -549,7 +558,7 @@ GEM
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.6.0)
|
||||
redis (4.8.1)
|
||||
redis-client (0.13.0)
|
||||
redis-client (0.14.0)
|
||||
connection_pool
|
||||
regexp_parser (2.7.0)
|
||||
reline (0.3.2)
|
||||
@@ -631,6 +640,9 @@ GEM
|
||||
rubyzip (2.3.2)
|
||||
rufus-scheduler (3.8.2)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
sanitize (6.0.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
sass (3.4.25)
|
||||
sass-rails (5.0.8)
|
||||
railties (>= 5.2.0)
|
||||
@@ -642,7 +654,7 @@ GEM
|
||||
semantic_range (3.0.0)
|
||||
shoulda-matchers (5.3.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (7.0.6)
|
||||
sidekiq (7.0.7)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
rack (>= 2.2.4)
|
||||
@@ -801,6 +813,7 @@ DEPENDENCIES
|
||||
digest
|
||||
dotenv-rails
|
||||
factory_bot_rails (= 6.2.0)
|
||||
faraday
|
||||
ffaker
|
||||
flipper
|
||||
flipper-active_record
|
||||
@@ -843,12 +856,13 @@ DEPENDENCIES
|
||||
paypal-sdk-merchant (= 1.117.2)
|
||||
pdf-reader
|
||||
pg (~> 1.2.3)
|
||||
private_address_check
|
||||
pry (~> 0.13.0)
|
||||
puma
|
||||
rack-mini-profiler (< 3.0.0)
|
||||
rack-rewrite
|
||||
rack-timeout
|
||||
rails (>= 6.1.4)
|
||||
rails
|
||||
rails-controller-testing
|
||||
rails-erd
|
||||
rails-i18n
|
||||
|
||||
@@ -55,28 +55,12 @@ angular.module("admin.enterprises")
|
||||
else
|
||||
alert ("#{manager.email}" + " " + t("is_already_manager"))
|
||||
|
||||
$scope.inviteManager = ->
|
||||
$scope.invite_errors = $scope.invite_success = null
|
||||
email = $scope.newUser
|
||||
|
||||
$http.post("/admin/manager_invitations", {email: email, enterprise_id: $scope.Enterprise.id}).then (response)->
|
||||
$scope.addManager({id: response.data.user, email: email})
|
||||
$scope.invite_success = t('user_invited', email: email)
|
||||
.catch (response) ->
|
||||
$scope.invite_errors = response.data.errors
|
||||
|
||||
$scope.resetModal = ->
|
||||
$scope.newUser = $scope.invite_errors = $scope.invite_success = null
|
||||
|
||||
$scope.removeLogo = ->
|
||||
$scope.performEnterpriseAction("removeLogo", "immediate_logo_removal_warning", "removed_logo_successfully")
|
||||
|
||||
$scope.removePromoImage = ->
|
||||
$scope.performEnterpriseAction("removePromoImage", "immediate_promo_image_removal_warning", "removed_promo_image_successfully")
|
||||
|
||||
$scope.removeTermsAndConditions = ->
|
||||
$scope.performEnterpriseAction("removeTermsAndConditions", "immediate_terms_and_conditions_removal_warning", "removed_terms_and_conditions_successfully")
|
||||
|
||||
$scope.performEnterpriseAction = (enterpriseActionName, warning_message_key, success_message_key) ->
|
||||
return unless confirm($scope.translation(warning_message_key))
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
angular.module("admin.enterprises").directive 'termsAndConditionsWarning', ($rootScope, $compile, $templateCache, DialogDefaults, $timeout) ->
|
||||
restrict: 'A'
|
||||
scope: true
|
||||
|
||||
link: (scope, element, attr) ->
|
||||
# This file input click handler will hold the browser file input dialog and show a warning modal
|
||||
scope.hold_file_input_and_show_warning_modal = (event) ->
|
||||
event.preventDefault()
|
||||
scope.template = $compile($templateCache.get('admin/modals/terms_and_conditions_warning.html'))(scope)
|
||||
if scope.template.dialog
|
||||
scope.template.dialog(DialogDefaults)
|
||||
scope.template.dialog('open')
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
element.bind 'click', scope.hold_file_input_and_show_warning_modal
|
||||
|
||||
# When the user presses continue in the warning modal, we open the browser file input dialog
|
||||
scope.continue = ->
|
||||
scope.template.dialog('close')
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
# unbind warning modal handler and click file input again to open the browser file input dialog
|
||||
element.unbind('click').trigger('click')
|
||||
# afterwards, bind warning modal handler again so that the warning is shown the next time
|
||||
$timeout ->
|
||||
element.bind 'click', scope.hold_file_input_and_show_warning_modal
|
||||
return
|
||||
|
||||
scope.close = ->
|
||||
scope.template.dialog('close')
|
||||
$rootScope.$evalAsync()
|
||||
return
|
||||
@@ -17,7 +17,14 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
]
|
||||
$scope.page = 1
|
||||
$scope.per_page = $scope.per_page_options[0].id
|
||||
|
||||
searchThrough = ["order_distributor_name",
|
||||
"order_bill_address_phone",
|
||||
"order_bill_address_firstname",
|
||||
"order_bill_address_lastname",
|
||||
"variant_product_supplier_name",
|
||||
"order_email",
|
||||
"order_number",
|
||||
"product_name"].join("_or_") + "_cont"
|
||||
|
||||
$scope.confirmRefresh = ->
|
||||
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
|
||||
@@ -26,7 +33,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.distributorFilter = ''
|
||||
$scope.supplierFilter = ''
|
||||
$scope.orderCycleFilter = ''
|
||||
$scope.quickSearch = ''
|
||||
$scope.query = ''
|
||||
$scope.startDate = undefined
|
||||
$scope.endDate = undefined
|
||||
event = new CustomEvent('flatpickr:clear')
|
||||
@@ -60,6 +67,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
|
||||
|
||||
RequestMonitor.load LineItems.index(
|
||||
"q[#{searchThrough}]": $scope.query,
|
||||
"q[order_state_not_eq]": "canceled",
|
||||
"q[order_shipment_state_not_eq]": "shipped",
|
||||
"q[order_completed_at_not_null]": "true",
|
||||
|
||||
@@ -14,7 +14,4 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
|
||||
'removePromoImage':
|
||||
url: '/api/v0/enterprises/:id/promo_image.json'
|
||||
method: 'DELETE'
|
||||
'removeTermsAndConditions':
|
||||
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
|
||||
method: 'DELETE'
|
||||
})
|
||||
|
||||
@@ -63,4 +63,3 @@ angular.module("admin.resources").factory 'Enterprises', ($q, $filter, Enterpris
|
||||
|
||||
removeLogo: performActionOnEnterpriseResource(EnterpriseResource.removeLogo)
|
||||
removePromoImage: performActionOnEnterpriseResource(EnterpriseResource.removePromoImage)
|
||||
removeTermsAndConditions: performActionOnEnterpriseResource(EnterpriseResource.removeTermsAndConditions)
|
||||
|
||||
@@ -8,7 +8,7 @@ handle_move = (e, data) ->
|
||||
node = data.rslt.o
|
||||
new_parent = data.rslt.np
|
||||
|
||||
url = new URL(base_url)
|
||||
url = new URL(Spree.routes.admin_taxonomy_taxons)
|
||||
url.pathname = url.pathname + '/' + node.attr("id")
|
||||
data = {
|
||||
_method: "put",
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
|
||||
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider, $locationProvider) ->
|
||||
$locationProvider.hashPrefix('')
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -55,6 +55,10 @@ angular.module('Darkswarm').factory "EnterpriseRegistrationService", ($http, Reg
|
||||
).catch((response) ->
|
||||
Loading.clear()
|
||||
alert(t('failed_to_update_enterprise_unknown'))
|
||||
if response.data.errors.instagram
|
||||
igErr = document.querySelector("#instagram-error")
|
||||
igErr.style.display = 'block'
|
||||
igErr.textContent = response.data.errors.instagram[0]
|
||||
)
|
||||
|
||||
prepare: =>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
%div
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.title' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.message_1' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.message_2' | t }}
|
||||
|
||||
.text-center
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), ng: { click: 'close()' } }
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), ng: { click: 'continue()' } }
|
||||
@@ -67,7 +67,7 @@ module Admin
|
||||
|
||||
def collection
|
||||
if json_request? && params[:enterprise_id].present?
|
||||
CustomersWithBalance.new(managed_enterprise_id).query.
|
||||
CustomersWithBalance.new(Customer.of(managed_enterprise_id)).query.
|
||||
includes(
|
||||
:enterprise,
|
||||
{ bill_address: [:state, :country] },
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ManagerInvitationsController < Spree::Admin::BaseController
|
||||
authorize_resource class: false
|
||||
|
||||
def create
|
||||
@email = params[:email]
|
||||
@enterprise = Enterprise.find(params[:enterprise_id])
|
||||
|
||||
authorize! :edit, @enterprise
|
||||
|
||||
existing_user = Spree::User.find_by(email: @email)
|
||||
|
||||
if existing_user
|
||||
render json: { errors: t('admin.enterprises.invite_manager.user_already_exists') },
|
||||
status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
|
||||
new_user = create_new_manager
|
||||
|
||||
if new_user
|
||||
render json: { user: new_user.id }, status: :ok
|
||||
else
|
||||
render json: { errors: t('admin.enterprises.invite_manager.error') },
|
||||
status: :internal_server_error
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_new_manager
|
||||
password = Devise.friendly_token
|
||||
new_user = Spree::User.create(email: @email, unconfirmed_email: @email, password: password)
|
||||
new_user.reset_password_token = Devise.friendly_token
|
||||
# Same time as used in Devise's lib/devise/models/recoverable.rb.
|
||||
new_user.reset_password_sent_at = Time.now.utc
|
||||
new_user.save!
|
||||
|
||||
@enterprise.users << new_user
|
||||
EnterpriseMailer.manager_invitation(@enterprise, new_user).deliver_later
|
||||
|
||||
new_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -106,7 +106,9 @@ module Api
|
||||
end
|
||||
|
||||
def json_api_error(message, **options)
|
||||
{ errors: [{ detail: message }] }.merge(options)
|
||||
error_options = options.delete(:error_options) || {}
|
||||
|
||||
{ errors: [{ detail: message }.merge(error_options)] }.merge(options)
|
||||
end
|
||||
|
||||
def json_api_invalid(message, errors)
|
||||
|
||||
@@ -6,11 +6,17 @@ module Api
|
||||
module V1
|
||||
class CustomersController < Api::V1::BaseController
|
||||
include AddressTransformation
|
||||
include ExtraFields
|
||||
|
||||
skip_authorization_check only: :index
|
||||
|
||||
before_action :authorize_action, only: [:show, :update, :destroy]
|
||||
|
||||
# Query parameters
|
||||
before_action only: [:index] do
|
||||
@extra_customer_fields = extra_fields :customer, [:balance]
|
||||
end
|
||||
|
||||
def index
|
||||
@pagy, customers = pagy(search_customers, pagy_options)
|
||||
|
||||
@@ -51,7 +57,11 @@ module Api
|
||||
private
|
||||
|
||||
def customer
|
||||
@customer ||= Customer.find(params[:id])
|
||||
@customer ||= if action_name == "show"
|
||||
CustomersWithBalance.new(Customer.where(id: params[:id])).query.first!
|
||||
else
|
||||
Customer.find(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_action
|
||||
@@ -61,6 +71,11 @@ module Api
|
||||
def search_customers
|
||||
customers = visible_customers.includes(:bill_address, :ship_address)
|
||||
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
|
||||
|
||||
if @extra_customer_fields.include?(:balance)
|
||||
customers = CustomersWithBalance.new(customers).query
|
||||
end
|
||||
|
||||
customers.ransack(params[:q]).result.order(:id)
|
||||
end
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ module CheckoutCallbacks
|
||||
prepend_before_action :require_distributor_chosen
|
||||
|
||||
before_action :load_order, :associate_user, :load_saved_addresses, :load_saved_credit_cards
|
||||
before_action :allowed_shipping_methods, if: -> {
|
||||
params[:step] == "details"
|
||||
}
|
||||
before_action :load_shipping_methods, if: -> { params[:step] == "details" }
|
||||
|
||||
before_action :ensure_order_not_completed
|
||||
before_action :ensure_checkout_allowed
|
||||
@@ -48,22 +46,8 @@ module CheckoutCallbacks
|
||||
@selected_card = nil
|
||||
end
|
||||
|
||||
def allowed_shipping_methods
|
||||
@allowed_shipping_methods ||= sorted_available_shipping_methods.filter(
|
||||
&method(:supports_all_products_shipping_categories?)
|
||||
)
|
||||
end
|
||||
|
||||
def sorted_available_shipping_methods
|
||||
available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
def supports_all_products_shipping_categories?(shipping_method)
|
||||
(products_shipping_categories - shipping_method.shipping_categories.pluck(:id)).empty?
|
||||
end
|
||||
|
||||
def products_shipping_categories
|
||||
@products_shipping_categories ||= @order.products.pluck(:shipping_category_id).uniq
|
||||
def load_shipping_methods
|
||||
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
def redirect_to_shop?
|
||||
|
||||
32
app/controllers/concerns/extra_fields.rb
Normal file
32
app/controllers/concerns/extra_fields.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# To be included in api controllers for handeling query params
|
||||
module ExtraFields
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def invalid_query_param(name, status, msg)
|
||||
render status: status, json: json_api_error(msg, error_options:
|
||||
{
|
||||
title: I18n.t("api.query_param.error.title"),
|
||||
source: { parameter: name },
|
||||
status: status,
|
||||
code: Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
|
||||
})
|
||||
end
|
||||
|
||||
def extra_fields(type, available_fields)
|
||||
fields = params.dig(:extra_fields, type)&.split(',')&.compact&.map(&:to_sym)
|
||||
return [] if fields.blank?
|
||||
|
||||
unknown_fields = fields - available_fields
|
||||
|
||||
if unknown_fields.present?
|
||||
invalid_query_param(
|
||||
"extra_fields[#{type}]", :unprocessable_entity,
|
||||
I18n.t("api.query_param.error.extra_fields", fields: unknown_fields.join(', '))
|
||||
)
|
||||
end
|
||||
|
||||
fields
|
||||
end
|
||||
end
|
||||
19
app/controllers/concerns/manager_invitations.rb
Normal file
19
app/controllers/concerns/manager_invitations.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ManagerInvitations
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def create_new_manager(email, enterprise)
|
||||
password = Devise.friendly_token
|
||||
new_user = Spree::User.create(email: email, unconfirmed_email: email, password: password)
|
||||
new_user.reset_password_token = Devise.friendly_token
|
||||
# Same time as used in Devise's lib/devise/models/recoverable.rb.
|
||||
new_user.reset_password_sent_at = Time.now.utc
|
||||
if new_user.save
|
||||
enterprise.users << new_user
|
||||
EnterpriseMailer.manager_invitation(enterprise, new_user).deliver_later
|
||||
end
|
||||
|
||||
new_user
|
||||
end
|
||||
end
|
||||
@@ -24,7 +24,7 @@ class SplitCheckoutController < ::BaseController
|
||||
check_step if params[:step]
|
||||
recalculate_tax if params[:step] == "summary"
|
||||
|
||||
flash_error_when_no_shipping_method_available if allowed_shipping_methods.none?
|
||||
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -168,7 +168,7 @@ class SplitCheckoutController < ::BaseController
|
||||
end
|
||||
|
||||
def shipping_method_ship_address_not_required?
|
||||
selected_shipping_method = allowed_shipping_methods&.select do |sm|
|
||||
selected_shipping_method = available_shipping_methods&.select do |sm|
|
||||
sm.id.to_s == params[:shipping_method_id]
|
||||
end
|
||||
|
||||
|
||||
47
app/controllers/webhook_endpoints_controller.rb
Normal file
47
app/controllers/webhook_endpoints_controller.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhookEndpointsController < ::BaseController
|
||||
before_action :load_resource, only: :destroy
|
||||
|
||||
def create
|
||||
webhook_endpoint = spree_current_user.webhook_endpoints.new(webhook_endpoint_params)
|
||||
|
||||
if webhook_endpoint.save
|
||||
flash[:success] = t('.success')
|
||||
else
|
||||
flash[:error] = t('.error')
|
||||
end
|
||||
|
||||
redirect_to redirect_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @webhook_endpoint.destroy
|
||||
flash[:success] = t('.success')
|
||||
else
|
||||
flash[:error] = t('.error')
|
||||
end
|
||||
|
||||
redirect_to redirect_path
|
||||
end
|
||||
|
||||
def load_resource
|
||||
@webhook_endpoint = spree_current_user.webhook_endpoints.find(params[:id])
|
||||
end
|
||||
|
||||
def webhook_endpoint_params
|
||||
params.require(:webhook_endpoint).permit(:url)
|
||||
end
|
||||
|
||||
def redirect_path
|
||||
if request.referer.blank? || request.referer.include?(spree.account_path)
|
||||
developer_settings_path
|
||||
else
|
||||
request.referer
|
||||
end
|
||||
end
|
||||
|
||||
def developer_settings_path
|
||||
"#{spree.account_path}#/developer_settings"
|
||||
end
|
||||
end
|
||||
27
app/jobs/order_cycle_opened_job.rb
Normal file
27
app/jobs/order_cycle_opened_job.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Trigger jobs for any order cycles that recently opened
|
||||
class OrderCycleOpenedJob < ApplicationJob
|
||||
def perform
|
||||
ActiveRecord::Base.transaction do
|
||||
recently_opened_order_cycles.find_each do |order_cycle|
|
||||
OrderCycleWebhookService.create_webhook_job(order_cycle, 'order_cycle.opened')
|
||||
end
|
||||
mark_as_opened(recently_opened_order_cycles)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def recently_opened_order_cycles
|
||||
@recently_opened_order_cycles ||= OrderCycle
|
||||
.where(opened_at: nil)
|
||||
.where(orders_open_at: 1.hour.ago..Time.zone.now)
|
||||
.lock.order(:id)
|
||||
end
|
||||
|
||||
def mark_as_opened(order_cycles)
|
||||
now = Time.zone.now
|
||||
order_cycles.update_all(opened_at: now, updated_at: now)
|
||||
end
|
||||
end
|
||||
50
app/jobs/webhook_delivery_job.rb
Normal file
50
app/jobs/webhook_delivery_job.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "faraday"
|
||||
require "private_address_check"
|
||||
require "private_address_check/tcpsocket_ext"
|
||||
|
||||
# Deliver a webhook payload
|
||||
# As a delayed job, it can run asynchronously and handle retries.
|
||||
class WebhookDeliveryJob < ApplicationJob
|
||||
# General failed request error that we're going to use to signal
|
||||
# the job runner to retry our webhook worker.
|
||||
class FailedWebhookRequestError < StandardError; end
|
||||
|
||||
queue_as :default
|
||||
|
||||
def perform(url, event, payload)
|
||||
body = {
|
||||
id: job_id,
|
||||
at: Time.zone.now.to_s,
|
||||
event: event,
|
||||
data: payload,
|
||||
}
|
||||
|
||||
# Request user-submitted url, preventing any private connections being made
|
||||
# (SSRF).
|
||||
# This method may allow the socket to open, but is necessary in order to
|
||||
# protect from TOC/TOU.
|
||||
# Note that private_address_check provides some methods for pre-validating,
|
||||
# but they're not as comprehensive and so unnecessary here. Simply
|
||||
# momentarily opening sockets probably can't cause DoS or other damage.
|
||||
PrivateAddressCheck.only_public_connections do
|
||||
notify_endpoint(url, body)
|
||||
end
|
||||
end
|
||||
|
||||
def notify_endpoint(url, body)
|
||||
connection = Faraday.new(
|
||||
request: { timeout: 30 },
|
||||
headers: {
|
||||
'User-Agent' => 'openfoodnetwork_webhook/1.0',
|
||||
'Content-Type' => 'application/json',
|
||||
}
|
||||
)
|
||||
response = connection.post(url, body.to_json)
|
||||
|
||||
# Raise a failed request error and let job runner handle retrying.
|
||||
# In theory, only 5xx errors should be retried, but who knows.
|
||||
raise FailedWebhookRequestError, response.status.to_s unless response.success?
|
||||
end
|
||||
end
|
||||
@@ -61,4 +61,9 @@ class CustomerSchema < JsonApiSchema
|
||||
def self.relationships
|
||||
[:enterprise]
|
||||
end
|
||||
|
||||
# Optional attributes included with eg: CustomerSchema.schema(extra_fields: :balance)
|
||||
def self.balance
|
||||
{ balance: { type: :number, format: :double } }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,84 +19,77 @@ class JsonApiSchema
|
||||
end
|
||||
|
||||
def schema(options = {})
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
},
|
||||
meta: { type: :object },
|
||||
links: { type: :object }
|
||||
},
|
||||
required: [:data]
|
||||
}
|
||||
Structure.schema(data_properties(**options))
|
||||
end
|
||||
|
||||
def collection(options)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
type: :object,
|
||||
properties: {
|
||||
pagination: {
|
||||
type: :object,
|
||||
properties: {
|
||||
results: { type: :integer, example: 250 },
|
||||
pages: { type: :integer, example: 5 },
|
||||
page: { type: :integer, example: 2 },
|
||||
per_page: { type: :integer, example: 50 },
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:pagination]
|
||||
},
|
||||
links: {
|
||||
type: :object,
|
||||
properties: {
|
||||
self: { type: :string },
|
||||
first: { type: :string },
|
||||
prev: { type: :string, nullable: true },
|
||||
next: { type: :string, nullable: true },
|
||||
last: { type: :string }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:data, :meta, :links]
|
||||
}
|
||||
Structure.collection(data_properties(**options))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def data_properties(require_all: false)
|
||||
def data_properties(require_all: false, extra_fields: nil)
|
||||
extra_fields_result = get_extra_fields(extra_fields)
|
||||
attributes = get_attributes(extra_fields_result)
|
||||
required = get_required(require_all, extra_fields, extra_fields_result)
|
||||
|
||||
Structure.data_properties(object_name, attributes, required, relationship_properties)
|
||||
end
|
||||
|
||||
def relationship_properties
|
||||
relationships.to_h { |name| [name, relationship_schema(name)] }
|
||||
end
|
||||
|
||||
# Example
|
||||
# MySchema.schema(extra_fields: :my_method)
|
||||
# => extra_fields_result = MySchema.my_method
|
||||
# => attributes = attributes.merge(extra_fields_result)
|
||||
#
|
||||
# MySchema.schema(extra_fields: {name: :my_method, required: true, opts: {method_opt: true}})
|
||||
# => extra_fields_result = MySchema.my_method(method_opt: true)
|
||||
# => attributes = attributes.merge(extra_fields_result)
|
||||
# => required += extra_fields_result.keys
|
||||
#
|
||||
# MySchema.schema(extra_fields: [:my_method, :another_method])
|
||||
# => extra_fields_result = MySchema.my_method.merge(another_method)
|
||||
# => attributes = attribtues.merge(extra_fields_result)
|
||||
#
|
||||
# To test use eg:
|
||||
# MySchema.schema(extra_fields: :my_method)
|
||||
# .dig(:properties, :data, :properties, :attributes)
|
||||
def get_extra_fields(extra_fields)
|
||||
case extra_fields
|
||||
when Symbol
|
||||
public_send(extra_fields)
|
||||
when Hash
|
||||
public_send(extra_fields[:name], **extra_fields[:opts].to_h)
|
||||
when Array
|
||||
obj = {}
|
||||
|
||||
extra_fields.each do |w|
|
||||
obj.merge!(get_extra_fields(w))
|
||||
end
|
||||
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
def get_required(require_all, extra_fields, extra_fields_result)
|
||||
required = require_all ? all_attributes : required_attributes
|
||||
|
||||
{
|
||||
id: { type: :string, example: "1" },
|
||||
type: { type: :string, example: object_name },
|
||||
attributes: {
|
||||
type: :object,
|
||||
properties: attributes,
|
||||
required: required
|
||||
},
|
||||
relationships: {
|
||||
type: :object,
|
||||
properties: relationships.to_h do |name|
|
||||
[
|
||||
name,
|
||||
relationship_schema(name)
|
||||
]
|
||||
end
|
||||
}
|
||||
}
|
||||
if extra_fields.is_a?(Hash) && extra_fields[:required] == true && extra_fields_result.present?
|
||||
required += extra_fields_result.keys
|
||||
end
|
||||
|
||||
required
|
||||
end
|
||||
|
||||
def get_attributes(extra_fields_result)
|
||||
if [extra_fields_result, attributes].all?{ |obj| obj.respond_to?(:merge) }
|
||||
attributes.merge(extra_fields_result)
|
||||
else
|
||||
attributes
|
||||
end
|
||||
end
|
||||
|
||||
def relationship_schema(name)
|
||||
|
||||
83
app/json_schemas/json_api_schema/structure.rb
Normal file
83
app/json_schemas/json_api_schema/structure.rb
Normal file
@@ -0,0 +1,83 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
|
||||
class JsonApiSchema
|
||||
module Structure
|
||||
extend self
|
||||
|
||||
def schema(data_properties)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :object,
|
||||
properties: data_properties
|
||||
},
|
||||
meta: { type: :object },
|
||||
links: { type: :object }
|
||||
},
|
||||
required: [:data]
|
||||
}
|
||||
end
|
||||
|
||||
def collection(data_properties)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: data_properties
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
type: :object,
|
||||
properties: {
|
||||
pagination: {
|
||||
type: :object,
|
||||
properties: {
|
||||
results: { type: :integer, example: 250 },
|
||||
pages: { type: :integer, example: 5 },
|
||||
page: { type: :integer, example: 2 },
|
||||
per_page: { type: :integer, example: 50 },
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:pagination]
|
||||
},
|
||||
links: {
|
||||
type: :object,
|
||||
properties: {
|
||||
self: { type: :string },
|
||||
first: { type: :string },
|
||||
prev: { type: :string, nullable: true },
|
||||
next: { type: :string, nullable: true },
|
||||
last: { type: :string }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:data, :meta, :links]
|
||||
}
|
||||
end
|
||||
|
||||
def data_properties(object_name, attributes, required, relationship_properties)
|
||||
{
|
||||
id: { type: :string, example: "1" },
|
||||
type: { type: :string, example: object_name },
|
||||
attributes: {
|
||||
type: :object,
|
||||
properties: attributes,
|
||||
required: required
|
||||
},
|
||||
relationships: {
|
||||
type: :object,
|
||||
properties: relationship_properties
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
@@ -17,7 +17,7 @@ class Enterprise < ApplicationRecord
|
||||
}.freeze
|
||||
VALID_INSTAGRAM_REGEX = %r{\A[a-zA-Z0-9._]{1,30}([^/-]*)\z}
|
||||
|
||||
searchable_attributes :sells, :is_primary_producer
|
||||
searchable_attributes :sells, :is_primary_producer, :name
|
||||
searchable_associations :properties
|
||||
searchable_scopes :is_primary_producer, :is_distributor, :is_hub, :activated, :visible,
|
||||
:ready_for_checkout, :not_ready_for_checkout
|
||||
@@ -450,7 +450,8 @@ class Enterprise < ApplicationRecord
|
||||
end
|
||||
|
||||
def strip_url(url)
|
||||
url&.sub(%r{(https?://)?}, '')
|
||||
# Strip protocol and trailing slash
|
||||
url&.sub(%r{(https?://)?}, '')&.sub(%r{/\z}, '')
|
||||
end
|
||||
|
||||
def correct_whatsapp_url(phone_number)
|
||||
@@ -458,11 +459,11 @@ class Enterprise < ApplicationRecord
|
||||
end
|
||||
|
||||
def correct_instagram_url(url)
|
||||
url && strip_url(url.downcase).sub(%r{www.instagram.com/}, '').sub(%r{instagram.com/}, '').delete("@")
|
||||
url && strip_url(url.downcase).sub(%r{(www\.)?instagram.com/}, '').delete("@")
|
||||
end
|
||||
|
||||
def correct_twitter_url(url)
|
||||
url && strip_url(url).sub(%r{www.twitter.com/}, '').delete("@")
|
||||
url && strip_url(url).sub(%r{(www\.)?twitter.com/}, '').delete("@")
|
||||
end
|
||||
|
||||
def set_unused_address_fields
|
||||
|
||||
@@ -34,6 +34,7 @@ class OrderCycle < ApplicationRecord
|
||||
|
||||
attr_accessor :incoming_exchanges, :outgoing_exchanges
|
||||
|
||||
before_update :reset_opened_at, if: :will_save_change_to_orders_open_at?
|
||||
before_update :reset_processed_at, if: :will_save_change_to_orders_close_at?
|
||||
after_save :sync_subscriptions, if: :opening?
|
||||
|
||||
@@ -333,6 +334,14 @@ class OrderCycle < ApplicationRecord
|
||||
errors.add(:orders_close_at, :after_orders_open_at)
|
||||
end
|
||||
|
||||
def reset_opened_at
|
||||
# Reset only if order cycle is opening again at a later date
|
||||
return unless orders_open_at.present? && orders_open_at_was.present?
|
||||
return unless orders_open_at > orders_open_at_was
|
||||
|
||||
self.opened_at = nil
|
||||
end
|
||||
|
||||
def reset_processed_at
|
||||
return unless orders_close_at.present? && orders_close_at_was.present?
|
||||
return unless orders_close_at > orders_close_at_was
|
||||
|
||||
@@ -4,7 +4,7 @@ module Spree
|
||||
class Address < ApplicationRecord
|
||||
include AddressDisplay
|
||||
|
||||
searchable_attributes :firstname, :lastname
|
||||
searchable_attributes :firstname, :lastname, :phone
|
||||
searchable_associations :country, :state
|
||||
|
||||
belongs_to :country, class_name: "Spree::Country"
|
||||
|
||||
@@ -14,7 +14,7 @@ module Spree
|
||||
|
||||
searchable_attributes :number, :state, :shipment_state, :payment_state, :distributor_id,
|
||||
:order_cycle_id, :email, :total, :customer_id
|
||||
searchable_associations :shipping_method, :bill_address
|
||||
searchable_associations :shipping_method, :bill_address, :distributor
|
||||
searchable_scopes :complete, :incomplete
|
||||
|
||||
checkout_flow do
|
||||
|
||||
@@ -38,7 +38,10 @@ module Spree
|
||||
has_many :customers
|
||||
has_many :credit_cards
|
||||
has_many :report_rendering_options, class_name: "::ReportRenderingOptions", dependent: :destroy
|
||||
has_many :webhook_endpoints, dependent: :destroy
|
||||
|
||||
accepts_nested_attributes_for :enterprise_roles, allow_destroy: true
|
||||
accepts_nested_attributes_for :webhook_endpoints
|
||||
|
||||
accepts_nested_attributes_for :bill_address
|
||||
accepts_nested_attributes_for :ship_address
|
||||
@@ -148,10 +151,6 @@ module Spree
|
||||
spree_orders.incomplete.where(created_by_id: id).order('created_at DESC').first
|
||||
end
|
||||
|
||||
def flipper_id
|
||||
"#{self.class.name};#{id}"
|
||||
end
|
||||
|
||||
def disabled
|
||||
disabled_at.present?
|
||||
end
|
||||
|
||||
6
app/models/webhook_endpoint.rb
Normal file
6
app/models/webhook_endpoint.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Records a webhook url to send notifications to
|
||||
class WebhookEndpoint < ApplicationRecord
|
||||
validates :url, presence: true
|
||||
end
|
||||
@@ -1,28 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Fetches the customers of the specified enterprise including the aggregated balance across the
|
||||
# customer's orders. That is, we get the total balance for each customer with this enterprise.
|
||||
# Adds an aggregated 'balance_value' to each customer based on their order history
|
||||
#
|
||||
class CustomersWithBalance
|
||||
def initialize(enterprise)
|
||||
@enterprise = enterprise
|
||||
def initialize(customers)
|
||||
@customers = customers
|
||||
end
|
||||
|
||||
def query
|
||||
Customer.of(enterprise).
|
||||
@customers.
|
||||
joins(left_join_complete_orders).
|
||||
group("customers.id").
|
||||
select("customers.*").
|
||||
select(outstanding_balance_sum)
|
||||
select("#{outstanding_balance_sum} AS balance_value")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :enterprise
|
||||
|
||||
def outstanding_balance_sum
|
||||
"SUM(#{OutstandingBalance.new.statement}) AS balance_value"
|
||||
end
|
||||
|
||||
# The resulting orders are in states that belong after the checkout. Only these can be considered
|
||||
# for a customer's balance.
|
||||
def left_join_complete_orders
|
||||
@@ -36,4 +30,8 @@ class CustomersWithBalance
|
||||
states = Spree::Order::FINALIZED_STATES.map { |state| Arel::Nodes.build_quoted(state) }
|
||||
Arel::Nodes::In.new(Spree::Order.arel_table[:state], states)
|
||||
end
|
||||
|
||||
def outstanding_balance_sum
|
||||
"SUM(#{OutstandingBalance.new.statement})::float"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,4 +23,8 @@ class ApplicationReflex < StimulusReflex::Reflex
|
||||
def current_ability
|
||||
Spree::Ability.new(current_user)
|
||||
end
|
||||
|
||||
def with_locale(&block)
|
||||
I18n.with_locale(current_user.locale, &block)
|
||||
end
|
||||
end
|
||||
|
||||
35
app/reflexes/bulk_actions_in_orders_list_reflex.rb
Normal file
35
app/reflexes/bulk_actions_in_orders_list_reflex.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BulkActionsInOrdersListReflex < ApplicationReflex
|
||||
def resend_confirmation_email(order_ids)
|
||||
orders(order_ids).find_each do |o|
|
||||
Spree::OrderMailer.confirm_email_for_customer(o.id, true).deliver_later if can? :resend, o
|
||||
end
|
||||
|
||||
success("admin.resend_confirmation_emails_feedback", order_ids.count)
|
||||
end
|
||||
|
||||
def send_invoice(order_ids)
|
||||
count = 0
|
||||
orders(order_ids).find_each do |o|
|
||||
next unless o.distributor.can_invoice? && (o.resumed? || o.complete?)
|
||||
|
||||
Spree::OrderMailer.invoice_email(o.id).deliver_later
|
||||
count += 1
|
||||
end
|
||||
|
||||
success("admin.send_invoice_feedback", count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def success(i18n_key, count)
|
||||
flash[:success] = I18n.t(i18n_key, count: count)
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
|
||||
end
|
||||
|
||||
def orders(order_ids)
|
||||
Spree::Order.where(id: order_ids)
|
||||
end
|
||||
end
|
||||
10
app/reflexes/enterprise_edit_reflex.rb
Normal file
10
app/reflexes/enterprise_edit_reflex.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
class EnterpriseEditReflex < ApplicationReflex
|
||||
def remove_terms_and_conditions
|
||||
@enterprise = Enterprise.find(element.dataset['enterprise-id'])
|
||||
throw :forbidden unless can?(:remove_terms_and_conditions, @enterprise)
|
||||
|
||||
@enterprise.terms_and_conditions.purge_later
|
||||
end
|
||||
end
|
||||
42
app/reflexes/invite_manager_reflex.rb
Normal file
42
app/reflexes/invite_manager_reflex.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InviteManagerReflex < ApplicationReflex
|
||||
include ManagerInvitations
|
||||
|
||||
def invite
|
||||
email = params[:email]
|
||||
enterprise = Enterprise.find(params[:enterprise_id])
|
||||
|
||||
authorize! :edit, enterprise
|
||||
|
||||
existing_user = Spree::User.find_by(email: email)
|
||||
|
||||
locals = { error: nil, success: nil, email: email, enterprise: enterprise }
|
||||
|
||||
if existing_user
|
||||
locals[:error] = I18n.t('admin.enterprises.invite_manager.user_already_exists')
|
||||
|
||||
return_morph(locals)
|
||||
return
|
||||
end
|
||||
|
||||
new_user = create_new_manager(email, enterprise)
|
||||
|
||||
if new_user.errors.empty?
|
||||
locals[:success] = true
|
||||
else
|
||||
locals[:error] = new_user.errors.full_messages.to_sentence
|
||||
end
|
||||
|
||||
return_morph(locals)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def return_morph(locals)
|
||||
morph "#add_manager_modal",
|
||||
with_locale {
|
||||
render(partial: "admin/enterprises/form/add_new_unregistered_manager", locals: locals)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -1,13 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ResendConfirmationEmailReflex < ApplicationReflex
|
||||
def confirm(order_ids)
|
||||
Spree::Order.where(id: order_ids).find_each do |o|
|
||||
Spree::OrderMailer.confirm_email_for_customer(o.id, true).deliver_later if can? :resend, o
|
||||
end
|
||||
|
||||
flash[:success] = I18n.t("admin.resend_confirmation_emails_feedback", count: order_ids.count)
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
|
||||
end
|
||||
end
|
||||
@@ -16,6 +16,10 @@ module Api
|
||||
address(object.shipping_address)
|
||||
end
|
||||
|
||||
attribute :balance, if: proc { |record|
|
||||
record.respond_to?(:balance_value)
|
||||
}, &:balance_value
|
||||
|
||||
belongs_to :enterprise, links: {
|
||||
related: ->(object) {
|
||||
url_helpers.api_v1_enterprise_url(id: object.enterprise_id)
|
||||
|
||||
@@ -18,9 +18,7 @@ class OrderAvailablePaymentMethods
|
||||
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor,
|
||||
"FilterPaymentMethods", customer&.tag_list)
|
||||
applicator.filter!(payment_methods)
|
||||
|
||||
payment_methods.uniq
|
||||
applicator.filter(payment_methods)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -32,7 +30,7 @@ class OrderAvailablePaymentMethods
|
||||
distributor.payment_methods.where(
|
||||
id: available_distributor_payment_methods_ids
|
||||
)
|
||||
end.available.select(&:configured?)
|
||||
end.available.select(&:configured?).uniq
|
||||
end
|
||||
|
||||
def available_distributor_payment_methods_ids
|
||||
|
||||
@@ -14,25 +14,32 @@ class OrderAvailableShippingMethods
|
||||
def to_a
|
||||
return [] if distributor.blank?
|
||||
|
||||
shipping_methods = shipping_methods_before_tag_rules_applied
|
||||
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor,
|
||||
"FilterShippingMethods", customer&.tag_list)
|
||||
applicator.filter!(shipping_methods)
|
||||
|
||||
shipping_methods.uniq
|
||||
filter_by_category(tag_rules.filter(shipping_methods))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def shipping_methods_before_tag_rules_applied
|
||||
def filter_by_category(methods)
|
||||
return methods unless OpenFoodNetwork::FeatureToggle.enabled?(:match_shipping_categories,
|
||||
distributor&.owner)
|
||||
|
||||
required_category_ids = order.products.pluck(:shipping_category_id).to_set
|
||||
return methods if required_category_ids.empty?
|
||||
|
||||
methods.select do |method|
|
||||
provided_category_ids = method.shipping_categories.pluck(:id).to_set
|
||||
required_category_ids.subset?(provided_category_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def shipping_methods
|
||||
if order_cycle.nil? || order_cycle.simple?
|
||||
distributor.shipping_methods
|
||||
else
|
||||
distributor.shipping_methods.where(
|
||||
id: available_distributor_shipping_methods_ids
|
||||
)
|
||||
end.frontend.to_a
|
||||
end.frontend.to_a.uniq
|
||||
end
|
||||
|
||||
def available_distributor_shipping_methods_ids
|
||||
@@ -40,4 +47,10 @@ class OrderAvailableShippingMethods
|
||||
.where(distributor_id: distributor.id)
|
||||
.select(:shipping_method_id)
|
||||
end
|
||||
|
||||
def tag_rules
|
||||
OpenFoodNetwork::TagRuleApplicator.new(
|
||||
distributor, "FilterShippingMethods", customer&.tag_list
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
21
app/services/order_cycle_webhook_service.rb
Normal file
21
app/services/order_cycle_webhook_service.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Create a webhook payload for an order cycle event.
|
||||
# The payload will be delivered asynchronously.
|
||||
class OrderCycleWebhookService
|
||||
def self.create_webhook_job(order_cycle, event)
|
||||
webhook_payload = order_cycle
|
||||
.slice(:id, :name, :orders_open_at, :orders_close_at, :coordinator_id)
|
||||
.merge(coordinator_name: order_cycle.coordinator.name)
|
||||
|
||||
# Endpoints for coordinator owner
|
||||
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints
|
||||
|
||||
# Plus unique endpoints for distributor owners (ignore duplicates)
|
||||
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints)
|
||||
|
||||
webhook_endpoints.each do |endpoint|
|
||||
WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -15,7 +15,10 @@ module PermittedAttributes
|
||||
private
|
||||
|
||||
def permitted_attributes
|
||||
[:email, :password, :password_confirmation, :disabled]
|
||||
[
|
||||
:email, :password, :password_confirmation, :disabled,
|
||||
{ webhook_endpoints_attributes: [:id, :url] },
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,15 +29,11 @@ module Shop
|
||||
|
||||
private
|
||||
|
||||
# order_cycles is a ActiveRecord::Relation that is modified with reject in the TagRuleApplicator
|
||||
# If this relation is reloaded (for example by calling count on it), the modifications are lost
|
||||
def apply_tag_rules!(order_cycles)
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor,
|
||||
"FilterOrderCycles",
|
||||
@customer&.tag_list)
|
||||
applicator.filter!(order_cycles)
|
||||
|
||||
order_cycles
|
||||
applicator.filter(order_cycles)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
%div#add_manager_modal
|
||||
%form{ "data-reflex": "submit->InviteManager#invite", "data-reflex-serialize-form": true }
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
= t('js.admin.modals.invite_title')
|
||||
|
||||
- if success
|
||||
%p.alert-box.ok= t('user_invited', email: email)
|
||||
|
||||
- if error
|
||||
%p.alert-box.error= error
|
||||
|
||||
= text_field_tag :email, nil, class: 'fullwidth margin-bottom-20'
|
||||
= hidden_field_tag :enterprise_id, @enterprise&.id || enterprise.id
|
||||
|
||||
.modal-actions
|
||||
- if success
|
||||
%input{ class: "button icon-plus secondary", type: 'button', value: t('js.admin.modals.close'), "data-action": "click->help-modal#close" }
|
||||
- else
|
||||
%input{ class: "button icon-plus secondary", type: 'button', value: t('js.admin.modals.cancel'), "data-action": "click->help-modal#close" }
|
||||
= submit_tag "#{t('js.admin.modals.invite')}"
|
||||
|
||||
@@ -31,22 +31,39 @@
|
||||
= f.label :invoice_text, t('.invoice_text')
|
||||
.omega.eight.columns
|
||||
= f.text_area :invoice_text, style: "width: 100%; height: 100px;"
|
||||
|
||||
.row
|
||||
.row{ data: { controller: 'terms-and-conditions', "terms-and-conditions-message-value": t('js.admin.enterprises.form.images.immediate_terms_and_conditions_removal_warning') } }
|
||||
.alpha.three.columns
|
||||
= f.label :terms_and_conditions, t('.terms_and_conditions')
|
||||
%i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "terms_and_conditions_info_modal" }
|
||||
|
||||
.omega.eight.columns
|
||||
%a{ href: '{{ Enterprise.terms_and_conditions }}', target: '_blank', ng: { if: 'Enterprise.terms_and_conditions' } }
|
||||
= '{{ Enterprise.terms_and_conditions_file_name }}'
|
||||
= t('.uploaded_on')
|
||||
= '{{ Enterprise.terms_and_conditions_updated_at }}'
|
||||
.omega.eight.columns#terms_and_conditions{data: { 'reflex-root': '#terms_and_conditions' } }
|
||||
- if @enterprise.terms_and_conditions.attached?
|
||||
= link_to "#{@enterprise.terms_and_conditions.blob.filename} #{ t('.uploaded_on') } #{@enterprise.terms_and_conditions.blob.created_at}", url_for(@enterprise.terms_and_conditions), target: '_blank'
|
||||
%div
|
||||
%a.icon-trash{ href: '#', data: { action: 'click->terms-and-conditions#remove', "terms-and-conditions-message-value": t('js.admin.enterprises.form.images.immediate_terms_and_conditions_removal_warning'), 'enterprise-id': @enterprise.id}}
|
||||
= t('.remove_terms_and_conditions')
|
||||
.pad-top
|
||||
= f.file_field :terms_and_conditions, accept: 'application/pdf', 'terms-and-conditions-warning' => 'true'
|
||||
.pad-top
|
||||
%a.button.red{ href: '', ng: {click: 'removeTermsAndConditions()', if: 'Enterprise.terms_and_conditions'} }
|
||||
= t('.remove_terms_and_conditions')
|
||||
%div
|
||||
.button.small{ data: { controller: 'help-modal-link', action: 'click->help-modal-link#open', "help-modal-link-target-value": "terms_and_conditions_warning_modal" } }
|
||||
= t('.upload')
|
||||
%span{ data: { "terms-and-conditions-target": "filename" } }
|
||||
= f.file_field :terms_and_conditions, accept: 'application/pdf', style: 'display: none;', data: { "terms-and-conditions-target": "fileinput" }
|
||||
|
||||
= render HelpModalComponent.new(id: "terms_and_conditions_warning_modal", close_button: false ) do
|
||||
%div
|
||||
.margin-bottom-30.text-center
|
||||
.text-big{ style: 'color: red'}
|
||||
= t('js.admin.modals.terms_and_conditions_warning.title')
|
||||
.margin-bottom-30
|
||||
%p
|
||||
= t('js.admin.modals.terms_and_conditions_warning.message_1')
|
||||
.margin-bottom-30
|
||||
%p
|
||||
= t('js.admin.modals.terms_and_conditions_warning.message_2')
|
||||
|
||||
.text-center
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), "data-action": "click->help-modal#close"}
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), "data-action": "click->help-modal#close click->terms-and-conditions#add" }
|
||||
|
||||
|
||||
= f.fields_for :business_address, @enterprise.business_address || @enterprise.build_business_address do |bf|
|
||||
@@ -63,13 +80,12 @@
|
||||
%legend= t('.invoice_item_sorting_legend')
|
||||
.three.columns.alpha
|
||||
%label= t('.sort_items_by_supplier?')
|
||||
%div{'ofn-with-tip' => t('.sort_items_by_supplier_tip')}
|
||||
%a= t 'admin.whats_this'
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.sort_items_by_supplier_tip')}
|
||||
.three.columns
|
||||
= f.radio_button :preferred_invoice_order_by_supplier, true, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'true'
|
||||
= f.radio_button :preferred_invoice_order_by_supplier, true
|
||||
= f.label :preffered_invoice_order_by_supplier, t('.enabled'), value: :true
|
||||
.five.columns.omega
|
||||
= f.radio_button :preferred_invoice_order_by_supplier, false, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'false'
|
||||
= f.radio_button :preferred_invoice_order_by_supplier, false
|
||||
= f.label :preferred_invoice_order_by_name, t('.disabled'), value: :false
|
||||
|
||||
= render HelpModalComponent.new(id: "terms_and_conditions_info_modal") do
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
=f.label :owner_id, t('.owner')
|
||||
- if full_permissions
|
||||
%span.required *
|
||||
%div{'ofn-with-tip' => t('.owner_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.owner_tip')}
|
||||
.eight.columns.omega
|
||||
- if full_permissions
|
||||
= f.hidden_field :owner_id, class: "select2 fullwidth", 'user-select' => 'Enterprise.owner', 'ng-model' => 'Enterprise.owner'
|
||||
@@ -19,8 +18,7 @@
|
||||
=f.label :user_ids, t('.notifications')
|
||||
- if full_permissions
|
||||
%span.required *
|
||||
%div{'ofn-with-tip' => t('.contact_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.contact_tip')}
|
||||
.eight.columns.omega
|
||||
- if full_permissions
|
||||
%select.select2.fullwidth{id: 'receives_notifications_dropdown', name: 'receives_notifications', ng: {model: 'receivesNotifications', init: "receivesNotifications = '#{@enterprise.contact.id}'"}}
|
||||
@@ -34,8 +32,7 @@
|
||||
=f.label :user_ids, t('.managers')
|
||||
- if full_permissions
|
||||
%span.required *
|
||||
%div{'ofn-with-tip' => t('.managers_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.managers_tip')}
|
||||
.eight.columns.omega
|
||||
- if full_permissions
|
||||
%table.managers
|
||||
@@ -66,8 +63,7 @@
|
||||
.three.columns.alpha
|
||||
%label
|
||||
= t('.invite_manager')
|
||||
%div{'ofn-with-tip' => t('.invite_manager_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('.invite_manager_tip')}
|
||||
.eight.columns.omega
|
||||
.row
|
||||
%a.button{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "invite-manager-modal" }
|
||||
@@ -76,22 +72,4 @@
|
||||
-# add to admin footer to avoid nesting invitation form inside enterprise form
|
||||
- content_for :admin_footer do
|
||||
= render HelpModalComponent.new(id: "invite-manager-modal", close_button: false) do
|
||||
%div{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}}
|
||||
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
= t('js.admin.modals.invite_title')
|
||||
|
||||
%p.alert-box.ok{ng: {show: 'invite_success'}}
|
||||
{{invite_success}}
|
||||
|
||||
%p.alert-box.error{ng: {show: 'invite_errors'}}
|
||||
{{invite_errors}}
|
||||
|
||||
%input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}}
|
||||
|
||||
.margin-bottom-20.text-center
|
||||
%button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}}
|
||||
= t('js.admin.modals.invite')
|
||||
%button.text-center.margin-top-10{"data-action": "click->help-modal#close", ng: {show: 'invite_success', click: 'resetModal();'}}
|
||||
= t('js.admin.modals.close')
|
||||
= render partial: 'admin/enterprises/form/add_new_unregistered_manager', locals: { error: nil, success: nil }
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%fieldset.no-border-bottom.print-hidden
|
||||
%legend{ align: 'center'}= t(:report_filters)
|
||||
= render partial: "admin/reports/filters/#{@report_type}", locals: { f: f }
|
||||
- if @report_subtype && lookup_context.exists?(@report_subtype, "admin/reports/filters/", true)
|
||||
- if @report_subtype && lookup_context.exists?(@report_subtype, "admin/reports/filters", true)
|
||||
= render partial: "admin/reports/filters/#{@report_subtype}", locals: { f: f }
|
||||
|
||||
%fieldset.print-hidden
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
%span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } }
|
||||
{{ enterprise.name }}
|
||||
|
||||
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('images',about)" } }
|
||||
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('images', about)" } }
|
||||
.row
|
||||
.small-12.columns
|
||||
.alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } }
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
.field
|
||||
%label{ for: 'enterprise_instagram' }= t(".instagram")+":"
|
||||
%input.chunky{ id: 'enterprise_instagram', placeholder: "{{'registration.steps.social.instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } }
|
||||
%span.error.small-12.columns#instagram-error{ style: "display: none" }
|
||||
|
||||
.row.buttons
|
||||
.small-12.columns
|
||||
|
||||
@@ -76,8 +76,8 @@
|
||||
- display_ship_address = false
|
||||
- ship_method_description = nil
|
||||
|
||||
- selected_shipping_method ||= @allowed_shipping_methods[0].id if @allowed_shipping_methods.length == 1
|
||||
- @allowed_shipping_methods.each do |shipping_method|
|
||||
- selected_shipping_method ||= @shipping_methods[0].id if @shipping_methods.length == 1
|
||||
- @shipping_methods.each do |shipping_method|
|
||||
- ship_method_is_selected = shipping_method.id == selected_shipping_method.to_i
|
||||
%div.checkout-input.checkout-input-radio
|
||||
= fields_for shipping_method do |shipping_method_form|
|
||||
|
||||
@@ -18,22 +18,28 @@
|
||||
%input.red{ type: "button", value: "Save Changes", ng: { click: "submit()", disabled: "!bulk_order_form.$dirty" } }
|
||||
%legend{ align: 'center'}= t(:search)
|
||||
%div{ :class => "sixteen columns alpha" }
|
||||
.filter_select{ :class => "four columns" }
|
||||
.quick_search.three.columns.alpha
|
||||
%label{ for: 'quick_filter' }
|
||||
%br
|
||||
%input.quick-search.fullwidth{ ng: {model: 'query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchResults()" }
|
||||
.one.columns
|
||||
|
||||
.filter_select{ :class => "three columns" }
|
||||
%label{ :for => 'supplier_filter' }
|
||||
= t("admin.producer")
|
||||
%br
|
||||
%input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter' } }
|
||||
.filter_select{ :class => "four columns" }
|
||||
.filter_select{ :class => "three columns" }
|
||||
%label{ :for => 'distributor_filter' }
|
||||
= t("admin.shop")
|
||||
%br
|
||||
%input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter' } }
|
||||
.filter_select{ :class => "four columns" }
|
||||
.filter_select{ :class => "three columns" }
|
||||
%label{ :for => 'order_cycle_filter' }
|
||||
= t("admin.order_cycle")
|
||||
%br
|
||||
%input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter' } }
|
||||
.date_filter{class: "four columns"}
|
||||
.date_filter{class: "three columns"}
|
||||
%label
|
||||
= t("date_range")
|
||||
%br
|
||||
@@ -97,8 +103,6 @@
|
||||
.clear
|
||||
|
||||
%div{ ng: { hide: 'RequestMonitor.loading || line_items.length == 0' }, style: "display: flex; justify-content: flex-start; column-gap: 10px; margin-bottom: 15px" }
|
||||
%div{ style: "flex-grow: 1" }
|
||||
%input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
|
||||
-# This -20px is a hack to make the dropdowns align properly
|
||||
%div{ style: "margin-right: -20px;" }
|
||||
= render 'admin/shared/bulk_actions_dropdown'
|
||||
@@ -162,7 +166,7 @@
|
||||
= "#{t('admin.price')} (#{Spree::Money.currency_symbol})"
|
||||
%th.actions
|
||||
|
||||
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | filter:quickSearch | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
|
||||
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
|
||||
%td.bulk
|
||||
%input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true }
|
||||
%td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }}
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
|
||||
= t('.resend_confirmation')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" }
|
||||
= t('.send_invoice')
|
||||
%div.menu_item
|
||||
%span.name.invoices-modal{'ng-controller' => 'bulkInvoiceCtrl', 'ng-click' => 'createBulkInvoice()' }
|
||||
= t('.print_invoices')
|
||||
@@ -124,6 +127,11 @@
|
||||
= render ConfirmModalComponent.new(id: "resend_confirmation", confirm_actions: "click->resend-confirmation-email#confirm", controllers: "resend-confirmation-email") do
|
||||
.margin-bottom-30
|
||||
= t('.resend_confirmation_confirm_html')
|
||||
|
||||
= render ConfirmModalComponent.new(id: "send_invoice", confirm_actions: "click->send-invoice#confirm", controllers: "send-invoice") do
|
||||
.margin-bottom-30
|
||||
= t('.send_invoice_confirm_html')
|
||||
|
||||
= render ConfirmModalComponent.new(id: "cancel_orders", confirm_actions: "click->cancel-orders#confirm", controllers: "cancel-orders", message: "spree/admin/orders/messages/cancel_orders") do
|
||||
.margin-bottom-30
|
||||
= t("js.admin.orders.cancel_the_order_html")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
%script{ type: "text/ng-template", id: "account/developer_settings.html" }
|
||||
%h3= t('.title')
|
||||
= render partial: 'api_keys'
|
||||
= render partial: 'webhook_endpoints'
|
||||
|
||||
33
app/views/spree/users/_webhook_endpoints.html.haml
Normal file
33
app/views/spree/users/_webhook_endpoints.html.haml
Normal file
@@ -0,0 +1,33 @@
|
||||
%section{ id: "webhook_endpoints" }
|
||||
%hr
|
||||
%h3= t('.title')
|
||||
%p= t('.description')
|
||||
|
||||
%table{width: "100%"}
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.event_type.header')
|
||||
%th= t('.url.header')
|
||||
%th.actions
|
||||
%tbody
|
||||
-# Existing endpoints
|
||||
- @user.webhook_endpoints.each do |webhook_endpoint|
|
||||
%tr
|
||||
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
|
||||
%td= webhook_endpoint.url
|
||||
%td.actions
|
||||
- if webhook_endpoint.persisted?
|
||||
= button_to account_webhook_endpoint_path(webhook_endpoint), method: :delete,
|
||||
class: "tiny alert no-margin",
|
||||
data: { confirm: I18n.t(:are_you_sure)} do
|
||||
= I18n.t(:delete)
|
||||
|
||||
-# Create new
|
||||
- if @user.webhook_endpoints.empty? # Only one allowed for now.
|
||||
%tr
|
||||
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
|
||||
%td
|
||||
= form_for(@user.webhook_endpoints.build, url: account_webhook_endpoints_path, id: 'new_webhook_endpoint') do |f|
|
||||
= f.url_field :url, placeholder: t('.url.create_placeholder'), required: true, size: 64
|
||||
%td.actions
|
||||
= button_tag t(:create), class: 'button primary tiny no-margin', form: 'new_webhook_endpoint'
|
||||
20
app/webpacker/controllers/bulk_actions_controller.js
Normal file
20
app/webpacker/controllers/bulk_actions_controller.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
// abstract
|
||||
confirm(action) {
|
||||
this.stimulate(action, this.getOrdersIds());
|
||||
}
|
||||
|
||||
// private
|
||||
getOrdersIds() {
|
||||
const checkboxes = document.querySelectorAll(
|
||||
"#listing_orders input[name='order_ids[]']:checked"
|
||||
);
|
||||
return Array.from(checkboxes).map((checkbox) => checkbox.value);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
import BulkActionsController from "./bulk_actions_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
export default class extends BulkActionsController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
const order_ids = [];
|
||||
document
|
||||
.querySelectorAll("#listing_orders input[name='order_ids[]']:checked")
|
||||
.forEach((checkbox) => {
|
||||
order_ids.push(checkbox.value);
|
||||
});
|
||||
|
||||
this.stimulate("ResendConfirmationEmailReflex#confirm", order_ids);
|
||||
super.confirm("BulkActionsInOrdersList#resend_confirmation_email");
|
||||
}
|
||||
}
|
||||
|
||||
11
app/webpacker/controllers/send_invoice_controller.js
Normal file
11
app/webpacker/controllers/send_invoice_controller.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import BulkActionsController from "./bulk_actions_controller";
|
||||
|
||||
export default class extends BulkActionsController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
super.confirm("BulkActionsInOrdersList#send_invoice");
|
||||
}
|
||||
}
|
||||
30
app/webpacker/controllers/terms_and_conditions_controller.js
Normal file
30
app/webpacker/controllers/terms_and_conditions_controller.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
static targets = ["filename", "fileinput"];
|
||||
static values = {
|
||||
message: String,
|
||||
};
|
||||
|
||||
connect() {
|
||||
super.connect();
|
||||
this.fileinputTarget.addEventListener("change", (event) => {
|
||||
this.filenameTarget.innerText = event.target.files[0].name;
|
||||
});
|
||||
}
|
||||
|
||||
remove(event) {
|
||||
let confirmation = confirm(this.messageValue);
|
||||
if (confirmation) {
|
||||
location.hash = "";
|
||||
this.stimulate(
|
||||
"EnterpriseEdit#remove_terms_and_conditions",
|
||||
event.target
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add() {
|
||||
this.fileinputTarget.click();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
|
||||
ENV["NODE_ENV"] ||= "development"
|
||||
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
|
||||
ENV["NODE_ENV"] ||= "development"
|
||||
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
|
||||
@@ -249,7 +249,5 @@ module Openfoodnetwork
|
||||
config.active_storage.variable_content_types += ["image/svg+xml"]
|
||||
|
||||
config.exceptions_app = self.routes
|
||||
|
||||
config.autoloader = :zeitwerk
|
||||
end
|
||||
end
|
||||
|
||||
15
config/initializers/cookie_rotator.rb
Normal file
15
config/initializers/cookie_rotator.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#key-generator-digest-class-changing-to-use-sha256
|
||||
Rails.application.config.after_initialize do
|
||||
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
|
||||
salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
|
||||
secret_key_base = Rails.application.secret_key_base
|
||||
|
||||
key_generator = ActiveSupport::KeyGenerator.new(
|
||||
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
|
||||
)
|
||||
key_len = ActiveSupport::MessageEncryptor.key_len
|
||||
secret = key_generator.generate_key(salt, key_len)
|
||||
|
||||
cookies.rotate :encrypted, secret
|
||||
end
|
||||
end
|
||||
@@ -1,14 +1,5 @@
|
||||
require "flipper"
|
||||
require "flipper/adapters/active_record"
|
||||
require "flipper/instrumentation/log_subscriber"
|
||||
|
||||
Flipper.configure do |config|
|
||||
config.default do
|
||||
adapter = Flipper::Adapters::ActiveRecord.new
|
||||
instrumented = Flipper::Adapters::Instrumented.new(adapter, instrumenter: ActiveSupport::Notifications)
|
||||
Flipper.new(instrumented, instrumenter: ActiveSupport::Notifications)
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.env.production?
|
||||
Flipper::UI.configure do |config|
|
||||
@@ -17,6 +8,4 @@ if Rails.env.production?
|
||||
end
|
||||
end
|
||||
|
||||
Rails.configuration.middleware.use Flipper::Middleware::Memoizer, preload_all: true
|
||||
|
||||
Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? }
|
||||
|
||||
@@ -6,9 +6,13 @@ cy:
|
||||
spree/shipping_method: Dull Anfon
|
||||
attributes:
|
||||
spree/order/ship_address:
|
||||
address2: "Cyfeiriad danfon llinell 2"
|
||||
city: "Tref y cyfeiriad danfon"
|
||||
country: "Gwlad y cyfeiriad danfon"
|
||||
phone: "Rhif ffôn"
|
||||
firstname: "Enw cyntaf"
|
||||
lastname: "Enw olaf"
|
||||
zipcode: "Cod post y cyfeiriad danfon"
|
||||
spree/user:
|
||||
password: "Cyfrinair"
|
||||
password_confirmation: "Cadarnhau'r cyfrinair"
|
||||
@@ -1321,6 +1325,7 @@ cy:
|
||||
addresses: Cyfeiriadau
|
||||
payment_methods: Adroddiad Dulliau Talu
|
||||
delivery: Adroddiad Cyflenwi
|
||||
sales_tax_totals_by_producer: Cyfanswm Trethi Gwerthu fesul Cynhyrchwr
|
||||
tax_types: Mathau o dreth
|
||||
tax_rates: Cyfraddau Treth
|
||||
pack_by_customer: Pecyn fesul Cwsmer
|
||||
@@ -1810,6 +1815,7 @@ cy:
|
||||
message_html: "Mae gennych chi archeb ar gyfer y cylch archebu hwn eisoes. Gwiriwch y %{cart} i weld yr eitemau a archebwyd o'r blaen. Gallwch hefyd ganslo eitemau cyn belled â bod y cylch archebu ar agor."
|
||||
step1:
|
||||
contact_information:
|
||||
title: Manylion cyswllt
|
||||
email:
|
||||
label: E-bost
|
||||
phone:
|
||||
@@ -1863,10 +1869,13 @@ cy:
|
||||
title: Manylion dosbarthu
|
||||
edit: Golygu
|
||||
address: Cyfeiriad dosbarthu
|
||||
instructions: Cyfarwyddiadau
|
||||
payment_method:
|
||||
title: Dull talu
|
||||
edit: Golygu
|
||||
instructions: Cyfarwyddiadau
|
||||
order:
|
||||
title: Manylion yr archeb
|
||||
edit: Golygu
|
||||
terms_and_conditions:
|
||||
message_html: "Rwy'n cytuno i %{terms_and_conditions_link} y gwerthwr."
|
||||
@@ -1879,6 +1888,7 @@ cy:
|
||||
submit: Cwblhau'r archeb
|
||||
cancel: Nôl at y Dull Talu
|
||||
errors:
|
||||
saving_failed: "Wedi methu cadw, llenwch y meysydd a amlygwyd. %{messages}"
|
||||
terms_not_accepted: Mae angen ichi dderbyn y Telerau ac Amodau
|
||||
required: Ni chaniateir gadael maes yn wag
|
||||
invalid_number: "Noder rhif ffôn dilys os gwelwch yn dda"
|
||||
@@ -2661,6 +2671,8 @@ cy:
|
||||
report_header_tax_on_delivery: "Treth ar Gyflenwi (%{currency_symbol})"
|
||||
report_header_tax_on_fees: "Treth ar Ffioedd (%{currency_symbol})"
|
||||
report_header_tax_category: "Categori Treth"
|
||||
report_header_tax_rate_name: "Enw’r Gyfradd Dreth"
|
||||
report_header_tax_rate: "Cyfradd Dreth"
|
||||
report_header_total_tax: "Cyfanswm Treth (%{currency_symbol})"
|
||||
report_header_total_excl_tax: "Cyfanswm heb dreth (%{currency_symbol})"
|
||||
report_header_total_incl_tax: "Cyfanswm gan gynnwys treth (%{currency_symbol})"
|
||||
@@ -2683,6 +2695,7 @@ cy:
|
||||
report_header_supplier: Cyflenwr
|
||||
report_header_producer: Cynhyrchydd
|
||||
report_header_producer_suburb: Maestref y Cynhyrchydd
|
||||
report_header_producer_tax_status: Statws Treth y Cynhyrchwr
|
||||
report_header_producer_charges_sales_tax?: Cofrestrwyd ar gyfer GST/TAW
|
||||
report_header_unit: Uned
|
||||
report_header_group_buy_unit_quantity: Nifer Unedau Grŵp Prynu
|
||||
@@ -2699,10 +2712,12 @@ cy:
|
||||
report_header_distributor_address: Cyfeiriad dosbarthwr
|
||||
report_header_distributor_city: Dinas ddosbarthu
|
||||
report_header_distributor_postcode: Cod post dosbarthwr
|
||||
report_header_distributor_tax_status: Statws Treth y Dosbarthwr
|
||||
report_header_delivery_address: Cyfeiriad Dosbarthu
|
||||
report_header_delivery_postcode: Cod Post Cyflenwi
|
||||
report_header_bulk_unit_size: Maint Uned Swmp
|
||||
report_header_weight: Pwysau
|
||||
report_header_final_weight_volume: Terfynol (Pwysau/Cyfaint)
|
||||
report_header_height: Uchder
|
||||
report_header_width: Lled
|
||||
report_header_depth: Dyfnder
|
||||
@@ -2879,6 +2894,7 @@ cy:
|
||||
deleting_item_will_cancel_order: "Bydd y weithred hon yn golygu un neu fwy o archebion gwag, fydd yn cael eu canslo. Ydych chi'n siwr eich bod am wneud hyn?"
|
||||
modals:
|
||||
got_it: "Yn deall"
|
||||
confirm: "Cadarnhau"
|
||||
close: "Cau"
|
||||
continue: "Parhau"
|
||||
cancel: "Canslo"
|
||||
@@ -3452,6 +3468,8 @@ cy:
|
||||
server_error: "Nam gyda'r gweinydd"
|
||||
shipping_method_names:
|
||||
UPS Ground: "UPS dros y tir"
|
||||
pick_up: "Casglu ar y fferm"
|
||||
delivery: "Llofnodwyd, cadarnhawyd a chyflenwyd"
|
||||
start_date: "Dyddiad cychwyn"
|
||||
successfully_removed: "Llwyddwyd i ddileu"
|
||||
taxonomy_edit: "Golygu tacsonomi"
|
||||
@@ -3536,6 +3554,7 @@ cy:
|
||||
display_currency: "Arddangos arian cyfred"
|
||||
choose_currency: "Dewis Arian Cyfred"
|
||||
mail_method_settings: "Gosodiadau Dull Postio"
|
||||
mail_settings_notice_html: "<b>Bydd unrhyw newid a wneir fan hyn yn un dros dro at ddiben dadfygio’n unig</b>, a hwyrach y caiff ei droi yn ôl yn y dyfodol. <br>Gellir gwneud newidiadau parhaol trwy ddiweddaru cyfrinachau’r achos a’u cyflenwi trwy ddefnyddio <a href='https://github.com/openfoodfoundation/ofn-install'>ofn-install</a>. Cysylltwch â thîm byd-eang OFN am fanylion pellach."
|
||||
general: "Cyffredinol"
|
||||
enable_mail_delivery: "Galluogi Dosbarthu trwy'r Post"
|
||||
send_mails_as: "Anfon eitemau drwy'r Post fel"
|
||||
@@ -3769,6 +3788,7 @@ cy:
|
||||
print_invoices: "Argraffu anfonebau"
|
||||
cancel_orders: "Canslo Archebion"
|
||||
resend_confirmation: "Ailanfon Cadarnhad "
|
||||
resend_confirmation_confirm_html: "Bydd hwn yn ail-anfon yr ebost cadarnhau at y cwsmer. <br />Ydych chi’n siŵr eich bod am wneud hyn?"
|
||||
selected:
|
||||
zero: "Ni ddewiswyd archeb."
|
||||
one: "Dewiswyd 1 archeb"
|
||||
@@ -3917,6 +3937,7 @@ cy:
|
||||
title: "Cynnyrch Newydd"
|
||||
new_product: "Cynnyrch Newydd"
|
||||
supplier: "Cyflenwr"
|
||||
supplier_select_placeholder: "Dewis cyflenwr"
|
||||
product_name: "Enw Cynnyrch"
|
||||
units: "Maint yr Uned"
|
||||
value: "Gwerth"
|
||||
@@ -4340,6 +4361,7 @@ cy:
|
||||
search_input:
|
||||
placeholder: Chwilio
|
||||
selector_with_filter:
|
||||
selected_items: "Dewiswyd %{count}"
|
||||
search_placeholder: Chwilio
|
||||
pagination:
|
||||
next: Nesaf
|
||||
|
||||
@@ -936,6 +936,7 @@ en:
|
||||
legend: "Address"
|
||||
business_details:
|
||||
legend: "Business Details"
|
||||
upload: 'upload'
|
||||
abn: ABN
|
||||
abn_placeholder: eg. 99 123 456 789
|
||||
acn: ACN
|
||||
@@ -1602,6 +1603,9 @@ en:
|
||||
resend_confirmation_emails_feedback:
|
||||
one: "Confirmation email sent for 1 order."
|
||||
other: "Confirmation emails sent for %{count} orders."
|
||||
send_invoice_feedback:
|
||||
one: "Invoice email sent for 1 order."
|
||||
other: "Invoice emails sent for %{count} orders."
|
||||
|
||||
# API
|
||||
#
|
||||
@@ -1621,6 +1625,11 @@ en:
|
||||
destroy_attachment_does_not_exist: "Terms and Conditions file does not exist"
|
||||
orders:
|
||||
failed_to_update: "Failed to update order"
|
||||
query_param:
|
||||
error:
|
||||
title: Invalid query parameter
|
||||
extra_fields: "Unsupported fields: %{fields}"
|
||||
|
||||
|
||||
# Frontend views
|
||||
#
|
||||
@@ -3565,6 +3574,14 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
previous: "Previous"
|
||||
last: "Last"
|
||||
|
||||
webhook_endpoints:
|
||||
create:
|
||||
success: Webhook endpoint successfully created
|
||||
error: Webhook endpoint failed to create
|
||||
destroy:
|
||||
success: Webhook endpoint successfully deleted
|
||||
error: Webhook endpoint failed to delete
|
||||
|
||||
spree:
|
||||
order_updated: "Order Updated"
|
||||
add_country: "Add country"
|
||||
@@ -3945,6 +3962,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
cancel_orders: "Cancel Orders"
|
||||
resend_confirmation: "Resend Confirmation"
|
||||
resend_confirmation_confirm_html: "This will resend the confirmation email to the customer.<br />Are you sure you want to proceed?"
|
||||
send_invoice: "Send Invoices"
|
||||
send_invoice_confirm_html: "This will email customer invoices for all selected complete orders. <br>Are you sure you want to proceed?"
|
||||
selected:
|
||||
zero: "No order selected"
|
||||
one: "1 order selected"
|
||||
@@ -4360,6 +4379,16 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
api_keys:
|
||||
regenerate_key: "Regenerate Key"
|
||||
title: API key
|
||||
webhook_endpoints:
|
||||
title: Webhook Endpoints
|
||||
description: Events in the system may trigger webhooks to external systems.
|
||||
event_types:
|
||||
order_cycle_opened: Order Cycle Opened
|
||||
event_type:
|
||||
header: Event type
|
||||
url:
|
||||
header: Endpoint URL
|
||||
create_placeholder: Enter the URL of the remote webhook endpoint
|
||||
developer_settings:
|
||||
title: Developer Settings
|
||||
form:
|
||||
|
||||
@@ -6,12 +6,22 @@ en_CA:
|
||||
spree/shipping_method: Shipping/Pick-up Method
|
||||
attributes:
|
||||
spree/order/ship_address:
|
||||
address1: "Shipping address (House number + Street)"
|
||||
address2: "Shipping address line 2"
|
||||
city: "Shipping address city"
|
||||
country: "Shipping address country"
|
||||
phone: "Phone number"
|
||||
firstname: "First name"
|
||||
lastname: "Last name"
|
||||
zipcode: "Shipping address postal code"
|
||||
spree/order/bill_address:
|
||||
address1: "Billing address (House number & Street)"
|
||||
zipcode: "Billing address Postal Code"
|
||||
city: "Billing address City"
|
||||
country: "Billing address country"
|
||||
firstname: "Billing address first name"
|
||||
lastname: "Billing address last name"
|
||||
phone: Customer phone
|
||||
spree/user:
|
||||
password: "Password"
|
||||
password_confirmation: "Password confirmation"
|
||||
@@ -1313,6 +1323,7 @@ en_CA:
|
||||
total_by_customer: Total by Customer
|
||||
total_by_supplier: Total By Supplier
|
||||
supplier_totals: Order Cycle Supplier Totals
|
||||
percentage: "%{value} %"
|
||||
supplier_totals_by_distributor: Order Cycle Supplier Totals by Distributor
|
||||
totals_by_supplier: Order Cycle Distributor Totals by Supplier
|
||||
customer_totals: Order Cycle Customer Totals
|
||||
@@ -1323,6 +1334,8 @@ en_CA:
|
||||
addresses: Addresses
|
||||
payment_methods: Payment Methods Report
|
||||
delivery: Delivery Report
|
||||
sales_tax_totals_by_producer: Sales Tax Totals by Producer
|
||||
sales_tax_totals_by_order: Sales Tax Totals by Order
|
||||
tax_types: Tax Types
|
||||
tax_rates: Tax Rates
|
||||
pack_by_customer: Pack By Customer
|
||||
@@ -2671,6 +2684,7 @@ en_CA:
|
||||
report_header_tax_on_delivery: "Tax on Delivery (%{currency_symbol})"
|
||||
report_header_tax_on_fees: "Tax on Fees (%{currency_symbol})"
|
||||
report_header_tax_category: "Tax Category"
|
||||
report_header_tax_rate_name: "Tax Rate Name"
|
||||
report_header_tax_rate: "Tax Rate"
|
||||
report_header_total_tax: "Total Tax (%{currency_symbol})"
|
||||
report_header_total_excl_tax: "Total excl. tax (%{currency_symbol})"
|
||||
@@ -2694,6 +2708,7 @@ en_CA:
|
||||
report_header_supplier: Supplier
|
||||
report_header_producer: Producer
|
||||
report_header_producer_suburb: Producer City/Town
|
||||
report_header_producer_tax_status: Producer Tax Status
|
||||
report_header_producer_charges_sales_tax?: Tax registered
|
||||
report_header_unit: Unit
|
||||
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
|
||||
@@ -2710,6 +2725,7 @@ en_CA:
|
||||
report_header_distributor_address: Distributor address
|
||||
report_header_distributor_city: Distributor city
|
||||
report_header_distributor_postcode: Distributor postal code
|
||||
report_header_distributor_tax_status: Distributor Tax Status
|
||||
report_header_delivery_address: Delivery Address
|
||||
report_header_delivery_postcode: Delivery Postal Code
|
||||
report_header_bulk_unit_size: Bulk Unit Size
|
||||
@@ -3083,6 +3099,9 @@ en_CA:
|
||||
cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer."
|
||||
restock_item: "Restock Items: Return this item to stock"
|
||||
restock_items: "Restock Items: Return all items to stock"
|
||||
delete_line_items_html:
|
||||
one: "This will delete one line item from the order. <br />Are you sure you want to proceed?"
|
||||
other: "This will delete %{count} line items from the order.<br />Are you sure you want to proceed?"
|
||||
resend_user_email_confirmation:
|
||||
resend: "Resend"
|
||||
sending: "Resend..."
|
||||
|
||||
@@ -1520,6 +1520,10 @@ fr:
|
||||
one: "Emails envoyés pour 1 commande."
|
||||
many: "Emails envoyés pour %{count} commandes."
|
||||
other: "Emails envoyés pour %{count} commandes."
|
||||
send_invoice_feedback:
|
||||
one: "Emails envoyés pour 1 commande."
|
||||
many: "Emails envoyés pour %{count} commandes."
|
||||
other: "Emails envoyés pour %{count} commandes."
|
||||
api:
|
||||
unknown_error: "Quelque chose n'a pas fonctionné. Notre équipe a été notifiée."
|
||||
invalid_api_key: "La clé API (%{key}) n'est pas valide."
|
||||
@@ -1536,6 +1540,10 @@ fr:
|
||||
destroy_attachment_does_not_exist: "Aucun fichier"
|
||||
orders:
|
||||
failed_to_update: "La mise à jour de la commande échoué"
|
||||
query_param:
|
||||
error:
|
||||
title: Paramètre de la requête invalide
|
||||
extra_fields: "Champs invalides : %{fields}"
|
||||
checkout:
|
||||
already_ordered:
|
||||
cart: "panier"
|
||||
@@ -3128,6 +3136,10 @@ fr:
|
||||
cancel_the_order_send_cancelation_email: "Envoyer un email d'annulation à l'acheteur"
|
||||
restock_item: "Recharger le stock de ce produit"
|
||||
restock_items: "Recharger le stock de tous les produits"
|
||||
delete_line_items_html:
|
||||
one: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
|
||||
many: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
|
||||
other: "Cette action va annuler la commande. <br /> Etes-vous sûr de vouloir continuer ?"
|
||||
resend_user_email_confirmation:
|
||||
resend: "Renvoyer"
|
||||
sending: "Renvoi...."
|
||||
@@ -3444,6 +3456,13 @@ fr:
|
||||
first: "Début"
|
||||
previous: "Précédent"
|
||||
last: "Fin"
|
||||
webhook_endpoints:
|
||||
create:
|
||||
success: Le webhook a bien été créé.
|
||||
error: Le webhook n'a pas pu être créé.
|
||||
destroy:
|
||||
success: Le webhook a bien été supprimé.
|
||||
error: Le webhook n'a pas pu être supprimé.
|
||||
spree:
|
||||
add_country: "Ajouter un pays"
|
||||
add_state: "Ajouter un département"
|
||||
@@ -3805,6 +3824,8 @@ fr:
|
||||
cancel_orders: "Annuler les commandes"
|
||||
resend_confirmation: "Renvoyer la confirmation"
|
||||
resend_confirmation_confirm_html: "Cette action va renvoyer l'email de confirmation de commande. Etes-vous sûr de vouloir continuer ?"
|
||||
send_invoice: "Envoyer les factures"
|
||||
send_invoice_confirm_html: "Vous allez envoyer les factures clients pour toutes les commandes sélectionnées. <br>Souhaitez-vous continuer ?"
|
||||
selected:
|
||||
zero: "Aucune commande sélectionnée"
|
||||
one: "1 commande sélectionnée"
|
||||
@@ -4221,6 +4242,16 @@ fr:
|
||||
api_keys:
|
||||
regenerate_key: "Regénérer la clé"
|
||||
title: Clé API
|
||||
webhook_endpoints:
|
||||
title: 'Webhook '
|
||||
description: Les événements dans le système peuvent déclencher des webhooks vers des systèmes externes.
|
||||
event_types:
|
||||
order_cycle_opened: Cycle de vente ouvert
|
||||
event_type:
|
||||
header: Type d'évènement
|
||||
url:
|
||||
header: URL du point de terminaison
|
||||
create_placeholder: Entrez l'URL du point de terminaison du webhook
|
||||
developer_settings:
|
||||
title: Paramètres développeurs
|
||||
form:
|
||||
|
||||
@@ -6,9 +6,22 @@ fr_CA:
|
||||
spree/shipping_method: Option d'expédition
|
||||
attributes:
|
||||
spree/order/ship_address:
|
||||
address1: "Adresse de livraison (Numéro et Rue)"
|
||||
address2: "Adresse (Numéro et rue)"
|
||||
city: "Adresse de livraison - Ville"
|
||||
country: "Pays"
|
||||
phone: "Numéro de téléphone"
|
||||
firstname: "Prénom"
|
||||
lastname: "Nom de famille"
|
||||
zipcode: "Code postal"
|
||||
spree/order/bill_address:
|
||||
address1: "Adresse de facturation (Numéro et rue)"
|
||||
zipcode: "Adresse de facturation - Code postal"
|
||||
city: "Adresse de facturation - Ville"
|
||||
country: "Adresse de facturation - Pays"
|
||||
firstname: "Adresse de facturation - Prénom"
|
||||
lastname: "Adresse de facturation - Nom"
|
||||
phone: Téléphone
|
||||
spree/user:
|
||||
password: "Mot de passe"
|
||||
password_confirmation: "Confirmation du mot de passe"
|
||||
@@ -409,8 +422,11 @@ fr_CA:
|
||||
filters:
|
||||
categories:
|
||||
title: Conditions de transport
|
||||
selected_categories: "%{count} catégories sélectionnées"
|
||||
producers:
|
||||
title: Producteurs
|
||||
selected_producers: "%{count} producteurs sélectionnés"
|
||||
per_page: "%{count} éléments par page"
|
||||
colums: Colonnes
|
||||
columns:
|
||||
name: Nom
|
||||
@@ -666,6 +682,7 @@ fr_CA:
|
||||
not_found: n'a pas été trouvé dans la base de donnée
|
||||
category_not_found: n'est pas conforme aux catégories utilisées. Merci de modifier les catégories en utilisant celles listées sur la page d'import ou vérifier qu'il n'y ait pas de faute de frappe ou d'espace à fin du mot.
|
||||
not_updatable: ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits
|
||||
values_must_be_same: doit être identique pour les produits avec un nom identique
|
||||
blank: Champ obligatoire
|
||||
products_no_permission: vous n'avez pas les droits requis pour gérer les produits de cette entreprise
|
||||
inventory_no_permission: Vous n'avez pas la permission de créer un catalogue boutique pour ce producteur
|
||||
@@ -1309,6 +1326,7 @@ fr_CA:
|
||||
total_by_customer: Total par acheteur
|
||||
total_by_supplier: Total par producteur
|
||||
supplier_totals: Totaux Cycle de Vente par Producteur
|
||||
percentage: "%{value}%"
|
||||
supplier_totals_by_distributor: Totaux Cycle de Vente par Producteur pour chaque Hub Distributeur
|
||||
totals_by_supplier: Totaux Cycle de Vente par Hub Distributeur pour chaque Producteur
|
||||
customer_totals: Totaux Cycle de Vente par Acheteur
|
||||
@@ -1319,6 +1337,8 @@ fr_CA:
|
||||
addresses: Adresses
|
||||
payment_methods: Rapport Méthodes de Paiement
|
||||
delivery: Rapport de Livraison
|
||||
sales_tax_totals_by_producer: Détail des montants de taxes par producteur
|
||||
sales_tax_totals_by_order: Détail des montants de taxes par commande
|
||||
tax_types: Type de taxe
|
||||
tax_rates: Taux de taxe
|
||||
pack_by_customer: Préparation des commandes par Acheteur
|
||||
@@ -1497,6 +1517,10 @@ fr_CA:
|
||||
stripe_connect_fail: Désolé, la connexion de votre compte Stripe a échoué :-(
|
||||
stripe_connect_settings:
|
||||
resource: Configuration de Stripe Connect
|
||||
resend_confirmation_emails_feedback:
|
||||
one: "Emails envoyés pour 1 commande."
|
||||
many: "Emails envoyés pour %{count} commandes"
|
||||
other: "Emails envoyés pour %{count} commandes."
|
||||
api:
|
||||
unknown_error: "Quelque chose n'a pas fonctionné. Notre équipe a été notifiée."
|
||||
invalid_api_key: " La clé API (%{key}) n'est pas valide."
|
||||
@@ -1807,6 +1831,7 @@ fr_CA:
|
||||
message_html: "Vous avez déjà passé une commande pour ce cycle de vente. Vérifiez votre %{cart} pour voir les produits commandés. Vous pouvez annuler ou modifier votre commande jusqu'à la fermeture du cycle de vente."
|
||||
step1:
|
||||
contact_information:
|
||||
title: Contact
|
||||
email:
|
||||
label: Email
|
||||
phone:
|
||||
@@ -1860,10 +1885,13 @@ fr_CA:
|
||||
title: Détails de livraison
|
||||
edit: Modifier
|
||||
address: Adresse de livraison
|
||||
instructions: Instructions
|
||||
payment_method:
|
||||
title: Méthode de paiement
|
||||
edit: Modifier
|
||||
instructions: Instructions
|
||||
order:
|
||||
title: Total commande
|
||||
edit: Modifier
|
||||
terms_and_conditions:
|
||||
message_html: "J'accepte les %{terms_and_conditions_link}"
|
||||
@@ -1876,6 +1904,7 @@ fr_CA:
|
||||
submit: Valider ma commande
|
||||
cancel: Retour à la méthode de paiement
|
||||
errors:
|
||||
saving_failed: "La sauvegarde n'a pas fonctionné, veuillez mettre à jour les champs en rouge.%{messages}"
|
||||
terms_not_accepted: Merci d'accepter les CGU & CGV.
|
||||
required: Ce champ ne peut pas être vide
|
||||
invalid_number: "Merci de renseigner un numéro de téléphone valide"
|
||||
@@ -2576,6 +2605,7 @@ fr_CA:
|
||||
report_customers_cycle: "Cycle de vente"
|
||||
report_customers_type: "Type de rapport"
|
||||
report_customers_csv: "Télécharger en csv"
|
||||
report_customers: Acheteur
|
||||
report_producers: "Producteurs"
|
||||
report_type: "Type de rapport"
|
||||
report_hubs: "Hubs"
|
||||
@@ -2657,6 +2687,7 @@ fr_CA:
|
||||
report_header_tax_on_delivery: "Taxe sur livraison (%{currency_symbol})"
|
||||
report_header_tax_on_fees: "Taxe sur commission hub (%{currency_symbol})"
|
||||
report_header_tax_category: "Type de taxe"
|
||||
report_header_tax_rate_name: "Taxe"
|
||||
report_header_tax_rate: "Taxe applicable"
|
||||
report_header_total_tax: "Total Taxe (%{currency_symbol})"
|
||||
report_header_total_excl_tax: "Total HT (%{currency_symbol})"
|
||||
@@ -2667,6 +2698,7 @@ fr_CA:
|
||||
report_header_customer_code: Code acheteur
|
||||
report_header_product: Produit
|
||||
report_header_product_properties: Propriétés / labels Produits
|
||||
report_header_product_tax_category: Taxe applicable
|
||||
report_header_quantity: Nb commandé
|
||||
report_header_max_quantity: Quantité Max
|
||||
report_header_variant: Variante
|
||||
@@ -2679,6 +2711,8 @@ fr_CA:
|
||||
report_header_supplier: Fournisseur
|
||||
report_header_producer: Producteur
|
||||
report_header_producer_suburb: Ville Producteur
|
||||
report_header_producer_tax_status: Soumis à la taxe
|
||||
report_header_producer_charges_sales_tax?: Soumis à la GST
|
||||
report_header_unit: Unité
|
||||
report_header_group_buy_unit_quantity: Nb d'unités achetées (vente par lots)
|
||||
report_header_cost: Coût
|
||||
@@ -2694,10 +2728,12 @@ fr_CA:
|
||||
report_header_distributor_address: Adresse Hub Distributeur
|
||||
report_header_distributor_city: Ville Distributeur
|
||||
report_header_distributor_postcode: Code Postal Distributeur
|
||||
report_header_distributor_tax_status: Statut de la boutique
|
||||
report_header_delivery_address: Adresse Livraison
|
||||
report_header_delivery_postcode: Code Postal Livraison
|
||||
report_header_bulk_unit_size: Quantité totale du lot
|
||||
report_header_weight: Poids
|
||||
report_header_final_weight_volume: Poids ou volume livré
|
||||
report_header_height: Hauteur
|
||||
report_header_width: Largeur
|
||||
report_header_depth: Profondeur
|
||||
@@ -2876,6 +2912,7 @@ fr_CA:
|
||||
deleting_item_will_cancel_order: "Cette opération va rendre une ou plusieurs commandes vides, sans aucun produit. Elles vont ainsi être annulées. Souhaitez-vous continuer ?"
|
||||
modals:
|
||||
got_it: "J'ai compris"
|
||||
confirm: "Valider"
|
||||
close: "Fermer"
|
||||
continue: "Suivant"
|
||||
cancel: "Annuler"
|
||||
@@ -3432,6 +3469,8 @@ fr_CA:
|
||||
server_error: "Erreur serveur"
|
||||
shipping_method_names:
|
||||
UPS Ground: "UPS Ground"
|
||||
pick_up: "Retrait"
|
||||
delivery: "Signé, scellé, livré"
|
||||
start_date: "Date de début"
|
||||
successfully_removed: "Supprimé avec succès"
|
||||
taxonomy_edit: "Modifier la taxonomie"
|
||||
@@ -3516,6 +3555,7 @@ fr_CA:
|
||||
display_currency: "Afficher la devise"
|
||||
choose_currency: "Choisir la devise"
|
||||
mail_method_settings: "Paramètre méthode mail"
|
||||
mail_settings_notice_html: "<b>Les modifications apportées ici seront temporaires</b>et peuvent changer à la prochaine mise à jour. <br> Si vous souhaitez réaliser des changements permanents, un administrateur système doit se charger de mettre à jour les informations et provisionner le serveur en utilisant<a href='https://github.com/openfoodfoundation/ofn-install'> ofn-install </a>."
|
||||
general: "Général"
|
||||
enable_mail_delivery: "Permettre distribution des mails"
|
||||
send_mails_as: "Envoyer les mails en tant que"
|
||||
@@ -3638,6 +3678,7 @@ fr_CA:
|
||||
messages:
|
||||
included_price_validation: "Ce cycle de vente a déjà été utilisé par un acheteur et ne peut être supprimé. Pour empêcher aux acheteurs d'y accéder, veuillez plutôt le fermer."
|
||||
blank: "Champ obligatoire"
|
||||
invalid_instagram_url: "Uniquement le nom d'utilisateur / identifiant par ex. le_prof"
|
||||
layouts:
|
||||
admin:
|
||||
login_nav:
|
||||
@@ -3748,6 +3789,7 @@ fr_CA:
|
||||
print_invoices: "Imprimer les factures"
|
||||
cancel_orders: "Annuler les commandes"
|
||||
resend_confirmation: "Renvoyer la confirmation"
|
||||
resend_confirmation_confirm_html: "Cette action va renvoyer l'email de confirmation de commande. Etes-vous sûr de vouloir continuer ?"
|
||||
selected:
|
||||
zero: "Aucune commande sélectionnée"
|
||||
one: "1 commande sélectionnée"
|
||||
@@ -3896,6 +3938,7 @@ fr_CA:
|
||||
title: "Nouveau Produit"
|
||||
new_product: "Nouveau Produit"
|
||||
supplier: "Fournisseur"
|
||||
supplier_select_placeholder: "Sélectionner un producteur"
|
||||
product_name: "Nom du Produit"
|
||||
units: "Unité de mesure"
|
||||
value: "Nb unités"
|
||||
@@ -3941,6 +3984,9 @@ fr_CA:
|
||||
select_and_search: "Sélectionner les filtres et cliquez sur %{option} pour accéder aux données."
|
||||
customer_names_message:
|
||||
customer_names_tip: "Si les noms des acheteurs sont masqué, vous pouvez contacter le gestionnaire de la boutique. Il pourra vous donner accès à cette information."
|
||||
products_and_inventory:
|
||||
all_products:
|
||||
message: "Attention les stocks correspondent aux stocks producteurs et non au stock du catalogue boutique."
|
||||
users:
|
||||
index:
|
||||
listing_users: "Liste des utilisateurs"
|
||||
@@ -4304,6 +4350,7 @@ fr_CA:
|
||||
search_input:
|
||||
placeholder: Chercher
|
||||
selector_with_filter:
|
||||
selected_items: "%{count} sélectionné"
|
||||
search_placeholder: Chercher
|
||||
pagination:
|
||||
next: Suivant
|
||||
|
||||
@@ -796,7 +796,7 @@ hu:
|
||||
order_date: "Elkészült:"
|
||||
max: "Max"
|
||||
product_unit: "Termék: Egység"
|
||||
weight_volume: "Súly/térfogat (g)"
|
||||
weight_volume: "Súly/térfogat (kg)"
|
||||
ask: "Kérdez?"
|
||||
page_title: "Tömeges megrendelés kezelése"
|
||||
actions_delete: "Kiválasztottak törlése"
|
||||
@@ -1311,9 +1311,9 @@ hu:
|
||||
delivery: Kézbesítési jelentés
|
||||
tax_types: Adótípusok
|
||||
tax_rates: Adókulcsok
|
||||
pack_by_customer: Csomagolás Ügyfél által
|
||||
pack_by_supplier: Csomagolás Szállító által
|
||||
pack_by_product: Csomagolás Termékenként
|
||||
pack_by_customer: Összekészítés Ügyfelenként
|
||||
pack_by_supplier: Összekészítés Szállítónként
|
||||
pack_by_product: Összekészítés Termékenként
|
||||
revenues_by_hub:
|
||||
name: Bevételek Átvételi pont szerint
|
||||
description: Bevételek átvételi pont szerint
|
||||
@@ -1355,7 +1355,7 @@ hu:
|
||||
table:
|
||||
select_and_search: "Válaszd ki a szűrőket, és kattintson a %{option} elemre az adatok eléréséhez."
|
||||
headings:
|
||||
hub: "Kerékagy"
|
||||
hub: "Átadópont"
|
||||
customer_code: "Kód"
|
||||
first_name: "Keresztnév"
|
||||
last_name: "Vezetéknév"
|
||||
@@ -1363,8 +1363,8 @@ hu:
|
||||
product: "Termék"
|
||||
variant: "Változat"
|
||||
quantity: "Mennyiség"
|
||||
is_temperature_controlled: "TempControlled?"
|
||||
temp_controlled: "TempControlled?"
|
||||
is_temperature_controlled: "Hűtést igényel?"
|
||||
temp_controlled: "Hűtést igényel?"
|
||||
price: "Ár"
|
||||
rendering_options:
|
||||
generate_report: "Jelentést készít"
|
||||
@@ -1376,7 +1376,7 @@ hu:
|
||||
raw_data: Nyers adatok
|
||||
formatted_data: Formázott adatok
|
||||
packing:
|
||||
name: "Csomagolási jelentések"
|
||||
name: "Összekészítési jelentések"
|
||||
subscriptions:
|
||||
index:
|
||||
title: "Előfizetések"
|
||||
@@ -2469,7 +2469,7 @@ hu:
|
||||
loading_customers: "Ügyfelek betöltése"
|
||||
no_customers_found: "Nem található ügyfelek"
|
||||
go: "Megy"
|
||||
hub: "Kerékagy"
|
||||
hub: "Átadópont"
|
||||
producer: "Termelő"
|
||||
product: "Termék"
|
||||
price: "Ár"
|
||||
@@ -2588,7 +2588,7 @@ hu:
|
||||
report_header_address: Cím
|
||||
report_header_billing_address: számlázási cím
|
||||
report_header_relationship: Kapcsolat
|
||||
report_header_hub: Kerékagy
|
||||
report_header_hub: Átadópont
|
||||
report_header_hub_address: Átvételi pont címe
|
||||
report_header_to_hub: Átvételi ponthoz
|
||||
report_header_hub_code: Átvételi pont kód
|
||||
@@ -2706,7 +2706,7 @@ hu:
|
||||
report_header_sum_max_total: "Összeg Max összesen"
|
||||
report_header_total_excl_vat: "Összesen, kivéve adó (%{currency_symbol})"
|
||||
report_header_total_incl_vat: "Összesen, beleértve adó (%{currency_symbol})"
|
||||
report_header_temp_controlled: TempControlled?
|
||||
report_header_temp_controlled: Hűtést igényel?
|
||||
report_header_is_producer: Termelő?
|
||||
report_header_not_confirmed: Nem megerősített
|
||||
report_header_gst_on_income: GST a bevételekre
|
||||
@@ -2831,7 +2831,7 @@ hu:
|
||||
error: Hiba
|
||||
unavailable: Nem érhető el
|
||||
profile: Profil
|
||||
hub: Kerékagy
|
||||
hub: Átadópont
|
||||
shop: Átadópont
|
||||
choose: Választ
|
||||
resolve_errors: Kérjük, oldja meg a következő hibákat
|
||||
|
||||
@@ -42,8 +42,6 @@ Openfoodnetwork::Application.routes.draw do
|
||||
resources :tag_rules, only: [:destroy]
|
||||
end
|
||||
|
||||
resources :manager_invitations, only: [:create]
|
||||
|
||||
resources :enterprise_relationships
|
||||
resources :enterprise_roles
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ Spree::Core::Engine.routes.draw do
|
||||
put '/password/change' => 'user_passwords#update', :as => :update_password
|
||||
end
|
||||
|
||||
resource :account, :controller => 'users'
|
||||
resource :account, :controller => 'users' do
|
||||
resources :webhook_endpoints, only: [:create, :destroy], controller: '/webhook_endpoints'
|
||||
end
|
||||
|
||||
match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management", via: :get
|
||||
match '/admin/payment_methods/show_provider_preferences' => 'admin/payment_methods#show_provider_preferences', :via => :get
|
||||
|
||||
@@ -15,5 +15,7 @@
|
||||
every: "5m"
|
||||
SubscriptionConfirmJob:
|
||||
every: "5m"
|
||||
OrderCycleOpenedJob:
|
||||
every: "5m"
|
||||
OrderCycleClosingJob:
|
||||
every: "5m"
|
||||
|
||||
@@ -2,4 +2,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
|
||||
|
||||
const environment = require('./environment')
|
||||
|
||||
const config = environment.toWebpackConfig();
|
||||
config.output.filename = "js/[name]-[hash].js";
|
||||
|
||||
module.exports = environment.toWebpackConfig()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddOpenedAtToOrderCycle < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :order_cycles, :opened_at, :timestamp
|
||||
end
|
||||
end
|
||||
11
db/migrate/20221028051650_create_webhook_endpoints.rb
Normal file
11
db/migrate/20221028051650_create_webhook_endpoints.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateWebhookEndpoints < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :webhook_endpoints do |t|
|
||||
t.string :url, null: false
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddSpreeUserReferenceToWebhookEndpoint < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :webhook_endpoints, :user_id, :bigint, default: 0, null: false
|
||||
add_index :webhook_endpoints, :user_id
|
||||
add_foreign_key :webhook_endpoints, :spree_users, column: :user_id
|
||||
end
|
||||
end
|
||||
12
db/schema.rb
12
db/schema.rb
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2023_02_13_160135) do
|
||||
ActiveRecord::Schema[6.1].define(version: 2023_02_13_160135) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
@@ -310,6 +310,7 @@ ActiveRecord::Schema.define(version: 2023_02_13_160135) do
|
||||
t.datetime "processed_at"
|
||||
t.boolean "automatic_notifications", default: false
|
||||
t.boolean "mails_sent", default: false
|
||||
t.datetime "opened_at"
|
||||
end
|
||||
|
||||
create_table "order_cycles_distributor_payment_methods", id: false, force: :cascade do |t|
|
||||
@@ -1189,6 +1190,14 @@ ActiveRecord::Schema.define(version: 2023_02_13_160135) do
|
||||
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
|
||||
end
|
||||
|
||||
create_table "webhook_endpoints", force: :cascade do |t|
|
||||
t.string "url", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.bigint "user_id", default: 0, null: false
|
||||
t.index ["user_id"], name: "index_webhook_endpoints_on_user_id"
|
||||
end
|
||||
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk"
|
||||
@@ -1293,4 +1302,5 @@ ActiveRecord::Schema.define(version: 2023_02_13_160135) do
|
||||
add_foreign_key "subscriptions", "spree_shipping_methods", column: "shipping_method_id", name: "subscriptions_shipping_method_id_fk"
|
||||
add_foreign_key "variant_overrides", "enterprises", column: "hub_id", name: "variant_overrides_hub_id_fk"
|
||||
add_foreign_key "variant_overrides", "spree_variants", column: "variant_id", name: "variant_overrides_variant_id_fk"
|
||||
add_foreign_key "webhook_endpoints", "spree_users", column: "user_id"
|
||||
end
|
||||
|
||||
@@ -42,7 +42,7 @@ module DfcProvider
|
||||
end
|
||||
|
||||
def authorization_control
|
||||
DfcProvider::AuthorizationControl.new(request)
|
||||
AuthorizationControl.new(request)
|
||||
end
|
||||
|
||||
def not_found
|
||||
|
||||
@@ -22,7 +22,7 @@ module DfcProvider
|
||||
|
||||
def variant
|
||||
@variant ||=
|
||||
DfcProvider::VariantFetcher.new(current_enterprise).scope.find(params[:id])
|
||||
VariantFetcher.new(current_enterprise).scope.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ module DfcProvider
|
||||
|
||||
def variant
|
||||
@variant ||=
|
||||
DfcProvider::VariantFetcher.new(current_enterprise).scope.find(params[:id])
|
||||
VariantFetcher.new(current_enterprise).scope.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,11 +32,11 @@ module DfcProvider
|
||||
end
|
||||
|
||||
def supplies
|
||||
DfcProvider::VariantFetcher.new(object).scope
|
||||
VariantFetcher.new(object).scope
|
||||
end
|
||||
|
||||
def manages
|
||||
DfcProvider::VariantFetcher.new(object).scope
|
||||
VariantFetcher.new(object).scope
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
54
engines/dfc_provider/app/services/authorization_control.rb
Normal file
54
engines/dfc_provider/app/services/authorization_control.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Service used to authorize the user on DCF Provider API
|
||||
# It controls an OICD Access token and an enterprise.
|
||||
class AuthorizationControl
|
||||
# Copied from: https://login.lescommuns.org/auth/realms/data-food-consortium/
|
||||
LES_COMMUNES_PUBLIC_KEY = <<~KEY
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl68JGqAILFzoi/1+6siXXp2vylu+7mPjYKjKelTtHFYXWVkbmVptCsamHlY3jRhqSQYe6M1SKfw8D+uXrrWsWficYvpdlV44Vm7uETZOr1/XBOjpWOi1vLmBVtX6jFeqN1BxfE1PxLROAiGn+MeMg90AJKShD2c5RoNv26e20dgPhshRVFPUGru+0T1RoKyIa64z/qcTcTVD2V7KX+ANMweRODdoPAzQFGGjTnL1uUqIdUwSfHSpXYnKxXOsnPC3Mowkv8UIGWWDxS/yzhWc7sOk1NmC7pb+Cg7G8NKj+Pp9qQZnXF39Dg95ZsxJrl6fyPFvTo3zf9CPG/fUM1CkkwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
KEY
|
||||
|
||||
def self.public_key
|
||||
OpenSSL::PKey::RSA.new(LES_COMMUNES_PUBLIC_KEY)
|
||||
end
|
||||
|
||||
def initialize(request)
|
||||
@request = request
|
||||
end
|
||||
|
||||
def user
|
||||
oidc_user || ofn_user
|
||||
rescue JWT::ExpiredSignature
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def oidc_user
|
||||
find_ofn_user(decode_token) if access_token
|
||||
end
|
||||
|
||||
def ofn_user
|
||||
@request.env['warden']&.user
|
||||
end
|
||||
|
||||
def decode_token
|
||||
JWT.decode(
|
||||
access_token,
|
||||
self.class.public_key,
|
||||
true, { algorithm: "RS256" }
|
||||
).first
|
||||
end
|
||||
|
||||
def access_token
|
||||
@request.headers['Authorization'].to_s.split(' ').last
|
||||
end
|
||||
|
||||
def find_ofn_user(payload)
|
||||
return if payload["email"].blank?
|
||||
|
||||
Spree::User.find_by(uid: payload["email"])
|
||||
end
|
||||
end
|
||||
@@ -1,56 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Service used to authorize the user on DCF Provider API
|
||||
# It controls an OICD Access token and an enterprise.
|
||||
module DfcProvider
|
||||
class AuthorizationControl
|
||||
# Copied from: https://login.lescommuns.org/auth/realms/data-food-consortium/
|
||||
LES_COMMUNES_PUBLIC_KEY = <<~KEY
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl68JGqAILFzoi/1+6siXXp2vylu+7mPjYKjKelTtHFYXWVkbmVptCsamHlY3jRhqSQYe6M1SKfw8D+uXrrWsWficYvpdlV44Vm7uETZOr1/XBOjpWOi1vLmBVtX6jFeqN1BxfE1PxLROAiGn+MeMg90AJKShD2c5RoNv26e20dgPhshRVFPUGru+0T1RoKyIa64z/qcTcTVD2V7KX+ANMweRODdoPAzQFGGjTnL1uUqIdUwSfHSpXYnKxXOsnPC3Mowkv8UIGWWDxS/yzhWc7sOk1NmC7pb+Cg7G8NKj+Pp9qQZnXF39Dg95ZsxJrl6fyPFvTo3zf9CPG/fUM1CkkwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
KEY
|
||||
|
||||
def self.public_key
|
||||
OpenSSL::PKey::RSA.new(LES_COMMUNES_PUBLIC_KEY)
|
||||
end
|
||||
|
||||
def initialize(request)
|
||||
@request = request
|
||||
end
|
||||
|
||||
def user
|
||||
oidc_user || ofn_user
|
||||
rescue JWT::ExpiredSignature
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def oidc_user
|
||||
find_ofn_user(decode_token) if access_token
|
||||
end
|
||||
|
||||
def ofn_user
|
||||
@request.env['warden']&.user
|
||||
end
|
||||
|
||||
def decode_token
|
||||
JWT.decode(
|
||||
access_token,
|
||||
self.class.public_key,
|
||||
true, { algorithm: "RS256" }
|
||||
).first
|
||||
end
|
||||
|
||||
def access_token
|
||||
@request.headers['Authorization'].to_s.split(' ').last
|
||||
end
|
||||
|
||||
def find_ofn_user(payload)
|
||||
return if payload["email"].blank?
|
||||
|
||||
Spree::User.find_by(uid: payload["email"])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Service used to fetch variants related to an entreprise.
|
||||
# It improves maintenance as it is the central point requesting
|
||||
# Spree::Varaint inside the DfcProvider engine.
|
||||
module DfcProvider
|
||||
class VariantFetcher
|
||||
def initialize(enterprise)
|
||||
@enterprise = enterprise
|
||||
end
|
||||
|
||||
def scope
|
||||
Spree::Variant.
|
||||
joins(product: :supplier).
|
||||
where('enterprises.id' => @enterprise.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
16
engines/dfc_provider/app/services/variant_fetcher.rb
Normal file
16
engines/dfc_provider/app/services/variant_fetcher.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Service used to fetch variants related to an enterprise.
|
||||
# It improves maintenance as it is the central point requesting
|
||||
# Spree::Variant inside the DfcProvider engine.
|
||||
class VariantFetcher
|
||||
def initialize(enterprise)
|
||||
@enterprise = enterprise
|
||||
end
|
||||
|
||||
def scope
|
||||
Spree::Variant.not_master.
|
||||
joins(:product).
|
||||
where(spree_products: { supplier: @enterprise })
|
||||
end
|
||||
end
|
||||
@@ -17,7 +17,7 @@ describe DfcProvider::EnterprisesController, type: :controller do
|
||||
|
||||
context 'with an authenticated user' do
|
||||
before do
|
||||
allow_any_instance_of(DfcProvider::AuthorizationControl)
|
||||
allow_any_instance_of(AuthorizationControl)
|
||||
.to receive(:user)
|
||||
.and_return(user)
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ describe DfcProvider::PersonsController, type: :controller do
|
||||
|
||||
context 'with an authenticated user' do
|
||||
before do
|
||||
allow_any_instance_of(DfcProvider::AuthorizationControl)
|
||||
allow_any_instance_of(AuthorizationControl)
|
||||
.to receive(:user)
|
||||
.and_return(user)
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ describe DfcProvider::SuppliedProductsController, type: :controller do
|
||||
|
||||
context 'with an authenticated user' do
|
||||
before do
|
||||
allow_any_instance_of(DfcProvider::AuthorizationControl)
|
||||
allow_any_instance_of(AuthorizationControl)
|
||||
.to receive(:user)
|
||||
.and_return(user)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require DfcProvider::Engine.root.join("spec/spec_helper")
|
||||
|
||||
describe DfcProvider::AuthorizationControl do
|
||||
describe AuthorizationControl do
|
||||
include AuthorizationHelper
|
||||
|
||||
let(:user) { create(:oidc_user) }
|
||||
|
||||
26
engines/dfc_provider/spec/services/variant_fetcher_spec.rb
Normal file
26
engines/dfc_provider/spec/services/variant_fetcher_spec.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require DfcProvider::Engine.root.join("spec/spec_helper")
|
||||
|
||||
describe VariantFetcher do
|
||||
subject { VariantFetcher.new(enterprise) }
|
||||
let(:enterprise) { build(:enterprise) }
|
||||
let(:other_enterprise) { build(:enterprise) }
|
||||
|
||||
it "returns an empty set" do
|
||||
expect(subject.scope).to eq []
|
||||
end
|
||||
|
||||
it "returns the variants of a supplier" do
|
||||
product = create(:product, supplier: enterprise)
|
||||
|
||||
expect(subject.scope.count).to eq 1
|
||||
expect(subject.scope).to eq product.variants
|
||||
end
|
||||
|
||||
it "ignores the variants of another enterprise" do
|
||||
create(:product, supplier: other_enterprise)
|
||||
|
||||
expect(subject.scope).to eq []
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ module AuthorizationHelper
|
||||
|
||||
def allow_token_for(payload)
|
||||
private_key = OpenSSL::PKey::RSA.generate 2048
|
||||
allow(DfcProvider::AuthorizationControl).to receive(:public_key).
|
||||
allow(AuthorizationControl).to receive(:public_key).
|
||||
and_return(private_key.public_key)
|
||||
|
||||
JWT.encode(payload, private_key, "RS256")
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
%strong
|
||||
= cookie_name
|
||||
%span /
|
||||
= t 'legal.cookies_policy.cookie_domain'
|
||||
{{ 'legal.cookies_policy.cookie_domain' | t }}
|
||||
- if cookie_domain
|
||||
= cookie_domain
|
||||
- else
|
||||
= "{{ instance_hostname }}"
|
||||
%p
|
||||
= cookie_desc
|
||||
= "{{ '#{cookie_desc}' | t }}"
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
%h2
|
||||
= t 'legal.cookies_policy.header'
|
||||
{{ 'legal.cookies_policy.header' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.desc_part_1'
|
||||
{{ 'legal.cookies_policy.desc_part_1' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.desc_part_2'
|
||||
{{ 'legal.cookies_policy.desc_part_2' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.desc_part_3'
|
||||
{{ 'legal.cookies_policy.desc_part_3' | t }}
|
||||
|
||||
%h2
|
||||
= t 'legal.cookies_policy.essential_cookies'
|
||||
{{ 'legal.cookies_policy.essential_cookies' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.essential_cookies_desc'
|
||||
{{ 'legal.cookies_policy.essential_cookies_desc' | t }}
|
||||
|
||||
%table{ng: { controller:"CookiesPolicyModalCtrl"}}
|
||||
= render_cookie_entry( "_ofn_session_id", t( "legal.cookies_policy.cookie_session_desc" ) )
|
||||
= render_cookie_entry( "cookies_consent", t( "legal.cookies_policy.cookie_consent_desc" ) )
|
||||
= render_cookie_entry( "remember_spree_user_token", t( "legal.cookies_policy.cookie_remember_me_desc" ) )
|
||||
= render_cookie_entry( "qos_token", t( "legal.cookies_policy.cookie_openstreemap_desc" ), "openstreetmap.org" )
|
||||
= render_cookie_entry( "_ofn_session_id", "legal.cookies_policy.cookie_session_desc" )
|
||||
= render_cookie_entry( "cookies_consent", "legal.cookies_policy.cookie_consent_desc" )
|
||||
= render_cookie_entry( "remember_spree_user_token", "legal.cookies_policy.cookie_remember_me_desc" )
|
||||
= render_cookie_entry( "qos_token", "legal.cookies_policy.cookie_openstreemap_desc", "openstreetmap.org" )
|
||||
%tr
|
||||
%td
|
||||
%p
|
||||
%strong m
|
||||
%span /
|
||||
= t 'legal.cookies_policy.cookie_domain'
|
||||
{{ 'legal.cookies_policy.cookie_domain' | t }}
|
||||
= "m.stripe.com"
|
||||
%p
|
||||
%strong nsr
|
||||
%span /
|
||||
= t 'legal.cookies_policy.cookie_domain'
|
||||
{{ 'legal.cookies_policy.cookie_domain' | t }}
|
||||
= "m.stripe.network"
|
||||
%p
|
||||
%strong __stripe_sid
|
||||
%span /
|
||||
= t 'legal.cookies_policy.cookie_domain'
|
||||
{{ 'legal.cookies_policy.cookie_domain' | t }}
|
||||
= "{{ instance_hostname }}"
|
||||
%p
|
||||
%strong __stripe_mid
|
||||
%span /
|
||||
= t 'legal.cookies_policy.cookie_domain'
|
||||
{{ 'legal.cookies_policy.cookie_domain' | t }}
|
||||
= "{{ instance_hostname }}"
|
||||
%p
|
||||
= t 'legal.cookies_policy.cookie_stripe_desc'
|
||||
{{ 'legal.cookies_policy.cookie_stripe_desc' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.essential_cookies_note'
|
||||
{{ 'legal.cookies_policy.essential_cookies_note' | t }}
|
||||
|
||||
- if Spree::Config.cookies_policy_matomo_section
|
||||
%h2
|
||||
= t 'legal.cookies_policy.statistics_cookies'
|
||||
{{ 'legal.cookies_policy.statistics_cookies' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.statistics_cookies_desc'
|
||||
{{ 'legal.cookies_policy.statistics_cookies_desc' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.statistics_cookies_matomo_desc_html'
|
||||
{{ 'legal.cookies_policy.statistics_cookies_matomo_desc_html' | t }}
|
||||
|
||||
%table{ng: { controller:"CookiesPolicyModalCtrl"}}
|
||||
= render_cookie_entry( "_pk_ref, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_basics_desc" ) )
|
||||
= render_cookie_entry( "_pk_hsr, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_heatmap_desc" ) )
|
||||
= render_cookie_entry( "piwik_ignore, _pk_cvar, _pk_id and _pk_ses", t( "legal.cookies_policy.cookie_matomo_ignore_desc" ) )
|
||||
= render_cookie_entry( "_pk_ref, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_basics_desc" )
|
||||
= render_cookie_entry( "_pk_hsr, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_heatmap_desc" )
|
||||
= render_cookie_entry( "piwik_ignore, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_ignore_desc" )
|
||||
|
||||
- if Spree::Config.matomo_url.present?
|
||||
%p
|
||||
= t 'legal.cookies_policy.statistics_cookies_matomo_optout'
|
||||
{{ 'legal.cookies_policy.statistics_cookies_matomo_optout' | t }}
|
||||
%p
|
||||
%iframe{ src: matomo_iframe_src }
|
||||
|
||||
%h2
|
||||
= t 'legal.cookies_policy.disabling_cookies_header'
|
||||
{{ 'legal.cookies_policy.disabling_cookies_header' | t }}
|
||||
%p
|
||||
= t 'legal.cookies_policy.disabling_cookies_desc'
|
||||
{{ 'legal.cookies_policy.disabling_cookies_desc' | t }}
|
||||
%ul
|
||||
%li
|
||||
%a{ href: t( 'legal.cookies_policy.disabling_cookies_firefox_link' ), target: "_blank" }
|
||||
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_firefox_link' | t }}", target: "_blank" }
|
||||
Firefox
|
||||
%li
|
||||
%a{ href: t( 'legal.cookies_policy.disabling_cookies_chrome_link' ), target: "_blank" }
|
||||
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_chrome_link' | t }}", target: "_blank" }
|
||||
Chrome
|
||||
%li
|
||||
%a{ href: t( 'legal.cookies_policy.disabling_cookies_ie_link' ), target: "_blank" }
|
||||
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_ie_link' | t }}", target: "_blank" }
|
||||
Internet Explorer
|
||||
%li
|
||||
%a{ href: t( 'legal.cookies_policy.disabling_cookies_safari_link' ), target: "_blank" }
|
||||
%a{ "ng-href" => "{{ 'legal.cookies_policy.disabling_cookies_safari_link' | t }}", target: "_blank" }
|
||||
Safari
|
||||
%p
|
||||
= t 'legal.cookies_policy.disabling_cookies_note'
|
||||
{{ 'legal.cookies_policy.disabling_cookies_note' | t }}
|
||||
|
||||
%a.close-reveal-modal{"ng-click" => "$close()"}
|
||||
%i.ofn-i_009-close
|
||||
|
||||
@@ -13,10 +13,8 @@ module OpenFoodNetwork
|
||||
@customer_tags = customer_tags || []
|
||||
end
|
||||
|
||||
def filter!(subject)
|
||||
return unless subject.respond_to?(:any?) && subject.any?
|
||||
|
||||
subject.to_a.reject! do |element|
|
||||
def filter(subject)
|
||||
subject.to_a.reject do |element|
|
||||
if rule_class.respond_to?(:tagged_children_for)
|
||||
children = rule_class.tagged_children_for(element)
|
||||
children.reject! { |child| reject?(child) }
|
||||
|
||||
@@ -10,7 +10,13 @@ module Reporting
|
||||
@report = report
|
||||
end
|
||||
|
||||
# Strip header and summary rows for these formats
|
||||
def raw_render?
|
||||
@report.params[:report_format].in?(['csv'])
|
||||
end
|
||||
|
||||
# Do not format values for these output formats
|
||||
def unformatted_render?
|
||||
@report.params[:report_format].in?(['json', 'csv'])
|
||||
end
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ module Reporting
|
||||
def slice_and_format_row(row)
|
||||
result = row.to_h.select { |k, _v| k.in?(report.fields_to_show) }
|
||||
|
||||
unless report.raw_render?
|
||||
unless report.unformatted_render?
|
||||
result = result.map { |k, v| [k, format_cell(v, k)] }.to_h
|
||||
end
|
||||
OpenStruct.new(result)
|
||||
@@ -47,12 +47,17 @@ module Reporting
|
||||
|
||||
proc_args = [group_value, datas.map(&:item), datas.map(&:full_row)]
|
||||
row = rule[:summary_row].call(*proc_args)
|
||||
row = add_summary_row_type(row)
|
||||
row = slice_and_format_row(OpenStruct.new(row.reverse_merge!(blank_row)))
|
||||
add_summary_row_label(row, rule, proc_args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_summary_row_type(row)
|
||||
row.reverse_merge!({ report_row_type: "summary" })
|
||||
end
|
||||
|
||||
def add_summary_row_label(row, rule, proc_args)
|
||||
previous_key = nil
|
||||
label = rule[:summary_row_label]
|
||||
|
||||
@@ -6,7 +6,8 @@ module Reporting
|
||||
attr_accessor :user, :params, :ransack_params
|
||||
|
||||
delegate :render_as, :as_json, :to_html, :to_csv, :to_xlsx, :to_pdf, :to_json, to: :renderer
|
||||
delegate :raw_render?, :html_render?, :display_header_row?, :display_summary_row?, to: :renderer
|
||||
delegate :unformatted_render?, :html_render?, :display_header_row?, :display_summary_row?,
|
||||
to: :renderer
|
||||
|
||||
delegate :rows, :table_rows, :grouped_data, to: :rows_builder
|
||||
delegate :available_headers, :table_headers, :fields_to_hide, :fields_to_show,
|
||||
|
||||
15
package.json
15
package.json
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@floating-ui/dom": "^1.2.4",
|
||||
"@floating-ui/dom": "^1.2.5",
|
||||
"@hotwired/turbo": "^7.3.0",
|
||||
"@rails/webpacker": "5.4.4",
|
||||
"babel-loader": "^8.2.3",
|
||||
@@ -38,20 +38,19 @@
|
||||
"stimulus-flatpickr": "^1.4.0",
|
||||
"stimulus_reflex": "3.5.0-pre9",
|
||||
"tom-select": "^2.0.0",
|
||||
"webpack": "~4"
|
||||
"webpack": "~4",
|
||||
"webpack-cli": "~4",
|
||||
"webpack-dev-server": "~4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@webpack-cli/serve": "*",
|
||||
"husky": "^8.0.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"jasmine-core": "~4.6.0",
|
||||
"jest": "^27.4.7",
|
||||
"karma": "~6.4.1",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coffee-preprocessor": "~1.0.1",
|
||||
"karma-jasmine": "~0.3.8",
|
||||
"prettier": "2.8.4",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"webpack-cli": "~3",
|
||||
"webpack-dev-server": "~3"
|
||||
"prettier": "2.8.6",
|
||||
"pretty-quick": "^3.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user