Compare commits

...

266 Commits

Author SHA1 Message Date
Matt-Yorkley
7e7eb67e0a Merge pull request #4714 from luisramos0/fix_permissions_load_problem
Add require to fix loading issue. Some other Permissions module was being loaded
2020-01-23 15:56:27 +01:00
luisramos0
1548eaeb7e Add frozen_string_literal magic comment 2020-01-23 14:42:19 +00:00
luisramos0
4b0ddcda01 Add require to fix loading issue. Some other Permissions module was being loaded 2020-01-23 11:42:10 +00:00
Luis Ramos
f5adf6a9d7 Merge pull request #4697 from mkllnk/4457-line-item-spec
Stabilise spec by not creating noise data
2020-01-23 11:35:34 +00:00
Matt-Yorkley
86231672a6 Merge pull request #4703 from openfoodfoundation/daniellemoorhead-patch-1
Updated shopfront message placeholder for home tab
2020-01-23 12:32:08 +01:00
Luis Ramos
e47bd019a1 Merge pull request #4656 from mkllnk/config
Use strings for all environment variables
2020-01-23 11:07:17 +00:00
Matt-Yorkley
0011caccc6 Update all locales with the latest Transifex translations 2020-01-23 11:45:58 +01:00
Matt-Yorkley
ad5a599ddf Merge pull request #4710 from openfoodfoundation/transifex
Transifex
2020-01-23 11:37:10 +01:00
Luis Ramos
43d7609b81 Merge pull request #4707 from Matt-Yorkley/mobile-checkout-tabs
Remove tabs from cart and checkout
2020-01-23 10:19:50 +00:00
Transifex-Openfoodnetwork
56d17bca6a Updating translations for config/locales/nb.yml 2020-01-23 19:43:37 +11:00
Transifex-Openfoodnetwork
7f4e53f411 Updating translations for config/locales/fr.yml 2020-01-23 19:41:23 +11:00
Transifex-Openfoodnetwork
bd16b80d5f Updating translations for config/locales/en_FR.yml 2020-01-23 19:38:34 +11:00
Transifex-Openfoodnetwork
c8a1f9b93c Updating translations for config/locales/fr.yml 2020-01-23 19:38:12 +11:00
Maikel
9a8cd60d5a Merge pull request #4694 from Matt-Yorkley/rails-4-prep-routes
Rails 4 prep: spree routes
2020-01-23 17:42:37 +11:00
Maikel
4d11dced39 Merge pull request #4687 from openfoodfoundation/dependabot/bundler/ddtrace-0.31.1
Bump ddtrace from 0.31.0 to 0.31.1
2020-01-23 16:35:38 +11:00
Maikel
de2f09db75 Merge pull request #4698 from openfoodfoundation/transifex
Transifex
2020-01-23 15:59:36 +11:00
Luis Ramos
494cb00774 Merge pull request #4585 from luisramos0/backend_partial_calc_fields
Bring spree_backend calculator fields partial to OFN
2020-01-22 20:27:10 +00:00
Luis Ramos
d782bea458 Merge pull request #4633 from luisramos0/fix_prod_link_bug
Fix bug in frontoffice order details where the product image was a broken link
2020-01-22 20:25:24 +00:00
Matt-Yorkley
444747c8b2 Split shopping_shared/details partial into header and tabs partials and only include tabs in shop page (not cart and checkout). 2020-01-22 11:30:43 +01:00
Danni M
95f51e2db9 Updated shopfront message placeholder
Change required now that there is a home tab instead of a green shopfront message.

