Compare commits

...

131 Commits

Author SHA1 Message Date
Pau Pérez Fabregat
be19d50639 Merge pull request #4768 from openfoodfoundation/transifex
Transifex
2020-02-06 11:11:23 +01:00
Transifex-Openfoodnetwork
0ceb8ab6c4 Updating translations for config/locales/ca.yml 2020-02-06 21:09:16 +11:00
Pau Pérez Fabregat
e387c7db83 Merge pull request #4548 from luisramos0/improve_truncate_data
Improve truncate data process
2020-02-06 11:01:36 +01:00
Pau Pérez Fabregat
d5df48f3c0 Merge pull request #4737 from openfoodfoundation/transifex
Transifex
2020-02-06 11:00:46 +01:00
Pau Pérez Fabregat
c9abdac2e0 Merge pull request #4722 from mkllnk/code-climate-rubocop
Bump Code Climate's Rubocop version to their latest
2020-02-06 10:32:46 +01:00
Transifex-Openfoodnetwork
ff08d9f210 Updating translations for config/locales/nb.yml 2020-02-06 09:39:52 +11:00
Luis Ramos
9313a57d19 Merge pull request #4511 from luisramos0/backend_ctrl_search
Bring spree_backend search controller to OFN
2020-02-05 15:54:30 +00:00
luisramos0
b0f2e01c70 Delete extra inventory_units that are for some unknown reason connected to shipments of orders to delete but not to orders to delete 2020-02-05 16:30:14 +11:00
Maikel
747be81aec Link to new release template 2020-02-05 15:21:26 +11:00
Maikel
fd124daf50 Add task to notify instance managers of upcoming changes 2020-02-05 14:58:57 +11:00
Transifex-Openfoodnetwork
ef33d27e6c Updating translations for config/locales/en_FR.yml 2020-02-05 06:55:27 +11:00
Transifex-Openfoodnetwork
3a01e00d7b Updating translations for config/locales/fr.yml 2020-02-05 06:51:14 +11:00
Luis Ramos
81103f3f71 Merge pull request #4742 from CSCI-462-01-2020/Issue4109
Edit new_resource partial
2020-02-04 19:44:16 +00:00
Luis Ramos
a8b48a561f Merge pull request #4733 from lin-d-hop/master
Fixing incorrect payment and balance calculation
2020-02-04 18:11:48 +00:00
Luis Ramos
c7038f6ac9 Merge pull request #4620 from luisramos0/spree_backend_css
Bring basic CSS rules from spree backend
2020-02-04 18:06:15 +00:00
Luis Ramos
ea75714561 Merge pull request #4635 from luisramos0/main_app_routes
Replace use of spree.root_path and root_url with main_app.root_path and main_app.root_url
2020-02-04 16:02:48 +00:00
luisramos0
b7d19dd34c Remove unused route from Spree.routes 2020-02-04 14:55:34 +00:00
luisramos0
44d1b3f00c Remove unused search/users controller action 2020-02-04 14:55:34 +00:00
luisramos0
f8376c0aeb Remove unused route search_customer_addresses 2020-02-04 14:55:34 +00:00
luisramos0
88464d58c2 Fix some rubocop issues and extract ransack hash to reduce repetition 2020-02-04 14:55:34 +00:00
luisramos0
962779bba1 Merge search controller with decorator 2020-02-04 14:54:51 +00:00
luisramos0
1ae73dd6ae Add search_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-02-04 14:54:51 +00:00
luisramos0
f48ab57782 Add explicit css import statements where they are needed 2020-02-04 14:52:44 +00:00
luisramos0
8b6ebfb351 Add imports for new css files from spree_backend 2020-02-04 14:52:44 +00:00
luisramos0
9c0788c3e0 Bring shared css rules from spree_backend 2020-02-04 14:52:17 +00:00
luisramos0
f61258d0b4 Bring components css from spree_backend
pagination and navigation rules were merged
2020-02-04 14:52:17 +00:00
luisramos0
289f62f115 Bring and require date picker css from spree_backend 2020-02-04 14:52:17 +00:00
luisramos0
c3a98d5a4f Bring css dependencies from spree_backend
These dependencies are in spree_core so we dont need to bring them to ofn right now
2020-02-04 14:52:17 +00:00
Luis Ramos
d8374e9caa Merge pull request #4614 from luisramos0/base_admin_js
Bring some basic js functions and dependencies from spree_backend
2020-02-04 14:48:38 +00:00
luisramos0
0648f23e94 Move css dependencies to specific css files and add some required dependencies from spree_backend 2020-02-04 14:47:51 +00:00
luisramos0
e37cb2d63f Remove underscope from select2 css file name 2020-02-04 14:47:51 +00:00
luisramos0
ee64238bdd Add jquery.adaptivemenu require but commeent out the activation for now because it is already called in spree_backend
This can be reverted when spree_backend is removed
2020-02-04 14:47:51 +00:00
luisramos0
d3130e111f Remove on click events
These events can only be added to OFN when spree_backend is removed otherwise the evetns will be registered twice
2020-02-04 14:47:51 +00:00
luisramos0
93f4eee887 Make newly imported js files compatible with the karma build 2020-02-04 14:47:51 +00:00
luisramos0
408ada9097 Remove unnecessary build exception 2020-02-04 14:47:51 +00:00
luisramos0
e14ebd9503 Add required variables from spree_backend 2020-02-04 14:47:51 +00:00
luisramos0
289b99c30e Bring and require equalize from spree_backend 2020-02-04 14:47:26 +00:00
luisramos0
f85c36a17e Bring and require select2 from spree_backend 2020-02-04 14:47:26 +00:00
luisramos0
4715df3258 Bring and require jquery.horizontalNav.js from spree_backend 2020-02-04 14:47:26 +00:00
luisramos0
b20be101cc Bring and require spin from spree_backend and respective progress indicator js and css 2020-02-04 14:47:26 +00:00
luisramos0
d424987587 Bring and require jquery.vAlign.js from spree_backend 2020-02-04 14:47:26 +00:00
luisramos0
a0575430dd Require spree.js from spree_core for some basic functions like Spree.url 2020-02-04 14:46:57 +00:00
luisramos0
c1e6344b18 Merge spree js function and override 2020-02-04 14:46:57 +00:00
luisramos0
ed0198382f Bring spree_backend admin.js.erb, rename it to base.js.erb and remove requires that are not used in ofn 2020-02-04 14:46:57 +00:00
blainebillings
577fb88843 Move Create and Cancel References to Actions Namespace 2020-02-04 09:39:01 -05:00
Pau Pérez Fabregat
2b879221b2 Merge pull request #4756 from kristinalim/fix/4489-flaky_spec_for_editing_oc
4489 Flaky spec when editing outgoing exchanges in OC
2020-02-04 15:38:37 +01:00
blainebillings
22db2e99f0 Merge branch 'Issue4109' of https://github.com/CSCI-462-01-2020/openfoodnetwork into Issue4109 2020-02-04 09:38:19 -05:00
blainebillings
ef4aa488c3 Moves Create and Cancel References to Actions Namespace 2020-02-04 09:34:28 -05:00
Pau Pérez Fabregat
57775f49bc Merge pull request #4739 from openfoodfoundation/dependabot/bundler/rack-mini-profiler-1.1.6
Bump rack-mini-profiler from 1.1.4 to 1.1.6
2020-02-04 15:29:20 +01:00
Pau Pérez Fabregat
e8a12d9897 Merge pull request #4738 from openfoodfoundation/dependabot/bundler/oj-3.10.2
Bump oj from 3.10.1 to 3.10.2
2020-02-04 15:28:40 +01:00
Pau Pérez Fabregat
f3b8d5b868 Merge pull request #4741 from openfoodfoundation/dependabot/bundler/bugsnag-6.13.0
Bump bugsnag from 6.12.2 to 6.13.0
2020-02-04 15:28:00 +01:00
Pau Pérez Fabregat
246235b921 Merge pull request #4667 from Matt-Yorkley/rails-4-prep
Rails 4 prep
2020-02-04 15:20:28 +01:00
Luis Ramos
0ded41afee Merge pull request #4513 from luisramos0/backend_ctrl_orders
Bring spree_backend orders controller to OFN
2020-02-04 11:51:54 +00:00
luisramos0
d32e106bf0 Add partials needed in the orders edit page 2020-02-04 11:51:13 +00:00
luisramos0
bbb3748d3c Simplify orders events logic 2020-02-04 11:51:13 +00:00
luisramos0
08dd992344 Merge orders_helper with decorator 2020-02-04 11:51:13 +00:00
luisramos0
914244a1ee Bring orders helper from spree_backend 2020-02-04 11:51:13 +00:00
luisramos0
e5f089610c Bring admin orders routes from spree_backend 2020-02-04 11:51:13 +00:00
luisramos0
6e26841817 Fix load_order before filter and remove unused open and close adjustments actions 2020-02-04 11:50:44 +00:00
luisramos0
ab60c4a9dd Remove unnecessary index action override because this is no longer a decorator
Remove useless todo comment
2020-02-04 11:50:44 +00:00
luisramos0
7009cd89e0 Fix rubocop issues in customer details controller and update rubocop manual todo accordingly 2020-02-04 11:50:44 +00:00
luisramos0
b93af37ea9 Merge customer details decorator with controller 2020-02-04 11:50:03 +00:00
luisramos0
76b6a85509 Merge spree/admin/orders_controller and its decorator and fix a few rubocop issues 2020-02-04 11:50:03 +00:00
luisramos0
a1d4b4ee98 Add orders_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-02-04 11:50:03 +00:00
luisramos0
51bca7ce2f Add customer_details_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-02-04 11:50:03 +00:00
Luis Ramos
70147f908a Merge pull request #4611 from luisramos0/taxonomies_js
Bring js code to make taxonomies admin work without spree_backend
2020-02-04 11:47:27 +00:00
Luis Ramos
c4bf4f001f Merge pull request #4509 from luisramos0/backend_ctrl_payments
Bring spree_backend payments controller to OFN
2020-02-04 11:46:23 +00:00
Matt-Yorkley
7d71f21753 Add frozen_string_literal comment to new class 2020-02-04 11:11:24 +01:00
Matt-Yorkley
285c78a5e4 Remove use of #pluck and ensure subquery does not include all columns 2020-02-04 11:10:13 +01:00
luisramos0
9a2ad16926 Make explicit the dependency to font-awesome css rules 2020-02-04 09:34:59 +00:00
luisramos0
afec21eb3d Move imports to where they are needed 2020-02-04 09:34:59 +00:00
luisramos0
651ee720c6 Remove underscore from new jstree css file 2020-02-04 09:34:40 +00:00
luisramos0
2e6c5e1fad Add dependencies from spree_backend required by jstree 2020-02-04 09:34:40 +00:00
luisramos0
87366ae7fc Remove usage of jConfirm so that we dont have to bring dependency jquery.alerts from spree_backend 2020-02-04 09:33:58 +00:00
luisramos0
27aea0b277 Bring js code from spree_backend to make taxons admin work 2020-02-04 09:33:58 +00:00
luisramos0
d4edc9f20d Bring jstress css from spree_backend 2020-02-04 09:33:58 +00:00
luisramos0
6c7991be75 Bring jquery.jstree from spree_backend 2020-02-04 09:33:34 +00:00
luisramos0
3f81352df5 Bring jquery.cookie from spree_backend 2020-02-04 09:33:34 +00:00
Kristina Lim
9d0e26ae28 Wait for products panel to show in outgoing exchanges feature spec 2020-02-04 16:20:47 +08:00
Luis Ramos
d80554a14a Merge pull request #4744 from luisramos0/oc_pag_bug
Make pagination optional in the ExchangeProductsController
2020-02-03 17:19:06 +00:00
luisramos0
a5fe5fb448 Remove usage of deleted const DEFAULT_PAGE
If params[:page] is not in the request, the results will not be paginated now
2020-02-03 15:04:34 +00:00
luisramos0
4c51d60bfd Make pagination optional in the ExchangeProductsController 2020-02-03 13:11:26 +00:00
blainebillings
9218008530 Edit new_resource Partial 2020-02-02 11:22:54 -05:00
myersca1
f36c5b8938 Edit new_resource partial 2020-02-02 11:19:10 -05:00
dependabot-preview[bot]
67199fd2d6 Bump bugsnag from 6.12.2 to 6.13.0
Bumps [bugsnag](https://github.com/bugsnag/bugsnag-ruby) from 6.12.2 to 6.13.0.
- [Release notes](https://github.com/bugsnag/bugsnag-ruby/releases)
- [Changelog](https://github.com/bugsnag/bugsnag-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bugsnag/bugsnag-ruby/compare/v6.12.2...v6.13.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-31 19:15:09 +00:00
Transifex-Openfoodnetwork
18d17ec674 Updating translations for config/locales/fr_CA.yml 2020-02-01 04:02:53 +11:00
Transifex-Openfoodnetwork
3981ee7ec1 Updating translations for config/locales/en_CA.yml 2020-02-01 03:54:02 +11:00
Luis Ramos
a0475ee8a4 Merge pull request #4613 from luisramos0/autocomplete
Bring remaining variants autocomplete (and related) JS code from spree_backend
2020-01-31 16:08:50 +00:00
Luis Ramos
4cdc604f45 Merge pull request #4718 from luisramos0/variant_count
Make Order Cycle exchange tab summary not count inventory variants that are hidden
2020-01-31 16:03:21 +00:00
Luis Ramos
dbf44c41b2 Merge pull request #4657 from luisramos0/pi-xls
Remove Product import code for xls suppport
2020-01-31 12:12:25 +00:00
dependabot-preview[bot]
25c4aed368 Bump rack-mini-profiler from 1.1.4 to 1.1.6
Bumps [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) from 1.1.4 to 1.1.6.
- [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.1.4...v1.1.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-30 19:28:45 +00:00
dependabot-preview[bot]
c5a6ef673c Bump oj from 3.10.1 to 3.10.2
Bumps [oj](https://github.com/ohler55/oj) from 3.10.1 to 3.10.2.
- [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.1...v3.10.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-30 19:26:07 +00:00
Transifex-Openfoodnetwork
79ba15fe9a Updating translations for config/locales/en_GB.yml 2020-01-31 03:53:48 +11:00
luisramos0
e192207f4e Fix bug in before filters migration, needs to load payment for the show action 2020-01-30 10:27:26 +00:00
lin-d-hop
baf1ecb436 Fixing incorrect payment and balance calculation
Corrections and added specs
2020-01-29 16:06:06 +00:00
luisramos0
947914724a Add frozen string literal magic comment 2020-01-28 18:02:58 +00:00
luisramos0
b5004f1cbf Add specs for ExchangeProductsRenderer#exchange_variants 2020-01-28 18:02:58 +00:00
luisramos0
4596399bc2 Extract logic from controller to renderer service
Re-using the filter_visible method for both products and variants
2020-01-28 18:02:58 +00:00
luisramos0
685abccb61 Make variant count consider oc config and not count variants that are hidden in the inventory of the coordinator of the OC 2020-01-28 18:02:58 +00:00
luisramos0
9254928656 Remove underscore from product_autocomplete css file 2020-01-28 13:53:04 +00:00
luisramos0
228997c35b Add handlebars dependency from spree_backend 2020-01-28 13:53:04 +00:00
luisramos0
667f44336d Remove unused split logic from variant autocomplete, the split functionality is not used in ofn because we only have one stock location 2020-01-28 13:53:04 +00:00
luisramos0
0a136ff2fb Comment out click event registration as this will duplicate events until the spree_backend code is there
These can be activated when spree_backend is removed
2020-01-28 13:53:04 +00:00
luisramos0
3f3577e73c Remove encoding tag from admin/spree/orders js files 2020-01-28 13:53:04 +00:00
luisramos0
cf1664bed3 Bring some variant autocomplete styling from spree_backend 2020-01-28 13:53:04 +00:00
luisramos0
b8aee4e857 Bring address_states as is from spre_backend 2020-01-28 13:53:04 +00:00
luisramos0
cfe3435851 Bring shipments.js as is from spree_backend 2020-01-28 13:53:04 +00:00
luisramos0
53e342ba1a Bring variant_autocomplete from spree_backend as is 2020-01-28 13:53:04 +00:00
luisramos0
6bdb14248c Require lodash.underscore to support _. calls in js
spree_backend relies on Underscore.js 1.4.4, from Jan 2013, in some auto complete functions, here we will be depending on Lo-Dash 2.4.1, from Dec 2013
2020-01-28 13:53:03 +00:00
luisramos0
8e27291b15 require handlebars extensions from spree_core to allow translations inside handlebar templates 2020-01-28 13:52:19 +00:00
Maikel Linke
ec67736dff Bump Code Climate's Rubocop version to their latest
Our used version is still higher than that.
2020-01-28 17:12:28 +11:00
luisramos0
766303b332 Add required payment_helper from spree_backend 2020-01-22 20:22:28 +00:00
luisramos0
74226fbdf8 Fix one rubocop issue and update rubocop todo manual for the new controller 2020-01-22 20:22:28 +00:00
luisramos0
dc5374e284 Merge filter_payment_methods and load_data 2020-01-22 20:21:21 +00:00
luisramos0
f6ecf57737 Make before filters easier to understand using the fact that there are only index, create, new and fire actions in this controller 2020-01-22 20:21:21 +00:00
luisramos0
940953b043 Add missing translations for payments 2020-01-22 20:21:21 +00:00
luisramos0
fbc5887fa6 Bring payments routes from spree_backend 2020-01-22 20:21:21 +00:00
luisramos0
61ce849546 Merge spree/admin/payments_controller with its decorator and fix a few rubocop issues 2020-01-22 20:21:21 +00:00
luisramos0
afddaed9fc Add empty but needed payments templates for check payment method 2020-01-22 20:21:21 +00:00
luisramos0
12158d73fa Add payments_controller from spree_backend so that we can now merge it with the OFN's decorator 2020-01-22 20:21:21 +00:00
Matt-Yorkley
5f3abbf00e Refactor BaseController 2020-01-22 12:17:23 +01:00
Matt-Yorkley
a02c58e231 Add join_table to enterprise groups relation
has_and_belongs_to_many relationships now require a join_table
2020-01-22 12:17:23 +01:00
Matt-Yorkley
59ebfb9bd4 Fix subquery errors triggered by #warn_invalid_order_cycles 2020-01-22 12:17:23 +01:00
Matt-Yorkley
57ca1d54bb Fix issue with each_serializer not being called in some cases in Rails 4. 2020-01-22 12:17:23 +01:00
luisramos0
138248e1c9 Product import only works with csv right now. Here we remove dead code
This can be reverted if we start suppporting xls and ods in the future
2020-01-16 09:59:27 +00:00
luisramos0
5fccd5fe58 Add rubocop exception for application controller class length and fix one line length issue 2020-01-12 19:42:07 +00:00
luisramos0
4658b7a533 Fix rubocop issues in app/controllers/application_controller 2019-12-28 19:10:02 +00:00
luisramos0
d0f33e7c8a Avoid code in devise that is not needed and simply return the root path
This fixes a broken spec with error "No route matches {:controller=>"home"}"
2019-12-28 18:47:14 +00:00
luisramos0
55bb328d48 Prefix all calls to root_url with main_app 2019-12-28 18:46:50 +00:00
luisramos0
a8a6fce385 Prefix root_path with main_app so that ofn's route is used. The spree root path will no longer be defined when spree_backend is dropped 2019-12-28 18:45:57 +00:00
luisramos0
60677a2414 Make login nav use main_app router 2019-12-28 18:41:16 +00:00
109 changed files with 10673 additions and 457 deletions

View File

@@ -2,7 +2,7 @@ version: "2"
plugins:
rubocop:
enabled: true
channel: "rubocop-0-57"
channel: "rubocop-0-76"
config:
file: ".rubocop_styleguide.yml"
scss-lint:

View File

@@ -1,7 +1,7 @@
---
name: Release task
about: Track the process of a new release
title: ''
title: 'Release v'
labels: ''
assignees: ''
@@ -10,10 +10,13 @@ assignees: ''
Steps:
- [ ] Include translations
- [ ] Draft: https://github.com/openfoodfoundation/openfoodnetwork/releases/new <!-- replace the URL -->
- [ ] [Draft new release]
- [ ] Notify #instance-managers of user-facing changes.
- [ ] Test: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master <!-- replace the URL -->
- [ ] Publish and notify #global-community
- [ ] Deploy and notify #instance-managers
- [ ] Nudge next release manager
The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing.
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A

View File

@@ -39,11 +39,9 @@ Layout/LineLength:
- app/controllers/admin/variant_overrides_controller.rb
- app/controllers/api/enterprise_attachment_controller.rb
- app/controllers/api/product_images_controller.rb
- app/controllers/application_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/adjustments_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
- app/controllers/spree/paypal_controller_decorator.rb
- app/controllers/stripe/callbacks_controller.rb
@@ -361,15 +359,15 @@ Metrics/AbcSize:
- app/controllers/enterprises_controller.rb
- app/controllers/spree/admin/adjustments_controller_decorator.rb
- app/controllers/spree/admin/image_settings_controller.rb
- app/controllers/spree/admin/orders/customer_details_controller_decorator.rb
- app/controllers/spree/admin/orders_controller_decorator.rb
- app/controllers/spree/admin/orders/customer_details_controller.rb
- app/controllers/spree/admin/orders_controller.rb
- 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/payments_controller.rb
- app/controllers/spree/admin/products_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/search_controller_decorator.rb
- app/controllers/spree/admin/search_controller.rb
- app/controllers/spree/admin/taxons_controller.rb
- app/controllers/spree/admin/users_controller.rb
- app/controllers/spree/admin/variants_controller.rb
@@ -496,7 +494,6 @@ Metrics/CyclomaticComplexity:
- app/controllers/admin/enterprise_fees_controller.rb
- app/controllers/admin/enterprises_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/taxons_controller.rb
- app/controllers/spree/orders_controller.rb
- app/helpers/checkout_helper.rb
@@ -527,7 +524,6 @@ Metrics/PerceivedComplexity:
- app/controllers/admin/enterprises_controller.rb
- app/controllers/api/variants_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/taxons_controller.rb
- app/controllers/spree/orders_controller.rb
- app/helpers/checkout_helper.rb
@@ -567,13 +563,13 @@ Metrics/MethodLength:
- app/controllers/checkout_controller.rb
- app/controllers/shop_controller.rb
- app/controllers/spree/admin/image_settings_controller.rb
- app/controllers/spree/admin/orders/customer_details_controller_decorator.rb
- app/controllers/spree/admin/orders/customer_details_controller.rb
- app/controllers/spree/admin/orders_controller.rb
- app/controllers/spree/admin/payment_methods_controller.rb
- app/controllers/spree/admin/payments_controller_decorator.rb
- app/controllers/spree/admin/payments_controller.rb
- app/controllers/spree/admin/products_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/search_controller_decorator.rb
- app/controllers/spree/admin/tax_categories_controller.rb
- app/controllers/spree/admin/taxons_controller.rb
- app/controllers/spree/admin/users_controller.rb
@@ -652,8 +648,10 @@ Metrics/ClassLength:
- app/controllers/admin/order_cycles_controller.rb
- app/controllers/admin/subscriptions_controller.rb
- app/controllers/api/products_controller.rb
- app/controllers/application_controller.rb
- app/controllers/checkout_controller.rb
- app/controllers/spree/admin/base_controller.rb
- app/controllers/spree/admin/orders_controller.rb
- app/controllers/spree/admin/payment_methods_controller.rb
- app/controllers/spree/admin/reports_controller.rb
- app/controllers/spree/admin/resource_controller.rb

View File

@@ -335,6 +335,7 @@ Rails/OutputSafety:
- 'app/helpers/spree/admin/zones_helper.rb'
- 'app/helpers/spree/reports_helper.rb'
- 'app/helpers/spree/admin/navigation_helper.rb'
- 'app/helpers/spree/admin/orders_helper.rb'
- 'app/serializers/api/product_serializer.rb'
- 'lib/spree/money_decorator.rb'
- 'spec/features/admin/orders_spec.rb'

View File

@@ -94,7 +94,6 @@ gem 'wkhtmltopdf-binary'
gem 'foreigner'
gem 'immigrant'
gem 'roo', '~> 2.8.2'
gem 'roo-xls', '~> 1.1.0'
gem 'whenever', require: false

View File

@@ -166,7 +166,7 @@ GEM
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
blockenspiel (0.5.0)
bugsnag (6.12.2)
bugsnag (6.13.0)
concurrent-ruby (~> 1.0)
builder (3.0.4)
byebug (9.0.6)
@@ -492,7 +492,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.10.1)
oj (3.10.2)
orm_adapter (0.5.0)
paper_trail (5.2.3)
activerecord (>= 3.0, < 6.0)
@@ -530,7 +530,7 @@ GEM
rack (1.4.7)
rack-cache (1.9.0)
rack (>= 0.4)
rack-mini-profiler (1.1.4)
rack-mini-profiler (1.1.6)
rack (>= 1.2.0)
rack-protection (1.5.5)
rack
@@ -587,10 +587,6 @@ GEM
roo (2.8.2)
nokogiri (~> 1)
rubyzip (>= 1.2.1, < 2.0.0)
roo-xls (1.1.0)
nokogiri
roo (>= 2.0.0beta1, < 3)
spreadsheet (> 0.9.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
@@ -624,7 +620,6 @@ GEM
rubocop-rails (2.4.2)
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-ole (1.2.12.1)
ruby-progressbar (1.10.1)
ruby-rc4 (0.1.5)
rubyzip (1.3.0)
@@ -653,8 +648,6 @@ GEM
tilt (>= 1.3, < 3)
spinjs-rails (1.4)
rails (>= 3.1)
spreadsheet (1.1.7)
ruby-ole (>= 1.0)
spring (1.7.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
@@ -791,7 +784,6 @@ DEPENDENCIES
redcarpet
roadie-rails (~> 1.3.0)
roo (~> 2.8.2)
roo-xls (~> 1.1.0)
rspec-rails (>= 3.5.2)
rspec-retry
rubocop

View File

@@ -12,6 +12,10 @@
//= require jquery.ui.all
//= require jquery-ui-timepicker-addon
//= require jquery.powertip
//= require jquery.cookie
//= require jquery.jstree/jquery.jstree
//= require jquery.vAlign
//= require jquery.horizontalNav
//= require angular
//= require angular-resource
//= require angular-animate
@@ -20,13 +24,20 @@
//= require ../shared/ng-infinite-scroll.min.js
//= require ../shared/ng-tags-input.min.js
//= require angular-rails-templates
//= require lodash.underscore.js
// spree
//= require spree
//= require admin/spree-select2
//= require admin/spree_backend
//= require modernizr
//= require spin
//= require jquery.adaptivemenu
//= require equalize
//= require css_browser_selector_dev
//= require responsive-tables
//= require admin/spree_paypal_express
//= require admin/handlebar_extensions
// OFN specific
//= require_tree ../templates/admin

View File

@@ -8,7 +8,7 @@ angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
index: (params={}, callback=null) ->
ExchangeProductResource.index params, (data) =>
(callback || angular.noop)(data.products, data.pagination.pages, data.pagination.results)
(callback || angular.noop)(data.products, data.pagination?.pages, data.pagination?.results)
countVariants: (params={}, callback=null) ->
ExchangeProductResource.variant_count params, (data) =>

View File

@@ -0,0 +1,228 @@
//= require_self
//= require admin/handlebar_extensions
//= require admin/variant_autocomplete
/**
This is a collection of javascript functions and whatnot
under the spree namespace that do stuff we find helpful.
Hopefully, this will evolve into a propper class.
**/
jQuery(function($) {
// Make main menu use full width
mainMenu = $('.fullwidth-menu')
if (typeof mainMenu.horizontalNav === 'function' )
mainMenu.horizontalNav({
tableDisplay: false,
responsiveDelay: 0
});
// Vertical align of checkbox fields
if (typeof $('.field.checkbox label').vAlign === 'function' )
$('.field.checkbox label').vAlign()
// if (typeof Spree !== 'undefined') {
// $('.main-menu-wrapper ul').AdaptiveMenu({
// text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
// klass: "dropdown"
// });
// }
// Add some tips
if (typeof $('.with-tip').powerTip === 'function' ) {
$('.with-tip').powerTip({
smartPlacement: true,
fadeInTime: 50,
fadeOutTime: 50,
intentPollInterval: 300
});
$('.with-tip').on({
powerTipPreRender: function(){
$('#powerTip').addClass($(this).attr("data-action"));
$('#powerTip').addClass($(this).attr("data-tip-color"));
},
powerTipClose: function(){
$('#powerTip').removeClass($(this).attr("data-action"))
}
});
}
// Make flash messages dissapear
setTimeout('$(".flash").fadeOut()', 5000);
// Highlight hovered table column
$('table tbody tr td.actions a').hover(function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).attr('data-action')
tr.addClass(klass)
tr.prev().addClass('before-' + klass);
}, function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).attr('data-action')
tr.removeClass(klass)
tr.prev().removeClass('before-' + klass);
});
// Trunkate text in page_title that didn't fit
var truncate_elements = $('.truncate');
truncate_elements.each(function(){
$(this).trunk8();
});
$(window).resize(function (event) {
truncate_elements.each(function(){
$(this).trunk8();
})
});
// Make height of dt/dd elements the same
if (typeof $("dl").equalize === 'function' )
$("dl").equalize('outerHeight');
});
$.fn.visible = function(cond) { this[cond ? 'show' : 'hide' ]() };
// Overriding a broken function in Spree. Bug report at
// https://github.com/spree/spree/issues/4032
show_flash_error = function(message) {
error_div = $('.flash.error');
if (error_div.length > 0) {
error_div.html(message);
error_div.show();
} else {
if ($("#content .toolbar").length > 0) {
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
} else {
$("#progress").before('<div class="flash error">' + message + '</div>');
}
}
}
// Apply to individual radio button that makes another element visible when checked
$.fn.radioControlsVisibilityOfElement = function(dependentElementSelector){
if(!this.get(0)){ return }
showValue = this.get(0).value;
radioGroup = $("input[name='" + this.get(0).name + "']");
radioGroup.each(function(){
$(this).click(function(){
$(dependentElementSelector).visible(this.checked && this.value == showValue)
});
if(this.checked){ this.click() }
});
}
$(document).ready(function() {
if (typeof Spree !== 'undefined') {
handle_date_picker_fields = function(){
$('.datepicker').datepicker({
dateFormat: Spree.translations.date_picker,
dayNames: Spree.translations.abbr_day_names,
dayNamesMin: Spree.translations.abbr_day_names,
monthNames: Spree.translations.month_names,
prevText: Spree.translations.previous,
nextText: Spree.translations.next,
showOn: "focus"
});
// Correctly display range dates
$('.date-range-filter .datepicker-from').datepicker('option', 'onSelect', function(selectedDate) {
$(".date-range-filter .datepicker-to" ).datepicker( "option", "minDate", selectedDate );
});
$('.date-range-filter .datepicker-to').datepicker('option', 'onSelect', function(selectedDate) {
$(".date-range-filter .datepicker-from" ).datepicker( "option", "maxDate", selectedDate );
});
}
handle_date_picker_fields();
}
$(".observe_field").on('change', function() {
target = $(this).attr("data-update");
ajax_indicator = $(this).attr("data-ajax-indicator") || '#busy_indicator';
$(target).hide();
$(ajax_indicator).show();
$.ajax({ dataType: 'html',
url: $(this).attr("data-base-url")+encodeURIComponent($(this).val()),
type: 'get',
success: function(data){
$(target).html(data);
$(ajax_indicator).hide();
$(target).show();
}
});
});
$('.spree_add_fields').click(function() {
var target = $(this).data("target");
var new_table_row = $(target + ' tr:visible:last').clone();
var new_id = new Date().getTime();
new_table_row.find("input, select").each(function () {
var el = $(this);
el.val("");
if (typeof el.attr("id") !== 'undefined') el.attr("id", el.attr("id").replace(/\d+/, new_id))
if (typeof el.attr("name") !== 'undefined') el.attr("name", el.attr("name").replace(/\d+/, new_id))
})
// When cloning a new row, set the href of all icons to be an empty "#"
// This is so that clicking on them does not perform the actions for the
// duplicated row
new_table_row.find("a").each(function () {
var el = $(this);
el.attr('href', '#');
})
$(target).prepend(new_table_row);
})
// Fix sortable helper
var fixHelper = function(e, ui) {
ui.children().each(function() {
$(this).width($(this).width());
});
return ui;
};
$('table.sortable').ready(function(){
var td_count = $(this).find('tbody tr:first-child td').length
if (typeof $('table.sortable tbody').sortable !== 'function' )
return
$('table.sortable tbody').sortable(
{
handle: '.handle',
helper: fixHelper,
placeholder: 'ui-sortable-placeholder',
update: function(event, ui) {
$("#progress").show();
positions = {};
$.each($('table.sortable tbody tr'), function(position, obj){
reg = /spree_(\w+_?)+_(\d+)/;
parts = reg.exec($(obj).attr('id'));
if (parts) {
positions['positions['+parts[2]+']'] = position;
}
});
$.ajax({
type: 'POST',
dataType: 'script',
url: $(ui.item).closest("table.sortable").data("sortable-link"),
data: positions,
success: function(data){ $("#progress").hide(); }
});
},
start: function (event, ui) {
// Set correct height for placehoder (from dragged tr)
ui.placeholder.height(ui.item.height())
// Fix placeholder content to make it correct width
ui.placeholder.html("<td colspan='"+(td_count-1)+"'></td><td class='actions'></td>")
},
stop: function (event, ui) {
// Fix odd/even classes after reorder
$("table.sortable tr:even").removeClass("odd even").addClass("even");
$("table.sortable tr:odd").removeClass("odd even").addClass("odd");
}
});
});
});

View File

@@ -0,0 +1,27 @@
var update_state = function(region) {
var country = $('span#' + region + 'country .select2').select2('val');
var state_select = $('span#' + region + 'state select.select2');
var state_input = $('span#' + region + 'state input.state_name');
$.get(Spree.routes.states_search + "?country_id=" + country, function(data) {
var states = data["states"]
if (states.length > 0) {
state_select.html('');
var states_with_blank = [{name: '', id: ''}].concat(states);
$.each(states_with_blank, function(pos,state) {
var opt = $(document.createElement('option'))
.attr('value', state.id)
.html(state.name);
state_select.append(opt);
});
state_select.prop("disabled", false).show();
state_select.select2();
state_input.hide().prop("disabled", true);
} else {
state_input.prop("disabled", false).show();
state_select.select2('destroy').hide();
}
})
};

View File

@@ -0,0 +1,64 @@
// Shipments AJAX API
$(document).ready(function() {
handle_ship_click = function(){
var link = $(this);
var shipment_number = link.data('shipment-number');
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + "/ship.json");
$.ajax({
type: "PUT",
url: url
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}
// $('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
//handle shipping method edit click
// $('a.edit-method').click(toggleMethodEdit);
// $('a.cancel-method').click(toggleMethodEdit);
handle_shipping_method_save = function(){
var link = $(this);
var shipment_number = link.data('shipment-number');
var selected_shipping_rate_id = link.parents('tbody').find("select#selected_shipping_rate_id[data-shipment-number='" + shipment_number + "']").val();
var unlock = link.parents('tbody').find("input[name='open_adjustment'][data-shipment-number='" + shipment_number + "']:checked").val();
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
$.ajax({
type: "PUT",
url: url,
data: { shipment: { selected_shipping_rate_id: selected_shipping_rate_id, unlock: unlock } }
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}
// $('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
//handle tracking edit click
// $('a.edit-tracking').click(toggleTrackingEdit);
// $('a.cancel-tracking').click(toggleTrackingEdit);
handle_tracking_save = function(){
var link = $(this);
var shipment_number = link.data('shipment-number');
var tracking = link.parents('tbody').find('input#tracking').val();
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
$.ajax({
type: "PUT",
url: url,
data: { shipment: { tracking: tracking } }
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}
$('[data-hook=admin_order_edit_form] a.save-tracking').click(handle_tracking_save);
});

View File

@@ -0,0 +1,177 @@
// variant autocompletion
$(document).ready(function() {
if ($('#variant_autocomplete_template').length > 0) {
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
window.variantStockTemplate = Handlebars.compile($('#variant_autocomplete_stock_template').text());
// handle variant selection, show stock level.
$('#add_variant_id').change(function(){
var variant_id = $(this).val();
var variant = _.find(window.variants, function(variant){
return variant.id == variant_id
})
$('#stock_details').html(variantStockTemplate({variant: variant}));
$('#stock_details').show();
$('button.add_variant').click(addVariantFromStockLocation);
// Add some tips
$('.with-tip').powerTip({
smartPlacement: true,
fadeInTime: 50,
fadeOutTime: 50,
intentPollInterval: 300
});
});
//handle edit click
// $('a.edit-item').click(toggleItemEdit);
//handle cancel click
// $('a.cancel-item').click(toggleItemEdit);
handle_save_click = function(){
var save = $(this);
var shipment_number = save.data('shipment-number');
var variant_id = save.data('variant-id');
var quantity = parseInt(save.parents('tr').find('input.line_item_quantity').val());
toggleItemEdit();
adjustItems(shipment_number, variant_id, quantity);
return false;
}
// $('a.save-item').click(handle_save_click);
handle_delete_click = function(){
var del = $(this);
var shipment_number = del.data('shipment-number');
var variant_id = del.data('variant-id');
toggleItemEdit();
adjustItems(shipment_number, variant_id, 0);
}
// $('a.delete-item').click(handle_delete_click);
}
});
adjustItems = function(shipment_number, variant_id, quantity){
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
var url = Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number;
var new_quantity = 0;
if(inventory_units.length<quantity){
url += "/add"
new_quantity = (quantity - inventory_units.length);
}else if(inventory_units.length>quantity){
url += "/remove"
new_quantity = (inventory_units.length - quantity);
}
url += '.json';
if(new_quantity!=0){
$.ajax({
type: "PUT",
url: Spree.url(url),
data: { variant_id: variant_id, quantity: new_quantity }
}).done(function( msg ) {
window.location.reload();
});
}
}
toggleTrackingEdit = function(){
var link = $(this);
link.parents('tbody').find('tr.edit-tracking').toggle();
link.parents('tbody').find('tr.show-tracking').toggle();
}
toggleMethodEdit = function(){
var link = $(this);
link.parents('tbody').find('tr.edit-method').toggle();
link.parents('tbody').find('tr.show-method').toggle();
}
toggleItemEdit = function(){
var link = $(this);
link.parent().find('a.edit-item').toggle();
link.parent().find('a.cancel-item').toggle();
link.parent().find('a.save-item').toggle();
link.parent().find('a.delete-item').toggle();
link.parents('tr').find('td.item-qty-show').toggle();
link.parents('tr').find('td.item-qty-edit').toggle();
return false;
}
addVariantFromStockLocation = function() {
$('#stock_details').hide();
var variant_id = $('input.variant_autocomplete').val();
var stock_location_id = $(this).data('stock-location-id');
var quantity = $("input.quantity[data-stock-location-id='" + stock_location_id + "']").val();
var shipment = _.find(shipments, function(shipment){
return shipment.stock_location_id == stock_location_id && (shipment.state == 'ready' || shipment.state == 'pending');
});
if(shipment==undefined){
$.ajax({
type: "POST",
url: Spree.url(Spree.routes.orders_api + "/" + order_number + "/shipments.json"),
data: { variant_id: variant_id, quantity: quantity, stock_location_id: stock_location_id }
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}else{
//add to existing shipment
adjustItems(shipment.number, variant_id, quantity);
}
return 1
}
formatVariantResult = function(variant) {
if (variant["images"][0] != undefined && variant["images"][0].urls != undefined) {
variant.image = variant.images[0].urls.mini
}
return variantTemplate({ variant: variant })
}
$.fn.variantAutocomplete = function() {
this.parent().children(".options_placeholder").attr('id', this.parent().data('index'))
this.select2({
placeholder: Spree.translations.variant_placeholder,
minimumInputLength: 3,
ajax: {
url: Spree.url(Spree.routes.variants_search),
datatype: 'json',
data: function(term, page) {
return {
q: {
"product_name_or_sku_cont": term
}
}
},
results: function (data, page) {
window.variants = data['variants'];
return { results: data['variants'] }
}
},
formatResult: formatVariantResult,
formatSelection: function (variant) {
$(this.element).parent().children('.options_placeholder').html(variant.options_text)
return variant.name;
}
})
}

View File

@@ -0,0 +1,27 @@
$(document).ready ->
opts =
lines: 11
length: 2
width: 3
radius: 9
corners: 1
rotate: 0
color: '#fff'
speed: 0.8
trail: 48
shadow: false
hwaccel: true
className: 'spinner'
zIndex: 2e9
top: 'auto'
left: 'auto'
target = document.getElementById("spinner")
$(document).ajaxStart ->
$("#progress").fadeIn()
spinner = new Spinner(opts).spin(target)
$(document).ajaxStop ->
$("#progress").fadeOut()

View File

@@ -0,0 +1,8 @@
//= require select2
jQuery(function($) {
// Make select beautiful
if (typeof $('select.select2').select2 === 'function' )
$('select.select2').select2({
allowClear: true
});
})

View File

@@ -0,0 +1,22 @@
root = exports ? this
root.taxon_tree_menu = (obj, context) ->
base_url = Spree.url(Spree.routes.taxonomy_taxons_path)
admin_base_url = Spree.url(Spree.routes.admin_taxonomy_taxons_path)
edit_url = admin_base_url.clone()
edit_url.setPath(edit_url.path() + '/' + obj.attr("id") + "/edit");
create:
label: "<i class='icon-plus'></i> " + Spree.translations.add,
action: (obj) -> context.create(obj)
rename:
label: "<i class='icon-pencil'></i> " + Spree.translations.rename,
action: (obj) -> context.rename(obj)
remove:
label: "<i class='icon-trash'></i> " + Spree.translations.remove,
action: (obj) -> context.remove(obj)
edit:
separator_before: true,
label: "<i class='icon-edit'></i> " + Spree.translations.edit,
action: (obj) -> window.location = edit_url.toString()

View File

@@ -0,0 +1,127 @@
handle_ajax_error = (XMLHttpRequest, textStatus, errorThrown) ->
$.jstree.rollback(last_rollback)
$("#ajax_error").show().html("<strong>" + server_error + "</strong><br />" + taxonomy_tree_error)
handle_move = (e, data) ->
last_rollback = data.rlbk
position = data.rslt.cp
node = data.rslt.o
new_parent = data.rslt.np
url = Spree.url(base_url).clone()
url.setPath url.path() + '/' + node.attr("id")
$.ajax
type: "POST",
dataType: "json",
url: url.toString(),
data: ({_method: "put", "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
error: handle_ajax_error
true
handle_create = (e, data) ->
last_rollback = data.rlbk
node = data.rslt.obj
name = data.rslt.name
position = data.rslt.position
new_parent = data.rslt.parent
$.ajax
type: "POST",
dataType: "json",
url: base_url.toString(),
data: ({"taxon[name]": name, "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
error: handle_ajax_error,
success: (data,result) ->
node.attr('id', data.id)
handle_rename = (e, data) ->
last_rollback = data.rlbk
node = data.rslt.obj
name = data.rslt.new_name
url = Spree.url(base_url).clone()
url.setPath(url.path() + '/' + node.attr("id"))
$.ajax
type: "POST",
dataType: "json",
url: url.toString(),
data: {_method: "put", "taxon[name]": name },
error: handle_ajax_error
handle_delete = (e, data) ->
last_rollback = data.rlbk
node = data.rslt.obj
delete_url = base_url.clone()
delete_url.setPath delete_url.path() + '/' + node.attr("id")
if confirm(Spree.translations.are_you_sure_delete)
$.ajax
type: "POST",
dataType: "json",
url: delete_url.toString(),
data: {_method: "delete"},
error: handle_ajax_error
else
$.jstree.rollback(last_rollback)
last_rollback = null
root = exports ? this
root.setup_taxonomy_tree = (taxonomy_id) ->
if taxonomy_id != undefined
# this is defined within admin/taxonomies/edit
root.base_url = Spree.url(Spree.routes.taxonomy_taxons_path)
$.ajax
url: base_url.path().replace("/taxons", "/jstree"),
success: (taxonomy) ->
last_rollback = null
conf =
json_data:
data: taxonomy,
ajax:
url: (e) ->
base_url.path() + '/' + e.attr('id') + '/jstree'
themes:
theme: "apple",
url: "/assets/jquery.jstree/themes/apple/style.css"
strings:
new_node: new_taxon,
loading: Spree.translations.loading + "..."
crrm:
move:
check_move: (m) ->
position = m.cp
node = m.o
new_parent = m.np
# no parent or cant drag and drop
if !new_parent || node.attr("rel") == "root"
return false
# can't drop before root
if new_parent.attr("id") == "taxonomy_tree" && position == 0
return false
true
contextmenu:
items: (obj) ->
taxon_tree_menu(obj, this)
plugins: ["themes", "json_data", "dnd", "crrm", "contextmenu"]
$("#taxonomy_tree").jstree(conf)
.bind("move_node.jstree", handle_move)
.bind("remove.jstree", handle_delete)
.bind("create.jstree", handle_create)
.bind("rename.jstree", handle_rename)
.bind "loaded.jstree", ->
$(this).jstree("core").toggle_node($('.jstree-icon').first())
$("#taxonomy_tree a").on "dblclick", (e) ->
$("#taxonomy_tree").jstree("rename", this)
# surpress form submit on enter/return
$(document).keypress (e) ->
if e.keyCode == 13
e.preventDefault()

View File

@@ -16,22 +16,6 @@ $(document).ready(function() {
});
});
// Overriding a broken function in Spree. Bug report at
// https://github.com/spree/spree/issues/4032
show_flash_error = function(message) {
error_div = $('.flash.error');
if (error_div.length > 0) {
error_div.html(message);
error_div.show();
} else {
if ($("#content .toolbar").length > 0) {
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
} else {
$("#progress").before('<div class="flash error">' + message + '</div>');
}
}
}
$(document).ready(function(){
$('a.close').click(function(event){
event.preventDefault();

View File

@@ -7,6 +7,9 @@
*= require admin/spree_backend
*= require jquery.powertip
*= require responsive-tables
*= require normalize
*= require skeleton
*= require jquery.ui.datepicker
*= require jquery-ui-timepicker-addon
*= require shared/textAngular
*= require shared/ng-tags-input.min
@@ -14,15 +17,27 @@
*= require_self
*/
//************************************************************************//
//************************************************************************//
@import 'globals/functions';
@import 'globals/variables';
@import 'variables';
@import 'globals/mixins';
@import 'shared/typography';
@import 'shared/tables';
@import 'shared/icons';
@import 'shared/forms';
@import 'shared/layout';
@import 'plugins/powertip';
@import 'plugins/jstree';
@import 'plugins/font-awesome';
@import 'hacks/mozilla';
@import 'hacks/opera';
@import 'hacks/ie';
@import 'variables';
@import 'components/*';
@import 'plugins/font-awesome';
@import 'pages/*';
@import '*';

View File

@@ -0,0 +1,33 @@
@import 'admin/globals/variables';
table tbody tr {
&.highlight {
@each $action in $actions {
&.action-#{$action} td {
background-color: get-value($actions, $actions-bg-colors, $action);
border-color: get-value($actions, $actions-brd-colors, $action);
}
}
&.action-remove td, &.action-void td {
text-decoration: line-through;
&.actions {
text-decoration: none;
}
}
}
&.before-highlight {
@each $action in $actions {
&.action-#{$action} td {
border-bottom-color: get-value($actions, $actions-brd-colors, $action);
}
}
}
td.actions {
background-color: transparent !important;
}
}

View File

@@ -1,12 +0,0 @@
#admin-menu {
li {
a {
font-weight: 600;
}
a::before {
font-weight: normal;
padding-top: 0;
}
}
}

View File

@@ -0,0 +1,158 @@
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
@import 'admin/plugins/font-awesome';
.date-range-filter {
.range-divider {
padding: 0;
}
input.datepicker {
width: 96px !important;
}
}
#ui-datepicker-div {
@include border-radius($border-radius);
border-color: $color-3;
padding: 0;
margin-top: 10px;
&:before {
content: '';
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid $color-3;
top: 0px;
margin-top: -10px;
left: 25px;
z-index: 1;
}
.ui-datepicker-header {
padding: 0;
background-image: none;
background-color: $color-3;
border: none;
border-bottom: none;
border-radius: 0;
height: 32px;
.ui-datepicker-prev, .ui-datepicker-next {
border-radius: 0;
top: 0;
height: 32px;
&:hover {
border: none;
background-image: none;
background-color: $color-3;
cursor: pointer;
}
.ui-icon {
background-image: none;
text-indent: 0;
color: $color-1;
width: 10px;
margin-left: -5px;
@extend [class^="icon-"]:before;
&:hover {
color: very-light($color-2, 25);
}
}
}
.ui-datepicker-prev {
left: 0;
.ui-icon {
@extend .icon-arrow-left;
}
}
.ui-datepicker-next {
right: 0;
.ui-icon {
@extend .icon-arrow-right;
}
&:hover {
.ui-icon {
margin-left: -5px;
}
}
}
.ui-datepicker-title {
color: $color-1;
text-transform: uppercase;
font-size: 85% !important;
padding: 6px 10px;
}
}
table.ui-datepicker-calendar {
border: none;
thead {
th {
border-bottom: 1px solid $color-border;
border-right: 1px solid $color-border;
color: $color-body-text;
width: 33px;
&:last-child {
border-right: none;
}
}
}
tbody {
tr:hover {
td {
background-color: transparent !important;
}
}
&:last-child tr:last-child td {
border-bottom: none;
}
td {
a {
border: 1px solid transparent;
background-color: $color-1;
background-image: none;
font-size: 85%;
color: $color-body-text;
&.ui-state-active {
background-color: $color-2;
color: $color-1;
}
&:hover {
background-color: $color-2;
color: $color-1;
border-color: darken($color-2, 5);
}
}
&.ui-state-disabled {
.ui-state-default {
border: none;
background-image: none;
background-color: transparent;
}
}
&.ui-datepicker-today {
a {
background-color: $color-6;
color: $color-1;
border: 1px solid darken($color-6, 5);
}
}
}
}
}
}

View File

@@ -0,0 +1,45 @@
@import 'admin/globals/variables';
.errorExplanation {
padding: 5px;
border: 1px solid very-light($color-error, 12);
background-color: very-light($color-error, 6);
border-radius: 3px;
color: very-light($color-error, 30);
margin-bottom: 15px;
h2 {
font-size: 140%;
color: very-light($color-error, 30);
margin-bottom: 5px;
}
p {
padding: 10px 0;
}
ul {
list-style-position: inside;
li {
font-weight: $font-weight-bold;
}
}
}
.flash {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 16px;
text-align: center;
z-index: 1000;
font-size: 120%;
color: $color-1;
font-weight: 600;
&.notice { background-color: rgba($color-notice, 0.8) }
&.success { background-color: rgba($color-success, 0.8) }
&.error { background-color: rgba($color-error, 0.8) }
}

View File

@@ -0,0 +1,162 @@
@import 'admin/globals/variables';
// Navigation
//---------------------------------------------------
.inline-menu {
margin: 0;
-webkit-margin-before: 0;
-webkit-padding-start: 0;
}
nav.menu {
ul {
list-style: none;
li {
a {
padding: 10px 0;
display: block;
position: relative;
text-align: left;
border: 1px solid transparent;
text-transform: uppercase;
font-weight: 600;
font-size: 90%;
}
&.active a {
color: $color-2;
border-left-width: 0;
border-bottom-color: $color-2;
}
&:hover a {
color: $color-2;
}
}
}
}
[data-hook="admin_login_navigation_bar"] {
ul {
text-align: right;
li {
padding: 5px 0 5px 10px;
text-align: right;
font-size: 90%;
color: $color-link;
margin-top: 8px;
&[data-hook="user-logged-in-as"] {
width: 50%;
color: $color-body-text;
}
&:hover {
i {
color: $color-2;
}
}
}
}
}
#admin-menu {
background-color: $color-3;
li {
min-width: 90px;
a {
display: block;
padding: 25px 20px;
color: $color-1 !important;
text-transform: uppercase;
position: relative;
text-align: center;
font-weight: 600;
i {
display: inline;
}
&:hover {
background-color: $color-2;
&:after {
content: '';
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 5px solid $color-2;
bottom: 0px;
margin-bottom: -5px;
left: 50%;
margin-left: -10px;
z-index: 1;
}
}
span.text {
font-weight: 600;
}
}
a::before {
font-weight: normal;
padding-top: 0;
}
.dropdown {
width: 300px;
background-color: $color-3;
width: 200px;
z-index: 100000;
> li {
width: 200px !important;
a:after {
display: none;
}
}
}
&.selected a {
@extend a:hover;
}
}
}
#sub-menu {
background-color: $color-2;
padding-bottom: 0;
li {
a {
display: block;
padding: 12px 20px;
color: $color-1;
text-align: center;
text-transform: uppercase;
position: relative;
font-size: 85%;
}
&.selected a, a:hover {
&:after {
content: '';
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 5px solid $color-2;
bottom: 0px;
margin-bottom: -5px;
left: 50%;
margin-left: -10px;
z-index: 1;
}
}
}
}

View File

@@ -1,8 +1,23 @@
@import 'admin/globals/variables';
@import "admin/variables";
.pagination {
text-align: center;
margin: 2em 0 1em;
padding: 10px 0;
.page {
padding: 5px 8px;
text-align: center;
display: inline-block;
text-align: center;
&.current {
background-color: $color-2;
border-radius: 3px;
color: $color-1;
}
}
button {
margin: 0 0.35em;

View File

@@ -0,0 +1,29 @@
.select2-result-label {
.variant-autocomplete-item {
.variant-details {
padding: 0 10px;
float: left;
}
.variant-image {
margin-top: 5px;
background-color: white;
float: left;
margin-right: 10px;
}
ul.variant-details {
li {
display: inline-block;
&:after {
content: ' / ';
}
&:last-child:after {
content: '';
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
#progress {
display: none;
position: fixed;
top: 0;
z-index: 1000;
opacity: 0.8;
width: 100%;
.wrapper {
@include border-radius(10px);
top: -10px;
position: absolute;
left: 50%;
width: 200px;
margin-left: -100px;
padding: 11px 0;
background-color: $color-3;
color: $color-1;
text-align: center;
}
#spinner {
position: absolute;
top: 10px;
left: 50%;
margin-left: -5px;
}
.progress-message {
font-size: 120%;
font-weight: $font-weight-bold;
margin-top: 20px;
text-transform: uppercase;
}
}

View File

@@ -0,0 +1,28 @@
@import 'admin/globals/variables';
// Sidebar
//---------------------------------------------------
#sidebar {
overflow: visible;
border-top: 1px solid $color-border;
margin-top: 17px;
.sidebar-title {
color: $color-2;
text-transform: uppercase;
text-align: center;
font-size: 14px;
font-weight: 600;
> span {
display: inline;
background: #fff;
padding: 5px 10px;
position: relative;
top: -14px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
}

View File

@@ -0,0 +1,36 @@
@import 'admin/globals/variables';
.state {
text-transform: uppercase;
font-size: 80%;
font-weight: 600;
&:before {
content: '';
position: relative;
display: inline-block;
margin-right: 3px;
border-radius: $body-font-size/2;
width: $body-font-size - 4px;
height: $body-font-size - 4px;
}
@each $state in $states {
&.#{$state}:before {
background-color: get-value($states, $states-bg-colors, $state);
// &, a {
// color: get-value($states, $states-text-colors, $state);
// }
}
}
}
table tbody tr {
&[class*="state"] td:first-child {
border-left-width: 3px;
}
&.state-complete td:first-child { border-left-color: $color-success }
&.state-cart td:first-child { border-left-color: very-light($color-notice, 6) }
&.state-canceled td:first-child { border-left-color: $color-error }
}

View File

@@ -0,0 +1,14 @@
#table-filter {
.field {
input[type="text"], input[type="phone"],
input[type="email"], input[type="number"],
input[type="url"] {
width: 100%;
}
}
.actions {
text-align: center;
}
}

View File

@@ -0,0 +1,25 @@
// Make color very close to white
@function very-light($color, $adjust: 3){
@if type-of($adjust) == 'number' and $adjust > 0 {
@for $i from 0 through 100 {
@if lighten($color, $i) == white and ($i - $adjust) > $adjust {
@return lighten($color, $i - $adjust);
}
}
}
@else {
@debug "Please correct $adjust value. It should be number and larger then 0. Currently it is '#{type-of($adjust)}' with value '#{$adjust}'"
}
};
// Quick fix for dynamic variables missing in SASS
@function get-value($prop, $val, $search) {
$n1: index($prop, $search);
$n2: index($val, $search);
@if($n1) {
@return nth($val, $n1);
} @else {
@return nth($prop, $n2);
}
}

View File

@@ -0,0 +1,135 @@
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
@import 'admin/plugins/font-awesome';
#taxonomy_tree {
> ul, .jstree-icon {
background-image: none;
}
.jstree-icon {
@extend [class^="icon-"]:before;
}
.jstree-open > .jstree-icon {
@extend .icon-caret-down;
}
.jstree-closed > .jstree-icon {
@extend .icon-caret-right;
}
li {
background-image: none;
a {
background-color: very-light($color-3);
border: 1px solid $color-border;
color: $color-body-text;
font-weight: $font-weight-bold;
text-shadow: none;
width: 90%;
height: auto;
line-height: inherit;
padding: 5px 0 5px 10px;
margin-bottom: 10px;
.jstree-icon {
padding-left: 0px;
@extend .icon-move;
}
}
}
}
#vakata-dragged.jstree-apple .jstree-invalid,
#vakata-dragged.jstree-apple .jstree-ok,
#jstree-marker {
background-image: none !important;
background-color: transparent !important;
@extend [class^="icon-"]:before;
}
#vakata-dragged.jstree-apple .jstree-invalid {
@extend .icon-remove;
color: $color-5;
}
#vakata-dragged.jstree-apple .jstree-ok {
@extend .icon-ok;
color: $color-2;
}
#jstree-marker {
@extend .icon-caret-right;
color: $color-body-text !important;
width: 4px !important;
}
#jstree-marker-line {
@include border-radius($border-radius !important);
height: 0px !important;
margin-left: 5px !important;
margin-top: -2px !important;
border: none !important;
border-bottom: 1px solid $color-body-text !important;
background-color: very-light($color-3) !important;
-webkit-box-shadow: none !important;
-moz-box-shadow: none !important;
box-shadow: none !important;
}
#vakata-contextmenu {
background-color: $color-3 !important;
-moz-box-shadow: none !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
border: none !important;
@include border-radius($border-radius !important);
&:before {
content: '';
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid $color-3;
top: 0px;
margin-top: -10px;
left: 25px;
z-index: 1;
}
a {
color: $color-1 !important;
line-height: inherit !important;
padding: 5px 10px !important;
margin: 0 !important;
font-size: 90% !important;
&:hover {
@include border-radius($border-radius !important);
background-color: $color-2 !important;
border: none !important;
-moz-box-shadow: none !important;
-webkit-box-shadow: none !important;
line-height: inherit !important;
padding: 5px 10px !important;
margin: 0 !important;
}
}
li:first-child a:hover:before {
content: '';
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid $color-2;
top: 0px;
margin-top: -10px;
left: 25px;
z-index: 1;
}
li.vakata-separator {
display: none;
}
}

View File

@@ -0,0 +1,193 @@
@import 'admin/globals/functions';
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
@import 'admin/shared/forms';
@import 'admin/plugins/font-awesome';
.select2-container {
&:hover .select2-choice, &.select2-container-active .select2-choice {
background-color: $color-sel-hover-bg !important;
border-color: $color-sel-hover-bg !important;
}
.select2-choice {
background-image: none !important;
background-color: $color-sel-bg;
border: none !important;
box-shadow: none !important;
@include border-radius($border-radius);
color: $color-1 !important;
font-size: 90%;
height: 31px;
line-height: inherit !important;
padding: 5px 15px 7px;
span {
display: block;
padding: 2px;
}
.select2-search-choice-close {
background-image: none !important;
font-size: 100% !important;
@extend .icon-remove;
@extend [class^="icon-"]:before;
margin-top: 2px;
}
}
&.select2-container-active {
.select2-choice {
box-shadow: none !important;
}
&.select2-dropdown-open .select2-choice div b {
@extend .icon-caret-up
}
}
}
.select2-drop {
border-color: $color-sel-hover-bg;
box-shadow: none !important;
z-index: 1000000;
&.select2-drop-above {
border-color: $color-sel-hover-bg;
}
}
.select2-search {
@extend .icon-search;
font-size: 100%;
color: darken($color-border, 15);
padding: 0 9px 0 0;
&:before {
@extend [class^="icon-"]:before;
position: absolute;
top: 13px;
left: 13px;
}
input {
@extend input[type="text"];
padding: 6px 0 6px 25px;
margin: 5px 0 0 5px;
font-family: $base-font-family;
font-size: 90%;
box-shadow: none;
background-image: none;
}
}
.select2-container .select2-choice .select2-arrow {
background-image: none;
background: transparent;
border: 0;
b {
padding-top: 7px;
display: block;
width: 100%;
height: 100%;
background: none;
font-family: FontAwesome;
font-weight: 200 !important;
&:before {
content: "\f0d7";
}
}
}
.select2-results {
padding-left: 0 !important;
li {
font-size: 85% !important;
&.select2-highlighted {
.select2-result-label {
&, h6 {
color: $color-1 !important;
}
}
}
.select2-result-label {
color: $color-body-text;
min-height: 22px;
clear: both;
overflow: auto;
}
&.select2-no-results, &.select2-searching {
padding: 5px;
background-color: transparent;
color: $color-body-text;
text-align: center;
font-weight: $font-weight-bold;
text-transform: uppercase;
}
}
.select2-highlighted {
background-color: $color-sel-bg;
}
}
.select2-container-multi {
&.select2-container-active, &.select2-dropdown-open {
.select2-choices {
border-color: $color-sel-hover-bg !important;
box-shadow: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.select2-choices {
@extend input[type="text"];
padding: 6px 3px 3px 3px;
box-shadow: none;
background-image: none !important;
.select2-search-choice {
@include border-radius($border-radius);
margin: 0 0 3px 3px;
background-image: none;
background-color: $color-sel-bg;
border: none;
box-shadow: none;
color: $color-1 !important;
font-size: 85%;
&:hover {
background-color: $color-sel-hover-bg;
}
.select2-search-choice-close {
background-image: none !important;
font-size: 85% !important;
@extend .icon-remove;
@extend [class^="icon-"]:before;
margin-left: 2px;
color: $color-1;
}
}
}
}
label .select2-container {
margin-top: -6px;
.select2-choice {
span {
text-transform: none;
font-weight: normal;
}
}
}

View File

@@ -0,0 +1,270 @@
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
input[type="text"],
input[type="password"],
input[type="email"],
input[type="date"],
input[type="datetime"],
input[type="time"],
input[type="url"],
input[type="number"],
input[type="tel"],
textarea, fieldset {
@include border-radius($border-radius);
padding: 7px 10px;
border: 1px solid $color-txt-brd;
color: $color-txt-text;
font-size: 90%;
&:focus {
outline: none;
border-color: $color-txt-hover-brd;
}
&[disabled] {
opacity: 0.7;
}
}
textarea {
line-height: 19px;
}
.fullwidth {
width: 100%;
}
label {
font-weight: 600;
text-transform: uppercase;
font-size: 85%;
display: inline;
margin-bottom: 5px;
color: $color-4;
&.inline {
display: inline-block !important;
}
&.block {
display: block !important;
}
}
.label-block label { display: block }
input[type="submit"],
input[type="button"],
button, .button {
@include border-radius($border-radius);
display: inline-block;
padding: 8px 15px;
border: none;
background-color: $color-btn-bg;
color: $color-btn-text;
text-transform: uppercase;
font-weight: 600 !important;
&:before {
font-weight: normal !important;
}
&:visited, &:active, &:focus { color: $color-btn-text }
&:hover {
background-color: $color-btn-hover-bg;
color: $color-btn-hover-text;
}
&:active:focus {
box-shadow: 0 0 8px 0 darken($color-btn-hover-bg, 5) inset;
}
&.fullwidth {
width: 100%;
text-align: center;
}
}
span.info {
font-style: italic;
font-size: 85%;
color: lighten($color-body-text, 15);
display: block;
line-height: 20px;
margin: 5px 0;
}
.field {
padding: 10px 0;
&.checkbox {
min-height: 73px;
input[type="checkbox"] {
display: inline-block;
width: auto;
}
label {
cursor: pointer;
display: block;
}
}
ul {
border-top: 1px solid $color-border;
list-style: none;
padding-top: 5px;
li {
display: inline-block;
padding-right: 10px;
label {
font-weight: normal;
text-transform: none;
}
&.white-space-nowrap {
white-space: nowrap;
}
}
}
&.withError {
.field_with_errors {
label {
color: very-light($color-error, 30);
}
input {
border-color: very-light($color-error, 15);
}
}
.formError {
color: very-light($color-error, 30);
font-style: italic;
font-size: 85%;
}
}
}
fieldset {
box-shadow: none;
box-sizing: border-box;
border-color: $color-border;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
margin-left: 0;
margin-right: 0;
position: relative;
margin-bottom: 35px;
padding: 10px 0 15px 0;
background-color: transparent;
border-left: none;
border-right: none;
border-radius: 0;
&.no-border-bottom {
border-bottom: none;
margin-bottom: 0;
}
&.no-border-top {
border-top: none;
padding-top: 0;
}
legend {
background-color: $color-1;
color: $color-2;
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
text-align: center;
padding: 8px 15px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
i {
color: $color-link;
}
}
label {
color: lighten($color-body-text, 8);
}
.filter-actions {
margin-bottom: -32px;
margin-top: 15px;
text-align: center;
form {
display: inline-block;
}
button, .button, input[type="submit"], input[type="button"], span.or {
@include border-radius($border-radius);
-webkit-box-shadow: 0 0 0 15px $color-1;
-moz-box-shadow: 0 0 0 15px $color-1;
-ms-box-shadow: 0 0 0 15px $color-1;
-o-box-shadow: 0 0 0 15px $color-1;
box-shadow: 0 0 0 15px $color-1;
&:hover {
border-color: $color-1;
}
}
span.or {
background-color: $color-1;
border-width: 5px;
margin-left: 5px;
margin-right: 5px;
position: relative;
-webkit-box-shadow: 0 0 0 5px $color-1;
-moz-box-shadow: 0 0 0 5px $color-1;
-ms-box-shadow: 0 0 0 5px $color-1;
-o-box-shadow: 0 0 0 5px $color-1;
box-shadow: 0 0 0 5px $color-1;
}
}
&.labels-inline {
.field {
margin-bottom: 0;
display: table;
width: 100%;
label, input {
display: table-cell !important;
}
input {
width: 100%;
}
&.checkbox {
input {
width: auto !important
}
}
}
.actions {
padding: 0;
text-align: right;
}
}
}
.form-actions {
margin-top: 18px;
}
.form-buttons {
text-align: center;
}

View File

@@ -0,0 +1,25 @@
@import 'admin/plugins/font-awesome';
// Some fixes for fontwesome stylesheets
[class^="icon-"], [class*=" icon-"] {
&:before {
padding-right: 5px;
}
&.button, &.icon_link {
width: auto;
&:before {
padding-top: 3px;
}
}
}
.icon-email:before { @extend .icon-envelope:before }
.icon-resume:before { @extend .icon-refresh:before }
.icon-cancel:before,
.icon-void:before { @extend .icon-remove:before }
.icon-capture:before { @extend .icon-ok:before }
.icon-credit:before { @extend .icon-ok:before }

View File

@@ -0,0 +1,92 @@
@import 'admin/globals/variables';
// Basics
//---------------------------------------------------
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
// Helpers
.block-table {
display: table;
width: 100%;
.table-cell {
display: table-cell;
vertical-align: middle;
padding: 0 10px;
&:first-child {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
}
}
.hidden {
display: none;
}
// For block grids
.frameless {
margin-left: -10px;
margin-right: -10px;
}
// Header
//---------------------------------------------------
#header {
background-color: $color-1;
padding: 5px 0;
}
#logo { height: 40px }
[data-hook="admin-title"] { font-size: 14px }
.page-title {
i {
color: $color-2;
}
}
// Content
//---------------------------------------------------
#content {
background-color: $color-1;
position: relative;
z-index: 0;
padding: 0;
margin-top: 15px;
}
#content-header {
padding: 15px 0;
background-color: very-light($color-3, 4);
border-bottom: 1px solid $color-border;
.page-title {
font-size: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page-actions {
text-align: right;
form {
display: inline-block;
}
}
}
// Footer
//---------------------------------------------------
#footer {
margin-top: 15px;
border-top: 1px solid $color-border;
padding: 10px 0;
}

View File

@@ -0,0 +1,208 @@
@import 'admin/globals/variables';
@import 'admin/plugins/font-awesome';
table {
width: 100%;
margin-bottom: 15px;
border-collapse: separate;
th, td {
padding: 7px 5px;
border-right: 1px solid $color-border;
border-bottom: 1px solid $color-border;
vertical-align: middle;
text-overflow: ellipsis;
img {
border: 1px solid transparent;
}
&:first-child {
border-left: 1px solid $color-border;
}
a {
border-bottom: 1px dotted lighten($color-link, 10);
&:hover {
border-color: lighten($color-link-hover, 10);
}
}
.handle {
display: block !important;
text-align: center;
padding-right: 0;
}
&.actions {
background-color: transparent;
border: none !important;
text-align: center;
span.text {
font-size: $body-font-size;
}
[class*='icon-'].no-text {
font-size: 120%;
background-color: very-light($color-3);
border: 1px solid $color-border;
border-radius: 15px;
width: 29px;
height: 29px;
display: inline-block;
padding-top: 2px;
&:before {
text-align: center !important;
width: 27px;
display: inline-block;
}
&:hover {
border-color: transparent;
}
}
button[class*='icon-'] {
color: $color-link;
padding: 0 !important;
}
.icon-envelope-alt, .icon-eye-open {
color: $color-link;
padding-left: 0px;
&:hover {
background-color: $color-3;
color: $color-1;
}
}
.icon-trash:hover, .icon-void:hover {
background-color: $color-error;
color: $color-1;
}
.icon-cancel:hover {
background-color: $color-notice;
color: $color-1;
}
.icon-edit:hover, .icon-capture:hover, .icon-ok:hover, .icon-plus:hover {
background-color: $color-success;
color: $color-1;
}
.icon-copy:hover {
background-color: $color-notice;
color: $color-1;
}
}
input[type="number"],
input[type="text"] {
width: 100%;
}
&.no-border {
border-right: none;
}
.handle {
@extend [class^="icon-"]:before;
@extend .icon-reorder;
cursor: move;
}
}
&.no-borders {
td, th {
border: none !important;
}
}
thead {
th {
padding: 10px;
border-top: 1px solid $color-border;
border-bottom: none;
background-color: $color-tbl-thead;
text-transform: uppercase;
font-size: 85%;
font-weight: $font-weight-bold;
}
}
tbody {
tr {
&:first-child th,
&:first-child td {
border-top: 1px solid $color-border;
}
&.even td {
background-color: $color-tbl-even;
img {
border: 1px solid very-light($color-3, 6);
}
}
&:hover td {
background-color: very-light($color-3, 5);
img {
border: 1px solid $color-border;
}
}
&.deleted td {
background-color: very-light($color-error, 6);
border-color: very-light($color-error, 15);
}
&.ui-sortable-placeholder td {
border: 1px solid $color-2 !important;
visibility: visible !important;
&.actions {
background-color: transparent;
border-right: none !important;
border-top: none !important;
border-bottom: none !important;
border-left: 1px solid $color-2 !important;
}
}
&.ui-sortable-helper {
width: 100%;
td {
background-color: lighten($color-3, 33);
border-bottom: 1px solid $color-border;
&.actions {
display: none;
}
}
}
}
&.no-border-top tr:first-child td {
border-top: none;
}
&.grand-total {
td {
border-color: $color-2 !important;
text-transform: uppercase;
font-size: 110%;
font-weight: 600;
background-color: lighten($color-2, 50);
}
.total {
background-color: $color-2;
color: $color-1;
}
}
}
}

View File

@@ -0,0 +1,134 @@
@import 'admin/globals/variables';
// Base
//--------------------------------------------------------------
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; font-size: 13px; }
body {
font-family: $base-font-family;
font-size: $body-font-size;
font-weight: 400;
color: $color-body-text;
text-rendering: optimizeLegibility;
}
hr {
border-top: 1px solid $color-border;
border-bottom: 1px solid white;
border-left: none;
}
strong, b {
font-weight: 600;
}
// links
//--------------------------------------------------------------
a {
color: $color-link;
text-decoration: none;
line-height: inherit;
&, &:hover, &:active, &:visited, &:focus {
outline: none;
}
&:visited {
color: $color-link-visited;
}
&:focus {
color: $color-link-focus;
}
&:active {
color: $color-link-active;
}
&:hover {
color: $color-link-hover;
}
}
// Headings
//--------------------------------------------------------------
h1,h2,h3,h4,h5,h6 {
font-weight: 600;
color: $color-headers;
line-height: 1.1;
}
h1 { font-size: $h1-size; line-height: $h1-size + 6 }
h2 { font-size: $h2-size; line-height: $h1-size + 4 }
h3 { font-size: $h3-size; line-height: $h1-size + 2 }
h4 { font-size: $h4-size; line-height: $h1-size }
h5 { font-size: $h5-size; line-height: $h1-size }
h6 { font-size: $h6-size; line-height: $h1-size }
// Lists
//--------------------------------------------------------------
ul {
&.inline-menu {
li {
display: inline-block;
}
}
&.fields {
list-style: none;
padding: 0;
margin: 0;
}
}
dl {
width: 100%;
overflow: hidden;
margin: 5px 0;
color: lighten($color-body-text, 15);
dt, dd {
float: left;
line-height: 16px;
padding: 5px;
text-align: justify;
}
dt {
width: 40%;
font-weight: 600;
padding-left: 0;
text-transform: uppercase;
font-size: 85%;
}
dd {
width: 60%;
padding-right: 0;
}
dd:after {
content: '';
clear: both;
}
}
// Helpers
.align-center { text-align: center }
.align-right { text-align: right }
.align-left { text-align: left }
.align-justify { text-align: justify }
.uppercase { text-transform: uppercase }
.green { color: $color-2 }
.blue { color: $color-3 }
.red { color: $color-5 }
.yellow { color: $color-6 }
.no-objects-found {
text-align: center;
font-size: 120%;
text-transform: uppercase;
padding: 40px 0px;
color: lighten($color-body-text, 15);
}

View File

@@ -1,5 +1,6 @@
require 'open_food_network/referer_parser'
require 'open_food_network/permissions'
require 'open_food_network/order_cycle_permissions'
module Admin
class EnterprisesController < ResourceController

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
# This controller lists products that can be added to an exchange
#
# Pagination is optional and can be required by using param[:page]
module Api
class ExchangeProductsController < Api::BaseController
DEFAULT_PAGE = 1
DEFAULT_PER_PAGE = 100
skip_authorization_check only: [:index]
@@ -29,22 +32,28 @@ module Api
def render_variant_count
render text: {
count: Spree::Variant.
not_master.
where(product_id: products).
count
count: variants.count
}.to_json
end
def variants
renderer.exchange_variants(@incoming, @enterprise)
end
def products
ExchangeProductsRenderer.
new(@order_cycle, spree_current_user).
exchange_products(@incoming, @enterprise)
renderer.exchange_products(@incoming, @enterprise)
end
def renderer
@renderer ||= ExchangeProductsRenderer.
new(@order_cycle, spree_current_user)
end
def paginated_products
return products unless pagination_required?
products.
page(params[:page] || DEFAULT_PAGE).
page(params[:page]).
per(params[:per_page] || DEFAULT_PER_PAGE)
end
@@ -74,19 +83,23 @@ module Api
order_cycle: @order_cycle
)
render text: {
products: serializer,
pagination: pagination_data(paginated_products)
}.to_json
result = { products: serializer }
result = result.merge(pagination: pagination_data(paginated_products)) if pagination_required?
render text: result.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,
page: params[:page].to_i,
per_page: (params[:per_page] || DEFAULT_PER_PAGE).to_i
}
end
def pagination_required?
params[:page].present?
end
end
end

View File

@@ -23,7 +23,11 @@ class ApplicationController < ActionController::Base
referer_path = OpenFoodNetwork::RefererParser.path(request.referer)
if referer_path
is_checkout_path_the_referer = [main_app.checkout_path].include?(referer_path)
session["spree_user_return_to"] = is_checkout_path_the_referer ? referer_path : root_path
session["spree_user_return_to"] = if is_checkout_path_the_referer
referer_path
else
main_app.root_path
end
end
end
@@ -48,11 +52,11 @@ class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource_or_scope)
return session[:shopfront_redirect] if session[:shopfront_redirect]
stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
stored_location_for(resource_or_scope) || main_app.root_path
end
def after_sign_out_path_for(_resource_or_scope)
session[:shopfront_redirect] || root_path
session[:shopfront_redirect] || main_app.root_path
end
private
@@ -74,7 +78,7 @@ class ApplicationController < ActionController::Base
def require_distributor_chosen
unless @distributor = current_distributor
redirect_to spree.root_path
redirect_to main_app.root_path
false
end
end
@@ -86,26 +90,29 @@ class ApplicationController < ActionController::Base
end
def check_hub_ready_for_checkout
# This condition is more rigourous than required by development to avoid coupling this
# condition to every controller spec
if current_distributor && current_order &&
current_distributor.respond_to?(:ready_for_checkout?) &&
!current_distributor.ready_for_checkout?
if current_distributor_closed?
current_order.empty!
current_order.set_distribution! nil, nil
flash[:info] = "The hub you have selected is temporarily closed for orders. Please try again later."
redirect_to root_url
flash[:info] = "The hub you have selected is temporarily closed for orders. "\
"Please try again later."
redirect_to main_app.root_url
end
end
def current_distributor_closed?
current_distributor &&
current_order &&
current_distributor.respond_to?(:ready_for_checkout?) &&
!current_distributor.ready_for_checkout?
end
def check_order_cycle_expiry
if current_order_cycle.andand.closed?
session[:expired_order_cycle_id] = current_order_cycle.id
current_order.empty!
current_order.set_order_cycle! nil
flash[:info] = "The order cycle you've selected has just closed. Please try again!"
redirect_to root_url
redirect_to main_app.root_url
end
end

View File

@@ -15,11 +15,10 @@ module Spree
# 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 flash[:notice].present?
return if distributors.empty? || flash[:notice].present?
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
warning = OrderCycleWarning.new(spree_current_user).call
flash[:notice] = warning if warning.present?
end
# This is in Spree::Core::ControllerHelpers::Auth
@@ -30,7 +29,7 @@ module Spree
redirect_to '/unauthorized'
else
store_location
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end
@@ -93,24 +92,6 @@ module Spree
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
@@ -121,13 +102,17 @@ module Spree
def render_as_json(data, options = {})
ams_prefix = options.delete :ams_prefix
if [Array, ActiveRecord::Relation].include? data.class
if each_serializer_required?(data)
render options.merge(json: data, each_serializer: serializer(ams_prefix))
else
render options.merge(json: data, serializer: serializer(ams_prefix))
end
end
def each_serializer_required?(data)
['Array', 'ActiveRecord::Relation'].include?(data.class.name)
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}."

View File

@@ -0,0 +1,71 @@
module Spree
module Admin
module Orders
class CustomerDetailsController < Spree::Admin::BaseController
before_filter :load_order
before_filter :check_authorization
before_filter :set_guest_checkout_status, only: :update
def show
edit
render action: :edit
end
def edit
country_id = Address.default.country.id
@order.build_bill_address(country_id: country_id) if @order.bill_address.nil?
@order.build_ship_address(country_id: country_id) if @order.ship_address.nil?
end
def update
if @order.update_attributes(params[:order])
if params[:guest_checkout] == "false"
@order.associate_user!(Spree.user_class.find_by_email(@order.email))
end
AdvanceOrderService.new(@order).call
@order.shipments.map(&:refresh_rates)
flash[:success] = Spree.t('customer_details_updated')
redirect_to admin_order_customer_path(@order)
else
render action: :edit
end
end
# Inherit CanCan permissions for the current order
def model_class
load_order unless @order
@order
end
private
def load_order
@order = Order.find_by_number!(params[:order_id], include: :adjustments)
end
def check_authorization
load_order
session[:access_token] ||= params[:token]
resource = @order
action = params[:action].to_sym
action = :edit if action == :show # show route renders :edit for this controller
authorize! action, resource, session[:access_token]
end
def set_guest_checkout_status
registered_user = Spree::User.find_by_email(params[:order][:email])
params[:order][:guest_checkout] = registered_user.nil?
return unless registered_user
@order.user_id = registered_user.id
end
end
end
end
end

View File

@@ -1,49 +0,0 @@
Spree::Admin::Orders::CustomerDetailsController.class_eval do
before_filter :check_authorization
before_filter :set_guest_checkout_status, only: :update
def update
if @order.update_attributes(params[:order])
if params[:guest_checkout] == "false"
@order.associate_user!(Spree.user_class.find_by_email(@order.email))
end
AdvanceOrderService.new(@order).call
@order.shipments.map &:refresh_rates
flash[:success] = Spree.t('customer_details_updated')
redirect_to admin_order_customer_path(@order)
else
render action: :edit
end
end
# Inherit CanCan permissions for the current order
def model_class
load_order unless @order
@order
end
private
def check_authorization
load_order
session[:access_token] ||= params[:token]
resource = @order
action = params[:action].to_sym
action = :edit if action == :show # show route renders :edit for this controller
authorize! action, resource, session[:access_token]
end
def set_guest_checkout_status
registered_user = Spree::User.find_by_email(params[:order][:email])
params[:order][:guest_checkout] = registered_user.nil?
return unless registered_user
@order.user_id = registered_user.id
end
end

View File

@@ -0,0 +1,150 @@
require 'open_food_network/spree_api_key_loader'
module Spree
module Admin
class OrdersController < Spree::Admin::BaseController
require 'spree/core/gateway_error'
include OpenFoodNetwork::SpreeApiKeyLoader
helper CheckoutHelper
before_filter :load_order, only: [:edit, :update, :fire, :resend,
:invoice, :print, :print_ticket]
before_filter :load_distribution_choices, only: [:new, :edit, :update]
# Ensure that the distributor is set for an order when
before_filter :ensure_distribution, only: :new
# After updating an order, the fees should be updated as well
# Currently, adding or deleting line items does not trigger updating the
# fees! This is a quick fix for that.
# TODO: update fees when adding/removing line items
# instead of the update_distribution_charge method.
after_filter :update_distribution_charge, only: :update
before_filter :require_distributor_abn, only: :invoice
respond_to :html, :json
def new
@order = Order.create
@order.created_by = try_spree_current_user
@order.save
redirect_to edit_admin_order_url(@order)
end
def edit
@order.shipments.map(&:refresh_rates)
AdvanceOrderService.new(@order).call
# The payment step shows an error of 'No pending payments'
# Clearing the errors from the order object will stop this error
# appearing on the edit page where we don't want it to.
@order.errors.clear
end
def update
unless @order.update_attributes(params[:order]) && @order.line_items.present?
if @order.line_items.empty?
@order.errors.add(:line_items, Spree.t('errors.messages.blank'))
end
return redirect_to(edit_admin_order_path(@order),
flash: { error: @order.errors.full_messages.join(', ') })
end
@order.update!
if @order.complete?
redirect_to edit_admin_order_path(@order)
else
# Jump to next step if order is not complete
redirect_to admin_order_customer_path(@order)
end
end
def bulk_management
load_spree_api_key
end
def fire
event = params[:e]
if @order.public_send(event.to_s)
flash[:success] = Spree.t(:order_updated)
else
flash[:error] = Spree.t(:cannot_perform_operation)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message.to_s
ensure
redirect_to :back
end
def resend
Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver
flash[:success] = t(:order_email_resent)
respond_with(@order) { |format| format.html { redirect_to :back } }
end
def invoice
pdf = InvoiceRenderer.new.render_to_string(@order)
Spree::OrderMailer.invoice_email(@order.id, pdf).deliver
flash[:success] = t('admin.orders.invoice_email_sent')
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
end
def print
render InvoiceRenderer.new.args(@order)
end
def print_ticket
render template: "spree/admin/orders/ticket", layout: false
end
def update_distribution_charge
@order.update_distribution_charge!
end
private
def load_order
@order = Order.find_by_number!(params[:id], include: :adjustments) if params[:id]
authorize! action, @order
end
def model_class
Spree::Order
end
def require_distributor_abn
return if @order.distributor.abn.present?
flash[:error] = t(:must_have_valid_business_number,
enterprise_name: @order.distributor.name)
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
end
def load_distribution_choices
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
ocs = OrderCycle.managed_by(spree_current_user)
@order_cycles = ocs.soonest_closing +
ocs.soonest_opening +
ocs.closed +
ocs.undated
end
def ensure_distribution
unless @order
@order = Spree::Order.new
@order.generate_order_number
@order.save!
end
return if @order.distribution_set?
render 'set_distribution', locals: { order: @order }
end
end
end
end

View File

@@ -1,120 +0,0 @@
require 'open_food_network/spree_api_key_loader'
Spree::Admin::OrdersController.class_eval do
include OpenFoodNetwork::SpreeApiKeyLoader
helper CheckoutHelper
before_filter :load_order, only: %i[show edit update fire resend invoice print print_ticket]
before_filter :load_distribution_choices, only: [:new, :edit, :update]
# Ensure that the distributor is set for an order when
before_filter :ensure_distribution, only: :new
# After updating an order, the fees should be updated as well
# Currently, adding or deleting line items does not trigger updating the
# fees! This is a quick fix for that.
# TODO: update fees when adding/removing line items
# instead of the update_distribution_charge method.
after_filter :update_distribution_charge, only: :update
before_filter :require_distributor_abn, only: :invoice
respond_to :html, :json
def index
# Overriding the action so we only render the page template. An angular request
# within the page then fetches the data it needs from Api::OrdersController
end
def bulk_management
load_spree_api_key
end
def edit
@order.shipments.map &:refresh_rates
AdvanceOrderService.new(@order).call
# The payment step shows an error of 'No pending payments'
# Clearing the errors from the order object will stop this error
# appearing on the edit page where we don't want it to.
@order.errors.clear
end
# Re-implement spree method so that it redirects to edit instead of rendering edit
# This allows page reloads while adding variants to the order (/edit), without being redirected to customer details page (/update)
def update
unless @order.update_attributes(params[:order]) && @order.line_items.present?
@order.errors.add(:line_items, Spree.t('errors.messages.blank')) if @order.line_items.empty?
return redirect_to edit_admin_order_path(@order), flash: { error: @order.errors.full_messages.join(', ') }
end
@order.update!
if @order.complete?
redirect_to edit_admin_order_path(@order)
else
# Jump to next step if order is not complete
redirect_to admin_order_customer_path(@order)
end
end
# Overwrite to use confirm_email_for_customer instead of confirm_email.
# This uses a new template. See mailers/spree/order_mailer_decorator.rb.
def resend
Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver
flash[:success] = t(:order_email_resent)
respond_with(@order) { |format| format.html { redirect_to :back } }
end
def invoice
pdf = InvoiceRenderer.new.render_to_string(@order)
Spree::OrderMailer.invoice_email(@order.id, pdf).deliver
flash[:success] = t('admin.orders.invoice_email_sent')
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
end
def print
render InvoiceRenderer.new.args(@order)
end
def print_ticket
render template: "spree/admin/orders/ticket", layout: false
end
def update_distribution_charge
@order.update_distribution_charge!
end
private
def require_distributor_abn
if @order.distributor.abn.blank?
flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name)
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
end
end
def load_distribution_choices
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
ocs = OrderCycle.managed_by(spree_current_user)
@order_cycles = ocs.soonest_closing +
ocs.soonest_opening +
ocs.closed +
ocs.undated
end
def ensure_distribution
unless @order
@order = Spree::Order.new
@order.generate_order_number
@order.save!
end
unless @order.distribution_set?
render 'set_distribution', locals: { order: @order }
end
end
end

View File

@@ -0,0 +1,129 @@
# frozen_string_literal: true
module Spree
module Admin
class PaymentsController < Spree::Admin::BaseController
before_filter :load_order, except: [:show]
before_filter :load_payment, only: [:fire, :show]
before_filter :load_data
before_filter :can_transition_to_payment
respond_to :html
def index
@payments = @order.payments
redirect_to new_admin_order_payment_url(@order) if @payments.empty?
end
def new
@payment = @order.payments.build
end
def create
@payment = @order.payments.build(object_params)
load_payment_source
begin
unless @payment.save
redirect_to admin_order_payments_path(@order)
return
end
if @order.completed?
@payment.process!
flash[:success] = flash_message_for(@payment, :successfully_created)
redirect_to admin_order_payments_path(@order)
else
AdvanceOrderService.new(@order).call!
flash[:success] = Spree.t(:new_order_completed)
redirect_to edit_admin_order_url(@order)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message.to_s
redirect_to new_admin_order_payment_path(@order)
end
end
# When a user fires an event, take them back to where they came from
# (we can't use respond_override because Spree no longer uses respond_with)
def fire
event = params[:e]
return unless event && @payment.payment_source
# Because we have a transition method also called void, we do this to avoid conflicts.
event = "void_transaction" if event == "void"
if @payment.public_send("#{event}!")
flash[:success] = t(:payment_updated)
else
flash[:error] = t(:cannot_perform_operation)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message
ensure
redirect_to request.referer
end
private
def load_payment_source
if @payment.payment_method.is_a?(Spree::Gateway) &&
@payment.payment_method.payment_profiles_supported? &&
params[:card].present? &&
(params[:card] != 'new')
@payment.source = CreditCard.find_by_id(params[:card])
end
end
def object_params
if params[:payment] &&
params[:payment_source] &&
source_params = params.delete(:payment_source)[params[:payment][:payment_method_id]]
params[:payment][:source_attributes] = source_params
end
params[:payment]
end
def load_data
@amount = params[:amount] || load_order.total
# Only show payments for the order's distributor
@payment_methods = PaymentMethod.
available(:back_end).
select{ |pm| pm.has_distributor? @order.distributor }
@payment_method = if @payment && @payment.payment_method
@payment.payment_method
else
@payment_methods.first
end
@previous_cards = @order.credit_cards.with_payment_profile
end
# At this point admin should have passed through Customer Details step
# where order.next is called which leaves the order in payment step
#
# Orders in complete step also allows to access this controller
#
# Otherwise redirect user to that step
def can_transition_to_payment
return if @order.payment? || @order.complete?
flash[:notice] = Spree.t(:fill_in_customer_info)
redirect_to edit_admin_order_customer_url(@order)
end
def load_order
@order = Order.find_by_number!(params[:order_id])
authorize! action, @order
@order
end
def load_payment
@payment = Payment.find(params[:id])
end
end
end
end

View File

@@ -1,59 +0,0 @@
Spree::Admin::PaymentsController.class_eval do
append_before_filter :filter_payment_methods
def create
@payment = @order.payments.build(object_params)
if @payment.payment_method.is_a?(Spree::Gateway) && @payment.payment_method.payment_profiles_supported? && params[:card].present? && (params[:card] != 'new')
@payment.source = CreditCard.find_by_id(params[:card])
end
begin
unless @payment.save
redirect_to admin_order_payments_path(@order)
return
end
if @order.completed?
@payment.process!
flash[:success] = flash_message_for(@payment, :successfully_created)
redirect_to admin_order_payments_path(@order)
else
AdvanceOrderService.new(@order).call!
flash[:success] = Spree.t(:new_order_completed)
redirect_to edit_admin_order_url(@order)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message.to_s
redirect_to new_admin_order_payment_path(@order)
end
end
# When a user fires an event, take them back to where they came from
# (we can't use respond_override because Spree no longer uses respond_with)
def fire
event = params[:e]
return unless event && @payment.payment_source
# Because we have a transition method also called void, we do this to avoid conflicts.
event = "void_transaction" if event == "void"
if @payment.public_send("#{event}!")
flash[:success] = t(:payment_updated)
else
flash[:error] = t(:cannot_perform_operation)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message
ensure
redirect_to request.referer
end
private
# Only show payments for the order's distributor
def filter_payment_methods
@payment_methods = @payment_methods.select{ |pm| pm.has_distributor? @order.distributor }
@payment_method ||= @payment_methods.first
end
end

View File

@@ -0,0 +1,43 @@
module Spree
module Admin
class SearchController < Spree::Admin::BaseController
# http://spreecommerce.com/blog/2010/11/02/json-hijacking-vulnerability/
before_filter :check_json_authenticity, only: :index
respond_to :json
def known_users
@users = if exact_match = Spree.user_class.find_by_email(params[:q])
[exact_match]
else
spree_current_user.known_users.ransack(ransack_hash).result.limit(10)
end
render json: @users, each_serializer: ::Api::Admin::UserSerializer
end
def customers
@customers = []
if spree_current_user.enterprises.pluck(:id).include? params[:distributor_id].to_i
@customers = Customer.
ransack(m: 'or', email_start: params[:q], name_start: params[:q]).
result.
where(enterprise_id: params[:distributor_id])
end
render json: @customers, each_serializer: ::Api::Admin::CustomerSerializer
end
private
def ransack_hash
{
m: 'or',
email_start: params[:q],
ship_address_firstname_start: params[:q],
ship_address_lastname_start: params[:q],
bill_address_firstname_start: params[:q],
bill_address_lastname_start: params[:q]
}
end
end
end
end

View File

@@ -1,36 +0,0 @@
Spree::Admin::SearchController.class_eval do
def known_users
@users = if exact_match = Spree.user_class.find_by_email(params[:q])
[exact_match]
else
spree_current_user.known_users.ransack(
m: 'or',
email_start: params[:q],
ship_address_firstname_start: params[:q],
ship_address_lastname_start: params[:q],
bill_address_firstname_start: params[:q],
bill_address_lastname_start: params[:q]
).result.limit(10)
end
render json: @users, each_serializer: Api::Admin::UserSerializer
end
def customers
@customers = if spree_current_user.enterprises.pluck(:id).include? params[:distributor_id].to_i
Customer.ransack(m: 'or', email_start: params[:q], name_start: params[:q])
.result.where(enterprise_id: params[:distributor_id])
else
[]
end
render json: @customers, each_serializer: Api::Admin::CustomerSerializer
end
def users_with_ams
users_without_ams
render json: @users, each_serializer: Api::Admin::UserSerializer
end
alias_method_chain :users, :ams
end

View File

@@ -71,7 +71,7 @@ module Spree
@order = order_to_update
unless @order
flash[:error] = t(:order_not_found)
redirect_to(root_path) && return
redirect_to(main_app.root_path) && return
end
if @order.update_attributes(params[:order])

View File

@@ -32,7 +32,7 @@ module Spree
session[:guest_token] = nil
end
redirect_back_or_default(root_url)
redirect_back_or_default(main_app.root_url)
else
render :new
end

View File

@@ -1,6 +1,17 @@
module Spree
module Admin
module OrdersHelper
def event_links
links = []
links << event_link("cancel") if @order.can_cancel?
links << event_link("resume") if @order.can_resume?
links.join('&nbsp;').html_safe
end
def line_item_shipment_price(line_item, quantity)
Spree::Money.new(line_item.price * quantity, currency: line_item.currency)
end
def order_links(order)
@order ||= order
links = []
@@ -100,6 +111,14 @@ module Spree
icon: 'icon-trash',
confirm: t(:are_you_sure) }
end
def event_link(event)
button_link_to(Spree.t(event),
fire_admin_order_url(@order, e: event),
method: :put,
icon: "icon-#{event}",
data: { confirm: Spree.t(:order_sure_want_to, event: Spree.t(event)) })
end
end
end
end

