mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-28 01:53:25 +00:00
Compare commits
500 Commits
v4.1.21
...
v4.2.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fddb76f002 | ||
|
|
3e3fc4a5a2 | ||
|
|
4a2fd10bdb | ||
|
|
1a2c1d004c | ||
|
|
6ff2d4a9d5 | ||
|
|
b3d81154e6 | ||
|
|
4a15ed9b39 | ||
|
|
1d8b8c3be8 | ||
|
|
454a4e3435 | ||
|
|
076efd653d | ||
|
|
4facab0335 | ||
|
|
bea080a9b1 | ||
|
|
86731d7e30 | ||
|
|
b7efa1b018 | ||
|
|
4a0ed99919 | ||
|
|
45995ac984 | ||
|
|
421ffae78c | ||
|
|
f29e569f1b | ||
|
|
727eef3c4f | ||
|
|
ddd9ae6ce2 | ||
|
|
fd1cd4a28a | ||
|
|
9261d7b8fe | ||
|
|
b169f1baf1 | ||
|
|
2e05210596 | ||
|
|
8645277a05 | ||
|
|
f3d9b3d7ed | ||
|
|
c22c1ddf32 | ||
|
|
97f0d76fcd | ||
|
|
d70acb57b6 | ||
|
|
ab52028d1c | ||
|
|
0544baa75c | ||
|
|
acda515c8a | ||
|
|
ad0effc565 | ||
|
|
b7cf397e15 | ||
|
|
fb2279d26d | ||
|
|
cb6cb05eca | ||
|
|
d0acd47935 | ||
|
|
e32797d2fd | ||
|
|
f34b68a6e3 | ||
|
|
f914a37a28 | ||
|
|
641df45fcd | ||
|
|
ec7d4b7c21 | ||
|
|
94f87b3bd7 | ||
|
|
a3d996d5f5 | ||
|
|
8a62d95c48 | ||
|
|
7b2da4dbe4 | ||
|
|
0bb523c399 | ||
|
|
ca3c896b2e | ||
|
|
34286a8f8d | ||
|
|
533acda922 | ||
|
|
be88b95e8c | ||
|
|
f4c7f24cce | ||
|
|
8779ef6b77 | ||
|
|
5dfc3c7a62 | ||
|
|
9873380b94 | ||
|
|
a37f54e4b3 | ||
|
|
03b24bfc38 | ||
|
|
cf687864cc | ||
|
|
d28545a7b2 | ||
|
|
0b71f7298e | ||
|
|
9992f9c83b | ||
|
|
f09b719af6 | ||
|
|
6b3ffddb74 | ||
|
|
0c7f962bf0 | ||
|
|
0f618dfbf9 | ||
|
|
513b26f47c | ||
|
|
2e3023eacc | ||
|
|
2eeeb74374 | ||
|
|
1be3b508bf | ||
|
|
294f7c2fb5 | ||
|
|
89212736e9 | ||
|
|
35b5ca3a51 | ||
|
|
56fab26235 | ||
|
|
b7991e5ae8 | ||
|
|
2008c10c6f | ||
|
|
0218f75f34 | ||
|
|
0545c9de28 | ||
|
|
b54ec4354a | ||
|
|
767afe1fba | ||
|
|
2dd31d970e | ||
|
|
8a943f50ef | ||
|
|
3b01c44eae | ||
|
|
5105ea345f | ||
|
|
b259f59d1e | ||
|
|
eeb525aedb | ||
|
|
9874c7996e | ||
|
|
c7c5965eb5 | ||
|
|
cd30012334 | ||
|
|
42da443901 | ||
|
|
b3e1ffe9e2 | ||
|
|
71aca960ee | ||
|
|
3808398807 | ||
|
|
93751f9ccb | ||
|
|
944d40e093 | ||
|
|
22fe652e89 | ||
|
|
7a9ed7a73c | ||
|
|
b25192c31d | ||
|
|
529858946f | ||
|
|
88bc417868 | ||
|
|
287e8f5845 | ||
|
|
caccbb698b | ||
|
|
452a3fa933 | ||
|
|
288a35f062 | ||
|
|
5f78fdce8b | ||
|
|
392166b57a | ||
|
|
2985d2af15 | ||
|
|
e9513f6172 | ||
|
|
4a99a7d1de | ||
|
|
41c11baa3f | ||
|
|
3d1b61ae6e | ||
|
|
881a708ecf | ||
|
|
9d943625a3 | ||
|
|
e20c2e3ced | ||
|
|
01be6fb1f3 | ||
|
|
1fee45035c | ||
|
|
e55462d18b | ||
|
|
09247b21cd | ||
|
|
209b56ffd7 | ||
|
|
cbb6bd63d4 | ||
|
|
213c0dd060 | ||
|
|
d53d38906a | ||
|
|
1ff98a1d8a | ||
|
|
f012559ca8 | ||
|
|
aafa416b9c | ||
|
|
cae032a224 | ||
|
|
94d8ada362 | ||
|
|
92677385fa | ||
|
|
372939b260 | ||
|
|
8945d0cc33 | ||
|
|
8b05fb2210 | ||
|
|
c00a35b5a4 | ||
|
|
86a3f91ca2 | ||
|
|
6a9fcbdd0c | ||
|
|
38d6b5d452 | ||
|
|
6141641c23 | ||
|
|
d942adcdd0 | ||
|
|
bfc3e385ba | ||
|
|
f5ecc6ffe4 | ||
|
|
61f8dfd809 | ||
|
|
51db3d5997 | ||
|
|
87ec803fba | ||
|
|
37a9338d0c | ||
|
|
3eccb65407 | ||
|
|
c1864fb8e5 | ||
|
|
44965ff235 | ||
|
|
e59d753333 | ||
|
|
8903496a1b | ||
|
|
edc0520ce3 | ||
|
|
bf5e7a12d1 | ||
|
|
9c231a831d | ||
|
|
f2ac888f2c | ||
|
|
fb834d3143 | ||
|
|
24acf21c6d | ||
|
|
ec7d7b0ad8 | ||
|
|
ae728caac6 | ||
|
|
2758f83a2a | ||
|
|
5162c5ecf9 | ||
|
|
5ff1c1c7c2 | ||
|
|
c98d669b92 | ||
|
|
11b224f805 | ||
|
|
664e2a5423 | ||
|
|
183a6f8fc7 | ||
|
|
d7df714280 | ||
|
|
132ab28070 | ||
|
|
89aa76dbbc | ||
|
|
4635e67707 | ||
|
|
8945142149 | ||
|
|
7cb6afbd96 | ||
|
|
30ea017fa2 | ||
|
|
126a6aa84a | ||
|
|
e2eb92fd90 | ||
|
|
ff1b895286 | ||
|
|
da2e7b404f | ||
|
|
ce9bd37c9b | ||
|
|
d57d812ab4 | ||
|
|
99c0416cc7 | ||
|
|
335de1d038 | ||
|
|
5b9a5c1df6 | ||
|
|
72641ba317 | ||
|
|
34fcb4a1f7 | ||
|
|
b370ee328a | ||
|
|
5fe8060ea6 | ||
|
|
a8afe7fee3 | ||
|
|
8bbb80ef55 | ||
|
|
fe2ee57cb3 | ||
|
|
af58ba523c | ||
|
|
9dc3c5a06c | ||
|
|
1c1f9d73a3 | ||
|
|
0b885b3954 | ||
|
|
7bcfda0a52 | ||
|
|
92bbcbb7ce | ||
|
|
c36ad96acc | ||
|
|
ec64e5c8f7 | ||
|
|
95cb6e93e7 | ||
|
|
ce0e33fffa | ||
|
|
b6cdb04a27 | ||
|
|
92bb23d914 | ||
|
|
4eb550431e | ||
|
|
8330339046 | ||
|
|
e34d4c773c | ||
|
|
e669ed9620 | ||
|
|
6262dc3a2e | ||
|
|
cb540d66c6 | ||
|
|
1f80c2add4 | ||
|
|
898c470c00 | ||
|
|
2046317f16 | ||
|
|
413c97cfa8 | ||
|
|
2b4d9be907 | ||
|
|
8f9c50a311 | ||
|
|
9306e054b8 | ||
|
|
4ea1dd68af | ||
|
|
9cb3dc4c42 | ||
|
|
2ddc64ce4d | ||
|
|
1aae8bba99 | ||
|
|
3e5656e638 | ||
|
|
96f267eefe | ||
|
|
30e68450d2 | ||
|
|
2b3db15505 | ||
|
|
99beacf723 | ||
|
|
67453de649 | ||
|
|
f516b7056d | ||
|
|
1f6b5ef309 | ||
|
|
7528e878dc | ||
|
|
2981c35482 | ||
|
|
06dbb3b545 | ||
|
|
2da7e9ab44 | ||
|
|
a145b97c28 | ||
|
|
3985c9fb06 | ||
|
|
67d3bd7414 | ||
|
|
4162830bee | ||
|
|
c004f16c9a | ||
|
|
e607d5c161 | ||
|
|
4b629eae11 | ||
|
|
6f5dd5ab0c | ||
|
|
a6c69c8530 | ||
|
|
6276a90258 | ||
|
|
4a65333a83 | ||
|
|
268a73a41d | ||
|
|
4dde49c881 | ||
|
|
f831154b6f | ||
|
|
91448c18f2 | ||
|
|
d82f7c3c4d | ||
|
|
7516cf6de3 | ||
|
|
0c2a3a9bb6 | ||
|
|
01e915800e | ||
|
|
2c67d6d238 | ||
|
|
4009f3313f | ||
|
|
54d64c1c2c | ||
|
|
db3ca2e66e | ||
|
|
b60eeb8dc4 | ||
|
|
d38e14ca5c | ||
|
|
a75af09c58 | ||
|
|
8825ff2711 | ||
|
|
0b56cdabea | ||
|
|
1149278a48 | ||
|
|
3a4713b8c9 | ||
|
|
5635f5ad46 | ||
|
|
c4431ccac6 | ||
|
|
1e4f096639 | ||
|
|
6ba255ce85 | ||
|
|
2e989083bd | ||
|
|
3e02db7bd9 | ||
|
|
86bd6cb102 | ||
|
|
5eca0a7811 | ||
|
|
ba1e21ad75 | ||
|
|
28de8bbb8d | ||
|
|
0327cc0d0b | ||
|
|
15e7a5a4df | ||
|
|
66386683b6 | ||
|
|
1ea2656bfb | ||
|
|
af69064d3a | ||
|
|
1c68293cac | ||
|
|
143c977a19 | ||
|
|
356d759dbe | ||
|
|
f7a767988c | ||
|
|
029df03ee6 | ||
|
|
c001463f8a | ||
|
|
727aa553e7 | ||
|
|
8aa4631e21 | ||
|
|
c8b5ce64a4 | ||
|
|
aed757e349 | ||
|
|
18572cbbfa | ||
|
|
f3706bdb00 | ||
|
|
cbb9e01a25 | ||
|
|
94bba56726 | ||
|
|
0cc056ab7f | ||
|
|
5444d0d0b9 | ||
|
|
d23846bfa2 | ||
|
|
6b1dd00dec | ||
|
|
aa75c2defe | ||
|
|
dd905e4388 | ||
|
|
fbe378d3b9 | ||
|
|
d5fbb40b93 | ||
|
|
1dc57a3518 | ||
|
|
c6e982b0f9 | ||
|
|
0d29fc78c3 | ||
|
|
4d8c44d890 | ||
|
|
4eb2b5d483 | ||
|
|
a3a1e27cb1 | ||
|
|
a4bffc61f1 | ||
|
|
26d6969b5b | ||
|
|
e6c65f524a | ||
|
|
762b6fad65 | ||
|
|
48246117cd | ||
|
|
bf8186bb84 | ||
|
|
fd804d819e | ||
|
|
5e0c901790 | ||
|
|
760e7ad6e2 | ||
|
|
48613a4b42 | ||
|
|
1aed6d0ee6 | ||
|
|
65faf33b87 | ||
|
|
99161a19ea | ||
|
|
8b047cd449 | ||
|
|
b2b8ec8008 | ||
|
|
2087a17fa9 | ||
|
|
0eb77cf7d3 | ||
|
|
3fc71c0e99 | ||
|
|
afcdfce37d | ||
|
|
d16b6fcad5 | ||
|
|
cb697599d4 | ||
|
|
d0a93fb512 | ||
|
|
eef59bbaae | ||
|
|
7181694401 | ||
|
|
7753d9e1ea | ||
|
|
be9156d40f | ||
|
|
09bd7126d8 | ||
|
|
9bce2ca64c | ||
|
|
fedfeb6dd3 | ||
|
|
6459d9ea36 | ||
|
|
5e3e8c32c3 | ||
|
|
ff3cc6e771 | ||
|
|
ad19142042 | ||
|
|
5470dc6c68 | ||
|
|
706f6025e9 | ||
|
|
ea12fcb45f | ||
|
|
47c59e6172 | ||
|
|
5b6c23780e | ||
|
|
374bc31a60 | ||
|
|
d34ab71efa | ||
|
|
abdab5a22a | ||
|
|
e726d2d736 | ||
|
|
044bf35c81 | ||
|
|
010b39b671 | ||
|
|
4079e61beb | ||
|
|
dc00221dc4 | ||
|
|
eef9dc346d | ||
|
|
f29dc3f978 | ||
|
|
1a66c1148b | ||
|
|
c4fc8e9396 | ||
|
|
e2b1c0d93f | ||
|
|
71b6c129d6 | ||
|
|
20142ca632 | ||
|
|
8929fa4dcd | ||
|
|
843f57ad4f | ||
|
|
95b1911f38 | ||
|
|
f8b8298fbe | ||
|
|
6a48bdbb82 | ||
|
|
85ff203e3f | ||
|
|
f229479a1e | ||
|
|
10685b4ca2 | ||
|
|
729e9eda66 | ||
|
|
b2613f3331 | ||
|
|
5f2f5456a0 | ||
|
|
be1c255067 | ||
|
|
a516193ca8 | ||
|
|
6a71be2a6b | ||
|
|
1c2a60d543 | ||
|
|
a4a5586345 | ||
|
|
742ae761a3 | ||
|
|
379eda7c41 | ||
|
|
3e00ab261e | ||
|
|
5063f377c3 | ||
|
|
d789fb32e9 | ||
|
|
51420934f3 | ||
|
|
adc7e97e62 | ||
|
|
e5e8953a09 | ||
|
|
aa6e5ae799 | ||
|
|
8d12c7a692 | ||
|
|
41746459fa | ||
|
|
3baed683b1 | ||
|
|
5fb94dcb22 | ||
|
|
913a4560d1 | ||
|
|
cc19dc7d03 | ||
|
|
ad5c834342 | ||
|
|
17989b2f90 | ||
|
|
c9248f872d | ||
|
|
a71e95da27 | ||
|
|
b9efda9f64 | ||
|
|
4800abf324 | ||
|
|
f73f3b17e1 | ||
|
|
66cdd56428 | ||
|
|
d759409e38 | ||
|
|
b9e18a1ec1 | ||
|
|
9a5318965c | ||
|
|
9230329343 | ||
|
|
3901a18b72 | ||
|
|
4730bbdc36 | ||
|
|
9e55a306ee | ||
|
|
2758097776 | ||
|
|
f5eb83c483 | ||
|
|
aafe7c90a8 | ||
|
|
64ba004e5d | ||
|
|
6d0d57a4d2 | ||
|
|
7c1a4d6791 | ||
|
|
877002afd8 | ||
|
|
cc120aecb6 | ||
|
|
992cb9d920 | ||
|
|
b9d3eef381 | ||
|
|
532d566308 | ||
|
|
eae8129435 | ||
|
|
ed0ed3240e | ||
|
|
5d8cdc3246 | ||
|
|
27e31ae09d | ||
|
|
f4ce08d39c | ||
|
|
87c79a5941 | ||
|
|
7bdf814d7d | ||
|
|
19270f1777 | ||
|
|
f079fcb49a | ||
|
|
1c80e88758 | ||
|
|
c535ed5789 | ||
|
|
53544ee66a | ||
|
|
621933c727 | ||
|
|
df6cd5b266 | ||
|
|
790bcc9ff2 | ||
|
|
6f6ad05ac2 | ||
|
|
ac361d3b64 | ||
|
|
13a45557a4 | ||
|
|
0dd398abba | ||
|
|
56912bb807 | ||
|
|
7446dfb0b7 | ||
|
|
37801f1345 | ||
|
|
43b284a175 | ||
|
|
ab5a323044 | ||
|
|
08755acfd1 | ||
|
|
c9e40d084c | ||
|
|
0948ce47ed | ||
|
|
3a0f50395f | ||
|
|
4606d81a28 | ||
|
|
4bdce59648 | ||
|
|
ba5bdf688c | ||
|
|
df7227443a | ||
|
|
b3771bb623 | ||
|
|
8ceeaaa7d4 | ||
|
|
ad52805a0d | ||
|
|
f09f6df181 | ||
|
|
13b7d80632 | ||
|
|
ccc3322c12 | ||
|
|
0f6b335a22 | ||
|
|
6ba351f68f | ||
|
|
4877f96b4b | ||
|
|
ffe2a3076c | ||
|
|
fb83ae2ad4 | ||
|
|
b9e0a5ecf7 | ||
|
|
ea3e15f0c8 | ||
|
|
bcf6c7984e | ||
|
|
08d4aea2b9 | ||
|
|
483ec42328 | ||
|
|
65596f8467 | ||
|
|
d273e97b88 | ||
|
|
b410585449 | ||
|
|
ac08602d94 | ||
|
|
0b1697c62b | ||
|
|
7ca6434b14 | ||
|
|
5fc7d72b09 | ||
|
|
771f7b0393 | ||
|
|
12d989568e | ||
|
|
4aa70c1ffd | ||
|
|
b89715149c | ||
|
|
75fc35574e | ||
|
|
414bf5d074 | ||
|
|
bd9bed7323 | ||
|
|
2e59812bc1 | ||
|
|
d87e1805af | ||
|
|
d66d6d6bd6 | ||
|
|
3dbf00f302 | ||
|
|
c102ce8e7e | ||
|
|
8e61428cce | ||
|
|
a222b507fb | ||
|
|
56bc554f29 | ||
|
|
028d02ccca | ||
|
|
46f9d3ef81 | ||
|
|
76f14a03c6 | ||
|
|
39f4feed4a | ||
|
|
cc4192047e | ||
|
|
3128232d7e | ||
|
|
c4e2c8cb4c | ||
|
|
7d8ded5ab8 | ||
|
|
21affa6250 | ||
|
|
8ffe6f6052 | ||
|
|
8e070f55ff | ||
|
|
3effff8821 | ||
|
|
e72cc516a8 | ||
|
|
4fbaa65534 | ||
|
|
0c2a267bea | ||
|
|
1b8c1bd27a | ||
|
|
b43a68d717 | ||
|
|
05756616dd | ||
|
|
d03b52a163 | ||
|
|
67ae2e72f3 | ||
|
|
db45d7f4eb |
7
.env
7
.env
@@ -61,10 +61,3 @@ SMTP_PASSWORD="f00d"
|
||||
# STRIPE_INSTANCE_PUBLISHABLE_KEY="pk_test_xxxx" # This can be a test key or a live key
|
||||
# STRIPE_CLIENT_ID="ca_xxxx" # This can be a development ID or a production ID
|
||||
# STRIPE_ENDPOINT_SECRET="whsec_xxxx"
|
||||
|
||||
# Feature toggles
|
||||
#
|
||||
# Adding user emails separated by commas will enable them the use of certain features. See
|
||||
# config/initializers/feature_toggles.rb for details.
|
||||
#
|
||||
# BETA_TESTERS="ofn@example.com,superadmin@example.com"
|
||||
|
||||
46
.github/workflows/mapi.yml
vendored
Normal file
46
.github/workflows/mapi.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: 'Mayhem for API'
|
||||
on: [push]
|
||||
jobs:
|
||||
test:
|
||||
if: ${{ github.repository_owner == 'openfoodfoundation' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: docker/build
|
||||
- run: docker-compose up --detach
|
||||
- run: until curl -f -s http://localhost:3000; do echo "waiting for api server"; sleep 1; done
|
||||
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="update spree_users set spree_api_key='testing' where login='ofn@example.com'"
|
||||
# equivalent to Flipper.enable(:api_v1)
|
||||
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="insert into flipper_features (key, created_at, updated_at) values ('api_v1', localtimestamp, localtimestamp)"
|
||||
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="insert into flipper_gates (feature_key, key, value, created_at, updated_at) values ('api_v1', 'boolean', 'true', localtimestamp, localtimestamp)"
|
||||
|
||||
# Run Mayhem for API
|
||||
- name: Run Mayhem for API
|
||||
uses: ForAllSecure/mapi-action@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
mapi-token: ${{ secrets.MAPI_TOKEN }}
|
||||
api-url: http://localhost:3000
|
||||
api-spec: swagger/v1/swagger.yaml
|
||||
target: openfoodfoundation/openfoodnetwork
|
||||
duration: 1min
|
||||
sarif-report: mapi.sarif
|
||||
html-report: mapi.html
|
||||
run-args: |
|
||||
--header-auth
|
||||
X-Api-Token: testing
|
||||
|
||||
# Archive HTML report
|
||||
- name: Archive Mayhem for API report
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mapi-report
|
||||
path: mapi.html
|
||||
|
||||
# Upload SARIF file (only available on public repos or github enterprise)
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: mapi.sarif
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@ db/*.csv
|
||||
log/*.log
|
||||
log/*.log.lck
|
||||
log/*.log.*
|
||||
/storage
|
||||
tmp/
|
||||
.idea/*
|
||||
\#*
|
||||
|
||||
@@ -27,11 +27,16 @@ Metrics/BlockLength:
|
||||
"class_eval",
|
||||
"collection",
|
||||
"context",
|
||||
"delete",
|
||||
"describe",
|
||||
"feature",
|
||||
"get",
|
||||
"it",
|
||||
"member",
|
||||
"namespace",
|
||||
"path",
|
||||
"post",
|
||||
"put",
|
||||
"resource",
|
||||
"resources",
|
||||
"scenario",
|
||||
@@ -74,6 +79,11 @@ Lint/RaiseException:
|
||||
Lint/StructNewOverride:
|
||||
Enabled: true
|
||||
|
||||
Naming/VariableNumber:
|
||||
AllowedIdentifiers:
|
||||
- street_address_1
|
||||
- street_address_2
|
||||
|
||||
Bundler/DuplicatedGem:
|
||||
Enabled: false
|
||||
|
||||
@@ -95,6 +105,10 @@ Lint/UselessAssignment:
|
||||
Exclude:
|
||||
- spec/**/*
|
||||
|
||||
Lint/MissingSuper:
|
||||
Exclude:
|
||||
- 'app/components/**/*'
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 30 # default 17
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400`
|
||||
# on 2022-02-25 01:04:47 UTC using RuboCop version 1.22.2.
|
||||
# on 2022-03-29 16:07:39 UTC using RuboCop version 1.22.2.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
@@ -53,7 +53,7 @@ Layout/LeadingCommentSpace:
|
||||
Exclude:
|
||||
- 'spec/system/admin/enterprises_spec.rb'
|
||||
|
||||
# Offense count: 828
|
||||
# Offense count: 856
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||
# URISchemes: http, https
|
||||
@@ -108,7 +108,6 @@ Layout/LineLength:
|
||||
- 'app/services/order_syncer.rb'
|
||||
- 'app/services/products_renderer.rb'
|
||||
- 'app/services/variant_units/variant_and_line_item_naming.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
|
||||
- 'engines/order_management/spec/services/order_management/order/updater_spec.rb'
|
||||
- 'engines/web/app/helpers/web/cookies_policy_helper.rb'
|
||||
@@ -117,15 +116,19 @@ Layout/LineLength:
|
||||
- 'lib/open_food_network/enterprise_fee_applicator.rb'
|
||||
- 'lib/open_food_network/enterprise_fee_calculator.rb'
|
||||
- 'lib/open_food_network/enterprise_issue_validator.rb'
|
||||
- 'lib/open_food_network/lettuce_share_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/open_food_network/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/reports/line_items.rb'
|
||||
- 'lib/open_food_network/sales_tax_report.rb'
|
||||
- 'lib/open_food_network/scope_variants_for_search.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/reporting/line_items.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/report_data/enterprise_fee_type_total.rb'
|
||||
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/distributor_totals_by_supplier_report.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/products_and_inventory/lettuce_share_report.rb'
|
||||
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'lib/tasks/data.rake'
|
||||
- 'lib/tasks/enterprises.rake'
|
||||
@@ -176,19 +179,20 @@ Layout/LineLength:
|
||||
- 'spec/helpers/spree/admin/base_helper_spec.rb'
|
||||
- 'spec/jobs/subscription_confirm_job_spec.rb'
|
||||
- 'spec/jobs/subscription_placement_job_spec.rb'
|
||||
- 'spec/lib/open_food_network/customers_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
|
||||
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_management_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_grouper_spec.rb'
|
||||
- 'spec/lib/open_food_network/permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
|
||||
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
|
||||
- 'spec/lib/reports/customers_report_spec.rb'
|
||||
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
|
||||
- 'spec/lib/reports/order_grouper_spec.rb'
|
||||
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
|
||||
- 'spec/lib/reports/packing/packing_report_spec.rb'
|
||||
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
|
||||
- 'spec/lib/reports/xero_invoices_report_spec.rb'
|
||||
- 'spec/lib/stripe/authorize_response_patcher_spec.rb'
|
||||
- 'spec/mailers/order_mailer_spec.rb'
|
||||
- 'spec/mailers/producer_mailer_spec.rb'
|
||||
@@ -309,7 +313,15 @@ Layout/MultilineMethodCallBraceLayout:
|
||||
Exclude:
|
||||
- 'lib/reporting/queries/joins.rb'
|
||||
|
||||
# Offense count: 17
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
||||
# SupportedStyles: aligned, indented, indented_relative_to_receiver
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Exclude:
|
||||
- 'lib/reporting/reports/customers/customers_report.rb'
|
||||
|
||||
# Offense count: 20
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowInHeredoc.
|
||||
Layout/TrailingWhitespace:
|
||||
@@ -331,7 +343,7 @@ Lint/ConstantDefinitionInBlock:
|
||||
- 'lib/tasks/users.rake'
|
||||
- 'spec/controllers/spree/admin/base_controller_spec.rb'
|
||||
- 'spec/helpers/serializer_helper_spec.rb'
|
||||
- 'spec/lib/open_food_network/reports/line_items_spec.rb'
|
||||
- 'spec/lib/reports/line_items_spec.rb'
|
||||
- 'spec/models/spree/ability_spec.rb'
|
||||
- 'spec/models/spree/gateway_spec.rb'
|
||||
- 'spec/models/spree/preferences/configuration_spec.rb'
|
||||
@@ -366,11 +378,6 @@ Lint/IneffectiveAccessModifier:
|
||||
Exclude:
|
||||
- 'app/models/spree/user.rb'
|
||||
|
||||
# Offense count: 2
|
||||
Lint/MissingSuper:
|
||||
Exclude:
|
||||
- 'app/components/distributor_title_component.rb'
|
||||
- 'app/components/example_component.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
@@ -403,7 +410,7 @@ Lint/UselessMethodDefinition:
|
||||
- 'app/controllers/spree/user_registrations_controller.rb'
|
||||
- 'app/models/spree/gateway.rb'
|
||||
|
||||
# Offense count: 39
|
||||
# Offense count: 38
|
||||
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max.
|
||||
Metrics/AbcSize:
|
||||
Exclude:
|
||||
@@ -424,22 +431,20 @@ Metrics/AbcSize:
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/preferences/preferable_class_methods.rb'
|
||||
- 'app/models/spree/return_authorization.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/customers_report.rb'
|
||||
- 'lib/open_food_network/group_buy_report.rb'
|
||||
- 'lib/open_food_network/order_and_distributor_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/customers/customers_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/sales_tax_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_distributors/orders_and_distributors_report.rb'
|
||||
- 'lib/reporting/reports/packing/customer.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
- 'lib/spree/core/s3_support.rb'
|
||||
- 'lib/tasks/enterprises.rake'
|
||||
- 'spec/services/order_checkout_restart_spec.rb'
|
||||
|
||||
# Offense count: 45
|
||||
# Offense count: 43
|
||||
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
|
||||
# IgnoredMethods: refine
|
||||
Metrics/BlockLength:
|
||||
@@ -463,12 +468,12 @@ Metrics/BlockLength:
|
||||
- 'spec/factories/subscription_factory.rb'
|
||||
- 'spec/factories/user_factory.rb'
|
||||
- 'spec/factories/variant_factory.rb'
|
||||
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
|
||||
- 'spec/requests/api/orders_spec.rb'
|
||||
- 'spec/spec_helper.rb'
|
||||
- 'spec/support/cancan_helper.rb'
|
||||
- 'spec/support/matchers/select2_matchers.rb'
|
||||
- 'spec/support/matchers/table_matchers.rb'
|
||||
- 'spec/swagger_helper.rb'
|
||||
- 'spec/system/admin/order_cycles/complex_updating_specific_time_spec.rb'
|
||||
- 'spec/system/consumer/shopping/checkout_spec.rb'
|
||||
|
||||
@@ -478,7 +483,7 @@ Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
|
||||
# Offense count: 49
|
||||
# Offense count: 50
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Exclude:
|
||||
@@ -522,18 +527,17 @@ Metrics/ClassLength:
|
||||
- 'app/services/cart_service.rb'
|
||||
- 'app/services/order_syncer.rb'
|
||||
- 'engines/order_management/app/services/order_management/order/updater.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/open_food_network/enterprise_fee_calculator.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/open_food_network/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/permissions.rb'
|
||||
- 'lib/open_food_network/users_and_enterprises_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
|
||||
# Offense count: 40
|
||||
# Offense count: 39
|
||||
# Configuration parameters: IgnoredMethods, Max.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
@@ -559,20 +563,19 @@ Metrics/CyclomaticComplexity:
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/spree/zone.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/customers_report.rb'
|
||||
- 'lib/open_food_network/enterprise_issue_validator.rb'
|
||||
- 'lib/open_food_network/group_buy_report.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/customers/customers_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
- 'lib/spree/core/controller_helpers/respond_with.rb'
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'spec/models/product_importer_spec.rb'
|
||||
|
||||
# Offense count: 31
|
||||
# Offense count: 32
|
||||
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
|
||||
Metrics/MethodLength:
|
||||
Exclude:
|
||||
@@ -582,22 +585,23 @@ Metrics/MethodLength:
|
||||
- 'app/controllers/spree/orders_controller.rb'
|
||||
- 'app/helpers/checkout_helper.rb'
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
- 'app/json_schemas/json_api_schema.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
- 'app/models/spree/gateway/pay_pal_express.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
- 'app/models/spree/preferences/preferable_class_methods.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/open_food_network/order_cycle_management_report.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'lib/tasks/sample_data/product_factory.rb'
|
||||
|
||||
# Offense count: 51
|
||||
# Offense count: 54
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ModuleLength:
|
||||
Exclude:
|
||||
@@ -628,17 +632,20 @@ Metrics/ModuleLength:
|
||||
- 'spec/controllers/spree/admin/adjustments_controller_spec.rb'
|
||||
- 'spec/controllers/spree/admin/payment_methods_controller_spec.rb'
|
||||
- 'spec/lib/open_food_network/address_finder_spec.rb'
|
||||
- 'spec/lib/open_food_network/customers_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_management_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_grouper_spec.rb'
|
||||
- 'spec/lib/open_food_network/permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
|
||||
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
|
||||
- 'spec/lib/reports/customers_report_spec.rb'
|
||||
- 'spec/lib/reports/enterprise_fee_summary/authorizer_spec.rb'
|
||||
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
|
||||
- 'spec/lib/reports/order_grouper_spec.rb'
|
||||
- 'spec/lib/reports/orders_and_fulfillment/customer_totals_report_spec.rb'
|
||||
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
|
||||
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
|
||||
- 'spec/models/spree/adjustment_spec.rb'
|
||||
- 'spec/models/spree/credit_card_spec.rb'
|
||||
- 'spec/models/spree/line_item_spec.rb'
|
||||
@@ -659,11 +666,11 @@ Metrics/ParameterLists:
|
||||
Exclude:
|
||||
- 'app/helpers/angular_form_builder.rb'
|
||||
- 'app/models/product_import/entry_processor.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'spec/support/controller_requests_helper.rb'
|
||||
- 'spec/system/admin/reports_spec.rb'
|
||||
|
||||
# Offense count: 8
|
||||
# Offense count: 7
|
||||
# Configuration parameters: IgnoredMethods, Max.
|
||||
Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
@@ -672,9 +679,8 @@ Metrics/PerceivedComplexity:
|
||||
- 'app/models/enterprise_relationship.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/open_food_network/group_buy_report.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
|
||||
# Offense count: 9
|
||||
Naming/AccessorMethodName:
|
||||
@@ -719,7 +725,7 @@ Naming/VariableNumber:
|
||||
- 'app/controllers/spree/orders_controller.rb'
|
||||
- 'app/models/content_configuration.rb'
|
||||
- 'app/models/preference_sections/main_links_section.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
|
||||
- 'lib/spree/core/controller_helpers/common.rb'
|
||||
- 'spec/controllers/spree/admin/search_controller_spec.rb'
|
||||
- 'spec/factories/stock_location_factory.rb'
|
||||
@@ -898,7 +904,7 @@ Rails/LexicallyScopedActionFilter:
|
||||
- 'app/controllers/spree/admin/zones_controller.rb'
|
||||
- 'app/controllers/spree/users_controller.rb'
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 19
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'app/controllers/spree/admin/reports_controller.rb'
|
||||
@@ -1095,9 +1101,9 @@ Style/MissingRespondToMissing:
|
||||
# Offense count: 1
|
||||
Style/MixinUsage:
|
||||
Exclude:
|
||||
- 'lib/open_food_network/orders_and_fulfillments_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/orders_and_fulfillment_report.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Offense count: 3
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: literals, strict
|
||||
@@ -1121,7 +1127,7 @@ Style/NestedModifier:
|
||||
- 'spec/system/admin/payments_stripe_spec.rb'
|
||||
- 'spec/system/admin/reports_spec.rb'
|
||||
|
||||
# Offense count: 25
|
||||
# Offense count: 26
|
||||
# Configuration parameters: AllowedMethods.
|
||||
# AllowedMethods: respond_to_missing?
|
||||
Style/OptionalBooleanParameter:
|
||||
@@ -1135,16 +1141,7 @@ Style/OptionalBooleanParameter:
|
||||
- 'app/models/spree/order_contents.rb'
|
||||
- 'app/models/spree/preferences/file_configuration.rb'
|
||||
- 'app/models/spree/shipment.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
|
||||
- 'lib/open_food_network/customers_report.rb'
|
||||
- 'lib/open_food_network/order_and_distributor_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillments_report.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/products_and_inventory_report_base.rb'
|
||||
- 'lib/open_food_network/users_and_enterprises_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
- 'lib/spree/core/delegate_belongs_to.rb'
|
||||
- 'spec/support/request/web_helper.rb'
|
||||
@@ -1164,11 +1161,10 @@ Style/RedundantReturn:
|
||||
Exclude:
|
||||
- 'app/controllers/spree/admin/shipping_methods_controller.rb'
|
||||
|
||||
# Offense count: 213
|
||||
# Offense count: 205
|
||||
Style/Send:
|
||||
Exclude:
|
||||
- 'app/controllers/split_checkout_controller.rb'
|
||||
- 'engines/order_management/spec/services/order_management/reports/bulk_coop/bulk_coop_report_spec.rb'
|
||||
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
|
||||
- 'spec/controllers/checkout_controller_spec.rb'
|
||||
- 'spec/controllers/payment_gateways/paypal_controller_spec.rb'
|
||||
@@ -1180,13 +1176,10 @@ Style/Send:
|
||||
- 'spec/lib/open_food_network/address_finder_spec.rb'
|
||||
- 'spec/lib/open_food_network/enterprise_fee_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
|
||||
- 'spec/lib/open_food_network/lettuce_share_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/sales_tax_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/xero_invoices_report_spec.rb'
|
||||
- 'spec/lib/reports/xero_invoices_report_spec.rb'
|
||||
- 'spec/lib/stripe/webhook_handler_spec.rb'
|
||||
- 'spec/models/calculator/weight_spec.rb'
|
||||
- 'spec/models/enterprise_spec.rb'
|
||||
@@ -1211,7 +1204,7 @@ Style/SingleArgumentDig:
|
||||
Exclude:
|
||||
- 'app/services/checkout/form_data_adapter.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
Style/SlicingWithRange:
|
||||
Exclude:
|
||||
@@ -1219,9 +1212,8 @@ Style/SlicingWithRange:
|
||||
- 'app/services/embedded_page_service.rb'
|
||||
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'spec/lib/open_food_network/order_grouper_spec.rb'
|
||||
|
||||
# Offense count: 31
|
||||
# Offense count: 28
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: Mode.
|
||||
Style/StringConcatenation:
|
||||
@@ -1238,11 +1230,8 @@ Style/StringConcatenation:
|
||||
- 'app/serializers/api/enterprise_shopfront_list_serializer.rb'
|
||||
- 'app/services/embedded_page_service.rb'
|
||||
- 'app/services/products_renderer.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
|
||||
- 'lib/spree/api/controller_setup.rb'
|
||||
- 'lib/spree/core/environment_extension.rb'
|
||||
- 'spec/lib/open_food_network/order_grouper_spec.rb'
|
||||
- 'spec/models/spree/line_item_spec.rb'
|
||||
- 'spec/models/spree/product_spec.rb'
|
||||
- 'spec/models/spree/variant_spec.rb'
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -23,7 +23,9 @@ RUN apt-get update && apt-get install -y \
|
||||
imagemagick \
|
||||
unzip \
|
||||
libjemalloc-dev \
|
||||
libssl-dev
|
||||
libssl-dev \
|
||||
ca-certificates \
|
||||
gnupg
|
||||
|
||||
# Setup ENV variables
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
|
||||
@@ -40,14 +42,13 @@ RUN git clone --depth 1 https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
|
||||
git clone --depth 1 https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
|
||||
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
|
||||
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install $(cat .ruby-version) && \
|
||||
rbenv global $(cat .ruby-version) && \
|
||||
gem install bundler --version=1.17.3
|
||||
rbenv global $(cat .ruby-version)
|
||||
|
||||
# Install Postgres
|
||||
RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main' > /etc/apt/sources.list.d/pgdg.list" && \
|
||||
wget --quiet -O - https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | apt-key add - && \
|
||||
RUN sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main' >> /etc/apt/sources.list.d/pgdg.list" && \
|
||||
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null && \
|
||||
apt-get update && \
|
||||
apt-get install -yqq --no-install-recommends postgresql-client-9.5 libpq-dev
|
||||
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 - \
|
||||
@@ -68,6 +69,9 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
|
||||
# Copy code and install app dependencies
|
||||
COPY . /usr/src/app/
|
||||
|
||||
# Install Bundler
|
||||
RUN ./script/install-bundler
|
||||
|
||||
# Install front-end dependencies
|
||||
RUN yarn install
|
||||
|
||||
|
||||
7
Gemfile
7
Gemfile
@@ -8,6 +8,11 @@ gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gem
|
||||
|
||||
gem 'rails', '>= 6.1.4'
|
||||
|
||||
# Active Storage
|
||||
gem "active_storage_validations"
|
||||
gem "aws-sdk-s3", require: false
|
||||
gem "image_processing"
|
||||
|
||||
gem 'activemerchant', '>= 1.78.0'
|
||||
gem 'rexml'
|
||||
gem 'angular-rails-templates', '>= 0.3.0'
|
||||
@@ -57,6 +62,7 @@ gem 'devise-token_authenticatable'
|
||||
gem 'jwt', '~> 2.3'
|
||||
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
|
||||
|
||||
gem 'jsonapi-serializer'
|
||||
gem 'pagy', '~> 5.1'
|
||||
|
||||
gem 'rswag-api'
|
||||
@@ -80,7 +86,6 @@ gem 'bootsnap', require: false
|
||||
gem 'geocoder'
|
||||
gem 'gmaps4rails'
|
||||
gem 'mimemagic', '> 0.3.5'
|
||||
gem 'paperclip', '~> 3.4.1'
|
||||
gem 'paper_trail', '~> 12.1.0'
|
||||
gem 'rack-rewrite'
|
||||
gem 'rack-ssl', require: 'rack/ssl'
|
||||
|
||||
47
Gemfile.lock
47
Gemfile.lock
@@ -101,6 +101,11 @@ GEM
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
active_model_serializers (0.8.4)
|
||||
activemodel (>= 3.0)
|
||||
active_storage_validations (0.9.7)
|
||||
activejob (>= 5.2.0)
|
||||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
globalid (>= 0.3.6)
|
||||
@@ -158,11 +163,27 @@ GEM
|
||||
awesome_nested_set (3.4.0)
|
||||
activerecord (>= 4.0.0, < 7.0)
|
||||
awesome_print (1.9.2)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.570.0)
|
||||
aws-sdk (1.67.0)
|
||||
aws-sdk-v1 (= 1.67.0)
|
||||
aws-sdk-core (3.130.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.55.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.113.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sdk-v1 (1.67.0)
|
||||
json (~> 1.4)
|
||||
nokogiri (~> 1)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
axlsx_styler (1.1.0)
|
||||
activesupport (>= 3.1)
|
||||
caxlsx (>= 2.0.2)
|
||||
@@ -198,10 +219,7 @@ GEM
|
||||
rubyzip (>= 1.3.0, < 3)
|
||||
childprocess (4.1.0)
|
||||
chronic (0.10.2)
|
||||
climate_control (0.2.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
coderay (1.1.3)
|
||||
coffee-rails (5.0.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
@@ -331,9 +349,13 @@ GEM
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.9.0)
|
||||
i18n (>= 0.6.6)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
ipaddress (0.8.3)
|
||||
jmespath (1.6.1)
|
||||
jquery-rails (4.4.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
@@ -346,6 +368,8 @@ GEM
|
||||
json_spec (1.1.5)
|
||||
multi_json (~> 1.0)
|
||||
rspec (>= 2.0, < 4.0)
|
||||
jsonapi-serializer (2.2.0)
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.3.0)
|
||||
knapsack (4.0.0)
|
||||
rake
|
||||
@@ -365,12 +389,10 @@ GEM
|
||||
marcel (1.0.2)
|
||||
matrix (0.4.2)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2021.0225)
|
||||
mimemagic (0.4.3)
|
||||
nokogiri (~> 1)
|
||||
rake
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.0)
|
||||
mini_racer (0.4.0)
|
||||
@@ -399,12 +421,6 @@ GEM
|
||||
paper_trail (12.1.0)
|
||||
activerecord (>= 5.2)
|
||||
request_store (~> 1.1)
|
||||
paperclip (3.4.2)
|
||||
activemodel (>= 3.0.0)
|
||||
activerecord (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
cocaine (~> 0.5.0)
|
||||
mime-types
|
||||
parallel (1.21.0)
|
||||
paranoia (2.4.3)
|
||||
activerecord (>= 4.0, < 6.2)
|
||||
@@ -558,6 +574,8 @@ GEM
|
||||
rubocop (>= 1.7.0, < 2.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby-vips (2.1.4)
|
||||
ffi (~> 1.12)
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.2)
|
||||
rufus-scheduler (3.7.0)
|
||||
@@ -681,6 +699,7 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
actionpack-action_caching
|
||||
active_model_serializers (= 0.8.4)
|
||||
active_storage_validations
|
||||
activemerchant (>= 1.78.0)
|
||||
activerecord-import
|
||||
activerecord-postgresql-adapter
|
||||
@@ -695,6 +714,7 @@ DEPENDENCIES
|
||||
awesome_nested_set
|
||||
awesome_print
|
||||
aws-sdk (= 1.67.0)
|
||||
aws-sdk-s3
|
||||
bigdecimal (= 3.0.2)
|
||||
bootsnap
|
||||
bugsnag
|
||||
@@ -734,11 +754,13 @@ DEPENDENCIES
|
||||
hiredis
|
||||
i18n
|
||||
i18n-js (~> 3.9.0)
|
||||
image_processing
|
||||
immigrant
|
||||
jquery-rails (= 4.4.0)
|
||||
jquery-ui-rails (~> 4.2)
|
||||
json
|
||||
json_spec (~> 1.1.4)
|
||||
jsonapi-serializer
|
||||
jwt (~> 2.3)
|
||||
knapsack
|
||||
letter_opener (>= 1.4.1)
|
||||
@@ -751,7 +773,6 @@ DEPENDENCIES
|
||||
order_management!
|
||||
pagy (~> 5.1)
|
||||
paper_trail (~> 12.1.0)
|
||||
paperclip (~> 3.4.1)
|
||||
paranoia (~> 2.4)
|
||||
paypal-sdk-merchant (= 1.117.2)
|
||||
pdf-reader
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
angular.module("ofn.admin").directive "select2NoSearch", ($timeout) ->
|
||||
restrict: 'CA'
|
||||
link: (scope, element, attrs) ->
|
||||
$timeout ->
|
||||
element.select2
|
||||
minimumResultsForSearch: Infinity
|
||||
@@ -9,9 +9,9 @@ angular.module("admin.enterpriseFees").directive 'spreeEnsureCalculatorPreferenc
|
||||
settings = element.parent().parent().find('div.calculator-settings')
|
||||
if value == orig_calculator_type
|
||||
settings.show()
|
||||
settings.find('input').prop 'disabled', false
|
||||
settings.find('input, select').prop 'disabled', false
|
||||
else
|
||||
settings.hide()
|
||||
settings.find('input').prop 'disabled', true
|
||||
settings.find('input, select').prop 'disabled', true
|
||||
return
|
||||
return
|
||||
|
||||
@@ -16,6 +16,7 @@ angular.module("admin.enterprises")
|
||||
{ name: 'shipping_methods', label: t('shipping_methods'), icon_class: "icon-truck", show: "showShippingMethods()" }
|
||||
{ name: 'payment_methods', label: t('payment_methods'), icon_class: "icon-money", show: "showPaymentMethods()" }
|
||||
{ name: 'enterprise_fees', label: t('enterprise_fees'), icon_class: "icon-tasks", show: "showEnterpriseFees()" }
|
||||
{ name: 'enterprise_permissions', label: t('enterprise_permissions'), icon_class: "icon-plug" }
|
||||
{ name: 'inventory_settings', label: t('inventory_settings'), icon_class: "icon-list-ol", show: "enterpriseIsShop()" }
|
||||
{ name: 'tag_rules', label: t('tag_rules'), icon_class: "icon-random", show: "enterpriseIsShop()" }
|
||||
{ name: 'shop_preferences', label: t('shop_preferences'), icon_class: "icon-shopping-cart", show: "enterpriseIsShop()" }
|
||||
@@ -44,3 +45,5 @@ angular.module("admin.enterprises")
|
||||
|
||||
$scope.enterpriseIsShop = ->
|
||||
$scope.Enterprise.sells != "none"
|
||||
|
||||
$scope.menu.redirect_function('enterprise_permissions', '/admin/enterprise_relationships')
|
||||
|
||||
@@ -104,15 +104,45 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
else
|
||||
StatusMessage.display 'failure', t "unsaved_changes_error"
|
||||
|
||||
$scope.cancelOrder = (order, sendEmailCancellation) ->
|
||||
return $http(
|
||||
method: 'GET'
|
||||
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}")
|
||||
|
||||
$scope.deleteLineItem = (lineItem) ->
|
||||
if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
|
||||
LineItems.delete lineItem
|
||||
if lineItem.order.item_count == 1
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
|
||||
if confirm
|
||||
$scope.cancelOrder(lineItem.order, sendEmailCancellation).then(->
|
||||
$scope.refreshData()
|
||||
)
|
||||
else
|
||||
$scope.refreshData()
|
||||
, "js.admin.deleting_item_will_cancel_order")
|
||||
else if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
|
||||
LineItems.delete(lineItem, () -> $scope.refreshData())
|
||||
|
||||
$scope.deleteLineItems = (lineItemsToDelete) ->
|
||||
existingState = $scope.confirmDelete
|
||||
$scope.confirmDelete = false
|
||||
$scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked
|
||||
$scope.confirmDelete = existingState
|
||||
$scope.deleteLineItems = (lineItems) ->
|
||||
lineItemsToDelete = lineItems.filter (item) -> item.checked
|
||||
willCancelOrders = false
|
||||
itemsPerOrder = new Map()
|
||||
for item in lineItemsToDelete
|
||||
{ order } = item
|
||||
if itemsPerOrder.has(order)
|
||||
itemsPerOrder.get(order).push(item)
|
||||
else
|
||||
itemsPerOrder.set(order, [item])
|
||||
willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length)
|
||||
|
||||
if willCancelOrders
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
|
||||
if confirm
|
||||
itemsPerOrder.forEach (items, order) =>
|
||||
if order.item_count == items.length
|
||||
$scope.cancelOrder(order, sendEmailCancellation).then(-> $scope.refreshData())
|
||||
else
|
||||
Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData())
|
||||
, "js.admin.deleting_item_will_cancel_order")
|
||||
|
||||
$scope.allBoxesChecked = ->
|
||||
checkedCount = $scope.filteredLineItems.reduce (count,lineItem) ->
|
||||
@@ -182,9 +212,9 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size") && $scope.selectedUnitsProduct.group_buy_unit_size > 0 &&
|
||||
$scope.selectedUnitsProduct.hasOwnProperty("variant_unit") &&
|
||||
( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" )
|
||||
scale = $scope.getScale($scope.selectedUnitsProduct, $scope.selectedUnitsVariant)
|
||||
sumOfUnitValues = sumOfUnitValues / scale if scale == 28.35 || scale == 453.6 # divide by scale if smallest unit
|
||||
$scope.roundToThreeDecimals(sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size * $scope.selectedUnitsVariant.unit_value)
|
||||
scale = $scope.selectedUnitsProduct.variant_unit_scale
|
||||
sumOfUnitValues = sumOfUnitValues * scale unless scale == 28.35 || scale == 453.6
|
||||
$scope.roundToThreeDecimals(sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size)
|
||||
else
|
||||
''
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels) ->
|
||||
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels, Enterprise) ->
|
||||
OrderCycleResource = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, {
|
||||
'index': { method: 'GET', isArray: true}
|
||||
'new' : { method: 'GET', params: { action_name: "new" } }
|
||||
@@ -50,7 +50,14 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
|
||||
(callback || angular.noop)()
|
||||
|
||||
addDistributor: (new_distributor_id, callback) ->
|
||||
this.order_cycle.outgoing_exchanges.push({ enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] })
|
||||
exchange = { enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] }
|
||||
if (Enterprise.hub_enterprises.length == 1)
|
||||
editable = this.order_cycle["editable_variants_for_outgoing_exchanges"][new_distributor_id] || []
|
||||
variants = this.incomingExchangesVariants()
|
||||
for variant in variants when variant in editable
|
||||
exchange.variants[variant] = true
|
||||
|
||||
this.order_cycle.outgoing_exchanges.push(exchange)
|
||||
$timeout ->
|
||||
(callback || angular.noop)()
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy
|
||||
angular.extend(@byID[orderCycle.id], orderCycle)
|
||||
angular.extend(@pristineByID[orderCycle.id], orderCycle)
|
||||
form.$setPristine() if form?
|
||||
StatusMessage.display('success', "Order cycles have been updated.")
|
||||
StatusMessage.display('success', t('order_cycles_bulk_update_notice'))
|
||||
, (response) =>
|
||||
if response.data.errors?
|
||||
StatusMessage.display('failure', response.data.errors[0])
|
||||
|
||||
@@ -30,3 +30,14 @@ angular.module("admin.side_menu")
|
||||
for item in @items when item.name is name
|
||||
return item
|
||||
null
|
||||
|
||||
redirect_function: (elementID , href) =>
|
||||
window.addEventListener 'load', ->
|
||||
element = document.getElementById(elementID)
|
||||
if !element
|
||||
return
|
||||
element.addEventListener 'click', ->
|
||||
window.location.replace(href)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ $(function() {
|
||||
if (calculator_select.val() === original_calc_type) {
|
||||
$('div.calculator-settings').show();
|
||||
$('.calculator-settings-warning').hide();
|
||||
$('.calculator-settings').find('input,textarea').prop("disabled", false);
|
||||
$('.calculator-settings').find('input,textarea,select').prop("disabled", false);
|
||||
} else {
|
||||
$('div.calculator-settings').hide();
|
||||
$('.calculator-settings-warning').show();
|
||||
$('.calculator-settings').find('input,textarea').prop("disabled", true);
|
||||
$('.calculator-settings').find('input,textarea,select').prop("disabled", true);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
var update_state = function(region) {
|
||||
var country = $('span#' + region + 'country .select2').select2('val');
|
||||
var state_select = $('span#' + region + 'state select.select2');
|
||||
|
||||
$.get(Spree.routes.states_search + "?country_id=" + country, function(states) {
|
||||
state_select.html('');
|
||||
var states_with_blank = [{name: '', id: ''}].concat(states);
|
||||
$.each(states_with_blank, function(pos,state) {
|
||||
var opt = $(document.createElement('option'))
|
||||
.attr('value', state.id)
|
||||
.html(state.name);
|
||||
state_select.append(opt);
|
||||
});
|
||||
state_select.prop("disabled", false).show();
|
||||
state_select.select2();
|
||||
})
|
||||
};
|
||||
@@ -4,6 +4,7 @@ $(document).ready(function() {
|
||||
|
||||
initAlert()
|
||||
initConfirm()
|
||||
initCancelOrder()
|
||||
|
||||
if ($('#variant_autocomplete_template').length > 0) {
|
||||
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
|
||||
@@ -52,12 +53,12 @@ $(document).ready(function() {
|
||||
}
|
||||
toggleItemEdit();
|
||||
|
||||
adjustItems(shipment_number, variant_id, quantity);
|
||||
adjustItems(shipment_number, variant_id, quantity, true);
|
||||
return false;
|
||||
}
|
||||
$('a.save-item').click(handle_save_click);
|
||||
|
||||
handle_delete_click = function(elementSelector){
|
||||
handle_delete_click = function(elementSelector, restock_item){
|
||||
var del = $(elementSelector);
|
||||
del.hide()
|
||||
var shipment_number = del.data('shipment-number');
|
||||
@@ -65,26 +66,37 @@ $(document).ready(function() {
|
||||
|
||||
toggleItemEdit();
|
||||
|
||||
adjustItems(shipment_number, variant_id, 0);
|
||||
adjustItems(shipment_number, variant_id, 0, restock_item);
|
||||
}
|
||||
|
||||
$('a.delete-item').click((event) => {
|
||||
ofnConfirm(() => {
|
||||
handle_delete_click('#custom-confirm');
|
||||
});
|
||||
try {
|
||||
var del = $('a.delete-item');
|
||||
var shipment_number = del.data('shipment-number');
|
||||
var variant_id = del.data('variant-id');
|
||||
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
|
||||
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
|
||||
if (inventory_units.length !== shipment.inventory_units.length) {
|
||||
ofnConfirm((reStockItem) => {
|
||||
handle_delete_click('#custom-confirm', reStockItem);
|
||||
});
|
||||
} else {
|
||||
adjustItems(shipment_number, variant_id, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
adjustItems = function(shipment_number, variant_id, quantity){
|
||||
adjustItems = function(shipment_number, variant_id, quantity, restock_item){
|
||||
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
|
||||
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
|
||||
|
||||
if (quantity == 0 && inventory_units.length == shipment.inventory_units.length) {
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation) => {
|
||||
if (quantity === 0 && inventory_units.length === shipment.inventory_units.length) {
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_item) => {
|
||||
if (confirm) {
|
||||
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, () => {
|
||||
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
|
||||
var redirectTo = new URL(Spree.routes.cancel_order.toString());
|
||||
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
|
||||
window.location.href = redirectTo.toString();
|
||||
@@ -93,23 +105,26 @@ adjustItems = function(shipment_number, variant_id, quantity){
|
||||
});
|
||||
return;
|
||||
}
|
||||
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, () => {
|
||||
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units, callback) {
|
||||
doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units, restock_item, callback) {
|
||||
var url = Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number;
|
||||
|
||||
var new_quantity = 0;
|
||||
var data = { variant_id: variant_id };
|
||||
if (inventory_units.length < quantity) {
|
||||
url += "/add";
|
||||
new_quantity = (quantity - inventory_units.length);
|
||||
} else if (inventory_units.length > quantity) {
|
||||
url += "/remove"
|
||||
new_quantity = (inventory_units.length - quantity);
|
||||
data.restock_item = restock_item;
|
||||
}
|
||||
url += '.json';
|
||||
data.quantity = new_quantity;
|
||||
|
||||
if (new_quantity == 0) {
|
||||
ofnAlert(t("js.admin.orders.quantity_unchanged"));
|
||||
@@ -117,7 +132,7 @@ doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units,
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: Spree.url(url),
|
||||
data: { variant_id: variant_id, quantity: new_quantity }
|
||||
data: data
|
||||
}).done(function( msg ) {
|
||||
callback();
|
||||
});
|
||||
@@ -171,7 +186,7 @@ addVariantFromStockLocation = function() {
|
||||
});
|
||||
}else{
|
||||
//add to existing shipment
|
||||
adjustItems(shipment.number, variant_id, quantity);
|
||||
adjustItems(shipment.number, variant_id, quantity, true);
|
||||
}
|
||||
return 1
|
||||
}
|
||||
@@ -194,16 +209,21 @@ ofnAlert = function(message) {
|
||||
$('#custom-alert').show();
|
||||
}
|
||||
|
||||
ofnCancelOrderAlert = function(callback) {
|
||||
ofnCancelOrderAlert = function(callback, i18nKey) {
|
||||
if (i18nKey == undefined) {
|
||||
i18nKey = "js.admin.orders.cancel_the_order_html";
|
||||
}
|
||||
$('#custom-confirm .message').html(
|
||||
` ${t("js.admin.orders.cancel_the_order_html")}
|
||||
` ${t(i18nKey)}
|
||||
<div class="form">
|
||||
<input type="checkbox" name="send_cancellation_email" value="1" id="send_cancellation_email" />
|
||||
<label for="send_cancellation_email">${t("js.admin.orders.cancel_the_order_send_cancelation_email")}</label>
|
||||
<input type="checkbox" name="send_cancellation_email" value="1" id="send_cancellation_email" checked="true" />
|
||||
<label for="send_cancellation_email">${t("js.admin.orders.cancel_the_order_send_cancelation_email")}</label><br />
|
||||
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
|
||||
<label for="restock_items">${t("js.admin.orders.restock_items")}</label>
|
||||
</div>`);
|
||||
$('#custom-confirm button.confirm').unbind( "click" ).click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
callback(true, $('#send_cancellation_email').is(':checked'));
|
||||
callback(true, $('#send_cancellation_email').is(':checked'), $('#restock_items').is(':checked'));
|
||||
});
|
||||
$('#custom-confirm button.cancel').click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
@@ -213,7 +233,30 @@ ofnCancelOrderAlert = function(callback) {
|
||||
}
|
||||
|
||||
ofnConfirm = function(callback) {
|
||||
$('#custom-confirm .message').html(
|
||||
` ${t("are_you_sure")}
|
||||
<div class="form">
|
||||
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
|
||||
<label for="restock_items">${t("js.admin.orders.restock_item")}</label>
|
||||
</div>`);
|
||||
$('#custom-confirm').data($(event.target).data());
|
||||
$('#custom-confirm button.confirm').click(callback);
|
||||
$('#custom-confirm button.confirm').click(() => {
|
||||
callback($('#restock_items').is(':checked'));
|
||||
});
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
initCancelOrder = function() {
|
||||
$('#cancel_order_form').submit(function(e){
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
|
||||
if (confirm) {
|
||||
var redirectTo = new URL(Spree.routes.cancel_order.toString());
|
||||
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
|
||||
redirectTo.searchParams.append("restock_items", restock_items);
|
||||
window.location.href = redirectTo.toString();
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
angular.module("admin.utils").directive 'helpModal', ($rootScope, $compile, $templateCache, $window, DialogDefaults) ->
|
||||
restrict: 'C'
|
||||
scope:
|
||||
template: '@'
|
||||
link: (scope, element, attr) ->
|
||||
# Compile modal template
|
||||
template = $compile($templateCache.get(scope.template))(scope)
|
||||
|
||||
# Load Dialog Options
|
||||
template.dialog(DialogDefaults)
|
||||
|
||||
# Link opening of dialog to click event on element
|
||||
element.bind 'click', (e) ->
|
||||
template.dialog('open')
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
scope.close = ->
|
||||
template.dialog('close')
|
||||
$rootScope.$evalAsync()
|
||||
return
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart) ->
|
||||
angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart, Shopfront) ->
|
||||
$scope.updateCart = (line_item) ->
|
||||
Cart.adjust($scope.variant.line_item)
|
||||
|
||||
@@ -69,3 +69,6 @@ angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart)
|
||||
$scope.addBulk = (quantity) ->
|
||||
$scope.add(quantity)
|
||||
$modal.open(templateUrl: "bulk_buy_modal.html", scope: $scope, windowClass: "product-bulk-modal")
|
||||
|
||||
$scope.displayRemainingInStock = ->
|
||||
Shopfront.shopfront.preferred_product_low_stock_display && $scope.available() <= 3 && !$scope.variant.line_item.quantity
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
angular.module('Darkswarm').directive "helpModal", ($modal, $compile, $templateCache)->
|
||||
restrict: 'A'
|
||||
scope:
|
||||
helpText: "@helpModal"
|
||||
|
||||
link: (scope, elem, attrs, ctrl)->
|
||||
compiled = $compile($templateCache.get('help-modal.html'))(scope)
|
||||
|
||||
elem.on "click", =>
|
||||
$modal.open(controller: ctrl, template: compiled, scope: scope, windowClass: 'help-modal small')
|
||||
|
||||
scope.$on "$destroy", ->
|
||||
elem.off("click")
|
||||
@@ -3,7 +3,7 @@ angular.module('Darkswarm').directive 'mapSearch', ($timeout, Search) ->
|
||||
restrict: 'E'
|
||||
require: ['^uiGmapGoogleMap', 'ngModel']
|
||||
replace: true
|
||||
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '"></input>'
|
||||
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '" onfocus="this.select()"></input>'
|
||||
scope: {}
|
||||
|
||||
controller: ($scope) ->
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
%div
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.business_address_info.message' | t }}
|
||||
|
||||
.text-center
|
||||
%input.button.red.icon-plus{ type: 'button', value: '{{ "js.admin.modals.got_it" | t }}', ng: { click: 'close()' } }
|
||||
@@ -1,19 +0,0 @@
|
||||
#invite-manager-modal{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}}
|
||||
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
= t('js.admin.modals.invite_title')
|
||||
|
||||
%p.alert-box.ok{ng: {show: 'invite_success'}}
|
||||
{{invite_success}}
|
||||
|
||||
%p.alert-box.error{ng: {show: 'invite_errors'}}
|
||||
{{invite_errors}}
|
||||
|
||||
%input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}}
|
||||
|
||||
.margin-bottom-20.text-center
|
||||
%button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}}
|
||||
= t('js.admin.modals.invite')
|
||||
%button.text-center.margin-top-10{ng: {show: 'invite_success', click: 'resetModal(); close()'}}
|
||||
= t('js.admin.modals.close')
|
||||
@@ -1,25 +0,0 @@
|
||||
#tag-rule-help
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
{{ 'js.admin.modals.tag_rule_help.title' | t }}
|
||||
|
||||
.margin-bottom-30
|
||||
.text-normal
|
||||
{{ 'js.admin.modals.tag_rule_help.overview' | t }}
|
||||
%p
|
||||
{{ 'js.admin.modals.tag_rule_help.overview_text' | t }}
|
||||
|
||||
.margin-bottom-30
|
||||
.text-normal
|
||||
{{ 'js.admin.modals.tag_rule_help.by_default_rules' | t }}
|
||||
%p
|
||||
{{ 'js.admin.modals.tag_rule_help.by_default_rules_text' | t }}
|
||||
|
||||
.margin-bottom-30
|
||||
.text-normal
|
||||
{{ 'js.admin.modals.tag_rule_help.customer_tagged_rules' | t }}
|
||||
%p
|
||||
{{ 'js.admin.modals.tag_rule_help.customer_tagged_rules_text' | t }}
|
||||
|
||||
.text-center
|
||||
%input.button.red.icon-plus{ type: 'button', value: t('js.admin.modals.got_it'), ng: { click: 'close()' } }
|
||||
@@ -1,13 +0,0 @@
|
||||
%div
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
{{ 'js.admin.modals.terms_and_conditions_info.title' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_info.message_1' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_info.message_2' | t }}
|
||||
|
||||
.text-center
|
||||
%input.button.red.icon-plus{ type: 'button', value: t('js.admin.modals.got_it'), ng: { click: 'close()' } }
|
||||
26
app/components/help_modal_component.rb
Normal file
26
app/components/help_modal_component.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class HelpModalComponent < ViewComponent::Base
|
||||
def initialize(id:, close_button: true)
|
||||
@id = id
|
||||
@close_button = close_button
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_button_class
|
||||
if namespace == "admin"
|
||||
"red"
|
||||
else
|
||||
"primary"
|
||||
end
|
||||
end
|
||||
|
||||
def close_button?
|
||||
!!@close_button
|
||||
end
|
||||
|
||||
def namespace
|
||||
helpers.controller_path.split("/").first
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
%div{ id: @id, "data-controller": "help-modal", "data-action": "keyup@document->help-modal#closeIfEscapeKey" }
|
||||
.reveal-modal-bg.fade{ "data-help-modal-target": "background", "data-action": "click->help-modal#close" }
|
||||
.reveal-modal.fade.small.help-modal{ "data-help-modal-target": "modal" }
|
||||
= content
|
||||
|
||||
- if close_button?
|
||||
.text-center
|
||||
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.got_it'), "data-action": "click->help-modal#close" }
|
||||
@@ -0,0 +1,10 @@
|
||||
.help-modal {
|
||||
visibility: visible;
|
||||
position: fixed;
|
||||
top: 3em;
|
||||
}
|
||||
|
||||
/* prevent arrow on selected admin menu item appearing above modal */
|
||||
body.modal-open #admin-menu li.selected a::after {
|
||||
z-index: 0;
|
||||
}
|
||||
@@ -10,14 +10,14 @@ module Admin
|
||||
|
||||
def update
|
||||
params.each do |name, value|
|
||||
if ContentConfig.has_preference?(name) || ContentConfig.has_attachment?(name)
|
||||
ContentConfig.public_send("#{name}=", value)
|
||||
if value.is_a?(ActionDispatch::Http::UploadedFile)
|
||||
blob = store_file(value)
|
||||
update_preference("#{name}_blob_id", blob.id)
|
||||
else
|
||||
update_preference(name, value)
|
||||
end
|
||||
end
|
||||
|
||||
# Save any uploaded images
|
||||
ContentConfig.save
|
||||
|
||||
flash[:success] =
|
||||
t(:successfully_updated, resource: I18n.t('admin.contents.edit.your_content'))
|
||||
|
||||
@@ -26,6 +26,22 @@ module Admin
|
||||
|
||||
private
|
||||
|
||||
def store_file(attachable)
|
||||
ActiveStorage::Blob.create_and_upload!(
|
||||
io: attachable.open,
|
||||
filename: attachable.original_filename,
|
||||
content_type: attachable.content_type,
|
||||
service_name: :local,
|
||||
identify: false,
|
||||
)
|
||||
end
|
||||
|
||||
def update_preference(name, value)
|
||||
return unless ContentConfig.has_preference?(name)
|
||||
|
||||
ContentConfig.public_send("#{name}=", value)
|
||||
end
|
||||
|
||||
def preference_sections
|
||||
[
|
||||
PreferenceSections::HeaderSection.new,
|
||||
|
||||
@@ -234,8 +234,10 @@ module Admin
|
||||
end
|
||||
|
||||
def update_enterprise_notifications
|
||||
if params.key? :receives_notifications
|
||||
@enterprise.update_contact params[:receives_notifications]
|
||||
user_id = params[:receives_notifications].to_i
|
||||
|
||||
if user_id.positive? && @enterprise.user_ids.include?(user_id)
|
||||
@enterprise.update_contact(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ module Admin
|
||||
@order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user)
|
||||
|
||||
if @order_cycle_form.save
|
||||
update_nil_subscription_line_items_price_estimate(@order_cycle)
|
||||
respond_to do |format|
|
||||
flash[:notice] = I18n.t(:order_cycles_update_notice) if params[:reloading] == '1'
|
||||
format.html { redirect_back(fallback_location: root_path) }
|
||||
@@ -76,6 +77,7 @@ module Admin
|
||||
|
||||
def bulk_update
|
||||
if order_cycle_set&.save
|
||||
bulk_update_nil_subscription_line_items_price_estimate
|
||||
render_as_json @order_cycles,
|
||||
ams_prefix: 'index',
|
||||
current_user: spree_current_user,
|
||||
@@ -86,6 +88,27 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_update_nil_subscription_line_items_price_estimate
|
||||
@collection.upcoming.each do |order_cycle|
|
||||
update_nil_subscription_line_items_price_estimate(order_cycle)
|
||||
end
|
||||
end
|
||||
|
||||
def update_nil_subscription_line_items_price_estimate(order_cycle)
|
||||
order_cycle.schedules.each do |schedule|
|
||||
Subscription.where(schedule_id: schedule.id).each do |subscription|
|
||||
shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id)
|
||||
subscription.subscription_line_items.nil_price_estimate.each do |line_item|
|
||||
variant = OrderManagement::Subscriptions::
|
||||
VariantsList.eligible_variants(shop).find_by(id: line_item.variant_id)
|
||||
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
|
||||
price = variant.price + fee_calculator.indexed_fees_for(variant)
|
||||
line_item.update_column(:price_estimate, price)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clone
|
||||
@order_cycle = OrderCycle.find params[:id]
|
||||
@order_cycle.clone!
|
||||
|
||||
@@ -5,14 +5,23 @@ module Admin
|
||||
include ReportsActions
|
||||
helper ReportsHelper
|
||||
|
||||
before_action :authorize_report
|
||||
before_action :authorize_report, only: [:show]
|
||||
|
||||
# Define model class for Can? permissions
|
||||
def model_class
|
||||
Admin::ReportsController
|
||||
end
|
||||
|
||||
def index
|
||||
@reports = reports.select do |report_type, _description|
|
||||
can? report_type, :report
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
render_report && return if ransack_params.blank?
|
||||
@report = report_class.new(spree_current_user, params, request)
|
||||
|
||||
@report = report_class.new(spree_current_user, ransack_params, report_options)
|
||||
|
||||
if export_spreadsheet?
|
||||
if report_format.present?
|
||||
export_report
|
||||
else
|
||||
render_report
|
||||
@@ -22,33 +31,23 @@ module Admin
|
||||
private
|
||||
|
||||
def export_report
|
||||
render report_format.to_sym => @report.public_send("to_#{report_format}"),
|
||||
:filename => report_filename
|
||||
send_data @report.render_as(report_format, controller: self), filename: report_filename
|
||||
end
|
||||
|
||||
def render_report
|
||||
assign_view_data
|
||||
load_form_options
|
||||
|
||||
render report_type
|
||||
render "show"
|
||||
end
|
||||
|
||||
def assign_view_data
|
||||
@report_type = report_type
|
||||
@report_subtype = report_subtype || report_loader.default_report_subtype
|
||||
@report_subtypes = report_class.report_subtypes.map do |subtype|
|
||||
[t("packing.#{subtype}_report", scope: i18n_scope), subtype]
|
||||
end
|
||||
end
|
||||
@report_subtypes = report_subtypes
|
||||
@report_subtype = report_subtype
|
||||
|
||||
def load_form_options
|
||||
return unless form_options_required?
|
||||
# Initialize data
|
||||
params[:display_summary_row] = true if request.get?
|
||||
|
||||
form_options = Reporting::FrontendData.new(spree_current_user)
|
||||
|
||||
@distributors = form_options.distributors.to_a
|
||||
@suppliers = form_options.suppliers.to_a
|
||||
@order_cycles = form_options.order_cycles.to_a
|
||||
@data = Reporting::FrontendData.new(spree_current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ module Api
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
unless @enterprise.public_send("#{attachment_name}?")
|
||||
unless @enterprise.public_send(attachment_name).attached?
|
||||
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message)
|
||||
end
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@ module Api
|
||||
authorize! :update, @enterprise
|
||||
|
||||
if params[:logo] && @enterprise.update( logo: params[:logo] )
|
||||
render html: @enterprise.logo.url(:medium), status: :ok
|
||||
elsif params[:promo] && @enterprise.update( promo_image: params[:promo] )
|
||||
render html: @enterprise.promo_image.url(:medium), status: :ok
|
||||
render(html: @enterprise.logo_url(:medium), status: :ok)
|
||||
elsif params[:promo] && @enterprise.update!( promo_image: params[:promo] )
|
||||
render(html: @enterprise.promo_image_url(:medium), status: :ok)
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
|
||||
@@ -10,7 +10,8 @@ module Api
|
||||
before_action :validate_report, :authorize_report, :validate_query
|
||||
|
||||
def show
|
||||
@report = report_class.new(current_api_user, ransack_params, report_options)
|
||||
params[:report_format] = 'json'
|
||||
@report = report_class.new(current_api_user, params)
|
||||
|
||||
render_report
|
||||
end
|
||||
|
||||
@@ -79,8 +79,9 @@ module Api
|
||||
def remove
|
||||
variant = scoped_variant(params[:variant_id])
|
||||
quantity = params[:quantity].to_i
|
||||
restock_item = params.fetch(:restock_item, "true") == "true"
|
||||
|
||||
@order.contents.remove(variant, quantity, @shipment)
|
||||
@order.contents.remove(variant, quantity, @shipment, restock_item)
|
||||
@shipment.reload if @shipment.persisted?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
|
||||
119
app/controllers/api/v1/base_controller.rb
Normal file
119
app/controllers/api/v1/base_controller.rb
Normal file
@@ -0,0 +1,119 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class BaseController < ActionController::API
|
||||
include CanCan::ControllerAdditions
|
||||
include RequestTimeouts
|
||||
include Pagy::Backend
|
||||
include JsonApiPagination
|
||||
include RaisingParameters
|
||||
|
||||
check_authorization
|
||||
|
||||
attr_accessor :current_api_user
|
||||
|
||||
before_action :authenticate_user
|
||||
before_action :restrict_feature
|
||||
|
||||
rescue_from StandardError, with: :error_during_processing
|
||||
rescue_from CanCan::AccessDenied, with: :unauthorized
|
||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
||||
rescue_from Pagy::VariableError, with: :invalid_pagination
|
||||
rescue_from ActionController::ParameterMissing, with: :missing_parameter
|
||||
rescue_from ActionController::UnpermittedParameters, with: :unpermitted_parameters
|
||||
|
||||
private
|
||||
|
||||
def authenticate_user
|
||||
return if (@current_api_user = request.env['warden'].user)
|
||||
|
||||
if api_key.blank?
|
||||
# An anonymous user
|
||||
@current_api_user = Spree::User.new
|
||||
return
|
||||
end
|
||||
|
||||
return if (@current_api_user = Spree::User.find_by(spree_api_key: api_key.to_s))
|
||||
|
||||
invalid_api_key
|
||||
end
|
||||
|
||||
def restrict_feature
|
||||
not_found unless Flipper.enabled?(:api_v1, @current_api_user)
|
||||
end
|
||||
|
||||
def current_ability
|
||||
Spree::Ability.new(current_api_user)
|
||||
end
|
||||
|
||||
def api_key
|
||||
request.headers["X-Api-Token"] || params[:token]
|
||||
end
|
||||
|
||||
def error_during_processing(exception)
|
||||
Bugsnag.notify(exception)
|
||||
|
||||
if Rails.env.development? || Rails.env.test?
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(exception.message, meta: exception.backtrace)
|
||||
else
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(I18n.t(:unknown_error, scope: "api"))
|
||||
end
|
||||
end
|
||||
|
||||
def invalid_pagination(exception)
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(exception.message)
|
||||
end
|
||||
|
||||
def missing_parameter(error)
|
||||
message = I18n.t(:missing_parameter, param: error.param, scope: :api)
|
||||
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(message)
|
||||
end
|
||||
|
||||
def unpermitted_parameters(error)
|
||||
message = I18n.t(:unpermitted_parameters, params: error.params.join(", "), scope: :api)
|
||||
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(message)
|
||||
end
|
||||
|
||||
def invalid_resource!(resource = nil)
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_invalid(
|
||||
I18n.t(:invalid_resource, scope: "api"),
|
||||
resource&.errors
|
||||
)
|
||||
end
|
||||
|
||||
def invalid_api_key
|
||||
render status: :unauthorized,
|
||||
json: json_api_error(I18n.t(:invalid_api_key, key: api_key, scope: "api"))
|
||||
end
|
||||
|
||||
def unauthorized
|
||||
render status: :unauthorized,
|
||||
json: json_api_error(I18n.t(:unauthorized, scope: "api"))
|
||||
end
|
||||
|
||||
def not_found
|
||||
render status: :not_found,
|
||||
json: json_api_error(I18n.t(:resource_not_found, scope: "api"))
|
||||
end
|
||||
|
||||
def json_api_error(message, **options)
|
||||
{ errors: [{ detail: message }] }.merge(options)
|
||||
end
|
||||
|
||||
def json_api_invalid(message, errors)
|
||||
error_response = { errors: [{ detail: message }] }
|
||||
error_response.merge!(meta: { validation_errors: errors.to_a }) if errors.any?
|
||||
error_response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
136
app/controllers/api/v1/customers_controller.rb
Normal file
136
app/controllers/api/v1/customers_controller.rb
Normal file
@@ -0,0 +1,136 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/permissions'
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class CustomersController < Api::V1::BaseController
|
||||
skip_authorization_check only: :index
|
||||
|
||||
before_action :set_customer, only: [:show, :update, :destroy]
|
||||
before_action :authorize_action, only: [:show, :update, :destroy]
|
||||
|
||||
def index
|
||||
@pagy, customers = pagy(search_customers, pagy_options)
|
||||
|
||||
render json: Api::V1::CustomerSerializer.new(customers, pagination_options)
|
||||
end
|
||||
|
||||
def show
|
||||
render json: Api::V1::CustomerSerializer.new(@customer, include_options)
|
||||
end
|
||||
|
||||
def create
|
||||
authorize! :update, Enterprise.find(customer_params[:enterprise_id])
|
||||
@customer = Customer.new(customer_params)
|
||||
|
||||
if @customer.save
|
||||
render json: Api::V1::CustomerSerializer.new(@customer), status: :created
|
||||
else
|
||||
invalid_resource! @customer
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @customer.update(customer_params)
|
||||
render json: Api::V1::CustomerSerializer.new(@customer)
|
||||
else
|
||||
invalid_resource! @customer
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @customer.destroy
|
||||
render json: Api::V1::CustomerSerializer.new(@customer)
|
||||
else
|
||||
invalid_resource! @customer
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_customer
|
||||
@customer = Customer.find(params[:id])
|
||||
end
|
||||
|
||||
def authorize_action
|
||||
authorize! action_name.to_sym, @customer
|
||||
end
|
||||
|
||||
def search_customers
|
||||
customers = visible_customers.includes(:bill_address, :ship_address)
|
||||
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
|
||||
customers.ransack(params[:q]).result
|
||||
end
|
||||
|
||||
def visible_customers
|
||||
current_api_user.customers.or(
|
||||
Customer.where(enterprise_id: editable_enterprises)
|
||||
)
|
||||
end
|
||||
|
||||
def customer_params
|
||||
attributes = params.require(:customer).permit(
|
||||
:email, :enterprise_id,
|
||||
:code, :first_name, :last_name,
|
||||
:billing_address, shipping_address: [
|
||||
:phone, :latitude, :longitude,
|
||||
:first_name, :last_name,
|
||||
:street_address_1, :street_address_2,
|
||||
:postal_code, :locality, :region, :country,
|
||||
]
|
||||
).to_h
|
||||
|
||||
attributes.merge!(tag_list: params[:tags]) if params.key?(:tags)
|
||||
|
||||
transform_address!(attributes, :billing_address, :bill_address)
|
||||
transform_address!(attributes, :shipping_address, :ship_address)
|
||||
|
||||
attributes
|
||||
end
|
||||
|
||||
def transform_address!(attributes, from, to)
|
||||
return unless attributes.key?(from)
|
||||
|
||||
address = attributes.delete(from)
|
||||
|
||||
if address.nil?
|
||||
attributes[to] = nil
|
||||
return
|
||||
end
|
||||
|
||||
address.transform_keys! do |key|
|
||||
{
|
||||
phone: :phone, latitude: :latitude, longitude: :longitude,
|
||||
first_name: :firstname, last_name: :lastname,
|
||||
street_address_1: :address1, street_address_2: :address2,
|
||||
postal_code: :zipcode,
|
||||
locality: :city,
|
||||
region: :state_name,
|
||||
country: :country,
|
||||
}.with_indifferent_access[key]
|
||||
end
|
||||
|
||||
if address[:state_name].present?
|
||||
address[:state] = Spree::State.find_by(name: address[:state_name])
|
||||
end
|
||||
|
||||
if address[:country].present?
|
||||
address[:country] = Spree::Country.find_by(name: address[:country])
|
||||
end
|
||||
|
||||
attributes["#{to}_attributes"] = address
|
||||
end
|
||||
|
||||
def editable_enterprises
|
||||
OpenFoodNetwork::Permissions.new(current_api_user).editable_enterprises.select(:id)
|
||||
end
|
||||
|
||||
def include_options
|
||||
fields = [params.fetch(:include, [])].flatten
|
||||
|
||||
{ include: fields.map(&:to_s) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -29,6 +29,7 @@ class ApplicationController < ActionController::Base
|
||||
helper 'footer_links'
|
||||
helper 'discourse'
|
||||
helper 'checkout'
|
||||
helper 'link'
|
||||
helper 'terms_and_conditions'
|
||||
|
||||
protect_from_forgery
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
module CheckoutCallbacks
|
||||
extend ActiveSupport::Concern
|
||||
include EnterprisesHelper
|
||||
|
||||
included do
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
@@ -46,10 +47,7 @@ module CheckoutCallbacks
|
||||
end
|
||||
|
||||
def load_shipping_methods
|
||||
@shipping_methods = Spree::ShippingMethod.
|
||||
for_distributor(@order.distributor).
|
||||
display_on_checkout.
|
||||
order(:name)
|
||||
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
def redirect_to_shop?
|
||||
|
||||
74
app/controllers/concerns/json_api_pagination.rb
Normal file
74
app/controllers/concerns/json_api_pagination.rb
Normal file
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module JsonApiPagination
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
DEFAULT_PER_PAGE = 50
|
||||
MAX_PER_PAGE = 200
|
||||
|
||||
def pagination_options
|
||||
{
|
||||
is_collection: true,
|
||||
meta: meta_options,
|
||||
links: links_options,
|
||||
}
|
||||
end
|
||||
|
||||
def pagy_options
|
||||
{ items: final_per_page_value }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def meta_options
|
||||
{
|
||||
pagination: {
|
||||
results: @pagy.count,
|
||||
pages: total_pages,
|
||||
page: current_page,
|
||||
per_page: final_per_page_value
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def links_options
|
||||
{
|
||||
self: pagination_url(current_page),
|
||||
first: pagination_url(1),
|
||||
prev: pagination_url(previous_page),
|
||||
next: pagination_url(next_page),
|
||||
last: pagination_url(total_pages)
|
||||
}
|
||||
end
|
||||
|
||||
def pagination_url(page_number)
|
||||
return if page_number.nil?
|
||||
|
||||
url_for(only_path: false, params: request.query_parameters.merge(page: page_number))
|
||||
end
|
||||
|
||||
# User-specified value, or DEFAULT_PER_PAGE, capped at MAX_PER_PAGE
|
||||
def final_per_page_value
|
||||
(params[:per_page] || DEFAULT_PER_PAGE).to_i.clamp(1, MAX_PER_PAGE)
|
||||
end
|
||||
|
||||
def current_page
|
||||
(params[:page] || 1).to_i
|
||||
end
|
||||
|
||||
def total_pages
|
||||
@pagy.pages
|
||||
end
|
||||
|
||||
def previous_page
|
||||
return nil if current_page < 2
|
||||
|
||||
current_page - 1
|
||||
end
|
||||
|
||||
def next_page
|
||||
return nil if current_page >= total_pages
|
||||
|
||||
current_page + 1
|
||||
end
|
||||
end
|
||||
23
app/controllers/concerns/raising_parameters.rb
Normal file
23
app/controllers/concerns/raising_parameters.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# The API uses strict parameter checking.
|
||||
#
|
||||
# We want to raise errors when unused or unpermitted parameters are given
|
||||
# to the API. You then know straight away when a parameter isn't used.
|
||||
module RaisingParameters
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# ActionController manages this config on a per-class basis. The subclass
|
||||
# enables us to raise errors only here and not in the rest of the app.
|
||||
class Parameters < ActionController::Parameters
|
||||
def self.action_on_unpermitted_parameters
|
||||
:raise
|
||||
end
|
||||
end
|
||||
|
||||
# We override the params method so that we always use the strict parameters.
|
||||
# We could rename this method if we need access to the orginal as well.
|
||||
def params
|
||||
Parameters.new(super.to_unsafe_hash)
|
||||
end
|
||||
end
|
||||
@@ -3,10 +3,14 @@
|
||||
module ReportsActions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def reports
|
||||
Reporting::Reports::List.all
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authorize_report
|
||||
authorize! report_type&.to_sym, :report
|
||||
authorize! report_type.to_sym, :report
|
||||
end
|
||||
|
||||
def report_class
|
||||
@@ -23,31 +27,26 @@ module ReportsActions
|
||||
params[:report_type]
|
||||
end
|
||||
|
||||
def report_subtypes
|
||||
reports[report_type.to_sym] || []
|
||||
end
|
||||
|
||||
def report_subtypes_codes
|
||||
report_subtypes.map(&:second).map(&:to_s)
|
||||
end
|
||||
|
||||
def report_subtype
|
||||
params[:report_subtype]
|
||||
params[:report_subtype] || report_subtypes_codes.first
|
||||
end
|
||||
|
||||
def ransack_params
|
||||
raw_params[:q]
|
||||
end
|
||||
|
||||
def report_options
|
||||
raw_params[:options]
|
||||
end
|
||||
|
||||
def report_format
|
||||
params[:report_format]
|
||||
end
|
||||
|
||||
def export_spreadsheet?
|
||||
['xlsx', 'ods', 'csv'].include?(report_format)
|
||||
end
|
||||
|
||||
def form_options_required?
|
||||
[:packing, :customers, :products_and_inventory, :order_cycle_management].
|
||||
include? report_type.to_sym
|
||||
end
|
||||
|
||||
def report_filename
|
||||
"#{report_type || action_name}_#{file_timestamp}.#{report_format}"
|
||||
end
|
||||
|
||||
@@ -14,6 +14,7 @@ class SplitCheckoutController < ::BaseController
|
||||
helper 'terms_and_conditions'
|
||||
helper 'checkout'
|
||||
helper 'spree/orders'
|
||||
helper EnterprisesHelper
|
||||
helper OrderHelper
|
||||
|
||||
before_action :set_checkout_redirect
|
||||
@@ -30,12 +31,16 @@ class SplitCheckoutController < ::BaseController
|
||||
advance_order_state
|
||||
redirect_to_step
|
||||
else
|
||||
flash.now[:error] = I18n.t('split_checkout.errors.global')
|
||||
flash.now[:error] ||= I18n.t('split_checkout.errors.global')
|
||||
|
||||
render status: :unprocessable_entity, operations: cable_car.
|
||||
replace("#checkout", partial("split_checkout/checkout")).
|
||||
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
|
||||
end
|
||||
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))
|
||||
end
|
||||
|
||||
private
|
||||
@@ -127,7 +132,7 @@ class SplitCheckoutController < ::BaseController
|
||||
when "confirmation"
|
||||
redirect_to checkout_step_path(:summary)
|
||||
else
|
||||
redirect_to order_path(@order)
|
||||
redirect_to order_path(@order, order_token: @order.token)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ module Spree
|
||||
|
||||
def fire
|
||||
event = params[:e]
|
||||
@order.send_cancellation_email = params[:send_cancellation_email] == "true"
|
||||
@order.send_cancellation_email = params[:send_cancellation_email] != "false"
|
||||
@order.restock_items = params.fetch(:restock_items, "true") == "true"
|
||||
|
||||
if @order.public_send(event.to_s)
|
||||
flash[:success] = Spree.t(:order_updated)
|
||||
else
|
||||
|
||||
@@ -39,9 +39,6 @@ module Spree
|
||||
render :new
|
||||
end
|
||||
end
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
@object.errors.add(:base, t('spree.admin.products.image_upload_error'))
|
||||
respond_with(@object)
|
||||
end
|
||||
|
||||
def show
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
require 'open_food_network/reports/list'
|
||||
require 'open_food_network/order_and_distributor_report'
|
||||
require 'open_food_network/products_and_inventory_report'
|
||||
require 'open_food_network/lettuce_share_report'
|
||||
require 'open_food_network/group_buy_report'
|
||||
require 'open_food_network/order_grouper'
|
||||
require 'open_food_network/customers_report'
|
||||
require 'open_food_network/users_and_enterprises_report'
|
||||
require 'open_food_network/order_cycle_management_report'
|
||||
require 'open_food_network/sales_tax_report'
|
||||
require 'open_food_network/xero_invoices_report'
|
||||
require 'open_food_network/payments_report'
|
||||
require 'open_food_network/orders_and_fulfillments_report'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
class ReportsController < Spree::Admin::BaseController
|
||||
include Spree::ReportsHelper
|
||||
helper ::ReportsHelper
|
||||
|
||||
ORDER_MANAGEMENT_ENGINE_REPORTS = [
|
||||
:bulk_coop,
|
||||
:enterprise_fee_summary
|
||||
].freeze
|
||||
|
||||
helper_method :render_content?
|
||||
|
||||
before_action :cache_search_state
|
||||
# Fetches user's distributors, suppliers and order_cycles
|
||||
before_action :load_basic_data, only: [:customers, :products_and_inventory, :order_cycle_management]
|
||||
before_action :load_associated_data, only: [:orders_and_fulfillment]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def report_types
|
||||
OpenFoodNetwork::Reports::List.all
|
||||
end
|
||||
|
||||
def index
|
||||
@reports = authorized_reports
|
||||
respond_with(@reports)
|
||||
end
|
||||
|
||||
def customers
|
||||
@report_types = report_types[:customers]
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, raw_params,
|
||||
render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def order_cycle_management
|
||||
raw_params[:q] ||= {}
|
||||
|
||||
@report_types = report_types[:order_cycle_management]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
|
||||
raw_params,
|
||||
render_content?
|
||||
@table = @report.table_items
|
||||
|
||||
render_report(@report.header, @table, params[:csv],
|
||||
"order_cycle_management_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def orders_and_distributors
|
||||
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
|
||||
raw_params,
|
||||
render_content?
|
||||
@search = @report.search
|
||||
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
|
||||
render_report(@report.header, @report.table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def sales_tax
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, raw_params,
|
||||
render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
|
||||
end
|
||||
|
||||
def payments
|
||||
# -- Prepare Form Options
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, raw_params,
|
||||
render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "payments_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def orders_and_fulfillment
|
||||
raw_params[:q] ||= orders_and_fulfillment_default_filters
|
||||
|
||||
@report_types = report_types[:orders_and_fulfillment]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
@include_blank = I18n.t(:all)
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
|
||||
raw_params,
|
||||
render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def products_and_inventory
|
||||
@report_types = report_types[:products_and_inventory]
|
||||
@report = if params[:report_type] != 'lettuce_share'
|
||||
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
|
||||
raw_params,
|
||||
render_content?
|
||||
else
|
||||
OpenFoodNetwork::LettuceShareReport.new spree_current_user,
|
||||
raw_params,
|
||||
render_content?
|
||||
end
|
||||
|
||||
render_report @report.header,
|
||||
@report.table,
|
||||
params[:csv],
|
||||
"products_and_inventory_#{timestamp}.csv"
|
||||
end
|
||||
|
||||
def users_and_enterprises
|
||||
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new raw_params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv],
|
||||
"users_and_enterprises_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def xero_invoices
|
||||
raw_params[:q] ||= {}
|
||||
|
||||
@distributors = my_distributors
|
||||
@order_cycles = my_order_cycles
|
||||
|
||||
@report = OpenFoodNetwork::XeroInvoicesReport.new(spree_current_user,
|
||||
raw_params,
|
||||
render_content?)
|
||||
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_class
|
||||
Spree::Admin::ReportsController
|
||||
end
|
||||
|
||||
# Some actions are changing the `params` object. That is unfortunate Spree
|
||||
# behavior and we are building on it. So we have to look at `params` early
|
||||
# to check if we are searching or just displaying a report search form.
|
||||
def cache_search_state
|
||||
search_keys = [
|
||||
# search parameter for ransack
|
||||
:q,
|
||||
# common in all reports, only set for CSV rendering
|
||||
:csv,
|
||||
# `button` is included in all forms. It's not important for searching,
|
||||
# but the Users & Enterprises report doesn't have any other parameter
|
||||
# for an empty search. So we use this one to display data.
|
||||
:button,
|
||||
# Some reports use filtering by enterprise or order cycle
|
||||
:distributor_id,
|
||||
:supplier_id,
|
||||
:order_cycle_id,
|
||||
# Xero Invoices can be filtered by date
|
||||
:invoice_date,
|
||||
:due_date
|
||||
]
|
||||
@searching = search_keys.any? { |key| raw_params.key? key }
|
||||
end
|
||||
|
||||
# We don't want to render data unless search params are supplied.
|
||||
# Compiling data can take a long time.
|
||||
def render_content?
|
||||
@searching
|
||||
end
|
||||
|
||||
def render_report(header, table, create_csv, csv_file_name)
|
||||
send_data csv_report(header, table), filename: csv_file_name if create_csv
|
||||
@header = header
|
||||
@table = table
|
||||
# Rendering HTML is the default.
|
||||
end
|
||||
|
||||
def load_associated_data
|
||||
form_options = Reporting::FrontendData.new(spree_current_user)
|
||||
|
||||
@distributors = form_options.distributors
|
||||
@suppliers = form_options.suppliers
|
||||
@order_cycles = form_options.order_cycles
|
||||
end
|
||||
|
||||
def csv_report(header, table)
|
||||
CSV.generate do |csv|
|
||||
csv << header
|
||||
table.each { |row| csv << row }
|
||||
end
|
||||
end
|
||||
|
||||
def load_basic_data
|
||||
@distributors = my_distributors
|
||||
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
|
||||
@order_cycles = my_order_cycles
|
||||
end
|
||||
|
||||
# Load managed distributor enterprises of current user
|
||||
def my_distributors
|
||||
Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
# Load managed producer enterprises of current user
|
||||
def my_suppliers
|
||||
Enterprise.is_primary_producer.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
def suppliers_of_products_distributed_by(distributors)
|
||||
supplier_ids = Spree::Product.in_distributors(distributors.select('enterprises.id')).
|
||||
select('spree_products.supplier_id')
|
||||
|
||||
Enterprise.where(id: supplier_ids)
|
||||
end
|
||||
|
||||
# Load order cycles the current user has access to
|
||||
def my_order_cycles
|
||||
OrderCycle.
|
||||
active_or_complete.
|
||||
visible_by(spree_current_user).
|
||||
order('orders_close_at DESC')
|
||||
end
|
||||
|
||||
def order_grouper_table
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns, @report
|
||||
order_grouper.table(@report.table_items)
|
||||
end
|
||||
|
||||
def authorized_reports
|
||||
all_reports = [
|
||||
:orders_and_distributors,
|
||||
:bulk_coop,
|
||||
:payments,
|
||||
:orders_and_fulfillment,
|
||||
:customers,
|
||||
:products_and_inventory,
|
||||
:users_and_enterprises,
|
||||
:enterprise_fee_summary,
|
||||
:order_cycle_management,
|
||||
:sales_tax,
|
||||
:xero_invoices,
|
||||
:packing
|
||||
]
|
||||
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
|
||||
reports.map { |report| [report, describe_report(report)] }.to_h
|
||||
end
|
||||
|
||||
def describe_report(report)
|
||||
name = I18n.t(:name, scope: [:admin, :reports, report])
|
||||
description = begin
|
||||
I18n.t!(:description, scope: [:admin, :reports, report])
|
||||
rescue I18n::MissingTranslationData
|
||||
render_to_string(
|
||||
partial: "#{report}_description",
|
||||
layout: false,
|
||||
locals: { report_types: report_types[report] }
|
||||
).html_safe
|
||||
end
|
||||
{ name: name, url: url_for_report(report), description: description }
|
||||
end
|
||||
|
||||
def url_for_report(report)
|
||||
if report_in_order_management_engine?(report)
|
||||
main_app.public_send("new_order_management_reports_#{report}_url".to_sym)
|
||||
else
|
||||
spree.public_send("#{report}_admin_reports_url".to_sym)
|
||||
end
|
||||
rescue NoMethodError
|
||||
main_app.admin_reports_url(report_type: report)
|
||||
end
|
||||
|
||||
# List of reports that have been moved to the Order Management engine
|
||||
def report_in_order_management_engine?(report)
|
||||
ORDER_MANAGEMENT_ENGINE_REPORTS.include?(report)
|
||||
end
|
||||
|
||||
def timestamp
|
||||
Time.zone.now.strftime("%Y%m%d")
|
||||
end
|
||||
|
||||
def orders_and_fulfillment_default_filters
|
||||
now = Time.zone.now
|
||||
{ completed_at_gt: (now - 1.month).beginning_of_day,
|
||||
completed_at_lt: (now + 1.day).beginning_of_day }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -13,6 +13,7 @@ module Spree
|
||||
|
||||
helper 'spree/base'
|
||||
|
||||
prepend_before_action :handle_unconfirmed_email
|
||||
before_action :set_checkout_redirect, only: :create
|
||||
after_action :ensure_valid_locale_persisted, only: :create
|
||||
|
||||
@@ -49,6 +50,22 @@ module Spree
|
||||
Spree.t(:login)
|
||||
end
|
||||
|
||||
def handle_unconfirmed_email
|
||||
render_unconfirmed_response if email_unconfirmed?
|
||||
end
|
||||
|
||||
def email_unconfirmed?
|
||||
Spree::User.where(email: params.dig(:spree_user, :email), confirmed_at: nil).exists?
|
||||
end
|
||||
|
||||
def render_unconfirmed_response
|
||||
render status: :unprocessable_entity, operations: cable_car.inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert", locals: { type: "alert", message: t(:email_unconfirmed),
|
||||
unconfirmed: true, tab: "login" })
|
||||
)
|
||||
end
|
||||
|
||||
def ensure_valid_locale_persisted
|
||||
# When creating a new user session we have to wait until after a successful
|
||||
# login to be able to persist a selected locale on the current user
|
||||
|
||||
@@ -62,8 +62,7 @@ module Spree
|
||||
if params[:user][:password].present?
|
||||
# this logic needed b/c devise wants to log us out after password changes
|
||||
Spree::User.reset_password_by_token(params[:user])
|
||||
sign_in(@user, event: :authentication,
|
||||
bypass: true)
|
||||
bypass_sign_in(@user)
|
||||
end
|
||||
redirect_to spree.account_url, notice: Spree.t(:account_updated)
|
||||
else
|
||||
|
||||
@@ -23,7 +23,7 @@ class UserConfirmationsController < DeviseController
|
||||
end
|
||||
else
|
||||
render operations: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
"##{params[:tab] || 'forgot'}-feedback",
|
||||
partial("layouts/alert", locals: { type: "success", message: t("devise.confirmations.send_instructions") })
|
||||
)
|
||||
return
|
||||
|
||||
@@ -29,7 +29,8 @@ class UserPasswordsController < Spree::UserPasswordsController
|
||||
render status: :unprocessable_entity, operations: cable_car.inner_html(
|
||||
"#forgot-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t(:email_unconfirmed), unconfirmed: true })
|
||||
locals: { type: "alert", message: t(:email_unconfirmed),
|
||||
unconfirmed: true, tab: "forgot" })
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -27,6 +27,17 @@ module ApplicationHelper
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(feature, user)
|
||||
end
|
||||
|
||||
def language_meta_tags
|
||||
return if I18n.available_locales.one?
|
||||
|
||||
I18n.available_locales.map do |locale|
|
||||
tag.link(
|
||||
hreflang: locale.to_s.gsub("_", "-").downcase,
|
||||
href: "#{request.protocol}#{request.host_with_port}/locales/#{locale}"
|
||||
)
|
||||
end.join("\n").html_safe
|
||||
end
|
||||
|
||||
def ng_form_for(name, *args, &block)
|
||||
options = args.extract_options!
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module GroupsHelper
|
||||
module LinkHelper
|
||||
def link_to_service(baseurl, name, html_options = {}, &block)
|
||||
return if name.blank?
|
||||
|
||||
@@ -15,8 +15,4 @@ module GroupsHelper
|
||||
prefix + url
|
||||
end
|
||||
end
|
||||
|
||||
def strip_url(url)
|
||||
url&.sub(%r{^https?://}i, '')
|
||||
end
|
||||
end
|
||||
@@ -9,7 +9,24 @@ module ReportsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def report_subtypes(report)
|
||||
Reporting::ReportLoader.new(report).report_subtypes
|
||||
def report_payment_method_options(orders)
|
||||
orders.map do |order|
|
||||
payment_method = order.payments.last&.payment_method
|
||||
|
||||
next unless payment_method
|
||||
|
||||
[payment_method.name, payment_method.id]
|
||||
end.compact.uniq
|
||||
end
|
||||
|
||||
def report_shipping_method_options(orders)
|
||||
orders.map do |o|
|
||||
sm = o.shipping_method
|
||||
[sm&.name, sm&.id]
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def currency_symbol
|
||||
Spree::Money.currency_symbol
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ module Spree
|
||||
klass = EnterpriseGroup if klass == :group
|
||||
klass = VariantOverride if klass == :Inventory
|
||||
klass = ProductImport::ProductImporter if klass == :import
|
||||
klass = Spree::Admin::ReportsController if klass == :report
|
||||
klass = ::Admin::ReportsController if klass == :report
|
||||
klass
|
||||
end
|
||||
|
||||
@@ -115,7 +115,7 @@ module Spree
|
||||
if html_options[:method] &&
|
||||
html_options[:method].to_s.downcase != 'get' &&
|
||||
!html_options[:remote]
|
||||
form_tag(url, method: html_options.delete(:method)) do
|
||||
form_tag(url, method: html_options.delete(:method), id: html_options.delete(:form_id)) do
|
||||
button(text, html_options.delete(:icon), nil, html_options)
|
||||
end
|
||||
else
|
||||
|
||||
@@ -5,8 +5,8 @@ module Spree
|
||||
module OrdersHelper
|
||||
def event_links
|
||||
links = []
|
||||
links << event_link("cancel") if @order.can_cancel?
|
||||
links << event_link("resume") if @order.can_resume?
|
||||
links << cancel_event_link if @order.can_cancel?
|
||||
links << resume_event_link if @order.can_resume?
|
||||
links.join(' ').html_safe
|
||||
end
|
||||
|
||||
@@ -114,12 +114,19 @@ module Spree
|
||||
confirm: t(:are_you_sure) }
|
||||
end
|
||||
|
||||
def event_link(event)
|
||||
event_label = I18n.t(event, scope: "actions")
|
||||
def cancel_event_link
|
||||
event_label = I18n.t("cancel", scope: "actions")
|
||||
button_link_to(event_label,
|
||||
fire_admin_order_url(@order, e: "cancel"),
|
||||
method: :put, icon: "icon-cancel", form_id: "cancel_order_form")
|
||||
end
|
||||
|
||||
def resume_event_link
|
||||
event_label = I18n.t("resume", scope: "actions")
|
||||
confirm_message = I18n.t("admin.orders.edit.order_sure_want_to", event: event_label)
|
||||
button_link_to(event_label,
|
||||
fire_admin_order_url(@order, e: event),
|
||||
method: :put, icon: "icon-#{event}",
|
||||
fire_admin_order_url(@order, e: "resume"),
|
||||
method: :put, icon: "icon-resume",
|
||||
data: { confirm: confirm_message })
|
||||
end
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spree/money'
|
||||
|
||||
module Spree
|
||||
module ReportsHelper
|
||||
def report_payment_method_options(orders)
|
||||
orders.map do |order|
|
||||
payment_method = order.payments.last&.payment_method
|
||||
|
||||
next unless payment_method
|
||||
|
||||
[payment_method.name, payment_method.id]
|
||||
end.compact.uniq
|
||||
end
|
||||
|
||||
def report_shipping_method_options(orders)
|
||||
orders.map do |o|
|
||||
sm = o.shipping_method
|
||||
[sm&.name, sm&.id]
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def xero_report_types
|
||||
[[I18n.t(:summary), 'summary'],
|
||||
[I18n.t(:detailed), 'detailed']]
|
||||
end
|
||||
|
||||
def currency_symbol
|
||||
Spree::Money.currency_symbol
|
||||
end
|
||||
end
|
||||
end
|
||||
64
app/json_schemas/customer_schema.rb
Normal file
64
app/json_schemas/customer_schema.rb
Normal file
@@ -0,0 +1,64 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CustomerSchema < JsonApiSchema
|
||||
def self.object_name
|
||||
"customer"
|
||||
end
|
||||
|
||||
def self.attributes
|
||||
{
|
||||
id: { type: :integer, example: 1 },
|
||||
enterprise_id: { type: :integer, example: 2 },
|
||||
first_name: { type: :string, nullable: true, example: "Alice" },
|
||||
last_name: { type: :string, nullable: true, example: "Springs" },
|
||||
code: { type: :string, nullable: true, example: "BUYER1" },
|
||||
email: { type: :string, example: "alice@example.com" },
|
||||
allow_charges: { type: :boolean, example: false },
|
||||
tags: { type: :array, items: { type: :string }, example: ["staff", "discount"] },
|
||||
terms_and_conditions_accepted_at: {
|
||||
type: :string, format: "date-time", nullable: true,
|
||||
example: "2022-03-12T15:55:00.000+11:00",
|
||||
},
|
||||
billing_address: {
|
||||
type: :object, nullable: true,
|
||||
example: nil,
|
||||
},
|
||||
shipping_address: {
|
||||
type: :object, nullable: true,
|
||||
example: address_example,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def self.address_example
|
||||
{
|
||||
phone: "0404 333 222 111",
|
||||
latitude: -37.817375100000,
|
||||
longitude: 144.964803195704,
|
||||
first_name: "Alice",
|
||||
last_name: "Springs",
|
||||
street_address_1: "1 Flinders Street",
|
||||
street_address_2: "",
|
||||
postal_code: "1234",
|
||||
locality: "Melbourne",
|
||||
region: "Victoria",
|
||||
country: "Australia",
|
||||
}
|
||||
end
|
||||
|
||||
def self.required_attributes
|
||||
[:enterprise_id, :email]
|
||||
end
|
||||
|
||||
def self.writable_attributes
|
||||
attributes.except(
|
||||
:id,
|
||||
:allow_charges,
|
||||
:terms_and_conditions_accepted_at,
|
||||
)
|
||||
end
|
||||
|
||||
def self.relationships
|
||||
[:enterprise]
|
||||
end
|
||||
end
|
||||
24
app/json_schemas/errors_schema.rb
Normal file
24
app/json_schemas/errors_schema.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ErrorsSchema
|
||||
def self.schema
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
errors: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: {
|
||||
title: { type: :string },
|
||||
detail: { type: :string },
|
||||
source: { type: :object }
|
||||
},
|
||||
required: [:detail]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:errors]
|
||||
}
|
||||
end
|
||||
end
|
||||
114
app/json_schemas/json_api_schema.rb
Normal file
114
app/json_schemas/json_api_schema.rb
Normal file
@@ -0,0 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class JsonApiSchema
|
||||
class << self
|
||||
def attributes
|
||||
{}
|
||||
end
|
||||
|
||||
def required_attributes
|
||||
[]
|
||||
end
|
||||
|
||||
def relationships
|
||||
[]
|
||||
end
|
||||
|
||||
def all_attributes
|
||||
attributes.keys
|
||||
end
|
||||
|
||||
def schema(options = {})
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
},
|
||||
meta: { type: :object },
|
||||
links: { type: :object }
|
||||
},
|
||||
required: [:data]
|
||||
}
|
||||
end
|
||||
|
||||
def collection(options)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
type: :object,
|
||||
properties: {
|
||||
pagination: {
|
||||
type: :object,
|
||||
properties: {
|
||||
results: { type: :integer, example: 250 },
|
||||
pages: { type: :integer, example: 5 },
|
||||
page: { type: :integer, example: 2 },
|
||||
per_page: { type: :integer, example: 50 },
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:pagination]
|
||||
},
|
||||
links: {
|
||||
type: :object,
|
||||
properties: {
|
||||
self: { type: :string },
|
||||
first: { type: :string },
|
||||
prev: { type: :string, nullable: true },
|
||||
next: { type: :string, nullable: true },
|
||||
last: { type: :string }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:data, :meta, :links]
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def data_properties(require_all: false)
|
||||
required = require_all ? all_attributes : required_attributes
|
||||
|
||||
{
|
||||
id: { type: :string, example: "1" },
|
||||
type: { type: :string, example: object_name },
|
||||
attributes: {
|
||||
type: :object,
|
||||
properties: attributes,
|
||||
required: required
|
||||
},
|
||||
relationships: {
|
||||
type: :object,
|
||||
properties: relationships.to_h do |name|
|
||||
[
|
||||
name,
|
||||
relationship_schema(name)
|
||||
]
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def relationship_schema(name)
|
||||
if is_singular?(name)
|
||||
RelationshipSchema.schema(name)
|
||||
else
|
||||
RelationshipSchema.collection(name)
|
||||
end
|
||||
end
|
||||
|
||||
def is_singular?(name)
|
||||
name.to_s.singularize == name.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
48
app/json_schemas/relationship_schema.rb
Normal file
48
app/json_schemas/relationship_schema.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RelationshipSchema
|
||||
def self.schema(resource_name = nil)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :object,
|
||||
properties: {
|
||||
id: { type: :string },
|
||||
type: { type: :string, example: resource_name }
|
||||
}
|
||||
},
|
||||
links: {
|
||||
type: :object,
|
||||
properties: {
|
||||
related: { type: :string }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def self.collection(resource_name = nil)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: {
|
||||
id: { type: :string },
|
||||
type: { type: :string, example: resource_name }
|
||||
}
|
||||
}
|
||||
},
|
||||
links: {
|
||||
type: :object,
|
||||
properties: {
|
||||
related: { type: :string }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
43
app/models/concerns/file_preferences.rb
Normal file
43
app/models/concerns/file_preferences.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FilePreferences
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
@default_urls = {}
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def file_preference(name, default_url: nil)
|
||||
preference "#{name}_blob_id", :integer
|
||||
@default_urls[name] = default_url if default_url
|
||||
end
|
||||
|
||||
def default_url(name)
|
||||
@default_urls[name]
|
||||
end
|
||||
end
|
||||
|
||||
def preference_type(key)
|
||||
if has_preference?("#{key}_blob_id")
|
||||
:file
|
||||
else
|
||||
super(key)
|
||||
end
|
||||
end
|
||||
|
||||
def url_for(name)
|
||||
blob = blob_for(name)
|
||||
|
||||
if blob
|
||||
Rails.application.routes.url_helpers.url_for(blob)
|
||||
else
|
||||
self.class.default_url(name)
|
||||
end
|
||||
end
|
||||
|
||||
def blob_for(name)
|
||||
blob_id = get_preference("#{name}_blob_id")
|
||||
ActiveStorage::Blob.find_by(id: blob_id) if blob_id
|
||||
end
|
||||
end
|
||||
@@ -1,23 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/paperclippable'
|
||||
|
||||
class ContentConfiguration < Spree::Preferences::FileConfiguration
|
||||
include OpenFoodNetwork::Paperclippable
|
||||
class ContentConfiguration < Spree::Preferences::Configuration
|
||||
include FilePreferences
|
||||
|
||||
# Header
|
||||
preference :logo, :file
|
||||
preference :logo_mobile, :file
|
||||
preference :logo_mobile_svg, :file
|
||||
has_attached_file :logo, default_url: "/default_images/ofn-logo.png"
|
||||
has_attached_file :logo_mobile
|
||||
has_attached_file :logo_mobile_svg, default_url: "/default_images/ofn-logo-mobile.svg"
|
||||
file_preference :logo, default_url: "/default_images/ofn-logo.png"
|
||||
file_preference :logo_mobile
|
||||
file_preference :logo_mobile_svg, default_url: "/default_images/ofn-logo-mobile.svg"
|
||||
|
||||
# Home page
|
||||
preference :home_page_alert_html, :text
|
||||
preference :home_hero, :file
|
||||
file_preference :home_hero, default_url: "/default_images/home.jpg"
|
||||
preference :home_show_stats, :boolean, default: true
|
||||
has_attached_file :home_hero, default_url: "/default_images/home.jpg"
|
||||
|
||||
# Map
|
||||
preference :open_street_map_enabled, :boolean, default: false
|
||||
@@ -66,8 +60,7 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration
|
||||
preference :menu_7_icon_name, :string, default: "ofn-i_013-help"
|
||||
|
||||
# Footer
|
||||
preference :footer_logo, :file
|
||||
has_attached_file :footer_logo, default_url: "/default_images/ofn-logo-footer.png"
|
||||
file_preference :footer_logo, default_url: "/default_images/ofn-logo-footer.png"
|
||||
|
||||
# Other
|
||||
preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet"
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
require 'spree/core/s3_support'
|
||||
|
||||
class Enterprise < ApplicationRecord
|
||||
include Spree::Core::S3Support
|
||||
|
||||
SELLS = %w(unspecified none own any).freeze
|
||||
ENTERPRISE_SEARCH_RADIUS = 100
|
||||
# The next Rails version will have named variants but we need to store them
|
||||
# ourselves for now.
|
||||
LOGO_SIZES = {
|
||||
thumb: { resize_to_limit: [100, 100] },
|
||||
small: { resize_to_limit: [180, 180] },
|
||||
medium: { resize_to_limit: [300, 300] },
|
||||
}.freeze
|
||||
PROMO_IMAGE_SIZES = {
|
||||
thumb: { resize_to_limit: [100, 100] },
|
||||
medium: { resize_to_fill: [720, 156] },
|
||||
large: { resize_to_fill: [1200, 260] },
|
||||
}.freeze
|
||||
|
||||
searchable_attributes :sells, :is_primary_producer
|
||||
searchable_associations :properties
|
||||
@@ -19,6 +27,8 @@ class Enterprise < ApplicationRecord
|
||||
preference :shopfront_producer_order, :string, default: ""
|
||||
preference :shopfront_order_cycle_order, :string, default: "orders_close_at"
|
||||
preference :shopfront_product_sorting_method, :string, default: "by_category"
|
||||
preference :invoice_order_by_supplier, :boolean, default: false
|
||||
preference :product_low_stock_display, :boolean, default: false
|
||||
|
||||
# Allow hubs to restrict visible variants to only those in their inventory
|
||||
preference :product_selection_from_inventory_only, :boolean, default: false
|
||||
@@ -70,31 +80,16 @@ class Enterprise < ApplicationRecord
|
||||
tag_rule[:preferred_customer_tags].blank?
|
||||
}
|
||||
|
||||
has_attached_file :logo,
|
||||
styles: { medium: "300x300>", small: "180x180>", thumb: "100x100>" },
|
||||
url: '/images/enterprises/logos/:id/:style/:basename.:extension',
|
||||
path: 'public/images/enterprises/logos/:id/:style/:basename.:extension'
|
||||
has_one_attached :logo
|
||||
has_one_attached :promo_image
|
||||
has_one_attached :terms_and_conditions
|
||||
|
||||
has_attached_file :promo_image,
|
||||
styles: {
|
||||
large: ["1200x260#", :jpg],
|
||||
medium: ["720x156#", :jpg],
|
||||
thumb: ["100x100>", :jpg]
|
||||
},
|
||||
url: '/images/enterprises/promo_images/:id/:style/:basename.:extension',
|
||||
path: 'public/images/enterprises/promo_images/:id/:style/:basename.:extension'
|
||||
validates_attachment_content_type :logo, content_type: %r{\Aimage/.*\Z}
|
||||
validates_attachment_content_type :promo_image, content_type: %r{\Aimage/.*\Z}
|
||||
|
||||
has_attached_file :terms_and_conditions,
|
||||
url: '/files/enterprises/terms_and_conditions/:id/:basename.:extension',
|
||||
path: 'public/files/enterprises/terms_and_conditions/:id/:basename.:extension'
|
||||
validates_attachment_content_type :terms_and_conditions,
|
||||
content_type: "application/pdf",
|
||||
message: I18n.t(:enterprise_terms_and_conditions_type_error)
|
||||
|
||||
supports_s3 :logo
|
||||
supports_s3 :promo_image
|
||||
validates :logo, content_type: %r{\Aimage/.*\Z}
|
||||
validates :promo_image, content_type: %r{\Aimage/.*\Z}
|
||||
validates :terms_and_conditions, content_type: {
|
||||
in: "application/pdf",
|
||||
message: I18n.t(:enterprise_terms_and_conditions_type_error),
|
||||
}
|
||||
|
||||
validates :name, presence: true
|
||||
validate :name_is_unique
|
||||
@@ -118,7 +113,8 @@ class Enterprise < ApplicationRecord
|
||||
after_rollback :restore_permalink
|
||||
|
||||
scope :by_name, -> { order('name') }
|
||||
scope :visible, -> { where(visible: true) }
|
||||
scope :visible, -> { where(visible: "public") }
|
||||
scope :not_hidden, -> { where.not(visible: "hidden") }
|
||||
scope :activated, -> { where("sells != 'unspecified'") }
|
||||
scope :ready_for_checkout, lambda {
|
||||
joins(:shipping_methods).
|
||||
@@ -265,7 +261,7 @@ class Enterprise < ApplicationRecord
|
||||
|
||||
def plus_relatives_and_oc_producers(order_cycles)
|
||||
oc_producer_ids = Exchange.in_order_cycle(order_cycles).incoming.pluck :sender_id
|
||||
Enterprise.is_primary_producer.relatives_of_one_union_others(id, oc_producer_ids | [id])
|
||||
Enterprise.not_hidden.is_primary_producer.relatives_of_one_union_others(id, oc_producer_ids | [id])
|
||||
end
|
||||
|
||||
def relatives_including_self
|
||||
@@ -280,6 +276,22 @@ class Enterprise < ApplicationRecord
|
||||
relatives_including_self.is_primary_producer
|
||||
end
|
||||
|
||||
def logo_url(name)
|
||||
return unless logo.variable?
|
||||
|
||||
Rails.application.routes.url_helpers.url_for(
|
||||
logo.variant(LOGO_SIZES[name])
|
||||
)
|
||||
end
|
||||
|
||||
def promo_image_url(name)
|
||||
return unless promo_image.variable?
|
||||
|
||||
Rails.application.routes.url_helpers.url_for(
|
||||
promo_image.variant(PROMO_IMAGE_SIZES[name])
|
||||
)
|
||||
end
|
||||
|
||||
def website
|
||||
strip_url self[:website]
|
||||
end
|
||||
@@ -399,6 +411,10 @@ class Enterprise < ApplicationRecord
|
||||
abn.present?
|
||||
end
|
||||
|
||||
def public?
|
||||
visible == "public"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def devise_mailer
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/locking'
|
||||
require 'spree/core/s3_support'
|
||||
|
||||
class EnterpriseGroup < ApplicationRecord
|
||||
include PermalinkGenerator
|
||||
include Spree::Core::S3Support
|
||||
|
||||
acts_as_list
|
||||
|
||||
@@ -27,21 +25,11 @@ class EnterpriseGroup < ApplicationRecord
|
||||
|
||||
delegate :phone, :address1, :address2, :city, :zipcode, :state, :country, to: :address
|
||||
|
||||
has_attached_file :logo,
|
||||
styles: { medium: "100x100" },
|
||||
url: '/images/enterprise_groups/logos/:id/:style/:basename.:extension',
|
||||
path: 'public/images/enterprise_groups/logos/:id/:style/:basename.:extension'
|
||||
has_one_attached :logo
|
||||
has_one_attached :promo_image
|
||||
|
||||
has_attached_file :promo_image,
|
||||
styles: { large: ["1200x260#", :jpg] },
|
||||
url: '/images/enterprise_groups/promo_images/:id/:style/:basename.:extension',
|
||||
path: 'public/images/enterprise_groups/promo_images/:id/:style/:basename.:extension'
|
||||
|
||||
validates_attachment_content_type :logo, content_type: %r{\Aimage/.*\Z}
|
||||
validates_attachment_content_type :promo_image, content_type: %r{\Aimage/.*\Z}
|
||||
|
||||
supports_s3 :logo
|
||||
supports_s3 :promo_image
|
||||
validates :logo, content_type: %r{\Aimage/.*\Z}
|
||||
validates :promo_image, content_type: %r{\Aimage/.*\Z}
|
||||
|
||||
scope :by_position, -> { order('position ASC') }
|
||||
scope :on_front_page, -> { where(on_front_page: true) }
|
||||
|
||||
@@ -10,7 +10,8 @@ class OrderCycle < ApplicationRecord
|
||||
belongs_to :coordinator, class_name: 'Enterprise'
|
||||
|
||||
has_many :coordinator_fee_refs, class_name: 'CoordinatorFee'
|
||||
has_many :coordinator_fees, through: :coordinator_fee_refs, source: :enterprise_fee
|
||||
has_many :coordinator_fees, through: :coordinator_fee_refs, source: :enterprise_fee,
|
||||
dependent: :destroy
|
||||
|
||||
has_many :exchanges, dependent: :destroy
|
||||
|
||||
|
||||
@@ -236,12 +236,10 @@ module Spree
|
||||
:validate_data, :reset_absent_products], ProductImport::ProductImporter
|
||||
|
||||
# Reports page
|
||||
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :payments,
|
||||
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing],
|
||||
Spree::Admin::ReportsController
|
||||
can [:admin, :show, :packing], :report
|
||||
add_bulk_coop_abilities
|
||||
add_enterprise_fee_summary_abilities
|
||||
can [:admin, :index, :show], ::Admin::ReportsController
|
||||
can [:admin, :show, :customers, :orders_and_distributors, :group_buys, :payments,
|
||||
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management,
|
||||
:packing, :enterprise_fee_summary, :bulk_coop], :report
|
||||
end
|
||||
|
||||
def add_order_cycle_management_abilities(user)
|
||||
@@ -317,11 +315,10 @@ module Spree
|
||||
end
|
||||
|
||||
# Reports page
|
||||
can [:admin, :index, :customers, :group_buys, :sales_tax, :payments,
|
||||
can [:admin, :index, :show], ::Admin::ReportsController
|
||||
can [:admin, :customers, :group_buys, :sales_tax, :payments,
|
||||
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
|
||||
:order_cycle_management, :xero_invoices], Spree::Admin::ReportsController
|
||||
add_bulk_coop_abilities
|
||||
add_enterprise_fee_summary_abilities
|
||||
:order_cycle_management, :xero_invoices, :enterprise_fee_summary, :bulk_coop], :report
|
||||
|
||||
can [:create], Customer
|
||||
can [:admin, :index, :update,
|
||||
@@ -346,19 +343,5 @@ module Spree
|
||||
user.enterprises.include?(enterprise_relationship.child)
|
||||
end
|
||||
end
|
||||
|
||||
def add_bulk_coop_abilities
|
||||
# Reveal the report link in spree/admin/reports#index
|
||||
can [:bulk_coop], Spree::Admin::ReportsController
|
||||
# Allow direct access to the report resource
|
||||
can [:admin, :new, :create], :bulk_coop
|
||||
end
|
||||
|
||||
def add_enterprise_fee_summary_abilities
|
||||
# Reveal the report link in spree/admin/reports#index
|
||||
can [:enterprise_fee_summary], Spree::Admin::ReportsController
|
||||
# Allow direct access to the report resource
|
||||
can [:admin, :new, :create], :enterprise_fee_summary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -105,6 +105,10 @@ module Spree
|
||||
render_address([city, zipcode, state&.name])
|
||||
end
|
||||
|
||||
def address_and_city
|
||||
[address1, address2, city].select(&:present?).join(' ')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_zipcode?
|
||||
|
||||
@@ -1,101 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spree/core/s3_support'
|
||||
|
||||
module Spree
|
||||
class Image < Asset
|
||||
validates_attachment_presence :attachment
|
||||
SIZES = {
|
||||
mini: { resize_to_fill: [48, 48] },
|
||||
small: { resize_to_fill: [227, 227] },
|
||||
product: { resize_to_limit: [240, 240] },
|
||||
large: { resize_to_limit: [600, 600] },
|
||||
}.freeze
|
||||
|
||||
has_one_attached :attachment
|
||||
|
||||
validates :attachment, attached: true, content_type: %r{\Aimage/.*\Z}
|
||||
validate :no_attachment_errors
|
||||
|
||||
# This is where the styles are used in the app:
|
||||
# - mini: used in the BackOffice: Bulk Product Edit page and Order Cycle edit page
|
||||
# - small: used in the FrontOffice: Product List page
|
||||
# - product: used in the BackOffice: Product Image upload modal in the Bulk Product Edit page
|
||||
# and Product image edit page
|
||||
# - large: used in the FrontOffice: product modal
|
||||
has_attached_file :attachment,
|
||||
styles: { mini: "48x48#", small: "227x227#",
|
||||
product: "240x240>", large: "600x600>" },
|
||||
default_style: :product,
|
||||
url: '/spree/products/:id/:style/:basename.:extension',
|
||||
path: ':rails_root/public/spree/products/:id/:style/:basename.:extension',
|
||||
convert_options: { all: '-strip -auto-orient -colorspace sRGB' }
|
||||
|
||||
# save the w,h of the original image (from which others can be calculated)
|
||||
# we need to look at the write-queue for images which have not been saved yet
|
||||
after_post_process :find_dimensions
|
||||
|
||||
include Spree::Core::S3Support
|
||||
supports_s3 :attachment
|
||||
|
||||
# used by admin products autocomplete
|
||||
def mini_url
|
||||
attachment.url(:mini, false)
|
||||
def variant(name)
|
||||
attachment.variant(SIZES[name])
|
||||
end
|
||||
|
||||
def find_dimensions
|
||||
return if attachment.errors.present?
|
||||
def url(size)
|
||||
return unless attachment.variable?
|
||||
|
||||
geometry = Paperclip::Geometry.from_file(local_filename_of_original)
|
||||
|
||||
self.attachment_width = geometry.width
|
||||
self.attachment_height = geometry.height
|
||||
end
|
||||
|
||||
def local_filename_of_original
|
||||
temporary = attachment.queued_for_write[:original]
|
||||
|
||||
if temporary&.path.present?
|
||||
temporary.path
|
||||
else
|
||||
attachment.path
|
||||
end
|
||||
Rails.application.routes.url_helpers.url_for(variant(size))
|
||||
end
|
||||
|
||||
# if there are errors from the plugin, then add a more meaningful message
|
||||
def no_attachment_errors
|
||||
return if attachment.errors.empty?
|
||||
return if errors[:attachment].empty?
|
||||
|
||||
if errors.all? { |e| e.type == "Paperclip::Errors::NotIdentifiedByImageMagickError" }
|
||||
if errors.all? { |e| e.type == :content_type_invalid }
|
||||
attachment.errors.clear
|
||||
errors.add :base, I18n.t('spree.admin.products.image_upload_error')
|
||||
else
|
||||
errors.add :attachment,
|
||||
I18n.t('spree.admin.products.paperclip_image_error', attachment_file_name: attachment_file_name)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def self.set_attachment_attribute(attribute_name, attribute_value)
|
||||
attachment_definitions[:attachment][attribute_name] = attribute_value
|
||||
end
|
||||
|
||||
def self.set_storage_attachment_attributes
|
||||
if Spree::Config[:use_s3]
|
||||
set_s3_attachment_attributes
|
||||
else
|
||||
attachment_definitions[:attachment].delete(:storage)
|
||||
end
|
||||
end
|
||||
|
||||
def self.set_s3_attachment_attributes
|
||||
set_attachment_attribute(:storage, :s3)
|
||||
set_attachment_attribute(:s3_credentials, s3_credentials)
|
||||
set_attachment_attribute(:s3_headers,
|
||||
ActiveSupport::JSON.decode(Spree::Config[:s3_headers]))
|
||||
set_attachment_attribute(:bucket, Spree::Config[:s3_bucket])
|
||||
|
||||
# We use :s3_alias_url (virtual host url style) and set the URL on property s3_host_alias
|
||||
set_attachment_attribute(:s3_host_alias, attachment_definitions[:attachment][:url])
|
||||
set_attachment_attribute(:url, ":s3_alias_url")
|
||||
end
|
||||
private_class_method :set_s3_attachment_attributes
|
||||
|
||||
def self.s3_credentials
|
||||
{ access_key_id: Spree::Config[:s3_access_key],
|
||||
secret_access_key: Spree::Config[:s3_secret],
|
||||
bucket: Spree::Config[:s3_bucket] }
|
||||
end
|
||||
private_class_method :s3_credentials
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,6 +50,8 @@ module Spree
|
||||
|
||||
attr_accessor :skip_stock_check, :target_shipment # Allows manual skipping of Stock::AvailabilityValidator
|
||||
|
||||
attribute :restock_item, type: :boolean, default: true
|
||||
|
||||
# -- Scopes
|
||||
scope :managed_by, lambda { |user|
|
||||
if user.has_spree_role?('admin')
|
||||
|
||||
@@ -106,6 +106,7 @@ module Spree
|
||||
after_save_commit DefaultAddressUpdater
|
||||
|
||||
attribute :send_cancellation_email, type: :boolean, default: true
|
||||
attribute :restock_items, type: :boolean, default: true
|
||||
# -- Scopes
|
||||
scope :not_empty, -> {
|
||||
left_outer_joins(:line_items).where.not(spree_line_items: { id: nil })
|
||||
@@ -429,9 +430,10 @@ module Spree
|
||||
|
||||
def empty!
|
||||
line_items.destroy_all
|
||||
adjustments.destroy_all
|
||||
all_adjustments.destroy_all
|
||||
payments.clear
|
||||
shipments.destroy_all
|
||||
restart_checkout_flow if state == "payment"
|
||||
end
|
||||
|
||||
def state_changed(name)
|
||||
@@ -609,6 +611,14 @@ module Spree
|
||||
address
|
||||
end
|
||||
|
||||
def sorted_line_items
|
||||
if distributor.preferred_invoice_order_by_supplier
|
||||
line_items.sort_by { |li| [li.supplier.name, li.product.name] }
|
||||
else
|
||||
line_items.sort_by { |li| [li.product.name] }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fee_handler
|
||||
@@ -661,11 +671,12 @@ module Spree
|
||||
shipments.each(&:cancel!)
|
||||
|
||||
OrderMailer.cancel_email(id).deliver_later if send_cancellation_email
|
||||
self.payment_state = 'credit_owed' unless shipped?
|
||||
update(payment_state: updater.update_payment_state)
|
||||
end
|
||||
|
||||
def after_resume
|
||||
shipments.each(&:resume!)
|
||||
update(payment_state: updater.update_payment_state)
|
||||
end
|
||||
|
||||
def use_billing?
|
||||
|
||||
@@ -19,8 +19,8 @@ module Spree
|
||||
|
||||
# Get current line item for variant
|
||||
# Remove variant qty from line_item
|
||||
def remove(variant, quantity = nil, shipment = nil)
|
||||
line_item = remove_from_line_item(variant, quantity, shipment)
|
||||
def remove(variant, quantity = nil, shipment = nil, restock_item = true)
|
||||
line_item = remove_from_line_item(variant, quantity, shipment, restock_item)
|
||||
update_shipment(shipment)
|
||||
order.update_order_fees! if order.completed?
|
||||
update_order
|
||||
@@ -97,9 +97,9 @@ module Spree
|
||||
line_item
|
||||
end
|
||||
|
||||
def remove_from_line_item(variant, quantity, shipment = nil)
|
||||
def remove_from_line_item(variant, quantity, shipment = nil, restock_item = true)
|
||||
line_item = find_line_item_by_variant(variant, true)
|
||||
|
||||
line_item.restock_item = restock_item
|
||||
quantity.present? ? line_item.quantity += -quantity : line_item.quantity = 0
|
||||
line_item.target_shipment = shipment
|
||||
|
||||
|
||||
@@ -44,12 +44,15 @@ module Spree
|
||||
quantity = variant_units.size - line_item.quantity
|
||||
|
||||
if shipment.present?
|
||||
remove_from_shipment(shipment, line_item.variant, quantity)
|
||||
remove_from_shipment(shipment, line_item.variant, quantity, line_item.restock_item)
|
||||
else
|
||||
order.shipments.each do |each_shipment|
|
||||
break if quantity == 0
|
||||
|
||||
quantity -= remove_from_shipment(each_shipment, line_item.variant, quantity)
|
||||
quantity -= remove_from_shipment(each_shipment,
|
||||
line_item.variant,
|
||||
quantity,
|
||||
line_item.restock_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -83,7 +86,7 @@ module Spree
|
||||
quantity
|
||||
end
|
||||
|
||||
def remove_from_shipment(shipment, variant, quantity)
|
||||
def remove_from_shipment(shipment, variant, quantity, restock_item)
|
||||
return 0 if quantity == 0 || shipment.shipped?
|
||||
|
||||
shipment_units = shipment.inventory_units_for(variant).reject do |variant_unit|
|
||||
@@ -101,7 +104,7 @@ module Spree
|
||||
shipment.destroy if shipment.inventory_units.reload.count == 0
|
||||
|
||||
# removing this from shipment, and adding to stock_location
|
||||
if order.completed?
|
||||
if order.completed? && restock_item
|
||||
shipment.stock_location.restock variant, removed_quantity, shipment
|
||||
end
|
||||
|
||||
|
||||
@@ -183,9 +183,11 @@ module Spree
|
||||
|
||||
def validate_source
|
||||
if source && !skip_source_validation && !source.valid?
|
||||
source.errors.each do |field, error|
|
||||
field_name = I18n.t("activerecord.attributes.#{source.class.to_s.underscore}.#{field}")
|
||||
errors.add(Spree.t(source.class.to_s.demodulize.underscore), "#{field_name} #{error}")
|
||||
source.errors.each do |error|
|
||||
field_name =
|
||||
I18n.t("activerecord.attributes.#{source.class.to_s.underscore}.#{error.attribute}")
|
||||
errors.add(Spree.t(source.class.to_s.demodulize.underscore),
|
||||
"#{field_name} #{error.message}")
|
||||
end
|
||||
end
|
||||
errors.blank?
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Preferences
|
||||
class FileConfiguration < Configuration
|
||||
def self.preference(name, type, *args)
|
||||
if type == :file
|
||||
super "#{name}_file_name", :string, *args
|
||||
super "#{name}_content_type", :string, *args
|
||||
super "#{name}_file_size", :integer, *args
|
||||
super "#{name}_updated_at", :string, *args
|
||||
|
||||
else
|
||||
super name, type, *args
|
||||
end
|
||||
end
|
||||
|
||||
def get_preference(key)
|
||||
if !has_preference?(key) && has_attachment?(key)
|
||||
public_send key
|
||||
else
|
||||
super key
|
||||
end
|
||||
end
|
||||
alias :[] :get_preference
|
||||
|
||||
def preference_type(name)
|
||||
if has_attachment? name
|
||||
:file
|
||||
else
|
||||
super name
|
||||
end
|
||||
end
|
||||
|
||||
# Spree's Configuration responds to preference methods via method_missing, but doesn't
|
||||
# override respond_to?, which consequently reports those methods as unavailable. Paperclip
|
||||
# errors if respond_to? isn't correct, so we override it here.
|
||||
def respond_to?(method, include_all = false)
|
||||
name = method.to_s.delete('=')
|
||||
super(self.class.preference_getter_method(name), include_all) || super(method, include_all)
|
||||
end
|
||||
|
||||
def has_attachment?(name)
|
||||
self.class.respond_to?(:attachment_definitions) &&
|
||||
self.class.attachment_definitions.key?(name.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -78,8 +78,7 @@ module Spree
|
||||
where(producer_properties: { property_id: property_ids }).
|
||||
or(
|
||||
where(spree_product_properties: { property_id: property_ids })
|
||||
).
|
||||
distinct
|
||||
)
|
||||
}
|
||||
|
||||
delegate_belongs_to :master, :sku, :price, :currency, :display_amount, :display_price, :weight,
|
||||
|
||||
@@ -192,7 +192,7 @@ module Spree
|
||||
end
|
||||
|
||||
def after_cancel
|
||||
manifest.each { |item| manifest_restock(item) }
|
||||
manifest.each { |item| manifest_restock(item) } if order.restock_items
|
||||
end
|
||||
|
||||
def after_resume
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
module Spree
|
||||
class Taxon < ApplicationRecord
|
||||
include Spree::Core::S3Support
|
||||
|
||||
acts_as_nested_set dependent: :destroy
|
||||
|
||||
belongs_to :taxonomy, class_name: 'Spree::Taxonomy', touch: true
|
||||
@@ -14,15 +12,6 @@ module Spree
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
has_attached_file :icon,
|
||||
styles: { mini: '32x32>', normal: '128x128>' },
|
||||
default_style: :mini,
|
||||
url: '/spree/taxons/:id/:style/:basename.:extension',
|
||||
path: ':rails_root/public/spree/taxons/:id/:style/:basename.:extension',
|
||||
default_url: '/assets/default_taxon.png'
|
||||
|
||||
supports_s3 :icon
|
||||
|
||||
# Indicate which filters should be used for this taxon
|
||||
def applicable_filters
|
||||
[]
|
||||
|
||||
@@ -9,6 +9,7 @@ class SubscriptionLineItem < ApplicationRecord
|
||||
validates :quantity, presence: true, numericality: { only_integer: true }
|
||||
|
||||
default_scope { order('id ASC') }
|
||||
scope :nil_price_estimate, -> { where(price_estimate: nil) }
|
||||
|
||||
def total_estimate
|
||||
(price_estimate || 0) * (quantity || 0)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TermsOfServiceFile < ApplicationRecord
|
||||
has_attached_file :attachment
|
||||
has_one_attached :attachment
|
||||
|
||||
validates :attachment, presence: true
|
||||
validates :attachment, attached: true
|
||||
|
||||
# The most recently uploaded file is the current one.
|
||||
def self.current
|
||||
@@ -11,7 +11,11 @@ class TermsOfServiceFile < ApplicationRecord
|
||||
end
|
||||
|
||||
def self.current_url
|
||||
current&.attachment&.url || Spree::Config.footer_tos_url
|
||||
if current
|
||||
Rails.application.routes.url_helpers.url_for(current.attachment)
|
||||
else
|
||||
Spree::Config.footer_tos_url
|
||||
end
|
||||
end
|
||||
|
||||
# If no file has been uploaded, we don't know when the old terms have
|
||||
|
||||
@@ -12,7 +12,9 @@ module Api
|
||||
:preferred_shopfront_product_sorting_method, :owner, :contact, :users, :tag_groups,
|
||||
:default_tag_group, :require_login, :allow_guest_orders, :allow_order_changes,
|
||||
:logo, :promo_image, :terms_and_conditions,
|
||||
:terms_and_conditions_file_name, :terms_and_conditions_updated_at
|
||||
:terms_and_conditions_file_name, :terms_and_conditions_updated_at,
|
||||
:preferred_invoice_order_by_supplier, :preferred_product_low_stock_display,
|
||||
:visible
|
||||
|
||||
has_one :owner, serializer: Api::Admin::UserSerializer
|
||||
has_many :users, serializer: Api::Admin::UserSerializer
|
||||
@@ -20,21 +22,26 @@ module Api
|
||||
has_one :business_address, serializer: Api::AddressSerializer
|
||||
|
||||
def logo
|
||||
attachment_urls(object.logo, [:thumb, :small, :medium])
|
||||
attachment_urls(object.logo, Enterprise::LOGO_SIZES)
|
||||
end
|
||||
|
||||
def promo_image
|
||||
attachment_urls(object.promo_image, [:thumb, :medium, :large])
|
||||
attachment_urls(object.promo_image, Enterprise::PROMO_IMAGE_SIZES)
|
||||
end
|
||||
|
||||
def terms_and_conditions
|
||||
return unless object.terms_and_conditions.file?
|
||||
return unless object.terms_and_conditions.attached?
|
||||
|
||||
object.terms_and_conditions.url
|
||||
Rails.application.routes.url_helpers.
|
||||
url_for(object.terms_and_conditions)
|
||||
end
|
||||
|
||||
def terms_and_conditions_file_name
|
||||
object.terms_and_conditions_blob&.filename
|
||||
end
|
||||
|
||||
def terms_and_conditions_updated_at
|
||||
object.terms_and_conditions_updated_at&.to_s
|
||||
object.terms_and_conditions_blob&.created_at&.to_s
|
||||
end
|
||||
|
||||
def tag_groups
|
||||
@@ -74,19 +81,19 @@ module Api
|
||||
|
||||
# Returns a hash of URLs for specified versions of an attachment.
|
||||
#
|
||||
# Example:
|
||||
# Example result:
|
||||
#
|
||||
# attachment_urls(object.logo, [:thumb, :small, :medium])
|
||||
# # {
|
||||
# # thumb: LOGO_THUMB_URL,
|
||||
# # small: LOGO_SMALL_URL,
|
||||
# # medium: LOGO_MEDIUM_URL
|
||||
# # }
|
||||
def attachment_urls(attachment, versions)
|
||||
return unless attachment.file?
|
||||
# {
|
||||
# thumb: LOGO_THUMB_URL,
|
||||
# small: LOGO_SMALL_URL,
|
||||
# medium: LOGO_MEDIUM_URL
|
||||
# }
|
||||
def attachment_urls(attachment, styles)
|
||||
return unless attachment.variable?
|
||||
|
||||
versions.each_with_object({}) do |version, urls|
|
||||
urls[version] = attachment.url(version)
|
||||
styles.transform_values do |transformation|
|
||||
Rails.application.routes.url_helpers.
|
||||
url_for(attachment.variant(transformation))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module Api
|
||||
end
|
||||
|
||||
def image_url
|
||||
object.images.present? ? object.images.first.attachment.url(:mini) : nil
|
||||
object.images.first&.url(:mini)
|
||||
end
|
||||
|
||||
def master_id
|
||||
|
||||
@@ -7,7 +7,7 @@ module Api
|
||||
:edit_path, :state, :payment_state, :shipment_state,
|
||||
:payments_path, :ready_to_ship, :ready_to_capture, :created_at,
|
||||
:distributor_name, :special_instructions, :display_outstanding_balance,
|
||||
:item_total, :adjustment_total, :payment_total, :total
|
||||
:item_total, :adjustment_total, :payment_total, :total, :item_count
|
||||
|
||||
has_one :distributor, serializer: Api::Admin::IdSerializer
|
||||
has_one :order_cycle, serializer: Api::Admin::IdSerializer
|
||||
@@ -69,6 +69,10 @@ module Api
|
||||
object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: '%B %d, %Y')
|
||||
end
|
||||
|
||||
def item_count
|
||||
object.line_items.count
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def spree_routes_helper
|
||||
|
||||
@@ -13,19 +13,11 @@ module Api
|
||||
has_one :master, serializer: Api::Admin::VariantSerializer
|
||||
|
||||
def image_url
|
||||
if object.images.present?
|
||||
object.images.first.attachment.url(:product)
|
||||
else
|
||||
"/noimage/product.png"
|
||||
end
|
||||
object.images.first&.url(:product) || "/noimage/product.png"
|
||||
end
|
||||
|
||||
def thumb_url
|
||||
if object.images.present?
|
||||
object.images.first.attachment.url(:mini)
|
||||
else
|
||||
"/noimage/mini.png"
|
||||
end
|
||||
object.images.first&.url(:mini) || "/noimage/mini.png"
|
||||
end
|
||||
|
||||
def on_hand
|
||||
|
||||
@@ -33,9 +33,7 @@ module Api
|
||||
end
|
||||
|
||||
def image
|
||||
return if object.product.images.empty?
|
||||
|
||||
object.product.images.first.mini_url
|
||||
object.product.images.first&.url(:mini)
|
||||
end
|
||||
|
||||
def in_stock
|
||||
|
||||
@@ -44,11 +44,11 @@ module Api
|
||||
end
|
||||
|
||||
def logo
|
||||
enterprise.logo(:medium) if enterprise.logo?
|
||||
enterprise.logo_url(:medium)
|
||||
end
|
||||
|
||||
def promo_image
|
||||
enterprise.promo_image(:large) if enterprise.promo_image?
|
||||
enterprise.promo_image_url(:large)
|
||||
end
|
||||
|
||||
def path
|
||||
|
||||
@@ -9,7 +9,7 @@ module Api
|
||||
:instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor,
|
||||
:phone, :visible, :email_address, :hash, :logo, :promo_image, :path, :category,
|
||||
:active, :producers, :orders_close_at, :hubs, :taxons, :supplied_taxons, :pickup,
|
||||
:delivery
|
||||
:delivery, :preferred_product_low_stock_display
|
||||
|
||||
has_one :address, serializer: Api::AddressSerializer
|
||||
has_many :supplied_properties, serializer: Api::PropertySerializer
|
||||
@@ -41,11 +41,11 @@ module Api
|
||||
end
|
||||
|
||||
def logo
|
||||
enterprise.logo(:medium) if enterprise.logo?
|
||||
enterprise.logo_url(:medium)
|
||||
end
|
||||
|
||||
def promo_image
|
||||
enterprise.promo_image(:large) if enterprise.promo_image?
|
||||
enterprise.promo_image_url(:large)
|
||||
end
|
||||
|
||||
def path
|
||||
|
||||
@@ -4,18 +4,18 @@ class Api::ImageSerializer < ActiveModel::Serializer
|
||||
attributes :id, :alt, :thumb_url, :small_url, :image_url, :large_url
|
||||
|
||||
def thumb_url
|
||||
object.attachment.url(:mini, false)
|
||||
object.url(:mini)
|
||||
end
|
||||
|
||||
def small_url
|
||||
object.attachment.url(:small, false)
|
||||
object.url(:small)
|
||||
end
|
||||
|
||||
def image_url
|
||||
object.attachment.url(:product, false)
|
||||
object.url(:product)
|
||||
end
|
||||
|
||||
def large_url
|
||||
object.attachment.url(:large, false)
|
||||
object.url(:large)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ module Api
|
||||
|
||||
has_many :payments, serializer: Api::PaymentSerializer
|
||||
|
||||
attributes :adjustments
|
||||
attributes :adjustments, :customer_id
|
||||
|
||||
def adjustments
|
||||
adjustments = object.all_adjustments.where(
|
||||
|
||||
@@ -9,7 +9,7 @@ module Api
|
||||
end
|
||||
|
||||
def logo
|
||||
object.logo(:small) if object.logo?
|
||||
object.logo_url(:small)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
app/serializers/api/v1/address_serializer.rb
Normal file
18
app/serializers/api/v1/address_serializer.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class AddressSerializer < BaseSerializer
|
||||
attributes :phone, :latitude, :longitude
|
||||
|
||||
attribute :first_name, &:firstname
|
||||
attribute :last_name, &:lastname
|
||||
attribute :street_address_1, &:address1
|
||||
attribute :street_address_2, &:address2
|
||||
attribute :postal_code, &:zipcode
|
||||
attribute :locality, &:city
|
||||
attribute :region, &:state_name
|
||||
attribute :country, ->(object, _) { object.country.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/serializers/api/v1/base_serializer.rb
Normal file
13
app/serializers/api/v1/base_serializer.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class BaseSerializer
|
||||
include JSONAPI::Serializer
|
||||
|
||||
def self.url_helpers
|
||||
Rails.application.routes.url_helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
30
app/serializers/api/v1/customer_serializer.rb
Normal file
30
app/serializers/api/v1/customer_serializer.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class CustomerSerializer < BaseSerializer
|
||||
attributes :id, :enterprise_id, :first_name, :last_name, :code, :email,
|
||||
:allow_charges, :terms_and_conditions_accepted_at
|
||||
|
||||
attribute :tags, &:tag_list
|
||||
|
||||
attribute :billing_address do |object|
|
||||
address(object.billing_address)
|
||||
end
|
||||
|
||||
attribute :shipping_address do |object|
|
||||
address(object.shipping_address)
|
||||
end
|
||||
|
||||
belongs_to :enterprise, links: {
|
||||
related: ->(object) {
|
||||
url_helpers.api_v1_enterprise_url(id: object.enterprise_id)
|
||||
}
|
||||
}
|
||||
|
||||
def self.address(record)
|
||||
AddressSerializer.new(record).serializable_hash.dig(:data, :attributes)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
15
app/serializers/api/v1/enterprise_serializer.rb
Normal file
15
app/serializers/api/v1/enterprise_serializer.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
class EnterpriseSerializer < BaseSerializer
|
||||
attributes :id, :name
|
||||
|
||||
has_many :customers, links: {
|
||||
related: ->(object) {
|
||||
url_helpers.api_v1_enterprise_customers_url(enterprise_id: object.id)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -35,11 +35,7 @@ class Api::VariantSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def thumb_url
|
||||
if object.product.images.present?
|
||||
object.product.images.first.attachment.url(:mini)
|
||||
else
|
||||
"/noimage/mini.png"
|
||||
end
|
||||
object.product.images.first&.url(:mini) || "/noimage/mini.png"
|
||||
end
|
||||
|
||||
def unit_price_price
|
||||
|
||||
@@ -2,21 +2,12 @@
|
||||
|
||||
class ImageImporter
|
||||
def import(url, product)
|
||||
attach(download(url), product)
|
||||
end
|
||||
valid_url = URI.parse(url)
|
||||
file = open(valid_url.to_s)
|
||||
filename = File.basename(valid_url.path)
|
||||
|
||||
private
|
||||
|
||||
def download(url)
|
||||
local_file = Tempfile.new
|
||||
remote_file = open(url)
|
||||
IO.copy_stream(remote_file, local_file)
|
||||
local_file
|
||||
end
|
||||
|
||||
def attach(file, product)
|
||||
Spree::Image.create(
|
||||
attachment: file,
|
||||
attachment: { io: file, filename: filename },
|
||||
viewable_id: product.master.id,
|
||||
viewable_type: Spree::Variant,
|
||||
)
|
||||
|
||||
@@ -33,6 +33,8 @@ module PermittedAttributes
|
||||
:preferred_shopfront_closed_message, :preferred_shopfront_taxon_order,
|
||||
: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
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ class TermsOfService
|
||||
return false unless accepted_at = customer&.terms_and_conditions_accepted_at
|
||||
|
||||
accepted_at > if distributor
|
||||
distributor.terms_and_conditions_updated_at
|
||||
distributor.terms_and_conditions_blob.created_at
|
||||
else
|
||||
TermsOfServiceFile.updated_at
|
||||
end
|
||||
@@ -20,6 +20,6 @@ class TermsOfService
|
||||
end
|
||||
|
||||
def self.distributor_terms_required?(distributor)
|
||||
distributor.terms_and_conditions.file?
|
||||
distributor.terms_and_conditions.attached?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,8 +38,13 @@
|
||||
# post.valid? # => false
|
||||
# post.errors[:published_at] # => ["must be valid"]
|
||||
class DateTimeStringValidator < ActiveModel::EachValidator
|
||||
NOT_STRING_ERROR = I18n.t("validators.date_time_string_validator.not_string_error")
|
||||
INVALID_FORMAT_ERROR = I18n.t("validators.date_time_string_validator.invalid_format_error")
|
||||
def self.not_string_error
|
||||
I18n.t("validators.date_time_string_validator.not_string_error")
|
||||
end
|
||||
|
||||
def self.invalid_format_error
|
||||
I18n.t("validators.date_time_string_validator.invalid_format_error")
|
||||
end
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
return if value.nil? || value == ""
|
||||
@@ -53,13 +58,13 @@ class DateTimeStringValidator < ActiveModel::EachValidator
|
||||
def validate_attribute_is_string(record, attribute, value)
|
||||
return if value.is_a?(String)
|
||||
|
||||
record.errors.add(attribute, NOT_STRING_ERROR)
|
||||
record.errors.add(attribute, DateTimeStringValidator.not_string_error)
|
||||
end
|
||||
|
||||
def validate_attribute_is_datetime_string(record, attribute, value)
|
||||
return unless value.is_a?(String)
|
||||
|
||||
datetime = Time.zone.parse(value)
|
||||
record.errors.add(attribute, INVALID_FORMAT_ERROR) if datetime.blank?
|
||||
record.errors.add(attribute, DateTimeStringValidator.invalid_format_error) if datetime.blank?
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user