mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-16 19:16:49 +00:00
Compare commits
274 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4960402643 | ||
|
|
3557c10cd6 | ||
|
|
45dd421f1f | ||
|
|
11653c9430 | ||
|
|
d068094044 | ||
|
|
b892bf726e | ||
|
|
ba45dd84dd | ||
|
|
92e2a83e0e | ||
|
|
f42f513f8c | ||
|
|
54342254b6 | ||
|
|
470761da86 | ||
|
|
6fdf9fa038 | ||
|
|
bbec01a9cb | ||
|
|
e3001cc8c4 | ||
|
|
ad0d0e393e | ||
|
|
2690ff5eb5 | ||
|
|
2594382488 | ||
|
|
89cb3b7c14 | ||
|
|
3e0b358370 | ||
|
|
f206b7ed9e | ||
|
|
946b69242c | ||
|
|
a409d3b97e | ||
|
|
1f661c1e69 | ||
|
|
97f51d24b8 | ||
|
|
c3d274c84f | ||
|
|
0b03152f3b | ||
|
|
6f492d868c | ||
|
|
dab2a4af75 | ||
|
|
f7935bde34 | ||
|
|
61850f7e92 | ||
|
|
7df456c1e3 | ||
|
|
2fa86492d1 | ||
|
|
85860692ae | ||
|
|
8ad4d2cc2e | ||
|
|
c967eca9c4 | ||
|
|
7fff82eec3 | ||
|
|
c30ed0f7c8 | ||
|
|
cde420716b | ||
|
|
ee37c5d4a3 | ||
|
|
e9d236f761 | ||
|
|
8b78c28feb | ||
|
|
871b2f8816 | ||
|
|
214bdc6fbe | ||
|
|
d2fce053a7 | ||
|
|
11a7aa9f96 | ||
|
|
c0203e7091 | ||
|
|
44cc5cc665 | ||
|
|
6c8e383ab8 | ||
|
|
5c6d9a092e | ||
|
|
8d500a2c27 | ||
|
|
425674c4e5 | ||
|
|
e91e827f29 | ||
|
|
8f201c830a | ||
|
|
1b536d9265 | ||
|
|
1b8a9244a6 | ||
|
|
a40e883d17 | ||
|
|
6bf21d2abd | ||
|
|
29b9777a96 | ||
|
|
283dd84ad6 | ||
|
|
03828c5608 | ||
|
|
dad4061ff9 | ||
|
|
b19df0dfbf | ||
|
|
efb2ae6843 | ||
|
|
407fca17cb | ||
|
|
33409725a5 | ||
|
|
c70a694111 | ||
|
|
9c88c77066 | ||
|
|
42585bb074 | ||
|
|
b0211d0930 | ||
|
|
0675422509 | ||
|
|
5ff1b0d358 | ||
|
|
43cbac7d17 | ||
|
|
98e4f867b4 | ||
|
|
ebb15781d5 | ||
|
|
028c4f4281 | ||
|
|
ef7dfa3b4f | ||
|
|
71f9415d21 | ||
|
|
12848acf21 | ||
|
|
4f751c2711 | ||
|
|
920e2564a4 | ||
|
|
6b4e150e2a | ||
|
|
2d43e32688 | ||
|
|
a6cd393c57 | ||
|
|
b1060bf1c9 | ||
|
|
1bf5fbaf3a | ||
|
|
2ace3afd67 | ||
|
|
0bfceb877d | ||
|
|
c1d28b2e8f | ||
|
|
2841b42296 | ||
|
|
98e44e7a40 | ||
|
|
51c6c19f80 | ||
|
|
26947a64c0 | ||
|
|
df5c21048a | ||
|
|
8a124fb6be | ||
|
|
ced49e0217 | ||
|
|
75b7a1864b | ||
|
|
281d0eb417 | ||
|
|
496f5199c9 | ||
|
|
f13c0e1dca | ||
|
|
f801d4a4cb | ||
|
|
d94da8df1e | ||
|
|
70d9cfa08e | ||
|
|
193ae7936e | ||
|
|
0971e8d9b5 | ||
|
|
e78b44b9ca | ||
|
|
c5779eff81 | ||
|
|
b616c14f63 | ||
|
|
03cb14c926 | ||
|
|
da78e06a39 | ||
|
|
f4c351febf | ||
|
|
30184ac6aa | ||
|
|
a123d45eca | ||
|
|
a75f16e23c | ||
|
|
546e90b286 | ||
|
|
bf9f47a000 | ||
|
|
3da6e6b5ed | ||
|
|
111705f6f2 | ||
|
|
68d5327b12 | ||
|
|
688285c374 | ||
|
|
15058299d8 | ||
|
|
4e92f33b1c | ||
|
|
c7cb982c9a | ||
|
|
34e9112c0a | ||
|
|
28eb11bda5 | ||
|
|
d8e82d9c88 | ||
|
|
061fc321b4 | ||
|
|
5201e54f82 | ||
|
|
5192a08082 | ||
|
|
94d3f136e2 | ||
|
|
db3a923ea7 | ||
|
|
619eb43c57 | ||
|
|
680b6a7122 | ||
|
|
d9e9add78b | ||
|
|
8893824b06 | ||
|
|
5c221621d3 | ||
|
|
60d7ed5036 | ||
|
|
e71a747925 | ||
|
|
eedd421055 | ||
|
|
e74328f1d7 | ||
|
|
bb502c2e65 | ||
|
|
6abd652251 | ||
|
|
2f6b730ed4 | ||
|
|
3821eede51 | ||
|
|
2ff464321c | ||
|
|
fc8d9252d5 | ||
|
|
3e5becf70a | ||
|
|
ef841b161f | ||
|
|
c11dd2c1fb | ||
|
|
8bbc545e39 | ||
|
|
fc08913152 | ||
|
|
19ae3651fa | ||
|
|
66307c9277 | ||
|
|
65084422cb | ||
|
|
6f8ab56d2e | ||
|
|
ab6cdd4c78 | ||
|
|
865d81d28f | ||
|
|
020af0c0e4 | ||
|
|
5abda22831 | ||
|
|
89dac47aa6 | ||
|
|
e18d973047 | ||
|
|
afeeaf0a9b | ||
|
|
3010717ece | ||
|
|
0c48dbe0d5 | ||
|
|
f7b215b7ac | ||
|
|
29520542db | ||
|
|
00a3976905 | ||
|
|
3bd8e430f9 | ||
|
|
940f806e5f | ||
|
|
77733169fc | ||
|
|
c34ced2f65 | ||
|
|
2051d811cf | ||
|
|
0335cf2441 | ||
|
|
d3ddc8a221 | ||
|
|
25e00d5210 | ||
|
|
2a556ab329 | ||
|
|
80a94011b8 | ||
|
|
d4f2704213 | ||
|
|
91bf3c67cd | ||
|
|
307a9effb0 | ||
|
|
56e8f7b0c6 | ||
|
|
de58aa1187 | ||
|
|
da0ef8531b | ||
|
|
08f2254a0e | ||
|
|
477524039f | ||
|
|
e5e5bf8c17 | ||
|
|
4999e231bc | ||
|
|
6b857a5683 | ||
|
|
f12ee186ee | ||
|
|
93ef95f58e | ||
|
|
49dd1dd1fa | ||
|
|
04d2f3f50d | ||
|
|
f43bdec0f4 | ||
|
|
b79bd8ef05 | ||
|
|
4dfc56954c | ||
|
|
8c08827d35 | ||
|
|
a56081ad5b | ||
|
|
996c6d35f3 | ||
|
|
89036db666 | ||
|
|
dd9fec58a4 | ||
|
|
4fa44e6c64 | ||
|
|
4ad2a1a723 | ||
|
|
5279be4dc6 | ||
|
|
348c1ff7da | ||
|
|
c20ae0de1e | ||
|
|
3452864503 | ||
|
|
d2f29b7d73 | ||
|
|
62cdda2ce2 | ||
|
|
e6123969d2 | ||
|
|
90ce52612d | ||
|
|
7e6d40f370 | ||
|
|
b6e4ff908f | ||
|
|
c16c1a5e62 | ||
|
|
3f5aceff99 | ||
|
|
b8e9925601 | ||
|
|
667a514ed2 | ||
|
|
25108f4c70 | ||
|
|
6f83607aa8 | ||
|
|
70aff98581 | ||
|
|
812fb974dd | ||
|
|
0a91f3d392 | ||
|
|
c7c19e47de | ||
|
|
e1845dddac | ||
|
|
2e10336a47 | ||
|
|
37d3c025e9 | ||
|
|
6eb52aa540 | ||
|
|
6809198c96 | ||
|
|
3b7aebd6da | ||
|
|
7dc5bc87d1 | ||
|
|
0c43d0f16a | ||
|
|
fb1ad4c65f | ||
|
|
04748b6e0e | ||
|
|
094fc039e9 | ||
|
|
e4f40d14b8 | ||
|
|
4085aa22dc | ||
|
|
a4add889a8 | ||
|
|
f9f6793d10 | ||
|
|
a62687b1a7 | ||
|
|
a48fd0828c | ||
|
|
6cfb86e578 | ||
|
|
a168126cfa | ||
|
|
88c833ec83 | ||
|
|
c887efee54 | ||
|
|
86abff224e | ||
|
|
9a10c8c25a | ||
|
|
4461b33491 | ||
|
|
5ae9016f09 | ||
|
|
67ab8f63e0 | ||
|
|
40e64acd37 | ||
|
|
78fea7c7f2 | ||
|
|
4c61666fc7 | ||
|
|
0c6a12a6f0 | ||
|
|
0da245f912 | ||
|
|
eba0c9c4f7 | ||
|
|
25618c009e | ||
|
|
068ac40a76 | ||
|
|
5868fd1b7c | ||
|
|
540129854e | ||
|
|
f747ab568a | ||
|
|
d3a25282de | ||
|
|
f3d687cbea | ||
|
|
6894cb5dd7 | ||
|
|
e0fd180edd | ||
|
|
fd68cbf56d | ||
|
|
9a1b1498bf | ||
|
|
2a9b6f8e8c | ||
|
|
ca97adb724 | ||
|
|
54d5d0ed80 | ||
|
|
ac5cbcf19e | ||
|
|
2674df026a | ||
|
|
c1c0eca7c4 | ||
|
|
e10ea4b686 | ||
|
|
fd81429f39 | ||
|
|
913f8bbad0 | ||
|
|
fc951e92ac |
@@ -11,3 +11,9 @@ OFN_REDIS_URL="redis://localhost:6379/1"
|
||||
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
|
||||
|
||||
SITE_URL="0.0.0.0:3000"
|
||||
|
||||
# Deactivate rack-timeout in development.
|
||||
# https://github.com/zombocom/rack-timeout#configuring
|
||||
RACK_TIMEOUT_SERVICE_TIMEOUT="0"
|
||||
RACK_TIMEOUT_WAIT_TIMEOUT="0"
|
||||
RACK_TIMEOUT_WAIT_OVERTIME="0"
|
||||
|
||||
1
.github/workflows/brakeman-analysis.yml
vendored
1
.github/workflows/brakeman-analysis.yml
vendored
@@ -41,6 +41,7 @@ jobs:
|
||||
- name: Scan
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
brakeman -f sarif -o output.sarif.json .
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
|
||||
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -68,7 +68,6 @@ jobs:
|
||||
bundle exec rake db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
env:
|
||||
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: 864ef557d85ea8e603e086c0387d5154
|
||||
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
|
||||
@@ -82,8 +81,8 @@ jobs:
|
||||
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
|
||||
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
knapsack_rspec_models:
|
||||
|
||||
6
.github/workflows/linters.yml
vendored
6
.github/workflows/linters.yml
vendored
@@ -9,7 +9,11 @@ jobs:
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- uses: ruby/setup-ruby@v1
|
||||
|
||||
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
|
||||
- name: rubocop
|
||||
uses: reviewdog/action-rubocop@v2
|
||||
with:
|
||||
@@ -32,6 +36,8 @@ jobs:
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
|
||||
- name: prettier
|
||||
uses: EPMatt/reviewdog-action-prettier@v1
|
||||
with:
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
#
|
||||
# These are the rules we agreed upon and we work towards.
|
||||
AllCops:
|
||||
NewCops: disable
|
||||
NewCops: enable
|
||||
SuggestExtensions: false
|
||||
TargetRailsVersion: 5.0
|
||||
Exclude:
|
||||
- 'bin/**/*'
|
||||
- 'db/**/*'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@ TL;DR:
|
||||
* Maintain a clean commit history
|
||||
* Use a style consistent with the rest of the codebase
|
||||
* Before submitting, [rebase your work][rebase] on the current master branch
|
||||
* After submitting, be sure to check the [CI test results](ci). Click on a ❌ result to view the logged results and investigate.
|
||||
|
||||
From here, your pull request will progress through the [Review, Test, Merge & Deploy process][process].
|
||||
|
||||
@@ -71,3 +72,4 @@ From here, your pull request will progress through the [Review, Test, Merge & De
|
||||
[ofn-transifex]: https://www.transifex.com/open-food-foundation/open-food-network/
|
||||
[i18n]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Internationalisation-%28i18n%29
|
||||
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2
|
||||
[ci]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Continuous-Integration
|
||||
|
||||
20
Dockerfile
20
Dockerfile
@@ -28,8 +28,9 @@ RUN apt-get update && apt-get install -y \
|
||||
gnupg
|
||||
|
||||
# Setup ENV variables
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:/usr/local/src/nodenv/shims:/usr/local/src/nodenv/bin:$PATH
|
||||
ENV RBENV_ROOT /usr/local/src/rbenv
|
||||
ENV NODENV_ROOT /usr/local/src/nodenv
|
||||
ENV CONFIGURE_OPTS --disable-install-doc
|
||||
ENV BUNDLE_PATH /bundles
|
||||
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so
|
||||
@@ -53,10 +54,19 @@ RUN sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-
|
||||
apt-get update && \
|
||||
apt-get install -yqq --no-install-recommends postgresql-client-10 libpq-dev
|
||||
|
||||
# Install NodeJs and yarn
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
|
||||
&& apt-get install --no-install-recommends -y nodejs \
|
||||
&& npm install -g yarn
|
||||
|
||||
# trim spaces and line return from .node-version file
|
||||
COPY .node-version .node-version.raw
|
||||
RUN cat .node-version.raw | tr -d '\r\t ' > .node-version
|
||||
|
||||
# Install Node and Yarn with Nodenv
|
||||
RUN git clone --depth 1 https://github.com/nodenv/nodenv.git ${NODENV_ROOT} && \
|
||||
git clone --depth 1 https://github.com/nodenv/node-build.git ${NODENV_ROOT}/plugins/node-build && \
|
||||
git clone --depth 1 https://github.com/pine/nodenv-yarn-install.git ${NODENV_ROOT}/plugins/nodenv-yarn-install && \
|
||||
git clone --depth 1 https://github.com/nodenv/nodenv-package-rehash.git ${NODENV_ROOT}/plugins/nodenv-package-rehash && \
|
||||
echo 'eval "$(nodenv init -)"' >> /etc/profile.d/nodenv.sh && \
|
||||
nodenv install $(cat .node-version) && \
|
||||
nodenv global $(cat .node-version)
|
||||
|
||||
# Install Chrome
|
||||
RUN wget --quiet -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
|
||||
|
||||
@@ -48,14 +48,14 @@ Fetch the latest version of `master` from `upstream` (ie. the main repo):
|
||||
First, you need to create the database user the app will use by manually typing the following in your terminal:
|
||||
|
||||
```sh
|
||||
$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
|
||||
sudo --login --user=postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
|
||||
```
|
||||
|
||||
This will create the "ofn" user as superuser and allowing it to create databases. If this command fails, check the [troubleshooting section](#creating-the-database) for an alternative.
|
||||
|
||||
Next, it is _strongly recommended_ to run the setup script.
|
||||
```sh
|
||||
$ script/setup
|
||||
./script/setup
|
||||
```
|
||||
If the script succeeds you're ready to start developing. If not, take a look at the output as it should be informative enough to help you troubleshoot.
|
||||
|
||||
@@ -114,13 +114,13 @@ Below are fixes to potential issues that can happen during the installation proc
|
||||
|
||||
#### Creating the database
|
||||
|
||||
If the ```$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"``` command doesn't work, you can run the following commands instead:
|
||||
If the `sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"` command doesn't work, you can run the following commands instead:
|
||||
```
|
||||
$ createuser --superuser --pwprompt ofn
|
||||
Enter password for new role: f00d
|
||||
Enter it again: f00d
|
||||
$ createdb open_food_network_dev --owner=ofn
|
||||
$ createdb open_food_network_test --owner=ofn
|
||||
createuser --superuser --pwprompt ofn
|
||||
# Enter password for new role: f00d
|
||||
# Enter it again: f00d
|
||||
createdb open_food_network_dev --owner=ofn
|
||||
createdb open_food_network_test --owner=ofn
|
||||
```
|
||||
If these commands succeed, you should be able to [continue the setup process](#get-it-running).
|
||||
|
||||
|
||||
6
Gemfile
6
Gemfile
@@ -92,6 +92,7 @@ gem 'gmaps4rails'
|
||||
gem 'mimemagic', '> 0.3.5'
|
||||
gem 'paper_trail', '~> 12.1'
|
||||
gem 'rack-rewrite'
|
||||
gem 'rack-timeout'
|
||||
gem 'roadie-rails'
|
||||
|
||||
gem 'hiredis'
|
||||
@@ -100,8 +101,8 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-scheduler'
|
||||
|
||||
gem "cable_ready", "5.0.0.pre9"
|
||||
gem "stimulus_reflex", "3.5.0.pre9"
|
||||
gem "cable_ready", "5.0.0.rc2"
|
||||
gem "stimulus_reflex", "3.5.0.rc2"
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
@@ -141,7 +142,6 @@ gem "private_address_check"
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'rack-timeout'
|
||||
gem 'sd_notify' # For better Systemd process management. Used by Puma.
|
||||
end
|
||||
|
||||
|
||||
90
Gemfile.lock
90
Gemfile.lock
@@ -137,7 +137,7 @@ GEM
|
||||
activerecord (>= 6.0, < 7.1)
|
||||
acts_as_list (1.0.4)
|
||||
activerecord (>= 4.2)
|
||||
addressable (2.8.1)
|
||||
addressable (2.8.2)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
aes_key_wrap (1.1.0)
|
||||
afm (0.2.2)
|
||||
@@ -157,16 +157,16 @@ GEM
|
||||
awesome_nested_set (3.5.0)
|
||||
activerecord (>= 4.0.0, < 7.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.711.0)
|
||||
aws-sdk-core (3.170.0)
|
||||
aws-partitions (1.750.0)
|
||||
aws-sdk-core (3.171.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.62.0)
|
||||
aws-sdk-kms (1.63.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.119.1)
|
||||
aws-sdk-s3 (1.121.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
@@ -184,16 +184,14 @@ GEM
|
||||
bullet (7.0.7)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.0.pre9)
|
||||
actioncable (>= 5.2)
|
||||
cable_ready (5.0.0.rc2)
|
||||
actionpack (>= 5.2)
|
||||
actionview (>= 5.2)
|
||||
activerecord (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
thread-local (>= 1.1.0)
|
||||
cancancan (1.15.0)
|
||||
capybara (3.38.0)
|
||||
capybara (3.39.0)
|
||||
addressable
|
||||
matrix
|
||||
mini_mime (>= 0.1.3)
|
||||
@@ -217,11 +215,11 @@ GEM
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
combine_pdf (1.0.22)
|
||||
combine_pdf (1.0.23)
|
||||
matrix
|
||||
ruby-rc4 (>= 0.1.5)
|
||||
concurrent-ruby (1.2.2)
|
||||
connection_pool (2.3.0)
|
||||
connection_pool (2.4.0)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
@@ -243,11 +241,11 @@ GEM
|
||||
libddwaf (~> 1.6.2.0.0)
|
||||
msgpack
|
||||
debase-ruby_core_source (3.2.0)
|
||||
debug (1.7.1)
|
||||
debug (1.7.2)
|
||||
irb (>= 1.5.0)
|
||||
reline (>= 0.3.1)
|
||||
debugger-linecache (1.2.0)
|
||||
devise (4.9.0)
|
||||
devise (4.9.2)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
@@ -255,8 +253,8 @@ GEM
|
||||
warden (~> 1.2.3)
|
||||
devise-encryptable (0.2.0)
|
||||
devise (>= 2.1.0)
|
||||
devise-i18n (1.10.3)
|
||||
devise (>= 4.8.0)
|
||||
devise-i18n (1.11.0)
|
||||
devise (>= 4.9.0)
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.5.0)
|
||||
@@ -351,7 +349,7 @@ GEM
|
||||
activerecord (>= 3.0)
|
||||
io-console (0.6.0)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.6.2)
|
||||
irb (1.6.3)
|
||||
reline (>= 0.3.0)
|
||||
jmespath (1.6.2)
|
||||
jquery-rails (4.4.0)
|
||||
@@ -375,7 +373,7 @@ GEM
|
||||
jsonapi-serializer (2.2.0)
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.7.0)
|
||||
knapsack_pro (3.8.0)
|
||||
knapsack_pro (3.9.0)
|
||||
rake
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
@@ -388,7 +386,7 @@ GEM
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.19.1)
|
||||
loofah (2.20.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.8.1)
|
||||
@@ -427,8 +425,8 @@ GEM
|
||||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.14.2)
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.14.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
oauth2 (1.4.11)
|
||||
@@ -467,7 +465,7 @@ GEM
|
||||
parallel (1.22.1)
|
||||
paranoia (2.6.1)
|
||||
activerecord (>= 5.1, < 7.1)
|
||||
parser (3.2.1.1)
|
||||
parser (3.2.2.0)
|
||||
ast (~> 2.4.1)
|
||||
paypal-sdk-core (0.3.4)
|
||||
multi_json (~> 1.0)
|
||||
@@ -487,7 +485,7 @@ GEM
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.1.1)
|
||||
puma (6.2.2)
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.6.2)
|
||||
@@ -505,7 +503,7 @@ GEM
|
||||
rack-proxy (0.7.6)
|
||||
rack
|
||||
rack-rewrite (1.5.1)
|
||||
rack-test (2.0.2)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rack-timeout (0.6.3)
|
||||
rails (7.0.4.3)
|
||||
@@ -558,10 +556,10 @@ GEM
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.6.0)
|
||||
redis (4.8.1)
|
||||
redis-client (0.14.0)
|
||||
redis-client (0.14.1)
|
||||
connection_pool
|
||||
regexp_parser (2.7.0)
|
||||
reline (0.3.2)
|
||||
regexp_parser (2.8.0)
|
||||
reline (0.3.3)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
rack (>= 1.4)
|
||||
@@ -614,19 +612,19 @@ GEM
|
||||
rswag-ui (2.8.0)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.48.1)
|
||||
rubocop (1.50.2)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.26.0, < 2.0)
|
||||
rubocop-ast (>= 1.28.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.27.0)
|
||||
rubocop-ast (1.28.0)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-rails (2.18.0)
|
||||
rubocop-rails (2.19.1)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
@@ -654,7 +652,7 @@ GEM
|
||||
semantic_range (3.0.0)
|
||||
shoulda-matchers (5.3.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (7.0.7)
|
||||
sidekiq (7.0.9)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
rack (>= 2.2.4)
|
||||
@@ -689,18 +687,18 @@ GEM
|
||||
state_machines-activerecord (0.8.0)
|
||||
activerecord (>= 5.1)
|
||||
state_machines-activemodel (>= 0.8.0)
|
||||
stimulus_reflex (3.5.0.pre9)
|
||||
actioncable (>= 5.2)
|
||||
actionpack (>= 5.2)
|
||||
actionview (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
cable_ready (>= 5.0.0.pre9)
|
||||
nokogiri
|
||||
rack
|
||||
railties (>= 5.2)
|
||||
redis
|
||||
stringex (2.8.5)
|
||||
stripe (8.3.0)
|
||||
stimulus_reflex (3.5.0.rc2)
|
||||
actioncable (>= 5.2, < 8)
|
||||
actionpack (>= 5.2, < 8)
|
||||
actionview (>= 5.2, < 8)
|
||||
activesupport (>= 5.2, < 8)
|
||||
cable_ready (>= 5.0.0.rc2)
|
||||
nokogiri (~> 1.0)
|
||||
rack (>= 2, < 4)
|
||||
railties (>= 5.2, < 8)
|
||||
redis (>= 4.0, < 6.0)
|
||||
stringex (2.8.6)
|
||||
stripe (8.5.0)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
@@ -718,7 +716,7 @@ GEM
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (2.4.2)
|
||||
uniform_notifier (1.16.0)
|
||||
valid_email2 (4.0.5)
|
||||
valid_email2 (4.0.6)
|
||||
activemodel (>= 3.2)
|
||||
mail (~> 2.5)
|
||||
validate_email (0.1.6)
|
||||
@@ -793,7 +791,7 @@ DEPENDENCIES
|
||||
bootsnap
|
||||
bugsnag
|
||||
bullet
|
||||
cable_ready (= 5.0.0.pre9)
|
||||
cable_ready (= 5.0.0.rc2)
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
catalog!
|
||||
@@ -891,7 +889,7 @@ DEPENDENCIES
|
||||
spring
|
||||
spring-commands-rspec
|
||||
state_machines-activerecord
|
||||
stimulus_reflex (= 3.5.0.pre9)
|
||||
stimulus_reflex (= 3.5.0.rc2)
|
||||
stringex (~> 2.8.5)
|
||||
stripe
|
||||
test-unit (~> 3.5)
|
||||
|
||||
4
Procfile
4
Procfile
@@ -1,5 +1,5 @@
|
||||
# Foreman Procfile. Start all dev server processes with: `foreman start`
|
||||
|
||||
rails: bundle exec rails s -p 3000
|
||||
rails: DEV_CACHING=true bundle exec rails s -p 3000
|
||||
webpack: ./bin/webpack-dev-server
|
||||
sidekiq: bundle exec sidekiq -q mailers -q default
|
||||
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
|
||||
|
||||
@@ -12,5 +12,6 @@ angular.module("ofn.admin", [
|
||||
"admin.orders"
|
||||
]).config ($httpProvider, $locationProvider, $qProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
# for the next line, you should also probably check file: app/assets/javascripts/admin/utils/utils.js.coffee
|
||||
$locationProvider.hashPrefix('')
|
||||
$qProvider.errorOnUnhandledRejections(false)
|
||||
|
||||
@@ -11,5 +11,6 @@ angular.module("admin.indexUtils").factory 'SortOptions', ->
|
||||
sortingExpr
|
||||
|
||||
toggle: (predicate) ->
|
||||
@reverse = (@predicate == predicate) && !@reverse
|
||||
# predicate is a string or an array of strings
|
||||
@reverse = (JSON.stringify(@predicate) == JSON.stringify(predicate)) && !@reverse
|
||||
@predicate = predicate
|
||||
|
||||
@@ -17,6 +17,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
]
|
||||
$scope.page = 1
|
||||
$scope.per_page = $scope.per_page_options[0].id
|
||||
$scope.filterByVariantId = null
|
||||
searchThrough = ["order_distributor_name",
|
||||
"order_bill_address_phone",
|
||||
"order_bill_address_firstname",
|
||||
@@ -68,6 +69,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
|
||||
RequestMonitor.load LineItems.index(
|
||||
"q[#{searchThrough}]": $scope.query,
|
||||
"q[variant_id_eq]": $scope.filterByVariantId if $scope.filterByVariantId,
|
||||
"q[order_state_not_eq]": "canceled",
|
||||
"q[order_shipment_state_not_eq]": "shipped",
|
||||
"q[order_completed_at_not_null]": "true",
|
||||
@@ -176,6 +178,17 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.setSelectedUnitsVariant = (unitsProduct,unitsVariant) ->
|
||||
$scope.selectedUnitsProduct = unitsProduct
|
||||
$scope.selectedUnitsVariant = unitsVariant
|
||||
$scope.filterByVariantId = unitsVariant.id
|
||||
$scope.page = 1
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.resetSelectedUnitsVariant = ->
|
||||
$scope.selectedUnitsProduct = { }
|
||||
$scope.selectedUnitsVariant = { }
|
||||
$scope.filterByVariantId = null
|
||||
$scope.sharedResource = false
|
||||
$scope.page = 1
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.getLineItemScale = (lineItem) ->
|
||||
if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume")
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
angular.module("admin.lineItems").filter "variantFilter", ->
|
||||
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
|
||||
filtered = []
|
||||
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
|
||||
(lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) )
|
||||
filtered
|
||||
@@ -18,7 +18,6 @@ angular.module('admin.orderCycles')
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
NavigationCheck.clear()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.update(destination, $scope.order_cycle_form)
|
||||
|
||||
|
||||
@@ -33,9 +33,11 @@ angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, C
|
||||
StatusMessage.display 'notice', "You have unsaved changes" if newVal
|
||||
|
||||
$scope.showMore = (days) ->
|
||||
orderCycles = OrderCycles.index(ams_prefix: "index",
|
||||
"q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit - days)}",
|
||||
"q[orders_close_at_lteq]": "#{daysFromToday($scope.ordersCloseAtLimit)}"
|
||||
)
|
||||
$scope.ordersCloseAtLimit -= days
|
||||
existingIDs = Object.keys(OrderCycles.byID)
|
||||
orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}", "q[id_not_in][]": existingIDs)
|
||||
orderCycles.$promise.then ->
|
||||
$scope.orderCycles.push(orderCycle) for orderCycle in orderCycles
|
||||
compileData()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider, $locationProvider) ->
|
||||
# for the next line, you should also probably check file: app/assets/javascripts/admin/admin_ofn.js.coffee
|
||||
$locationProvider.hashPrefix('')
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -2,6 +2,7 @@ angular.module('Darkswarm').factory "EnterpriseModal", ($modal, $rootScope, $htt
|
||||
# Build a modal popup for an enterprise.
|
||||
new class EnterpriseModal
|
||||
open: (enterprise)->
|
||||
return if enterprise.visible == 'hidden'
|
||||
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
|
||||
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ConfirmModalComponent < ModalComponent
|
||||
def initialize(id:, confirm_actions: nil, controllers: nil, message: nil)
|
||||
def initialize(id:, confirm_actions: nil, controllers: nil, message: nil, confirm_reflexes: nil)
|
||||
super(id: id, close_button: true)
|
||||
@confirm_actions = confirm_actions
|
||||
@confirm_reflexes = confirm_reflexes
|
||||
@controllers = controllers
|
||||
@message = message
|
||||
end
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
|
||||
.modal-actions
|
||||
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.cancel'), "data-action": "click->modal#close" }
|
||||
%input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions }
|
||||
%input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions, "data-reflex": @confirm_reflexes }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require 'open_food_network/address_finder'
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module Admin
|
||||
class CustomersController < Admin::ResourceController
|
||||
before_action :load_managed_shops, only: :index, if: :html_request?
|
||||
@@ -67,7 +68,7 @@ module Admin
|
||||
|
||||
def collection
|
||||
if json_request? && params[:enterprise_id].present?
|
||||
CustomersWithBalance.new(Customer.of(managed_enterprise_id)).query.
|
||||
CustomersWithBalance.new(customers).query.
|
||||
includes(
|
||||
:enterprise,
|
||||
{ bill_address: [:state, :country] },
|
||||
@@ -79,6 +80,15 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def customers
|
||||
return @customers if @customers.present?
|
||||
|
||||
@customers = Customer.managed_by(spree_current_user)
|
||||
return @customers if params[:enterprise_id].blank?
|
||||
|
||||
@customers = @customers.where(enterprise_id: params[:enterprise_id])
|
||||
end
|
||||
|
||||
def managed_enterprise_id
|
||||
@managed_enterprise_id ||= Enterprise.managed_by(spree_current_user).
|
||||
select('enterprises.id').find_by(id: params[:enterprise_id])
|
||||
@@ -120,3 +130,4 @@ module Admin
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
|
||||
@@ -50,7 +50,7 @@ module Admin
|
||||
if params[:stimulus]
|
||||
@enterprise.is_primary_producer = params[:is_primary_producer]
|
||||
@enterprise.sells = params[:enterprise_sells]
|
||||
render operations: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
|
||||
render cable_ready: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
|
||||
.morph("#permalink", partial("admin/enterprises/form/permalink"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
module Admin
|
||||
class ReportsController < Spree::Admin::BaseController
|
||||
include ActiveStorage::SetCurrent
|
||||
include ReportsActions
|
||||
helper ReportsHelper
|
||||
|
||||
@@ -21,11 +22,13 @@ module Admin
|
||||
def show
|
||||
@report = report_class.new(spree_current_user, params, render: render_data?)
|
||||
|
||||
if report_format.present?
|
||||
if params[:report_format].present?
|
||||
export_report
|
||||
else
|
||||
show_report
|
||||
end
|
||||
rescue Timeout::Error
|
||||
render_timeout_error
|
||||
end
|
||||
|
||||
private
|
||||
@@ -36,6 +39,7 @@ module Admin
|
||||
|
||||
def show_report
|
||||
assign_view_data
|
||||
@table = render_report_as(:html) if render_data?
|
||||
render "show"
|
||||
end
|
||||
|
||||
@@ -45,7 +49,6 @@ module Admin
|
||||
@report_subtype = report_subtype
|
||||
@report_title = report_title
|
||||
@rendering_options = rendering_options
|
||||
@table = render_report_as(:html) if render_data?
|
||||
@data = Reporting::FrontendData.new(spree_current_user)
|
||||
end
|
||||
|
||||
@@ -55,17 +58,43 @@ module Admin
|
||||
|
||||
def render_report_as(format)
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user)
|
||||
job = ReportJob.new
|
||||
JobProcessor.perform_forked(
|
||||
job,
|
||||
report_class, spree_current_user, params, format
|
||||
@blob = ReportBlob.create_for_upload_later!(report_filename)
|
||||
ReportJob.perform_later(
|
||||
report_class, spree_current_user, params, format, @blob
|
||||
)
|
||||
Timeout.timeout(max_wait_time) do
|
||||
sleep 1 until @blob.content_stored?
|
||||
end
|
||||
|
||||
# This result has been rendered by Rails in safe mode already.
|
||||
job.result.html_safe # rubocop:disable Rails/OutputSafety
|
||||
@blob.result.html_safe # rubocop:disable Rails/OutputSafety
|
||||
else
|
||||
@report.render_as(format)
|
||||
end
|
||||
end
|
||||
|
||||
def render_timeout_error
|
||||
assign_view_data
|
||||
if @blob
|
||||
@error = ".report_taking_longer_html"
|
||||
@error_url = @blob.url
|
||||
else
|
||||
@error = ".report_taking_longer"
|
||||
@error_url = ""
|
||||
end
|
||||
render "show"
|
||||
end
|
||||
|
||||
def max_wait_time
|
||||
# This value is used by rack-timeout and nginx, usually 30 seconds in
|
||||
# staging and production:
|
||||
server_timeout = ENV.fetch("RACK_TIMEOUT_SERVICE_TIMEOUT", "15").to_f
|
||||
|
||||
# Zero disables the timeout:
|
||||
return 0 if server_timeout.zero?
|
||||
|
||||
# We want to time out earlier than nginx:
|
||||
server_timeout - 2.seconds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,7 +16,8 @@ module Admin
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if view_context.subscriptions_setup_complete?(@shops)
|
||||
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user).includes([:distributors, :cached_incoming_exchanges])
|
||||
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user)
|
||||
.includes([:distributors, :cached_incoming_exchanges])
|
||||
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).includes(:taggings)
|
||||
@payment_method_tags = payment_method_tags_by_id
|
||||
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user)
|
||||
@@ -25,7 +26,10 @@ module Admin
|
||||
render :setup_explanation
|
||||
end
|
||||
end
|
||||
format.json { render_as_json @collection, ams_prefix: params[:ams_prefix], payment_method_tags: payment_method_tags_by_id }
|
||||
format.json {
|
||||
render_as_json @collection, ams_prefix: params[:ams_prefix],
|
||||
payment_method_tags: payment_method_tags_by_id
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ module Admin
|
||||
|
||||
options = [{ id: '0', name: 'All' }]
|
||||
import_dates.collect(&:import_date).map { |i|
|
||||
options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long))
|
||||
options.push(id: i.to_date, name: i.to_date.to_fs(:long))
|
||||
}
|
||||
|
||||
options
|
||||
|
||||
36
app/controllers/admin/vouchers_controller.rb
Normal file
36
app/controllers/admin/vouchers_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class VouchersController < ResourceController
|
||||
before_action :load_enterprise
|
||||
|
||||
def new
|
||||
@voucher = Voucher.new
|
||||
end
|
||||
|
||||
def create
|
||||
voucher_params = permitted_resource_params.merge(enterprise: @enterprise)
|
||||
@voucher = Voucher.create(voucher_params)
|
||||
|
||||
if @voucher.save
|
||||
redirect_to(
|
||||
"#{edit_admin_enterprise_path(@enterprise)}#vouchers_panel",
|
||||
flash: { success: flash_message_for(@voucher, :successfully_created) }
|
||||
)
|
||||
else
|
||||
flash[:error] = @voucher.errors.full_messages.to_sentence
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = Enterprise.find_by permalink: params[:enterprise_id]
|
||||
end
|
||||
|
||||
def permitted_resource_params
|
||||
params.require(:voucher).permit(:code)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -80,9 +80,7 @@ module Api
|
||||
end
|
||||
|
||||
def visible_customers
|
||||
current_api_user.customers.or(
|
||||
Customer.where(enterprise_id: editable_enterprises)
|
||||
)
|
||||
Customer.managed_by(current_api_user)
|
||||
end
|
||||
|
||||
def customer_params
|
||||
@@ -106,10 +104,6 @@ module Api
|
||||
attributes
|
||||
end
|
||||
|
||||
def editable_enterprises
|
||||
OpenFoodNetwork::Permissions.new(current_api_user).editable_enterprises.select(:id)
|
||||
end
|
||||
|
||||
def include_options
|
||||
fields = [params.fetch(:include, [])].flatten
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ require 'open_food_network/address_finder'
|
||||
class CheckoutController < ::BaseController
|
||||
include OrderStockCheck
|
||||
include OrderCompletion
|
||||
include WhiteLabel
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
@@ -27,6 +28,8 @@ class CheckoutController < ::BaseController
|
||||
before_action :associate_user
|
||||
before_action :check_authorization
|
||||
|
||||
before_action :hide_ofn_navigation, only: :edit
|
||||
|
||||
helper 'spree/orders'
|
||||
|
||||
def edit; end
|
||||
|
||||
@@ -31,7 +31,7 @@ module OrderStockCheck
|
||||
flash[:info] = I18n.t('order_cycle_closed')
|
||||
respond_to do |format|
|
||||
format.cable_ready {
|
||||
render status: :see_other, operations: cable_car.redirect_to(url: main_app.shop_path)
|
||||
render status: :see_other, cable_ready: cable_car.redirect_to(url: main_app.shop_path)
|
||||
}
|
||||
format.json { render json: { path: main_app.shop_path }, status: :see_other }
|
||||
format.html { redirect_to main_app.shop_path, status: :see_other }
|
||||
|
||||
@@ -56,7 +56,7 @@ module ReportsActions
|
||||
end
|
||||
|
||||
def report_format
|
||||
params[:report_format]
|
||||
params[:report_format].presence || "html"
|
||||
end
|
||||
|
||||
def report_filename
|
||||
|
||||
21
app/controllers/concerns/white_label.rb
Normal file
21
app/controllers/concerns/white_label.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WhiteLabel
|
||||
extend ActiveSupport::Concern
|
||||
include EnterprisesHelper
|
||||
|
||||
def hide_ofn_navigation(distributor = current_distributor)
|
||||
return false unless OpenFoodNetwork::FeatureToggle.enabled?(:white_label)
|
||||
|
||||
# if the distributor has the hide_ofn_navigation preference set to true
|
||||
# then we should hide the OFN navigation
|
||||
@hide_ofn_navigation = distributor.hide_ofn_navigation
|
||||
|
||||
# if the distributor has the hide_ofn_navigation preference
|
||||
# set to false, there is no need to check the white_label_logo preference
|
||||
return unless @hide_ofn_navigation
|
||||
|
||||
@white_label_logo = distributor.white_label_logo
|
||||
@white_label_distributor = distributor
|
||||
end
|
||||
end
|
||||
@@ -7,6 +7,7 @@ class EnterprisesController < BaseController
|
||||
helper Spree::ProductsHelper
|
||||
include OrderCyclesHelper
|
||||
include SerializerHelper
|
||||
include WhiteLabel
|
||||
|
||||
protect_from_forgery except: :check_permalink
|
||||
|
||||
@@ -14,6 +15,7 @@ class EnterprisesController < BaseController
|
||||
prepend_before_action :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop
|
||||
|
||||
before_action :clean_permalink, only: :check_permalink
|
||||
before_action :hide_ofn_navigation, only: :shop
|
||||
|
||||
respond_to :js, only: :permalink_checker
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class SplitCheckoutController < ::BaseController
|
||||
include CheckoutCallbacks
|
||||
include OrderCompletion
|
||||
include CablecarResponses
|
||||
include WhiteLabel
|
||||
|
||||
helper 'terms_and_conditions'
|
||||
helper 'checkout'
|
||||
@@ -18,6 +19,7 @@ class SplitCheckoutController < ::BaseController
|
||||
helper OrderHelper
|
||||
|
||||
before_action :set_checkout_redirect
|
||||
before_action :hide_ofn_navigation, only: [:edit, :update]
|
||||
|
||||
def edit
|
||||
redirect_to_step_based_on_order unless params[:step]
|
||||
@@ -41,7 +43,7 @@ class SplitCheckoutController < ::BaseController
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = I18n.t(:spree_gateway_error_flash_for_checkout, error: e.message)
|
||||
@order.update_column(:state, "payment")
|
||||
render operations: cable_car.redirect_to(url: checkout_step_path(:payment))
|
||||
render cable_ready: cable_car.redirect_to(url: checkout_step_path(:payment))
|
||||
end
|
||||
|
||||
private
|
||||
@@ -52,7 +54,7 @@ class SplitCheckoutController < ::BaseController
|
||||
messages: order_error_messages
|
||||
)
|
||||
|
||||
render status: :unprocessable_entity, operations: cable_car.
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.
|
||||
replace("#checkout", partial("split_checkout/checkout")).
|
||||
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
|
||||
end
|
||||
@@ -130,7 +132,7 @@ class SplitCheckoutController < ::BaseController
|
||||
return unless selected_payment_method&.external_gateway?
|
||||
return unless (redirect_url = selected_payment_method.external_payment_url(order: @order))
|
||||
|
||||
render operations: cable_car.redirect_to(url: redirect_url)
|
||||
render cable_ready: cable_car.redirect_to(url: redirect_url)
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ module Spree
|
||||
def product_import_dates
|
||||
options = [{ id: '0', name: '' }]
|
||||
product_import_dates_query.collect(&:import_date).
|
||||
map { |i| options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long)) }
|
||||
map { |i| options.push(id: i.to_date, name: i.to_date.to_fs(:long)) }
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module Spree
|
||||
|
||||
def collection
|
||||
params[:q] ||= {}
|
||||
params[:q][:s] ||= "ascend_by_name"
|
||||
params[:q][:s] ||= "name asc"
|
||||
@search = super.ransack(params[:q])
|
||||
@pagy, @zones = pagy(@search.result, items: Spree::Config[:orders_per_page])
|
||||
@zones
|
||||
|
||||
@@ -5,6 +5,7 @@ module Spree
|
||||
include OrderCyclesHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
include CablecarResponses
|
||||
include WhiteLabel
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
@@ -14,7 +15,8 @@ module Spree
|
||||
respond_to :html, :json
|
||||
|
||||
before_action :check_authorization
|
||||
before_action :set_current_order, only: :update
|
||||
before_action :set_order_from_params, only: :show
|
||||
before_action :set_current_order, only: [:edit, :update]
|
||||
before_action :filter_order_params, only: :update
|
||||
|
||||
prepend_before_action :require_order_authentication, only: :show
|
||||
@@ -23,10 +25,12 @@ module Spree
|
||||
before_action :check_hub_ready_for_checkout, only: :edit
|
||||
before_action :check_at_least_one_line_item, only: :update
|
||||
|
||||
def show
|
||||
@order = Spree::Order.find_by!(number: params[:id])
|
||||
before_action only: [:show, :edit] do
|
||||
hide_ofn_navigation(@order.distributor)
|
||||
end
|
||||
|
||||
def show; end
|
||||
|
||||
def empty
|
||||
if @order = current_order
|
||||
@order.empty!
|
||||
@@ -37,7 +41,6 @@ module Spree
|
||||
|
||||
# Patching to redirect to shop if order is empty
|
||||
def edit
|
||||
@order = current_order(true)
|
||||
@insufficient_stock_lines = @order.insufficient_stock_lines
|
||||
@unavailable_order_variants = OrderCycleDistributedVariants.
|
||||
new(current_order_cycle, current_distributor).unavailable_order_variants(@order)
|
||||
@@ -101,11 +104,15 @@ module Spree
|
||||
flash[:error] = I18n.t(:orders_could_not_cancel)
|
||||
end
|
||||
render status: :found,
|
||||
operations: cable_car.redirect_to(url: request.referer || main_app.order_path(@order))
|
||||
cable_ready: cable_car.redirect_to(url: request.referer || main_app.order_path(@order))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_order_from_params
|
||||
@order = Spree::Order.find_by!(number: params[:id])
|
||||
end
|
||||
|
||||
def set_current_order
|
||||
@order = current_order(true)
|
||||
end
|
||||
|
||||
@@ -24,11 +24,11 @@ module Spree
|
||||
if spree_user_signed_in?
|
||||
flash[:success] = t('devise.success.logged_in_succesfully')
|
||||
|
||||
render operations: cable_car.redirect_to(
|
||||
render cable_ready: cable_car.redirect_to(
|
||||
url: return_url_or_default(after_sign_in_path_for(spree_current_user))
|
||||
)
|
||||
else
|
||||
render status: :unauthorized, operations: cable_car.inner_html(
|
||||
render status: :unauthorized, cable_ready: cable_car.inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t('devise.failure.invalid') })
|
||||
)
|
||||
@@ -60,7 +60,7 @@ module Spree
|
||||
end
|
||||
|
||||
def render_unconfirmed_response
|
||||
render status: :unprocessable_entity, operations: cable_car.inner_html(
|
||||
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" })
|
||||
|
||||
@@ -30,10 +30,11 @@ module Spree
|
||||
registered = Spree::User.find_by(email: params[:email]).present?
|
||||
|
||||
if registered
|
||||
render status: :ok, operations: cable_car.
|
||||
render status: :ok, cable_ready: cable_car.
|
||||
inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t('devise.failure.already_registered') })
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t('devise.failure.already_registered') })
|
||||
).
|
||||
dispatch_event(name: "login:modal:open")
|
||||
else
|
||||
@@ -45,12 +46,14 @@ module Spree
|
||||
@user = Spree::User.new(user_params)
|
||||
|
||||
if @user.save
|
||||
render operations: cable_car.inner_html(
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#signup-feedback",
|
||||
partial("layouts/alert", locals: { type: "success", message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed') })
|
||||
partial("layouts/alert",
|
||||
locals: { type: "success",
|
||||
message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed') })
|
||||
)
|
||||
else
|
||||
render status: :unprocessable_entity, operations: cable_car.morph(
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.morph(
|
||||
"#signup-tab",
|
||||
partial("layouts/signup_tab", locals: { signup_form_user: @user })
|
||||
)
|
||||
|
||||
@@ -22,9 +22,10 @@ class UserConfirmationsController < DeviseController
|
||||
set_flash_message(:error, :confirmation_not_sent)
|
||||
end
|
||||
else
|
||||
render operations: cable_car.inner_html(
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"##{params[:tab] || 'forgot'}-feedback",
|
||||
partial("layouts/alert", locals: { type: "success", message: t("devise.confirmations.send_instructions") })
|
||||
partial("layouts/alert",
|
||||
locals: { type: "success", message: t("devise.confirmations.send_instructions") })
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -11,12 +11,12 @@ class UserPasswordsController < Spree::UserPasswordsController
|
||||
self.resource = resource_class.send_reset_password_instructions(raw_params[resource_name])
|
||||
|
||||
if resource.errors.empty?
|
||||
render operations: cable_car.inner_html(
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert", locals: { type: "success", message: t(:password_reset_sent) })
|
||||
)
|
||||
else
|
||||
render status: :not_found, operations: cable_car.inner_html(
|
||||
render status: :not_found, cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t(:email_not_found) })
|
||||
)
|
||||
@@ -26,7 +26,7 @@ class UserPasswordsController < Spree::UserPasswordsController
|
||||
private
|
||||
|
||||
def render_unconfirmed_response
|
||||
render status: :unprocessable_entity, operations: cable_car.inner_html(
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t(:email_unconfirmed),
|
||||
|
||||
@@ -22,6 +22,14 @@ module Admin
|
||||
show_enterprise_fees = can?(:manage_enterprise_fees,
|
||||
enterprise) && (is_shop || enterprise.is_primary_producer)
|
||||
|
||||
build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods,
|
||||
show_payment_methods, show_enterprise_fees)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods,
|
||||
show_payment_methods, show_enterprise_fees)
|
||||
[
|
||||
{ name: 'primary_details', icon_class: "icon-home", show: true, selected: 'selected' },
|
||||
{ name: 'address', icon_class: "icon-map-marker", show: true },
|
||||
@@ -34,13 +42,20 @@ module Admin
|
||||
{ name: 'shipping_methods', icon_class: "icon-truck", show: show_shipping_methods },
|
||||
{ name: 'payment_methods', icon_class: "icon-money", show: show_payment_methods },
|
||||
{ name: 'enterprise_fees', icon_class: "icon-tasks", show: show_enterprise_fees },
|
||||
{ name: 'vouchers', icon_class: "icon-ticket", show: true },
|
||||
{ name: 'enterprise_permissions', icon_class: "icon-plug", show: true,
|
||||
href: admin_enterprise_relationships_path },
|
||||
{ name: 'inventory_settings', icon_class: "icon-list-ol", show: is_shop },
|
||||
{ name: 'tag_rules', icon_class: "icon-random", show: is_shop },
|
||||
{ name: 'shop_preferences', icon_class: "icon-shopping-cart", show: is_shop },
|
||||
{ name: 'users', icon_class: "icon-user", show: true }
|
||||
]
|
||||
{ name: 'users', icon_class: "icon-user", show: true },
|
||||
] + [add_white_label_if_feature_activated].compact
|
||||
end
|
||||
|
||||
def add_white_label_if_feature_activated
|
||||
return nil unless OpenFoodNetwork::FeatureToggle.enabled?(:white_label)
|
||||
|
||||
{ name: 'white_label', icon_class: "icon-leaf", show: true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,8 @@ class AngularFormBuilder < ActionView::Helpers::FormBuilder
|
||||
@template.ng_options_for_select(choices, angular_field), options
|
||||
end
|
||||
|
||||
def ng_collection_select(method, collection, value_method, text_method, angular_field, options = {})
|
||||
def ng_collection_select(method, collection, value_method,
|
||||
text_method, angular_field, options = {})
|
||||
options.reverse_merge!('id' => angular_id(method), 'ng-model' => angular_model(method).to_s)
|
||||
|
||||
@template.select_tag angular_name(method),
|
||||
|
||||
@@ -139,8 +139,8 @@ module CheckoutHelper
|
||||
def stripe_card_options(cards)
|
||||
cards.map do |cc|
|
||||
[
|
||||
"#{cc.brand} #{cc.last_digits} #{I18n.t(:card_expiry_abbreviation)}:#{cc.month.to_s.rjust(2, '0')}/#{cc.year}",
|
||||
cc.id
|
||||
"#{cc.brand} #{cc.last_digits} #{I18n.t(:card_expiry_abbreviation)}:"\
|
||||
"#{cc.month.to_s.rjust(2, '0')}/#{cc.year}", cc.id
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
module ReportsHelper
|
||||
def report_order_cycle_options(order_cycles)
|
||||
order_cycles.map do |oc|
|
||||
orders_open_at = oc.orders_open_at&.to_s(:short) || 'NA'
|
||||
orders_close_at = oc.orders_close_at&.to_s(:short) || 'NA'
|
||||
orders_open_at = oc.orders_open_at&.to_fs(:short) || 'NA'
|
||||
orders_close_at = oc.orders_close_at&.to_fs(:short) || 'NA'
|
||||
["#{oc.name} (#{orders_open_at} - #{orders_close_at})".html_safe, oc.id]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,34 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Renders a report and saves it to a temporary file.
|
||||
class ReportJob < ActiveJob::Base
|
||||
def perform(report_class, user, params, format)
|
||||
# Renders a report and stores it in a given blob.
|
||||
class ReportJob < ApplicationJob
|
||||
def perform(report_class, user, params, format, blob)
|
||||
report = report_class.new(user, params, render: true)
|
||||
result = report.render_as(format)
|
||||
write(result)
|
||||
end
|
||||
|
||||
def done?
|
||||
@done ||= File.file?(filename)
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= read_result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write(result)
|
||||
File.write(filename, result, mode: "wb")
|
||||
end
|
||||
|
||||
def read_result
|
||||
File.read(filename)
|
||||
ensure
|
||||
File.unlink(filename)
|
||||
end
|
||||
|
||||
def filename
|
||||
Rails.root.join("tmp/report-#{job_id}")
|
||||
blob.store(result)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ class JsonApiSchema
|
||||
end
|
||||
|
||||
def relationship_properties
|
||||
relationships.to_h { |name| [name, relationship_schema(name)] }
|
||||
relationships.index_with { |name| relationship_schema(name) }
|
||||
end
|
||||
|
||||
# Example
|
||||
|
||||
23
app/mailers/application_mailer.rb
Normal file
23
app/mailers/application_mailer.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
# Inline stylesheets
|
||||
include Roadie::Rails::Automatic
|
||||
|
||||
helper TermsAndConditionsHelper
|
||||
|
||||
default from: -> { Spree::Config[:mails_from] }
|
||||
layout 'mailer'
|
||||
|
||||
def money(amount)
|
||||
Spree::Money.new(amount).to_s
|
||||
end
|
||||
helper_method :money
|
||||
|
||||
protected
|
||||
|
||||
def roadie_options
|
||||
# This lets us specify assets using relative paths in email templates
|
||||
super.merge(url_options: { host: URI(main_app.root_url).host })
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'devise/mailers/helpers'
|
||||
class EnterpriseMailer < Spree::BaseMailer
|
||||
class EnterpriseMailer < ApplicationMailer
|
||||
include Devise::Mailers::Helpers
|
||||
include I18nHelper
|
||||
|
||||
@@ -12,7 +12,6 @@ class EnterpriseMailer < Spree::BaseMailer
|
||||
enterprise: @enterprise.name,
|
||||
sitename: Spree::Config[:site_name])
|
||||
mail(to: enterprise.contact.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
@@ -20,12 +19,10 @@ class EnterpriseMailer < Spree::BaseMailer
|
||||
def manager_invitation(enterprise, user)
|
||||
@enterprise = enterprise
|
||||
@instance = Spree::Config[:site_name]
|
||||
@instance_email = from_address
|
||||
|
||||
I18n.with_locale valid_locale(@enterprise.owner) do
|
||||
subject = t('enterprise_mailer.invite_manager.subject', enterprise: @enterprise.name)
|
||||
mail(to: user.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PaymentMailer < Spree::BaseMailer
|
||||
class PaymentMailer < ApplicationMailer
|
||||
include I18nHelper
|
||||
|
||||
def authorize_payment(payment)
|
||||
@@ -8,7 +8,7 @@ class PaymentMailer < Spree::BaseMailer
|
||||
subject = I18n.t('spree.payment_mailer.authorize_payment.subject',
|
||||
distributor: @payment.order.distributor.name)
|
||||
I18n.with_locale valid_locale(@payment.order.user) do
|
||||
mail(to: payment.order.email, from: from_address, subject: subject)
|
||||
mail(to: payment.order.email, subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,7 +19,6 @@ class PaymentMailer < Spree::BaseMailer
|
||||
order: @payment.order)
|
||||
I18n.with_locale valid_locale(shop_owner) do
|
||||
mail(to: shop_owner.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProducerMailer < Spree::BaseMailer
|
||||
class ProducerMailer < ApplicationMailer
|
||||
include I18nHelper
|
||||
|
||||
def order_cycle_report(producer, order_cycle)
|
||||
@@ -14,7 +14,6 @@ class ProducerMailer < Spree::BaseMailer
|
||||
|
||||
mail(
|
||||
to: @producer.contact.email,
|
||||
from: from_address,
|
||||
subject: subject,
|
||||
reply_to: @coordinator.contact.email,
|
||||
cc: @coordinator.contact.email
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class BaseMailer < ActionMailer::Base
|
||||
# Inline stylesheets
|
||||
include Roadie::Rails::Automatic
|
||||
|
||||
helper TermsAndConditionsHelper
|
||||
|
||||
layout 'mailer'
|
||||
|
||||
def from_address
|
||||
Spree::Config[:mails_from]
|
||||
end
|
||||
|
||||
def money(amount)
|
||||
Spree::Money.new(amount).to_s
|
||||
end
|
||||
helper_method :money
|
||||
|
||||
protected
|
||||
|
||||
def roadie_options
|
||||
# This lets us specify assets using relative paths in email templates
|
||||
super.merge(url_options: { host: URI(main_app.root_url).host })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class OrderMailer < BaseMailer
|
||||
class OrderMailer < ApplicationMailer
|
||||
helper 'checkout'
|
||||
helper SpreeCurrencyHelper
|
||||
helper Spree::PaymentMethodsHelper
|
||||
@@ -13,7 +13,6 @@ module Spree
|
||||
@order = find_order(order_or_order_id)
|
||||
I18n.with_locale valid_locale(@order.user) do
|
||||
mail(to: @order.email,
|
||||
from: from_address,
|
||||
subject: mail_subject(t('spree.order_mailer.cancel_email.subject'), resend))
|
||||
end
|
||||
end
|
||||
@@ -23,7 +22,6 @@ module Spree
|
||||
I18n.with_locale valid_locale(@order.distributor.owner) do
|
||||
subject = I18n.t('spree.order_mailer.cancel_email_for_shop.subject')
|
||||
mail(to: @order.distributor.contact.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
@@ -33,7 +31,6 @@ module Spree
|
||||
I18n.with_locale valid_locale(@order.user) do
|
||||
subject = mail_subject(t('spree.order_mailer.confirm_email.subject'), resend)
|
||||
mail(to: @order.email,
|
||||
from: from_address,
|
||||
subject: subject,
|
||||
reply_to: @order.distributor.contact.email)
|
||||
end
|
||||
@@ -44,7 +41,6 @@ module Spree
|
||||
I18n.with_locale valid_locale(@order.user) do
|
||||
subject = mail_subject(t('spree.order_mailer.confirm_email.subject'), resend)
|
||||
mail(to: @order.distributor.contact.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
@@ -56,7 +52,6 @@ module Spree
|
||||
attach_file("invoice-#{@order.number}.pdf", pdf)
|
||||
I18n.with_locale valid_locale(@order.user) do
|
||||
mail(to: @order.email,
|
||||
from: from_address,
|
||||
subject: mail_subject(t(:invoice), false),
|
||||
reply_to: @order.distributor.contact.email)
|
||||
end
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class ShipmentMailer < BaseMailer
|
||||
class ShipmentMailer < ApplicationMailer
|
||||
def shipped_email(shipment, delivery:)
|
||||
@shipment = shipment.respond_to?(:id) ? shipment : Spree::Shipment.find(shipment)
|
||||
@delivery = delivery
|
||||
subject = base_subject
|
||||
mail(to: @shipment.order.email, from: from_address, subject: subject)
|
||||
mail(to: @shipment.order.email, subject: subject)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
class TestMailer < BaseMailer
|
||||
class TestMailer < ApplicationMailer
|
||||
def test_email(user)
|
||||
recipient = user.respond_to?(:id) ? user : Spree::User.find(user)
|
||||
subject = "#{Spree::Config[:site_name]} #{t('spree.test_mailer.test_email.subject')}"
|
||||
mail(to: recipient.email, from: from_address, subject: subject)
|
||||
mail(to: recipient.email, subject: subject)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This mailer is configured to be the Devise mailer
|
||||
# Some methods here override Devise::Mailer methods
|
||||
module Spree
|
||||
class UserMailer < BaseMailer
|
||||
class UserMailer < ApplicationMailer
|
||||
include I18nHelper
|
||||
|
||||
helper MailerHelper
|
||||
@@ -16,7 +16,7 @@ module Spree
|
||||
"#{I18n.t('spree.user_mailer.reset_password_instructions.subject')}"
|
||||
|
||||
I18n.with_locale valid_locale(user) do
|
||||
mail(to: user.email, from: from_address, subject: subject)
|
||||
mail(to: user.email, subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ module Spree
|
||||
def signup_confirmation(user)
|
||||
@user = user
|
||||
I18n.with_locale valid_locale(@user) do
|
||||
mail(to: user.email, from: from_address,
|
||||
mail(to: user.email,
|
||||
subject: t(:welcome_to) + ' ' + Spree::Config[:site_name])
|
||||
end
|
||||
end
|
||||
@@ -39,7 +39,6 @@ module Spree
|
||||
I18n.with_locale valid_locale(@user) do
|
||||
subject = t('spree.user_mailer.confirmation_instructions.subject')
|
||||
mail(to: confirmation_email_address,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubscriptionMailer < Spree::BaseMailer
|
||||
class SubscriptionMailer < ApplicationMailer
|
||||
helper 'checkout'
|
||||
helper MailerHelper
|
||||
helper ShopMailHelper
|
||||
@@ -37,7 +37,6 @@ class SubscriptionMailer < Spree::BaseMailer
|
||||
@shop = Enterprise.find(summary.shop_id)
|
||||
@summary = summary
|
||||
mail(to: @shop.contact.email,
|
||||
from: from_address,
|
||||
subject: "#{Spree::Config[:site_name]} #{t('subscription_mailer.placement_summary_email.subject')}")
|
||||
end
|
||||
|
||||
@@ -45,7 +44,6 @@ class SubscriptionMailer < Spree::BaseMailer
|
||||
@shop = Enterprise.find(summary.shop_id)
|
||||
@summary = summary
|
||||
mail(to: @shop.contact.email,
|
||||
from: from_address,
|
||||
subject: "#{Spree::Config[:site_name]} #{t('subscription_mailer.confirmation_summary_email.subject')}")
|
||||
end
|
||||
|
||||
@@ -56,7 +54,6 @@ class SubscriptionMailer < Spree::BaseMailer
|
||||
confirm_email_subject = t('spree.order_mailer.confirm_email.subject')
|
||||
subject = "#{Spree::Config[:site_name]} #{confirm_email_subject} ##{order.number}"
|
||||
mail(to: order.email,
|
||||
from: from_address,
|
||||
subject: subject,
|
||||
reply_to: order.distributor.contact.email)
|
||||
end
|
||||
|
||||
@@ -4,4 +4,8 @@ module AddressDisplay
|
||||
def full_name_reverse
|
||||
[lastname, firstname].reject(&:blank?).join(" ")
|
||||
end
|
||||
|
||||
def full_name_for_sorting
|
||||
[last_name, first_name].reject(&:blank?).join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,6 +35,7 @@ class Customer < ApplicationRecord
|
||||
uniqueness: { scope: :enterprise_id, message: I18n.t('validation_msg_is_associated_with_an_exising_customer') }
|
||||
|
||||
scope :of, ->(enterprise) { where(enterprise_id: enterprise) }
|
||||
scope :managed_by, ->(user) { user&.persisted? ? where(user: user).or(of(Enterprise.managed_by(user))) : none }
|
||||
|
||||
before_create :associate_user
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ class Enterprise < ApplicationRecord
|
||||
medium: { resize_to_fill: [720, 156] },
|
||||
large: { resize_to_fill: [1200, 260] },
|
||||
}.freeze
|
||||
WHITE_LABEL_LOGO_SIZES = {
|
||||
default: { gravity: "Center", resize_to_fill: [217, 44] },
|
||||
mobile: { gravity: "Center", resize_to_fill: [75, 26] },
|
||||
}.freeze
|
||||
VALID_INSTAGRAM_REGEX = %r{\A[a-zA-Z0-9._]{1,30}([^/-]*)\z}
|
||||
|
||||
searchable_attributes :sells, :is_primary_producer, :name
|
||||
@@ -65,6 +69,7 @@ class Enterprise < ApplicationRecord
|
||||
has_many :inventory_items
|
||||
has_many :tag_rules
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers
|
||||
|
||||
delegate :latitude, :longitude, :city, :state_name, to: :address
|
||||
|
||||
@@ -83,9 +88,14 @@ class Enterprise < ApplicationRecord
|
||||
has_one_attached :logo
|
||||
has_one_attached :promo_image
|
||||
has_one_attached :terms_and_conditions
|
||||
has_one_attached :white_label_logo
|
||||
|
||||
validates :logo, content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :promo_image, content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :logo,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :promo_image,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :terms_and_conditions, content_type: {
|
||||
in: "application/pdf",
|
||||
message: I18n.t(:enterprise_terms_and_conditions_type_error),
|
||||
@@ -100,7 +110,11 @@ class Enterprise < ApplicationRecord
|
||||
validate :shopfront_taxons
|
||||
validate :shopfront_producers
|
||||
validate :enforce_ownership_limit, if: lambda { owner_id_changed? && !owner_id.nil? }
|
||||
validates :instagram, format: { with: VALID_INSTAGRAM_REGEX, message: Spree.t('errors.messages.invalid_instagram_url') }, allow_blank: true
|
||||
validates :instagram,
|
||||
format: {
|
||||
with: VALID_INSTAGRAM_REGEX,
|
||||
message: Spree.t('errors.messages.invalid_instagram_url')
|
||||
}, allow_blank: true
|
||||
|
||||
before_validation :initialize_permalink, if: lambda { permalink.nil? }
|
||||
before_validation :set_unused_address_fields
|
||||
@@ -262,7 +276,7 @@ class Enterprise < ApplicationRecord
|
||||
|
||||
def plus_parents_and_order_cycle_producers(order_cycles)
|
||||
oc_producer_ids = Exchange.in_order_cycle(order_cycles).incoming.pluck :sender_id
|
||||
Enterprise.not_hidden.is_primary_producer.parents_of_one_union_others(id, oc_producer_ids | [id])
|
||||
Enterprise.is_primary_producer.parents_of_one_union_others(id, oc_producer_ids | [id])
|
||||
end
|
||||
|
||||
def relatives_including_self
|
||||
@@ -293,6 +307,14 @@ class Enterprise < ApplicationRecord
|
||||
)
|
||||
end
|
||||
|
||||
def white_label_logo_url(name = :default)
|
||||
return unless white_label_logo.variable?
|
||||
|
||||
Rails.application.routes.url_helpers.url_for(
|
||||
white_label_logo.variant(WHITE_LABEL_LOGO_SIZES[name])
|
||||
)
|
||||
end
|
||||
|
||||
def website
|
||||
strip_url self[:website]
|
||||
end
|
||||
|
||||
@@ -28,8 +28,12 @@ class EnterpriseGroup < ApplicationRecord
|
||||
has_one_attached :logo
|
||||
has_one_attached :promo_image
|
||||
|
||||
validates :logo, content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :promo_image, content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :logo,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :promo_image,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
|
||||
scope :by_position, -> { order('position ASC') }
|
||||
scope :on_front_page, -> { where(on_front_page: true) }
|
||||
|
||||
@@ -13,6 +13,7 @@ class EnterpriseRelationship < ApplicationRecord
|
||||
|
||||
after_save :update_permissions_of_child_variant_overrides
|
||||
before_destroy :revoke_all_child_variant_overrides
|
||||
before_destroy :destroy_related_exchanges
|
||||
|
||||
scope :with_enterprises, -> {
|
||||
joins("
|
||||
@@ -102,6 +103,10 @@ class EnterpriseRelationship < ApplicationRecord
|
||||
child_variant_overrides.update_all(permission_revoked_at: Time.zone.now)
|
||||
end
|
||||
|
||||
def destroy_related_exchanges
|
||||
Exchange.where(sender: parent, receiver: child, incoming: true).destroy_all
|
||||
end
|
||||
|
||||
def child_variant_overrides
|
||||
VariantOverride.unscoped.for_hubs(child)
|
||||
.joins(variant: :product).where("spree_products.supplier_id IN (?)", parent)
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class EnterpriseRelationshipPermission < ApplicationRecord
|
||||
belongs_to :enterprise_relationship
|
||||
default_scope { order('name') }
|
||||
before_destroy :destroy_related_exchanges
|
||||
|
||||
def destroy_related_exchanges
|
||||
return if name != "add_to_order_cycle"
|
||||
|
||||
Exchange
|
||||
.where(sender: enterprise_relationship.parent,
|
||||
receiver: enterprise_relationship.child, incoming: true).destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,4 +3,9 @@
|
||||
class ExchangeVariant < ApplicationRecord
|
||||
belongs_to :exchange
|
||||
belongs_to :variant, class_name: 'Spree::Variant'
|
||||
after_destroy :destroy_related_outgoing_variants
|
||||
|
||||
def destroy_related_outgoing_variants
|
||||
VariantDeleter.new.destroy_related_outgoing_variants(variant_id, exchange.order_cycle)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,8 @@ module ProductImport
|
||||
:variants_created, :variants_updated, :enterprise_products,
|
||||
:total_enterprise_products, :products_reset_count
|
||||
|
||||
def initialize(importer, validator, import_settings, spreadsheet_data, editable_enterprises, import_time, updated_ids)
|
||||
def initialize(importer, validator, import_settings, spreadsheet_data,
|
||||
editable_enterprises, import_time, updated_ids)
|
||||
@importer = importer
|
||||
@validator = validator
|
||||
@settings = Settings.new(import_settings)
|
||||
|
||||
@@ -39,6 +39,8 @@ module ProductImport
|
||||
enterprise_validation(entry)
|
||||
unit_fields_validation(entry)
|
||||
variant_of_product_validation(entry)
|
||||
price_validation(entry)
|
||||
on_hand_on_demand_validation(entry)
|
||||
|
||||
next if entry.enterprise_id.blank?
|
||||
|
||||
@@ -170,6 +172,11 @@ module ProductImport
|
||||
error: I18n.t('admin.product_import.model.blank'))
|
||||
end
|
||||
|
||||
unless is_numeric(entry.units) && entry.units.to_f > 0
|
||||
mark_as_invalid(entry, attribute: 'units',
|
||||
error: I18n.t('admin.product_import.model.incorrect_value'))
|
||||
end
|
||||
|
||||
return if import_into_inventory?
|
||||
|
||||
# unit_type must be valid type
|
||||
@@ -189,6 +196,42 @@ module ProductImport
|
||||
error: I18n.t('admin.product_import.model.conditional_blank'))
|
||||
end
|
||||
|
||||
def is_numeric(value)
|
||||
return true unless Float(value, exception: false).nil?
|
||||
end
|
||||
|
||||
def price_validation(entry)
|
||||
return if is_numeric(entry.price)
|
||||
|
||||
error_string = if empty_or_placeholder_value(entry.price)
|
||||
'admin.product_import.model.blank'
|
||||
else
|
||||
'admin.product_import.model.incorrect_value'
|
||||
end
|
||||
mark_as_invalid(entry, attribute: 'price', error: I18n.t(error_string))
|
||||
end
|
||||
|
||||
def on_hand_on_demand_validation(entry)
|
||||
on_hand_present_numeric = !empty_or_placeholder_value(entry.on_hand) &&
|
||||
is_numeric(entry.on_hand)
|
||||
on_hand_value = entry.on_hand&.to_i
|
||||
on_demand_present_numeric = !empty_or_placeholder_value(entry.on_demand) &&
|
||||
is_numeric(entry.on_demand)
|
||||
on_demand_value = entry.on_demand&.to_i
|
||||
|
||||
return if (on_hand_present_numeric && on_hand_value >= 0) ||
|
||||
(on_demand_present_numeric && on_demand_value == 1)
|
||||
|
||||
mark_as_invalid(entry, attribute: 'on_hand',
|
||||
error: I18n.t('admin.product_import.model.incorrect_value'))
|
||||
mark_as_invalid(entry, attribute: 'on_demand',
|
||||
error: I18n.t('admin.product_import.model.incorrect_value'))
|
||||
end
|
||||
|
||||
def empty_or_placeholder_value(value)
|
||||
value.blank? || value.to_s.strip == "-"
|
||||
end
|
||||
|
||||
def variant_of_product_validation(entry)
|
||||
return if entry.producer.blank? || entry.name.blank?
|
||||
|
||||
@@ -293,8 +336,7 @@ module ProductImport
|
||||
entry.primary_taxon_id = @spreadsheet_data.categories_index[category_name]
|
||||
else
|
||||
mark_as_invalid(entry, attribute: "category",
|
||||
error: I18n.t(:error_not_found_in_database,
|
||||
name: category_name))
|
||||
error: I18n.t('admin.product_import.model.category_not_found'))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
36
app/models/report_blob.rb
Normal file
36
app/models/report_blob.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Stores a generated report.
|
||||
class ReportBlob < ActiveStorage::Blob
|
||||
def self.create_for_upload_later!(filename)
|
||||
# ActiveStorage discourages modifying a blob later but we need a blob
|
||||
# before we know anything about the report file. It enables us to use the
|
||||
# same blob in the controller to read the result.
|
||||
create_before_direct_upload!(
|
||||
filename: filename,
|
||||
byte_size: 0,
|
||||
checksum: "0",
|
||||
content_type: content_type(filename),
|
||||
).tap do |blob|
|
||||
ActiveStorage::PurgeJob.set(wait: 1.month).perform_later(blob)
|
||||
end
|
||||
end
|
||||
|
||||
def self.content_type(filename)
|
||||
MIME::Types.of(filename).first&.to_s || "application/octet-stream"
|
||||
end
|
||||
|
||||
def store(content)
|
||||
io = StringIO.new(content)
|
||||
upload(io, identify: false)
|
||||
save!
|
||||
end
|
||||
|
||||
def content_stored?
|
||||
@content_stored ||= reload.checksum != "0"
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= download
|
||||
end
|
||||
end
|
||||
@@ -179,6 +179,8 @@ module Spree
|
||||
can [:admin, :create], :manager_invitation
|
||||
|
||||
can [:admin, :index], :oidc_setting
|
||||
|
||||
can [:admin, :create], Voucher
|
||||
end
|
||||
|
||||
def add_product_management_abilities(user)
|
||||
|
||||
@@ -11,7 +11,10 @@ module Spree
|
||||
|
||||
has_one_attached :attachment
|
||||
|
||||
validates :attachment, attached: true, content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validates :attachment,
|
||||
attached: true,
|
||||
processable_image: true,
|
||||
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
|
||||
validate :no_attachment_errors
|
||||
|
||||
def variant(name)
|
||||
|
||||
@@ -311,7 +311,8 @@ module Spree
|
||||
# Creates new tax charges if there are any applicable rates. If prices already
|
||||
# include taxes then price adjustments are created instead.
|
||||
def create_tax_charge!
|
||||
return if state.in?(["cart", "address", "delivery"]) && OpenFoodNetwork::FeatureToggle.enabled?(:split_checkout)
|
||||
return if state.in?(["cart", "address", "delivery"]) &&
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:split_checkout)
|
||||
|
||||
clear_legacy_taxes!
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ module Spree
|
||||
presence: { if: ->(p) { %w(weight volume).include? p.variant_unit } }
|
||||
validates :variant_unit_name,
|
||||
presence: { if: ->(p) { p.variant_unit == 'items' } }
|
||||
validate :validate_image_for_master
|
||||
|
||||
attr_accessor :option_values_hash
|
||||
|
||||
@@ -474,5 +475,11 @@ module Spree
|
||||
requested = permalink.presence || permalink_was.presence || name.presence || 'product'
|
||||
self.permalink = create_unique_permalink(requested.parameterize)
|
||||
end
|
||||
|
||||
def validate_image_for_master
|
||||
return if master.images.all?(&:valid?)
|
||||
|
||||
errors.add(:base, I18n.t('spree.admin.products.image_not_processable'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ module Spree
|
||||
belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product'
|
||||
|
||||
delegate_belongs_to :product, :name, :description, :permalink, :available_on,
|
||||
:tax_category_id, :shipping_category_id, :meta_description,
|
||||
:tax_category_id, :shipping_category_id,
|
||||
:meta_keywords, :tax_category, :shipping_category
|
||||
|
||||
has_many :inventory_units, inverse_of: :variant
|
||||
|
||||
15
app/models/voucher.rb
Normal file
15
app/models/voucher.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
class Voucher < ApplicationRecord
|
||||
belongs_to :enterprise
|
||||
|
||||
validates :code, presence: true, uniqueness: { scope: :enterprise_id }
|
||||
|
||||
def value
|
||||
10
|
||||
end
|
||||
|
||||
def display_value
|
||||
Spree::Money.new(value)
|
||||
end
|
||||
end
|
||||
12
app/reflexes/concerns/enterprise_concern.rb
Normal file
12
app/reflexes/concerns/enterprise_concern.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module EnterpriseConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_reflex do
|
||||
@enterprise = Enterprise.find_by(permalink: params[:id])
|
||||
authorize! :update, @enterprise
|
||||
end
|
||||
end
|
||||
end
|
||||
24
app/reflexes/white_label_reflex.rb
Normal file
24
app/reflexes/white_label_reflex.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WhiteLabelReflex < ApplicationReflex
|
||||
include EnterpriseConcern
|
||||
delegate :view_context, to: :controller
|
||||
|
||||
def remove_logo
|
||||
@enterprise.update!(white_label_logo: nil)
|
||||
|
||||
f = ActionView::Helpers::FormBuilder.new(:enterprise, @enterprise, view_context, {})
|
||||
|
||||
html = with_locale {
|
||||
render(partial: "admin/enterprises/form/white_label",
|
||||
locals: { f: f, enterprise: @enterprise })
|
||||
}
|
||||
morph "#white_label_panel", html
|
||||
|
||||
flash[:success] = with_locale {
|
||||
I18n.t("admin.enterprises.form.white_label.remove_logo_success")
|
||||
}
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
|
||||
end
|
||||
end
|
||||
@@ -14,7 +14,7 @@ module Api
|
||||
:logo, :promo_image, :terms_and_conditions,
|
||||
:terms_and_conditions_file_name, :terms_and_conditions_updated_at,
|
||||
:preferred_invoice_order_by_supplier, :preferred_product_low_stock_display,
|
||||
:visible
|
||||
:visible, :hide_ofn_navigation, :white_label_logo
|
||||
|
||||
has_one :owner, serializer: Api::Admin::UserSerializer
|
||||
has_many :users, serializer: Api::Admin::UserSerializer
|
||||
@@ -29,6 +29,10 @@ module Api
|
||||
attachment_urls(object.promo_image, Enterprise::PROMO_IMAGE_SIZES)
|
||||
end
|
||||
|
||||
def white_label_logo
|
||||
attachment_urls(object.white_label_logo, Enterprise::WHITE_LABEL_LOGO_SIZES)
|
||||
end
|
||||
|
||||
def terms_and_conditions
|
||||
return unless object.terms_and_conditions.attached?
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
module Api
|
||||
module Admin
|
||||
class OrderSerializer < ActiveModel::Serializer
|
||||
attributes :id, :number, :user_id, :full_name, :email, :phone, :completed_at,
|
||||
:completed_at_utc_iso8601, :display_total,
|
||||
include AddressDisplay
|
||||
|
||||
attributes :id, :number, :user_id, :full_name, :full_name_for_sorting, :email, :phone,
|
||||
:completed_at, :completed_at_utc_iso8601, :display_total,
|
||||
:edit_path, :state, :payment_state, :shipment_state,
|
||||
:payments_path, :ready_to_ship, :ready_to_capture, :created_at,
|
||||
:distributor_name, :special_instructions, :display_outstanding_balance,
|
||||
@@ -17,6 +19,14 @@ module Api
|
||||
object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" )
|
||||
end
|
||||
|
||||
def first_name
|
||||
object.billing_address&.first_name || ""
|
||||
end
|
||||
|
||||
def last_name
|
||||
object.billing_address&.last_name || ""
|
||||
end
|
||||
|
||||
def distributor_name
|
||||
object.distributor&.name
|
||||
end
|
||||
|
||||
@@ -9,7 +9,8 @@ module Api
|
||||
:instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor,
|
||||
:phone, :whatsapp_phone, :whatsapp_url, :visible, :email_address, :hash, :logo,
|
||||
:promo_image, :path, :category, :active, :producers, :orders_close_at, :hubs,
|
||||
:taxons, :supplied_taxons, :pickup, :delivery, :preferred_product_low_stock_display
|
||||
:taxons, :supplied_taxons, :pickup, :delivery, :preferred_product_low_stock_display,
|
||||
:hide_ofn_navigation, :white_label_logo
|
||||
|
||||
has_one :address, serializer: Api::AddressSerializer
|
||||
has_many :supplied_properties, serializer: Api::PropertySerializer
|
||||
@@ -48,6 +49,10 @@ module Api
|
||||
enterprise.promo_image_url(:large)
|
||||
end
|
||||
|
||||
def white_label_logo
|
||||
enterprise.white_label_logo_url
|
||||
end
|
||||
|
||||
def path
|
||||
enterprise_shop_path(enterprise)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
module Api
|
||||
class EnterpriseThinSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :active, :path
|
||||
attributes :name, :id, :active, :path, :visible
|
||||
|
||||
has_one :address, serializer: Api::AddressSerializer
|
||||
|
||||
|
||||
@@ -10,6 +10,14 @@ class ContentScrubber < Rails::Html::PermitScrubber
|
||||
self.attributes = ALLOWED_ATTRIBUTES
|
||||
end
|
||||
|
||||
def scrub(node)
|
||||
if node.name == 'p' && (node.children.empty? || node.text.blank?)
|
||||
node.remove
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def skip_node?(node)
|
||||
node.text?
|
||||
end
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Forks into a separate process to contain memory usage and timeout errors.
|
||||
class JobProcessor
|
||||
def self.perform_forked(job, *args)
|
||||
# Reports should abort when puma threads are killed to avoid wasting
|
||||
# resources. Nobody would be collecting the result. We still need to
|
||||
# implement a way to email or download reports later.
|
||||
timeout = ENV.fetch("RACK_TIMEOUT_WAIT_TIMEOUT", "30").to_i
|
||||
|
||||
child = fork do
|
||||
Process.setproctitle("Job worker #{job.job_id}")
|
||||
Timeout.timeout(timeout) do
|
||||
job.perform(*args)
|
||||
end
|
||||
|
||||
# Exit is not a good idea within a Rails process but Rubocop doesn't know
|
||||
# that we are in a forked process.
|
||||
exit # rubocop:disable Rails/Exit
|
||||
end
|
||||
|
||||
# Wait for the forked child process to exit.
|
||||
Process.waitpid(child)
|
||||
ensure
|
||||
# If this Puma thread is interrupted then we need to detach the child
|
||||
# process to avoid it becoming a zombie.
|
||||
Process.detach(child)
|
||||
end
|
||||
end
|
||||
@@ -34,7 +34,8 @@ module PermittedAttributes
|
||||
:preferred_shopfront_producer_order, :preferred_shopfront_order_cycle_order,
|
||||
:show_customer_names_to_suppliers, :preferred_shopfront_product_sorting_method,
|
||||
:preferred_invoice_order_by_supplier,
|
||||
:preferred_product_low_stock_display
|
||||
:preferred_product_low_stock_display,
|
||||
:hide_ofn_navigation, :white_label_logo,
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ module PermittedAttributes
|
||||
:variant_unit, :variant_unit_scale, :unit_value, :unit_description, :variant_unit_name,
|
||||
:display_as, :sku, :available_on, :group_buy, :group_buy_unit_size,
|
||||
:taxon_ids, :primary_taxon_id, :tax_category_id, :shipping_category_id,
|
||||
:meta_keywords, :meta_description, :notes, :inherits_properties,
|
||||
:meta_keywords, :notes, :inherits_properties,
|
||||
{ product_properties_attributes: [:id, :property_name, :value],
|
||||
variants_attributes: [PermittedAttributes::Variant.attributes],
|
||||
images_attributes: [:attachment] }
|
||||
|
||||
@@ -11,6 +11,15 @@ class VariantDeleter
|
||||
variant.destroy
|
||||
end
|
||||
|
||||
def destroy_related_outgoing_variants(variant_id, order_cycle)
|
||||
internal_variants = ExchangeVariant.where(variant_id: variant_id).
|
||||
joins(:exchange).
|
||||
where(
|
||||
exchanges: { order_cycle: order_cycle, incoming: false }
|
||||
)
|
||||
internal_variants.destroy_all
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def only_variant_on_product?(variant)
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:name] }.legend")
|
||||
|
||||
- when 'vouchers'
|
||||
- if feature?(:vouchers, spree_current_user)
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
= render "admin/enterprises/form/#{ item[:form_name] || item[:name] }", f: f
|
||||
|
||||
- else
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
= bf.label :state_id, t(:state)
|
||||
%span.required *
|
||||
.four.columns
|
||||
= bf.select :country_id, options_for_select(available_countries.map { |c| [c.name, c.id] }, @enterprise.business_address.country_id), {}, { "data-controller": "tom-select", "data-dependent-select-target": "source", "data-action": "dependent-select#handleSelectChange", class: "primary" }
|
||||
= bf.select :country_id, options_for_select(available_countries.map { |c| [c.name, c.id] }, @enterprise.business_address.country_id), { include_blank: t(".select_country") }, { "data-controller": "tom-select", "data-dependent-select-target": "source", "data-action": "dependent-select#handleSelectChange", class: "primary" }
|
||||
.four.columns.omega
|
||||
- states = @enterprise.business_address.country.present? ? @enterprise.business_address.country&.states&.map { |s| [s.name, s.id] } : []
|
||||
= bf.select :state_id, states, {}, { "data-controller": "tom-select", "data-dependent-select-target": "select", class: "primary" }
|
||||
= bf.select :state_id, states, { include_blank: t(".select_state") }, { disabled: @enterprise.business_address.country_id.nil?, "data-controller": "tom-select", "data-dependent-select-target": "select", class: "primary" }
|
||||
|
||||
.row
|
||||
.three.columns.alpha
|
||||
|
||||
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
@@ -0,0 +1,33 @@
|
||||
.text-right
|
||||
%a.button{ href: "#{new_admin_enterprise_voucher_path(@enterprise)}"}
|
||||
= t('.add_new')
|
||||
%br
|
||||
|
||||
- if @enterprise.vouchers.present?
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.voucher_code')
|
||||
%th= t('.rate')
|
||||
/%th= t('.label')
|
||||
/%th= t('.purpose')
|
||||
/%th= t('.expiry')
|
||||
/%th= t('.use_limit')
|
||||
/%th= t('.customers')
|
||||
/%th= t('.net_value')
|
||||
%tbody
|
||||
- @enterprise.vouchers.each do |voucher|
|
||||
%tr
|
||||
%td= voucher.code
|
||||
%td= voucher.display_value
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
|
||||
- else
|
||||
%p.text-center
|
||||
= t('.no_voucher_yet')
|
||||
|
||||
23
app/views/admin/enterprises/form/_white_label.html.haml
Normal file
23
app/views/admin/enterprises/form/_white_label.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
- @object ||= enterprise
|
||||
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= f.label :hide_ofn_navigation, t('.hide_ofn_navigation')
|
||||
.three.columns
|
||||
= f.check_box :hide_ofn_navigation, { "data-controller": "checkbox-display", "data-checkbox-display-target-id-value": "white_label_logo" }
|
||||
|
||||
.row{id: "white_label_logo"}
|
||||
.three.columns.alpha
|
||||
= f.label :white_label_logo, t('.upload_logo')
|
||||
.three.columns
|
||||
= image_tag @object.white_label_logo_url if @object.white_label_logo.present?
|
||||
= f.file_field :white_label_logo, accept: "image/*"
|
||||
- if @object.white_label_logo.variable?
|
||||
%button.button.small{ type: "button", "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "remove_logo" }
|
||||
= t('.remove_logo')
|
||||
|
||||
|
||||
- if @object.white_label_logo.variable?
|
||||
= render ConfirmModalComponent.new(id: "remove_logo", confirm_reflexes: "click->WhiteLabel#remove_logo" ) do
|
||||
.margin-bottom-30
|
||||
= t('.remove_logo_confirm')
|
||||
@@ -18,7 +18,8 @@
|
||||
.report__header.print-hidden
|
||||
- if @report.message.present?
|
||||
%p.report__message= @report.message
|
||||
- if request.post?
|
||||
- if request.post? && !@error
|
||||
%button.btn-print.icon-print{ onclick: "window.print()"}= t(:report_print)
|
||||
|
||||
= t(@error, link: link_to(t(".report_link_label"), @error_url)) if @error
|
||||
= @table
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.side_menu#side_menu
|
||||
- if @enterprise
|
||||
- enterprise_side_menu_items(@enterprise).each do |item|
|
||||
- next unless item[:show]
|
||||
- next if !item[:show] || (item[:name] == 'vouchers' && !feature?(:vouchers, spree_current_user))
|
||||
%a.menu_item{ href: item[:href] || "##{item[:name]}_panel", id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: item[:selected] }
|
||||
%i{ class: item[:icon_class] }
|
||||
%span= t(".enterprise.#{item[:name] }")
|
||||
|
||||
23
app/views/admin/vouchers/new.html.haml
Normal file
23
app/views/admin/vouchers/new.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
= form_with model: @voucher, url: admin_enterprise_vouchers_path(@enterprise), html: { name: "voucher_form" } do |f|
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
.four.columns.alpha.text-right
|
||||
%a.button{ href: "#{edit_admin_enterprise_path(@enterprise)}#!#vouchers_panel"}
|
||||
= t('.back')
|
||||
.twelve.columns.omega
|
||||
.row
|
||||
.eight.columns.text-center
|
||||
%legend= t(".legend")
|
||||
.four.columns.text-right
|
||||
= f.submit t('.save'), class: 'red'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :code, t('.voucher_code')
|
||||
.omega.eight.columns
|
||||
= f.text_area :code, rows: 6, class: 'fullwidth'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :amount, t('.voucher_amount')
|
||||
.omega.eight.columns
|
||||
= Spree::Money.currency_symbol
|
||||
= f.text_field :amount, value: @voucher.value, disabled: true
|
||||
@@ -3,21 +3,25 @@
|
||||
%ul.nav-logo
|
||||
%li.ofn-logo
|
||||
%a{href: main_app.root_path}
|
||||
%img{src: ContentConfig.url_for(:logo)}
|
||||
- if @white_label_logo&.variable?
|
||||
= image_tag @white_label_distributor.white_label_logo_url(:default)
|
||||
- else
|
||||
%img{src: ContentConfig.url_for(:logo)}
|
||||
%li.powered-by
|
||||
%img{src: '/favicon.ico'}
|
||||
%span
|
||||
= t 'powered_by'
|
||||
%a{href: '/'}
|
||||
= t 'title'
|
||||
%ul.nav-main-menu
|
||||
- [*1..7].each do |menu_number|
|
||||
- menu_name = "menu_#{menu_number}"
|
||||
- if ContentConfig[menu_name].present?
|
||||
%li
|
||||
%a{href: t("#{menu_name}_url") }
|
||||
%span.nav-primary
|
||||
= t "#{menu_name}_title"
|
||||
- unless @hide_ofn_navigation
|
||||
%ul.nav-main-menu
|
||||
- [*1..7].each do |menu_number|
|
||||
- menu_name = "menu_#{menu_number}"
|
||||
- if ContentConfig[menu_name].present?
|
||||
%li
|
||||
%a{href: t("#{menu_name}_url") }
|
||||
%span.nav-primary
|
||||
= t "#{menu_name}_title"
|
||||
%ul.nav-icons-menu
|
||||
- if OpenFoodNetwork::I18nConfig.selectable_locales.count > 1
|
||||
= render 'shared/menu/language_selector'
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
%section.left
|
||||
.ofn-logo
|
||||
%a{href: main_app.root_path}
|
||||
%img{src: ContentConfig.url_for(:logo_mobile), srcset: ContentConfig.url_for(:logo_mobile_svg), width: "75", height: "26"}
|
||||
- if @white_label_logo&.variable?
|
||||
= image_tag @white_label_distributor.white_label_logo_url(:mobile)
|
||||
- else
|
||||
%img{src: ContentConfig.url_for(:logo_mobile), srcset: ContentConfig.url_for(:logo_mobile_svg), width: "75", height: "26"}
|
||||
|
||||
%section.right{"ng-cloak" => true}
|
||||
%span.cart-span{"ng-class" => "{ dirty: Cart.dirty || Cart.empty(), 'pure-dirty': Cart.dirty }"}
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
%h3
|
||||
%a{"ng-click" => "triggerProductModal()", href: 'javascript:void(0)'}
|
||||
%span{"ng-bind" => "::product.name"}
|
||||
%p.product-description{ng: {"bind-html": "::product.description_html", click: "triggerProductModal()", show: "product.description_html.length"}}
|
||||
.product-producer
|
||||
= t :products_from
|
||||
%span
|
||||
%enterprise-modal
|
||||
%span{"ng-bind" => "::enterprise.name"}
|
||||
.product-description{ng: {"bind-html": "::product.description_html", click: "triggerProductModal()", show: "product.description_html.length"}}
|
||||
%div{ "ng-switch" => "enterprise.visible" }
|
||||
.product-producer
|
||||
= t :products_from
|
||||
%span{ "ng-switch-when": "hidden", "ng-bind" => "::enterprise.name"}
|
||||
%span{ "ng-switch-default": true }
|
||||
%enterprise-modal{"ng-bind" => "::enterprise.name"}
|
||||
|
||||
.product-properties.filter-shopfront.property-selectors
|
||||
%filter-selector{ 'selector-set' => "productPropertySelectors", objects: "[product] | propertiesWithValuesOf" }
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
= t :shopping_producers_of_hub, hub: '{{ shopfront.name }}'
|
||||
%ul.small-block-grid-1.medium-block-grid-2.large-block-grid-3
|
||||
%li{"ng-repeat" => "enterprise in shopfront.producers"}
|
||||
%enterprise-modal
|
||||
%enterprise-modal{"ng-if": "enterprise.visible != 'hidden'"}
|
||||
%i.ofn-i_036-producers
|
||||
{{ enterprise.name }}
|
||||
%span{"ng-if": "enterprise.visible == 'hidden'"}
|
||||
%i.ofn-i_036-producers
|
||||
{{ enterprise.name }}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
%h6.text-center{ 'ng-hide' => 'sharedResource' } {{ selectedUnitsVariant.full_name }}
|
||||
.three.columns
|
||||
%h6.text-right
|
||||
%a{ :href => '#', 'ng-click' => 'selectedUnitsVariant = {};selectedUnitsProduct = {};sharedResource=false;' }= t('admin.clear')
|
||||
%a{ :href => '#', 'ng-click' => 'resetSelectedUnitsVariant()' }= t('admin.clear')
|
||||
%hr
|
||||
.row
|
||||
.one.columns.alpha
|
||||
@@ -133,7 +133,7 @@
|
||||
%a{ :href => '', 'ng-click' => "sorting.toggle('order.number')" }
|
||||
= t("admin.orders.bulk_management.order_no")
|
||||
%th.full_name{ 'ng-show' => 'columns.full_name.visible' }
|
||||
%a{ :href => '', 'ng-click' => "sorting.toggle('order.full_name')" }
|
||||
%a{ :href => '', 'ng-click' => "sorting.toggle('order.full_name_for_sorting')" }
|
||||
= t("admin.name")
|
||||
%th.email{ 'ng-show' => 'columns.email.visible' }
|
||||
%a{ :href => '', 'ng-click' => "sorting.toggle('order.email')" }
|
||||
@@ -166,11 +166,11 @@
|
||||
= "#{t('admin.price')} (#{Spree::Money.currency_symbol})"
|
||||
%th.actions
|
||||
|
||||
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
|
||||
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
|
||||
%td.bulk
|
||||
%input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true }
|
||||
%td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }}
|
||||
%td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }}
|
||||
%td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name_for_sorting }}
|
||||
%td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }}
|
||||
%td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }}
|
||||
%td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }}
|
||||
|
||||
@@ -21,27 +21,27 @@
|
||||
|
||||
.row.index-controls{'ng-show' => '!RequestMonitor.loading && orders.length > 0'}
|
||||
%div{style: "display: flex; justify-content: space-between;"}
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
{{ "spree.admin.orders.index.selected" | t:{count: selected_orders.length} }}
|
||||
.ofn-drop-down{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
%span{ :class => 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
|
||||
= t('.resend_confirmation')
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
{{ "spree.admin.orders.index.selected" | t:{count: selected_orders.length} }}
|
||||
.ofn-drop-down{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
%span{ :class => 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
|
||||
= t('.resend_confirmation')
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" }
|
||||
= t('.send_invoice')
|
||||
%div.menu_item
|
||||
%span.name.invoices-modal{'ng-controller' => 'bulkInvoiceCtrl', 'ng-click' => 'createBulkInvoice()' }
|
||||
= t('.print_invoices')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('.cancel_orders')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('.cancel_orders')
|
||||
|
||||
= render partial: 'admin/shared/angular_per_page_controls', locals: { position: "right", model: "orders" }
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
= f.field_container :shipping_categories do
|
||||
= f.label :shipping_category_id, t(:shipping_categories)
|
||||
= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, { :include_blank => t(:none) }, { :class => 'select2' })
|
||||
= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, {}, { :class => 'select2' })
|
||||
= f.error_message_on :shipping_category
|
||||
|
||||
= f.field_container :tax_category do
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
%span.icon-question-sign{ 'ofn-with-tip' => t('admin.products.seo.product_search_tip') }
|
||||
%br/
|
||||
= f.text_field :meta_keywords, :class => 'fullwidth', :rows => 6
|
||||
= f.field_container :meta_description do
|
||||
= f.label :meta_description, t('admin.products.seo.SEO_keywords')
|
||||
%span.icon-question-sign{ 'ofn-with-tip' => t('admin.products.seo.seo_tip') }
|
||||
%br/
|
||||
= f.text_field :meta_description, :class => 'fullwidth', :rows => 6
|
||||
.alpha.eleven.columns
|
||||
= f.field_container :notes do
|
||||
= f.label :notes, t(:notes)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user