View File

@@ -0,0 +1,11 @@
module Spree
module Admin
module PaymentsHelper
def payment_method_name(payment)
# hack to allow us to retrieve the name of a "deleted" payment method
id = payment.payment_method_id
Spree::PaymentMethod.find_with_destroyed(id).name
end
end
end
end

View File

@@ -9,6 +9,6 @@ Spree::BaseMailer.class_eval do
def roadie_options
# This lets us specify assets using relative paths in email templates
super.merge(url_options: { host: URI(spree.root_url).host })
super.merge(url_options: { host: URI(main_app.root_url).host })
end
end

View File

@@ -25,7 +25,8 @@ class Enterprise < ActiveRecord::Base
has_many :relationships_as_child, class_name: 'EnterpriseRelationship',
foreign_key: 'child_id',
dependent: :destroy
has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup'
has_and_belongs_to_many :groups, join_table: 'enterprise_groups_enterprises',
class_name: 'EnterpriseGroup'
has_many :producer_properties, foreign_key: 'producer_id'
has_many :properties, through: :producer_properties
has_many :supplied_products, class_name: 'Spree::Product',
@@ -115,7 +116,10 @@ class Enterprise < ActiveRecord::Base
scope :not_ready_for_checkout, lambda {
# When ready_for_checkout is empty, return all rows when there are no enterprises ready for
# checkout.
ready_enterprises = Enterprise.ready_for_checkout.select('enterprises.id')
ready_enterprises = Enterprise.ready_for_checkout.
except(:select).
select('DISTINCT enterprises.id')
if ready_enterprises.present?
where("enterprises.id NOT IN (?)", ready_enterprises)
else

View File

@@ -5,7 +5,7 @@ class EnterpriseGroup < ActiveRecord::Base
include PermalinkGenerator
acts_as_list
has_and_belongs_to_many :enterprises
has_and_belongs_to_many :enterprises, join_table: 'enterprise_groups_enterprises'
belongs_to :owner, class_name: 'Spree::User', foreign_key: :owner_id, inverse_of: :owned_groups
belongs_to :address, class_name: 'Spree::Address'
accepts_nested_attributes_for :address

View File

@@ -63,7 +63,7 @@ class OrderCycle < ActiveRecord::Base
if user.has_spree_role?('admin')
scoped
else
where('coordinator_id IN (?)', user.enterprises.map(&:id))
where(coordinator_id: user.enterprises)
end
}

