mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-28 01:53:25 +00:00
Compare commits
31 Commits
v4.4.12
...
v4.4.0-mas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd889133cb | ||
|
|
4ac3dda398 | ||
|
|
9d5d269f1f | ||
|
|
f4405775f6 | ||
|
|
3a38361857 | ||
|
|
68b59ab7a6 | ||
|
|
a56541216e | ||
|
|
05d9646f3e | ||
|
|
b1de64bf3e | ||
|
|
e976cc6d95 | ||
|
|
9a89b22364 | ||
|
|
1b304e2aa4 | ||
|
|
48cdca59fd | ||
|
|
9095abfed2 | ||
|
|
aa9fd682d8 | ||
|
|
5b73ccb213 | ||
|
|
dda3cfa58d | ||
|
|
9da649a296 | ||
|
|
2e53b9a0c6 | ||
|
|
527e305e2f | ||
|
|
89b59f97ee | ||
|
|
26d3cffba3 | ||
|
|
86703bb545 | ||
|
|
8e99f496ff | ||
|
|
2f2506e698 | ||
|
|
7ef9c2f56a | ||
|
|
ee4402f751 | ||
|
|
79a2d1228d | ||
|
|
da3202460c | ||
|
|
75ccc5c72f | ||
|
|
aca72e6071 |
8
.env
8
.env
@@ -52,8 +52,6 @@ SMTP_PASSWORD="f00d"
|
||||
|
||||
# see="https://developers.google.com/maps/documentation/javascript/get-api-key
|
||||
# GOOGLE_MAPS_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
# see https://developers.google.com/maps/documentation/javascript/localization#Region
|
||||
# GOOGLE_MAPS_REGION="XX"
|
||||
|
||||
# Stripe details for instance account
|
||||
# Find these under 'Developers' -> 'API keys' in your Stripe account dashboard.
|
||||
@@ -63,9 +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"
|
||||
|
||||
# New relic settings
|
||||
# see: https://one.eu.newrelic.com/admin-portal/, Administration > API keys to get the license key
|
||||
# NEW_RELIC_AGENT_ENABLED=true
|
||||
# NEW_RELIC_APP_NAME="Open Food Network"
|
||||
# NEW_RELIC_LICENSE_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
33
.github/ISSUE_TEMPLATE/release.md
vendored
33
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -7,34 +7,21 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 1. Preparation on Thursday
|
||||
## Preparation on Thursday
|
||||
|
||||
- [ ] Merge pull requests in the [Ready To Go] column
|
||||
- [ ] Include translations
|
||||
<details><summary>Command line instructions:</summary>
|
||||
<pre>
|
||||
<code>
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
tx pull --force
|
||||
git commit -a -m "Update all locales with the latest Transifex translations"
|
||||
git push upstream master
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
- [ ] Create a tag: `git push upstream HEAD:refs/tags/vX.Y.Z`
|
||||
- [ ] Include translations: `tx pull --force`
|
||||
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
|
||||
- Select new release tag
|
||||
- _Generate release notes_ and check to ensure all items are arranged in the right category.
|
||||
- [ ] Notify [#instance-managers] of both user-facing :eyes: and :warning: API changes.
|
||||
- [ ] Notify [#instance-managers] of user-facing changes.
|
||||
|
||||
## 2. Testing
|
||||
## Testing
|
||||
|
||||
- [ ] [Find build] of the release commit and copy it below.
|
||||
- [ ] Move this issue to Test Ready.
|
||||
- [ ] Notify `@testers` in [#testing].
|
||||
- [ ] Test build: [Deploy to Staging] with release tag.
|
||||
- [ ] Test build: <!-- paste build link here, e.g. https://semaphore...builds/1234 -->
|
||||
|
||||
## 3. Finish on Tuesday
|
||||
## Finish on Tuesday
|
||||
|
||||
- [ ] Publish and notify [#global-community] (this is automatically posted with a plugin)
|
||||
- [ ] Deploy the new release to all managed instances.
|
||||
@@ -42,7 +29,7 @@ assignees: ''
|
||||
<pre>
|
||||
cd ofn-install
|
||||
git pull
|
||||
ansible-playbook --limit all-prod --extra-vars "git_version=vX.Y.Z" playbooks/deploy.yml
|
||||
ansible-playbook --limit all-prod --extra-vars "git_version=vx.y.z" playbooks/deploy.yml
|
||||
</pre>
|
||||
</details>
|
||||
- [ ] Notify [#instance-managers]:
|
||||
@@ -53,9 +40,9 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
|
||||
|
||||
[Ready To Go]: #zenhub
|
||||
[Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
|
||||
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
|
||||
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
|
||||
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00
|
||||
[Deploy to Staging]: https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/stage.yml
|
||||
[Find build]: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
|
||||
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2
|
||||
|
||||
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -20,12 +20,7 @@
|
||||
|
||||
<!-- Please select one for your PR and delete the other. -->
|
||||
|
||||
Changelog Category (reviewers may add a label for the release notes):
|
||||
|
||||
- [x] User facing changes
|
||||
- [ ] API changes (V0, V1, DFC or Webhook)
|
||||
- [ ] Technical changes only
|
||||
- [ ] Feature toggled
|
||||
Changelog Category: User facing changes | Technical changes
|
||||
|
||||
<!-- Choose a pull request title above which explains your change to a
|
||||
a user of the Open Food Network app. -->
|
||||
|
||||
23
.github/release.yml
vendored
23
.github/release.yml
vendored
@@ -1,23 +0,0 @@
|
||||
changelog:
|
||||
categories:
|
||||
- title: "User-facing changes 👀"
|
||||
labels:
|
||||
- '*'
|
||||
exclude:
|
||||
labels:
|
||||
- api changes
|
||||
- dependencies
|
||||
- feature toggled
|
||||
- technical changes only
|
||||
- title: "API changes ⚠️"
|
||||
labels:
|
||||
- api changes
|
||||
- title: "Features under construction 🚧"
|
||||
labels:
|
||||
- feature toggled
|
||||
- title: "Technical changes 🛠️"
|
||||
labels:
|
||||
- technical changes only
|
||||
- title: "Dependencies 📦"
|
||||
labels:
|
||||
- dependencies
|
||||
28
.github/workflows/build.yml
vendored
28
.github/workflows/build.yml
vendored
@@ -17,7 +17,7 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
controllers:
|
||||
knapsack_rspec_controllers:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
models:
|
||||
knapsack_rspec_models:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -106,10 +106,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [5]
|
||||
ci_node_total: [7]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1, 2, 3, 4]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
system_admin:
|
||||
knapsack_rspec_system_admin:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -175,10 +175,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [13]
|
||||
ci_node_total: [10]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
system_consumer:
|
||||
knapsack_rspec_system_consumer:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -253,10 +253,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [12]
|
||||
ci_node_total: [10]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -310,7 +310,7 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
engines:
|
||||
knapsack_rspec_engines:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -331,10 +331,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [2]
|
||||
ci_node_total: [5]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1]
|
||||
ci_node_index: [0, 1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -388,7 +388,7 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
test_the_rest:
|
||||
knapsack_rspec_test_the_rest:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
|
||||
2
.github/workflows/mapi.yml
vendored
2
.github/workflows/mapi.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
with:
|
||||
mapi-token: ${{ secrets.MAPI_TOKEN }}
|
||||
api-url: http://localhost:3000
|
||||
api-spec: swagger/v1.yaml
|
||||
api-spec: swagger/v1/swagger.yaml
|
||||
target: openfoodfoundation/openfoodnetwork
|
||||
duration: 1min
|
||||
sarif-report: mapi.sarif
|
||||
|
||||
31
.github/workflows/stage.yml
vendored
31
.github/workflows/stage.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "Deploy to Staging"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -13,32 +13,24 @@ on:
|
||||
- staging.openfoodnetwork.org.uk
|
||||
- staging.openfoodnetwork.org.au
|
||||
- staging.coopcircuits.fr
|
||||
commit_ref:
|
||||
description: "Commit Reference"
|
||||
type: string
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy_pr:
|
||||
if: contains(fromJSON('["pr-staged-uk", "pr-staged-au", "pr-staged-fr"]'), github.event.label.name)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check user has write access"
|
||||
uses: "lannonbr/repo-permission-check-action@2.0.2"
|
||||
with:
|
||||
permission: "write"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure deployment key
|
||||
if: success()
|
||||
run: |
|
||||
install -m 600 -D /dev/null ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
|
||||
|
||||
- name: Deploy to Staging
|
||||
if: success()
|
||||
env:
|
||||
LABEL: ${{ github.event.label.name }}
|
||||
run: |
|
||||
ssh ofn-deploy@${{ github.event.label.description }} -o LogLevel=ERROR "pull-request-${{ github.event.pull_request.number }} ."
|
||||
|
||||
@@ -46,21 +38,12 @@ jobs:
|
||||
if: ${{ inputs.server }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check user has write access"
|
||||
uses: "lannonbr/repo-permission-check-action@2.0.2"
|
||||
with:
|
||||
permission: "write"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure deployment key
|
||||
if: success()
|
||||
run: |
|
||||
install -m 600 -D /dev/null ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
|
||||
|
||||
- name: Deploy to Staging
|
||||
if: success()
|
||||
run: |
|
||||
ssh ofn-deploy@${{ inputs.server }} -o LogLevel=ERROR "$GITHUB_REF_NAME ${{ inputs.commit_ref || github.sha }}"
|
||||
ssh ofn-deploy@${{ inputs.server }} -o LogLevel=ERROR "$GITHUB_REF_NAME $GITHUB_SHA"
|
||||
|
||||
@@ -13,8 +13,7 @@ postcss.config.js
|
||||
|
||||
# SCSS
|
||||
# Enabled: most of admin
|
||||
/app/webpacker/css/admin/globals/mixins.scss
|
||||
/app/webpacker/css/admin/globals/variables.scss
|
||||
/app/webpacker/css/admin/globals/
|
||||
/app/webpacker/css/admin/shared/
|
||||
/app/webpacker/css/admin_v3/globals/variables.scss
|
||||
/app/webpacker/css/darkswarm/
|
||||
|
||||
@@ -25,7 +25,6 @@ Metrics/BlockLength:
|
||||
AllowedMethods: [
|
||||
"class_eval",
|
||||
"collection",
|
||||
"configure",
|
||||
"context",
|
||||
"delete",
|
||||
"describe",
|
||||
@@ -39,28 +38,17 @@ Metrics/BlockLength:
|
||||
"put",
|
||||
"resource",
|
||||
"resources",
|
||||
"response",
|
||||
"scenario",
|
||||
"shared_examples",
|
||||
"shared_examples_for",
|
||||
"xdescribe",
|
||||
]
|
||||
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
||||
|
||||
Rails/ApplicationRecord:
|
||||
Exclude:
|
||||
# Migrations should not contain application code:
|
||||
- "db/migrate/*.rb"
|
||||
|
||||
# Allow many-to-many associations without explicit model.
|
||||
# - It avoids the additional code of a model class.
|
||||
# - It simplifies the declaration of the association.
|
||||
# - Rails may know that there are no callbacks associated.
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
AllowedMethods:
|
||||
- "touch"
|
||||
@@ -101,8 +89,6 @@ Naming/VariableNumber:
|
||||
AllowedIdentifiers:
|
||||
- street_address_1
|
||||
- street_address_2
|
||||
AllowedPatterns:
|
||||
- _v[\d]+
|
||||
|
||||
Bundler/DuplicatedGem:
|
||||
Enabled: false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
3.1.4
|
||||
3.0.3
|
||||
|
||||
13
Gemfile
13
Gemfile
@@ -1,10 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby "3.0.3"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
ruby File.read('.ruby-version').chomp
|
||||
|
||||
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
|
||||
|
||||
gem 'rails'
|
||||
@@ -103,8 +102,8 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-scheduler'
|
||||
|
||||
gem "cable_ready", "5.0.1"
|
||||
gem "stimulus_reflex", "3.5.0.rc3"
|
||||
gem "cable_ready", "5.0.0.rc2"
|
||||
gem "stimulus_reflex", "3.5.0.rc2"
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
@@ -116,6 +115,8 @@ gem 'spreadsheet_architect' # write spreadsheets
|
||||
|
||||
gem 'whenever', require: false
|
||||
|
||||
gem 'test-unit', '~> 3.5'
|
||||
|
||||
gem 'coffee-rails', '~> 5.0.0'
|
||||
|
||||
gem 'angular_rails_csrf'
|
||||
@@ -138,9 +139,8 @@ gem 'mini_portile2', '~> 2.8'
|
||||
gem "faraday"
|
||||
gem "private_address_check"
|
||||
|
||||
gem 'newrelic_rpm'
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'sd_notify' # For better Systemd process management. Used by Puma.
|
||||
end
|
||||
|
||||
@@ -159,7 +159,6 @@ group :test, :development do
|
||||
gem 'rspec-retry', require: false
|
||||
gem 'rswag-specs'
|
||||
gem 'shoulda-matchers'
|
||||
gem 'stimulus_reflex_testing'
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
|
||||
290
Gemfile.lock
290
Gemfile.lock
@@ -26,6 +26,9 @@ PATH
|
||||
remote: engines/dfc_provider
|
||||
specs:
|
||||
dfc_provider (0.0.1)
|
||||
active_model_serializers (~> 0.8.4)
|
||||
jwt (~> 2.2)
|
||||
rspec (~> 3.9)
|
||||
|
||||
PATH
|
||||
remote: engines/order_management
|
||||
@@ -41,49 +44,49 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.1.0)
|
||||
actioncable (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actioncable (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailbox (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailer (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionpack (7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actionpack-action_caching (1.2.2)
|
||||
actionpack (>= 4.0.0)
|
||||
actiontext (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actiontext (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
@@ -95,38 +98,37 @@ GEM
|
||||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activejob (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
globalid (>= 0.3.6)
|
||||
activemerchant (1.123.0)
|
||||
activesupport (>= 4.2)
|
||||
builder (>= 2.1.2, < 4.0.0)
|
||||
i18n (>= 0.6.9)
|
||||
nokogiri (~> 1.4)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord-import (1.5.0)
|
||||
activemodel (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
activerecord (7.0.5)
|
||||
activemodel (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
activerecord-import (1.4.1)
|
||||
activerecord (>= 4.2)
|
||||
activerecord-postgresql-adapter (0.0.1)
|
||||
pg
|
||||
activerecord-session_store (2.1.0)
|
||||
actionpack (>= 6.1)
|
||||
activerecord (>= 6.1)
|
||||
cgi (>= 0.3.6)
|
||||
activerecord-session_store (2.0.0)
|
||||
actionpack (>= 5.2.4.1)
|
||||
activerecord (>= 5.2.4.1)
|
||||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 2.0.8, < 4)
|
||||
railties (>= 6.1)
|
||||
activestorage (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rack (>= 2.0.8, < 3)
|
||||
railties (>= 5.2.4.1)
|
||||
activestorage (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.0.5)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
@@ -155,35 +157,34 @@ GEM
|
||||
awesome_nested_set (3.5.0)
|
||||
activerecord (>= 4.0.0, < 7.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.826.0)
|
||||
aws-sdk-core (3.183.0)
|
||||
aws-partitions (1.779.0)
|
||||
aws-sdk-core (3.174.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.71.0)
|
||||
aws-sdk-core (~> 3, >= 3.177.0)
|
||||
aws-sdk-kms (1.66.0)
|
||||
aws-sdk-core (~> 3, >= 3.174.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.135.0)
|
||||
aws-sdk-core (~> 3, >= 3.181.0)
|
||||
aws-sdk-s3 (1.124.0)
|
||||
aws-sdk-core (~> 3, >= 3.174.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.6)
|
||||
aws-sigv4 (1.6.0)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
base64 (0.1.1)
|
||||
bcrypt (3.1.18)
|
||||
bigdecimal (3.0.2)
|
||||
bindata (2.4.15)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.16.0)
|
||||
msgpack (~> 1.2)
|
||||
bugsnag (6.26.0)
|
||||
bugsnag (6.25.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (7.0.7)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.1)
|
||||
cable_ready (5.0.0.rc2)
|
||||
actionpack (>= 5.2)
|
||||
actionview (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
@@ -204,7 +205,6 @@ GEM
|
||||
marcel (~> 1.0)
|
||||
nokogiri (~> 1.10, >= 1.10.4)
|
||||
rubyzip (>= 1.3.0, < 3)
|
||||
cgi (0.3.6)
|
||||
choice (0.2.0)
|
||||
chronic (0.10.2)
|
||||
coderay (1.1.3)
|
||||
@@ -234,9 +234,15 @@ GEM
|
||||
activerecord (>= 5.a)
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
datafoodconsortium-connector (1.0.0.pre.alpha.8)
|
||||
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
|
||||
datafoodconsortium-connector (1.0.0.pre.alpha.6)
|
||||
virtual_assembly-semantizer (~> 1.0, >= 1.0.4)
|
||||
date (3.3.3)
|
||||
ddtrace (1.12.1)
|
||||
debase-ruby_core_source (= 3.2.1)
|
||||
libdatadog (~> 2.0.0.1.0)
|
||||
libddwaf (~> 1.9.0.0.0)
|
||||
msgpack
|
||||
debase-ruby_core_source (3.2.1)
|
||||
debug (1.8.0)
|
||||
irb (>= 1.5.0)
|
||||
reline (>= 0.3.1)
|
||||
@@ -270,8 +276,7 @@ GEM
|
||||
factory_bot_rails (6.2.0)
|
||||
factory_bot (~> 6.2.0)
|
||||
railties (>= 5.0.0)
|
||||
faraday (2.7.11)
|
||||
base64
|
||||
faraday (2.7.6)
|
||||
faraday-net_http (>= 2.0, < 3.1)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-follow_redirects (0.3.0)
|
||||
@@ -282,7 +287,7 @@ GEM
|
||||
concurrent-ruby (~> 1.1)
|
||||
webrick (~> 1.7)
|
||||
websocket-driver (>= 0.6, < 0.8)
|
||||
ffaker (2.23.0)
|
||||
ffaker (2.21.0)
|
||||
ffi (1.15.5)
|
||||
flipper (0.26.2)
|
||||
concurrent-ruby (< 2)
|
||||
@@ -318,9 +323,9 @@ GEM
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
geocoder (1.8.2)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
geocoder (1.8.1)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
gmaps4rails (2.1.2)
|
||||
good_migrations (0.2.1)
|
||||
activerecord (>= 3.1)
|
||||
@@ -356,20 +361,20 @@ GEM
|
||||
jquery-ui-rails (4.2.1)
|
||||
railties (>= 3.2.16)
|
||||
json (2.6.3)
|
||||
json-canonicalization (0.3.2)
|
||||
json-canonicalization (0.3.1)
|
||||
json-jwt (1.16.3)
|
||||
activesupport (>= 4.2)
|
||||
aes_key_wrap
|
||||
bindata
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
json-ld (3.2.5)
|
||||
json-ld (3.2.3)
|
||||
htmlentities (~> 4.3)
|
||||
json-canonicalization (~> 0.3, >= 0.3.2)
|
||||
json-canonicalization (~> 0.3)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
multi_json (~> 1.15)
|
||||
rack (>= 2.2, < 4)
|
||||
rdf (~> 3.2, >= 3.2.10)
|
||||
rack (~> 2.2)
|
||||
rdf (~> 3.2, >= 3.2.9)
|
||||
json-schema (3.0.0)
|
||||
addressable (>= 2.8)
|
||||
json_spec (1.1.5)
|
||||
@@ -378,13 +383,15 @@ GEM
|
||||
jsonapi-serializer (2.2.0)
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.7.1)
|
||||
knapsack_pro (5.7.0)
|
||||
knapsack_pro (5.1.0)
|
||||
rake
|
||||
language_server-protocol (3.17.0.3)
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
letter_opener (1.8.1)
|
||||
launchy (>= 2.2, < 3)
|
||||
libdatadog (2.0.0.1.0)
|
||||
libddwaf (1.9.0.0.1)
|
||||
ffi (~> 1.0)
|
||||
link_header (0.0.8)
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
@@ -400,16 +407,16 @@ GEM
|
||||
marcel (1.0.2)
|
||||
matrix (0.4.2)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.5.1)
|
||||
mime-types (3.4.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2023.0808)
|
||||
mime-types-data (3.2021.0225)
|
||||
mimemagic (0.4.3)
|
||||
nokogiri (~> 1)
|
||||
rake
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.4)
|
||||
minitest (5.20.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.2)
|
||||
minitest (5.18.0)
|
||||
monetize (1.12.0)
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
@@ -417,7 +424,7 @@ GEM
|
||||
msgpack (1.7.1)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
net-imap (0.3.7)
|
||||
net-imap (0.3.4)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
@@ -426,9 +433,8 @@ GEM
|
||||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
newrelic_rpm (9.5.0)
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.4)
|
||||
nokogiri (1.15.2)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
oauth2 (1.4.11)
|
||||
@@ -482,19 +488,20 @@ GEM
|
||||
ruby-rc4
|
||||
ttfunk
|
||||
pg (1.2.3)
|
||||
power_assert (2.0.3)
|
||||
private_address_check (0.5.0)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
public_suffix (5.0.3)
|
||||
puma (6.4.0)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.3.0)
|
||||
nio4r (~> 2.0)
|
||||
query_count (1.1.1)
|
||||
activerecord (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
raabro (1.4.0)
|
||||
racc (1.7.1)
|
||||
rack (2.2.8)
|
||||
racc (1.7.0)
|
||||
rack (2.2.7)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
rack-oauth2 (1.21.3)
|
||||
@@ -511,27 +518,26 @@ GEM
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rack-timeout (0.6.3)
|
||||
rails (7.0.8)
|
||||
actioncable (= 7.0.8)
|
||||
actionmailbox (= 7.0.8)
|
||||
actionmailer (= 7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actiontext (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rails (7.0.5)
|
||||
actioncable (= 7.0.5)
|
||||
actionmailbox (= 7.0.5)
|
||||
actionmailer (= 7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
actiontext (= 7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activemodel (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.8)
|
||||
railties (= 7.0.5)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
activesupport (>= 5.0.1.rc1)
|
||||
rails-dom-testing (2.2.0)
|
||||
activesupport (>= 5.0.0)
|
||||
minitest
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-erd (1.7.2)
|
||||
activerecord (>= 4.2)
|
||||
@@ -541,13 +547,13 @@ GEM
|
||||
rails-html-sanitizer (1.6.0)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
rails-i18n (7.0.8)
|
||||
rails-i18n (7.0.7)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
rails_safe_tasks (1.0.0)
|
||||
railties (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
railties (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
@@ -561,11 +567,11 @@ GEM
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rdf (3.2.11)
|
||||
rdf (3.2.9)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
redcarpet (3.6.0)
|
||||
redis (4.8.1)
|
||||
redis-client (0.17.0)
|
||||
redis-client (0.14.1)
|
||||
connection_pool
|
||||
regexp_parser (2.8.1)
|
||||
reline (0.3.3)
|
||||
@@ -575,7 +581,7 @@ GEM
|
||||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.6)
|
||||
rexml (3.2.5)
|
||||
roadie (5.0.1)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -610,32 +616,30 @@ GEM
|
||||
rspec-support (~> 3.12)
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.12.1)
|
||||
rswag-api (2.10.1)
|
||||
rspec-support (3.12.0)
|
||||
rswag-api (2.9.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rswag-specs (2.10.1)
|
||||
rswag-specs (2.9.0)
|
||||
activesupport (>= 3.1, < 7.1)
|
||||
json-schema (>= 2.2, < 4.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rspec-core (>= 2.14)
|
||||
rswag-ui (2.10.1)
|
||||
rswag-ui (2.9.0)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.56.3)
|
||||
base64 (~> 0.1.1)
|
||||
rubocop (1.52.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.3)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.1, < 2.0)
|
||||
rubocop-ast (>= 1.28.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.29.0)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-rails (2.21.1)
|
||||
rubocop-rails (2.19.1)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
@@ -649,7 +653,7 @@ GEM
|
||||
rubyzip (2.3.2)
|
||||
rufus-scheduler (3.8.2)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
sanitize (6.0.2)
|
||||
sanitize (6.0.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
sass (3.4.25)
|
||||
@@ -663,7 +667,7 @@ GEM
|
||||
semantic_range (3.0.0)
|
||||
shoulda-matchers (5.3.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (7.1.4)
|
||||
sidekiq (7.1.2)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
rack (>= 2.2.4)
|
||||
@@ -691,43 +695,43 @@ GEM
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
state_machines (0.6.0)
|
||||
state_machines-activemodel (0.9.0)
|
||||
activemodel (>= 6.0)
|
||||
state_machines (>= 0.6.0)
|
||||
state_machines-activerecord (0.9.0)
|
||||
activerecord (>= 6.0)
|
||||
state_machines-activemodel (>= 0.9.0)
|
||||
stimulus_reflex (3.5.0.rc3)
|
||||
state_machines (0.5.0)
|
||||
state_machines-activemodel (0.8.0)
|
||||
activemodel (>= 5.1)
|
||||
state_machines (>= 0.5.0)
|
||||
state_machines-activerecord (0.8.0)
|
||||
activerecord (>= 5.1)
|
||||
state_machines-activemodel (>= 0.8.0)
|
||||
stimulus_reflex (3.5.0.rc2)
|
||||
actioncable (>= 5.2, < 8)
|
||||
actionpack (>= 5.2, < 8)
|
||||
actionview (>= 5.2, < 8)
|
||||
activesupport (>= 5.2, < 8)
|
||||
cable_ready (~> 5.0)
|
||||
cable_ready (>= 5.0.0.rc2)
|
||||
nokogiri (~> 1.0)
|
||||
rack (>= 2, < 4)
|
||||
railties (>= 5.2, < 8)
|
||||
redis (>= 4.0, < 6.0)
|
||||
stimulus_reflex_testing (0.3.0)
|
||||
stimulus_reflex (>= 3.3.0)
|
||||
stringex (2.8.6)
|
||||
stripe (9.3.0)
|
||||
stripe (8.5.0)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
httpclient (>= 2.4)
|
||||
temple (0.8.2)
|
||||
test-unit (3.6.0)
|
||||
power_assert
|
||||
thor (1.2.2)
|
||||
thread-local (1.1.0)
|
||||
tilt (2.1.0)
|
||||
timecop (0.9.8)
|
||||
timeout (0.4.0)
|
||||
timecop (0.9.6)
|
||||
timeout (0.3.2)
|
||||
ttfunk (1.7.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (2.4.2)
|
||||
uniform_notifier (1.16.0)
|
||||
valid_email2 (5.0.3)
|
||||
valid_email2 (4.0.6)
|
||||
activemodel (>= 3.2)
|
||||
mail (~> 2.5)
|
||||
validate_email (0.1.6)
|
||||
@@ -736,8 +740,8 @@ GEM
|
||||
validate_url (1.0.15)
|
||||
activemodel (>= 3.0.0)
|
||||
public_suffix
|
||||
vcr (6.2.0)
|
||||
view_component (3.6.0)
|
||||
vcr (6.1.0)
|
||||
view_component (3.2.0)
|
||||
activesupport (>= 5.2.0, < 8.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
method_source (~> 1.0)
|
||||
@@ -745,11 +749,11 @@ GEM
|
||||
rails (>= 5.2, < 8.0)
|
||||
stimulus_reflex (>= 3.5.0.pre2)
|
||||
view_component (>= 2.28.0)
|
||||
virtual_assembly-semantizer (1.0.5)
|
||||
virtual_assembly-semantizer (1.0.4)
|
||||
json-ld (~> 3.2, >= 3.2.3)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
web-console (4.2.1)
|
||||
web-console (4.2.0)
|
||||
actionview (>= 6.0.0)
|
||||
activemodel (>= 6.0.0)
|
||||
bindex (>= 0.4.0)
|
||||
@@ -767,7 +771,7 @@ GEM
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webrick (1.7.0)
|
||||
websocket-driver (0.7.6)
|
||||
websocket-driver (0.7.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
whenever (1.0.0)
|
||||
@@ -778,7 +782,7 @@ GEM
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.11)
|
||||
zeitwerk (2.6.8)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -804,7 +808,7 @@ DEPENDENCIES
|
||||
bootsnap
|
||||
bugsnag
|
||||
bullet
|
||||
cable_ready (= 5.0.1)
|
||||
cable_ready (= 5.0.0.rc2)
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
catalog!
|
||||
@@ -814,6 +818,7 @@ DEPENDENCIES
|
||||
database_cleaner
|
||||
datafoodconsortium-connector
|
||||
db2fog!
|
||||
ddtrace
|
||||
debug (>= 1.0.0)
|
||||
debugger-linecache
|
||||
devise
|
||||
@@ -855,7 +860,6 @@ DEPENDENCIES
|
||||
mimemagic (> 0.3.5)
|
||||
mini_portile2 (~> 2.8)
|
||||
monetize (~> 1.11)
|
||||
newrelic_rpm
|
||||
oauth2 (~> 1.4.7)
|
||||
omniauth-rails_csrf_protection
|
||||
omniauth_openid_connect
|
||||
@@ -903,10 +907,10 @@ DEPENDENCIES
|
||||
spring
|
||||
spring-commands-rspec
|
||||
state_machines-activerecord
|
||||
stimulus_reflex (= 3.5.0.rc3)
|
||||
stimulus_reflex_testing
|
||||
stimulus_reflex (= 3.5.0.rc2)
|
||||
stringex (~> 2.8.5)
|
||||
stripe
|
||||
test-unit (~> 3.5)
|
||||
timecop
|
||||
valid_email2
|
||||
vcr
|
||||
@@ -921,7 +925,7 @@ DEPENDENCIES
|
||||
wkhtmltopdf-binary
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.1.4p223
|
||||
ruby 3.0.3p157
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.3
|
||||
|
||||
1
Rakefile
1
Rakefile
@@ -7,3 +7,4 @@
|
||||
require_relative 'config/application'
|
||||
|
||||
Openfoodnetwork::Application.load_tasks
|
||||
|
||||
|
||||
@@ -68,6 +68,25 @@
|
||||
//= require textAngular.min.js
|
||||
//= require i18n/translations
|
||||
//= require darkswarm/i18n.translate.js
|
||||
//= require moment/min/moment.min.js
|
||||
//= require moment/locale/ar.js
|
||||
//= require moment/locale/ca.js
|
||||
//= require moment/locale/de.js
|
||||
//= require moment/locale/en-gb.js
|
||||
//= require moment/locale/es.js
|
||||
//= require moment/locale/fil.js
|
||||
//= require moment/locale/fr.js
|
||||
//= require moment/locale/it.js
|
||||
//= require moment/locale/nb.js
|
||||
//= require moment/locale/nl-be.js
|
||||
//= require moment/locale/pt-br.js
|
||||
//= require moment/locale/pt.js
|
||||
//= require moment/locale/ru.js
|
||||
//= require moment/locale/sv.js
|
||||
//= require moment/locale/tr.js
|
||||
//= require moment/locale/pl.js
|
||||
|
||||
//= require js-big-decimal/dist/web/js-big-decimal.min.js
|
||||
|
||||
// foundation
|
||||
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js
|
||||
|
||||
@@ -113,7 +113,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
|
||||
|
||||
editProductUrl = (product, variant) ->
|
||||
"/admin/products/" + product.id + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
|
||||
$scope.editWarn = (product, variant) ->
|
||||
if confirm_unsaved_changes()
|
||||
@@ -135,7 +135,6 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
display_name: null
|
||||
on_hand: null
|
||||
price: null
|
||||
tax_category_id: null
|
||||
DisplayProperties.setShowVariants product.id, true
|
||||
|
||||
|
||||
@@ -163,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm(t("are_you_sure"))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/v0/products/" + product.id + "/variants/" + variant.id
|
||||
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
).then (response) ->
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
@@ -347,9 +346,15 @@ filterSubmitProducts = (productsToFilter) ->
|
||||
if product.hasOwnProperty("category_id")
|
||||
filteredProduct.primary_taxon_id = product.category_id
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("tax_category_id")
|
||||
filteredProduct.tax_category_id = product.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("inherits_properties")
|
||||
filteredProduct.inherits_properties = product.inherits_properties
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("available_on")
|
||||
filteredProduct.available_on = product.available_on
|
||||
hasUpdatableProperty = true
|
||||
if filteredMaster?
|
||||
filteredProduct.master_attributes = filteredMaster
|
||||
hasUpdatableProperty = true
|
||||
@@ -387,9 +392,6 @@ filterSubmitVariant = (variant) ->
|
||||
if variant.hasOwnProperty("display_name")
|
||||
filteredVariant.display_name = variant.display_name
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("tax_category_id")
|
||||
filteredVariant.tax_category_id = variant.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("display_as")
|
||||
filteredVariant.display_as = variant.display_as
|
||||
hasUpdatableProperty = true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module("admin.dropdown").directive "linksDropdown", ($window)->
|
||||
restrict: "C"
|
||||
scope:
|
||||
links: "="
|
||||
templateUrl: "admin/links_dropdown.html"
|
||||
@@ -11,8 +11,14 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
|
||||
pendingChanges.remove(scope.object().id, scope.attr)
|
||||
scope.clear()
|
||||
else
|
||||
change =
|
||||
object: scope.object()
|
||||
type: scope.type
|
||||
attr: scope.attr
|
||||
value: if value? then value else ""
|
||||
scope: scope
|
||||
scope.pending()
|
||||
addPendingChange(scope.attr, value ? "")
|
||||
pendingChanges.add(scope.object().id, scope.attr, change)
|
||||
|
||||
scope.reset = (value) ->
|
||||
scope.savedValue = value
|
||||
@@ -28,33 +34,3 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
|
||||
|
||||
scope.clear = ->
|
||||
switchClass( element, "", ["update-pending", "update-error", "update-success"], false )
|
||||
|
||||
# When a list of customer is filtered and we removed the "filtered value" from a customer, we
|
||||
# want to make sure the customer is updated. IE. filtering by tag, and removing said tag.
|
||||
# Deleting the "filtered value" from a customer will remove the customer entry, thus
|
||||
# removing "objForUpdate" directive from the active scope. That means $watch won't pick up
|
||||
# the attribute changed.
|
||||
# To ensure the customer is still updated, we check on the $destroy event to see if
|
||||
# the attribute has changed, if so we queue up the change.
|
||||
scope.$on '$destroy', (value) ->
|
||||
# No update
|
||||
return if scope.object()[scope.attr] is scope.savedValue
|
||||
|
||||
# For some reason the code attribute is removed from the object when cleared, so we add
|
||||
# an emptyvalue so it gets updated properly
|
||||
if scope.attr is "code" and scope.object()[scope.attr] is undefined
|
||||
scope.object()["code"] = ""
|
||||
|
||||
# Queuing up change
|
||||
addPendingChange(scope.attr, scope.object()[scope.attr])
|
||||
|
||||
# private
|
||||
|
||||
addPendingChange = (attr, value) ->
|
||||
change =
|
||||
object: scope.object()
|
||||
type: scope.type
|
||||
attr: attr
|
||||
value: value
|
||||
scope: scope
|
||||
pendingChanges.add(scope.object().id, attr, change)
|
||||
|
||||
@@ -11,5 +11,24 @@
|
||||
|
||||
//= require i18n/translations
|
||||
//= require darkswarm/i18n.translate.js
|
||||
//= require moment/min/moment.min.js
|
||||
//= require moment/locale/ar.js
|
||||
//= require moment/locale/ca.js
|
||||
//= require moment/locale/de.js
|
||||
//= require moment/locale/en-gb.js
|
||||
//= require moment/locale/es.js
|
||||
//= require moment/locale/fil.js
|
||||
//= require moment/locale/fr.js
|
||||
//= require moment/locale/it.js
|
||||
//= require moment/locale/nb.js
|
||||
//= require moment/locale/nl-be.js
|
||||
//= require moment/locale/pt-br.js
|
||||
//= require moment/locale/pt.js
|
||||
//= require moment/locale/ru.js
|
||||
//= require moment/locale/sv.js
|
||||
//= require moment/locale/tr.js
|
||||
//= require moment/locale/pl.js
|
||||
|
||||
window.angular = { module: function(noop){ return { value: function(){} } } }
|
||||
//= require js-big-decimal/dist/web/js-big-decimal.min.js
|
||||
|
||||
window.angular = { module: function(noop){ return { value: function(){} } } }
|
||||
@@ -29,6 +29,24 @@
|
||||
#
|
||||
#= require angular-flash.min.js
|
||||
#
|
||||
#= require moment/min/moment.min.js
|
||||
#= require moment/locale/ar.js
|
||||
#= require moment/locale/ca.js
|
||||
#= require moment/locale/de.js
|
||||
#= require moment/locale/en-gb.js
|
||||
#= require moment/locale/es.js
|
||||
#= require moment/locale/fil.js
|
||||
#= require moment/locale/fr.js
|
||||
#= require moment/locale/it.js
|
||||
#= require moment/locale/nb.js
|
||||
#= require moment/locale/nl-be.js
|
||||
#= require moment/locale/pt-br.js
|
||||
#= require moment/locale/pt.js
|
||||
#= require moment/locale/ru.js
|
||||
#= require moment/locale/sv.js
|
||||
#= require moment/locale/tr.js
|
||||
#= require moment/locale/pl.js
|
||||
#
|
||||
#= require modernizr
|
||||
#
|
||||
#= require foundation-sites/js/foundation.js
|
||||
|
||||
@@ -9,9 +9,10 @@ angular.module('Darkswarm').controller "RegistrationFormCtrl", ($scope, Registra
|
||||
$scope.create = (form) ->
|
||||
if ($scope.valid(form))
|
||||
$scope.disableButton()
|
||||
EnterpriseRegistrationService.create($scope.enableButton).then(() ->
|
||||
EnterpriseRegistrationService.create().then(() ->
|
||||
$scope.enableButton()
|
||||
)
|
||||
end
|
||||
|
||||
$scope.update = (nextStep, form) ->
|
||||
EnterpriseRegistrationService.update(nextStep) if $scope.valid(form)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
.ofn-drop-down
|
||||
%span
|
||||
%i.icon-check
|
||||
{{ 'admin.actions' | t }}
|
||||
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div{ 'ng-repeat' => "link in links" }
|
||||
%a.menu_item{ 'ng-if': "link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method }}", "ujs-navigate": "false", confirm: "{{link.confirm}}" } }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
%a.menu_item{ 'ng-if': "link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", "data-confirm": "{{link.confirm}}" }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
%a.menu_item{ 'ng-if': "!link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}" }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
@@ -19,7 +19,7 @@
|
||||
.name {{ product.name }}
|
||||
.supplier {{ product.supplier_name }}
|
||||
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges | filter:variantSuppliedToOrderCycle as filteredVariants'}
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges | filter:variantSuppliedToOrderCycle'}
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
value: 1,
|
||||
@@ -27,8 +27,5 @@
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
|
||||
{{ variant.label }}
|
||||
|
||||
%em{ 'ng-if' => 'filteredVariants.length === 0' }
|
||||
{{ 'js.admin.panels.exchange_products.no_variants' | t }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
.exchange-load-all-variants
|
||||
%div
|
||||
{{ 'js.admin.panels.exchange_products.variants_loaded' | t:{ num_of_variants_loaded: enterprises[exchange.enterprise_id].loaded_variants, total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
%em{ 'ng-if': 'enterprises[exchange.enterprise_id].loaded_variants > exchangeTotalVariants(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.some_variants_hidden' | t }}
|
||||
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
@@ -1 +1 @@
|
||||
.question-mark-icon{"ng-class" => "{open: tt_isOpen}", type: 'button'}
|
||||
%button.question-mark-icon{"ng-class" => "{open: tt_isOpen}", type: 'button'}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ScopedChannel < ApplicationCable::Channel
|
||||
class << self
|
||||
def for_id(id)
|
||||
"ScopedChannel:#{id}"
|
||||
end
|
||||
end
|
||||
|
||||
def subscribed
|
||||
stream_from "ScopedChannel:#{params[:id]}"
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ConfirmModalComponent < ModalComponent
|
||||
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil,
|
||||
confirm_reflexes: nil)
|
||||
super(id:, close_button: true)
|
||||
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil, confirm_reflexes: nil)
|
||||
super(id: id, close_button: true)
|
||||
@confirm_actions = confirm_actions
|
||||
@reflex = reflex
|
||||
@confirm_reflexes = confirm_reflexes
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
class HelpModalComponent < ModalComponent
|
||||
def initialize(id:, close_button: true)
|
||||
super(id:, close_button:)
|
||||
super(id: id, close_button: close_button)
|
||||
end
|
||||
end
|
||||
|
||||
15
app/components/pagination_component.rb
Normal file
15
app/components/pagination_component.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PaginationComponent < ViewComponentReflex::Component
|
||||
def initialize(pagy:, data:)
|
||||
super
|
||||
@count = pagy.count
|
||||
@page = pagy.page
|
||||
@per_page = pagy.items
|
||||
@pages = pagy.pages
|
||||
@next = pagy.next
|
||||
@prev = pagy.prev
|
||||
@data = data
|
||||
@series = pagy.series
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
= component_controller do
|
||||
%nav{"aria-label": "pagination"}
|
||||
.pagination
|
||||
.pagination-prev{data: @prev.nil? ? nil : @data, "data-page": @prev, class: "#{'inactive' if @prev.nil?}"}
|
||||
= I18n.t "components.pagination.previous"
|
||||
.pagination-pages
|
||||
- @series.each do |page|
|
||||
- if page == :gap
|
||||
.pagination-gap
|
||||
…
|
||||
- else
|
||||
.pagination-page{data: @data, "data-page": page, class: "#{'active' if page.to_i == @page}"}
|
||||
= page
|
||||
.pagination-next{data: @next.nil? ? nil : @data, "data-page": @next, class: "#{'inactive' if @next.nil?}"}
|
||||
= I18n.t "components.pagination.next"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
nav {
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
font-size: 14px;
|
||||
|
||||
.pagination-prev, .pagination-next {
|
||||
cursor: pointer;
|
||||
|
||||
&:after, &:before {
|
||||
font-size: 2em;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
cursor: default;
|
||||
color: $disabled-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-prev {
|
||||
margin-left: 10px;
|
||||
|
||||
&:before {
|
||||
content: "‹";
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-next {
|
||||
margin-right: 10px;
|
||||
|
||||
&:after {
|
||||
content: "›";
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.pagination-pages {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
.pagination-gap, .pagination-page {
|
||||
padding: 0 0.5rem;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.pagination-gap {
|
||||
color: $disabled-dark;
|
||||
}
|
||||
|
||||
.pagination-page {
|
||||
color: $color-4;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
border-top: 3px solid $spree-blue;
|
||||
color: $spree-blue;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
app/components/product_component.rb
Normal file
59
app/components/product_component.rb
Normal file
@@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProductComponent < ViewComponentReflex::Component
|
||||
DATETIME_FORMAT = '%F %T'
|
||||
|
||||
def initialize(product:, columns:)
|
||||
super
|
||||
@product = product
|
||||
@image = @product.image if product.image.present?
|
||||
@columns = columns.map do |c|
|
||||
{
|
||||
id: c[:value],
|
||||
value: column_value(c[:value])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# This must be define when using ProductComponent.with_collection()
|
||||
def collection_key
|
||||
@product.id
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
||||
def column_value(column)
|
||||
case column
|
||||
when 'name'
|
||||
@product.name
|
||||
when 'price'
|
||||
@product.price
|
||||
when 'unit'
|
||||
"#{@product.variants.first.unit_value} #{@product.variant_unit}"
|
||||
when 'producer'
|
||||
@product.supplier.name
|
||||
when 'category'
|
||||
@product.taxons.map(&:name).join(', ')
|
||||
when 'sku'
|
||||
@product.sku
|
||||
when 'on_hand'
|
||||
@product.on_hand || 0
|
||||
when 'on_demand'
|
||||
@product.on_demand
|
||||
when 'tax_category'
|
||||
@product.tax_category.name
|
||||
when 'inherits_properties'
|
||||
@product.inherits_properties
|
||||
when 'available_on'
|
||||
format_date(@product.available_on)
|
||||
when 'import_date'
|
||||
format_date(@product.import_date)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
||||
|
||||
private
|
||||
|
||||
def format_date(date)
|
||||
date&.strftime(DATETIME_FORMAT) || ''
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
%tr
|
||||
- @columns.each do |column|
|
||||
%td.products_column{class: column[:id]}
|
||||
- if column[:id] == "name" && @image&.attachment.present?
|
||||
= image_tag @image.url(:mini)
|
||||
= column[:value]
|
||||
180
app/components/products_table_component.rb
Normal file
180
app/components/products_table_component.rb
Normal file
@@ -0,0 +1,180 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProductsTableComponent < ViewComponentReflex::Component
|
||||
include Pagy::Backend
|
||||
|
||||
SORTABLE_COLUMNS = ['name', 'import_date'].freeze
|
||||
SELECTABLE_COLUMNS = [
|
||||
{ label: I18n.t("admin.products_page.columns_selector.price"), value: "price" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.unit"), value: "unit" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.producer"), value: "producer" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.category"), value: "category" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.sku"), value: "sku" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.on_hand"), value: "on_hand" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.on_demand"), value: "on_demand" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.tax_category"), value: "tax_category" },
|
||||
{
|
||||
label: I18n.t("admin.products_page.columns_selector.inherits_properties"),
|
||||
value: "inherits_properties"
|
||||
},
|
||||
{ label: I18n.t("admin.products_page.columns_selector.available_on"), value: "available_on" },
|
||||
{ label: I18n.t("admin.products_page.columns_selector.import_date"), value: "import_date" }
|
||||
].sort do |a, b|
|
||||
a[:label] <=> b[:label]
|
||||
end.freeze
|
||||
|
||||
PER_PAGE_VALUE = [10, 25, 50, 100].freeze
|
||||
PER_PAGE = PER_PAGE_VALUE.map { |value| { label: value, value: value } }
|
||||
NAME_COLUMN = {
|
||||
label: I18n.t("admin.products_page.columns.name"), value: "name", sortable: true
|
||||
}.freeze
|
||||
|
||||
def initialize(user:)
|
||||
super
|
||||
@user = user
|
||||
@selectable_columns = SELECTABLE_COLUMNS
|
||||
@columns_selected = ['unit', 'price', 'on_hand', 'category', 'import_date']
|
||||
@per_page = PER_PAGE
|
||||
@per_page_selected = [10]
|
||||
@categories = [{ label: "All", value: "all" }] +
|
||||
Spree::Taxon.order(:name)
|
||||
.map { |taxon| { label: taxon.name, value: taxon.id.to_s } }
|
||||
@categories_selected = ["all"]
|
||||
@producers = [{ label: "All", value: "all" }] +
|
||||
OpenFoodNetwork::Permissions.new(@user)
|
||||
.managed_product_enterprises.is_primary_producer.by_name
|
||||
.map { |producer| { label: producer.name, value: producer.id.to_s } }
|
||||
@producers_selected = ["all"]
|
||||
@page = 1
|
||||
@sort = { column: "name", direction: "asc" }
|
||||
@search_term = ""
|
||||
end
|
||||
|
||||
# any change on a "reflex_data_attributes" (defined in the template) will trigger a re render
|
||||
def before_render
|
||||
fetch_products
|
||||
refresh_columns
|
||||
end
|
||||
|
||||
# Element refers to the component the data is set on
|
||||
def search_term
|
||||
# Element is SearchInputComponent
|
||||
@search_term = element.dataset['value']
|
||||
end
|
||||
|
||||
def toggle_column
|
||||
# Element is SelectorComponent
|
||||
column = element.dataset['value']
|
||||
@columns_selected = if @columns_selected.include?(column)
|
||||
@columns_selected - [column]
|
||||
else
|
||||
@columns_selected + [column]
|
||||
end
|
||||
end
|
||||
|
||||
def click_sort
|
||||
# Element is TableHeaderComponent
|
||||
@sort = {
|
||||
column: element.dataset['sort-value'],
|
||||
direction: element.dataset['sort-direction'] == "asc" ? "desc" : "asc"
|
||||
}
|
||||
end
|
||||
|
||||
def toggle_per_page
|
||||
# Element is SelectorComponent
|
||||
selected = element.dataset['value'].to_i
|
||||
@per_page_selected = [selected] if PER_PAGE_VALUE.include?(selected)
|
||||
end
|
||||
|
||||
def toggle_category
|
||||
# Element is SelectorWithFilterComponent
|
||||
category_clicked = element.dataset['value']
|
||||
@categories_selected = toggle_selector_with_filter(category_clicked, @categories_selected)
|
||||
end
|
||||
|
||||
def toggle_producer
|
||||
# Element is SelectorWithFilterComponent
|
||||
producer_clicked = element.dataset['value']
|
||||
@producers_selected = toggle_selector_with_filter(producer_clicked, @producers_selected)
|
||||
end
|
||||
|
||||
def change_page
|
||||
# Element is PaginationComponent
|
||||
page = element.dataset['page'].to_i
|
||||
@page = page if page > 0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_columns
|
||||
@columns = @columns_selected.map do |column|
|
||||
{
|
||||
label: I18n.t("admin.products_page.columns.#{column}"),
|
||||
value: column,
|
||||
sortable: SORTABLE_COLUMNS.include?(column)
|
||||
}
|
||||
end.sort! { |a, b| a[:label] <=> b[:label] }
|
||||
@columns.unshift(NAME_COLUMN)
|
||||
end
|
||||
|
||||
def toggle_selector_with_filter(clicked, selected)
|
||||
selected = if selected.include?(clicked)
|
||||
selected - [clicked]
|
||||
else
|
||||
selected + [clicked]
|
||||
end
|
||||
|
||||
if clicked == "all" || selected.empty?
|
||||
selected = ["all"]
|
||||
elsif selected.include?("all") && selected.length > 1
|
||||
selected -= ["all"]
|
||||
end
|
||||
selected
|
||||
end
|
||||
|
||||
def fetch_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(@user).editable_products.merge(product_scope)
|
||||
@products = product_query.ransack(ransack_query).result
|
||||
@pagy, @products = pagy(@products, items: @per_page_selected.first, page: @page)
|
||||
end
|
||||
|
||||
def product_scope
|
||||
scope = if @user.has_spree_role?("admin") || @user.enterprises.present?
|
||||
Spree::Product
|
||||
else
|
||||
Spree::Product.active
|
||||
end
|
||||
|
||||
scope.includes(product_query_includes)
|
||||
end
|
||||
|
||||
def ransack_query
|
||||
query = { s: "#{@sort[:column]} #{@sort[:direction]}" }
|
||||
|
||||
query = if @producers_selected.include?("all")
|
||||
query.merge({ supplier_id_eq: "" })
|
||||
else
|
||||
query.merge({ supplier_id_in: @producers_selected })
|
||||
end
|
||||
|
||||
query = query.merge({ name_cont: @search_term }) if @search_term.present?
|
||||
|
||||
if @categories_selected.include?("all")
|
||||
query.merge({ primary_taxon_id_eq: "" })
|
||||
else
|
||||
query.merge({ primary_taxon_id_in: @categories_selected })
|
||||
end
|
||||
end
|
||||
|
||||
def product_query_includes
|
||||
[
|
||||
:image,
|
||||
variants: [
|
||||
:default_price,
|
||||
:stock_locations,
|
||||
:stock_items,
|
||||
:variant_overrides
|
||||
]
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
= component_controller(class: "products-table") do
|
||||
.products-table-form
|
||||
.products-table-form_filter_results
|
||||
= render(SearchInputComponent.new(value: @search_term, data: reflex_data_attributes(:search_term)))
|
||||
.products-table-form_categories_selector
|
||||
= render(SelectorWithFilterComponent.new(title: t("admin.products_page.filters.categories.title"), selected: @categories_selected, items: @categories, data: reflex_data_attributes(:toggle_category), selected_items_i18n_key: "admin.products_page.filters.categories.selected_categories"))
|
||||
.products-table-form_producers_selector
|
||||
= render(SelectorWithFilterComponent.new(title: t("admin.products_page.filters.producers.title"), selected: @producers_selected, items: @producers, data: reflex_data_attributes(:toggle_producer), selected_items_i18n_key: "admin.products_page.filters.producers.selected_producers"))
|
||||
.products-table-form_per-page_selector
|
||||
= render(SelectorComponent.new(title: t('admin.products_page.filters.per_page', count: @per_page_selected[0]), selected: @per_page_selected, items: @per_page, data: reflex_data_attributes(:toggle_per_page)))
|
||||
.products-table-form_columns_selector
|
||||
= render(SelectorComponent.new(title: t("admin.products_page.filters.columns"), selected: @columns_selected, items: @selectable_columns, data: reflex_data_attributes(:toggle_column)))
|
||||
|
||||
.products-table_table
|
||||
%table
|
||||
= render(TableHeaderComponent.new(columns: @columns, sort: @sort, data: reflex_data_attributes(:click_sort)))
|
||||
%tbody
|
||||
= render(ProductComponent.with_collection(@products, columns: @columns))
|
||||
|
||||
.products-table-form_pagination
|
||||
= render(PaginationComponent.new(pagy: @pagy, data: reflex_data_attributes(:change_page)))
|
||||
@@ -0,0 +1,47 @@
|
||||
.products-table {
|
||||
.products-table-form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
|
||||
grid-gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.products-table_table {
|
||||
box-shadow: 0 10px 10px -1px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.products-table-form_pagination {
|
||||
position: relative;
|
||||
top: -15px;
|
||||
|
||||
nav, .pagination {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.products-table.loading {
|
||||
.products-table-form_pagination, .products-table_table {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.products-table_table {
|
||||
&:before {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50px 50px;
|
||||
background-image: url("../images/spinning-circles.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/components/search_input_component.rb
Normal file
9
app/components/search_input_component.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SearchInputComponent < ViewComponentReflex::Component
|
||||
def initialize(value: nil, data: {})
|
||||
super
|
||||
@value = value
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
= component_controller do
|
||||
%div.search-input
|
||||
%input{type: 'text', placeholder: t("components.search_input.placeholder"), id: 'search_query', data: {action: 'debounced:input->search-input#search'}, value: @value}
|
||||
.search-button{data: @data}
|
||||
%i.fa.fa-search
|
||||
@@ -0,0 +1,23 @@
|
||||
.search-input {
|
||||
border: 1px solid $disabled-light;
|
||||
height: 3em;
|
||||
display: flex;
|
||||
line-height: 3em;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
border: none;
|
||||
height: 3em;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
padding-right: 10px;
|
||||
padding-left: 5px;
|
||||
cursor: pointer;
|
||||
color: $color-4;
|
||||
}
|
||||
|
||||
}
|
||||
17
app/components/selector_component.rb
Normal file
17
app/components/selector_component.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SelectorComponent < ViewComponentReflex::Component
|
||||
def initialize(title:, selected:, items:, data: {})
|
||||
super
|
||||
@title = title
|
||||
@items = items.map do |item|
|
||||
{
|
||||
label: item[:label],
|
||||
value: item[:value],
|
||||
selected: selected.include?(item[:value])
|
||||
}
|
||||
end
|
||||
@selected = selected
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
= component_controller do
|
||||
.selector.selector-close
|
||||
.selector-main{ data: { action: "click->selector#toggle" } }
|
||||
.selector-main-title
|
||||
= @title
|
||||
.selector-arrow
|
||||
.selector-wrapper
|
||||
.selector-items
|
||||
- @items.each do |item|
|
||||
.selector-item{ class: ("selected" if item[:selected]), data: @data, "data-value": item[:value] }
|
||||
= item[:label]
|
||||
86
app/components/selector_component/selector_component.scss
Normal file
86
app/components/selector_component/selector_component.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
.selector {
|
||||
position: relative;
|
||||
|
||||
.selector-main {
|
||||
border: 1px solid $disabled-light;
|
||||
height: 3em;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.selector-main-title {
|
||||
line-height: 3em;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.selector-arrow {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
height: 3em;
|
||||
width: 1.5em;
|
||||
top: -1px;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
margin-top: -5px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid $disabled-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selector-wrapper {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
margin-top: -1px;
|
||||
border: 1px solid $disabled-light;
|
||||
|
||||
.selector-items {
|
||||
overflow-y: auto;
|
||||
min-height: 6em;
|
||||
|
||||
.selector-item {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border-bottom: 1px solid $disabled-light;
|
||||
position: relative;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
&:after {
|
||||
content: "✓";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.selector-close {
|
||||
.selector-wrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
app/components/selector_with_filter_component.rb
Normal file
11
app/components/selector_with_filter_component.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SelectorWithFilterComponent < SelectorComponent
|
||||
def initialize(title:, selected:, items:, data: {},
|
||||
selected_items_i18n_key: 'components.selector_with_filter.selected_items')
|
||||
super(title: title, selected: selected, items: items, data: data)
|
||||
@selected_items = items.select { |item| @selected.include?(item[:value]) }
|
||||
@selected_items_i18n_key = selected_items_i18n_key
|
||||
@items = items
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
= component_controller do
|
||||
.super-selector.selector.selector-close
|
||||
.selector-main{ data: { action: "click->selector-with-filter#toggle" } }
|
||||
.super-selector-label
|
||||
= @title
|
||||
.super-selector-selected-items
|
||||
- case @selected_items.length
|
||||
- when 1, 2
|
||||
- @selected_items.each do |item|
|
||||
.super-selector-selected-item
|
||||
= item[:label]
|
||||
- else
|
||||
.super-selector-selected-item
|
||||
= t(@selected_items_i18n_key, count: @selected_items.length)
|
||||
.selector-arrow
|
||||
.selector-wrapper
|
||||
.super-selector-search
|
||||
%input{type: "text", placeholder: t("components.selector_with_filter.search_placeholder"), data: { action: "debounced:input->selector-with-filter#filter" } }
|
||||
.selector-items
|
||||
- @items.each do |item|
|
||||
.selector-item{ class: ("selected" if item[:selected]), data: @data.merge({ "selector-with-filter-target": "items" }), "data-value": item[:value] }
|
||||
= item[:label]
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
.super-selector {
|
||||
position: relative;
|
||||
|
||||
.selector-main {
|
||||
.super-selector-label {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
position: absolute;
|
||||
top: -1em;
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.super-selector-selected-items {
|
||||
margin-left: 5px;
|
||||
margin-right: 2em;
|
||||
margin-top: 7px;
|
||||
display: flex;
|
||||
|
||||
.super-selector-selected-item {
|
||||
border: 1px solid $pale-blue;
|
||||
background-color: $spree-light-blue;
|
||||
border-radius: 20px;
|
||||
height: 2em;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
padding-top: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-wrapper {
|
||||
.super-selector-search {
|
||||
border-bottom: 1px solid $disabled-light;
|
||||
padding: 10px 5px;
|
||||
|
||||
input {
|
||||
border: 1px solid $disabled-light;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
app/components/table_header_component.rb
Normal file
10
app/components/table_header_component.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TableHeaderComponent < ViewComponentReflex::Component
|
||||
def initialize(columns:, sort:, data: {})
|
||||
super
|
||||
@columns = columns
|
||||
@sort = sort
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
= component_controller do
|
||||
%thead.table-header
|
||||
%tr
|
||||
- @columns.each do |column|
|
||||
%th{class: (column[:sortable] ? "th-sortable " : "" ) + (@sort[:column] == column[:value] ? " th-sorted-#{@sort[:direction]}" : ""), data: (@data if column[:sortable] == true), "data-sort-value": column[:value], "data-sort-direction": @sort[:direction]}
|
||||
= column[:label]
|
||||
@@ -0,0 +1,23 @@
|
||||
thead.table-header {
|
||||
th {
|
||||
&.th-sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.th-sorted-asc, &.th-sorted-desc {
|
||||
&:after {
|
||||
display: inline-block;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
&.th-sorted-asc {
|
||||
&:after {
|
||||
content: "⇧";
|
||||
}
|
||||
}
|
||||
&.th-sorted-desc {
|
||||
&:after {
|
||||
content: "⇩";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ module Admin
|
||||
@line_items = order_permissions.
|
||||
editable_line_items.where(order_id: orders).
|
||||
includes(:variant).
|
||||
ransack(params[:q]).result.order(:id)
|
||||
ransack(params[:q]).result
|
||||
|
||||
@pagy, @line_items = pagy(@line_items) if pagination_required?
|
||||
|
||||
@@ -34,8 +34,7 @@ module Admin
|
||||
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
order.with_lock do
|
||||
if order.contents.update_item(@line_item, line_item_params)
|
||||
# No Content, does not trigger ng resource auto-update
|
||||
render body: nil, status: :no_content
|
||||
render body: nil, status: :no_content # No Content, does not trigger ng resource auto-update
|
||||
else
|
||||
render json: { errors: @line_item.errors }, status: :precondition_failed
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module Admin
|
||||
respond_override update: { json: {
|
||||
success: lambda {
|
||||
tag_rule_mapping = TagRule.mapping_for(Enterprise.where(id: @customer.enterprise))
|
||||
render_as_json @customer, tag_rule_mapping:
|
||||
render_as_json @customer, tag_rule_mapping: tag_rule_mapping
|
||||
},
|
||||
failure: lambda {
|
||||
render json: { errors: @customer.errors.full_messages },
|
||||
@@ -25,7 +25,7 @@ module Admin
|
||||
format.json do
|
||||
render json: @collection,
|
||||
each_serializer: ::Api::Admin::CustomerWithBalanceSerializer,
|
||||
tag_rule_mapping:,
|
||||
tag_rule_mapping: tag_rule_mapping,
|
||||
customer_tags: customer_tags_by_id
|
||||
end
|
||||
end
|
||||
@@ -36,13 +36,11 @@ module Admin
|
||||
end
|
||||
|
||||
def create
|
||||
@customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
|
||||
|
||||
@customer = Customer.new(customer_params)
|
||||
if user_can_create_customer?
|
||||
@customer.created_manually = true
|
||||
if @customer.save
|
||||
tag_rule_mapping = TagRule.mapping_for(Enterprise.where(id: @customer.enterprise))
|
||||
render_as_json @customer, tag_rule_mapping:
|
||||
render_as_json @customer, tag_rule_mapping: tag_rule_mapping
|
||||
else
|
||||
render json: { errors: @customer.errors.full_messages }, status: :bad_request
|
||||
end
|
||||
@@ -85,7 +83,7 @@ module Admin
|
||||
def customers
|
||||
return @customers if @customers.present?
|
||||
|
||||
@customers = Customer.visible.managed_by(spree_current_user)
|
||||
@customers = Customer.managed_by(spree_current_user)
|
||||
return @customers if params[:enterprise_id].blank?
|
||||
|
||||
@customers = @customers.where(enterprise_id: params[:enterprise_id])
|
||||
|
||||
@@ -6,6 +6,8 @@ module Admin
|
||||
class EnterpriseFeesController < Admin::ResourceController
|
||||
before_action :load_enterprise_fee_set, only: :index
|
||||
before_action :load_data
|
||||
before_action :check_enterprise_fee_input, only: [:bulk_update]
|
||||
before_action :check_calculators_compatibility_with_taxes, only: [:bulk_update]
|
||||
|
||||
def index
|
||||
@include_calculators = params[:include_calculators].present?
|
||||
@@ -34,8 +36,7 @@ module Admin
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
# Forms has strong parameters, so we don't need to validate them in controller
|
||||
@enterprise_fee_set = EnterpriseFeesBulkUpdate.new(params)
|
||||
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
|
||||
|
||||
if @enterprise_fee_set.save
|
||||
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
|
||||
@@ -48,7 +49,7 @@ module Admin
|
||||
private
|
||||
|
||||
def load_enterprise_fee_set
|
||||
@enterprise_fee_set = Sets::EnterpriseFeeSet.new collection:
|
||||
@enterprise_fee_set = Sets::EnterpriseFeeSet.new collection: collection
|
||||
end
|
||||
|
||||
def load_data
|
||||
@@ -61,7 +62,9 @@ module Admin
|
||||
when :for_order_cycle
|
||||
order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
|
||||
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
|
||||
order_cycle ||= OrderCycle.new(coordinator:) if coordinator.present?
|
||||
if order_cycle.nil? && coordinator.present?
|
||||
order_cycle = OrderCycle.new(coordinator: coordinator)
|
||||
end
|
||||
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user,
|
||||
order_cycle).visible_enterprises
|
||||
EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name')
|
||||
@@ -88,5 +91,47 @@ module Admin
|
||||
|
||||
main_app.admin_enterprise_fees_path
|
||||
end
|
||||
|
||||
def enterprise_fee_bulk_params
|
||||
params.require(:sets_enterprise_fee_set).permit(
|
||||
collection_attributes: [
|
||||
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
|
||||
:inherits_tax_category, :calculator_type,
|
||||
{ calculator_attributes: PermittedAttributes::Calculator.attributes }
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check_enterprise_fee_input
|
||||
enterprise_fee_bulk_params['collection_attributes'].each do |_, fee_row|
|
||||
enterprise_fees = fee_row['calculator_attributes']&.slice(
|
||||
:preferred_flat_percent, :preferred_amount,
|
||||
:preferred_first_item, :preferred_additional_item,
|
||||
:preferred_minimal_amount, :preferred_normal_amount,
|
||||
:preferred_discount_amount, :preferred_per_unit
|
||||
)
|
||||
|
||||
next unless enterprise_fees
|
||||
|
||||
enterprise_fees.each do |_, enterprise_amount|
|
||||
unless enterprise_amount.nil? || Float(enterprise_amount, exception: false)
|
||||
flash[:error] = I18n.t(:calculator_preferred_value_error)
|
||||
return redirect_to redirect_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_calculators_compatibility_with_taxes
|
||||
enterprise_fee_bulk_params['collection_attributes'].each do |_, enterprise_fee|
|
||||
next unless enterprise_fee['inherits_tax_category'] == "true"
|
||||
next unless EnterpriseFee::PER_ORDER_CALCULATORS.include?(enterprise_fee['calculator_type'])
|
||||
|
||||
flash[:error] = I18n.t(
|
||||
'activerecord.errors.models.enterprise_fee.inherit_tax_requires_per_item_calculator'
|
||||
)
|
||||
return redirect_to redirect_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ module Admin
|
||||
# The ! version is important to raise a RecordNotFound error.
|
||||
def find_resource
|
||||
permalink = params[:id] || params[:enterprise_group_id]
|
||||
EnterpriseGroup.find_by!(permalink:)
|
||||
EnterpriseGroup.find_by!(permalink: permalink)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -16,8 +16,7 @@ module Admin
|
||||
@enterprise_relationship = EnterpriseRelationship.new enterprise_relationship_params
|
||||
|
||||
if @enterprise_relationship.save
|
||||
render plain: Api::Admin::EnterpriseRelationshipSerializer
|
||||
.new(@enterprise_relationship).to_json
|
||||
render plain: Api::Admin::EnterpriseRelationshipSerializer.new(@enterprise_relationship).to_json
|
||||
else
|
||||
render status: :bad_request,
|
||||
json: { errors: @enterprise_relationship.errors.full_messages.join(', ') }
|
||||
|
||||
@@ -38,7 +38,7 @@ module Admin
|
||||
format.html
|
||||
format.json {
|
||||
render_as_json @collection, ams_prefix: params[:ams_prefix],
|
||||
spree_current_user:
|
||||
spree_current_user: spree_current_user
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -47,12 +47,12 @@ module Admin
|
||||
@object = Enterprise.where(permalink: params[:id]).
|
||||
includes(users: [:ship_address, :bill_address]).first
|
||||
@object.build_custom_tab if @object.custom_tab.nil?
|
||||
return unless params[:stimulus]
|
||||
|
||||
@enterprise.is_primary_producer = params[:is_primary_producer]
|
||||
@enterprise.sells = params[:enterprise_sells]
|
||||
render cable_ready: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
|
||||
.morph("#permalink", partial("admin/enterprises/form/permalink"))
|
||||
if params[:stimulus]
|
||||
@enterprise.is_primary_producer = params[:is_primary_producer]
|
||||
@enterprise.sells = params[:enterprise_sells]
|
||||
render cable_ready: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
|
||||
.morph("#permalink", partial("admin/enterprises/form/permalink"))
|
||||
end
|
||||
end
|
||||
|
||||
def welcome
|
||||
@@ -63,7 +63,6 @@ module Admin
|
||||
tag_rules_attributes = params[object_name].delete :tag_rules_attributes
|
||||
update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present?
|
||||
update_enterprise_notifications
|
||||
update_vouchers
|
||||
|
||||
delete_custom_tab if params[:custom_tab] == 'false'
|
||||
|
||||
@@ -73,7 +72,7 @@ module Admin
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
format.json {
|
||||
render_as_json @object, ams_prefix: 'index', spree_current_user:
|
||||
render_as_json @object, ams_prefix: 'index', spree_current_user: spree_current_user
|
||||
}
|
||||
end
|
||||
else
|
||||
@@ -93,7 +92,7 @@ module Admin
|
||||
return render :welcome, layout: "spree/layouts/bare_admin"
|
||||
end
|
||||
|
||||
attributes = { sells: register_params[:sells], visible: "only_through_links" }
|
||||
attributes = { sells: register_params[:sells], visible: true }
|
||||
|
||||
if @enterprise.update(attributes)
|
||||
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
|
||||
@@ -122,12 +121,8 @@ module Admin
|
||||
def for_order_cycle
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render(
|
||||
json: @collection,
|
||||
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer,
|
||||
order_cycle: @order_cycle,
|
||||
spree_current_user:
|
||||
)
|
||||
render json: @collection,
|
||||
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, order_cycle: @order_cycle, spree_current_user: spree_current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -136,7 +131,7 @@ module Admin
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render_as_json @collection, ams_prefix: params[:ams_prefix] || 'basic',
|
||||
spree_current_user:
|
||||
spree_current_user: spree_current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -183,7 +178,9 @@ module Admin
|
||||
when :for_order_cycle
|
||||
@order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
|
||||
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
|
||||
@order_cycle ||= OrderCycle.new(coordinator:) if coordinator.present?
|
||||
if @order_cycle.nil? && coordinator.present?
|
||||
@order_cycle = OrderCycle.new(coordinator: coordinator)
|
||||
end
|
||||
|
||||
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle)
|
||||
.visible_enterprises
|
||||
@@ -264,44 +261,32 @@ module Admin
|
||||
def update_enterprise_notifications
|
||||
user_id = params[:receives_notifications].to_i
|
||||
|
||||
return unless user_id.positive? && @enterprise.user_ids.include?(user_id)
|
||||
|
||||
@enterprise.update_contact(user_id)
|
||||
end
|
||||
|
||||
def update_vouchers
|
||||
params_voucher_ids = params[:enterprise][:voucher_ids].to_a.map(&:to_i)
|
||||
voucher_ids = @enterprise.vouchers.map(&:id)
|
||||
deleted_voucher_ids = @enterprise.vouchers.only_deleted.map(&:id)
|
||||
|
||||
vouchers_to_destroy = voucher_ids - params_voucher_ids
|
||||
Voucher.where(id: vouchers_to_destroy).destroy_all if vouchers_to_destroy.present?
|
||||
|
||||
vouchers_to_restore = deleted_voucher_ids.intersection(params_voucher_ids)
|
||||
Voucher.restore(vouchers_to_restore) if vouchers_to_restore.present?
|
||||
if user_id.positive? && @enterprise.user_ids.include?(user_id)
|
||||
@enterprise.update_contact(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def create_calculator_for(rule, attrs)
|
||||
return unless attrs[:calculator_type].present? && attrs[:calculator_attributes].present?
|
||||
|
||||
rule.update(calculator_type: attrs[:calculator_type])
|
||||
attrs[:calculator_attributes].merge!( id: rule.calculator.id )
|
||||
if attrs[:calculator_type].present? && attrs[:calculator_attributes].present?
|
||||
rule.update(calculator_type: attrs[:calculator_type])
|
||||
attrs[:calculator_attributes].merge!( id: rule.calculator.id )
|
||||
end
|
||||
end
|
||||
|
||||
def check_can_change_bulk_sells
|
||||
return if spree_current_user.admin?
|
||||
|
||||
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
|
||||
unless spree_current_user == Enterprise.find_by(id: enterprise_params[:id]).owner
|
||||
enterprise_params.delete :sells
|
||||
unless spree_current_user.admin?
|
||||
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
|
||||
unless spree_current_user == Enterprise.find_by(id: enterprise_params[:id]).owner
|
||||
enterprise_params.delete :sells
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_can_change_sells
|
||||
return if spree_current_user.admin? || spree_current_user == @enterprise.owner
|
||||
|
||||
enterprise_params.delete :sells
|
||||
unless spree_current_user.admin? || spree_current_user == @enterprise.owner
|
||||
enterprise_params.delete :sells
|
||||
end
|
||||
end
|
||||
|
||||
def override_owner
|
||||
@@ -309,31 +294,31 @@ module Admin
|
||||
end
|
||||
|
||||
def override_sells
|
||||
return if spree_current_user.admin?
|
||||
|
||||
has_hub = spree_current_user.owned_enterprises.is_hub.any?
|
||||
new_enterprise_is_producer = Enterprise.new(enterprise_params).is_primary_producer
|
||||
enterprise_params[:sells] = has_hub && !new_enterprise_is_producer ? 'any' : 'none'
|
||||
unless spree_current_user.admin?
|
||||
has_hub = spree_current_user.owned_enterprises.is_hub.any?
|
||||
new_enterprise_is_producer = Enterprise.new(enterprise_params).is_primary_producer
|
||||
enterprise_params[:sells] = has_hub && !new_enterprise_is_producer ? 'any' : 'none'
|
||||
end
|
||||
end
|
||||
|
||||
def check_can_change_owner
|
||||
return if ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
|
||||
|
||||
enterprise_params.delete :owner_id
|
||||
end
|
||||
|
||||
def check_can_change_bulk_owner
|
||||
return if spree_current_user.admin?
|
||||
|
||||
bulk_params[:collection_attributes].each do |_i, enterprise_params|
|
||||
unless ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
|
||||
enterprise_params.delete :owner_id
|
||||
end
|
||||
end
|
||||
|
||||
def check_can_change_managers
|
||||
return if ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
|
||||
def check_can_change_bulk_owner
|
||||
unless spree_current_user.admin?
|
||||
bulk_params[:collection_attributes].each do |_i, enterprise_params|
|
||||
enterprise_params.delete :owner_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
enterprise_params.delete :user_ids
|
||||
def check_can_change_managers
|
||||
unless ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
|
||||
enterprise_params.delete :user_ids
|
||||
end
|
||||
end
|
||||
|
||||
def strip_new_properties
|
||||
|
||||
@@ -182,17 +182,17 @@ module Admin
|
||||
end
|
||||
|
||||
def load_data_for_index
|
||||
return unless json_request?
|
||||
|
||||
# Split ransack params into all those that currently exist and new ones
|
||||
# to limit returned ocs to recent or undated
|
||||
orders_close_at_gt = raw_params[:q]&.delete(:orders_close_at_gt) || 31.days.ago
|
||||
raw_params[:q] = {
|
||||
g: [raw_params.delete(:q) || {}, { m: 'or',
|
||||
orders_close_at_gt:,
|
||||
orders_close_at_null: true }]
|
||||
}
|
||||
@collection = collection
|
||||
if json_request?
|
||||
# Split ransack params into all those that currently exist and new ones
|
||||
# to limit returned ocs to recent or undated
|
||||
orders_close_at_gt = raw_params[:q]&.delete(:orders_close_at_gt) || 31.days.ago
|
||||
raw_params[:q] = {
|
||||
g: [raw_params.delete(:q) || {}, { m: 'or',
|
||||
orders_close_at_gt: orders_close_at_gt,
|
||||
orders_close_at_null: true }]
|
||||
}
|
||||
@collection = collection
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_after_update_path
|
||||
@@ -245,10 +245,10 @@ module Admin
|
||||
|
||||
order_cycle_params.delete :coordinator_id
|
||||
|
||||
return if Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator)
|
||||
|
||||
order_cycle_params.delete_if do |k, _v|
|
||||
[:name, :orders_open_at, :orders_close_at].include? k.to_sym
|
||||
unless Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator)
|
||||
order_cycle_params.delete_if do |k, _v|
|
||||
[:name, :orders_open_at, :orders_close_at].include? k.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -37,14 +37,8 @@ module Admin
|
||||
end
|
||||
|
||||
def reset_absent_products
|
||||
@importer = ProductImport::ProductImporter.new(
|
||||
File.new(file_path),
|
||||
spree_current_user,
|
||||
import_into: params[:import_into],
|
||||
enterprises_to_reset: params[:enterprises_to_reset],
|
||||
updated_ids: params[:updated_ids],
|
||||
settings: params[:settings]
|
||||
)
|
||||
@importer = ProductImport::ProductImporter.new(File.new(file_path),
|
||||
spree_current_user, import_into: params[:import_into], enterprises_to_reset: params[:enterprises_to_reset], updated_ids: params[:updated_ids], settings: params[:settings])
|
||||
|
||||
if params.key?(:enterprises_to_reset) && params.key?(:updated_ids)
|
||||
@importer.reset_absent(params[:updated_ids])
|
||||
@@ -56,19 +50,14 @@ module Admin
|
||||
private
|
||||
|
||||
def validate_upload_presence
|
||||
return if params[:file] || (params[:filepath] && File.exist?(params[:filepath]))
|
||||
|
||||
redirect_to '/admin/product_import', notice: I18n.t(:product_import_file_not_found_notice)
|
||||
unless params[:file] || (params[:filepath] && File.exist?(params[:filepath]))
|
||||
redirect_to '/admin/product_import', notice: I18n.t(:product_import_file_not_found_notice)
|
||||
end
|
||||
end
|
||||
|
||||
def process_data(method)
|
||||
@importer = ProductImport::ProductImporter.new(
|
||||
File.new(file_path),
|
||||
spree_current_user,
|
||||
start: params[:start],
|
||||
end: params[:end],
|
||||
settings: params[:settings]
|
||||
)
|
||||
@importer = ProductImport::ProductImporter.new(File.new(file_path),
|
||||
spree_current_user, start: params[:start], end: params[:end], settings: params[:settings])
|
||||
|
||||
begin
|
||||
@importer.public_send("#{method}_entries")
|
||||
@@ -90,11 +79,11 @@ module Admin
|
||||
end
|
||||
|
||||
def check_spreadsheet_has_data(importer)
|
||||
return if importer.item_count
|
||||
|
||||
redirect_to '/admin/product_import',
|
||||
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
|
||||
true
|
||||
unless importer.item_count
|
||||
redirect_to '/admin/product_import',
|
||||
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def save_uploaded_file(upload)
|
||||
@@ -139,8 +128,7 @@ module Admin
|
||||
end
|
||||
|
||||
def raise_invalid_file_path
|
||||
redirect_to '/admin/product_import',
|
||||
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
|
||||
redirect_to '/admin/product_import', notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
|
||||
raise 'Invalid File Path'
|
||||
end
|
||||
TEMP_FILE_PATH_REGEX = %r{^/tmp/product_import[A-Za-z0-9-]*/import\.csv$}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ProductsV3Controller < Spree::Admin::BaseController
|
||||
class ProductsController < Spree::Admin::BaseController
|
||||
def index; end
|
||||
end
|
||||
end
|
||||
@@ -13,7 +13,7 @@ module Admin
|
||||
if @proxy_order.cancel
|
||||
render_as_json @proxy_order
|
||||
else
|
||||
render json: { errors: [t('.could_not_cancel_the_order')] },
|
||||
render json: { errors: [t('admin.proxy_orders.cancel.could_not_cancel_the_order')] },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
@@ -22,7 +22,7 @@ module Admin
|
||||
if @proxy_order.resume
|
||||
render_as_json @proxy_order
|
||||
else
|
||||
render json: { errors: [t('.could_not_resume_the_order')] },
|
||||
render json: { errors: [t('admin.proxy_orders.resume.could_not_resume_the_order')] },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,29 +22,24 @@ module Admin
|
||||
def show
|
||||
@report = report_class.new(spree_current_user, params, render: render_data?)
|
||||
|
||||
@background_reports = OpenFoodNetwork::FeatureToggle
|
||||
.enabled?(:background_reports, spree_current_user)
|
||||
|
||||
if @background_reports && request.post?
|
||||
return background(report_format)
|
||||
end
|
||||
|
||||
if params[:report_format].present?
|
||||
export_report
|
||||
else
|
||||
show_report
|
||||
end
|
||||
rescue Timeout::Error
|
||||
render_timeout_error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def export_report
|
||||
send_data @report.render_as(report_format), filename: report_filename
|
||||
send_data render_report_as(report_format), filename: report_filename
|
||||
end
|
||||
|
||||
def show_report
|
||||
assign_view_data
|
||||
@table = @report.render_as(:html) if render_data?
|
||||
@table = render_report_as(:html) if render_data?
|
||||
render "show"
|
||||
end
|
||||
|
||||
@@ -55,39 +50,51 @@ module Admin
|
||||
@report_title = report_title
|
||||
@rendering_options = rendering_options
|
||||
@data = Reporting::FrontendData.new(spree_current_user)
|
||||
|
||||
variant_id_in = params[:variant_id_in]&.compact_blank
|
||||
load_selected_variant if variant_id_in.present?
|
||||
end
|
||||
|
||||
# Orders and Fulfillment Reports include a per product filter, load any selected product
|
||||
def load_selected_variant
|
||||
variant = Spree::Variant.find(params[:variant_id_in][0])
|
||||
@variant_serialized = Api::Admin::VariantSerializer.new(variant)
|
||||
end
|
||||
|
||||
def render_data?
|
||||
request.post?
|
||||
end
|
||||
|
||||
def background(format)
|
||||
cable_ready[ScopedChannel.for_id(params[:uuid])]
|
||||
.inner_html(
|
||||
selector: "#report-table",
|
||||
html: render_to_string(partial: "admin/reports/loading")
|
||||
).scroll_into_view(
|
||||
selector: "#report-table",
|
||||
block: "start"
|
||||
).broadcast
|
||||
def render_report_as(format)
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user)
|
||||
@blob = ReportBlob.create_for_upload_later!(report_filename)
|
||||
ReportJob.perform_later(
|
||||
report_class, spree_current_user, params, format, @blob
|
||||
)
|
||||
Timeout.timeout(max_wait_time) do
|
||||
sleep 1 until @blob.content_stored?
|
||||
end
|
||||
|
||||
blob = ReportBlob.create_for_upload_later!(report_filename)
|
||||
# This result has been rendered by Rails in safe mode already.
|
||||
@blob.result.html_safe # rubocop:disable Rails/OutputSafety
|
||||
else
|
||||
@report.render_as(format)
|
||||
end
|
||||
end
|
||||
|
||||
ReportJob.perform_later(
|
||||
report_class:, user: spree_current_user, params:,
|
||||
format:, blob:, channel: ScopedChannel.for_id(params[:uuid]),
|
||||
)
|
||||
def render_timeout_error
|
||||
assign_view_data
|
||||
if @blob
|
||||
@error = ".report_taking_longer_html"
|
||||
@error_url = @blob.expiring_service_url
|
||||
else
|
||||
@error = ".report_taking_longer"
|
||||
@error_url = ""
|
||||
end
|
||||
render "show"
|
||||
end
|
||||
|
||||
head :no_content
|
||||
def max_wait_time
|
||||
# This value is used by rack-timeout and nginx, usually 30 seconds in
|
||||
# staging and production:
|
||||
server_timeout = ENV.fetch("RACK_TIMEOUT_SERVICE_TIMEOUT", "15").to_f
|
||||
|
||||
# Zero disables the timeout:
|
||||
return 0 if server_timeout.zero?
|
||||
|
||||
# We want to time out earlier than nginx:
|
||||
server_timeout - 2.seconds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,6 +24,18 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @object.update(permitted_resource_params)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
else
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@object.attributes = permitted_resource_params
|
||||
if @object.save
|
||||
@@ -37,21 +49,9 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @object.update(permitted_resource_params)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
else
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
|
||||
def update_positions
|
||||
params[:positions].each do |id, index|
|
||||
model_class.where(id:).update_all(position: index)
|
||||
model_class.where(id: id).update_all(position: index)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
@@ -8,13 +8,15 @@ module Admin
|
||||
include PaperTrailLogging
|
||||
|
||||
before_action :adapt_params, only: [:update]
|
||||
before_action :editable_order_cycle_ids_for_create, only: [:create]
|
||||
before_action :editable_order_cycle_ids_for_update, only: [:update]
|
||||
before_action :check_dependent_subscriptions, only: [:destroy]
|
||||
|
||||
after_action :sync_subscriptions_for_update, only: :update
|
||||
|
||||
respond_to :json
|
||||
|
||||
OVERRIDE_RESPONSE = { json: {
|
||||
respond_override create: { json: {
|
||||
success: lambda {
|
||||
render_as_json @schedule,
|
||||
editable_schedule_ids: permissions.editable_schedules.pluck(:id)
|
||||
@@ -23,10 +25,17 @@ module Admin
|
||||
render json: { errors: @schedule.errors.full_messages },
|
||||
status: :unprocessable_entity
|
||||
}
|
||||
} }.freeze
|
||||
|
||||
respond_override create: OVERRIDE_RESPONSE
|
||||
respond_override update: OVERRIDE_RESPONSE
|
||||
} }
|
||||
respond_override update: { json: {
|
||||
success: lambda {
|
||||
render_as_json @schedule,
|
||||
editable_schedule_ids: permissions.editable_schedules.pluck(:id)
|
||||
},
|
||||
failure: lambda {
|
||||
render json: { errors: @schedule.errors.full_messages },
|
||||
status: :unprocessable_entity
|
||||
}
|
||||
} }
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
@@ -41,23 +50,22 @@ module Admin
|
||||
end
|
||||
|
||||
def create
|
||||
@schedule_form = ScheduleForm.new(params, spree_current_user, @schedule)
|
||||
return respond_with(@schedule) if params[:order_cycle_ids].blank?
|
||||
|
||||
@schedule.attributes = permitted_resource_params
|
||||
|
||||
if @schedule.save
|
||||
@schedule.order_cycle_ids = params[:order_cycle_ids]
|
||||
@schedule.save!
|
||||
|
||||
if @schedule_form.save
|
||||
flash[:success] = flash_message_for(@schedule, :successfully_created)
|
||||
@existing_order_cycle_ids = []
|
||||
sync_subscriptions_for_create
|
||||
|
||||
flash[:success] = flash_message_for(@schedule, :successfully_created)
|
||||
end
|
||||
|
||||
respond_with(@schedule)
|
||||
end
|
||||
|
||||
def update
|
||||
@existing_order_cycle_ids = @schedule.order_cycle_ids
|
||||
@object = ScheduleForm.new(params, spree_current_user, @schedule)
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
@@ -88,6 +96,37 @@ module Admin
|
||||
params[:schedule][:order_cycle_ids] = params[:order_cycle_ids]
|
||||
end
|
||||
|
||||
def editable_order_cycle_ids_for_create
|
||||
return unless params[:order_cycle_ids]
|
||||
|
||||
@existing_order_cycle_ids = []
|
||||
result = editable_order_cycles(params[:order_cycle_ids])
|
||||
|
||||
params[:order_cycle_ids] = result
|
||||
end
|
||||
|
||||
def editable_order_cycle_ids_for_update
|
||||
return unless params[:schedule][:order_cycle_ids]
|
||||
|
||||
@existing_order_cycle_ids = @schedule.order_cycle_ids
|
||||
result = editable_order_cycles(params[:schedule][:order_cycle_ids])
|
||||
|
||||
params[:schedule][:order_cycle_ids] = result
|
||||
@schedule.order_cycle_ids = result
|
||||
end
|
||||
|
||||
def editable_order_cycles(requested)
|
||||
permitted = OrderCycle
|
||||
.where(id: params[:order_cycle_ids] | @existing_order_cycle_ids)
|
||||
.merge(OrderCycle.managed_by(spree_current_user))
|
||||
.pluck(:id)
|
||||
result = @existing_order_cycle_ids
|
||||
result |= (requested & permitted) # add any requested & permitted ids
|
||||
# remove any existing and permitted ids that were not specifically requested
|
||||
result -= ((result & permitted) - requested)
|
||||
result
|
||||
end
|
||||
|
||||
def check_dependent_subscriptions
|
||||
return if Subscription.where(schedule_id: @schedule).empty?
|
||||
|
||||
@@ -101,14 +140,14 @@ module Admin
|
||||
@permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
end
|
||||
|
||||
def sync_subscriptions_for_create
|
||||
return unless params[:order_cycle_ids]
|
||||
def sync_subscriptions_for_update
|
||||
return unless params[:schedule][:order_cycle_ids] && @object.errors.blank?
|
||||
|
||||
sync_subscriptions
|
||||
end
|
||||
|
||||
def sync_subscriptions_for_update
|
||||
return unless params[:schedule][:order_cycle_ids] && @schedule.errors.blank?
|
||||
def sync_subscriptions_for_create
|
||||
return unless params[:order_cycle_ids]
|
||||
|
||||
sync_subscriptions
|
||||
end
|
||||
@@ -116,7 +155,6 @@ module Admin
|
||||
def sync_subscriptions
|
||||
removed_ids = @existing_order_cycle_ids - @schedule.order_cycle_ids
|
||||
new_ids = @schedule.order_cycle_ids - @existing_order_cycle_ids
|
||||
|
||||
return unless removed_ids.any? || new_ids.any?
|
||||
|
||||
subscriptions = Subscription.where(schedule_id: @schedule)
|
||||
|
||||
@@ -21,7 +21,7 @@ module Admin
|
||||
def update
|
||||
Spree::Config.set(settings_params.to_h)
|
||||
resource = t('admin.controllers.stripe_connect_settings.resource')
|
||||
flash[:success] = t(:successfully_updated, resource:)
|
||||
flash[:success] = t(:successfully_updated, resource: resource)
|
||||
redirect_to_edit
|
||||
end
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ module Admin
|
||||
if view_context.subscriptions_setup_complete?(@shops)
|
||||
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user)
|
||||
.includes([:distributors, :cached_incoming_exchanges])
|
||||
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user)
|
||||
.includes(:taggings)
|
||||
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).includes(:taggings)
|
||||
@payment_method_tags = payment_method_tags_by_id
|
||||
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user)
|
||||
else
|
||||
@@ -101,8 +100,7 @@ module Admin
|
||||
end
|
||||
|
||||
def load_shops
|
||||
@shops = Enterprise.managed_by(spree_current_user)
|
||||
.is_distributor.where(enable_subscriptions: true)
|
||||
@shops = Enterprise.managed_by(spree_current_user).is_distributor.where(enable_subscriptions: true)
|
||||
end
|
||||
|
||||
def load_form_data
|
||||
@@ -141,9 +139,7 @@ module Admin
|
||||
@open_orders_to_keep = @subscription.proxy_orders.placed_and_open.pluck(:id)
|
||||
return if @open_orders_to_keep.empty? || params[:open_orders] == 'keep'
|
||||
|
||||
render json: {
|
||||
errors: { open_orders: t('admin.subscriptions.confirm_cancel_open_orders_msg') }
|
||||
},
|
||||
render json: { errors: { open_orders: t('admin.subscriptions.confirm_cancel_open_orders_msg') } },
|
||||
status: :conflict
|
||||
end
|
||||
|
||||
@@ -151,9 +147,7 @@ module Admin
|
||||
return if params[:canceled_orders] == 'notified'
|
||||
return if @subscription.proxy_orders.active.canceled.empty?
|
||||
|
||||
render json: {
|
||||
errors: { canceled_orders: t('admin.subscriptions.resume_canceled_orders_msg') }
|
||||
},
|
||||
render json: { errors: { canceled_orders: t('admin.subscriptions.resume_canceled_orders_msg') } },
|
||||
status: :conflict
|
||||
end
|
||||
|
||||
|
||||
@@ -9,33 +9,22 @@ module Admin
|
||||
end
|
||||
|
||||
def create
|
||||
@voucher = Voucher.new(
|
||||
permitted_resource_params.merge(enterprise: @enterprise)
|
||||
)
|
||||
voucher_params = permitted_resource_params.merge(enterprise: @enterprise)
|
||||
@voucher = Voucher.create(voucher_params)
|
||||
|
||||
if @voucher.save
|
||||
flash[:success] = I18n.t(:successfully_created, resource: "Voucher")
|
||||
redirect_to edit_admin_enterprise_path(@enterprise, anchor: :vouchers_panel)
|
||||
redirect_to(
|
||||
"#{edit_admin_enterprise_path(@enterprise)}#vouchers_panel",
|
||||
flash: { success: flash_message_for(@voucher, :successfully_created) }
|
||||
)
|
||||
else
|
||||
render_error
|
||||
flash[:error] = @voucher.errors.full_messages.to_sentence
|
||||
render :new
|
||||
end
|
||||
rescue ActiveRecord::SubclassNotFound
|
||||
@voucher.errors.add(:type)
|
||||
render_error
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
# Rails unique validation doesn't work with soft deleted object, so we rescue the database
|
||||
# exception to display a nice message to the user
|
||||
@voucher.errors.add(:code, :taken)
|
||||
render_error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_error
|
||||
flash[:error] = @voucher.errors.full_messages.to_sentence
|
||||
render :new
|
||||
end
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = OpenFoodNetwork::Permissions
|
||||
.new(spree_current_user)
|
||||
@@ -44,7 +33,7 @@ module Admin
|
||||
end
|
||||
|
||||
def permitted_resource_params
|
||||
params.require(:voucher).permit(:code, :amount, :type)
|
||||
params.require(:voucher).permit(:code, :amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ module Api
|
||||
skip_authorization_check only: :index
|
||||
|
||||
def index
|
||||
@customers = current_api_user.customers.visible
|
||||
@customers = current_api_user.customers
|
||||
render json: @customers, each_serializer: CustomerSerializer
|
||||
end
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ module Api
|
||||
@enterprise.update!(attachment_name => nil)
|
||||
render json: @enterprise,
|
||||
serializer: Admin::EnterpriseSerializer,
|
||||
spree_current_user:
|
||||
spree_current_user: spree_current_user
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
@@ -74,7 +74,7 @@ module Api
|
||||
end
|
||||
|
||||
def override_visible
|
||||
enterprise_params[:visible] = "only_through_links"
|
||||
enterprise_params[:visible] = false
|
||||
end
|
||||
|
||||
def enterprise_params
|
||||
|
||||
@@ -5,6 +5,11 @@ module Api
|
||||
class OrdersController < Api::V0::BaseController
|
||||
include PaginationData
|
||||
|
||||
def show
|
||||
authorize! :read, order
|
||||
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
|
||||
end
|
||||
|
||||
def index
|
||||
authorize! :admin, Spree::Order
|
||||
|
||||
@@ -21,11 +26,6 @@ module Api
|
||||
}
|
||||
end
|
||||
|
||||
def show
|
||||
authorize! :read, order
|
||||
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
|
||||
end
|
||||
|
||||
def update
|
||||
authorize! :admin, order
|
||||
|
||||
|
||||
@@ -10,10 +10,12 @@ module Api
|
||||
respond_to :json
|
||||
DEFAULT_PER_PAGE = 15
|
||||
|
||||
before_action :set_default_available_on, only: :create
|
||||
|
||||
skip_authorization_check only: [:show, :bulk_products, :overridable]
|
||||
|
||||
def show
|
||||
@product = product_finder.find_product
|
||||
@product = find_product(params[:id])
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer
|
||||
end
|
||||
|
||||
@@ -21,16 +23,21 @@ module Api
|
||||
authorize! :create, Spree::Product
|
||||
@product = Spree::Product.new(product_params)
|
||||
|
||||
if @product.save
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
|
||||
else
|
||||
invalid_resource!(@product)
|
||||
begin
|
||||
if @product.save
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
|
||||
else
|
||||
invalid_resource!(@product)
|
||||
end
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
@product.permalink = nil
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize! :update, Spree::Product
|
||||
@product = product_finder.find_product
|
||||
@product = find_product(params[:id])
|
||||
if @product.update(product_params)
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :ok
|
||||
else
|
||||
@@ -40,20 +47,36 @@ module Api
|
||||
|
||||
def destroy
|
||||
authorize! :delete, Spree::Product
|
||||
@product = product_finder.find_product
|
||||
@product = find_product(params[:id])
|
||||
authorize! :delete, @product
|
||||
@product.destroy
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
|
||||
end
|
||||
|
||||
def bulk_products
|
||||
@products = product_finder.bulk_products
|
||||
product_query = OpenFoodNetwork::Permissions.
|
||||
new(current_api_user).
|
||||
editable_products.
|
||||
merge(product_scope)
|
||||
|
||||
if params[:import_date].present?
|
||||
product_query = product_query.
|
||||
imported_on(params[:import_date]).
|
||||
group_by_products_id
|
||||
end
|
||||
|
||||
@products = product_query.
|
||||
ransack(query_params_with_defaults).
|
||||
result
|
||||
|
||||
render_paged_products @products
|
||||
end
|
||||
|
||||
def overridable
|
||||
@products = product_finder.paged_products_for_producers
|
||||
producer_ids = OpenFoodNetwork::Permissions.new(current_api_user).
|
||||
variant_override_producers.by_name.select('enterprises.id')
|
||||
|
||||
@products = paged_products_for_producers producer_ids
|
||||
|
||||
render_paged_products @products, ::Api::Admin::ProductSimpleSerializer
|
||||
end
|
||||
@@ -62,7 +85,7 @@ module Api
|
||||
#
|
||||
def clone
|
||||
authorize! :create, Spree::Product
|
||||
original_product = product_finder.find_product_to_be_cloned
|
||||
original_product = find_product(params[:product_id])
|
||||
authorize! :update, original_product
|
||||
|
||||
@product = original_product.duplicate
|
||||
@@ -72,8 +95,39 @@ module Api
|
||||
|
||||
private
|
||||
|
||||
def product_finder
|
||||
ProductScopeQuery.new(current_api_user, params)
|
||||
def find_product(id)
|
||||
product_scope.find_by!(permalink: id.to_s)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
product_scope.find(id)
|
||||
end
|
||||
|
||||
def product_scope
|
||||
if current_api_user.has_spree_role?("admin") || current_api_user.enterprises.present?
|
||||
scope = Spree::Product
|
||||
if params[:show_deleted]
|
||||
scope = scope.with_deleted
|
||||
end
|
||||
else
|
||||
scope = Spree::Product.active
|
||||
end
|
||||
|
||||
scope.includes(product_query_includes)
|
||||
end
|
||||
|
||||
def product_query_includes
|
||||
[
|
||||
image: { attachment_attachment: :blob },
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides]
|
||||
]
|
||||
end
|
||||
|
||||
def paged_products_for_producers(producer_ids)
|
||||
Spree::Product.where(nil).
|
||||
merge(product_scope).
|
||||
includes(variants: [:product, :default_price, :stock_items]).
|
||||
where(supplier_id: producer_ids).
|
||||
by_producer.by_name.
|
||||
ransack(params[:q]).result
|
||||
end
|
||||
|
||||
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
|
||||
@@ -90,10 +144,18 @@ module Api
|
||||
}
|
||||
end
|
||||
|
||||
def query_params_with_defaults
|
||||
(params[:q] || {}).reverse_merge(s: 'created_at desc')
|
||||
end
|
||||
|
||||
def product_params
|
||||
@product_params ||=
|
||||
params.permit(product: PermittedAttributes::Product.attributes)[:product].to_h
|
||||
end
|
||||
|
||||
def set_default_available_on
|
||||
product_params[:available_on] ||= Time.zone.now
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,16 +44,14 @@ module Api
|
||||
|
||||
def ready
|
||||
authorize! :read, Spree::Shipment
|
||||
|
||||
unless @shipment.ready? || @shipment.can_ready?
|
||||
return render(
|
||||
json: { error: I18n.t(:cannot_ready, scope: "spree.api.shipment") },
|
||||
status: :unprocessable_entity
|
||||
)
|
||||
unless @shipment.ready?
|
||||
if @shipment.can_ready?
|
||||
@shipment.ready!
|
||||
else
|
||||
render(json: { error: I18n.t(:cannot_ready, scope: "spree.api.shipment") },
|
||||
status: :unprocessable_entity) && return
|
||||
end
|
||||
end
|
||||
|
||||
@shipment.ready! unless @shipment.ready?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
@@ -110,7 +108,7 @@ module Api
|
||||
end
|
||||
|
||||
def get_or_create_shipment(stock_location_id)
|
||||
@order.shipment || @order.shipments.create(stock_location_id:)
|
||||
@order.shipment || @order.shipments.create(stock_location_id: stock_location_id)
|
||||
end
|
||||
|
||||
def shipment_params
|
||||
|
||||
@@ -50,7 +50,7 @@ module Api
|
||||
private
|
||||
|
||||
def product
|
||||
@product ||= Spree::Product.find(params[:product_id]) if params[:product_id]
|
||||
@product ||= Spree::Product.find_by(permalink: params[:product_id]) if params[:product_id]
|
||||
end
|
||||
|
||||
def scope
|
||||
|
||||
@@ -69,14 +69,14 @@ module Api
|
||||
end
|
||||
|
||||
def missing_parameter(error)
|
||||
message = I18n.t('api.missing_parameter', param: error.param)
|
||||
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('api.unpermitted_parameters', params: error.params.join(", "))
|
||||
message = I18n.t(:unpermitted_parameters, params: error.params.join(", "), scope: :api)
|
||||
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(message)
|
||||
|
||||
@@ -29,8 +29,7 @@ module Api
|
||||
|
||||
def create
|
||||
authorize! :update, Enterprise.find(customer_params[:enterprise_id])
|
||||
customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
|
||||
customer.assign_attributes(customer_params)
|
||||
customer = Customer.new(customer_params)
|
||||
|
||||
if customer.save
|
||||
render json: Api::V1::CustomerSerializer.new(customer), status: :created
|
||||
@@ -81,7 +80,7 @@ module Api
|
||||
end
|
||||
|
||||
def visible_customers
|
||||
Customer.visible.managed_by(current_api_user)
|
||||
Customer.managed_by(current_api_user)
|
||||
end
|
||||
|
||||
def customer_params
|
||||
@@ -97,7 +96,6 @@ module Api
|
||||
]
|
||||
).to_h
|
||||
|
||||
attributes.merge!(created_manually: true)
|
||||
attributes.merge!(tag_list: params[:tags]) if params.key?(:tags)
|
||||
|
||||
transform_address!(attributes, :billing_address, :bill_address)
|
||||
|
||||
@@ -115,25 +115,25 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def require_distributor_chosen
|
||||
return if (@distributor = current_distributor)
|
||||
|
||||
redirect_to main_app.root_path
|
||||
false
|
||||
unless (@distributor = current_distributor)
|
||||
redirect_to main_app.root_path
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def require_order_cycle
|
||||
return if current_order_cycle
|
||||
|
||||
redirect_to main_app.shop_path
|
||||
unless current_order_cycle
|
||||
redirect_to main_app.shop_path
|
||||
end
|
||||
end
|
||||
|
||||
def check_hub_ready_for_checkout
|
||||
return unless current_distributor_closed?
|
||||
|
||||
current_order.empty!
|
||||
current_order.set_distribution! nil, nil
|
||||
flash[:info] = I18n.t('order_cycles_closed_for_hub')
|
||||
redirect_to main_app.root_url
|
||||
if current_distributor_closed?
|
||||
current_order.empty!
|
||||
current_order.set_distribution! nil, nil
|
||||
flash[:info] = I18n.t('order_cycles_closed_for_hub')
|
||||
redirect_to main_app.root_url
|
||||
end
|
||||
end
|
||||
|
||||
def current_distributor_closed?
|
||||
|
||||
@@ -14,7 +14,7 @@ module CheckoutCallbacks
|
||||
prepend_before_action :require_order_cycle
|
||||
prepend_before_action :require_distributor_chosen
|
||||
|
||||
before_action :load_order, :associate_user, :load_saved_addresses
|
||||
before_action :load_order, :associate_user, :load_saved_addresses, :load_saved_credit_cards
|
||||
before_action :load_shipping_methods, if: -> { params[:step] == "details" }
|
||||
|
||||
before_action :ensure_order_not_completed
|
||||
@@ -30,6 +30,8 @@ module CheckoutCallbacks
|
||||
@order.manual_shipping_selection = true
|
||||
@order.checkout_processing = true
|
||||
|
||||
@voucher_adjustment = @order.voucher_adjustments.first
|
||||
|
||||
redirect_to(main_app.shop_path) && return if redirect_to_shop?
|
||||
redirect_to_cart_path && return unless valid_order_line_items?
|
||||
end
|
||||
@@ -41,6 +43,11 @@ module CheckoutCallbacks
|
||||
@order.ship_address ||= finder.ship_address
|
||||
end
|
||||
|
||||
def load_saved_credit_cards
|
||||
@saved_credit_cards = spree_current_user&.credit_cards&.with_payment_profile.to_a
|
||||
@selected_card = nil
|
||||
end
|
||||
|
||||
def load_shipping_methods
|
||||
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
|
||||
end
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CheckoutSteps
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
def summary_step?
|
||||
params[:step] == "summary"
|
||||
end
|
||||
|
||||
def payment_step?
|
||||
params[:step] == "payment"
|
||||
end
|
||||
|
||||
def redirect_to_step_based_on_order
|
||||
case @order.state
|
||||
when "cart", "address", "delivery"
|
||||
redirect_to checkout_step_path(:details)
|
||||
when "payment"
|
||||
redirect_to checkout_step_path(:payment)
|
||||
when "confirmation"
|
||||
redirect_to checkout_step_path(:summary)
|
||||
else
|
||||
redirect_to order_path(@order, order_token: @order.token)
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_step
|
||||
case params[:step]
|
||||
when "details"
|
||||
return redirect_to checkout_step_path(:payment)
|
||||
when "payment"
|
||||
return redirect_to checkout_step_path(:summary)
|
||||
end
|
||||
redirect_to_step_based_on_order
|
||||
end
|
||||
|
||||
def check_step
|
||||
case @order.state
|
||||
when "cart", "address", "delivery"
|
||||
redirect_to checkout_step_path(:details) unless params[:step] == "details"
|
||||
when "payment"
|
||||
redirect_to checkout_step_path(:payment) if params[:step] == "summary"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,11 +5,11 @@ module ExtraFields
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def invalid_query_param(name, status, msg)
|
||||
render status:, json: json_api_error(msg, error_options:
|
||||
render status: status, json: json_api_error(msg, error_options:
|
||||
{
|
||||
title: I18n.t("api.query_param.error.title"),
|
||||
source: { parameter: name },
|
||||
status:,
|
||||
status: status,
|
||||
code: Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
|
||||
})
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ module ManagerInvitations
|
||||
|
||||
def create_new_manager(email, enterprise)
|
||||
password = Devise.friendly_token
|
||||
new_user = Spree::User.create(email:, unconfirmed_email: email, password:)
|
||||
new_user = Spree::User.create(email: email, unconfirmed_email: email, password: password)
|
||||
new_user.reset_password_token = Devise.friendly_token
|
||||
# Same time as used in Devise's lib/devise/models/recoverable.rb.
|
||||
new_user.reset_password_sent_at = Time.now.utc
|
||||
|
||||
@@ -41,8 +41,8 @@ module OrderCompletion
|
||||
main_app.order_path(@order, order_token: @order.token)
|
||||
end
|
||||
|
||||
def order_failed_route(step: 'details')
|
||||
main_app.checkout_step_path(step:)
|
||||
def order_failed_route
|
||||
main_app.checkout_path
|
||||
end
|
||||
|
||||
def order_invalid_for_checkout?
|
||||
@@ -60,8 +60,8 @@ module OrderCompletion
|
||||
|
||||
def process_payment_completion!
|
||||
unless @order.process_payments!
|
||||
payment_failed
|
||||
return redirect_to order_failed_route(step: 'payment')
|
||||
processing_failed
|
||||
return redirect_to order_failed_route
|
||||
end
|
||||
|
||||
if OrderWorkflow.new(@order).next && @order.complete?
|
||||
@@ -82,20 +82,12 @@ module OrderCompletion
|
||||
order_completion_reset(@order)
|
||||
end
|
||||
|
||||
def payment_failed
|
||||
notify_failure
|
||||
end
|
||||
|
||||
def processing_failed
|
||||
notify_failure
|
||||
Checkout::PostCheckoutActions.new(@order).failure
|
||||
end
|
||||
|
||||
def notify_failure(error = RuntimeError.new(order_processing_error))
|
||||
def processing_failed(error = RuntimeError.new(order_processing_error))
|
||||
Bugsnag.notify(error) do |payload|
|
||||
payload.add_metadata :order, @order
|
||||
end
|
||||
flash[:error] = order_processing_error if flash.blank?
|
||||
Checkout::PostCheckoutActions.new(@order).failure
|
||||
end
|
||||
|
||||
def order_processing_error
|
||||
|
||||
@@ -74,8 +74,8 @@ module ReportsActions
|
||||
def rendering_options
|
||||
@rendering_options ||= ReportRenderingOptions.where(
|
||||
user: spree_current_user,
|
||||
report_type:,
|
||||
report_subtype:
|
||||
report_type: report_type,
|
||||
report_subtype: report_subtype
|
||||
).first_or_create do |report_rendering_options|
|
||||
report_rendering_options.options = {
|
||||
fields_to_show: if request.get?
|
||||
|
||||
@@ -16,7 +16,7 @@ module RequestTimeouts
|
||||
respond_to do |type|
|
||||
type.html {
|
||||
render status: :gateway_timeout,
|
||||
file: Rails.public_path.join('500.html'),
|
||||
file: Rails.root.join("public/500.html"),
|
||||
formats: [:html],
|
||||
layout: nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ErrorsController < ApplicationController
|
||||
|
||||
event.add_metadata(:request, request.env)
|
||||
end
|
||||
render status: :not_found, formats: :html
|
||||
render status: :not_found
|
||||
end
|
||||
|
||||
def internal_server_error
|
||||
|
||||
@@ -4,14 +4,14 @@ class HomeController < BaseController
|
||||
layout 'darkswarm'
|
||||
|
||||
def index
|
||||
return unless ContentConfig.home_show_stats
|
||||
|
||||
@num_distributors = cached_count('distributors', Enterprise.is_distributor.activated.visible)
|
||||
@num_producers = cached_count('producers', Enterprise.is_primary_producer.activated.visible)
|
||||
@num_orders = cached_count('orders', Spree::Order.complete)
|
||||
@num_users = cached_count(
|
||||
'users', Spree::Order.complete.select('DISTINCT spree_orders.user_id')
|
||||
)
|
||||
if ContentConfig.home_show_stats
|
||||
@num_distributors = cached_count('distributors', Enterprise.is_distributor.activated.visible)
|
||||
@num_producers = cached_count('producers', Enterprise.is_primary_producer.activated.visible)
|
||||
@num_orders = cached_count('orders', Spree::Order.complete)
|
||||
@num_users = cached_count(
|
||||
'users', Spree::Order.complete.select('DISTINCT spree_orders.user_id')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def sell; end
|
||||
|
||||
@@ -31,7 +31,7 @@ class LineItemsController < BaseController
|
||||
|
||||
def unauthorized
|
||||
status = spree_current_user ? 403 : 401
|
||||
render(body: nil, status:) && return
|
||||
render(body: nil, status: status) && return
|
||||
end
|
||||
|
||||
def not_found
|
||||
|
||||
@@ -27,11 +27,8 @@ module PaymentGateways
|
||||
redirect_to provider.express_checkout_url(pp_response, useraction: 'commit')
|
||||
else
|
||||
flash[:error] =
|
||||
Spree.t(
|
||||
'flash.generic_error',
|
||||
scope: 'paypal',
|
||||
reasons: pp_response.errors.map(&:long_message).join(" "),
|
||||
)
|
||||
Spree.t('flash.generic_error', scope: 'paypal',
|
||||
reasons: pp_response.errors.map(&:long_message).join(" "))
|
||||
redirect_to main_app.checkout_step_path(:payment)
|
||||
end
|
||||
rescue SocketError
|
||||
@@ -50,7 +47,7 @@ module PaymentGateways
|
||||
payer_id: params[:PayerID]
|
||||
),
|
||||
amount: @order.total,
|
||||
payment_method:
|
||||
payment_method: payment_method
|
||||
)
|
||||
|
||||
process_payment_completion!
|
||||
|
||||
@@ -8,7 +8,6 @@ class SplitCheckoutController < ::BaseController
|
||||
include OrderStockCheck
|
||||
include Spree::BaseHelper
|
||||
include CheckoutCallbacks
|
||||
include CheckoutSteps
|
||||
include OrderCompletion
|
||||
include CablecarResponses
|
||||
include WhiteLabel
|
||||
@@ -25,13 +24,14 @@ class SplitCheckoutController < ::BaseController
|
||||
def edit
|
||||
redirect_to_step_based_on_order unless params[:step]
|
||||
check_step if params[:step]
|
||||
recalculate_tax if params[:step] == "summary"
|
||||
|
||||
return if available_shipping_methods.any?
|
||||
|
||||
flash[:error] = I18n.t('split_checkout.errors.no_shipping_methods_available')
|
||||
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
|
||||
end
|
||||
|
||||
def update
|
||||
return process_voucher if params[:apply_voucher].present?
|
||||
|
||||
if confirm_order || update_order
|
||||
return if performed?
|
||||
|
||||
@@ -51,13 +51,85 @@ class SplitCheckoutController < ::BaseController
|
||||
private
|
||||
|
||||
def render_error
|
||||
flash.now[:error] ||= I18n.t('split_checkout.errors.saving_failed')
|
||||
flash.now[:error] ||= I18n.t(
|
||||
'split_checkout.errors.saving_failed',
|
||||
messages: order_error_messages
|
||||
)
|
||||
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.
|
||||
replace("#checkout", partial("split_checkout/checkout")).
|
||||
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
|
||||
end
|
||||
|
||||
def render_voucher_section_or_redirect
|
||||
respond_to do |format|
|
||||
format.cable_ready { render_voucher_section }
|
||||
format.html { redirect_to checkout_step_path(:payment) }
|
||||
end
|
||||
end
|
||||
|
||||
# Using the power of cable_car we replace only the #voucher_section instead of reloading the page
|
||||
def render_voucher_section
|
||||
render(
|
||||
status: :ok,
|
||||
cable_ready: cable_car.replace(
|
||||
"#voucher-section",
|
||||
partial(
|
||||
"split_checkout/voucher_section",
|
||||
locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def order_error_messages
|
||||
# Remove ship_address.* errors if no shipping method is not selected
|
||||
remove_ship_address_errors if no_ship_address_needed?
|
||||
|
||||
# Reorder errors to make sure the most important ones are shown first
|
||||
# and finally, return the error messages to sentence
|
||||
reorder_errors.map(&:full_message).to_sentence
|
||||
end
|
||||
|
||||
def no_ship_address_needed?
|
||||
@order.errors[:shipping_method].present? || params[:ship_address_same_as_billing] == "1"
|
||||
end
|
||||
|
||||
def remove_ship_address_errors
|
||||
@order.errors.delete("ship_address.firstname")
|
||||
@order.errors.delete("ship_address.address1")
|
||||
@order.errors.delete("ship_address.city")
|
||||
@order.errors.delete("ship_address.phone")
|
||||
@order.errors.delete("ship_address.lastname")
|
||||
@order.errors.delete("ship_address.zipcode")
|
||||
end
|
||||
|
||||
def reorder_errors
|
||||
@order.errors.sort_by do |e|
|
||||
case e.attribute
|
||||
when /email/i then 0
|
||||
when /phone/i then 1
|
||||
when /bill_address/i then 2 + bill_address_error_order(e)
|
||||
else 20
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bill_address_error_order(error)
|
||||
case error.attribute
|
||||
when /firstname/i then 0
|
||||
when /lastname/i then 1
|
||||
when /address1/i then 2
|
||||
when /city/i then 3
|
||||
when /zipcode/i then 4
|
||||
else 5
|
||||
end
|
||||
end
|
||||
|
||||
def flash_error_when_no_shipping_method_available
|
||||
flash[:error] = I18n.t('split_checkout.errors.no_shipping_methods_available')
|
||||
end
|
||||
|
||||
def check_payments_adjustments
|
||||
@order.payments.each(&:ensure_correct_adjustment)
|
||||
end
|
||||
@@ -68,7 +140,7 @@ class SplitCheckoutController < ::BaseController
|
||||
|
||||
def confirm_order
|
||||
return unless summary_step? && @order.confirmation?
|
||||
return unless validate_current_step
|
||||
return unless validate_summary! && @order.errors.empty?
|
||||
|
||||
@order.customer.touch :terms_and_conditions_accepted_at
|
||||
|
||||
@@ -94,15 +166,81 @@ class SplitCheckoutController < ::BaseController
|
||||
def update_order
|
||||
return if params[:confirm_order] || @order.errors.any?
|
||||
|
||||
# If we have "pick up" shipping method (require_ship_address is set to false), use the
|
||||
# distributor address as shipping address
|
||||
use_shipping_address_from_distributor if shipping_method_ship_address_not_required?
|
||||
|
||||
@order.select_shipping_method(params[:shipping_method_id])
|
||||
@order.update(order_params)
|
||||
@order.update_totals_and_states
|
||||
|
||||
validate_current_step
|
||||
validate_current_step!
|
||||
|
||||
@order.errors.empty?
|
||||
end
|
||||
|
||||
def validate_current_step
|
||||
Checkout::Validation.new(@order, params).call && @order.errors.empty?
|
||||
def use_shipping_address_from_distributor
|
||||
@order.ship_address = @order.address_from_distributor
|
||||
|
||||
# Add the missing data
|
||||
bill_address = params[:order][:bill_address_attributes]
|
||||
@order.ship_address.firstname = bill_address[:firstname]
|
||||
@order.ship_address.lastname = bill_address[:lastname]
|
||||
@order.ship_address.phone = bill_address[:phone]
|
||||
|
||||
# Remove shipping address from parameter so we don't override the address we just set
|
||||
params[:order].delete(:ship_address_attributes)
|
||||
end
|
||||
|
||||
def shipping_method_ship_address_not_required?
|
||||
selected_shipping_method = available_shipping_methods&.select do |sm|
|
||||
sm.id.to_s == params[:shipping_method_id]
|
||||
end
|
||||
|
||||
return false if selected_shipping_method.empty?
|
||||
|
||||
selected_shipping_method.first.require_ship_address == false
|
||||
end
|
||||
|
||||
def process_voucher
|
||||
if add_voucher
|
||||
render_voucher_section_or_redirect
|
||||
elsif @order.errors.present?
|
||||
render_error
|
||||
end
|
||||
end
|
||||
|
||||
def add_voucher
|
||||
if params.dig(:order, :voucher_code).blank?
|
||||
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
|
||||
return false
|
||||
end
|
||||
|
||||
# Fetch Voucher
|
||||
voucher = Voucher.find_by(code: params[:order][:voucher_code], enterprise: @order.distributor)
|
||||
|
||||
if voucher.nil?
|
||||
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
|
||||
return false
|
||||
end
|
||||
|
||||
adjustment = voucher.create_adjustment(voucher.code, @order)
|
||||
|
||||
if !adjustment.valid?
|
||||
@order.errors.add(:voucher, I18n.t('split_checkout.errors.add_voucher_error'))
|
||||
adjustment.errors.each { |error| @order.errors.import(error) }
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def summary_step?
|
||||
params[:step] == "summary"
|
||||
end
|
||||
|
||||
def payment_step?
|
||||
params[:step] == "payment"
|
||||
end
|
||||
|
||||
def advance_order_state
|
||||
@@ -111,7 +249,77 @@ class SplitCheckoutController < ::BaseController
|
||||
OrderWorkflow.new(@order).advance_checkout(raw_params.slice(:shipping_method_id))
|
||||
end
|
||||
|
||||
def validate_current_step!
|
||||
step = ([params[:step]] & ["details", "payment", "summary"]).first
|
||||
send("validate_#{step}!")
|
||||
end
|
||||
|
||||
def validate_details!
|
||||
return true if params[:shipping_method_id].present?
|
||||
|
||||
@order.errors.add :shipping_method, I18n.t('split_checkout.errors.select_a_shipping_method')
|
||||
end
|
||||
|
||||
def validate_payment!
|
||||
return true if params.dig(:order, :payments_attributes, 0, :payment_method_id).present?
|
||||
|
||||
@order.errors.add :payment_method, I18n.t('split_checkout.errors.select_a_payment_method')
|
||||
end
|
||||
|
||||
def validate_summary!
|
||||
return true if params[:accept_terms]
|
||||
return true unless TermsOfService.required?(@order.distributor)
|
||||
|
||||
@order.errors.add(:terms_and_conditions, t("split_checkout.errors.terms_not_accepted"))
|
||||
end
|
||||
|
||||
def order_params
|
||||
@order_params ||= Checkout::Params.new(@order, params, spree_current_user).call
|
||||
end
|
||||
|
||||
def redirect_to_step_based_on_order
|
||||
case @order.state
|
||||
when "cart", "address", "delivery"
|
||||
redirect_to checkout_step_path(:details)
|
||||
when "payment"
|
||||
redirect_to checkout_step_path(:payment)
|
||||
when "confirmation"
|
||||
redirect_to checkout_step_path(:summary)
|
||||
else
|
||||
redirect_to order_path(@order, order_token: @order.token)
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_step
|
||||
case params[:step]
|
||||
when "details"
|
||||
return redirect_to checkout_step_path(:payment)
|
||||
when "payment"
|
||||
return redirect_to checkout_step_path(:summary)
|
||||
end
|
||||
redirect_to_step_based_on_order
|
||||
end
|
||||
|
||||
def check_step
|
||||
case @order.state
|
||||
when "cart", "address", "delivery"
|
||||
redirect_to checkout_step_path(:details) unless params[:step] == "details"
|
||||
when "payment"
|
||||
redirect_to checkout_step_path(:payment) if params[:step] == "summary"
|
||||
end
|
||||
end
|
||||
|
||||
def recalculate_tax
|
||||
@order.create_tax_charge!
|
||||
@order.update_order!
|
||||
|
||||
apply_voucher if @order.voucher_adjustments.present?
|
||||
end
|
||||
|
||||
def apply_voucher
|
||||
VoucherAdjustmentsService.calculate(@order)
|
||||
|
||||
# update order to take into account the voucher we applied
|
||||
@order.update_order!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ module Spree
|
||||
|
||||
def model_class
|
||||
const_name = controller_name.classify
|
||||
return "Spree::#{const_name}".constantize if Object.const_defined?("Spree::#{const_name}")
|
||||
return "Spree::#{const_name}".constantize if Spree.const_defined?(const_name)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ module Spree
|
||||
# This will make resource controller redirect correctly after deleting product images.
|
||||
# This can be removed after upgrading to Spree 2.1.
|
||||
# See here https://github.com/spree/spree/commit/334a011d2b8e16355e4ae77ae07cd93f7cbc8fd1
|
||||
belongs_to 'spree/product'
|
||||
belongs_to 'spree/product', find_by: :permalink
|
||||
|
||||
before_action :load_data
|
||||
|
||||
@@ -20,10 +20,6 @@ module Spree
|
||||
render layout: !request.xhr?
|
||||
end
|
||||
|
||||
def edit
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
def create
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
set_viewable
|
||||
@@ -38,6 +34,10 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
def update
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
set_viewable
|
||||
@@ -80,7 +80,7 @@ module Spree
|
||||
end
|
||||
|
||||
def load_data
|
||||
@product = Product.find(params[:product_id])
|
||||
@product = Product.find_by(permalink: params[:product_id])
|
||||
end
|
||||
|
||||
def set_viewable
|
||||
|
||||
@@ -4,30 +4,30 @@ module Spree
|
||||
module Admin
|
||||
class InvoicesController < Spree::Admin::BaseController
|
||||
respond_to :json
|
||||
authorize_resource class: false
|
||||
|
||||
def index
|
||||
@order = Spree::Order.find_by(number: params[:order_id])
|
||||
authorize! :invoice, @order
|
||||
def create
|
||||
invoice_service = BulkInvoiceService.new
|
||||
invoice_service.start_pdf_job(params[:order_ids])
|
||||
|
||||
render json: invoice_service.id, status: :ok
|
||||
end
|
||||
|
||||
def show
|
||||
invoice_id = params[:id]
|
||||
invoice_pdf = filepath(invoice_id)
|
||||
invoice_pdf = BulkInvoiceService.new.filepath(invoice_id)
|
||||
|
||||
send_file(invoice_pdf, type: 'application/pdf', disposition: :inline)
|
||||
end
|
||||
|
||||
def generate
|
||||
@order = Order.find_by(number: params[:order_id])
|
||||
authorize! :invoice, @order
|
||||
OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice
|
||||
redirect_back(fallback_location: spree.admin_dashboard_path)
|
||||
end
|
||||
def poll
|
||||
invoice_id = params[:invoice_id]
|
||||
|
||||
private
|
||||
|
||||
def filepath(invoice_id)
|
||||
"tmp/invoices/#{invoice_id}.pdf"
|
||||
if BulkInvoiceService.new.invoice_created? invoice_id
|
||||
render json: { created: true }, status: :ok
|
||||
else
|
||||
render json: { created: false }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,7 @@ module Spree
|
||||
flash[:error] = Spree.t('admin.mail_methods.testmail.delivery_error')
|
||||
end
|
||||
rescue StandardError => e
|
||||
flash[:error] = Spree.t('admin.mail_methods.testmail.error') % ({ e: })
|
||||
flash[:error] = Spree.t('admin.mail_methods.testmail.error') % { e: e }
|
||||
ensure
|
||||
redirect_to spree.edit_admin_mail_methods_url
|
||||
end
|
||||
|
||||
@@ -24,6 +24,7 @@ module Spree
|
||||
end
|
||||
|
||||
refresh_shipment_rates
|
||||
recalculate_taxes
|
||||
OrderWorkflow.new(@order).advance_to_payment
|
||||
|
||||
flash[:success] = Spree.t('customer_details_updated')
|
||||
@@ -43,14 +44,23 @@ module Spree
|
||||
|
||||
def build_addresses
|
||||
country_id = Address.default.country.id
|
||||
@order.build_bill_address(country_id:) if @order.bill_address.nil?
|
||||
@order.build_ship_address(country_id:) if @order.ship_address.nil?
|
||||
@order.build_bill_address(country_id: country_id) if @order.bill_address.nil?
|
||||
@order.build_ship_address(country_id: country_id) if @order.ship_address.nil?
|
||||
end
|
||||
|
||||
def refresh_shipment_rates
|
||||
@order.shipments.map(&:refresh_rates)
|
||||
end
|
||||
|
||||
def recalculate_taxes
|
||||
# If the order's address has been changed, the tax zone could be different,
|
||||
# which means a different set of tax rates might be applicable.
|
||||
@order.create_tax_charge!
|
||||
Spree::TaxRate.adjust(@order, @order.adjustments.admin)
|
||||
|
||||
@order.update_totals_and_states
|
||||
end
|
||||
|
||||
def order_params
|
||||
params.require(:order).permit(
|
||||
:email,
|
||||
|
||||
@@ -99,10 +99,6 @@ module Spree
|
||||
end
|
||||
|
||||
def print
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:invoices)
|
||||
@order = @order.invoices.find(params[:invoice_id]).presenter
|
||||
end
|
||||
|
||||
render_with_wicked_pdf InvoiceRenderer.new.args(@order)
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ProductPropertiesController < ::Admin::ResourceController
|
||||
belongs_to 'spree/product'
|
||||
belongs_to 'spree/product', find_by: :permalink
|
||||
before_action :find_properties
|
||||
before_action :setup_property, only: [:index]
|
||||
|
||||
|
||||
@@ -16,22 +16,8 @@ module Spree
|
||||
before_action :load_spree_api_key, only: [:index, :variant_overrides]
|
||||
before_action :strip_new_properties, only: [:create, :update]
|
||||
|
||||
def index
|
||||
@current_user = spree_current_user
|
||||
@show_latest_import = params[:latest_import] || false
|
||||
end
|
||||
|
||||
def show
|
||||
session[:return_to] ||= request.referer
|
||||
redirect_to( action: :edit )
|
||||
end
|
||||
|
||||
def new
|
||||
@object.shipping_category_id = DefaultShippingCategory.find_or_create.id
|
||||
end
|
||||
|
||||
def edit
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
@object.shipping_category = DefaultShippingCategory.find_or_create
|
||||
end
|
||||
|
||||
def create
|
||||
@@ -49,6 +35,20 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
session[:return_to] ||= request.referer
|
||||
redirect_to( action: :edit )
|
||||
end
|
||||
|
||||
def index
|
||||
@current_user = spree_current_user
|
||||
@show_latest_import = params[:latest_import] || false
|
||||
end
|
||||
|
||||
def edit
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
def update
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
|
||||
@@ -103,7 +103,7 @@ module Spree
|
||||
protected
|
||||
|
||||
def find_resource
|
||||
Product.find(params[:id])
|
||||
Product.find_by!(permalink: params[:id])
|
||||
end
|
||||
|
||||
def location_after_save
|
||||
@@ -180,7 +180,7 @@ module Spree
|
||||
select('DISTINCT spree_variants.import_date').
|
||||
joins(:product).
|
||||
where('spree_products.supplier_id IN (?)', editable_enterprises.collect(&:id)).
|
||||
where.not(spree_variants: { import_date: nil }).
|
||||
where('spree_variants.import_date IS NOT NULL').
|
||||
where(spree_variants: { deleted_at: nil }).
|
||||
order('spree_variants.import_date DESC')
|
||||
end
|
||||
|
||||
@@ -5,12 +5,6 @@ module Spree
|
||||
class TaxonsController < Spree::Admin::BaseController
|
||||
respond_to :html, :json, :js
|
||||
|
||||
def edit
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.find(params[:id])
|
||||
@permalink_part = @taxon.permalink.split("/").last
|
||||
end
|
||||
|
||||
def create
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.build(params[:taxon])
|
||||
@@ -32,6 +26,12 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.find(params[:id])
|
||||
@permalink_part = @taxon.permalink.split("/").last
|
||||
end
|
||||
|
||||
def update
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.find(params[:id])
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class UsersController < ::Admin::ResourceController
|
||||
helper I18nHelper
|
||||
|
||||
rescue_from Spree::User::DestroyWithOrdersError, with: :user_destroy_with_orders_error
|
||||
|
||||
after_action :sign_in_if_change_own_password, only: :update
|
||||
@@ -30,7 +28,7 @@ module Spree
|
||||
if @user.save
|
||||
|
||||
if roles
|
||||
@user.spree_roles = roles.compact_blank.collect{ |r| Spree::Role.find(r) }
|
||||
@user.spree_roles = roles.reject(&:blank?).collect{ |r| Spree::Role.find(r) }
|
||||
end
|
||||
|
||||
flash.now[:success] = Spree.t(:created_successfully)
|
||||
@@ -47,7 +45,7 @@ module Spree
|
||||
|
||||
if @user.update(user_params)
|
||||
if roles
|
||||
@user.spree_roles = roles.compact_blank.collect{ |r| Spree::Role.find(r) }
|
||||
@user.spree_roles = roles.reject(&:blank?).collect{ |r| Spree::Role.find(r) }
|
||||
end
|
||||
|
||||
flash.now[:success] = update_message
|
||||
@@ -129,10 +127,6 @@ module Spree
|
||||
params[:user][:email] != @user.email
|
||||
end
|
||||
|
||||
def build_resource
|
||||
model_class.new(locale: I18n.default_locale)
|
||||
end
|
||||
|
||||
def user_params
|
||||
::PermittedAttributes::User.new(params).call(
|
||||
%i[enterprise_limit show_api_key_view]
|
||||
|
||||
@@ -5,20 +5,30 @@ require 'open_food_network/scope_variants_for_search'
|
||||
module Spree
|
||||
module Admin
|
||||
class VariantsController < ::Admin::ResourceController
|
||||
belongs_to 'spree/product'
|
||||
|
||||
before_action :load_data, only: [:new, :edit]
|
||||
belongs_to 'spree/product', find_by: :permalink
|
||||
|
||||
def index
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
def new
|
||||
def edit
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
@object.shipping_category ||= DefaultShippingCategory.find_or_create
|
||||
end
|
||||
|
||||
def edit
|
||||
def update
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
|
||||
if @object.update(permitted_resource_params)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
redirect_to spree.admin_product_variants_url(params[:product_id], @url_filters)
|
||||
else
|
||||
redirect_to spree.edit_admin_product_variant_url(params[:product_id],
|
||||
@object,
|
||||
@url_filters)
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
@@ -43,19 +53,6 @@ module Spree
|
||||
@object.on_hand = on_hand.to_i if on_hand.present?
|
||||
end
|
||||
|
||||
def update
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
|
||||
if @object.update(permitted_resource_params)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
redirect_to spree.admin_product_variants_url(params[:product_id], @url_filters)
|
||||
else
|
||||
redirect_to spree.edit_admin_product_variant_url(params[:product_id],
|
||||
@object,
|
||||
@url_filters)
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
scoper = OpenFoodNetwork::ScopeVariantsForSearch.new(variant_search_params)
|
||||
@variants = scoper.search
|
||||
@@ -110,13 +107,6 @@ module Spree
|
||||
:include_out_of_stock
|
||||
).to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_data
|
||||
@tax_categories = TaxCategory.order(:name)
|
||||
@shipping_categories = ShippingCategory.order(:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,10 +69,6 @@ module Spree
|
||||
if @order.contents.update_cart(order_params)
|
||||
@order.recreate_all_fees! # Enterprise fees on line items and on the order itself
|
||||
|
||||
# Re apply the voucher
|
||||
VoucherAdjustmentsService.new(@order).update
|
||||
@order.update_totals_and_states
|
||||
|
||||
if @order.complete?
|
||||
@order.update_payment_fees!
|
||||
@order.create_tax_charge!
|
||||
@@ -133,10 +129,10 @@ module Spree
|
||||
end
|
||||
|
||||
def filter_order_params
|
||||
return unless params[:order] && params[:order][:line_items_attributes]
|
||||
|
||||
params[:order][:line_items_attributes] =
|
||||
remove_missing_line_items(params[:order][:line_items_attributes])
|
||||
if params[:order] && params[:order][:line_items_attributes]
|
||||
params[:order][:line_items_attributes] =
|
||||
remove_missing_line_items(params[:order][:line_items_attributes])
|
||||
end
|
||||
end
|
||||
|
||||
def remove_missing_line_items(attrs)
|
||||
@@ -180,10 +176,10 @@ module Spree
|
||||
items = params[:order][:line_items_attributes]
|
||||
&.select{ |_k, attrs| attrs["quantity"].to_i > 0 }
|
||||
|
||||
return unless items.empty?
|
||||
|
||||
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
|
||||
redirect_to main_app.order_path(order_to_update)
|
||||
if items.empty?
|
||||
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
|
||||
redirect_to main_app.order_path(order_to_update)
|
||||
end
|
||||
end
|
||||
|
||||
def order_params
|
||||
|
||||
@@ -33,8 +33,8 @@ module Spree
|
||||
render status: :ok, cable_ready: cable_car.
|
||||
inner_html(
|
||||
"#login-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t('devise.failure.already_registered') })
|
||||
partial("layouts/alert",
|
||||
locals: { type: "alert", message: t('devise.failure.already_registered') })
|
||||
).
|
||||
dispatch_event(name: "login:modal:open")
|
||||
else
|
||||
@@ -48,11 +48,9 @@ module Spree
|
||||
if @user.save
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#signup-feedback",
|
||||
partial("layouts/alert",
|
||||
locals: {
|
||||
type: "success",
|
||||
message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed')
|
||||
})
|
||||
partial("layouts/alert",
|
||||
locals: { type: "success",
|
||||
message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed') })
|
||||
)
|
||||
else
|
||||
render status: :unprocessable_entity, cable_ready: cable_car.morph(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user