mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-10 03:30:22 +00:00
Merge latest master into julesemmac/6584-map-location-confirm and resolve conflict in api/enterprises_controller.rb
This commit is contained in:
@@ -2,9 +2,9 @@ version: "2"
|
||||
plugins:
|
||||
rubocop:
|
||||
enabled: true
|
||||
channel: "rubocop-0-76"
|
||||
channel: "rubocop-1-12"
|
||||
config:
|
||||
file: ".rubocop_styleguide.yml"
|
||||
file: ".rubocop.yml"
|
||||
scss-lint:
|
||||
enabled: true
|
||||
checks:
|
||||
|
||||
174
.github/workflows/build.yml
vendored
174
.github/workflows/build.yml
vendored
@@ -8,8 +8,53 @@ on:
|
||||
env:
|
||||
DISABLE_KNAPSACK: true
|
||||
TIMEZONE: UTC
|
||||
COVERAGE: true
|
||||
|
||||
jobs:
|
||||
test-controllers-and-serializers:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
ports: ["5432:5432"]
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
POSTGRES_DB: open_food_network_test
|
||||
POSTGRES_USER: ofn
|
||||
POSTGRES_PASSWORD: f00d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14.15.5'
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Set up application.yml
|
||||
run: cp config/application.yml.example config/application.yml
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create RAILS_ENV=test
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run controller tests
|
||||
run: bundle exec rspec --profile -- spec/controllers spec/serializers
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-models:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
@@ -49,9 +94,12 @@ jobs:
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec spec/models
|
||||
run: bundle exec rspec --profile -- spec/models
|
||||
|
||||
test-admin-features:
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-admin-features-1:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -90,9 +138,12 @@ jobs:
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run admin feature tests
|
||||
run: bundle exec rspec --profile -- spec/features/admin/*_spec.rb
|
||||
run: bundle exec rspec --profile -- spec/features/admin/[a-o0-9]*_spec.rb
|
||||
|
||||
test-admin-features-folders:
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-admin-features-2:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -131,7 +182,10 @@ jobs:
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run admin feature tests
|
||||
run: bundle exec rspec --profile --pattern "spec/features/admin/*/*_spec.rb"
|
||||
run: bundle exec rspec --profile -- spec/features/admin/[p-z]*_spec.rb
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-consumer-features:
|
||||
runs-on: ubuntu-18.04
|
||||
@@ -174,48 +228,10 @@ jobs:
|
||||
- name: Run consumer feature tests
|
||||
run: bundle exec rspec --profile -- spec/features/consumer
|
||||
|
||||
test-controllers:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
ports: ["5432:5432"]
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
POSTGRES_DB: open_food_network_test
|
||||
POSTGRES_USER: ofn
|
||||
POSTGRES_PASSWORD: f00d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14.15.5'
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Set up application.yml
|
||||
run: cp config/application.yml.example config/application.yml
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create RAILS_ENV=test
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec spec/controllers
|
||||
|
||||
test-other:
|
||||
test-engines-etc:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -256,20 +272,54 @@ jobs:
|
||||
- name: Run JS tests
|
||||
run: RAILS_ENV=test bundle exec rake karma:run
|
||||
|
||||
- name: Run all other tests
|
||||
run: |
|
||||
bundle exec rspec \
|
||||
spec/helpers/ \
|
||||
spec/initializers/ \
|
||||
spec/jobs/ \
|
||||
spec/lib/ \
|
||||
spec/mailers/ \
|
||||
spec/queries/ \
|
||||
spec/requests/ \
|
||||
spec/serializers/ \
|
||||
spec/services/ \
|
||||
spec/validators/ \
|
||||
spec/views
|
||||
# Migration tests need to be run in a separate task.
|
||||
# See: https://github.com/openfoodfoundation/openfoodnetwork/pull/6924#issuecomment-813056525
|
||||
- name: Run migration tests
|
||||
run: bundle exec rspec --pattern "spec/{migrations}/**/*_spec.rb"
|
||||
|
||||
- name: Run engines tests
|
||||
run: bundle exec rake ofn:specs:engines:rspec
|
||||
- name: Run all other tests
|
||||
run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib,migrations"]
|
||||
|
||||
test-the-rest:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
ports: ["5432:5432"]
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
POSTGRES_DB: open_food_network_test
|
||||
POSTGRES_USER: ofn
|
||||
POSTGRES_PASSWORD: f00d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14.15.5'
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Set up application.yml
|
||||
run: cp config/application.yml.example config/application.yml
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create RAILS_ENV=test
|
||||
bundle exec rake db:schema:load RAILS_ENV=test
|
||||
|
||||
- name: Run admin feature folders, engines, lib
|
||||
run: bundle exec rspec --profile --pattern "engines/*/spec/{,/*/**}/*_spec.rb,spec/features/admin/*/*_spec.rb,spec/lib/{,/*/**}/*_spec.rb"
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,3 +47,4 @@ coverage
|
||||
/reports/
|
||||
!/reports/README.md
|
||||
bin/
|
||||
/spec/components/stories/**/*.stories.json
|
||||
|
||||
@@ -10,8 +10,8 @@ inherit_from:
|
||||
# The automatically generated todo list to ignore all current violations.
|
||||
- .rubocop_todo.yml
|
||||
|
||||
# Our Open Food Network style guide. It's used by Code Climate. If you want to see all violations,
|
||||
# then use only that configuration (like Code Climate):
|
||||
# Our Open Food Network style guide. If you want to see all violations,
|
||||
# then use only that configuration:
|
||||
#
|
||||
# bundle exec rubocop -c .rubocop_styleguide.yml
|
||||
#
|
||||
|
||||
@@ -21,6 +21,25 @@
|
||||
Layout/LineLength:
|
||||
Max: 100
|
||||
Exclude:
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/paypal_controller.rb
|
||||
- engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb
|
||||
- engines/order_management/spec/services/order_management/order/updater_spec.rb
|
||||
- engines/order_management/spec/services/order_management/reports/bulk_coop/bulk_coop_report_spec.rb
|
||||
- lib/open_food_network/reports/line_items.rb
|
||||
- spec/controllers/spree/admin/orders/invoices_spec.rb
|
||||
- spec/controllers/spree/admin/tax_rates_controller_spec.rb
|
||||
- spec/controllers/user_passwords_controller_spec.rb
|
||||
- spec/features/admin/configuration/general_settings_spec.rb
|
||||
- spec/features/consumer/shopping/unit_price_spec.rb
|
||||
- spec/helpers/admin/orders_helper_spec.rb
|
||||
- spec/lib/open_food_network/order_cycle_management_report_spec.rb
|
||||
- spec/lib/stripe/authorize_response_patcher_spec.rb
|
||||
- spec/services/bulk_invoice_service_spec.rb
|
||||
- spec/services/content_sanitizer_spec.rb
|
||||
- spec/services/process_payment_intent_spec.rb
|
||||
- spec/support/features/datepicker_helper.rb
|
||||
- app/controllers/admin/bulk_line_items_controller.rb
|
||||
- app/controllers/admin/contents_controller.rb
|
||||
- app/controllers/admin/customers_controller.rb
|
||||
@@ -230,7 +249,6 @@ Layout/LineLength:
|
||||
- spec/lib/open_food_network/products_and_inventory_report_spec.rb
|
||||
- spec/lib/open_food_network/scope_variant_to_hub_spec.rb
|
||||
- spec/lib/open_food_network/tag_rule_applicator_spec.rb
|
||||
- spec/lib/open_food_network/user_balance_calculator_spec.rb
|
||||
- spec/lib/open_food_network/users_and_enterprises_report_spec.rb
|
||||
- spec/lib/open_food_network/xero_invoices_report_spec.rb
|
||||
- spec/lib/spree/core/calculated_adjustments_spec.rb
|
||||
@@ -329,6 +347,29 @@ Layout/LineLength:
|
||||
Metrics/AbcSize:
|
||||
Max: 15
|
||||
Exclude:
|
||||
- app/controllers/admin/schedules_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/general_settings_controller.rb
|
||||
- app/controllers/spree/admin/images_controller.rb
|
||||
- app/controllers/spree/admin/mail_methods_controller.rb
|
||||
- app/controllers/spree/admin/shipping_methods_controller.rb
|
||||
- app/controllers/spree/paypal_controller.rb
|
||||
- app/helpers/spree/base_helper.rb
|
||||
- app/jobs/subscription_placement_job.rb
|
||||
- app/models/order_cycle.rb
|
||||
- app/models/product_import/unit_converter.rb
|
||||
- app/models/spree/gateway/pay_pal_express.rb
|
||||
- app/serializers/api/admin/enterprise_serializer.rb
|
||||
- app/services/order_factory.rb
|
||||
- app/services/variants_stock_levels.rb
|
||||
- engines/order_management/app/services/order_management/subscriptions/form.rb
|
||||
- lib/open_food_network/enterprise_fee_calculator.rb
|
||||
- lib/open_food_network/order_grouper.rb
|
||||
- lib/spree/core/controller_helpers/auth.rb
|
||||
- lib/spree/core/controller_helpers/common.rb
|
||||
- lib/spree/core/product_duplicator.rb
|
||||
- lib/stripe/authorize_response_patcher.rb
|
||||
- lib/stripe/profile_storer.rb
|
||||
- app/controllers/admin/bulk_line_items_controller.rb
|
||||
- app/controllers/admin/customers_controller.rb
|
||||
- app/controllers/admin/enterprise_fees_controller.rb
|
||||
@@ -468,7 +509,7 @@ Metrics/AbcSize:
|
||||
|
||||
Metrics/BlockLength:
|
||||
Max: 25
|
||||
ExcludedMethods: [
|
||||
IgnoredMethods: [
|
||||
"class_eval",
|
||||
"collection",
|
||||
"context",
|
||||
@@ -482,6 +523,8 @@ Metrics/BlockLength:
|
||||
"scenario"
|
||||
]
|
||||
Exclude:
|
||||
- spec/features/admin/order_cycles/complex_updating_specific_time_spec.rb
|
||||
- spec/features/admin/tag_rules_spec.rb
|
||||
- app/models/spree/order/checkout.rb
|
||||
- app/models/spree/payment/processing.rb
|
||||
- app/models/spree/shipment.rb
|
||||
@@ -514,6 +557,27 @@ Metrics/BlockLength:
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 6
|
||||
Exclude:
|
||||
- app/controllers/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller.rb
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
- app/models/product_import/entry_validator.rb
|
||||
- app/models/spree/order_inventory.rb
|
||||
- app/models/spree/order.rb
|
||||
- app/models/spree/shipment.rb
|
||||
- app/models/spree/tax_rate.rb
|
||||
- app/models/spree/variant.rb
|
||||
- engines/order_management/app/services/order_management/reports/enterprise_fee_summary/parameters.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_decorator.rb
|
||||
- lib/open_food_network/group_buy_report.rb
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_permissions.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/spree/core/controller_helpers/common.rb
|
||||
- lib/stripe/authorize_response_patcher.rb
|
||||
- lib/stripe/credit_card_clone_destroyer.rb
|
||||
- spec/support/i18n_translations_checker.rb
|
||||
- app/controllers/admin/enterprise_fees_controller.rb
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
@@ -552,6 +616,18 @@ Metrics/CyclomaticComplexity:
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 7
|
||||
Exclude:
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
- app/models/product_import/entry_validator.rb
|
||||
- app/models/spree/order_inventory.rb
|
||||
- app/models/spree/shipment.rb
|
||||
- app/models/spree/variant.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_decorator.rb
|
||||
- lib/open_food_network/group_buy_report.rb
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_permissions.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
@@ -702,10 +778,18 @@ Metrics/MethodLength:
|
||||
- spec/features/consumer/shopping/variant_overrides_spec.rb
|
||||
- spec/models/product_importer_spec.rb
|
||||
- spec/support/i18n_translations_checker.rb
|
||||
- app/controllers/admin/bulk_line_items_controller.rb
|
||||
- app/controllers/spree/paypal_controller.rb
|
||||
- app/jobs/subscription_placement_job.rb
|
||||
- app/models/spree/gateway/pay_pal_express.rb
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 100
|
||||
Exclude:
|
||||
- app/controllers/admin/customers_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller.rb
|
||||
- app/controllers/spree/paypal_controller.rb
|
||||
- engines/order_management/app/services/order_management/order/updater.rb
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/admin/order_cycles_controller.rb
|
||||
- app/controllers/admin/resource_controller.rb
|
||||
@@ -764,6 +848,7 @@ Metrics/ModuleLength:
|
||||
- app/models/spree/payment/processing.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/summary_spec.rb
|
||||
- lib/open_food_network/column_preference_defaults.rb
|
||||
- spec/controllers/admin/order_cycles_controller_spec.rb
|
||||
- spec/controllers/api/orders_controller_spec.rb
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Our Open Food Network style guide.
|
||||
#
|
||||
# These are the rules we agreed upon and we work towards. Code Climate uses
|
||||
# these rules to rate our code and detect new violations. But when you run
|
||||
# rubocop locally, the default configuration file `.rubocop.yml` loads
|
||||
# our "todo lists" to ignore all current violations.
|
||||
# These are the rules we agreed upon and we work towards.
|
||||
AllCops:
|
||||
TargetRailsVersion: 4.0
|
||||
NewCops: disable
|
||||
SuggestExtensions: false
|
||||
TargetRailsVersion: 5.0
|
||||
Exclude:
|
||||
- 'bin/**/*'
|
||||
- 'db/**/*'
|
||||
@@ -183,7 +182,7 @@ Metrics/AbcSize:
|
||||
|
||||
Metrics/BlockLength:
|
||||
Max: 25
|
||||
ExcludedMethods: [
|
||||
IgnoredMethods: [
|
||||
"class_eval",
|
||||
"collection",
|
||||
"context",
|
||||
@@ -217,3 +216,6 @@ Metrics/ParameterLists:
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 7
|
||||
|
||||
Naming/PredicateName:
|
||||
Enabled: false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
2.4.4
|
||||
2.5.8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/bin/env ruby
|
||||
|
||||
SimpleCov.start 'rails' do
|
||||
minimum_coverage 54
|
||||
|
||||
add_filter '/bin/'
|
||||
add_filter '/config/'
|
||||
add_filter '/jobs/application_job.rb'
|
||||
@@ -15,4 +13,8 @@ SimpleCov.start 'rails' do
|
||||
add_filter '/script'
|
||||
add_filter '/log'
|
||||
add_filter '/db'
|
||||
add_filter '/lib/tasks/sample_data/'
|
||||
end
|
||||
|
||||
require 'codecov'
|
||||
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
||||
|
||||
7
.storybook/main.js
Normal file
7
.storybook/main.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
stories: ['../spec/components/stories/**/*.stories.json'],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-controls',
|
||||
],
|
||||
};
|
||||
2
.storybook/preview-head.html
Normal file
2
.storybook/preview-head.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" media="screen" href="http://localhost:3000/assets/darkswarm/all.css" />
|
||||
5
.storybook/preview.js
Normal file
5
.storybook/preview.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const parameters = {
|
||||
server: {
|
||||
url: `http://localhost:3000/rails/stories`,
|
||||
},
|
||||
};
|
||||
@@ -6,12 +6,13 @@ This is a general guide to setting up an Open Food Network **development environ
|
||||
|
||||
The fastest way to make it work locally is to use Docker, you only need to setup git, see the [Docker setup guide](docker/README.md).
|
||||
Otherwise, for a local setup you will need:
|
||||
* Ruby 2.3.7 and bundler
|
||||
* Ruby 2.4.4 and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file)
|
||||
* PostgreSQL database
|
||||
* Chrome (for testing)
|
||||
|
||||
The following guides will provide OS-specific step-by-step instructions to get these requirements installed:
|
||||
- [Ubuntu Setup Guide][ubuntu]
|
||||
- [Debian Setup Guide][debian]
|
||||
- [OSX Setup Guide][osx]
|
||||
|
||||
If you are likely to need to manage multiple version of ruby on your local machine, we recommend version managers such as [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/).
|
||||
@@ -20,7 +21,7 @@ For those new to Rails, the following tutorial will help get you up to speed wit
|
||||
|
||||
### Get it
|
||||
|
||||
If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine.
|
||||
So you have set up your local environment according to the requirements listed above. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine:
|
||||
|
||||
git clone https://github.com/YOUR_GITHUB_USERNAME_HERE/openfoodnetwork
|
||||
|
||||
@@ -116,6 +117,7 @@ If these commands succeed, you should be able to [continue the setup process](#g
|
||||
[developer-wiki]: https://github.com/openfoodfoundation/openfoodnetwork/wiki
|
||||
[osx]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Development-Environment-Setup:-OS-X
|
||||
[ubuntu]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Development-Environment-Setup:-Ubuntu
|
||||
[debian]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Development-Environment-Setup:-Debian
|
||||
[wiki]: https://github.com/openfoodfoundation/openfoodnetwork/wiki
|
||||
[zeus]: https://github.com/burke/zeus
|
||||
[rubocop]: https://rubocop.readthedocs.io/en/latest/
|
||||
|
||||
23
Gemfile
23
Gemfile
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby "2.4.4"
|
||||
ruby "2.5.8"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'rails', '~> 5.0.0'
|
||||
@@ -15,7 +15,7 @@ gem 'sass', '<= 4.7.1'
|
||||
gem 'sass-rails', '< 6.0.0'
|
||||
|
||||
gem 'i18n'
|
||||
gem 'i18n-js', '~> 3.8.1'
|
||||
gem 'i18n-js', '~> 3.8.2'
|
||||
gem 'rails-i18n'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
|
||||
@@ -31,7 +31,7 @@ gem 'web', path: './engines/web'
|
||||
gem 'activerecord-postgresql-adapter'
|
||||
gem 'pg', '~> 0.21.0'
|
||||
|
||||
gem 'acts_as_list', '0.9.19'
|
||||
gem 'acts_as_list', '1.0.4'
|
||||
gem 'cancancan', '~> 1.15.0'
|
||||
gem 'ffaker'
|
||||
gem 'highline', '2.0.3' # Necessary for the install generator
|
||||
@@ -48,7 +48,7 @@ gem 'devise'
|
||||
gem 'devise-encryptable'
|
||||
gem 'devise-token_authenticatable'
|
||||
gem 'jwt', '~> 2.2'
|
||||
gem 'oauth2', '~> 1.4.4' # Used for Stripe Connect
|
||||
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
|
||||
|
||||
gem 'daemons'
|
||||
gem 'delayed_job_active_record'
|
||||
@@ -83,7 +83,7 @@ gem 'roadie-rails', '~> 1.3.0'
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
gem 'wkhtmltopdf-binary', '0.12.5' # We need to upgrade our CI before we can bump this :/
|
||||
gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.8.3'
|
||||
@@ -95,7 +95,7 @@ gem 'test-unit', '~> 3.4'
|
||||
gem 'coffee-rails', '~> 4.2.2'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'mini_racer', '0.3.1'
|
||||
gem 'mini_racer', '0.4.0'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
@@ -113,6 +113,12 @@ gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', branch: 'ofn-rails-4'
|
||||
|
||||
gem 'good_migrations'
|
||||
|
||||
gem 'flipper'
|
||||
gem 'flipper-active_record'
|
||||
gem 'flipper-ui'
|
||||
|
||||
gem "view_component", require: "view_component/engine"
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'unicorn-worker-killer'
|
||||
@@ -125,7 +131,7 @@ group :test, :development do
|
||||
gem 'bullet'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner', require: false
|
||||
gem "factory_bot_rails", '5.2.0', require: false
|
||||
gem "factory_bot_rails", '6.1.0', require: false
|
||||
gem 'fuubar', '~> 2.5.1'
|
||||
gem 'json_spec', '~> 1.1.4'
|
||||
gem 'knapsack'
|
||||
@@ -141,6 +147,7 @@ group :test, :development do
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'codecov', require: false
|
||||
gem 'simplecov', require: false
|
||||
gem 'test-prof'
|
||||
gem 'webmock'
|
||||
@@ -159,6 +166,8 @@ group :development do
|
||||
gem 'spring'
|
||||
gem 'spring-commands-rspec'
|
||||
|
||||
gem "view_component_storybook", require: "view_component/storybook/engine"
|
||||
|
||||
# 1.0.9 fixed openssl issues on macOS https://github.com/eventmachine/eventmachine/issues/602
|
||||
# While we don't require this gem directly, no dependents forced the upgrade to a version
|
||||
# greater than 1.0.9, so we just required the latest available version here.
|
||||
|
||||
126
Gemfile.lock
126
Gemfile.lock
@@ -77,7 +77,7 @@ GEM
|
||||
activejob (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemerchant (1.107.4)
|
||||
activemerchant (1.119.0)
|
||||
activesupport (>= 4.2)
|
||||
builder (>= 2.1.2, < 4.0.0)
|
||||
i18n (>= 0.6.9)
|
||||
@@ -105,8 +105,8 @@ GEM
|
||||
tzinfo (~> 1.1)
|
||||
acts-as-taggable-on (7.0.0)
|
||||
activerecord (>= 5.0, < 6.2)
|
||||
acts_as_list (0.9.19)
|
||||
activerecord (>= 3.0)
|
||||
acts_as_list (1.0.4)
|
||||
activerecord (>= 4.2)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
andand (1.3.3)
|
||||
@@ -114,7 +114,7 @@ GEM
|
||||
railties (>= 4.2, < 7)
|
||||
sprockets (>= 3.0, < 5)
|
||||
tilt
|
||||
angular_rails_csrf (4.2.0)
|
||||
angular_rails_csrf (4.5.0)
|
||||
railties (>= 3, < 7)
|
||||
angularjs-file-upload-rails (2.4.1)
|
||||
angularjs-rails (1.5.5)
|
||||
@@ -130,7 +130,7 @@ GEM
|
||||
json (~> 1.4)
|
||||
nokogiri (~> 1)
|
||||
bcrypt (3.1.16)
|
||||
bugsnag (6.19.0)
|
||||
bugsnag (6.20.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (6.1.4)
|
||||
@@ -138,13 +138,13 @@ GEM
|
||||
uniform_notifier (~> 1.11)
|
||||
byebug (11.1.3)
|
||||
cancancan (1.15.0)
|
||||
capybara (3.32.2)
|
||||
capybara (3.35.3)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (~> 1.5)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
xpath (~> 3.2)
|
||||
childprocess (3.0.0)
|
||||
chronic (0.10.2)
|
||||
@@ -152,6 +152,8 @@ GEM
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codecov (0.5.1)
|
||||
simplecov (>= 0.15, < 0.22)
|
||||
coderay (1.1.3)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
@@ -187,12 +189,13 @@ GEM
|
||||
daemons (1.3.1)
|
||||
dalli (2.7.11)
|
||||
database_cleaner (1.99.0)
|
||||
ddtrace (0.46.0)
|
||||
ddtrace (0.48.0)
|
||||
ffi (~> 1.0)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
delayed_job (4.1.9)
|
||||
activesupport (>= 3.0, < 6.2)
|
||||
delayed_job_active_record (4.1.5)
|
||||
delayed_job_active_record (4.1.6)
|
||||
activerecord (>= 3.0, < 6.2)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
delayed_job_web (1.4.4)
|
||||
@@ -211,22 +214,35 @@ GEM
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.4.4)
|
||||
docile (1.3.4)
|
||||
docile (1.3.5)
|
||||
erubi (1.10.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
excon (0.79.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (5.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
factory_bot_rails (5.2.0)
|
||||
factory_bot (~> 5.2.0)
|
||||
railties (>= 4.2.0)
|
||||
faraday (1.0.1)
|
||||
factory_bot (6.1.0)
|
||||
activesupport (>= 5.0.0)
|
||||
factory_bot_rails (6.1.0)
|
||||
factory_bot (~> 6.1.0)
|
||||
railties (>= 5.0.0)
|
||||
faraday (1.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffaker (2.16.0)
|
||||
ruby2_keywords
|
||||
faraday-net_http (1.0.1)
|
||||
ffaker (2.18.0)
|
||||
ffi (1.15.0)
|
||||
figaro (1.2.0)
|
||||
thor (>= 0.14.0, < 2)
|
||||
flipper (0.20.4)
|
||||
flipper-active_record (0.20.4)
|
||||
activerecord (>= 5.0, < 7)
|
||||
flipper (~> 0.20.4)
|
||||
flipper-ui (0.20.4)
|
||||
erubi (>= 1.0.0, < 2.0.0)
|
||||
flipper (~> 0.20.4)
|
||||
rack (>= 1.4, < 3)
|
||||
rack-protection (>= 1.5.3, < 2.2.0)
|
||||
fog-aws (2.0.1)
|
||||
fog-core (~> 1.38)
|
||||
fog-json (~> 1.0)
|
||||
@@ -252,7 +268,7 @@ GEM
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
geocoder (1.6.6)
|
||||
geocoder (1.6.7)
|
||||
get_process_mem (0.2.7)
|
||||
ffi (~> 1.0)
|
||||
globalid (0.4.2)
|
||||
@@ -266,9 +282,9 @@ GEM
|
||||
tilt
|
||||
hashdiff (1.0.1)
|
||||
highline (2.0.3)
|
||||
i18n (1.8.9)
|
||||
i18n (1.8.10)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.8.1)
|
||||
i18n-js (3.8.2)
|
||||
i18n (>= 0.6.6)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
@@ -300,13 +316,13 @@ GEM
|
||||
kaminari-core (= 1.2.1)
|
||||
kaminari-core (1.2.1)
|
||||
kgio (2.11.3)
|
||||
knapsack (1.20.0)
|
||||
knapsack (1.22.0)
|
||||
rake
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (8.4.255.0)
|
||||
libv8-node (15.14.0.0)
|
||||
loofah (2.9.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
@@ -316,10 +332,10 @@ GEM
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2020.1104)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
mini_racer (0.3.1)
|
||||
libv8 (~> 8.4.255)
|
||||
mini_mime (1.0.3)
|
||||
mini_portile2 (2.5.0)
|
||||
mini_racer (0.4.0)
|
||||
libv8-node (~> 15.14.0.0)
|
||||
minitest (5.14.4)
|
||||
monetize (1.11.0)
|
||||
money (~> 6.12)
|
||||
@@ -332,9 +348,10 @@ GEM
|
||||
mustermann (1.1.1)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.10)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
oauth2 (1.4.4)
|
||||
nokogiri (1.11.2)
|
||||
mini_portile2 (~> 2.5.0)
|
||||
racc (~> 1.4)
|
||||
oauth2 (1.4.7)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
@@ -353,7 +370,7 @@ GEM
|
||||
parallel (1.20.1)
|
||||
paranoia (2.4.3)
|
||||
activerecord (>= 4.0, < 6.2)
|
||||
parser (3.0.0.0)
|
||||
parser (3.0.1.0)
|
||||
ast (~> 2.4.1)
|
||||
paypal-sdk-core (0.3.4)
|
||||
multi_json (~> 1.0)
|
||||
@@ -371,6 +388,7 @@ GEM
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.5.2)
|
||||
rack (2.2.3)
|
||||
rack-mini-profiler (2.3.1)
|
||||
rack (>= 1.2.0)
|
||||
@@ -425,13 +443,13 @@ GEM
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.5.1)
|
||||
regexp_parser (1.8.2)
|
||||
regexp_parser (2.1.1)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rexml (3.2.4)
|
||||
rexml (3.2.5)
|
||||
roadie (3.5.1)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -477,7 +495,7 @@ GEM
|
||||
rswag-ui (2.4.0)
|
||||
actionpack (>= 3.1, < 7.0)
|
||||
railties (>= 3.1, < 7.0)
|
||||
rubocop (1.11.0)
|
||||
rubocop (1.13.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
@@ -511,10 +529,12 @@ GEM
|
||||
rubyzip (>= 1.2.2)
|
||||
shoulda-matchers (4.5.1)
|
||||
activesupport (>= 4.2.0)
|
||||
simplecov (0.18.5)
|
||||
simplecov (0.21.2)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov_json_formatter (0.1.2)
|
||||
sinatra (2.1.0)
|
||||
mustermann (~> 1.0)
|
||||
rack (~> 2.2)
|
||||
@@ -540,8 +560,8 @@ GEM
|
||||
stringex (2.8.5)
|
||||
stripe (5.30.0)
|
||||
temple (0.8.2)
|
||||
test-prof (0.11.3)
|
||||
test-unit (3.4.0)
|
||||
test-prof (1.0.2)
|
||||
test-unit (3.4.1)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
@@ -552,23 +572,27 @@ GEM
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (2.0.0)
|
||||
unicorn (5.8.0)
|
||||
unicorn (6.0.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
rack
|
||||
unicorn
|
||||
unicorn-worker-killer (0.4.4)
|
||||
unicorn-worker-killer (0.4.5)
|
||||
get_process_mem (~> 0)
|
||||
unicorn (>= 4, < 6)
|
||||
unicorn (>= 4, < 7)
|
||||
uniform_notifier (1.14.1)
|
||||
view_component (2.28.0)
|
||||
activesupport (>= 5.0.0, < 7.0)
|
||||
view_component_storybook (0.8.0)
|
||||
view_component (>= 2.2)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
webdrivers (4.6.0)
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (>= 3.0, < 4.0)
|
||||
webmock (3.12.1)
|
||||
webmock (3.12.2)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -579,7 +603,7 @@ GEM
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.1.0)
|
||||
activesupport
|
||||
wkhtmltopdf-binary (0.12.5)
|
||||
wkhtmltopdf-binary (0.12.6.5)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -595,7 +619,7 @@ DEPENDENCIES
|
||||
activerecord-postgresql-adapter
|
||||
activerecord-session_store
|
||||
acts-as-taggable-on (~> 7.0)
|
||||
acts_as_list (= 0.9.19)
|
||||
acts_as_list (= 1.0.4)
|
||||
andand
|
||||
angular-rails-templates (>= 0.3.0)
|
||||
angular_rails_csrf
|
||||
@@ -611,6 +635,7 @@ DEPENDENCIES
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
catalog!
|
||||
codecov
|
||||
coffee-rails (~> 4.2.2)
|
||||
combine_pdf
|
||||
compass-rails
|
||||
@@ -628,9 +653,12 @@ DEPENDENCIES
|
||||
devise-token_authenticatable
|
||||
dfc_provider!
|
||||
eventmachine (>= 1.2.3)
|
||||
factory_bot_rails (= 5.2.0)
|
||||
factory_bot_rails (= 6.1.0)
|
||||
ffaker
|
||||
figaro
|
||||
flipper
|
||||
flipper-active_record
|
||||
flipper-ui
|
||||
fog-aws (>= 0.6.0)
|
||||
foundation-icons-sass-rails
|
||||
foundation-rails (= 5.5.2.1)
|
||||
@@ -641,7 +669,7 @@ DEPENDENCIES
|
||||
haml
|
||||
highline (= 2.0.3)
|
||||
i18n
|
||||
i18n-js (~> 3.8.1)
|
||||
i18n-js (~> 3.8.2)
|
||||
immigrant
|
||||
jquery-migrate-rails
|
||||
jquery-rails (= 4.4.0)
|
||||
@@ -652,9 +680,9 @@ DEPENDENCIES
|
||||
kaminari (~> 1.2.1)
|
||||
knapsack
|
||||
letter_opener (>= 1.4.1)
|
||||
mini_racer (= 0.3.1)
|
||||
mini_racer (= 0.4.0)
|
||||
monetize (~> 1.11)
|
||||
oauth2 (~> 1.4.4)
|
||||
oauth2 (~> 1.4.7)
|
||||
ofn-qz!
|
||||
order_management!
|
||||
paper_trail (~> 10.3.1)
|
||||
@@ -698,15 +726,17 @@ DEPENDENCIES
|
||||
uglifier (>= 1.0.3)
|
||||
unicorn-rails
|
||||
unicorn-worker-killer
|
||||
view_component
|
||||
view_component_storybook
|
||||
web!
|
||||
webdrivers
|
||||
webmock
|
||||
whenever
|
||||
wicked_pdf
|
||||
wkhtmltopdf-binary (= 0.12.5)
|
||||
wkhtmltopdf-binary
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.4.4p296
|
||||
ruby 2.5.8p224
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[](https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2)
|
||||
[](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork)
|
||||
[](https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/build.yml)
|
||||
[](https://codecov.io/gh/openfoodfoundation/openfoodnetwork)
|
||||
[](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork)
|
||||
|
||||
# Open Food Network
|
||||
|
||||
@@ -34,7 +34,7 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration
|
||||
|
||||
## Testing
|
||||
|
||||
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline.
|
||||
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board](https://github.com/openfoodfoundation/openfoodnetwork/projects/31) for some good first issues, both on manual and automated testing (RSpec/Capybara).
|
||||
|
||||
We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them!
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
//= require admin/spree/handlebar_extensions
|
||||
|
||||
// OFN specific
|
||||
//= require ../shared/shared
|
||||
//= require_tree ../shared/directives
|
||||
//= require_tree ../templates/shared
|
||||
//= require_tree ../templates/admin
|
||||
//= require ./admin_ofn
|
||||
//= require ./customers/customers
|
||||
|
||||
@@ -147,7 +147,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm("Are you sure?")
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/products/" + product.id
|
||||
url: "/api/v0/products/" + product.id
|
||||
).success (data) ->
|
||||
$scope.products.splice $scope.products.indexOf(product), 1
|
||||
DirtyProducts.deleteProduct product.id
|
||||
@@ -162,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm(t("are_you_sure"))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
).success (data) ->
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
angular.module('admin.enterpriseFees').directive 'spreeDeleteResource', ->
|
||||
(scope, element, attrs) ->
|
||||
if scope.enterprise_fee.id
|
||||
url = '/api/enterprise_fees/' + scope.enterprise_fee.id
|
||||
url = '/api/v0/enterprise_fees/' + scope.enterprise_fee.id
|
||||
html = '<a href="' + url + '" class="delete-resource icon_link icon-trash no-text" data-action="remove" data-confirm="' + t('are_you_sure') + '" url="' + url + '"></a>'
|
||||
#var html = '<a href="'+url+'" class="delete-resource" data-confirm="Are you sure?"><img alt="Delete" src="/assets/admin/icons/delete.png" /> Delete</a>';
|
||||
element.append html
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("admin.indexUtils").factory "resources", ($resource) ->
|
||||
LineItem = $resource '/api/orders/:order_number/line_items/:line_item_id.json',
|
||||
LineItem = $resource '/api/v0/orders/:order_number/line_items/:line_item_id.json',
|
||||
{ order_number: '@order_number', line_item_id: '@line_item_id'},
|
||||
'update': { method: 'PUT' }
|
||||
Customer = $resource '/admin/customers/:customer_id.json',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
|
||||
ExchangeProductResource = $resource('/api/exchanges/:exchange_id/products.json', {}, {
|
||||
ExchangeProductResource = $resource('/api/v0/exchanges/:exchange_id/products.json', {}, {
|
||||
'index': { method: 'GET' }
|
||||
'variant_count': { method: 'GET', params: { action_name: "variant_count" }}
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ angular.module("admin.products").controller "editUnitsCtrl", ($scope, VariantUni
|
||||
if $scope.product.variant_unit == 'items'
|
||||
$scope.variant_unit_with_scale = 'items'
|
||||
else
|
||||
$scope.variant_unit_with_scale = $scope.product.variant_unit + '_' + $scope.product.variant_unit_scale
|
||||
$scope.variant_unit_with_scale = $scope.product.variant_unit + '_' + $scope.product.variant_unit_scale.replace(/\.0$/, '');
|
||||
|
||||
$scope.setFields = ->
|
||||
if $scope.variant_unit_with_scale == 'items'
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
angular.module("admin.products")
|
||||
.controller "unitsCtrl", ($scope, VariantUnitManager, OptionValueNamer) ->
|
||||
.controller "unitsCtrl", ($scope, VariantUnitManager, OptionValueNamer, UnitPrices) ->
|
||||
$scope.product = { master: {} }
|
||||
$scope.product.master.product = $scope.product
|
||||
$scope.placeholder_text = ""
|
||||
|
||||
$scope.$watchCollection '[product.variant_unit_with_scale, product.master.unit_value_with_description]', ->
|
||||
$scope.$watchCollection '[product.variant_unit_with_scale, product.master.unit_value_with_description, product.price, product.variant_unit_name]', ->
|
||||
$scope.processVariantUnitWithScale()
|
||||
$scope.processUnitValueWithDescription()
|
||||
$scope.processUnitPrice()
|
||||
$scope.placeholder_text = new OptionValueNamer($scope.product.master).name()
|
||||
|
||||
$scope.variant_unit_options = VariantUnitManager.variantUnitOptions()
|
||||
@@ -32,6 +33,14 @@ angular.module("admin.products")
|
||||
$scope.product.master.unit_value *= $scope.product.variant_unit_scale if $scope.product.master.unit_value && $scope.product.variant_unit_scale
|
||||
$scope.product.master.unit_description = match[3]
|
||||
|
||||
$scope.processUnitPrice = ->
|
||||
price = $scope.product.price
|
||||
scale = $scope.product.variant_unit_scale
|
||||
unit_type = $scope.product.variant_unit
|
||||
unit_value = $scope.product.master.unit_value
|
||||
variant_unit_name = $scope.product.variant_unit_name
|
||||
$scope.unit_price = UnitPrices.displayableUnitPrice(price, scale, unit_type, unit_value, variant_unit_name)
|
||||
|
||||
$scope.hasVariants = (product) ->
|
||||
Object.keys(product.variants).length > 0
|
||||
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
angular.module("admin.products").controller "variantUnitsCtrl", ($scope, VariantUnitManager, $timeout) ->
|
||||
angular.module("admin.products").controller "variantUnitsCtrl", ($scope, VariantUnitManager, $timeout, UnitPrices) ->
|
||||
|
||||
$scope.unitName = (scale, type) ->
|
||||
VariantUnitManager.getUnitName(scale, type)
|
||||
|
||||
$scope.$watchCollection "[unit_value_human, variant.price]", ->
|
||||
$scope.processUnitPrice()
|
||||
|
||||
$scope.processUnitPrice = ->
|
||||
if ($scope.variant)
|
||||
price = $scope.variant.price
|
||||
scale = $scope.scale
|
||||
unit_type = angular.element("#product_variant_unit").val()
|
||||
if (unit_type != "items")
|
||||
$scope.updateValue()
|
||||
unit_value = $scope.unit_value
|
||||
else
|
||||
unit_value = 1
|
||||
variant_unit_name = angular.element("#product_variant_unit_name").val()
|
||||
$scope.unit_price = UnitPrices.displayableUnitPrice(price, scale, unit_type, unit_value, variant_unit_name)
|
||||
|
||||
$scope.scale = angular.element('#product_variant_unit_scale').val()
|
||||
|
||||
$scope.updateValue = ->
|
||||
@@ -11,4 +27,6 @@ angular.module("admin.products").controller "variantUnitsCtrl", ($scope, Variant
|
||||
|
||||
variant_unit_value = angular.element('#variant_unit_value').val()
|
||||
$scope.unit_value_human = variant_unit_value / $scope.scale
|
||||
|
||||
$timeout -> $scope.processUnitPrice()
|
||||
$timeout -> $scope.updateValue()
|
||||
|
||||
@@ -1 +1 @@
|
||||
angular.module("admin.products", ["textAngular", "admin.utils"])
|
||||
angular.module("admin.products", ["textAngular", "admin.utils", "OFNShared"])
|
||||
|
||||
@@ -8,7 +8,7 @@ angular.module("ofn.admin").factory "ProductImageService", (FileUploader, SpreeA
|
||||
autoUpload: true
|
||||
|
||||
configure: (product) =>
|
||||
@imageUploader.url = "/api/product_images/#{product.id}"
|
||||
@imageUploader.url = "/api/v0/product_images/#{product.id}"
|
||||
@imagePreview = product.image_url
|
||||
@imageUploader.onSuccessItem = (image, response) =>
|
||||
product.thumb_url = response.thumb_url
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
angular.module("admin.products").factory "UnitPrices", (VariantUnitManager, localizeCurrencyFilter) ->
|
||||
class UnitPrices
|
||||
@displayableUnitPrice: (price, scale, unit_type, unit_value, variant_unit_name) ->
|
||||
if price && !isNaN(price) && unit_type && unit_value
|
||||
value = localizeCurrencyFilter(UnitPrices.price(price, scale, unit_type, unit_value, variant_unit_name))
|
||||
unit = UnitPrices.unit(scale, unit_type, variant_unit_name)
|
||||
return value + " / " + unit
|
||||
return null
|
||||
|
||||
@price: (price, scale, unit_type, unit_value) ->
|
||||
price / @denominator(scale, unit_type, unit_value)
|
||||
|
||||
@denominator: (scale, unit_type, unit_value) ->
|
||||
unit = @unit(scale, unit_type)
|
||||
if unit == "lb"
|
||||
unit_value / 453.6
|
||||
else if unit == "kg"
|
||||
unit_value / 1000
|
||||
else
|
||||
unit_value
|
||||
|
||||
@unit: (scale, unit_type, variant_unit_name = '') ->
|
||||
if variant_unit_name.length > 0
|
||||
variant_unit_name
|
||||
else if unit_type == "items"
|
||||
"item"
|
||||
else if VariantUnitManager.systemOfMeasurement(scale, unit_type) == "imperial"
|
||||
"lb"
|
||||
else if unit_type == "weight"
|
||||
"kg"
|
||||
else if unit_type == "volume"
|
||||
"L"
|
||||
@@ -67,3 +67,9 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
|
||||
scaleSystem = @units[unitType][scale]['system']
|
||||
(parseFloat(scale) for scale, scaleInfo of @units[unitType] when scaleInfo['system'] == scaleSystem).sort (a, b) ->
|
||||
a - b
|
||||
|
||||
@systemOfMeasurement: (scale, unitType) ->
|
||||
if @units[unitType][scale]
|
||||
@units[unitType][scale]['system']
|
||||
else
|
||||
'custom'
|
||||
|
||||
@@ -9,12 +9,12 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
|
||||
'update':
|
||||
method: 'PUT'
|
||||
'removeLogo':
|
||||
url: '/api/enterprises/:id/logo.json'
|
||||
url: '/api/v0/enterprises/:id/logo.json'
|
||||
method: 'DELETE'
|
||||
'removePromoImage':
|
||||
url: '/api/enterprises/:id/promo_image.json'
|
||||
url: '/api/v0/enterprises/:id/promo_image.json'
|
||||
method: 'DELETE'
|
||||
'removeTermsAndConditions':
|
||||
url: '/api/enterprises/:id/terms_and_conditions.json'
|
||||
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
|
||||
method: 'DELETE'
|
||||
})
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
angular.module("admin.resources").factory 'OrderResource', ($resource) ->
|
||||
$resource('/admin/orders/:id/:action.json', {}, {
|
||||
'index':
|
||||
url: '/api/orders.json'
|
||||
url: '/api/v0/orders.json'
|
||||
method: 'GET'
|
||||
'update':
|
||||
method: 'PUT'
|
||||
'capture':
|
||||
url: '/api/orders/:id/capture.json'
|
||||
url: '/api/v0/orders/:id/capture.json'
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
'ship':
|
||||
url: '/api/orders/:id/ship.json'
|
||||
url: '/api/v0/orders/:id/ship.json'
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
angular.module("admin.resources").factory 'ProductResource', ($resource) ->
|
||||
$resource('/admin/product/:id/:action.json', {}, {
|
||||
'index':
|
||||
url: '/api/products/bulk_products.json'
|
||||
url: '/api/v0/products/bulk_products.json'
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
@@ -10,8 +10,8 @@ angular.module("ofn.admin").factory "BulkProducts", (ProductResource, dataFetche
|
||||
angular.extend(@pagination, data.pagination)
|
||||
|
||||
cloneProduct: (product) ->
|
||||
$http.post("/api/products/" + product.id + "/clone").success (data) =>
|
||||
dataFetcher("/api/products/" + data.id + "?template=bulk_show").then (newProduct) =>
|
||||
$http.post("/api/v0/products/" + product.id + "/clone").success (data) =>
|
||||
dataFetcher("/api/v0/products/" + data.id + "?template=bulk_show").then (newProduct) =>
|
||||
@unpackProduct newProduct
|
||||
@insertProductAfter(product, newProduct)
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, $fil
|
||||
preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",")
|
||||
type: "TagRule::#{ruleType}"
|
||||
switch ruleType
|
||||
when "DiscountOrder"
|
||||
newRule.calculator = { preferred_flat_percent: 0 }
|
||||
when "FilterShippingMethods"
|
||||
newRule.peferred_shipping_method_tags = []
|
||||
newRule.preferred_matched_shipping_methods_visibility = "visible"
|
||||
|
||||
@@ -8,7 +8,6 @@ angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templ
|
||||
template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope)
|
||||
|
||||
scope.ruleTypes = [
|
||||
# { id: "DiscountOrder", name: 'Apply a discount to orders' }
|
||||
{ id: "FilterProducts", name: t('js.tag_rules.show_hide_variants') }
|
||||
{ id: "FilterShippingMethods", name: t('js.tag_rules.show_hide_shipping') }
|
||||
{ id: "FilterPaymentMethods", name: t('js.tag_rules.show_hide_payment') }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
angular.module("admin.utils").factory "StatusMessage", ($timeout) ->
|
||||
angular.module("admin.utils").factory "StatusMessage", ->
|
||||
new class StatusMessage
|
||||
types:
|
||||
progress: {timeout: false, style: {color: '#ff9906'}}
|
||||
alert: {timeout: 5000, style: {color: 'grey'}}
|
||||
notice: {timeout: false, style: {color: 'grey'}}
|
||||
success: {timeout: 5000, style: {color: '#9fc820'}}
|
||||
failure: {timeout: false, style: {color: '#da5354'}}
|
||||
progress: {style: {color: '#ff9906'}}
|
||||
alert: {style: {color: 'grey'}}
|
||||
notice: {style: {color: 'grey'}}
|
||||
success: {style: {color: '#9fc820'}}
|
||||
failure: {style: {color: '#da5354'}}
|
||||
|
||||
statusMessage:
|
||||
text: ""
|
||||
@@ -25,13 +25,7 @@ angular.module("admin.utils").factory "StatusMessage", ($timeout) ->
|
||||
display: (type, text) ->
|
||||
@statusMessage.text = text
|
||||
@statusMessage.style = @types[type].style
|
||||
$timeout.cancel @statusMessage.timeout if @statusMessage.timeout
|
||||
timeout = @types[type].timeout
|
||||
if timeout
|
||||
@statusMessage.timeout = $timeout =>
|
||||
@clear()
|
||||
, timeout, true
|
||||
null # So we don't return weird timeouts
|
||||
null
|
||||
|
||||
clear: ->
|
||||
@statusMessage.text = ''
|
||||
|
||||
@@ -42,7 +42,7 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
url = "/api/products/overridable?page=::page::;per_page=100"
|
||||
url = "/api/v0/products/overridable?page=::page::;per_page=100"
|
||||
PagedFetcher.fetch url, $scope.addProducts
|
||||
|
||||
$scope.addProducts = (data) ->
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#= require ../shared/ng-infinite-scroll.min.js
|
||||
#= require ../shared/angular-local-storage.js
|
||||
#= require ../shared/angular-slideables.js
|
||||
#= require ../shared/shared
|
||||
#= require_tree ../shared/directives
|
||||
#= require angularjs-file-upload
|
||||
#= require i18n/translations
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http,
|
||||
$scope.shopfront_loading = true
|
||||
$scope.toggle_tab(event)
|
||||
|
||||
$http.get("/api/shops/" + $scope.hub.id)
|
||||
$http.get("/api/v0/shops/" + $scope.hub.id)
|
||||
.success (data) ->
|
||||
$scope.shopfront_loading = false
|
||||
$scope.hub = data
|
||||
|
||||
@@ -24,7 +24,7 @@ Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll,
|
||||
$scope.shopfront_loading = true
|
||||
$scope.toggle_tab(event)
|
||||
|
||||
$http.get("/api/shops/" + $scope.producer.id)
|
||||
$http.get("/api/v0/shops/" + $scope.producer.id)
|
||||
.success (data) ->
|
||||
$scope.shopfront_loading = false
|
||||
$scope.producer = data
|
||||
|
||||
@@ -10,7 +10,8 @@ window.Darkswarm = angular.module("Darkswarm", [
|
||||
'uiGmapgoogle-maps',
|
||||
'duScroll',
|
||||
'angularFileUpload',
|
||||
'angularSlideables'
|
||||
'angularSlideables',
|
||||
'OFNShared'
|
||||
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
|
||||
$httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
|
||||
$httpProvider.defaults.headers.common['Accept'] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -3,7 +3,15 @@ Darkswarm.directive "bodyScroll", ($rootScope, BodyScroll) ->
|
||||
scope: true
|
||||
link: (scope, elem, attrs) ->
|
||||
$rootScope.$on "toggleBodyScroll", ->
|
||||
if BodyScroll.disabled
|
||||
elem.addClass "disable-scroll"
|
||||
if BodyScroll.disabled && document.body.scrollHeight > document.body.clientHeight
|
||||
document.body.style.top = "-#{window.scrollY}px"
|
||||
document.body.style.position = 'fixed'
|
||||
document.body.style.overflowY = 'scroll'
|
||||
document.body.style.width = '100%'
|
||||
else
|
||||
elem.removeClass "disable-scroll"
|
||||
scrollY = parseInt(document.body.style.top) * -1
|
||||
document.body.style.position = ''
|
||||
document.body.style.top = ''
|
||||
document.body.style.overflowY = ''
|
||||
document.body.style.width = ''
|
||||
window.scrollTo(0, scrollY) if scrollY
|
||||
|
||||
@@ -4,4 +4,5 @@ Darkswarm.directive "shopVariantWithUnitPrice", ->
|
||||
templateUrl: 'shop_variant_with_unit_price.html'
|
||||
scope:
|
||||
variant: '='
|
||||
show_unit_price: '=showunitprice'
|
||||
controller: 'ShopVariantCtrl'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("Darkswarm").factory 'Customer', ($resource, $injector, Messages) ->
|
||||
Customer = $resource('/api/customers/:id/:action.json', {}, {
|
||||
Customer = $resource('/api/v0/customers/:id/:action.json', {}, {
|
||||
'index':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
|
||||
@@ -8,5 +8,5 @@ Darkswarm.factory "EnterpriseImageService", (FileUploader, spreeApiKey) ->
|
||||
autoUpload: true
|
||||
|
||||
configure: (enterprise) =>
|
||||
@imageUploader.url = "/api/enterprises/#{enterprise.id}/update_image"
|
||||
@imageUploader.url = "/api/v0/enterprises/#{enterprise.id}/update_image"
|
||||
@imageUploader.onSuccessItem = (image, response) => @imageSrc = response
|
||||
|
||||
@@ -5,7 +5,7 @@ Darkswarm.factory "EnterpriseModal", ($modal, $rootScope, $http)->
|
||||
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
|
||||
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
|
||||
|
||||
$http.get("/api/shops/" + enterprise.id).success (data) ->
|
||||
$http.get("/api/v0/shops/" + enterprise.id).success (data) ->
|
||||
scope.enterprise = data
|
||||
$modal.open(templateUrl: "enterprise_modal.html", scope: scope)
|
||||
.error (data) ->
|
||||
|
||||
@@ -18,7 +18,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
Loading.message = t('creating') + " " + @enterprise.name
|
||||
$http(
|
||||
method: "POST"
|
||||
url: "/api/enterprises"
|
||||
url: "/api/v0/enterprises"
|
||||
data:
|
||||
enterprise: @prepare()
|
||||
use_geocoder: @useGeocoder()
|
||||
@@ -43,7 +43,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
Loading.message = t('updating') + " " + @enterprise.name
|
||||
$http(
|
||||
method: "PUT"
|
||||
url: "/api/enterprises/#{@enterprise.id}"
|
||||
url: "/api/v0/enterprises/#{@enterprise.id}"
|
||||
data:
|
||||
enterprise: @prepare()
|
||||
use_geocoder: @useGeocoder()
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
Darkswarm.factory 'OrderCycleResource', ($resource) ->
|
||||
$resource('/api/order_cycles/:id.json', {}, {
|
||||
$resource('/api/v0/order_cycles/:id.json', {}, {
|
||||
'products':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/products.json'
|
||||
url: '/api/v0/order_cycles/:id/products.json'
|
||||
params:
|
||||
id: '@id'
|
||||
'taxons':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/taxons.json'
|
||||
url: '/api/v0/order_cycles/:id/taxons.json'
|
||||
params:
|
||||
id: '@id'
|
||||
'properties':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/properties.json'
|
||||
url: '/api/v0/order_cycles/:id/properties.json'
|
||||
params:
|
||||
id: '@id'
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Darkswarm.factory 'ShopsResource', ($resource) ->
|
||||
$resource('/api/shops/:id.json', {}, {
|
||||
$resource('/api/v0/shops/:id.json', {}, {
|
||||
'closed_shops':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/shops/closed_shops.json'
|
||||
url: '/api/v0/shops/closed_shops.json'
|
||||
})
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
Darkswarm.directive "questionMarkWithTooltip", ($tooltip)->
|
||||
|
||||
OFNShared.directive "questionMarkWithTooltip", ($tooltip)->
|
||||
# We use the $tooltip service from Angular foundation to give us boilerplate
|
||||
# Subsequently we patch the scope, template and restrictions
|
||||
tooltip = $tooltip 'questionMarkWithTooltip', 'questionMarkWithTooltip', 'click'
|
||||
tooltip.scope =
|
||||
context: "="
|
||||
tooltip.templateUrl = "question_mark_with_tooltip_icon.html"
|
||||
key: "="
|
||||
tooltip.templateUrl = "shared/question_mark_with_tooltip_icon.html"
|
||||
tooltip.replace = true
|
||||
tooltip.restrict = 'E'
|
||||
tooltip
|
||||
|
||||
# This is automatically referenced via naming convention in $tooltip
|
||||
Darkswarm.directive 'questionMarkWithTooltipPopup', ->
|
||||
OFNShared.directive 'questionMarkWithTooltipPopup', ->
|
||||
restrict: 'EA'
|
||||
replace: true
|
||||
templateUrl: 'question_mark_with_tooltip.html'
|
||||
templateUrl: 'shared/question_mark_with_tooltip.html'
|
||||
scope: false
|
||||
4
app/assets/javascripts/shared/shared.js.coffee
Normal file
4
app/assets/javascripts/shared/shared.js.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
window.OFNShared = angular.module("OFNShared", [
|
||||
"mm.foundation"
|
||||
]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
@@ -2,12 +2,21 @@
|
||||
.columns.small-12
|
||||
%h3{"ng-bind" => "::variant.extended_name"}
|
||||
|
||||
.row.variant-bulk-buy-price-summary
|
||||
.columns.small-6
|
||||
.variant-unit {{ ::variant.unit_to_display }}
|
||||
.columns.small-6
|
||||
.flex.variant-bulk-buy-price-summary{style: "justify-content: space-around;"}
|
||||
.variant-unit
|
||||
{{ ::variant.unit_to_display }}
|
||||
.div
|
||||
{{ variant.line_item.total_price | localizeCurrency }}
|
||||
|
||||
.unit-price{"ng-if": "show_unit_price"}
|
||||
%question-mark-with-tooltip{"question-mark-with-tooltip": "_",
|
||||
"question-mark-with-tooltip-append-to-body": "true",
|
||||
"question-mark-with-tooltip-placement": "top",
|
||||
"question-mark-with-tooltip-animation": true,
|
||||
style: "margin-right: 5px",
|
||||
key: "'js.shopfront.unit_price_tooltip'"}
|
||||
{{ variant.unit_price_price | localizeCurrency }} / {{ variant.unit_price_unit }}
|
||||
|
||||
.row
|
||||
.columns.small-12.medium-6
|
||||
.variant-bulk-buy-quantity-label
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.joyride-tip-guide.question-mark-tooltip{class: "{{ context }}", ng: {class: "{ in: tt_isOpen, fade: tt_animation }", show: "tt_isOpen"}}
|
||||
.background{ng: {click: "tt_isOpen = false"}}
|
||||
.joyride-content-wrapper
|
||||
{{ "js.shopfront.unit_price_tooltip" | t }}
|
||||
{{ key | t }}
|
||||
%span.joyride-nub.bottom
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
%question-mark-with-tooltip{"question-mark-with-tooltip" => "_",
|
||||
"question-mark-with-tooltip-append-to-body" => "true",
|
||||
"question-mark-with-tooltip-placement" => "top",
|
||||
"question-mark-with-tooltip-animation" => true}
|
||||
{{ variant.unit_price_price | localizeCurrency }} / {{ variant.unit_price_unit }}
|
||||
"question-mark-with-tooltip-animation" => true,
|
||||
key: "'js.shopfront.unit_price_tooltip'"}
|
||||
{{ variant.unit_price_price | localizeCurrency }} / {{ variant.unit_price_unit }}
|
||||
|
||||
.medium-2.large-2.columns.total-price
|
||||
%span{"ng-class" => "{filled: variant.line_item.total_price}"}
|
||||
|
||||
@@ -48,3 +48,6 @@
|
||||
@import 'components/*';
|
||||
@import 'pages/*';
|
||||
@import '*';
|
||||
|
||||
@import "../shared/question-mark-icon";
|
||||
@import "question-mark-tooltip";
|
||||
|
||||
18
app/assets/stylesheets/admin/question-mark-tooltip.scss
Normal file
18
app/assets/stylesheets/admin/question-mark-tooltip.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
.joyride-tip-guide {
|
||||
background: $color-3;
|
||||
color: $white;
|
||||
font-family: inherit;
|
||||
font-weight: $font-weight-normal;
|
||||
position: absolute;
|
||||
z-index: 101;
|
||||
padding: 5px 15px;
|
||||
|
||||
.joyride-nub.bottom {
|
||||
border: 10px solid;
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,13 @@ button.bulk-buy-add.variant-quantity {
|
||||
}
|
||||
}
|
||||
|
||||
// Hide number arrows on Chrome, Safari, Edge, Opera
|
||||
.variant-quantity::-webkit-outer-spin-button,
|
||||
.variant-quantity::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.variant-bulk-buy-price-summary {
|
||||
color: $disabled-med;
|
||||
margin-bottom: 1em;
|
||||
|
||||
@@ -21,3 +21,5 @@
|
||||
ofn-modal {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@import "../shared/question-mark-icon";
|
||||
|
||||
@@ -165,7 +165,3 @@ a.button.large {
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "variables/variables";
|
||||
|
||||
.unit-price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -26,14 +28,27 @@
|
||||
background-image: none;
|
||||
background-color: $teal-500;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
@include icon-font;
|
||||
content: "";
|
||||
color: $white;
|
||||
vertical-align: super;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Question mark icon into a field
|
||||
.field .question-mark-icon {
|
||||
width: 15px;
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.joyride-tip-guide.question-mark-tooltip {
|
||||
width: 16rem;
|
||||
max-width: 65%;
|
||||
@@ -41,6 +56,7 @@
|
||||
margin-left: -7.4rem;
|
||||
margin-top: -0.1rem;
|
||||
background-color: transparent;
|
||||
z-index: $modal-zIndex + 1;
|
||||
|
||||
.background {
|
||||
position: fixed;
|
||||
28
app/assets/stylesheets/shared/variables/variables.scss
Normal file
28
app/assets/stylesheets/shared/variables/variables.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
$padding-small: 0.5rem;
|
||||
$radius-small: 0.25em;
|
||||
$white: #fff;
|
||||
$dynamic-blue: #3d8dd1;
|
||||
$teal-500: #0096ad;
|
||||
|
||||
/* Defined in foundation-rails components/_reveal.scss */
|
||||
$modal-zIndex: 1005;
|
||||
|
||||
@font-face {
|
||||
font-family: 'OFN';
|
||||
src: font-url('OFN-v2.eot');
|
||||
src: font-url('OFN-v2.eot') format('embedded-opentype'),
|
||||
font-url('OFN-v2.woff') format('woff'),
|
||||
font-url('OFN-v2.ttf') format('truetype'),
|
||||
font-url('OFN-v2.svg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@mixin icon-font {
|
||||
font-family: "OFN";
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
7
app/components/distributor_title_component.rb
Normal file
7
app/components/distributor_title_component.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DistributorTitleComponent < ViewComponent::Base
|
||||
def initialize(name:)
|
||||
@name = name
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
%h3= @name
|
||||
7
app/components/example_component.rb
Normal file
7
app/components/example_component.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExampleComponent < ViewComponent::Base
|
||||
def initialize(title:)
|
||||
@title = title
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
%h1 #{@title}
|
||||
@@ -18,21 +18,13 @@ module Admin
|
||||
format.html
|
||||
format.json do
|
||||
render json: @collection,
|
||||
each_serializer: index_each_serializer,
|
||||
each_serializer: ::Api::Admin::CustomerWithBalanceSerializer,
|
||||
tag_rule_mapping: tag_rule_mapping,
|
||||
customer_tags: customer_tags_by_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index_each_serializer
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, spree_current_user)
|
||||
::Api::Admin::CustomerWithBalanceSerializer
|
||||
else
|
||||
::Api::Admin::CustomerWithCalculatedBalanceSerializer
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
render_as_json @customer, ams_prefix: params[:ams_prefix]
|
||||
end
|
||||
@@ -53,15 +45,12 @@ module Admin
|
||||
|
||||
# copy of Admin::ResourceController without flash notice
|
||||
def destroy
|
||||
invoke_callbacks(:destroy, :before)
|
||||
if @object.destroy
|
||||
invoke_callbacks(:destroy, :after)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_destroy }
|
||||
format.js { render partial: "spree/admin/shared/destroy" }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:destroy, :fails)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_destroy }
|
||||
format.json { render json: { errors: @object.errors.full_messages }, status: :conflict }
|
||||
@@ -73,21 +62,18 @@ module Admin
|
||||
|
||||
def collection
|
||||
if json_request? && params[:enterprise_id].present?
|
||||
customers_relation.
|
||||
includes(:bill_address, :ship_address, user: :credit_cards)
|
||||
CustomersWithBalance.new(managed_enterprise_id).query.
|
||||
includes(
|
||||
:enterprise,
|
||||
{ bill_address: [:state, :country] },
|
||||
{ ship_address: [:state, :country] },
|
||||
user: :credit_cards
|
||||
)
|
||||
else
|
||||
Customer.where('1=0')
|
||||
end
|
||||
end
|
||||
|
||||
def customers_relation
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, spree_current_user)
|
||||
CustomersWithBalance.new(managed_enterprise_id).query
|
||||
else
|
||||
Customer.of(managed_enterprise_id)
|
||||
end
|
||||
end
|
||||
|
||||
def managed_enterprise_id
|
||||
@managed_enterprise_id ||= Enterprise.managed_by(spree_current_user).
|
||||
select('enterprises.id').find_by(id: params[:enterprise_id])
|
||||
|
||||
@@ -48,12 +48,11 @@ module Admin
|
||||
end
|
||||
|
||||
def update
|
||||
invoke_callbacks(:update, :before)
|
||||
tag_rules_attributes = params[object_name].delete :tag_rules_attributes
|
||||
update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present?
|
||||
update_enterprise_notifications
|
||||
|
||||
if @object.update(enterprise_params)
|
||||
invoke_callbacks(:update, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
@@ -61,7 +60,6 @@ module Admin
|
||||
format.json { render_as_json @object, ams_prefix: 'index', spree_current_user: spree_current_user }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:update, :fails)
|
||||
respond_with(@object) do |format|
|
||||
format.json { render json: { errors: @object.errors.messages }, status: :unprocessable_entity }
|
||||
end
|
||||
@@ -219,7 +217,6 @@ module Admin
|
||||
tag_rules_attributes.select{ |_i, attrs| attrs[:type].present? }.each do |_i, attrs|
|
||||
rule = @object.tag_rules.find_by(id: attrs.delete(:id)) ||
|
||||
attrs[:type].constantize.new(enterprise: @object)
|
||||
create_calculator_for(rule, attrs) if rule.type == "TagRule::DiscountOrder" && rule.calculator.nil?
|
||||
|
||||
rule.update(attrs.permit(PermittedAttributes::TagRules.attributes))
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'action_callbacks'
|
||||
|
||||
module Admin
|
||||
class ResourceController < Spree::Admin::BaseController
|
||||
helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
|
||||
@@ -11,7 +9,6 @@ module Admin
|
||||
respond_to :js, except: [:show, :index]
|
||||
|
||||
def new
|
||||
invoke_callbacks(:new_action, :before)
|
||||
respond_with(@object) do |format|
|
||||
format.html { render layout: !request.xhr? }
|
||||
format.js { render layout: false }
|
||||
@@ -26,32 +23,26 @@ module Admin
|
||||
end
|
||||
|
||||
def update
|
||||
invoke_callbacks(:update, :before)
|
||||
if @object.update(permitted_resource_params)
|
||||
invoke_callbacks(:update, :after)
|
||||
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
|
||||
invoke_callbacks(:update, :fails)
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
invoke_callbacks(:create, :before)
|
||||
@object.attributes = permitted_resource_params
|
||||
if @object.save
|
||||
invoke_callbacks(:create, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_created)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:create, :fails)
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
@@ -67,16 +58,13 @@ module Admin
|
||||
end
|
||||
|
||||
def destroy
|
||||
invoke_callbacks(:destroy, :before)
|
||||
if @object.destroy
|
||||
invoke_callbacks(:destroy, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
format.js { render partial: "spree/admin/shared/destroy" }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:destroy, :fails)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
end
|
||||
@@ -92,7 +80,6 @@ module Admin
|
||||
|
||||
class << self
|
||||
attr_accessor :parent_data
|
||||
attr_accessor :callbacks
|
||||
|
||||
def belongs_to(model_name, options = {})
|
||||
@parent_data ||= {}
|
||||
@@ -100,26 +87,6 @@ module Admin
|
||||
@parent_data[:model_class] = model_name.to_s.classify.constantize
|
||||
@parent_data[:find_by] = options[:find_by] || :id
|
||||
end
|
||||
|
||||
def new_action
|
||||
@callbacks ||= {}
|
||||
@callbacks[:new_action] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def create
|
||||
@callbacks ||= {}
|
||||
@callbacks[:create] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def update
|
||||
@callbacks ||= {}
|
||||
@callbacks[:update] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def destroy
|
||||
@callbacks ||= {}
|
||||
@callbacks[:destroy] ||= ActionCallbacks.new
|
||||
end
|
||||
end
|
||||
|
||||
def model_class
|
||||
@@ -212,17 +179,6 @@ module Admin
|
||||
collection_url
|
||||
end
|
||||
|
||||
def invoke_callbacks(action, callback_type)
|
||||
callbacks = self.class.callbacks || {}
|
||||
return if callbacks[action].nil?
|
||||
|
||||
case callback_type.to_sym
|
||||
when :before then callbacks[action].before_methods.each { |method| __send__ method }
|
||||
when :after then callbacks[action].after_methods.each { |method| __send__ method }
|
||||
when :fails then callbacks[action].fails_methods.each { |method| __send__ method }
|
||||
end
|
||||
end
|
||||
|
||||
# URL helpers
|
||||
def new_object_url(options = {})
|
||||
if parent_data.present?
|
||||
|
||||
@@ -9,7 +9,8 @@ module Admin
|
||||
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]
|
||||
update.after :sync_subscriptions_for_update
|
||||
|
||||
after_action :sync_subscriptions_for_update, only: :update
|
||||
|
||||
respond_to :json
|
||||
|
||||
@@ -120,7 +121,7 @@ module Admin
|
||||
end
|
||||
|
||||
def sync_subscriptions_for_update
|
||||
return unless params[:schedule][:order_cycle_ids]
|
||||
return unless params[:schedule][:order_cycle_ids] && @object.errors.blank?
|
||||
|
||||
sync_subscriptions
|
||||
end
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# Base controller for OFN's API
|
||||
require_dependency 'spree/api/controller_setup'
|
||||
require "spree/core/controller_helpers/ssl"
|
||||
|
||||
module Api
|
||||
class BaseController < ActionController::Metal
|
||||
include RawParams
|
||||
include ActionController::StrongParameters
|
||||
include ActionController::RespondWith
|
||||
include Spree::Api::ControllerSetup
|
||||
include Spree::Core::ControllerHelpers::SSL
|
||||
include ::ActionController::Head
|
||||
include ::ActionController::ConditionalGet
|
||||
include ActionView::Layouts
|
||||
|
||||
layout false
|
||||
|
||||
attr_accessor :current_api_user
|
||||
|
||||
before_action :set_content_type
|
||||
before_action :authenticate_user
|
||||
|
||||
rescue_from Exception, with: :error_during_processing
|
||||
rescue_from CanCan::AccessDenied, with: :unauthorized
|
||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
||||
|
||||
helper Spree::Api::ApiHelpers
|
||||
|
||||
ssl_allowed
|
||||
|
||||
# Include these because we inherit from ActionController::Metal
|
||||
# rather than ActionController::Base and these are required for AMS
|
||||
include ActionController::Serialization
|
||||
include ActionController::UrlFor
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
use_renderers :json
|
||||
check_authorization
|
||||
|
||||
def respond_with_conflict(json_hash)
|
||||
render json: json_hash, status: :conflict
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Use logged in user (spree_current_user) for API authentication (current_api_user)
|
||||
def authenticate_user
|
||||
return if @current_api_user = spree_current_user
|
||||
|
||||
if api_key.blank?
|
||||
# An anonymous user
|
||||
@current_api_user = Spree.user_class.new
|
||||
return
|
||||
end
|
||||
|
||||
return if @current_api_user = Spree.user_class.find_by(spree_api_key: api_key.to_s)
|
||||
|
||||
invalid_api_key
|
||||
end
|
||||
|
||||
def set_content_type
|
||||
headers["Content-Type"] = "application/json"
|
||||
end
|
||||
|
||||
def error_during_processing(exception)
|
||||
Bugsnag.notify(exception)
|
||||
|
||||
render(json: { exception: exception.message },
|
||||
status: :unprocessable_entity) && return
|
||||
end
|
||||
|
||||
def current_ability
|
||||
Spree::Ability.new(current_api_user)
|
||||
end
|
||||
|
||||
def api_key
|
||||
request.headers["X-Spree-Token"] || params[:token]
|
||||
end
|
||||
helper_method :api_key
|
||||
|
||||
def invalid_resource!(resource)
|
||||
@resource = resource
|
||||
render(json: { error: I18n.t(:invalid_resource, scope: "spree.api"),
|
||||
errors: @resource.errors },
|
||||
status: :unprocessable_entity)
|
||||
end
|
||||
|
||||
def invalid_api_key
|
||||
render(json: { error: I18n.t(:invalid_api_key, key: api_key, scope: "spree.api") },
|
||||
status: :unauthorized) && return
|
||||
end
|
||||
|
||||
def unauthorized
|
||||
render(json: { error: I18n.t(:unauthorized, scope: "spree.api") },
|
||||
status: :unauthorized) && return
|
||||
end
|
||||
|
||||
def not_found
|
||||
render(json: { error: I18n.t(:resource_not_found, scope: "spree.api") },
|
||||
status: :not_found) && return
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,37 +0,0 @@
|
||||
module Api
|
||||
class CustomersController < Api::BaseController
|
||||
skip_authorization_check only: :index
|
||||
|
||||
def index
|
||||
@customers = current_api_user.customers
|
||||
render json: @customers, each_serializer: CustomerSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@customer = Customer.find(params[:id])
|
||||
authorize! :update, @customer
|
||||
|
||||
client_secret = RecurringPayments.setup_for(@customer) if params[:customer][:allow_charges]
|
||||
|
||||
if @customer.update(customer_params)
|
||||
add_recurring_payment_info(client_secret)
|
||||
render json: @customer, serializer: CustomerSerializer, status: :ok
|
||||
else
|
||||
invalid_resource!(@customer)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_recurring_payment_info(client_secret)
|
||||
return unless client_secret
|
||||
|
||||
@customer.gateway_recurring_payment_client_secret = client_secret
|
||||
@customer.gateway_shop_id = @customer.enterprise.stripe_account&.stripe_user_id
|
||||
end
|
||||
|
||||
def customer_params
|
||||
params.require(:customer).permit(:code, :email, :enterprise_id, :allow_charges)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,42 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'api/admin/enterprise_serializer'
|
||||
|
||||
module Api
|
||||
class EnterpriseAttachmentController < Api::BaseController
|
||||
class MissingImplementationError < StandardError; end
|
||||
class UnknownEnterpriseAuthorizationActionError < StandardError; end
|
||||
|
||||
before_action :load_enterprise
|
||||
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message) unless @enterprise.public_send("#{attachment_name}?")
|
||||
|
||||
@enterprise.update!(attachment_name => nil)
|
||||
render json: @enterprise, serializer: Admin::EnterpriseSerializer, spree_current_user: spree_current_user
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def attachment_name
|
||||
raise MissingImplementationError, "Method attachment_name should be defined"
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
raise MissingImplementationError, "Method enterprise_authorize_action should be defined"
|
||||
end
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = Enterprise.find_by(permalink: params[:enterprise_id].to_s)
|
||||
raise UnknownEnterpriseAuthorizationActionError if enterprise_authorize_action.blank?
|
||||
|
||||
authorize!(enterprise_authorize_action, @enterprise)
|
||||
end
|
||||
|
||||
def destroy_attachment_does_not_exist_error_message
|
||||
I18n.t("api.enterprise_#{attachment_name}.destroy_attachment_does_not_exist")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
module Api
|
||||
class EnterpriseFeesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
authorize! :destroy, enterprise_fee
|
||||
|
||||
if enterprise_fee.destroy
|
||||
render plain: I18n.t(:successfully_removed), status: :no_content
|
||||
else
|
||||
render plain: enterprise_fee.errors.full_messages.first, status: :forbidden
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enterprise_fee
|
||||
@enterprise_fee ||= EnterpriseFee.find_by id: params[:id]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,82 +0,0 @@
|
||||
module Api
|
||||
class EnterprisesController < Api::BaseController
|
||||
include GeocodeEnterpriseAddress
|
||||
|
||||
before_action :override_owner, only: [:create, :update]
|
||||
before_action :check_type, only: :update
|
||||
before_action :override_sells, only: [:create, :update]
|
||||
before_action :override_visible, only: [:create, :update]
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
authorize! :create, Enterprise
|
||||
|
||||
# params[:user_ids] breaks the enterprise creation
|
||||
# We remove them from params and save them after creating the enterprise
|
||||
user_ids = enterprise_params.delete(:user_ids)
|
||||
@enterprise = Enterprise.new(enterprise_params)
|
||||
if @enterprise.save
|
||||
geocode_address_if_use_geocoder
|
||||
@enterprise.user_ids = user_ids
|
||||
render json: @enterprise.id, status: :created
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
|
||||
authorize! :update, @enterprise
|
||||
|
||||
if @enterprise.update(enterprise_params)
|
||||
geocode_address_if_use_geocoder
|
||||
render json: @enterprise.id, status: :ok
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
def update_image
|
||||
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
|
||||
authorize! :update, @enterprise
|
||||
|
||||
if params[:logo] && @enterprise.update( logo: params[:logo] )
|
||||
render plain: @enterprise.logo.url(:medium), status: :ok
|
||||
elsif params[:promo] && @enterprise.update( promo_image: params[:promo] )
|
||||
render plain: @enterprise.promo_image.url(:medium), status: :ok
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def override_owner
|
||||
enterprise_params[:owner_id] = current_api_user.id
|
||||
end
|
||||
|
||||
def check_type
|
||||
enterprise_params.delete :type unless current_api_user.admin?
|
||||
end
|
||||
|
||||
def override_sells
|
||||
has_hub = current_api_user.owned_enterprises.is_hub.any?
|
||||
new_enterprise_is_producer = !!enterprise_params[:is_primary_producer]
|
||||
|
||||
enterprise_params[:sells] = if has_hub && !new_enterprise_is_producer
|
||||
'any'
|
||||
else
|
||||
'unspecified'
|
||||
end
|
||||
end
|
||||
|
||||
def override_visible
|
||||
enterprise_params[:visible] = false
|
||||
end
|
||||
|
||||
def enterprise_params
|
||||
@enterprise_params ||= PermittedAttributes::Enterprise.new(params).call.
|
||||
to_h.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,100 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This controller lists products that can be added to an exchange
|
||||
#
|
||||
# Pagination is optional and can be required by using param[:page]
|
||||
module Api
|
||||
class ExchangeProductsController < Api::BaseController
|
||||
include PaginationData
|
||||
DEFAULT_PER_PAGE = 100
|
||||
|
||||
skip_authorization_check only: [:index]
|
||||
|
||||
# If exchange_id is present in the URL:
|
||||
# Lists Products that can be added to that Exchange
|
||||
#
|
||||
# If exchange_id is not present in the URL:
|
||||
# Lists Products of the Enterprise given that can be added to the given Order Cycle
|
||||
# In this case parameters are: enterprise_id, order_cycle_id and incoming
|
||||
# (order_cycle_id is not necessary for incoming exchanges)
|
||||
def index
|
||||
if exchange_params[:exchange_id].present?
|
||||
load_data_from_exchange
|
||||
else
|
||||
load_data_from_other_params
|
||||
end
|
||||
|
||||
render_variant_count && return if params[:action_name] == "variant_count"
|
||||
|
||||
render_paginated_products paginated_products
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_variant_count
|
||||
render plain: {
|
||||
count: variants.count
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def variants
|
||||
renderer.exchange_variants(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def products
|
||||
renderer.exchange_products(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def renderer
|
||||
@renderer ||= ExchangeProductsRenderer.
|
||||
new(@order_cycle, spree_current_user)
|
||||
end
|
||||
|
||||
def paginated_products
|
||||
return products unless pagination_required?
|
||||
|
||||
products.
|
||||
page(params[:page]).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
end
|
||||
|
||||
def load_data_from_exchange
|
||||
exchange = Exchange.find_by(id: exchange_params[:exchange_id])
|
||||
|
||||
@order_cycle = exchange.order_cycle
|
||||
@incoming = exchange.incoming
|
||||
@enterprise = exchange.sender
|
||||
end
|
||||
|
||||
def load_data_from_other_params
|
||||
@enterprise = Enterprise.find_by(id: exchange_params[:enterprise_id])
|
||||
|
||||
# This will be a string (eg "true") when it arrives via params, but we want a boolean
|
||||
@incoming = ActiveModel::Type::Boolean.new.cast exchange_params[:incoming]
|
||||
|
||||
if exchange_params[:order_cycle_id]
|
||||
@order_cycle = OrderCycle.find_by(id: exchange_params[:order_cycle_id])
|
||||
elsif !@incoming
|
||||
raise "order_cycle_id is required to list products for new outgoing exchange"
|
||||
end
|
||||
end
|
||||
|
||||
def render_paginated_products(paginated_products)
|
||||
serialized_products = ActiveModel::ArraySerializer.new(
|
||||
paginated_products,
|
||||
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
|
||||
order_cycle: @order_cycle
|
||||
)
|
||||
|
||||
render json: {
|
||||
products: serialized_products,
|
||||
pagination: pagination_data(paginated_products)
|
||||
}
|
||||
end
|
||||
|
||||
def exchange_params
|
||||
params.permit(:enterprise_id, :exchange_id, :order_cycle_id, :incoming).
|
||||
to_h.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
module Api
|
||||
class LogosController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
:logo
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
case action_name.to_sym
|
||||
when :destroy
|
||||
:remove_logo
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,101 +0,0 @@
|
||||
module Api
|
||||
class OrderCyclesController < Api::BaseController
|
||||
include EnterprisesHelper
|
||||
include ApiActionCaching
|
||||
|
||||
skip_authorization_check
|
||||
skip_before_action :authenticate_user, :ensure_api_key, only: [:taxons, :properties]
|
||||
|
||||
caches_action :taxons, :properties,
|
||||
expires_in: CacheService::FILTERS_EXPIRY,
|
||||
cache_path: proc { |controller| controller.request.url }
|
||||
|
||||
def products
|
||||
return render_no_products unless order_cycle.open?
|
||||
|
||||
products = ProductsRenderer.new(
|
||||
distributor,
|
||||
order_cycle,
|
||||
customer,
|
||||
search_params
|
||||
).products_json
|
||||
|
||||
render plain: products
|
||||
rescue ProductsRenderer::NoProducts
|
||||
render_no_products
|
||||
end
|
||||
|
||||
def taxons
|
||||
taxons = Spree::Taxon.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
|
||||
render plain: ActiveModel::ArraySerializer.new(
|
||||
taxons, each_serializer: Api::TaxonSerializer
|
||||
).to_json
|
||||
end
|
||||
|
||||
def properties
|
||||
render plain: ActiveModel::ArraySerializer.new(
|
||||
product_properties | producer_properties, each_serializer: Api::PropertySerializer
|
||||
).to_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_no_products
|
||||
render status: :not_found, json: {}
|
||||
end
|
||||
|
||||
def product_properties
|
||||
Spree::Property.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def producer_properties
|
||||
producers = Enterprise.
|
||||
joins(:supplied_products).
|
||||
where(spree_products: { id: distributed_products })
|
||||
|
||||
Spree::Property.
|
||||
joins(:producer_properties).
|
||||
where(producer_properties: { producer_id: producers }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def search_params
|
||||
permitted_search_params = params.slice :q, :page, :per_page
|
||||
|
||||
if permitted_search_params.key? :q
|
||||
permitted_search_params[:q].slice!(*permitted_ransack_params)
|
||||
end
|
||||
|
||||
permitted_search_params
|
||||
end
|
||||
|
||||
def permitted_ransack_params
|
||||
[:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont,
|
||||
:properties_id_or_supplier_properties_id_in_any,
|
||||
:primary_taxon_id_in_any]
|
||||
end
|
||||
|
||||
def distributor
|
||||
@distributor ||= Enterprise.find_by(id: params[:distributor])
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
@order_cycle ||= OrderCycle.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def customer
|
||||
@current_api_user.andand.customer_of(distributor) || nil
|
||||
end
|
||||
|
||||
def distributed_products
|
||||
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,67 +0,0 @@
|
||||
module Api
|
||||
class OrdersController < Api::BaseController
|
||||
include PaginationData
|
||||
|
||||
def show
|
||||
authorize! :read, order
|
||||
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
|
||||
end
|
||||
|
||||
def index
|
||||
authorize! :admin, Spree::Order
|
||||
|
||||
orders = SearchOrders.new(params, current_api_user).orders
|
||||
|
||||
render json: {
|
||||
orders: serialized_orders(orders),
|
||||
pagination: pagination_data(orders)
|
||||
}
|
||||
end
|
||||
|
||||
def ship
|
||||
authorize! :admin, order
|
||||
|
||||
if order.ship
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
render json: { error: I18n.t('api.orders.failed_to_update') }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def capture
|
||||
authorize! :admin, order
|
||||
|
||||
pending_payment = order.pending_payments.first
|
||||
|
||||
return payment_capture_failed unless order.payment_required? && pending_payment
|
||||
|
||||
if pending_payment.capture!
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
payment_capture_failed
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
error_during_processing(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payment_capture_failed
|
||||
render json: { error: I18n.t(:payment_processing_failed) }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def serialized_orders(orders)
|
||||
ActiveModel::ArraySerializer.new(
|
||||
orders,
|
||||
each_serializer: Api::Admin::OrderSerializer
|
||||
)
|
||||
end
|
||||
|
||||
def order
|
||||
@order ||= Spree::Order.
|
||||
where(number: params[:id]).
|
||||
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
|
||||
first!
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
module Api
|
||||
class ProductImagesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def update_product_image
|
||||
@product = Spree::Product.find(params[:product_id])
|
||||
authorize! :update, @product
|
||||
|
||||
if @product.images.first.nil?
|
||||
@image = Spree::Image.create(attachment: params[:file], viewable_id: @product.master.id, viewable_type: 'Spree::Variant')
|
||||
render json: @image, serializer: ImageSerializer, status: :created
|
||||
else
|
||||
@image = @product.images.first
|
||||
@image.update(attachment: params[:file])
|
||||
render json: @image, serializer: ImageSerializer, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,159 +0,0 @@
|
||||
require 'open_food_network/permissions'
|
||||
require 'spree/core/product_duplicator'
|
||||
|
||||
module Api
|
||||
class ProductsController < Api::BaseController
|
||||
include PaginationData
|
||||
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 = find_product(params[:id])
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize! :create, Spree::Product
|
||||
@product = Spree::Product.new(product_params)
|
||||
|
||||
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 = find_product(params[:id])
|
||||
if @product.update(product_params)
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :ok
|
||||
else
|
||||
invalid_resource!(@product)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :delete, Spree::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
|
||||
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.
|
||||
page(params[:page] || 1).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
|
||||
render_paged_products @products
|
||||
end
|
||||
|
||||
def overridable
|
||||
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
|
||||
|
||||
# POST /api/products/:product_id/clone
|
||||
#
|
||||
def clone
|
||||
authorize! :create, Spree::Product
|
||||
original_product = find_product(params[:product_id])
|
||||
authorize! :update, original_product
|
||||
|
||||
@product = original_product.duplicate
|
||||
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
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
|
||||
[
|
||||
master: [:images],
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
|
||||
{ option_values: :option_type }]
|
||||
]
|
||||
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.
|
||||
page(params[:page]).per(params[:per_page])
|
||||
end
|
||||
|
||||
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
|
||||
serialized_products = ActiveModel::ArraySerializer.new(
|
||||
products,
|
||||
each_serializer: product_serializer
|
||||
)
|
||||
|
||||
render json: {
|
||||
products: serialized_products,
|
||||
pagination: pagination_data(products)
|
||||
}
|
||||
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
|
||||
@@ -1,16 +0,0 @@
|
||||
module Api
|
||||
class PromoImagesController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
:promo_image
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
case action_name.to_sym
|
||||
when :destroy
|
||||
:remove_promo_image
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,112 +0,0 @@
|
||||
require 'open_food_network/scope_variant_to_hub'
|
||||
|
||||
module Api
|
||||
class ShipmentsController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :find_order
|
||||
before_action :find_and_update_shipment, only: [:ship, :ready, :add, :remove]
|
||||
|
||||
def create
|
||||
variant = scoped_variant(params[:variant_id])
|
||||
quantity = params[:quantity].to_i
|
||||
@shipment = get_or_create_shipment(params[:stock_location_id])
|
||||
|
||||
@order.contents.add(variant, quantity, nil, @shipment)
|
||||
|
||||
@shipment.refresh_rates
|
||||
@shipment.save!
|
||||
|
||||
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
def update
|
||||
authorize! :read, Spree::Shipment
|
||||
@shipment = @order.shipments.find_by!(number: params[:id])
|
||||
params[:shipment] ||= []
|
||||
unlock = params[:shipment].delete(:unlock)
|
||||
|
||||
if unlock == 'yes'
|
||||
@shipment.fee_adjustment.open
|
||||
end
|
||||
|
||||
@shipment.update(shipment_params[:shipment])
|
||||
|
||||
if unlock == 'yes'
|
||||
@shipment.fee_adjustment.close
|
||||
end
|
||||
|
||||
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
def ready
|
||||
authorize! :read, Spree::Shipment
|
||||
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
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
def ship
|
||||
authorize! :read, Spree::Shipment
|
||||
unless @shipment.shipped?
|
||||
@shipment.ship!
|
||||
end
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
def add
|
||||
variant = scoped_variant(params[:variant_id])
|
||||
quantity = params[:quantity].to_i
|
||||
|
||||
@order.contents.add(variant, quantity, nil, @shipment)
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
def remove
|
||||
variant = scoped_variant(params[:variant_id])
|
||||
quantity = params[:quantity].to_i
|
||||
|
||||
@order.contents.remove(variant, quantity, @shipment)
|
||||
@shipment.reload if @shipment.persisted?
|
||||
|
||||
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_order
|
||||
@order = Spree::Order.find_by!(number: params[:order_id])
|
||||
authorize! :read, @order
|
||||
end
|
||||
|
||||
def find_and_update_shipment
|
||||
@shipment = @order.shipments.find_by!(number: params[:id])
|
||||
@shipment.update(shipment_params[:shipment]) if shipment_params[:shipment].present?
|
||||
@shipment.reload
|
||||
end
|
||||
|
||||
def scoped_variant(variant_id)
|
||||
variant = Spree::Variant.find(variant_id)
|
||||
OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant)
|
||||
variant
|
||||
end
|
||||
|
||||
def get_or_create_shipment(stock_location_id)
|
||||
@order.shipment || @order.shipments.create(stock_location_id: stock_location_id)
|
||||
end
|
||||
|
||||
def shipment_params
|
||||
params.permit(
|
||||
[:id, :order_id, :variant_id, :quantity,
|
||||
{ shipment: [:tracking, :selected_shipping_rate_id] }]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,27 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class ShopsController < BaseController
|
||||
respond_to :json
|
||||
skip_authorization_check only: [:show, :closed_shops]
|
||||
|
||||
def show
|
||||
enterprise = Enterprise.find_by(id: params[:id])
|
||||
|
||||
render plain: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
|
||||
end
|
||||
|
||||
def closed_shops
|
||||
@active_distributor_ids = []
|
||||
@earliest_closing_times = []
|
||||
|
||||
serialized_closed_shops = ActiveModel::ArraySerializer.new(
|
||||
ShopsListService.new.closed_shops,
|
||||
each_serializer: Api::EnterpriseSerializer,
|
||||
data: OpenFoodNetwork::EnterpriseInjectionData.new
|
||||
)
|
||||
|
||||
render json: serialized_closed_shops
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,44 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class StatesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_authorization_check
|
||||
|
||||
def index
|
||||
render json: states, each_serializer: Api::StateSerializer, status: :ok
|
||||
end
|
||||
|
||||
def show
|
||||
@state = scope.find(params[:id])
|
||||
render json: @state, serializer: Api::StateSerializer, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scope
|
||||
if params[:country_id]
|
||||
@country = Spree::Country.find(params[:country_id])
|
||||
@country.states
|
||||
else
|
||||
Spree::State.all
|
||||
end
|
||||
end
|
||||
|
||||
def states
|
||||
states = scope.ransack(params[:q]).result.
|
||||
includes(:country).order('name ASC')
|
||||
|
||||
if pagination?
|
||||
states = states.page(params[:page]).per(params[:per_page])
|
||||
end
|
||||
|
||||
states
|
||||
end
|
||||
|
||||
def pagination?
|
||||
params[:page] || params[:per_page]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
module Api
|
||||
class StatusesController < ::BaseController
|
||||
respond_to :json
|
||||
|
||||
def job_queue
|
||||
render json: { alive: job_queue_alive? }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def job_queue_alive?
|
||||
Spree::Config.last_job_queue_heartbeat_at.present? &&
|
||||
Time.parse(Spree::Config.last_job_queue_heartbeat_at).in_time_zone > 6.minutes.ago
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,12 +0,0 @@
|
||||
module Api
|
||||
class TaxonomiesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_authorization_check only: :jstree
|
||||
|
||||
def jstree
|
||||
@taxonomy = Spree::Taxonomy.find(params[:id])
|
||||
render json: @taxonomy.root, serializer: Api::TaxonJstreeSerializer
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,76 +0,0 @@
|
||||
module Api
|
||||
class TaxonsController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_authorization_check only: [:index, :show, :jstree]
|
||||
|
||||
def index
|
||||
@taxons = if taxonomy
|
||||
taxonomy.root.children
|
||||
elsif params[:ids]
|
||||
Spree::Taxon.where(id: raw_params[:ids].split(","))
|
||||
else
|
||||
Spree::Taxon.ransack(raw_params[:q]).result
|
||||
end
|
||||
render json: @taxons, each_serializer: Api::TaxonSerializer
|
||||
end
|
||||
|
||||
def jstree
|
||||
@taxon = taxon
|
||||
render json: @taxon.children, each_serializer: Api::TaxonJstreeSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize! :create, Spree::Taxon
|
||||
@taxon = Spree::Taxon.new(taxon_params)
|
||||
@taxon.taxonomy_id = params[:taxonomy_id]
|
||||
taxonomy = Spree::Taxonomy.find_by(id: params[:taxonomy_id])
|
||||
|
||||
if taxonomy.nil?
|
||||
@taxon.errors[:taxonomy_id] = I18n.t(:invalid_taxonomy_id, scope: 'spree.api')
|
||||
invalid_resource!(@taxon) && return
|
||||
end
|
||||
|
||||
@taxon.parent_id = taxonomy.root.id unless params.dig(:taxon, :parent_id)
|
||||
|
||||
if @taxon.save
|
||||
render json: @taxon, serializer: Api::TaxonSerializer, status: :created
|
||||
else
|
||||
invalid_resource!(@taxon)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize! :update, Spree::Taxon
|
||||
if taxon.update(taxon_params)
|
||||
render json: taxon, serializer: Api::TaxonSerializer, status: :ok
|
||||
else
|
||||
invalid_resource!(taxon)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :delete, Spree::Taxon
|
||||
taxon.destroy
|
||||
render json: taxon, serializer: Api::TaxonSerializer, status: :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def taxonomy
|
||||
return if params[:taxonomy_id].blank?
|
||||
|
||||
@taxonomy ||= Spree::Taxonomy.find(params[:taxonomy_id])
|
||||
end
|
||||
|
||||
def taxon
|
||||
@taxon ||= taxonomy.taxons.find(params[:id])
|
||||
end
|
||||
|
||||
def taxon_params
|
||||
return if params[:taxon].blank?
|
||||
|
||||
params.require(:taxon).permit([:name, :parent_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class TermsAndConditionsController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
:terms_and_conditions
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
case action_name.to_sym
|
||||
when :destroy
|
||||
:remove_terms_and_conditions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
105
app/controllers/api/v0/base_controller.rb
Normal file
105
app/controllers/api/v0/base_controller.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Base controller for OFN's API
|
||||
require_dependency 'spree/api/controller_setup'
|
||||
require "spree/core/controller_helpers/ssl"
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class BaseController < ActionController::Metal
|
||||
include RawParams
|
||||
include ActionController::StrongParameters
|
||||
include ActionController::RespondWith
|
||||
include Spree::Api::ControllerSetup
|
||||
include Spree::Core::ControllerHelpers::SSL
|
||||
include ::ActionController::Head
|
||||
include ::ActionController::ConditionalGet
|
||||
include ActionView::Layouts
|
||||
|
||||
layout false
|
||||
|
||||
attr_accessor :current_api_user
|
||||
|
||||
before_action :set_content_type
|
||||
before_action :authenticate_user
|
||||
|
||||
rescue_from Exception, with: :error_during_processing
|
||||
rescue_from CanCan::AccessDenied, with: :unauthorized
|
||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
||||
|
||||
ssl_allowed
|
||||
|
||||
# Include these because we inherit from ActionController::Metal
|
||||
# rather than ActionController::Base and these are required for AMS
|
||||
include ActionController::Serialization
|
||||
include ActionController::UrlFor
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
use_renderers :json
|
||||
check_authorization
|
||||
|
||||
def respond_with_conflict(json_hash)
|
||||
render json: json_hash, status: :conflict
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Use logged in user (spree_current_user) for API authentication (current_api_user)
|
||||
def authenticate_user
|
||||
return if @current_api_user = spree_current_user
|
||||
|
||||
if api_key.blank?
|
||||
# An anonymous user
|
||||
@current_api_user = Spree.user_class.new
|
||||
return
|
||||
end
|
||||
|
||||
return if @current_api_user = Spree.user_class.find_by(spree_api_key: api_key.to_s)
|
||||
|
||||
invalid_api_key
|
||||
end
|
||||
|
||||
def set_content_type
|
||||
headers["Content-Type"] = "application/json"
|
||||
end
|
||||
|
||||
def error_during_processing(exception)
|
||||
Bugsnag.notify(exception)
|
||||
|
||||
render(json: { exception: exception.message },
|
||||
status: :unprocessable_entity) && return
|
||||
end
|
||||
|
||||
def current_ability
|
||||
Spree::Ability.new(current_api_user)
|
||||
end
|
||||
|
||||
def api_key
|
||||
request.headers["X-Spree-Token"] || params[:token]
|
||||
end
|
||||
helper_method :api_key
|
||||
|
||||
def invalid_resource!(resource)
|
||||
@resource = resource
|
||||
render(json: { error: I18n.t(:invalid_resource, scope: "spree.api"),
|
||||
errors: @resource.errors },
|
||||
status: :unprocessable_entity)
|
||||
end
|
||||
|
||||
def invalid_api_key
|
||||
render(json: { error: I18n.t(:invalid_api_key, key: api_key, scope: "spree.api") },
|
||||
status: :unauthorized) && return
|
||||
end
|
||||
|
||||
def unauthorized
|
||||
render(json: { error: I18n.t(:unauthorized, scope: "spree.api") },
|
||||
status: :unauthorized) && return
|
||||
end
|
||||
|
||||
def not_found
|
||||
render(json: { error: I18n.t(:resource_not_found, scope: "spree.api") },
|
||||
status: :not_found) && return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
41
app/controllers/api/v0/customers_controller.rb
Normal file
41
app/controllers/api/v0/customers_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class CustomersController < Api::V0::BaseController
|
||||
skip_authorization_check only: :index
|
||||
|
||||
def index
|
||||
@customers = current_api_user.customers
|
||||
render json: @customers, each_serializer: CustomerSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@customer = Customer.find(params[:id])
|
||||
authorize! :update, @customer
|
||||
|
||||
client_secret = RecurringPayments.setup_for(@customer) if params[:customer][:allow_charges]
|
||||
|
||||
if @customer.update(customer_params)
|
||||
add_recurring_payment_info(client_secret)
|
||||
render json: @customer, serializer: CustomerSerializer, status: :ok
|
||||
else
|
||||
invalid_resource!(@customer)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_recurring_payment_info(client_secret)
|
||||
return unless client_secret
|
||||
|
||||
@customer.gateway_recurring_payment_client_secret = client_secret
|
||||
@customer.gateway_shop_id = @customer.enterprise.stripe_account&.stripe_user_id
|
||||
end
|
||||
|
||||
def customer_params
|
||||
params.require(:customer).permit(:code, :email, :enterprise_id, :allow_charges)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
49
app/controllers/api/v0/enterprise_attachment_controller.rb
Normal file
49
app/controllers/api/v0/enterprise_attachment_controller.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'api/admin/enterprise_serializer'
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class EnterpriseAttachmentController < Api::V0::BaseController
|
||||
class MissingImplementationError < StandardError; end
|
||||
|
||||
class UnknownEnterpriseAuthorizationActionError < StandardError; end
|
||||
|
||||
before_action :load_enterprise
|
||||
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
unless @enterprise.public_send("#{attachment_name}?")
|
||||
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message)
|
||||
end
|
||||
|
||||
@enterprise.update!(attachment_name => nil)
|
||||
render json: @enterprise,
|
||||
serializer: Admin::EnterpriseSerializer,
|
||||
spree_current_user: spree_current_user
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def attachment_name
|
||||
raise MissingImplementationError, "Method attachment_name should be defined"
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
raise MissingImplementationError, "Method enterprise_authorize_action should be defined"
|
||||
end
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = Enterprise.find_by(permalink: params[:enterprise_id].to_s)
|
||||
raise UnknownEnterpriseAuthorizationActionError if enterprise_authorize_action.blank?
|
||||
|
||||
authorize!(enterprise_authorize_action, @enterprise)
|
||||
end
|
||||
|
||||
def destroy_attachment_does_not_exist_error_message
|
||||
I18n.t("api.enterprise_#{attachment_name}.destroy_attachment_does_not_exist")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
app/controllers/api/v0/enterprise_fees_controller.rb
Normal file
25
app/controllers/api/v0/enterprise_fees_controller.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class EnterpriseFeesController < Api::V0::BaseController
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
authorize! :destroy, enterprise_fee
|
||||
|
||||
if enterprise_fee.destroy
|
||||
render plain: I18n.t(:successfully_removed), status: :no_content
|
||||
else
|
||||
render plain: enterprise_fee.errors.full_messages.first, status: :forbidden
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enterprise_fee
|
||||
@enterprise_fee ||= EnterpriseFee.find_by id: params[:id]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
86
app/controllers/api/v0/enterprises_controller.rb
Normal file
86
app/controllers/api/v0/enterprises_controller.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class EnterprisesController < Api::V0::BaseController
|
||||
include GeocodeEnterpriseAddress
|
||||
|
||||
before_action :override_owner, only: [:create, :update]
|
||||
before_action :check_type, only: :update
|
||||
before_action :override_sells, only: [:create, :update]
|
||||
before_action :override_visible, only: [:create, :update]
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
authorize! :create, Enterprise
|
||||
|
||||
# params[:user_ids] breaks the enterprise creation
|
||||
# We remove them from params and save them after creating the enterprise
|
||||
user_ids = enterprise_params.delete(:user_ids)
|
||||
@enterprise = Enterprise.new(enterprise_params)
|
||||
if @enterprise.save
|
||||
geocode_address_if_use_geocoder
|
||||
@enterprise.user_ids = user_ids
|
||||
render json: @enterprise.id, status: :created
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
|
||||
authorize! :update, @enterprise
|
||||
|
||||
if @enterprise.update(enterprise_params)
|
||||
geocode_address_if_use_geocoder
|
||||
render json: @enterprise.id, status: :ok
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
def update_image
|
||||
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
|
||||
authorize! :update, @enterprise
|
||||
|
||||
if params[:logo] && @enterprise.update( logo: params[:logo] )
|
||||
render plain: @enterprise.logo.url(:medium), status: :ok
|
||||
elsif params[:promo] && @enterprise.update( promo_image: params[:promo] )
|
||||
render plain: @enterprise.promo_image.url(:medium), status: :ok
|
||||
else
|
||||
invalid_resource!(@enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def override_owner
|
||||
enterprise_params[:owner_id] = current_api_user.id
|
||||
end
|
||||
|
||||
def check_type
|
||||
enterprise_params.delete :type unless current_api_user.admin?
|
||||
end
|
||||
|
||||
def override_sells
|
||||
has_hub = current_api_user.owned_enterprises.is_hub.any?
|
||||
new_enterprise_is_producer = !!enterprise_params[:is_primary_producer]
|
||||
|
||||
enterprise_params[:sells] = if has_hub && !new_enterprise_is_producer
|
||||
'any'
|
||||
else
|
||||
'unspecified'
|
||||
end
|
||||
end
|
||||
|
||||
def override_visible
|
||||
enterprise_params[:visible] = false
|
||||
end
|
||||
|
||||
def enterprise_params
|
||||
@enterprise_params ||= PermittedAttributes::Enterprise.new(params).call.
|
||||
to_h.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
102
app/controllers/api/v0/exchange_products_controller.rb
Normal file
102
app/controllers/api/v0/exchange_products_controller.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This controller lists products that can be added to an exchange
|
||||
#
|
||||
# Pagination is optional and can be required by using param[:page]
|
||||
module Api
|
||||
module V0
|
||||
class ExchangeProductsController < Api::V0::BaseController
|
||||
include PaginationData
|
||||
DEFAULT_PER_PAGE = 100
|
||||
|
||||
skip_authorization_check only: [:index]
|
||||
|
||||
# If exchange_id is present in the URL:
|
||||
# Lists Products that can be added to that Exchange
|
||||
#
|
||||
# If exchange_id is not present in the URL:
|
||||
# Lists Products of the Enterprise given that can be added to the given Order Cycle
|
||||
# In this case parameters are: enterprise_id, order_cycle_id and incoming
|
||||
# (order_cycle_id is not necessary for incoming exchanges)
|
||||
def index
|
||||
if exchange_params[:exchange_id].present?
|
||||
load_data_from_exchange
|
||||
else
|
||||
load_data_from_other_params
|
||||
end
|
||||
|
||||
render_variant_count && return if params[:action_name] == "variant_count"
|
||||
|
||||
render_paginated_products paginated_products
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_variant_count
|
||||
render plain: {
|
||||
count: variants.count
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def variants
|
||||
renderer.exchange_variants(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def products
|
||||
renderer.exchange_products(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def renderer
|
||||
@renderer ||= ExchangeProductsRenderer.
|
||||
new(@order_cycle, spree_current_user)
|
||||
end
|
||||
|
||||
def paginated_products
|
||||
return products unless pagination_required?
|
||||
|
||||
products.
|
||||
page(params[:page]).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
end
|
||||
|
||||
def load_data_from_exchange
|
||||
exchange = Exchange.find_by(id: exchange_params[:exchange_id])
|
||||
|
||||
@order_cycle = exchange.order_cycle
|
||||
@incoming = exchange.incoming
|
||||
@enterprise = exchange.sender
|
||||
end
|
||||
|
||||
def load_data_from_other_params
|
||||
@enterprise = Enterprise.find_by(id: exchange_params[:enterprise_id])
|
||||
|
||||
# This will be a string (eg "true") when it arrives via params, but we want a boolean
|
||||
@incoming = ActiveModel::Type::Boolean.new.cast exchange_params[:incoming]
|
||||
|
||||
if exchange_params[:order_cycle_id]
|
||||
@order_cycle = OrderCycle.find_by(id: exchange_params[:order_cycle_id])
|
||||
elsif !@incoming
|
||||
raise "order_cycle_id is required to list products for new outgoing exchange"
|
||||
end
|
||||
end
|
||||
|
||||
def render_paginated_products(paginated_products)
|
||||
serialized_products = ActiveModel::ArraySerializer.new(
|
||||
paginated_products,
|
||||
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
|
||||
order_cycle: @order_cycle
|
||||
)
|
||||
|
||||
render json: {
|
||||
products: serialized_products,
|
||||
pagination: pagination_data(paginated_products)
|
||||
}
|
||||
end
|
||||
|
||||
def exchange_params
|
||||
params.permit(:enterprise_id, :exchange_id, :order_cycle_id, :incoming).
|
||||
to_h.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
20
app/controllers/api/v0/logos_controller.rb
Normal file
20
app/controllers/api/v0/logos_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class LogosController < Api::V0::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
:logo
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
case action_name.to_sym
|
||||
when :destroy
|
||||
:remove_logo
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
105
app/controllers/api/v0/order_cycles_controller.rb
Normal file
105
app/controllers/api/v0/order_cycles_controller.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class OrderCyclesController < Api::V0::BaseController
|
||||
include EnterprisesHelper
|
||||
include ApiActionCaching
|
||||
|
||||
skip_authorization_check
|
||||
skip_before_action :authenticate_user, :ensure_api_key, only: [:taxons, :properties]
|
||||
|
||||
caches_action :taxons, :properties,
|
||||
expires_in: CacheService::FILTERS_EXPIRY,
|
||||
cache_path: proc { |controller| controller.request.url }
|
||||
|
||||
def products
|
||||
return render_no_products unless order_cycle.open?
|
||||
|
||||
products = ProductsRenderer.new(
|
||||
distributor,
|
||||
order_cycle,
|
||||
customer,
|
||||
search_params
|
||||
).products_json
|
||||
|
||||
render plain: products
|
||||
rescue ProductsRenderer::NoProducts
|
||||
render_no_products
|
||||
end
|
||||
|
||||
def taxons
|
||||
taxons = Spree::Taxon.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
|
||||
render plain: ActiveModel::ArraySerializer.new(
|
||||
taxons, each_serializer: Api::TaxonSerializer
|
||||
).to_json
|
||||
end
|
||||
|
||||
def properties
|
||||
render plain: ActiveModel::ArraySerializer.new(
|
||||
product_properties | producer_properties, each_serializer: Api::PropertySerializer
|
||||
).to_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_no_products
|
||||
render status: :not_found, json: {}
|
||||
end
|
||||
|
||||
def product_properties
|
||||
Spree::Property.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def producer_properties
|
||||
producers = Enterprise.
|
||||
joins(:supplied_products).
|
||||
where(spree_products: { id: distributed_products })
|
||||
|
||||
Spree::Property.
|
||||
joins(:producer_properties).
|
||||
where(producer_properties: { producer_id: producers }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def search_params
|
||||
permitted_search_params = params.slice :q, :page, :per_page
|
||||
|
||||
if permitted_search_params.key? :q
|
||||
permitted_search_params[:q].slice!(*permitted_ransack_params)
|
||||
end
|
||||
|
||||
permitted_search_params
|
||||
end
|
||||
|
||||
def permitted_ransack_params
|
||||
[:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont,
|
||||
:properties_id_or_supplier_properties_id_in_any,
|
||||
:primary_taxon_id_in_any]
|
||||
end
|
||||
|
||||
def distributor
|
||||
@distributor ||= Enterprise.find_by(id: params[:distributor])
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
@order_cycle ||= OrderCycle.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def customer
|
||||
@current_api_user.andand.customer_of(distributor) || nil
|
||||
end
|
||||
|
||||
def distributed_products
|
||||
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
72
app/controllers/api/v0/orders_controller.rb
Normal file
72
app/controllers/api/v0/orders_controller.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
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
|
||||
|
||||
orders = SearchOrders.new(params, current_api_user).orders
|
||||
|
||||
render json: {
|
||||
orders: serialized_orders(orders),
|
||||
pagination: pagination_data(orders)
|
||||
}
|
||||
end
|
||||
|
||||
def ship
|
||||
authorize! :admin, order
|
||||
|
||||
if order.ship
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
render json: { error: I18n.t('api.orders.failed_to_update') },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def capture
|
||||
authorize! :admin, order
|
||||
|
||||
pending_payment = order.pending_payments.first
|
||||
|
||||
return payment_capture_failed unless order.payment_required? && pending_payment
|
||||
|
||||
if pending_payment.capture!
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
payment_capture_failed
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
error_during_processing(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payment_capture_failed
|
||||
render json: { error: I18n.t(:payment_processing_failed) }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def serialized_orders(orders)
|
||||
ActiveModel::ArraySerializer.new(
|
||||
orders,
|
||||
each_serializer: Api::Admin::OrderSerializer
|
||||
)
|
||||
end
|
||||
|
||||
def order
|
||||
@order ||= Spree::Order.
|
||||
where(number: params[:id]).
|
||||
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
|
||||
first!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
app/controllers/api/v0/product_images_controller.rb
Normal file
27
app/controllers/api/v0/product_images_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class ProductImagesController < Api::V0::BaseController
|
||||
respond_to :json
|
||||
|
||||
def update_product_image
|
||||
@product = Spree::Product.find(params[:product_id])
|
||||
authorize! :update, @product
|
||||
|
||||
if @product.images.first.nil?
|
||||
@image = Spree::Image.create(
|
||||
attachment: params[:file],
|
||||
viewable_id: @product.master.id,
|
||||
viewable_type: 'Spree::Variant'
|
||||
)
|
||||
render json: @image, serializer: ImageSerializer, status: :created
|
||||
else
|
||||
@image = @product.images.first
|
||||
@image.update(attachment: params[:file])
|
||||
render json: @image, serializer: ImageSerializer, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
163
app/controllers/api/v0/products_controller.rb
Normal file
163
app/controllers/api/v0/products_controller.rb
Normal file
@@ -0,0 +1,163 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/permissions'
|
||||
require 'spree/core/product_duplicator'
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class ProductsController < Api::V0::BaseController
|
||||
include PaginationData
|
||||
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 = find_product(params[:id])
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize! :create, Spree::Product
|
||||
@product = Spree::Product.new(product_params)
|
||||
|
||||
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 = find_product(params[:id])
|
||||
if @product.update(product_params)
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :ok
|
||||
else
|
||||
invalid_resource!(@product)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :delete, Spree::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
|
||||
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.
|
||||
page(params[:page] || 1).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
|
||||
render_paged_products @products
|
||||
end
|
||||
|
||||
def overridable
|
||||
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
|
||||
|
||||
# POST /api/products/:product_id/clone
|
||||
#
|
||||
def clone
|
||||
authorize! :create, Spree::Product
|
||||
original_product = find_product(params[:product_id])
|
||||
authorize! :update, original_product
|
||||
|
||||
@product = original_product.duplicate
|
||||
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
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
|
||||
[
|
||||
master: [:images],
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
|
||||
{ option_values: :option_type }]
|
||||
]
|
||||
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.
|
||||
page(params[:page]).per(params[:per_page])
|
||||
end
|
||||
|
||||
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
|
||||
serialized_products = ActiveModel::ArraySerializer.new(
|
||||
products,
|
||||
each_serializer: product_serializer
|
||||
)
|
||||
|
||||
render json: {
|
||||
products: serialized_products,
|
||||
pagination: pagination_data(products)
|
||||
}
|
||||
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
|
||||
20
app/controllers/api/v0/promo_images_controller.rb
Normal file
20
app/controllers/api/v0/promo_images_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V0
|
||||
class PromoImagesController < Api::V0::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
:promo_image
|
||||
end
|
||||
|
||||
def enterprise_authorize_action
|
||||
case action_name.to_sym
|
||||
when :destroy
|
||||
:remove_promo_image
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user