Compare commits

...

95 Commits

Author SHA1 Message Date
Maikel Linke
20cabe6f12 Update from Transifex 2019-10-17 17:11:10 +11:00
Maikel
fe849b8dfd Merge pull request #4367 from openfoodfoundation/transifex
Transifex
2019-10-17 17:07:23 +11:00
Luis Ramos
167b44c30f Merge pull request #4365 from kristinalim/fix/4308-eager_load_associations_in_orders_and_fulfillment_reports
4308 Eager-load associations in Orders and Fulfillment reports
2019-10-16 21:39:30 +01:00
Luis Ramos
ad247e2116 Merge pull request #4268 from luisramos0/add_order_endpoint
Add api/orders/{order_number} API endpoint
2019-10-16 21:22:27 +01:00
Luis Ramos
e46875376e Merge pull request #4168 from mkllnk/4160-product-report-filters
4160 Fix product report filters
2019-10-16 14:47:55 +01:00
Luis Ramos
d7223c508a Merge pull request #4289 from luisramos0/spree_be_config_general
Move general_settings, mail_methods and image_settings from spree_backend to OFN
2019-10-16 12:08:36 +01:00
Kristina Lim
a91a23aa39 Eager-load associations for Orders and Fulfillment report subtypes 2019-10-16 19:06:35 +08:00
Luis Ramos
370f66e36b Merge pull request #4364 from kristinalim/refactor/4308-refactor_orders_and_fulfillment_report_types
4308 Refactor Orders and Fulfillment report subtypes
2019-10-16 10:30:24 +01:00
Luis Ramos
f7cdcf02e0 Merge pull request #4343 from openfoodfoundation/dependabot/bundler/oauth2-1.4.2
Bump oauth2 from 1.4.1 to 1.4.2
2019-10-16 10:29:25 +01:00
Pau Pérez Fabregat
1ee43fb495 Merge pull request #4358 from openfoodfoundation/dependabot/bundler/rspec-rails-3.9.0
Bump rspec-rails from 3.8.2 to 3.9.0
2019-10-15 10:25:23 +02:00
Transifex-Openfoodnetwork
e139d24639 Updating translations for config/locales/en_CA.yml 2019-10-15 12:06:18 +11:00
Luis Ramos
0dfe749496 Merge pull request #4356 from kristinalim/fix/4256-set_dalli_value_max_size
4256 Configure dalli store value max bytes from env variable
2019-10-14 18:41:00 +01:00
Luis Ramos
d6eade6fa3 Merge pull request #4363 from jonleighton/fix-docker-setup
Fix error during first run of docker-compose
2019-10-14 13:38:04 +01:00
Luis Ramos
109aed684e Merge pull request #4344 from openfoodfoundation/dependabot/bundler/ddtrace-0.28.0
Bump ddtrace from 0.27.0 to 0.28.0
2019-10-14 12:39:52 +01:00
Luis Ramos
35d76ac991 Merge pull request #4298 from luisramos0/variant_edit
Make unit description visible in the variant edit page even for products which variant_unit is items
2019-10-13 19:54:41 +01:00
Luis Ramos
1ab9e9d7a5 Merge pull request #4299 from luisramos0/drop_order_ship_method_id
Remove unused field orders.shipping_method_id
2019-10-13 19:54:13 +01:00
Luis Ramos
e5e716e150 Merge pull request #4336 from openfoodfoundation/dependabot/bundler/rubyzip-1.3.0
[Security] Bump rubyzip from 1.2.2 to 1.3.0
2019-10-13 19:53:48 +01:00
Luis Ramos
b6510c3ab6 Merge pull request #4262 from daningenthron/daningenthron/default-shipping-category
Prefill shipping category forms with default values
2019-10-13 19:52:43 +01:00
Transifex-Openfoodnetwork
a158a986c6 Updating translations for config/locales/en_US.yml 2019-10-11 13:06:17 +11:00
Kristina Lim
c7643db66f Remove unnecessary Orders and Fulfillment lines in .rubocop_manual_todo.yml 2019-10-10 21:05:06 +08:00
Kristina Lim
eb8c22aa06 Disable violated cops in Order and Fulfillment report subtypes 2019-10-10 21:01:46 +08:00
Kristina Lim
d0656485d7 Make report_klass in OrdersAndFulfillmentsReport compact 2019-10-10 21:01:46 +08:00
Kristina Lim
640cc1b6d3 Address Metrics/LineLength in Orders and Fulfillments reports 2019-10-10 21:01:45 +08:00
Kristina Lim
d65d17a9f3 Freeze report type string in Order and Fulfillment reports 2019-10-10 21:01:45 +08:00
Kristina Lim
bbea00e431 Delegate methods in Orders and Fulfillment report to report object 2019-10-10 21:01:45 +08:00
Kristina Lim
3cecba70e8 Refactor and memoize report object in Orders and Fulfillment report 2019-10-10 21:01:44 +08:00
Kristina Lim
8bbff09066 Refactor report class in Orders and Fulfillment report 2019-10-10 21:01:44 +08:00
Kristina Lim
a7a89d7ccb Add attr reader for report type in Orders and Fulfillment report 2019-10-10 21:01:44 +08:00
Kristina Lim
3ccf76ff5f Add smoke test for Customer Totals report 2019-10-10 21:01:34 +08:00
Kristina Lim
6004208496 Move specific logic for Customer Totals report to class 2019-10-10 20:05:35 +08:00
Kristina Lim
3ce9c712cf Add smoke test for Distributor Totals by Supplier report 2019-10-10 20:05:35 +08:00
Kristina Lim
bfb0032fd2 Move specific logic for Distributor Totals by Supplier report to class 2019-10-10 20:05:29 +08:00
Kristina Lim
fe37516ead Add smoke test for Supplier Totals by Distributor report 2019-10-10 19:58:49 +08:00
Kristina Lim
289b75e143 Move specific logic for Supplier Totals by Distributor report to class 2019-10-10 19:57:41 +08:00
Kristina Lim
09f0f8c33f Move "order_cycle_supplier_totals" report type to constant 2019-10-10 19:54:29 +08:00
Kristina Lim
8bc1718978 Add smoke test for Supplier Totals report 2019-10-10 19:50:28 +08:00
Kristina Lim
cbfce69a6d Move specific logic for Supplier Totals report to class 2019-10-10 19:48:59 +08:00
Kristina Lim
4e0ecdd44c Rename params in OrdersAndFulfillmentsReport to options 2019-10-10 15:57:51 +08:00
Jon Leighton
23ab9a4bed Fix error during first run of docker-compose
Prior to this commit, the db container would create a database named
“ofn” (the same as $POSTGRES_USER).

Then, when the web container started, it would run `rake db:reset`. This
would load the Rails environment, which ends up requiring some model
files, which eventually end up trying to connect to the
“open_food_network_dev” database, which doesn’t exist. Therefore setting
up the database fails, and it’s impossible to boot the web container.

As a side note, I’m not convinced that bootstrapping the database as
part of the container’s command is the best strategy (if for no other
reason that this will wipe my database every time I run `docker-compose
up`). But this commit doesn’t change that.

What it does is add the $POSTGRES_DB environment variable so that the db
container creates the “open_food_network_dev” database (which is blank).
Then, when `rake db:reset` runs, it’ll successfully connect to this
(empty) database while loading the environment, before deleting and recreating it.

