mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-28 01:53:25 +00:00
Compare commits
335 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b455755bfc | ||
|
|
619ecf9432 | ||
|
|
4f152a9151 | ||
|
|
8d7252f078 | ||
|
|
08399e753d | ||
|
|
8d721ccac8 | ||
|
|
b7a1b39c1a | ||
|
|
e276fb0386 | ||
|
|
f86eb3fb82 | ||
|
|
0d46a3bc2e | ||
|
|
0418163ad7 | ||
|
|
2e67899bcc | ||
|
|
e2536ffe71 | ||
|
|
2c6b758f66 | ||
|
|
fb0f379c43 | ||
|
|
7a4aa9dcb1 | ||
|
|
4e8d37b0b5 | ||
|
|
616de3a9c0 | ||
|
|
16b3da66c2 | ||
|
|
691995eeaa | ||
|
|
045482e07d | ||
|
|
62f25d4d31 | ||
|
|
618c7028e2 | ||
|
|
13cec27f6b | ||
|
|
5b925517f0 | ||
|
|
167099badf | ||
|
|
40d8839bc4 | ||
|
|
da1e1a9859 | ||
|
|
43ad7654b6 | ||
|
|
15d83724ea | ||
|
|
47652e9d25 | ||
|
|
b5315c2123 | ||
|
|
97ce4eaccd | ||
|
|
8814427677 | ||
|
|
4b69e192ec | ||
|
|
9c99f4868c | ||
|
|
39d59c3a5f | ||
|
|
ad9a12da0e | ||
|
|
9968776726 | ||
|
|
d99a88817d | ||
|
|
1b36cce816 | ||
|
|
4b157abd4d | ||
|
|
dc494bb0f9 | ||
|
|
b3e7b12b86 | ||
|
|
b69e6827cc | ||
|
|
340f459912 | ||
|
|
33a96d5a0f | ||
|
|
93d6db9d44 | ||
|
|
c1068af0ba | ||
|
|
482c2a4a6e | ||
|
|
2557a4de39 | ||
|
|
ab1f43f1ac | ||
|
|
f921524588 | ||
|
|
5cbd507c1e | ||
|
|
532bfaaa2d | ||
|
|
70b31a4212 | ||
|
|
9da3b0ea01 | ||
|
|
51df612c51 | ||
|
|
3be0cca230 | ||
|
|
283d13eb35 | ||
|
|
b21224d5ff | ||
|
|
4744d7b741 | ||
|
|
ac1dd74e23 | ||
|
|
970f486ec0 | ||
|
|
8c244e8b56 | ||
|
|
c3cc01c677 | ||
|
|
c63e60d782 | ||
|
|
11e8c9456d | ||
|
|
860a21f86f | ||
|
|
d24af18ff4 | ||
|
|
33b191f439 | ||
|
|
a690d39864 | ||
|
|
7c0586db7b | ||
|
|
461fd00ccd | ||
|
|
d33cabd6b2 | ||
|
|
7b21a9d30f | ||
|
|
c00e7eeecf | ||
|
|
afdb044386 | ||
|
|
b98552003c | ||
|
|
38f1754738 | ||
|
|
f872201fef | ||
|
|
ad23735384 | ||
|
|
6fd78d4647 | ||
|
|
cd3a80c502 | ||
|
|
f8c88ea8c7 | ||
|
|
4ba55c4067 | ||
|
|
813249bad7 | ||
|
|
162c58ac39 | ||
|
|
02d47d0a0d | ||
|
|
e86bf441ab | ||
|
|
f9617b8156 | ||
|
|
4650e30c09 | ||
|
|
2749965e73 | ||
|
|
e87965bda0 | ||
|
|
9f2c35d407 | ||
|
|
79af9efd29 | ||
|
|
9dcb3ec748 | ||
|
|
90c23d0245 | ||
|
|
99e238d92d | ||
|
|
b731f9b9e4 | ||
|
|
da90cdd9f6 | ||
|
|
f86c925209 | ||
|
|
0040e4d454 | ||
|
|
876d37d19a | ||
|
|
36d617bceb | ||
|
|
7c0528ad83 | ||
|
|
f25d51e772 | ||
|
|
8eb9709a04 | ||
|
|
5f31baa022 | ||
|
|
7b559e2f83 | ||
|
|
a94ccd4493 | ||
|
|
fc6ed9ab4b | ||
|
|
c115ab7a0d | ||
|
|
f3428494fc | ||
|
|
d748972fca | ||
|
|
b9c7925008 | ||
|
|
c11b93a4dc | ||
|
|
61e0688392 | ||
|
|
5971cdc6e2 | ||
|
|
b3a1d1269a | ||
|
|
a0011bd2e9 | ||
|
|
2219513c93 | ||
|
|
fb713d8721 | ||
|
|
90761e0766 | ||
|
|
6fb4048bfd | ||
|
|
c3def926f1 | ||
|
|
d6cde5c793 | ||
|
|
756e876f61 | ||
|
|
5e57325ce2 | ||
|
|
cd956dadda | ||
|
|
2a3b076170 | ||
|
|
e6d556c809 | ||
|
|
f35b2be228 | ||
|
|
cc9cb966b5 | ||
|
|
34f9e08824 | ||
|
|
7d5db81017 | ||
|
|
1b03528aca | ||
|
|
9a90e46b78 | ||
|
|
7e4886ad67 | ||
|
|
9297dbfa63 | ||
|
|
7bfe51d067 | ||
|
|
6ad49cc080 | ||
|
|
b58834b11f | ||
|
|
d4811648f1 | ||
|
|
77fe1fa6f9 | ||
|
|
2a7e8816c0 | ||
|
|
988f903164 | ||
|
|
108e313e48 | ||
|
|
e502aad7c9 | ||
|
|
9af57e1fc4 | ||
|
|
a0c23825b8 | ||
|
|
4e5238d1bb | ||
|
|
26b8f62325 | ||
|
|
ee087f9eff | ||
|
|
3152362b9d | ||
|
|
601c921b4b | ||
|
|
39fbf01e98 | ||
|
|
69586ae5ae | ||
|
|
c87eb05c0e | ||
|
|
a1f363c53b | ||
|
|
76c6362db0 | ||
|
|
5c3308de36 | ||
|
|
a82b2c2799 | ||
|
|
4895010fe0 | ||
|
|
962097f237 | ||
|
|
941707a0df | ||
|
|
85439b4b54 | ||
|
|
30804da259 | ||
|
|
35724bec81 | ||
|
|
1e2b8bad3f | ||
|
|
6cbc6fbca3 | ||
|
|
88a0737916 | ||
|
|
b0b03cbb5f | ||
|
|
f5823bd618 | ||
|
|
d9453979b1 | ||
|
|
2cde74b91a | ||
|
|
3fd007fa3d | ||
|
|
e3431c7954 | ||
|
|
9fa715c709 | ||
|
|
f7e119d06f | ||
|
|
52ee5929cf | ||
|
|
1e58882217 | ||
|
|
ca23f12451 | ||
|
|
cce37aa915 | ||
|
|
f808fe9685 | ||
|
|
dd5db396b4 | ||
|
|
e121a799ed | ||
|
|
5dfef4a3ae | ||
|
|
8236603f76 | ||
|
|
315d52961a | ||
|
|
dcb6f4676d | ||
|
|
3455ceb722 | ||
|
|
7361b2da0b | ||
|
|
3ebacbc31a | ||
|
|
c0bcf177e7 | ||
|
|
8dfede11c6 | ||
|
|
bac123f223 | ||
|
|
fdc775ae6d | ||
|
|
26b39d6626 | ||
|
|
8953e8481d | ||
|
|
1ce76a3166 | ||
|
|
1c703905fe | ||
|
|
feed223ab4 | ||
|
|
90f962a886 | ||
|
|
82f6484031 | ||
|
|
282fb44da4 | ||
|
|
82f40d2e93 | ||
|
|
cae1655ec3 | ||
|
|
e8e48f7c64 | ||
|
|
26702e6f0d | ||
|
|
8cd9e94148 | ||
|
|
a934b60f67 | ||
|
|
7fc9a6bf93 | ||
|
|
781bf940f6 | ||
|
|
92382ca473 | ||
|
|
76a3e913df | ||
|
|
23ab6bb489 | ||
|
|
fd3c1c1343 | ||
|
|
368da19993 | ||
|
|
419f4490d6 | ||
|
|
39245d55e2 | ||
|
|
fe8200b7e8 | ||
|
|
c799f15067 | ||
|
|
54f1047dcb | ||
|
|
c1e599deef | ||
|
|
f3c60148c1 | ||
|
|
2e08c9c44e | ||
|
|
93f2af7e7d | ||
|
|
cb7a4b67ce | ||
|
|
41a8d06326 | ||
|
|
a957df1205 | ||
|
|
a1bdfa0a20 | ||
|
|
1adf94093e | ||
|
|
476daf0d30 | ||
|
|
18ef5cc69a | ||
|
|
a69528c432 | ||
|
|
9dfecde6a7 | ||
|
|
c73f28b434 | ||
|
|
0cd9fa91a8 | ||
|
|
252943e9de | ||
|
|
cab4b2fb28 | ||
|
|
80bd6defcb | ||
|
|
bd367cb154 | ||
|
|
d0f48687e2 | ||
|
|
25063d2c4d | ||
|
|
bad04b70a9 | ||
|
|
5479572a08 | ||
|
|
79a9dbcf68 | ||
|
|
61c7c26822 | ||
|
|
969dcae8d0 | ||
|
|
ce5a95ff4f | ||
|
|
0d5330d388 | ||
|
|
3676acf244 | ||
|
|
caf6b087c1 | ||
|
|
24d6c1e386 | ||
|
|
a120e390d0 | ||
|
|
98a2bf5d47 | ||
|
|
bb0c1e7a0f | ||
|
|
55a15b914c | ||
|
|
8ce14a55c8 | ||
|
|
d1f47f6956 | ||
|
|
2d50dace20 | ||
|
|
bbbbe71bc4 | ||
|
|
b3dc76b8cf | ||
|
|
ad4b26e86d | ||
|
|
c2a7a89381 | ||
|
|
68e3623861 | ||
|
|
577aa55f98 | ||
|
|
b8e62b3d84 | ||
|
|
dbb8e07b9a | ||
|
|
19550ed4fe | ||
|
|
4d0c710e3b | ||
|
|
fccde70690 | ||
|
|
b8de75b1ef | ||
|
|
06bfd07fec | ||
|
|
e98cf78b4c | ||
|
|
13229cc0c1 | ||
|
|
03b7c07495 | ||
|
|
a3c08ceb7c | ||
|
|
0f59333cd9 | ||
|
|
ad4ce14486 | ||
|
|
3173c79e8f | ||
|
|
ca14d557c1 | ||
|
|
59a3a5bd92 | ||
|
|
a226088f5c | ||
|
|
e91fab5702 | ||
|
|
e09853af0c | ||
|
|
c65fcc1072 | ||
|
|
3bb68ec07e | ||
|
|
2c97638aa1 | ||
|
|
ceee9671d9 | ||
|
|
6b494be7ff | ||
|
|
a6855e6bc1 | ||
|
|
7ca43eb4a1 | ||
|
|
74b5ac559f | ||
|
|
07c236497c | ||
|
|
caf2ff9bb4 | ||
|
|
1b2a17d7e4 | ||
|
|
ce46115139 | ||
|
|
9fd2ff7620 | ||
|
|
98a25c1c7f | ||
|
|
12b86a35af | ||
|
|
e1f4210aa8 | ||
|
|
584b976dff | ||
|
|
4073238654 | ||
|
|
d7505bcef4 | ||
|
|
f6a7225c47 | ||
|
|
377f33b64f | ||
|
|
73b27f14ab | ||
|
|
0b497fbb77 | ||
|
|
5e4df41ec8 | ||
|
|
72085be896 | ||
|
|
e0bc8f9cdc | ||
|
|
efcb442a80 | ||
|
|
a38023475c | ||
|
|
4a6ba29b99 | ||
|
|
7f961d90c2 | ||
|
|
0ac4021729 | ||
|
|
ac662de789 | ||
|
|
23c57cb354 | ||
|
|
d6ef56af6e | ||
|
|
059e36318e | ||
|
|
7a72121b1b | ||
|
|
c01cca33c7 | ||
|
|
631306cfb3 | ||
|
|
f4d59305d7 | ||
|
|
c526e72539 | ||
|
|
e217a6fca8 | ||
|
|
6aa7ef3c21 | ||
|
|
bf0e5c0d44 | ||
|
|
6bd2f5af8d | ||
|
|
7bf54088a6 | ||
|
|
4792040240 | ||
|
|
dc631026d4 | ||
|
|
c05532c166 |
@@ -1,2 +0,0 @@
|
||||
defaults
|
||||
IE 11
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -8,4 +8,3 @@
|
||||
# Same thing for following files, but they don't have an sh extension
|
||||
pre-commit eol=lf
|
||||
webpack-dev-server eol=lf
|
||||
install-bundler eol=lf
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
#### What? Why?
|
||||
## What? Why?
|
||||
|
||||
- Closes # <!-- Insert issue number here. -->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
|
||||
#### What should we test?
|
||||
## What should we test?
|
||||
<!-- List which features should be tested and how.
|
||||
This can be similar to the Steps to Reproduce in the issue.
|
||||
Also think of other parts of the app which could be affected
|
||||
@@ -16,7 +16,7 @@
|
||||
- Visit ... page.
|
||||
-
|
||||
|
||||
#### Release notes
|
||||
## Release notes
|
||||
|
||||
<!-- Please select one for your PR and delete the other. -->
|
||||
|
||||
@@ -33,12 +33,12 @@ Changelog Category (reviewers may add a label for the release notes):
|
||||
The title of the pull request will be included in the release notes.
|
||||
|
||||
|
||||
#### Dependencies
|
||||
## Dependencies
|
||||
<!-- Does this PR depend on another one?
|
||||
Add the link or remove this section. -->
|
||||
|
||||
|
||||
|
||||
#### Documentation updates
|
||||
## Documentation updates
|
||||
<!-- Are there any wiki pages that need updating after merging this PR?
|
||||
List them here or remove this section. -->
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -31,6 +31,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
# Only specific requirements are specified in Gemfile, so don't touch it.
|
||||
versioning-strategy: lockfile-only
|
||||
@@ -39,6 +41,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
# Only specific requirements are specified in package.json, so don't touch it.
|
||||
versioning-strategy: lockfile-only
|
||||
|
||||
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup redis
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
uses: supercharge/redis-github-action@1.8.1
|
||||
with:
|
||||
redis-version: 6
|
||||
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup redis
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
uses: supercharge/redis-github-action@1.8.1
|
||||
with:
|
||||
redis-version: 6
|
||||
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup redis
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
uses: supercharge/redis-github-action@1.8.1
|
||||
with:
|
||||
redis-version: 6
|
||||
|
||||
@@ -290,7 +290,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup redis
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
uses: supercharge/redis-github-action@1.8.1
|
||||
with:
|
||||
redis-version: 6
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.2.9
|
||||
3.4.8
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ruby:3.2.9-alpine3.19 AS base
|
||||
FROM ruby:3.4.8-alpine3.19 AS base
|
||||
ENV LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8 \
|
||||
TZ=Europe/London \
|
||||
@@ -31,4 +31,4 @@ FROM development-base
|
||||
COPY . $RAILS_ROOT
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
RUN bundle install --jobs "$(nproc)"
|
||||
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules
|
||||
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules
|
||||
|
||||
@@ -83,11 +83,8 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
|
||||
# Copy code and install app dependencies
|
||||
COPY . /usr/src/app/
|
||||
|
||||
# Install Bundler
|
||||
RUN ./script/install-bundler
|
||||
|
||||
# Install front-end dependencies
|
||||
RUN yarn install
|
||||
|
||||
# Run bundler install in parallel with the amount of available CPUs
|
||||
RUN bundle install --jobs="$(nproc)"
|
||||
RUN bundle install --jobs="$(nproc)"
|
||||
|
||||
12
Gemfile
12
Gemfile
@@ -18,7 +18,7 @@ gem 'activemerchant'
|
||||
gem 'angular-rails-templates'
|
||||
gem 'ransack', '~> 4.1.0'
|
||||
gem 'responders'
|
||||
gem 'webpacker', '~> 5'
|
||||
gem 'shakapacker', '8.4.0'
|
||||
|
||||
# Indirect dependency but we access it directly in JS specs.
|
||||
# It turns out to be hard to upgrade but please do if you can.
|
||||
@@ -49,7 +49,7 @@ gem 'acts_as_list', '1.0.4'
|
||||
gem 'cancancan', '~> 1.15.0'
|
||||
gem 'digest'
|
||||
gem 'ffaker'
|
||||
gem 'highline', '2.0.3' # Necessary for the install generator
|
||||
gem 'highline'
|
||||
gem 'json'
|
||||
gem 'monetize', '~> 1.11'
|
||||
gem 'paranoia', '~> 2.4'
|
||||
@@ -57,7 +57,8 @@ gem 'state_machines-activerecord'
|
||||
gem 'stringex', '~> 2.8.5', require: false
|
||||
|
||||
gem 'paypal-sdk-merchant', '1.117.2'
|
||||
gem 'stripe', '~> 13'
|
||||
gem 'stripe', '~> 15'
|
||||
gem "taler"
|
||||
|
||||
gem 'devise'
|
||||
gem 'devise-encryptable'
|
||||
@@ -111,7 +112,7 @@ gem "turbo_power"
|
||||
gem "turbo-rails"
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
gem 'wicked_pdf', github: "openfoodfoundation/wicked_pdf", branch: "master"
|
||||
gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'immigrant'
|
||||
@@ -126,7 +127,8 @@ gem 'angular_rails_csrf'
|
||||
|
||||
gem 'jquery-rails', '4.4.0'
|
||||
gem 'jquery-ui-rails', '~> 4.2'
|
||||
gem "select2-rails", github: "openfoodfoundation/select2-rails", branch: "v349_with_thor_v1"
|
||||
gem "select2-rails", github: "openfoodfoundation/select2-rails",
|
||||
branch: "v349_with-relaxed-dependencies"
|
||||
|
||||
gem 'good_migrations'
|
||||
|
||||
|
||||
207
Gemfile.lock
207
Gemfile.lock
@@ -10,13 +10,21 @@ GIT
|
||||
|
||||
GIT
|
||||
remote: https://github.com/openfoodfoundation/select2-rails.git
|
||||
revision: fc240e85fbdf1878ff3c39d972c0cd9a312f5ed4
|
||||
branch: v349_with_thor_v1
|
||||
revision: 9693e0cc5b04938da46692d3fa83aa8934791981
|
||||
branch: v349_with-relaxed-dependencies
|
||||
specs:
|
||||
select2-rails (3.4.9)
|
||||
sass-rails
|
||||
thor (>= 0.14)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/openfoodfoundation/wicked_pdf.git
|
||||
revision: bce498de547cdf00d037fdbec29fae844d69ee8e
|
||||
branch: master
|
||||
specs:
|
||||
wicked_pdf (2.8.1)
|
||||
activesupport
|
||||
ostruct
|
||||
|
||||
GIT
|
||||
remote: https://github.com/podia/stimulus_reflex_testing.git
|
||||
revision: abac2ee34de347c589795b4d1a8e83e0baafb201
|
||||
@@ -104,11 +112,12 @@ GEM
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
active_model_serializers (0.8.4)
|
||||
activemodel (>= 3.0)
|
||||
active_storage_validations (1.1.4)
|
||||
activejob (>= 5.2.0)
|
||||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
active_storage_validations (3.0.3)
|
||||
activejob (>= 6.1.4)
|
||||
activemodel (>= 6.1.4)
|
||||
activestorage (>= 6.1.4)
|
||||
activesupport (>= 6.1.4)
|
||||
marcel (>= 1.0.3)
|
||||
activejob (7.1.6)
|
||||
activesupport (= 7.1.6)
|
||||
globalid (>= 0.3.6)
|
||||
@@ -158,8 +167,8 @@ GEM
|
||||
zeitwerk (>= 2.4, < 3.0)
|
||||
acts_as_list (1.0.4)
|
||||
activerecord (>= 4.2)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
addressable (2.8.8)
|
||||
public_suffix (>= 2.0.2, < 8.0)
|
||||
aes_key_wrap (1.1.0)
|
||||
afm (0.2.2)
|
||||
angular-rails-templates (1.4.0)
|
||||
@@ -176,8 +185,8 @@ GEM
|
||||
ast (2.4.3)
|
||||
attr_required (1.0.2)
|
||||
aws-eventstream (1.4.0)
|
||||
aws-partitions (1.1191.0)
|
||||
aws-sdk-core (3.239.2)
|
||||
aws-partitions (1.1196.0)
|
||||
aws-sdk-core (3.240.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
@@ -188,7 +197,7 @@ GEM
|
||||
aws-sdk-kms (1.118.0)
|
||||
aws-sdk-core (~> 3, >= 3.239.1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.206.0)
|
||||
aws-sdk-s3 (1.208.0)
|
||||
aws-sdk-core (~> 3, >= 3.234.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
@@ -201,12 +210,12 @@ GEM
|
||||
bigdecimal (3.3.1)
|
||||
bindata (2.5.1)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.19.0)
|
||||
bootsnap (1.22.0)
|
||||
msgpack (~> 1.2)
|
||||
bugsnag (6.28.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.3.0)
|
||||
bullet (8.0.8)
|
||||
bullet (8.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.6)
|
||||
@@ -233,7 +242,7 @@ GEM
|
||||
marcel (~> 1.0)
|
||||
nokogiri (~> 1.10, >= 1.10.4)
|
||||
rubyzip (>= 1.3.0, < 3)
|
||||
cgi (0.5.0)
|
||||
cgi (0.5.1)
|
||||
childprocess (5.0.0)
|
||||
choice (0.2.0)
|
||||
chronic (0.10.2)
|
||||
@@ -248,10 +257,10 @@ GEM
|
||||
combine_pdf (1.0.31)
|
||||
matrix
|
||||
ruby-rc4 (>= 0.1.5)
|
||||
concurrent-ruby (1.3.5)
|
||||
concurrent-ruby (1.3.6)
|
||||
connection_pool (2.5.5)
|
||||
cookiejar (0.3.4)
|
||||
crack (1.0.0)
|
||||
crack (1.0.1)
|
||||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
@@ -269,7 +278,7 @@ GEM
|
||||
database_cleaner-core (2.0.1)
|
||||
datafoodconsortium-connector (1.2.0)
|
||||
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
|
||||
date (3.5.0)
|
||||
date (3.5.1)
|
||||
debug (1.11.0)
|
||||
irb (~> 1.10)
|
||||
reline (>= 0.3.8)
|
||||
@@ -289,7 +298,7 @@ GEM
|
||||
diff-lcs (1.6.2)
|
||||
digest (3.2.1)
|
||||
docile (1.4.1)
|
||||
dotenv (3.1.8)
|
||||
dotenv (3.2.0)
|
||||
drb (2.2.3)
|
||||
em-http-request (1.1.7)
|
||||
addressable (>= 2.3.4)
|
||||
@@ -304,7 +313,7 @@ GEM
|
||||
eventmachine (>= 1.0.0.beta.1)
|
||||
email_validator (2.2.4)
|
||||
activemodel
|
||||
erb (6.0.0)
|
||||
erb (6.0.1)
|
||||
erubi (1.13.1)
|
||||
et-orbi (1.3.0)
|
||||
tzinfo
|
||||
@@ -317,19 +326,21 @@ GEM
|
||||
factory_bot_rails (6.2.0)
|
||||
factory_bot (~> 6.2.0)
|
||||
railties (>= 5.0.0)
|
||||
faraday (2.9.0)
|
||||
faraday-net_http (>= 2.0, < 3.2)
|
||||
faraday (2.14.1)
|
||||
faraday-net_http (>= 2.0, < 3.5)
|
||||
json
|
||||
logger
|
||||
faraday-follow_redirects (0.4.0)
|
||||
faraday (>= 1, < 3)
|
||||
faraday-net_http (3.1.1)
|
||||
net-http
|
||||
faraday-net_http (3.4.2)
|
||||
net-http (~> 0.5)
|
||||
ferrum (0.14)
|
||||
addressable (~> 2.5)
|
||||
concurrent-ruby (~> 1.1)
|
||||
webrick (~> 1.7)
|
||||
websocket-driver (>= 0.6, < 0.8)
|
||||
ffaker (2.25.0)
|
||||
ffi (1.17.2)
|
||||
ffi (1.17.3)
|
||||
flipper (1.3.6)
|
||||
concurrent-ruby (< 2)
|
||||
flipper-active_record (1.3.6)
|
||||
@@ -379,7 +390,7 @@ GEM
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
tilt
|
||||
haml_lint (0.67.0)
|
||||
haml_lint (0.68.0)
|
||||
haml (>= 5.0)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
@@ -387,8 +398,10 @@ GEM
|
||||
sysexits (~> 1.1)
|
||||
hashdiff (1.2.1)
|
||||
hashery (2.1.2)
|
||||
hashie (5.0.0)
|
||||
highline (2.0.3)
|
||||
hashie (5.1.0)
|
||||
logger
|
||||
highline (3.1.2)
|
||||
reline
|
||||
htmlentities (4.4.2)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.14.7)
|
||||
@@ -415,10 +428,11 @@ GEM
|
||||
activerecord (>= 3.0)
|
||||
invisible_captcha (2.3.0)
|
||||
rails (>= 5.2)
|
||||
io-console (0.8.1)
|
||||
io-console (0.8.2)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.15.3)
|
||||
irb (1.17.0)
|
||||
pp (>= 0.6.0)
|
||||
prism (>= 1.3.0)
|
||||
rdoc (>= 4.0.0)
|
||||
reline (>= 0.4.2)
|
||||
jmespath (1.6.2)
|
||||
@@ -428,7 +442,7 @@ GEM
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.2.1)
|
||||
railties (>= 3.2.16)
|
||||
json (2.15.2)
|
||||
json (2.18.1)
|
||||
json-canonicalization (1.0.0)
|
||||
json-jwt (1.17.0)
|
||||
activesupport (>= 4.2)
|
||||
@@ -454,7 +468,7 @@ GEM
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.10.2)
|
||||
base64
|
||||
knapsack_pro (8.4.0)
|
||||
knapsack_pro (9.2.2)
|
||||
rake
|
||||
language_server-protocol (3.17.0.5)
|
||||
launchy (3.0.0)
|
||||
@@ -464,11 +478,12 @@ GEM
|
||||
launchy (>= 2.2, < 4)
|
||||
link_header (0.0.8)
|
||||
lint_roller (1.1.0)
|
||||
listen (3.9.0)
|
||||
listen (3.10.0)
|
||||
logger
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
logger (1.7.0)
|
||||
loofah (2.24.1)
|
||||
loofah (2.25.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.9.0)
|
||||
@@ -491,7 +506,8 @@ GEM
|
||||
logger
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.6)
|
||||
minitest (5.26.2)
|
||||
minitest (6.0.1)
|
||||
prism (~> 1.5)
|
||||
monetize (1.13.0)
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
@@ -500,8 +516,8 @@ GEM
|
||||
multi_json (1.17.0)
|
||||
multi_xml (0.6.0)
|
||||
mutex_m (0.3.0)
|
||||
net-http (0.7.0)
|
||||
uri
|
||||
net-http (0.9.1)
|
||||
uri (>= 0.11.1)
|
||||
net-imap (0.5.12)
|
||||
date
|
||||
net-protocol
|
||||
@@ -511,9 +527,9 @@ GEM
|
||||
timeout
|
||||
net-smtp (0.5.1)
|
||||
net-protocol
|
||||
newrelic_rpm (9.23.0)
|
||||
newrelic_rpm (9.24.0)
|
||||
nio4r (2.7.5)
|
||||
nokogiri (1.18.10)
|
||||
nokogiri (1.19.0)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nokogiri-html5-inference (0.3.0)
|
||||
@@ -530,7 +546,7 @@ GEM
|
||||
logger
|
||||
rack (>= 2.2.3)
|
||||
rack-protection
|
||||
omniauth-rails_csrf_protection (1.0.2)
|
||||
omniauth-rails_csrf_protection (2.0.1)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (~> 2.0)
|
||||
omniauth_openid_connect (0.8.0)
|
||||
@@ -551,6 +567,7 @@ GEM
|
||||
webfinger (~> 2.0)
|
||||
orm_adapter (0.5.0)
|
||||
ostruct (0.6.1)
|
||||
package_json (0.2.0)
|
||||
pagy (9.4.0)
|
||||
paper_trail (17.0.0)
|
||||
activerecord (>= 7.1)
|
||||
@@ -558,7 +575,7 @@ GEM
|
||||
parallel (1.27.0)
|
||||
paranoia (2.6.4)
|
||||
activerecord (>= 5.1, < 7.2)
|
||||
parser (3.3.10.0)
|
||||
parser (3.3.10.2)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
paypal-sdk-core (0.3.4)
|
||||
@@ -576,15 +593,16 @@ GEM
|
||||
pp (0.6.3)
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
prism (1.6.0)
|
||||
prism (1.9.0)
|
||||
private_address_check (0.5.0)
|
||||
pry (0.15.2)
|
||||
pry (0.16.0)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
psych (5.2.6)
|
||||
reline (>= 0.6.0)
|
||||
psych (5.3.1)
|
||||
date
|
||||
stringio
|
||||
public_suffix (6.0.2)
|
||||
public_suffix (7.0.0)
|
||||
puffing-billy (4.0.2)
|
||||
addressable (~> 2.5)
|
||||
em-http-request (~> 1.1, >= 1.1.0)
|
||||
@@ -600,7 +618,7 @@ GEM
|
||||
railties (>= 4.2)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.1)
|
||||
rack (2.2.21)
|
||||
rack (2.2.22)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
rack-oauth2 (2.3.0)
|
||||
@@ -613,7 +631,7 @@ GEM
|
||||
rack-protection (3.2.0)
|
||||
base64 (>= 0.1.0)
|
||||
rack (~> 2.2, >= 2.2.4)
|
||||
rack-proxy (0.7.6)
|
||||
rack-proxy (0.7.7)
|
||||
rack
|
||||
rack-rewrite (1.5.1)
|
||||
rack-session (1.0.2)
|
||||
@@ -674,7 +692,7 @@ GEM
|
||||
activesupport (>= 6.1.5)
|
||||
i18n
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
rb-inotify (0.11.1)
|
||||
ffi (~> 1.0)
|
||||
rdf (3.3.4)
|
||||
bcp47_spec (~> 0.2)
|
||||
@@ -683,7 +701,7 @@ GEM
|
||||
logger (~> 1.5)
|
||||
ostruct (~> 0.6)
|
||||
readline (~> 0.0)
|
||||
rdoc (6.16.0)
|
||||
rdoc (7.2.0)
|
||||
erb
|
||||
psych (>= 4.0.0)
|
||||
tsort
|
||||
@@ -699,9 +717,9 @@ GEM
|
||||
io-console (~> 0.5)
|
||||
request_store (1.7.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
responders (3.2.0)
|
||||
actionpack (>= 7.0)
|
||||
railties (>= 7.0)
|
||||
rexml (3.4.4)
|
||||
roadie (5.2.1)
|
||||
css_parser (~> 1.4)
|
||||
@@ -756,7 +774,7 @@ GEM
|
||||
rswag-ui (2.17.0)
|
||||
actionpack (>= 5.2, < 8.2)
|
||||
railties (>= 5.2, < 8.2)
|
||||
rubocop (1.81.7)
|
||||
rubocop (1.84.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -764,25 +782,25 @@ GEM
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 2.9.3, < 3.0)
|
||||
rubocop-ast (>= 1.47.1, < 2.0)
|
||||
rubocop-ast (>= 1.49.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.48.0)
|
||||
rubocop-ast (1.49.0)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.4)
|
||||
prism (~> 1.7)
|
||||
rubocop-capybara (2.22.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-factory_bot (2.28.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-rails (2.34.2)
|
||||
rubocop-rails (2.34.3)
|
||||
activesupport (>= 4.2.0)
|
||||
lint_roller (~> 1.1)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.75.0, < 2.0)
|
||||
rubocop-ast (>= 1.44.0, < 2.0)
|
||||
rubocop-rspec (3.8.0)
|
||||
rubocop-rspec (3.9.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.81)
|
||||
rubocop-rspec_rails (2.32.0)
|
||||
@@ -803,23 +821,23 @@ GEM
|
||||
sanitize (7.0.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.16.8)
|
||||
sass (3.4.25)
|
||||
sass-rails (5.0.8)
|
||||
railties (>= 5.2.0)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sd_notify (0.1.1)
|
||||
securerandom (0.4.1)
|
||||
semantic_range (3.0.0)
|
||||
semantic_range (3.1.0)
|
||||
shakapacker (8.4.0)
|
||||
activesupport (>= 5.2)
|
||||
package_json
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
shoulda-matchers (7.0.1)
|
||||
activesupport (>= 7.1)
|
||||
sidekiq (7.2.4)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
rack (>= 2.2.4)
|
||||
redis-client (>= 0.19.0)
|
||||
sidekiq (7.3.10)
|
||||
base64
|
||||
connection_pool (>= 2.3.0, < 3)
|
||||
logger
|
||||
rack (>= 2.2.4, < 3.3)
|
||||
redis-client (>= 0.23.0, < 1)
|
||||
sidekiq-scheduler (5.0.3)
|
||||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 6, < 8)
|
||||
@@ -834,7 +852,7 @@ GEM
|
||||
caxlsx (<= 4.0)
|
||||
csv
|
||||
rodf
|
||||
spring (4.4.0)
|
||||
spring (4.4.2)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
spring-commands-rubocop (0.4.0)
|
||||
@@ -867,21 +885,22 @@ GEM
|
||||
railties (>= 5.2)
|
||||
redis (>= 4.0, < 6.0)
|
||||
stringex (2.8.6)
|
||||
stringio (3.1.8)
|
||||
stripe (13.5.1)
|
||||
stringio (3.2.0)
|
||||
stripe (15.5.0)
|
||||
swd (2.0.3)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
sysexits (1.2.0)
|
||||
temple (0.8.2)
|
||||
taler (0.1.0)
|
||||
temple (0.10.4)
|
||||
terminal-table (4.0.0)
|
||||
unicode-display_width (>= 1.1.1, < 4)
|
||||
thor (1.4.0)
|
||||
thor (1.5.0)
|
||||
thread-local (1.1.0)
|
||||
tilt (2.6.1)
|
||||
timeout (0.4.4)
|
||||
timeout (0.6.0)
|
||||
tsort (0.2.0)
|
||||
ttfunk (1.8.0)
|
||||
bigdecimal (~> 3.1)
|
||||
@@ -902,8 +921,8 @@ GEM
|
||||
simplecov_json_formatter
|
||||
unicode-display_width (3.2.0)
|
||||
unicode-emoji (~> 4.1)
|
||||
unicode-emoji (4.1.0)
|
||||
uniform_notifier (1.17.0)
|
||||
unicode-emoji (4.2.0)
|
||||
uniform_notifier (1.18.0)
|
||||
uri (1.1.1)
|
||||
valid_email2 (5.2.3)
|
||||
activemodel (>= 3.2)
|
||||
@@ -913,7 +932,8 @@ GEM
|
||||
public_suffix
|
||||
validates_lengths_from_database (0.8.0)
|
||||
activerecord (>= 4)
|
||||
vcr (6.2.0)
|
||||
vcr (6.3.1)
|
||||
base64
|
||||
view_component (4.1.1)
|
||||
actionview (>= 7.1.0, < 8.2)
|
||||
activesupport (>= 7.1.0, < 8.2)
|
||||
@@ -935,15 +955,10 @@ GEM
|
||||
activesupport
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
webmock (3.25.1)
|
||||
webmock (3.26.1)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
webpacker (5.4.4)
|
||||
activesupport (>= 5.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webrick (1.9.2)
|
||||
websocket-driver (0.7.7)
|
||||
base64
|
||||
@@ -951,14 +966,11 @@ GEM
|
||||
websocket-extensions (0.1.5)
|
||||
whenever (1.1.0)
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.8.2)
|
||||
activesupport
|
||||
ostruct
|
||||
wkhtmltopdf-binary (0.12.6.10)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.7.3)
|
||||
zeitwerk (2.7.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -1016,7 +1028,7 @@ DEPENDENCIES
|
||||
good_migrations
|
||||
haml
|
||||
haml_lint
|
||||
highline (= 2.0.3)
|
||||
highline
|
||||
i18n
|
||||
i18n-js (~> 3.9.0)
|
||||
i18n-tasks
|
||||
@@ -1080,6 +1092,7 @@ DEPENDENCIES
|
||||
rubocop-rspec_rails
|
||||
sd_notify
|
||||
select2-rails!
|
||||
shakapacker (= 8.4.0)
|
||||
shoulda-matchers
|
||||
sidekiq
|
||||
sidekiq-scheduler
|
||||
@@ -1093,7 +1106,8 @@ DEPENDENCIES
|
||||
stimulus_reflex
|
||||
stimulus_reflex_testing!
|
||||
stringex (~> 2.8.5)
|
||||
stripe (~> 13)
|
||||
stripe (~> 15)
|
||||
taler
|
||||
turbo-rails
|
||||
turbo_power
|
||||
undercover
|
||||
@@ -1105,13 +1119,12 @@ DEPENDENCIES
|
||||
web!
|
||||
web-console
|
||||
webmock
|
||||
webpacker (~> 5)
|
||||
whenever
|
||||
wicked_pdf
|
||||
wicked_pdf!
|
||||
wkhtmltopdf-binary
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.2.9p265
|
||||
ruby 3.4.8p72
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.19
|
||||
4.0.3
|
||||
|
||||
2
Procfile
2
Procfile
@@ -1,5 +1,5 @@
|
||||
# Foreman Procfile. Start all dev server processes with: `foreman start`
|
||||
|
||||
rails: DEV_CACHING=true bundle exec rails s -p 3000
|
||||
webpack: ./bin/webpack-dev-server
|
||||
webpack: ./bin/shakapacker-dev-server
|
||||
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Foreman Procfile for Docker env. Start all dev server processes with: `bundle exec foreman start -f Procfile.docker`
|
||||
|
||||
webpack: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/webpack-dev-server
|
||||
webpack: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/shakapacker-dev-server
|
||||
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
|
||||
rails: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0
|
||||
rails: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0
|
||||
|
||||
@@ -38,16 +38,13 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
|
||||
# To ensure the customer is still updated, we check on the $destroy event to see if
|
||||
# the attribute has changed, if so we queue up the change.
|
||||
scope.$on '$destroy', (value) ->
|
||||
# No update
|
||||
return if scope.object()[scope.attr] is scope.savedValue
|
||||
currentValue = scope.object()[scope.attr] || ""
|
||||
|
||||
# For some reason the code attribute is removed from the object when cleared, so we add
|
||||
# an emptyvalue so it gets updated properly
|
||||
if scope.attr is "code" and scope.object()[scope.attr] is undefined
|
||||
scope.object()["code"] = ""
|
||||
# No update
|
||||
return if currentValue is scope.savedValue
|
||||
|
||||
# Queuing up change
|
||||
addPendingChange(scope.attr, scope.object()[scope.attr])
|
||||
addPendingChange(scope.attr, currentValue)
|
||||
|
||||
# private
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
angular.module('Darkswarm').controller "TmpCtrl", ($scope)->
|
||||
$scope.test = {foo: "bar"}
|
||||
@@ -1 +0,0 @@
|
||||
@import './mail/all.scss';
|
||||
@@ -1,3 +0,0 @@
|
||||
@import '../../../webpacker/css/admin/globals/palette.scss';
|
||||
@import 'email';
|
||||
@import 'payments_list';
|
||||
@@ -1,17 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SearchableDropdownComponent < ViewComponent::Base
|
||||
REMOVED_SEARCH_PLUGIN = { 'tom-select-options-value': '{ "plugins": [] }' }.freeze
|
||||
MINIMUM_OPTIONS_FOR_SEARCH_FIELD = 11 # at least 11 options are required for the search field
|
||||
|
||||
def initialize(
|
||||
form:,
|
||||
name:,
|
||||
options:,
|
||||
selected_option:,
|
||||
placeholder_value:,
|
||||
form: nil,
|
||||
placeholder_value: '',
|
||||
include_blank: false,
|
||||
aria_label: '',
|
||||
multiple: false,
|
||||
remote_url: nil,
|
||||
other_attrs: {}
|
||||
)
|
||||
@f = form
|
||||
@@ -21,13 +22,15 @@ class SearchableDropdownComponent < ViewComponent::Base
|
||||
@placeholder_value = placeholder_value
|
||||
@include_blank = include_blank
|
||||
@aria_label = aria_label
|
||||
@multiple = multiple
|
||||
@remote_url = remote_url
|
||||
@other_attrs = other_attrs
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank,
|
||||
:aria_label, :other_attrs
|
||||
:aria_label, :multiple, :remote_url, :other_attrs
|
||||
|
||||
def classes
|
||||
"fullwidth #{'no-input' if remove_search_plugin?}"
|
||||
@@ -36,11 +39,33 @@ class SearchableDropdownComponent < ViewComponent::Base
|
||||
def data
|
||||
{
|
||||
controller: "tom-select",
|
||||
'tom-select-placeholder-value': placeholder_value
|
||||
}.merge(remove_search_plugin? ? REMOVED_SEARCH_PLUGIN : {})
|
||||
'tom-select-placeholder-value': placeholder_value,
|
||||
'tom-select-options-value': tom_select_options_value,
|
||||
'tom-select-remote-url-value': remote_url,
|
||||
}
|
||||
end
|
||||
|
||||
def tom_select_options_value
|
||||
plugins = []
|
||||
plugins << 'virtual_scroll' if @remote_url.present?
|
||||
plugins << 'dropdown_input' unless remove_search_plugin?
|
||||
plugins << 'remove_button' if multiple
|
||||
|
||||
{
|
||||
plugins:,
|
||||
maxItems: multiple ? nil : 1,
|
||||
}
|
||||
end
|
||||
|
||||
def uses_form_builder?
|
||||
f.present?
|
||||
end
|
||||
|
||||
def remove_search_plugin?
|
||||
@remove_search_plugin ||= options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD
|
||||
# Remove the search plugin when:
|
||||
# - the select is multiple (it already includes a search field), or
|
||||
# - there is no remote URL and the options are below the search threshold
|
||||
@remove_search_plugin ||= multiple ||
|
||||
(@remote_url.nil? && options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label, **other_attrs
|
||||
- if uses_form_builder?
|
||||
= f.select name, options, { selected: selected_option, include_blank:, multiple: }, class: classes, data:, 'aria-label': aria_label, **other_attrs
|
||||
- else
|
||||
= select_tag name, options_for_select(options, selected_option), include_blank:, multiple:, class: classes, data:, 'aria-label': aria_label, **other_attrs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
|
||||
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_actions: "click->modal#close", confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
|
||||
%div{class: "margin-bottom-30"}
|
||||
%p= t('spree.admin.orders.shipment.mark_as_shipped_message_html')
|
||||
%div{class: "margin-bottom-30"}
|
||||
|
||||
16
app/components/webhook_endpoint_form_component.rb
Normal file
16
app/components/webhook_endpoint_form_component.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhookEndpointFormComponent < ViewComponent::Base
|
||||
def initialize(webhooks:, webhook_type:)
|
||||
@webhooks = webhooks
|
||||
@webhook_type = webhook_type
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :webhooks, :webhook_type
|
||||
|
||||
def is_webhook_payment_status?
|
||||
webhook_type == "payment_status_changed"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
-# Create new endpoints
|
||||
- if webhooks.empty? # Only one allowed for now.
|
||||
%tr
|
||||
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
|
||||
%td
|
||||
= form_with(url: helpers.account_webhook_endpoints_path, id: "#{webhook_type}_webhook_endpoint") do |f|
|
||||
= f.url_field :'webhook_endpoint[url]', id: "#{webhook_type}_webhook_endpoint_url", placeholder: t('components.webhook_endpoint_form.url.create_placeholder'), required: true, size: 64
|
||||
= f.hidden_field :'webhook_endpoint[webhook_type]', id: "#{webhook_type}_webhook_endpoint_webhook_type", value: webhook_type
|
||||
%td.actions
|
||||
= button_tag t(:create), class: 'button primary tiny no-margin', form: "#{webhook_type}_webhook_endpoint"
|
||||
|
||||
-# Existing endpoints
|
||||
- webhooks.each do |webhook_endpoint|
|
||||
%tr
|
||||
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
|
||||
%td= webhook_endpoint.url
|
||||
%td.actions.endpoints-actions
|
||||
- if webhook_endpoint.persisted?
|
||||
= button_to helpers.account_webhook_endpoint_path(webhook_endpoint), method: :delete,
|
||||
class: "tiny alert no-margin",
|
||||
data: { confirm: I18n.t(:are_you_sure) } do
|
||||
= I18n.t(:delete)
|
||||
|
||||
- if is_webhook_payment_status?
|
||||
= form_tag helpers.webhook_endpoint_test_account_path(webhook_endpoint), class: "button_to", 'data-turbo': true do
|
||||
= button_tag type: "submit", class: "tiny alert no-margin", data: { confirm: I18n.t(:are_you_sure) } do
|
||||
= I18n.t("components.webhook_endpoint_form.test_endpoint")
|
||||
@@ -162,6 +162,18 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @object.destroy
|
||||
flash.now[:success] = flash_message_for(@object, :successfully_removed)
|
||||
else
|
||||
flash.now[:error] = @object.errors.full_messages.to_sentence
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream { render :destroy, status: :ok }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def delete_custom_tab
|
||||
|
||||
@@ -7,6 +7,7 @@ module Admin
|
||||
|
||||
before_action :init_filters_params
|
||||
before_action :init_pagination_params
|
||||
before_action :init_none_tag
|
||||
|
||||
def index
|
||||
fetch_products
|
||||
@@ -179,6 +180,8 @@ module Admin
|
||||
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.editable_products.merge(product_scope_with_includes).ransack(ransack_query).result
|
||||
|
||||
product_query = apply_tags_filter(product_query)
|
||||
|
||||
# Postgres requires ORDER BY expressions to appear in the SELECT list when using DISTINCT.
|
||||
# When the current ransack sort uses the computed stock columns, include them in the select
|
||||
# so the generated COUNT/DISTINCT query is valid.
|
||||
@@ -225,12 +228,51 @@ module Admin
|
||||
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
|
||||
end
|
||||
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
|
||||
query.merge!(variants_tags_name_in: @tags) if @tags.present?
|
||||
query.merge!(@q) if @q
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
# Apply tags filter with OR logic:
|
||||
# - Products with variants having selected tags
|
||||
# - OR products with variants having no tags (when "None" is selected)
|
||||
#
|
||||
# Note: This cannot be implemented using Ransack because Ransack applies
|
||||
# AND semantics across associations and cannot express OR logic that combines
|
||||
# the presence and absence of the same associated records.
|
||||
def apply_tags_filter(base_query)
|
||||
return base_query if @tags.blank?
|
||||
|
||||
tag_names = Array(@tags).dup
|
||||
has_none_tag = (tag_names.delete(@none_tag_value) == @none_tag_value)
|
||||
|
||||
queries = []
|
||||
|
||||
if tag_names.any?
|
||||
# Products with at least one variant having one of the selected tags
|
||||
tagged_product_ids = Spree::Variant
|
||||
.joins(taggings: :tag)
|
||||
.where(tags: { name: tag_names })
|
||||
.select(:product_id)
|
||||
|
||||
queries << base_query.where(id: tagged_product_ids)
|
||||
end
|
||||
|
||||
if has_none_tag
|
||||
# Products where no variants have any tags
|
||||
tagged_product_ids = Spree::Variant
|
||||
.joins(:taggings)
|
||||
.select(:product_id)
|
||||
|
||||
queries << base_query.where.not(id: tagged_product_ids)
|
||||
end
|
||||
|
||||
return base_query if queries.empty?
|
||||
|
||||
# Combine queries using ActiveRecord's or method
|
||||
queries.reduce { |combined, query| combined.or(query) }
|
||||
end
|
||||
|
||||
# Optimise by pre-loading required columns
|
||||
def product_query_includes
|
||||
[
|
||||
@@ -289,6 +331,10 @@ module Admin
|
||||
t('.error')
|
||||
end
|
||||
end
|
||||
|
||||
def init_none_tag
|
||||
@none_tag_value = '""'
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
|
||||
@@ -4,6 +4,7 @@ module Admin
|
||||
class ReportsController < Spree::Admin::BaseController
|
||||
include ActiveStorage::SetCurrent
|
||||
include ReportsActions
|
||||
include Reports::AjaxSearch
|
||||
|
||||
helper ReportsHelper
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ module Admin
|
||||
|
||||
def destroy
|
||||
if @object.destroy
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
flash[:success] = Spree.t(:successfully_removed)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
format.js { render partial: "spree/admin/shared/destroy" }
|
||||
@@ -76,7 +76,7 @@ module Admin
|
||||
protected
|
||||
|
||||
def resource_not_found
|
||||
flash[:error] = flash_message_for(model_class.new, :not_found)
|
||||
flash[:error] = Spree.t(:not_found)
|
||||
redirect_to collection_url
|
||||
end
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ module Admin
|
||||
class StripeAccountsController < Spree::Admin::BaseController
|
||||
def connect
|
||||
payload = params.permit(:enterprise_id).to_h
|
||||
key = Openfoodnetwork::Application.config.secret_token
|
||||
key = Rails.application.secret_key_base
|
||||
url_params = { state: JWT.encode(payload, key, 'HS256'), scope: "read_write" }
|
||||
redirect_to Stripe::OAuth.authorize_url(url_params)
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ module Admin
|
||||
|
||||
status = :ok
|
||||
if @rule.destroy
|
||||
flash[:success] = Spree.t(:successfully_removed, resource: "Tag Rule")
|
||||
flash[:success] = Spree.t(:successfully_removed, resource: Spree.t(:tag_rule))
|
||||
else
|
||||
flash.now[:error] = t(".destroy_error")
|
||||
status = :internal_server_error
|
||||
|
||||
@@ -14,7 +14,7 @@ module Admin
|
||||
)
|
||||
|
||||
if @voucher.save
|
||||
flash[:success] = I18n.t(:successfully_created, resource: "Voucher")
|
||||
flash[:success] = I18n.t(:successfully_created, resource: Spree.t(:voucher))
|
||||
redirect_to edit_admin_enterprise_path(@enterprise, anchor: :vouchers_panel)
|
||||
else
|
||||
render_error
|
||||
|
||||
108
app/controllers/concerns/reports/ajax_search.rb
Normal file
108
app/controllers/concerns/reports/ajax_search.rb
Normal file
@@ -0,0 +1,108 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reports
|
||||
module AjaxSearch
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def search_enterprise_fees
|
||||
report = report_class.new(spree_current_user, params, render: false)
|
||||
fee_ids = enterprise_fee_ids(report.search.result)
|
||||
query = EnterpriseFee.where(id: fee_ids)
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_enterprise_fee_owners
|
||||
report = report_class.new(spree_current_user, params, render: false)
|
||||
owner_ids = enterprise_fee_owner_ids(report.search.result)
|
||||
query = Enterprise.where(id: owner_ids)
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_distributors
|
||||
query = frontend_data.distributors
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_order_cycles
|
||||
query = frontend_data.order_cycles
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_order_customers
|
||||
query = frontend_data.order_customers
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_suppliers
|
||||
query = frontend_data.orders_suppliers
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_search_response(query)
|
||||
page = (params[:page] || 1).to_i
|
||||
per_page = 30
|
||||
|
||||
filtered_query = apply_search_filter(query)
|
||||
total_count = filtered_query.size
|
||||
items = paginated_items(filtered_query, page, per_page)
|
||||
results = format_results(items)
|
||||
|
||||
{ results: results, pagination: { more: (page * per_page) < total_count } }
|
||||
end
|
||||
|
||||
def apply_search_filter(query)
|
||||
search_term = params[:q]
|
||||
return query if search_term.blank?
|
||||
|
||||
escaped_search_term = ActiveRecord::Base.sanitize_sql_like(search_term)
|
||||
pattern = "%#{escaped_search_term}%"
|
||||
|
||||
# Handle different model types
|
||||
if query.model == OrderCycle
|
||||
query.where("order_cycles.name ILIKE ?", pattern)
|
||||
elsif query.model == Customer
|
||||
query.where("customers.email ILIKE ?", pattern)
|
||||
else
|
||||
query.where("name ILIKE ?", pattern)
|
||||
end
|
||||
end
|
||||
|
||||
def paginated_items(query, page, per_page)
|
||||
if query.model == Customer
|
||||
query.order(:email).offset((page - 1) * per_page).limit(per_page).pluck(:email, :id)
|
||||
elsif query.model == OrderCycle
|
||||
query.order('order_cycles.orders_close_at DESC')
|
||||
.offset((page - 1) * per_page)
|
||||
.limit(per_page).pluck(
|
||||
:name, :id
|
||||
)
|
||||
else
|
||||
query.order(:name).offset((page - 1) * per_page).limit(per_page).pluck(:name, :id)
|
||||
end
|
||||
end
|
||||
|
||||
def format_results(items)
|
||||
items.map { |label, value| { value:, label: } }
|
||||
end
|
||||
|
||||
def frontend_data
|
||||
@frontend_data ||= Reporting::FrontendData.new(spree_current_user)
|
||||
end
|
||||
|
||||
def enterprise_fee_owner_ids(orders)
|
||||
EnterpriseFee.where(id: enterprise_fee_ids(orders)).select(:enterprise_id)
|
||||
end
|
||||
|
||||
def enterprise_fee_ids(orders)
|
||||
Spree::Adjustment.enterprise_fee.where(order_id: orders.select(:id)).select(:originator_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
16
app/controllers/payment_gateways/taler_controller.rb
Normal file
16
app/controllers/payment_gateways/taler_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PaymentGateways
|
||||
class TalerController < BaseController
|
||||
include OrderCompletion
|
||||
|
||||
# The Taler merchant backend has taken the payment.
|
||||
# Now we just need to confirm that and update our local database
|
||||
# before finalising the order.
|
||||
def confirm
|
||||
payment = Spree::Payment.find(params[:payment_id])
|
||||
@order = payment.order
|
||||
process_payment_completion!
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -68,7 +68,7 @@ module Spree
|
||||
destroy_before
|
||||
|
||||
if @object.destroy
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
flash[:success] = Spree.t(:successfully_removed)
|
||||
end
|
||||
|
||||
redirect_to location_after_save
|
||||
|
||||
@@ -11,10 +11,11 @@ module Spree
|
||||
respond_to :html
|
||||
|
||||
PAYMENT_METHODS = %w{
|
||||
Spree::PaymentMethod::Check
|
||||
Spree::Gateway::PayPalExpress
|
||||
Spree::Gateway::StripeSCA
|
||||
}.index_with(&:constantize).freeze
|
||||
Spree::PaymentMethod::Check
|
||||
Spree::PaymentMethod::Taler
|
||||
}.freeze
|
||||
|
||||
def create
|
||||
force_environment
|
||||
@@ -95,7 +96,7 @@ module Spree
|
||||
@payment_method = PaymentMethod.find(params[:pm_id])
|
||||
end
|
||||
else
|
||||
@payment_method = PAYMENT_METHODS.fetch(params[:provider_type], PaymentMethod).new
|
||||
@payment_method = PaymentMethod.new(type: params[:provider_type])
|
||||
end
|
||||
|
||||
render partial: 'provider_settings'
|
||||
@@ -117,7 +118,7 @@ module Spree
|
||||
end
|
||||
|
||||
def validate_payment_method_provider
|
||||
valid_payment_methods = Rails.application.config.spree.payment_methods.map(&:to_s)
|
||||
valid_payment_methods = PAYMENT_METHODS
|
||||
return if valid_payment_methods.include?(params[:payment_method][:type])
|
||||
|
||||
flash[:error] = Spree.t(:invalid_payment_provider)
|
||||
@@ -133,11 +134,9 @@ module Spree
|
||||
end
|
||||
|
||||
def load_providers
|
||||
providers = Gateway.providers.sort_by(&:name)
|
||||
providers = PAYMENT_METHODS.dup
|
||||
|
||||
unless show_stripe?
|
||||
providers.reject! { |provider| stripe_provider?(provider) }
|
||||
end
|
||||
providers.delete("Spree::Gateway::StripeSCA") unless show_stripe?
|
||||
|
||||
providers
|
||||
end
|
||||
@@ -164,10 +163,6 @@ module Spree
|
||||
@payment_method.try(:type) == "Spree::Gateway::StripeSCA"
|
||||
end
|
||||
|
||||
def stripe_provider?(provider)
|
||||
provider.name.ends_with?("StripeSCA")
|
||||
end
|
||||
|
||||
def base_params
|
||||
@base_params ||= PermittedAttributes::PaymentMethod.new(params[:payment_method]).
|
||||
call.to_h.with_indifferent_access
|
||||
|
||||
@@ -95,8 +95,7 @@ module Spree
|
||||
private
|
||||
|
||||
def load_payment_source
|
||||
if @payment.payment_method.is_a?(Spree::Gateway) &&
|
||||
@payment.payment_method.payment_profiles_supported? &&
|
||||
if @payment.payment_method.is_a?(Gateway::StripeSCA) &&
|
||||
params[:card].present? &&
|
||||
(params[:card] != 'new')
|
||||
@payment.source = CreditCard.find_by(id: params[:card])
|
||||
|
||||
@@ -16,7 +16,7 @@ module Spree
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
|
||||
if @object.destroy
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
flash[:success] = Spree.t(:successfully_removed)
|
||||
end
|
||||
# if destroy fails it won't show any errors to the user
|
||||
redirect_to spree.admin_product_product_properties_url(params[:product_id], @url_filters)
|
||||
|
||||
@@ -36,7 +36,7 @@ module Spree
|
||||
end
|
||||
|
||||
@object.touch :deleted_at
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
flash[:success] = Spree.t(:successfully_removed)
|
||||
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
|
||||
@@ -5,7 +5,7 @@ module Spree
|
||||
class TaxCategoriesController < ::Admin::ResourceController
|
||||
def destroy
|
||||
if @object.destroy
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
flash[:success] = Spree.t(:successfully_removed)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
format.js { render partial: "spree/admin/shared/destroy" }
|
||||
|
||||
@@ -9,7 +9,6 @@ module Spree
|
||||
include Spree::Core::ControllerHelpers::Auth
|
||||
include Spree::Core::ControllerHelpers::Common
|
||||
include Spree::Core::ControllerHelpers::Order
|
||||
include CablecarResponses
|
||||
|
||||
helper 'spree/base'
|
||||
|
||||
@@ -24,14 +23,12 @@ module Spree
|
||||
if spree_user_signed_in?
|
||||
flash[:success] = t('devise.success.logged_in_succesfully')
|
||||
|
||||
render cable_ready: cable_car.redirect_to(
|
||||
url: return_url_or_default(after_sign_in_path_for(spree_current_user))
|
||||
)
|
||||
redirect_to return_url_or_default(after_sign_in_path_for(spree_current_user))
|
||||
else
|
||||
render status: :unauthorized, cable_ready: cable_car.inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t('devise.failure.invalid') })
|
||||
)
|
||||
message = t('devise.failure.invalid')
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'login-feedback', partial: 'layouts/alert', locals: { message:, type: 'alert' }
|
||||
), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,11 +57,13 @@ module Spree
|
||||
end
|
||||
|
||||
def render_unconfirmed_response
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t(:email_unconfirmed),
|
||||
unconfirmed: true, tab: "login" })
|
||||
)
|
||||
message = t(:email_unconfirmed)
|
||||
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'login-feedback',
|
||||
partial: 'layouts/alert', locals: { type: "alert", message:, unconfirmed: true,
|
||||
tab: "login", email: params.dig(:spree_user, :email) }
|
||||
), status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def ensure_valid_locale_persisted
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
module Spree
|
||||
class UsersController < ::BaseController
|
||||
include I18nHelper
|
||||
include CablecarResponses
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
@@ -25,34 +24,17 @@ module Spree
|
||||
@unconfirmed_email = spree_current_user.unconfirmed_email
|
||||
end
|
||||
|
||||
# Endpoint for queries to check if a user is already registered
|
||||
def registered_email
|
||||
registered = Spree::User.find_by(email: params[:email]).present?
|
||||
|
||||
if registered
|
||||
render status: :ok, cable_ready: cable_car.
|
||||
inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t('devise.failure.already_registered') })
|
||||
).
|
||||
dispatch_event(name: "login:modal:open")
|
||||
else
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@user = Spree::User.new(user_params)
|
||||
|
||||
if @user.save
|
||||
flash[:success] = t('devise.user_registrations.spree_user.signed_up_but_unconfirmed')
|
||||
render cable_ready: cable_car.redirect_to(url: main_app.root_path)
|
||||
redirect_to main_app.root_path
|
||||
else
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.morph(
|
||||
"#signup-tab",
|
||||
partial("layouts/signup_tab", locals: { signup_form_user: @user })
|
||||
)
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'signup-tab',
|
||||
partial: 'layouts/signup_tab', locals: { signup_form_user: @user }
|
||||
), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -97,13 +79,10 @@ module Spree
|
||||
end
|
||||
|
||||
def render_alert_timestamp_error_message
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#signup-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: {
|
||||
type: "alert",
|
||||
message: InvisibleCaptcha.timestamp_error_message
|
||||
})
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'signup-feedback',
|
||||
partial: 'layouts/alert',
|
||||
locals: { type: "alert", message: InvisibleCaptcha.timestamp_error_message }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
class UserConfirmationsController < DeviseController
|
||||
# Needed for access to current_ability, so we can authorize! actions
|
||||
include Spree::Core::ControllerHelpers::Auth
|
||||
include CablecarResponses
|
||||
|
||||
# GET /resource/confirmation?confirmation_token=abcdef
|
||||
def show
|
||||
@@ -29,12 +28,12 @@ class UserConfirmationsController < DeviseController
|
||||
set_flash_message(:error, :confirmation_not_sent)
|
||||
end
|
||||
else
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"##{params[:tab] || 'forgot'}-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "success", message: t("devise.confirmations.send_instructions") })
|
||||
flash.now[:sucess] = t("devise.confirmations.send_instructions")
|
||||
|
||||
return render turbo_stream: turbo_stream.update(
|
||||
"#{params[:tab] || 'forgot'}-feedback",
|
||||
partial: 'shared/flashes', locals: { flashes: flash }
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
respond_with_navigational(resource){ redirect_to login_path }
|
||||
|
||||
@@ -1,37 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UserPasswordsController < Spree::UserPasswordsController
|
||||
include CablecarResponses
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
def create
|
||||
return render_unconfirmed_response if user_unconfirmed?
|
||||
|
||||
self.resource = resource_class.send_reset_password_instructions(raw_params[resource_name])
|
||||
status = :ok
|
||||
|
||||
if resource.errors.empty?
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert", locals: { type: "success", message: t(:password_reset_sent) })
|
||||
)
|
||||
message, type = [t(:password_reset_sent), :success]
|
||||
else
|
||||
render status: :not_found, cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t(:email_not_found) })
|
||||
)
|
||||
message, type = [t(:email_not_found), :alert]
|
||||
status = :not_found
|
||||
end
|
||||
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'forgot-feedback',
|
||||
partial: 'layouts/alert',
|
||||
locals: { type:, message:, tab: 'forgot',
|
||||
unconfirmed: false, email: params.dig(:spree_user, :email) }
|
||||
), status:
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_unconfirmed_response
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t(:email_unconfirmed),
|
||||
unconfirmed: true, tab: "forgot" })
|
||||
)
|
||||
message, type, unconfirmed, tab = [t(:email_unconfirmed), :alert, true, 'forgot']
|
||||
|
||||
render turbo_stream: turbo_stream.update(
|
||||
'forgot-feedback',
|
||||
partial: 'layouts/alert',
|
||||
locals: { type:, message:, tab:,
|
||||
unconfirmed:, email: params.dig(:spree_user, :email) }
|
||||
), status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def user_unconfirmed?
|
||||
|
||||
@@ -90,11 +90,13 @@ class VoucherAdjustmentsController < BaseController
|
||||
voucher_code: voucher_params[:voucher_code], enterprise: @order.distributor
|
||||
)
|
||||
voucher = vine_voucher_validator.validate
|
||||
errors = vine_voucher_validator.errors
|
||||
|
||||
return nil if vine_voucher_validator.errors[:not_found_voucher].present?
|
||||
return nil if errors[:not_found_voucher].present?
|
||||
|
||||
if vine_voucher_validator.errors.present?
|
||||
@order.errors.add(:voucher_code, I18n.t('checkout.errors.add_voucher_error'))
|
||||
if errors.present?
|
||||
message = errors[:invalid_voucher] || I18n.t('checkout.errors.add_voucher_error')
|
||||
@order.errors.add(:voucher_code, message)
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhookEndpointsController < BaseController
|
||||
before_action :load_resource, only: :destroy
|
||||
before_action :load_resource, only: [:destroy, :test]
|
||||
|
||||
def create
|
||||
webhook_endpoint = spree_current_user.webhook_endpoints.new(webhook_endpoint_params)
|
||||
@@ -25,12 +25,30 @@ class WebhookEndpointsController < BaseController
|
||||
redirect_to redirect_path
|
||||
end
|
||||
|
||||
def test
|
||||
at = Time.zone.now
|
||||
test_payload = Payments::WebhookPayload.test_data.to_hash
|
||||
|
||||
WebhookDeliveryJob.perform_later(@webhook_endpoint.url, "payment.completed", test_payload, at:)
|
||||
|
||||
flash[:success] = t(".success")
|
||||
respond_with do |format|
|
||||
format.turbo_stream do
|
||||
render turbo_stream: turbo_stream.update(
|
||||
:flashes, partial: "shared/flashes", locals: { flashes: flash }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_resource
|
||||
@webhook_endpoint = spree_current_user.webhook_endpoints.find(params[:id])
|
||||
end
|
||||
|
||||
def webhook_endpoint_params
|
||||
params.require(:webhook_endpoint).permit(:url)
|
||||
params.require(:webhook_endpoint).permit(:url, :webhook_type)
|
||||
end
|
||||
|
||||
def redirect_path
|
||||
|
||||
@@ -72,7 +72,23 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
# Update "v1" to invalidate existing cache key
|
||||
def cache_key_with_locale(key, locale)
|
||||
Array.wrap(key) + [locale.to_s, I18nDigests.for_locale(locale)]
|
||||
Array.wrap(key) + ["v3", locale.to_s, I18nDigests.for_locale(locale)]
|
||||
end
|
||||
|
||||
def pdf_stylesheet_pack_tag(source)
|
||||
# With shakapacker dev server running, the wicked_pdf_stylesheet_pack_tag will produce a
|
||||
# relative path, because we don't have `config.action_controller.asset_host`. Relative path
|
||||
# can't be resolved by `wkhtmltopdf`. So we pass the wepacker dev server host and port to
|
||||
# the shakapacker helper, so it generates the correct url.
|
||||
# For more info: https://stackoverflow.com/questions/58490299/how-to-include-css-stylesheet-into-wicked-pdf/60541688#60541688
|
||||
if running_in_development?
|
||||
options = { media: "all",
|
||||
host: "#{Shakapacker.dev_server.host}:#{Shakapacker.dev_server.port}" }
|
||||
stylesheet_pack_tag(source, **options)
|
||||
else
|
||||
wicked_pdf_stylesheet_pack_tag(source)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,8 +63,11 @@ module EnterprisesHelper
|
||||
url = object_url(enterprise)
|
||||
name = t(:delete)
|
||||
options = {}
|
||||
options[:class] = "delete-resource"
|
||||
options[:data] = { action: 'remove', confirm: enterprise_confirm_delete_message(enterprise) }
|
||||
options[:data] = {
|
||||
turbo: true,
|
||||
'turbo-method': 'delete',
|
||||
'turbo-confirm': enterprise_confirm_delete_message(enterprise)
|
||||
}
|
||||
link_to_with_icon 'icon-trash', name, url, options
|
||||
end
|
||||
|
||||
|
||||
@@ -34,29 +34,8 @@ module ReportsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def fee_name_options(orders)
|
||||
EnterpriseFee.where(id: enterprise_fee_ids(orders))
|
||||
.pluck(:name, :id)
|
||||
end
|
||||
|
||||
def fee_owner_options(orders)
|
||||
Enterprise.where(id: enterprise_fee_owner_ids(orders))
|
||||
.pluck(:name, :id)
|
||||
end
|
||||
|
||||
delegate :currency_symbol, to: :'Spree::Money'
|
||||
|
||||
def enterprise_fee_owner_ids(orders)
|
||||
EnterpriseFee.where(id: enterprise_fee_ids(orders))
|
||||
.pluck(:enterprise_id)
|
||||
end
|
||||
|
||||
def enterprise_fee_ids(orders)
|
||||
Spree::Adjustment.enterprise_fee
|
||||
.where(order_id: orders.map(&:id))
|
||||
.pluck(:originator_id)
|
||||
end
|
||||
|
||||
def datepicker_time(datetime)
|
||||
datetime = Time.zone.parse(datetime) if datetime.is_a? String
|
||||
datetime.strftime('%Y-%m-%d %H:%M')
|
||||
|
||||
18
app/helpers/spree/admin/payment_methods_helper.rb
Normal file
18
app/helpers/spree/admin/payment_methods_helper.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
module PaymentMethodsHelper
|
||||
def payment_method_type_name(class_name)
|
||||
scope = "spree.admin.payment_methods.providers"
|
||||
key = class_name.demodulize.downcase
|
||||
|
||||
I18n.t(key, scope:)
|
||||
end
|
||||
|
||||
def payment_method_type_options(providers)
|
||||
providers.map { |p| [payment_method_type_name(p), p] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,7 +12,10 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
self.include_root_in_json = true
|
||||
|
||||
def self.image_service
|
||||
ENV["S3_BUCKET"].present? ? :amazon_public : :local
|
||||
return :local if ENV["S3_BUCKET"].blank?
|
||||
return :amazon_public if ENV["S3_ENDPOINT"].blank?
|
||||
|
||||
:s3_compatible_storage_public
|
||||
end
|
||||
|
||||
# We might have a development environment without S3 but with a database
|
||||
|
||||
@@ -50,11 +50,11 @@ class Enterprise < ApplicationRecord
|
||||
has_many :distributed_orders, class_name: 'Spree::Order',
|
||||
foreign_key: 'distributor_id',
|
||||
inverse_of: :distributor,
|
||||
dependent: :restrict_with_exception
|
||||
dependent: :restrict_with_error
|
||||
|
||||
belongs_to :address, class_name: 'Spree::Address'
|
||||
belongs_to :business_address, optional: true, class_name: 'Spree::Address', dependent: :destroy
|
||||
has_many :enterprise_fees, dependent: :restrict_with_exception
|
||||
has_many :enterprise_fees, dependent: :restrict_with_error
|
||||
has_many :enterprise_roles, dependent: :destroy
|
||||
has_many :users, through: :enterprise_roles
|
||||
belongs_to :owner, class_name: 'Spree::User',
|
||||
@@ -62,18 +62,18 @@ class Enterprise < ApplicationRecord
|
||||
has_many :distributor_payment_methods,
|
||||
inverse_of: :distributor,
|
||||
foreign_key: :distributor_id,
|
||||
dependent: :restrict_with_exception
|
||||
dependent: :restrict_with_error
|
||||
has_many :distributor_shipping_methods,
|
||||
inverse_of: :distributor,
|
||||
foreign_key: :distributor_id,
|
||||
dependent: :restrict_with_exception
|
||||
dependent: :restrict_with_error
|
||||
has_many :payment_methods, through: :distributor_payment_methods
|
||||
has_many :shipping_methods, through: :distributor_shipping_methods
|
||||
has_many :customers, dependent: :destroy
|
||||
has_many :inventory_items, dependent: :destroy
|
||||
has_many :tag_rules, dependent: :destroy
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers, dependent: :restrict_with_exception
|
||||
has_many :vouchers, dependent: :restrict_with_error
|
||||
has_many :connected_apps, dependent: :destroy
|
||||
has_many :dfc_permissions, dependent: :destroy
|
||||
has_one :custom_tab, dependent: :destroy
|
||||
@@ -111,14 +111,14 @@ class Enterprise < ApplicationRecord
|
||||
end
|
||||
|
||||
validates :logo,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
|
||||
validates :promo_image,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
|
||||
validates :white_label_logo,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
|
||||
validates :terms_and_conditions, content_type: {
|
||||
in: "application/pdf",
|
||||
message: I18n.t(:enterprise_terms_and_conditions_type_error),
|
||||
|
||||
@@ -29,11 +29,11 @@ class EnterpriseGroup < ApplicationRecord
|
||||
has_one_attached :promo_image, service: image_service
|
||||
|
||||
validates :logo,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
|
||||
validates :promo_image,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
|
||||
|
||||
scope :by_position, -> { order('position ASC') }
|
||||
scope :on_front_page, -> { where(on_front_page: true) }
|
||||
|
||||
@@ -95,8 +95,8 @@ class Invoice
|
||||
def display_line_item_tax_rate(item)
|
||||
all_tax_adjustments.select { |a|
|
||||
a.adjustable.type == 'Spree::LineItem' && a.adjustable.id == item.id
|
||||
}.map(&:originator).map { |tr|
|
||||
number_to_percentage(tr.amount * 100, precision: 1)
|
||||
}.map(&:originator).map(&:amount).sort.map { |amount|
|
||||
number_to_percentage(amount * 100, precision: 1)
|
||||
}.join(", ")
|
||||
end
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ module Spree
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
REPORTS_SEARCH_ACTIONS = [
|
||||
:search_enterprise_fees, :search_enterprise_fee_owners, :search_distributors,
|
||||
:search_suppliers, :search_order_cycles, :search_order_customers
|
||||
].freeze
|
||||
|
||||
def initialize(user)
|
||||
clear_aliased_actions
|
||||
|
||||
@@ -260,7 +265,8 @@ module Spree
|
||||
can [:admin, :index, :import], ::Admin::DfcProductImportsController
|
||||
|
||||
# Reports page
|
||||
can [:admin, :index, :show, :create], ::Admin::ReportsController
|
||||
can [:admin, :index, :show, :create, *REPORTS_SEARCH_ACTIONS],
|
||||
::Admin::ReportsController
|
||||
can [:admin, :show, :create, :customers, :orders_and_distributors, :group_buys, :payments,
|
||||
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management,
|
||||
:packing, :enterprise_fee_summary, :bulk_coop, :suppliers], :report
|
||||
@@ -392,7 +398,7 @@ module Spree
|
||||
end
|
||||
|
||||
# Reports page
|
||||
can [:admin, :index, :show, :create], ::Admin::ReportsController
|
||||
can [:admin, :index, :show, :create, *REPORTS_SEARCH_ACTIONS], ::Admin::ReportsController
|
||||
can [:admin, :customers, :group_buys, :sales_tax, :payments,
|
||||
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
|
||||
:order_cycle_management, :xero_invoices, :enterprise_fee_summary, :bulk_coop], :report
|
||||
|
||||
@@ -31,7 +31,6 @@ module Spree
|
||||
preference :admin_products_per_page, :integer, default: 10
|
||||
# Should only be true if you don't need to track inventory
|
||||
preference :allow_backorder_shipping, :boolean, default: false
|
||||
preference :allow_checkout_on_gateway_error, :boolean, default: false
|
||||
preference :allow_guest_checkout, :boolean, default: true
|
||||
preference :currency_decimal_mark, :string, default: "."
|
||||
preference :currency_symbol_position, :string, default: "before"
|
||||
|
||||
@@ -5,7 +5,7 @@ module Spree
|
||||
acts_as_taggable
|
||||
include PaymentMethodDistributors
|
||||
|
||||
delegate :authorize, :purchase, :capture, :void, :credit, to: :provider
|
||||
delegate :authorize, :purchase, :capture, :void, :credit, :refund, to: :provider
|
||||
|
||||
validates :name, :type, presence: true
|
||||
|
||||
@@ -35,6 +35,10 @@ module Spree
|
||||
end
|
||||
|
||||
def method_missing(method, *)
|
||||
message = "Deprecated delegation of Gateway##{method}"
|
||||
Alert.raise(message)
|
||||
raise message if Rails.env.local?
|
||||
|
||||
if @provider.nil? || !@provider.respond_to?(method)
|
||||
super
|
||||
else
|
||||
@@ -42,10 +46,6 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def payment_profiles_supported?
|
||||
false
|
||||
end
|
||||
|
||||
def method_type
|
||||
'gateway'
|
||||
end
|
||||
|
||||
@@ -35,10 +35,6 @@ module Spree
|
||||
ActiveMerchant::Billing::StripePaymentIntentsGateway
|
||||
end
|
||||
|
||||
def payment_profiles_supported?
|
||||
true
|
||||
end
|
||||
|
||||
def stripe_account_id
|
||||
StripeAccount.find_by(enterprise_id: preferred_enterprise_id)&.stripe_user_id
|
||||
end
|
||||
@@ -84,7 +80,7 @@ module Spree
|
||||
end
|
||||
|
||||
# NOTE: this method is required by Spree::Payment::Processing
|
||||
def void(payment_intent_id, _creditcard, gateway_options)
|
||||
def void(payment_intent_id, gateway_options)
|
||||
payment_intent_response = Stripe::PaymentIntent.retrieve(
|
||||
payment_intent_id, stripe_account: stripe_account_id
|
||||
)
|
||||
@@ -101,7 +97,13 @@ module Spree
|
||||
end
|
||||
|
||||
# NOTE: this method is required by Spree::Payment::Processing
|
||||
def credit(money, _creditcard, payment_intent_id, gateway_options)
|
||||
def credit(money, payment_intent_id, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.refund(money, payment_intent_id, gateway_options)
|
||||
end
|
||||
|
||||
# NOTE: this method is required by Spree::Payment::Processing
|
||||
def refund(money, payment_intent_id, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.refund(money, payment_intent_id, gateway_options)
|
||||
end
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
module Spree
|
||||
class Image < Asset
|
||||
ACCEPTED_CONTENT_TYPES = %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
|
||||
has_one_attached :attachment, service: image_service do |attachment|
|
||||
attachment.variant :mini, resize_to_fill: [48, 48]
|
||||
attachment.variant :small, resize_to_fill: [227, 227]
|
||||
@@ -11,8 +13,8 @@ module Spree
|
||||
|
||||
validates :attachment,
|
||||
attached: true,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
processable_file: true,
|
||||
content_type: ACCEPTED_CONTENT_TYPES
|
||||
validate :no_attachment_errors
|
||||
|
||||
def self.default_image_url(size)
|
||||
|
||||
@@ -24,6 +24,8 @@ module Spree
|
||||
before_validation :copy_price
|
||||
before_validation :copy_tax_category
|
||||
before_validation :copy_dimensions
|
||||
before_validation :copy_product_name, on: :create
|
||||
before_validation :copy_variant_name, on: :create
|
||||
|
||||
validates :quantity, numericality: {
|
||||
only_integer: true,
|
||||
@@ -250,6 +252,18 @@ module Spree
|
||||
adjustments.enterprise_fee
|
||||
end
|
||||
|
||||
def full_variant_name
|
||||
return variant_name if variant_name.present?
|
||||
|
||||
variant.full_name
|
||||
end
|
||||
|
||||
def full_product_name
|
||||
return product_name if product_name.present?
|
||||
|
||||
variant.product.name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def computed_weight_from_variant
|
||||
@@ -274,6 +288,18 @@ module Spree
|
||||
order.create_tax_charge!
|
||||
end
|
||||
|
||||
def copy_product_name
|
||||
return if variant.nil? || variant.product.nil?
|
||||
|
||||
self.product_name = variant.product.name
|
||||
end
|
||||
|
||||
def copy_variant_name
|
||||
return if variant.nil?
|
||||
|
||||
self.variant_name = variant.full_name
|
||||
end
|
||||
|
||||
def update_inventory_before_destroy
|
||||
# This is necessary before destroying the line item
|
||||
# so that update_inventory will restore stock to the variant
|
||||
|
||||
@@ -437,18 +437,13 @@ module Spree
|
||||
#
|
||||
# Returns:
|
||||
# - true if all pending_payments processed successfully
|
||||
# - true if a payment failed, ie. raised a GatewayError
|
||||
# which gets rescued and converted to TRUE when
|
||||
# :allow_checkout_gateway_error is set to true
|
||||
# - false if a payment failed, ie. raised a GatewayError
|
||||
# which gets rescued and converted to FALSE when
|
||||
# :allow_checkout_on_gateway_error is set to false
|
||||
# which gets rescued and converted to FALSE
|
||||
#
|
||||
def process_payments!
|
||||
process_each_payment(&:process!)
|
||||
rescue Core::GatewayError => e
|
||||
result = !!Spree::Config[:allow_checkout_on_gateway_error]
|
||||
errors.add(:base, e.message) && (return result)
|
||||
errors.add(:base, e.message) && (return false)
|
||||
end
|
||||
|
||||
def process_payments_offline!
|
||||
|
||||
@@ -34,7 +34,7 @@ module Spree
|
||||
|
||||
# invalidate previously entered payments
|
||||
after_create :invalidate_old_payments
|
||||
after_save :create_payment_profile, if: :profiles_supported?
|
||||
after_save :create_payment_profile
|
||||
|
||||
# update the order totals, etc.
|
||||
after_save :ensure_correct_adjustment, :update_order
|
||||
@@ -101,6 +101,24 @@ module Spree
|
||||
end
|
||||
|
||||
after_transition to: :completed, do: :set_captured_at
|
||||
after_transition do |payment, transition|
|
||||
# Catch any exceptions to prevent any rollback potentially
|
||||
# preventing payment from going through
|
||||
ActiveSupport::Notifications.instrument(
|
||||
"ofn.payment_transition", payment: payment, event: transition.to
|
||||
)
|
||||
rescue StandardError => e
|
||||
Rails.logger.fatal "ActiveSupport::Notification.instrument failed params: " \
|
||||
"<event_type:ofn.payment_transition> " \
|
||||
"<payment_id:#{payment.id}> " \
|
||||
"<event:#{transition.to}>"
|
||||
Alert.raise(
|
||||
e,
|
||||
metadata: {
|
||||
event_tye: "ofn.payment_transition", payment_id: payment.id, event: transition.to
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def money
|
||||
@@ -199,18 +217,13 @@ module Spree
|
||||
errors.blank?
|
||||
end
|
||||
|
||||
def profiles_supported?
|
||||
payment_method.respond_to?(:payment_profiles_supported?) &&
|
||||
payment_method.payment_profiles_supported?
|
||||
end
|
||||
|
||||
def create_payment_profile
|
||||
return unless source.is_a?(CreditCard)
|
||||
return unless source.try(:save_requested_by_customer?)
|
||||
return unless source.number || source.gateway_payment_profile_id
|
||||
return unless source.gateway_customer_profile_id.nil?
|
||||
|
||||
payment_method.create_profile(self)
|
||||
payment_method.try(:create_profile, self)
|
||||
rescue ActiveMerchant::ConnectionError => e
|
||||
gateway_error e
|
||||
end
|
||||
|
||||
@@ -58,16 +58,7 @@ module Spree
|
||||
protect_from_connection_error do
|
||||
check_environment
|
||||
|
||||
response = if payment_method.payment_profiles_supported?
|
||||
# Gateways supporting payment profiles will need access to credit
|
||||
# card object because this stores the payment profile information
|
||||
# so supply the authorization itself as well as the credit card,
|
||||
# rather than just the authorization code
|
||||
payment_method.void(response_code, source, gateway_options)
|
||||
else
|
||||
# Standard ActiveMerchant void usage
|
||||
payment_method.void(response_code, gateway_options)
|
||||
end
|
||||
response = payment_method.void(response_code, gateway_options)
|
||||
|
||||
record_response(response)
|
||||
|
||||
@@ -86,20 +77,11 @@ module Spree
|
||||
|
||||
credit_amount = calculate_refund_amount(credit_amount)
|
||||
|
||||
response = if payment_method.payment_profiles_supported?
|
||||
payment_method.credit(
|
||||
(credit_amount * 100).round,
|
||||
source,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
else
|
||||
payment_method.credit(
|
||||
(credit_amount * 100).round,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
end
|
||||
response = payment_method.credit(
|
||||
(credit_amount * 100).round,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
|
||||
record_response(response)
|
||||
|
||||
@@ -125,20 +107,11 @@ module Spree
|
||||
|
||||
refund_amount = calculate_refund_amount(refund_amount)
|
||||
|
||||
response = if payment_method.payment_profiles_supported?
|
||||
payment_method.refund(
|
||||
(refund_amount * 100).round,
|
||||
source,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
else
|
||||
payment_method.refund(
|
||||
(refund_amount * 100).round,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
end
|
||||
response = payment_method.refund(
|
||||
(refund_amount * 100).round,
|
||||
response_code,
|
||||
gateway_options
|
||||
)
|
||||
|
||||
record_response(response)
|
||||
|
||||
@@ -183,6 +156,7 @@ module Spree
|
||||
|
||||
options.merge!({ billing_address: order.bill_address.try(:active_merchant_hash),
|
||||
shipping_address: order.ship_address.try(:active_merchant_hash) })
|
||||
options.merge!(payment: self)
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
@@ -52,10 +52,6 @@ module Spree
|
||||
.where(environment: [Rails.env, "", nil])
|
||||
}
|
||||
|
||||
def self.providers
|
||||
Rails.application.config.spree.payment_methods
|
||||
end
|
||||
|
||||
def configured?
|
||||
!stripe? || stripe_configured?
|
||||
end
|
||||
@@ -93,12 +89,8 @@ module Spree
|
||||
type.demodulize.downcase
|
||||
end
|
||||
|
||||
def self.find_with_destroyed(*args)
|
||||
unscoped { find(*args) }
|
||||
end
|
||||
|
||||
def payment_profiles_supported?
|
||||
false
|
||||
def self.find_with_destroyed(*)
|
||||
unscoped { find(*) }
|
||||
end
|
||||
|
||||
def source_required?
|
||||
@@ -117,11 +109,6 @@ module Spree
|
||||
distributors.include?(distributor)
|
||||
end
|
||||
|
||||
def self.clean_name
|
||||
i18n_key = "spree.admin.payment_methods.providers.#{name.demodulize.downcase}"
|
||||
I18n.t(i18n_key)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def distributor_validation
|
||||
|
||||
94
app/models/spree/payment_method/taler.rb
Normal file
94
app/models/spree/payment_method/taler.rb
Normal file
@@ -0,0 +1,94 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "taler"
|
||||
|
||||
module Spree
|
||||
class PaymentMethod
|
||||
# GNU Taler is a distributed, open source payment system.
|
||||
# You need a hosted Taler backend server to process payments.
|
||||
#
|
||||
# For testing, you can use the official demo backend:
|
||||
#
|
||||
# - Merchant UX: https://backend.demo.taler.net
|
||||
# - Username: sandbox
|
||||
# - Password: sandbox
|
||||
#
|
||||
# Configure this payment method for testing with:
|
||||
#
|
||||
# - backend_url: https://backend.demo.taler.net/instances/sandbox
|
||||
# - api_key: sandbox
|
||||
class Taler < PaymentMethod
|
||||
preference :backend_url, :string
|
||||
preference :api_key, :password
|
||||
|
||||
# Name of the view to display during checkout
|
||||
def method_type
|
||||
"check" # empty view
|
||||
end
|
||||
|
||||
def external_gateway?
|
||||
true
|
||||
end
|
||||
|
||||
# The backend provides this URL. It can look like this:
|
||||
# https://backend.demo.taler.net/instances/blog/orders/2026..?token=S8Y..&session_id=b0b..
|
||||
def external_payment_url(options)
|
||||
order = options.fetch(:order)
|
||||
payment = load_payment(order)
|
||||
|
||||
payment.source ||= self
|
||||
payment.response_code ||= create_taler_order(payment)
|
||||
payment.redirect_auth_url ||= fetch_order_url(payment)
|
||||
payment.save! if payment.changed?
|
||||
|
||||
payment.redirect_auth_url
|
||||
end
|
||||
|
||||
# Main method called by Spree::Payment::Processing during checkout
|
||||
# when the user is redirected back to the app.
|
||||
#
|
||||
# The payment has already been made and we need to verify the success.
|
||||
def purchase(_money, _source, gateway_options)
|
||||
payment = gateway_options[:payment]
|
||||
|
||||
return unless payment.response_code
|
||||
|
||||
taler_order = client.fetch_order(payment.response_code)
|
||||
status = taler_order["order_status"]
|
||||
success = (status == "paid")
|
||||
message = I18n.t(status, default: status, scope: "taler.order_status")
|
||||
|
||||
ActiveMerchant::Billing::Response.new(success, message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_payment(order)
|
||||
order.payments.checkout.where(payment_method: self).last
|
||||
end
|
||||
|
||||
def create_taler_order(payment)
|
||||
# We are ignoring currency for now so that we can test with the
|
||||
# current demo backend only working with the KUDOS currency.
|
||||
taler_amount = "KUDOS:#{payment.amount}"
|
||||
urls = Rails.application.routes.url_helpers
|
||||
new_order = client.create_order(
|
||||
taler_amount,
|
||||
I18n.t("payment_method_taler.order_summary"),
|
||||
urls.payment_gateways_confirm_taler_url(payment_id: payment.id),
|
||||
)
|
||||
|
||||
new_order["order_id"]
|
||||
end
|
||||
|
||||
def fetch_order_url(payment)
|
||||
order = client.fetch_order(payment.response_code)
|
||||
order["order_status_url"]
|
||||
end
|
||||
|
||||
def client
|
||||
@client ||= ::Taler::Client.new(preferred_backend_url, preferred_api_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -251,7 +251,7 @@ module Spree
|
||||
transaction do
|
||||
ExchangeVariant.
|
||||
where(exchange_variants: { variant_id: variants.with_deleted.
|
||||
select(:id) }).destroy_all
|
||||
select(:id) }).destroy_all
|
||||
|
||||
yield
|
||||
end
|
||||
|
||||
@@ -35,8 +35,8 @@ module Spree
|
||||
taxons
|
||||
.pluck('spree_taxons.id, enterprises.id AS enterprise_id')
|
||||
.each_with_object({}) do |(taxon_id, enterprise_id), collection|
|
||||
collection[enterprise_id.to_i] ||= Set.new
|
||||
collection[enterprise_id.to_i] << taxon_id
|
||||
collection[enterprise_id.to_i] ||= Set.new
|
||||
collection[enterprise_id.to_i] << taxon_id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -167,8 +167,8 @@ module Spree
|
||||
# In Rails 3, merging two scopes on the same column will consider only the last scope.
|
||||
def self.in_distributor(distributor)
|
||||
where(id: ExchangeVariant.select(:variant_id).
|
||||
joins(:exchange).
|
||||
where('exchanges.incoming = ? AND exchanges.receiver_id = ?', false, distributor))
|
||||
joins(:exchange).
|
||||
where('exchanges.incoming = ? AND exchanges.receiver_id = ?', false, distributor))
|
||||
end
|
||||
|
||||
def self.indexed
|
||||
@@ -179,11 +179,11 @@ module Spree
|
||||
# "where(id:" is necessary so that the returned relation has no includes
|
||||
# The relation without includes will not be readonly and allow updates on it
|
||||
where(spree_variants: { id: joins(:prices).
|
||||
where(deleted_at: nil).
|
||||
where('spree_prices.currency' =>
|
||||
where(deleted_at: nil).
|
||||
where('spree_prices.currency' =>
|
||||
currency || CurrentConfig.get(:currency)).
|
||||
where.not(spree_prices: { amount: nil }).
|
||||
select("spree_variants.id") })
|
||||
where.not(spree_prices: { amount: nil }).
|
||||
select("spree_variants.id") })
|
||||
end
|
||||
|
||||
def self.linked_to(semantic_id)
|
||||
|
||||
@@ -2,5 +2,11 @@
|
||||
|
||||
# Records a webhook url to send notifications to
|
||||
class WebhookEndpoint < ApplicationRecord
|
||||
WEBHOOK_TYPES = %w(order_cycle_opened payment_status_changed).freeze
|
||||
|
||||
validates :url, presence: true
|
||||
validates :webhook_type, presence: true, inclusion: { in: WEBHOOK_TYPES }
|
||||
|
||||
scope :order_cycle_opened, -> { where(webhook_type: "order_cycle_opened") }
|
||||
scope :payment_status, -> { where(webhook_type: "payment_status_changed") }
|
||||
end
|
||||
|
||||
@@ -60,8 +60,8 @@ class ProductScopeQuery
|
||||
|
||||
def product_query_includes
|
||||
[
|
||||
image: { attachment_attachment: :blob },
|
||||
variants: [:default_price, :stock_items, :variant_overrides]
|
||||
{ image: { attachment_attachment: :blob },
|
||||
variants: [:default_price, :stock_items, :variant_overrides] }
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ class DfcCatalogImporter
|
||||
.includes(:semantic_links).references(:semantic_links)
|
||||
.where.not(semantic_links: { semantic_id: present_ids })
|
||||
.select do |variant|
|
||||
# Variants that were in the same catalog before:
|
||||
variant.semantic_links.map(&:semantic_id).any? do |semantic_id|
|
||||
FdcUrlBuilder.new(semantic_id).catalog_url == catalog_url
|
||||
end
|
||||
# Variants that were in the same catalog before:
|
||||
variant.semantic_links.map(&:semantic_id).any? do |semantic_id|
|
||||
FdcUrlBuilder.new(semantic_id).catalog_url == catalog_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,9 @@ class ImageImporter
|
||||
|
||||
image = Spree::Image.create do |img|
|
||||
PrivateAddressCheck.only_public_connections do
|
||||
img.attachment.attach(io: valid_url.open, filename:, metadata:)
|
||||
io = valid_url.open
|
||||
content_type = Marcel::MimeType.for(io)
|
||||
img.attachment.attach(io:, filename:, metadata:, content_type:)
|
||||
end
|
||||
end
|
||||
product.image = image if image
|
||||
|
||||
@@ -51,8 +51,8 @@ class LineItemSyncer
|
||||
def destroy_obsolete_items(order)
|
||||
order.line_items.
|
||||
where(variant_id: subscription_line_items.
|
||||
select(&:marked_for_destruction?).
|
||||
map(&:variant_id)).
|
||||
select(&:marked_for_destruction?).
|
||||
map(&:variant_id)).
|
||||
destroy_all
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ class LineItemSyncer
|
||||
end
|
||||
|
||||
def add_order_update_issue(order, line_item)
|
||||
issue_description = "#{line_item.product.name} - #{line_item.variant.full_name}"
|
||||
issue_description = "#{line_item.product.name} - #{line_item.full_variant_name}"
|
||||
issue_description << " - #{stock_issue_description(line_item)}" if line_item.insufficient_stock?
|
||||
order_update_issues.add(order, issue_description)
|
||||
end
|
||||
|
||||
@@ -11,10 +11,12 @@ module OrderCycles
|
||||
.merge(coordinator_name: order_cycle.coordinator.name)
|
||||
|
||||
# Endpoints for coordinator owner
|
||||
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints
|
||||
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints.order_cycle_opened
|
||||
|
||||
# Plus unique endpoints for distributor owners (ignore duplicates)
|
||||
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints)
|
||||
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map { |owner|
|
||||
owner.webhook_endpoints.order_cycle_opened
|
||||
}
|
||||
|
||||
webhook_endpoints.each do |endpoint|
|
||||
WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload, at:)
|
||||
|
||||
@@ -130,12 +130,11 @@ module Orders
|
||||
|
||||
def order_cycle_fees
|
||||
return @order_cycle_fees if defined? @order_cycle_fees
|
||||
return [] unless order_cycle && distributor
|
||||
|
||||
@order_cycle_fees = begin
|
||||
fees = []
|
||||
|
||||
return fees unless order_cycle && distributor
|
||||
|
||||
order_cycle.exchanges.supplying_to(distributor).each do |exchange|
|
||||
exchange.enterprise_fees.per_item.each do |enterprise_fee|
|
||||
fee_value = FeeValue.new(fee: enterprise_fee, role: exchange.role)
|
||||
|
||||
13
app/services/payments/status_changed_listener_service.rb
Normal file
13
app/services/payments/status_changed_listener_service.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Called by "ActiveSupport::Notifications" when an "ofn.payment_transition" occurs
|
||||
# Event originate from Spree::Payment event machine
|
||||
#
|
||||
module Payments
|
||||
class StatusChangedListenerService
|
||||
def call(_name, started, _finished, _unique_id, payload)
|
||||
event = "payment.#{payload[:event]}"
|
||||
Payments::WebhookService.create_webhook_job(payment: payload[:payment], event:, at: started)
|
||||
end
|
||||
end
|
||||
end
|
||||
84
app/services/payments/webhook_payload.rb
Normal file
84
app/services/payments/webhook_payload.rb
Normal file
@@ -0,0 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Payments
|
||||
class WebhookPayload
|
||||
def initialize(payment:, order:, enterprise:)
|
||||
@payment = payment
|
||||
@order = order
|
||||
@enterprise = enterprise
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
payment: @payment.slice(:updated_at, :amount, :state),
|
||||
enterprise: @enterprise.slice(:abn, :acn, :name)
|
||||
.merge(address: @enterprise.address.slice(:address1, :address2, :city, :zipcode)),
|
||||
order: @order.slice(:total, :currency).merge(line_items: line_items)
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
def self.test_data
|
||||
new(payment: test_payment, order: test_order, enterprise: test_enterprise)
|
||||
end
|
||||
|
||||
def self.test_payment
|
||||
{
|
||||
updated_at: Time.zone.now,
|
||||
amount: 0.00,
|
||||
state: "completed"
|
||||
}
|
||||
end
|
||||
|
||||
def self.test_order
|
||||
order = Spree::Order.new(
|
||||
total: 0.00,
|
||||
currency: "AUD",
|
||||
)
|
||||
|
||||
tax_category = Spree::TaxCategory.new(name: "VAT")
|
||||
product = Spree::Product.new(name: "Test product")
|
||||
Spree::Variant.new(product:, display_name: "")
|
||||
order.line_items << Spree::LineItem.new(
|
||||
quantity: 1,
|
||||
price: 20.00,
|
||||
tax_category:,
|
||||
product:,
|
||||
unit_presentation: "1kg"
|
||||
)
|
||||
|
||||
order
|
||||
end
|
||||
|
||||
def self.test_enterprise
|
||||
enterprise = Enterprise.new(
|
||||
abn: "65797115831",
|
||||
acn: "",
|
||||
name: "TEST Enterprise",
|
||||
)
|
||||
enterprise.address = Spree::Address.new(
|
||||
address1: "1 testing street",
|
||||
address2: "",
|
||||
city: "TestCity",
|
||||
zipcode: "1234"
|
||||
)
|
||||
|
||||
enterprise
|
||||
end
|
||||
|
||||
private_class_method :test_payment, :test_order, :test_enterprise
|
||||
|
||||
private
|
||||
|
||||
def line_items
|
||||
@order.line_items.map do |li|
|
||||
li.slice(:quantity, :price)
|
||||
.merge(
|
||||
tax_category_name: li.tax_category&.name,
|
||||
product_name: li.product.name,
|
||||
name_to_display: li.display_name,
|
||||
unit_to_display: li.unit_presentation
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
30
app/services/payments/webhook_service.rb
Normal file
30
app/services/payments/webhook_service.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Create a webhook payload for a payment status event.
|
||||
# The payload will be delivered asynchronously.
|
||||
|
||||
module Payments
|
||||
class WebhookService
|
||||
def self.create_webhook_job(payment:, event:, at:)
|
||||
order = payment.order
|
||||
payload = WebhookPayload.new(payment:, order:, enterprise: order.distributor).to_hash
|
||||
|
||||
coordinator = payment.order.order_cycle.coordinator
|
||||
webhook_urls(coordinator).each do |url|
|
||||
WebhookDeliveryJob.perform_later(url, event, payload, at:)
|
||||
end
|
||||
end
|
||||
|
||||
def self.webhook_urls(coordinator)
|
||||
# url for coordinator owner
|
||||
webhook_urls = coordinator.owner.webhook_endpoints.payment_status.pluck(:url)
|
||||
|
||||
# plus url for coordinator manager (ignore duplicate)
|
||||
users_webhook_urls = coordinator.users.flat_map do |user|
|
||||
user.webhook_endpoints.payment_status.pluck(:url)
|
||||
end
|
||||
|
||||
webhook_urls | users_webhook_urls
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,12 +14,12 @@ module PermittedAttributes
|
||||
|
||||
def self.attributes
|
||||
basic_permitted_attributes + [
|
||||
group_ids: [], user_ids: [],
|
||||
shipping_method_ids: [], payment_method_ids: [],
|
||||
address_attributes: PermittedAttributes::Address.attributes,
|
||||
business_address_attributes: PermittedAttributes::BusinessAddress.attributes,
|
||||
producer_properties_attributes: [:id, :property_name, :value, :_destroy],
|
||||
custom_tab_attributes: PermittedAttributes::CustomTab.attributes,
|
||||
{ group_ids: [], user_ids: [],
|
||||
shipping_method_ids: [], payment_method_ids: [],
|
||||
address_attributes: PermittedAttributes::Address.attributes,
|
||||
business_address_attributes: PermittedAttributes::BusinessAddress.attributes,
|
||||
producer_properties_attributes: [:id, :property_name, :value, :_destroy],
|
||||
custom_tab_attributes: PermittedAttributes::CustomTab.attributes },
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ module PermittedAttributes
|
||||
private
|
||||
|
||||
def attributes
|
||||
self.class.basic_attributes + [incoming_exchanges: permitted_exchange_attributes,
|
||||
outgoing_exchanges: permitted_exchange_attributes]
|
||||
self.class.basic_attributes + [{ incoming_exchanges: permitted_exchange_attributes,
|
||||
outgoing_exchanges: permitted_exchange_attributes }]
|
||||
end
|
||||
|
||||
def permitted_exchange_attributes
|
||||
|
||||
@@ -11,7 +11,7 @@ module PermittedAttributes
|
||||
[:name, :description, :type, :active,
|
||||
:environment, :display_on, :tag_list,
|
||||
:preferred_enterprise_id, :preferred_server, :preferred_login, :preferred_password,
|
||||
:calculator_type, :preferred_api_key,
|
||||
:calculator_type, :preferred_api_key, :preferred_backend_url,
|
||||
:preferred_signature, :preferred_solution, :preferred_landing_page, :preferred_logourl,
|
||||
:preferred_test_mode, :calculator_type, { distributor_ids: [] },
|
||||
{ calculator_attributes: PermittedAttributes::Calculator.attributes }]
|
||||
|
||||
@@ -26,11 +26,11 @@ module PermittedAttributes
|
||||
|
||||
def other_permitted_attributes
|
||||
[
|
||||
subscription_line_items_attributes: [
|
||||
:id, :quantity, :variant_id, :price_estimate, :_destroy
|
||||
],
|
||||
bill_address_attributes: PermittedAttributes::Address.attributes,
|
||||
ship_address_attributes: PermittedAttributes::Address.attributes
|
||||
{ subscription_line_items_attributes: [
|
||||
:id, :quantity, :variant_id, :price_estimate, :_destroy
|
||||
],
|
||||
bill_address_attributes: PermittedAttributes::Address.attributes,
|
||||
ship_address_attributes: PermittedAttributes::Address.attributes }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
module Vine
|
||||
class VoucherValidatorService
|
||||
VINE_ERRORS = {
|
||||
# https://github.com/openfoodfoundation/vine/blob/main/app/Enums/ApiResponse.php
|
||||
"This voucher has expired." => :expired,
|
||||
}.freeze
|
||||
|
||||
attr_reader :voucher_code, :errors
|
||||
|
||||
def initialize(voucher_code:, enterprise:)
|
||||
@@ -42,8 +47,10 @@ module Vine
|
||||
end
|
||||
|
||||
def handle_errors(response)
|
||||
if response[:status] == 400
|
||||
errors[:invalid_voucher] = I18n.t("vine_voucher_validator_service.errors.invalid_voucher")
|
||||
if [400, 409].include?(response[:status])
|
||||
message = response[:body] && JSON.parse(response[:body]).dig("meta", "message")
|
||||
key = VINE_ERRORS.fetch(message, :invalid_voucher)
|
||||
errors[:invalid_voucher] = I18n.t("vine_voucher_validator_service.errors.#{key}")
|
||||
elsif response[:status] == 404
|
||||
errors[:not_found_voucher] =
|
||||
I18n.t("vine_voucher_validator_service.errors.not_found_voucher")
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
%tbody
|
||||
= f.fields_for :collection do |enterprise_form|
|
||||
- enterprise = enterprise_form.object
|
||||
%tr{class: "enterprise-#{enterprise.id}"}
|
||||
%tr{class: "enterprise-#{enterprise.id}", id: "resource-#{enterprise.id}"}
|
||||
%td= link_to enterprise.name, main_app.edit_admin_enterprise_path(enterprise)
|
||||
%td
|
||||
= enterprise_form.check_box :is_primary_producer
|
||||
|
||||
4
app/views/admin/enterprises/destroy.turbo_stream.haml
Normal file
4
app/views/admin/enterprises/destroy.turbo_stream.haml
Normal file
@@ -0,0 +1,4 @@
|
||||
- unless flash[:error]
|
||||
= turbo_stream.remove "resource-#{@object.id}"
|
||||
= turbo_stream.append "flashes" do
|
||||
= render(partial: 'admin/shared/flashes', locals: { flashes: flash })
|
||||
@@ -18,7 +18,7 @@
|
||||
%span{ "ofn-with-tip": '{{ orderCycle.producerNames }}', "ng-show": 'orderCycle.producers.length > 3' }
|
||||
{{ orderCycle.producers.length }}
|
||||
= t('.suppliers')
|
||||
%span{ "ng-hide": 'orderCycle.producers.length > 3', "ng-bind": 'orderCycle.producerNames' }
|
||||
%span{ "ng-hide": 'orderCycle.producers.length > 3', "ng-bind-html": 'orderCycle.producerNames' }
|
||||
%td.coordinator{ "ng-show": 'columns.coordinator.visible', "ng-bind-html": 'orderCycle.coordinator.name' }
|
||||
%td.shops{ "ng-show": 'columns.shops.visible' }
|
||||
%span{ "ofn-with-tip": '{{ orderCycle.shopNames }}', "ng-show": 'orderCycle.shops.length > 3' }
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
- select_tag_options = { class: "fullwidth",
|
||||
multiple: true ,
|
||||
data: { controller: "tom-select", "tom-select-placeholder-value": t(".select_tag"), "tom-select-options-value": '{ "maxItems": 5 , "plugins": { "remove_button": {} , "no_active_items": {}, "checkbox_options": { "checkedClassNames": ["ts-checked"], "uncheckedClassNames": ["ts-unchecked"] } } }' } }
|
||||
= select_tag :tags_name_in, options_for_select(available_tags, tags), select_tag_options
|
||||
= select_tag :tags_name_in, options_for_select(available_tags.unshift([t('.tags.none'), @none_tag_value]), tags), select_tag_options
|
||||
.submit
|
||||
.search-button
|
||||
= button_tag t(".search"), class: "secondary icon-search relaxed", name: nil
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
#no-products-actions
|
||||
%a{ href: "/admin/products/new", class: "button icon-plus", icon: "icon-plus" }
|
||||
= t(:new_product)
|
||||
%a{ href: "/admin/products/import", class: "button icon-upload secondary", icon: "icon-upload" }
|
||||
%a{ href: admin_product_import_path, class: "button icon-upload secondary", icon: "icon-upload" }
|
||||
= t(".import_products")
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
%td.col-sku.field.naked_inputs
|
||||
= f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku')
|
||||
= error_message_on variant, :sku
|
||||
%td.col-unir_scale.field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' }
|
||||
%td.col-unit_scale.field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' }
|
||||
= f.hidden_field :variant_unit
|
||||
= f.hidden_field :variant_unit_scale
|
||||
= f.select :variant_unit_with_scale,
|
||||
|
||||
@@ -1,23 +1,50 @@
|
||||
- search_url_query = {report_type: :enterprise_fee_summary, report_subtype: :enterprise_fees_with_tax_report_by_order}
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_hubs)
|
||||
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
|
||||
.omega.fourteen.columns
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :distributor_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :distributor_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_distributors_url))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :order_cycle_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :order_cycle_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_order_cycles_url))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:fee_name)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:enterprise_fee_id_in, fee_name_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :enterprise_fee_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :enterprise_fee_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_enterprise_fees_url(search_url_query)))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:fee_owner)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:enterprise_fee_owner_id_in, fee_owner_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_owner_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :enterprise_fee_owner_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :enterprise_fee_owner_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_enterprise_fee_owners_url(search_url_query)))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_customers)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:customer_id_in, customer_email_options(@data.order_customers), {selected: params.dig(:q, :customer_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :customer_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :customer_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_order_customers_url))
|
||||
|
||||
@@ -1,27 +1,57 @@
|
||||
- search_url_query = {report_type: :enterprise_fee_summary, report_subtype: :enterprise_fees_with_tax_report_by_producer}
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_hubs)
|
||||
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
.omega.fourteen.columns
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :distributor_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :distributor_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_distributors_url))
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_producers)
|
||||
.omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@data.orders_suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true})
|
||||
.omega.fourteen.columns
|
||||
= render(SearchableDropdownComponent.new(name: :supplier_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:supplier_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_suppliers_url))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :order_cycle_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :order_cycle_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_order_cycles_url))
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:fee_name)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:enterprise_fee_id_in, fee_name_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :enterprise_fee_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :enterprise_fee_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_enterprise_fees_url(search_url_query)))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:fee_owner)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:enterprise_fee_owner_id_in, fee_owner_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_owner_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :enterprise_fee_owner_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :enterprise_fee_owner_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_enterprise_fee_owners_url(search_url_query)))
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_customers)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:customer_id_in, customer_email_options(@data.order_customers), {selected: params.dig(:q, :customer_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :customer_id_in,
|
||||
options: [],
|
||||
selected_option: params.dig(:q, :customer_id_in),
|
||||
multiple: true,
|
||||
remote_url: admin_reports_search_order_customers_url))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.alert-box{ class: "#{type}" }
|
||||
= message
|
||||
- if local_assigns[:unconfirmed]
|
||||
%a{ "data-action": "login-modal#resend_confirmation", "data-tab": local_assigns[:tab] }
|
||||
= link_to spree_user_confirmation_path(spree_user: { email: }, tab: local_assigns[:tab]), data: { turbo_method: :post } do
|
||||
= t('devise.confirmations.resend_confirmation_email')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#forgot-tab
|
||||
= form_with url: spree_user_password_path, scope: :spree_user, data: { remote: "true" } do |form|
|
||||
= form_with url: spree_user_password_path, scope: :spree_user, data: { turbo: true } do |form|
|
||||
.row
|
||||
.large-12.columns#forgot-feedback
|
||||
|
||||
.row
|
||||
.large-12.columns
|
||||
= form.label :email, t(:signup_email)
|
||||
= form.email_field :email, { tabindex: 1, inputmode: "email", "data-login-modal-target": "email", "data-action": "input->login-modal#emailOnInput" }
|
||||
= form.email_field :email, { tabindex: 1, inputmode: "email" }
|
||||
.row
|
||||
.large-12.columns
|
||||
= form.submit t(:reset_password), { class: "button primary", tabindex: 2 }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#login-content
|
||||
= form_with url: spree_user_session_path, scope: :spree_user, data: { remote: "true" } do |form|
|
||||
= form_with url: spree_user_session_path, scope: :spree_user, data: { turbo: true } do |form|
|
||||
.row
|
||||
.large-12.columns#login-feedback
|
||||
- confirmation_result = request.query_parameters[:validation]
|
||||
@@ -10,7 +10,7 @@
|
||||
.row
|
||||
.large-12.columns
|
||||
= form.label :email, t(:email)
|
||||
= form.email_field :email, { tabindex: 1, inputmode: "email", autocomplete: "off", "data-login-modal-target": "email", "data-action": "input->login-modal#emailOnInput" }
|
||||
= form.email_field :email, { tabindex: 1, inputmode: "email", autocomplete: "off" }
|
||||
.row
|
||||
.large-12.columns
|
||||
= form.label :password, t(:password)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
- signup_form_user = Spree::User.new if local_assigns[:signup_form_user].nil?
|
||||
|
||||
#signup-tab
|
||||
= form_with model: signup_form_user, url: spree.account_path, scope: :user, data: { remote: "true" } do |form|
|
||||
= form_with model: signup_form_user, url: spree.account_path, scope: :user, data: { turbo: true } do |form|
|
||||
.row
|
||||
.large-12.columns#signup-feedback
|
||||
|
||||
.row
|
||||
.large-12.columns
|
||||
= form.label :email, t(:signup_email)
|
||||
= form.email_field :email, { tabindex: 1, "data-login-modal-target": "email", "data-action": "input->login-modal#emailOnInput" }
|
||||
= form.email_field :email, { tabindex: 1 }
|
||||
= form.error_message_on :email
|
||||
.row
|
||||
.large-12.columns
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
- else
|
||||
= favicon_link_tag "/favicon-staging.ico"
|
||||
%link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"}
|
||||
%link{href: asset_pack_path("media/fonts/OFN-v2.woff"), rel: "preload", as: "font", crossorigin: "anonymous"}
|
||||
%link{href: asset_pack_path("static/OFN-v2.woff"), rel: "preload", as: "font", crossorigin: "anonymous"}
|
||||
= render "layouts/matomo_tag"
|
||||
= language_meta_tags
|
||||
|
||||
= stylesheet_pack_tag "darkswarm", "data-turbo-track": "reload", media: "screen"
|
||||
= javascript_pack_tag "application", "data-turbo-track": "reload"
|
||||
= javascript_pack_tag "application", "data-turbo-track": "reload", defer: false # do not use defer because our javascript currently depend on order of execution of loaded script.
|
||||
|
||||
= render "layouts/shopfront_script" if @shopfront_layout
|
||||
= render "layouts/bugsnag_js"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
||||
%title
|
||||
= Spree::Config[:site_name]
|
||||
= stylesheet_link_tag 'mail', media: "screen"
|
||||
= stylesheet_pack_tag "mail", media: "screen"
|
||||
%body{:bgcolor => "#FFFFFF" }
|
||||
- unless @hide_ofn_navigation
|
||||
%table.head-wrap
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
= stylesheet_pack_tag "darkswarm", media: "screen"
|
||||
= javascript_include_tag "darkswarm/all"
|
||||
= javascript_pack_tag "application"
|
||||
= javascript_pack_tag "application", defer: false # do not use defer because our javascript currently depend on order of execution of loaded script.
|
||||
|
||||
= csrf_meta_tags
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= wicked_pdf_stylesheet_link_tag "mail"
|
||||
= pdf_stylesheet_pack_tag "mail"
|
||||
|
||||
%table{:width => "100%"}
|
||||
%tbody
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= wicked_pdf_stylesheet_link_tag "mail"
|
||||
= pdf_stylesheet_pack_tag "mail"
|
||||
|
||||
%table{:width => "100%"}
|
||||
%tbody
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= wicked_pdf_stylesheet_link_tag "mail"
|
||||
= pdf_stylesheet_pack_tag "mail"
|
||||
|
||||
%table{:width => "100%"}
|
||||
%tbody
|
||||
@@ -102,4 +102,4 @@
|
||||
= render partial: 'spree/admin/orders/_invoice/order_note'
|
||||
|
||||
.text-center
|
||||
= link_to_platform_terms
|
||||
= link_to_platform_terms
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
.alpha.four.columns
|
||||
= label :payment_method, :type, t('.provider')
|
||||
.omega.twelve.columns
|
||||
= collection_select(:payment_method, :type, @providers, :to_s, :clean_name, {}, { class: 'select2 fullwidth', 'provider-prefs-for' => "#{@object.id}"})
|
||||
= select(:payment_method, :type, payment_method_type_options(@providers), {}, { class: 'select2 fullwidth', required: true, placeholder: t("admin.choose"), 'provider-prefs-for' => "#{@object.id}"})
|
||||
|
||||
%div{"ng-include" => "include_html" }
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
- method.distributors.each do |distributor|
|
||||
= distributor.name
|
||||
%br/
|
||||
%td= method.class.clean_name
|
||||
%td= payment_method_type_name(method.class.name)
|
||||
- if spree_current_user.admin?
|
||||
%td.align-center= method.environment.to_s.titleize
|
||||
%td.align-center= method.display_on.blank? ? t('.both') : t('.' + method.display_on.to_s)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user