View File

@@ -1,3 +1,7 @@
# frozen_string_literal: true
require 'open_food_network/order_cycle_permissions'
class ExchangeProductsRenderer
def initialize(order_cycle, user)
@order_cycle = order_cycle
@@ -12,6 +16,14 @@ class ExchangeProductsRenderer
end
end
def exchange_variants(incoming, enterprise)
variants_relation = Spree::Variant.
not_master.
where(product_id: exchange_products(incoming, enterprise).select(&:id))
filter_visible(variants_relation)
end
private
def products_for_incoming_exchange(enterprise)
@@ -21,12 +33,16 @@ class ExchangeProductsRenderer
def supplied_products(enterprises_query_matcher)
products_relation = Spree::Product.where(supplier_id: enterprises_query_matcher)
filter_visible(products_relation)
end
def filter_visible(relation)
if @order_cycle.present? &&
@order_cycle.prefers_product_selection_from_coordinator_inventory_only?
products_relation = products_relation.visible_for(@order_cycle.coordinator)
relation = relation.visible_for(@order_cycle.coordinator)
end
products_relation
relation
end
def products_for_outgoing_exchange

View File

@@ -0,0 +1,37 @@
# frozen_string_literal: true
class OrderCycleWarning
def initialize(current_user)
@current_user = current_user
end
def call
distributors = active_distributors_not_ready_for_checkout
return if distributors.empty?
active_distributors_not_ready_for_checkout_message(distributors)
end
private
attr_reader :current_user
def active_distributors_not_ready_for_checkout
ocs = OrderCycle.managed_by(current_user).active
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
Enterprise.where(id: distributors.map(&:id)).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
end