Note that I had to manually delete the `openfoodnetwork_postgres` volume
in order to reset my local state, after making this change.
2019-10-10 12:17:22 +11:00
Maikel Linke
f623446e3e Avoid additional query in inventory reports 2019-10-10 11:11:14 +11:00
Maikel Linke
6944fe1e46 Make order cycle filter chainable with other filters 2019-10-10 10:06:36 +11:00
Maikel Linke
7c9e3d7f06 Spec combination of all variant filters 2019-10-10 10:06:36 +11:00
Dan Ingenthron
bdcadf9fc6 Update changes to reflect new non-Spree shipping method controller 2019-10-08 18:37:16 -05:00
Dan Ingenthron
9b7139fd45 Add default shipping category during object creation; revert forms 2019-10-08 18:37:16 -05:00
Dan Ingenthron
543e275d2e Add custom shipping category to pass package spec 2019-10-08 18:37:16 -05:00
Dan Ingenthron
560fa6b949 Update shipping category factory with default 2019-10-08 18:37:16 -05:00
Dan Ingenthron
dbf34da87b Rubocop fixes 2019-10-08 18:37:16 -05:00
Dan Ingenthron
d6022062e1 Use default for create product spec; auto-fill field 2019-10-08 18:37:16 -05:00
Dan Ingenthron
b082d3301b Add prechecked category to shipping method spec 2019-10-08 18:37:16 -05:00
Dan Ingenthron
48cd542138 Service spec 2019-10-08 18:37:15 -05:00
Dan Ingenthron
e2d341c9c2 Add default category to seeds 2019-10-08 18:37:15 -05:00
Dan Ingenthron
89873a2640 Add and auto-check default category in shipping method create 2019-10-08 18:37:15 -05:00
Dan Ingenthron
b4be2cc2d4 Add default shipping category service and update create product form 2019-10-08 18:37:15 -05:00
Dan Ingenthron
35f89a9750 Update spec to prefill shipping category in Create form 2019-10-08 18:37:15 -05:00
Dan Ingenthron
592a53b6f5 Remove blank option from shipping category dropdown 2019-10-08 18:37:15 -05:00
dependabot-preview[bot]
eab9d42eb0 Bump rspec-rails from 3.8.2 to 3.9.0
Bumps [rspec-rails](https://github.com/rspec/rspec-rails) from 3.8.2 to 3.9.0.
- [Release notes](https://github.com/rspec/rspec-rails/releases)
- [Changelog](https://github.com/rspec/rspec-rails/blob/master/Changelog.md)
- [Commits](https://github.com/rspec/rspec-rails/compare/v3.8.2...v3.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-08 20:56:10 +00:00
Kristina Lim
cf05369ca9 Configure dalli store value max bytes from env variable 2019-10-08 17:58:43 +08:00
luisramos0
a2bc61cb4d Remove unused field orders.shipping_method_id 2019-10-08 09:09:19 +01:00
luisramos0
c5a17bcde0 Fix rubocop issues in feature specs related to configuration 2019-10-02 21:26:10 +01:00
luisramos0
b712ec7f13 Transpec feature specs brought from spre_backend 2019-10-02 21:26:09 +01:00
luisramos0
32a7f13dd2 Bring feature specs for configuration pages from spree_backend 2019-10-02 21:26:09 +01:00
luisramos0
713769b497 Fix rubocop issues in mail_methods_controller_spec 2019-10-02 21:26:09 +01:00
luisramos0
495de37620 Fix more rubocop issues in image_settings_controller_spec 2019-10-02 21:26:09 +01:00
luisramos0
05d24cf11a Transpec image_setting_controller_spec 2019-10-02 21:26:09 +01:00
luisramos0
edd84530af Fix simle rubocopo issues in image_settings ctrl spec 2019-10-02 21:26:09 +01:00
luisramos0
6677543de0 bring 2 specs from spree_backend to cover image_settings page and mail_methods page 2019-10-02 21:26:09 +01:00
luisramos0
30aa31252b Fix simple rubocop issues in helpers 2019-10-02 21:26:09 +01:00
luisramos0
b83d74a609 Bring general_settings_helper from spree_backend 2019-10-02 21:26:09 +01:00
luisramos0
d31b50be3d Bring spree/admin configuration routes to ofn 2019-10-02 21:26:09 +01:00
luisramos0
42e3f2f2f4 Convert spree/admin/mail_methods from erb to haml 2019-10-02 21:25:23 +01:00
luisramos0
96737da128 Add spree_backend mail methods views that are missing in ofn 2019-10-02 21:25:23 +01:00
luisramos0
7f9f0d840c Merge decorators into controllers and remove decorators 2019-10-02 21:25:23 +01:00
luisramos0
686840e262 Fix basic rubocop issues in recently added controllers from spree_backend 2019-10-02 21:25:23 +01:00
luisramos0
2377b833ee Bring general settings and image settings controllers that are overrides in ofn to ofn so we can merge them with their decorators in a second step 2019-10-02 21:25:23 +01:00
luisramos0
3f3c33bce6 Fix basic rubocop issues in newly added controller 2019-10-02 21:25:23 +01:00
luisramos0
e35eff95bb Add mail_methods controller from spree_backend related to config 2019-10-02 21:25:23 +01:00
dependabot-preview[bot]
de9476a8a5 Bump ddtrace from 0.27.0 to 0.28.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.27.0 to 0.28.0.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.27.0...v0.28.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-01 21:12:18 +00:00
dependabot-preview[bot]
bce41a2247 Bump oauth2 from 1.4.1 to 1.4.2
Bumps [oauth2](https://github.com/oauth-xx/oauth2) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/oauth-xx/oauth2/releases)
- [Changelog](https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/oauth-xx/oauth2/compare/v1.4.1...v1.4.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-01 21:04:45 +00:00
dependabot-preview[bot]
36883bc051 [Security] Bump rubyzip from 1.2.2 to 1.3.0
Bumps [rubyzip](https://github.com/rubyzip/rubyzip) from 1.2.2 to 1.3.0. **This update includes a security fix.**
- [Release notes](https://github.com/rubyzip/rubyzip/releases)
- [Changelog](https://github.com/rubyzip/rubyzip/blob/master/Changelog.md)
- [Commits](https://github.com/rubyzip/rubyzip/compare/v1.2.2...v1.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-01 09:55:53 +00:00
luisramos0
50731e929e Remove some attributes from test as they are already verified subsequently 2019-09-25 09:54:33 +01:00
luisramos0
baa09b88f7 Fix issue with nil current_order where shipping_method serializer requires a current_order to calculate the shipping fees 2019-09-25 09:52:10 +01:00
luisramos0
2f60a85593 Improve spec/controllers/api/orders_controller_spec, make it more readable 2019-09-23 23:50:42 +01:00
luisramos0
78cf35807a Improve preloading of order query to avoid N+1 queries 2019-09-23 23:50:39 +01:00
luisramos0
eb85dccac1 Remove single letter variable names 2019-09-22 21:07:32 +01:00
luisramos0
f32454b404 Add feature spec to validate unit_description is editable for products with unit items, regression for #3649 2019-09-22 21:02:32 +01:00
luisramos0
591efecde6 Make unit description field visible in the variant edit page even for products which variant_unit is items 2019-09-22 16:42:03 +01:00
luisramos0
24afa21885 Revert "Change products controller to clear variants unit description if variant_unit is items"
This reverts commit 1a4e83d633.
2019-09-22 16:31:26 +01:00
luisramos0
bdb3dd5aaf Fix long lines in app/serializers 2019-09-19 16:32:09 +01:00
luisramos0
25fbab2e37 Use memoized order method 2019-09-19 16:32:09 +01:00
luisramos0
4d37aaac64 Use have_http_status and remove check for error message, that's something for the base_controller test to test 2019-09-19 16:32:09 +01:00
luisramos0
19e28cb14a Make spec/controllers/api/orders_controller_spec more simple assuming adjustments will always come in the same order 2019-09-19 16:32:09 +01:00
luisramos0
a44a251d96 Remove duplicated attributes tag from all serializers to create consistency 2019-09-19 16:32:09 +01:00
luisramos0
2921ee19e1 Add api/order/{order_number} ednpoint and its new order detailed serializer 2019-09-19 16:32:09 +01:00
luisramos0
6796d91a07 Add some basic attributes to address and order serializers that will be used in the order show api endpoint 2019-09-19 16:32:09 +01:00
luisramos0
69afcf7510 Improve readability in order permissions 2019-09-19 16:32:09 +01:00
78 changed files with 1596 additions and 538 deletions

View File

@@ -110,18 +110,6 @@ Metrics/LineLength:
- app/models/variant_override.rb
- app/models/variant_override_set.rb
- app/overrides/add_enterprise_fees_to_admin_configurations_menu.rb
- app/serializers/api/admin/basic_enterprise_serializer.rb
- app/serializers/api/admin/enterprise_fee_serializer.rb
- app/serializers/api/admin/enterprise_serializer.rb
- app/serializers/api/admin/exchange_serializer.rb
- app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb
- app/serializers/api/admin/index_enterprise_serializer.rb
- app/serializers/api/admin/index_order_cycle_serializer.rb
- app/serializers/api/admin/line_item_serializer.rb
- app/serializers/api/admin/order_cycle_serializer.rb
- app/serializers/api/admin/subscription_serializer.rb
- app/serializers/api/admin/tag_rule_serializer.rb
- app/serializers/api/admin/variant_override_serializer.rb
- app/services/cart_service.rb
- app/services/default_stock_location.rb
- app/services/embedded_page_service.rb
@@ -148,7 +136,6 @@ Metrics/LineLength:
- lib/open_food_network/order_cycle_form_applicator.rb
- lib/open_food_network/order_cycle_management_report.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/products_cache.rb
@@ -454,7 +441,6 @@ Metrics/AbcSize:
- 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/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
@@ -532,7 +518,6 @@ Metrics/CyclomaticComplexity:
- lib/discourse/single_sign_on.rb
- lib/open_food_network/bulk_coop_report.rb
- lib/open_food_network/enterprise_issue_validator.rb
- lib/open_food_network/orders_and_fulfillments_report.rb
- lib/spree/core/controller_helpers/order_decorator.rb
- lib/spree/core/controller_helpers/respond_with_decorator.rb
- lib/spree/localized_number.rb
@@ -557,7 +542,6 @@ Metrics/PerceivedComplexity:
- lib/discourse/single_sign_on.rb
- lib/open_food_network/bulk_coop_report.rb
- lib/open_food_network/enterprise_issue_validator.rb
- lib/open_food_network/orders_and_fulfillments_report.rb
- lib/spree/core/controller_helpers/order_decorator.rb
- lib/spree/core/controller_helpers/respond_with_decorator.rb
- lib/spree/localized_number.rb
@@ -627,7 +611,6 @@ Metrics/MethodLength:
- 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/packing_report.rb
- lib/open_food_network/payments_report.rb
- lib/open_food_network/permissions.rb
@@ -671,7 +654,6 @@ Metrics/ClassLength:
- 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/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

View File

@@ -39,7 +39,7 @@ gem 'activemerchant', '~> 1.78'
gem 'devise', '~> 2.2.5'
gem 'devise-encryptable', '0.2.0'
gem 'jwt', '~> 2.2'
gem 'oauth2', '~> 1.4.1' # Used for Stripe Connect
gem 'oauth2', '~> 1.4.2' # Used for Stripe Connect
gem 'daemons'
gem 'delayed_job_active_record'

View File

@@ -223,7 +223,7 @@ GEM
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.27.0)
ddtrace (0.28.0)
msgpack
debugger-linecache (1.2.0)
deface (1.0.2)
@@ -491,8 +491,8 @@ GEM
newrelic_rpm (3.18.1.330)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
oauth2 (1.4.1)
faraday (>= 0.8, < 0.16.0)
oauth2 (1.4.2)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
@@ -596,29 +596,29 @@ GEM
nokogiri
roo (>= 2.0.0beta1, < 3)
spreadsheet (> 0.9.0)
rspec (3.8.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-core (3.8.0)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.2)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.0)
rspec-support (~> 3.9.0)
rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-rails (3.8.2)
rspec-support (~> 3.9.0)
rspec-rails (3.9.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-support (~> 3.9.0)
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-support (3.8.0)
rspec-support (3.9.0)
rubocop (0.57.2)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
@@ -630,7 +630,7 @@ GEM
ruby-ole (1.2.12.1)
ruby-progressbar (1.10.1)
ruby-rc4 (0.1.5)
rubyzip (1.2.2)
rubyzip (1.3.0)
safe_yaml (1.0.5)
sass (3.3.14)
sass-rails (3.2.6)
@@ -777,7 +777,7 @@ DEPENDENCIES
momentjs-rails
newrelic_rpm (~> 3.0)
nokogiri (>= 1.6.7.1)
oauth2 (~> 1.4.1)
oauth2 (~> 1.4.2)
ofn-qz!
oj
order_management!

View File

@@ -1,5 +1,10 @@
module Api
class OrdersController < BaseController
def show
authorize! :read, order
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
end
def index
authorize! :admin, Spree::Order
@@ -19,5 +24,12 @@ module Api
each_serializer: Api::Admin::OrderSerializer
)
end
def order
@order ||= Spree::Order.
where(number: params[:id]).
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
first!
end
end
end

View File

@@ -0,0 +1,35 @@
module Spree
module Admin
class GeneralSettingsController < Spree::Admin::BaseController
def edit
@preferences_general = [:site_name, :default_seo_title, :default_meta_keywords,
:default_meta_description, :site_url, :bugherd_api_key]
@preferences_security = [:allow_ssl_in_production,
:allow_ssl_in_staging, :allow_ssl_in_development_and_test,
:check_for_spree_alerts]
@preferences_currency = [:display_currency, :hide_cents]
end
def update
params.each do |name, value|
next unless Spree::Config.has_preference? name
Spree::Config[name] = value
end
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:general_settings))
redirect_to edit_admin_general_settings_path
end
def dismiss_alert
return unless request.xhr? && params[:alert_id]
dismissed = Spree::Config[:dismissed_spree_alerts] || ''
Spree::Config.set(dismissed_spree_alerts: dismissed.
split(',').
push(params[:alert_id]).
join(','))
filter_dismissed_alerts
render nothing: true
end
end
end
end

View File

@@ -1,14 +0,0 @@
module Spree
module Admin
GeneralSettingsController.class_eval do
end
module GeneralSettingsEditPreferences
def edit
super
@preferences_general << :bugherd_api_key
end
end
GeneralSettingsController.prepend(GeneralSettingsEditPreferences)
end
end

View File

@@ -0,0 +1,79 @@
module Spree
module Admin
class ImageSettingsController < Spree::Admin::BaseController
def edit
@styles = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
@headers = ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
end
def update
update_styles(params)
update_headers(params) if Spree::Config[:use_s3]
Spree::Config.set(params[:preferences])
update_paperclip_settings
respond_to do |format|
format.html {
flash[:success] = Spree.t(:image_settings_updated)
redirect_to spree.edit_admin_image_settings_path
}
end
end
private
def update_styles(params)
if params[:new_attachment_styles].present?
params[:new_attachment_styles].each do |_index, style|
params[:attachment_styles][style[:name]] = style[:value] unless style[:value].empty?
end
end
styles = params[:attachment_styles]
Spree::Config[:attachment_styles] = ActiveSupport::JSON.encode(styles) unless styles.nil?
end
def update_headers(params)
if params[:new_s3_headers].present?
params[:new_s3_headers].each do |_index, header|
params[:s3_headers][header[:name]] = header[:value] unless header[:value].empty?
end
end
headers = params[:s3_headers]
Spree::Config[:s3_headers] = ActiveSupport::JSON.encode(headers) unless headers.nil?
end
def update_paperclip_settings
if Spree::Config[:use_s3]
s3_creds = { access_key_id: Spree::Config[:s3_access_key],
secret_access_key: Spree::Config[:s3_secret],
bucket: Spree::Config[:s3_bucket] }
Spree::Image.attachment_definitions[:attachment][:storage] = :s3
Spree::Image.attachment_definitions[:attachment][:s3_credentials] = s3_creds
Spree::Image.attachment_definitions[:attachment][:s3_headers] =
ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
Spree::Image.attachment_definitions[:attachment][:bucket] = Spree::Config[:s3_bucket]
else
Spree::Image.attachment_definitions[:attachment].delete :storage
end
Spree::Image.attachment_definitions[:attachment][:styles] =
ActiveSupport::JSON.decode(Spree::Config[:attachment_styles]).symbolize_keys!
Spree::Image.attachment_definitions[:attachment][:path] = Spree::Config[:attachment_path]
Spree::Image.attachment_definitions[:attachment][:default_url] =
Spree::Config[:attachment_default_url]
Spree::Image.attachment_definitions[:attachment][:default_style] =
Spree::Config[:attachment_default_style]
# Spree stores attachent definitions in JSON. This converts the style name and format to
# strings. However, when paperclip encounters these, it doesn't recognise the format.
# Here we solve that problem by converting format and style name to symbols.
Spree::Image.reformat_styles
end
end
end
end

View File

@@ -1,11 +0,0 @@
Spree::Admin::ImageSettingsController.class_eval do
# Spree stores attachent definitions in JSON. This converts the style name and format to
# strings. However, when paperclip encounters these, it doesn't recognise the format.
# Here we solve that problem by converting format and style name to symbols.
def update_paperclip_settings_with_format_styles
update_paperclip_settings_without_format_styles
Spree::Image.reformat_styles
end
alias_method_chain :update_paperclip_settings, :format_styles
end

View File

@@ -0,0 +1,39 @@
module Spree
module Admin
class MailMethodsController < Spree::Admin::BaseController
after_filter :initialize_mail_settings
def update
if params[:smtp_password].blank?
params.delete(:smtp_password)
end
params.each do |name, value|
next unless Spree::Config.has_preference? name
Spree::Config[name] = value
end
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:mail_methods))
render :edit
end
def testmail
if TestMailer.test_email(try_spree_current_user).deliver
flash[:success] = Spree.t('admin.mail_methods.testmail.delivery_success')
else
flash[:error] = Spree.t('admin.mail_methods.testmail.delivery_error')
end
rescue StandardError => e
flash[:error] = Spree.t('admin.mail_methods.testmail.error') % { e: e }
ensure
redirect_to edit_admin_mail_method_url
end
private
def initialize_mail_settings
Spree::Core::MailSettings.init
end
end
end
end

View File

@@ -30,6 +30,11 @@ Spree::Admin::ProductsController.class_eval do
@show_latest_import = params[:latest_import] || false
end
def new
@object.shipping_category = DefaultShippingCategory.find_or_create
super
end
def create
delete_stock_params_and_set_after do
super
@@ -44,8 +49,6 @@ Spree::Admin::ProductsController.class_eval do
delete_stock_params_and_set_after do
super
end
clear_variants_unit_description if @object.variant_unit == 'items'
end
def bulk_update
@@ -190,10 +193,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

@@ -19,6 +19,11 @@ module Spree
collection
end
def new
@object.shipping_categories = [DefaultShippingCategory.find_or_create]
super
end
def destroy
# Our reports are not adapted to soft deleted shipping_methods so here we prevent
# the deletion (even soft) of shipping_methods that are referenced in orders

View File

@@ -0,0 +1,13 @@
module Spree
module Admin
module GeneralSettingsHelper
def currency_options
currencies = ::Money::Currency.table.map do |_code, details|
iso = details[:iso_code]
[iso, "#{details[:name]} (#{iso})"]
end
options_from_collection_for_select(currencies, :first, :last, Spree::Config[:currency])
end
end
end
end

View File

@@ -207,12 +207,15 @@ class AbilityDecorator
end
def add_order_management_abilities(user)
# Enterprise User can only access orders that they are a distributor for
can [:index, :create], Spree::Order
can [:read, :update, :fire, :resend, :invoice, :print, :print_ticket], Spree::Order do |order|
# We allow editing orders with a nil distributor as this state occurs
# during the order creation process from the admin backend
order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user)
order.distributor.nil? ||
# Enterprise User can access orders that they are a distributor for
user.enterprises.include?(order.distributor) ||
# Enterprise User can access orders that are placed inside a OC they coordinate
order.order_cycle.andand.coordinated_by?(user)
end
can [:admin, :bulk_management, :managed], Spree::Order do
user.admin? || user.enterprises.any?(&:is_distributor)

View File

@@ -4,7 +4,11 @@ class Api::AddressSerializer < ActiveModel::Serializer
attributes :id, :zipcode, :city, :state_name, :state_id,
:phone, :firstname, :lastname, :address1, :address2, :city, :country_id,
:zipcode
:zipcode, :country_name
def country_name
object.country.andand.name
end
def state_name
object.state.andand.abbr

View File

@@ -0,0 +1,8 @@
module Api
class AdjustmentSerializer < ActiveModel::Serializer
attributes :id, :amount, :label, :eligible,
:source_type, :source_id,
:adjustable_type, :adjustable_id,
:originator_type, :originator_id
end
end

View File

@@ -1,4 +1,4 @@
class Api::Admin::BasicEnterpriseSerializer < ActiveModel::Serializer
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids
attributes :producer_profile_only, :permalink
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category,
:payment_method_ids, :shipping_method_ids, :producer_profile_only, :permalink
end

View File

@@ -1,6 +1,6 @@
class Api::Admin::CustomerSerializer < ActiveModel::Serializer
attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list, :name
attributes :allow_charges, :default_card_present?
attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list, :name,
:allow_charges, :default_card_present?
has_one :ship_address, serializer: Api::AddressSerializer
has_one :bill_address, serializer: Api::AddressSerializer

View File

@@ -1,6 +1,6 @@
class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :inherits_tax_category, :calculator_type
attributes :enterprise_name, :calculator_description, :calculator_settings
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :inherits_tax_category,
:calculator_type, :enterprise_name, :calculator_description, :calculator_settings
def enterprise_name
object.enterprise.andand.name
@@ -16,7 +16,9 @@ class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
result = nil
options[:controller].__send__(:with_format, :html) do
result = options[:controller].render_to_string partial: 'admin/enterprise_fees/calculator_settings', locals: { enterprise_fee: object }
result = options[:controller].
render_to_string(partial: 'admin/enterprise_fees/calculator_settings',
locals: { enterprise_fee: object })
end
result.gsub('[0]', '[{{ $index }}]').gsub('_0_', '_{{ $index }}_')

View File

@@ -1,11 +1,12 @@
class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids
attributes :producer_profile_only, :long_description, :permalink
attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order
attributes :preferred_product_selection_from_inventory_only
attributes :owner, :contact, :users, :tag_groups, :default_tag_group
attributes :require_login, :allow_guest_orders, :allow_order_changes
attributes :logo, :promo_image
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :permalink,
:payment_method_ids, :shipping_method_ids, :producer_profile_only, :long_description,
:preferred_shopfront_message, :preferred_shopfront_closed_message,
:preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order,
:preferred_product_selection_from_inventory_only,
:owner, :contact, :users, :tag_groups, :default_tag_group,
:require_login, :allow_guest_orders, :allow_order_changes,
:logo, :promo_image
has_one :owner, serializer: Api::Admin::UserSerializer
has_many :users, serializer: Api::Admin::UserSerializer
@@ -21,7 +22,9 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
def tag_groups
object.tag_rules.prioritised.reject(&:is_default).each_with_object([]) do |tag_rule, tag_groups|
tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.split(",").map{ |t| { text: t } })
tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.
split(",").
map{ |t| { text: t } })
if tag_group[:rules].empty?
tag_groups << tag_group
tag_group[:position] = tag_groups.count
@@ -32,13 +35,16 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
def default_tag_group
default_rules = object.tag_rules.select(&:is_default)
serialized_rules = ActiveModel::ArraySerializer.new(default_rules, each_serializer: Api::Admin::TagRuleSerializer)
serialized_rules =
ActiveModel::ArraySerializer.new(default_rules,
each_serializer: Api::Admin::TagRuleSerializer)
{ tags: [], rules: serialized_rules }
end
def find_match(tag_groups, tags)
tag_groups.each do |tag_group|
return tag_group if tag_group[:tags].length == tags.length && (tag_group[:tags] & tags) == tag_group[:tags]
return tag_group if tag_group[:tags].length == tags.length &&
(tag_group[:tags] & tags) == tag_group[:tags]
end
{ tags: tags, rules: [] }
end

View File

@@ -1,6 +1,7 @@
class Api::Admin::ExchangeSerializer < ActiveModel::Serializer
attributes :id, :sender_id, :receiver_id, :incoming, :variants, :receival_instructions, :pickup_time, :pickup_instructions
attributes :tags, :tag_list
attributes :id, :sender_id, :receiver_id, :incoming, :variants,
:receival_instructions, :pickup_time, :pickup_instructions,
:tags, :tag_list
has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer

View File

@@ -1,12 +1,16 @@
require 'open_food_network/enterprise_issue_validator'
class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
attributes :id, :name, :managed, :supplied_products
attributes :issues_summary_supplier, :issues_summary_distributor
attributes :is_primary_producer, :is_distributor, :sells
attributes :id, :name, :managed, :supplied_products,
:issues_summary_supplier, :issues_summary_distributor,
:is_primary_producer, :is_distributor, :sells
def issues_summary_supplier
issues = OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
issues =
OpenFoodNetwork::EnterpriseIssueValidator.
new(object).
issues_summary(confirmation_only: true)
if issues.nil? && products.empty?
issues = "no products in inventory"
end
@@ -23,7 +27,8 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
def supplied_products
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
ActiveModel::ArraySerializer.new(products, each_serializer: serializer, order_cycle: order_cycle)
ActiveModel::ArraySerializer.new(products, each_serializer: serializer,
order_cycle: order_cycle)
end
private

View File

@@ -1,9 +1,8 @@
require 'open_food_network/enterprise_issue_validator'
class Api::Admin::IndexEnterpriseSerializer < ActiveModel::Serializer
attributes :name, :id, :permalink, :is_primary_producer, :sells, :producer_profile_only, :owned, :edit_path
attributes :issues, :warnings
attributes :name, :id, :permalink, :is_primary_producer, :sells, :producer_profile_only, :owned,
:edit_path, :issues, :warnings
def owned
return true if options[:spree_current_user].admin?

View File

@@ -5,9 +5,9 @@ module Api
class IndexOrderCycleSerializer < ActiveModel::Serializer
include OrderCyclesHelper
attributes :id, :name, :orders_open_at, :orders_close_at, :status, :variant_count, :deletable
attributes :coordinator, :producers, :shops, :viewing_as_coordinator
attributes :edit_path, :clone_path, :delete_path, :subscriptions_count
attributes :id, :name, :orders_open_at, :orders_close_at, :status, :variant_count, :deletable,
:coordinator, :producers, :shops, :viewing_as_coordinator,
:edit_path, :clone_path, :delete_path, :subscriptions_count
has_many :schedules, serializer: Api::Admin::IdNameSerializer
@@ -68,7 +68,10 @@ module Api
private
def visible_enterprises
@visible_enterprises ||= OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_enterprises
@visible_enterprises ||=
OpenFoodNetwork::OrderCyclePermissions.
new(options[:current_user], object).
visible_enterprises
end
end
end

View File

@@ -1,5 +1,6 @@
class Api::Admin::LineItemSerializer < ActiveModel::Serializer
attributes :id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume, :units_product, :units_variant
attributes :id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume,
:units_product, :units_variant
has_one :order, serializer: Api::Admin::IdSerializer
@@ -20,7 +21,8 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer
end
def max_quantity
return object.quantity unless object.max_quantity.present? && object.max_quantity > object.quantity
return object.quantity unless object.max_quantity.present? &&
object.max_quantity > object.quantity
object.max_quantity
end
end

View File

@@ -1,10 +1,10 @@
require 'open_food_network/order_cycle_permissions'
class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges
attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges
attributes :visible_variants_for_outgoing_exchanges
attributes :viewing_as_coordinator, :schedule_ids, :subscriptions_count
attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges,
:editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges,
:visible_variants_for_outgoing_exchanges,
:viewing_as_coordinator, :schedule_ids, :subscriptions_count
has_many :coordinator_fees, serializer: Api::IdSerializer
@@ -25,8 +25,14 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
end
def exchanges
scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_exchanges.by_enterprise_name
ActiveModel::ArraySerializer.new(scoped_exchanges, each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user])
scoped_exchanges =
OpenFoodNetwork::OrderCyclePermissions.
new(options[:current_user], object).
visible_exchanges.by_enterprise_name
ActiveModel::ArraySerializer.
new(scoped_exchanges, each_serializer: Api::Admin::ExchangeSerializer,
current_user: options[:current_user])
end
def editable_variants_for_incoming_exchanges
@@ -66,9 +72,13 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
# for shops. We need this here to allow hubs to restrict visible variants to only those in
# their inventory if they so choose
variants = if enterprise.prefers_product_selection_from_inventory_only?
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).visible_for(enterprise)
permissions.
visible_variants_for_outgoing_exchanges_to(enterprise).
visible_for(enterprise)
else
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).not_hidden_for(enterprise)
permissions.
visible_variants_for_outgoing_exchanges_to(enterprise).
not_hidden_for(enterprise)
end.pluck(:id)
visible[enterprise.id] = variants if variants.any?
end

