Compare commits

..

2 Commits

Author SHA1 Message Date
Luis Ramos
63bcbb8fa5 Merge pull request #4008 from luisramos0/fuubar241
Upgrade fuubar to 241 to skip yanked 240
2019-07-04 15:33:50 +01:00
luisramos0
0784d39f8e Upgrade fuubar to 241 to skip yanked 240 2019-07-04 15:19:51 +01:00
390 changed files with 3855 additions and 10406 deletions

View File

@@ -1,4 +0,0 @@
.git
.gitignore
log/*
tmp/*

View File

@@ -68,6 +68,7 @@ Metrics/LineLength:
- app/helpers/shop_helper.rb
- app/helpers/spree/admin/base_helper_decorator.rb
- app/helpers/spree/admin/navigation_helper_decorator.rb
- app/helpers/spree/admin/orders_helper_decorator.rb
- app/helpers/spree/orders_helper.rb
- app/jobs/subscription_confirm_job.rb
- app/mailers/subscription_mailer.rb
@@ -80,7 +81,9 @@ Metrics/LineLength:
- app/models/enterprise_fee.rb
- app/models/enterprise_group.rb
- app/models/enterprise_role.rb
- app/models/exchange.rb
- app/models/inventory_item.rb
- app/models/order_cycle.rb
- app/models/product_import/entry_processor.rb
- app/models/product_import/entry_validator.rb
- app/models/product_import/product_importer.rb
@@ -151,10 +154,12 @@ Metrics/LineLength:
- lib/open_food_network/order_and_distributor_report.rb
- lib/open_food_network/order_cycle_form_applicator.rb
- lib/open_food_network/order_cycle_management_report.rb
- lib/open_food_network/order_cycle_permissions.rb
- lib/open_food_network/order_grouper.rb
- lib/open_food_network/orders_and_fulfillments_report.rb
- lib/open_food_network/payments_report.rb
- lib/open_food_network/permalink_generator.rb
- lib/open_food_network/permissions.rb
- lib/open_food_network/products_cache.rb
- lib/open_food_network/products_renderer.rb
- lib/open_food_network/proxy_order_syncer.rb
@@ -424,6 +429,7 @@ Metrics/AbcSize:
- app/helpers/checkout_helper.rb
- app/helpers/i18n_helper.rb
- app/helpers/order_cycles_helper.rb
- app/helpers/spree/admin/orders_helper_decorator.rb
- app/helpers/spree/orders_helper.rb
- app/jobs/subscription_placement_job.rb
- app/mailers/producer_mailer.rb
@@ -466,7 +472,6 @@ Metrics/AbcSize:
- lib/open_food_network/orders_and_fulfillments_report.rb
- lib/open_food_network/packing_report.rb
- lib/open_food_network/payments_report.rb
- lib/open_food_network/permissions.rb
- lib/open_food_network/products_and_inventory_report.rb
- lib/open_food_network/reports/line_items.rb
- lib/open_food_network/sales_tax_report.rb
@@ -488,72 +493,6 @@ Metrics/AbcSize:
- spec/models/product_importer_spec.rb
- spec/support/performance_helper.rb
Metrics/BlockLength:
Max: 25
ExcludedMethods: ["class_eval", "collection", "context", "describe", "it", "member", "namespace", "resource", "resources"]
Exclude:
- lib/tasks/data.rake
- lib/tasks/dev.rake
- spec/controllers/spree/admin/invoices_controller_spec.rb
- spec/factories/variant_factory.rb
- spec/features/admin/adjustments_spec.rb
- spec/features/admin/bulk_order_management_spec.rb
- spec/features/admin/bulk_product_update_spec.rb
- spec/features/admin/caching_spec.rb
- spec/features/admin/content_spec.rb
- spec/features/admin/customers_spec.rb
- spec/features/admin/enterprise_fees_spec.rb
- spec/features/admin/enterprise_groups_spec.rb
- spec/features/admin/enterprise_relationships_spec.rb
- spec/features/admin/enterprise_roles_spec.rb
- spec/features/admin/enterprises/images_spec.rb
- spec/features/admin/enterprises/index_spec.rb
- spec/features/admin/enterprises_spec.rb
- spec/features/admin/enterprise_user_spec.rb
- spec/features/admin/multilingual_spec.rb
- spec/features/admin/order_cycles_spec.rb
- spec/features/admin/orders_spec.rb
- spec/features/admin/overview_spec.rb
- spec/features/admin/payment_method_spec.rb
- spec/features/admin/product_import_spec.rb
- spec/features/admin/products_spec.rb
- spec/features/admin/reports/enterprise_fee_summaries_spec.rb
- spec/features/admin/reports_spec.rb
- spec/features/admin/schedules_spec.rb
- spec/features/admin/shipping_methods_spec.rb
- spec/features/admin/subscriptions_spec.rb
- spec/features/admin/tag_rules_spec.rb
- spec/features/admin/tax_settings_spec.rb
- spec/features/admin/users_spec.rb
- spec/features/admin/variant_overrides_spec.rb
- spec/features/admin/variants_spec.rb
- spec/features/consumer/account/cards_spec.rb
- spec/features/consumer/account/settings_spec.rb
- spec/features/consumer/account_spec.rb
- spec/features/consumer/authentication_spec.rb
- spec/features/consumer/cookies_spec.rb
- spec/features/consumer/external_services_spec.rb
- spec/features/consumer/groups_spec.rb
- spec/features/consumer/multilingual_spec.rb
- spec/features/consumer/producers_spec.rb
- spec/features/consumer/registration_spec.rb
- spec/features/consumer/shopping/cart_spec.rb
- spec/features/consumer/shopping/checkout_auth_spec.rb
- spec/features/consumer/shopping/checkout_spec.rb
- spec/features/consumer/shopping/embedded_groups_spec.rb
- spec/features/consumer/shopping/embedded_shopfronts_spec.rb
- spec/features/consumer/shopping/orders_spec.rb
- spec/features/consumer/shopping/products_spec.rb
- spec/features/consumer/shopping/shopping_spec.rb
- spec/features/consumer/shopping/variant_overrides_spec.rb
- spec/features/consumer/shops_spec.rb
- spec/lib/open_food_network/group_buy_report_spec.rb
- spec/models/tag_rule/discount_order_spec.rb
- spec/spec_helper.rb
- spec/support/delayed_job_helper.rb
- spec/support/matchers/select2_matchers.rb
- spec/support/matchers/table_matchers.rb
Metrics/CyclomaticComplexity:
Max: 6
Exclude:
@@ -565,6 +504,7 @@ Metrics/CyclomaticComplexity:
- app/helpers/checkout_helper.rb
- app/helpers/i18n_helper.rb
- app/helpers/order_cycles_helper.rb
- app/helpers/spree/admin/orders_helper_decorator.rb
- app/models/enterprise.rb
- app/models/enterprise_relationship.rb
- app/models/product_import/entry_processor.rb
@@ -594,6 +534,7 @@ Metrics/PerceivedComplexity:
- app/helpers/checkout_helper.rb
- app/helpers/i18n_helper.rb
- app/helpers/order_cycles_helper.rb
- app/helpers/spree/admin/orders_helper_decorator.rb
- app/models/enterprise_relationship.rb
- app/models/product_import/entry_processor.rb
- app/models/product_import/entry_validator.rb
@@ -639,6 +580,7 @@ Metrics/MethodLength:
- app/controllers/user_registrations_controller.rb
- app/helpers/checkout_helper.rb
- app/helpers/order_cycles_helper.rb
- app/helpers/spree/admin/orders_helper_decorator.rb
- app/jobs/subscription_placement_job.rb
- app/mailers/producer_mailer.rb
- app/models/column_preference.rb

View File

@@ -13,6 +13,8 @@ AllCops:
- 'script/**/*'
- 'vendor/**/*'
- 'node_modules/**/*'
# The parser gem fails to parse this file with out current Ruby version.
- 'spec/factories.rb'
# Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase
- 'engines/web/Gemfile'
@@ -185,8 +187,7 @@ Metrics/AbcSize:
Max: 15
Metrics/BlockLength:
Max: 25
ExcludedMethods: ["class_eval", "collection", "context", "describe", "it", "member", "namespace", "resource", "resources"]
ExcludedMethods: ["collection", "context", "describe", "it", "member", "namespace", "resource", "resources"]
Metrics/BlockNesting:
Max: 3

View File

@@ -61,6 +61,7 @@ Lint/IneffectiveAccessModifier:
- 'app/models/column_preference.rb'
- 'app/services/mail_configuration.rb'
- 'lib/open_food_network/feature_toggle.rb'
- 'lib/open_food_network/products_cache.rb'
- 'spec/lib/open_food_network/reports/report_spec.rb'
# Offense count: 1
@@ -86,6 +87,7 @@ Lint/UselessAccessModifier:
- 'app/models/column_preference.rb'
- 'app/services/mail_configuration.rb'
- 'lib/open_food_network/feature_toggle.rb'
- 'lib/open_food_network/products_cache.rb'
- 'lib/open_food_network/reports/bulk_coop_report.rb'
- 'spec/lib/open_food_network/reports/report_spec.rb'
@@ -120,6 +122,11 @@ Naming/AccessorMethodName:
- 'spec/support/request/shop_workflow.rb'
- 'spec/support/request/web_helper.rb'
# Offense count: 1
Naming/BinaryOperatorParameterName:
Exclude:
- 'app/models/exchange.rb'
# Offense count: 1
# Configuration parameters: Blacklist.
# Blacklist: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$))
@@ -168,6 +175,7 @@ Naming/UncommunicativeMethodParamName:
- 'app/helpers/admin/injection_helper.rb'
- 'app/helpers/spree/admin/base_helper_decorator.rb'
- 'app/helpers/spree/base_helper_decorator.rb'
- 'app/models/exchange.rb'
- 'app/services/subscription_validator.rb'
- 'lib/open_food_network/reports/bulk_coop_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
@@ -425,6 +433,7 @@ Style/GuardClause:
- 'app/services/order_syncer.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/products_cache.rb'
- 'lib/open_food_network/products_renderer.rb'
- 'lib/open_food_network/rack_request_blocker.rb'
- 'lib/open_food_network/variant_and_line_item_naming.rb'

60
.travis.yml Normal file
View File

@@ -0,0 +1,60 @@
language: ruby
sudo: false
cache: bundler
bundler_args: --without development
rvm:
- "2.1.5"
addons:
postgresql: "9.5"
# Set the timezone for phantomjs with TZ
# Set the timezone for karma with TIMEZONE
env:
global:
- TZ="Australia/Melbourne"
- TIMEZONE="Australia/Melbourne"
- CI_NODE_TOTAL=5
matrix:
- CI_NODE_INDEX=0
- CI_NODE_INDEX=1
- CI_NODE_INDEX=2
- CI_NODE_INDEX=3 RSPEC_ENGINES="true"
- CI_NODE_INDEX=4 KARMA="true" GITHUB_DEPLOY="true"
before_script:
- cp config/database.travis.yml config/database.yml
- cp config/application.yml.example config/application.yml
- RAILS_ENV=test bundle exec rake db:create db:schema:load
# Only install PhantomJS if it is not already present (ie. cached)
- npm list -g phantomjs-prebuilt@~2.1.7 --depth=0 || npm install -g phantomjs-prebuilt@~2.1.7
- export PATH=`npm bin -g`:$PATH
- >
if [ "$KARMA" = "true" ]; then
npm install -g npm@'3.8.8'
npm install
npm install -g karma-cli@0.1.2
fi
script:
- 'if [ "$KARMA" = "true" ]; then bundle exec rake karma:run; else echo "Skipping karma run"; fi'
- 'if [ "$RSPEC_ENGINES" = "true" ]; then bundle exec rake ofn:specs:engines:rspec; else echo "Skipping RSpec run in engines"; fi'
- "bundle exec rake 'knapsack:rspec[--format progress --tag ~performance]'"
after_success:
- >
if [ "$GITHUB_DEPLOY" = "true" -a "$TRAVIS_PULL_REQUEST" = "false" -a -n "$TRAVIS_BRANCH" -a "$TRAVIS_BRANCH" != "transifex" -a -n "$GITHUB_API_SECRET" ]; then
description="`git show "$TRAVIS_BRANCH" -s --oneline --no-color`"
data="{
\"ref\":\"$TRAVIS_BRANCH\",
\"description\":\"$description\",
\"environment\":\"staging\",
\"required_contexts\":[]}"
curl -u "$GITHUB_API_SECRET" -d "$data" "https://api.github.com/repos/$TRAVIS_REPO_SLUG/deployments"
else
echo "Not deploying on this build."
fi
notifications:
email: false

View File

@@ -1,47 +0,0 @@
### Docker
It is possible to setup the Open Food Network app easily with Docker and Docker Compose.
The objective is to spare configuration time, in order to help people testing the app and contribute to it.
It can also be used as documentation. It is not perfect but it is used in many other projects and many devs are used to it nowadays.
### Install Docker
Please check the documentation here, https://docs.docker.com/install/ to install Docker.
For Docker Compose, information are here: https://docs.docker.com/compose/install/.
Better to have at least 2GB free on your computer in order to download images and create containers for Open Food Network app.
### Use Docker with Open Food Network
Open a terminal with a shell.
Clone the repository:
```sh
$ git clone git@github.com:openfoodfoundation/openfoodnetwork.git
```
Go at the root of the app:
```sh
$ cd openfoodnetwork
```
Download the Docker images and build the containers:
```sh
$ docker-compose build
```
Run the app with all the required containers:
```sh
$ docker-compose up
```
This command will setup the database and seed it with sample data. The default admin user is 'ofn@example.com' with 'ofn123' password.
Check the app in the browser at `http:://localhost:3000`.
You will then get the trace of the containers in the terminal. You can stop the containers using Ctrl-C in the terminal.

View File

@@ -1,31 +0,0 @@
FROM ubuntu:18.04
# Install all the requirements
RUN apt-get update && apt-get install -y curl git build-essential software-properties-common wget zlib1g-dev libssl1.0-dev libreadline-dev libyaml-dev libffi-dev libxml2-dev libxslt1-dev wait-for-it
# Setup ENV variables
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
ENV RBENV_ROOT /usr/local/src/rbenv
ENV RUBY_VERSION 2.1.5
ENV CONFIGURE_OPTS --disable-install-doc
# Rbenv & Ruby part
RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
${RBENV_ROOT}/plugins/ruby-build/install.sh && \
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
rbenv install $RUBY_VERSION && \
rbenv global $RUBY_VERSION && \
gem install bundler --version=1.17.2
# Postgres
RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main' > /etc/apt/sources.list.d/pgdg.list" && \
wget --quiet -O - https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | apt-key add - && \
apt-get update && \
apt-get install -yqq --no-install-recommends postgresql-client-9.5 libpq-dev
ENV BUNDLE_PATH /bundles
COPY . /usr/src/app/
WORKDIR /usr/src/app