View File

@@ -54,7 +54,7 @@
.row{ ng: { show: "Enterprise.sells == 'own' || Enterprise.sells == 'any'" } }
.three.columns.alpha
= f.label :permalink, t('.permalink')
%div{'ofn-with-tip' => t('.permalink_tip', link: spree.root_url)}
%div{'ofn-with-tip' => t('.permalink_tip', link: main_app.root_url)}
%a= t('admin.whats_this')
.six.columns
= f.text_field :permalink, { 'ng-model' => "Enterprise.permalink", placeholder: "eg. your-shop-name", 'ng-model-options' => "{ updateOn: 'default blur', debounce: {'default': 300, 'blur': 0} }" }
@@ -69,7 +69,7 @@
%div{'ofn-with-tip' => t('.link_to_front_tip')}
%a= t('admin.whats_this')
.eight.columns.omega
= surround spree.root_url, "/shop" do
= surround main_app.root_url, "/shop" do
{{Enterprise.permalink}}
.row
.three.columns.alpha

View File

@@ -42,10 +42,10 @@
%tr
%td{:align => "center"}
%p
%a{:href => "#{ URI.join(spree.root_url, Spree::Config.footer_tos_url).to_s }", :target => "_blank"}
%a{:href => "#{ URI.join(main_app.root_url, Spree::Config.footer_tos_url).to_s }", :target => "_blank"}
= t :terms_of_service
|
%a{:href => "#{ spree.root_url }"}
%a{:href => "#{ main_app.root_url }"}
= Spree::Config[:site_name]
/ | <a href="#"><unsubscribe>Unsubscribe</unsubscribe></a>
%td