View File

@@ -1,8 +1,9 @@
class Api::Admin::OrderSerializer < ActiveModel::Serializer
attributes :id, :number, :full_name, :email, :phone, :completed_at, :display_total
attributes :edit_path, :state, :payment_state, :shipment_state
attributes :payments_path, :ship_path, :ready_to_ship, :created_at
attributes :distributor_name, :special_instructions, :payment_capture_path
attributes :id, :number, :user_id, :full_name, :email, :phone, :completed_at, :display_total,
:edit_path, :state, :payment_state, :shipment_state,
:payments_path, :ship_path, :ready_to_ship, :created_at,
:distributor_name, :special_instructions, :payment_capture_path,
:item_total, :adjustment_total, :payment_total, :total
has_one :distributor, serializer: Api::Admin::IdSerializer
has_one :order_cycle, serializer: Api::Admin::IdSerializer

View File

@@ -1,9 +1,10 @@
module Api
module Admin
class SubscriptionSerializer < ActiveModel::Serializer
attributes :id, :shop_id, :customer_id, :schedule_id, :payment_method_id, :shipping_method_id, :begins_at, :ends_at
attributes :customer_email, :schedule_name, :edit_path, :canceled_at, :paused_at, :state
attributes :shipping_fee_estimate, :payment_fee_estimate
attributes :id, :shop_id, :customer_id, :schedule_id, :payment_method_id, :shipping_method_id,
:begins_at, :ends_at,
:customer_email, :schedule_name, :edit_path, :canceled_at, :paused_at, :state,
:shipping_fee_estimate, :payment_fee_estimate
has_many :subscription_line_items, serializer: Api::Admin::SubscriptionLineItemSerializer
has_many :closed_proxy_orders, serializer: Api::Admin::ProxyOrderSerializer

View File

@@ -16,7 +16,8 @@ module Api::Admin::TagRule
end
class FilterShippingMethodsSerializer < BaseSerializer
attributes :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags, :shipping_method_tags
attributes :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags,
:shipping_method_tags
def shipping_method_tags
object.preferred_shipping_method_tags.split(",")
@@ -24,7 +25,8 @@ module Api::Admin::TagRule
end
class FilterPaymentMethodsSerializer < BaseSerializer
attributes :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags, :payment_method_tags
attributes :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags,
:payment_method_tags
def payment_method_tags
object.preferred_payment_method_tags.split(",")

View File

@@ -1,6 +1,6 @@
class Api::Admin::VariantOverrideSerializer < ActiveModel::Serializer
attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock, :resettable
attributes :tag_list, :tags, :import_date
attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock,
:resettable, :tag_list, :tags, :import_date
def tag_list
object.tag_list.join(",")

View File

@@ -1,8 +1,8 @@
class Api::Admin::VariantSerializer < ActiveModel::Serializer
attributes :id, :name, :producer_name, :image, :sku, :import_date
attributes :options_text, :unit_value, :unit_description, :unit_to_display
attributes :display_as, :display_name, :name_to_display
attributes :price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name
attributes :id, :name, :producer_name, :image, :sku, :import_date,
:options_text, :unit_value, :unit_description, :unit_to_display,
:display_as, :display_name, :name_to_display,
:price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name
has_many :variant_overrides