10
Gemfile
View File

@@ -3,12 +3,11 @@ ruby "2.1.5"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
gem 'i18n', '~> 0.6.11'
gem 'i18n-js', '~> 3.3.0'
gem 'i18n-js', '~> 3.2.2'
gem 'rails', '~> 3.2.22'
gem 'rails-i18n', '~> 3.0.0'
gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
# Patched version. See http://rubysec.com/advisories/CVE-2015-5312/.
gem 'nokogiri', '>= 1.6.7.1'
@@ -20,10 +19,9 @@ gem 'pg'
# OFN-maintained and patched version of Spree v2.0.4. See
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-2.0-upgrade
# for details.
gem 'spree_api', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_backend', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_core', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '2-0-stable'
gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable'
# Our branch contains two changes
@@ -36,8 +34,6 @@ gem 'stripe'
# which is needed for Pin Payments (and possibly others).
gem 'activemerchant', '~> 1.78'
gem 'devise', '~> 2.2.5'
gem 'devise-encryptable', '0.1.2'
gem 'jwt', '~> 2.2'
gem 'oauth2', '~> 1.4.1' # Used for Stripe Connect

View File

@@ -31,9 +31,16 @@ GIT
GIT
remote: https://github.com/openfoodfoundation/spree.git
revision: 8a8585a43cd04d1a50dc65227f337a91b18d66d5
revision: 46d6f8f5fd434105b0c69958276d1727a3066a97
branch: 2-0-4-stable
specs:
spree (2.0.4)
spree_api (= 2.0.4)
spree_backend (= 2.0.4)
spree_cmd (= 2.0.4)
spree_core (= 2.0.4)
spree_frontend (= 2.0.4)
spree_sample (= 2.0.4)
spree_api (2.0.4)
rabl (= 0.8.4)
spree_core (= 2.0.4)
@@ -46,6 +53,8 @@ GIT
select2-rails (~> 3.4.7)
spree_api (= 2.0.4)
spree_core (= 2.0.4)
spree_cmd (2.0.4)
thor (>= 0.14.6)
spree_core (2.0.4)
activemerchant (~> 1.34)
acts_as_list (= 0.2.0)
@@ -66,6 +75,28 @@ GIT
state_machine (= 1.2.0)
stringex (~> 1.5.1)
truncate_html (= 0.9.2)
spree_frontend (2.0.4)
canonical-rails
deface (>= 0.9.0)
jquery-rails (~> 3.0.0)
rails (~> 3.2.13)
spree_api (= 2.0.4)
spree_core (= 2.0.4)
stringex (~> 1.5.1)
spree_sample (2.0.4)
spree_core (= 2.0.4)
GIT
remote: https://github.com/spree/spree_auth_devise.git
revision: 0181835fb6ac77a05191d26f6f32a0f4a548d851
branch: 2-0-stable
specs:
spree_auth_devise (2.0.0)
devise (~> 2.2.5)
devise-encryptable (= 0.1.2)
spree_backend (~> 2.0.0)
spree_core (~> 2.0.0)
spree_frontend (~> 2.0.0)
GIT
remote: https://github.com/spree/spree_i18n.git
@@ -129,8 +160,6 @@ GEM
activesupport (= 3.2.22.5)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activerecord-import (1.0.2)
activerecord (>= 3.2)
activeresource (3.2.22.5)
activemodel (= 3.2.22.5)
activesupport (= 3.2.22.5)
@@ -160,7 +189,7 @@ GEM
json (~> 1.4)
nokogiri (>= 1.4.4)
uuidtools (~> 2.1)
bcrypt (3.1.13)
bcrypt (3.1.11)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
blockenspiel (0.5.0)
@@ -169,6 +198,8 @@ GEM
builder (3.0.4)
byebug (9.0.6)
cancan (1.6.10)
canonical-rails (0.1.0)
rails (>= 3.1, < 5.1)
capybara (2.18.0)
addressable
mini_mime (>= 0.1.3)
@@ -248,7 +279,7 @@ GEM
devise (>= 2.1.0)
diff-lcs (1.3)
diffy (3.3.0)
docile (1.3.2)
docile (1.3.1)
dry-inflector (0.1.2)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
@@ -461,7 +492,7 @@ GEM
httparty (0.16.2)
multi_xml (>= 0.5.2)
i18n (0.6.11)
i18n-js (3.3.0)
i18n-js (3.2.2)
i18n (>= 0.6.6)
immigrant (0.3.6)
activerecord (>= 3.0)
@@ -483,7 +514,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.11.2)
knapsack (1.17.2)
knapsack (1.17.1)
rake
launchy (2.4.3)
addressable (~> 2.3)
@@ -606,7 +637,7 @@ GEM
trollop (~> 2.1)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (3.5.0)
redcarpet (3.4.0)
ref (2.0.0)
request_store (1.4.1)
rack (>= 1.4)
@@ -673,7 +704,7 @@ GEM
shellany (0.0.1)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
simplecov (0.17.0)
simplecov (0.16.1)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
@@ -698,7 +729,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
state_machine (1.2.0)
stringex (1.5.1)
stripe (4.19.0)
stripe (4.18.1)
faraday (~> 0.13)
net-http-persistent (~> 3.0)
therubyracer (0.12.0)
@@ -754,7 +785,6 @@ PLATFORMS
DEPENDENCIES
active_model_serializers (= 0.8.4)
activemerchant (~> 1.78)
activerecord-import
acts-as-taggable-on (~> 3.4)
andand
angular-rails-templates (~> 0.3.0)
@@ -779,8 +809,6 @@ DEPENDENCIES
deface (= 1.0.2)
delayed_job_active_record
delayed_job_web
devise (~> 2.2.5)
devise-encryptable (= 0.1.2)
diffy
eventmachine (>= 1.2.3)
factory_bot_rails
@@ -798,7 +826,7 @@ DEPENDENCIES
guard-rspec (~> 4.7.3)
haml
i18n (~> 0.6.11)
i18n-js (~> 3.3.0)
i18n-js (~> 3.2.2)
immigrant
jquery-migrate-rails
jquery-rails (= 3.0.4)
@@ -840,9 +868,8 @@ DEPENDENCIES
simplecov
skylight (< 2.0)
spinjs-rails
spree_api!
spree_backend!
spree_core!
spree!
spree_auth_devise!
spree_i18n!
spree_paypal_express!
spring (= 1.7.2)

View File

@@ -27,13 +27,10 @@ If you're interested in provisioning a server, see [ofn-install][ofn-install] fo
We also have a [Super Admin Guide][super-admin-guide] to help with configuration of new servers.
## Testing
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!
## Licence
Copyright (c) 2012 - 2019 Open Food Foundation, released under the AGPL licence.
Copyright (c) 2012 - 2018 Open Food Foundation, released under the AGPL licence.
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
[slack-invite]: https://openfoodnetwork.org/slack-invite

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="18" viewBox="0 0 20 18">
<path fill="#FFF" fill-opacity=".5" fill-rule="nonzero" d="M18.338 10.593l1.64-7.312a.841.841 0 0 0-.812-1.031H5.528L5.21.675A.836.836 0 0 0 4.393 0H.833A.839.839 0 0 0 0 .844v.562c0 .466.373.844.833.844H3.26l2.439 12.074c-.584.34-.977.977-.977 1.707 0 1.088.87 1.969 1.945 1.969 1.074 0 1.944-.881 1.944-1.969 0-.55-.224-1.049-.584-1.406h7.28c-.36.357-.585.855-.585 1.406 0 1.088.87 1.969 1.945 1.969 1.074 0 1.944-.881 1.944-1.969 0-.78-.447-1.453-1.096-1.772l.191-.853a.841.841 0 0 0-.812-1.031h-9.32l-.228-1.125h10.179c.389 0 .726-.273.813-.657z"/>
</svg>

Before

Width:  |  Height:  |  Size: 648 B

View File

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h16v16H0z"/>
<path fill="#F4704C" d="M13.85 5.928h-1.234v-1.54c0-2.338-2.026-4.234-4.525-4.234-2.5 0-4.526 1.896-4.526 4.235v1.54H2.33c-.682 0-1.235.516-1.235 1.154v7.7c0 .637.553 1.154 1.235 1.154h11.52c.681 0 1.234-.517 1.234-1.154v-7.7c0-.638-.553-1.155-1.234-1.155zM4.8 4.388c0-1.7 1.473-3.08 3.29-3.08 1.818 0 3.292 1.38 3.292 3.08v1.54H4.799v-1.54z"/>
<path fill="#FFF" d="M8.296 13.23c1.243 0 2.25-.942 2.25-2.105 0-1.162-1.007-2.105-2.25-2.105-1.242 0-2.25.943-2.25 2.105.003 1.162 1.009 2.103 2.25 2.106zm0-3.211c.654 0 1.183.495 1.183 1.106 0 .612-.53 1.107-1.183 1.107s-1.183-.495-1.183-1.107c0-.61.53-1.106 1.183-1.106z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 812 B

View File

@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<defs>
<path id="a" d="M15 15.2a.79.79 0 0 1-.778.8H1.778A.79.79 0 0 1 1 15.2v-2.4c0-2.21 1.741-4 3.889-4h6.222c2.148 0 3.889 1.79 3.889 4v2.4zM8 8C5.852 8 4.111 6.21 4.111 4S5.852 0 8 0c2.148 0 3.889 1.79 3.889 4S10.148 8 8 8z"/>
</defs>
<use fill="#F4704C" fill-rule="nonzero" xlink:href="#a"/>
</svg>

Before

Width:  |  Height:  |  Size: 451 B

View File

@@ -15,6 +15,7 @@
//= require angular-animate
//= require angular-sanitize
//= require admin/spree_backend
//= require admin/spree_auth
//= require admin/spree_paypal_express
//= require ../shared/ng-infinite-scroll.min.js
//= require ../shared/ng-tags-input.min.js

View File

@@ -5,7 +5,7 @@ angular.module('admin.orderCycles')
$scope.distributor_enterprises = Enterprise.hub_enterprises
$scope.supplied_products = Enterprise.supplied_products
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
$scope.schedules = Schedules.index()
$scope.OrderCycle = OrderCycle
$scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id})

View File

@@ -1,12 +1,12 @@
angular.module('admin.orderCycles')
.controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
.controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor) ->
order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
$scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id)
$scope.supplier_enterprises = Enterprise.producer_enterprises
$scope.distributor_enterprises = Enterprise.hub_enterprises
$scope.supplied_products = Enterprise.supplied_products
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id)
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
$scope.schedules = Schedules.index()
$scope.OrderCycle = OrderCycle
$scope.order_cycle = OrderCycle.load(order_cycle_id)

View File

@@ -1,7 +1,7 @@
angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
$scope.StatusMessage = StatusMessage
$scope.OrderCycle = OrderCycle
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
$scope.schedules = Schedules.index()
$scope.order_cycle = OrderCycle.new {coordinator_id: ocInstance.coordinator_id}, =>
# TODO: make this a get method, which only fetches one enterprise
$scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) =>

View File

@@ -1,11 +1,11 @@
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, StatusMessage, ocInstance) ->
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, StatusMessage) ->
$scope.orderCycleId = ->
$location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
$scope.StatusMessage = StatusMessage
$scope.enterprises = Enterprise.index(order_cycle_id: $scope.orderCycleId())
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.orderCycleId())
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
$scope.schedules = Schedules.index()
$scope.OrderCycle = OrderCycle
$scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) =>
$scope.init()

View File