View File

@@ -94,7 +94,7 @@
.row
.small-12.medium-3.medium-offset-2.columns.text-left
%a{href: root_path}
%a{href: main_app.root_path}
%img{src: ContentConfig.footer_logo.url, width: "220"}
.small-12.medium-5.columns.text-left
%p.text-small

View File

@@ -2,7 +2,7 @@
%section.top-bar-section
%ul.left
%li.ofn-logo
%a{href: root_path}
%a{href: main_app.root_path}
%img{src: ContentConfig.logo.url}
%li.powered-by
%img{src: '/favicon.ico'}

View File

@@ -5,7 +5,7 @@
%section.left
.ofn-logo
%a{href: root_path}
%a{href: main_app.root_path}
%img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"}
%section.right{"ng-cloak" => true}

View File

@@ -1,7 +1,7 @@
%aside.left-off-canvas-menu.show-for-medium-down{ ng: { controller: "OffcanvasCtrl" } }
%ul.off-canvas-list
%li.ofn-logo
%a{href: root_path}
%a{href: main_app.root_path}
%img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"}
- [*1..7].each do |menu_number|
- menu_name = "menu_#{menu_number}"

View File

@@ -9,6 +9,6 @@
.alert-box.blocked-cookies.hidden
%p= t('blocked_cookies_alert')
%a.button.allow{target: '_blank', href: "#{spree.root_url}embedded_shopfront/shopfront_session/"}
%a.button.allow{target: '_blank', href: "#{main_app.root_url}embedded_shopfront/shopfront_session/"}
= t('allow_cookies')