View File

@@ -0,0 +1,12 @@
module Api
class OrderDetailedSerializer < Api::Admin::OrderSerializer
has_one :shipping_method, serializer: Api::ShippingMethodSerializer
has_one :ship_address, serializer: Api::AddressSerializer
has_one :bill_address, serializer: Api::AddressSerializer
has_many :line_items, serializer: Api::LineItemSerializer
has_many :adjustments, serializer: Api::AdjustmentSerializer
has_many :payments, serializer: Api::PaymentSerializer
end
end

View File

@@ -1,9 +1,9 @@
module Api
class OrderSerializer < ActiveModel::Serializer
attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state
attributes :outstanding_balance, :payments, :path, :cancel_path
attributes :changes_allowed, :changes_allowed_until, :item_count
attributes :shop_id
attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state,
:outstanding_balance, :payments, :path, :cancel_path,
:changes_allowed, :changes_allowed_until, :item_count,
:shop_id
has_many :payments, serializer: Api::PaymentSerializer

View File

@@ -1,6 +1,7 @@
module Api
class PaymentSerializer < ActiveModel::Serializer
attributes :amount, :updated_at, :payment_method, :state
def payment_method
object.payment_method.try(:name)
end

View File

@@ -1,9 +1,9 @@
class Api::VariantSerializer < ActiveModel::Serializer
attributes :id, :is_master, :product_name, :sku
attributes :options_text, :unit_value, :unit_description, :unit_to_display
attributes :display_as, :display_name, :name_to_display
attributes :price, :on_demand, :on_hand, :fees, :price_with_fees
attributes :tag_list
attributes :id, :is_master, :product_name, :sku,
:options_text, :unit_value, :unit_description, :unit_to_display,
:display_as, :display_name, :name_to_display,
:price, :on_demand, :on_hand, :fees, :price_with_fees,
:tag_list
delegate :price, to: :object

View File

@@ -0,0 +1,13 @@
# Encapsulates the concept of default stock location in creation of a product or a shipping method.
class DefaultShippingCategory
NAME = 'Default'.freeze
def self.create!
Spree::ShippingCategory.create!(name: NAME)
end
def self.find_or_create
Spree::ShippingCategory.find_or_create_by_name(NAME)
end
end

View File

@@ -0,0 +1,60 @@
%div
.row
.alpha.six.columns
%fieldset.no-border-bottom
%legend{align: "center"}= t("spree.general")
.field
= preference_field_tag("enable_mail_delivery", Spree::Config[:enable_mail_delivery], type: :boolean)
= label_tag :enable_mail_delivery, t("spree.enable_mail_delivery")
.field
= label_tag :mails_from, t("spree.send_mails_as")
%br/
= text_field_tag :mails_from, Spree::Config[:mails_from], maxlength: 256, class: 'fullwidth'
%br/
%span.info
= t("spree.smtp_send_all_emails_as_from_following_address")
.field
= label_tag :mail_bcc, t("spree.send_copy_of_all_mails_to")
%br/
= text_field_tag :mail_bcc, Spree::Config[:mail_bcc], maxlength: 256, class: 'fullwidth'
%br/
%span.info
= t("spree.smtp_send_copy_to_this_addresses")
.field
= label_tag :intercept_email, t("spree.intercept_email_address")
%br/
= text_field_tag :intercept_email, Spree::Config[:intercept_email], maxlength: 256, class: 'fullwidth'
%br/
%span.info
= t("spree.intercept_email_instructions")
.six.columns.omega
%fieldset.no-border-bottom
%legend{align: "center"}= t("spree.smtp")
.field
= label_tag :mail_domain, t("spree.smtp_domain")
%br/
= text_field_tag :mail_domain, Spree::Config[:mail_domain], class: 'fullwidth'
.field
= label_tag :mail_host, t("spree.smtp_mail_host")
%br/
= text_field_tag :mail_host, Spree::Config[:mail_host], class: 'fullwidth'
.field
= label_tag :mail_port, t("spree.smtp_port")
%br/
= text_field_tag :mail_port, Spree::Config[:mail_port], class: 'fullwidth'
.field
= label_tag :secure_connection_type, t("spree.secure_connection_type")
%br/
= select_tag(:secure_connection_type, options_from_collection_for_select(Spree::Core::MailSettings::SECURE_CONNECTION_TYPES.map{|w| Spree.t(w.downcase.to_sym, default: w)}, :to_s, :to_s, Spree::Config[:secure_connection_type]), class: 'select2 fullwidth')
.field
= label_tag :mail_auth_type, t("spree.smtp_authentication_type")
%br/
= select_tag(:mail_auth_type, options_from_collection_for_select(Spree::Core::MailSettings::MAIL_AUTH.map{|w| Spree.t(w.downcase.to_sym, default: w)}, :to_s, :to_s, Spree::Config[:mail_auth_type]), class: 'select2 fullwidth')
.field
= label_tag :smtp_username, t("spree.smtp_username")
%br/
= text_field_tag :smtp_username, Spree::Config[:smtp_username], class: 'fullwidth'
.field
= label_tag :preferred_smtp_password, t("spree.smtp_password")
%br/
= password_field_tag :smtp_password, Spree::Config[:smtp_password], class: 'fullwidth'

View File

@@ -0,0 +1,15 @@
= render partial: 'spree/admin/shared/configuration_menu'
- content_for :page_title do
= t("spree.mail_method_settings")
- content_for :page_actions do
%li
= link_to_with_icon 'icon-envelope-alt', t("spree.admin.mail_methods.send_testmail"), testmail_admin_mail_method_path, method: :post, title: t("spree.admin.mail_methods.send_testmail"), class: 'send_mail button no-text'
= render partial: 'spree/shared/error_messages', locals: { target: @mail_method }
= form_tag admin_mail_method_path, method: :put do |f|
%fieldset.no-border-top
= render partial: 'form', locals: { f: f }
.form-buttons.filter-actions.actions= button t("spree.actions.update"), 'icon-refresh'

View File

@@ -1,4 +1,4 @@
= f.field_container :shipping_categories do
= f.label :shipping_category_id, t(:shipping_category)
= f.collection_select(:shipping_category_id, Spree::ShippingCategory.all, :id, :name, {:include_blank => true}, {:class => 'select2 fullwidth'})
= f.collection_select(:shipping_category_id, Spree::ShippingCategory.all, :id, :name, {:include_blank => false}, {:class => 'select2 fullwidth'})
= f.error_message_on :shipping_category_id

View File

@@ -14,9 +14,9 @@
= text_field_tag :unit_value_human, nil, {class: "fullwidth", 'ng-model' => 'unit_value_human', 'ng-change' => 'updateValue()'}
= f.text_field :unit_value, {hidden: true, 'ng-value' => 'unit_value'}
.field
= f.label :unit_description, t(:spree_admin_unit_description)
= f.text_field :unit_description, class: "fullwidth", placeholder: t('admin.products.unit_name_placeholder')
.field
= f.label :unit_description, t(:spree_admin_unit_description)
= f.text_field :unit_description, class: "fullwidth", placeholder: t('admin.products.unit_name_placeholder')
%div
- @product.option_types.each do |option_type|

View File

@@ -56,3 +56,5 @@ SMTP_PASSWORD: 'f00d'
# STRIPE_INSTANCE_PUBLISHABLE_KEY: "pk_test_xxxx" # This can be a test key or a live key
# STRIPE_CLIENT_ID: "ca_xxxx" # This can be a development ID or a production ID
# STRIPE_ENDPOINT_SECRET: "whsec_xxxx"
MEMCACHED_VALUE_MAX_MEGABYTES: 4

View File

@@ -40,7 +40,9 @@ Openfoodnetwork::Application.configure do
# config.logger = SyslogLogger.new
# Use a different cache store in production
config.cache_store = :dalli_store
memcached_value_max_megabytes = ENV.fetch("MEMCACHED_VALUE_MAX_MEGABYTES", 1).to_i
memcached_value_max_bytes = memcached_value_max_megabytes * 1024 * 1024
config.cache_store = :dalli_store, { value_max_bytes: memcached_value_max_bytes }
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"

View File

@@ -40,7 +40,9 @@ Openfoodnetwork::Application.configure do
# config.logger = SyslogLogger.new
# Use a different cache store in production
config.cache_store = :dalli_store
memcached_value_max_megabytes = ENV.fetch("MEMCACHED_VALUE_MAX_MEGABYTES", 1).to_i
memcached_value_max_bytes = memcached_value_max_megabytes * 1024 * 1024
config.cache_store = :dalli_store, { value_max_bytes: memcached_value_max_bytes }
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"

View File

@@ -119,9 +119,9 @@ en_CA:
shipment_mailer:
shipped_email:
dear_customer: "Dear Customer, "
instructions: "Your order has been shipped"
shipment_summary: "Shipment Summary"
subject: "Shipment Notification"
instructions: "Your order has been shipped or picked up."
shipment_summary: "Shipment/Pick Up Summary"
subject: "Shipment/Pick Up Notification"
thanks: "Thank you for your business."
track_information: "Tracking Information: %{tracking}"
track_link: "Tracking Link: %{url}"
@@ -311,7 +311,7 @@ en_CA:
tag_has_rules: "Existing rules for this tag: %{num}"
has_one_rule: "has one rule"
has_n_rules: "has %{num} rules"
unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?"
unsaved_confirm_leave: "There are unsaved changes on this page. Continue without saving?"
unsaved_changes: "You have unsaved changes"
shopfront_settings:
embedded_shopfront_settings: "Embedded Shopfront Settings"
@@ -608,7 +608,7 @@ en_CA:
email_address_placeholder: eg. inquiries@fresh-food.com
email_address_tip: "This email address will be displayed in your public profile"
phone: Phone
phone_placeholder: eg. 98 7654 3210
phone_placeholder: eg. 519 885 8888
website: Website
website_placeholder: eg. www.truffles.com
enterprise_fees:
@@ -1196,7 +1196,7 @@ en_CA:
city: City
city_placeholder: eg. Northcote
postcode: Postal Code
postcode_placeholder: eg. 3070
postcode_placeholder: eg. N0B 2L0
suburb: City
state: Province
country: Country
@@ -1452,7 +1452,7 @@ en_CA:
set_a_password: "You will then be prompted to set a password before you are able to administer the enterprise."
mistakenly_sent: "Not sure why you have received this email? Please contact %{owner_email} for more information."
producer_mail_greeting: "Dear"
producer_mail_text_before: "We now have all the consumer orders for the next food drop."
producer_mail_text_before: "We now have all the orders for the next pick-up/delivery."
producer_mail_order_text: "Here is a summary of the orders for your products:"
producer_mail_delivery_instructions: "Stock pickup/delivery instructions:"
producer_mail_signoff: "Thanks and best wishes"
@@ -1469,7 +1469,7 @@ en_CA:
shopping_producers_of_hub: "%{hub}'s producers:"
enterprises_next_closing: "Next order closing"
enterprises_ready_for: "Ready for"
enterprises_choose: "Choose when you want your order:"
enterprises_choose: "Choose from the dropdown:"
maps_open: "Open"
maps_closed: "Closed"
hubs_buy: "Shop for:"
@@ -1600,7 +1600,7 @@ en_CA:
sell_hubs_detail: "Set up a profile for your food enterprise or organisation on the OFN. At any time you can upgrade your profile to a multi-producer shop."
sell_groups_detail: "Set up a tailored directory of enterprises (producers and other food enterprises) for your region or for your organisation."
sell_user_guide: "Find out more in our user guide."
sell_listing_price: "Listing on the OFN is free. Opening and running a shop on OFN is free up to $500 of monthly sales. If you sell more we will invoice you for 2% of sales to a maximum of $100/month. For more detail on pricing visit the Software Platform section via the About link in the top menu."
sell_listing_price: "Listing on the OFN is free. Opening and running a shop on OFN is free up to $500 of monthly sales. If you sell more we will invoice you for 2% of sales to a maximum of $150/month. For more detail on pricing visit the Software Platform section via the About link in the top menu."
sell_embed: "We can also embed an OFN shop in your own customised website or build a customised local food network website for your region."
sell_ask_services: "Ask us about OFN services."
shops_title: Shops
@@ -1741,11 +1741,11 @@ en_CA:
address1_field_placeholder: "e.g. 123 Cranberry Drive"
address1_field_error: "Please enter an address"
address2_field: "Address line 2:"
suburb_field: "Suburb:"
suburb_field_placeholder: "e.g. Northcote"
suburb_field_error: "Please enter a suburb"
suburb_field: "City/Town"
suburb_field_placeholder: "e.g. Kitchener"
suburb_field_error: "Please enter a City or Town"
postcode_field: "Postal Code"
postcode_field_placeholder: "e.g. 3070"
postcode_field_placeholder: "e.g. N0B 2L0"
postcode_field_error: "Postal Code"
state_field: "Province:"
state_field_error: "Province required"
@@ -1758,7 +1758,7 @@ en_CA:
contact_field_placeholder: "Contact Name"
contact_field_required: "You need to enter a primary contact."
phone_field: "Phone number"
phone_field_placeholder: "eg. (03) 1234 5678"
phone_field_placeholder: "eg.(519) 885 8888"
type:
title: "Type"
headline: "Last step to add %{enterprise}!"
@@ -1782,8 +1782,8 @@ en_CA:
enterprise_long_desc_length: "%{num} characters / up to 600 recommended"
enterprise_abn: "Business Number"
enterprise_abn_placeholder: "Number will show on invoices"
enterprise_acn: "Business Number"
enterprise_acn_placeholder: "eg. 123 456 789"
enterprise_acn: "Additional identifier"
enterprise_acn_placeholder: "if entered, will show on invoices"
enterprise_tax_required: "You need to make a selection."
images:
title: "Images"
@@ -1887,10 +1887,10 @@ en_CA:
admin_enterprise_groups_data_powertip_logo: "This is the logo for the group"
admin_enterprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile"
admin_enterprise_groups_contact: "Contact"
admin_enterprise_groups_contact_phone_placeholder: "eg. 98 7654 3210"
admin_enterprise_groups_contact_phone_placeholder: "eg. 519 885 5566"
admin_enterprise_groups_contact_address1_placeholder: "eg. 123 High Street"
admin_enterprise_groups_contact_city: "Suburb"
admin_enterprise_groups_contact_city_placeholder: "eg. Northcote"
admin_enterprise_groups_contact_city: "City/Town"
admin_enterprise_groups_contact_city_placeholder: "eg. Kitchener"
admin_enterprise_groups_contact_zipcode: "Postal Code"
admin_enterprise_groups_contact_zipcode_placeholder: "eg. 3070"
admin_enterprise_groups_contact_state_id: "Province"
@@ -1975,7 +1975,7 @@ en_CA:
change_package: "Change Package"
spree_admin_single_enterprise_hint: "Hint: To allow people to find you, turn on your visibility under"
spree_admin_eg_pickup_from_school: "eg. 'Pick-up from Primary School'"
spree_admin_eg_collect_your_order: "eg. 'Please collect your order from 123 Imaginary St, Northcote, 3070'"
spree_admin_eg_collect_your_order: "eg. 'Please collect your order from 123 Imaginary St, YourCity'"
spree_classification_primary_taxon_error: "Taxon %{taxon} is the primary taxon of %{product} and cannot be deleted"
spree_order_availability_error: "Distributor or order cycle cannot supply the products in your cart"
spree_order_populator_error: "That distributor or order cycle can't supply all the products in your cart. Please choose another."
@@ -2046,7 +2046,7 @@ en_CA:
report_header_first_name: First Name
report_header_last_name: Last Name
report_header_phone: Phone
report_header_suburb: Suburb
report_header_suburb: City/Town
report_header_address: Address
report_header_billing_address: Billing Address
report_header_relationship: Relationship
@@ -2102,7 +2102,7 @@ en_CA:
report_header_taxons: Taxons
report_header_supplier: Supplier
report_header_producer: Producer
report_header_producer_suburb: Producer Suburb
report_header_producer_suburb: Producer City/Town
report_header_unit: Unit
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
report_header_cost: Cost
@@ -2782,6 +2782,7 @@ en_CA:
discount_amount: "Discount Amount"
email: Email
account_updated: "Account updated!"
email_updated: "The account will be updated once the new email is confirmed."
my_account: "My account"
date: "Date"
time: "Time"