>An optional message to welcome customers and explain how to shop with you. If text is entered here it will be displayed in a home tab when customers first arrive at your shopfront.
2020-01-22 14:24:20 +11:00
Transifex-Openfoodnetwork
0463e05296 Updating translations for config/locales/pt_BR.yml 2020-01-22 10:35:12 +11:00
Transifex-Openfoodnetwork
f176e2ba91 Updating translations for config/locales/pt_BR.yml 2020-01-22 10:32:03 +11:00
Maikel Linke
30888a9e59 Stabilise spec by not creating noise data
We were using a more complex order factory that created more data than
we needed. The order contained a second line item and therefor the
manifest had two items as well. Sometimes we would pick the wrong one.
2020-01-22 09:49:37 +11:00
Luis Ramos
6bb4f2477c Merge pull request #4586 from luisramos0/states_erb
Make states/new.js template depend on the states/new.haml template in OFN (not the states/new.erb in spree)
2020-01-21 22:47:28 +00:00
Matt-Yorkley
cca824f207 Ensure routes declared with match specify a method 2020-01-20 17:32:00 +01:00
Matt-Yorkley
147f7d4850 Separate non-report routes from reports route list for clarity 2020-01-20 17:30:46 +01:00
Matt-Yorkley
8024dcec64 Merge double Spree routes declarations into one
The app has a lot of problems with this in Rails 4
2020-01-20 17:27:26 +01:00
Luis Ramos
1c3fd26eef Merge pull request #4655 from Matt-Yorkley/mobile-shoptabs
Mobile: shop tabs
2020-01-20 08:51:59 +00:00
Matt-Yorkley
f8145f8429 Update shop tab button styling in mobile 2020-01-19 15:59:37 +01:00
Matt-Yorkley
a1256f9aeb Remove old green box styling from shopfront messages 2020-01-19 15:57:01 +01:00
dependabot-preview[bot]
094c175a64 Bump ddtrace from 0.31.0 to 0.31.1
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.31.0 to 0.31.1.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/v0.31.1/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.31.0...v0.31.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-17 19:14:25 +00:00
Luis Ramos
dd9f2a063f Merge pull request #4682 from openfoodfoundation/dependabot/bundler/mini_racer-0.2.9
Bump mini_racer from 0.2.4 to 0.2.9
2020-01-17 13:36:02 +00:00
Luis Ramos
4973bcb9b6 Merge pull request #4677 from openfoodfoundation/dependabot/bundler/oj-3.10.1
Bump oj from 3.10.0 to 3.10.1
2020-01-17 13:14:15 +00:00
Maikel
9ee701bec1 Merge pull request #4683 from openfoodfoundation/dependabot/bundler/webmock-3.8.0
Bump webmock from 3.7.6 to 3.8.0
2020-01-17 09:14:50 +11:00
Maikel
e2469ded9a Merge pull request #4680 from openfoodfoundation/transifex
Transifex
2020-01-17 09:03:10 +11:00
dependabot-preview[bot]
eaa914a360 Bump webmock from 3.7.6 to 3.8.0
Bumps [webmock](https://github.com/bblimke/webmock) from 3.7.6 to 3.8.0.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.7.6...v3.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-16 19:19:26 +00:00
dependabot-preview[bot]
ea2fa9913d Bump mini_racer from 0.2.4 to 0.2.9
Bumps [mini_racer](https://github.com/discourse/mini_racer) from 0.2.4 to 0.2.9.
- [Release notes](https://github.com/discourse/mini_racer/releases)
- [Changelog](https://github.com/rubyjs/mini_racer/blob/master/CHANGELOG)
- [Commits](https://github.com/discourse/mini_racer/compare/v0.2.4...v0.2.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-16 19:15:52 +00:00
Matt-Yorkley
b69b5ea81a Merge pull request #4678 from mkllnk/4490-tag-rules-spec
4490 tag rules spec
2020-01-16 14:28:37 +01:00
Luis Ramos
18b3a8122d Merge pull request #4508 from luisramos0/backend_ctrl_prods_vars
Bring spree_backend products and variants controllers to OFN
2020-01-16 13:01:31 +00:00
luisramos0
1e203ae419 Remove destroy with js format in variants controller, we dont use this endpoint in OFN and it avoids having to bring additional code from spree_backend 2020-01-16 11:59:36 +00:00
luisramos0
70a24b25e2 Update rubocop manual todo and Remove some unnecessary comments 2020-01-16 11:59:36 +00:00
luisramos0
f7490ae28b Remove unused stock and destroy actions and bring product routes from spree_backend 2020-01-16 11:58:59 +00:00
luisramos0
836abbec92 Fix rubocop issues in variant controller 2020-01-16 11:58:59 +00:00
luisramos0
d153237b69 Merge variants decorator with controller 2020-01-16 11:58:59 +00:00
luisramos0
56b6bf82bb Fix some rubocop issues 2020-01-16 11:58:59 +00:00
luisramos0
0d44f298fe Merge spree/admin/products_controller with its decorator 2020-01-16 11:58:59 +00:00
luisramos0
ff3611f52a Add variants_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-01-16 11:58:59 +00:00
luisramos0
7fa9b4699c Add products_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-01-16 11:58:59 +00:00
Transifex-Openfoodnetwork
3dadb7f91c Updating translations for config/locales/en_GB.yml 2020-01-16 20:12:18 +11:00
Transifex-Openfoodnetwork
dd5c867298 Updating translations for config/locales/en_GB.yml 2020-01-16 20:09:10 +11:00
Transifex-Openfoodnetwork
918a0f053d Updating translations for config/locales/ar.yml 2020-01-16 18:25:27 +11:00
Transifex-Openfoodnetwork
2171125e85 Updating translations for config/locales/ar.yml 2020-01-16 18:22:18 +11:00
Transifex-Openfoodnetwork
5d0cb3549a Updating translations for config/locales/ar.yml 2020-01-16 18:19:07 +11:00
Maikel Linke
12fde5b9fc Update all locales with the latest Transifex translations 2020-01-16 15:28:57 +11:00
Maikel Linke
6b18d4a60c Avoid false positives in specs
When switching from one tab to another, a `have_no_selector` check could
match because the new tab hasn't loaded yet. Now we use that check after
we verified other content on the new tab.
2020-01-16 15:10:25 +11:00
Maikel Linke
4d53f47d88 Leave tag rules spec in clean state 2020-01-16 15:10:25 +11:00
Maikel Linke
3a9aaf6764 Wait for the first enterprise tab to be loaded 2020-01-16 15:10:25 +11:00
Maikel Linke
42aa15767e DRY navigating to Tag Rules section 2020-01-16 15:10:25 +11:00
Maikel
0da6275d41 Merge pull request #4675 from Matt-Yorkley/rails-4-prep-3
Rails 4 prep continued
2020-01-16 15:08:11 +11:00
Maikel
52d2d1d23c Merge pull request #4668 from Matt-Yorkley/rails-4-prep-2
Rails 4 prep: admin dashboard route
2020-01-16 14:16:32 +11:00
Maikel
4dfc020eaa Merge pull request #4666 from openfoodfoundation/transifex
Transifex
2020-01-16 12:10:12 +11:00
Maikel
8ef52f97e5 Merge pull request #4665 from openfoodfoundation/dependabot/bundler/selenium-webdriver-3.142.7
Bump selenium-webdriver from 3.142.6 to 3.142.7
2020-01-16 12:09:29 +11:00
Maikel
68a7f547b1 Merge pull request #4664 from openfoodfoundation/dependabot/bundler/test-unit-3.3.5
Bump test-unit from 3.3.4 to 3.3.5
2020-01-16 12:08:51 +11:00
Maikel
7a0f2d47a3 Merge pull request #4660 from openfoodfoundation/dependabot/bundler/activerecord-import-1.0.4
Bump activerecord-import from 1.0.3 to 1.0.4
2020-01-16 12:06:15 +11:00
Luis Ramos
4affd01b7b Merge pull request #4627 from luisramos0/mini_racer
Update libv8 and keep it only in Gemfile.lock as a dependency of mini_racer
2020-01-15 22:25:25 +00:00
Luis Ramos
0432c73f19 Merge pull request #4510 from luisramos0/backend_ctrl_resource
Bring spree_backend resource controller to OFN
2020-01-15 22:22:24 +00:00
dependabot-preview[bot]
6163661610 Bump oj from 3.10.0 to 3.10.1
Bumps [oj](https://github.com/ohler55/oj) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/ohler55/oj/releases)
- [Changelog](https://github.com/ohler55/oj/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/ohler55/oj/compare/v3.10.0...v3.10.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-15 19:15:31 +00:00
Matt-Yorkley
103902c006 Replace deprecated arguments in #find_by
Failure/Error: enterprise = Enterprise.find_by(name: enterprise_name, select: 'id, is_primary_producer')

       ActiveRecord::StatementInvalid:
         PG::UndefinedColumn: ERROR:  column enterprises.select does not exist
         LINE 1: ...HERE "enterprises"."name" = 'User Enterprise' AND "enterpris...
         : SELECT  "enterprises".* FROM "enterprises"  WHERE "enterprises"."name" = 'User Enterprise' AND "enterprises"."select" = 'id, is_primary_producer' LIMIT 1
       # ./app/models/product_import/spreadsheet_data.rb:48:in `block in create_enterprises_index'
       # ./app/models/product_import/spreadsheet_data.rb:43:in `each'
2020-01-15 16:39:33 +01:00
Matt-Yorkley
d2933e35f1 Explicitly require Spree::Admin::BaseHelper
NameError: undefined method `preference_field_tag' for module `Spree::Admin::BaseHelper'
/home/runner/openfoodnetwork/vendor/bundle/ruby/2.3.0/gems/activesupport-4.0.13/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method'
/home/runner/openfoodnetwork/vendor/bundle/ruby/2.3.0/gems/activesupport-4.0.13/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method_chain'
/home/runner/openfoodnetwork/app/helpers/spree/admin/base_helper_decorator.rb:11:in `<module:BaseHelper>'
/home/runner/openfoodnetwork/app/helpers/spree/admin/base_helper_decorator.rb:3:in `<module:Admin>'
2020-01-15 13:07:50 +01:00
Matt-Yorkley
0b5e341ae7 Explicitly require Spree::Admin::NavigationHelper
NameError: undefined method `klass_for' for module `Spree::Admin::NavigationHelper'
/home/runner/openfoodnetwork/vendor/bundle/ruby/2.3.0/gems/activesupport-4.0.13/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method'
/home/runner/openfoodnetwork/vendor/bundle/ruby/2.3.0/gems/activesupport-4.0.13/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method_chain'
/home/runner/openfoodnetwork/app/helpers/spree/admin/navigation_helper_decorator.rb:18:in `<module:NavigationHelper>'
2020-01-15 13:07:46 +01:00
Matt-Yorkley
7d36c3b5aa Make some private methods public in orders_and_fulfillments_report.rb
Fixes errors for "call to private method ..."
2020-01-15 13:05:36 +01:00
Matt-Yorkley
d511763733 Replace deprecated #in? method 2020-01-15 12:57:38 +01:00
Matt-Yorkley
a059c11d0f Update post/delete calls in credit_cards_controller_spec.rb
ActionController::UrlGenerationError:
       No route matches {:action=>"new_from_token", :cc_type=>"visa", :controller=>"spree/credit_cards", :exp_month=>12, :exp_year=>2020, :format=>:json, :last4=>4242, :token=>"tok_234bd2c22"}
     # ./spec/controllers/spree/credit_cards_controller_spec.rb:36:in `block (5 levels) in <top (required)>'
     # ./spec/controllers/spree/credit_cards_controller_spec.rb:36:in `block (4 levels) in <top (required)>'
2020-01-15 12:57:38 +01:00
Matt-Yorkley
cecc19ae1d Add recommended paper_trail config
DEPRECATION WARNING: PaperTrail.track_associations has not been set. As of PaperTrail 5, it defaults to false. Tracking associations is an experimental feature so we recommend setting PaperTrail.config.track_associations = false in your config/initializers/paper_trail.rb . (called from block (3 levels) in <top (required)> at /home/user/Github/openfoodnetwork/spec/controllers/admin/column_preferences_controller_spec.rb:10)
2020-01-15 12:57:38 +01:00
Matt-Yorkley
d4311a848e Fix default_scope deprecated syntax 2020-01-15 12:57:38 +01:00
Transifex-Openfoodnetwork
d64573f7fd Updating translations for config/locales/nb.yml 2020-01-15 22:29:37 +11:00
Transifex-Openfoodnetwork
a9fe6ec1b5 Updating translations for config/locales/nb.yml 2020-01-15 22:26:30 +11:00
Transifex-Openfoodnetwork
f14bbc5ed9 Updating translations for config/locales/nb.yml 2020-01-15 22:23:20 +11:00
Matt-Yorkley
5ba8efec2c Fix enterprise group spec
EnterpriseGroup.by_position now returns an ActiveRecord::Relation in Rails 4, so in this test case it doesn't respond as an array
2020-01-15 11:19:14 +01:00
Luis Ramos
3bf38b7c08 Merge pull request #4549 from luisramos0/arelize_order_permissions
Use arel in order permissions visible orders and editable orders so that we dont have queries with gigantic IN clauses
2020-01-14 14:24:34 +00:00
Transifex-Openfoodnetwork
a591e0736f Updating translations for config/locales/en_CA.yml 2020-01-14 23:41:38 +11:00
Luis Ramos
3080eb9dfd Merge pull request #4578 from kshlyk/remove_soft_delete_from_product_and_variant_api
Removing duplicate API method soft_delete for both products and variants
2020-01-14 11:32:10 +00:00
Transifex-Openfoodnetwork
ed98a16eec Updating translations for config/locales/fr.yml 2020-01-14 20:18:13 +11:00
Transifex-Openfoodnetwork
7a924bd9ca Updating translations for config/locales/fr.yml 2020-01-14 20:15:05 +11:00
Transifex-Openfoodnetwork
7639e19184 Updating translations for config/locales/en_FR.yml 2020-01-14 20:13:12 +11:00
Transifex-Openfoodnetwork
71aff7e1d2 Updating translations for config/locales/fr.yml 2020-01-14 20:11:56 +11:00
dependabot-preview[bot]
2506667bca Bump selenium-webdriver from 3.142.6 to 3.142.7
Bumps [selenium-webdriver](https://github.com/SeleniumHQ/selenium) from 3.142.6 to 3.142.7.
- [Release notes](https://github.com/SeleniumHQ/selenium/releases)
- [Changelog](https://github.com/SeleniumHQ/selenium/blob/master/rb/CHANGES)
- [Commits](https://github.com/SeleniumHQ/selenium/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-13 19:37:48 +00:00
Luis Ramos
f87a553230 Merge pull request #4648 from openfoodfoundation/dependabot/bundler/rubocop-0.79.0
Bump rubocop from 0.68.1 to 0.79.0
2020-01-13 19:35:50 +00:00
Luis Ramos
3171b60d6f Merge pull request #4580 from Matt-Yorkley/order_capture
Use asynchronous requests for order capture and ship actions
2020-01-13 17:14:40 +00:00
Matt-Yorkley
94afc70ea7 Refactor conditionals in contact tab view 2020-01-13 17:14:22 +01:00
Matt-Yorkley
d14d6fe865 Refactor open order cycle checks 2020-01-13 17:11:27 +01:00
Matt-Yorkley
0546acd6f9 Refactor #shop_tabs use and remove #first_visible_tab 2020-01-13 17:05:28 +01:00
Matt-Yorkley
aedaec8365 Improve layout of orders closed message 2020-01-13 17:05:28 +01:00
Matt-Yorkley
d508e7772c Fix angular templates bug
Angular controller data was being partially preserved when switching back and forth between tab templates, causing the ProductsCtrl to hold duplicate datasets when it is re-initialized after going from the shop tab to another tab, then back again.
2020-01-13 17:05:28 +01:00
Matt-Yorkley
43eef0c8c6 Adjust layout for fullwidth enterprise image 2020-01-13 17:05:28 +01:00
Matt-Yorkley
e658893e70 Fix ancient display bug where the order cycle selection popover shows over the top of the floating navigtion bar 2020-01-13 17:05:28 +01:00
Matt-Yorkley
e7d88f0df9 Move "orders closed" message to "shop" tab and adjust "home" tab message display logic 2020-01-13 17:05:28 +01:00
Matt-Yorkley
56223bb960 Improve about tab mobile and tablet display 2020-01-13 17:05:28 +01:00
Matt-Yorkley
17cf099f99 Always keep one shop tab open (don't toggle closed) 2020-01-13 17:05:28 +01:00
Matt-Yorkley
49aacf879f Change row nesting to fix product list layout display issues 2020-01-13 17:05:28 +01:00
Matt-Yorkley
c8c4f3e746 Improve mobile display 2020-01-13 17:05:28 +01:00
Matt-Yorkley
219f6ad78e Select first visible tab on page load if none are specified in the URL 2020-01-13 17:05:27 +01:00
Matt-Yorkley
48f329a716 Rework layout and CSS 2020-01-13 17:05:26 +01:00
Luis Ramos
516398fbd6 Merge pull request #4638 from luisramos0/fix_cart_s2
Replace adjustment.open with adjustment.fire_events(open) to avoid method name conflict
2020-01-13 14:16:56 +00:00
Luis Ramos
4a1b74c136 Merge pull request #4634 from luisramos0/remove_dead_code
Remove some dead code
2020-01-13 11:09:04 +00:00
Matt-Yorkley
b9edea7c0e Rename 'admin/overview#index' route from :admin to :admin_dashboard
Fixes issues with route declarations in Rails 4
2020-01-12 13:11:33 +01:00
Transifex-Openfoodnetwork
b9053f9fd2 Updating translations for config/locales/en_NZ.yml 2020-01-12 18:37:55 +11:00
dependabot-preview[bot]
f297cff8c7 Bump test-unit from 3.3.4 to 3.3.5
Bumps [test-unit](https://github.com/test-unit/test-unit) from 3.3.4 to 3.3.5.
- [Release notes](https://github.com/test-unit/test-unit/releases)
- [Commits](https://github.com/test-unit/test-unit/compare/3.3.4...3.3.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-10 19:17:47 +00:00
Luis Ramos
7563d38b4b Merge pull request #4658 from openfoodfoundation/dependabot/bundler/ddtrace-0.31.0
Bump ddtrace from 0.30.0 to 0.31.0
2020-01-10 10:52:04 +00:00
Luis Ramos
83523a676f Merge pull request #4659 from openfoodfoundation/dependabot/bundler/webdrivers-4.2.0
Bump webdrivers from 4.1.3 to 4.2.0
2020-01-10 10:51:10 +00:00
Maikel Linke
abcfb5ce8d Update Rubocop config for new version
- A couple of cops moved into a different namespace.
- The target ruby version doesn't have to be specified, it's read from
  .ruby-version which has been updated to Ruby 2.3.
- Some files were missing in the todo lists.
2020-01-10 08:59:44 +11:00
Maikel Linke
8ee6d1c320 Add rubocop-rails gem
The rails cops used to be in the rubocop gem itself but moved into their
own gem recently. Our style guide refers to these cops though.
2020-01-10 08:59:44 +11:00
dependabot-preview[bot]
1bc19ad6a4 Bump rubocop from 0.68.1 to 0.79.0
Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 0.68.1 to 0.79.0.
- [Release notes](https://github.com/rubocop-hq/rubocop/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.68.1...v0.79.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-10 08:59:44 +11:00
Maikel
191b8064ed Merge pull request #4629 from openfoodfoundation/dependabot/bundler/unicorn-5.5.2
Bump unicorn from 5.5.1 to 5.5.2
2020-01-10 08:29:17 +11:00
dependabot-preview[bot]
e28274db14 Bump activerecord-import from 1.0.3 to 1.0.4
Bumps [activerecord-import](https://github.com/zdennis/activerecord-import) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/zdennis/activerecord-import/releases)
- [Changelog](https://github.com/zdennis/activerecord-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zdennis/activerecord-import/compare/v1.0.3...v1.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-09 19:16:47 +00:00
Luis Ramos
02dec1d6cd Merge pull request #4649 from tomekr/4623-remove-additional-footeer-links
Remove duplicate footer links
2020-01-09 18:04:30 +00:00
Luis Ramos
df04c837a5 Merge pull request #4631 from Matt-Yorkley/oc_buttons
Increase space for buttons in savebar layout
2020-01-09 17:56:01 +00:00
Luis Ramos
7a6c085b63 Merge pull request #4625 from openfoodfoundation/dependabot/bundler/roo-2.8.2
Bump roo from 2.7.1 to 2.8.2
2020-01-09 17:55:24 +00:00
dependabot-preview[bot]
a4317b70f4 Bump webdrivers from 4.1.3 to 4.2.0
Bumps [webdrivers](https://github.com/titusfortner/webdrivers) from 4.1.3 to 4.2.0.
- [Release notes](https://github.com/titusfortner/webdrivers/releases)
- [Changelog](https://github.com/titusfortner/webdrivers/blob/master/CHANGELOG.md)
- [Commits](https://github.com/titusfortner/webdrivers/compare/v4.1.3...v4.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 19:16:43 +00:00
dependabot-preview[bot]
cb90fb052d Bump ddtrace from 0.30.0 to 0.31.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.30.0 to 0.31.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.30.0...v0.31.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 19:15:48 +00:00
Matt-Yorkley
14bcba081d Adapt shopfront_helper to show/hide panels and display new tabs 2020-01-08 14:50:27 +01:00
Matt-Yorkley
fa0b365b28 Move "shop messages" and product list from main view into new "Home" and "Shop" tabs 2020-01-08 13:26:21 +01:00
Matt-Yorkley
673c587c3b Move tab templates to their own directory for clarity 2020-01-08 13:26:21 +01:00
Luis Ramos
18e5f9ba64 Merge pull request #4647 from jeduardo824/remove-groups-tab
remove group link from shop menu when there are no groups
2020-01-08 11:44:51 +00:00
Luis Ramos
e9a5b889de Merge pull request #4606 from cyrillefr/lone-hub-checked-by-default-4428
Select by default single Hub/Shop option on creation of payment/shipping method
2020-01-08 11:44:16 +00:00
Luis Ramos
cc57b0c200 Merge pull request #4609 from openfoodfoundation/dependabot/bundler/excon-0.71.1
[Security] Bump excon from 0.62.0 to 0.71.1
2020-01-08 09:49:13 +00:00
Maikel
a8040e986d Merge pull request #4626 from openfoodfoundation/dependabot/bundler/i18n-js-3.5.1
Bump i18n-js from 3.5.0 to 3.5.1
2020-01-08 17:58:53 +11:00
Maikel
2269f824c0 Merge pull request #4637 from openfoodfoundation/transifex
Transifex
2020-01-08 17:56:37 +11:00
Maikel Linke
b14193b6b7 Use strings for all environment variables
Figaro complained:

  WARNING: Use strings for Figaro configuration. 25 was converted to "25".
  WARNING: Use strings for Figaro configuration. 4 was converted to "4".

Those numbers have been converted to strings in our example config.
2020-01-08 12:14:23 +11:00
Eduardo
c45a3c2303 remove group tabs from shop view when there are no groups 2020-01-07 11:24:46 -03:00
Tomek Rabczak
1327d80446 fixup: Revert non en.yml locale changes as they are generated via transifex 2020-01-06 16:33:36 -06:00
Tomek Rabczak
43a3ac0a7b Remove no longer used locales 2020-01-06 13:56:35 -06:00
Tomek Rabczak
b948312de7 Remove additional footer links 2020-01-06 13:47:54 -06:00
Transifex-Openfoodnetwork
7063de4734 Updating translations for config/locales/fr_CA.yml 2020-01-04 06:22:08 +11:00
Transifex-Openfoodnetwork
56c1a9cca2 Updating translations for config/locales/fr_CA.yml 2020-01-04 06:19:02 +11:00
Transifex-Openfoodnetwork
9555bfcc93 Updating translations for config/locales/fr.yml 2020-01-04 06:17:34 +11:00
Transifex-Openfoodnetwork
e455a47135 Updating translations for config/locales/fr.yml 2020-01-04 06:14:27 +11:00
Transifex-Openfoodnetwork
e15e71a3a7 Updating translations for config/locales/en_CA.yml 2020-01-04 06:07:34 +11:00
Matt-Yorkley
91d959b7d6 Remove "previous" button from order cycle savebar 2020-01-03 16:25:26 +01:00
luisramos0
108f57a705 Replace adjustment.open with adjustment.fire_events(open) to avoid method name conflict 2020-01-02 16:58:11 +00:00
Transifex-Openfoodnetwork
af42159e09 Updating translations for config/locales/en_GB.yml 2020-01-03 01:37:14 +11:00
Transifex-Openfoodnetwork
8e55c39ca0 Updating translations for config/locales/en_GB.yml 2020-01-03 01:34:04 +11:00
luisramos0
6cfb060184 Update all locales with the latest Transifex translations 2020-01-02 10:37:13 +00:00
Luis Ramos
0cfb7269c8 Merge pull request #4596 from openfoodfoundation/transifex
Transifex
2020-01-02 10:34:20 +00:00
luisramos0
cbec495620 Fix some rubocop issues in order_and_distributor_report 2019-12-29 19:01:44 +00:00
luisramos0
55eea21bb0 Adapt order_and_distributor_report to the new editable_orders query 2019-12-29 19:01:44 +00:00
luisramos0
f63c7cf54f Extract visible_orders_where_values to a private method 2019-12-29 19:01:44 +00:00
luisramos0
13633e8bea Use arel in order permissions visible orders and editable orders so that we dont have queries with gigantic IN clauses
The | operators here were converting the relations to long lists of IDs, in our current particular issue, an IN clause with 100k order_ids
2019-12-29 19:01:44 +00:00
luisramos0
093edb66d3 Remove unused view
This was introduced in 2013 and removed in 2014, see log below:

git log -Saddress_form_simple
commit 510333288c
Author: Maikel Linke <mkllnk@web.de>
Date:   Sun Mar 2 13:05:15 2014 +1100

    first steps of new design

commit 2e1de9a6d3
Author: Rohan Mitchell <rohan@rohanmitchell.com>
Date:   Fri Aug 23 13:12:36 2013 +1000

    Fix admin create order - remove override on spree address form, update to spree patched to fix respond_override (spree issue #2210), fix nil state error
2019-12-28 18:25:28 +00:00
luisramos0
fb25ddd219 Remove reference to user banners, this code is not used in OFN. 2019-12-28 18:13:04 +00:00
luisramos0
1295d33fa7 Fix bug in frontoffice order details where the product image was a link to nowhere 2019-12-28 17:54:28 +00:00
luisramos0
d54850f097 Move ActionCallBacks out of the spree namespace 2019-12-27 17:04:40 +00:00
luisramos0
f8451a2511 Bring needed action_callbacks from spree_backend 2019-12-27 17:04:31 +00:00
luisramos0
1a88549954 Update rubocop todo lists 2019-12-27 17:04:23 +00:00
luisramos0
f79182253a Fix some rubocop issues 2019-12-27 17:03:39 +00:00
luisramos0
8cfd7c610b Use nested module instead of class Spree::Admin:: 2019-12-27 17:03:39 +00:00
luisramos0
486b5e9edc Merge resource_decorator into resource controller 2019-12-27 17:03:39 +00:00
luisramos0
8fe3abfd45 Add resource_controller from spree_backend so that we can now merge it with the OFN's decorator 2019-12-27 17:03:39 +00:00
Luis Ramos
0e7dafea46 Merge pull request #4560 from kshlyk/fix_billing_addres_in_pdf_invoice
Billing address in pdf invoice fixed
2019-12-27 16:45:06 +00:00
Matt-Yorkley
4c9cc7460a Increase space for buttons in savebar layout 2019-12-27 17:08:37 +01:00
dependabot-preview[bot]
c9e3f58aed Bump unicorn from 5.5.1 to 5.5.2
Bumps [unicorn](https://bogomips.org/unicorn/) from 5.5.1 to 5.5.2.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-26 19:16:30 +00:00
Luis Ramos
4136306abf Updating mini_racer and libv8 at the same time 2019-12-26 11:01:09 +00:00
Luis Ramos
9f1eaf0b66 Merge pull request #4512 from luisramos0/backend_ctrl_base
Bring spree_backend base controller to OFN
2019-12-24 12:01:07 +00:00
dependabot-preview[bot]
4771612adb Bump i18n-js from 3.5.0 to 3.5.1
Bumps [i18n-js](https://github.com/fnando/i18n-js) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/fnando/i18n-js/releases)
- [Changelog](https://github.com/fnando/i18n-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fnando/i18n-js/compare/v3.5.0...v3.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 19:21:17 +00:00
dependabot-preview[bot]
7ce3dfe365 Bump roo from 2.7.1 to 2.8.2
Bumps [roo](https://github.com/roo-rb/roo) from 2.7.1 to 2.8.2.
- [Release notes](https://github.com/roo-rb/roo/releases)
- [Changelog](https://github.com/roo-rb/roo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/roo-rb/roo/compare/v2.7.1...v2.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 19:17:19 +00:00
Luis Ramos
6f13707b9d Merge pull request #4556 from openfoodfoundation/dependabot/bundler/stripe-5.11.0
Bump stripe from 4.24.0 to 5.11.0
2019-12-23 16:19:05 +00:00
Luis Ramos
64cb104434 Merge pull request #4577 from openfoodfoundation/dependabot/bundler/oj-3.10.0
Bump oj from 3.7.12 to 3.10.0
2019-12-23 16:14:33 +00:00
Luis Ramos
1606f9900f Merge pull request #4507 from luisramos0/backend_ctrl_reports
Bring spree_backend reports controller to OFN
2019-12-23 16:14:07 +00:00
Luis Ramos
9f0e8b0b2e Merge pull request #4558 from openfoodfoundation/dependabot/bundler/mini_racer-0.2.4
Bump mini_racer from 0.1.15 to 0.2.4
2019-12-23 16:13:10 +00:00
Luis Ramos
d796e96470 Merge pull request #4575 from Matt-Yorkley/admin_orders
Fix route on "continue" button on admin adjustments page
2019-12-23 16:12:28 +00:00
Luis Ramos
ba7f78ef60 Merge pull request #4515 from luisramos0/backend_ctrl_adjustments
Bring spree_backend adjustments controller to OFN
2019-12-23 16:05:04 +00:00
Maikel
988e146240 Merge pull request #4564 from openfoodfoundation/dependabot/bundler/rack-mini-profiler-1.1.4
Bump rack-mini-profiler from 1.0.0 to 1.1.4
2019-12-20 17:42:04 +11:00
cyrillefr
e6d9ec7bd7 Small fixes for default single Hub/Shop options issue
- removed mode variable
 - reverted html template accordingly
 - added a more specific helper
 - fixed some short variable names
2019-12-19 21:08:34 +01:00
Transifex-Openfoodnetwork
3acc53a389 Updating translations for config/locales/ar.yml 2019-12-20 04:33:09 +11:00
Transifex-Openfoodnetwork
9c9fc999de Updating translations for config/locales/ar.yml 2019-12-20 04:30:00 +11:00
Transifex-Openfoodnetwork
d824c84ce6 Updating translations for config/locales/fr.yml 2019-12-19 19:14:24 +11:00
Transifex-Openfoodnetwork
1a301f3dbb Updating translations for config/locales/fr.yml 2019-12-19 19:11:16 +11:00
Transifex-Openfoodnetwork
fad4f3b22a Updating translations for config/locales/en_FR.yml 2019-12-19 19:08:18 +11:00
Transifex-Openfoodnetwork
6080c99850 Updating translations for config/locales/fr.yml 2019-12-19 19:08:07 +11:00
Transifex-Openfoodnetwork
2944acff8a Updating translations for config/locales/nb.yml 2019-12-19 09:52:35 +11:00
dependabot-preview[bot]
2068a59b72 [Security] Bump excon from 0.62.0 to 0.71.1
Bumps [excon](https://github.com/excon/excon) from 0.62.0 to 0.71.1. **This update includes a security fix.**
- [Release notes](https://github.com/excon/excon/releases)
- [Changelog](https://github.com/excon/excon/blob/master/changelog.txt)
- [Commits](https://github.com/excon/excon/compare/v0.62.0...v0.71.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-18 19:33:23 +00:00
Luis Ramos
3063439ed1 Merge pull request #4541 from Matt-Yorkley/new_fee_errors
Handle validation messages when saving new fees
2019-12-18 13:35:45 +00:00
Matt-Yorkley
6bed05c721 Add missing feature test for order "Ship" button 2019-12-18 14:15:48 +01:00
Matt-Yorkley
a3ee6674ea Use asynchronous requests for order capture and ship actions 2019-12-18 14:15:46 +01:00
cyrillefr
e59077e63e Select by default single Hub/Shop option on creation of payment/shipping method
- added a helper
- added mode(new/edit) in payment/shipping views
- updated checkbox creation
- added tests
2019-12-18 10:45:00 +01:00
Transifex-Openfoodnetwork
f9f8d85841 Updating translations for config/locales/fr.yml 2019-12-18 04:37:36 +11:00
Transifex-Openfoodnetwork
0042690e18 Updating translations for config/locales/en_FR.yml 2019-12-18 04:35:31 +11:00
Transifex-Openfoodnetwork
5f1111b52b Updating translations for config/locales/fr.yml 2019-12-18 04:34:28 +11:00
Luis Ramos
25ded0d23c Merge pull request #4471 from luisramos0/paginate_exc_prods
Paginate Exchange Products API endpoint
2019-12-17 15:25:15 +00:00
Luis Ramos
a5458150ca Merge pull request #4455 from luisramos0/oc_prods
In the OC edit page, load products only when each products tab is opened by user
2019-12-17 13:40:19 +00:00
Pau Pérez Fabregat
96eebbabf3 Merge pull request #4590 from luisramos0/capybara
Upgrade capybara and webdrivers
2019-12-17 10:55:59 +01:00
luisramos0
c58e6fa964 Upgrade capybara 2019-12-16 15:25:45 +00:00
luisramos0
fbe8f5195c Upgrade webdrivers to latest 2019-12-16 15:25:36 +00:00
luisramos0
9648693329 Fix a problem in states/new template where erb instead of haml was referenced 2019-12-15 21:42:52 +00:00
luisramos0
9df9061ec1 Bring missing translation from spree and use root namespace for translations instead of the spre namespace 2019-12-15 21:39:26 +00:00
luisramos0
67526df420 Convert calculator fields partial to haml 2019-12-15 21:32:31 +00:00
luisramos0
766f8ab407 Add partial needed in payment and shipping methods 2019-12-15 21:24:50 +00:00
luisramos0
f587bbb7d5 Remove unnecessary helper 2019-12-15 21:03:53 +00:00
luisramos0
2793693a7c Improve if clause readability 2019-12-15 21:03:53 +00:00
luisramos0
07e2317369 Replace deprecated URI.unescape with CGI.unescape 2019-12-15 21:03:53 +00:00
luisramos0
042162eda8 Delete unused method 2019-12-15 21:03:53 +00:00
luisramos0
795f13d73a Remove spree alerts feature that would check spree website for security alerts
This is not something we need running such an old version of spree
2019-12-15 21:03:53 +00:00
luisramos0
f1814f1b67 Fix most rubocop issues in spree/admin/base_controller 2019-12-15 21:03:53 +00:00
luisramos0
4ab7b78cb8 Merge base_controller with its decorator 2019-12-15 21:03:53 +00:00
luisramos0
11631c3a33 Add base_controller from spree_backend so that we can now merge it with the OFN's decorator 2019-12-15 21:03:53 +00:00
Konstantin Shlyk
0212381362 fix for api/variants_controller_spec 2019-12-14 21:43:38 +03:00
Konstantin Shlyk
e6ca6bacac soft_delete api method deleted for products and variants 2019-12-14 01:05:35 +03:00
dependabot-preview[bot]
59df45b8cf Bump oj from 3.7.12 to 3.10.0
Bumps [oj](https://github.com/ohler55/oj) from 3.7.12 to 3.10.0.
- [Release notes](https://github.com/ohler55/oj/releases)
- [Changelog](https://github.com/ohler55/oj/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/ohler55/oj/compare/v3.7.12...v3.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-13 19:17:46 +00:00
Luis Ramos
5892e85869 Merge pull request #4559 from openfoodfoundation/dependabot/bundler/selenium-webdriver-3.142.6
Bump selenium-webdriver from 3.141.0 to 3.142.6
2019-12-13 18:05:03 +00:00
luisramos0
c59326743b Remove sales total report 2019-12-13 16:44:46 +00:00
luisramos0
cb3397fd1a Convert reports index and sales_total views from erb to haml 2019-12-13 16:44:46 +00:00
luisramos0
2bd4de3e29 Add sales total report view 2019-12-13 16:44:46 +00:00
luisramos0
b2c5be775e Fix some rubocop issues 2019-12-13 16:44:46 +00:00
luisramos0
d22212ccfa Merge spree/admin/reports_controller with its decorator 2019-12-13 16:44:44 +00:00
luisramos0
dd600cd163 Add reports_controller from spree_backend so that we can now merge it with the OFN's decorator 2019-12-13 15:20:03 +00:00
Matt-Yorkley
f189ca8004 Fix route on "continue" button on admin adjustments page 2019-12-13 13:51:41 +01:00
Luis Ramos
db7146014c Rename exchange products file names to increase clarity 2019-12-13 12:24:19 +00:00
Luis Ramos
c6af55d9ae Restructure exchange products panels so that OC simple form does not include header and footer (product count and pagination) of the non-simple OC form 2019-12-13 12:24:16 +00:00
Maikel
0f588dbe0b Merge pull request #4561 from openfoodfoundation/transifex
Transifex
2019-12-13 17:02:32 +11:00
dependabot-preview[bot]
9c14d8ff36 Bump rack-mini-profiler from 1.0.0 to 1.1.4
Bumps [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) from 1.0.0 to 1.1.4.
- [Release notes](https://github.com/MiniProfiler/rack-mini-profiler/releases)
- [Changelog](https://github.com/MiniProfiler/rack-mini-profiler/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MiniProfiler/rack-mini-profiler/compare/v1.0.0...v1.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-12 19:14:17 +00:00
luisramos0
a104bf8efd Add feature spec to validate load all and select all exchange products 2019-12-12 18:49:11 +00:00
luisramos0
a039ef13f4 Add spec for exchange products controller pagination 2019-12-12 18:49:11 +00:00
luisramos0
7ad8951375 DRY exchange products templates by extracting header and footer content to partial templates 2019-12-12 18:49:11 +00:00
luisramos0
99e59595b4 Add spinner when loading all products in an exchange 2019-12-12 18:48:36 +00:00
luisramos0
3264355f12 Add some basic test coverage to exchange_products_controller and exchange_products_renderer 2019-12-12 18:48:36 +00:00
luisramos0
7e3b6e2b5d Fix select all checkbox state in both incoming and outgoing exchanges by using the total count of variants to see if all variants are selected or not, even if not all variants are loaded 2019-12-12 18:48:36 +00:00
luisramos0
a9598c5d97 Only show load more and load all if not all products are loaded already 2019-12-12 18:48:36 +00:00
luisramos0
3e5b7ebbf1 Show exchange products list only after first page of products is loaded 2019-12-12 18:48:36 +00:00
luisramos0
1a9c3007b0 Make select all work again in incoming exchanges by loading alll products before triggering the select all process 2019-12-12 18:48:35 +00:00
luisramos0
2f7fd1482a Add link to load all products at the top of the list of products and include summary of number of products loaded already 2019-12-12 18:48:35 +00:00
luisramos0
0416521772 Add load all prouducts button to list of exchange products 2019-12-12 18:48:35 +00:00
luisramos0
cfe3f72d0e Add load more button to list of exchange products 2019-12-12 18:48:35 +00:00
luisramos0
00478cc57c Add count variants endpoint and use it instead of loading exchange products 2019-12-12 18:48:35 +00:00
luisramos0
d5e42ee1e5 Paginate exchange products results
This commit breaks the OC page when there are more than 100 products in an exchange
2019-12-12 18:48:35 +00:00
luisramos0
9451f1b66d Remove funky panel open listener code and initialize the panel data with a ng-init in the panel template 2019-12-12 18:44:45 +00:00
luisramos0
ff584f9be9 Adapt exchange products routes to make api/exchanges/products (without exchange_id) also go to exchange_products#index 2019-12-12 18:44:45 +00:00
luisramos0
a589ba38da Rename exchanges_products_controller to better exchange_products_controller 2019-12-12 18:44:45 +00:00
luisramos0
a4a2f98b6e Rename Product service to more appropriate ExchangeProduct 2019-12-12 18:44:45 +00:00
luisramos0
f5ddbfbac3 Make Product service more simple, there's no need to keep the data structure, that is already kept in the controller 2019-12-12 18:44:45 +00:00
luisramos0
a66a4c3edb Remove unnecessary passing of scopes around in controllers 2019-12-12 18:44:45 +00:00
luisramos0
8179252924 Replace the use of this with 2019-12-12 18:44:45 +00:00
luisramos0
0bec492208 Fix some rubocop issues 2019-12-12 18:44:45 +00:00
luisramos0
5cf50f0adf Convert the manipulation of arrays into active record relations: let the DB do the matching job 2019-12-12 18:44:45 +00:00
luisramos0
197fb36524 Extract ExchangeProductsRenderer from ExchangesProductsController 2019-12-12 18:44:45 +00:00
luisramos0
62e6f09d94 Make exchange products more simple 2019-12-12 18:44:45 +00:00
luisramos0
79b2460664 Make simple create and edit OC load exchange products on init
Simple create makes a new type of call to exchange products with no exchange_id and no prder_cycle_id, it simply lists supplied products for a given enterprise
2019-12-12 18:44:44 +00:00
luisramos0
66f3656bb5 Register products panel listeners after OrderCycle is loaded instead of using recurrent timeouts
Also, use this same approach for the case where a new distributor or new supplier is added to the list of exchanges
2019-12-12 18:44:44 +00:00
luisramos0
6b087adab8 Add route to process requests to exchanges/products without exchange id
This is needed when products for an exchange that is not yet saved are requested
2019-12-12 18:44:44 +00:00
luisramos0
3653b88da6 Make exchanges_products_controller more independent of the exchange field 2019-12-12 18:44:44 +00:00
luisramos0
3223bf930d Make total number of products in exchange work again.
Currently we are just loading the products from the server and count them.
This can be improved easily in two ways:
- we can switch this to a specific product count call to the server so that we dont load all products all the time
- or we paginate the products result and fetch the total_number from the payload of the first page.
2019-12-12 18:44:44 +00:00
luisramos0
2b3bc6d1ff Remove supplied products from enterprise serializer and from UI side
This list of products s now loaded in a specific call to ExchangeProducts and for each specific exchange
2019-12-12 18:44:44 +00:00
luisramos0
24d7672abb Use new exchange products endpoint for outgoing exchanges and make the exchange products panel work for outgoing exchanges 2019-12-12 18:44:44 +00:00
luisramos0
89628c27f3 Move exchange products endpoint to api namespace and make it work for outgoing exchanges 2019-12-12 18:44:44 +00:00
luisramos0
9adbdc377d Add new admin/exchange/products endpoint that replaces Enterprise/for_order_cycle#supplied_products 2019-12-12 18:44:44 +00:00
luisramos0
883cd81058 Load enterprise supplied products when the exchange products panel is opened for the first time 2019-12-12 18:44:44 +00:00
Transifex-Openfoodnetwork
0d7d029255 Updating translations for config/locales/en_GB.yml 2019-12-13 02:07:44 +11:00
Konstantin Shlyk
c5229dd763 billing address in pdf invoice fixed 2019-12-11 23:10:51 +03:00
dependabot-preview[bot]
99d4190814 Bump selenium-webdriver from 3.141.0 to 3.142.6
Bumps [selenium-webdriver](https://github.com/SeleniumHQ/selenium) from 3.141.0 to 3.142.6.
- [Release notes](https://github.com/SeleniumHQ/selenium/releases)
- [Changelog](https://github.com/SeleniumHQ/selenium/blob/master/rb/CHANGES)
- [Commits](https://github.com/SeleniumHQ/selenium/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 19:35:45 +00:00
dependabot-preview[bot]
f086c02e13 Bump mini_racer from 0.1.15 to 0.2.4
Bumps [mini_racer](https://github.com/discourse/mini_racer) from 0.1.15 to 0.2.4.
- [Release notes](https://github.com/discourse/mini_racer/releases)
- [Changelog](https://github.com/rubyjs/mini_racer/blob/master/CHANGELOG)
- [Commits](https://github.com/discourse/mini_racer/compare/v0.1.15...v0.2.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 19:35:03 +00:00
dependabot-preview[bot]
b726f961fc Bump stripe from 4.24.0 to 5.11.0
Bumps [stripe](https://github.com/stripe/stripe-ruby) from 4.24.0 to 5.11.0.
- [Release notes](https://github.com/stripe/stripe-ruby/releases)
- [Changelog](https://github.com/stripe/stripe-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-ruby/compare/v4.24.0...v5.11.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 19:33:30 +00:00
Matt-Yorkley
29e30c388e Make error message translatable 2019-12-07 15:48:46 +01:00
Matt-Yorkley
54a40fe79c Handle validation messages when saving new fees 2019-12-07 14:51:17 +01:00
luisramos0
4e7b397c5a Bring orders adjustments route from spree_backend 2019-11-28 16:26:22 +00:00
luisramos0
842e191c5f Remove toggle_state action that is not used in OFN 2019-11-28 16:20:35 +00:00
luisramos0
68bf599a1a Merge spree/admin/adjustments_controller with decorator 2019-11-27 21:59:15 +00:00
luisramos0
a10966b66b Add adjustments_controller from spree_backend so that we can now merge it with the OFN's decorator 2019-11-27 21:59:15 +00:00
190 changed files with 4370 additions and 2353 deletions

View File

@@ -4,6 +4,7 @@
#
# The configuration is split into three files. Look into those files for more details.
#
require: rubocop-rails
inherit_from:
# The automatically generated todo list to ignore all current violations.

View File

@@ -18,7 +18,7 @@
#
# This process probably doesn't need repeating. Otherwise there is plenty
# of room for improvements and automation.
Metrics/LineLength:
Layout/LineLength:
Max: 100
Exclude:
- Gemfile
@@ -42,7 +42,6 @@ Metrics/LineLength:
- app/controllers/application_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/adjustments_controller_decorator.rb
- app/controllers/spree/admin/base_controller_decorator.rb
- app/controllers/spree/admin/orders_controller_decorator.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/credit_cards_controller.rb
@@ -115,7 +114,6 @@ Metrics/LineLength:
- lib/open_food_network/enterprise_issue_validator.rb
- lib/open_food_network/group_buy_report.rb
- lib/open_food_network/lettuce_share_report.rb
- lib/open_food_network/order_and_distributor_report.rb
- lib/open_food_network/order_cycle_form_applicator.rb
- lib/open_food_network/order_cycle_management_report.rb
- lib/open_food_network/payments_report.rb
@@ -144,6 +142,7 @@ Metrics/LineLength:
- spec/controllers/admin/subscriptions_controller_spec.rb
- spec/controllers/admin/variant_overrides_controller_spec.rb
- spec/controllers/api/base_controller_spec.rb
- spec/controllers/api/exchange_products_controller_spec.rb
- spec/controllers/api/logos_controller_spec.rb
- spec/controllers/api/order_cycles_controller_spec.rb
- spec/controllers/api/orders_controller_spec.rb
@@ -305,6 +304,7 @@ Metrics/LineLength:
- spec/serializers/api/admin/exchange_serializer_spec.rb
- spec/serializers/api/admin/for_order_cycle/enterprise_serializer_spec.rb
- spec/serializers/api/admin/for_order_cycle/supplied_product_serializer_spec.rb
- spec/serializers/api/admin/order_cycle_serializer_spec.rb
- spec/serializers/api/admin/subscription_customer_serializer_spec.rb
- spec/serializers/api/admin/variant_override_serializer_spec.rb
- spec/serializers/api/current_order_serializer_spec.rb
@@ -312,11 +312,13 @@ Metrics/LineLength:
- spec/serializers/api/order_serializer_spec.rb
- spec/services/cart_service_spec.rb
- spec/services/embedded_page_service_spec.rb
- spec/services/exchange_products_renderer_spec.rb
- spec/services/order_cycle_distributed_products_spec.rb
- spec/services/order_cycle_distributed_variants_spec.rb
- spec/services/order_cycle_form_spec.rb
- spec/services/order_factory_spec.rb
- spec/services/order_syncer_spec.rb
- spec/services/permissions/order_spec.rb
- spec/services/product_tag_rules_filterer_spec.rb
- spec/services/products_renderer_spec.rb
- spec/services/subscription_estimator_spec.rb
@@ -365,12 +367,13 @@ Metrics/AbcSize:
- app/controllers/spree/admin/overview_controller.rb
- app/controllers/spree/admin/payment_methods_controller.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/products_controller_decorator.rb
- app/controllers/spree/admin/reports_controller_decorator.rb
- app/controllers/spree/admin/reports_controller.rb
- app/controllers/spree/admin/resource_controller.rb
- app/controllers/spree/admin/products_controller.rb
- app/controllers/spree/admin/search_controller_decorator.rb
- app/controllers/spree/admin/taxons_controller.rb
- app/controllers/spree/admin/users_controller.rb
- app/controllers/spree/admin/variants_controller_decorator.rb
- app/controllers/spree/admin/variants_controller.rb
- app/controllers/spree/checkout_controller.rb
- app/controllers/spree/credit_cards_controller.rb
- app/controllers/spree/orders_controller.rb
@@ -564,8 +567,9 @@ Metrics/MethodLength:
- app/controllers/spree/admin/orders/customer_details_controller_decorator.rb
- app/controllers/spree/admin/payment_methods_controller.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/products_controller_decorator.rb
- app/controllers/spree/admin/reports_controller_decorator.rb
- app/controllers/spree/admin/reports_controller.rb
- app/controllers/spree/admin/resource_controller.rb
- app/controllers/spree/admin/products_controller.rb
- app/controllers/spree/admin/search_controller_decorator.rb
- app/controllers/spree/admin/tax_categories_controller.rb
- app/controllers/spree/admin/taxons_controller.rb
@@ -644,7 +648,11 @@ Metrics/ClassLength:
- app/controllers/admin/subscriptions_controller.rb
- app/controllers/api/products_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/base_controller.rb
- app/controllers/spree/admin/payment_methods_controller.rb
- app/controllers/spree/admin/reports_controller.rb
- app/controllers/spree/admin/resource_controller.rb
- app/controllers/spree/admin/products_controller.rb
- app/controllers/spree/admin/users_controller.rb
- app/controllers/spree/orders_controller.rb
- app/models/enterprise.rb
@@ -700,6 +708,7 @@ Metrics/ModuleLength:
- spec/models/spree/payment_spec.rb
- spec/models/spree/product_spec.rb
- spec/models/spree/variant_spec.rb
- spec/services/permissions/order_spec.rb
- spec/support/request/web_helper.rb
Metrics/ParameterLists:

View File

@@ -5,7 +5,6 @@
# rubocop locally, the default configuration file `.rubocop.yml` loads
# our "todo lists" to ignore all current violations.
AllCops:
TargetRubyVersion: 2.2
TargetRailsVersion: 3.2
Exclude:
- 'bin/**/*'
@@ -41,7 +40,7 @@ Layout/MultilineMethodCallIndentation:
Enabled: true
EnforcedStyle: indented
Metrics/LineLength:
Layout/LineLength:
Max: 100
## TEMPORARY/CONTESTED SETTINGS

File diff suppressed because it is too large Load Diff

16
Gemfile
View File

@@ -3,7 +3,7 @@ ruby "2.3.7"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
gem 'i18n', '~> 0.6.11'
gem 'i18n-js', '~> 3.5.0'
gem 'i18n-js', '~> 3.5.1'
gem 'rails', '~> 3.2.22'
gem 'rails-i18n', '~> 3.0.0'
gem 'rails_safe_tasks', '~> 1.0'
@@ -93,7 +93,7 @@ gem 'wkhtmltopdf-binary'
gem 'foreigner'
gem 'immigrant'
gem 'roo', '~> 2.7.0'
gem 'roo', '~> 2.8.2'
gem 'roo-xls', '~> 1.1.0'
gem 'whenever', require: false
@@ -106,10 +106,7 @@ group :assets do
gem 'coffee-rails', '~> 3.2.1'
gem 'compass-rails'
gem 'mini_racer', '0.1.15'
# Previously we found that libv8 6.7.288.46.1 breakis the compilation of mini_racer.
# Now we see that we need to set the version explicitly. Nothing else depends on libv8.
gem 'libv8', '6.3.292.48.1'
gem 'mini_racer', '0.2.9'
gem 'uglifier', '>= 1.0.3'
@@ -135,7 +132,7 @@ group :test, :development do
# Pretty printed test output
gem 'atomic'
gem 'awesome_print'
gem 'capybara', '>= 2.15.4'
gem 'capybara', '>= 2.18.0' # 3.0 requires nokogiri 1.8
gem 'database_cleaner', '0.7.1', require: false
gem "factory_bot_rails", require: false
gem 'fuubar', '~> 2.5.0'
@@ -148,7 +145,7 @@ group :test, :development do
gem 'shoulda-matchers'
gem 'timecop'
gem 'unicorn-rails'
gem 'webdrivers', '3.8.1'
gem 'webdrivers'
end
group :test do
@@ -163,7 +160,8 @@ group :development do
gem 'debugger-linecache'
gem "newrelic_rpm", "~> 3.0"
gem 'pry-byebug', '>= 3.4.3'
gem 'rubocop', '>= 0.49.1'
gem 'rubocop'
gem 'rubocop-rails'
gem 'spring', '1.7.2'
gem 'spring-commands-rspec'

View File

@@ -129,7 +129,7 @@ GEM
activesupport (= 3.2.22.5)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activerecord-import (1.0.3)
activerecord-import (1.0.4)
activerecord (>= 3.2)
activerecord-postgresql-adapter (0.0.1)
pg
@@ -178,8 +178,7 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (>= 2.0, < 4.0)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
childprocess (3.0.0)
chronic (0.10.2)
chunky_png (1.3.10)
climate_control (0.2.0)
@@ -213,7 +212,6 @@ GEM
sass-rails (< 5.1)
sprockets (< 4.0)
concurrent-ruby (1.1.5)
connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
css_parser (1.7.0)
@@ -225,7 +223,7 @@ GEM
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.30.0)
ddtrace (0.31.1)
msgpack
debugger-linecache (1.2.0)
deface (1.0.2)
@@ -256,17 +254,17 @@ GEM
dry-inflector (0.1.2)
erubis (2.7.0)
eventmachine (1.2.7)
excon (0.62.0)
excon (0.71.1)
execjs (2.7.0)
factory_bot (4.10.0)
activesupport (>= 3.0.0)
factory_bot_rails (4.10.0)
factory_bot (~> 4.10.0)
railties (>= 3.0.0)
faraday (0.15.4)
faraday (0.17.1)
multipart-post (>= 1.2, < 3)
ffaker (1.22.1)
ffi (1.10.0)
ffi (1.11.3)
figaro (1.1.1)
thor (~> 0.14)
fission (0.5.0)
@@ -439,7 +437,7 @@ GEM
httparty (0.16.2)
multi_xml (>= 0.5.2)
i18n (0.6.11)
i18n-js (3.5.0)
i18n-js (3.5.1)
i18n (>= 0.6.6)
immigrant (0.3.6)
activerecord (>= 3.0)
@@ -467,7 +465,7 @@ GEM
addressable (~> 2.3)
letter_opener (1.7.0)
launchy (~> 2.2)
libv8 (6.3.292.48.1)
libv8 (7.3.492.27.1)
mail (2.5.5)
mime-types (~> 1.16)
treetop (~> 1.4.8)
@@ -475,8 +473,8 @@ GEM
mime-types (1.25.1)
mini_mime (1.0.1)
mini_portile2 (2.1.0)
mini_racer (0.1.15)
libv8 (~> 6.3)
mini_racer (0.2.9)
libv8 (>= 6.9.411)
momentjs-rails (2.20.1)
railties (>= 3.1)
money (5.1.1)
@@ -485,8 +483,6 @@ GEM
multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
net-http-persistent (3.1.0)
connection_pool (~> 2.2)
newrelic_rpm (3.18.1.330)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
@@ -496,7 +492,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.7.12)
oj (3.10.1)
orm_adapter (0.5.0)
paper_trail (5.2.3)
activerecord (>= 3.0, < 6.0)
@@ -507,10 +503,10 @@ GEM
activesupport (>= 3.0.0)
cocaine (~> 0.5.0)
mime-types
parallel (1.18.0)
parallel (1.19.1)
paranoia (1.3.4)
activerecord (~> 3.1)
parser (2.6.5.0)
parser (2.7.0.2)
ast (~> 2.4.0)
paypal-sdk-core (0.2.10)
multi_json (~> 1.0)
@@ -528,13 +524,13 @@ GEM
pry-byebug (3.4.3)
byebug (>= 9.0, < 9.1)
pry (~> 0.10)
public_suffix (3.1.1)
public_suffix (4.0.3)
rabl (0.8.4)
activesupport (>= 2.3.14)
rack (1.4.7)
rack-cache (1.9.0)
rack (>= 0.4)
rack-mini-profiler (1.0.0)
rack-mini-profiler (1.1.4)
rack (>= 1.2.0)
rack-protection (1.5.5)
rack
@@ -588,9 +584,9 @@ GEM
roadie-rails (1.3.0)
railties (>= 3.0, < 5.3)
roadie (~> 3.1)
roo (2.7.1)
roo (2.8.2)
nokogiri (~> 1)
rubyzip (~> 1.1, < 2.0.0)
rubyzip (>= 1.2.1, < 2.0.0)
roo-xls (1.1.0)
nokogiri
roo (>= 2.0.0beta1, < 3)
@@ -618,13 +614,16 @@ GEM
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.9.0)
rubocop (0.68.1)
rubocop (0.79.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.6)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.4.1)
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-ole (1.2.12.1)
ruby-progressbar (1.10.1)
ruby-rc4 (0.1.5)
@@ -638,9 +637,9 @@ GEM
select2-rails (3.4.9)
sass-rails
thor (~> 0.14)
selenium-webdriver (3.141.0)
childprocess (~> 0.5)
rubyzip (~> 1.2, >= 1.2.2)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
simplecov (0.17.1)
@@ -666,10 +665,8 @@ GEM
tilt (~> 1.1, != 1.3.0)
state_machine (1.2.0)
stringex (1.5.1)
stripe (4.24.0)
faraday (~> 0.13)
net-http-persistent (~> 3.0)
test-unit (3.3.4)
stripe (5.11.0)
test-unit (3.3.5)
power_assert
thor (0.20.3)
tilt (1.4.1)
@@ -682,11 +679,11 @@ GEM
turbo-sprockets-rails3 (0.3.14)
railties (> 3.2.8, < 4.0.0)
sprockets (>= 2.2.0)
tzinfo (0.3.55)
tzinfo (0.3.56)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.5.0)
unicorn (5.5.1)
unicode-display_width (1.6.0)
unicorn (5.5.2)
kgio (~> 2.6)
raindrops (~> 0.7)
unicorn-rails (2.2.1)
@@ -699,11 +696,11 @@ GEM
railties (>= 3.0)
warden (1.2.7)
rack (>= 1.0)
webdrivers (3.8.1)
webdrivers (4.2.0)
nokogiri (~> 1.6)
rubyzip (~> 1.0)
selenium-webdriver (~> 3.0)
webmock (3.7.6)
rubyzip (>= 1.3.0)
selenium-webdriver (>= 3.0, < 4.0)
webmock (3.8.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -734,7 +731,7 @@ DEPENDENCIES
blockenspiel
bugsnag
byebug (~> 9.0.0)
capybara (>= 2.15.4)
capybara (>= 2.18.0)
coffee-rails (~> 3.2.1)
combine_pdf
compass-rails
@@ -763,7 +760,7 @@ DEPENDENCIES
gmaps4rails
haml
i18n (~> 0.6.11)
i18n-js (~> 3.5.0)
i18n-js (~> 3.5.1)
immigrant
jquery-migrate-rails
jquery-rails (= 3.0.4)
@@ -772,8 +769,7 @@ DEPENDENCIES
kaminari (~> 0.14.1)
knapsack
letter_opener (>= 1.4.1)
libv8 (= 6.3.292.48.1)
mini_racer (= 0.1.15)
mini_racer (= 0.2.9)
momentjs-rails
newrelic_rpm (~> 3.0)
nokogiri (>= 1.6.7.1)
@@ -794,11 +790,12 @@ DEPENDENCIES
rails_safe_tasks (~> 1.0)
redcarpet
roadie-rails (~> 1.3.0)
roo (~> 2.7.0)
roo (~> 2.8.2)
roo-xls (~> 1.1.0)
rspec-rails (>= 3.5.2)
rspec-retry
rubocop (>= 0.49.1)
rubocop
rubocop-rails
sass (~> 3.3)
sass-rails (~> 3.2.3)
selenium-webdriver
@@ -821,7 +818,7 @@ DEPENDENCIES
unicorn
unicorn-rails
web!
webdrivers (= 3.8.1)
webdrivers
webmock
whenever
wicked_pdf

View File

@@ -142,7 +142,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm("Are you sure?")
$http(
method: "DELETE"
url: "/api/products/" + product.id + "/soft_delete"
url: "/api/products/" + product.id
).success (data) ->
$scope.products.splice $scope.products.indexOf(product), 1
DirtyProducts.deleteProduct product.id
@@ -157,7 +157,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm(t("are_you_sure"))
$http(
method: "DELETE"
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete"
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id
).success (data) ->
$scope.removeVariant(product, variant)
else

View File

@@ -1,5 +1,31 @@
angular.module('admin.orderCycles').controller 'AdminOrderCycleIncomingCtrl', ($scope, $controller, $location, Enterprise, ocInstance) ->
angular.module('admin.orderCycles').controller 'AdminOrderCycleIncomingCtrl', ($scope, $controller, $location, Enterprise, OrderCycle, ExchangeProduct, ocInstance) ->
$controller('AdminOrderCycleExchangesCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
$scope.enterpriseTotalVariants = (enterprise) ->
Enterprise.totalVariants(enterprise)
$scope.view = 'incoming'
$scope.exchangeTotalVariants = (exchange) ->
return unless $scope.enterprises? && $scope.enterprises[exchange.enterprise_id]?
enterprise = $scope.enterprises[exchange.enterprise_id]
return enterprise.numVariants if enterprise.numVariants?
enterprise.numVariants = 0
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: true}
ExchangeProduct.countVariants params, (variants_count) ->
enterprise.numVariants = variants_count
$scope.setSelectAllVariantsCheckboxValue(exchange, enterprise.numVariants)
return enterprise.numVariants
$scope.addSupplier = ($event) ->
$event.preventDefault()
OrderCycle.addSupplier $scope.new_supplier_id
# To select all variants we first need to load them all from the server
#
# This is only needed in Incoming exchanges as here we use supplied_products,
# in Outgoing Exchanges the variants are loaded as part of the Exchange payload
$scope.selectAllVariants = (exchange, selected) ->
$scope.loadAllExchangeProducts(exchange).then ->
$scope.setExchangeVariants(exchange, $scope.suppliedVariants(exchange.enterprise_id), selected)
$scope.$apply()

View File

@@ -18,11 +18,11 @@ angular.module('admin.orderCycles')
$scope.cancel = (destination) ->
$window.location = destination
# Used in panels/exchange_supplied_products.html
# Used in panels/exchange_products_supplied.html
$scope.suppliedVariants = (enterprise_id) ->
Enterprise.suppliedVariants(enterprise_id)
# Used in panels/exchange_supplied_products.html and panels/exchange_distributed_products.html
# Used in panels/exchange_products_supplied.html and panels/exchange_products_distributed.html
$scope.setExchangeVariants = (exchange, variants, selected) ->
OrderCycle.setExchangeVariants(exchange, variants, selected)

View File

@@ -1,10 +1,15 @@
angular.module('admin.orderCycles')
.controller 'AdminOrderCycleExchangesCtrl', ($scope, $controller, $filter, $window, $location, $timeout, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
.controller 'AdminOrderCycleExchangesCtrl', ($scope, $controller, $filter, $window, $location, $timeout, OrderCycle, ExchangeProduct, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
$controller('AdminEditOrderCycleCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
$scope.supplier_enterprises = Enterprise.producer_enterprises
$scope.distributor_enterprises = Enterprise.hub_enterprises
$scope.supplied_products = Enterprise.supplied_products
$scope.productsLoading = ->
RequestMonitor.loading
$scope.setSelectAllVariantsCheckboxValue = (exchange, totalNumberOfVariants) ->
exchange.select_all_variants = $scope.exchangeSelectedVariants(exchange) >= totalNumberOfVariants
$scope.exchangeSelectedVariants = (exchange) ->
OrderCycle.exchangeSelectedVariants(exchange)
@@ -29,14 +34,6 @@ angular.module('admin.orderCycles')
OrderCycle.removeExchangeFee(exchange, index)
$scope.order_cycle_form.$dirty = true
$scope.addSupplier = ($event) ->
$event.preventDefault()
OrderCycle.addSupplier($scope.new_supplier_id)
$scope.addDistributor = ($event) ->
$event.preventDefault()
OrderCycle.addDistributor($scope.new_distributor_id)
$scope.setPickupTimeFieldDirty = (index) ->
$timeout ->
pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
@@ -44,3 +41,36 @@ angular.module('admin.orderCycles')
$scope.removeDistributionOfVariant = (variant_id) ->
OrderCycle.removeDistributionOfVariant(variant_id)
$scope.loadExchangeProducts = (exchange, page = 1) ->
enterprise = $scope.enterprises[exchange.enterprise_id]
enterprise.supplied_products ?= []
return if enterprise.last_page_loaded? && enterprise.last_page_loaded >= page
enterprise.last_page_loaded = page
incoming = true if $scope.view == 'incoming'
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: incoming, page: page}
ExchangeProduct.index params, (products, num_of_pages, num_of_products) ->
enterprise.num_of_pages = num_of_pages
enterprise.num_of_products = num_of_products
enterprise.supplied_products.push products...
$scope.loadMoreExchangeProducts = (exchange) ->
$scope.loadExchangeProducts(exchange, $scope.enterprises[exchange.enterprise_id].last_page_loaded + 1)
$scope.loadAllExchangeProducts = (exchange) ->
enterprise = $scope.enterprises[exchange.enterprise_id]
if enterprise.last_page_loaded < enterprise.num_of_pages
for page_to_load in [(enterprise.last_page_loaded + 1)..enterprise.num_of_pages]
RequestMonitor.load $scope.loadExchangeProducts(exchange, page_to_load).$promise
RequestMonitor.loadQueue
# initialize exchange products panel if not yet done
$scope.exchangeProdutsPanelInitialized = []
$scope.initializeExchangeProductsPanel = (exchange) ->
return if $scope.exchangeProdutsPanelInitialized[exchange.enterprise_id]
RequestMonitor.load $scope.loadExchangeProducts(exchange).$promise
$scope.exchangeProdutsPanelInitialized[exchange.enterprise_id] = true

View File

@@ -1,8 +1,7 @@
angular.module('admin.orderCycles').controller 'AdminOrderCycleOutgoingCtrl', ($scope, $controller, $filter, $location, OrderCycle, ocInstance, StatusMessage) ->
$controller('AdminOrderCycleExchangesCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
$scope.productSuppliedToOrderCycle = (product) ->
OrderCycle.productSuppliedToOrderCycle(product)
$scope.view = 'outgoing'
$scope.variantSuppliedToOrderCycle = (variant) ->
OrderCycle.variantSuppliedToOrderCycle(variant)
@@ -10,6 +9,15 @@ angular.module('admin.orderCycles').controller 'AdminOrderCycleOutgoingCtrl', ($
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
$scope.exchangeTotalVariants = (exchange) ->
totalNumberOfVariants = $scope.incomingExchangeVariantsFor(exchange.enterprise_id).length
$scope.setSelectAllVariantsCheckboxValue(exchange, totalNumberOfVariants)
totalNumberOfVariants
$scope.addDistributor = ($event) ->
$event.preventDefault()
OrderCycle.addDistributor $scope.new_distributor_id
$scope.submit = ($event, destination) ->
$event.preventDefault()
StatusMessage.display 'progress', t('js.saving')

View File

@@ -1,23 +1,33 @@
angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, $controller, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, $controller, $window, OrderCycle, Enterprise, EnterpriseFee, ExchangeProduct, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
$scope.order_cycle = OrderCycle.new {coordinator_id: ocInstance.coordinator_id}, =>
# TODO: make this a get method, which only fetches one enterprise
$scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) =>
$scope.init(enterprises)
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
$scope.init = (enterprises) ->
enterprise = enterprises[Object.keys(enterprises)[0]]
OrderCycle.addSupplier enterprise.id
OrderCycle.addDistributor enterprise.id
OrderCycle.order_cycle.coordinator_id = enterprise.id
OrderCycle.addDistributor enterprise.id, $scope.setOutgoingExchange
OrderCycle.addSupplier enterprise.id, $scope.loadExchangeProducts
$scope.setOutgoingExchange = ->
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
# All variants start as checked
OrderCycle.setExchangeVariants(OrderCycle.order_cycle.incoming_exchanges[0],
Enterprise.suppliedVariants(enterprise.id), true)
$scope.loadExchangeProducts = ->
$scope.incoming_exchange = OrderCycle.order_cycle.incoming_exchanges[0]
OrderCycle.order_cycle.coordinator_id = enterprise.id
params = { enterprise_id: $scope.incoming_exchange.enterprise_id, incoming: true }
ExchangeProduct.index params, $scope.storeProductsAndSelectAllVariants
$scope.storeProductsAndSelectAllVariants = (products) ->
$scope.enterprises[$scope.incoming_exchange.enterprise_id].supplied_products = products
# All variants start as checked
OrderCycle.setExchangeVariants($scope.incoming_exchange,
Enterprise.suppliedVariants($scope.incoming_exchange.enterprise_id), true)
$scope.removeDistributionOfVariant = angular.noop

View File

@@ -1,4 +1,4 @@
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $controller, $location, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, StatusMessage, ocInstance) ->
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $controller, $location, $window, OrderCycle, Enterprise, EnterpriseFee, ExchangeProduct, Schedules, RequestMonitor, StatusMessage, ocInstance) ->
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
$scope.orderCycleId = ->
@@ -11,6 +11,12 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl",
$scope.init = ->
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
$scope.loadExchangeProducts()
$scope.loadExchangeProducts = ->
exchange = OrderCycle.order_cycle.incoming_exchanges[0]
ExchangeProduct.index { exchange_id: exchange.id }, (products) ->
$scope.enterprises[exchange.enterprise_id].supplied_products = products
$scope.removeDistributionOfVariant = angular.noop

View File

@@ -12,7 +12,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
enterprises: {}
producer_enterprises: []
hub_enterprises: []
supplied_products: []
loaded: false
index: (params={}, callback=null) ->
@@ -22,9 +21,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
@producer_enterprises.push(enterprise) if enterprise.is_primary_producer
@hub_enterprises.push(enterprise) if enterprise.sells == 'any'
for product in enterprise.supplied_products
@supplied_products.push(product)
@loaded = true
(callback || angular.noop)(@enterprises)
@@ -39,13 +35,4 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
variant.id for variant in product.variants
else
[product.master_id]
totalVariants: (enterprise) ->
numVariants = 0
if enterprise
counts = for product in enterprise.supplied_products
numVariants += if product.variants.length == 0 then 1 else product.variants.length
numVariants
})

View File

@@ -0,0 +1,16 @@
angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
ExchangeProductResource = $resource('/api/exchanges/:exchange_id/products.json', {}, {
'index': { method: 'GET' }
'variant_count': { method: 'GET', params: { action_name: "variant_count" }}
})
{
ExchangeProductResource: ExchangeProductResource
index: (params={}, callback=null) ->
ExchangeProductResource.index params, (data) =>
(callback || angular.noop)(data.products, data.pagination.pages, data.pagination.results)
countVariants: (params={}, callback=null) ->
ExchangeProductResource.variant_count params, (data) =>
(callback || angular.noop)(data.count)
})

View File

@@ -1,4 +1,4 @@
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, StatusMessage, Panels) ->
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels) ->
OrderCycleResource = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, {
'index': { method: 'GET', isArray: true}
'new' : { method: 'GET', params: { action_name: "new" } }
@@ -44,11 +44,15 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
@removeDistributionOfVariant(variant.id) if exchange.incoming
addSupplier: (new_supplier_id) ->
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
addSupplier: (new_supplier_id, callback) ->
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
$timeout ->
(callback || angular.noop)()
addDistributor: (new_distributor_id) ->
this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []})
addDistributor: (new_distributor_id, callback) ->
this.order_cycle.outgoing_exchanges.push({ enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] })
$timeout ->
(callback || angular.noop)()
removeExchange: (exchange) ->
if exchange.incoming
@@ -71,18 +75,6 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
removeExchangeFee: (exchange, index) ->
exchange.enterprise_fees.splice(index, 1)
productSuppliedToOrderCycle: (product) ->
product_variant_ids = (variant.id for variant in product.variants)
variant_ids = [product.master_id].concat(product_variant_ids)
incomingExchangesVariants = this.incomingExchangesVariants()
# TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
# Use a better algorithm if needed.
# Also, incomingExchangesVariants is called every time, when it only needs to be
# called once per change to incoming variants. Some sort of caching?
ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
ids.length > 0
variantSuppliedToOrderCycle: (variant) ->
this.incomingExchangesVariants().indexOf(variant.id) != -1
@@ -143,7 +135,8 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
delete(service.order_cycle.exchanges)
service.loaded = true
(callback || angular.noop)(service.order_cycle)
$timeout ->
(callback || angular.noop)(service.order_cycle)
this.order_cycle

View File

@@ -1,4 +1,4 @@
angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor, Orders, SortOptions, $window, $filter) ->
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter) ->
$scope.RequestMonitor = RequestMonitor
$scope.pagination = Orders.pagination
$scope.orders = Orders.all
@@ -13,6 +13,7 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
$scope.selected = false
$scope.select_all = false
$scope.poll = 0
$scope.rowStatus = {}
$scope.initialise = ->
$scope.per_page = 15
@@ -69,6 +70,23 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
$scope.fetchResults()
, true
$scope.capturePayment = (order) ->
$scope.rowAction('capture', order)
$scope.shipOrder = (order) ->
$scope.rowAction('ship', order)
$scope.rowAction = (action, order) ->
$scope.rowStatus[order.id] = "loading"
Orders[action](order).$promise.then (data) ->
$scope.rowStatus[order.id] = "success"
$timeout(->
$scope.rowStatus[order.id] = null
, 1500)
, (error) ->
$scope.rowStatus[order.id] = "error"
$scope.changePage = (newPage) ->
$scope.page = newPage
$scope.fetchResults(newPage)

View File

@@ -5,4 +5,14 @@ angular.module("admin.resources").factory 'OrderResource', ($resource) ->
method: 'GET'
'update':
method: 'PUT'
'capture':
url: '/api/orders/:id/capture.json'
method: 'PUT'
params:
id: '@id'
'ship':
url: '/api/orders/:id/ship.json'
method: 'PUT'
params:
id: '@id'
})

View File

@@ -44,5 +44,19 @@ angular.module("admin.resources").factory 'Orders', ($q, OrderResource, RequestM
changed.push attr unless attr is "$$hashKey"
changed
capture: (order) ->
@processAction('capture', order)
ship: (order) ->
@processAction('ship', order)
processAction: (action, order) ->
OrderResource[action] {id: order.number}, (data) =>
if data.id
angular.merge(order, data)
data
, (response) =>
response.data
resetAttribute: (order, attribute) ->
order[attribute] = @pristineByID[order.id][attribute]

View File

@@ -80,3 +80,11 @@ Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, Ord
$scope.query = ""
$scope.taxonSelectors.clearAll()
$scope.propertySelectors.clearAll()
$scope.refreshStaleData = ->
# If the products template has already been loaded but the controller is being initialized
# again after the template has switched, refresh loaded data to avoid conflicts
if $scope.Products.products.length > 0
$scope.Products.products = []
$scope.update_filters()
$scope.loadProducts()

View File

@@ -5,13 +5,15 @@ Darkswarm.directive "tabsetCtrl", (Tabsets, $location) ->
selected: "@"
navigate: "="
prefix: "@?"
alwaysopen: "="
controller: ($scope, $element) ->
if $scope.navigate
path = $location.path()?.match(/^\/\w+$/)?[0]
$scope.selected = path[1..] if path
this.toggle = (name) ->
Tabsets.toggle($scope.id, name)
state = if $scope.alwaysopen then 'open' else null
Tabsets.toggle($scope.id, name, state)
this.select = (selection) ->
$scope.$broadcast("selection:changed", selection)

View File

@@ -1,5 +1,7 @@
.row.exchange-distributed-products
.sixteen.columns.alpha.omega
.row.exchange-distributed-products{'ng-init' => 'initializeExchangeProductsPanel(exchange)'}
.sixteen.columns.alpha.omega{ 'ng-show' => 'enterprises[exchange.enterprise_id].supplied_products.length != 0' }
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_header.html'" }
.exchange-select-all-variants
%label
%input{ type: 'checkbox', name: 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants',
@@ -7,11 +9,10 @@
'ng-model' => 'exchange.select_all_variants',
'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)',
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' }
{{ 'admin.select_all' | t }}
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
.exchange-products
-# Scope product list based on permissions the current user has to view variants in this exchange
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
.exchange-products{ 'ng-hide' => 'productsLoading()' }
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products | filter:visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
.exchange-product-details
%label
%img{'ng-src' => '{{ product.image_url }}'}
@@ -26,3 +27,5 @@
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
{{ variant.label }}
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }

View File

@@ -0,0 +1,11 @@
.pagination{ 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages && !productsLoading()'}
.button{ 'ng-click' => 'loadMoreExchangeProducts(exchange)' }
{{ 'js.admin.panels.exchange_products.load_more_products' | t }}
.button{ 'ng-click' => 'loadAllExchangeProducts(exchange)' }
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
.sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' }
%br
%img.spinner{ src: "/assets/spinning-circles.svg" }
%h1
{{ 'js.admin.panels.exchange_products.loading_products' | t }}

View File

@@ -0,0 +1,5 @@
.exchange-load-all-variants
%div
{{ 'js.admin.panels.exchange_products.products_loaded' | t:{ num_of_products_loaded: enterprises[exchange.enterprise_id].supplied_products.length, total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}

View File

@@ -0,0 +1,12 @@
.row.exchange-supplied-products
.sixteen.columns.alpha.omega
.exchange-select-all-variants
%label
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
value: 1,
'ng-model' => 'exchange.select_all_variants',
'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)',
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
{{ 'admin.select_all' | t }}
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }

View File

@@ -0,0 +1,16 @@
.row.exchange-supplied-products{'ng-init' => 'initializeExchangeProductsPanel(exchange)'}
.sixteen.columns.alpha.omega{ 'ng-show' => 'enterprises[exchange.enterprise_id].supplied_products.length != 0' }
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_header.html'" }
.exchange-select-all-variants
%label
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
value: 1,
'ng-model' => 'exchange.select_all_variants',
'ng-change' => 'selectAllVariants(exchange, exchange.select_all_variants)',
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }

View File

@@ -0,0 +1,16 @@
.exchange-products{ 'ng-hide' => 'productsLoading()' }
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
.exchange-product-details
%label
%img{'ng-src' => '{{ product.image_url }}'}
{{ product.name }}
.exchange-product-variant{'ng-repeat' => 'variant in product.variants'}
%label
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
value: 1,
'ng-model' => 'exchange.variants[variant.id]',
'ofn-sync-distributions' => '{{ variant.id }}',
'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
{{ variant.label }}

View File

@@ -1,29 +0,0 @@
.row.exchange-supplied-products
.sixteen.columns.alpha.omega
.exchange-select-all-variants
%label
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
value: 1,
'ng-model' => 'exchange.select_all_variants',
'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)',
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
{{ 'admin.select_all' | t }}
.exchange-products
-# No need to scope product list based on permissions, because if an incoming exchange is visible,
-# then all of the variants within it should be visible. May change in the future?
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
.exchange-product-details
%label
%img{'ng-src' => '{{ product.image_url }}'}
{{ product.name }}
.exchange-product-variant{'ng-repeat' => 'variant in product.variants'}
%label
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
value: 1,
'ng-model' => 'exchange.variants[variant.id]',
'ofn-sync-distributions' => '{{ variant.id }}',
'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
{{ variant.label }}

View File

@@ -1,9 +1,9 @@
#save-bar.animate-show{ ng: { show: 'dirty || persist || StatusMessage.active()' } }
.container
.eight.columns.alpha
.seven.columns.alpha
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
{{ StatusMessage.statusMessage.text || "&nbsp;" }}
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #da5354' }
{{ StatusMessage.invalidMessage || "&nbsp;" }}
.eight.columns.omega.text-right{ ng: { transclude: true } }
.nine.columns.omega.text-right{ ng: { transclude: true } }

View File

@@ -0,0 +1,32 @@
@import '../variables';
.row-loading {
opacity: .5;
}
.row-loading-icons {
margin-left: 3em;
position: absolute;
.spinner {
border: 0;
width: 2.3em;
}
i {
font-size: 2.3em;
opacity: .75;
&::before {
vertical-align: top;
}
&.success {
color: $spree-green;
}
&.error {
color: $warning-red;
}
}
}

View File

@@ -104,9 +104,13 @@ form.order_cycle {
width: 20px;
}
.exchange-load-all-variants {
margin: 0px 5px 20px 5px;
}
.exchange-select-all-variants {
clear: both;
margin: 5px;
margin: 15px 5px 25px 5px;
}
.exchange-products {
@@ -209,6 +213,7 @@ table#listing_enterprise_groups {
#loading {
text-align: center;
img.spinner {
border: 0px;
width: 100px;
height: 100px;
}

View File

@@ -19,6 +19,10 @@ ordercycle {
margin: 0;
font-weight: 700;
}
@media all and (max-width: 768px) {
z-index: 10;
}
}
}

View File

@@ -114,12 +114,7 @@
margin-bottom: 0px;
}
.alert-box.shopfront-message {
border: 2px solid $clr-turquoise;
border-radius: 5px;
background-color: $clr-turquoise-light;
color: $clr-turquoise;
.shopfront-message {
a {
color: #0096ad;

View File

@@ -4,20 +4,25 @@
// Tabs styling
.tabset-ctrl#shop-tabs {
background: url("/assets/gray_jean.png") top left repeat;
@include box-shadow(inset 0 2px 3px 0 rgba(0, 0, 0, 0.15));
.tab-buttons {
background: url("/assets/gray_jean.png") top left repeat;
display: block;
color: $dark-grey;
@include box-shadow(inset 0 2px 3px 0 rgba(0, 0, 0, 0.15));
color: $dark-grey;
.row:first-child {
display: flex;
justify-content: space-between;
}
}
.tab {
text-align: center;
border-top: 4px solid transparent;
@media all and (max-width: 640px) {
text-align: left;
}
display: inline-block;
width: 100%;
>a {
outline: none;
@@ -35,7 +40,7 @@
line-height: 1;
font-size: 0.875em;
text-shadow: 0 -1px 1px #ffffff;
padding: 1em;
padding: 1em 2em;
border: none;
&:hover, &:focus, &:active {
@@ -50,51 +55,13 @@
padding: 0.35em 0 0.65em 0;
text-shadow: none;
}
&:after {
padding-left: 8px;
content: "";
visibility: hidden;
@include icon-font;
@media all and (max-width: 640px) {
content: "";
}
}
}
&:hover {
a:after {
visibility: visible;
}
}
&.selected {
border-top: 4px solid $clr-brick;
@media all and (max-width: 640px) {
border-top: 4px solid transparent;
background-color: $clr-brick;
}
border-bottom: 4px solid $clr-brick;
a {
color: $clr-brick;
@media all and (max-width: 640px) {
color: white;
}
}
a:after {
content: "";
visibility: visible;
@include icon-font;
@media all and (max-width: 640px) {
content: "";
}
}
}
}
@@ -102,43 +69,45 @@
// content revealed in accordion
.tab-view {
margin-bottom: 0;
padding: 0;
margin-bottom: 5em;
background: none;
border: none;
img {
margin: 0px 0px 0px 40px;
}
.content {
padding: 1.25em 0;
background-color: $white;
p {
max-width: 100%;
@media all and (max-width: 768px) {
height: auto !important;
img {
margin: 0px 0px 0px 40px;
}
}
ul {
list-style-type: none;
padding-left: none;
}
h5 {
margin-bottom: 1em;
}
.header {
text-align: center;
text-transform: uppercase;
color: $dark-grey;
border-bottom: 1px solid $disabled-dark;
margin-top: 0.75rem;
margin-bottom: 0.75rem;
padding-bottom: 0.25rem;
font-size: 0.875rem;
}
p {
max-width: 100%;
.panel {
padding-bottom: 1.25em;
border-color: $clr-brick-bright;
background-color: rgba(255, 255, 255, 0);
@media all and (max-width: 768px) {
height: auto !important;
}
}
ul {
list-style-type: none;
padding-left: none;
}
.header {
text-align: center;
text-transform: uppercase;
color: $dark-grey;
border-bottom: 1px solid $disabled-dark;
margin-top: 0.75rem;
margin-bottom: 0.75rem;
padding-bottom: 0.25rem;
font-size: 0.875rem;
}
}
}
}

View File

@@ -28,15 +28,12 @@ module Admin
def bulk_update
@enterprise_fee_set = EnterpriseFeeSet.new(params[:enterprise_fee_set])
if @enterprise_fee_set.save
redirect_path = main_app.admin_enterprise_fees_path
if params.key? :enterprise_id
redirect_path = main_app.admin_enterprise_fees_path(enterprise_id: params[:enterprise_id])
end
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
else
render :index
redirect_to redirect_path,
flash: { error: @enterprise_fee_set.errors.full_messages.to_sentence }
end
end
@@ -73,5 +70,13 @@ module Admin
def current_enterprise
Enterprise.find params[:enterprise_id] if params.key? :enterprise_id
end
def redirect_path
if params.key? :enterprise_id
return main_app.admin_enterprise_fees_path(enterprise_id: params[:enterprise_id])
end
main_app.admin_enterprise_fees_path
end
end
end

View File

@@ -66,7 +66,7 @@ module Admin
if @enterprise.update_attributes(attributes)
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
redirect_to admin_path
redirect_to admin_dashboard_path
else
flash[:error] = I18n.t(:enterprise_register_error, enterprise: @enterprise.name)
render :welcome, layout: "spree/layouts/bare_admin"

View File

@@ -22,7 +22,7 @@ module Admin
redirect_to main_app.edit_admin_enterprise_path(stripe_account.enterprise)
rescue ActiveRecord::RecordNotFound
flash[:error] = "Failed to disconnect Stripe."
redirect_to spree.admin_path
redirect_to spree.admin_dashboard_path
end
def status

View File

@@ -0,0 +1,92 @@
# This controller lists products that can be added to an exchange
module Api
class ExchangeProductsController < Api::BaseController
DEFAULT_PAGE = 1
DEFAULT_PER_PAGE = 100
skip_authorization_check only: [:index]
# If exchange_id is present in the URL:
# Lists Products that can be added to that Exchange
#
# If exchange_id is not present in the URL:
# Lists Products of the Enterprise given that can be added to the given Order Cycle
# In this case parameters are: enterprise_id, order_cycle_id and incoming
# (order_cycle_id is not necessary for incoming exchanges)
def index
if params[:exchange_id].present?
load_data_from_exchange
else
load_data_from_other_params
end
render_variant_count && return if params[:action_name] == "variant_count"
render_paginated_products paginated_products
end
private
def render_variant_count
render text: {
count: Spree::Variant.
not_master.
where(product_id: products).
count
}.to_json
end
def products
ExchangeProductsRenderer.
new(@order_cycle, spree_current_user).
exchange_products(@incoming, @enterprise)
end
def paginated_products
products.
page(params[:page] || DEFAULT_PAGE).
per(params[:per_page] || DEFAULT_PER_PAGE)
end
def load_data_from_exchange
exchange = Exchange.find_by_id(params[:exchange_id])
@order_cycle = exchange.order_cycle
@incoming = exchange.incoming
@enterprise = exchange.sender
end
def load_data_from_other_params
@enterprise = Enterprise.find_by_id(params[:enterprise_id])
if params[:order_cycle_id]
@order_cycle = OrderCycle.find_by_id(params[:order_cycle_id])
elsif !params[:incoming]
raise "order_cycle_id is required to list products for new outgoing exchange"
end
@incoming = params[:incoming]
end
def render_paginated_products(paginated_products)
serializer = ActiveModel::ArraySerializer.new(
paginated_products,
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
order_cycle: @order_cycle
)
render text: {
products: serializer,
pagination: pagination_data(paginated_products)
}.to_json
end
def pagination_data(paginated_products)
{
results: paginated_products.total_count,
pages: paginated_products.num_pages,
page: (params[:page] || DEFAULT_PAGE).to_i,
per_page: (params[:per_page] || DEFAULT_PER_PAGE).to_i
}
end
end
end

View File

@@ -16,8 +16,38 @@ module Api
}
end
def ship
authorize! :admin, order
if order.ship
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
render json: { error: I18n.t('api.orders.failed_to_update') }, status: :unprocessable_entity
end
end
def capture
authorize! :admin, order
pending_payment = order.pending_payments.first
return payment_capture_failed unless order.payment_required? && pending_payment
if pending_payment.capture!
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
payment_capture_failed
end
rescue Spree::Core::GatewayError => e
error_during_processing(e)
end
private
def payment_capture_failed
render json: { error: t(:payment_processing_failed) }, status: :unprocessable_entity
end
def serialized_orders(orders)
ActiveModel::ArraySerializer.new(
orders,

View File

@@ -42,8 +42,8 @@ module Api
def destroy
authorize! :delete, Spree::Product
@product = find_product(params[:id])
@product.update_attribute(:deleted_at, Time.zone.now)
@product.variants_including_master.update_all(deleted_at: Time.zone.now)
authorize! :delete, @product
@product.destroy
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
end
@@ -71,14 +71,6 @@ module Api
render_paged_products @products
end
def soft_delete
authorize! :delete, Spree::Product
@product = find_product(params[:product_id])
authorize! :delete, @product
@product.destroy
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
end
# POST /api/products/:product_id/clone
#
def clone

View File

@@ -35,18 +35,12 @@ module Api
end
end
def soft_delete
@variant = scope.find(params[:variant_id])
authorize! :delete, @variant
VariantDeleter.new.delete(@variant)
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
end
def destroy
authorize! :delete, Spree::Variant
@variant = scope.find(params[:id])
@variant.destroy
authorize! :delete, @variant
VariantDeleter.new.delete(@variant)
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
end

View File

@@ -1,12 +1,23 @@
module Spree
module Admin
AdjustmentsController.class_eval do
class AdjustmentsController < ResourceController
belongs_to 'spree/order', find_by: :number
destroy.after :reload_order
prepend_before_filter :set_included_tax, only: [:create, :update]
before_filter :set_default_tax_rate, only: :edit
before_filter :enable_updates, only: :update
private
def reload_order
@order.reload
end
def collection
parent.adjustments.eligible
end
# Choose a default tax rate to show on the edit form. The adjustment stores its included
# tax in dollars, but doesn't store the source of the tax (ie. TaxRate that generated it).
# We guess which tax rate here, choosing:
@@ -15,28 +26,34 @@ module Spree
# When we have to go with 2, we show an error message to ask the admin to check that the
# correct tax is being applied.
def set_default_tax_rate
if @adjustment.included_tax > 0
trs = TaxRate.match(@order)
tr_yielding_matching_tax = trs.select { |tr| tr.compute_tax(@adjustment.amount) == @adjustment.included_tax }.first.andand.id
tr_valid_for_order = TaxRate.match(@order).first.andand.id
return if @adjustment.included_tax <= 0
@tax_rate_id = tr_yielding_matching_tax || tr_valid_for_order
tax_rates = TaxRate.match(@order)
tax_rate_with_matching_tax = find_tax_rate_with_matching_tax(tax_rates)
tax_rate_valid_for_order = tax_rates.first.andand.id
if tr_yielding_matching_tax.nil?
@adjustment.errors.add :tax_rate_id, I18n.t(:adjustments_tax_rate_error)
end
@tax_rate_id = tax_rate_with_matching_tax || tax_rate_valid_for_order
return unless tax_rate_with_matching_tax.nil?
@adjustment.errors.add :tax_rate_id, I18n.t(:adjustments_tax_rate_error)
end
def find_tax_rate_with_matching_tax(tax_rates)
tax_rates_yielding_matching_tax = tax_rates.select do |tr|
tr.compute_tax(@adjustment.amount) == @adjustment.included_tax
end
tax_rates_yielding_matching_tax.first.andand.id
end
def set_included_tax
included_tax = 0
if params[:tax_rate_id].present?
tax_rate = TaxRate.find params[:tax_rate_id]
amount = params[:adjustment][:amount].to_f
params[:adjustment][:included_tax] = tax_rate.compute_tax amount
else
params[:adjustment][:included_tax] = 0
included_tax = tax_rate.compute_tax amount
end
params[:adjustment][:included_tax] = included_tax
end
# Spree 2.0 keeps shipping fee adjustments open unless they are manually

View File

@@ -0,0 +1,142 @@
module Spree
module Admin
class BaseController < Spree::BaseController
ssl_required
helper 'spree/admin/navigation'
layout '/spree/layouts/admin'
include I18nHelper
before_filter :authorize_admin
before_filter :set_locale
before_filter :warn_invalid_order_cycles, if: :html_request?
# Warn the user when they have an active order cycle with hubs that are not ready
# for checkout (ie. does not have valid shipping and payment methods).
def warn_invalid_order_cycles
distributors = active_distributors_not_ready_for_checkout
return if distributors.empty? || flash[:notice].present?
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
end
# This is in Spree::Core::ControllerHelpers::Auth
# But you can't easily reopen modules in Ruby
def unauthorized
if try_spree_current_user
flash[:error] = t(:authorization_failure)
redirect_to '/unauthorized'
else
store_location
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end
protected
def model_class
const_name = controller_name.classify
return "Spree::#{const_name}".constantize if Spree.const_defined?(const_name)
nil
end
def action
params[:action].to_sym
end
def authorize_admin
if respond_to?(:model_class, true) && model_class
record = model_class
else
# This allows specificity for each non-resource controller
# (to be consistent with "authorize_resource :class => false", see https://github.com/ryanb/cancan/blob/60cf6a67ef59c0c9b63bc27ea0101125c4193ea6/lib/cancan/controller_resource.rb#L146)
record = self.class.to_s.
sub("Controller", "").
underscore.split('/').last.singularize.to_sym
end
authorize! :admin, record
authorize! resource_authorize_action, record
end
def resource_authorize_action
action
end
def flash_message_for(object, event_sym)
resource_desc = object.class.model_name.human
resource_desc += " \"#{object.name}\"" if object.respond_to?(:name) && object.name.present?
Spree.t(event_sym, resource: resource_desc)
end
def render_js_for_destroy
render partial: '/spree/admin/shared/destroy'
end
# Index request for JSON needs to pass a CSRF token in order to prevent JSON Hijacking
def check_json_authenticity
return unless request.format.js? || request.format.json?
return unless protect_against_forgery?
auth_token = params[request_forgery_protection_token]
return if auth_token && form_authenticity_token == CGI.unescape(auth_token)
raise(ActionController::InvalidAuthenticityToken)
end
def config_locale
Spree::Backend::Config[:locale]
end
private
def active_distributors_not_ready_for_checkout
ocs = OrderCycle.managed_by(spree_current_user).active
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
end
def active_distributors_not_ready_for_checkout_message(distributors)
distributor_names = distributors.map(&:name).join ', '
if distributors.count > 1
I18n.t(:active_distributors_not_ready_for_checkout_message_plural,
distributor_names: distributor_names)
else
I18n.t(:active_distributors_not_ready_for_checkout_message_singular,
distributor_names: distributor_names)
end
end
def html_request?
request.format.html?
end
def json_request?
request.format.json?
end
def render_as_json(data, options = {})
ams_prefix = options.delete :ams_prefix
if [Array, ActiveRecord::Relation].include? data.class
render options.merge(json: data, each_serializer: serializer(ams_prefix))
else
render options.merge(json: data, serializer: serializer(ams_prefix))
end
end
def serializer(ams_prefix)
unless ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym)
raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}."
end
prefix = ams_prefix.andand.classify || ""
name = controller_name.classify
"::Api::Admin::#{prefix}#{name}Serializer".constantize
end
end
end
end

View File

@@ -1,105 +0,0 @@
require 'spree/core/controller_helpers/respond_with_decorator'
Spree::Admin::BaseController.class_eval do
include I18nHelper
layout 'spree/layouts/admin'
before_filter :set_locale
before_filter :warn_invalid_order_cycles, if: :html_request?
# Warn the user when they have an active order cycle with hubs that are not ready
# for checkout (ie. does not have valid shipping and payment methods).
def warn_invalid_order_cycles
distributors = active_distributors_not_ready_for_checkout
if distributors.any? && flash[:notice].nil?
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
end
end
# Override Spree method
# It's a shame Spree doesn't just let CanCan handle this in it's own way
def authorize_admin
if respond_to?(:model_class, true) && model_class
record = model_class
else
# this line changed to allow specificity for each non-resource controller (to be consistent with "authorize_resource :class => false", see https://github.com/ryanb/cancan/blob/60cf6a67ef59c0c9b63bc27ea0101125c4193ea6/lib/cancan/controller_resource.rb#L146)
record = self.class.to_s.sub("Controller", "").underscore.split('/').last.singularize.to_sym
end
authorize! :admin, record
authorize! resource_authorize_action, record
end
def resource_authorize_action
action
end
# This is in Spree::Core::ControllerHelpers::Auth
# But you can't easily reopen modules in Ruby
def unauthorized
if try_spree_current_user
flash[:error] = t(:authorization_failure)
redirect_to '/unauthorized'
else
store_location
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end
protected
def model_class
const_name = controller_name.classify
if Spree.const_defined?(const_name)
return "Spree::#{const_name}".constantize
end
nil
end
private
def active_distributors_not_ready_for_checkout
ocs = OrderCycle.managed_by(spree_current_user).active
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
end
def active_distributors_not_ready_for_checkout_message(distributors)
distributor_names = distributors.map(&:name).join ', '
if distributors.count > 1
I18n.t(:active_distributors_not_ready_for_checkout_message_plural, distributor_names: distributor_names)
else
I18n.t(:active_distributors_not_ready_for_checkout_message_singular, distributor_names: distributor_names)
end
end
def html_request?
request.format.html?
end
def json_request?
request.format.json?
end
def render_as_json(data, options = {})
ams_prefix = options.delete :ams_prefix
if [Array, ActiveRecord::Relation].include? data.class
render options.merge(json: data, each_serializer: serializer(ams_prefix))
else
render options.merge(json: data, serializer: serializer(ams_prefix))
end
end
def serializer(ams_prefix)
if ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym)
prefix = ams_prefix.andand.classify || ""
name = controller_name.classify
"Api::Admin::#{prefix}#{name}Serializer".constantize
else
raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}."
end
end
end

View File

@@ -5,8 +5,7 @@ module Spree
@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]
:allow_ssl_in_staging, :allow_ssl_in_development_and_test]
@preferences_currency = [:display_currency, :hide_cents]
end
@@ -20,18 +19,6 @@ module Spree
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

@@ -0,0 +1,251 @@
require 'open_food_network/spree_api_key_loader'
require 'open_food_network/referer_parser'
require 'open_food_network/permissions'
module Spree
module Admin
class ProductsController < ResourceController
helper 'spree/products'
include OpenFoodNetwork::SpreeApiKeyLoader
include OrderCyclesHelper
include EnterprisesHelper
create.before :create_before
update.before :update_before
before_filter :load_data
before_filter :load_form_data, only: [:index, :new, :create, :edit, :update]
before_filter :load_spree_api_key, only: [:index, :variant_overrides]
before_filter :strip_new_properties, only: [:create, :update]
respond_override create: { html: {
success: lambda {
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
},
failure: lambda {
render :new
}
} }
def new
@object.shipping_category = DefaultShippingCategory.find_or_create
super
end
def create
delete_stock_params_and_set_after do
super
end
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
invoke_callbacks(:create, :fails)
@object.errors.add(:base, t('spree.admin.products.image_upload_error'))
respond_with(@object)
end
def show
session[:return_to] ||= request.referer
redirect_to( action: :edit )
end
def index
@current_user = spree_current_user
@show_latest_import = params[:latest_import] || false
end
def update
original_supplier_id = @product.supplier_id
delete_stock_params_and_set_after do
super
if original_supplier_id != @product.supplier_id
ExchangeVariantDeleter.new.delete(@product)
end
end
end
def bulk_update
product_set = product_set_from_params(params)
product_set.collection.each { |p| authorize! :update, p }
if product_set.save
redirect_to main_app.bulk_products_api_products_path( bulk_index_query(params) )
elsif product_set.errors.present?
render json: { errors: product_set.errors }, status: :bad_request
else
render nothing: true, status: :internal_server_error
end
end
def clone
@new = @product.duplicate
flash[:success] = if @new.save
Spree.t('notice_messages.product_cloned')
else
Spree.t('notice_messages.product_not_cloned')
end
redirect_to edit_admin_product_url(@new)
end
protected
def find_resource
Product.find_by_permalink!(params[:id])
end
def location_after_save
spree.edit_admin_product_url(@product)
end
def load_data
@taxons = Taxon.order(:name)
@option_types = OptionType.order(:name)
@tax_categories = TaxCategory.order(:name)
@shipping_categories = ShippingCategory.order(:name)
end
def collection
return @collection if @collection.present?
params[:q] ||= {}
params[:q][:deleted_at_null] ||= "1"
params[:q][:s] ||= "name asc"
@collection = Spree::Product
@collection = @collection.with_deleted if params[:q].delete(:deleted_at_null).blank?
# @search needs to be defined as this is passed to search_form_for
@search = @collection.ransack(params[:q])
@collection = @search.result.
managed_by(spree_current_user).
group_by_products_id.
includes(product_includes).
page(params[:page]).
per(Spree::Config[:admin_products_per_page])
if params[:q][:s].include?("master_default_price_amount")
# PostgreSQL compatibility
@collection = @collection.group("spree_prices.amount")
end
@collection
end
def create_before
return if params[:product][:prototype_id].blank?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
def update_before
# We only reset the product properties if we're receiving a post from the form on that tab
return unless params[:clear_product_properties]
params[:product] ||= {}
end
def product_includes
[{ variants: [:images, { option_values: :option_type }] },
{ master: [:images, :default_price] }]
end
def collection_actions
[:index, :bulk_update]
end
private
def product_set_from_params(params)
collection_hash = Hash[params[:products].each_with_index.map { |p, i| [i, p] }]
Spree::ProductSet.new(collection_attributes: collection_hash)
end
def bulk_index_query(params)
params[:filters].to_h.merge(page: params[:page], per_page: params[:per_page])
end
def load_form_data
@producers = OpenFoodNetwork::Permissions.new(spree_current_user).
managed_product_enterprises.is_primary_producer.by_name
@taxons = Spree::Taxon.order(:name)
@import_dates = product_import_dates.uniq.to_json
end
def product_import_dates
options = [{ id: '0', name: '' }]
product_import_dates_query.collect(&:import_date).
map { |i| options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long)) }
options
end
def product_import_dates_query
Spree::Variant.
select('DISTINCT spree_variants.import_date').
joins(:product).
where('spree_products.supplier_id IN (?)', editable_enterprises.collect(&:id)).
where('spree_variants.import_date IS NOT NULL').
where(spree_variants: { is_master: false }).
where(spree_variants: { deleted_at: nil }).
order('spree_variants.import_date DESC')
end
def strip_new_properties
return if spree_current_user.admin? || params[:product][:product_properties_attributes].nil?
names = Spree::Property.pluck(:name)
params[:product][:product_properties_attributes].each do |key, property|
unless names.include? property[:property_name]
params[:product][:product_properties_attributes].delete key
end
end
end
def delete_stock_params_and_set_after
on_demand = params[:product].delete(:on_demand)
on_hand = params[:product].delete(:on_hand)
yield
set_stock_levels(@product, on_hand, on_demand) if @product.valid?
end
def set_stock_levels(product, on_hand, on_demand)
variant = product_variant(product)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant)
raise e
end
end
def notify_bugsnag(error, product, variant)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def product_variant(product)
if product.variants.any?
product.variants.first
else
product.master
end
end
def set_product_master_variant_price_to_zero
@product.price = 0 if @product.price.nil?
end
end
end
end

View File

@@ -1,201 +0,0 @@
require 'open_food_network/spree_api_key_loader'
require 'open_food_network/referer_parser'
require 'open_food_network/permissions'
Spree::Admin::ProductsController.class_eval do
include OpenFoodNetwork::SpreeApiKeyLoader
include OrderCyclesHelper
include EnterprisesHelper
before_filter :load_data
before_filter :load_form_data, only: [:index, :new, :create, :edit, :update]
before_filter :load_spree_api_key, only: [:index, :variant_overrides]
before_filter :strip_new_properties, only: [:create, :update]
respond_override create: { html: {
success: lambda {
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
},
failure: lambda {
render :new
}
} }
def index
@current_user = spree_current_user
@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
end
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
invoke_callbacks(:create, :fails)
@object.errors.add(:base, t('spree.admin.products.image_upload_error'))
respond_with(@object)
end
def update
original_supplier_id = @product.supplier_id
delete_stock_params_and_set_after do
super
ExchangeVariantDeleter.new.delete(@product) if original_supplier_id != @product.supplier_id
end
end
def bulk_update
product_set = product_set_from_params(params)
# Ensure we're authorised to update all products
product_set.collection.each { |p| authorize! :update, p }
if product_set.save
redirect_to main_app.bulk_products_api_products_path( bulk_index_query(params) )
else
if product_set.errors.present?
render json: { errors: product_set.errors }, status: :bad_request
else
render nothing: true, status: :internal_server_error
end
end
end
protected
def collection
# This method is copied directly from the spree product controller
# except where we narrow the search below with the managed_by search to support
# enterprise users.
# TODO: There has to be a better way!!!
return @collection if @collection.present?
params[:q] ||= {}
params[:q][:deleted_at_null] ||= "1"
params[:q][:s] ||= "name asc"
# The next line is modified.
# Hit Spree::Product instead of super, avoiding cancan error for fetching
# records with block permissions via accessible_by.
@collection = Spree::Product
@collection = @collection.with_deleted if params[:q].delete(:deleted_at_null).blank?
# @search needs to be defined as this is passed to search_form_for
@search = @collection.ransack(params[:q])
@collection = @search.result.
managed_by(spree_current_user). # this line is added to the original spree code!!!!!
group_by_products_id.
includes(product_includes).
page(params[:page]).
per(Spree::Config[:admin_products_per_page])
if params[:q][:s].include?("master_default_price_amount")
# PostgreSQL compatibility
@collection = @collection.group("spree_prices.amount")
end
@collection
end
def collection_actions
[:index, :bulk_update]
end
private
def product_set_from_params(params)
collection_hash = Hash[params[:products].each_with_index.map { |p, i| [i, p] }]
Spree::ProductSet.new(collection_attributes: collection_hash)
end
def bulk_index_query(params)
params[:filters].to_h.merge(page: params[:page], per_page: params[:per_page])
end
def load_form_data
@producers = OpenFoodNetwork::Permissions.new(spree_current_user).
managed_product_enterprises.is_primary_producer.by_name
@taxons = Spree::Taxon.order(:name)
@import_dates = product_import_dates.uniq.to_json
end
def product_import_dates
options = [{ id: '0', name: '' }]
product_import_dates_query.collect(&:import_date).
map { |i| options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long)) }
options
end
def product_import_dates_query
Spree::Variant.
select('DISTINCT spree_variants.import_date').
joins(:product).
where('spree_products.supplier_id IN (?)', editable_enterprises.collect(&:id)).
where('spree_variants.import_date IS NOT NULL').
where(spree_variants: { is_master: false }).
where(spree_variants: { deleted_at: nil }).
order('spree_variants.import_date DESC')
end
def strip_new_properties
return if spree_current_user.admin? || params[:product][:product_properties_attributes].nil?
names = Spree::Property.pluck(:name)
params[:product][:product_properties_attributes].each do |key, property|
unless names.include? property[:property_name]
params[:product][:product_properties_attributes].delete key
end
end
end
def delete_stock_params_and_set_after
on_demand = params[:product].delete(:on_demand)
on_hand = params[:product].delete(:on_hand)
yield
set_stock_levels(@product, on_hand, on_demand) if @product.valid?
end
def set_stock_levels(product, on_hand, on_demand)
variant = product_variant(product)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant)
raise e
end
end
def notify_bugsnag(error, product, variant)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def product_variant(product)
if product.variants.any?
product.variants.first
else
product.master
end
end
def set_product_master_variant_price_to_zero
@product.price = 0 if @product.price.nil?
end
end

View File

@@ -0,0 +1,317 @@
require 'csv'
require 'open_food_network/reports/list'
require 'open_food_network/order_and_distributor_report'
require 'open_food_network/products_and_inventory_report'
require 'open_food_network/lettuce_share_report'
require 'open_food_network/group_buy_report'
require 'open_food_network/order_grouper'
require 'open_food_network/customers_report'
require 'open_food_network/users_and_enterprises_report'
require 'open_food_network/order_cycle_management_report'
require 'open_food_network/packing_report'
require 'open_food_network/sales_tax_report'
require 'open_food_network/xero_invoices_report'
require 'open_food_network/bulk_coop_report'
require 'open_food_network/payments_report'
require 'open_food_network/orders_and_fulfillments_report'
module Spree
module Admin
class ReportsController < Spree::Admin::BaseController
include Spree::ReportsHelper
helper_method :render_content?
before_filter :cache_search_state
# Fetches user's distributors, suppliers and order_cycles
before_filter :load_data,
only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
respond_to :html
def report_types
OpenFoodNetwork::Reports::List.all
end
def index
@reports = authorized_reports
respond_with(@reports)
end
def customers
@report_types = report_types[:customers]
@report_type = params[:report_type]
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, params, render_content?
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
end
def order_cycle_management
params[:q] ||= {}
@report_types = report_types[:order_cycle_management]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
params,
render_content?
@table = @report.table_items
render_report(@report.header, @table, params[:csv],
"order_cycle_management_#{timestamp}.csv")
end
def packing
params[:q] ||= {}
@report_types = report_types[:packing]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params, render_content?
@table = order_grouper_table
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
end
def orders_and_distributors
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
params,
render_content?
@search = @report.search
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
render_report(@report.header, @report.table, params[:csv], csv_file_name)
end
def sales_tax
@distributors = my_distributors
@report_type = params[:report_type]
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params, render_content?
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
end
def bulk_coop
# -- Prepare form options
@distributors = my_distributors
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params, render_content?
@table = order_grouper_table
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def payments
# -- Prepare Form Options
@distributors = my_distributors
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params, render_content?
@table = order_grouper_table
csv_file_name = "payments_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def orders_and_fulfillment
params[:q] ||= orders_and_fulfillment_default_filters
# -- Prepare Form Options
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
# My distributors and any distributors distributing products I supply
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
# My suppliers and any suppliers supplying products I distribute
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
@order_cycles = my_order_cycles
@report_types = report_types[:orders_and_fulfillment]
@report_type = params[:report_type]
@include_blank = I18n.t(:all)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
params,
render_content?
@table = order_grouper_table
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def products_and_inventory
@report_types = report_types[:products_and_inventory]
@report = if params[:report_type] != 'lettuce_share'
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
params,
render_content?
else
OpenFoodNetwork::LettuceShareReport.new spree_current_user,
params,
render_content?
end
render_report @report.header,
@report.table,
params[:csv],
"products_and_inventory_#{timestamp}.csv"
end
def users_and_enterprises
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content?
render_report(@report.header, @report.table, params[:csv],
"users_and_enterprises_#{timestamp}.csv")
end
def xero_invoices
params[:q] ||= {}
@distributors = my_distributors
@order_cycles = my_order_cycles
@report = OpenFoodNetwork::XeroInvoicesReport.new(spree_current_user,
params,
render_content?)
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
end
private
def model_class
Spree::Admin::ReportsController
end
# Some actions are changing the `params` object. That is unfortunate Spree
# behavior and we are building on it. So we have to look at `params` early
# to check if we are searching or just displaying a report search form.
def cache_search_state
search_keys = [
# search parameter for ransack
:q,
# common in all reports, only set for CSV rendering
:csv,
# `button` is included in all forms. It's not important for searching,
# but the Users & Enterprises report doesn't have any other parameter
# for an empty search. So we use this one to display data.
:button,
# Some reports use filtering by enterprise or order cycle
:distributor_id,
:supplier_id,
:order_cycle_id,
# Xero Invoices can be filtered by date
:invoice_date,
:due_date
]
@searching = search_keys.any? { |key| params.key? key }
end
# We don't want to render data unless search params are supplied.
# Compiling data can take a long time.
def render_content?
@searching
end
def render_report(header, table, create_csv, csv_file_name)
send_data csv_report(header, table), filename: csv_file_name if create_csv
@header = header
@table = table
# Rendering HTML is the default.
end
def csv_report(header, table)
CSV.generate do |csv|
csv << header
table.each { |row| csv << row }
end
end
def load_data
@distributors = my_distributors
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
@order_cycles = my_order_cycles
end
# Load managed distributor enterprises of current user
def my_distributors
Enterprise.is_distributor.managed_by(spree_current_user)
end
# Load managed producer enterprises of current user
def my_suppliers
Enterprise.is_primary_producer.managed_by(spree_current_user)
end
def suppliers_of_products_distributed_by(distributors)
distributors.map { |d| Spree::Product.in_distributor(d).includes(:supplier).all }.
flatten.map(&:supplier).uniq
end
# Load order cycles the current user has access to
def my_order_cycles
OrderCycle.
active_or_complete.
accessible_by(spree_current_user).
order('orders_close_at DESC')
end
def order_grouper_table
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
order_grouper.table(@report.table_items)
end
def authorized_reports
all_reports = [
:orders_and_distributors,
:bulk_coop,
:payments,
:orders_and_fulfillment,
:customers,
:products_and_inventory,
:users_and_enterprises,
:enterprise_fee_summary,
:order_cycle_management,
:sales_tax,
:xero_invoices,
:packing
]
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
reports.map { |report| [report, describe_report(report)] }.to_h
end
def describe_report(report)
name = I18n.t(:name, scope: [:admin, :reports, report])
description = begin
I18n.t!(:description, scope: [:admin, :reports, report])
rescue I18n::MissingTranslationData
render_to_string(
partial: "#{report}_description",
layout: false,
locals: { report_types: report_types[report] }
).html_safe
end
{ name: name, url: url_for_report(report), description: description }
end
def url_for_report(report)
public_send("#{report}_admin_reports_url".to_sym)
rescue NoMethodError
url_for([:new, :admin, :reports, report.to_s.singularize])
end
def timestamp
Time.zone.now.strftime("%Y%m%d")
end
def orders_and_fulfillment_default_filters
now = Time.zone.now
{ completed_at_gt: (now - 1.month).beginning_of_day,
completed_at_lt: (now + 1.day).beginning_of_day }
end
end
end
end

View File

@@ -1,302 +0,0 @@
require 'csv'
require 'open_food_network/reports/list'
require 'open_food_network/order_and_distributor_report'
require 'open_food_network/products_and_inventory_report'
require 'open_food_network/lettuce_share_report'
require 'open_food_network/group_buy_report'
require 'open_food_network/order_grouper'
require 'open_food_network/customers_report'
require 'open_food_network/users_and_enterprises_report'
require 'open_food_network/order_cycle_management_report'
require 'open_food_network/packing_report'
require 'open_food_network/sales_tax_report'
require 'open_food_network/xero_invoices_report'
require 'open_food_network/bulk_coop_report'
require 'open_food_network/payments_report'
require 'open_food_network/orders_and_fulfillments_report'
Spree::Admin::ReportsController.class_eval do
include Spree::ReportsHelper
helper_method :render_content?
before_filter :cache_search_state
# Fetches user's distributors, suppliers and order_cycles
before_filter :load_data,
only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
def report_types
OpenFoodNetwork::Reports::List.all
end
# Override spree reports list.
def index
@reports = authorized_reports
respond_with(@reports)
end
def customers
@report_types = report_types[:customers]
@report_type = params[:report_type]
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, params, render_content?
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
end
def order_cycle_management
params[:q] ||= {}
@report_types = report_types[:order_cycle_management]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
params,
render_content?
@table = @report.table_items
render_report(@report.header, @table, params[:csv], "order_cycle_management_#{timestamp}.csv")
end
def packing
params[:q] ||= {}
@report_types = report_types[:packing]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params, render_content?
@table = order_grouper_table
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
end
def orders_and_distributors
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
params,
render_content?
@search = @report.search
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
render_report(@report.header, @report.table, params[:csv], csv_file_name)
end
def sales_tax
@distributors = my_distributors
@report_type = params[:report_type]
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params, render_content?
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
end
def bulk_coop
# -- Prepare form options
@distributors = my_distributors
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params, render_content?
@table = order_grouper_table
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def payments
# -- Prepare Form Options
@distributors = my_distributors
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params, render_content?
@table = order_grouper_table
csv_file_name = "payments_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def orders_and_fulfillment
params[:q] ||= orders_and_fulfillment_default_filters
# -- Prepare Form Options
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
# My distributors and any distributors distributing products I supply
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
# My suppliers and any suppliers supplying products I distribute
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
@order_cycles = my_order_cycles
@report_types = report_types[:orders_and_fulfillment]
@report_type = params[:report_type]
@include_blank = I18n.t(:all)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
params,
render_content?
@table = order_grouper_table
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def products_and_inventory
@report_types = report_types[:products_and_inventory]
@report = if params[:report_type] != 'lettuce_share'
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
params,
render_content?
else
OpenFoodNetwork::LettuceShareReport.new spree_current_user, params, render_content?
end
render_report(@report.header,
@report.table,
params[:csv],
"products_and_inventory_#{timestamp}.csv")
end
def users_and_enterprises
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content?
render_report(@report.header,
@report.table,
params[:csv],
"users_and_enterprises_#{timestamp}.csv")
end
def xero_invoices
params[:q] ||= {}
@distributors = my_distributors
@order_cycles = my_order_cycles
@report = OpenFoodNetwork::XeroInvoicesReport.new spree_current_user, params, render_content?
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
end
private
# Some actions are changing the `params` object. That is unfortunate Spree
# behavior and we are building on it. So we have to look at `params` early
# to check if we are searching or just displaying a report search form.
def cache_search_state
search_keys = [
# search parameter for ransack
:q,
# common in all reports, only set for CSV rendering
:csv,
# `button` is included in all forms. It's not important for searching,
# but the Users & Enterprises report doesn't have any other parameter
# for an empty search. So we use this one to display data.
:button,
# Some reports use filtering by enterprise or order cycle
:distributor_id,
:supplier_id,
:order_cycle_id,
# Xero Invoices can be filtered by date
:invoice_date,
:due_date
]
@searching = search_keys.any? { |key| params.key? key }
end
# We don't want to render data unless search params are supplied.
# Compiling data can take a long time.
def render_content?
@searching
end
def render_report(header, table, create_csv, csv_file_name)
send_data csv_report(header, table), filename: csv_file_name if create_csv
@header = header
@table = table
# Rendering HTML is the default.
end
def csv_report(header, table)
CSV.generate do |csv|
csv << header
table.each { |row| csv << row }
end
end
def load_data
@distributors = my_distributors
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
@order_cycles = my_order_cycles
end
# Load managed distributor enterprises of current user
def my_distributors
Enterprise.is_distributor.managed_by(spree_current_user)
end
# Load managed producer enterprises of current user
def my_suppliers
Enterprise.is_primary_producer.managed_by(spree_current_user)
end
def suppliers_of_products_distributed_by(distributors)
distributors.map { |d| Spree::Product.in_distributor(d).includes(:supplier).all }.
flatten.map(&:supplier).uniq
end
# Load order cycles the current user has access to
def my_order_cycles
OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC')
end
def order_grouper_table
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
order_grouper.table(@report.table_items)
end
def authorized_reports
all_reports = [
:orders_and_distributors,
:bulk_coop,
:payments,
:orders_and_fulfillment,
:customers,
:products_and_inventory,
:sales_total,
:users_and_enterprises,
:enterprise_fee_summary,
:order_cycle_management,
:sales_tax,
:xero_invoices,
:packing
]
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
reports.map { |report| [report, describe_report(report)] }.to_h
end
def describe_report(report)
name = I18n.t(:name, scope: [:admin, :reports, report])
description = begin
I18n.t!(:description, scope: [:admin, :reports, report])
rescue I18n::MissingTranslationData
render_to_string(
partial: "#{report}_description",
layout: false,
locals: { report_types: report_types[report] }
).html_safe
end
{ name: name, url: url_for_report(report), description: description }
end
def url_for_report(report)
public_send("#{report}_admin_reports_url".to_sym)
rescue NoMethodError
url_for([:new, :admin, :reports, report.to_s.singularize])
end
def timestamp
Time.zone.now.strftime("%Y%m%d")
end
def orders_and_fulfillment_default_filters
now = Time.zone.now
{ completed_at_gt: (now - 1.month).beginning_of_day,
completed_at_lt: (now + 1.day).beginning_of_day }
end
end

View File

@@ -0,0 +1,275 @@
require 'action_callbacks'
module Spree
module Admin
class ResourceController < Spree::Admin::BaseController
helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
before_filter :load_resource, except: [:update_positions]
rescue_from ActiveRecord::RecordNotFound, with: :resource_not_found
rescue_from CanCan::AccessDenied, with: :unauthorized
respond_to :html
respond_to :js, except: [:show, :index]
def new
invoke_callbacks(:new_action, :before)
respond_with(@object) do |format|
format.html { render layout: !request.xhr? }
format.js { render layout: false }
end
end
def edit
respond_with(@object) do |format|
format.html { render layout: !request.xhr? }
format.js { render layout: false }
end
end
def update
invoke_callbacks(:update, :before)
if @object.update_attributes(params[object_name])
invoke_callbacks(:update, :after)
flash[:success] = flash_message_for(@object, :successfully_updated)
respond_with(@object) do |format|
format.html { redirect_to location_after_save }
format.js { render layout: false }
end
else
invoke_callbacks(:update, :fails)
respond_with(@object)
end
end
def create
invoke_callbacks(:create, :before)
@object.attributes = params[object_name]
if @object.save
invoke_callbacks(:create, :after)
flash[:success] = flash_message_for(@object, :successfully_created)
respond_with(@object) do |format|
format.html { redirect_to location_after_save }
format.js { render layout: false }
end
else
invoke_callbacks(:create, :fails)
respond_with(@object)
end
end
def update_positions
params[:positions].each do |id, index|
model_class.where(id: id).update_all(position: index)
end
respond_to do |format|
format.js { render text: 'Ok' }
end
end
def destroy
invoke_callbacks(:destroy, :before)
if @object.destroy
invoke_callbacks(:destroy, :after)
flash[:success] = flash_message_for(@object, :successfully_removed)
respond_with(@object) do |format|
format.html { redirect_to collection_url }
format.js { render partial: "spree/admin/shared/destroy" }
end
else
invoke_callbacks(:destroy, :fails)
respond_with(@object) do |format|
format.html { redirect_to collection_url }
end
end
end
protected
def resource_not_found
flash[:error] = flash_message_for(model_class.new, :not_found)
redirect_to collection_url
end
class << self
attr_accessor :parent_data
attr_accessor :callbacks
def belongs_to(model_name, options = {})
@parent_data ||= {}
@parent_data[:model_name] = model_name
@parent_data[:model_class] = model_name.to_s.classify.constantize
@parent_data[:find_by] = options[:find_by] || :id
end
def new_action
@callbacks ||= {}
@callbacks[:new_action] ||= ActionCallbacks.new
end
def create
@callbacks ||= {}
@callbacks[:create] ||= ActionCallbacks.new
end
def update
@callbacks ||= {}
@callbacks[:update] ||= ActionCallbacks.new
end
def destroy
@callbacks ||= {}
@callbacks[:destroy] ||= ActionCallbacks.new
end
end
def model_class
"Spree::#{controller_name.classify}".constantize
end
def model_name
parent_data[:model_name].gsub('spree/', '')
end
def object_name
controller_name.singularize
end
def load_resource
if member_action?
@object ||= load_resource_instance
# call authorize! a third time (called twice already in Admin::BaseController)
# this time we pass the actual instance so fine-grained abilities can control
# access to individual records, not just entire models.
authorize! action, @object
instance_variable_set("@#{object_name}", @object)
# If we don't have access, clear the object
unless can? action, @object
instance_variable_set("@#{object_name}", nil)
end
authorize! action, @object
else
@collection ||= collection
# note: we don't call authorize here as the collection method should use
# CanCan's accessible_by method to restrict the actual records returned
instance_variable_set("@#{controller_name}", @collection)
end
end
def load_resource_instance
if new_actions.include?(action)
build_resource
elsif params[:id]
find_resource
end
end
def parent_data
self.class.parent_data
end
def parent
return nil if parent_data.blank?
@parent ||= parent_data[:model_class].
public_send("find_by_#{parent_data[:find_by]}", params["#{model_name}_id"])
instance_variable_set("@#{model_name}", @parent)
end
def find_resource
if parent_data.present?
parent.public_send(controller_name).find(params[:id])
else
model_class.find(params[:id])
end
end
def build_resource
if parent_data.present?
parent.public_send(controller_name).build
else
model_class.new
end
end
def collection
return parent.public_send(controller_name) if parent_data.present?
if model_class.respond_to?(:accessible_by) &&
!current_ability.has_block?(params[:action], model_class)
model_class.accessible_by(current_ability, action)
else
model_class.scoped
end
end
def location_after_save
collection_url
end
def invoke_callbacks(action, callback_type)
callbacks = self.class.callbacks || {}
return if callbacks[action].nil?
case callback_type.to_sym
when :before then callbacks[action].before_methods.each { |method| __send__ method }
when :after then callbacks[action].after_methods.each { |method| __send__ method }
when :fails then callbacks[action].fails_methods.each { |method| __send__ method }
end
end
# URL helpers
def new_object_url(options = {})
if parent_data.present?
spree.new_polymorphic_url([:admin, parent, model_class], options)
else
spree.new_polymorphic_url([:admin, model_class], options)
end
end
def edit_object_url(object, options = {})
if parent_data.present?
spree.public_send "edit_admin_#{model_name}_#{object_name}_url", parent, object, options
else
spree.public_send "edit_admin_#{object_name}_url", object, options
end
end
def object_url(object = nil, options = {})
target = object || @object
if parent_data.present?
spree.public_send "admin_#{model_name}_#{object_name}_url", parent, target, options
else
spree.public_send "admin_#{object_name}_url", target, options
end
end
def collection_url(options = {})
if parent_data.present?
spree.polymorphic_url([:admin, parent, model_class], options)
else
spree.polymorphic_url([:admin, model_class], options)
end
end
def collection_actions
[:index]
end
def member_action?
!collection_actions.include? action
end
def new_actions
[:new, :create]
end
end
end
end

View File

@@ -1,20 +0,0 @@
module AuthorizeOnLoadResource
def load_resource
super
if member_action?
# If we don't have access, clear the object
unless can? action, @object
instance_variable_set("@#{object_name}", nil)
end
authorize! action, @object
end
end
end
Spree::Admin::ResourceController.prepend(AuthorizeOnLoadResource)
Spree::Admin::ResourceController.class_eval do
rescue_from CanCan::AccessDenied, with: :unauthorized
end

View File

@@ -0,0 +1,68 @@
require 'open_food_network/scope_variants_for_search'
module Spree
module Admin
class VariantsController < ResourceController
helper 'spree/products'
belongs_to 'spree/product', find_by: :permalink
new_action.before :new_before
def create
on_demand = params[:variant].delete(:on_demand)
on_hand = params[:variant].delete(:on_hand)
super
return unless @object.present? && @object.valid?
@object.on_demand = on_demand if on_demand.present?
@object.on_hand = on_hand.to_i if on_hand.present?
end
def search
scoper = OpenFoodNetwork::ScopeVariantsForSearch.new(params)
@variants = scoper.search
render json: @variants, each_serializer: ::Api::Admin::VariantSerializer
end
def destroy
@variant = Spree::Variant.find(params[:id])
flash[:success] = if VariantDeleter.new.delete(@variant)
Spree.t('notice_messages.variant_deleted')
else
Spree.t('notice_messages.variant_not_deleted')
end
respond_with(@variant) do |format|
format.html { redirect_to admin_product_variants_url(params[:product_id]) }
end
end
protected
def create_before
option_values = params[:new_variant]
option_values.andand.each_value { |id| @object.option_values << OptionValue.find(id) }
@object.save
end
def new_before
@object.attributes = @object.product.master.
attributes.except('id', 'created_at', 'deleted_at', 'sku', 'is_master')
# Shallow Clone of the default price to populate the price field.
@object.default_price = @object.product.master.default_price.clone
end
def collection
@deleted = params.key?(:deleted) && params[:deleted] == "on" ? "checked" : ""
@collection ||= if @deleted.blank?
super
else
Variant.where(product_id: parent.id).deleted
end
@collection
end
end
end
end

View File

@@ -1,45 +0,0 @@
require 'open_food_network/scope_variants_for_search'
Spree::Admin::VariantsController.class_eval do
helper 'spree/products'
def create
on_demand = params[:variant].delete(:on_demand)
on_hand = params[:variant].delete(:on_hand)
super
if @object.present? && @object.valid?
@object.on_demand = on_demand if on_demand.present?
@object.on_hand = on_hand.to_i if on_hand.present?
end
end
def search
scoper = OpenFoodNetwork::ScopeVariantsForSearch.new(params)
@variants = scoper.search
render json: @variants, each_serializer: Api::Admin::VariantSerializer
end
def destroy
@variant = Spree::Variant.find(params[:id])
flash[:success] = if VariantDeleter.new.delete(@variant) # This line changed
Spree.t('notice_messages.variant_deleted')
else
Spree.t('notice_messages.variant_not_deleted')
end
respond_with(@variant) do |format|
format.html { redirect_to admin_product_variants_url(params[:product_id]) }
format.js { render_js_for_destroy }
end
end
protected
def create_before
option_values = params[:new_variant]
option_values.andand.each_value { |id| @object.option_values << OptionValue.find(id) }
@object.save
end
end

View File

@@ -176,7 +176,7 @@ module Spree
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
hash[adjustment.id] = adjustment.state
end
@order.adjustments.each(&:open)
@order.adjustments.each { |adjustment| adjustment.fire_events(:open) }
yield

View File

@@ -0,0 +1,11 @@
module Admin
module EnterprisesHelper
def add_check_if_single(count)
if count == 1
{ checked: true }
else
{}
end
end
end
end

View File

@@ -1,3 +1,7 @@
# frozen_string_literal: true
require 'open_food_network/permissions'
module OrderCyclesHelper
def current_order_cycle
@current_order_cycle ||= current_order(false).andand.order_cycle

View File

@@ -22,10 +22,24 @@ module ShopHelper
def shop_tabs
[
{ name: 'about', title: t(:shopping_tabs_about, distributor: current_distributor.name), cols: 6 },
{ name: 'producers', title: t(:label_producers), cols: 2 },
{ name: 'contact', title: t(:shopping_tabs_contact), cols: 2 },
{ name: 'groups', title: t(:label_groups), cols: 2 },
]
{ name: 'home', title: t(:shopping_tabs_home), show: show_home_tab? },
{ name: 'shop', title: t(:shopping_tabs_shop), show: !require_customer? },
{ name: 'about', title: t(:shopping_tabs_about), show: true },
{ name: 'producers', title: t(:label_producers), show: true },
{ name: 'contact', title: t(:shopping_tabs_contact), show: true },
{ name: 'groups', title: t(:label_groups), show: current_distributor.groups.any? },
].select{ |tab| tab[:show] }
end
def show_home_tab?
require_customer? || current_distributor.preferred_shopfront_message.present?
end
def shopfront_closed_message?
no_open_order_cycles? && current_distributor.preferred_shopfront_closed_message.present?
end
def no_open_order_cycles?
@order_cycles && @order_cycles.empty?
end
end

View File

@@ -1,3 +1,5 @@
require 'spree/admin/base_helper'
module Spree
module Admin
module BaseHelper

View File

@@ -1,3 +1,5 @@
require 'spree/admin/navigation_helper'
module Spree
module Admin
module NavigationHelper

View File

@@ -1,3 +1,3 @@
class EnterpriseRelationshipPermission < ActiveRecord::Base
default_scope order('name')
default_scope { order('name') }
end

View File

@@ -142,9 +142,9 @@ class OrderCycle < ActiveRecord::Base
oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name)
oc.orders_open_at = oc.orders_close_at = nil
oc.coordinator_fee_ids = coordinator_fee_ids
# rubocop:disable Metrics/LineLength
# rubocop:disable Layout/LineLength
oc.preferred_product_selection_from_coordinator_inventory_only = preferred_product_selection_from_coordinator_inventory_only
# rubocop:enable Metrics/LineLength
# rubocop:enable Layout/LineLength
oc.save!
exchanges.each { |e| e.clone!(oc) }
oc.reload

View File

@@ -2,7 +2,7 @@ class ProducerProperty < ActiveRecord::Base
belongs_to :producer, class_name: 'Enterprise', touch: true
belongs_to :property, class_name: 'Spree::Property'
default_scope order("#{table_name}.position")
default_scope { order("#{table_name}.position") }
scope :ever_sold_by, ->(shop) {
joins(producer: { supplied_products: { variants: { exchanges: :order_cycle } } }).

View File

@@ -214,7 +214,9 @@ module ProductImport
end
def accepted_mimetype
File.extname(@file.path).in?('.csv', '.xls', '.xlsx', '.ods') ? @file.path.split('.').last.to_sym : false
return false unless ['.csv'].include? File.extname(@file.path)
@file.path.split('.').last.to_sym
end
def headers

View File

@@ -45,11 +45,11 @@ module ProductImport
next if @enterprises_index.key? enterprise_name
enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, is_primary_producer')
enterprise = Enterprise.select([:id, :is_primary_producer]).
where(name: enterprise_name).first
@enterprises_index[enterprise_name] =
{ id: enterprise.try(:id),
is_primary_producer: enterprise.try(:is_primary_producer) }
{ id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) }
end
@enterprises_index
end
@@ -60,7 +60,8 @@ module ProductImport
next unless entry.producer
producer_name = entry.producer
producer_id = @producers_index[producer_name] || Enterprise.find_by_name(producer_name, select: 'id, name').try(:id)
producer_id = @producers_index[producer_name] ||
Enterprise.select([:id, :name]).where(name: producer_name).first.try(:id)
@producers_index[producer_name] = producer_id
end
@producers_index
@@ -70,7 +71,8 @@ module ProductImport
@categories_index = {}
@entries.each do |entry|
category_name = entry.category
category_id = @categories_index[category_name] || Spree::Taxon.find_by_name(category_name, select: 'id, name').try(:id)
category_id = @categories_index[category_name] ||
Spree::Taxon.select([:id, :name]).where(name: category_name).first.try(:id)
@categories_index[category_name] = category_id
end
@categories_index

View File

@@ -1,7 +1,5 @@
module Spree
class User < ActiveRecord::Base
include Core::UserBanners
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,
:rememberable, :trackable, :validatable, :encryptable, encryptor: 'authlogic_sha512'

View File

@@ -6,6 +6,8 @@ class SubscriptionLineItem < ActiveRecord::Base
validates :variant, presence: true
validates :quantity, presence: true, numericality: { only_integer: true }
default_scope { order('id ASC') }
def total_estimate
(price_estimate || 0) * (quantity || 0)
end
@@ -22,6 +24,4 @@ class SubscriptionLineItem < ActiveRecord::Base
def price
price_estimate
end
default_scope order('id ASC')
end

View File

@@ -12,7 +12,7 @@ class VariantOverride < ActiveRecord::Base
# Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user.
validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
default_scope where(permission_revoked_at: nil)
default_scope { where(permission_revoked_at: nil) }
scope :for_hubs, lambda { |hubs|
where(hub_id: hubs)

View File

@@ -1,7 +1,7 @@
require 'open_food_network/enterprise_issue_validator'
class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
attributes :id, :name, :managed, :supplied_products,
attributes :id, :name, :managed,
:issues_summary_supplier, :issues_summary_distributor,
:is_primary_producer, :is_distributor, :sells
@@ -25,12 +25,6 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
Enterprise.managed_by(options[:spree_current_user]).include? object
end
def supplied_products
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
ActiveModel::ArraySerializer.new(products, each_serializer: serializer,
order_cycle: order_cycle)
end
private
def products_scope

View File

@@ -14,7 +14,8 @@ class Api::Admin::ForOrderCycle::SuppliedProductSerializer < ActiveModel::Serial
end
def variants
variants = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
variants = if order_cycle.present? &&
order_cycle.prefers_product_selection_from_coordinator_inventory_only?
object.variants.visible_for(order_cycle.coordinator)
else
object.variants

View File

@@ -1,8 +1,8 @@
class Api::Admin::OrderSerializer < ActiveModel::Serializer
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,
:payments_path, :ready_to_ship, :ready_to_capture, :created_at,
:distributor_name, :special_instructions,
:item_total, :adjustment_total, :payment_total, :total
has_one :distributor, serializer: Api::Admin::IdSerializer
@@ -28,15 +28,9 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer
spree_routes_helper.admin_order_payments_path(object)
end
def ship_path
spree_routes_helper.fire_admin_order_path(object, e: 'ship')
end
def payment_capture_path
def ready_to_capture
pending_payment = object.pending_payments.first
return '' unless object.payment_required? && pending_payment
spree_routes_helper.fire_admin_order_payment_path(object, pending_payment.id, e: 'capture')
object.payment_required? && pending_payment
end
def ready_to_ship

View File

@@ -0,0 +1,23 @@
class ActionCallbacks
attr_reader :before_methods
attr_reader :after_methods
attr_reader :fails_methods
def initialize
@before_methods = []
@after_methods = []
@fails_methods = []
end
def before(method)
@before_methods << method
end
def after(method)
@after_methods << method
end
def fails(method)
@fails_methods << method
end
end

View File

@@ -0,0 +1,86 @@
class ExchangeProductsRenderer
def initialize(order_cycle, user)
@order_cycle = order_cycle
@user = user
end
def exchange_products(incoming, enterprise)
if incoming
products_for_incoming_exchange(enterprise)
else
products_for_outgoing_exchange
end
end
private
def products_for_incoming_exchange(enterprise)
supplied_products(enterprise.id)
end
def supplied_products(enterprises_query_matcher)
products_relation = Spree::Product.where(supplier_id: enterprises_query_matcher)
if @order_cycle.present? &&
@order_cycle.prefers_product_selection_from_coordinator_inventory_only?
products_relation = products_relation.visible_for(@order_cycle.coordinator)
end
products_relation
end
def products_for_outgoing_exchange
supplied_products(enterprises_for_outgoing_exchange.select(:id)).
includes(:variants).
where("spree_variants.id": incoming_exchanges_variants)
end
def incoming_exchanges_variants
return @incoming_exchanges_variants if @incoming_exchanges_variants.present?
@incoming_exchanges_variants = []
visible_incoming_exchanges.each do |incoming_exchange|
@incoming_exchanges_variants.push(
*incoming_exchange.variants.merge(
visible_incoming_variants(incoming_exchange.sender)
).map(&:id).to_a
)
end
@incoming_exchanges_variants
end
def visible_incoming_exchanges
OpenFoodNetwork::OrderCyclePermissions.
new(@user, @order_cycle).
visible_exchanges.
by_enterprise_name.
incoming
end
def visible_incoming_variants(incoming_exchange_sender)
variants_relation = permitted_incoming_variants(incoming_exchange_sender)
if @order_cycle.prefers_product_selection_from_coordinator_inventory_only?
variants_relation = variants_relation.visible_for(@order_cycle.coordinator)
end
variants_relation
end
def permitted_incoming_variants(incoming_exchange_sender)
OpenFoodNetwork::OrderCyclePermissions.
new(@user, @order_cycle).
visible_variants_for_incoming_exchanges_from(incoming_exchange_sender)
end
def enterprises_for_outgoing_exchange
enterprises = OpenFoodNetwork::OrderCyclePermissions.
new(@user, @order_cycle)
.visible_enterprises
return enterprises if enterprises.empty?
enterprises.includes(
supplied_products: [:supplier, :variants, master: [:images]]
)
end
end

View File

@@ -7,17 +7,17 @@ module Permissions
# Find orders that the user can see
def visible_orders
Spree::Order.where(id:
managed_orders.select(:id) |
coordinated_orders.select(:id) |
produced_orders.select("spree_orders.id"))
Spree::Order.
with_line_items_variants_and_products_outer.
where(visible_orders_where_values)
end
# Any orders that the user can edit
def editable_orders
Spree::Order.where(id:
managed_orders.select(:id) |
coordinated_orders.select(:id) )
Spree::Order.where(
managed_orders_where_values.
or(coordinated_orders_where_values)
)
end
def visible_line_items
@@ -28,27 +28,45 @@ module Permissions
# Any line items that I can edit
def editable_line_items
Spree::LineItem.where(order_id: editable_orders)
Spree::LineItem.where(order_id: editable_orders.select(:id))
end
private
def visible_orders_where_values
# Grouping keeps the 2 where clauses from produced_orders_where_values inside parentheses
# This way it makes the OR work between the 3 types of orders:
# produced, managed and coordinated
Spree::Order.arel_table.
grouping(produced_orders_where_values).
or(managed_orders_where_values).
or(coordinated_orders_where_values)
end
# Any orders placed through any hub that I manage
def managed_orders
Spree::Order.where(distributor_id: @permissions.managed_enterprises.select("enterprises.id"))
def managed_orders_where_values
Spree::Order.
where(distributor_id: @permissions.managed_enterprises.select("enterprises.id")).
where_values.
reduce(:and)
end
# Any order that is placed through an order cycle one of my managed enterprises coordinates
def coordinated_orders
Spree::Order.where(order_cycle_id: @permissions.coordinated_order_cycles.select(:id))
def coordinated_orders_where_values
Spree::Order.
where(order_cycle_id: @permissions.coordinated_order_cycles.select(:id)).
where_values.
reduce(:and)
end
def produced_orders
def produced_orders_where_values
Spree::Order.with_line_items_variants_and_products_outer.
where(
distributor_id: granted_distributor_ids,
spree_products: { supplier_id: enterprises_with_associated_orders }
)
).
where_values.
reduce(:and)
end
def enterprises_with_associated_orders
@@ -69,7 +87,7 @@ module Permissions
# Any from visible orders, where the product is produced by one of my managed producers
def produced_line_items
Spree::LineItem.where(order_id: visible_orders.select(:id)).
Spree::LineItem.where(order_id: visible_orders.select("DISTINCT spree_orders.id")).
joins(:product).
where(spree_products:
{

View File

@@ -1,11 +1,7 @@
%tr{ ng: { class: "'#{type} #{type}-{{ exchange.enterprise_id }}'" } }
%td{:class => "#{type}_name"} {{ enterprises[exchange.enterprise_id].name }}
%td.products.panel-toggle.text-center{ name: "products" }
{{ exchangeSelectedVariants(exchange) }} /
- if type == 'supplier'
{{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }}
- else
{{ (incomingExchangeVariantsFor(exchange.enterprise_id)).length }}
{{ exchangeSelectedVariants(exchange) }} / {{ exchangeTotalVariants(exchange) }}
= t('.selected')
- if type == 'supplier'
%td.receival-details
@@ -34,11 +30,11 @@
- if type == 'supplier'
%tr.panel-row{ object: "exchange",
panels: "{products: 'exchange_supplied_products'}",
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,suppliedVariants,removeDistributionOfVariant",
panels: "{products: 'exchange_products_supplied'}",
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,selectAllVariants,suppliedVariants,removeDistributionOfVariant,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
colspan: 4 }
- if type == 'distributor'
%tr.panel-row{ object: "exchange",
panels: "{products: 'exchange_distributed_products', tags: 'exchange_tags'}",
locals: "$index,order_cycle,exchange,supplied_products,setExchangeVariants,incomingExchangeVariantsFor,productSuppliedToOrderCycle,variantSuppliedToOrderCycle",
panels: "{products: 'exchange_products_distributed', tags: 'exchange_tags'}",
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
colspan: 5 }

View File

@@ -17,7 +17,7 @@
%table.exchanges
%tbody{ng: {repeat: "exchange in order_cycle.incoming_exchanges"}}
%tr.products
%td{ ng: { include: "'admin/panels/exchange_supplied_products.html'" } }
%td{ ng: { include: "'admin/panels/exchange_products_simple.html'" } }
%br/
= label_tag t('.fees')

View File

@@ -7,7 +7,6 @@
= render 'wizard_progress'
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
%input{ type: "button", value: t('.previous'), ng: { click: "cancel('#{main_app.edit_admin_order_cycle_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } }
%input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
%input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
%input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } }

View File

@@ -7,7 +7,6 @@
= render 'wizard_progress'
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
%input{ type: "button", value: t('.previous'), ng: { click: "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } }
%input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
%input.red{ type: "button", value: t('.save_and_back_to_list'), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }

View File

@@ -15,7 +15,7 @@
%strong
= pickup_time current_order_cycle
= render partial: "shopping_shared/details"
= render partial: "shopping_shared/header"
%accordion{"close-others" => "false"}
%checkout.row{"ng-controller" => "CheckoutCtrl"}

View File

@@ -8,7 +8,7 @@
%p
= t(".email_userguide_html", link: link_to(t(".userguide"), ContentConfig.user_guide_link))
%p
= t(".email_admin_html", link: link_to(t(".admin_panel"), spree.admin_url))
= t(".email_admin_html", link: link_to(t(".admin_panel"), spree.admin_dashboard_url))
%p
= t(".email_community_html", link: link_to(t(".join_community"), ContentConfig.community_forum_url))

View File

@@ -36,11 +36,7 @@
= render partial: "shopping_shared/details"
= render partial: 'shop/messages'
- unless require_customer?
.row= render partial: "shop/products/form"
= render partial: "shopping_shared/header"
= render partial: "shopping_shared/tabs"
= render partial: "shared/footer"

View File

@@ -8,38 +8,6 @@
.small-12.medium-8.medium-offset-2.columns.text-center
.alert-box
= render 'shared/register_call'
.row
.small-12.medium-4.medium-offset-2.columns.text-center
%h6
= t '.footer_global_headline'
%p
%a{href: "http://www.openfoodnetwork.org", target: "_blank"}
= t '.footer_global_home'
%span &#124;
%a{href: "http://www.openfoodnetwork.org/news/", target: "_blank"}
= t '.footer_global_news'
%span &#124;
%a{href: "http://www.openfoodnetwork.org/about/history-team/", target: "_blank"}
= t '.footer_global_about'
%span &#124;
%a{href: "http://www.openfoodnetwork.org/contact/", target: "_blank"}
= t '.footer_global_contact'
.small-12.medium-4.columns.text-center
%h6
= t '.footer_sites_headline'
%p
%a{href: "http://dev.openfoodnetwork.org", target: "_blank"}
= t '.footer_sites_developer'
%span &#124;
%a{href: "http://community.openfoodnetwork.org", target: "_blank"}
= t '.footer_sites_community'
%span &#124;
%a{href: "http://www.openfoodnetwork.org/platform/user-guide/", target: "_blank"}
= t '.footer_sites_userguide'
.medium-2.columns.text-center
/ Placeholder
.footer-local
.row

View File

@@ -15,7 +15,7 @@
- if admin_user? or enterprise_user?
%li
%a{href: spree.admin_path, target:'_blank'}
%a{href: spree.admin_dashboard_path, target:'_blank'}
%i.ofn-i_021-tools
= t 'label_administration'

View File

@@ -7,7 +7,7 @@
- if admin_user? or enterprise_user?
%li
%a{href: spree.admin_path, target:'_blank'}
%a{href: spree.admin_dashboard_path, target:'_blank'}
%i.ofn-i_021-tools
= t 'label_admin'

View File

@@ -14,18 +14,8 @@
= t '.require_customer_html',
{contact: link_to(t('.contact'), '#contact'),
enterprise: current_distributor.name}
- elsif @order_cycles and @order_cycles.empty?
- if current_distributor.preferred_shopfront_closed_message.present?
.row
.small-12.columns
.shopfront_closed_message
= current_distributor.preferred_shopfront_closed_message.html_safe
- elsif current_distributor.preferred_shopfront_message.present?
.row
.small-12.columns
&nbsp;
.row
.small-12.columns
.alert-box.shopfront-message{ "ofn-inline-alert" => true, ng: { show: "visible" } }
.shopfront-message
= current_distributor.preferred_shopfront_message.html_safe
%a.close{ ng: { click: "close()" } } &times;

View File

@@ -1,5 +1,5 @@
.footer-pad.small-12.columns
%products{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true }
%products{"ng-controller" => "ProductsCtrl", "ng-init" => "refreshStaleData()", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true }
// TODO: Needs an ng-show to slide content down
.row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" }
@@ -28,26 +28,27 @@
.small-12.medium-6.large-6.large-offset-1.columns
= render partial: "shop/products/filters"
%div.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading' }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
%shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
%product{"ng-show" => "Products.loading"}
.row.summary
.small-12.columns.text-center
= t :products_loading
.row
.small-12.columns.text-center
%img.spinner{ src: "/assets/spinning-circles.svg" }
%div{"ng-show" => "Products.products.length == 0 && !Products.loading"}
.row.summary
.small-12.columns
%p.no-results
= t :search_no_results_html, query: "<strong>{{query}}</strong>".html_safe
.row
.small-12.columns
%form{action: main_app.cart_path}
%i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"}
%input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? '#{t(:products_updating_cart)}' : (Cart.empty() ? '#{t(:products_cart_empty)}' : '#{t(:products_edit_cart)}' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" }
%div.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading' }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
%shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
%product{"ng-show" => "Products.loading"}
.row.summary
.small-12.columns.text-center
= t :products_loading
.row
.small-12.columns.text-center
%img.spinner{ src: "/assets/spinning-circles.svg" }
%div{"ng-show" => "Products.products.length == 0 && !Products.loading"}
.row.summary
.small-12.columns
%p.no-results
= t :search_no_results_html, query: "<strong>{{query}}</strong>".html_safe
.row
.small-12.columns
%form{action: main_app.cart_path}
%i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"}
%input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? '#{t(:products_updating_cart)}' : (Cart.empty() ? '#{t(:products_cart_empty)}' : '#{t(:products_edit_cart)}' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" }

View File

@@ -1,9 +0,0 @@
%script{ type: "text/ng-template", id: "shop/about.html" }
.content#about{"ng-controller" => "AboutUsCtrl"}
.panel
.row
.small-12.large-8.columns
%img.hero-img-small{"ng-src" => "{{::shopfront.promo_image}}", "ng-if" => "::shopfront.promo_image"}
%p{"ng-bind-html" => "::shopfront.long_description"}
.small-12.large-4.columns
&nbsp;

View File

@@ -1,66 +0,0 @@
%script{ type: "text/ng-template", id: "shop/contact.html" }
.content#contact
.panel
.row
.small-12.large-4.columns
- if current_distributor.address.address1 || current_distributor.address.address2 || current_distributor.address.city || current_distributor.address.state || current_distributor.address.zipcode
%div.center
.header
= t :shopping_contact_address
%strong=current_distributor.name
%p
= current_distributor.address.address1
- unless current_distributor.address.address2.blank?
%br
= current_distributor.address.address2
%br
= current_distributor.address.city
= current_distributor.address.state
= current_distributor.address.zipcode
.small-12.large-4.columns
- if current_distributor.website || current_distributor.email_address || current_distributor.phone
%div.center
.header
= t :shopping_contact_web
%p
- unless current_distributor.phone.blank?
= current_distributor.phone
%br
- unless current_distributor.website.blank?
%a{href: "http://#{current_distributor.website}", target: "_blank" }
= current_distributor.website
%br
- unless current_distributor.email_address.blank?
%a{href: current_distributor.email_address.reverse, mailto: true}
%span.email
= current_distributor.email_address.reverse
.small-12.large-4.columns
- if current_distributor.twitter.present? || current_distributor.facebook.present? || current_distributor.linkedin.present? || current_distributor.instagram.present?
%div.center
.header
= t :shopping_contact_social
%div.follow-icons
- unless current_distributor.twitter.blank?
%span
%a{href: "http://twitter.com/#{current_distributor.twitter}", target: "_blank" }
%i.ofn-i_041-twitter
- unless current_distributor.facebook.blank?
%span
%a{href: "http://#{current_distributor.facebook}", target: "_blank" }
%i.ofn-i_044-facebook
/ = current_distributor.facebook
- unless current_distributor.linkedin.blank?
%span
%a{href: "http://#{current_distributor.linkedin}", target: "_blank" }
%i.ofn-i_042-linkedin
/ = current_distributor.linkedin
- unless current_distributor.instagram.blank?
%span
%a{href: "http://instagram.com/#{current_distributor.instagram}", target: "_blank" }
%i.ofn-i_043-instagram
/ = current_distributor.instagram

View File

@@ -1,14 +0,0 @@
%script{ type: "text/ng-template", id: "shop/groups.html" }
.content
.panel
.row
.small-12.large-4.columns
- if current_distributor.groups.length > 0
%h5
=current_distributor.name
= t :shopping_groups_part_of
%ul.bullet-list
- for group in current_distributor.groups
%li
%a{href: main_app.groups_path + "/#{group.permalink}"}
= group.name

View File

@@ -13,6 +13,3 @@
.small-12.medium-6.large-6.columns
= render partial: "shopping_shared/order_cycles"
= render partial: "shopping_shared/tabs" if distributor == current_distributor

View File

@@ -3,18 +3,6 @@
%ordercycle{"ng-controller" => "OrderCycleCtrl"}
- if @order_cycles and @order_cycles.empty?
%h4
%i.ofn-i_012-warning
= t :shopping_oc_closed
%p
= t :shopping_oc_closed_description
.text-right
%small
%em
= render partial: "shopping_shared/next_order_cycle"
= render partial: "shopping_shared/last_order_cycle"
- else
- unless no_open_order_cycles?
%form.custom
= yield :order_cycle_form

View File

@@ -1,12 +0,0 @@
%script{ type: "text/ng-template", id: "shop/producers.html" }
.content#producers{"ng-controller" => "ProducersTabCtrl"}
.panel
.row
.small-12.columns
%h5
= t :shopping_producers_of_hub, hub: '{{ shopfront.name }}'
%ul.small-block-grid-2.large-block-grid-4
%li{"ng-repeat" => "enterprise in shopfront.producers"}
%enterprise-modal
%i.ofn-i_036-producers
{{ enterprise.name }}

View File

@@ -1,9 +1,13 @@
- shop_tabs.each do |tab|
= render "shopping_shared/#{tab[:name]}"
- if (@order.andand.distributor || current_distributor) == current_distributor
.tabset-ctrl#shop-tabs{ navigate: 'true', prefix: 'shop', ng: { cloak: true } }
.row
- shop_tabs.each do |tab|
.small-12.columns.tab{ id: "tab_#{tab[:name]}", name: tab[:name], class: "medium-#{tab[:cols]}" }
%a{ href: 'javascript:void(0)' }=tab[:title]
.small-12.columns.tab-view
- shop_tabs.each do |tab|
= render "shopping_shared/tabs/#{tab[:name]}"
.tabset-ctrl#shop-tabs{ navigate: 'true', alwaysopen: 'true', selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } }
.tab-buttons
.row
- shop_tabs.each do |tab|
.tab{ id: "tab_#{tab[:name]}", name: tab[:name] }
%a{ href: 'javascript:void(0)' }=tab[:title]
.tab-view

View File

@@ -0,0 +1,10 @@
%script{ type: "text/ng-template", id: "shop/about.html" }
#about{"ng-controller" => "AboutUsCtrl"}
%img.hero-img-small{"ng-src" => "{{::shopfront.promo_image}}", "ng-if" => "::shopfront.promo_image"}
.content
.row
.small-12.columns
%p{"ng-bind-html" => "::shopfront.long_description"}
.small-12.large-4.columns
&nbsp;

View File

@@ -0,0 +1,65 @@
%script{ type: "text/ng-template", id: "shop/contact.html" }
.content#contact
.row
.small-12.large-4.columns
- if current_distributor.address.address1 || current_distributor.address.address2 || current_distributor.address.city || current_distributor.address.state || current_distributor.address.zipcode
%div.center
.header
= t :shopping_contact_address
%strong=current_distributor.name
%p
= current_distributor.address.address1
- unless current_distributor.address.address2.blank?
%br
= current_distributor.address.address2
%br
= current_distributor.address.city
= current_distributor.address.state
= current_distributor.address.zipcode
.small-12.large-4.columns
- if current_distributor.website || current_distributor.email_address || current_distributor.phone
%div.center
.header
= t :shopping_contact_web
%p
- if current_distributor.phone.present?
= current_distributor.phone
%br
- if current_distributor.website.present?
%a{href: "http://#{current_distributor.website}", target: "_blank" }
= current_distributor.website
%br
- if current_distributor.email_address.present?
%a{href: current_distributor.email_address.reverse, mailto: true}
%span.email
= current_distributor.email_address.reverse
.small-12.large-4.columns
- if current_distributor.twitter.present? || current_distributor.facebook.present? || current_distributor.linkedin.present? || current_distributor.instagram.present?
%div.center
.header
= t :shopping_contact_social
%div.follow-icons
- if current_distributor.twitter.present?
%span
%a{href: "http://twitter.com/#{current_distributor.twitter}", target: "_blank" }
%i.ofn-i_041-twitter
- if current_distributor.facebook.present?
%span
%a{href: "http://#{current_distributor.facebook}", target: "_blank" }
%i.ofn-i_044-facebook
/ = current_distributor.facebook
- if current_distributor.linkedin.present?
%span
%a{href: "http://#{current_distributor.linkedin}", target: "_blank" }
%i.ofn-i_042-linkedin
/ = current_distributor.linkedin
- if current_distributor.instagram.present?
%span
%a{href: "http://instagram.com/#{current_distributor.instagram}", target: "_blank" }
%i.ofn-i_043-instagram
/ = current_distributor.instagram

View File

@@ -0,0 +1,13 @@
%script{ type: "text/ng-template", id: "shop/groups.html" }
.content
.row
.small-12.large-4.columns
- if current_distributor.groups.length > 0
%h5
=current_distributor.name
= t :shopping_groups_part_of
%ul.bullet-list
- for group in current_distributor.groups
%li
%a{href: main_app.groups_path + "/#{group.permalink}"}
= group.name

View File

@@ -0,0 +1,3 @@
%script{ type: "text/ng-template", id: "shop/home.html" }
.content
= render partial: 'shop/messages'

View File

@@ -0,0 +1,11 @@
%script{ type: "text/ng-template", id: "shop/producers.html" }
.content#producers{"ng-controller" => "ProducersTabCtrl"}
.row
.small-12.columns
%h5
= t :shopping_producers_of_hub, hub: '{{ shopfront.name }}'
%ul.small-block-grid-2.large-block-grid-4
%li{"ng-repeat" => "enterprise in shopfront.producers"}
%enterprise-modal
%i.ofn-i_036-producers
{{ enterprise.name }}

View File

@@ -0,0 +1,23 @@
%script{ type: "text/ng-template", id: "shop/shop.html" }
.row
.small-12.columns
- if no_open_order_cycles?
.content
%h4
%i.ofn-i_012-warning
= t :shopping_oc_closed
%small
%em
(
= render partial: "shopping_shared/next_order_cycle"
= render partial: "shopping_shared/last_order_cycle"
)
%p
= t :shopping_oc_closed_description
- if shopfront_closed_message?
.shopfront_closed_message
= current_distributor.preferred_shopfront_closed_message.html_safe
- unless require_customer?
= render partial: "shop/products/form"

View File

@@ -10,4 +10,4 @@
= render :partial => 'adjustments_table'
= button_link_to t(:continue), @order.cart? ? new_admin_order_payment_url(@order) : admin_orders_url, :icon => 'icon-arrow-right'
= button_link_to t(:continue), admin_order_payments_url(@order), :icon => 'icon-arrow-right'

View File

@@ -47,7 +47,7 @@
%th.actions
%tbody
%tr{ng: {repeat: 'order in orders track by order.id', class: {even: "'even'", odd: "'odd'"}}, 'ng-class' => "'state-{{order.state}}'"}
%tr{ng: {repeat: 'order in orders track by order.id', class: {even: "'even'", odd: "'odd'"}}, 'ng-class' => "{'state-{{order.state}}': true, 'row-loading': rowStatus[order.id] == 'loading'}"}
%td.align-center
%input{type: 'checkbox', 'ng-model' => 'checkboxes[order.id]', 'ng-change' => 'toggleSelection(order.id)'}
%td.align-center
@@ -78,11 +78,15 @@
%td.align-center
%span{'ng-bind-html' => 'order.display_total'}
%td.actions
%div.row-loading-icons
%img.spinner{src: "/assets/spinning-circles.svg", ng: {show: 'rowStatus[order.id] == "loading"'} }
%i.success.icon-ok-sign{ng: {show: 'rowStatus[order.id] == "success"'} }
%i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')}
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
%div{'ng-if' => 'order.ready_to_ship'}
%a.icon-road.icon_link.with-tip.no-text{'ng-href' => '{{order.ship_path}}', 'data-action' => 'ship', 'data-confirm' => t(:are_you_sure), 'data-method' => 'put', rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
%div{'ng-if' => 'order.payment_capture_path'}
%a.icon-capture.icon_link.no-text{'ng-href' => '{{order.payment_capture_path}}', 'data-action' => 'capture', 'data-method' => 'put', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
%button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', 'data-confirm' => t(:are_you_sure), rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
%div{'ng-if' => 'order.ready_to_capture'}
%button.icon-capture.icon_link.no-text{'ng-click' => 'capturePayment(order)', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
.orders-loading{'ng-show' => 'RequestMonitor.loading'}
.row

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