mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-15 19:06:50 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7b3813fbe |
@@ -2,9 +2,9 @@ version: "2"
|
||||
plugins:
|
||||
rubocop:
|
||||
enabled: true
|
||||
channel: "rubocop-1-12"
|
||||
channel: "rubocop-0-76"
|
||||
config:
|
||||
file: ".rubocop.yml"
|
||||
file: ".rubocop_styleguide.yml"
|
||||
scss-lint:
|
||||
enabled: true
|
||||
checks:
|
||||
|
||||
5
.github/codecov.yml
vendored
5
.github/codecov.yml
vendored
@@ -1,5 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@@ -8,7 +8,6 @@ on:
|
||||
env:
|
||||
DISABLE_KNAPSACK: true
|
||||
TIMEZONE: UTC
|
||||
COVERAGE: true
|
||||
|
||||
jobs:
|
||||
test-controllers-and-serializers:
|
||||
@@ -52,9 +51,6 @@ jobs:
|
||||
- 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:
|
||||
@@ -96,9 +92,6 @@ jobs:
|
||||
- name: Run tests
|
||||
run: bundle exec rspec --profile -- spec/models
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-admin-features-1:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
@@ -140,9 +133,6 @@ jobs:
|
||||
- name: Run admin feature tests
|
||||
run: bundle exec rspec --profile -- spec/features/admin/[a-o0-9]*_spec.rb
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-admin-features-2:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
@@ -184,9 +174,6 @@ jobs:
|
||||
- name: Run admin feature tests
|
||||
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
|
||||
services:
|
||||
@@ -228,9 +215,6 @@ jobs:
|
||||
- name: Run consumer feature tests
|
||||
run: bundle exec rspec --profile -- spec/features/consumer
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
|
||||
test-engines-etc:
|
||||
runs-on: ubuntu-18.04
|
||||
services:
|
||||
@@ -272,13 +256,8 @@ jobs:
|
||||
- name: Run JS tests
|
||||
run: RAILS_ENV=test bundle exec rake karma:run
|
||||
|
||||
# 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 all other tests
|
||||
run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib,migrations"]
|
||||
run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib"]
|
||||
|
||||
test-the-rest:
|
||||
runs-on: ubuntu-18.04
|
||||
@@ -320,6 +299,3 @@ jobs:
|
||||
|
||||
- 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.5.0
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,4 +47,3 @@ coverage
|
||||
/reports/
|
||||
!/reports/README.md
|
||||
bin/
|
||||
/spec/components/stories/**/*.stories.json
|
||||
|
||||
@@ -1 +1 @@
|
||||
14.16.1
|
||||
5.12.0
|
||||
|
||||
@@ -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. If you want to see all violations,
|
||||
# then use only that configuration:
|
||||
# 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):
|
||||
#
|
||||
# bundle exec rubocop -c .rubocop_styleguide.yml
|
||||
#
|
||||
|
||||
@@ -249,6 +249,7 @@ 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
|
||||
@@ -848,7 +849,6 @@ 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,6 +1,9 @@
|
||||
# Our Open Food Network style guide.
|
||||
#
|
||||
# These are the rules we agreed upon and we work towards.
|
||||
# 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.
|
||||
AllCops:
|
||||
NewCops: disable
|
||||
SuggestExtensions: false
|
||||
|
||||
@@ -368,6 +368,84 @@ Rails/ApplicationMailer:
|
||||
Exclude:
|
||||
- 'app/mailers/spree/base_mailer.rb'
|
||||
|
||||
# Offense count: 73
|
||||
# Cop supports --auto-correct.
|
||||
Rails/ApplicationRecord:
|
||||
Exclude:
|
||||
- 'app/models/adjustment_metadata.rb'
|
||||
- 'app/models/column_preference.rb'
|
||||
- 'app/models/coordinator_fee.rb'
|
||||
- 'app/models/customer.rb'
|
||||
- 'app/models/distributor_shipping_method.rb'
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/enterprise_fee.rb'
|
||||
- 'app/models/enterprise_group.rb'
|
||||
- 'app/models/enterprise_relationship.rb'
|
||||
- 'app/models/enterprise_relationship_permission.rb'
|
||||
- 'app/models/enterprise_role.rb'
|
||||
- 'app/models/exchange.rb'
|
||||
- 'app/models/exchange_fee.rb'
|
||||
- 'app/models/exchange_variant.rb'
|
||||
- 'app/models/inventory_item.rb'
|
||||
- 'app/models/order_cycle.rb'
|
||||
- 'app/models/order_cycle_schedule.rb'
|
||||
- 'app/models/producer_property.rb'
|
||||
- 'app/models/proxy_order.rb'
|
||||
- 'app/models/schedule.rb'
|
||||
- 'app/models/spree/address.rb'
|
||||
- 'app/models/spree/adjustment.rb'
|
||||
- 'app/models/spree/asset.rb'
|
||||
- 'app/models/spree/calculator.rb'
|
||||
- 'app/models/spree/classification.rb'
|
||||
- 'app/models/spree/country.rb'
|
||||
- 'app/models/spree/credit_card.rb'
|
||||
- 'app/models/spree/inventory_unit.rb'
|
||||
- 'app/models/spree/line_item.rb'
|
||||
- 'app/models/spree/log_entry.rb'
|
||||
- 'app/models/spree/option_type.rb'
|
||||
- 'app/models/spree/option_value.rb'
|
||||
- 'app/models/spree/option_values_line_item.rb'
|
||||
- 'app/models/spree/order.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/payment.rb'
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
- 'app/models/spree/payment_method.rb'
|
||||
- 'app/models/spree/paypal_express_checkout.rb'
|
||||
- 'app/models/spree/preference.rb'
|
||||
- 'app/models/spree/price.rb'
|
||||
- 'app/models/spree/product.rb'
|
||||
- 'app/models/spree/product_option_type.rb'
|
||||
- 'app/models/spree/product_property.rb'
|
||||
- 'app/models/spree/property.rb'
|
||||
- 'app/models/spree/return_authorization.rb'
|
||||
- 'app/models/spree/role.rb'
|
||||
- 'app/models/spree/shipment.rb'
|
||||
- 'app/models/spree/shipping_category.rb'
|
||||
- 'app/models/spree/shipping_method.rb'
|
||||
- 'app/models/spree/shipping_method_category.rb'
|
||||
- 'app/models/spree/shipping_rate.rb'
|
||||
- 'app/models/spree/state.rb'
|
||||
- 'app/models/spree/state_change.rb'
|
||||
- 'app/models/spree/stock_item.rb'
|
||||
- 'app/models/spree/stock_location.rb'
|
||||
- 'app/models/spree/stock_movement.rb'
|
||||
- 'app/models/spree/tax_category.rb'
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/spree/taxon.rb'
|
||||
- 'app/models/spree/taxonomy.rb'
|
||||
- 'app/models/spree/tokenized_permission.rb'
|
||||
- 'app/models/spree/user.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/spree/zone.rb'
|
||||
- 'app/models/spree/zone_member.rb'
|
||||
- 'app/models/stripe_account.rb'
|
||||
- 'app/models/subscription.rb'
|
||||
- 'app/models/subscription_line_item.rb'
|
||||
- 'app/models/tag_rule.rb'
|
||||
- 'app/models/variant_override.rb'
|
||||
- 'lib/tasks/data/remove_transient_data.rb'
|
||||
- 'spec/models/spree/preferences/preferable_spec.rb'
|
||||
|
||||
# Offense count: 3
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
|
||||
@@ -931,8 +1009,10 @@ Style/FrozenStringLiteralComment:
|
||||
- 'app/helpers/spree/admin/base_helper.rb'
|
||||
- 'app/helpers/spree/admin/general_settings_helper.rb'
|
||||
- 'app/helpers/spree/admin/orders_helper.rb'
|
||||
- 'app/helpers/spree/admin/payments_helper.rb'
|
||||
- 'app/helpers/spree/admin/taxons_helper.rb'
|
||||
- 'app/helpers/spree/admin/zones_helper.rb'
|
||||
- 'app/helpers/spree/api/api_helpers.rb'
|
||||
- 'app/helpers/spree/orders_helper.rb'
|
||||
- 'app/helpers/spree/reports_helper.rb'
|
||||
- 'app/helpers/spree_currency_helper.rb'
|
||||
@@ -1032,6 +1112,7 @@ Style/FrozenStringLiteralComment:
|
||||
- 'app/serializers/api/uncached_enterprise_serializer.rb'
|
||||
- 'app/serializers/api/user_serializer.rb'
|
||||
- 'app/serializers/api/variant_serializer.rb'
|
||||
- 'app/services/action_callbacks.rb'
|
||||
- 'app/services/bulk_invoice_service.rb'
|
||||
- 'app/services/cart_service.rb'
|
||||
- 'app/services/create_order_cycle.rb'
|
||||
@@ -1089,7 +1170,7 @@ Style/FrozenStringLiteralComment:
|
||||
- 'engines/order_management/app/services/reports/renderers/base.rb'
|
||||
- 'engines/order_management/app/services/reports/report_data/base.rb'
|
||||
- 'engines/web/app/controllers/web/angular_templates_controller.rb'
|
||||
- 'engines/web/app/controllers/web/api/v0/cookies_consent_controller.rb'
|
||||
- 'engines/web/app/controllers/web/api/cookies_consent_controller.rb'
|
||||
- 'engines/web/app/controllers/web/application_controller.rb'
|
||||
- 'engines/web/app/helpers/web/cookies_policy_helper.rb'
|
||||
- 'engines/web/lib/web/cookies_consent.rb'
|
||||
@@ -1134,6 +1215,7 @@ Style/FrozenStringLiteralComment:
|
||||
- 'lib/open_food_network/scope_variants_for_search.rb'
|
||||
- 'lib/open_food_network/spree_api_key_loader.rb'
|
||||
- 'lib/open_food_network/tag_rule_applicator.rb'
|
||||
- 'lib/open_food_network/user_balance_calculator.rb'
|
||||
- 'lib/open_food_network/users_and_enterprises_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/spree/authentication_helpers.rb'
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.5.8
|
||||
2.4.4
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/bin/env ruby
|
||||
|
||||
SimpleCov.start 'rails' do
|
||||
minimum_coverage 54
|
||||
|
||||
add_filter '/bin/'
|
||||
add_filter '/config/'
|
||||
add_filter '/jobs/application_job.rb'
|
||||
@@ -13,8 +15,4 @@ 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
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
stories: ['../spec/components/stories/**/*.stories.json'],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-controls',
|
||||
],
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
<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" />
|
||||
@@ -1,5 +0,0 @@
|
||||
export const parameters = {
|
||||
server: {
|
||||
url: `http://localhost:3000/rails/stories`,
|
||||
},
|
||||
};
|
||||
@@ -6,7 +6,7 @@ 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 and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file)
|
||||
* 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)
|
||||
|
||||
@@ -78,6 +78,8 @@ Note: If your OS is not explicitly supported in the setup guides then not all te
|
||||
|
||||
Note: The time zone on your machine should match the one defined in `config/application.yml`.
|
||||
|
||||
The project is configured to use [Zeus][zeus] to reduce the pre-test startup time while Rails loads. See the [Zeus GitHub page][zeus] for usage instructions.
|
||||
|
||||
Once [npm dependencies are installed][karma], AngularJS tests can be run with:
|
||||
|
||||
./script/karma run
|
||||
@@ -117,6 +119,7 @@ If these commands succeed, you should be able to [continue the setup process](#g
|
||||
[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/
|
||||
[karma]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Karma
|
||||
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
|
||||
|
||||
36
Gemfile
36
Gemfile
@@ -1,21 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby "2.5.8"
|
||||
ruby "2.4.4"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'rails', '~> 5.2'
|
||||
gem 'rails', '~> 5.0.0'
|
||||
|
||||
gem 'activemerchant', '>= 1.78.0'
|
||||
gem 'angular-rails-templates', '>= 0.3.0'
|
||||
gem 'awesome_nested_set'
|
||||
gem 'ransack', '2.4.1'
|
||||
gem 'ransack', '2.3.0'
|
||||
gem 'responders'
|
||||
gem 'sass', '<= 4.7.1'
|
||||
gem 'sass-rails', '< 6.0.0'
|
||||
|
||||
gem 'i18n'
|
||||
gem 'i18n-js', '~> 3.8.3'
|
||||
gem 'i18n-js', '~> 3.8.2'
|
||||
gem 'rails-i18n'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
|
||||
@@ -29,9 +29,9 @@ gem "order_management", path: "./engines/order_management"
|
||||
gem 'web', path: './engines/web'
|
||||
|
||||
gem 'activerecord-postgresql-adapter'
|
||||
gem 'pg', '~> 1.2.3'
|
||||
gem 'pg', '~> 0.21.0'
|
||||
|
||||
gem 'acts_as_list', '1.0.4'
|
||||
gem 'acts_as_list', '0.9.19'
|
||||
gem 'cancancan', '~> 1.15.0'
|
||||
gem 'ffaker'
|
||||
gem 'highline', '2.0.3' # Necessary for the install generator
|
||||
@@ -75,19 +75,15 @@ gem 'dalli'
|
||||
gem 'figaro'
|
||||
gem 'geocoder'
|
||||
gem 'gmaps4rails'
|
||||
gem 'mimemagic', '> 0.3.5'
|
||||
gem 'paper_trail', '~> 10.3.1'
|
||||
gem 'paperclip', '~> 3.4.1'
|
||||
gem 'rack-rewrite'
|
||||
gem 'rack-ssl', require: 'rack/ssl'
|
||||
gem 'roadie-rails', '~> 2.2.0'
|
||||
|
||||
gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'hiredis'
|
||||
gem 'roadie-rails', '~> 1.3.0'
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
gem 'wkhtmltopdf-binary'
|
||||
gem 'wkhtmltopdf-binary', '0.12.5' # We need to upgrade our CI before we can bump this :/
|
||||
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.8.3'
|
||||
@@ -96,10 +92,10 @@ gem 'whenever', require: false
|
||||
|
||||
gem 'test-unit', '~> 3.4'
|
||||
|
||||
gem 'coffee-rails', '~> 5.0.0'
|
||||
gem 'coffee-rails', '~> 4.2.2'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'mini_racer', '0.4.0'
|
||||
gem 'mini_racer', '0.3.1'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
@@ -117,12 +113,6 @@ 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'
|
||||
@@ -130,11 +120,12 @@ end
|
||||
|
||||
group :test, :development do
|
||||
# Pretty printed test output
|
||||
gem 'atomic'
|
||||
gem 'awesome_print'
|
||||
gem 'bullet'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner', require: false
|
||||
gem "factory_bot_rails", '6.2.0', require: false
|
||||
gem "factory_bot_rails", '5.2.0', require: false
|
||||
gem 'fuubar', '~> 2.5.1'
|
||||
gem 'json_spec', '~> 1.1.4'
|
||||
gem 'knapsack'
|
||||
@@ -150,7 +141,6 @@ group :test, :development do
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'codecov', require: false
|
||||
gem 'simplecov', require: false
|
||||
gem 'test-prof'
|
||||
gem 'webmock'
|
||||
@@ -169,8 +159,6 @@ 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.
|
||||
|
||||
308
Gemfile.lock
308
Gemfile.lock
@@ -47,70 +47,66 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.6)
|
||||
actionpack (= 5.2.6)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.6)
|
||||
actionpack (= 5.2.6)
|
||||
actionview (= 5.2.6)
|
||||
activejob (= 5.2.6)
|
||||
actioncable (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
nio4r (>= 1.2, < 3.0)
|
||||
websocket-driver (~> 0.6.1)
|
||||
actionmailer (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activejob (= 5.0.7.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.6)
|
||||
actionview (= 5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
actionpack (5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
rack (~> 2.0)
|
||||
rack-test (~> 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionpack-action_caching (1.2.2)
|
||||
actionpack-action_caching (1.2.1)
|
||||
actionpack (>= 4.0.0)
|
||||
actionview (5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
actionview (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_model_serializers (0.8.4)
|
||||
activemodel (>= 3.0)
|
||||
activejob (5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
activejob (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemerchant (1.119.0)
|
||||
activemerchant (1.107.4)
|
||||
activesupport (>= 4.2)
|
||||
builder (>= 2.1.2, < 4.0.0)
|
||||
i18n (>= 0.6.9)
|
||||
nokogiri (~> 1.4)
|
||||
activemodel (5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
activerecord (5.2.6)
|
||||
activemodel (= 5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
arel (>= 9.0)
|
||||
activerecord-import (1.1.0)
|
||||
activemodel (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
activerecord (5.0.7.2)
|
||||
activemodel (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
arel (~> 7.0)
|
||||
activerecord-import (1.0.8)
|
||||
activerecord (>= 3.2)
|
||||
activerecord-postgresql-adapter (0.0.1)
|
||||
pg
|
||||
activerecord-session_store (2.0.0)
|
||||
actionpack (>= 5.2.4.1)
|
||||
activerecord (>= 5.2.4.1)
|
||||
activerecord-session_store (1.1.3)
|
||||
actionpack (>= 4.0)
|
||||
activerecord (>= 4.0)
|
||||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 2.0.8, < 3)
|
||||
railties (>= 5.2.4.1)
|
||||
activestorage (5.2.6)
|
||||
actionpack (= 5.2.6)
|
||||
activerecord (= 5.2.6)
|
||||
marcel (~> 1.0.0)
|
||||
activesupport (5.2.6)
|
||||
rack (>= 1.5.2, < 3)
|
||||
railties (>= 4.0)
|
||||
activesupport (5.0.7.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
acts-as-taggable-on (7.0.0)
|
||||
activerecord (>= 5.0, < 6.2)
|
||||
acts_as_list (1.0.4)
|
||||
activerecord (>= 4.2)
|
||||
acts_as_list (0.9.19)
|
||||
activerecord (>= 3.0)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
andand (1.3.3)
|
||||
@@ -118,12 +114,13 @@ GEM
|
||||
railties (>= 4.2, < 7)
|
||||
sprockets (>= 3.0, < 5)
|
||||
tilt
|
||||
angular_rails_csrf (4.5.0)
|
||||
angular_rails_csrf (4.2.0)
|
||||
railties (>= 3, < 7)
|
||||
angularjs-file-upload-rails (2.4.1)
|
||||
angularjs-rails (1.5.5)
|
||||
arel (9.0.0)
|
||||
arel (7.1.4)
|
||||
ast (2.4.2)
|
||||
atomic (1.1.101)
|
||||
awesome_nested_set (3.4.0)
|
||||
activerecord (>= 4.0.0, < 7.0)
|
||||
awesome_print (1.9.2)
|
||||
@@ -133,7 +130,7 @@ GEM
|
||||
json (~> 1.4)
|
||||
nokogiri (~> 1)
|
||||
bcrypt (3.1.16)
|
||||
bugsnag (6.20.0)
|
||||
bugsnag (6.19.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (6.1.4)
|
||||
@@ -141,13 +138,13 @@ GEM
|
||||
uniform_notifier (~> 1.11)
|
||||
byebug (11.1.3)
|
||||
cancancan (1.15.0)
|
||||
capybara (3.35.3)
|
||||
capybara (3.32.2)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
regexp_parser (~> 1.5)
|
||||
xpath (~> 3.2)
|
||||
childprocess (3.0.0)
|
||||
chronic (0.10.2)
|
||||
@@ -155,12 +152,10 @@ GEM
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codecov (0.5.2)
|
||||
simplecov (>= 0.15, < 0.22)
|
||||
coderay (1.1.3)
|
||||
coffee-rails (5.0.0)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 5.2.0)
|
||||
railties (>= 4.0.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
@@ -187,13 +182,12 @@ GEM
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.9.0)
|
||||
css_parser (1.7.1)
|
||||
addressable
|
||||
daemons (1.4.0)
|
||||
daemons (1.3.1)
|
||||
dalli (2.7.11)
|
||||
database_cleaner (1.99.0)
|
||||
ddtrace (0.49.0)
|
||||
ffi (~> 1.0)
|
||||
ddtrace (0.46.0)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
delayed_job (4.1.9)
|
||||
@@ -206,7 +200,7 @@ GEM
|
||||
delayed_job (> 2.0.3)
|
||||
rack-protection (>= 1.5.5)
|
||||
sinatra (>= 1.4.4)
|
||||
devise (4.8.0)
|
||||
devise (4.7.3)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
@@ -217,34 +211,25 @@ GEM
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.4.4)
|
||||
docile (1.3.5)
|
||||
erubi (1.10.0)
|
||||
docile (1.3.4)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
excon (0.79.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (6.2.0)
|
||||
activesupport (>= 5.0.0)
|
||||
factory_bot_rails (6.2.0)
|
||||
factory_bot (~> 6.2.0)
|
||||
railties (>= 5.0.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.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords
|
||||
faraday-net_http (1.0.1)
|
||||
ffaker (2.18.0)
|
||||
ffaker (2.16.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)
|
||||
@@ -270,7 +255,7 @@ GEM
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
geocoder (1.6.7)
|
||||
geocoder (1.6.6)
|
||||
get_process_mem (0.2.7)
|
||||
ffi (~> 1.0)
|
||||
globalid (0.4.2)
|
||||
@@ -284,10 +269,9 @@ GEM
|
||||
tilt
|
||||
hashdiff (1.0.1)
|
||||
highline (2.0.3)
|
||||
hiredis (0.6.3)
|
||||
i18n (1.8.10)
|
||||
i18n (1.8.9)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.8.3)
|
||||
i18n-js (3.8.2)
|
||||
i18n (>= 0.6.6)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
@@ -305,7 +289,7 @@ GEM
|
||||
json_spec (1.1.5)
|
||||
multi_json (~> 1.0)
|
||||
rspec (>= 2.0, < 4.0)
|
||||
jwt (2.2.3)
|
||||
jwt (2.2.2)
|
||||
kaminari (1.2.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.2.1)
|
||||
@@ -319,30 +303,26 @@ GEM
|
||||
kaminari-core (= 1.2.1)
|
||||
kaminari-core (1.2.1)
|
||||
kgio (2.11.3)
|
||||
knapsack (1.22.0)
|
||||
knapsack (1.20.0)
|
||||
rake
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
libv8-node (15.14.0.0)
|
||||
loofah (2.9.1)
|
||||
libv8 (8.4.255.0)
|
||||
loofah (2.9.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (1.0.1)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2020.1104)
|
||||
mimemagic (0.4.3)
|
||||
nokogiri (~> 1)
|
||||
rake
|
||||
mini_mime (1.1.0)
|
||||
mini_portile2 (2.5.1)
|
||||
mini_racer (0.4.0)
|
||||
libv8-node (~> 15.14.0.0)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
mini_racer (0.3.1)
|
||||
libv8 (~> 8.4.255)
|
||||
minitest (5.14.4)
|
||||
monetize (1.11.0)
|
||||
money (~> 6.12)
|
||||
@@ -354,10 +334,9 @@ GEM
|
||||
multipart-post (2.1.1)
|
||||
mustermann (1.1.1)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
nio4r (2.5.7)
|
||||
nokogiri (1.11.4)
|
||||
mini_portile2 (~> 2.5.0)
|
||||
racc (~> 1.4)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.10)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
oauth2 (1.4.7)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
@@ -377,14 +356,16 @@ GEM
|
||||
parallel (1.20.1)
|
||||
paranoia (2.4.3)
|
||||
activerecord (>= 4.0, < 6.2)
|
||||
parser (3.0.1.1)
|
||||
parser (3.0.0.0)
|
||||
ast (~> 2.4.1)
|
||||
paypal-sdk-core (0.3.4)
|
||||
multi_json (~> 1.0)
|
||||
xml-simple
|
||||
paypal-sdk-merchant (1.117.2)
|
||||
paypal-sdk-core (~> 0.3.0)
|
||||
pg (1.2.3)
|
||||
pg (0.21.0)
|
||||
polyamorous (2.3.0)
|
||||
activerecord (>= 5.0)
|
||||
power_assert (2.0.0)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
@@ -393,29 +374,27 @@ 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.2)
|
||||
rack-mini-profiler (2.3.1)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (2.1.0)
|
||||
rack
|
||||
rack-rewrite (1.5.1)
|
||||
rack-ssl (1.4.1)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.6)
|
||||
actioncable (= 5.2.6)
|
||||
actionmailer (= 5.2.6)
|
||||
actionpack (= 5.2.6)
|
||||
actionview (= 5.2.6)
|
||||
activejob (= 5.2.6)
|
||||
activemodel (= 5.2.6)
|
||||
activerecord (= 5.2.6)
|
||||
activestorage (= 5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (5.0.7.2)
|
||||
actioncable (= 5.0.7.2)
|
||||
actionmailer (= 5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activejob (= 5.0.7.2)
|
||||
activemodel (= 5.0.7.2)
|
||||
activerecord (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.6)
|
||||
railties (= 5.0.7.2)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
@@ -430,37 +409,38 @@ GEM
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 5.0, < 6)
|
||||
rails_safe_tasks (1.0.0)
|
||||
railties (5.2.6)
|
||||
actionpack (= 5.2.6)
|
||||
activesupport (= 5.2.6)
|
||||
railties (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.1)
|
||||
rake (13.0.3)
|
||||
ransack (2.4.1)
|
||||
activerecord (>= 5.2.4)
|
||||
activesupport (>= 5.2.4)
|
||||
ransack (2.3.0)
|
||||
actionpack (>= 5.0)
|
||||
activerecord (>= 5.0)
|
||||
activesupport (>= 5.0)
|
||||
i18n
|
||||
polyamorous (= 2.3.0)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.5.1)
|
||||
redis (4.2.5)
|
||||
regexp_parser (2.1.1)
|
||||
regexp_parser (1.8.2)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rexml (3.2.5)
|
||||
roadie (4.0.0)
|
||||
rexml (3.2.4)
|
||||
roadie (3.5.1)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.8)
|
||||
roadie-rails (2.2.0)
|
||||
railties (>= 5.1, < 6.2)
|
||||
roadie (>= 3.1, < 5.0)
|
||||
roadie-rails (1.3.0)
|
||||
railties (>= 3.0, < 5.3)
|
||||
roadie (~> 3.1)
|
||||
roo (2.8.3)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (>= 1.3.0, < 3.0.0)
|
||||
@@ -476,10 +456,10 @@ GEM
|
||||
rspec-mocks (3.10.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-rails (5.0.1)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rspec-rails (4.1.2)
|
||||
actionpack (>= 4.2)
|
||||
activesupport (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
rspec-core (~> 3.10)
|
||||
rspec-expectations (~> 3.10)
|
||||
rspec-mocks (~> 3.10)
|
||||
@@ -500,21 +480,21 @@ GEM
|
||||
rswag-ui (2.4.0)
|
||||
actionpack (>= 3.1, < 7.0)
|
||||
railties (>= 3.1, < 7.0)
|
||||
rubocop (1.15.0)
|
||||
rubocop (1.12.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml
|
||||
rubocop-ast (>= 1.5.0, < 2.0)
|
||||
rubocop-ast (>= 1.2.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.5.0)
|
||||
parser (>= 3.0.1.1)
|
||||
rubocop-rails (2.10.1)
|
||||
rubocop-ast (1.4.1)
|
||||
parser (>= 2.7.1.5)
|
||||
rubocop-rails (2.9.1)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.7.0, < 2.0)
|
||||
rubocop (>= 0.90.0, < 2.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby2_keywords (0.0.2)
|
||||
@@ -534,12 +514,10 @@ GEM
|
||||
rubyzip (>= 1.2.2)
|
||||
shoulda-matchers (4.5.1)
|
||||
activesupport (>= 4.2.0)
|
||||
simplecov (0.21.2)
|
||||
simplecov (0.18.5)
|
||||
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)
|
||||
@@ -556,17 +534,17 @@ GEM
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
state_machines (0.5.0)
|
||||
state_machines-activemodel (0.8.0)
|
||||
activemodel (>= 5.1)
|
||||
state_machines-activemodel (0.7.1)
|
||||
activemodel (>= 4.1)
|
||||
state_machines (>= 0.5.0)
|
||||
state_machines-activerecord (0.8.0)
|
||||
activerecord (>= 5.1)
|
||||
state_machines-activemodel (>= 0.8.0)
|
||||
state_machines-activerecord (0.6.0)
|
||||
activerecord (>= 4.1)
|
||||
state_machines-activemodel (>= 0.5.0)
|
||||
stringex (2.8.5)
|
||||
stripe (5.30.0)
|
||||
temple (0.8.2)
|
||||
test-prof (1.0.5)
|
||||
test-unit (3.4.1)
|
||||
test-prof (0.11.3)
|
||||
test-unit (3.4.0)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
@@ -577,38 +555,34 @@ GEM
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (2.0.0)
|
||||
unicorn (6.0.0)
|
||||
unicorn (5.8.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
rack
|
||||
unicorn
|
||||
unicorn-worker-killer (0.4.5)
|
||||
unicorn-worker-killer (0.4.4)
|
||||
get_process_mem (~> 0)
|
||||
unicorn (>= 4, < 7)
|
||||
unicorn (>= 4, < 6)
|
||||
uniform_notifier (1.14.1)
|
||||
view_component (2.31.1)
|
||||
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.13.0)
|
||||
webmock (3.12.2)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
websocket-driver (0.7.3)
|
||||
websocket-driver (0.6.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
whenever (1.0.0)
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.1.0)
|
||||
activesupport
|
||||
wkhtmltopdf-binary (0.12.6.5)
|
||||
wkhtmltopdf-binary (0.12.5)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -624,12 +598,13 @@ DEPENDENCIES
|
||||
activerecord-postgresql-adapter
|
||||
activerecord-session_store
|
||||
acts-as-taggable-on (~> 7.0)
|
||||
acts_as_list (= 1.0.4)
|
||||
acts_as_list (= 0.9.19)
|
||||
andand
|
||||
angular-rails-templates (>= 0.3.0)
|
||||
angular_rails_csrf
|
||||
angularjs-file-upload-rails (~> 2.4.1)
|
||||
angularjs-rails (= 1.5.5)
|
||||
atomic
|
||||
awesome_nested_set
|
||||
awesome_print
|
||||
aws-sdk (= 1.67.0)
|
||||
@@ -639,8 +614,7 @@ DEPENDENCIES
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
catalog!
|
||||
codecov
|
||||
coffee-rails (~> 5.0.0)
|
||||
coffee-rails (~> 4.2.2)
|
||||
combine_pdf
|
||||
compass-rails
|
||||
custom_error_message!
|
||||
@@ -657,12 +631,9 @@ DEPENDENCIES
|
||||
devise-token_authenticatable
|
||||
dfc_provider!
|
||||
eventmachine (>= 1.2.3)
|
||||
factory_bot_rails (= 6.2.0)
|
||||
factory_bot_rails (= 5.2.0)
|
||||
ffaker
|
||||
figaro
|
||||
flipper
|
||||
flipper-active_record
|
||||
flipper-ui
|
||||
fog-aws (>= 0.6.0)
|
||||
foundation-icons-sass-rails
|
||||
foundation-rails (= 5.5.2.1)
|
||||
@@ -672,9 +643,8 @@ DEPENDENCIES
|
||||
good_migrations
|
||||
haml
|
||||
highline (= 2.0.3)
|
||||
hiredis
|
||||
i18n
|
||||
i18n-js (~> 3.8.3)
|
||||
i18n-js (~> 3.8.2)
|
||||
immigrant
|
||||
jquery-migrate-rails
|
||||
jquery-rails (= 4.4.0)
|
||||
@@ -685,8 +655,7 @@ DEPENDENCIES
|
||||
kaminari (~> 1.2.1)
|
||||
knapsack
|
||||
letter_opener (>= 1.4.1)
|
||||
mimemagic (> 0.3.5)
|
||||
mini_racer (= 0.4.0)
|
||||
mini_racer (= 0.3.1)
|
||||
monetize (~> 1.11)
|
||||
oauth2 (~> 1.4.7)
|
||||
ofn-qz!
|
||||
@@ -695,21 +664,20 @@ DEPENDENCIES
|
||||
paperclip (~> 3.4.1)
|
||||
paranoia (~> 2.4)
|
||||
paypal-sdk-merchant (= 1.117.2)
|
||||
pg (~> 1.2.3)
|
||||
pg (~> 0.21.0)
|
||||
pry
|
||||
pry-byebug
|
||||
rack-mini-profiler (< 3.0.0)
|
||||
rack-rewrite
|
||||
rack-ssl
|
||||
rails (~> 5.2)
|
||||
rails (~> 5.0.0)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rails_safe_tasks (~> 1.0)
|
||||
ransack (= 2.4.1)
|
||||
ransack (= 2.3.0)
|
||||
redcarpet
|
||||
redis (>= 4.0)
|
||||
responders
|
||||
roadie-rails (~> 2.2.0)
|
||||
roadie-rails (~> 1.3.0)
|
||||
roo (~> 2.8.3)
|
||||
rspec-rails (>= 3.5.2)
|
||||
rspec-retry
|
||||
@@ -733,17 +701,15 @@ DEPENDENCIES
|
||||
uglifier (>= 1.0.3)
|
||||
unicorn-rails
|
||||
unicorn-worker-killer
|
||||
view_component
|
||||
view_component_storybook
|
||||
web!
|
||||
webdrivers
|
||||
webmock
|
||||
whenever
|
||||
wicked_pdf
|
||||
wkhtmltopdf-binary
|
||||
wkhtmltopdf-binary (= 0.12.5)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.5.8p224
|
||||
ruby 2.4.4p296
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[](https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/build.yml)
|
||||
[](https://codecov.io/gh/openfoodfoundation/openfoodnetwork)
|
||||
[](https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2)
|
||||
[](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork)
|
||||
[](https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/build.yml)
|
||||
|
||||
# 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. 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).
|
||||
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.
|
||||
|
||||
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!
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm("Are you sure?")
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/v0/products/" + product.id
|
||||
url: "/api/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/v0/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
url: "/api/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/v0/enterprise_fees/' + scope.enterprise_fee.id
|
||||
url = '/api/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/v0/orders/:order_number/line_items/:line_item_id.json',
|
||||
LineItem = $resource '/api/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/v0/exchanges/:exchange_id/products.json', {}, {
|
||||
ExchangeProductResource = $resource('/api/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.replace(/\.0$/, '');
|
||||
$scope.variant_unit_with_scale = $scope.product.variant_unit + '_' + $scope.product.variant_unit_scale
|
||||
|
||||
$scope.setFields = ->
|
||||
if $scope.variant_unit_with_scale == 'items'
|
||||
|
||||
@@ -8,7 +8,7 @@ angular.module("ofn.admin").factory "ProductImageService", (FileUploader, SpreeA
|
||||
autoUpload: true
|
||||
|
||||
configure: (product) =>
|
||||
@imageUploader.url = "/api/v0/product_images/#{product.id}"
|
||||
@imageUploader.url = "/api/product_images/#{product.id}"
|
||||
@imagePreview = product.image_url
|
||||
@imageUploader.onSuccessItem = (image, response) =>
|
||||
product.thumb_url = response.thumb_url
|
||||
|
||||
@@ -9,12 +9,12 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
|
||||
'update':
|
||||
method: 'PUT'
|
||||
'removeLogo':
|
||||
url: '/api/v0/enterprises/:id/logo.json'
|
||||
url: '/api/enterprises/:id/logo.json'
|
||||
method: 'DELETE'
|
||||
'removePromoImage':
|
||||
url: '/api/v0/enterprises/:id/promo_image.json'
|
||||
url: '/api/enterprises/:id/promo_image.json'
|
||||
method: 'DELETE'
|
||||
'removeTermsAndConditions':
|
||||
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
|
||||
url: '/api/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/v0/orders.json'
|
||||
url: '/api/orders.json'
|
||||
method: 'GET'
|
||||
'update':
|
||||
method: 'PUT'
|
||||
'capture':
|
||||
url: '/api/v0/orders/:id/capture.json'
|
||||
url: '/api/orders/:id/capture.json'
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
'ship':
|
||||
url: '/api/v0/orders/:id/ship.json'
|
||||
url: '/api/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/v0/products/bulk_products.json'
|
||||
url: '/api/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/v0/products/" + product.id + "/clone").success (data) =>
|
||||
dataFetcher("/api/v0/products/" + data.id + "?template=bulk_show").then (newProduct) =>
|
||||
$http.post("/api/products/" + product.id + "/clone").success (data) =>
|
||||
dataFetcher("/api/products/" + data.id + "?template=bulk_show").then (newProduct) =>
|
||||
@unpackProduct newProduct
|
||||
@insertProductAfter(product, newProduct)
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ 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,6 +8,7 @@ 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", ->
|
||||
angular.module("admin.utils").factory "StatusMessage", ($timeout) ->
|
||||
new class StatusMessage
|
||||
types:
|
||||
progress: {style: {color: '#ff9906'}}
|
||||
alert: {style: {color: 'grey'}}
|
||||
notice: {style: {color: 'grey'}}
|
||||
success: {style: {color: '#9fc820'}}
|
||||
failure: {style: {color: '#da5354'}}
|
||||
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'}}
|
||||
|
||||
statusMessage:
|
||||
text: ""
|
||||
@@ -25,7 +25,13 @@ angular.module("admin.utils").factory "StatusMessage", ->
|
||||
display: (type, text) ->
|
||||
@statusMessage.text = text
|
||||
@statusMessage.style = @types[type].style
|
||||
null
|
||||
$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
|
||||
|
||||
clear: ->
|
||||
@statusMessage.text = ''
|
||||
|
||||
@@ -42,7 +42,7 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
url = "/api/v0/products/overridable?page=::page::;per_page=100"
|
||||
url = "/api/products/overridable?page=::page::;per_page=100"
|
||||
PagedFetcher.fetch url, $scope.addProducts
|
||||
|
||||
$scope.addProducts = (data) ->
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#= require leaflet-1.6.0.js
|
||||
#= require leaflet-providers.js
|
||||
#= require lodash.underscore.js
|
||||
# bluebird.js and angular-simple-logger are dependencies of angular-google-maps.js 2.0.0
|
||||
# bluebird.js is a dependency of angular-google-maps.js 2.0.0
|
||||
#= require bluebird.js
|
||||
#= require angular-simple-logger.min.js
|
||||
#= require angular-scroll.min.js
|
||||
#= require angular-google-maps.min.js
|
||||
#= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js
|
||||
|
||||
@@ -3,7 +3,7 @@ Darkswarm.controller "EditBoughtOrderController", ($scope, $resource, $timeout,
|
||||
$scope.removeEnabled = true
|
||||
|
||||
$scope.deleteLineItem = (id) ->
|
||||
if Cart.isOnlyItemInOrder(id)
|
||||
if Cart.has_one_line_item()
|
||||
Messages.error(t 'orders_cannot_remove_the_final_item')
|
||||
$scope.removeEnabled = false
|
||||
$timeout (->
|
||||
|
||||
@@ -24,7 +24,7 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http,
|
||||
$scope.shopfront_loading = true
|
||||
$scope.toggle_tab(event)
|
||||
|
||||
$http.get("/api/v0/shops/" + $scope.hub.id)
|
||||
$http.get("/api/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/v0/shops/" + $scope.producer.id)
|
||||
$http.get("/api/shops/" + $scope.producer.id)
|
||||
.success (data) ->
|
||||
$scope.shopfront_loading = false
|
||||
$scope.producer = data
|
||||
|
||||
@@ -11,8 +11,6 @@ Darkswarm.controller "ProductsCtrl", ($scope, $sce, $filter, $rootScope, Product
|
||||
$scope.supplied_taxons = null
|
||||
$scope.supplied_properties = null
|
||||
$scope.showFilterSidebar = false
|
||||
$scope.activeTaxons = []
|
||||
$scope.activeProperties = []
|
||||
|
||||
# Update filters after initial load of shop tab
|
||||
$timeout =>
|
||||
@@ -22,7 +20,6 @@ Darkswarm.controller "ProductsCtrl", ($scope, $sce, $filter, $rootScope, Product
|
||||
$rootScope.$on "orderCycleSelected", ->
|
||||
$scope.update_filters()
|
||||
$scope.clearAll()
|
||||
$scope.page = 1
|
||||
|
||||
$scope.update_filters = ->
|
||||
order_cycle_id = OrderCycle.order_cycle.order_cycle_id
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, EnterpriseRegistrationService, availableCountries, GmapsGeo) ->
|
||||
Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, EnterpriseRegistrationService, availableCountries) ->
|
||||
$scope.currentStep = RegistrationService.currentStep
|
||||
$scope.enterprise = EnterpriseRegistrationService.enterprise
|
||||
$scope.select = RegistrationService.select
|
||||
$scope.geocodedAddress = ''
|
||||
$scope.latLong = null
|
||||
$scope.addressConfirmed = false
|
||||
|
||||
$scope.steps = ['details', 'contact', 'type', 'about', 'images', 'social']
|
||||
|
||||
# Filter countries without states since the form requires a state to be selected.
|
||||
@@ -24,26 +22,3 @@ Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, Enterpris
|
||||
|
||||
$scope.countryHasStates = ->
|
||||
$scope.enterprise.country.states.length > 0
|
||||
|
||||
$scope.map = {center: {latitude: 0.000000, longitude: 0.000000 }, zoom: 1}
|
||||
$scope.options = {scrollwheel: false}
|
||||
$scope.locateAddress = () ->
|
||||
{ address1, address2, city, state_id, zipcode } = $scope.enterprise.address
|
||||
addressQuery = [address1, address2, city, state_id, zipcode].filter((value) => !!value).join(", ")
|
||||
GmapsGeo.geocode addressQuery, (results, status) =>
|
||||
$scope.geocodedAddress = results && results[0]?.formatted_address
|
||||
location = results[0]?.geometry?.location
|
||||
if location
|
||||
$scope.$apply(() =>
|
||||
$scope.latLong = {latitude: location.lat(), longitude: location.lng()}
|
||||
$scope.map = {center: {latitude: location.lat(), longitude: location.lng()}, zoom: 16 }
|
||||
)
|
||||
|
||||
$scope.toggleAddressConfirmed = ->
|
||||
$scope.addressConfirmed = !$scope.addressConfirmed
|
||||
if $scope.addressConfirmed
|
||||
$scope.enterprise.address.latitude = $scope.latLong.latitude
|
||||
$scope.enterprise.address.longitude = $scope.latLong.longitude
|
||||
else
|
||||
$scope.enterprise.address.latitude = null
|
||||
$scope.enterprise.address.longitude = null
|
||||
|
||||
@@ -3,15 +3,7 @@ Darkswarm.directive "bodyScroll", ($rootScope, BodyScroll) ->
|
||||
scope: true
|
||||
link: (scope, elem, attrs) ->
|
||||
$rootScope.$on "toggleBodyScroll", ->
|
||||
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%'
|
||||
if BodyScroll.disabled
|
||||
elem.addClass "disable-scroll"
|
||||
else
|
||||
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
|
||||
elem.removeClass "disable-scroll"
|
||||
|
||||
@@ -4,5 +4,4 @@ Darkswarm.directive "shopVariantWithUnitPrice", ->
|
||||
templateUrl: 'shop_variant_with_unit_price.html'
|
||||
scope:
|
||||
variant: '='
|
||||
show_unit_price: '=showunitprice'
|
||||
controller: 'ShopVariantCtrl'
|
||||
|
||||
@@ -115,9 +115,8 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo
|
||||
@line_items = []
|
||||
localStorageService.clearAll() # One day this will have to be moar GRANULAR
|
||||
|
||||
isOnlyItemInOrder: (id) =>
|
||||
deletedItem = @line_items_finalised.find((item) -> item.id == id)
|
||||
@line_items_finalised.filter((item) -> item.order_id == deletedItem.order_id).length == 1
|
||||
has_one_line_item: =>
|
||||
@line_items_finalised.length == 1
|
||||
|
||||
removeFinalisedLineItem: (id) =>
|
||||
@line_items_finalised = @line_items_finalised.filter (item) ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("Darkswarm").factory 'Customer', ($resource, $injector, Messages) ->
|
||||
Customer = $resource('/api/v0/customers/:id/:action.json', {}, {
|
||||
Customer = $resource('/api/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/v0/enterprises/#{enterprise.id}/update_image"
|
||||
@imageUploader.url = "/api/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/v0/shops/" + enterprise.id).success (data) ->
|
||||
$http.get("/api/shops/" + enterprise.id).success (data) ->
|
||||
scope.enterprise = data
|
||||
$modal.open(templateUrl: "enterprise_modal.html", scope: scope)
|
||||
.error (data) ->
|
||||
|
||||
@@ -18,10 +18,9 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
Loading.message = t('creating') + " " + @enterprise.name
|
||||
$http(
|
||||
method: "POST"
|
||||
url: "/api/v0/enterprises"
|
||||
url: "/api/enterprises"
|
||||
data:
|
||||
enterprise: @prepare()
|
||||
use_geocoder: @useGeocoder()
|
||||
params:
|
||||
token: spreeApiKey
|
||||
).success((data) =>
|
||||
@@ -43,10 +42,9 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
Loading.message = t('updating') + " " + @enterprise.name
|
||||
$http(
|
||||
method: "PUT"
|
||||
url: "/api/v0/enterprises/#{@enterprise.id}"
|
||||
url: "/api/enterprises/#{@enterprise.id}"
|
||||
data:
|
||||
enterprise: @prepare()
|
||||
use_geocoder: @useGeocoder()
|
||||
params:
|
||||
token: spreeApiKey
|
||||
).success((data) ->
|
||||
@@ -65,7 +63,3 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
|
||||
enterprise.address_attributes = @enterprise.address if @enterprise.address?
|
||||
enterprise.address_attributes.country_id = @enterprise.country.id if @enterprise.country?
|
||||
enterprise
|
||||
|
||||
useGeocoder: =>
|
||||
if @enterprise.address? && !@enterprise.address.latitude? && !@enterprise.address.longitude?
|
||||
return "1"
|
||||
|
||||
@@ -10,7 +10,7 @@ Darkswarm.service "GmapsGeo", ->
|
||||
# console.log "Error: #{status}"
|
||||
geocode: (address, callback) ->
|
||||
geocoder = new google.maps.Geocoder()
|
||||
geocoder.geocode {'address': address, 'region': "<%= DefaultCountry.code %>"}, callback
|
||||
geocoder.geocode {'address': address, 'region': "<%= Spree::Country.find_by(id: Spree::Config[:default_country_id]).iso %>"}, callback
|
||||
|
||||
distanceBetween: (src, dst) ->
|
||||
google.maps.geometry.spherical.computeDistanceBetween @toLatLng(src), @toLatLng(dst)
|
||||
@@ -20,4 +20,4 @@ Darkswarm.service "GmapsGeo", ->
|
||||
if locatable.lat?
|
||||
locatable
|
||||
else
|
||||
new google.maps.LatLng locatable.latitude, locatable.longitude
|
||||
new google.maps.LatLng locatable.latitude, locatable.longitude
|
||||
@@ -1,21 +1,21 @@
|
||||
Darkswarm.factory 'OrderCycleResource', ($resource) ->
|
||||
$resource('/api/v0/order_cycles/:id.json', {}, {
|
||||
$resource('/api/order_cycles/:id.json', {}, {
|
||||
'products':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/v0/order_cycles/:id/products.json'
|
||||
url: '/api/order_cycles/:id/products.json'
|
||||
params:
|
||||
id: '@id'
|
||||
'taxons':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/v0/order_cycles/:id/taxons.json'
|
||||
url: '/api/order_cycles/:id/taxons.json'
|
||||
params:
|
||||
id: '@id'
|
||||
'properties':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/v0/order_cycles/:id/properties.json'
|
||||
url: '/api/order_cycles/:id/properties.json'
|
||||
params:
|
||||
id: '@id'
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Darkswarm.factory 'ShopsResource', ($resource) ->
|
||||
$resource('/api/v0/shops/:id.json', {}, {
|
||||
$resource('/api/shops/:id.json', {}, {
|
||||
'closed_shops':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/v0/shops/closed_shops.json'
|
||||
url: '/api/shops/closed_shops.json'
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ OFNShared.directive "questionMarkWithTooltip", ($tooltip)->
|
||||
# Subsequently we patch the scope, template and restrictions
|
||||
tooltip = $tooltip 'questionMarkWithTooltip', 'questionMarkWithTooltip', 'click'
|
||||
tooltip.scope =
|
||||
context: "="
|
||||
variant: "="
|
||||
key: "="
|
||||
tooltip.templateUrl = "shared/question_mark_with_tooltip_icon.html"
|
||||
tooltip.replace = true
|
||||
|
||||
@@ -2,21 +2,12 @@
|
||||
.columns.small-12
|
||||
%h3{"ng-bind" => "::variant.extended_name"}
|
||||
|
||||
.flex.variant-bulk-buy-price-summary{style: "justify-content: space-around;"}
|
||||
.variant-unit
|
||||
{{ ::variant.unit_to_display }}
|
||||
.div
|
||||
.row.variant-bulk-buy-price-summary
|
||||
.columns.small-6
|
||||
.variant-unit {{ ::variant.unit_to_display }}
|
||||
.columns.small-6
|
||||
{{ 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
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"question-mark-with-tooltip-placement" => "top",
|
||||
"question-mark-with-tooltip-animation" => true,
|
||||
key: "'js.shopfront.unit_price_tooltip'"}
|
||||
{{ variant.unit_price_price | localizeCurrency }} / {{ variant.unit_price_unit }}
|
||||
{{ 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}"}
|
||||
|
||||
@@ -117,13 +117,6 @@ 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;
|
||||
|
||||
@@ -181,10 +181,11 @@ shop ordercycle {
|
||||
}
|
||||
}
|
||||
|
||||
shop .tab-buttons ordercycle {
|
||||
shop navigation ordercycle {
|
||||
margin-top: 3.4em;
|
||||
padding: 1em;
|
||||
height: 7.6em;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 1em;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@import "mixins";
|
||||
@import 'typography';
|
||||
|
||||
$shop-navigation-zindex: 20;
|
||||
|
||||
section {
|
||||
:not(shop) navigation {
|
||||
box-shadow: $distributor-header-shadow;
|
||||
@@ -13,7 +11,7 @@ section {
|
||||
display: block;
|
||||
background: $white;
|
||||
position: relative;
|
||||
z-index: $shop-navigation-zindex;
|
||||
z-index: 20;
|
||||
|
||||
.details {
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -167,26 +167,6 @@
|
||||
height: 166px;
|
||||
}
|
||||
}
|
||||
|
||||
.map-container--registration {
|
||||
width: 100%;
|
||||
height: 244px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
map, .angular-google-map-container, google-map, .angular-google-map {
|
||||
display: block;
|
||||
height: 220px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#registration-type {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
color: $dark-grey;
|
||||
box-shadow: $distributor-header-shadow;
|
||||
position: relative;
|
||||
z-index: $shop-navigation-zindex + 1;
|
||||
z-index: 10;
|
||||
|
||||
.columns {
|
||||
display: flex;
|
||||
|
||||
@@ -165,3 +165,7 @@ a.button.large {
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
@include icon-font;
|
||||
content: "";
|
||||
color: $white;
|
||||
vertical-align: super;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +45,6 @@
|
||||
width: 15px;
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.joyride-tip-guide.question-mark-tooltip {
|
||||
@@ -56,7 +54,6 @@
|
||||
margin-left: -7.4rem;
|
||||
margin-top: -0.1rem;
|
||||
background-color: transparent;
|
||||
z-index: $modal-zIndex + 1;
|
||||
|
||||
.background {
|
||||
position: fixed;
|
||||
|
||||
@@ -4,9 +4,6 @@ $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');
|
||||
@@ -25,4 +22,4 @@ $modal-zIndex: 1005;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DistributorTitleComponent < ViewComponent::Base
|
||||
def initialize(name:)
|
||||
@name = name
|
||||
end
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
%h3= @name
|
||||
@@ -1,7 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExampleComponent < ViewComponent::Base
|
||||
def initialize(title:)
|
||||
@title = title
|
||||
end
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
%h1 #{@title}
|
||||
@@ -35,7 +35,7 @@ module Admin
|
||||
if @line_item.update(line_item_params)
|
||||
order.update_line_item_fees! @line_item
|
||||
order.update_order_fees!
|
||||
order.update_order!
|
||||
order.update!
|
||||
render body: nil, status: :no_content # No Content, does not trigger ng resource auto-update
|
||||
else
|
||||
render json: { errors: @line_item.errors }, status: :precondition_failed
|
||||
|
||||
@@ -18,13 +18,21 @@ module Admin
|
||||
format.html
|
||||
format.json do
|
||||
render json: @collection,
|
||||
each_serializer: ::Api::Admin::CustomerWithBalanceSerializer,
|
||||
each_serializer: index_each_serializer,
|
||||
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
|
||||
@@ -45,12 +53,15 @@ 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 }
|
||||
@@ -62,7 +73,7 @@ module Admin
|
||||
|
||||
def collection
|
||||
if json_request? && params[:enterprise_id].present?
|
||||
CustomersWithBalance.new(managed_enterprise_id).query.
|
||||
customers_relation.
|
||||
includes(
|
||||
:enterprise,
|
||||
{ bill_address: [:state, :country] },
|
||||
@@ -74,6 +85,14 @@ module Admin
|
||||
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])
|
||||
|
||||
@@ -28,7 +28,9 @@ module Admin
|
||||
def build_resource
|
||||
enterprise_group = super
|
||||
enterprise_group.address = Spree::Address.new
|
||||
enterprise_group.address.country = DefaultCountry.country
|
||||
enterprise_group.address.country = Spree::Country.find_by(
|
||||
id: Spree::Config[:default_country_id]
|
||||
)
|
||||
enterprise_group
|
||||
end
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ require 'open_food_network/order_cycle_permissions'
|
||||
|
||||
module Admin
|
||||
class EnterprisesController < Admin::ResourceController
|
||||
include GeocodeEnterpriseAddress
|
||||
|
||||
# These need to run before #load_resource so that @object is initialised with sanitised values
|
||||
prepend_before_action :override_owner, only: :create
|
||||
prepend_before_action :override_sells, only: :create
|
||||
@@ -24,8 +22,6 @@ module Admin
|
||||
before_action :load_properties, only: [:edit, :update]
|
||||
before_action :setup_property, only: [:edit]
|
||||
|
||||
after_action :geocode_address_if_use_geocoder, only: [:create, :update]
|
||||
|
||||
helper 'spree/products'
|
||||
include OrderCyclesHelper
|
||||
|
||||
@@ -47,11 +43,12 @@ 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 }
|
||||
@@ -59,6 +56,7 @@ 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
|
||||
@@ -119,7 +117,7 @@ module Admin
|
||||
def build_resource
|
||||
enterprise = super
|
||||
enterprise.address ||= Spree::Address.new
|
||||
enterprise.address.country ||= DefaultCountry.country
|
||||
enterprise.address.country ||= Spree::Country.find_by(id: Spree::Config[:default_country_id])
|
||||
enterprise
|
||||
end
|
||||
|
||||
@@ -216,6 +214,7 @@ 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,3 +1,5 @@
|
||||
require 'action_callbacks'
|
||||
|
||||
module Admin
|
||||
class ResourceController < Spree::Admin::BaseController
|
||||
helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
|
||||
@@ -9,6 +11,7 @@ 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 }
|
||||
@@ -23,26 +26,32 @@ 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
|
||||
@@ -58,13 +67,16 @@ 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
|
||||
@@ -80,6 +92,7 @@ module Admin
|
||||
|
||||
class << self
|
||||
attr_accessor :parent_data
|
||||
attr_accessor :callbacks
|
||||
|
||||
def belongs_to(model_name, options = {})
|
||||
@parent_data ||= {}
|
||||
@@ -87,6 +100,26 @@ 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
|
||||
@@ -179,6 +212,17 @@ 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,8 +9,7 @@ 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]
|
||||
|
||||
after_action :sync_subscriptions_for_update, only: :update
|
||||
update.after :sync_subscriptions_for_update
|
||||
|
||||
respond_to :json
|
||||
|
||||
@@ -121,7 +120,7 @@ module Admin
|
||||
end
|
||||
|
||||
def sync_subscriptions_for_update
|
||||
return unless params[:schedule][:order_cycle_ids] && @object.errors.blank?
|
||||
return unless params[:schedule][:order_cycle_ids]
|
||||
|
||||
sync_subscriptions
|
||||
end
|
||||
|
||||
103
app/controllers/api/base_controller.rb
Normal file
103
app/controllers/api/base_controller.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
# 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
|
||||
37
app/controllers/api/customers_controller.rb
Normal file
37
app/controllers/api/customers_controller.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
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
|
||||
42
app/controllers/api/enterprise_attachment_controller.rb
Normal file
42
app/controllers/api/enterprise_attachment_controller.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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
|
||||
21
app/controllers/api/enterprise_fees_controller.rb
Normal file
21
app/controllers/api/enterprise_fees_controller.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
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
|
||||
78
app/controllers/api/enterprises_controller.rb
Normal file
78
app/controllers/api/enterprises_controller.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
module Api
|
||||
class EnterprisesController < Api::BaseController
|
||||
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
|
||||
@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)
|
||||
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
|
||||
100
app/controllers/api/exchange_products_controller.rb
Normal file
100
app/controllers/api/exchange_products_controller.rb
Normal file
@@ -0,0 +1,100 @@
|
||||
# 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
|
||||
16
app/controllers/api/logos_controller.rb
Normal file
16
app/controllers/api/logos_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
101
app/controllers/api/order_cycles_controller.rb
Normal file
101
app/controllers/api/order_cycles_controller.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
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
|
||||
67
app/controllers/api/orders_controller.rb
Normal file
67
app/controllers/api/orders_controller.rb
Normal file
@@ -0,0 +1,67 @@
|
||||
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
|
||||
19
app/controllers/api/product_images_controller.rb
Normal file
19
app/controllers/api/product_images_controller.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
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
|
||||
159
app/controllers/api/products_controller.rb
Normal file
159
app/controllers/api/products_controller.rb
Normal file
@@ -0,0 +1,159 @@
|
||||
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
|
||||
16
app/controllers/api/promo_images_controller.rb
Normal file
16
app/controllers/api/promo_images_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
112
app/controllers/api/shipments_controller.rb
Normal file
112
app/controllers/api/shipments_controller.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
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
|
||||
27
app/controllers/api/shops_controller.rb
Normal file
27
app/controllers/api/shops_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# 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
|
||||
44
app/controllers/api/states_controller.rb
Normal file
44
app/controllers/api/states_controller.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
# 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
|
||||
16
app/controllers/api/statuses_controller.rb
Normal file
16
app/controllers/api/statuses_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
12
app/controllers/api/taxonomies_controller.rb
Normal file
12
app/controllers/api/taxonomies_controller.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
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
|
||||
76
app/controllers/api/taxons_controller.rb
Normal file
76
app/controllers/api/taxons_controller.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
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
|
||||
18
app/controllers/api/terms_and_conditions_controller.rb
Normal file
18
app/controllers/api/terms_and_conditions_controller.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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
|
||||
@@ -1,105 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Base controller for OFN's API
|
||||
require "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
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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
|
||||
@@ -1,49 +0,0 @@
|
||||
# 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
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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
|
||||
@@ -1,86 +0,0 @@
|
||||
# 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
|
||||
@@ -1,102 +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
|
||||
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
|
||||
@@ -1,20 +0,0 @@
|
||||
# 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
|
||||
@@ -1,105 +0,0 @@
|
||||
# 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
|
||||
@@ -1,72 +0,0 @@
|
||||
# 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
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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
|
||||
@@ -1,163 +0,0 @@
|
||||
# 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
|
||||
@@ -1,20 +0,0 @@
|
||||
# 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