View File

@@ -25,4 +25,4 @@
= button Spree.t('actions.update'), 'icon-refresh'
- content_for :head do
= javascript_include_tag 'admin/address_states.js'
= javascript_include_tag 'admin/spree/orders/address_states.js'

View File

@@ -0,0 +1,72 @@
<% if use_billing %>
<div class="field" style="position: absolute;margin-top: -15px;right: 0;">
<span data-hook="use_billing">
<%= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) %>
<%= label_tag 'order[use_billing]', Spree.t(:use_billing_address) %>
</span>
</div>
<% end %>
<% is_shipping_address = name == Spree.t(:shipping_address) %>
<% shipping_or_billing = is_shipping_address ? 'shipping' : 'billing' %>
<% s_or_b = is_shipping_address ? 's' : 'b' %>
<div id="<%= is_shipping_address ? 'shipping' : 'billing' %>" style="display: <%= (use_billing && (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.eql?(@order.ship_address))) ? 'none' : 'block' %>" data-hook="address_fields">
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :firstname, Spree.t(:first_name) + ':' %>
<%= f.text_field :firstname, :class => 'fullwidth' %>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :lastname, Spree.t(:last_name) + ':' %>
<%= f.text_field :lastname, :class => 'fullwidth' %>
</div>
<% if Spree::Config[:company] %>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :company, Spree.t(:company) + ':' %>
<%= f.text_field :company, :class => 'fullwidth' %>
</div>
<% end %>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :address1, Spree.t(:street_address) + ':' %>
<%= f.text_field :address1, :class => 'fullwidth' %>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :address2, Spree.t(:street_address_2) + ':' %>
<%= f.text_field :address2, :class => 'fullwidth' %>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :city, Spree.t(:city) + ':' %>
<%= f.text_field :city, :class => 'fullwidth' %>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :zipcode, Spree.t(:zip) + ':' %>
<%= f.text_field :zipcode, :class => 'fullwidth' %>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :country_id, Spree.t(:country) + ':' %>
<span id="<%= s_or_b %>country">
<%= f.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'select2 fullwidth'} %>
</span>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :state_id, Spree.t(:state) + ':' %>
<span id="<%= s_or_b %>state">
<%= f.text_field :state_name,
:style => "display: #{f.object.country.states.empty? ? 'block' : 'none' };",
:disabled => !f.object.country.states.empty?, :class => 'fullwidth state_name' %>
<%= f.collection_select :state_id, f.object.country.states.sort, :id, :name, {:include_blank => true}, {:class => 'select2 fullwidth', :style => "display: #{f.object.country.states.empty? ? 'none' : 'block' };", :disabled => f.object.country.states.empty?} %>
</span>
</div>
<div class="field <%= "#{shipping_or_billing}-row" %>">
<%= f.label :phone, Spree.t(:phone) + ':' %>
<%= f.phone_field :phone, :class => 'fullwidth' %>
</div>
</div>
<% content_for :head do %>
<%= javascript_tag do -%>
$(document).ready(function(){
$('span#<%= s_or_b %>country .select2').on('change', function() { update_state('<%= s_or_b %>'); });
});
<% end -%>
<% end %>