@@ -24,3 +24,8 @@ angular.module("admin.orders").controller "orderCtrl", ($scope, shops, orderCycl
for shop in $scope.shops
shop.disabled = !$scope.distributorHasOrderCycles(shop)
# Removes the split button introduced by spree in the order form
# We only have one stock location in OFN so it's meaningless to split the order between stock locations
# We delete it instead of hiding or changing CSS so that, when spree code toggles the element, nothing hapens
$('.split-item').remove()

View File

@@ -3,8 +3,6 @@ angular.module("admin.resources").factory 'ScheduleResource', ($resource) ->
'index':
method: 'GET'
isArray: true
params:
enterprise_id: '@enterprise_id'
'create':
method: 'POST'
'update':

View File

@@ -40,7 +40,7 @@ angular.module("admin.resources").factory "Schedules", ($q, $injector, RequestMo
delete @byID[schedule.id]
StatusMessage.display 'success', "#{t('js.admin.order_cycles.schedules.deleted_schedule')}: '#{schedule.name}'"
index: (params) ->
request = ScheduleResource.index params, (data) => @load(data)
index: ->
request = ScheduleResource.index (data) => @load(data)
RequestMonitor.load(request.$promise)
request

View File

@@ -5,7 +5,7 @@ angular.module("admin.subscriptions").controller "OrderUpdateIssuesController",
OrderCycles.byID[id].name
$scope.orderCycleCloses = (id) ->
closes_at = moment(OrderCycles.byID[id].orders_close_at, "YYYY-MM-DD HH:mm:SS Z")
closes_at = moment(OrderCycles.byID[id].orders_close_at)
key = if closes_at > moment() then "closes" else "closed"
text = t("js.subscriptions." + key)
"#{text} #{closes_at.fromNow()}"

View File

@@ -15,7 +15,7 @@ angular.module("admin.subscriptions").controller "OrdersPanelController", ($scop
$scope.orderCycleCloses = (id) ->
oc = OrderCycles.byID[id]
return t('js.subscriptions.close_date_not_set') unless oc?.orders_close_at?
closes_at = moment(oc.orders_close_at, "YYYY-MM-DD HH:mm:SS Z")
closes_at = moment(oc.orders_close_at)
text = if closes_at > moment() then t('js.subscriptions.closes') else t('js.subscriptions.closed')
"#{text} #{closes_at.fromNow()}"

View File

@@ -10,14 +10,14 @@ angular.module("admin.subscriptions").controller "ProductsPanelController", ($sc
$scope.save = ->
$scope.saving = true
StatusMessage.display 'progress', t('js.saving')
StatusMessage.display 'progress', 'Saving...'
$scope.subscription.update().then (response) ->
$scope.saving = false
StatusMessage.display 'success', t('js.changes_saved')
StatusMessage.display 'success', 'Saved'
, (response) ->
$scope.saving = false
if response.data?.errors?
keys = Object.keys(response.data.errors)
StatusMessage.display 'failure', response.data.errors[keys[0]][0]
else
StatusMessage.display 'success', t('js.changes_saved')
StatusMessage.display 'success', 'Saved'

View File

@@ -4,8 +4,8 @@ angular.module("admin.tagRules").directive "tagRule", ->
link: (scope, element, attrs) ->
scope.opt =
"TagRule::FilterShippingMethods":
textTop: t('js.admin.tag_rules.shipping_method_tagged_top')
textBottom: t('js.admin.tag_rules.shipping_method_tagged_bottom')
textTop: "Shipping methods tagged"
textBottom: "are:"
taggable: "shipping_method"
tagsAttr: "shipping_method_tags"
tagListAttr: "preferred_shipping_method_tags"
@@ -13,8 +13,8 @@ angular.module("admin.tagRules").directive "tagRule", ->
tagListFor: (rule) ->
rule.preferred_shipping_method_tags
"TagRule::FilterPaymentMethods":
textTop: t('js.admin.tag_rules.payment_method_tagged_top')
textBottom: t('js.admin.tag_rules.payment_method_tagged_bottom')
textTop: "Payment methods tagged"
textBottom: "are:"
taggable: "payment_method"
tagsAttr: "payment_method_tags"
tagListAttr: "preferred_payment_method_tags"
@@ -22,8 +22,8 @@ angular.module("admin.tagRules").directive "tagRule", ->
tagListFor: (rule) ->
rule.preferred_payment_method_tags
"TagRule::FilterOrderCycles":
textTop: t('js.admin.tag_rules.order_cycle_tagged_top')
textBottom: t('js.admin.tag_rules.order_cycle_tagged_bottom')
textTop: "Order Cycles tagged"
textBottom: "are:"
taggable: "exchange"
tagsAttr: "exchange_tags"
tagListAttr: "preferred_exchange_tags"
@@ -31,8 +31,8 @@ angular.module("admin.tagRules").directive "tagRule", ->
tagListFor: (rule) ->
rule.preferred_exchange_tags
"TagRule::FilterProducts":
textTop: t('js.admin.tag_rules.inventory_tagged_top')
textBottom: t('js.admin.tag_rules.inventory_tagged_bottom')
textTop: "Inventory variants tagged"
textBottom: "are:"
taggable: "variant"
tagsAttr: "variant_tags"
tagListAttr: "preferred_variant_tags"

View File

@@ -0,0 +1,12 @@
Darkswarm.controller "GroupEnterpriseNodeCtrl", ($scope, CurrentHub) ->
$scope.active = false
$scope.toggle = ->
$scope.active = !$scope.active
$scope.open = ->
$scope.active
$scope.current = ->
$scope.hub.id is CurrentHub.hub.id

View File

@@ -1,6 +1,22 @@
Darkswarm.controller "GroupPageCtrl", ($scope, enterprises, Enterprises, MapConfiguration, OfnMap) ->
Darkswarm.controller "GroupPageCtrl", ($scope, group_enterprises, Enterprises, MapConfiguration, OfnMap, visibleFilter, Navigation) ->
$scope.Enterprises = Enterprises
all_enterprises_by_id = Enterprises.enterprises_by_id
dereferenced_enterprises = group_enterprises.map (enterprise) =>
all_enterprises_by_id[enterprise.id]
visible_enterprises = visibleFilter dereferenced_enterprises
# TODO: this is duplicate code with app/assets/javascripts/darkswarm/services/enterprises.js.coffee
# It would be better to load only the needed enterprises (group + related shops).
$scope.group_producers = visible_enterprises.filter (enterprise) ->
enterprise.category in ["producer_hub", "producer_shop", "producer"]
$scope.group_hubs = visible_enterprises.filter (enterprise) ->
enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"]
$scope.producers_to_filter = $scope.group_producers
$scope.map = angular.copy MapConfiguration.options
$scope.mapMarkers = OfnMap.enterprise_markers enterprises
$scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
$scope.mapMarkers = OfnMap.enterprise_markers visible_enterprises
$scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1

View File

@@ -1,44 +1,9 @@
Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http, $timeout) ->
$scope.shopfront_loading = false
$scope.enterprise_details = []
Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, Navigation, $location, $templateCache, CurrentHub) ->
$scope.toggle = (e) ->
HashNavigation.toggle $scope.hub.hash if !angular.element(e.target).inheritedData('is-link')
$timeout ->
if $scope.open()
$scope.load_shopfront()
# Toggles shopfront tabs open/closed. Fetches enterprise details from the api, diplays them and adds them
# to $scope.enterprise_details, or simply displays the details again if previously fetched
$scope.toggle = (event) ->
if $scope.open()
$scope.toggle_tab(event)
return
if $scope.enterprise_details[$scope.hub.id]
$scope.hub = $scope.enterprise_details[$scope.hub.id]
$scope.toggle_tab(event)
return
$scope.load_shopfront(event)
$scope.load_shopfront = (event=null) ->
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/enterprises/" + $scope.hub.id + "/shopfront")
.success (data) ->
$scope.shopfront_loading = false
$scope.hub = data
$scope.enterprise_details[$scope.hub.id] = $scope.hub
.error (data) ->
console.error(data)
$scope.toggle_tab = (event) ->
HashNavigation.toggle $scope.hub.hash if event && !angular.element(event.target).inheritedData('is-link')
# Returns boolean: pulldown tab is currently open/closed
$scope.open = ->
HashNavigation.active $scope.hub.hash
# Returns boolean: is this hub the hub that the user is currently "shopping" in?
$scope.current = ->
$scope.hub.id is CurrentHub.hub.id

View File

@@ -1,11 +0,0 @@
Darkswarm.controller "OffcanvasCtrl", ($scope) ->
$scope.menu = $(".left-off-canvas-menu")
$scope.setOffcanvasMenuHeight = ->
$scope.menu.height($(window).height())
$scope.bind = ->
$(window).on("resize", $scope.setOffcanvasMenuHeight)
$scope.setOffcanvasMenuHeight()
$scope.bind()

View File

@@ -1,39 +1,6 @@
Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll, $http, $timeout) ->
$scope.shopfront_loading = false
$scope.enterprise_details = []
$timeout ->
if $scope.open()
$scope.load_shopfront()
# Toggles shopfront tabs open/closed. Fetches enterprise details from the api, diplays them and adds them
# to $scope.enterprise_details, or simply displays the details again if previously fetched
$scope.toggle = (event) ->
if $scope.open()
$scope.toggle_tab(event)
return
if $scope.enterprise_details[$scope.producer.id]
$scope.producer = $scope.enterprise_details[$scope.producer.id]
$scope.toggle_tab(event)
return
$scope.load_shopfront(event)
$scope.load_shopfront = (event=null) ->
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/enterprises/" + $scope.producer.id + "/shopfront")
.success (data) ->
$scope.shopfront_loading = false
$scope.producer = data
$scope.enterprise_details[$scope.producer.id] = $scope.producer
.error (data) ->
console.error(data)
$scope.toggle_tab = (event) ->
HashNavigation.toggle $scope.producer.hash if event && !angular.element(event.target).inheritedData('is-link')
Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll) ->
$scope.toggle = ->
HashNavigation.toggle $scope.producer.hash
$scope.open = ->
HashNavigation.active($scope.producer.hash)

View File

@@ -1,2 +1,2 @@
Darkswarm.controller "AboutUsCtrl", ($scope, Shopfront) ->
$scope.shopfront = Shopfront.shopfront
Darkswarm.controller "AboutUsCtrl", ($scope, CurrentHub) ->
$scope.CurrentHub = CurrentHub

View File

@@ -1,2 +1,4 @@
Darkswarm.controller "ProducersTabCtrl", ($scope, Shopfront, EnterpriseModal) ->
$scope.shopfront = Shopfront.shopfront
Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub, Enterprises, EnterpriseModal) ->
# Injecting Enterprises so CurrentHub.producers is dereferenced.
# We should probably dereference here instead and separate out CurrentHub dereferencing from the Enterprise factory.
$scope.CurrentHub = CurrentHub

View File

@@ -1,10 +1,15 @@
Darkswarm.directive "enterpriseModal", (EnterpriseModal) ->
Darkswarm.directive "enterpriseModal", ($modal, Enterprises, EnterpriseResource) ->
restrict: 'E'
replace: true
template: "<a ng-transclude></a>"
transclude: true
link: (scope, elem, attrs, ctrl) ->
elem.on "click", (event) =>
event.stopPropagation()
scope.modalInstance = EnterpriseModal.open scope.enterprise
elem.on "click", (ev) =>
ev.stopPropagation()
params =
id: scope.enterprise.id
EnterpriseResource.relatives params, (data) =>
Enterprises.addEnterprises data
scope.enterprise = Enterprises.enterprises_by_id[scope.enterprise.id]
Enterprises.dereferenceEnterprise scope.enterprise
scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'enterprise_modal.html', scope: scope)

View File

@@ -13,16 +13,11 @@ Darkswarm.directive "ofnOnHand", ->
ngModel.$setDirty = setDirty
ngModel.$parsers.push (viewValue) ->
available_quantity = scope.available_quantity()
if parseInt(viewValue) > available_quantity
alert t("js.insufficient_stock", {on_hand: available_quantity})
viewValue = available_quantity
on_hand = parseInt(attr.ofnOnHand)
if parseInt(viewValue) > on_hand
alert t("js.insufficient_stock", {on_hand: on_hand})
viewValue = on_hand
ngModel.$setViewValue viewValue
ngModel.$render()
viewValue
scope.available_quantity = ->
on_hand = parseInt(attr.ofnOnHand)
finalized_quantity = parseInt(attr.finalizedquantity) || 0 # finalizedquantity is optional
on_hand + finalized_quantity

View File

@@ -2,14 +2,7 @@ Darkswarm.directive "ofnPageAlert", ($timeout) ->
restrict: 'A'
scope: true
link: (scope, elem, attrs) ->
moveSelectors = [".off-canvas-wrap .inner-wrap",
".off-canvas-wrap .inner-wrap .fixed",
".off-canvas-fixed .top-bar",
".off-canvas-fixed ofn-flash",
".off-canvas-fixed nav.tab-bar",
".off-canvas-fixed .page-alert"]
container_elems = $(moveSelectors.join(", "))
container_elems = $(".off-canvas-wrap .inner-wrap, .off-canvas-wrap .inner-wrap .fixed, .page-alert")
# Wait a moment after page load before showing the alert. Otherwise we often miss the
# start of the animation.

View File

@@ -1,21 +1,18 @@
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, Matcher, Geo, $rootScope) ->
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope) ->
new class Enterprises
enterprises_by_id: {}
constructor: ->
# Populate Enterprises.enterprises from json in page.
@enterprises = enterprises
# Map enterprises to id/object pairs for lookup.
for enterprise in enterprises
@enterprises_by_id[enterprise.id] = enterprise
# Replace enterprise and taxons ids with actual objects.
@dereferenceEnterprises()
@producers = @enterprises.filter (enterprise)->
@visible_enterprises = visibleFilter @enterprises
@producers = @visible_enterprises.filter (enterprise)->
enterprise.category in ["producer_hub", "producer_shop", "producer"]
@hubs = @enterprises.filter (enterprise)->
@hubs = @visible_enterprises.filter (enterprise)->
enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"]
dereferenceEnterprises: ->
@@ -25,6 +22,8 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
@dereferenceEnterprise enterprise
dereferenceEnterprise: (enterprise) ->
@dereferenceProperty(enterprise, 'hubs', @enterprises_by_id)
@dereferenceProperty(enterprise, 'producers', @enterprises_by_id)
@dereferenceProperty(enterprise, 'taxons', Taxons.taxons_by_id)
@dereferenceProperty(enterprise, 'supplied_taxons', Taxons.taxons_by_id)

View File

@@ -1,3 +1,14 @@
Darkswarm.factory 'Groups', (groups) ->
Darkswarm.factory 'Groups', (groups, Enterprises, Dereferencer) ->
new class Groups
groups: groups
groups_by_id: {}
constructor: ->
for group in @groups
@groups_by_id[group.id] = group
@dereference()
dereference: ->
for group in @groups
Dereferencer.dereference group.enterprises, Enterprises.enterprises_by_id
for enterprise in Enterprises.enterprises
Dereferencer.dereference enterprise.groups, @groups_by_id

View File

@@ -1,4 +1,4 @@
Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal) ->
Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) ->
new class OfnMap
constructor: ->
@enterprises = @enterprise_markers(Enterprises.enterprises)
@@ -6,7 +6,7 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal) ->
enterprise.latitude != null || enterprise.longitude != null # Remove enterprises w/o lat or long
enterprise_markers: (enterprises) ->
@extend(enterprise) for enterprise in enterprises
@extend(enterprise) for enterprise in visibleFilter(enterprises)
# Adding methods to each enterprise
extend: (enterprise) ->

View File

