mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-20 19:56:48 +00:00
Compare commits
255 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a56081ad5b | ||
|
|
996c6d35f3 | ||
|
|
62cdda2ce2 | ||
|
|
e6123969d2 | ||
|
|
90ce52612d | ||
|
|
7e6d40f370 | ||
|
|
b6e4ff908f | ||
|
|
c16c1a5e62 | ||
|
|
3f5aceff99 | ||
|
|
b8e9925601 | ||
|
|
667a514ed2 | ||
|
|
25108f4c70 | ||
|
|
6f83607aa8 | ||
|
|
70aff98581 | ||
|
|
812fb974dd | ||
|
|
0a91f3d392 | ||
|
|
c7c19e47de | ||
|
|
e1845dddac | ||
|
|
2e10336a47 | ||
|
|
37d3c025e9 | ||
|
|
6eb52aa540 | ||
|
|
6809198c96 | ||
|
|
3b7aebd6da | ||
|
|
7dc5bc87d1 | ||
|
|
0c43d0f16a | ||
|
|
fb1ad4c65f | ||
|
|
04748b6e0e | ||
|
|
094fc039e9 | ||
|
|
e4f40d14b8 | ||
|
|
4085aa22dc | ||
|
|
a4add889a8 | ||
|
|
f9f6793d10 | ||
|
|
a62687b1a7 | ||
|
|
a48fd0828c | ||
|
|
6cfb86e578 | ||
|
|
a168126cfa | ||
|
|
88c833ec83 | ||
|
|
c887efee54 | ||
|
|
86abff224e | ||
|
|
9a10c8c25a | ||
|
|
4461b33491 | ||
|
|
5ae9016f09 | ||
|
|
67ab8f63e0 | ||
|
|
40e64acd37 | ||
|
|
78fea7c7f2 | ||
|
|
4c61666fc7 | ||
|
|
0c6a12a6f0 | ||
|
|
0da245f912 | ||
|
|
eba0c9c4f7 | ||
|
|
25618c009e | ||
|
|
068ac40a76 | ||
|
|
5868fd1b7c | ||
|
|
540129854e | ||
|
|
f747ab568a | ||
|
|
f9f619c036 | ||
|
|
31ffeab4cd | ||
|
|
1673a18eb6 | ||
|
|
29cdadd563 | ||
|
|
8dffb38bf5 | ||
|
|
ecd5033efa | ||
|
|
a13227defa | ||
|
|
465a295dfa | ||
|
|
9c84a6936a | ||
|
|
f3d687cbea | ||
|
|
6894cb5dd7 | ||
|
|
809a57dbdb | ||
|
|
6b30a654a6 | ||
|
|
9b749ee2e0 | ||
|
|
c27974c5a3 | ||
|
|
6f41a4168d | ||
|
|
db7ac904e9 | ||
|
|
96eaec908d | ||
|
|
e0fd180edd | ||
|
|
fd68cbf56d | ||
|
|
9a1b1498bf | ||
|
|
12de3ae584 | ||
|
|
75c3bf8b56 | ||
|
|
52c12ea896 | ||
|
|
2e4bbb7740 | ||
|
|
1b0880e7df | ||
|
|
84f319894a | ||
|
|
2a9b6f8e8c | ||
|
|
c4e58eab3c | ||
|
|
920e9e65bd | ||
|
|
78b978efba | ||
|
|
31db35675b | ||
|
|
6b25c36476 | ||
|
|
73fa6295ec | ||
|
|
b5f43b3c1c | ||
|
|
fcc68a0a19 | ||
|
|
4c2d7c0d1c | ||
|
|
eee5e8eee7 | ||
|
|
e227cb912b | ||
|
|
6f3a0e8812 | ||
|
|
d7e4962fba | ||
|
|
7ad5181026 | ||
|
|
c2133c8fa5 | ||
|
|
de5264138d | ||
|
|
bfbcdba98c | ||
|
|
0632553a4a | ||
|
|
a97d416bfd | ||
|
|
b306539163 | ||
|
|
caea53ab91 | ||
|
|
d7ee0ca434 | ||
|
|
7fa4aa62f9 | ||
|
|
ca97adb724 | ||
|
|
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 | ||
|
|
c1c0eca7c4 | ||
|
|
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 | ||
|
|
fd81429f39 | ||
|
|
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'
|
||||
|
||||
210
Gemfile.lock
210
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)
|
||||
@@ -151,16 +157,16 @@ GEM
|
||||
awesome_nested_set (3.5.0)
|
||||
activerecord (>= 4.0.0, < 7.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.711.0)
|
||||
aws-sdk-core (3.170.0)
|
||||
aws-partitions (1.733.0)
|
||||
aws-sdk-core (3.171.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.62.0)
|
||||
aws-sdk-kms (1.63.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.119.1)
|
||||
aws-sdk-s3 (1.119.2)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.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)
|
||||
debug (1.7.2)
|
||||
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)
|
||||
@@ -249,8 +255,8 @@ GEM
|
||||
warden (~> 1.2.3)
|
||||
devise-encryptable (0.2.0)
|
||||
devise (>= 2.1.0)
|
||||
devise-i18n (1.10.3)
|
||||
devise (>= 4.8.0)
|
||||
devise-i18n (1.11.0)
|
||||
devise (>= 4.9.0)
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.5.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)
|
||||
@@ -343,7 +351,7 @@ GEM
|
||||
activerecord (>= 3.0)
|
||||
io-console (0.6.0)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.6.2)
|
||||
irb (1.6.3)
|
||||
reline (>= 0.3.0)
|
||||
jmespath (1.6.2)
|
||||
jquery-rails (4.4.0)
|
||||
@@ -367,14 +375,14 @@ GEM
|
||||
jsonapi-serializer (2.2.0)
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.7.0)
|
||||
knapsack_pro (3.8.0)
|
||||
knapsack_pro (3.9.0)
|
||||
rake
|
||||
launchy (2.5.0)
|
||||
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,29 +500,28 @@ 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
|
||||
rack-rewrite (1.5.1)
|
||||
rack-test (2.0.2)
|
||||
rack-test (2.1.0)
|
||||
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,10 +558,10 @@ 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)
|
||||
reline (0.3.3)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
rack (>= 1.4)
|
||||
@@ -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)
|
||||
@@ -706,7 +718,7 @@ GEM
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (2.4.2)
|
||||
uniform_notifier (1.16.0)
|
||||
valid_email2 (4.0.5)
|
||||
valid_email2 (4.0.6)
|
||||
activemodel (>= 3.2)
|
||||
mail (~> 2.5)
|
||||
validate_email (0.1.6)
|
||||
@@ -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
|
||||
|
||||
@@ -55,11 +55,10 @@ module Admin
|
||||
|
||||
def render_report_as(format)
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user)
|
||||
job = ReportJob.new
|
||||
JobProcessor.perform_forked(
|
||||
job,
|
||||
job = ReportJob.perform_later(
|
||||
report_class, spree_current_user, params, format
|
||||
)
|
||||
sleep 1 until job.done?
|
||||
|
||||
# This result has been rendered by Rails in safe mode already.
|
||||
job.result.html_safe # rubocop:disable Rails/OutputSafety
|
||||
|
||||
36
app/controllers/admin/vouchers_controller.rb
Normal file
36
app/controllers/admin/vouchers_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class VouchersController < ResourceController
|
||||
before_action :load_enterprise
|
||||
|
||||
def new
|
||||
@voucher = Voucher.new
|
||||
end
|
||||
|
||||
def create
|
||||
voucher_params = permitted_resource_params.merge(enterprise: @enterprise)
|
||||
@voucher = Voucher.create(voucher_params)
|
||||
|
||||
if @voucher.save
|
||||
redirect_to(
|
||||
"#{edit_admin_enterprise_path(@enterprise)}#vouchers_panel",
|
||||
flash: { success: flash_message_for(@voucher, :successfully_created) }
|
||||
)
|
||||
else
|
||||
flash[:error] = @voucher.errors.full_messages.to_sentence
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = Enterprise.find_by permalink: params[:enterprise_id]
|
||||
end
|
||||
|
||||
def permitted_resource_params
|
||||
params.require(:voucher).permit(:code)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -106,7 +106,9 @@ module Api
|
||||
end
|
||||
|
||||
def json_api_error(message, **options)
|
||||
{ errors: [{ detail: message }] }.merge(options)
|
||||
error_options = options.delete(:error_options) || {}
|
||||
|
||||
{ errors: [{ detail: message }.merge(error_options)] }.merge(options)
|
||||
end
|
||||
|
||||
def json_api_invalid(message, errors)
|
||||
|
||||
@@ -6,11 +6,17 @@ module Api
|
||||
module V1
|
||||
class CustomersController < Api::V1::BaseController
|
||||
include AddressTransformation
|
||||
include ExtraFields
|
||||
|
||||
skip_authorization_check only: :index
|
||||
|
||||
before_action :authorize_action, only: [:show, :update, :destroy]
|
||||
|
||||
# Query parameters
|
||||
before_action only: [:index] do
|
||||
@extra_customer_fields = extra_fields :customer, [:balance]
|
||||
end
|
||||
|
||||
def index
|
||||
@pagy, customers = pagy(search_customers, pagy_options)
|
||||
|
||||
@@ -51,7 +57,11 @@ module Api
|
||||
private
|
||||
|
||||
def customer
|
||||
@customer ||= Customer.find(params[:id])
|
||||
@customer ||= if action_name == "show"
|
||||
CustomersWithBalance.new(Customer.where(id: params[:id])).query.first!
|
||||
else
|
||||
Customer.find(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_action
|
||||
@@ -61,6 +71,11 @@ module Api
|
||||
def search_customers
|
||||
customers = visible_customers.includes(:bill_address, :ship_address)
|
||||
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
|
||||
|
||||
if @extra_customer_fields.include?(:balance)
|
||||
customers = CustomersWithBalance.new(customers).query
|
||||
end
|
||||
|
||||
customers.ransack(params[:q]).result.order(:id)
|
||||
end
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ module CheckoutCallbacks
|
||||
prepend_before_action :require_distributor_chosen
|
||||
|
||||
before_action :load_order, :associate_user, :load_saved_addresses, :load_saved_credit_cards
|
||||
before_action :allowed_shipping_methods, if: -> {
|
||||
params[:step] == "details"
|
||||
}
|
||||
before_action :load_shipping_methods, if: -> { params[:step] == "details" }
|
||||
|
||||
before_action :ensure_order_not_completed
|
||||
before_action :ensure_checkout_allowed
|
||||
@@ -48,22 +46,8 @@ module CheckoutCallbacks
|
||||
@selected_card = nil
|
||||
end
|
||||
|
||||
def allowed_shipping_methods
|
||||
@allowed_shipping_methods ||= sorted_available_shipping_methods.filter(
|
||||
&method(:supports_all_products_shipping_categories?)
|
||||
)
|
||||
end
|
||||
|
||||
def sorted_available_shipping_methods
|
||||
available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
def supports_all_products_shipping_categories?(shipping_method)
|
||||
(products_shipping_categories - shipping_method.shipping_categories.pluck(:id)).empty?
|
||||
end
|
||||
|
||||
def products_shipping_categories
|
||||
@products_shipping_categories ||= @order.products.pluck(:shipping_category_id).uniq
|
||||
def load_shipping_methods
|
||||
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
def redirect_to_shop?
|
||||
|
||||
32
app/controllers/concerns/extra_fields.rb
Normal file
32
app/controllers/concerns/extra_fields.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# To be included in api controllers for handeling query params
|
||||
module ExtraFields
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def invalid_query_param(name, status, msg)
|
||||
render status: status, json: json_api_error(msg, error_options:
|
||||
{
|
||||
title: I18n.t("api.query_param.error.title"),
|
||||
source: { parameter: name },
|
||||
status: status,
|
||||
code: Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
|
||||
})
|
||||
end
|
||||
|
||||
def extra_fields(type, available_fields)
|
||||
fields = params.dig(:extra_fields, type)&.split(',')&.compact&.map(&:to_sym)
|
||||
return [] if fields.blank?
|
||||
|
||||
unknown_fields = fields - available_fields
|
||||
|
||||
if unknown_fields.present?
|
||||
invalid_query_param(
|
||||
"extra_fields[#{type}]", :unprocessable_entity,
|
||||
I18n.t("api.query_param.error.extra_fields", fields: unknown_fields.join(', '))
|
||||
)
|
||||
end
|
||||
|
||||
fields
|
||||
end
|
||||
end
|
||||
19
app/controllers/concerns/manager_invitations.rb
Normal file
19
app/controllers/concerns/manager_invitations.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ManagerInvitations
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def create_new_manager(email, enterprise)
|
||||
password = Devise.friendly_token
|
||||
new_user = Spree::User.create(email: email, unconfirmed_email: email, password: password)
|
||||
new_user.reset_password_token = Devise.friendly_token
|
||||
# Same time as used in Devise's lib/devise/models/recoverable.rb.
|
||||
new_user.reset_password_sent_at = Time.now.utc
|
||||
if new_user.save
|
||||
enterprise.users << new_user
|
||||
EnterpriseMailer.manager_invitation(enterprise, new_user).deliver_later
|
||||
end
|
||||
|
||||
new_user
|
||||
end
|
||||
end
|
||||
@@ -24,7 +24,7 @@ class SplitCheckoutController < ::BaseController
|
||||
check_step if params[:step]
|
||||
recalculate_tax if params[:step] == "summary"
|
||||
|
||||
flash_error_when_no_shipping_method_available if allowed_shipping_methods.none?
|
||||
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -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
|
||||
|
||||
@@ -13,7 +13,7 @@ module Spree
|
||||
|
||||
def collection
|
||||
params[:q] ||= {}
|
||||
params[:q][:s] ||= "ascend_by_name"
|
||||
params[:q][:s] ||= "name asc"
|
||||
@search = super.ransack(params[:q])
|
||||
@pagy, @zones = pagy(@search.result, items: Spree::Config[:orders_per_page])
|
||||
@zones
|
||||
|
||||
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
|
||||
@@ -14,6 +14,7 @@ module Admin
|
||||
producers.size == 1 ? producers.first.id : nil
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def enterprise_side_menu_items(enterprise)
|
||||
is_shop = enterprise.sells != "none"
|
||||
show_properties = !!enterprise.is_primary_producer
|
||||
@@ -34,6 +35,7 @@ module Admin
|
||||
{ name: 'shipping_methods', icon_class: "icon-truck", show: show_shipping_methods },
|
||||
{ name: 'payment_methods', icon_class: "icon-money", show: show_payment_methods },
|
||||
{ name: 'enterprise_fees', icon_class: "icon-tasks", show: show_enterprise_fees },
|
||||
{ name: 'vouchers', icon_class: "icon-ticket", show: true },
|
||||
{ name: 'enterprise_permissions', icon_class: "icon-plug", show: true,
|
||||
href: admin_enterprise_relationships_path },
|
||||
{ name: 'inventory_settings', icon_class: "icon-list-ol", show: is_shop },
|
||||
@@ -42,5 +44,6 @@ module Admin
|
||||
{ name: 'users', icon_class: "icon-user", show: true }
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
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
|
||||
@@ -65,6 +65,7 @@ class Enterprise < ApplicationRecord
|
||||
has_many :inventory_items
|
||||
has_many :tag_rules
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers
|
||||
|
||||
delegate :latitude, :longitude, :city, :state_name, to: :address
|
||||
|
||||
@@ -450,7 +451,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 +460,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
|
||||
|
||||
|
||||
@@ -179,6 +179,8 @@ module Spree
|
||||
can [:admin, :create], :manager_invitation
|
||||
|
||||
can [:admin, :index], :oidc_setting
|
||||
|
||||
can [:admin, :create], Voucher
|
||||
end
|
||||
|
||||
def add_product_management_abilities(user)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,7 +20,7 @@ module Spree
|
||||
belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product'
|
||||
|
||||
delegate_belongs_to :product, :name, :description, :permalink, :available_on,
|
||||
:tax_category_id, :shipping_category_id, :meta_description,
|
||||
:tax_category_id, :shipping_category_id,
|
||||
:meta_keywords, :tax_category, :shipping_category
|
||||
|
||||
has_many :inventory_units, inverse_of: :variant
|
||||
|
||||
15
app/models/voucher.rb
Normal file
15
app/models/voucher.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
class Voucher < ApplicationRecord
|
||||
belongs_to :enterprise
|
||||
|
||||
validates :code, presence: true, uniqueness: { scope: :enterprise_id }
|
||||
|
||||
def value
|
||||
10
|
||||
end
|
||||
|
||||
def display_value
|
||||
Spree::Money.new(value)
|
||||
end
|
||||
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)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Forks into a separate process to contain memory usage and timeout errors.
|
||||
class JobProcessor
|
||||
def self.perform_forked(job, *args)
|
||||
# Reports should abort when puma threads are killed to avoid wasting
|
||||
# resources. Nobody would be collecting the result. We still need to
|
||||
# implement a way to email or download reports later.
|
||||
timeout = ENV.fetch("RACK_TIMEOUT_WAIT_TIMEOUT", "30").to_i
|
||||
|
||||
child = fork do
|
||||
Process.setproctitle("Job worker #{job.job_id}")
|
||||
Timeout.timeout(timeout) do
|
||||
job.perform(*args)
|
||||
end
|
||||
|
||||
# Exit is not a good idea within a Rails process but Rubocop doesn't know
|
||||
# that we are in a forked process.
|
||||
exit # rubocop:disable Rails/Exit
|
||||
end
|
||||
|
||||
# Wait for the forked child process to exit.
|
||||
Process.waitpid(child)
|
||||
ensure
|
||||
# If this Puma thread is interrupted then we need to detach the child
|
||||
# process to avoid it becoming a zombie.
|
||||
Process.detach(child)
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
@@ -8,7 +8,7 @@ module PermittedAttributes
|
||||
:variant_unit, :variant_unit_scale, :unit_value, :unit_description, :variant_unit_name,
|
||||
:display_as, :sku, :available_on, :group_buy, :group_buy_unit_size,
|
||||
:taxon_ids, :primary_taxon_id, :tax_category_id, :shipping_category_id,
|
||||
:meta_keywords, :meta_description, :notes, :inherits_properties,
|
||||
:meta_keywords, :notes, :inherits_properties,
|
||||
{ product_properties_attributes: [:id, :property_name, :value],
|
||||
variants_attributes: [PermittedAttributes::Variant.attributes],
|
||||
images_attributes: [:attachment] }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:name] }.legend")
|
||||
|
||||
- when 'vouchers'
|
||||
- if feature?(:vouchers, spree_current_user)
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
= render "admin/enterprises/form/#{ item[:form_name] || item[:name] }", f: f
|
||||
|
||||
- else
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
|
||||
@@ -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 }
|
||||
|
||||
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
@@ -0,0 +1,33 @@
|
||||
.text-right
|
||||
%a.button{ href: "#{new_admin_enterprise_voucher_path(@enterprise)}"}
|
||||
= t('.add_new')
|
||||
%br
|
||||
|
||||
- if @enterprise.vouchers.present?
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.voucher_code')
|
||||
%th= t('.rate')
|
||||
/%th= t('.label')
|
||||
/%th= t('.purpose')
|
||||
/%th= t('.expiry')
|
||||
/%th= t('.use_limit')
|
||||
/%th= t('.customers')
|
||||
/%th= t('.net_value')
|
||||
%tbody
|
||||
- @enterprise.vouchers.each do |voucher|
|
||||
%tr
|
||||
%td= voucher.code
|
||||
%td= voucher.display_value
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
|
||||
- else
|
||||
%p.text-center
|
||||
= t('.no_voucher_yet')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.side_menu#side_menu
|
||||
- if @enterprise
|
||||
- enterprise_side_menu_items(@enterprise).each do |item|
|
||||
- next unless item[:show]
|
||||
- next if !item[:show] || (item[:name] == 'vouchers' && !feature?(:vouchers, spree_current_user))
|
||||
%a.menu_item{ href: item[:href] || "##{item[:name]}_panel", id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: item[:selected] }
|
||||
%i{ class: item[:icon_class] }
|
||||
%span= t(".enterprise.#{item[:name] }")
|
||||
|
||||
23
app/views/admin/vouchers/new.html.haml
Normal file
23
app/views/admin/vouchers/new.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
= form_with model: @voucher, url: admin_enterprise_vouchers_path(@enterprise), html: { name: "voucher_form" } do |f|
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
.four.columns.alpha.text-right
|
||||
%a.button{ href: "#{edit_admin_enterprise_path(@enterprise)}#!#vouchers_panel"}
|
||||
= t('.back')
|
||||
.twelve.columns.omega
|
||||
.row
|
||||
.eight.columns.text-center
|
||||
%legend= t(".legend")
|
||||
.four.columns.text-right
|
||||
= f.submit t('.save'), class: 'red'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :code, t('.voucher_code')
|
||||
.omega.eight.columns
|
||||
= f.text_area :code, rows: 6, class: 'fullwidth'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :amount, t('.voucher_amount')
|
||||
.omega.eight.columns
|
||||
= Spree::Money.currency_symbol
|
||||
= f.text_field :amount, value: @voucher.value, disabled: true
|
||||
@@ -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 }}
|
||||
|
||||
@@ -21,24 +21,27 @@
|
||||
|
||||
.row.index-controls{'ng-show' => '!RequestMonitor.loading && orders.length > 0'}
|
||||
%div{style: "display: flex; justify-content: space-between;"}
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
{{ "spree.admin.orders.index.selected" | t:{count: selected_orders.length} }}
|
||||
.ofn-drop-down{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
%span{ :class => 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
{{ "spree.admin.orders.index.selected" | t:{count: selected_orders.length} }}
|
||||
.ofn-drop-down{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
%span{ :class => 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%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')
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
%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')
|
||||
%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')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('.cancel_orders')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('.cancel_orders')
|
||||
|
||||
= render partial: 'admin/shared/angular_per_page_controls', locals: { position: "right", model: "orders" }
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
= f.field_container :shipping_categories do
|
||||
= f.label :shipping_category_id, t(:shipping_categories)
|
||||
= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, { :include_blank => t(:none) }, { :class => 'select2' })
|
||||
= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, {}, { :class => 'select2' })
|
||||
= f.error_message_on :shipping_category
|
||||
|
||||
= f.field_container :tax_category do
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
%span.icon-question-sign{ 'ofn-with-tip' => t('admin.products.seo.product_search_tip') }
|
||||
%br/
|
||||
= f.text_field :meta_keywords, :class => 'fullwidth', :rows => 6
|
||||
= f.field_container :meta_description do
|
||||
= f.label :meta_description, t('admin.products.seo.SEO_keywords')
|
||||
%span.icon-question-sign{ 'ofn-with-tip' => t('admin.products.seo.seo_tip') }
|
||||
%br/
|
||||
= f.text_field :meta_description, :class => 'fullwidth', :rows => 6
|
||||
.alpha.eleven.columns
|
||||
= f.field_container :notes do
|
||||
= f.label :notes, t(:notes)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart'
|
||||
= tab :reports, url: main_app.admin_reports_path, icon: 'icon-file'
|
||||
= tab :general_settings, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path
|
||||
= tab :enterprises, :enterprise_relationships, :oidc_settings, url: main_app.admin_enterprises_path
|
||||
= tab :enterprises, :enterprise_relationships, :vouchers, :oidc_settings, url: main_app.admin_enterprises_path
|
||||
= tab :customers, url: main_app.admin_customers_path
|
||||
= tab :enterprise_groups, url: main_app.admin_enterprise_groups_path, label: 'groups'
|
||||
- if can? :admin, Spree::User
|
||||
|
||||
@@ -66,12 +66,12 @@
|
||||
.field
|
||||
= f.label 'weight', t(:weight)+' (kg)'
|
||||
- value = number_with_precision(@variant.weight, precision: 2)
|
||||
= f.text_field 'weight', value: value, class: 'fullwidth'
|
||||
= f.number_field 'weight', value: value, class: 'fullwidth', step: 0.01
|
||||
|
||||
- [:height, :width, :depth].each do |field|
|
||||
.field
|
||||
= f.label field, t(field)
|
||||
- value = number_with_precision(@variant.send(field), precision: 2)
|
||||
= f.text_field field, value: value, class: 'fullwidth'
|
||||
= f.number_field field, value: value, class: 'fullwidth', step: 0.01
|
||||
|
||||
.clear
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,40 @@ export default class extends Controller {
|
||||
|
||||
// only display the default panel
|
||||
this.defaultTarget.style.display = "block";
|
||||
|
||||
// Display panel specified in url anchor
|
||||
const anchors = window.location.toString().split("#");
|
||||
let anchor = anchors.length > 1 ? anchors.pop() : "";
|
||||
|
||||
if (anchor != "") {
|
||||
// Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel" :(
|
||||
// strip the starting / if any
|
||||
if (anchor[0] == "/") {
|
||||
anchor = anchor.slice(1);
|
||||
}
|
||||
|
||||
this.updateActivePanel(anchor);
|
||||
|
||||
// tab
|
||||
const tab_id = anchor.split("_panel").shift();
|
||||
this.updateActiveTab(tab_id);
|
||||
}
|
||||
}
|
||||
|
||||
changeActivePanel(event) {
|
||||
this.updateActivePanel(`${event.currentTarget.id}_panel`);
|
||||
}
|
||||
|
||||
updateActivePanel(panel_id) {
|
||||
const newActivePanel = this.panelTargets.find(
|
||||
(panel) => panel.id == `${event.currentTarget.id}_panel`
|
||||
(panel) => panel.id == panel_id
|
||||
);
|
||||
|
||||
if (newActivePanel === undefined) {
|
||||
// No panel found
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentActivePanel.style.display = "none";
|
||||
newActivePanel.style.display = "block";
|
||||
}
|
||||
@@ -28,6 +55,18 @@ export default class extends Controller {
|
||||
event.currentTarget.classList.add(`${this.classNameValue}`);
|
||||
}
|
||||
|
||||
updateActiveTab(tab_id) {
|
||||
const newActiveTab = this.tabTargets.find((tab) => tab.id == tab_id);
|
||||
|
||||
if (newActiveTab === undefined) {
|
||||
// No tab found
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentActiveTab.classList.remove(`${this.classNameValue}`);
|
||||
newActiveTab.classList.add(`${this.classNameValue}`);
|
||||
}
|
||||
|
||||
get currentActiveTab() {
|
||||
return this.tabTargets.find((tab) => tab.classList.contains("selected"));
|
||||
}
|
||||
|
||||
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,22 +1,28 @@
|
||||
require "flipper"
|
||||
require "flipper/adapters/active_record"
|
||||
require "flipper/instrumentation/log_subscriber"
|
||||
require "open_food_network/feature_toggle"
|
||||
|
||||
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)
|
||||
Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? }
|
||||
|
||||
Flipper::UI.configure do |config|
|
||||
config.descriptions_source = ->(_keys) do
|
||||
# return has to be hash of {String key => String description}
|
||||
OpenFoodNetwork::FeatureToggle::CURRENT_FEATURES
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.env.production?
|
||||
Flipper::UI.configure do |config|
|
||||
config.banner_text = '⚠️ Production environment: be aware that the changes have an impact on the application. Please, read the how-to before: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Feature-toggle-with-Flipper'
|
||||
# Defaults to false. Set to true to show feature descriptions on the list
|
||||
# page as well as the view page.
|
||||
# config.show_feature_description_in_list = true
|
||||
|
||||
if Rails.env.production?
|
||||
config.banner_text = <<~TEXT
|
||||
⚠️ Production environment: be aware that the changes have an impact on the
|
||||
application. Please read the how-to before:
|
||||
https://github.com/openfoodfoundation/openfoodnetwork/wiki/Feature-toggles
|
||||
TEXT
|
||||
config.banner_class = 'danger'
|
||||
end
|
||||
end
|
||||
|
||||
Rails.configuration.middleware.use Flipper::Middleware::Memoizer, preload_all: true
|
||||
|
||||
Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? }
|
||||
# Add known feature toggles. This may fail if the database isn't setup yet.
|
||||
OpenFoodNetwork::FeatureToggle.setup! rescue ActiveRecord::StatementInvalid
|
||||
|
||||
@@ -650,7 +650,6 @@ ar:
|
||||
seo:
|
||||
product_search_keywords: "كلمات البحث المنتج"
|
||||
product_search_tip: "اكتب كلمات للمساعدة في البحث عن منتجاتك في المتاجر. استخدام المسافة لفصل كل كلمة رئيسية."
|
||||
SEO_keywords: "SEO الكلمات الرئيسية"
|
||||
seo_tip: "اكتب كلمات للمساعدة في البحث عن منتجاتك على الويب. استخدام المسافة لفصل كل كلمة رئيسية."
|
||||
search: "بحث"
|
||||
properties:
|
||||
@@ -1065,6 +1064,9 @@ ar:
|
||||
add_unregistered_user: "إضافة مستخدم غير مسجل"
|
||||
email_confirmed: "تأكيد البريد الإلكتروني"
|
||||
email_not_confirmed: "البريد الإلكتروني غير مؤكد"
|
||||
vouchers:
|
||||
rate: معدل
|
||||
customers: عميل
|
||||
actions:
|
||||
edit_profile: الإعدادات
|
||||
properties: الخصائص
|
||||
@@ -1506,6 +1508,11 @@ ar:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: لا يمكن حذف هذا الجدول لأنه يحتوي على اشتراكات مقترنة
|
||||
vouchers:
|
||||
new:
|
||||
back: عودة
|
||||
save: حفظ
|
||||
voucher_amount: القيمة
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "تم إلغاء الاتصال بـ Stripe"
|
||||
|
||||
@@ -634,7 +634,6 @@ ca:
|
||||
seo:
|
||||
product_search_keywords: "Paraules clau de cerca de producte"
|
||||
product_search_tip: "Escriviu paraules per ajudar-vos a cercar els vostres productes a les botigues. Utilitzeu espai per separar cada paraula clau."
|
||||
SEO_keywords: "Paraules clau de SEO"
|
||||
seo_tip: "Escriviu paraules per ajudar-vos a cercar els vostres productes a la web. Utilitzeu espai per separar cada paraula clau."
|
||||
search: "Cerca"
|
||||
properties:
|
||||
@@ -1049,6 +1048,9 @@ ca:
|
||||
add_unregistered_user: "Afegeix un usuari no registrat"
|
||||
email_confirmed: "S'ha confirmat el correu electrònic"
|
||||
email_not_confirmed: "Correu electrònic no confirmat"
|
||||
vouchers:
|
||||
rate: Taxa
|
||||
customers: Consumidora
|
||||
actions:
|
||||
edit_profile: Configuració
|
||||
properties: Propietats
|
||||
@@ -1464,6 +1466,11 @@ ca:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: Aquesta programació no es pot suprimir perquè té subscripcions associades
|
||||
vouchers:
|
||||
new:
|
||||
back: Enrere
|
||||
save: Desa
|
||||
voucher_amount: Quantitat
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "S'ha cancel·lat la connexió a Stripe"
|
||||
|
||||
@@ -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"
|
||||
@@ -637,7 +641,6 @@ cy:
|
||||
seo:
|
||||
product_search_keywords: "Allweddeiriau Chwilio Cynnyrch"
|
||||
product_search_tip: "Teipiwch eiriau i helpu i chwilio am eich cynnyrch yn y siopau. Defnyddiwch fwlch i wahanu pob allweddair."
|
||||
SEO_keywords: "Allweddeiriau SEO"
|
||||
seo_tip: "Teipiwch eiriau i helpu chwilio am eich cynnyrch ar y we. Defnyddiwch fwlch i wahanu pob allweddair."
|
||||
search: "Chwilio"
|
||||
properties:
|
||||
@@ -1053,6 +1056,9 @@ cy:
|
||||
add_unregistered_user: "Ychwanegwch ddefnyddiwr anghofrestredig"
|
||||
email_confirmed: "Cadarnhawyd yr e-bost "
|
||||
email_not_confirmed: "E-bost heb ei gadarnhau"
|
||||
vouchers:
|
||||
rate: Cyfradd
|
||||
customers: Cwsmer
|
||||
actions:
|
||||
edit_profile: Gosodiadau
|
||||
properties: Manylion
|
||||
@@ -1321,6 +1327,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
|
||||
@@ -1493,6 +1500,11 @@ cy:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: Nid yw'n bosib dileu'r amserlen hon oherwydd bod tanysgrifiadau cysylltiedig yn berthnasol
|
||||
vouchers:
|
||||
new:
|
||||
back: Yn ôl
|
||||
save: Cadw
|
||||
voucher_amount: Nifer
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Canslwyd y cysylltiad â Stripe"
|
||||
@@ -1516,6 +1528,9 @@ cy:
|
||||
destroy_attachment_does_not_exist: "Nid yw'r ffeil Telerau ac Amodau yn bodoli"
|
||||
orders:
|
||||
failed_to_update: "Wedi methu diweddaru'r archeb"
|
||||
query_param:
|
||||
error:
|
||||
title: Invalid query parameter
|
||||
checkout:
|
||||
already_ordered:
|
||||
cart: "basged"
|
||||
@@ -1810,6 +1825,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 +1879,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 +1898,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 +2681,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 +2705,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 +2722,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 +2904,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"
|
||||
@@ -3412,6 +3438,13 @@ cy:
|
||||
first: "Yn gyntaf"
|
||||
previous: "Blaenorol"
|
||||
last: "Diwethaf"
|
||||
webhook_endpoints:
|
||||
create:
|
||||
success: Webhook endpoint failed to create
|
||||
error: Webhook endpoint failed to create
|
||||
destroy:
|
||||
success: Webhook endpoint successfully deleted
|
||||
error: Webhook endpoint failed to delete
|
||||
spree:
|
||||
add_country: "Ychwanegu gwlad"
|
||||
add_state: "Ychwanegu sir"
|
||||
@@ -3452,6 +3485,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 +3571,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 +3805,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 +3954,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"
|
||||
@@ -4183,6 +4221,16 @@ cy:
|
||||
api_keys:
|
||||
regenerate_key: "Allwedd Adfer"
|
||||
title: Allwedd API
|
||||
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: Gosodiadau'r Datblygwr
|
||||
form:
|
||||
@@ -4340,6 +4388,7 @@ cy:
|
||||
search_input:
|
||||
placeholder: Chwilio
|
||||
selector_with_filter:
|
||||
selected_items: "Dewiswyd %{count}"
|
||||
search_placeholder: Chwilio
|
||||
pagination:
|
||||
next: Nesaf
|
||||
|
||||
@@ -624,7 +624,6 @@ de_CH:
|
||||
seo:
|
||||
product_search_keywords: "Stichwörter für die Produktsuche"
|
||||
product_search_tip: "Geben Sie Stichwörter ein, damit Ihre Produkte in den Läden besser gefunden werden. Trennen Sie mehrere Stichwörter voneinander mit Leerzeichen."
|
||||
SEO_keywords: "Stichwörter für die Suche im Internet"
|
||||
seo_tip: "Geben Sie Stichwörter ein, damit Ihre Produkte im Internet besser gefunden werden. Trennen Sie mehrere Stichwörter voneinander mit Leerzeichen."
|
||||
search: "Suche"
|
||||
properties:
|
||||
@@ -1034,6 +1033,9 @@ de_CH:
|
||||
add_unregistered_user: "Laden Sie einen nicht registrierten Benutzer ein"
|
||||
email_confirmed: "E-Mail-Adresse bestätigt"
|
||||
email_not_confirmed: "E-Mail-Adresse nicht bestätigt"
|
||||
vouchers:
|
||||
rate: Steuersatz
|
||||
customers: Kunde
|
||||
actions:
|
||||
edit_profile: Einstellungen
|
||||
properties: Eigenschaften
|
||||
@@ -1454,6 +1456,11 @@ de_CH:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: Dieser Zeitplan kann nicht gelöscht werden, da ihm Abonnements zugeordnet sind.
|
||||
vouchers:
|
||||
new:
|
||||
back: Zurück
|
||||
save: Speichern
|
||||
voucher_amount: Betrag
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Die Verbindung zu Stripe wurde abgebrochen."
|
||||
|
||||
@@ -640,7 +640,6 @@ de_DE:
|
||||
seo:
|
||||
product_search_keywords: "Stichwörter für die Produktsuche"
|
||||
product_search_tip: "Geben Sie Stichwörter ein, damit Ihre Produkte in den Läden besser gefunden werden. Trennen Sie mehrere Stichwörter voneinander mit Leerzeichen."
|
||||
SEO_keywords: "Stichwörter für die Suche im Internet"
|
||||
seo_tip: "Geben Sie Stichwörter ein, damit Ihre Produkte im Internet besser gefunden werden. Trennen Sie mehrere Stichwörter voneinander mit Leerzeichen."
|
||||
search: "Suche"
|
||||
properties:
|
||||
@@ -1058,6 +1057,9 @@ de_DE:
|
||||
add_unregistered_user: "Laden Sie einen nicht registrierten Benutzer ein"
|
||||
email_confirmed: "E-Mail-Adresse bestätigt"
|
||||
email_not_confirmed: "E-Mail-Adresse nicht bestätigt"
|
||||
vouchers:
|
||||
rate: Steuersatz
|
||||
customers: Kunde
|
||||
actions:
|
||||
edit_profile: Einstellungen
|
||||
properties: Eigenschaften
|
||||
@@ -1499,6 +1501,11 @@ de_DE:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: Dieser Zeitplan kann nicht gelöscht werden, da ihm Abonnements zugeordnet sind.
|
||||
vouchers:
|
||||
new:
|
||||
back: Zurück
|
||||
save: Speichern
|
||||
voucher_amount: Summe
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Die Verbindung zu Stripe wurde abgebrochen."
|
||||
|
||||
@@ -728,7 +728,6 @@ en:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -936,6 +935,7 @@ en:
|
||||
legend: "Address"
|
||||
business_details:
|
||||
legend: "Business Details"
|
||||
upload: 'upload'
|
||||
abn: ABN
|
||||
abn_placeholder: eg. 99 123 456 789
|
||||
acn: ACN
|
||||
@@ -1143,6 +1143,18 @@ en:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
legend: Vouchers
|
||||
voucher_code: Voucher Code
|
||||
rate: Rate
|
||||
label: Label
|
||||
purpose: Purpose
|
||||
expiry: Expiry
|
||||
use_limit: Use/Limit
|
||||
customers: Customer
|
||||
net_value: Net Value
|
||||
add_new: Add New
|
||||
no_voucher_yet: No Vouchers yet
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1381,6 +1393,7 @@ en:
|
||||
tag_rules: "Tag Rules"
|
||||
shop_preferences: "Shop Preferences"
|
||||
users: "Users"
|
||||
vouchers: Vouchers
|
||||
enterprise_group:
|
||||
primary_details: "Primary Details"
|
||||
users: "Users"
|
||||
@@ -1589,6 +1602,13 @@ en:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
legend: New Voucher
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_code: Voucher Code
|
||||
voucher_amount: Amount
|
||||
|
||||
# Admin controllers
|
||||
controllers:
|
||||
@@ -1602,6 +1622,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 +1644,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 +3281,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 +3593,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 +3981,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 +4398,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:
|
||||
|
||||
@@ -510,7 +510,6 @@ en_AU:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -897,6 +896,9 @@ en_AU:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1303,6 +1305,11 @@ en_AU:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
|
||||
@@ -477,7 +477,6 @@ en_BE:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -839,6 +838,9 @@ en_BE:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1220,6 +1222,11 @@ en_BE:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
|
||||
@@ -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"
|
||||
@@ -640,7 +650,6 @@ en_CA:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -1056,6 +1065,9 @@ en_CA:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1313,6 +1325,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 +1336,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
|
||||
@@ -1495,6 +1510,11 @@ en_CA:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
@@ -2671,6 +2691,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 +2715,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 +2732,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 +3106,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..."
|
||||
|
||||
@@ -482,7 +482,6 @@ en_DE:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -847,6 +846,9 @@ en_DE:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1230,6 +1232,11 @@ en_DE:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
|
||||
@@ -650,7 +650,6 @@ en_FR:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -1066,6 +1065,9 @@ en_FR:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1508,6 +1510,11 @@ en_FR:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
|
||||
@@ -6,12 +6,22 @@ en_GB:
|
||||
spree/shipping_method: Shipping Method
|
||||
attributes:
|
||||
spree/order/ship_address:
|
||||
address1: "Shipping address (Street + House number)"
|
||||
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 postcode"
|
||||
spree/order/bill_address:
|
||||
address1: "Billing address (Street + House number)"
|
||||
zipcode: "Billing address postcode"
|
||||
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"
|
||||
@@ -640,7 +650,6 @@ en_GB:
|
||||
seo:
|
||||
product_search_keywords: "Product Search Keywords"
|
||||
product_search_tip: "Type words to help search your products in the shops. Use space to separate each keyword."
|
||||
SEO_keywords: "SEO Keywords"
|
||||
seo_tip: "Type words to help search your products in the web. Use space to separate each keyword."
|
||||
search: "Search"
|
||||
properties:
|
||||
@@ -846,6 +855,7 @@ en_GB:
|
||||
legend: "Address"
|
||||
business_details:
|
||||
legend: "Business Details"
|
||||
upload: 'upload'
|
||||
abn: Company Number
|
||||
abn_placeholder: eg. 99 123 456 789
|
||||
acn: Charity Number
|
||||
@@ -1056,6 +1066,9 @@ en_GB:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
rate: Rate
|
||||
customers: Customer
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1325,6 +1338,7 @@ en_GB:
|
||||
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
|
||||
@@ -1497,6 +1511,11 @@ en_GB:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_amount: Amount
|
||||
controllers:
|
||||
enterprises:
|
||||
stripe_connect_cancelled: "Connection to Stripe has been cancelled"
|
||||
@@ -1520,6 +1539,10 @@ en_GB:
|
||||
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}"
|
||||
checkout:
|
||||
already_ordered:
|
||||
cart: "cart"
|
||||
@@ -3383,6 +3406,13 @@ en_GB:
|
||||
first: "First"
|
||||
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:
|
||||
add_country: "Add country"
|
||||
add_state: "Add county"
|
||||
@@ -3744,6 +3774,8 @@ en_GB:
|
||||
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"
|
||||
@@ -4159,6 +4191,16 @@ en_GB:
|
||||
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:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user