View File

@@ -1,5 +0,0 @@
<div class="form-buttons filter-actions actions" data-hook="buttons">
<%= button Spree.t('actions.create'), 'icon-ok' %>
<span class="or"><%= Spree.t(:or) %></span>
<%= link_to_with_icon 'icon-remove', Spree.t('actions.cancel'), collection_url, :class => 'button' %>
</div>

View File

@@ -0,0 +1,4 @@
.form-buttons.filter-actions.actions
= button t('actions.create'), 'icon-ok'
%span.or= t(:or)
= button_link_to t('actions.cancel'), collection_url, icon: 'icon-remove'

View File

@@ -2,7 +2,6 @@
Spree.routes = <%== {
:variants_search => spree.admin_search_variants_path(:format => 'json'),
:taxons_search => main_app.api_taxons_path(:format => 'json'),
:user_search => spree.admin_search_users_path(:format => 'json'),
:orders_api => main_app.api_orders_path
}.to_json %>;
</script>

View File

@@ -0,0 +1,7 @@
$('#order_tab_summary h5#order_status').html('<%= j Spree.t(:status) %>: <%= j Spree.t(@order.state, :scope => :order_state) %>');
$('#order_tab_summary h5#order_total').html('<%= j Spree.t(:total) %>: <%= j @order.display_total.to_html %>');
<% if @order.completed? %>
$('#order_tab_summary h5#payment_status').html('<%= j Spree.t(:payment) %>: <%= j Spree.t(@order.payment_state, :scope => :payment_states, :default => [:missing, "none"]) %>');
$('#order_tab_summary h5#shipment_status').html('<%= j Spree.t(:shipment) %>: <%= j Spree.t(@order.shipment_state, :scope => :shipment_state, :default => [:missing, "none"]) %>');
<% end %>

View File

@@ -8,7 +8,7 @@
= link_to t(:account), account_path
%li{"data-hook" => "user-logout-link"}
%i.icon-signout
= link_to t(:logout), spree.logout_path
= link_to t(:logout), logout_path
%li{"data-hook" => "store-frontend-link"}
%i.icon-external-link
= link_to t(".header.store"), spree.root_path, target: "_blank"
= link_to t(".header.store"), main_app.root_path, target: "_blank"

View File

@@ -1,6 +1,6 @@
%html{ lang: "en" }
%head{"data-hook" => "admin_inside_head"}= render :partial => 'spree/admin/shared/head'
%body.admin{"data-ajax-root-path" => spree.root_path}
%body.admin{"data-ajax-root-path" => main_app.root_path}
#wrapper{"data-hook" => ""}
- if flash[:error]
.flash.error= flash[:error]

View File