View File

@@ -163,7 +163,7 @@ en_GB:
home: "OFN"
title: Open Food Network
welcome_to: 'Welcome to '
site_meta_description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world."
site_meta_description: "The Open Food Network software platform allows farmers to sell produce online, at a price that works for them. It has been built specifically for selling food so it can handle tricky measures or stock levels that only food has - a dozen eggs, a bunch of parsley, a whole chicken that varies in weight…"
search_by_name: Search by name, town, county or postcode...
producers_join: UK producers are now welcome to join Open Food Network UK.
charges_sales_tax: Charges VAT?
@@ -1302,26 +1302,26 @@ en_GB:
cookies_policy_link: "cookies policy"
cookies_accept_button: "Accept Cookies"
home_shop: Shop Now
brandstory_headline: "Re-imagining Local Food"
brandstory_intro: "A learning community & tools to support local food systems to thrive"
brandstory_part1: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world."
brandstory_part2: "Then we need a way to make it real. A way to empower everyone who grows, sells and buys food. A way to tell all the stories, to handle all the logistics. A way to turn transaction into transformation every day."
brandstory_part3: "So we build an online marketplace that levels the playing field. Its transparent, so it creates real relationships. Its open source, so its owned by everyone. It scales to regions and nations, so people start versions across the world."
brandstory_part4: "It works everywhere. It changes everything."
brandstory_part5_strong: "We call it Open Food Network."
brandstory_part6: "We all love food. Now we can love our food system too."
brandstory_headline: "Growing Local Food Online"
brandstory_intro: "THE online community helping you to build a successful food enterprise"
brandstory_part1: "The Open Food Network software platform allows farmers to sell produce online, at a price that works for them. It has been built specifically for selling food so it can handle tricky measures or stock levels that only food has - a dozen eggs, a bunch of parsley, a whole chicken that varies in weight…"
brandstory_part2: "Food Producers can create an online shop, collect payments, sell through other shops on the platform and access reduced-rate courier services."
brandstory_part3: "Wholesalers can integrate OFN with their existing systems and manage buying groups to  supply customers with their produce through our national network of food hubs and shops."
brandstory_part4: "Communities can bring together producers in their local area to create virtual farmers markets, building a resilient local food economy."
brandstory_part5_strong: "And whats just as important as the software itself are the values which underpin it. "
brandstory_part6: "If you sell good food - as a farmer, farmers market, food co-op, or food hub- then choose software that aligns with your values to build food systems for people and planet, not profit. By working collectively rather than competitively, we share the costs of developing new software, and we ensure that our project is resilient!"
learn_body: "Explore models, stories and resources to support you to develop your fair food business or organisation. Find training, events and other opportunities to learn from peers."
learn_cta: "Get Inspired"
connect_body: "Search our full directories of producers, hubs and groups to find fair food traders near you. List your business or organisation on the OFN so buyers can find you. Join the community to get advice and solve problems together."
connect_cta: "Go Exploring"
system_headline: "Shopping - here's how it works."
system_step1: "1. Search"
system_step1_text: "Search our diverse, independent shops for seasonal local food. Search by neighbourhood and food category, or whether you prefer delivery or pickup."
system_step2: "2. Shop"
system_step2_text: "Transform your transactions with affordable local food from diverse producers and hubs. Know the stories behind your food and the people who make it!"
system_step3: "3. Pick-up / Delivery"
system_step3_text: "Hang on for your delivery, or visit your producer or hub for a more personal connection with your food. Food shopping as diverse as nature intended it."
cta_headline: "Shopping that makes the world a better place."
system_headline: "Selling on OFN - 3 easy steps"
system_step1: "1. Create Your Enterprise"
system_step1_text: "Set up your enterprise with a name, description, photos, contact details and social media links."
system_step2: "2. Add Your Products"
system_step2_text: "Add products to your shop - your own and/or from other producers around you. Set images, descriptions, prices, stock levels and flexible weights and measures."
system_step3: "3. Plan your Deliveries"
system_step3_text: "Set up payment methods. Create multiple pick-up points and delivery details. Create recurring orders and regular distributions."
cta_headline: "The sustainable Food Network of the future."
cta_label: "I'm Ready"
stats_headline: "We're creating a new food system."
stats_producers: "food producers"

View File

@@ -245,6 +245,7 @@ en_US:
expired: has expired, please request a new one
back_to_payments_list: "Back to Payments List"
maestro_or_solo_cards: "Debit cards"
backordered: "Backordered"
on hand: "On Hand"
ship: "Ship"
actions:
@@ -2629,6 +2630,8 @@ en_US:
name_or_sku: "Name or SKU (enter at least first 4 characters of product name)"
resend: Resend
back_to_orders_list: Back To Orders List
return_authorizations: Return Authorizations
cannot_create_returns: Cannot create returns as this order has no shipped units.
select_stock: "Select stock"
location: "Location"
count_on_hand: "Count On Hand"
@@ -2779,6 +2782,7 @@ en_US:
discount_amount: "Discount Amount"
email: Email
account_updated: "Account updated!"
email_updated: "The account will be updated once the new email is confirmed."
my_account: "My account"
date: "Date"
time: "Time"

View File

@@ -15,7 +15,7 @@ Openfoodnetwork::Application.routes.draw do
resources :variants, :only => [:index]
resources :orders, only: [:index] do
resources :orders, only: [:index, :show] do
get :managed, on: :collection
resources :shipments, :only => [:create, :update] do

View File

@@ -90,6 +90,23 @@ Spree::Core::Engine.routes.prepend do
end
# Configuration section
resource :general_settings do
collection do
post :dismiss_alert
end
end
resource :mail_method, :only => [:edit, :update] do
post :testmail, :on => :collection
end
resource :image_settings
resources :zones
resources :countries do
resources :states
end
resources :states
resources :taxonomies do
collection do
post :update_positions
@@ -109,12 +126,6 @@ Spree::Core::Engine.routes.prepend do
resources :tax_rates
resource :tax_settings
resources :tax_categories
resources :zones
resources :countries do
resources :states
end
resources :states
end
resources :orders do

View File

@@ -0,0 +1,9 @@
class DropOrdersShippingMethodId < ActiveRecord::Migration
def up
remove_column :spree_orders, :shipping_method_id
end
def down
add_column :spree_orders, :shipping_method_id, :integer
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20190918105234) do
ActiveRecord::Schema.define(:version => 20190922201034) do
create_table "adjustment_metadata", :force => true do |t|
t.integer "adjustment_id"
@@ -563,7 +563,6 @@ ActiveRecord::Schema.define(:version => 20190918105234) do
t.integer "bill_address_id"
t.integer "ship_address_id"
t.decimal "payment_total", :precision => 10, :scale => 2, :default => 0.0
t.integer "shipping_method_id"
t.string "shipment_state"
t.string "payment_state"
t.string "email"

View File

@@ -52,3 +52,4 @@ end
require File.join(File.dirname(__FILE__), 'default', 'users')
DefaultStockLocation.find_or_create
DefaultShippingCategory.find_or_create

View File

@@ -7,6 +7,7 @@ services:
environment:
POSTGRES_PASSWORD: f00d
POSTGRES_USER: ofn
POSTGRES_DB: open_food_network_dev
volumes:
- 'postgres:/var/lib/postgresql/data'
web:

View File

