mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-19 19:46:51 +00:00
Compare commits
192 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 | ||
|
|
6bdb89d090 | ||
|
|
a63531c8c2 | ||
|
|
de2a15e3e1 | ||
|
|
e5f5d42d59 | ||
|
|
f23de206d9 | ||
|
|
729bcdf291 | ||
|
|
8f67e9839d | ||
|
|
62d0ce290a | ||
|
|
96ead52fcc | ||
|
|
ff708952ab | ||
|
|
fdeeab5750 | ||
|
|
9e7e176cf9 | ||
|
|
2daa49406a | ||
|
|
125bc3d14b | ||
|
|
02300d6bb6 | ||
|
|
aba3968bca | ||
|
|
23c4298519 | ||
|
|
40a0d8e08c | ||
|
|
5cbec5521c | ||
|
|
d7047bb4cc | ||
|
|
8f6fdf3e31 | ||
|
|
1ce029d8e4 | ||
|
|
eb7d20d74d | ||
|
|
c4ba1e6616 | ||
|
|
f97ccb4203 | ||
|
|
df8283661d | ||
|
|
d9ee56b4a5 | ||
|
|
2230a1f179 | ||
|
|
a9c8d9f304 | ||
|
|
9d5ca2255b | ||
|
|
00a823b2fc | ||
|
|
3d81a6e280 | ||
|
|
739df4be01 | ||
|
|
b91cabc510 | ||
|
|
ba152f12ee | ||
|
|
778baba118 | ||
|
|
85c98c6d3e | ||
|
|
de9546587a | ||
|
|
9741935955 | ||
|
|
9d19f37fec | ||
|
|
718ac0ab80 | ||
|
|
d95c5ff8a8 | ||
|
|
797b98d686 | ||
|
|
3dc3ebe584 | ||
|
|
a7644f8e8b | ||
|
|
5dd2737811 | ||
|
|
6d05d57846 | ||
|
|
d9b534b829 | ||
|
|
ac739108a2 | ||
|
|
a78768c291 | ||
|
|
3f3ae97a40 | ||
|
|
ae166ae220 | ||
|
|
533f7048bf | ||
|
|
e399e27a7d | ||
|
|
63ba3defec | ||
|
|
b9a7ff903f | ||
|
|
fc6b61fc87 | ||
|
|
131772f7b2 | ||
|
|
440e776e3a | ||
|
|
56b9c28955 | ||
|
|
c34942dbf0 | ||
|
|
12906d1e13 | ||
|
|
103bc50bdc | ||
|
|
b6cccc2e1d | ||
|
|
918e583d01 | ||
|
|
7b8ccccdc3 | ||
|
|
fb01a6e244 | ||
|
|
0cae069769 | ||
|
|
b99383185b | ||
|
|
66485e2e16 | ||
|
|
c1b60d88d6 | ||
|
|
50bc48c96f | ||
|
|
8ad532c41a | ||
|
|
ebd5d706c2 | ||
|
|
08e23023b3 | ||
|
|
8658b1a743 | ||
|
|
5208094f05 | ||
|
|
8926a3f08d | ||
|
|
58ea3a10e8 | ||
|
|
5ca5c32da4 | ||
|
|
7a1a1286d2 | ||
|
|
5e61aa8a77 | ||
|
|
af92b9f464 | ||
|
|
ffaf4a837f | ||
|
|
c83dac58a3 | ||
|
|
d377300f32 | ||
|
|
b7a3f7263b | ||
|
|
e5ca8b0ee1 | ||
|
|
cf8d9c6d07 | ||
|
|
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'
|
||||
|
||||
186
Gemfile.lock
186
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,24 +230,24 @@ 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)
|
||||
date (3.3.3)
|
||||
ddtrace (1.9.0)
|
||||
ddtrace (1.10.1)
|
||||
debase-ruby_core_source (>= 0.10.16, <= 3.2.0)
|
||||
libdatadog (~> 1.0.1.1.0)
|
||||
libddwaf (~> 1.5.1.0.0)
|
||||
libdatadog (~> 2.0.0.1.0)
|
||||
libddwaf (~> 1.6.2.0.0)
|
||||
msgpack
|
||||
debase-ruby_core_source (3.2.0)
|
||||
debug (1.7.1)
|
||||
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)
|
||||
@@ -373,8 +381,8 @@ GEM
|
||||
addressable (~> 2.7)
|
||||
letter_opener (1.8.1)
|
||||
launchy (>= 2.2, < 3)
|
||||
libdatadog (1.0.1.1.0)
|
||||
libddwaf (1.5.1.0.0)
|
||||
libdatadog (2.0.0.1.0)
|
||||
libddwaf (1.6.2.0.0)
|
||||
ffi (~> 1.0)
|
||||
libv8-node (16.10.0.0)
|
||||
listen (3.8.0)
|
||||
@@ -402,12 +410,12 @@ 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)
|
||||
i18n (>= 0.6.4, <= 2)
|
||||
msgpack (1.6.0)
|
||||
msgpack (1.6.1)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
net-imap (0.3.4)
|
||||
@@ -459,7 +467,7 @@ GEM
|
||||
parallel (1.22.1)
|
||||
paranoia (2.6.1)
|
||||
activerecord (>= 5.1, < 7.1)
|
||||
parser (3.2.1.0)
|
||||
parser (3.2.1.1)
|
||||
ast (~> 2.4.1)
|
||||
paypal-sdk-core (0.3.4)
|
||||
multi_json (~> 1.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.2)
|
||||
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)
|
||||
@@ -605,7 +614,7 @@ GEM
|
||||
rswag-ui (2.8.0)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.47.0)
|
||||
rubocop (1.48.1)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.0.0)
|
||||
@@ -623,7 +632,7 @@ GEM
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
ruby-graphviz (1.2.5)
|
||||
rexml
|
||||
ruby-progressbar (1.12.0)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby-vips (2.1.4)
|
||||
ffi (~> 1.12)
|
||||
@@ -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",
|
||||
@@ -150,6 +158,10 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
else
|
||||
Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData())
|
||||
, "js.admin.deleting_item_will_cancel_order")
|
||||
else
|
||||
ofnDeleteLineItemsAlert(() ->
|
||||
Promise.all(LineItems.delete(item) for item in lineItemsToDelete).then(-> $scope.refreshData())
|
||||
, lineItemsToDelete.length)
|
||||
|
||||
$scope.allBoxesChecked = ->
|
||||
checkedCount = $scope.filteredLineItems.reduce (count,lineItem) ->
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -250,6 +250,18 @@ ofnCancelOrderAlert = function(callback, i18nKey) {
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
ofnDeleteLineItemsAlert = function(callback, count) {
|
||||
$('#custom-confirm .message').html(`${t("js.admin.orders.delete_line_items_html", {count: count})}`);
|
||||
$('#custom-confirm button.confirm').click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
callback();
|
||||
});
|
||||
$('#custom-confirm button.cancel').click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
});
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
ofnConfirm = function(callback) {
|
||||
$('#custom-confirm .message').html(
|
||||
` ${t("are_you_sure")}
|
||||
|
||||
@@ -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
|
||||
@@ -100,10 +100,13 @@ module Admin
|
||||
order_cycle.schedules.each do |schedule|
|
||||
Subscription.where(schedule_id: schedule.id).each do |subscription|
|
||||
shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id)
|
||||
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
|
||||
subscription.subscription_line_items.nil_price_estimate.each do |line_item|
|
||||
variant = OrderManagement::Subscriptions::
|
||||
VariantsList.eligible_variants(shop).find_by(id: line_item.variant_id)
|
||||
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
|
||||
# If the variant is not available in the shop, the price estimate will be nil
|
||||
next if variant.nil?
|
||||
|
||||
price = variant.price + fee_calculator.indexed_fees_for(variant)
|
||||
line_item.update_column(:price_estimate, price)
|
||||
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
|
||||
@@ -141,6 +141,10 @@ class SplitCheckoutController < ::BaseController
|
||||
def update_order
|
||||
return if params[:confirm_order] || @order.errors.any?
|
||||
|
||||
# If we have "pick up" shipping method (require_ship_address is set to false), use the
|
||||
# distributor address as shipping address
|
||||
use_shipping_address_from_distributor if shipping_method_ship_address_not_required?
|
||||
|
||||
@order.select_shipping_method(params[:shipping_method_id])
|
||||
@order.update(order_params)
|
||||
@order.updater.update_totals_and_states
|
||||
@@ -150,6 +154,29 @@ class SplitCheckoutController < ::BaseController
|
||||
@order.errors.empty?
|
||||
end
|
||||
|
||||
def use_shipping_address_from_distributor
|
||||
@order.ship_address = @order.address_from_distributor
|
||||
|
||||
# Add the missing data
|
||||
bill_address = params[:order][:bill_address_attributes]
|
||||
@order.ship_address.firstname = bill_address[:firstname]
|
||||
@order.ship_address.lastname = bill_address[:lastname]
|
||||
@order.ship_address.phone = bill_address[:phone]
|
||||
|
||||
# Remove shipping address from parameter so we don't override the address we just set
|
||||
params[:order].delete(:ship_address_attributes)
|
||||
end
|
||||
|
||||
def shipping_method_ship_address_not_required?
|
||||
selected_shipping_method = available_shipping_methods&.select do |sm|
|
||||
sm.id.to_s == params[:shipping_method_id]
|
||||
end
|
||||
|
||||
return false if selected_shipping_method.empty?
|
||||
|
||||
selected_shipping_method.first.require_ship_address == false
|
||||
end
|
||||
|
||||
def summary_step?
|
||||
params[:step] == "summary"
|
||||
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
|
||||
|
||||
@@ -271,10 +271,16 @@ module ProductImport
|
||||
end
|
||||
|
||||
def entry_matches_existing_variant?(entry, existing_variant)
|
||||
existing_variant.display_name == entry.display_name &&
|
||||
display_name_are_the_same?(entry, existing_variant) &&
|
||||
existing_variant.unit_value == entry.unit_value.to_f
|
||||
end
|
||||
|
||||
def display_name_are_the_same?(entry, existing_variant)
|
||||
return true if entry.display_name.blank? && existing_variant.display_name.blank?
|
||||
|
||||
existing_variant.display_name == entry.display_name
|
||||
end
|
||||
|
||||
def category_validation(entry)
|
||||
category_name = entry.category
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
= @order.bill_address.zipcode
|
||||
%div
|
||||
= @order.bill_address.country
|
||||
- if @order.special_instructions.present?
|
||||
%br
|
||||
%em
|
||||
= @order.special_instructions
|
||||
- if @order.shipping_method.description.present?
|
||||
%div
|
||||
.summary-subtitle
|
||||
|
||||
@@ -29,5 +29,5 @@
|
||||
- if spree_current_user
|
||||
.checkout-input
|
||||
= check_box_tag "order[payments_attributes][][source_attributes][save_requested_by_customer]", 1, false
|
||||
= label :save_requested_by_customer, t('split_checkout.step2.form.stripe.save_card'), { for: "save_requested_by_customer" }
|
||||
= label_tag :order_payments_attributes__source_attributes_save_requested_by_customer, t('split_checkout.step2.form.stripe.save_card')
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -240,6 +240,7 @@ module Openfoodnetwork
|
||||
config.generators.template_engine = :haml
|
||||
|
||||
Rails.application.routes.default_url_options[:host] = ENV["SITE_URL"]
|
||||
DfcProvider::Engine.routes.default_url_options[:host] = ENV["SITE_URL"]
|
||||
|
||||
Rails.autoloaders.main.ignore(Rails.root.join('app/webpacker'))
|
||||
|
||||
@@ -248,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
|
||||
#
|
||||
@@ -3253,6 +3262,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
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..."
|
||||
@@ -3562,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"
|
||||
@@ -3942,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"
|
||||
@@ -4357,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"
|
||||
@@ -1124,8 +1124,8 @@ hu:
|
||||
back_to_list: "Vissza a listához"
|
||||
save_and_back_to_list: "Mentés és Vissza a listához"
|
||||
choose_products_from: "Válasszon termékeket a következők közül:"
|
||||
re_notify_producers: Értesítse újra a gyártókat
|
||||
notify_producers_tip: Ez minden gyártónak e-mailt küld a rendeléseik listájával.
|
||||
re_notify_producers: Értesítse újra a termelőket
|
||||
notify_producers_tip: Ez minden termelőnak e-mailt küld a rendeléseik listájával.
|
||||
incoming:
|
||||
incoming: "Beérkező"
|
||||
supplier: "Termelő"
|
||||
@@ -1246,7 +1246,7 @@ hu:
|
||||
closed: zárva
|
||||
producer_properties:
|
||||
index:
|
||||
title: A gyártó tulajdonságai
|
||||
title: A termelő tulajdonságai
|
||||
proxy_orders:
|
||||
cancel:
|
||||
could_not_cancel_the_order: A rendelést nem lehetett törölni
|
||||
@@ -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"
|
||||
@@ -2088,7 +2088,7 @@ hu:
|
||||
producers_signup_motivation: Adja el ételeit, és mesélje el történeteit változatos új piacokon. Takarítson meg időt és pénzt minden rezsinél. Kockázat nélkül támogatjuk az innovációt. Kiegyenlítettük a játékteret.
|
||||
producers_signup_send: Csatlakozz most
|
||||
producers_signup_enterprise: Vállalkozási fiókok
|
||||
producers_signup_studies: Gyártóink történetei.
|
||||
producers_signup_studies: Termelőink történetei.
|
||||
producers_signup_cta_headline: Csatlakozz most!
|
||||
producers_signup_cta_action: Csatlakozz most
|
||||
producers_signup_detail: Itt a részlet.
|
||||
@@ -2454,12 +2454,12 @@ hu:
|
||||
new_order_cycle: "Új rendelési ciklus"
|
||||
new_order_cycle_tooltip: "Nyissa meg az üzletet egy bizonyos ideig"
|
||||
select_a_coordinator_for_your_order_cycle: "Válasszon koordinátort a rendelési ciklushoz"
|
||||
notify_producers: 'Értesítse a gyártókat'
|
||||
notify_producers: 'Értesítse a termelőket'
|
||||
edit_order_cycle: "Rendelési ciklus szerkesztése"
|
||||
roles: "Szerepek"
|
||||
update: "Frissítés"
|
||||
delete: Töröl
|
||||
add_producer_property: "Gyártói tulajdon hozzáadása"
|
||||
add_producer_property: "Termelői tulajdonság hozzáadása"
|
||||
in_progress: "Folyamatban"
|
||||
started_at: "-kor kezdődött"
|
||||
queued: "Sorban"
|
||||
@@ -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
|
||||
|
||||
@@ -6,9 +6,22 @@ it:
|
||||
spree/shipping_method: Metodo di spedizione
|
||||
attributes:
|
||||
spree/order/ship_address:
|
||||
address1: "Dati di consegna - Indirizzo"
|
||||
address2: "Dati di consegna - continuazione indirizzo"
|
||||
city: "Dati di consegna - città"
|
||||
country: "Dati di consegna - nazione"
|
||||
phone: "Numero di telefono"
|
||||
firstname: "Nome"
|
||||
lastname: "Cognome"
|
||||
zipcode: "Dati di consegna - CAP"
|
||||
spree/order/bill_address:
|
||||
address1: "Dati di fatturazione - indirizzo"
|
||||
zipcode: "Dati di fatturazione - CAP"
|
||||
city: "Dati di fatturazione - città"
|
||||
country: "Dati di fatturazione - nazione"
|
||||
firstname: "Dati di fatturazione - nome"
|
||||
lastname: "Dati di fatturazione - cognome"
|
||||
phone: Telefono
|
||||
spree/user:
|
||||
password: "Password"
|
||||
password_confirmation: "Conferma password"
|
||||
@@ -1311,6 +1324,7 @@ it:
|
||||
total_by_customer: Totale Per Consumatore
|
||||
total_by_supplier: Totale Per Fornitore
|
||||
supplier_totals: Totali Ciclo di richieste fornitori
|
||||
percentage: "%{value} %"
|
||||
supplier_totals_by_distributor: Totali Ciclo di richieste fornitori per distributore
|
||||
totals_by_supplier: Totali Ciclo di richieste fornitori per fornitore
|
||||
customer_totals: Ciclo Ordini Cliente Totale
|
||||
@@ -1321,6 +1335,8 @@ it:
|
||||
addresses: Indirizzi
|
||||
payment_methods: Rapporto Metodi di pagamento
|
||||
delivery: Rapporto Consegne
|
||||
sales_tax_totals_by_producer: Totali tasse vendita per produttore
|
||||
sales_tax_totals_by_order: Totali tasse vendita per ordine
|
||||
tax_types: Tipologia tariffe
|
||||
tax_rates: Aliquote IVA
|
||||
pack_by_customer: Imballato dal Cliente
|
||||
@@ -1582,7 +1598,7 @@ it:
|
||||
require_login_2_html: "Vuoi iniziare a acquistare qui? Per favore %{contact} %{enterprise} e chiedi di essere aggiunto."
|
||||
require_customer_html: "Se vuoi iniziare a comprare, per favore %{contact} %{enterprise} per chiedere di aggiungerti ai clienti."
|
||||
select_oc:
|
||||
select_oc_html: "Per vedere i prodotti disponibili,<span class='highlighted'>scegli la data di consegna</span> dal riquadro in alto a destra."
|
||||
select_oc_html: "Per vedere i prodotti disponibili, <span class='highlighted'>scegli la data di consegna</span> dal riquadro in alto a destra."
|
||||
products:
|
||||
summary:
|
||||
bulk: "Volume"
|
||||
@@ -1618,7 +1634,7 @@ it:
|
||||
menu_4_title: "Reti"
|
||||
menu_4_url: "/groups"
|
||||
menu_5_title: "Chi siamo"
|
||||
menu_5_url: "https://about.openfoodnetwork.org.au/"
|
||||
menu_5_url: "https://www.openfooditalia.org/"
|
||||
menu_6_title: "Blog"
|
||||
menu_6_url: "https://apropos.openfoodfrance.org/blog/"
|
||||
menu_7_title: "Support"
|
||||
@@ -1861,10 +1877,13 @@ it:
|
||||
title: Dettagli della consegna
|
||||
edit: Modifica
|
||||
address: Indirizzo di consegna
|
||||
instructions: Istruzioni
|
||||
payment_method:
|
||||
title: Metodo di pagamento
|
||||
edit: Modifica
|
||||
instructions: Istruzioni
|
||||
order:
|
||||
title: Dettagli ordine
|
||||
edit: Modifica
|
||||
terms_and_conditions:
|
||||
message_html: "Accetto i %{terms_and_conditions_link} del venditore."
|
||||
@@ -1877,6 +1896,7 @@ it:
|
||||
submit: Completa ordine
|
||||
cancel: Torna al metodo di pagamento
|
||||
errors:
|
||||
saving_failed: "Salvataggio fallito, per favore controlla i campi evidenziati. %{messages}"
|
||||
terms_not_accepted: Per favore accetta i Termini e Condizioni
|
||||
required: Il campo non può essere lasciato vuoto
|
||||
invalid_number: "Per favore inserisci un numero di telefono valido"
|
||||
@@ -2659,6 +2679,7 @@ it:
|
||||
report_header_tax_on_delivery: "Oneri sulla consegna (%{currency_symbol})"
|
||||
report_header_tax_on_fees: "Oneri sulle provvigioni (%{currency_symbol})"
|
||||
report_header_tax_category: "Categoria d'imposta"
|
||||
report_header_tax_rate_name: "Nome tassa"
|
||||
report_header_tax_rate: "Aliquota d'imposta"
|
||||
report_header_total_tax: "Oneri totali (%{currency_symbol})"
|
||||
report_header_total_excl_tax: "Totale imponibile (%{currency_symbol})"
|
||||
@@ -2682,6 +2703,7 @@ it:
|
||||
report_header_supplier: Fornitore
|
||||
report_header_producer: Produttore
|
||||
report_header_producer_suburb: Comune produttore
|
||||
report_header_producer_tax_status: Stato tassa produttore
|
||||
report_header_producer_charges_sales_tax?: Partita IVA / Codice fiscale
|
||||
report_header_unit: Unità
|
||||
report_header_group_buy_unit_quantity: Raggruppare per quantità di unità
|
||||
@@ -2698,6 +2720,7 @@ it:
|
||||
report_header_distributor_address: Indirizzo distributore
|
||||
report_header_distributor_city: Città distributore
|
||||
report_header_distributor_postcode: CAP distributore
|
||||
report_header_distributor_tax_status: Stato tassa distributore
|
||||
report_header_delivery_address: Indirizzo consegna
|
||||
report_header_delivery_postcode: CAP consegna
|
||||
report_header_bulk_unit_size: Quantità minima per Acquisto di Gruppo
|
||||
@@ -2787,7 +2810,7 @@ it:
|
||||
shipping: "Spedizione"
|
||||
shipping_methods: "Metodi di Spedizione"
|
||||
payment_methods: "Metodi di pagamento"
|
||||
payment_method_fee: "Imposta di transizione"
|
||||
payment_method_fee: "Imposta di transazione"
|
||||
payment_processing_failed: "Non è stato possibile elaborare il pagamento, per favore controlla i dettagli che hai inserito"
|
||||
payment_method_not_supported: "Questo metodo di pagamento non è supportato. Per favore scegline un altro."
|
||||
payment_updated: "Pagamento aggiornato"
|
||||
@@ -2879,6 +2902,7 @@ it:
|
||||
deleting_item_will_cancel_order: "Questa operazione risulterà in una o più gentili richieste vuote che saranno cancellate. Desideri proseguire?"
|
||||
modals:
|
||||
got_it: "Capito"
|
||||
confirm: "Conferma"
|
||||
close: "Ok"
|
||||
continue: "Continua"
|
||||
cancel: "Annulla"
|
||||
@@ -3429,6 +3453,8 @@ it:
|
||||
server_error: "Errore Server"
|
||||
shipping_method_names:
|
||||
UPS Ground: "UPS Ground"
|
||||
pick_up: "Ritiro in azienda"
|
||||
delivery: "Firmato, sigillato, consegnato"
|
||||
start_date: "Data inizio"
|
||||
successfully_removed: "Rimosso con successo"
|
||||
taxonomy_edit: "Modifica tassonomia"
|
||||
@@ -3513,6 +3539,7 @@ it:
|
||||
display_currency: "Visualizza valuta"
|
||||
choose_currency: "Scegli Valuta"
|
||||
mail_method_settings: "Impostazioni del metodo di posta"
|
||||
mail_settings_notice_html: "<b>Le modifiche efettuate saranno temporanee</b> solo per il debugging, potrebbero essere invertite in futuro. <br>Modifiche permanenti possono essere effettuate modificando i dati nascosti dell'istanza utilizzando <a href='https://github.com/openfoodfoundation/ofn-install'>intallazione ofn </a>. Contatta il team globale OFN per maggiori dettagli. "
|
||||
general: "Generale"
|
||||
enable_mail_delivery: "Abilita recapito posta"
|
||||
send_mails_as: "Invia mail come"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,10 +6,6 @@ module DfcProvider
|
||||
class BaseSerializer < ActiveModel::Serializer
|
||||
private
|
||||
|
||||
def host
|
||||
Rails.application.routes.default_url_options[:host]
|
||||
end
|
||||
|
||||
def dfc_provider_routes
|
||||
DfcProvider::Engine.routes.url_helpers
|
||||
end
|
||||
|
||||
@@ -17,7 +17,6 @@ module DfcProvider
|
||||
dfc_provider_routes.enterprise_catalog_item_url(
|
||||
enterprise_id: object.product.supplier_id,
|
||||
id: object.id,
|
||||
host: host
|
||||
)
|
||||
end
|
||||
|
||||
@@ -44,7 +43,6 @@ module DfcProvider
|
||||
dfc_provider_routes.enterprise_supplied_product_url(
|
||||
enterprise_id: object.product.supplier_id,
|
||||
id: object.id,
|
||||
host: host
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,7 +18,6 @@ module DfcProvider
|
||||
def id
|
||||
dfc_provider_routes.enterprise_url(
|
||||
id: object.id,
|
||||
host: host
|
||||
)
|
||||
end
|
||||
|
||||
@@ -33,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
|
||||
|
||||
@@ -30,7 +30,6 @@ module DfcProvider
|
||||
def id
|
||||
dfc_provider_routes.person_url(
|
||||
id: object.id,
|
||||
host: host
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ module DfcProvider
|
||||
dfc_provider_routes.enterprise_supplied_product_url(
|
||||
enterprise_id: object.product.supplier_id,
|
||||
id: object.id,
|
||||
host: host
|
||||
)
|
||||
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user