@@ -9,7 +9,7 @@
= t :email_signup_confirmed_email
%p
-# Remove http:// and trailing slashes from root url if they exist
= t :email_signup_shop_html, link: link_to(spree.root_url.sub(/http:\/\//,"").sub(/\/$/,""), spree.root_url, target: '_blank')
= t :email_signup_shop_html, link: link_to(main_app.root_url.sub(/http:\/\//,"").sub(/\/$/,""), main_app.root_url, target: '_blank')
%p &nbsp;
%hr/
%p &nbsp;

View File

@@ -50,6 +50,8 @@ ca:
shipping_method_ids: "Mètodes d'enviament"
payment_method_ids: "Mètodes de Pagament"
errors:
messages:
inclusion: "no està inclòs a la llista"
models:
subscription_validator:
attributes:
@@ -700,6 +702,11 @@ ca:
enable_subscriptions_false: "Deshabilitat"
enable_subscriptions_true: "Habilitat"
shopfront_message: "Missatge de la botiga"
shopfront_message_placeholder: >
Un missatge opcional per donar la benvinguda als clients i explicar
com comprar amb vosaltres. Si s'introdueix text aquí, es mostrarà a
la pestanya d'inici quan els clients arribin per primera vegada a la
vostra botiga.
shopfront_message_link_tooltip: "Inserir / editar enllaç"
shopfront_message_link_prompt: "Introduïu un URL per inserir"
shopfront_closed_message: "Missatge de tancament de la botiga"
@@ -1101,10 +1108,13 @@ ca:
destroy_attachment_does_not_exist: "El logotip no existeix"
enterprise_promo_image:
destroy_attachment_does_not_exist: "La imatge promocional no existeix"
orders:
failed_to_update: "No s'ha pogut actualitzar la comanda"
checkout:
already_ordered:
cart: "cistella"
message_html: "Ja teniu una comanda per a aquest cicle de comanda. Consulteu %{cart} per veure els articles que heu demanat anteriorment. També podeu cancel·lar articles sempre que el cicle de comanda estigui obert."
failed: "La comanda ha fallat. Informeu-nos perquè puguem processar la vostra comanda."
shops:
hubs:
show_closed_shops: "Mostra les botigues tancades"
@@ -1935,6 +1945,7 @@ ca:
tax_category: "Categoria d'impostos"
calculator: "Calculadora"
calculator_values: "Valors de la calculadora"
calculator_settings_warning: "Si canvieu el tipus de calculadora, primer heu de desar abans de poder editar la configuració de la calculadora"
flat_percent_per_item: "Percentatge fixe (per article)"
flat_rate_per_item: "Tarifa fixa (per article)"
flat_rate_per_order: "Tarifa fixa (per comanda)"
@@ -2419,6 +2430,12 @@ ca:
severity: Severitat
description: Descripció
resolve: Resoldre
exchange_products:
load_more_products: "Carrega més productes"
load_all_products: "Carrega tots els productes"
select_all_products: "Seleccioneu tots els productes %{total_number_of_products}"
products_loaded: "%{num_of_products_loaded} de %{total_number_of_products} productes carregats"
loading_products: "Carregant productes"
tag_rules:
shipping_method_tagged_top: "Els mètodes d'enviament etiquetats"
shipping_method_tagged_bottom: "son:"
@@ -2501,6 +2518,7 @@ ca:
customer_placeholder: "consumidora@example.org"
valid_email_error: "siusplau, introduïu una adreça de correu electrònic vàlida"
subscriptions:
error_saving: "S'ha produït un error en desar la subscripció"
new:
please_select_a_shop: "Si us plau, seleccioneu una botiga"
insufficient_stock: "No hi ha prou estoc disponible, només queda %{on_hand}"
@@ -2818,6 +2836,8 @@ ca:
zipcode: Codi postal
weight: Pes (per kg)
error_user_destroy_with_orders: "No es poden esborrar usuaris amb comandes completades"
cannot_create_payment_without_payment_methods: "No podeu crear un pagament per a una comanda sense que hi hagi definit cap mètode de pagament."
please_define_payment_methods: "Primer definiu alguns mètodes de pagament."
options: "Opcions"
actions:
update: "Actualitzar"
@@ -2904,6 +2924,7 @@ ca:
capture: "Captura"
ship: "Enviament"
edit: "Editar"
order_not_updated: "La comanda no s'ha pogut actualitzar"
note: "Nota"
first: "Primer"
last: "Últim"

View File

@@ -2988,6 +2988,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
zipcode: Postcode
weight: Weight (per kg)
error_user_destroy_with_orders: "Users with completed orders may not be deleted"
cannot_create_payment_without_payment_methods: "You cannot create a payment for an order without any payment methods defined."
please_define_payment_methods: "Please define some payment methods first."
options: "Options"
actions:

View File

@@ -701,6 +701,10 @@ en_CA:
enable_subscriptions_false: "Disabled"
enable_subscriptions_true: "Enabled"
shopfront_message: "Shopfront Message"
shopfront_message_placeholder: >
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.
shopfront_message_link_tooltip: "Insert/edit link"
shopfront_message_link_prompt: "Please enter a URL to insert"
shopfront_closed_message: "Shopfront Closed Message"
@@ -1107,6 +1111,7 @@ en_CA:
already_ordered:
cart: "cart"
message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open."
failed: "The checkout failed. Please let us know so that we can process your order."
shops:
hubs:
show_closed_shops: "Show closed shops"
@@ -1937,6 +1942,7 @@ en_CA:
tax_category: "Tax Category"
calculator: "Calculator"
calculator_values: "Calculator values"
calculator_settings_warning: "If you are changing the calculator type, you must save first before you can edit the calculator settings"
flat_percent_per_item: "Flat Percent (per item)"
flat_rate_per_item: "Flat Rate (per item)"
flat_rate_per_order: "Flat Rate (per order)"

View File

@@ -2830,6 +2830,8 @@ en_FR:
zipcode: Postcode
weight: Weight (per kg)
error_user_destroy_with_orders: "Users with completed orders may not be deleted"
cannot_create_payment_without_payment_methods: "You cannot create a payment for an order without any payment methods defined."
please_define_payment_methods: "Please define some payment methods first."
options: "Options"
actions:
update: "Update"

View File

@@ -701,6 +701,10 @@ en_GB:
enable_subscriptions_false: "Disabled"
enable_subscriptions_true: "Enabled"
shopfront_message: "Shopfront Message"
shopfront_message_placeholder: >
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.
shopfront_message_link_tooltip: "Insert / edit link"
shopfront_message_link_prompt: "Please enter a URL to insert"
shopfront_closed_message: "Shopfront Closed Message"
@@ -1107,6 +1111,7 @@ en_GB:
already_ordered:
cart: "cart"
message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open."
failed: "The checkout failed. Please let us know so that we can process your order."
shops:
hubs:
show_closed_shops: "Show closed shops"

View File

@@ -2860,6 +2860,8 @@ fr:
zipcode: Code postal
weight: Poids (au kg)
error_user_destroy_with_orders: "Les utilisateurs avec des commandes finalisées pourraient ne pas être supprimés"
cannot_create_payment_without_payment_methods: "Vous ne pouvez pas ajouter un paiement pour une commande sans avoir défini de méthode de paiement au préalable."
please_define_payment_methods: "Il faut que vous définissiez une méthode de paiement avant tout !"
options: "Options"
actions:
update: "Mettre à jour"

View File

@@ -703,6 +703,11 @@ fr_CA:
enable_subscriptions_false: "Désactivée"
enable_subscriptions_true: "Activée"
shopfront_message: "Message d'accueil de la boutique ouverte"
shopfront_message_placeholder: >
Vous pouvez indiquer ici un message de bienvenue ou un message expliquant
les particularités de votre boutique. Ce message s'affiche dans l'onglet
Accueil de votre boutique. Si aucun message n'est affiché, l'onglet
Accueil n'apparaitra pas.
shopfront_message_link_tooltip: "Insérer / modifier un lien"
shopfront_message_link_prompt: "Veuillez entrer l'URL à insérer"
shopfront_closed_message: "Message d'accueil de la boutique fermée"
@@ -1102,10 +1107,13 @@ fr_CA:
destroy_attachment_does_not_exist: "Aucun logo trouvé "
enterprise_promo_image:
destroy_attachment_does_not_exist: "Aucune bannière trouvée "
orders:
failed_to_update: "La mise à jour de la commande échoué"
checkout:
already_ordered:
cart: "panier"
message_html: "Vous avez déjà passé une commande pour ce cycle de vente. Vérifiez votre %{cart} pour voir les produits commandés. Vous pouvez annuler ou modifier votre commande jusqu'à la fermeture du cycle de vente."
failed: "La validation du paiement a échoué. Contactez-nous afin de finaliser votre commande."
shops:
hubs:
show_closed_shops: "Afficher les boutiques fermées"
@@ -1936,6 +1944,7 @@ fr_CA:
tax_category: "Type de taxe "
calculator: "Calculateur"
calculator_values: "Valeurs applicables"
calculator_settings_warning: "Si vous modifiez le type de calculateur, vous devez sauvegarder avant de modifier les paramètres du calculateur."
flat_percent_per_item: "Pourcentage net"
flat_rate_per_item: "Taux forfaitaire par article"
flat_rate_per_order: "Montant fixe par commande"
@@ -2920,6 +2929,7 @@ fr_CA:
capture: "Payée"
ship: "Expédier"
edit: "Modifier"
order_not_updated: "La commande n'a pas pu être mise à jour"
note: "Note"
first: "Début"
last: "Fin"

View File

@@ -701,6 +701,10 @@ nb:
enable_subscriptions_false: "Deaktivert"
enable_subscriptions_true: "Aktivert"
shopfront_message: "Melding Butikk"
shopfront_message_placeholder: >
En valgfri melding for å ønske kunder velkommen og forklare hvordan
de handler hos deg. Hvis tekst legges inn her, vil den vises i en hjemmefane
når kundene først ankommer butikken din.
shopfront_message_link_tooltip: "Sett inn / rediger lenke"
shopfront_message_link_prompt: "Vennligst skriv inn en URL for å sette inn"
shopfront_closed_message: "Melding Butikk Stengt"
@@ -1107,6 +1111,7 @@ nb:
already_ordered:
cart: "handlekurv"
message_html: "Du har allerede en bestilling for denne bestillingsrunden. Sjekk %{cart}en for å se varene du bestilte før. Du kan også avbryte varer så lenge bestillingsrunden er åpen."
failed: "Utsjekk fra kassen mislyktes. Gi oss beskjed slik at vi kan behandle bestillingen din."
shops:
hubs:
show_closed_shops: "Vis stengte butikker"
@@ -2824,6 +2829,8 @@ nb:
zipcode: Postnummer
weight: Vekt (per kg)
error_user_destroy_with_orders: "Brukere med fullførte ordrer kan ikke slettes"
cannot_create_payment_without_payment_methods: "Du kan ikke opprette en betaling for en bestilling uten at det er definert noen betalingsmetoder."
please_define_payment_methods: "Vennligst angi noen betalingsmetoder først."
options: "Valg"
actions:
update: "Oppdater"

View File

@@ -50,7 +50,6 @@ Spree::Core::Engine.routes.draw do
namespace :admin do
get '/search/known_users' => "search#known_users", :as => :search_known_users
get '/search/customers' => 'search#customers', :as => :search_customers
get '/search/customer_addresses' => 'search#customer_addresses', :as => :search_customer_addresses
resources :users
@@ -85,18 +84,32 @@ Spree::Core::Engine.routes.draw do
get '/variants/search', :to => "variants#search", :as => :search_variants
resources :orders do
get :invoice, on: :member
get :print, on: :member
get :print_ticket, on: :member
get :managed, on: :collection
member do
put :fire
get :fire
post :resend
get :invoice
get :print
get :print_ticket
end
collection do
get :managed
resources :invoices, only: [:create, :show] do
get :poll
end
end
resources :adjustments
resources :payments do
member do
put :fire
end
end
resource :customer, :controller => "orders/customer_details"
end
resources :users do

View File

@@ -77,7 +77,7 @@ module OpenFoodNetwork
ba.phone,
order.shipping_method.andand.name,
order.payments.first.andand.payment_method.andand.name,
order.payments.first.amount,
order.payment_total,
OpenFoodNetwork::UserBalanceCalculator.new(order.email, order.distributor).balance]
end
@@ -92,7 +92,7 @@ module OpenFoodNetwork
sa.phone,
order.shipping_method.andand.name,
order.payments.first.andand.payment_method.andand.name,
order.payments.first.amount,
order.payment_total,
OpenFoodNetwork::UserBalanceCalculator.new(order.email, order.distributor).balance,
has_temperature_controlled_items?(order),
order.special_instructions]

View File

@@ -6,25 +6,13 @@ module OpenFoodNetwork
end
def balance
payment_total - completed_order_total
-completed_orders.sum(&:outstanding_balance)
end
private
def completed_order_total
completed_orders.sum(&:total)
end
def payment_total
payments.sum(&:amount)
end
def completed_orders
Spree::Order.where(distributor_id: @distributor, email: @email).complete.not_state(:canceled)
end
def payments
Spree::Payment.where(order_id: completed_orders, state: "completed")
end
end
end

View File

@@ -1,5 +1,5 @@
Spree::Core::ControllerHelpers::Auth.class_eval do
def require_login_then_redirect_to(url)
redirect_to root_path(anchor: "login?after_login=#{url}")
redirect_to main_app.root_path(anchor: "login?after_login=#{url}")
end
end

View File

@@ -9,6 +9,9 @@ namespace :ofn do
sql_delete_from "
spree_inventory_units #{where_order_id_in_orders_to_delete}"
sql_delete_from "
spree_inventory_units
where shipment_id in (select id from spree_shipments #{where_order_id_in_orders_to_delete})"
truncate_adjustments

View File

@@ -51,12 +51,27 @@ module Api
let(:exchange) { order_cycle.exchanges.outgoing.first }
let(:products_relation) { Spree::Product.includes(:variants).where("spree_variants.id": exchange.variants.map(&:id)) }
it "paginates results" do
spree_get :index, exchange_id: exchange.id, page: 1, per_page: 1
before do
stub_const("Api::ExchangeProductsController::DEFAULT_PER_PAGE", 1)
end
expect(json_response["products"].size).to eq 1
expect(json_response["pagination"]["results"]).to eq 2
expect(json_response["pagination"]["pages"]).to eq 2
describe "when a specific page is requested" do
it "returns the requested page with paginated data" do
spree_get :index, exchange_id: exchange.id, page: 1
expect(json_response["products"].size).to eq 1
expect(json_response["pagination"]["results"]).to eq 2
expect(json_response["pagination"]["pages"]).to eq 2
end
end
describe "when no specific page is requested" do
it "returns all results without paginating" do
spree_get :index, exchange_id: exchange.id
expect(json_response["products"].size).to eq 2
expect(json_response["pagination"]).to be nil
end
end
end
end

View File

@@ -13,31 +13,6 @@ describe Spree::Admin::BaseController, type: :controller do
expect(response).to redirect_to root_path(anchor: "login?after_login=/spree/admin/base")
end
describe "displaying error messages for active distributors not ready for checkout" do
it "generates an error message when there is one distributor" do
distributor = double(:distributor, name: 'My Hub')
expect(controller.
send(:active_distributors_not_ready_for_checkout_message, [distributor])).
to eq(
"The hub My Hub is listed in an active order cycle, " \
"but does not have valid shipping and payment methods. " \
"Until you set these up, customers will not be able to shop at this hub."
)
end
it "generates an error message when there are several distributors" do
d1 = double(:distributor, name: 'Hub One')
d2 = double(:distributor, name: 'Hub Two')
expect(controller.
send(:active_distributors_not_ready_for_checkout_message, [d1, d2])).
to eq(
"The hubs Hub One, Hub Two are listed in an active order cycle, " \
"but do not have valid shipping and payment methods. " \
"Until you set these up, customers will not be able to shop at these hubs."
)
end
end
describe "rendering as json ActiveModelSerializer" do
context "when data is an object" do
let(:data) { { attr: 'value' } }

View File

@@ -369,7 +369,12 @@ feature '
find(:css, "tags-input .tags input").set "wholesale\n"
end
page.all("table.exchanges tr.distributor td.products").each(&:click)
exchange_rows = page.all("table.exchanges tbody")
exchange_rows.each do |exchange_row|
exchange_row.find("td.products").click
# Wait for the products panel to be visible.
expect(exchange_row).to have_selector "tr", count: 2
end
uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}"
check "order_cycle_outgoing_exchange_2_variants_#{v2.id}"

View File

@@ -69,7 +69,7 @@ module OpenFoodNetwork
create(:order_with_totals_and_distribution,
user: user1, distributor: hub1,
completed_at: 1.day.ago, state: "canceled")
} # total=10
} # total=13 (10 + 3 shipping fee)
let!(:p4) {
create(:payment, order: o4, amount: 20.00,
state: "completed")
@@ -79,6 +79,62 @@ module OpenFoodNetwork
expect(UserBalanceCalculator.new(o4.email, hub1).balance).to eq(-9) # = 15 + 2 - 13 - 13
end
end
context "with void payments" do
let!(:o4) {
create(:order_with_totals_and_distribution,
user: user1, distributor: hub1,
completed_at: 1.day.ago)
} # total=13 (10 + 3 shipping fee)
let!(:p4) {
create(:payment, order: o4, amount: 20.00,
state: "void")
}
it "does not include void in the balance" do
expect(UserBalanceCalculator.new(o4.email, hub1).balance).to eq(-22) # = 15 + 2 - 13 - 13 - 10
end
end
context "with invalid payments" do
let!(:o4) {
create(:order_with_totals_and_distribution,
user: user1, distributor: hub1,
completed_at: 1.day.ago)
} # total=13 (10 + 3 shipping fee)
let!(:p4) {
create(:payment, order: o4, amount: 20.00,
state: "invalid")
}
it "does not include invalid payments in the balance" do
expect(UserBalanceCalculator.new(o4.email, hub1).balance).to eq(-22) # = 15 + 2 - 13 - 13 - 10
end
end
context "with multiple payments on single order" do
let!(:o4) {
create(:order_with_totals_and_distribution,
user: user1, distributor: hub1,
completed_at: 1.day.ago)
} # total=13 (10 + 3 shipping fee)
let!(:p4) {
create(:payment, order: o4, amount: 4.00,
state: "completed")
}
let!(:p5) {
create(:payment, order: o4, amount: 5.00,
state: "completed")
}
let!(:p6) {
create(:payment, order: o4, amount: 6.00,
state: "completed")
}
it "includes orders with multiple payments in the balance" do
expect(UserBalanceCalculator.new(o4.email, hub1).balance).to eq(-7) # = 15 + 2 + 4 + 5 + 6 - 13 - 13 - 10
end
end
end
end
end

View File

@@ -26,4 +26,38 @@ describe ExchangeProductsRenderer do
end
end
end
describe "#exchange_variants" do
describe "for an incoming exchange" do
it "loads variants" do
exchange = order_cycle.exchanges.incoming.first
variants = renderer.exchange_variants(true, exchange.sender)
expect(variants.first.product.supplier.name).to eq exchange.variants.first.product.supplier.name
end
describe "when OC is showing only the coordinators inventory" do
let(:exchange_with_visible_variant) { order_cycle.exchanges.incoming.second }
let(:exchange_with_hidden_variant) { order_cycle.exchanges.incoming.first }
let!(:visible_inventory_item) { create(:inventory_item, enterprise: order_cycle.coordinator, variant: exchange_with_visible_variant.variants.first, visible: true) }
let!(:hidden_inventory_item) { create(:inventory_item, enterprise: order_cycle.coordinator, variant: exchange_with_hidden_variant.variants.first, visible: false) }
before do
order_cycle.prefers_product_selection_from_coordinator_inventory_only = true
end
it "renders visible inventory variants" do
variants = renderer.exchange_variants(true, exchange_with_visible_variant.sender)
expect(variants.size).to eq 1
end
it "does not render hidden inventory variants" do
variants = renderer.exchange_variants(true, exchange_with_hidden_variant.sender)
expect(variants.size).to eq 0
end
end
end
end
end

View File

@@ -0,0 +1,31 @@
require 'spec_helper'
describe OrderCycleWarning do
let(:user) { create(:user) }
let(:subject) { OrderCycleWarning }
let!(:distributor) { create(:enterprise, owner: user) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
describe "checking if user's managed order cycles have distributors not ready for checkout" do
context "with an invalid distributor" do
it "returns a warning message" do
expect(subject.new(user).call).to eq(
I18n.t(:active_distributors_not_ready_for_checkout_message_singular,
distributor_names: distributor.name)
)
end
end
context "with a valid distributor" do
let!(:distributor) {
create(:distributor_enterprise,
shipping_methods: [create(:shipping_method)],
payment_methods: [create(:payment_method)])
}
it "returns nil" do
expect(subject.new(user).call).to eq nil
end
end
end
end

41
vendor/assets/javascripts/equalize.js vendored Normal file
View File

@@ -0,0 +1,41 @@
/**
* equalize.js
* Author & copyright (c) 2012: Tim Svensen
* Dual MIT & GPL license
*
* Page: http://tsvensen.github.com/equalize.js
* Repo: https://github.com/tsvensen/equalize.js/
*
* The jQuery plugin for equalizing the height or width of elements.
*
* Equalize will accept any of the jQuery Dimension methods:
* height, outerHeight, innerHeight,
* width, outerWidth, innerWidth.
*
* EXAMPLE
* $('.parent').equalize(); // defaults to 'height'
* $('.parent').equalize('width'); // equalize the widths
*/
(function($, window, document, undefined) {
$.fn.equalize = function(equalize) {
var $containers = this, // this is the jQuery object
equalize = equalize || 'height',
type = (equalize.indexOf('eight') > 0) ? 'height' : 'width';
if (!$.isFunction($.fn[equalize])) { return false; }
return $containers.each(function() {
var $children = $(this).children(),
max = 0; // reset for each container
$children.each(function() {
var value = $(this)[equalize](); // call height(), outerHeight(), etc.
if (value > max) { max = value; } // update max
});
$children.css(type, max +'px'); // add CSS to children
});
};
}(jQuery, window, document));

1920
vendor/assets/javascripts/handlebars.js vendored Normal file

File diff suppressed because it is too large Load Diff

41
vendor/assets/javascripts/jquery.cookie.js vendored Executable file
View File

@@ -0,0 +1,41 @@
/**
* jQuery Cookie plugin
*
* Copyright (c) 2010 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
jQuery.cookie = function (key, value, options) {
// key and at least value given, set cookie...
if (arguments.length > 1 && String(value) !== "[object Object]") {
options = jQuery.extend({}, options);
if (value === null || value === undefined) {
options.expires = -1;
}
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
value = String(value);
return (document.cookie = [
encodeURIComponent(key), '=',
options.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// key and possibly options given, get cookie...
options = value || {};
var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
};

View File

@@ -0,0 +1,141 @@
/**
* jQuery Horizontal Navigation 1.0
* https://github.com/sebnitu/horizontalNav
*
* By Sebastian Nitu - Copyright 2012 - All rights reserved
* Author URL: http://sebnitu.com
*/
(function($) {
$.fn.horizontalNav = function(options) {
// Extend our default options with those provided.
var opts = $.extend({}, $.fn.horizontalNav.defaults, options);
return this.each(function () {
// Save our object
var $this = $(this);
// Build element specific options
// This lets me access options with this syntax: o.optionName
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
// Save the wrapper. The wrapper is the element that
// we figure out what the full width should be
if ($this.is('ul')) {
var ul_wrap = $this.parent();
} else {
var ul_wrap = $this;
}
// let's append a clearfixing element to the ul wrapper
ul_wrap.css({ 'zoom' : '1' }).append('<div class="clearHorizontalNav">');
$('.clearHorizontalNav').css({
'display' : 'block',
'overflow' : 'hidden',
'visibility' : 'hidden',
'width' : 0,
'height' : 0,
'clear' : 'both'
});
// Grab elements we'll need and add some default styles
var ul = $this.is('ul') ? $this : ul_wrap.find('> ul'), // The unordered list element
li = ul.find('> li'), // All list items
li_last = li.last(), // Last list item
li_count = li.size(), // The number of navigation elements
li_a = li.find('> a'); // Remove padding from the links
// If set to responsive, re-construct after every browser resize
if ( o.responsive === true ) {
// Only need to do this for IE7 and below
// or if we set tableDisplay to false
if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
resizeTrigger( _construct, o.responsiveDelay );
}
}
// Initiate the plugin
_construct();
// Returns the true inner width of an element
// Essentially it's the inner width without padding.
function trueInnerWidth(element) {
return element.innerWidth() - (
parseInt(element.css('padding-left')) + parseInt(element.css('padding-right'))
);
}
// Call funcion on browser resize
function resizeTrigger(callback, delay) {
// Delay before function is called
delay = delay || 100;
// Call function on resize
var resizeTimer;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
callback();
}, delay);
});
}
// The heavy lifting of this plugin. This is where we
// find and set the appropriate widths for list items
function _construct() {
if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
// IE7 doesn't support the "display: table" method
// so we need to do it the hard way.
// Add some styles
ul.css({ 'float' : 'left' });
li.css({ 'float' : 'left', 'width' : 'auto' });
li_a.css({ 'padding-left' : 0, 'padding-right' : 0 });
// Grabbing widths and doing some math
var ul_width = trueInnerWidth(ul),
ul_width_outer = ul.outerWidth(true),
ul_width_extra = ul_width_outer - ul_width,
full_width = trueInnerWidth(ul_wrap),
extra_width = (full_width - ul_width_extra) - ul_width,
li_padding = Math.floor( extra_width / li_count );
// Cycle through the list items and give them widths
li.each(function(index) {
var li_width = trueInnerWidth( $(this) );
$(this).css({ 'width' : (li_width + li_padding) + 'px' });
});
// Get the leftover pixels after we set every itms width
var li_last_width = trueInnerWidth(li_last) + ( (full_width - ul_width_extra) - trueInnerWidth(ul) );
// I hate to do this but for some reason Firefox (v13.0) and IE are always
// one pixel off when rendering. So this is a quick fix for that.
if ($.browser.mozilla || $.browser.msie) {
li_last_width = li_last_width - 1;
}
// Add the leftovers to the last navigation item
li_last.css({ 'width' : li_last_width + 'px' });
} else {
// Every modern browser supports the "display: table" method
// so this is the best way to do it for them.
ul.css({ 'display' : 'table', 'float' : 'none', 'width' : '100%' });
li.css({ 'display' : 'table-cell', 'float' : 'none' });
}
}
}); // @end of return this.each()
};
$.fn.horizontalNav.defaults = {
responsive : true,
responsiveDelay : 100,
tableDisplay : true
};
})(jQuery);

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