@@ -1,295 +1,55 @@
require "open_food_network/reports/line_items"
require "open_food_network/orders_and_fulfillments_report/supplier_totals_report"
require "open_food_network/orders_and_fulfillments_report/supplier_totals_by_distributor_report"
require "open_food_network/orders_and_fulfillments_report/distributor_totals_by_supplier_report"
require "open_food_network/orders_and_fulfillments_report/customer_totals_report"
require 'open_food_network/orders_and_fulfillments_report/default_report'
include Spree::ReportsHelper
module OpenFoodNetwork
class OrdersAndFulfillmentsReport
attr_reader :params
def initialize(permissions, params = {}, render_table = false)
@params = params
attr_reader :options, :report_type
delegate :header, :rules, :columns, to: :report
def initialize(permissions, options = {}, render_table = false)
@options = options
@report_type = options[:report_type]
@permissions = permissions
@render_table = render_table
end
def header
case params[:report_type]
when "order_cycle_supplier_totals"
[I18n.t(:report_header_producer), I18n.t(:report_header_product), I18n.t(:report_header_variant), I18n.t(:report_header_amount),
I18n.t(:report_header_total_units), I18n.t(:report_header_curr_cost_per_unit), I18n.t(:report_header_total_cost),
I18n.t(:report_header_status), I18n.t(:report_header_incoming_transport)]
when "order_cycle_supplier_totals_by_distributor"
[I18n.t(:report_header_producer), I18n.t(:report_header_product), I18n.t(:report_header_variant), I18n.t(:report_header_to_hub),
I18n.t(:report_header_amount), I18n.t(:report_header_curr_cost_per_unit), I18n.t(:report_header_total_cost),
I18n.t(:report_header_shipping_method)]
when "order_cycle_distributor_totals_by_supplier"
[I18n.t(:report_header_hub), I18n.t(:report_header_producer), I18n.t(:report_header_product), I18n.t(:report_header_variant),
I18n.t(:report_header_amount), I18n.t(:report_header_curr_cost_per_unit), I18n.t(:report_header_total_cost),
I18n.t(:report_header_total_shipping_cost), I18n.t(:report_header_shipping_method)]
when "order_cycle_customer_totals"
[I18n.t(:report_header_hub), I18n.t(:report_header_customer), I18n.t(:report_header_email), I18n.t(:report_header_phone),
I18n.t(:report_header_producer), I18n.t(:report_header_product), I18n.t(:report_header_variant), I18n.t(:report_header_amount),
I18n.t(:report_header_item_price, currency: currency_symbol),
I18n.t(:report_header_item_fees_price, currency: currency_symbol),
I18n.t(:report_header_admin_handling_fees, currency: currency_symbol),
I18n.t(:report_header_ship_price, currency: currency_symbol),
I18n.t(:report_header_pay_fee_price, currency: currency_symbol),
I18n.t(:report_header_total_price, currency: currency_symbol),
I18n.t(:report_header_paid), I18n.t(:report_header_shipping), I18n.t(:report_header_delivery),
I18n.t(:report_header_ship_street), I18n.t(:report_header_ship_street_2), I18n.t(:report_header_ship_city), I18n.t(:report_header_ship_postcode), I18n.t(:report_header_ship_state),
I18n.t(:report_header_comments), I18n.t(:report_header_sku),
I18n.t(:report_header_order_cycle), I18n.t(:report_header_payment_method), I18n.t(:report_header_customer_code), I18n.t(:report_header_tags),
I18n.t(:report_header_billing_street), I18n.t(:report_header_billing_street_2), I18n.t(:report_header_billing_city), I18n.t(:report_header_billing_postcode), I18n.t(:report_header_billing_state),]
else
DefaultReport.new(self).header
end
end
def search
Reports::LineItems.search_orders(permissions, params)
Reports::LineItems.search_orders(permissions, options)
end
def table_items
return [] unless @render_table
Reports::LineItems.list(permissions, params)
end
def rules
case params[:report_type]
when "order_cycle_supplier_totals"
[
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name }
}
]
when "order_cycle_supplier_totals_by_distributor"
[
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name },
summary_columns: [
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name }
}
]
when "order_cycle_distributor_totals_by_supplier"
[
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name },
summary_columns: [
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.map(&:order).uniq.sum(&:ship_total) },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name }
}
]
when "order_cycle_customer_totals"
[
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name }
},
{
group_by: proc { |line_item| line_item.order },
sort_by: proc { |order| order.bill_address.full_name_reverse },
summary_columns: [
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.order.bill_address.full_name },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
proc { |line_items| line_items.first.order.admin_and_handling_total },
proc { |line_items| line_items.first.order.ship_total },
proc { |line_items| line_items.first.order.payment_fee },
proc { |line_items| line_items.first.order.total },
proc { |line_items| line_items.first.order.paid? ? I18n.t(:yes) : I18n.t(:no) },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.first.order.special_instructions },
proc { |_line_items| "" },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items|
line_items.first.order.payments.first.andand.payment_method.andand.name
},
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name }
},
{
group_by: line_item_name,
sort_by: proc { |full_name| full_name }
}
]
else
DefaultReport.new(self).rules
end
end
# Returns a proc for each column displayed in each report type containing
# the logic to compute the value for each cell.
def columns
case params[:report_type]
when "order_cycle_supplier_totals"
[
supplier_name,
product_name,
line_items_name,
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| total_units(line_items) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" },
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
]
when "order_cycle_supplier_totals_by_distributor"
[
supplier_name,
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| I18n.t(:report_header_shipping_method) }
]
when "order_cycle_distributor_totals_by_supplier"
[proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" },
proc { |_line_items| I18n.t(:report_header_shipping_method) }]
when "order_cycle_customer_totals"
rsa = proc { |line_items| line_items.first.order.shipping_method.andand.delivery? }
[
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
proc { |line_items| line_items.first.order.email },
proc { |line_items| line_items.first.order.bill_address.phone },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.all? { |li| li.order.paid? } ? I18n.t(:yes) : I18n.t(:no) },
proc { |line_items| line_items.first.order.shipping_method.andand.name },
proc { |line_items| rsa.call(line_items) ? I18n.t(:yes) : I18n.t(:no) },
proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) },
proc { |_line_items| "" },
proc { |line_items| line_items.first.variant.sku },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code },
proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.tags.andand.join(', ') },
proc { |line_items| line_items.first.order.bill_address.andand.address1 },
proc { |line_items| line_items.first.order.bill_address.andand.address2 },
proc { |line_items| line_items.first.order.bill_address.andand.city },
proc { |line_items| line_items.first.order.bill_address.andand.zipcode },
proc { |line_items| line_items.first.order.bill_address.andand.state }
]
else
DefaultReport.new(self).columns
end
list_options = options.merge(line_item_includes: report.line_item_includes)
Reports::LineItems.list(permissions, list_options)
end
private
attr_reader :permissions
def report
@report ||= report_klass.new(self)
end
def report_klass
case report_type
when SupplierTotalsReport::REPORT_TYPE then SupplierTotalsReport
when SupplierTotalsByDistributorReport::REPORT_TYPE then SupplierTotalsByDistributorReport
when DistributorTotalsBySupplierReport::REPORT_TYPE then DistributorTotalsBySupplierReport
when CustomerTotalsReport::REPORT_TYPE then CustomerTotalsReport
else
DefaultReport
end
end
def supplier_name
proc { |line_items| line_items.first.variant.product.supplier.name }
end

View File

@@ -0,0 +1,198 @@
# rubocop:disable Metrics/ClassLength
module OpenFoodNetwork
class OrdersAndFulfillmentsReport
class CustomerTotalsReport
REPORT_TYPE = "order_cycle_customer_totals".freeze
attr_reader :context
delegate :line_item_name, to: :context
def initialize(context)
@context = context
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def header
[I18n.t(:report_header_hub), I18n.t(:report_header_customer), I18n.t(:report_header_email),
I18n.t(:report_header_phone), I18n.t(:report_header_producer),
I18n.t(:report_header_product), I18n.t(:report_header_variant),
I18n.t(:report_header_amount),
I18n.t(:report_header_item_price, currency: currency_symbol),
I18n.t(:report_header_item_fees_price, currency: currency_symbol),
I18n.t(:report_header_admin_handling_fees, currency: currency_symbol),
I18n.t(:report_header_ship_price, currency: currency_symbol),
I18n.t(:report_header_pay_fee_price, currency: currency_symbol),
I18n.t(:report_header_total_price, currency: currency_symbol),
I18n.t(:report_header_paid), I18n.t(:report_header_shipping),
I18n.t(:report_header_delivery), I18n.t(:report_header_ship_street),
I18n.t(:report_header_ship_street_2), I18n.t(:report_header_ship_city),
I18n.t(:report_header_ship_postcode), I18n.t(:report_header_ship_state),
I18n.t(:report_header_comments), I18n.t(:report_header_sku),
I18n.t(:report_header_order_cycle), I18n.t(:report_header_payment_method),
I18n.t(:report_header_customer_code), I18n.t(:report_header_tags),
I18n.t(:report_header_billing_street), I18n.t(:report_header_billing_street_2),
I18n.t(:report_header_billing_city), I18n.t(:report_header_billing_postcode),
I18n.t(:report_header_billing_state)]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def rules
[
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name }
},
{
group_by: proc { |line_item| line_item.order },
sort_by: proc { |order| order.bill_address.full_name_reverse },
summary_columns: [
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.order.bill_address.full_name },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
proc { |line_items| line_items.first.order.admin_and_handling_total },
proc { |line_items| line_items.first.order.ship_total },
proc { |line_items| line_items.first.order.payment_fee },
proc { |line_items| line_items.first.order.total },
proc { |line_items| line_items.first.order.paid? ? I18n.t(:yes) : I18n.t(:no) },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.first.order.special_instructions },
proc { |_line_items| "" },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items|
line_items.first.order.payments.first.andand.payment_method.andand.name
},
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name }
},
{
group_by: line_item_name,
sort_by: proc { |full_name| full_name }
}
]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
def columns
rsa = proc { |line_items| line_items.first.order.shipping_method.andand.delivery? }
[
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items|
bill_address = line_items.first.order.bill_address
bill_address.firstname + " " + bill_address.lastname
},
proc { |line_items| line_items.first.order.email },
proc { |line_items| line_items.first.order.bill_address.phone },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items|
line_items.all? { |li| li.order.paid? } ? I18n.t(:yes) : I18n.t(:no)
},
proc { |line_items| line_items.first.order.shipping_method.andand.name },
proc { |line_items| rsa.call(line_items) ? I18n.t(:yes) : I18n.t(:no) },
proc { |line_items|
line_items.first.order.ship_address.andand.address1 if rsa.call(line_items)
},
proc { |line_items|
line_items.first.order.ship_address.andand.address2 if rsa.call(line_items)
},
proc { |line_items|
line_items.first.order.ship_address.andand.city if rsa.call(line_items)
},
proc { |line_items|
line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items)
},
proc { |line_items|
line_items.first.order.ship_address.andand.state if rsa.call(line_items)
},
proc { |_line_items| "" },
proc { |line_items| line_items.first.variant.sku },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items|
payment = line_items.first.order.payments.first
payment.andand.payment_method.andand.name
},
proc { |line_items|
distributor = line_items.first.order.distributor
user = line_items.first.order.user
user.andand.customer_of(distributor).andand.code
},
proc { |line_items|
distributor = line_items.first.order.distributor
user = line_items.first.order.user
user.andand.customer_of(distributor).andand.tags.andand.join(', ')
},
proc { |line_items| line_items.first.order.bill_address.andand.address1 },
proc { |line_items| line_items.first.order.bill_address.andand.address2 },
proc { |line_items| line_items.first.order.bill_address.andand.city },
proc { |line_items| line_items.first.order.bill_address.andand.zipcode },
proc { |line_items| line_items.first.order.bill_address.andand.state }
]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/PerceivedComplexity
def line_item_includes
[{ variant: :product, order: [:bill_address, :shipments] }]
end
end
end
end
# rubocop:enable Metrics/ClassLength

View File

@@ -54,6 +54,10 @@ module OpenFoodNetwork
end
# rubocop:enable Metrics/AbcSize
def line_item_includes
[]
end
private
attr_reader :context

View File

@@ -0,0 +1,75 @@
module OpenFoodNetwork
class OrdersAndFulfillmentsReport
class DistributorTotalsBySupplierReport
REPORT_TYPE = "order_cycle_distributor_totals_by_supplier".freeze
attr_reader :context
def initialize(context)
@context = context
end
def header
[I18n.t(:report_header_hub), I18n.t(:report_header_producer),
I18n.t(:report_header_product), I18n.t(:report_header_variant),
I18n.t(:report_header_amount), I18n.t(:report_header_curr_cost_per_unit),
I18n.t(:report_header_total_cost), I18n.t(:report_header_total_shipping_cost),
I18n.t(:report_header_shipping_method)]
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def rules
[
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name },
summary_columns: [
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |line_items| line_items.map(&:order).uniq.sum(&:ship_total) },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name }
}
]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def columns
[proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" },
proc { |_line_items| I18n.t(:report_header_shipping_method) }]
end
# rubocop:enable Metrics/AbcSize
def line_item_includes
[:order, { variant: :product }]
end
end
end
end

View File

@@ -0,0 +1,76 @@
module OpenFoodNetwork
class OrdersAndFulfillmentsReport
class SupplierTotalsByDistributorReport
REPORT_TYPE = "order_cycle_supplier_totals_by_distributor".freeze
attr_reader :context
delegate :supplier_name, to: :context
def initialize(context)
@context = context
end
def header
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
I18n.t(:report_header_variant), I18n.t(:report_header_to_hub),
I18n.t(:report_header_amount), I18n.t(:report_header_curr_cost_per_unit),
I18n.t(:report_header_total_cost), I18n.t(:report_header_shipping_method)]
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def rules
[
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name },
summary_columns: [
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |_line_items| I18n.t('admin.reports.total') },
proc { |_line_items| "" },
proc { |_line_items| "" },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" }
]
},
{
group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name }
}
]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def columns
[
supplier_name,
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| I18n.t(:report_header_shipping_method) }
]
end
# rubocop:enable Metrics/AbcSize
def line_item_includes
[:order, { variant: :product }]
end
end
end
end

View File

@@ -0,0 +1,62 @@
module OpenFoodNetwork
class OrdersAndFulfillmentsReport
class SupplierTotalsReport
REPORT_TYPE = "order_cycle_supplier_totals".freeze
attr_reader :context
delegate :supplier_name, :product_name, :line_items_name, :total_units, to: :context
def initialize(context)
@context = context
end
def header
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
I18n.t(:report_header_variant), I18n.t(:report_header_amount),
I18n.t(:report_header_total_units), I18n.t(:report_header_curr_cost_per_unit),
I18n.t(:report_header_total_cost), I18n.t(:report_header_status),
I18n.t(:report_header_incoming_transport)]
end
# rubocop:disable Metrics/MethodLength
def rules
[
{
group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name }
},
{
group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name }
},
{
group_by: proc { |line_item| line_item.variant.full_name },
sort_by: proc { |full_name| full_name }
}
]
end
# rubocop:enable Metrics/MethodLength
# rubocop:disable Metrics/MethodLength
def columns
[
supplier_name,
product_name,
line_items_name,
proc { |line_items| line_items.sum(&:quantity) },
proc { |line_items| total_units(line_items) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum(&:amount) },
proc { |_line_items| "" },
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
]
end
# rubocop:enable Metrics/MethodLength
def line_item_includes
[{ variant: { product: :supplier } }]
end
end
end
end

View File

@@ -65,7 +65,11 @@ module OpenFoodNetwork
def filter_to_order_cycle(variants)
if params[:order_cycle_id].to_i > 0
order_cycle = OrderCycle.find params[:order_cycle_id]
variants.where(id: order_cycle.variants)
variant_ids = Exchange.in_order_cycle(order_cycle).
joins("INNER JOIN exchange_variants ON exchanges.id = exchange_variants.exchange_id").
select("DISTINCT exchange_variants.variant_id")
variants.where("spree_variants.id IN (#{variant_ids.to_sql})")
else
variants
end