@@ -1,4 +1,4 @@
Darkswarm.factory 'Products', ($resource, Shopfront, Dereferencer, Taxons, Properties, Cart, Variants) ->
Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Properties, Cart, Variants) ->
new class Products
constructor: ->
@update()
@@ -31,7 +31,7 @@ Darkswarm.factory 'Products', ($resource, Shopfront, Dereferencer, Taxons, Prope
dereference: ->
for product in @products
product.supplier = Shopfront.producers_by_id[product.supplier.id]
product.supplier = Enterprises.enterprises_by_id[product.supplier.id]
Dereferencer.dereference product.taxons, Taxons.taxons_by_id
product.properties = angular.copy(product.properties_with_values)

View File

@@ -1,8 +0,0 @@
Darkswarm.factory 'Shopfront', (shopfront) ->
new class Shopfront
shopfront: shopfront
producers_by_id: {}
constructor: ->
for producer in shopfront.producers
@producers_by_id[producer.id] = producer

View File

@@ -1,6 +1,6 @@
#new-subscription-dialog
.text-normal.margin-bottom-30.text-center
= t('js.admin.subscriptions.new.please_select_a_shop')
= t('admin.subscriptions.index.please_select_a_shop')
%form{ name: 'new_subscription_form', novalidate: true, ng: { submit: "newSubscription()" }}
@@ -8,7 +8,7 @@
%input.ofn-select2.fullwidth#new_subscription_shop_id{ ng: { model: 'shop_id' }, required: true, name: 'shop_id', data: 'shops' }
%div{ ng: { show: "submitted && new_subscription_form.$pristine" } }
.error{ ng: { show: "new_subscription_form.shop_id.$error.required" } }
= t('js.admin.subscriptions.new.please_select_a_shop')
= t('admin.subscriptions.index.please_select_a_shop')
.text-center
%input.button.red.icon-plus{ type: 'submit', value: t('continue') }

View File

@@ -7,4 +7,4 @@
%input.ofn-select2.fullwidth{ :id => 'rule_type_selector', ng: { model: "ruleType" }, data: "ruleTypes", 'min-search' => "5" }
.text-center
%input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", ng: { click: 'addRule(tagGroup, ruleType)' } }
%input.button.red.icon-plus{ type: 'button', value: "Add Rule", ng: { click: 'addRule(tagGroup, ruleType)' } }

View File

@@ -2,14 +2,14 @@
-# Do not show this for producer shops selling only their own produce,
-# Since a shopping link will already have been displayed in hub_details.html.haml
.row.active_table_row.pad-top{ "ng-if" => "enterprise.is_primary_producer && enterprise.hubs.length > 0 && !(enterprise.hubs.length == 1 && enterprise.hubs[0] == enterprise)"}
.columns.small-12.cta-container
.columns.small-12
.row
.columns.small-12.fat
%div{"ng-if" => "::enterprise.name"}
%label{"ng-bind-html" => "::'shop_for_products_html' | t:{enterprise: enterprise.name}"}
%label{"ng-html" => "::'shop_for_products_html' | t:{enterprise: enterprise.name}"}
%div.show-for-medium-up{"ng-if" => "::!enterprise.name"}
&nbsp;
.row
.row.cta-container
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
"ng-href" => "{{::hub.path}}", "ofn-empties-cart" => "hub",

View File

@@ -5,10 +5,12 @@
*
*= require admin/spree_backend
*= require admin/spree_auth
*= require jquery-ui-timepicker-addon
*= require shared/textAngular
*= require shared/ng-tags-input.min
*= require admin/custom
*= require_self
*/

View File

@@ -1 +1,2 @@
/* Custom fix */
.ui-timepicker-div.ui-timepicker-oneLine dl dd { width: 25%; }

View File

@@ -10,7 +10,6 @@
@import 'foundation-icons';
@import 'base/*';
@import 'layout/*';
@import '*';
@import 'pages/*';
@import '../web/all';

View File

@@ -1,52 +0,0 @@
// A bit arbitrary, works for it's use at time of implementation
$collapsible-max-height: 350px;
.collapsible-checkbox {
display: none;
}
.collapsible-label > ::before {
border-bottom: 5px solid transparent;
border-left: 5px solid currentColor;
border-top: 5px solid transparent;
content: ' ';
display: inline-block;
margin-right: .7rem;
transform: translateY(-2px);
transition: transform .2s ease-out;
vertical-align: middle;
}
.collapsible-content {
max-height: 0;
overflow: hidden;
transition: max-height .25s ease-in-out;
}
.collapsible-checkbox:checked + .collapsible-label + .collapsible-content {
max-height: $collapsible-max-height;
}
.collapsible-checkbox:checked + .collapsible-label > ::before {
transform: rotate(90deg) translateX(-3px);
}
@media only screen and (min-width: 1025px) {
// This double class is used to so this rule is more specific than the one in
// all.scss
.collapsible-label.collapsible-label-md {
margin-left: 0;
}
.collapsible-label-md > ::before {
display: none;
}
.collapsible-content-md {
max-height: $collapsible-max-height;
}
}

View File

@@ -60,6 +60,7 @@ body.embedded {
vertical-align: top;
&.cart {
div.joyride-tip-guide { // Cart Dropdown
top: 75px;
overflow: visible;

View File

@@ -0,0 +1,31 @@
@import "variables";
nav.top-bar {
margin-bottom: 0px;
a.icon {
&:hover {
text-decoration: none;
}
height: $topbar-height;
color: white;
i {
font-size: 29px;
line-height: $topbar-height;
}
span {
font-size: 13px;
display: inline-block;
line-height: $topbar-height;
height: $topbar-height;
vertical-align: top;
}
}
}
body > section[role='main'] {
padding: 0px;
}

View File

@@ -1,17 +0,0 @@
@import "compass/css3/transition";
.off-canvas-fixed {
@include transition(transform 1000ms ease-in-out);
}
.move-right > .off-canvas-fixed {
height: 100%;
-webkit-transform: translate3d(15.625rem, 0, 0);
transform: translate3d(15.625rem, 0, 0);
}
.left-off-canvas-menu {
-webkit-transform: none;
transform: none;
margin-left: -15.625rem;
}

View File

@@ -1,212 +1,103 @@
@import 'compass';
@import 'branding';
@import 'mixins';
@import 'typography';
@import 'variables';
@import "compass";
@import "branding";
@import "mixins";
@import "typography";
@import "variables";
nav.top-bar {
nav {
@include textpress;
text-shadow: none;
text-align: center;
font-size: 16px;
margin-bottom: 0;
height: $topbar-height;
}
@media #{$large-only} {
.top-bar--menu-item-with-icon span {
text-shadow: none;
// Create center style for nav ul (foundation provides left and right)
text-align: center;
.top-bar-section {
// Avoid menu items blocking logo
li:not(.has-form), li:not(.has-form) a:not(.button), li:not(.has-form) a:not(.button):hover {
background-color: transparent;
}
ul.center {
display: inline-block;
// By default, we center between the left and right uls, but we want to be centered
// relative to the whole page. The difference in width between the other uls is 74px,
// so we offset by that amount here.
margin-left: -74px;
}
}
.joyride-tip-guide .button {
text-shadow: none;
}
// Default overrides - big menu
.top-bar-section .has-dropdown > a {
padding-right: ($topbar-height / 3) !important;
i.ofn-i_022-cog, .ofn-i_071-globe {
font-size: 24px;
line-height: $topbar-height;
}
i.ofn-i_071-globe {
color: #666;
font-size: 27px
}
}
.top-bar-section .has-dropdown > a:after {
display: none;
}
.top-bar--current-hub-prefix,
.top-bar--current-hub-name {
display: inline-block;
.top-bar-section ul li > a {
font-size: 0.75rem;
height: $topbar-height;
opacity: 0.8;
&:hover, &:focus, &:active {
opacity: 1;
}
@include transition(all 0.3s ease-in-out);
}
.top-bar-section ul li.ofn-logo > a {
display: table-cell;
vertical-align: middle;
opacity: 1;
}
.top-bar--current-hub-name {
max-width: 10em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.nav-branded {
color: $brand-colour;
.top-bar-section ul li > a.top-bar--menu-item-with-icon {
display: inline;
}
.top-bar--menu-item-with-icon i,
.top-bar--menu-item-with-icon img {
line-height: $topbar-height;
}
.top-bar-section {
border-bottom: 1px solid $ofn-grey;
a.icon {
&:hover {
text-decoration: none;
}
> span {
display: inline-block;
font-weight: 300;
height: $topbar-height;
line-height: $topbar-height;
vertical-align: top;
span {
font-size: 13px;
}
}
// Avoid menu items blocking logo
li:not(.has-form), li:not(.has-form) a:not(.button), li:not(.has-form) a:not(.button):hover {
background-color: transparent;
.nav-primary {
@include headingFont;
font-size: 0.875rem;
font-weight: 300;
}
li.cart {
background-color: #f4704c;
a span {
color: white;
}
i {
color: white;
}
.count {
position: relative;
img {
width: 32px;
height: 26px;
margin-top: 16px;
margin-right: 5px;
}
span {
background-color: white;
border-radius: 20px;
color: #f4704c;
font-size: 12px;
line-height: 18px;
position: absolute;
right: -8px;
top: 8px;
width: 18px;
}
}
}
ul.center {
display: inline-block;
// By default, we center between the left and right uls, but we want to be centered
// relative to the whole page. The difference in width between the other uls is 74px,
// so we offset by that amount here.
margin-left: -74px;
ul .nav-primary {
text-transform: uppercase;
}
ul.dropdown {
border: 1px solid $smoke;
border-top: none;
}
ul.right {
> li {
border-left: 1px solid #ddd;
padding: 0 14px;
@media screen and (max-width: 1450px) {
padding: 0 6px;
}
}
li > a {
opacity: 0.8;
&:hover, &:focus, &:active {
opacity: 1;
}
i {
color: #f4704c;
display: inline-block;
margin-right: 2px;
margin-top: -3px;
vertical-align: middle;
}
img {
margin-right: 2px;
margin-top: -5px;
}
}
}
ul li > a {
font-size: 16px;
height: $topbar-height;
}
ul li.ofn-logo > a {
display: table-cell;
vertical-align: middle;
}
ul .nav-primary {
@include headingFont;
text-transform: uppercase;
font-weight: 300;
font-size: 16px;
}
.joyride-tip-guide .button {
text-shadow: none;
}
}
// Mobile Menu
.tab-bar {
background-color: white;
position: fixed;
width: 100%;
height: 2.8em;
z-index: 1;
.cart-span {
background-color: #f4704c;
padding: 13px;
a,
span {
color: white;
display: inline-block;
}
.count {
position: relative;
img {
margin-left: 2px;
width: 26px;
}
span {
background-color: white;
border-radius: 20px;
color: #f4704c;
font-size: 12px;
line-height: 16px;
position: absolute;
right: -10px;
text-align: center;
top: -9px;
width: 16px;
}
}
}
}
.off-canvas-list li.language-switcher ul li {
@@ -223,6 +114,8 @@ nav.top-bar {
}
.off-canvas-wrap.move-right .tab-bar .menu-icon span {
-moz-box-shadow: 0 0px 0 1px #666, 0 7px 0 1px #666, 0 14px 0 1px #666;
-webkit-box-shadow: 0 0px 0 1px #666, 0 7px 0 1px #666, 0 14px 0 1px #666;
box-shadow: 0 0px 0 1px #666, 0 7px 0 1px #666, 0 14px 0 1px #666;
}
@@ -234,19 +127,10 @@ nav.top-bar {
padding: 9px 0 0 9px;
}
// Leave space for tab bar, in screens smaller than large.
[role="main"] {
margin-top: 2.8em;
@media #{$large-up} {
margin-top: 0;
}
}
.top-bar .ofn-logo img {
height: auto;
width: auto;
max-height: 44px;
max-height: 51px;
max-width: 250px;
}
@@ -254,7 +138,7 @@ nav.top-bar {
background-color: white;
}
.off-canvas-wrap ul.off-canvas-list {
.off-canvas-wrap.move-right ul.off-canvas-list {
font-size: 0.875rem;
.li-menu {
@@ -274,10 +158,12 @@ nav.top-bar {
background-color: transparent;
color: $brand-colour;
}
@include transition(all 0.3s ease-in-out);
}
}
.off-canvas-wrap ul.off-canvas-list i {
.off-canvas-wrap.move-right ul.off-canvas-list i {
font-size: 1.5rem;
margin-right: 0.25rem;
}
@@ -286,8 +172,7 @@ nav.top-bar {
@media screen and (max-width: 1450px) {
nav .top-bar-section {
ul li a,
.has-dropdown > a {
ul li a, .has-dropdown > a {
padding: 0 ($topbar-height / 8) !important;
}
@@ -309,4 +194,10 @@ nav.top-bar {
// padding required to placehold for fixed menu bar
padding-top: 0;
}
section.right {
.nav-branded {
padding: 0 1em;
}
}
}

View File

@@ -2,12 +2,10 @@
@import "animations";
@import "compass/css3/transition";
$page-alert-height: 55px;
// Basic style \\
.page-alert {
.alert-box {
height: $page-alert-height;
height: 55px;
overflow: hidden;
border: 1px solid rgba($dark-grey, 0.35);
border-left: none;
@@ -47,26 +45,33 @@ $page-alert-height: 55px;
}
// Show-hide animation \\
.off-canvas-wrap .inner-wrap,
.off-canvas-fixed .top-bar,
.off-canvas-fixed ofn-flash,
.off-canvas-fixed nav.tab-bar,
.off-canvas-fixed .page-alert {
@include transition(all 1000ms ease-in-out);
.off-canvas-wrap .inner-wrap, .off-canvas-wrap .inner-wrap .fixed, nav.tab-bar {
@include transition(all, 1000ms, ease-in-out);
&.move-down {
margin-top: $page-alert-height;
margin-top: 55px;
@include transition(all, 1000ms, ease-in-out);
}
}
.off-canvas-wrap .page-alert {
top: -1 * $page-alert-height;
z-index: 100;
.off-canvas-wrap .inner-wrap .page-alert.fixed {
top: -55px;
z-index: 1;
// TODO: Compass to disable transition
-moz-transition: none;
-webkit-transition: none;
-o-transition: color 0 ease-in;
transition: none;
}
.off-canvas-wrap.move-right .inner-wrap.move-down {
.page-alert {
top: -55px * 2;
}
.left-off-canvas-menu {
top: -1 * $page-alert-height;
top: -55px;
}
}

View File

@@ -1,5 +1,4 @@
@import '../base/colors';
@import '../collapsible';
// Styling for login modal to style tabs
.reveal-modal.login-modal {
@@ -24,10 +23,3 @@
}
}
}
@media only screen and (min-width: 1025px) {
// make sure styling doesn't get messed up if resizing down and back up
.collapsible-menus-container {
min-height: 250px;
}
}

View File

@@ -13,9 +13,8 @@
$brand-colour: #f27052;
// Topbar
$topbar-height: rem-calc(64);
$topbar-link-padding: $topbar-height / 4;
$topbar-arrows: false;
$topbar-height: rem-calc(75);
$topbar-link-padding: $topbar-height / 3;
$topbar-bg: $white;
$topbar-bg-color: $topbar-bg;

View File

@@ -31,16 +31,7 @@ module Admin
def collection
return Schedule.where("1=0") unless json_request?
if params[:enterprise_id]
filter_schedules_by_enterprise_id(permissions.visible_schedules, params[:enterprise_id])
else
permissions.visible_schedules
end
end
# Filter schedules by OCs with a given coordinator id
def filter_schedules_by_enterprise_id(schedules, enterprise_id)
schedules.joins(:order_cycles).where(order_cycles: { coordinator_id: enterprise_id.to_i })
permissions.visible_schedules
end
def collection_actions

View File

@@ -5,7 +5,12 @@ module Api
before_filter :override_sells, only: [:create, :update]
before_filter :override_visible, only: [:create, :update]
respond_to :json
skip_authorization_check only: [:shopfront]
skip_authorization_check only: [:shopfront, :managed]
def managed
@enterprises = Enterprise.ransack(params[:q]).result.managed_by(current_api_user)
render params[:template] || :bulk_index
end
def create
authorize! :create, Enterprise

View File

@@ -0,0 +1,25 @@
module Api
class OrderCyclesController < Spree::Api::BaseController
respond_to :json
def managed
authorize! :admin, OrderCycle
authorize! :read, OrderCycle
@order_cycles = OrderCycle.ransack(params[:q]).result.managed_by(current_api_user)
render params[:template] || :bulk_index
end
def accessible
@order_cycles = if params[:as] == "distributor"
OrderCycle.ransack(params[:q]).result.
involving_managed_distributors_of(current_api_user).order('updated_at DESC')
elsif params[:as] == "producer"
OrderCycle.ransack(params[:q]).result.
involving_managed_producers_of(current_api_user).order('updated_at DESC')
else
OrderCycle.ransack(params[:q]).result.accessible_by(current_api_user)
end
render params[:template] || :bulk_index
end
end
end

View File

@@ -1,5 +1,4 @@
require 'open_food_network/referer_parser'
require 'spree/authentication_helpers'
class ApplicationController < ActionController::Base
protect_from_forgery
@@ -8,7 +7,6 @@ class ApplicationController < ActionController::Base
before_filter :set_cache_headers # prevent cart emptying via cache when using back button #1213
include EnterprisesHelper
include Spree::AuthenticationHelpers
def redirect_to(options = {}, response_status = {})
::Rails.logger.error("Redirected by #{begin

View File

@@ -8,6 +8,7 @@ class CheckoutController < Spree::CheckoutController
prepend_before_filter :require_order_cycle
prepend_before_filter :require_distributor_chosen
skip_before_filter :check_registration
before_filter :enable_embedded_shopfront
include OrderCyclesHelper
@@ -174,6 +175,10 @@ class CheckoutController < Spree::CheckoutController
end
end
def skip_state_validation?
true
end
def load_order
@order = current_order
redirect_to(main_app.shop_path) && return unless @order && @order.checkout_allowed?
@@ -201,11 +206,11 @@ class CheckoutController < Spree::CheckoutController
def redirect_to_cart_path
respond_to do |format|
format.html do
redirect_to main_app.cart_path
redirect_to cart_path
end
format.json do
render json: { path: main_app.cart_path }, status: :bad_request
render json: { path: cart_path }, status: :bad_request
end
end
end

View File

@@ -4,7 +4,6 @@ class EnterprisesController < BaseController
layout "darkswarm"
helper Spree::ProductsHelper
include OrderCyclesHelper
include SerializerHelper
# These prepended filters are in the reverse order of execution
prepend_before_filter :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop
@@ -15,10 +14,18 @@ class EnterprisesController < BaseController
respond_to :js, only: :permalink_checker
def shop
return redirect_to main_app.cart_path unless enough_stock?
return redirect_to spree.cart_path unless enough_stock?
set_noindex_meta_tag
@enterprise = current_distributor
enterprises = current_distributor
.plus_relatives_and_oc_producers(shop_order_cycles)
.activated
.includes(address: :state)
.all
enterprises = inject_json_ams('enterprises', enterprises)
render locals: { enterprises: enterprises }
end
def relatives
@@ -104,4 +111,14 @@ class EnterprisesController < BaseController
def set_noindex_meta_tag
@noindex_meta_tag = true unless current_distributor.visible?
end
def inject_json_ams(name, object)
options = {
each_serializer: Api::EnterpriseSerializer,
data: OpenFoodNetwork::EnterpriseInjectionData.new
}
serializer_instance = ActiveModel::ArraySerializer.new(object, options)
{ name: name, json: serializer_instance.to_json }
end
end

View File

@@ -1,6 +1,10 @@
class GroupsController < BaseController
layout 'darkswarm'
def index
@groups = EnterpriseGroup.on_front_page.by_position
end
def show
enable_embedded_shopfront
@hide_menu = true if @shopfront_layout == 'embedded'

View File

@@ -1,6 +0,0 @@
# For the API
ActionController::Metal.class_eval do
def spree_current_user
@spree_current_user ||= env['warden'].user
end
end

View File

@@ -3,14 +3,5 @@ class ProducersController < BaseController
before_filter :enable_embedded_shopfront
def index
@enterprises = Enterprise
.activated
.visible
.is_primary_producer
.includes(address: :state)
.includes(:properties)
.includes(supplied_products: :properties)
.all
end
def index; end
end

View File

@@ -6,8 +6,6 @@ class ShopsController < BaseController
def index
@enterprises = Enterprise
.activated
.visible
.is_distributor
.includes(address: :state)
.includes(:properties)
.includes(supplied_products: :properties)

View File

@@ -47,22 +47,12 @@ Spree::Admin::BaseController.class_eval do
end
end
protected
def model_class
const_name = controller_name.classify
if Spree.const_defined?(const_name)
return "Spree::#{const_name}".constantize
end
nil
end
private
def active_distributors_not_ready_for_checkout
ocs = OrderCycle.managed_by(spree_current_user).active
distributors = ocs.map(&:distributors).flatten.uniq
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
Enterprise.where('id IN (?)', distributors).not_ready_for_checkout
end
def active_distributors_not_ready_for_checkout_message(distributors)

View File

@@ -1,5 +1,4 @@
Spree::Admin::Orders::CustomerDetailsController.class_eval do
before_filter :check_authorization
before_filter :set_guest_checkout_status, only: :update
def update
@@ -26,17 +25,6 @@ Spree::Admin::Orders::CustomerDetailsController.class_eval do
private
def check_authorization
load_order
session[:access_token] ||= params[:token]
resource = @order
action = params[:action].to_sym
action = :edit if action == :show # show route renders :edit for this controller
authorize! action, resource, session[:access_token]
end
def set_guest_checkout_status
registered_user = Spree::User.find_by_email(params[:order][:email])

View File

@@ -33,18 +33,12 @@ Spree::Admin::ProductsController.class_eval do
delete_stock_params_and_set_after do
super
end
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
invoke_callbacks(:create, :fails)
@object.errors.add(:base, t('spree.admin.products.image_upload_error'))
respond_with(@object)
end
def update
delete_stock_params_and_set_after do
super
end
clear_variants_unit_description if @object.variant_unit == 'items'
end
def bulk_update
@@ -160,10 +154,4 @@ Spree::Admin::ProductsController.class_eval do
def set_product_master_variant_price_to_zero
@product.price = 0 if @product.price.nil?
end
def clear_variants_unit_description
@object.variants.each do |variant|
variant.update_attribute :unit_description, ''
end
end
end

View File

@@ -14,7 +14,3 @@ module AuthorizeOnLoadResource
end
Spree::Admin::ResourceController.prepend(AuthorizeOnLoadResource)
Spree::Admin::ResourceController.class_eval do
rescue_from CanCan::AccessDenied, :with => :unauthorized
end

View File

@@ -1,131 +0,0 @@
module Spree
module Admin
class UsersController < ResourceController
rescue_from Spree::User::DestroyWithOrdersError, with: :user_destroy_with_orders_error
after_filter :sign_in_if_change_own_password, only: :update
# http://spreecommerce.com/blog/2010/11/02/json-hijacking-vulnerability/
before_filter :check_json_authenticity, only: :index
before_filter :load_roles, only: [:edit, :new, :update, :create,
:generate_api_key, :clear_api_key]
def index
respond_with(@collection) do |format|
format.html
format.json { render json: json_data }
end
end
def create
if params[:user]
roles = params[:user].delete("spree_role_ids")
end
@user = Spree::User.new(params[:user])
if @user.save
if roles
@user.spree_roles = roles.reject(&:blank?).collect{ |r| Spree::Role.find(r) }
end
flash.now[:success] = Spree.t(:created_successfully)
render :edit
else
render :new
end
end
def update
if params[:user]
roles = params[:user].delete("spree_role_ids")
end
if @user.update_attributes(params[:user])
if roles
@user.spree_roles = roles.reject(&:blank?).collect{ |r| Spree::Role.find(r) }
end
flash.now[:success] = Spree.t(:account_updated)
end
render :edit
end
def generate_api_key
if @user.generate_spree_api_key!
flash[:success] = Spree.t('api.key_generated')
end
redirect_to edit_admin_user_path(@user)
end
def clear_api_key
if @user.clear_spree_api_key!
flash[:success] = Spree.t('api.key_cleared')
end
redirect_to edit_admin_user_path(@user)
end
protected
def collection
return @collection if @collection.present?
if request.xhr? && params[:q].present?
# Disabling proper nested include here due to rails 3.1 bug
@collection = Spree::User.
includes(:bill_address, :ship_address).
where("spree_users.email #{LIKE} :search
OR (spree_addresses.firstname #{LIKE} :search
AND spree_addresses.id = spree_users.bill_address_id)
OR (spree_addresses.lastname #{LIKE} :search
AND spree_addresses.id = spree_users.bill_address_id)
OR (spree_addresses.firstname #{LIKE} :search
AND spree_addresses.id = spree_users.ship_address_id)
OR (spree_addresses.lastname #{LIKE} :search
AND spree_addresses.id = spree_users.ship_address_id)",
search: "#{params[:q].strip}%").
limit(params[:limit] || 100)
else
@search = Spree::User.registered.ransack(params[:q])
@collection = @search.
result.
page(params[:page]).
per(Spree::Config[:admin_products_per_page])
end
end
private
# handling raise from Spree::Admin::ResourceController#destroy
def user_destroy_with_orders_error
invoke_callbacks(:destroy, :fails)
render status: :forbidden, text: Spree.t(:error_user_destroy_with_orders)
end
# Allow different formats of json data to suit different ajax calls
def json_data
json_format = params[:json_format] || 'default'
case json_format
when 'basic'
collection.map { |u| { 'id' => u.id, 'name' => u.email } }.to_json
else
address_fields = [:firstname, :lastname, :address1, :address2, :city,
:zipcode, :phone, :state_name, :state_id, :country_id]
includes = { only: address_fields, include: { state: { only: :name },
country: { only: :name } } }
collection.to_json(only: [:id, :email], include:
{ bill_address: includes, ship_address: includes })
end
end
def sign_in_if_change_own_password
return unless spree_current_user == @user && @user.password.present?
sign_in(@user, event: :authentication, bypass: true)
end
def load_roles
@roles = Spree::Role.scoped
end
end
end
end

View File

@@ -1,93 +0,0 @@
require 'open_food_network/address_finder'
module Spree
class CheckoutController < Spree::StoreController
include CheckoutHelper
ssl_required
before_filter :load_order
before_filter :ensure_order_not_completed
before_filter :ensure_checkout_allowed
before_filter :ensure_sufficient_stock_lines
before_filter :associate_user
before_filter :check_authorization
before_filter :enable_embedded_shopfront
helper 'spree/orders'
rescue_from Spree::Core::GatewayError, :with => :rescue_from_spree_gateway_error
def edit
flash.keep
redirect_to main_app.checkout_path
end
private
def load_order
@order = current_order
redirect_to main_app.cart_path && return unless @order
if params[:state]
redirect_to checkout_state_path(@order.state) if @order.can_go_to_state?(params[:state])
@order.state = params[:state]
end
setup_for_current_state
end
def ensure_checkout_allowed
redirect_to main_app.cart_path unless @order.checkout_allowed?
end
def ensure_order_not_completed
redirect_to main_app.cart_path if @order.completed?
end
def ensure_sufficient_stock_lines
if @order.insufficient_stock_lines.present?
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
redirect_to main_app.cart_path
end
end
def setup_for_current_state
method_name = :"before_#{@order.state}"
send(method_name) if respond_to?(method_name, true)
end
# Adapted from spree_last_address gem: https://github.com/TylerRick/spree_last_address
# Originally, we used a forked version of this gem, but encountered strange errors where
# it worked in dev but only intermittently in staging/prod.
def before_address
associate_user
finder = OpenFoodNetwork::AddressFinder.new(@order.email)
@order.bill_address = finder.bill_address
@order.ship_address = finder.ship_address
end
def before_delivery
return if params[:order].present?
packages = @order.shipments.map(&:to_package)
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
end
def before_payment
current_order.payments.destroy_all if request.put?
end
def rescue_from_spree_gateway_error
flash[:error] = Spree.t(:spree_gateway_error_flash_for_checkout)
render :edit
end
def check_authorization
authorize!(:edit, current_order, session[:access_token])
end
end
end

View File

@@ -0,0 +1,30 @@
require 'open_food_network/address_finder'
Spree::CheckoutController.class_eval do
include CheckoutHelper
before_filter :enable_embedded_shopfront
def edit
flash.keep
redirect_to main_app.checkout_path
end
private
def before_payment
current_order.payments.destroy_all if request.put?
end
# Adapted from spree_last_address gem: https://github.com/TylerRick/spree_last_address
# Originally, we used a forked version of this gem, but encountered strange errors where
# it worked in dev but only intermittently in staging/prod.
def before_address
associate_user
finder = OpenFoodNetwork::AddressFinder.new(@order.email)
@order.bill_address = finder.bill_address
@order.ship_address = finder.ship_address
end
end

View File

@@ -1,7 +0,0 @@
module Spree
class HomeController < Spree::StoreController
respond_to :html
def index; end
end
end

View File

@@ -1,226 +0,0 @@
require 'spree/core/controller_helpers/order_decorator'
require 'spree/core/controller_helpers/auth_decorator'
module Spree
class OrdersController < Spree::StoreController
include OrderCyclesHelper
layout 'darkswarm'
ssl_required :show
before_filter :check_authorization
rescue_from ActiveRecord::RecordNotFound, :with => :render_404
helper 'spree/products', 'spree/orders'
respond_to :html
respond_to :json
before_filter :update_distribution, only: :update
before_filter :filter_order_params, only: :update
before_filter :enable_embedded_shopfront
prepend_before_filter :require_order_authentication, only: :show
prepend_before_filter :require_order_cycle, only: :edit
prepend_before_filter :require_distributor_chosen, only: :edit
before_filter :check_hub_ready_for_checkout, only: :edit
before_filter :check_at_least_one_line_item, only: :update
def show
@order = Spree::Order.find_by_number!(params[:id])
end
def empty
if @order = current_order
@order.empty!
end
redirect_to main_app.cart_path
end
def check_authorization
session[:access_token] ||= params[:token]
order = Spree::Order.find_by_number(params[:id]) || current_order
if order
authorize! :edit, order, session[:access_token]
else
authorize! :create, Spree::Order
end
end
# Patching to redirect to shop if order is empty
def edit
@order = current_order(true)
@insufficient_stock_lines = @order.insufficient_stock_lines
@unavailable_order_variants = OrderCycleDistributedVariants.
new(current_order_cycle, current_distributor).unavailable_order_variants(@order)
if @order.line_items.empty?
redirect_to main_app.shop_path
else
associate_user
if @order.insufficient_stock_lines.present? || @unavailable_order_variants.present?
flash[:error] = t("spree.orders.error_flash_for_unavailable_items")
end
end
end
def update
@insufficient_stock_lines = []
@order = order_to_update
unless @order
flash[:error] = t(:order_not_found)
redirect_to(root_path) && return
end
if @order.update_attributes(params[:order])
discard_empty_line_items
with_open_adjustments { update_totals_and_taxes }
if @order == current_order
fire_event('spree.order.contents_changed')
else
@order.update_distribution_charge!
end
respond_with(@order) do |format|
format.html do
if params.key?(:checkout)
@order.next_transition.run_callbacks if @order.cart?
redirect_to checkout_state_path(@order.checkout_steps.first)
elsif @order.complete?
redirect_to order_path(@order)
else
redirect_to main_app.cart_path
end
end
end
else
# Show order with original values, not newly entered ones
@insufficient_stock_lines = @order.insufficient_stock_lines
@order.line_items(true)
respond_with(@order)
end
end
def update_distribution
@order = current_order(true)
if params[:commit] == 'Choose Hub'
distributor = Enterprise.is_distributor.find params[:order][:distributor_id]
@order.set_distributor! distributor
flash[:notice] = I18n.t(:order_choosing_hub_notice)
redirect_to request.referer
elsif params[:commit] == 'Choose Order Cycle'
@order.empty! # empty cart
order_cycle = OrderCycle.active.find params[:order][:order_cycle_id]
@order.set_order_cycle! order_cycle
flash[:notice] = I18n.t(:order_choosing_hub_notice)
redirect_to request.referer
end
end
def filter_order_params
if params[:order] && params[:order][:line_items_attributes]
params[:order][:line_items_attributes] =
remove_missing_line_items(params[:order][:line_items_attributes])
end
end
def remove_missing_line_items(attrs)
attrs.select do |_i, line_item|
Spree::LineItem.find_by_id(line_item[:id])
end
end
def clear
@order = current_order(true)
@order.empty!
@order.set_order_cycle! nil
redirect_to main_app.enterprise_path(@order.distributor.id)
end
def order_cycle_expired
@order_cycle = OrderCycle.find session[:expired_order_cycle_id]
end
def cancel
@order = Spree::Order.find_by_number!(params[:id])
authorize! :cancel, @order
if @order.cancel
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
else
flash[:error] = I18n.t(:orders_could_not_cancel)
end
redirect_to request.referer || order_path(@order)
end
private
# Updates the various denormalized total attributes of the order and
# recalculates the shipment taxes
def update_totals_and_taxes
@order.updater.update_totals
@order.shipment.ensure_correct_adjustment_with_included_tax if @order.shipment
end
# Sets the adjustments to open to perform the block's action and restores
# their state to whatever the they had. Note that it does not change any new
# adjustments that might get created in the yielded block.
def with_open_adjustments
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
hash[adjustment.id] = adjustment.state
end
@order.adjustments.each(&:open)
yield
@order.adjustments.each do |adjustment|
previous_state = previous_states[adjustment.id]
adjustment.update_attribute(:state, previous_state) if previous_state
end
end
def discard_empty_line_items
@order.line_items = @order.line_items.select { |li| li.quantity > 0 }
end
def require_order_authentication
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
require_login_then_redirect_to request.env['PATH_INFO']
end
def order_to_update
return @order_to_update if defined? @order_to_update
return @order_to_update = current_order unless params[:id]
@order_to_update = changeable_order_from_number
end
# If a specific order is requested, return it if it is COMPLETE and
# changes are allowed and the user has access. Return nil if not.
def changeable_order_from_number
order = Spree::Order.complete.find_by_number(params[:id])
return nil unless order.andand.changes_allowed? && can?(:update, order)
order
end
def check_at_least_one_line_item
return unless order_to_update.andand.complete?
items = params[:order][:line_items_attributes]
.andand.select{ |_k, attrs| attrs["quantity"].to_i > 0 }
if items.empty?
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
redirect_to order_path(order_to_update)
end
end
end
end

View File

@@ -0,0 +1,194 @@
require 'spree/core/controller_helpers/order_decorator'
require 'spree/core/controller_helpers/auth_decorator'
Spree::OrdersController.class_eval do
before_filter :update_distribution, only: :update
before_filter :filter_order_params, only: :update
before_filter :enable_embedded_shopfront
prepend_before_filter :require_order_authentication, only: :show
prepend_before_filter :require_order_cycle, only: :edit
prepend_before_filter :require_distributor_chosen, only: :edit
before_filter :check_hub_ready_for_checkout, only: :edit
before_filter :check_at_least_one_line_item, only: :update
include OrderCyclesHelper
layout 'darkswarm'
respond_to :json
# Patching to redirect to shop if order is empty
def edit
@order = current_order(true)
@insufficient_stock_lines = @order.insufficient_stock_lines
@unavailable_order_variants = OrderCycleDistributedVariants.new(current_order_cycle, current_distributor).unavailable_order_variants(@order)
if @order.line_items.empty?
redirect_to main_app.shop_path
else
associate_user
if @order.insufficient_stock_lines.present? || @unavailable_order_variants.present?
flash[:error] = t("spree.orders.error_flash_for_unavailable_items")
end
end
end
def update
@insufficient_stock_lines = []
@order = order_to_update
unless @order
flash[:error] = t(:order_not_found)
redirect_to(root_path) && return
end
if @order.update_attributes(params[:order])
discard_empty_line_items
with_open_adjustments { update_totals_and_taxes }
render(:edit) && return unless apply_coupon_code
if @order == current_order
fire_event('spree.order.contents_changed')
else
@order.update_distribution_charge!
end
respond_with(@order) do |format|
format.html do
if params.key?(:checkout)
@order.next_transition.run_callbacks if @order.cart?
redirect_to checkout_state_path(@order.checkout_steps.first)
elsif @order.complete?
redirect_to order_path(@order)
else
redirect_to cart_path
end
end
end
else
# Show order with original values, not newly entered ones
@insufficient_stock_lines = @order.insufficient_stock_lines
@order.line_items(true)
respond_with(@order)
end
end
def update_distribution
@order = current_order(true)
if params[:commit] == 'Choose Hub'
distributor = Enterprise.is_distributor.find params[:order][:distributor_id]
@order.set_distributor! distributor
flash[:notice] = I18n.t(:order_choosing_hub_notice)
redirect_to request.referer
elsif params[:commit] == 'Choose Order Cycle'
@order.empty! # empty cart
order_cycle = OrderCycle.active.find params[:order][:order_cycle_id]
@order.set_order_cycle! order_cycle
flash[:notice] = I18n.t(:order_choosing_hub_notice)
redirect_to request.referer
end
end
def filter_order_params
if params[:order] && params[:order][:line_items_attributes]
params[:order][:line_items_attributes] = remove_missing_line_items(params[:order][:line_items_attributes])
end
end
def remove_missing_line_items(attrs)
attrs.select do |_i, line_item|
Spree::LineItem.find_by_id(line_item[:id])
end
end
def clear
@order = current_order(true)
@order.empty!
@order.set_order_cycle! nil
redirect_to main_app.enterprise_path(@order.distributor.id)
end
def order_cycle_expired
@order_cycle = OrderCycle.find session[:expired_order_cycle_id]
end
def cancel
@order = Spree::Order.find_by_number!(params[:id])
authorize! :cancel, @order
if @order.cancel
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
else
flash[:error] = I18n.t(:orders_could_not_cancel)
end
redirect_to request.referer || order_path(@order)
end
private
# Updates the various denormalized total attributes of the order and
# recalculates the shipment taxes
def update_totals_and_taxes
@order.updater.update_totals
@order.shipment.ensure_correct_adjustment_with_included_tax if @order.shipment
end
# Sets the adjustments to open to perform the block's action and restores
# their state to whatever the they had. Note that it does not change any new
# adjustments that might get created in the yielded block.
def with_open_adjustments
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
hash[adjustment.id] = adjustment.state
end
@order.adjustments.each(&:open)
yield
@order.adjustments.each do |adjustment|
previous_state = previous_states[adjustment.id]
adjustment.update_attribute(:state, previous_state) if previous_state
end
end
def discard_empty_line_items
@order.line_items = @order.line_items.select { |li| li.quantity > 0 }
end
def require_order_authentication
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
require_login_then_redirect_to request.env['PATH_INFO']
end
def order_to_update
return @order_to_update if defined? @order_to_update
return @order_to_update = current_order unless params[:id]
@order_to_update = changeable_order_from_number
end
# If a specific order is requested, return it if it is COMPLETE and
# changes are allowed and the user has access. Return nil if not.
def changeable_order_from_number
order = Spree::Order.complete.find_by_number(params[:id])
return nil unless order.andand.changes_allowed? && can?(:update, order)
order
end
def check_at_least_one_line_item
return unless order_to_update.andand.complete?
items = params[:order][:line_items_attributes]
.andand.select{ |_k, attrs| attrs["quantity"].to_i > 0 }
if items.empty?
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
redirect_to order_path(order_to_update)
end
end
end

View File

@@ -1,14 +0,0 @@
module Spree
class StoreController < Spree::BaseController
layout 'darkswarm'
include Spree::Core::ControllerHelpers::Order
include I18nHelper
before_filter :set_locale
def unauthorized
render 'shared/unauthorized', status: :unauthorized
end
end
end

View File

@@ -0,0 +1,10 @@
class Spree::StoreController
layout 'darkswarm'
include I18nHelper
before_filter :set_locale
def unauthorized
render 'shared/unauthorized', status: :unauthorized
end
end

View File

@@ -1,44 +0,0 @@
module Spree
class UserPasswordsController < Devise::PasswordsController
helper 'spree/base', 'spree/store'
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Common
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::SSL
ssl_required
# Overridden due to bug in Devise.
# respond_with resource, :location => new_session_path(resource_name)
# is generating bad url /session/new.user
#
# overridden to:
# respond_with resource, :location => spree.login_path
#
def create
self.resource = resource_class.send_reset_password_instructions(params[resource_name])
if resource.errors.empty?
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, location: spree.login_path
else
respond_with_navigational(resource) { render :new }
end
end
# Devise::PasswordsController allows for blank passwords.
# Silly Devise::PasswordsController!
# Fixes spree/spree#2190.
def update
if params[:spree_user][:password].blank?
self.resource = resource_class.new
resource.reset_password_token = params[:spree_user][:reset_password_token]
set_flash_message(:error, :cannot_be_blank)
render :edit
else
super
end
end
end
end

View File

@@ -1,65 +0,0 @@
module Spree
class UserRegistrationsController < Devise::RegistrationsController
helper 'spree/base', 'spree/store'
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Common
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::SSL
ssl_required
before_filter :check_permissions, only: [:edit, :update]
skip_before_filter :require_no_authentication
# GET /resource/sign_up
def new
super
@user = resource
end
# POST /resource/sign_up
def create
@user = build_resource(params[:spree_user])
if resource.save
set_flash_message(:notice, :signed_up)
sign_in(:spree_user, @user)
session[:spree_user_signup] = true
associate_user
respond_with resource, location: after_sign_up_path_for(resource)
else
clean_up_passwords(resource)
render :new
end
end
# GET /resource/edit
def edit
super
end
# PUT /resource
def update
super
end
# DELETE /resource
def destroy
super
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
def cancel
super
end
protected
def check_permissions
authorize!(:create, resource)
end
end
end

View File

@@ -1,56 +0,0 @@
module Spree
class UserSessionsController < Devise::SessionsController
helper 'spree/base', 'spree/store'
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Common
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::SSL
ssl_required :new, :create, :destroy, :update
ssl_allowed :login_bar
before_filter :set_checkout_redirect, only: :create
def create
authenticate_spree_user!
if spree_user_signed_in?
respond_to do |format|
format.html {
flash[:success] = t('devise.success.logged_in_succesfully')
redirect_back_or_default(after_sign_in_path_for(spree_current_user))
}
format.js {
render json: { email: spree_current_user.login }, status: :ok
}
end
else
respond_to do |format|
format.html {
flash.now[:error] = t('devise.failure.invalid')
render :new
}
format.js {
render json: { message: t('devise.failure.invalid') }, status: :unauthorized
}
end
end
end
def nav_bar
render partial: 'spree/shared/nav_bar'
end
private
def accurate_title
Spree.t(:login)
end
def redirect_back_or_default(default)
redirect_to(session["spree_user_return_to"] || default)
session["spree_user_return_to"] = nil
end
end
end

View File

@@ -0,0 +1,29 @@
Spree::UserSessionsController.class_eval do
before_filter :set_checkout_redirect, only: :create
def create
authenticate_spree_user!
if spree_user_signed_in?
respond_to do |format|
format.html {
flash[:success] = t(:logged_in_succesfully)
redirect_back_or_default(after_sign_in_path_for(spree_current_user))
}
format.js {
render json: { email: spree_current_user.login }, status: :ok
}
end
else
respond_to do |format|
format.html {
flash.now[:error] = t('devise.failure.invalid')
render :new
}
format.js {
render json: { message: t('devise.failure.invalid') }, status: :unauthorized
}
end
end
end
end

View File

@@ -1,74 +0,0 @@
module Spree
class UsersController < Spree::StoreController
layout 'darkswarm'
ssl_required
skip_before_filter :set_current_order, only: :show
prepend_before_filter :load_object, only: [:show, :edit, :update]
prepend_before_filter :authorize_actions, only: :new
include Spree::Core::ControllerHelpers
include I18nHelper
before_filter :set_locale
before_filter :enable_embedded_shopfront
# Ignores invoice orders, only order where state: 'complete'
def show
@orders = @user.orders.where(state: 'complete').order('completed_at desc')
@unconfirmed_email = spree_current_user.unconfirmed_email
end
# Endpoint for queries to check if a user is already registered
def registered_email
user = Spree.user_class.find_by_email params[:email]
render json: { registered: user.present? }
end
def create
@user = Spree::User.new(params[:user])
if @user.save
if current_order
session[:guest_token] = nil
end
redirect_back_or_default(root_url)
else
render :new
end
end
def update
if @user.update_attributes(params[:user])
if params[:user][:password].present?
# this logic needed b/c devise wants to log us out after password changes
Spree::User.reset_password_by_token(params[:user])
sign_in(@user, event: :authentication,
bypass: true)
end
redirect_to spree.account_url, notice: Spree.t(:account_updated)
else
render :edit
end
end
private
def load_object
@user ||= spree_current_user
if @user
authorize! params[:action].to_sym, @user
else
redirect_to spree.login_path
end
end
def authorize_actions
authorize! params[:action].to_sym, Spree::User.new
end
def accurate_title
Spree.t(:my_account)
end
end
end

View File

@@ -0,0 +1,20 @@
Spree::UsersController.class_eval do
layout 'darkswarm'
include I18nHelper
before_filter :set_locale
before_filter :enable_embedded_shopfront
# Override of spree_auth_devise default
# Ignores invoice orders, only order where state: 'complete'
def show
@orders = @user.orders.where(state: 'complete').order('completed_at desc')
@unconfirmed_email = spree_current_user.unconfirmed_email
end
# Endpoint for queries to check if a user is already registered
def registered_email
user = Spree.user_class.find_by_email params[:email]
render json: { registered: user.present? }
end
end

View File

@@ -8,6 +8,6 @@ module EnterpriseFeesHelper
end
def enterprise_fee_type_options
EnterpriseFee::FEE_TYPES.map { |fee_type| [t("#{fee_type}_fee"), fee_type] }
EnterpriseFee::FEE_TYPES.map { |f| [f.capitalize, f] }
end
end

View File

@@ -1,41 +1,19 @@
require 'open_food_network/enterprise_injection_data'
module InjectionHelper
include SerializerHelper
def inject_enterprises(enterprises = Enterprise.activated.includes(address: :state).all)
inject_json_ams(
"enterprises",
'enterprises',
enterprises,
Api::EnterpriseSerializer,
enterprise_injection_data
)
end
def inject_groups
select_only = required_attributes EnterpriseGroup, Api::GroupListSerializer
inject_json_ams(
"groups",
EnterpriseGroup.on_front_page.by_position.select(select_only).includes(address: :state).all,
Api::GroupListSerializer
)
end
def inject_enterprise_shopfront(enterprise)
inject_json_ams(
"shopfront",
enterprise,
Api::EnterpriseShopfrontSerializer
)
end
def inject_enterprise_shopfront_list
select_only = required_attributes Enterprise, Api::EnterpriseShopfrontListSerializer
inject_json_ams(
"enterprises",
Enterprise.activated.visible.select(select_only).includes(address: :state).all,
'enterprises',
Enterprise.activated.includes(address: :state).all,
Api::EnterpriseShopfrontListSerializer
)
end
@@ -45,12 +23,7 @@ module InjectionHelper
end
def inject_group_enterprises
inject_json_ams(
"enterprises",
@group.enterprises.activated.all,
Api::EnterpriseSerializer,
enterprise_injection_data
)
inject_json_ams "group_enterprises", @group.enterprises.activated.all, Api::EnterpriseSerializer, enterprise_injection_data
end
def inject_current_hub
@@ -106,17 +79,17 @@ module InjectionHelper
end
def inject_saved_credit_cards
data = spree_current_user ? spree_current_user.credit_cards.with_payment_profile.all : []
data = if spree_current_user
spree_current_user.credit_cards.with_payment_profile.all
else
[]
end
inject_json_ams "savedCreditCards", data, Api::CreditCardSerializer
end
def inject_current_user
inject_json_ams "user", spree_current_user, Api::UserSerializer
end
def inject_rails_flash
inject_json_ams "railsFlash", OpenStruct.new(flash.to_hash), Api::RailsFlashSerializer
def inject_json(name, partial, opts = {})
render partial: "json/injection", locals: { name: name, partial: partial }.merge(opts)
end
def inject_json_ams(name, data, serializer, opts = {})

View File

@@ -3,13 +3,4 @@ module SerializerHelper
return [] if ids.blank?
ids.map { |id| { id: id } }
end
# Returns an array of the fields a serializer needs from it's object
# so we can #select only what the serializer will actually use
def required_attributes(model, serializer)
model_attributes = model.attribute_names
serializer_attributes = serializer._attributes.keys.map(&:to_s)
(serializer_attributes & model_attributes).map { |attr| "#{model.table_name}.#{attr}" }
end
end

View File

@@ -2,101 +2,26 @@ module Spree
module Admin
module OrdersHelper
def order_links(order)
@order ||= order
links = []
links << edit_order_link unless action_name == "edit"
links.concat(complete_order_links) if @order.complete?
links << ship_order_link if @order.ready_to_ship?
links << cancel_order_link if @order.can_cancel?
links
end
private
def complete_order_links
[resend_confirmation_link] + invoice_links + ticket_links
end
def invoice_links
return [] unless Spree::Config[:enable_invoices?]
[send_invoice_link, print_invoice_link]
end
def send_invoice_link
if @order.distributor.can_invoice?
send_invoice_link_with_url
else
send_invoice_link_without_url
links << { name: t(:edit_order), url: edit_admin_order_path(order), icon: 'icon-edit' } unless action_name == "edit"
if @order.complete?
links << { name: t(:resend_confirmation), url: resend_admin_order_path(order), icon: 'icon-email', method: 'post', confirm: t(:confirm_resend_order_confirmation) }
if @order.distributor.can_invoice?
links << { name: t(:send_invoice), url: invoice_admin_order_path(order), icon: 'icon-email', confirm: t(:confirm_send_invoice) }
else
links << { name: t(:send_invoice), url: "#", icon: 'icon-email', confirm: t(:must_have_valid_business_number, enterprise_name: order.distributor.name) }
end
links << { name: t(:print_invoice), url: print_admin_order_path(order), icon: 'icon-print', target: "_blank" }
if Spree::Config.enable_receipt_printing?
links << { name: t(:print_ticket), url: print_ticket_admin_order_path(order), icon: 'icon-print', target: "_blank" }
links << { name: t(:select_ticket_printer), url: "#{print_ticket_admin_order_path(order)}#select-printer", icon: 'icon-print', target: "_blank" }
end
end
end
def ticket_links
return [] unless Spree::Config[:enable_receipt_printing?]
[print_ticket_link, select_ticket_printer_link]
end
def edit_order_link
{ name: t(:edit_order),
url: edit_admin_order_path(@order),
icon: 'icon-edit' }
end
def resend_confirmation_link
{ name: t(:resend_confirmation),
url: resend_admin_order_path(@order),
icon: 'icon-email',
method: 'post',
confirm: t(:confirm_resend_order_confirmation) }
end
def send_invoice_link_with_url
{ name: t(:send_invoice),
url: invoice_admin_order_path(@order),
icon: 'icon-email',
confirm: t(:confirm_send_invoice) }
end
def send_invoice_link_without_url
{ name: t(:send_invoice),
url: "#",
icon: 'icon-email',
confirm: t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) }
end
def print_invoice_link
{ name: t(:print_invoice),
url: print_admin_order_path(@order),
icon: 'icon-print',
target: "_blank" }
end
def print_ticket_link
{ name: t(:print_ticket),
url: print_ticket_admin_order_path(@order),
icon: 'icon-print',
target: "_blank" }
end
def select_ticket_printer_link
{ name: t(:select_ticket_printer),
url: "#{print_ticket_admin_order_path(@order)}#select-printer",
icon: 'icon-print',
target: "_blank" }
end
def ship_order_link
{ name: t(:ship_order),
url: fire_admin_order_path(@order, e: 'ship'),
method: 'put',
icon: 'icon-truck',
confirm: t(:are_you_sure) }
end
def cancel_order_link
{ name: t(:cancel_order),
url: fire_admin_order_path(@order.number, e: 'cancel'),
icon: 'icon-trash',
confirm: t(:are_you_sure) }
if @order.ready_to_ship?
links << { name: t(:ship_order), url: fire_admin_order_path(@order, e: 'ship'), method: 'put', icon: 'icon-truck', confirm: t(:are_you_sure) }
end
links << { name: t(:cancel_order), url: fire_admin_order_path(@order.number, e: 'cancel'), icon: 'icon-trash', confirm: t(:are_you_sure) } if order.can_cancel?
links
end
end
end

View File

@@ -1,47 +0,0 @@
# This mailer is configured to be the Devise mailer
# Some methods here override Devise::Mailer methods
module Spree
class UserMailer < BaseMailer
include I18nHelper
# Overrides `Devise::Mailer.reset_password_instructions`
def reset_password_instructions(user)
recipient = user.respond_to?(:id) ? user : Spree.user_class.find(user)
@edit_password_reset_url = spree.
edit_spree_user_password_url(reset_password_token: recipient.reset_password_token)
mail(to: recipient.email, from: from_address,
subject: Spree::Config[:site_name] + ' ' +
I18n.t(:subject, scope: [:devise, :mailer, :reset_password_instructions]))
end
# This is a OFN specific email, not from Devise::Mailer
def signup_confirmation(user)
@user = user
I18n.with_locale valid_locale(@user) do
mail(to: user.email, from: from_address,
subject: t(:welcome_to) + Spree::Config[:site_name])
end
end
# Overrides `Devise::Mailer.confirmation_instructions`
def confirmation_instructions(user, _opts)
@user = user
@instance = Spree::Config[:site_name]
@contact = ContentConfig.footer_email
I18n.with_locale valid_locale(@user) do
subject = t('spree.user_mailer.confirmation_instructions.subject')
mail(to: confirmation_email_address,
from: from_address,
subject: subject)
end
end
private
def confirmation_email_address
@user.pending_reconfirmation? ? @user.unconfirmed_email : @user.email
end
end
end

View File

@@ -0,0 +1,32 @@
Spree::UserMailer.class_eval do
include I18nHelper
def signup_confirmation(user)
@user = user
I18n.with_locale valid_locale(@user) do
mail(to: user.email, from: from_address,
subject: t(:welcome_to) + Spree::Config[:site_name])
end
end
# Overriding `Spree::UserMailer.confirmation_instructions` which is
# overriding `Devise::Mailer.confirmation_instructions`.
def confirmation_instructions(user, _opts)
@user = user
@instance = Spree::Config[:site_name]
@contact = ContentConfig.footer_email
I18n.with_locale valid_locale(@user) do
subject = t('spree.user_mailer.confirmation_instructions.subject')
mail(to: confirmation_email_address,
from: from_address,
subject: subject)
end
end
private
def confirmation_email_address
@user.pending_reconfirmation? ? @user.unconfirmed_email : @user.email
end
end

View File

@@ -48,7 +48,7 @@ class SubscriptionMailer < Spree::BaseMailer
def send_mail(order)
I18n.with_locale valid_locale(order.user) do
confirm_email_subject = t('spree.order_mailer.confirm_email.subject')
confirm_email_subject = t('order_mailer.confirm_email.subject')
subject = "#{Spree::Config[:site_name]} #{confirm_email_subject} ##{order.number}"
mail(to: order.email,
from: from_address,

View File

@@ -1,8 +0,0 @@
module LineItemBasedAdjustmentHandling
extend ActiveSupport::Concern
included do
has_many :adjustments_for_which_source, class_name: "Spree::Adjustment", as: :source,
dependent: :destroy
end
end

View File

@@ -116,9 +116,9 @@ class Enterprise < ActiveRecord::Base
scope :not_ready_for_checkout, lambda {
# When ready_for_checkout is empty, return all rows when there are no enterprises ready for
# checkout.
ready_enterprises = Enterprise.ready_for_checkout.select('enterprises.id')
ready_enterprises = Enterprise.ready_for_checkout
if ready_enterprises.present?
where("enterprises.id NOT IN (?)", ready_enterprises)
where("id NOT IN (?)", ready_enterprises)
else
where("TRUE")
end
@@ -165,14 +165,14 @@ class Enterprise < ActiveRecord::Base
select('DISTINCT enterprises.*')
}
scope :distributing_products, lambda { |product_ids|
scope :distributing_products, lambda { |products|
exchanges = joins("
INNER JOIN exchanges
ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = 'f')
").
joins('INNER JOIN exchange_variants ON (exchange_variants.exchange_id = exchanges.id)').
joins('INNER JOIN spree_variants ON (spree_variants.id = exchange_variants.variant_id)').
where('spree_variants.product_id IN (?)', product_ids).select('DISTINCT enterprises.id')
where('spree_variants.product_id IN (?)', products).select('DISTINCT enterprises.id')
where(id: exchanges)
}
@@ -322,14 +322,6 @@ class Enterprise < ActiveRecord::Base
select('DISTINCT spree_taxons.*')
end
def current_distributed_taxons
Spree::Taxon
.select("DISTINCT spree_taxons.*")
.joins(products: :variants_including_master)
.joins("INNER JOIN (#{current_exchange_variants.to_sql}) \
AS exchange_variants ON spree_variants.id = exchange_variants.variant_id")
end
# Return all taxons for all supplied products
def supplied_taxons
Spree::Taxon.
@@ -375,14 +367,6 @@ class Enterprise < ActiveRecord::Base
private
def current_exchange_variants
ExchangeVariant.joins(exchange: :order_cycle)
.merge(Exchange.outgoing)
.select("DISTINCT exchange_variants.variant_id, exchanges.receiver_id AS enterprise_id")
.where("exchanges.receiver_id = ?", id)
.merge(OrderCycle.active.with_distributor(id))
end
def name_is_unique
dups = Enterprise.where(name: name)
dups = dups.where('id != ?', id) unless new_record?
@@ -465,7 +449,7 @@ class Enterprise < ActiveRecord::Base
end
def touch_distributors
Enterprise.distributing_products(supplied_products.select(:id)).
Enterprise.distributing_products(supplied_products).
where('enterprises.id != ?', id).
find_each(&:touch)
end

View File

@@ -25,8 +25,8 @@ class EnterpriseRelationship < ActiveRecord::Base
where('parent_id IN (?) OR child_id IN (?)', enterprises, enterprises)
}
scope :permitting, ->(enterprise_ids) { where('child_id IN (?)', enterprise_ids) }
scope :permitted_by, ->(enterprise_ids) { where('parent_id IN (?)', enterprise_ids) }
scope :permitting, ->(enterprises) { where('child_id IN (?)', enterprises) }
scope :permitted_by, ->(enterprises) { where('parent_id IN (?)', enterprises) }
scope :with_permission, ->(permission) {
joins(:permissions).

View File

@@ -1,12 +1,3 @@
# Representation of an enterprise being part of an order cycle.
#
# A producer can be part as supplier. The supplier's products can be selected to
# be available in the order cycle (incoming products).
#
# A selling enterprise can be part as distributor. The order cycle then appears
# in its shopfront. Any incoming product can be selected to be shown in the
# shopfront (outgoing products). But the set of shown products can be smaller
# than all incoming products.
class Exchange < ActiveRecord::Base
acts_as_taggable
@@ -35,25 +26,11 @@ class Exchange < ActiveRecord::Base
scope :to_enterprise, lambda { |enterprise| where(receiver_id: enterprise) }
scope :from_enterprises, lambda { |enterprises| where('exchanges.sender_id IN (?)', enterprises) }
scope :to_enterprises, lambda { |enterprises| where('exchanges.receiver_id IN (?)', enterprises) }
scope :involving, lambda { |enterprises|
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).
select('DISTINCT exchanges.*')
}
scope :supplying_to, lambda { |distributor|
where('exchanges.incoming OR exchanges.receiver_id = ?', distributor)
}
scope :with_variant, lambda { |variant|
joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant)
}
scope :with_any_variant, lambda { |variant_ids|
joins(:exchange_variants).
where(exchange_variants: { variant_id: variant_ids }).
select('DISTINCT exchanges.*')
}
scope :with_product, lambda { |product|
joins(:exchange_variants).
where('exchange_variants.variant_id IN (?)', product.variants_including_master)
}
scope :involving, lambda { |enterprises| where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).select('DISTINCT exchanges.*') }
scope :supplying_to, lambda { |distributor| where('exchanges.incoming OR exchanges.receiver_id = ?', distributor) }
scope :with_variant, lambda { |variant| joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant) }
scope :with_any_variant, lambda { |variants| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', variants).select('DISTINCT exchanges.*') }
scope :with_product, lambda { |product| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', product.variants_including_master) }
scope :by_enterprise_name, -> {
joins('INNER JOIN enterprises AS sender ON (sender.id = exchanges.sender_id)').
joins('INNER JOIN enterprises AS receiver ON (receiver.id = exchanges.receiver_id)').
@@ -72,12 +49,11 @@ class Exchange < ActiveRecord::Base
if user.has_spree_role?('admin')
scoped
else
joins("LEFT JOIN enterprises senders ON senders.id = exchanges.sender_id").
joins("LEFT JOIN enterprises receivers ON receivers.id = exchanges.receiver_id").
joins("LEFT JOIN enterprise_roles sender_roles ON sender_roles.enterprise_id = senders.id").
joins("LEFT JOIN enterprise_roles receiver_roles
ON receiver_roles.enterprise_id = receivers.id").
where("sender_roles.user_id = ? AND receiver_roles.user_id = ?", user.id, user.id)
joins('LEFT JOIN enterprises senders ON senders.id = exchanges.sender_id').
joins('LEFT JOIN enterprises receivers ON receivers.id = exchanges.receiver_id').
joins('LEFT JOIN enterprise_roles sender_roles ON sender_roles.enterprise_id = senders.id').
joins('LEFT JOIN enterprise_roles receiver_roles ON receiver_roles.enterprise_id = receivers.id').
where('sender_roles.user_id = ? AND receiver_roles.user_id = ?', user.id, user.id)
end
}
@@ -99,6 +75,20 @@ class Exchange < ActiveRecord::Base
incoming? ? sender : receiver
end
def to_h(core_only = false)
h = attributes.merge('variant_ids' => variant_ids.sort, 'enterprise_fee_ids' => enterprise_fee_ids.sort)
h.reject! { |k| %w(id order_cycle_id created_at updated_at).include? k } if core_only
h
end
def eql?(e)
if e.respond_to? :to_h
to_h(true) == e.to_h(true)
else
super e
end
end
def refresh_products_cache
OpenFoodNetwork::ProductsCache.exchange_changed self
end

View File

@@ -27,30 +27,17 @@ class OrderCycle < ActiveRecord::Base
preference :product_selection_from_coordinator_inventory_only, :boolean, default: false
scope :active, lambda {
where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?',
Time.zone.now,
Time.zone.now)
}
scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.zone.now, Time.zone.now) }
scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) }
scope :inactive, lambda {
where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?',
Time.zone.now,
Time.zone.now)
}
scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) }
scope :upcoming, lambda { where('order_cycles.orders_open_at > ?', Time.zone.now) }
scope :not_closed, lambda {
where('order_cycles.orders_close_at > ? OR order_cycles.orders_close_at IS NULL', Time.zone.now)
}
scope :closed, lambda {
where('order_cycles.orders_close_at < ?',
Time.zone.now).order("order_cycles.orders_close_at DESC")
}
scope :not_closed, lambda { where('order_cycles.orders_close_at > ? OR order_cycles.orders_close_at IS NULL', Time.zone.now) }
scope :closed, lambda { where('order_cycles.orders_close_at < ?', Time.zone.now).order("order_cycles.orders_close_at DESC") }
scope :undated, -> { where('order_cycles.orders_open_at IS NULL OR orders_close_at IS NULL') }
scope :dated, -> { where('orders_open_at IS NOT NULL AND orders_close_at IS NOT NULL') }
scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') }
# This scope returns all the closed orders
# TODO This method returns all the closed orders. So maybe we can replace it with :recently_closed.
scope :most_recently_closed, lambda { closed.order('order_cycles.orders_close_at DESC') }
scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') }
@@ -65,7 +52,7 @@ class OrderCycle < ActiveRecord::Base
if user.has_spree_role?('admin')
scoped
else
where('coordinator_id IN (?)', user.enterprises.map(&:id))
where('coordinator_id IN (?)', user.enterprises)
end
}
@@ -75,17 +62,14 @@ class OrderCycle < ActiveRecord::Base
scoped
else
with_exchanging_enterprises_outer.
where('order_cycles.coordinator_id IN (?) OR enterprises.id IN (?)',
user.enterprises.map(&:id),
user.enterprises.map(&:id)).
where('order_cycles.coordinator_id IN (?) OR enterprises.id IN (?)', user.enterprises, user.enterprises).
select('DISTINCT order_cycles.*')
end
}
scope :with_exchanging_enterprises_outer, lambda {
joins("LEFT OUTER JOIN exchanges ON (exchanges.order_cycle_id = order_cycles.id)").
joins("LEFT OUTER JOIN enterprises
ON (enterprises.id = exchanges.sender_id OR enterprises.id = exchanges.receiver_id)")
joins('LEFT OUTER JOIN exchanges ON (exchanges.order_cycle_id = order_cycles.id)').
joins('LEFT OUTER JOIN enterprises ON (enterprises.id = exchanges.sender_id OR enterprises.id = exchanges.receiver_id)')
}
scope :involving_managed_distributors_of, lambda { |user|
@@ -94,9 +78,7 @@ class OrderCycle < ActiveRecord::Base
# Order cycles where I managed an enterprise at either end of an outgoing exchange
# ie. coordinator or distributor
joins(:exchanges).merge(Exchange.outgoing).
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)',
enterprises.pluck(:id),
enterprises.pluck(:id)).
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).
select('DISTINCT order_cycles.*')
}
@@ -106,9 +88,7 @@ class OrderCycle < ActiveRecord::Base
# Order cycles where I managed an enterprise at either end of an incoming exchange
# ie. coordinator or producer
joins(:exchanges).merge(Exchange.incoming).
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)',
enterprises.pluck(:id),
enterprises.pluck(:id)).
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).
select('DISTINCT order_cycles.*')
}
@@ -133,8 +113,7 @@ class OrderCycle < ActiveRecord::Base
joins(:order_cycle).
merge(OrderCycle.active).
group('exchanges.receiver_id').
select("exchanges.receiver_id AS receiver_id,
MIN(order_cycles.orders_close_at) AS earliest_close_at").
select('exchanges.receiver_id AS receiver_id, MIN(order_cycles.orders_close_at) AS earliest_close_at').
map { |ex| [ex.receiver_id, ex.earliest_close_at.to_time] }
]
end
@@ -144,9 +123,7 @@ class OrderCycle < ActiveRecord::Base
oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name)
oc.orders_open_at = oc.orders_close_at = nil
oc.coordinator_fee_ids = coordinator_fee_ids
# rubocop:disable Metrics/LineLength
oc.preferred_product_selection_from_coordinator_inventory_only = preferred_product_selection_from_coordinator_inventory_only
# rubocop:enable Metrics/LineLength
oc.save!
exchanges.each { |e| e.clone!(oc) }
oc.reload
@@ -240,7 +217,7 @@ class OrderCycle < ActiveRecord::Base
end
def exchanges_supplying(order)
exchanges.supplying_to(order.distributor).with_any_variant(order.variants.map(&:id))
exchanges.supplying_to(order.distributor).with_any_variant(order.variants)
end
def coordinated_by?(user)
@@ -252,12 +229,8 @@ class OrderCycle < ActiveRecord::Base
end
def items_bought_by_user(user, distributor)
# The Spree::Order.complete scope only checks for completed_at date
# it does not ensure state is "complete"
orders = Spree::Order.complete.where(state: "complete",
user_id: user,
distributor_id: distributor,
order_cycle_id: self)
# The Spree::Order.complete scope only checks for completed_at date, does not ensure state is "complete"
orders = Spree::Order.complete.where(state: "complete", user_id: user, distributor_id: distributor, order_cycle_id: self)
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
items = Spree::LineItem.joins(:order).merge(orders)
items.each { |li| scoper.scope(li.variant) }

View File

@@ -4,11 +4,7 @@
module ProductImport
class EntryValidator
SKIP_VALIDATE_ON_UPDATE = [:description].freeze
# rubocop:disable Metrics/ParameterLists
def initialize(current_user, import_time, spreadsheet_data, editable_enterprises,
inventory_permissions, reset_counts, import_settings, all_entries)
def initialize(current_user, import_time, spreadsheet_data, editable_enterprises, inventory_permissions, reset_counts, import_settings)
@current_user = current_user
@import_time = import_time
@spreadsheet_data = spreadsheet_data
@@ -16,9 +12,7 @@ module ProductImport
@inventory_permissions = inventory_permissions
@reset_counts = reset_counts
@import_settings = import_settings
@all_entries = all_entries
end
# rubocop:enable Metrics/ParameterLists
def self.non_updatable_fields
{
@@ -36,7 +30,6 @@ module ProductImport
assign_enterprise_field(entry)
enterprise_validation(entry)
unit_fields_validation(entry)
variant_of_product_validation(entry)
next if entry.enterprise_id.blank?
@@ -165,27 +158,6 @@ module ProductImport
mark_as_invalid(entry, attribute: 'variant_unit_name', error: I18n.t('admin.product_import.model.conditional_blank')) unless entry.variant_unit_name && entry.variant_unit_name.present?
end
def variant_of_product_validation(entry)
return if entry.producer.blank? || entry.name.blank?
validate_unit_type_unchanged(entry)
validate_variant_unit_name_unchanged(entry)
end
def validate_unit_type_unchanged(entry)
return if entry.unit_type.blank?
reference_entry = all_entries_for_product(entry).first
return if entry.unit_type.to_s == reference_entry.unit_type.to_s
mark_as_not_updatable(entry, "unit_type")
end
def validate_variant_unit_name_unchanged(entry)
return if entry.variant_unit_name.blank?
reference_entry = all_entries_for_product(entry).first
return if entry.variant_unit_name.to_s == reference_entry.variant_unit_name.to_s
mark_as_not_updatable(entry, "variant_unit_name")
end
def producer_validation(entry)
producer_name = entry.producer
@@ -316,7 +288,6 @@ module ProductImport
def product_field_errors(entry, existing_product)
EntryValidator.non_updatable_fields.each do |display_name, attribute|
next if attributes_match?(attribute, existing_product, entry) || attributes_blank?(attribute, existing_product, entry)
next if ignore_when_updating_product?(attribute)
mark_as_invalid(entry, attribute: display_name, error: I18n.t('admin.product_import.model.not_updatable'))
end
end
@@ -327,10 +298,6 @@ module ProductImport
existing_product_value == convert_to_trusted_type(entry_value, existing_product_value)
end
def ignore_when_updating_product?(attribute)
SKIP_VALIDATE_ON_UPDATE.include? attribute
end
def convert_to_trusted_type(untrusted_attribute, trusted_attribute)
case trusted_attribute
when Integer
@@ -365,11 +332,6 @@ module ProductImport
entry.product_validations = options[:product_validations] if options[:product_validations]
end
def mark_as_not_updatable(entry, attribute)
mark_as_invalid(entry, attribute: attribute,
error: I18n.t("admin.product_import.model.not_updatable"))
end
def import_into_inventory?
@import_settings[:settings].andand['import_into'] == 'inventories'
end
@@ -424,19 +386,5 @@ module ProductImport
object.on_demand = object.count_on_hand.blank? if entry.on_demand.blank?
entry.on_hand_nil = object.count_on_hand.blank?
end
def all_entries_for_product(entry)
all_entries_by_product[entries_by_product_key(entry)]
end
def all_entries_by_product
@all_entries_by_product ||= @all_entries.group_by do |entry|
entries_by_product_key(entry)
end
end
def entries_by_product_key(entry)
[entry.producer.to_s, entry.name.to_s]
end
end
end

View File

@@ -13,7 +13,7 @@ module ProductImport
include ActiveModel::Conversion
include ActiveModel::Validations
attr_reader :entries, :updated_ids, :import_settings
attr_reader :updated_ids, :import_settings
def initialize(file, current_user, import_settings = {})
unless file.is_a?(File)
@@ -90,6 +90,10 @@ module ProductImport
@processor.total_enterprise_products
end
def all_entries
@entries
end
def entries_json
entries = {}
@entries.each do |entry|
@@ -180,11 +184,8 @@ module ProductImport
end
@spreadsheet_data = SpreadsheetData.new(@entries, @import_settings)
@validator = EntryValidator.new(@current_user, @import_time, @spreadsheet_data,
@editable_enterprises, @inventory_permissions, @reset_counts,
@import_settings, build_all_entries)
@processor = EntryProcessor.new(self, @validator, @import_settings, @spreadsheet_data,
@editable_enterprises, @import_time, @updated_ids)
@validator = EntryValidator.new(@current_user, @import_time, @spreadsheet_data, @editable_enterprises, @inventory_permissions, @reset_counts, @import_settings)
@processor = EntryProcessor.new(self, @validator, @import_settings, @spreadsheet_data, @editable_enterprises, @import_time, @updated_ids)
@processor.count_existing_items unless staged_import?
end
@@ -237,22 +238,28 @@ module ProductImport
end
def build_entries_in_range
# In the JS, start and end are calculated like this:
# start = (batchIndex * $scope.batchSize) + 1
# end = (batchIndex + 1) * $scope.batchSize
start_data_index = @import_settings[:start] - 1
end_data_index = @import_settings[:end] - 1
start_line = @import_settings[:start]
end_line = @import_settings[:end]
data_rows = rows[start_data_index..end_data_index]
@entries = build_entries_from_rows(data_rows, start_data_index)
(start_line..end_line).each do |i|
line_number = i + 1
row = @sheet.row(line_number)
row_data = Hash[[headers, row].transpose]
entry = SpreadsheetEntry.new(row_data)
entry.line_number = line_number
@entries.push entry
break if @sheet.last_row == line_number
end
end
def build_entries
@entries = build_entries_from_rows(rows)
end
def build_all_entries
build_entries_from_rows(rows)
rows.each_with_index do |row, i|
row_data = Hash[[headers, row].transpose]
entry = SpreadsheetEntry.new(row_data)
entry.line_number = i + 2
@entries.push entry
end
@entries
end
def save_all_valid
@@ -265,14 +272,5 @@ module ProductImport
return unless @file.path == Rails.root.join('tmp', 'product_import').to_s
File.delete(@file)
end
def build_entries_from_rows(rows, offset = 0)
rows.each_with_index.inject([]) do |entries, (row, i)|
row_data = Hash[[headers, row].transpose]
entry = SpreadsheetEntry.new(row_data)
entry.line_number = offset + i + 2
entries.push entry
end
end
end
end

View File

@@ -29,7 +29,6 @@ Spree::AppConfiguration.class_eval do
preference :matomo_site_id, :string, default: nil
# Invoices & Receipts
preference :enable_invoices?, :boolean, default: true
preference :invoice_style2?, :boolean, default: false
preference :enable_receipt_printing?, :boolean, default: false

View File

@@ -9,7 +9,7 @@ module Spree
[object]
elsif object.respond_to? :line_items
object.line_items
elsif object.respond_to?(:order) && object.order.present?
elsif object.order.present?
object.order.line_items
else
[object]

View File

@@ -15,7 +15,7 @@ Spree::CreditCard.class_eval do
belongs_to :user
after_create :ensure_single_default_card
after_save :ensure_single_default_card, if: :default_card_needs_updating?
after_save :ensure_single_default_card, if: :is_default_changed?
# Allows us to use a gateway_payment_profile_id to store Stripe Tokens
# Should be able to remove once we reach Spree v2.2.0
@@ -31,21 +31,13 @@ Spree::CreditCard.class_eval do
private
def reusable?
gateway_customer_profile_id.present?
end
def default_missing?
!user.credit_cards.exists?(is_default: true)
end
def default_card_needs_updating?
is_default_changed? || gateway_customer_profile_id_changed?
end
def ensure_single_default_card
return unless user
return unless is_default? || (reusable? && default_missing?)
return unless is_default? || default_missing?
user.credit_cards.update_all(['is_default=(id=?)', id])
self.is_default = true
end

Some files were not shown because too many files have changed in this diff Show More