View File

@@ -12,6 +12,10 @@ module OpenFoodNetwork
line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders))
line_items = line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
if params[:line_item_includes].present?
line_items = line_items.includes(*params[:line_item_includes])
end
hidden_line_items = line_items_with_hidden_details(permissions, line_items)
line_items.select{ |li|

View File

@@ -72,7 +72,7 @@ class ProductFactory
variant_unit: "weight",
variant_unit_scale: 1,
unit_value: 1,
shipping_category: Spree::ShippingCategory.find_or_create_by_name('Default')
shipping_category: DefaultShippingCategory.find_or_create
)
product = Spree::Product.create_with(params).find_or_create_by_name!(params[:name])
product.variants.first.update_attribute :on_demand, true

View File

@@ -5,13 +5,17 @@ module Api
include AuthenticationWorkflow
render_views
let!(:regular_user) { create(:user) }
let!(:admin_user) { create(:admin_user) }
let!(:distributor) { create(:distributor_enterprise) }
let!(:coordinator) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) }
describe '#index' do
let!(:distributor) { create(:distributor_enterprise) }
let!(:distributor2) { create(:distributor_enterprise) }
let!(:supplier) { create(:supplier_enterprise) }
let!(:coordinator) { create(:distributor_enterprise) }
let!(:coordinator2) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) }
let!(:supplier) { create(:supplier_enterprise) }
let!(:order_cycle2) { create(:simple_order_cycle, coordinator: coordinator2) }
let!(:order1) do
create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now,
@@ -45,8 +49,6 @@ module Api
create(:line_item_with_shipment, order: order3,
product: create(:product, supplier: supplier))
end
let!(:regular_user) { create(:user) }
let!(:admin_user) { create(:admin_user) }
context 'as a regular user' do
before do
@@ -156,6 +158,110 @@ module Api
end
end
describe "#show" do
let!(:order) { create(:completed_order_with_totals, order_cycle: order_cycle, distributor: distributor ) }
context "Resource not found" do
before { allow(controller).to receive(:spree_current_user) { admin_user } }
it "when no order number is given" do
get :show, id: nil
expect(response).to have_http_status(:not_found)
end
it "when order number given is not in the systen" do
get :show, id: "X1321313232"
expect(response).to have_http_status(:not_found)
end
end
context "access" do
it "returns unauthorized, as a regular user" do
allow(controller).to receive(:spree_current_user) { regular_user }
get :show, id: order.number
assert_unauthorized!
end
it "returns the order, as an admin user" do
allow(controller).to receive(:spree_current_user) { admin_user }
get :show, id: order.number
expect_order
end
it "returns the order, as the order distributor owner" do
allow(controller).to receive(:spree_current_user) { order.distributor.owner }
get :show, id: order.number
expect_order
end
it "returns unauthorized, as the order product's supplier owner" do
allow(controller).to receive(:spree_current_user) { order.line_items.first.variant.product.supplier.owner }
get :show, id: order.number
assert_unauthorized!
end
it "returns the order, as the Order Cycle coorinator owner" do
allow(controller).to receive(:spree_current_user) { order.order_cycle.coordinator.owner }
get :show, id: order.number
expect_order
end
end
context "as distributor owner" do
let!(:order) { create(:completed_order_with_fees, order_cycle: order_cycle, distributor: distributor ) }
before { allow(controller).to receive(:spree_current_user) { order.distributor.owner } }
it "can view an order not in a standard state" do
order.update_attributes(completed_at: nil, state: 'shipped')
get :show, id: order.number
expect_order
end
it "can view an order with weight calculator (this validates case where options[current_order] is nil on the shipping method serializer)" do
order.shipping_method.update_attribute(:calculator, create(:weight_calculator, calculable: order))
allow(controller).to receive(:current_order).and_return order
get :show, id: order.number
expect_order
end
it "returns an order with all required fields" do
get :show, id: order.number
expect_order
expect(json_response.symbolize_keys.keys).to include(*order_detailed_attributes)
expect(json_response[:bill_address]).to include(
'address1' => order.bill_address.address1,
'lastname' => order.bill_address.lastname
)
expect(json_response[:ship_address]).to include(
'address1' => order.ship_address.address1,
'lastname' => order.ship_address.lastname
)
expect(json_response[:shipping_method][:name]).to eq order.shipping_method.name
expect(json_response[:adjustments].first).to include(
'label' => "Transaction fee",
'amount' => order.adjustments.payment_fee.first.amount.to_s
)
expect(json_response[:adjustments].second).to include(
'label' => "Shipping",
'amount' => order.adjustments.shipping.first.amount.to_s
)
expect(json_response[:payments].first[:amount]).to eq order.payments.first.amount.to_s
expect(json_response[:line_items].size).to eq order.line_items.size
expect(json_response[:line_items].first[:variant][:product_name]). to eq order.line_items.first.variant.product.name
end
end
def expect_order
expect(response.status).to eq 200
expect(json_response[:number]).to eq order.number
end
end
private
def serialized_orders(orders)
@@ -181,5 +287,12 @@ module Api
:distributor_name, :special_instructions, :payment_capture_path
]
end
def order_detailed_attributes
[
:number, :item_total, :total, :state, :adjustment_total, :payment_total,
:completed_at, :shipment_state, :payment_state, :email, :special_instructions
]
end
end
end

View File

@@ -0,0 +1,72 @@
require 'spec_helper'
describe Spree::Admin::ImageSettingsController do
include AuthenticationWorkflow
before { login_as_admin }
context "updating image settings" do
it "should be able to update paperclip settings" do
spree_put :update, preferences: { "attachment_path" => "foo/bar",
"attachment_default_url" => "baz/bar" }
expect(Spree::Config[:attachment_path]).to eq("foo/bar")
expect(Spree::Config[:attachment_default_url]).to eq("baz/bar")
end
context "paperclip styles" do
it "should be able to update the paperclip styles" do
spree_put :update, "attachment_styles" => { "thumb" => "25x25>" }
updated_styles = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
expect(updated_styles["thumb"]).to eq("25x25>")
end
it "should be able to add a new style" do
spree_put :update, "attachment_styles" => {},
"new_attachment_styles" => { "1" => { "name" => "jumbo",
"value" => "2000x2000>" } }
styles = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
expect(styles["jumbo"]).to eq("2000x2000>")
end
end
context "amazon s3" do
after(:all) do
Spree::Image.attachment_definitions[:attachment].delete :storage
end
it "should be able to update s3 settings" do
spree_put :update, preferences:
{
"use_s3" => "1",
"s3_access_key" => "a_valid_key",
"s3_secret" => "a_secret",
"s3_bucket" => "some_bucket"
}
expect(Spree::Config[:use_s3]).to be_truthy
expect(Spree::Config[:s3_access_key]).to eq("a_valid_key")
expect(Spree::Config[:s3_secret]).to eq("a_secret")
expect(Spree::Config[:s3_bucket]).to eq("some_bucket")
end
context "headers" do
before(:each) { Spree::Config[:use_s3] = true }
it "should be able to update the s3 headers" do
spree_put :update, "preferences" => { "use_s3" => "1" },
"s3_headers" => { "Cache-Control" => "max-age=1111" }
headers = ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
expect(headers["Cache-Control"]).to eq("max-age=1111")
end
it "should be able to add a new header" do
spree_put :update, "s3_headers" => {},
"new_s3_headers" => { "1" => { "name" => "Charset",
"value" => "utf-8" } }
headers = ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
expect(headers["Charset"]).to eq("utf-8")
end
end
end
end
end

View File

@@ -0,0 +1,30 @@
require 'spec_helper'
describe Spree::Admin::MailMethodsController do
include AuthenticationWorkflow
before { login_as_admin }
context "#update" do
it "should reinitialize the mail settings" do
expect(Spree::Core::MailSettings).to receive(:init)
spree_put :update, enable_mail_delivery: "1", mails_from: "spree@example.com"
end
end
it "can trigger testmail" do
request.env["HTTP_REFERER"] = "/"
user = double('User', email: 'user@spree.com',
spree_api_key: 'fake',
id: nil,
owned_groups: nil)
allow(user).to receive_messages(enterprises: [create(:enterprise)], has_spree_role?: true)
allow(controller).to receive_messages(try_spree_current_user: user)
Spree::Config[:enable_mail_delivery] = "1"
ActionMailer::Base.perform_deliveries = true
expect {
spree_post :testmail
}.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end

View File

@@ -194,19 +194,6 @@ describe Spree::Admin::ProductsController, type: :controller do
end
end
describe "product variant unit is items" do
it "clears unit description of all variants of the product" do
product.variants.first.update_attribute :unit_description, "grams"
spree_put :update,
id: product,
product: {
variant_unit: "items",
variant_unit_name: "bag"
}
expect(product.reload.variants.first.unit_description).to be_empty
end
end
describe "product properties" do
context "as an enterprise user" do
let!(:property) { create(:property, name: "A nice name") }

View File

@@ -194,4 +194,9 @@ FactoryBot.modify do
# sets the default value for variant.on_demand
backorderable_default false
end
factory :shipping_category, class: Spree::ShippingCategory do
initialize_with { DefaultShippingCategory.find_or_create }
transient { name 'Default' }
end
end

View File

@@ -0,0 +1,38 @@
require 'spec_helper'
describe "General Settings" do
include AuthenticationWorkflow
before(:each) do
quick_login_as_admin
visit spree.admin_path
click_link "Configuration"
click_link "General Settings"
end
context "visiting general settings (admin)" do
it "should have the right content" do
expect(page).to have_content("General Settings")
expect(find("#site_name").value).to eq("Spree Demo Site")
expect(find("#site_url").value).to eq("demo.spreecommerce.com")
end
end
context "editing general settings (admin)" do
it "should be able to update the site name" do
fill_in "site_name", with: "Spree Demo Site99"
click_button "Update"
assert_successful_update_message(:general_settings)
expect(find("#site_name").value).to eq("Spree Demo Site99")
end
def assert_successful_update_message(resource)
flash = Spree.t(:successfully_updated, resource: Spree.t(resource))
within("[class='flash success']") do
expect(page).to have_content(flash)
end
end
end
end

View File

@@ -0,0 +1,40 @@
require 'spec_helper'
describe "image settings" do
include AuthenticationWorkflow
before do
quick_login_as_admin
visit spree.admin_path
click_link "Configuration"
click_link "Image Settings"
end
# Regression test for #2344
it "can update attachment_url" do
fill_in "Attachments URL", with: "foobar"
fill_in "Attachments Default URL", with: "barfoo"
fill_in "Attachments Path", with: "spec/dummy/tmp/bfaoro"
click_button "Update"
expect(Spree::Config[:attachment_url]).to eq("foobar")
expect(Spree::Config[:attachment_default_url]).to eq("barfoo")
expect(Spree::Config[:attachment_path]).to eq("spec/dummy/tmp/bfaoro")
end
# Regression test for #3069
context "updates style configs and uploads products" do
let!(:product) { create(:product) }
let(:file_path) { Rails.root + "spec/support/fixtures/thinking-cat.jpg" }
it "still uploads image gracefully" do
click_button "Update"
visit spree.new_admin_product_image_path(product)
attach_file('image_attachment', file_path)
expect {
click_on "Update"
}.to_not raise_error
end
end
end

View File

@@ -0,0 +1,32 @@
require 'spec_helper'
describe "Mail Methods" do
include AuthenticationWorkflow
before(:each) do
quick_login_as_admin
visit spree.admin_path
click_link "Configuration"
end
context "edit" do
before(:each) do
click_link "Mail Method Settings"
end
it "should be able to edit mail method settings" do
fill_in "mail_bcc", with: "spree@example.com99"
click_button "Update"
expect(page).to have_content("successfully updated!")
end
# Regression test for #2094
it "does not clear password if not provided" do
Spree::Config[:smtp_password] = "haxme"
click_button "Update"
expect(page).to have_content("successfully updated!")
expect(Spree::Config[:smtp_password]).not_to be_blank
end
end
end

View File

@@ -9,7 +9,7 @@ feature '
let!(:taxon) { create(:taxon) }
let!(:stock_location) { create(:stock_location, backorderable_default: false) }
let!(:shipping_category) { create(:shipping_category, name: 'Test Shipping Category') }
let!(:shipping_category) { DefaultShippingCategory.find_or_create }
background do
@supplier = create(:supplier_enterprise, name: 'New supplier')
@@ -32,6 +32,8 @@ feature '
click_link 'Products'
click_link 'New Product'
expect(find_field('product_shipping_category_id').text).to eq(shipping_category.name)
select 'New supplier', from: 'product_supplier_id'
fill_in 'product_name', with: 'A new product !!!'
select "Weight (kg)", from: 'product_variant_unit_with_scale'
@@ -40,7 +42,6 @@ feature '
fill_in 'product_price', with: '19.99'
fill_in 'product_on_hand', with: 5
select 'Test Tax Category', from: 'product_tax_category_id'
select 'Test Shipping Category', from: 'product_shipping_category_id'
page.find("div[id^='taTextElement']").native.send_keys('A description...')
click_button 'Create'
@@ -80,7 +81,6 @@ feature '
fill_in 'product_on_hand', with: 0
check 'product_on_demand'
select 'Test Tax Category', from: 'product_tax_category_id'
select 'Test Shipping Category', from: 'product_shipping_category_id'
page.find("div[id^='taTextElement']").native.send_keys('In demand, and on_demand! The hottest cakes in town.')
click_button 'Create'
@@ -122,7 +122,6 @@ feature '
select 'Weight (g)', from: 'product_variant_unit_with_scale'
fill_in 'product_unit_value_with_description', with: '500'
select taxon.name, from: "product_primary_taxon_id"
select 'Test Shipping Category', from: 'product_shipping_category_id'
select 'None', from: "product_tax_category_id"
# Should only have suppliers listed which the user can manage

View File

@@ -75,6 +75,7 @@ feature 'shipping methods' do
let(:sm1) { create(:shipping_method, name: 'One', distributors: [distributor1]) }
let(:sm2) { create(:shipping_method, name: 'Two', distributors: [distributor1, distributor2]) }
let(:sm3) { create(:shipping_method, name: 'Three', distributors: [distributor3]) }
let(:shipping_category) { create(:shipping_category) }
before(:each) do
enterprise_user.enterprise_roles.build(enterprise: distributor1).save
@@ -97,10 +98,12 @@ feature 'shipping methods' do
expect(page).to have_css 'div#shipping_method_zones_field'
expect(page).to have_field 'shipping_method_require_ship_address_true', checked: true
# Auto-check default shipping category
expect(page).to have_field shipping_category.name, checked: true
fill_in 'shipping_method_name', with: 'Teleport'
check "shipping_method_distributor_ids_#{distributor1.id}"
check "shipping_method_shipping_categories_"
find(:css, "tags-input .tags input").set "local\n"
within(".tags .tag-list") do
expect(page).to have_css '.tag-item', text: "local"

View File

@@ -9,11 +9,11 @@ feature '
scenario "creating a new variant" do
# Given a product with a unit-related option type
p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
# When I create a variant on the product
login_to_admin_section
visit spree.admin_product_variants_path p
visit spree.admin_product_variants_path product
click_link 'New Variant'
fill_in 'unit_value_human', with: '1'
@@ -21,40 +21,58 @@ feature '
click_button 'Create'
# Then the variant should have been created
expect(page).to have_content "Variant \"#{p.name}\" has been successfully created!"
expect(page).to have_content "Variant \"#{product.name}\" has been successfully created!"
end
scenario "editing unit value and description for a variant", js: true do
# Given a product with unit-related option types, with a variant
p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
v = p.variants.first
v.update_attributes( unit_value: 1, unit_description: 'foo' )
describe "editing unit value and description for a variant", js: true do
scenario "when variant_unit is weight" do
# Given a product with unit-related option types, with a variant
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
variant = product.variants.first
variant.update_attributes( unit_value: 1, unit_description: 'foo' )
# And the product has option types for the unit-related and non-unit-related option values
p.option_types << v.option_values.first.option_type
# And the product has option types for the unit-related and non-unit-related option values
product.option_types << variant.option_values.first.option_type
# When I view the variant
login_to_admin_section
visit spree.admin_product_variants_path p
page.find('table.index .icon-edit').click
# When I view the variant
login_to_admin_section
visit spree.admin_product_variants_path product
page.find('table.index .icon-edit').click
# Then I should not see a traditional option value field for the unit-related option value
expect(page).to have_no_selector "div[data-hook='presentation'] input"
# Then I should not see a traditional option value field for the unit-related option value
expect(page).to have_no_selector "div[data-hook='presentation'] input"
# And I should see unit value and description fields for the unit-related option value
expect(page).to have_field "unit_value_human", with: "1"
expect(page).to have_field "variant_unit_description", with: "foo"
# And I should see unit value and description fields for the unit-related option value
expect(page).to have_field "unit_value_human", with: "1"
expect(page).to have_field "variant_unit_description", with: "foo"
# When I update the fields and save the variant
fill_in "unit_value_human", with: "123"
fill_in "variant_unit_description", with: "bar"
click_button 'Update'
expect(page).to have_content %(Variant "#{p.name}" has been successfully updated!)
# When I update the fields and save the variant
fill_in "unit_value_human", with: "123"
fill_in "variant_unit_description", with: "bar"
click_button 'Update'
expect(page).to have_content %(Variant "#{product.name}" has been successfully updated!)
# Then the unit value and description should have been saved
v.reload
expect(v.unit_value).to eq(123)
expect(v.unit_description).to eq('bar')
# Then the unit value and description should have been saved
expect(variant.reload.unit_value).to eq(123)
expect(variant.unit_description).to eq('bar')
end
scenario "can update unit_description when variant_unit is items" do
product = create(:simple_product, variant_unit: "items", variant_unit_name: "bunches")
variant = product.variants.first
variant.update_attributes(unit_description: 'foo')
login_to_admin_section
visit spree.edit_admin_product_variant_path(product, variant)
expect(page).to_not have_field "unit_value_human"
expect(page).to have_field "variant_unit_description", with: "foo"
fill_in "variant_unit_description", with: "bar"
click_button 'Update'
expect(page).to have_content %(Variant "#{product.name}" has been successfully updated!)
expect(variant.reload.unit_description).to eq('bar')
end
end
describe "editing on hand and on demand values", js: true do
@@ -99,31 +117,29 @@ feature '
end
it "soft-deletes variants", js: true do
p = create(:simple_product)
v = create(:variant, product: p)
product = create(:simple_product)
variant = create(:variant, product: product)
login_to_admin_section
visit spree.admin_product_variants_path p
visit spree.admin_product_variants_path product
within "tr#spree_variant_#{v.id}" do
within "tr#spree_variant_#{variant.id}" do
accept_alert do
page.find('a.delete-resource').click
end
end
expect(page).not_to have_selector "tr#spree_variant_#{v.id}"
v.reload
expect(v.deleted_at).not_to be_nil
expect(page).not_to have_selector "tr#spree_variant_#{variant.id}"
expect(variant.reload.deleted_at).not_to be_nil
end
scenario "editing display name for a variant", js: true do
p = create(:simple_product)
v = p.variants.first
product = create(:simple_product)
variant = product.variants.first
# When I view the variant
login_to_admin_section
visit spree.admin_product_variants_path p
visit spree.admin_product_variants_path product
page.find('table.index .icon-edit').click
# It should allow the display name to be changed
@@ -134,11 +150,10 @@ feature '
fill_in "variant_display_name", with: "Display Name"
fill_in "variant_display_as", with: "Display As This"
click_button 'Update'
expect(page).to have_content %(Variant "#{p.name}" has been successfully updated!)
expect(page).to have_content %(Variant "#{product.name}" has been successfully updated!)
# Then the displayed values should have been saved
v.reload
expect(v.display_name).to eq("Display Name")
expect(v.display_as).to eq("Display As This")
expect(variant.reload.display_name).to eq("Display Name")
expect(variant.display_as).to eq("Display As This")
end
end

View File

@@ -0,0 +1,39 @@
require "spec_helper"
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::CustomerTotalsReport do
let!(:distributor) { create(:distributor_enterprise) }
let!(:customer) { create(:customer, enterprise: distributor) }
let!(:order) do
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
customer: customer, distributor: distributor)
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
end
let(:report_table) do
OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items)
end
it "generates the report" do
expect(report_table.length).to eq(2)
end
it "has a line item row" do
distributor_name_field = report_table.first[0]
expect(distributor_name_field).to eq distributor.name
customer_name_field = report_table.first[1]
expect(customer_name_field).to eq order.bill_address.full_name
total_field = report_table.last[5]
expect(total_field).to eq I18n.t("admin.reports.total")
end
end

View File

@@ -0,0 +1,37 @@
require "spec_helper"
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::DistributorTotalsBySupplierReport do
let!(:distributor) { create(:distributor_enterprise) }
let!(:order) do
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
end
let(:report_table) do
OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items)
end
it "generates the report" do
expect(report_table.length).to eq(2)
end
it "has a variant row under the distributor" do
distributor_name_field = report_table.first[0]
expect(distributor_name_field).to eq distributor.name
supplier = order.line_items.first.variant.product.supplier
supplier_name_field = report_table.first[1]
expect(supplier_name_field).to eq supplier.name
total_field = report_table.last[1]
expect(total_field).to eq I18n.t("admin.reports.total")
end
end

View File

@@ -0,0 +1,37 @@
require "spec_helper"
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::SupplierTotalsByDistributorReport do
let!(:distributor) { create(:distributor_enterprise) }
let!(:order) do
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
end
let(:report_table) do
OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items)
end
it "generates the report" do
expect(report_table.length).to eq(2)
end
it "has a variant row under the distributor" do
supplier = order.line_items.first.variant.product.supplier
supplier_name_field = report_table.first[0]
expect(supplier_name_field).to eq supplier.name
distributor_name_field = report_table.first[3]
expect(distributor_name_field).to eq distributor.name
total_field = report_table.last[3]
expect(total_field).to eq I18n.t("admin.reports.total")
end
end

View File

@@ -0,0 +1,31 @@
require "spec_helper"
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentsReport::SupplierTotalsReport do
let!(:distributor) { create(:distributor_enterprise) }
let!(:order) do
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
end
let(:current_user) { distributor.owner }
let(:permissions) { OpenFoodNetwork::Permissions.new(current_user) }
let(:report) do
report_options = { report_type: described_class::REPORT_TYPE }
OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions, report_options, true)
end
let(:report_table) do
OpenFoodNetwork::OrderGrouper.new(report.rules, report.columns).table(report.table_items)
end
it "generates the report" do
expect(report_table.length).to eq(1)
end
it "has a variant row" do
supplier = order.line_items.first.variant.product.supplier
supplier_name_field = report_table.first[0]
expect(supplier_name_field).to eq supplier.name
end
end

View File

@@ -171,10 +171,48 @@ module OpenFoodNetwork
end
it "should do all the filters at once" do
# The following data ensures that this spec fails if any of the
# filters fail. It's testing the filters are not impacting each other.
distributor = create(:distributor_enterprise)
product1 = create(:simple_product, supplier: supplier)
product2 = create(:simple_product, supplier: supplier)
order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.variants.first])
other_distributor = create(:distributor_enterprise)
other_supplier = create(:supplier_enterprise)
not_filtered_variant = create(:simple_product, supplier: supplier).variants.first
variant_filtered_by_order_cycle = create(:simple_product, supplier: supplier).variants.first
variant_filtered_by_distributor = create(:simple_product, supplier: supplier).variants.first
variant_filtered_by_supplier = create(:simple_product, supplier: other_supplier).variants.first
variant_filtered_by_stock = create(:simple_product, supplier: supplier, on_hand: 0).variants.first
# This OC contains all products except the one that should be filtered
# by order cycle. We create a separate OC further down to proof that
# the product is passing all other filters.
order_cycle = create(
:simple_order_cycle,
suppliers: [supplier, other_supplier],
distributors: [distributor, other_distributor],
variants: [
not_filtered_variant,
variant_filtered_by_distributor,
variant_filtered_by_supplier,
variant_filtered_by_stock
]
)
# Remove the distribution of one product for one distributor but still
# sell it through the other distributor.
order_cycle.exchanges.outgoing.find_by_receiver_id(distributor.id).
exchange_variants.
find_by_variant_id(variant_filtered_by_distributor).
destroy
# Make product available to be filtered later. See OC comment above.
create(
:simple_order_cycle,
suppliers: [supplier],
distributors: [distributor, other_distributor],
variants: [
variant_filtered_by_order_cycle
]
)
allow(subject).to receive(:params).and_return(
order_cycle_id: order_cycle.id,
@@ -182,7 +220,11 @@ module OpenFoodNetwork
distributor_id: distributor.id,
report_type: 'inventory'
)
subject.filter(variants)
expect(subject.filter(variants)).to match_array [not_filtered_variant]
# And it integrates with the ordering of the `variants` method.
expect(subject.variants).to match_array [not_filtered_variant]
end
end

View File

@@ -48,7 +48,7 @@ module Stock
describe '#shipping_categories' do
it "returns shipping categories that are not shipping categories of the order's products" do
package
other_shipping_category = create(:shipping_category)
other_shipping_category = Spree::ShippingCategory.create(name: "Custom")
expect(package.shipping_categories).to eq [shipping_method1.shipping_categories.first,
other_shipping_category]

View File

@@ -0,0 +1,38 @@
require 'spec_helper'
describe DefaultShippingCategory do
describe '.create!' do
it "names the location 'Default'" do
shipping_category = described_class.create!
expect(shipping_category.name).to eq 'Default'
end
end
describe 'find_or_create' do
context 'when a Default category already exists' do
let!(:category) do
Spree::ShippingCategory.create!(name: 'Default')
end
it 'returns the category' do
expect(described_class.find_or_create).to eq category
end
it 'does not create another category' do
expect { described_class.find_or_create }.not_to change(Spree::ShippingCategory, :count)
end
end
context 'when a Default category does not exist' do
it 'returns the category' do
category = described_class.find_or_create
expect(category.name).to eq 'Default'
end
it 'does not create another category' do
expect { described_class.find_or_create }
.to change(Spree::ShippingCategory, :count).from(0).to(1)
end
end
end
end