Compare commits

...

230 Commits

Author SHA1 Message Date
Matt-Yorkley
d7b130d293 Update all locales with the latest Transifex translations 2021-02-02 12:23:25 +00:00
Matt-Yorkley
617d914835 Merge pull request #6788 from jibees/6783-align-new-product-form
Use full width for "new product" form
2021-02-02 13:11:12 +01:00
Matt-Yorkley
b1349306f9 Merge pull request #6787 from jibees/6784-align-items-on-shipping-and-payment-methods-forms
Align items on shipping and payment methods forms
2021-02-02 13:08:28 +01:00
Pau Pérez Fabregat
9db9d2d138 Merge pull request #6786 from jibees/6781-align-packages-selection-on-registration
Package selection: use "columns" system to display 3 items on same line
2021-02-01 21:06:00 +01:00
Pau Pérez Fabregat
bcbd1ce8bc Merge pull request #6790 from openfoodfoundation/transifex
Transifex
2021-02-01 21:05:16 +01:00
Jean-Baptiste Bellet
370a108b3d Add stripe account owner selector as a new row
Otherwise, not displayed.
2021-02-01 18:00:28 +01:00
Transifex-Openfoodnetwork
38dde5e063 Updating translations for config/locales/es.yml 2021-02-02 03:04:56 +11:00
Transifex-Openfoodnetwork
fe79186bec Updating translations for config/locales/ca.yml 2021-02-02 03:03:33 +11:00
Pau Pérez Fabregat
d5413224b7 Merge pull request #6769 from openfoodfoundation/transifex
Transifex
2021-02-01 16:57:45 +01:00
Pau Pérez Fabregat
90466e19dc Merge pull request #6776 from Matt-Yorkley/dead-argument
Dead code: superfluous argument in CartService#populate
2021-02-01 16:49:02 +01:00
Jean-Baptiste Bellet
acbe0faa1e use full width for new product form
- use the sixteen full width: eight + eight or eight + four + four, ...
2021-02-01 16:35:55 +01:00
Pau Pérez Fabregat
1420c43a0a Merge pull request #6778 from openfoodfoundation/dependabot/bundler/database_cleaner-1.99.0
Bump database_cleaner from 1.8.5 to 1.99.0
2021-02-01 16:06:51 +01:00
Jean-Baptiste Bellet
adf67475be special aligment for stripe form 2021-02-01 15:39:51 +01:00
Jean-Baptiste Bellet
c44ce85f30 alignment for specific provider settings form in payment method 2021-02-01 15:31:39 +01:00
Jean-Baptiste Bellet
46e6bc6179 align payment method form
- enlarge the form to be "full size" (four + twelve = 16 columns)
2021-02-01 15:18:45 +01:00
Jean-Baptiste Bellet
e57acd3163 align shipping method form (editing)
- use `row` and no more `field` to be consistent
- enlarge the form to be "full size" (four + twelve = 16 columns)
- enlarge "hubs sidebar"
2021-02-01 15:11:47 +01:00
Pau Pérez Fabregat
0878db70fe Merge pull request #6780 from openfoodfoundation/dependabot/bundler/webmock-3.11.2
Bump webmock from 3.11.1 to 3.11.2
2021-02-01 14:57:45 +01:00
Pau Pérez Fabregat
640681d664 Merge pull request #6719 from coopdevs/stop-using-spree-req-helpers-in-users-controller-spec
[Rails 5] Stop using deprecated req. helpers in users specs
2021-02-01 14:56:28 +01:00
Jean-Baptiste Bellet
b7e23a7401 user "columns" system to display 3 items on same line 2021-02-01 14:15:50 +01:00
dependabot[bot]
863a7ec688 Bump webmock from 3.11.1 to 3.11.2
Bumps [webmock](https://github.com/bblimke/webmock) from 3.11.1 to 3.11.2.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.11.1...v3.11.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 06:29:04 +00:00
dependabot[bot]
e9c789a459 Bump database_cleaner from 1.8.5 to 1.99.0
Bumps [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) from 1.8.5 to 1.99.0.
- [Release notes](https://github.com/DatabaseCleaner/database_cleaner/releases)
- [Changelog](https://github.com/DatabaseCleaner/database_cleaner/blob/master/History.rdoc)
- [Commits](https://github.com/DatabaseCleaner/database_cleaner/compare/v1.8.5...v1.99.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 06:20:51 +00:00
Matt-Yorkley
3da21ce2bd Add historical note to CartService
Useful background when applying upstream fixes from Spree::OrderPopulator, and means grepping for OrderPopulator doesn't come up empty.
2021-01-30 16:57:05 +00:00
Matt-Yorkley
d39468013e Clarify CartService#populate arguments
This method doesn't use :products anywhere, it doesn't need to be passed in as part of the argument.
2021-01-30 16:52:59 +00:00
Andy Brett
87bce505ec Merge pull request #6765 from openfoodfoundation/dependabot/bundler/rack-mini-profiler-2.3.1
Bump rack-mini-profiler from 2.3.0 to 2.3.1
2021-01-29 20:04:13 -08:00
Andy Brett
fb5668afa6 Merge pull request #6764 from openfoodfoundation/dependabot/bundler/rubocop-1.9.0
Bump rubocop from 1.8.1 to 1.9.0
2021-01-29 20:03:56 -08:00
Transifex-Openfoodnetwork
8f163cf5b5 Updating translations for config/locales/en_CA.yml 2021-01-30 06:08:22 +11:00
Matt-Yorkley
b97734d918 Merge pull request #6720 from cillian/cancel-email-for-shop
When an order is cancelled by a customer send an email to the shop.
2021-01-29 18:30:28 +01:00
Cillian O'Ruanaidh
0cfd7de8a8 Fix the link to the order in the order cancelled email. 2021-01-29 14:14:11 +00:00
Matt-Yorkley
d03be3b8dc Update all locales with the latest Transifex translations 2021-01-29 11:42:09 +00:00
Matt-Yorkley
e4d7e03903 Merge pull request #6740 from Matt-Yorkley/carts-cleanup
Remove old abandoned carts
2021-01-29 12:32:16 +01:00
Cillian O'Ruanaidh
899dffec96 When an order is cancelled by a customer send an email to the shop.
Fixes #6435 i.e. If the customer paid for their order by Stripe/Paypal then the Enterprise needs to know that the order was cancelled in order to arrange a refund.  Refunds are not automatically processed when an order is cancelled.

This will send a very basic email to the shop, it only includes a link to view the cancelled order in the admin area initially.

I created a CustomerOrderCancellation object here because orders can be cancelled in two ways (1) by the customer, so an email should be sent to the shop. (2) by the shop, so an email doesn't need to be sent. However the code for cancelling order happens in Order#cancel via the state machine. Rather than passing some sort of parameter into #cancel to indicate whether it is a customer or shop cancelled order it might be clearer to have a CustomerOrderCancellation object, there could be other differences between customer or shop cancelled orders in future maybe.
2021-01-29 11:23:05 +00:00
Pau Pérez Fabregat
818dfc0399 Merge pull request #6751 from jibees/4971-profile-tabs-back-forward-buttons
Make user's profile tabs aware of location change event
2021-01-29 10:01:58 +01:00
dependabot[bot]
32761baa68 Bump rack-mini-profiler from 2.3.0 to 2.3.1
Bumps [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) from 2.3.0 to 2.3.1.
- [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/v2.3.0...v2.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-29 05:38:38 +00:00
dependabot[bot]
36380414a4 Bump rubocop from 1.8.1 to 1.9.0
Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/rubocop-hq/rubocop/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.8.1...v1.9.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-29 05:35:09 +00:00
Matt-Yorkley
9791287712 Run data cleanup job at 4:30am 2021-01-28 21:51:50 +00:00
Matt-Yorkley
4f7c8062a1 Create class to map join table and simplify code 2021-01-28 21:51:50 +00:00
Matt-Yorkley
d502320b14 Enable cascading deletes 2021-01-28 21:51:47 +00:00
Andy Brett
6fea2dbe46 Merge pull request #6760 from openfoodfoundation/dependabot/bundler/rswag-2.3.2
Bump rswag from 2.3.1 to 2.3.2
2021-01-28 13:11:33 -08:00
Pau Pérez Fabregat
45573dc522 Merge pull request #6753 from jibees/6746-epic-fullwdith-admin-layout-quick-wins
FullWidth admin layout quick wins
2021-01-28 21:10:06 +01:00
Pau Pérez Fabregat
0a1d4873e3 Merge pull request #6736 from jibees/6707-remove-anchor-jump
Remove anchor in URL so avoid browser scrolling to the top of the page when editing order
2021-01-28 21:09:08 +01:00
Pau Pérez Fabregat
abe2cb9fac Merge pull request #6728 from emclaughlin1215/add_tax_rates_i18n_key
Adding a tax_rates key to en.yml to match the key used everywhere.
2021-01-28 21:04:22 +01:00
Matt-Yorkley
dcbe9c4011 Merge pull request #6756 from andrewpbrett/fix-sca-regression
Fix SCA regression
2021-01-28 19:45:08 +01:00
Pau Pérez Fabregat
d4f6f903c9 Merge pull request #6759 from openfoodfoundation/dependabot/bundler/ddtrace-0.45.0
Bump ddtrace from 0.44.0 to 0.45.0
2021-01-28 16:40:29 +01:00
Pau Pérez Fabregat
6cc19ac6d5 Merge pull request #6726 from filipefurtad0/shipping_methods_spec_pending_fix
Fixes a pending test case and improves the spec run time
2021-01-28 16:39:39 +01:00
Pau Pérez Fabregat
2668747237 Merge pull request #6723 from luisramos0/spree_routes_2
Move spree orders routes out of the spree engine routes into the main app routes
2021-01-28 16:31:49 +01:00
Matt-Yorkley
57f429db58 Merge pull request #6606 from mkllnk/6081-add-to-cart
Add quantity input to shop front
2021-01-28 15:51:08 +01:00
Matt-Yorkley
de530c2a46 Merge pull request #6750 from mkllnk/6749-i18n-in-assets
Use JS I18n function in assets to avoid parsing error
2021-01-28 15:50:22 +01:00
Jean-Baptiste Bellet
553053bad1 use find instead of click_link because <a> element no longer has href attribute
- click_link only looks for <a> element with href attribute
 - Use a regexp for a case-insensitive search (as CSS use a uppercase transform)
2021-01-28 09:45:24 +01:00
dependabot[bot]
55cddbbc1a Bump rswag from 2.3.1 to 2.3.2
Bumps [rswag](https://github.com/rswag/rswag) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/rswag/rswag/releases)
- [Changelog](https://github.com/rswag/rswag/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rswag/rswag/compare/2.3.1...2.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-28 05:46:19 +00:00
dependabot[bot]
0a128d83d0 Bump ddtrace from 0.44.0 to 0.45.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.44.0 to 0.45.0.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.44.0...v0.45.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-28 05:41:53 +00:00
Luis Ramos
7cea4138c2 Fix spec setup 2021-01-27 22:44:52 +00:00
Matt-Yorkley
85c489d303 Ignore carts with failed payments in cleanup 2021-01-27 22:40:23 +00:00
Matt-Yorkley
e6c59fbd96 Update data retention periods
Sessions and cart data are removed if older than 3 months, instead of 6.
2021-01-27 22:40:23 +00:00
Matt-Yorkley
3fddaba4bf Extract private methods 2021-01-27 22:40:23 +00:00
Matt-Yorkley
0a88712926 Clear orphaned records in join table spree_option_value_line_items 2021-01-27 22:40:23 +00:00
Matt-Yorkley
4230d46a06 Remove carts older than 6 months 2021-01-27 22:40:23 +00:00
Maikel
8a757812e7 Merge pull request #6735 from coopdevs/enable-mail-delivery-always
Enable mail delivery always
2021-01-28 09:31:12 +11:00
Matt-Yorkley
f49355e5d3 Merge pull request #6724 from Matt-Yorkley/remove-price-adjustments
Delete dead code Spree::Order#price_adjustments
2021-01-27 23:29:38 +01:00
Luis Ramos
12dac82b8a Delete dead route 2021-01-27 22:08:06 +00:00
Luis Ramos
c604f4c0c6 Remove dead endpoint 2021-01-27 22:05:04 +00:00
Jean-Baptiste Bellet
bb21543ae0 remove useless brackets 2021-01-27 21:35:55 +01:00
Pau Pérez Fabregat
c3897dd3df Merge pull request #6643 from coopdevs/customer-balance-frontoffice
Customer balance frontoffice
2021-01-27 19:55:56 +01:00
Andy Brett
e6e20309aa Revert "don't try to process a payment if it's pending auth"
This reverts commit bba9e55006.
2021-01-27 08:06:20 -08:00
Jean-Baptiste Bellet
9b1c0e6806 avoid inserting div in the columns grid system
it break the css selectors (as :first-child, ...)

fixe: #6747
2021-01-27 15:05:48 +01:00
Jean-Baptiste Bellet
c5edd74d05 remove useless wrapper and align items (both right and left)
Fixe: #6747
2021-01-27 15:04:45 +01:00
Jean-Baptiste Bellet
a6fba8a065 Put content into grid columns system
Two columns of three + a ten column rather than empty column + content outside column system

correct #6745
2021-01-27 14:40:50 +01:00
Matt-Yorkley
7525620d6c Merge pull request #6070 from Matt-Yorkley/responsive-admin
Full-width admin layout
2021-01-27 12:45:06 +01:00
Matt-Yorkley
feca9e7838 Improve hubs sidebar in payment methods edit 2021-01-27 11:38:01 +00:00
Matt-Yorkley
398b4a09a1 Improve enterprise fees calculator fields 2021-01-27 11:38:01 +00:00
Matt-Yorkley
366588de95 Improve <legend> element centering 2021-01-27 11:38:01 +00:00
Matt-Yorkley
b5ab6cbf69 Improve button spacing in enterprises submenu 2021-01-27 11:38:01 +00:00
Matt-Yorkley
e762c1d02f Adjust buttons layout on enterprise shipping and payment methods page 2021-01-27 11:38:01 +00:00
Matt-Yorkley
f72cd6222f Adjust variant columns in inventory pages 2021-01-27 11:38:01 +00:00
Matt-Yorkley
79316560d6 Remove skeleton.css from /vendor/assets
This was recently imported from spree_core
2021-01-27 11:38:01 +00:00
Matt-Yorkley
539ff228e0 Adjust form layout on order cycle edit form (simple view) 2021-01-27 11:38:01 +00:00
Matt-Yorkley
afdb5e8c6f Add a max-width of 1400px for huge monitors (1600px or more) 2021-01-27 11:38:01 +00:00
Matt-Yorkley
d40ec2d63c Adjust order cycle edit form layout 2021-01-27 11:38:01 +00:00
Matt-Yorkley
0d99656b1b Adjust dashboard layout 2021-01-27 11:38:01 +00:00
Matt-Yorkley
52cfa0a103 Remove old Javascript hacks for the main menu and replace with flex-based CSS properties 2021-01-27 11:38:01 +00:00
Matt-Yorkley
d886b1dcea Tweak some styles and layout classes 2021-01-27 11:38:01 +00:00
Matt-Yorkley
29e74017a6 Replace fixed 960 grid 2021-01-27 11:38:01 +00:00
Pau Pérez Fabregat
9fe2cb1a30 Merge pull request #6681 from luisramos0/css
[Rails 5] Make the app work in rails 5
2021-01-27 12:08:27 +01:00
Jean-Baptiste Bellet
0242e1a0c9 avoid unnecessary javascript:void(0) 2021-01-27 10:26:05 +01:00
Jean-Baptiste Bellet
48c667d2dd add onLocationChangeSuccess event handler
watch this event and toggle class if needed.
2021-01-27 10:25:05 +01:00
Maikel Linke
06983c4dc7 Use JS I18n function in asset, avoid parsing error
A deployment to the French server failed because a translation contained
an apostrophe `'` and we were rendering it without escaping in
Javascript. We don't have that problem and avoid other issues by using
the javascript translate function. That way the error message is
translated in the browser with the user's language and we don't have to
do any additional escaping.
2021-01-27 14:12:25 +11:00
Maikel Linke
0393e902c4 Update translations from Transifex 2021-01-27 08:28:37 +11:00
Luis Ramos
ab135e30c0 Fix order_path in stripe connect spec 2021-01-25 20:25:13 +00:00
Luis Ramos
11fdbe5e9f Switch spree from namespace to scope in routes so helpers like order_path keep working 2021-01-25 20:01:08 +00:00
Luis Ramos
34ae3ca24d Replace spree_order_url with order_url so we can use scope in the routes file 2021-01-25 19:56:25 +00:00
Luis Ramos
f3da7afd55 Replace spree_order_path with order_path so we can use scope in the routes file 2021-01-25 19:55:42 +00:00
Luis Ramos
dbef7b60a6 Adapt spec to new orders url 2021-01-25 19:52:48 +00:00
Luis Ramos
21f48b52c8 Fix cancel order route 2021-01-25 19:52:48 +00:00
Luis Ramos
592474189d Fix order route in paypal controller 2021-01-25 19:52:48 +00:00
Luis Ramos
d52661fe0a Fix admin order form to use admin orders route 2021-01-25 19:52:48 +00:00
Luis Ramos
00fcb89af1 Fix order_url routes in a few places 2021-01-25 19:52:48 +00:00
Luis Ramos
dd4ca8a8e6 Fix order path in subs emailer view 2021-01-25 19:52:48 +00:00
Luis Ramos
f25e06ddb6 Fix order path route in payments controller 2021-01-25 19:52:48 +00:00
Luis Ramos
3813b80615 Adapt order path route in order details view 2021-01-25 19:52:48 +00:00
Luis Ramos
88c21ef82e Fix order path 2021-01-25 19:52:48 +00:00
Luis Ramos
60c3557c2d Fix checkout form order path 2021-01-25 19:52:48 +00:00
Luis Ramos
6e7ee02191 Change spree.order_path to spree_order_path everywhere 2021-01-25 19:52:48 +00:00
Luis Ramos
e98d97ae0d Move spree orders routes out of the spree engine routes into the main app routes, still inside the spree namespace 2021-01-25 19:52:48 +00:00
Pau Pérez Fabregat
10a60b1f72 Merge pull request #6729 from openfoodfoundation/dependabot/bundler/shoulda-matchers-4.5.1
Bump shoulda-matchers from 4.5.0 to 4.5.1
2021-01-25 16:34:13 +01:00
Jean-Baptiste Bellet
686dcaaa1b remove '#' as path for link_to
Adding '#' to the current url makes most of browser jump to the top of the page. Avoid this by deleting this added # (meaningless) and replacing it by empty string.
2021-01-25 16:09:12 +01:00
Pau Perez
5677c86f9b Remove enable_mail_delivery preference from DB
It's no longer used.
2021-01-25 13:34:13 +01:00
Pau Perez
4a5869b60c Remove ability to toggle mail delivery
OFN requires mails to work so there's no point in having this
conditional with the maintenance cost it entails.
2021-01-25 13:27:44 +01:00
Pau Pérez Fabregat
efd2c81877 Merge pull request #6722 from luisramos0/spree_routes
Replace spree_get with simple get
2021-01-25 09:12:51 +01:00
dependabot[bot]
70bd81430d Bump shoulda-matchers from 4.5.0 to 4.5.1
Bumps [shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/thoughtbot/shoulda-matchers/releases)
- [Changelog](https://github.com/thoughtbot/shoulda-matchers/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thoughtbot/shoulda-matchers/compare/v4.5.0...v4.5.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-25 06:13:04 +00:00
filipefurtad0
c5c9e803ab Fixes a pending test case and improves the spec run time 2021-01-24 13:32:04 +00:00
Erin McLaughlin
609db647bb Adding a tax_rates key to en.yml to match the key used everywhere. 2021-01-23 22:07:07 -05:00
Matt-Yorkley
0247386f82 Delete dead code Spree::Order#price_adjustments
This method returns the same thing as the Spree::Order#line_items_adjustments scope, but in a slightly less useful format (an array instead of a relation). The method's name is also totally inaccurate, as currently the only adjustments that appear on line items are tax adjustments for inclusive tax rates, which by definition have no effect on the price whatsoever...
2021-01-23 00:05:56 +00:00
Luis Ramos
1643636d43 Replace spree_get with simple call to get 2021-01-22 22:32:48 +00:00
Luis Ramos
d6a53cb84f Replace spree_get with simple call to get 2021-01-22 22:30:11 +00:00
Luis Ramos
94275eedfb Replace spree_get with simple call to get 2021-01-22 22:28:53 +00:00
Luis Ramos
59e900826e Replace spree_get with get
Spree helper method is not needed
2021-01-22 22:22:50 +00:00
Luis Ramos
be9ee376a3 Use get instead of spree_get
spree_get is not needed in these cases
2021-01-22 22:20:52 +00:00
Pau Perez
cc2e46890e Stop using deprecated req. helpers in users specs
This removes the following two deprecation warnings that we are getting
by millions (the two for each controller action test):

```
DEPRECATION WARNING: You are trying to generate the URL for a named route called "main_app" but no such route was found. In the future, this will result in an `ActionController::UrlGenerationError` exception. (called from process_action_with_route at /usr/
src/app/spec/support/controller_requests_helper.rb:49)
DEPRECATION WARNING: Passing the `use_route` option in functional tests are deprecated. Support for this option in the `process` method (and the related `get`, `head`, `post`, `patch`, `put` and `delete` helpers) will be removed in the next version without
 replacement. Functional tests are essentially unit tests for controllers and they should not require knowledge to how the application's routes are configured. Instead, you should explicitly pass the appropiate params to the `process` method. Previously th
e engines guide also contained an incorrect example that recommended using this option to test an engine's controllers within the dummy application. That recommendation was incorrect and has since been corrected. Instead, you should override the `@routes`
variable in the test case with `Foo::Engine.routes`. See the updated engines guide for details. (called from process_action_with_route at /usr/src/app/spec/support/controller_requests_helper.rb:49)
```

It slows down our test suite and clutters the output a lot. As per my
investigation, this is something that arose in
https://github.com/rails/rails/pull/17453 and addressed in
https://github.com/rails/rails/pull/17725. TL;DR: Engines need to define
their routes in controller tests as shown in
https://github.com/discourse/discourse/pull/3011.

This, however, revealed a much complex reality in our case. We're still
using a `Spree::Core::Engine` with its own routes at
`Spree::Core::Engine.routes`. So we can't skip defining `routes { }` for
each of its controllers unless we merge this engine into our app, but
that's going to require more effort. What could that entail in
https://github.com/openfoodfoundation/openfoodnetwork/compare/master...coopdevs:move-users-to-app-routes.

To make it even worse, note that we override spree's core routes from
our own, resulting in a controller whose actions are being served from
routes defined in either `config/routes.rb` or `config/spree/routes.rb`
🙈.
2021-01-22 11:42:17 +01:00
Maikel Linke
46d23f1f8a Fit bulk buy modal on small mobile screens
On some Iphones the modal wouldn't fit and there were line breaks
between the input fields. The inputs are now stacked vertically on small
screens so that there is still plenty of space for large numbers in the
quantity fields.
2021-01-22 16:44:52 +11:00
Maikel Linke
4ab48c4b85 Initialise valid quantities when choosing OCs
Line items were initialised with undefined quantities which makes it
impossible to distinguish between the initial unset quantity and a user
entering an invalid quantity. When a user changed order cycles, all
quantities are reset and the UI displayed the quantity modifier buttons
instead of the Add button.

Initialising with the valid quantity 0 helps us to display the Add
button in that situation.
2021-01-22 16:42:21 +11:00
Maikel Linke
bd731267ec Allow user to get maximum available quantity
When the user entered a number beyond the stock level, the browser was
correcting that to the max number which is very helpful. But Angular was
setting the model to undefined which removes the item from the cart.

Deactivating Angular's max behaviour let's us set the value ourselves
which is then used in the cart.
2021-01-22 16:42:21 +11:00
Maikel Linke
35b4e8c4d1 Allow editing of invalid quantities
If the user entered an invalid quantity, Angular set the model to
undefined and we removed the input field to show the add button. That
makes it impossible for a user to see what the maximum quantity to enter
would be. For example:

- The variant has a stock level of 5.
- The user enters 7.
- Angular sets it to undefined.
- The input field disappears.
- The user is startled and doesn't know how to proceed.

But now we hide the input only if it's deliberately set to zero.
2021-01-22 16:42:21 +11:00
Maikel Linke
0166400b03 Guard against invalid quantity input
The user can now type anything into the quantity field and some of it
may not be valid. These safe guards ensure that the buttons still work
even if the quantity is undefined or out of range.

Angular guards against the value being out of range but that has other
side-effects. We want to be able to de-activate some of Angular's
behaviour.
2021-01-22 16:42:20 +11:00
Maikel Linke
cf2a105b2a Prevent line breaks within plus/minus buttons 2021-01-22 16:42:20 +11:00
Maikel Linke
688be46b10 Avoid horizontal scroll bar on small screens 2021-01-22 16:42:20 +11:00
Maikel Linke
3558d01fce Give the new input fields more space
The additional input needs more space and we are adjusting columns here.
2021-01-22 16:42:20 +11:00
Maikel Linke
66423a1ec3 Add quantity input to shop front product list
Adding bigger quantities can now be done via an input field instead of
clicking a thousand times.

The add-button has been widened to match the new space requirements.
2021-01-22 16:42:20 +11:00
Maikel Linke
e5506df5ea Replace bulk quantity label with input field
Enable the user to enter a number directly.
2021-01-22 16:42:20 +11:00
Maikel Linke
af918e63ee Change bulk quantities on any quantity change
This prepares for changing the quantity with an input field. It also
applies if the quantity is changed after an ajax request.
2021-01-22 16:42:20 +11:00
Maikel Linke
7add9247d5 Update translations from Transifex 2021-01-22 11:58:27 +11:00
Maikel
d5376ce29d Merge pull request #6702 from openfoodfoundation/transifex
Transifex
2021-01-22 11:54:57 +11:00
Andy Brett
84689c43be Merge pull request #6534 from andrewpbrett/sca-emails
Send email when SCA authorization is required for admin payment
2021-01-21 13:53:35 -08:00
Andy Brett
91d5b55376 Merge pull request #6673 from openfoodfoundation/dependabot/bundler/paypal-sdk-merchant-1.117.2
Bump paypal-sdk-merchant from 1.106.1 to 1.117.2
2021-01-21 11:21:27 -08:00
Andy Brett
0f92b3c4de Merge pull request #6685 from Matt-Yorkley/angular-csrf
[Rails 5] Update Angular CSRF handling
2021-01-21 11:01:06 -08:00
Andy Brett
0e7f4b2f14 Merge pull request #6537 from mkllnk/simplify-mail-config
Simplify mail config
2021-01-21 10:59:43 -08:00
Andy Brett
d2b2e46124 Merge pull request #6701 from Matt-Yorkley/admin-order-performance
Fix performance issue in loading payment methods
2021-01-21 10:58:35 -08:00
Andy Brett
3ef4a74b84 Merge pull request #6655 from Matt-Yorkley/summing-amounts
Improve performance on summing adjustments and payment
2021-01-21 10:58:10 -08:00
Matt-Yorkley
d8b795ebee Merge pull request #6679 from Matt-Yorkley/soft-delete-enterprise-fees
Soft-delete Enterprise Fees
2021-01-21 19:57:29 +01:00
Andy Brett
70f30f5224 Merge pull request #6658 from andrewpbrett/item-counter-fix
Further fix #5989 (Item counter accepts values higher than the available stock)
2021-01-21 10:56:18 -08:00
Andy Brett
bba9e55006 don't try to process a payment if it's pending auth 2021-01-21 09:18:29 -08:00
Andy Brett
ce4621858d base authorization on the payment's order 2021-01-21 09:18:29 -08:00
Andy Brett
8bcaeff6c8 resolve merge conflict; add ssl helper to base controller 2021-01-21 09:18:29 -08:00
Andy Brett
1635b83c15 add missing translation key for payment actions 2021-01-21 09:18:29 -08:00
Andy Brett
903b2e7ff4 whitelist allowed events to be sent to a Payment 2021-01-21 09:18:29 -08:00
Andy Brett
ab5ffead1d require that the redirect url be to stripe.com and over https 2021-01-21 09:18:29 -08:00
Andy Brett
affc82b2b5 update payment jobs delivery methods 2021-01-21 09:18:29 -08:00
Andy Brett
d76db9ee51 update payment controller spec to move payment to pending 2021-01-21 09:18:29 -08:00
Andy Brett
d9b27bc556 move controller and mailer outside of spree namespace; use haml in template 2021-01-21 09:18:29 -08:00
Andy Brett
5f1669280c update to rspec 3 expect syntax 2021-01-21 09:17:07 -08:00
Andy Brett
b669ccdc74 refactor admin payments controller 2021-01-21 09:17:07 -08:00
Andy Brett
84b5fcf2ce add resend-authorization-email button to admin screen 2021-01-21 09:17:07 -08:00
Andy Brett
5c0408c68c pass paymentIntent ID to capture 2021-01-21 08:48:38 -08:00
Andy Brett
8507dacc10 pass return_url option to gateway authorize 2021-01-21 08:48:33 -08:00
Andy Brett
802e49bed3 add PaymentMailer and send email if payment auth is required 2021-01-21 08:24:01 -08:00
Cillian O'Ruanaidh
40f9b063fe Remove ability to create new product from products page, use /admin/products/new instead.
It's simpler if there is just one place to add a new product. Closes #6650

This removes the 'creating directly from the new product path' test scenario because we have another 'assigning important attributes' scenario above which provides enough coverage.
2021-01-21 08:24:01 -08:00
Pau Pérez Fabregat
1833ab5ffa Merge pull request #6710 from openfoodfoundation/dependabot/bundler/webdrivers-4.5.0
Bump webdrivers from 4.4.2 to 4.5.0
2021-01-21 12:27:02 +01:00
Pau Pérez Fabregat
3370271ad8 Merge pull request #6705 from coopdevs/fix-request-queuing-tracking
Pass request_queuing setting to Rack middleware
2021-01-21 12:04:22 +01:00
dependabot[bot]
b6e7307cb9 Bump webdrivers from 4.4.2 to 4.5.0
Bumps [webdrivers](https://github.com/titusfortner/webdrivers) from 4.4.2 to 4.5.0.
- [Release notes](https://github.com/titusfortner/webdrivers/releases)
- [Changelog](https://github.com/titusfortner/webdrivers/blob/master/CHANGELOG.md)
- [Commits](https://github.com/titusfortner/webdrivers/compare/v4.4.2...v4.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-21 05:16:36 +00:00
Andy Brett
77419a1e4b Merge pull request #6675 from cillian/single-new-product-view
Remove ability to create new product from products page, use /admin/products/new instead.
2021-01-20 19:23:33 -08:00
Andy Brett
e31d566f7f Merge pull request #6283 from luisramos0/sets
Move sets out of app/models to app/services/sets
2021-01-20 19:22:15 -08:00
Andy Brett
ca37976661 Merge pull request #6640 from jibees/2772-loading-spinner-reusable-component
Create a reusable "loading spinner" component
2021-01-20 19:21:31 -08:00
Andy Brett
1d4fa2983c Merge pull request #6466 from Matt-Yorkley/package-optimisation
Optimise Shipment#to_package
2021-01-20 19:20:56 -08:00
Andy Brett
4a19a368dd Merge pull request #6706 from Matt-Yorkley/adjustments-migration
Fix issue with orphaned adjustments in migration
2021-01-20 13:23:36 -08:00
Andy Brett
dd38c8b3e2 add down method for migration 2021-01-20 12:56:03 -08:00
Pau Perez
cc9e3fe69b Replace double negation with proper list of states
We rely now on the exhaustive list of states an order can be in after
checkout. What made this all a bit more messy is that I made up the
"checkout" order state, likely mixing it from the payment model states.

This simplifies things quite a bit and gives meaningful names to things.
2021-01-20 18:34:31 +01:00
Pau Perez
d9c065a311 Remove outdated comment
This comment was related to the feature we removed in
https://github.com/openfoodfoundation/openfoodnetwork/pull/3609.
2021-01-20 18:23:22 +01:00
Pau Perez
996761da67 Fix long line 2021-01-20 18:23:22 +01:00
Pau Perez
9bb49bb590 Ensure the query the class depends on is called 2021-01-20 18:23:22 +01:00
Pau Perez
db23428832 Test OutstandingBalance
This duplicates the scenarios tested for CustomersWithBalance.
2021-01-20 18:23:22 +01:00
Pau Perez
783863056d Extract query object out of UsersController
It improves the overall readability of the code and as a result, things
became easier to manage already.
2021-01-20 18:23:22 +01:00
Pau Perez
d18e79ab19 Move query object to app/queries/ and doc it 2021-01-20 18:23:22 +01:00
Pau Perez
e8ef4acb2b Hide new data fetching implementation under toggle 2021-01-20 18:23:22 +01:00
Pau Perez
37b7340eb1 Refactor specs to speed them up
We don't care about the conversion from hash to JSON (that's an
ActiveModel::Serializer responsibility that is thoroughly tested) but
our logic so we can skip that step which only slows down tests.

It consistently reduced ~1.5s on my machine but it's still too slow to wait
~8.5s to get feedback from them.
2021-01-20 18:23:22 +01:00
Pau Perez
a124f93b20 Remove old commented out code 2021-01-20 18:23:22 +01:00
Pau Perez
20abaaa950 Reuse outstanding balance statement across queries
Instead of relying on Spree::Order#outstanding_balance we make us of the
result set `balance_value` computed column. So, we ask PostgreSQL to
compute it instead of Ruby and then serialize it from that computed
column. That's a bit faster to compute that way and let's reuse logic.

We hide this new implementation under this features' toggle so it's only
used when enabled. We want hit the old behaviour by default.
2021-01-20 18:23:22 +01:00
Pau Perez
681a009eb6 Extract outstanding balance SQL CASE/WHEN 2021-01-20 18:23:22 +01:00
Pau Perez
caf1c9ecd9 Move data fetching from injection helper to action
Data fetching is a controller action responsibility. We shouldn't couple
the controller with it too much but it should trigger it, not the
view-layer.
2021-01-20 18:23:22 +01:00
Matt-Yorkley
e1c13bc194 Fix issue with orphaned adjustments in migration 2021-01-20 13:42:14 +00:00
Pau Perez
ba018df9c5 Pass request_queuing setting to Rack middleware
It turns out that this setting belongs to the Rack middleware Datadog
comes with to track requests (See
e4c430a174/docs/GettingStarted.md (rack)).

The way to pass this option to it is through `configuration[:rack]`
where the `TraceMiddleware` will read it from. See
f57aefe60a/lib/ddtrace/contrib/rack/middlewares.rb (L215-L217)
and
f57aefe60a/lib/ddtrace/contrib/rack/middlewares.rb (L30-L43).
2021-01-20 10:58:07 +01:00
Andy Brett
a53cc6bc92 Update all locales with the latest Transifex translations 2021-01-19 19:07:37 -08:00
Andy Brett
89ce850da6 Merge pull request #6703 from openfoodfoundation/fix-datadog-init
Revert "Enable request queuing tracking in Datadog"
2021-01-19 18:39:21 -08:00
Transifex-Openfoodnetwork
b9cf5b7a9b Updating translations for config/locales/de_DE.yml 2021-01-20 12:46:16 +11:00
Andy Brett
012289e95f Merge pull request #6687 from openfoodfoundation/dependabot/bundler/monetize-1.10.0
Bump monetize from 1.9.4 to 1.10.0
2021-01-19 17:23:33 -08:00
Matt-Yorkley
6a9505cf67 Ensure adjustments can access soft-deleted originators
Note: originator is generally an EnterpriseFee or a TaxRate
2021-01-19 16:46:14 +00:00
Matt-Yorkley
c4a8a38c8d Soft-delete enterprise fees
These objects can hold critical information related to adjustments and tax categories. If they are hard-deleted we lose vital data that's needed by associated records.
2021-01-19 16:46:14 +00:00
Matt-Yorkley
a184075c5c Fix performance issue in loading payment methods
This was loading and initializing every payment method in the database, and every calculator for each of those payment methods.

🔥
2021-01-19 15:33:44 +00:00
Pau Pérez Fabregat
b84fb5814e Merge pull request #6696 from Matt-Yorkley/migration-issues
Update adjustment migration
2021-01-19 16:02:04 +01:00
Pau Pérez Fabregat
8b01c9c8ba Merge pull request #6279 from luisramos0/base_controller
Merge Spree::BaseController with ApplicationController and merge StoreController with ::BaseController
2021-01-19 15:52:47 +01:00
Matt-Yorkley
79b86f535d Move class definitions in adjustment migration 2021-01-19 13:33:37 +00:00
Luis Ramos
8007554176 Remove test_after_commit gem, it's baked into rails 5 now 2021-01-19 01:15:43 +00:00
Luis Ramos
fc40775ca8 Make paypal controller inherit from base controller and not from old store controller 2021-01-18 23:33:05 +00:00
Maikel
334e270a11 Merge pull request #6552 from Matt-Yorkley/adjustments-order-association
[Adjustments] Associate all adjustments with an order
2021-01-19 08:43:13 +11:00
dependabot[bot]
d1eea4654a Bump monetize from 1.9.4 to 1.10.0
Bumps [monetize](https://github.com/RubyMoney/monetize) from 1.9.4 to 1.10.0.
- [Release notes](https://github.com/RubyMoney/monetize/releases)
- [Changelog](https://github.com/RubyMoney/monetize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/RubyMoney/monetize/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-18 16:10:24 +00:00
Luis Ramos
d8436c2dab Downgrade libv8 so that mini racer keeps working 2021-01-16 22:41:50 +00:00
Matt-Yorkley
ea9ebc8a33 Update Angular CSRF handling 2021-01-16 13:41:24 +00:00
Luis Ramos
7c9024101b Some css fixes to make it all work in rails 5 2021-01-15 23:26:37 +00:00
Luis Ramos
4060e7debf Replace usages of Spree::BaseController with ApplicationController 2021-01-15 21:59:16 +00:00
Luis Ramos
7a22367b4a Make controllers use ::Basecontroller instead of StoreController 2021-01-15 21:59:16 +00:00
Luis Ramos
23e6048bde Merge StoreController with BaseController 2021-01-15 21:59:16 +00:00
Luis Ramos
ff8a81cee7 Remove includes already present in parent ApplicationController 2021-01-15 21:59:16 +00:00
Luis Ramos
0eab1b2339 Merge Spree::BaseController with ApplicationController 2021-01-15 21:59:16 +00:00
Matt-Yorkley
c0c7c6ec78 Update checkout helper summing and add test coverage 2021-01-15 17:29:21 +00:00
Matt-Yorkley
1eb08ba31c Define basic models in migration 2021-01-15 16:17:18 +00:00
Cillian O'Ruanaidh
79668e06a7 Remove ability to create new product from products page, use /admin/products/new instead.
It's simpler if there is just one place to add a new product. Closes #6650

This removes the 'creating directly from the new product path' test scenario because we have another 'assigning important attributes' scenario above which provides enough coverage.
2021-01-15 10:24:50 +00:00
dependabot[bot]
8e4e276995 Bump paypal-sdk-merchant from 1.106.1 to 1.117.2
Bumps [paypal-sdk-merchant](https://github.com/paypal/merchant-sdk-ruby) from 1.106.1 to 1.117.2.
- [Release notes](https://github.com/paypal/merchant-sdk-ruby/releases)
- [Changelog](https://github.com/paypal/merchant-sdk-ruby/blob/master/ChangeLog.txt)
- [Commits](https://github.com/paypal/merchant-sdk-ruby/compare/v1.106.1...v1.117.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-15 05:49:20 +00:00
Luis Ramos
fd0bba19a7 Adapt enterprises code and specs to new namespace Sets::EnterpriseSet 2021-01-14 09:41:07 +00:00
Luis Ramos
ed0441dc41 Fix a few more rubocop issues 2021-01-14 09:41:07 +00:00
Luis Ramos
19b12092a0 Fix rubocop issues and adapt exceptions file 2021-01-14 09:41:07 +00:00
Luis Ramos
5d6d7f7ad0 Adapt enterprise fees code and specs to new namespace of Sets::EnterpriseFeeSet 2021-01-14 09:41:07 +00:00
Luis Ramos
96a351ad0e Adapt usage of Sets to their new location 2021-01-14 09:41:07 +00:00
Luis Ramos
187b4a1fc2 Add Sets namespace to each set file 2021-01-14 09:41:07 +00:00
Luis Ramos
8e65d29b02 Move sets to specific services namespace 2021-01-14 09:41:07 +00:00
Maikel Linke
c922a8fd4c Fix: Remove unused conditional
I forgot to remove this unused conditional in a previous commit. Spree
defaulted to overriding the email config and we never changed that.
2021-01-14 12:05:42 +11:00
Maikel Linke
f1618ec35f Remove old spec context
The specs all stay the same, just changing the indent.
2021-01-14 12:05:41 +11:00
Maikel Linke
be229c9002 Simplify email config by removing unused option
Spree used to give you more options to configure ActionMailer but our
setup is much simpler. We can remove unused code.

The removed option was never used by OFN and defaulted to true. We don't
need a database migration because the value isn't set in the database.
2021-01-14 12:05:41 +11:00
Andy Brett
8c4d12a501 limit item counter to max quantity available even if amount is manually filled in 2021-01-13 16:23:41 -08:00
Matt-Yorkley
aacd942697 Update specs that stub associations inaccurately
These specs fail if the code is using #sum on stubbed objects that don't respond to it nicely.
2021-01-13 17:32:20 +00:00
Matt-Yorkley
7d0ec48bcf Improve performance on summing adjustments and payment
:amount is a database field in these cases, as opposed to a method that returns a computed result. Calling `.sum(:amount)` is much more efficient here as it computes the sum at database level, as opposed to `.map(&:amount).sum`, which would fetch and instanciate all the objects first, and then sum the amounts after.
2021-01-13 16:16:49 +00:00
Pau Perez
e83a3ff76d Revert "Enable request queuing tracking in Datadog"
This reverts commit 91e5276140.
2021-01-12 10:56:11 +01:00
Matt-Yorkley
ca4de40fa2 Associate all adjustments with an order
This change is introduced in the adjustments updates from Spree 2.2 and used heavily in new scopes and methods (not included here).
2021-01-11 17:35:35 +00:00
Jean-Baptiste Bellet
8399b82bfd rename component to spinner
A name for a "thing" rather a state
2021-01-11 16:49:56 +01:00
Jean-Baptiste Bellet
26edd83a54 do not include inside a javascript asset
I made a mistake by using it inside a javascript asset
revert part of commit e34050f7c
2021-01-11 16:33:44 +01:00
Jean-Baptiste Bellet
71dc5a8ff0 remove trailing space 2021-01-11 16:29:13 +01:00
Jean-Baptiste Bellet
e34050f7cb add wrapper to show/hide loading spinner 2021-01-11 16:23:22 +01:00
Jean-Baptiste Bellet
544a7407e3 specify max-width to not overflow parent element 2021-01-11 16:22:47 +01:00
Jean-Baptiste Bellet
1b4254a5cf Revert "replace code by reusable component"
This reverts commit 4d953abbe9.
2021-01-11 16:12:17 +01:00
Jean-Baptiste Bellet
6f24ecd973 replace code by reusable component
There is no need to add .text-center as it's already on the parent component
2021-01-11 11:20:00 +01:00
Jean-Baptiste Bellet
4d953abbe9 replace code by reusable component
in a js file
2021-01-11 11:18:54 +01:00
Jean-Baptiste Bellet
b02b36b8e8 replace code by reusable component 2021-01-11 11:18:34 +01:00
Jean-Baptiste Bellet
776a61d1d9 create loading spinner reusable component 2021-01-11 11:17:33 +01:00
Matt-Yorkley
5b9db50250 Optimise Shipment#to_package
This is done in a later Spree commit to reduce the amount of processing done in larger orders.

See: ab01b1ec1e
2020-12-08 11:21:05 +00:00
241 changed files with 3968 additions and 3217 deletions

View File

@@ -33,7 +33,6 @@ Layout/LineLength:
- app/controllers/admin/product_import_controller.rb
- app/controllers/admin/schedules_controller.rb
- app/controllers/admin/subscriptions_controller.rb
- app/controllers/admin/variant_overrides_controller.rb
- app/controllers/api/enterprise_attachment_controller.rb
- app/controllers/api/product_images_controller.rb
- app/controllers/spree/paypal_controller_decorator.rb
@@ -71,7 +70,6 @@ Layout/LineLength:
- app/models/spree/variant.rb
- app/models/subscription.rb
- app/models/variant_override.rb
- app/models/variant_override_set.rb
- app/serializers/api/admin/subscription_line_item_serializer.rb
- app/services/cart_service.rb
- app/services/checkout/post_checkout_actions.rb
@@ -254,7 +252,6 @@ Layout/LineLength:
- spec/models/enterprise_relationship_spec.rb
- spec/models/enterprise_spec.rb
- spec/models/exchange_spec.rb
- spec/models/model_set_spec.rb
- spec/models/order_cycle_spec.rb
- spec/models/product_importer_spec.rb
- spec/models/product_import/reset_absent_spec.rb
@@ -385,7 +382,6 @@ Metrics/AbcSize:
- app/models/enterprise_group.rb
- app/models/enterprise.rb
- app/models/enterprise_relationship.rb
- app/models/model_set.rb
- app/models/product_import/entry_processor.rb
- app/models/product_import/entry_validator.rb
- app/models/product_import/product_importer.rb
@@ -413,6 +409,7 @@ Metrics/AbcSize:
- app/serializers/api/variant_serializer.rb
- app/services/cart_service.rb
- app/services/create_order_cycle.rb
- app/services/sets/model_set.rb
- app/services/order_cycle_form.rb
- app/services/order_syncer.rb
- app/services/variant_units/option_value_namer.rb
@@ -538,8 +535,8 @@ Metrics/CyclomaticComplexity:
- app/models/spree/product.rb
- app/models/spree/return_authorization.rb
- app/models/spree/zone.rb
- app/models/variant_override_set.rb
- app/services/cart_service.rb
- app/services/sets/variant_override_set.rb
- engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb
- engines/order_management/app/services/order_management/stock/estimator.rb
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb

View File

@@ -398,7 +398,6 @@ Style/ClassAndModuleChildren:
- 'app/models/calculator/flat_percent_per_item.rb'
- 'app/models/spree/gateway/migs.rb'
- 'app/models/spree/gateway/pin.rb'
- 'app/models/spree/product_set.rb'
- 'app/models/tag_rule/discount_order.rb'
- 'app/models/tag_rule/filter_order_cycles.rb'
- 'app/models/tag_rule/filter_payment_methods.rb'
@@ -575,7 +574,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/calculator/flat_percent_per_item.rb'
- 'app/models/calculator/weight.rb'
- 'app/models/column_preference.rb'
- 'app/models/column_preference_set.rb'
- 'app/models/concerns/address_display.rb'
- 'app/models/concerns/adjustment_scopes.rb'
- 'app/models/concerns/line_item_based_adjustment_handling.rb'
@@ -588,17 +586,13 @@ Style/FrozenStringLiteralComment:
- 'app/models/customer.rb'
- 'app/models/distributor_shipping_method.rb'
- 'app/models/enterprise_fee.rb'
- 'app/models/enterprise_fee_set.rb'
- 'app/models/enterprise_relationship_permission.rb'
- 'app/models/enterprise_role.rb'
- 'app/models/enterprise_set.rb'
- 'app/models/exchange.rb'
- 'app/models/exchange_fee.rb'
- 'app/models/exchange_variant.rb'
- 'app/models/inventory_item.rb'
- 'app/models/model_set.rb'
- 'app/models/order_cycle.rb'
- 'app/models/order_cycle_set.rb'
- 'app/models/preference_sections/footer_and_external_links_section.rb'
- 'app/models/preference_sections/group_signup_page_section.rb'
- 'app/models/preference_sections/header_section.rb'
@@ -623,7 +617,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/spree/gateway/pin.rb'
- 'app/models/spree/gateway/stripe_connect.rb'
- 'app/models/spree/preferences/file_configuration.rb'
- 'app/models/spree/product_set.rb'
- 'app/models/spree/property.rb'
- 'app/models/spree/user.rb'
- 'app/models/stripe_account.rb'
@@ -636,7 +629,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/tag_rule/filter_products.rb'
- 'app/models/tag_rule/filter_shipping_methods.rb'
- 'app/models/variant_override.rb'
- 'app/models/variant_override_set.rb'
- 'app/serializers/api/address_serializer.rb'
- 'app/serializers/api/adjustment_serializer.rb'
- 'app/serializers/api/cached_enterprise_serializer.rb'

11
Gemfile
View File

@@ -21,6 +21,7 @@ if ENV['DEPENDENCIES_NEXT']
gem 'responders'
gem 'sass', '<= 4.7.1'
gem 'sass-rails', '< 6.0.0'
gem 'libv8', '< 8'
else
gem 'rails', '~> 4.2'
@@ -34,6 +35,10 @@ else
gem 'db2fog'
gem 'unicorn'
group :test do
gem 'test_after_commit' # needed to test Devise callbacks
end
end
gem 'i18n'
@@ -56,12 +61,12 @@ gem 'cancancan', '~> 1.7.0'
gem 'ffaker'
gem 'highline', '2.0.3' # Necessary for the install generator
gem 'json'
gem 'monetize', '~> 1.1'
gem 'monetize', '~> 1.10'
gem 'paranoia', '~> 2.4'
gem 'state_machines-activerecord'
gem 'stringex', '~> 2.8.5'
gem 'paypal-sdk-merchant', '1.106.1'
gem 'paypal-sdk-merchant', '1.117.2'
gem 'stripe'
gem 'devise'
@@ -119,6 +124,7 @@ gem 'mini_racer', '0.2.15'
gem 'uglifier', '>= 1.0.3'
gem 'angular_rails_csrf'
gem 'foundation-icons-sass-rails'
gem 'foundation-rails', '= 5.5.2.1'
@@ -159,7 +165,6 @@ end
group :test do
gem 'simplecov', require: false
gem 'test-prof'
gem 'test_after_commit' # needed to test Devise callbacks
gem 'webmock'
# See spec/spec_helper.rb for instructions
# gem 'perftools.rb'

View File

@@ -105,10 +105,12 @@ GEM
railties (>= 3.1)
sprockets (~> 2.0)
tilt
angular_rails_csrf (4.2.0)
railties (>= 3, < 7)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.5.5)
arel (6.0.4)
ast (2.4.1)
ast (2.4.2)
atomic (1.1.101)
awesome_nested_set (3.3.1)
activerecord (>= 4.0.0, < 7.0)
@@ -164,7 +166,7 @@ GEM
compass (~> 1.0.0)
sass-rails (< 5.1)
sprockets (< 4.0)
concurrent-ruby (1.1.7)
concurrent-ruby (1.1.8)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -172,12 +174,12 @@ GEM
addressable
daemons (1.3.1)
dalli (2.7.11)
database_cleaner (1.8.5)
database_cleaner (1.99.0)
db2fog (0.9.0)
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.44.0)
ddtrace (0.45.0)
msgpack
debugger-linecache (1.2.0)
delayed_job (4.1.9)
@@ -436,7 +438,7 @@ GEM
letter_opener (1.7.0)
launchy (~> 2.2)
libv8 (7.3.492.27.1)
loofah (2.8.0)
loofah (2.9.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -450,11 +452,11 @@ GEM
mini_racer (0.2.15)
libv8 (> 7.3)
minitest (5.14.3)
monetize (1.9.4)
monetize (1.10.0)
money (~> 6.12)
money (6.13.8)
money (6.14.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.3.3)
msgpack (1.4.2)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
@@ -483,11 +485,11 @@ GEM
activerecord (>= 4.0, < 6.2)
parser (3.0.0.0)
ast (~> 2.4.1)
paypal-sdk-core (0.2.10)
paypal-sdk-core (0.3.4)
multi_json (~> 1.0)
xml-simple
paypal-sdk-merchant (1.106.1)
paypal-sdk-core (~> 0.2.3)
paypal-sdk-merchant (1.117.2)
paypal-sdk-core (~> 0.3.0)
pg (0.21.0)
power_assert (1.2.0)
pry (0.13.1)
@@ -498,7 +500,7 @@ GEM
pry (~> 0.13.0)
public_suffix (4.0.6)
rack (1.6.13)
rack-mini-profiler (2.3.0)
rack-mini-profiler (2.3.1)
rack (>= 1.2.0)
rack-protection (1.5.5)
rack
@@ -596,20 +598,20 @@ GEM
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.10.1)
rswag (2.3.1)
rswag-api (= 2.3.1)
rswag-specs (= 2.3.1)
rswag-ui (= 2.3.1)
rswag-api (2.3.1)
rswag (2.3.2)
rswag-api (= 2.3.2)
rswag-specs (= 2.3.2)
rswag-ui (= 2.3.2)
rswag-api (2.3.2)
railties (>= 3.1, < 7.0)
rswag-specs (2.3.1)
rswag-specs (2.3.2)
activesupport (>= 3.1, < 7.0)
json-schema (~> 2.2)
railties (>= 3.1, < 7.0)
rswag-ui (2.3.1)
rswag-ui (2.3.2)
actionpack (>= 3.1, < 7.0)
railties (>= 3.1, < 7.0)
rubocop (1.8.1)
rubocop (1.9.0)
parallel (~> 1.10)
parser (>= 3.0.0.0)
rainbow (>= 2.2.2, < 4.0)
@@ -618,7 +620,7 @@ GEM
rubocop-ast (>= 1.2.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.4.0)
rubocop-ast (1.4.1)
parser (>= 2.7.1.5)
rubocop-rails (2.9.1)
activesupport (>= 4.2.0)
@@ -640,7 +642,7 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
shoulda-matchers (4.5.0)
shoulda-matchers (4.5.1)
activesupport (>= 4.2.0)
simplecov (0.18.5)
docile (~> 1.1)
@@ -700,11 +702,11 @@ GEM
unicorn (>= 4, < 6)
warden (1.2.7)
rack (>= 1.0)
webdrivers (4.4.2)
webdrivers (4.5.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (>= 3.0, < 4.0)
webmock (3.11.1)
webmock (3.11.2)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -713,7 +715,7 @@ GEM
wicked_pdf (2.1.0)
activesupport
wkhtmltopdf-binary (0.12.5)
xml-simple (1.1.5)
xml-simple (1.1.8)
xmlrpc (0.3.0)
xpath (3.2.0)
nokogiri (~> 1.8)
@@ -732,6 +734,7 @@ DEPENDENCIES
acts_as_list (= 0.9.19)
andand
angular-rails-templates (~> 0.3.0)
angular_rails_csrf
angularjs-file-upload-rails (~> 2.4.1)
angularjs-rails (= 1.5.5)
atomic
@@ -783,14 +786,14 @@ DEPENDENCIES
knapsack
letter_opener (>= 1.4.1)
mini_racer (= 0.2.15)
monetize (~> 1.1)
monetize (~> 1.10)
oauth2 (~> 1.4.4)
ofn-qz!
order_management!
paper_trail (~> 10.3.1)
paperclip (~> 3.4.1)
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.106.1)
paypal-sdk-merchant (= 1.117.2)
pg (~> 0.21.0)
pry
pry-byebug

View File

@@ -104,6 +104,8 @@ GEM
railties (>= 4.2, < 7)
sprockets (>= 3.0, < 5)
tilt
angular_rails_csrf (4.2.0)
railties (>= 3, < 7)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.5.5)
arel (7.1.4)
@@ -268,7 +270,7 @@ GEM
addressable (~> 2.3)
letter_opener (1.7.0)
launchy (~> 2.2)
libv8 (8.4.255.0)
libv8 (7.3.492.27.1)
loofah (2.7.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
@@ -500,8 +502,6 @@ GEM
test-prof (0.7.5)
test-unit (3.3.7)
power_assert
test_after_commit (1.1.0)
activerecord (>= 3.2)
thor (0.20.3)
thread_safe (0.3.6)
tilt (2.0.10)
@@ -556,6 +556,7 @@ DEPENDENCIES
acts_as_list (= 0.9.19)
andand
angular-rails-templates (>= 0.3.0)
angular_rails_csrf
angularjs-file-upload-rails (~> 2.4.1)
angularjs-rails (= 1.5.5)
atomic
@@ -605,6 +606,7 @@ DEPENDENCIES
kaminari (~> 1.2.1)
knapsack
letter_opener (>= 1.4.1)
libv8 (< 8)
mini_racer (= 0.2.15)
monetize (~> 1.1)
oauth2 (~> 1.4.4)
@@ -646,7 +648,6 @@ DEPENDENCIES
stripe
test-prof
test-unit (~> 3.3)
test_after_commit
timecop
uglifier (>= 1.0.3)
unicorn-rails

View File

@@ -10,5 +10,4 @@ angular.module("ofn.admin", [
"admin.taxons",
"infinite-scroll"
]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -15,8 +15,6 @@
//= require jquery.cookie
//= require jquery.jstree/jquery.jstree
//= require jquery.vAlign
//= require jquery.horizontalNav
//= require jquery.adaptivemenu
//= require angular
//= require angular-resource
//= require angular-animate

View File

@@ -1,2 +1,2 @@
angular.module("admin.indexUtils", ['admin.resources', 'ngSanitize', 'templates', 'admin.utils']).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content");
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -1,8 +1,4 @@
angular.module('admin.orderCycles', ['ngTagsInput', 'admin.indexUtils', 'admin.enterprises'])
.config ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
.directive 'datetimepicker', ($timeout, $parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->

View File

@@ -1,3 +1,2 @@
angular.module("admin.productImport", ["ngResource"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -8,27 +8,10 @@ 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()
// We activate AdaptiveMenu only if not on webdriver
// Re-adjusting the admin menu during tests causes tests to fail.
if (!navigator.webdriver && 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({

View File

@@ -40,7 +40,13 @@ $(document).ready(function() {
var variant_id = save.data('variant-id');
var quantity = parseInt(save.parents('tr').find('input.line_item_quantity').val());
var maxQuantity = parseInt(save.parents('tr').find('input.line_item_quantity').attr("max"));
if (quantity > maxQuantity) {
quantity = maxQuantity;
save.parents('tr').find('input.line_item_quantity').val(maxQuantity);
alert(t("js.admin.orders.quantity_adjusted"));
}
toggleItemEdit();
adjustItems(shipment_number, variant_id, quantity);
@@ -77,7 +83,9 @@ adjustItems = function(shipment_number, variant_id, quantity){
}
url += '.json';
if(new_quantity!=0){
if (new_quantity == 0) {
alert(t("js.admin.orders.quantity_unchanged"));
} else {
$.ajax({
type: "PUT",
url: Spree.url(url),

View File

@@ -1 +1,2 @@
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -6,28 +6,62 @@ Darkswarm.controller "ShopVariantCtrl", ($scope, $modal, Cart) ->
return if old_value[0] == null && new_value[0] == null
Cart.adjust($scope.variant.line_item)
$scope.add = (quantity) ->
$scope.variant.line_item.quantity ||= 0
$scope.$watch "variant.line_item.quantity", ->
item = $scope.variant.line_item
item.quantity += quantity
if $scope.variant.product.group_buy
if item.quantity > $scope.available()
item.quantity = $scope.available()
$scope.$watch "variant.line_item.max_quantity", ->
item = $scope.variant.line_item
if item.max_quantity > $scope.available()
item.max_quantity = $scope.available()
if $scope.variant.product.group_buy
$scope.$watch "variant.line_item.quantity", ->
item = $scope.variant.line_item
if item.quantity < 1 || item.max_quantity < item.quantity
item.max_quantity = item.quantity
$scope.$watch "variant.line_item.max_quantity", ->
item = $scope.variant.line_item
if item.max_quantity < item.quantity
item.quantity = item.max_quantity
$scope.quantity = ->
$scope.variant.line_item.quantity || 0
$scope.maxQuantity = ->
$scope.variant.line_item.max_quantity || $scope.sanitizedQuantity()
$scope.sanitizedQuantity = ->
Math.max(0, Math.min($scope.quantity(), $scope.available()))
$scope.sanitizedMaxQuantity = ->
Math.max($scope.sanitizedQuantity(), Math.min($scope.maxQuantity(), $scope.available()))
$scope.add = (quantity) ->
item = $scope.variant.line_item
item.quantity = $scope.sanitizedQuantity() + quantity
$scope.addMax = (quantity) ->
item = $scope.variant.line_item
item.max_quantity += quantity
if item.max_quantity < item.quantity
item.quantity = item.max_quantity
item.max_quantity = $scope.sanitizedMaxQuantity() + quantity
$scope.canAdd = (quantity) ->
wantedQuantity = $scope.variant.line_item.quantity + quantity
wantedQuantity = $scope.sanitizedQuantity() + quantity
$scope.quantityValid(wantedQuantity)
$scope.canAddMax = (quantity) ->
variant = $scope.variant
wantedQuantity = variant.line_item.max_quantity + quantity
wantedQuantity = $scope.sanitizedMaxQuantity() + quantity
$scope.quantityValid(wantedQuantity) && variant.line_item.quantity > 0
$scope.available = ->
variant = $scope.variant
variant.on_demand && Infinity || variant.on_hand
$scope.quantityValid = (quantity) ->
variant = $scope.variant
minimum = 0

View File

@@ -12,7 +12,6 @@ window.Darkswarm = angular.module("Darkswarm", [
'angularFileUpload',
'angularSlideables'
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
$httpProvider.defaults.headers['common']['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
$httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"

View File

@@ -1,4 +1,4 @@
Darkswarm.directive "tabsetCtrl", (Tabsets, $location) ->
Darkswarm.directive "tabsetCtrl", (Tabsets, $location, $rootScope) ->
restrict: "C"
scope:
id: "@"
@@ -9,7 +9,13 @@ Darkswarm.directive "tabsetCtrl", (Tabsets, $location) ->
if $scope.navigate
path = $location.path()?.match(/^\/\w+$/)?[0]
$scope.selected = path[1..] if path
# Watch location change success event to operate back/forward buttons
$rootScope.$on "$locationChangeSuccess", ->
if $scope.navigate
path = $location.path()?.match(/^\/\w+$/)?[0]
Tabsets.toggle($scope.id, path[1..] if path)
this.toggle = (name) ->
Tabsets.toggle($scope.id, name)

View File

@@ -23,5 +23,5 @@ Darkswarm.factory 'Variants', ->
lineItemFor: (variant) ->
variant: variant
quantity: null
max_quantity: null
quantity: 0
max_quantity: 0

View File

@@ -9,27 +9,33 @@
{{ variant.line_item.total_price | localizeCurrency }}
.row
.columns.small-6
.columns.small-12.medium-6
.variant-bulk-buy-quantity-label
{{ "js.shopfront.bulk_buy_modal.min_quantity" | t }}
%div
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}>
-# U+FF0D Fullwidth Hyphen-Minus
%span.bulk-buy.variant-quantity>
{{ variant.line_item.quantity }}
%input.bulk-buy.variant-quantity{
type: "number",
min: "0",
max: "{{ available() }}",
ng: {model: "variant.line_item.quantity", max: "Infinity"}}>
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}}
-# U+FF0B Fullwidth Plus Sign
.columns.small-6
.columns.small-12.medium-6
.variant-bulk-buy-quantity-label
{{ "js.shopfront.bulk_buy_modal.max_quantity" | t }}
%div
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(-1)", disabled: "!canAddMax(-1)"}}>
-# U+FF0D Fullwidth Hyphen-Minus
%span.bulk-buy.variant-quantity>
{{ variant.line_item.max_quantity }}
%input.bulk-buy.variant-quantity{
type: "number",
min: "0",
max: "{{ available() }}",
ng: {model: "variant.line_item.max_quantity", max: "Infinity"}}>
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(1)", disabled: "!canAddMax(1)"}}
-# U+FF0B Fullwidth Plus Sign

View File

@@ -1,14 +1,22 @@
.small-5.medium-3.large-3.columns.text-right{"ng-if" => "::!variant.product.group_buy"}
.small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::!variant.product.group_buy"}
%button.add-variant{type: "button", ng: {if: "!variant.line_item.quantity", click: "add(1)", disabled: "!canAdd(1)"}}
{{ "js.shopfront.variant.add_to_cart" | t }}
%button.variant-quantity{type: "button", ng: {if: "variant.line_item.quantity", click: "add(-1)"}}>
-# U+FF0D Fullwidth Hyphen-Minus
%button.variant-quantity{type: "button", ng: {if: "variant.line_item.quantity", click: "add(1)", disabled: "!canAdd(1)"}}
-# U+FF0B Fullwidth Plus Sign
%br
.variant-quantity-inputs{ng: {if: "variant.line_item.quantity == 0"}}
%button.add-variant{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}}
{{ "js.shopfront.variant.add_to_cart" | t }}
.variant-quantity-inputs{ng: {if: "variant.line_item.quantity != 0"}}
%button.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}>
-# U+FF0D Fullwidth Hyphen-Minus
%input.variant-quantity{
type: "number",
min: "0",
max: "{{ available() }}",
ng: {model: "variant.line_item.quantity", max: "Infinity"}
}>
%button.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}}
-# U+FF0B Fullwidth Plus Sign
.variant-quantity-display{ng: {class: "{visible: variant.line_item.quantity}"}}
{{ "js.shopfront.variant.quantity_in_cart" | t:{quantity: variant.line_item.quantity || 0} }}
%input{type: :hidden,

View File

@@ -1,4 +1,4 @@
.small-5.medium-3.large-3.columns.text-right{"ng-if" => "::variant.product.group_buy"}
.small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::variant.product.group_buy"}
%button.add-variant{type: "button", ng: {if: "!variant.line_item.quantity", click: "addBulk(1)", disabled: "!canAdd(1)"}}
{{ "js.shopfront.variant.add_to_cart" | t }}

View File

@@ -1,5 +1,5 @@
.variants.row
.small-4.medium-4.large-6.columns.variant-name
.small-4.medium-4.large-5.columns.variant-name
.inline{"ng-if" => "::variant.display_name"} {{ ::variant.display_name }}
.variant-unit {{ ::variant.unit_to_display }}
.small-3.medium-3.large-2.columns.variant-price
@@ -8,7 +8,7 @@
"price-breakdown-placement" => "bottom",
"price-breakdown-animation" => true}
{{ variant.price_with_fees | localizeCurrency }}
.medium-2.large-1.columns.total-price
.medium-2.large-2.columns.total-price
%span{"ng-class" => "{filled: variant.line_item.total_price}"}
{{ variant.line_item.total_price | localizeCurrency }}

View File

@@ -5,7 +5,6 @@
*
*= require normalize
*= require skeleton
*= require responsive-tables
*= require jquery.powertip
*= require jquery.ui.datepicker

View File

@@ -65,12 +65,17 @@ nav.menu {
#admin-menu {
background-color: $color-3;
ul {
display: flex;
}
li {
min-width: 90px;
flex-grow: 1;
a {
display: block;
padding: 25px 20px;
padding: 25px 5px;
color: $color-1 !important;
text-transform: uppercase;
position: relative;
@@ -160,3 +165,11 @@ nav.menu {
}
}
}
#header figure {
margin: 0.25em 0;
}
#login-nav {
line-height: 1.75em;
}

View File

@@ -66,7 +66,6 @@ div.dashboard_item {
a {
border-radius: 0px 4px 0px 0px;
margin-left: 8px;
height: 100%;
padding: 15px 2px 0px 2px;
}

View File

@@ -0,0 +1,134 @@
// Grid Calculations
// Adjust $col-gutter (space between columns, as percentage) to adjust everything else automatically
$col-gutter: 2;
$total-gutter: $col-gutter * 15;
$total-colspace: 100 - $total-gutter;
$gutter-width: $col-gutter / 100;
$col-width: ($total-colspace / 16) / 100;
$col-1: $col-width;
$col-2: ($col-width * 2) + $gutter-width;
$col-3: ($col-width * 3) + ($gutter-width * 2);
$col-4: ($col-width * 4) + ($gutter-width * 3);
$col-5: ($col-width * 5) + ($gutter-width * 4);
$col-6: ($col-width * 6) + ($gutter-width * 5);
$col-7: ($col-width * 7) + ($gutter-width * 6);
$col-8: ($col-width * 8) + ($gutter-width * 7);
$col-9: ($col-width * 9) + ($gutter-width * 8);
$col-10: ($col-width * 10) + ($gutter-width * 9);
$col-11: ($col-width * 11) + ($gutter-width * 10);
$col-12: ($col-width * 12) + ($gutter-width * 11);
$col-13: ($col-width * 13) + ($gutter-width * 12);
$col-14: ($col-width * 14) + ($gutter-width * 13);
$col-15: ($col-width * 15) + ($gutter-width * 14);
$col-16: 100;
// Grid Classes
.container {
position: relative;
width: 100%;
margin: 0 auto;
padding: 0 1.5%;
box-sizing: border-box;
display: flex;
max-width: 1400px;
&.no-gutter {
padding: 0;
}
.row {
width: 100%;
margin-bottom: 1.5em;
}
}
.container::after,
.row::after,
.clearfix::after,
.clear::after {
content: "";
display: table;
clear: both;
}
.column,
.columns {
margin-left: percentage($gutter-width);
float: left;
box-sizing: border-box;
}
.column.one,
.columns.one { width: percentage($col-1); }
.columns.two { width: percentage($col-2); }
.columns.three { width: percentage($col-3); }
.columns.four { width: percentage($col-4); }
.columns.five { width: percentage($col-5); }
.columns.six { width: percentage($col-6); }
.columns.seven { width: percentage($col-7); }
.columns.eight { width: percentage($col-8); }
.columns.nine { width: percentage($col-9); }
.columns.ten { width: percentage($col-10); }
.columns.eleven { width: percentage($col-11); }
.columns.twelve { width: percentage($col-12); }
.columns.thirteen { width: percentage($col-13); }
.columns.fourteen { width: percentage($col-14);}
.columns.fifteen { width: percentage($col-15); }
.columns.sixteen { width: 100%; }
.column.offset-by-one,
.columns.offset-by-one { margin-left: $col-2; }
.columns.offset-by-two { margin-left: $col-3; }
.columns.offset-by-three { margin-left: $col-4; }
.columns.offset-by-four { margin-left: $col-5; }
.columns.offset-by-five { margin-left: $col-6; }
.columns.offset-by-six { margin-left: $col-7; }
.columns.offset-by-seven { margin-left: $col-8; }
.columns.offset-by-eight { margin-left: $col-9; }
.columns.offset-by-nine { margin-left: $col-10; }
.columns.offset-by-ten { margin-left: $col-11; }
.columns.offset-by-eleven { margin-left: $col-12; }
.columns.offset-by-twelve { margin-left: $col-13; }
.columns.offset-by-thirteen { margin-left: $col-14; }
.columns.offset-by-fourteen { margin-left: $col-15; }
.columns.offset-by-fifteen { margin-left: 100%; }
.column.alpha,
.columns.alpha,
.columns.sixteen,
.column:first-child,
.columns:first-child {
margin-left: 0;
}

View File

@@ -185,6 +185,7 @@ fieldset {
text-transform: uppercase;
text-align: center;
padding: 8px 15px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

View File

@@ -16,6 +16,7 @@
}
.icon-email:before { @extend .icon-envelope:before }
.icon-resend_authorization_email:before { @extend .icon-envelope:before }
.icon-resume:before { @extend .icon-refresh:before }
.icon-cancel:before,

View File

@@ -6,15 +6,25 @@
.darkswarm {
// #search
@include placeholder(rgba(0, 0, 0, 0.4), #777);
.row .columns.variant-quantity-column {
padding-left: 0;
}
}
.reveal-modal.product-bulk-modal {
width: 26em;
width: 27em;
}
// Components to add variants to cart and change quantities
//
// They are not nested so that they can be used in modals.
.variant-quantity-inputs {
height: 2.5rem;
white-space: nowrap;
}
button.add-variant, button.variant-quantity {
height: 2.5rem;
border-radius: 0;
@@ -48,7 +58,7 @@ button.add-variant, button.variant-quantity {
}
button.add-variant {
min-width: 6rem;
min-width: 7rem;
padding: 0 1em;
&[disabled] {
@@ -59,7 +69,7 @@ button.add-variant {
}
button.variant-quantity {
width: 3rem;
width: 2.25rem;
&:nth-of-type(1):not(.bulk-buy):not(.bulk-buy-add) {
border-right: .1em solid $orange-400;
@@ -71,7 +81,7 @@ button.variant-quantity {
font-size: 0.875em;
margin-top: 0.25em;
text-align: center;
width: 6rem;
width: 7rem;
visibility: hidden;
&.visible {
@@ -83,20 +93,28 @@ button.bulk-buy.variant-quantity {
background-color: transparent;
border: .1em solid $grey-200;
color: inherit;
width: 3.5rem;
}
button.bulk-buy-add.variant-quantity {
width: 2.5rem;
}
span.bulk-buy.variant-quantity {
[type="number"].variant-quantity {
border: .1em solid $grey-200;
height: 2.5rem;
display: inline-block;
min-width: 3em;
padding: .5em;
width: 2.5rem;
padding: 0;
text-align: center;
vertical-align: top;
appearance: none;
-webkit-appearance: none;
-moz-appearance: textfield;
&.bulk-buy {
width: 5rem;
}
}
.variant-bulk-buy-price-summary {

View File

@@ -3,9 +3,9 @@
// See https://github.com/zurb/foundation/issues/3855#issuecomment-30372252
@import "variables";
@import "components/global";
@import "components/buttons";
@import "components/panels";
@import "foundation/components/global";
@import "foundation/components/buttons";
@import "foundation/components/panels";
#sidebar {
margin-top: 1.875em;

View File

@@ -29,7 +29,8 @@ module Admin
collection_hash = Hash[permitted_params[:column_preferences].
each_with_index.map { |cp, i| [i, cp] }]
collection_hash.select!{ |_i, cp| cp[:action_name] == permitted_params[:action_name] }
@cp_set = ColumnPreferenceSet.new @column_preferences, collection_attributes: collection_hash
@cp_set = Sets::ColumnPreferenceSet.new(@column_preferences,
collection_attributes: collection_hash)
end
def collection

View File

@@ -27,7 +27,7 @@ module Admin
end
def bulk_update
@enterprise_fee_set = EnterpriseFeeSet.new(enterprise_fee_bulk_params)
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
@@ -40,7 +40,7 @@ module Admin
private
def load_enterprise_fee_set
@enterprise_fee_set = EnterpriseFeeSet.new collection: collection
@enterprise_fee_set = Sets::EnterpriseFeeSet.new collection: collection
end
def load_data
@@ -80,7 +80,7 @@ module Admin
end
def enterprise_fee_bulk_params
params.require(:enterprise_fee_set).permit(
params.require(:sets_enterprise_fee_set).permit(
collection_attributes: [
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
:inherits_tax_category, :calculator_type,

View File

@@ -81,7 +81,7 @@ module Admin
end
def bulk_update
@enterprise_set = EnterpriseSet.new(collection, bulk_params)
@enterprise_set = Sets::EnterpriseSet.new(collection, bulk_params)
if @enterprise_set.save
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
@@ -129,7 +129,7 @@ module Admin
private
def load_enterprise_set
@enterprise_set = EnterpriseSet.new(collection) if spree_current_user.admin?
@enterprise_set = Sets::EnterpriseSet.new(collection) if spree_current_user.admin?
end
def load_countries
@@ -235,7 +235,7 @@ module Admin
def check_can_change_bulk_sells
unless spree_current_user.admin?
params[:enterprise_set][:collection_attributes].each do |_i, enterprise_params|
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
unless spree_current_user == Enterprise.find_by(id: enterprise_params[:id]).owner
enterprise_params.delete :sells
end
@@ -269,7 +269,7 @@ module Admin
def check_can_change_bulk_owner
unless spree_current_user.admin?
params[:enterprise_set][:collection_attributes].each do |_i, enterprise_params|
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
enterprise_params.delete :owner_id
end
end
@@ -321,7 +321,7 @@ module Admin
end
def bulk_params
params.require(:enterprise_set).permit(
params.require(:sets_enterprise_set).permit(
collection_attributes: PermittedAttributes::Enterprise.attributes
)
end

View File

@@ -223,7 +223,7 @@ module Admin
end
def order_cycle_set
@order_cycle_set ||= OrderCycleSet.new(@order_cycles, order_cycle_bulk_params)
@order_cycle_set ||= Sets::OrderCycleSet.new(@order_cycles, order_cycle_bulk_params)
end
def require_order_cycle_set_params

View File

@@ -60,14 +60,17 @@ module Admin
for_hubs(editable_enterprises.collect(&:id))
options = [{ id: '0', name: 'All' }]
import_dates.collect(&:import_date).map { |i| options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long)) }
import_dates.collect(&:import_date).map { |i|
options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long))
}
options
end
def load_collection
collection_hash = Hash[variant_overrides_params.each_with_index.map { |vo, i| [i, vo] }]
@vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash
@vo_set = Sets::VariantOverrideSet.new(@variant_overrides,
collection_attributes: collection_hash)
end
def collection
@@ -82,8 +85,8 @@ module Admin
[:index, :bulk_update, :bulk_reset]
end
# This has been pulled from ModelSet as it is useful for compiling a list of errors on any generic collection (not necessarily a ModelSet)
# Could be pulled down into a lower level controller if it is useful in other high level controllers
# This method is also present in ModelSet
# This is useful for compiling a list of errors on any generic collection
def collection_errors
errors = ActiveModel::Errors.new self
full_messages = @collection.map { |element| element.errors.full_messages }.flatten

View File

@@ -1,8 +1,13 @@
# frozen_string_literal: true
require "application_responder"
require 'open_food_network/referer_parser'
require_dependency 'spree/authentication_helpers'
require "application_responder"
require 'cancan'
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'spree/core/controller_helpers/common'
require 'open_food_network/referer_parser'
class ApplicationController < ActionController::Base
self.responder = ApplicationResponder
@@ -10,6 +15,11 @@ class ApplicationController < ActionController::Base
protect_from_forgery
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include Spree::Core::ControllerHelpers::Common
prepend_before_action :restrict_iframes
before_action :set_cache_headers # prevent cart emptying via cache when using back button #1213
@@ -22,6 +32,8 @@ class ApplicationController < ActionController::Base
raise ActiveModel::ForbiddenAttributesError, params.to_s
end
respond_to :html
def redirect_to(options = {}, response_status = {})
::Rails.logger.error("Redirected by #{begin
caller(1).first
@@ -123,7 +135,6 @@ class ApplicationController < ActionController::Base
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] = I18n.t('order_cycle_closed')
@@ -150,3 +161,5 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
end
require 'spree/i18n/initializer'

View File

@@ -1,17 +1,16 @@
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/common'
# frozen_string_literal: true
require 'spree/core/controller_helpers/order'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'open_food_network/tag_rule_applicator'
class BaseController < ApplicationController
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Common
layout 'darkswarm'
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include I18nHelper
include EnterprisesHelper
include OrderCyclesHelper
helper 'spree/base'

View File

@@ -11,7 +11,7 @@ class CartController < BaseController
Spree::Adjustment.without_callbacks do
cart_service = CartService.new(order)
cart_service.populate(params.slice(:products, :variants, :quantity), true)
cart_service.populate(params.slice(:variants, :quantity), true)
if cart_service.valid?
order.update_distribution_charge!
order.cap_quantity_at_stock!

View File

@@ -2,7 +2,7 @@
require 'open_food_network/address_finder'
class CheckoutController < Spree::StoreController
class CheckoutController < ::BaseController
layout 'darkswarm'
include OrderStockCheck
@@ -142,7 +142,7 @@ class CheckoutController < Spree::StoreController
def handle_redirect_from_stripe
if OrderWorkflow.new(@order).next && order_complete?
checkout_succeeded
redirect_to(spree.order_path(@order)) && return
redirect_to(order_path(@order)) && return
else
checkout_failed
end
@@ -202,10 +202,10 @@ class CheckoutController < Spree::StoreController
def update_succeeded_response
respond_to do |format|
format.html do
respond_with(@order, location: spree.order_path(@order))
respond_with(@order, location: order_path(@order))
end
format.json do
render json: { path: spree.order_path(@order) }, status: :ok
render json: { path: order_path(@order) }, status: :ok
end
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
class PaymentsController < BaseController
ssl_required :redirect_to_authorize
respond_to :html
prepend_before_action :require_logged_in, only: :redirect_to_authorize
def redirect_to_authorize
@payment = Spree::Payment.find(params[:id])
authorize! :show, @payment.order
if url = @payment.cvv_response_message
redirect_to url
else
redirect_to order_url(@payment.order)
end
end
private
def require_logged_in
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
class PaymentsController < BaseController
ssl_required :redirect_to_authorize
respond_to :html
prepend_before_action :require_logged_in, only: :redirect_to_authorize
def redirect_to_authorize
@payment = Spree::Payment.find(params[:id])
authorize! :show, @payment.order
if url = @payment.cvv_response_message
redirect_to url
else
redirect_to order_url(@payment.order)
end
end
private
def require_logged_in
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end

View File

@@ -1,6 +1,6 @@
module Spree
module Admin
class BaseController < Spree::BaseController
class BaseController < ApplicationController
ssl_required
helper 'spree/admin/navigation'

View File

@@ -56,7 +56,7 @@ module Spree
# 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}!")
if allowed_events.include?(event) && @payment.public_send("#{event}!")
flash[:success] = t(:payment_updated)
else
flash[:error] = t(:cannot_perform_operation)
@@ -116,7 +116,7 @@ module Spree
# Only show payments for the order's distributor
@payment_methods = PaymentMethod.
available(:back_end).
select{ |pm| pm.has_distributor? @order.distributor }
for_distributor(@order.distributor)
@payment_method = if @payment&.payment_method
@payment.payment_method
@@ -153,10 +153,18 @@ module Spree
def authorize_stripe_sca_payment
return unless @payment.payment_method.class == Spree::Gateway::StripeSCA
@payment.authorize!
return if @payment.pending? && @payment.cvv_response_message.nil?
@payment.authorize!(main_app.order_path(@payment.order, only_path: false))
raise Spree::Core::GatewayError, I18n.t('authorization_failure')
raise Spree::Core::GatewayError, I18n.t('authorization_failure') unless @payment.pending?
return unless @payment.cvv_response_message.present?
PaymentMailer.authorize_payment(@payment).deliver_later
raise Spree::Core::GatewayError, I18n.t('action_required')
end
def allowed_events
%w{capture void_transaction credit refund resend_authorization_email}
end
end
end

View File

@@ -163,7 +163,7 @@ module Spree
def product_set_from_params(_params)
collection_hash = Hash[products_params.each_with_index.map { |p, i| [i, p] }]
Spree::ProductSet.new(collection_attributes: collection_hash)
Sets::ProductSet.new(collection_attributes: collection_hash)
end
def products_params

View File

@@ -1,20 +0,0 @@
# frozen_string_literal: true
require 'cancan'
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'spree/core/controller_helpers/common'
module Spree
class BaseController < ApplicationController
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include Spree::Core::ControllerHelpers::Common
respond_to :html
end
end
require 'spree/i18n/initializer'

View File

@@ -7,7 +7,7 @@
# to CheckoutController directly in the routes
# with a slash like "to: '/checkout#edit'", but it does not work in this case.
module Spree
class CheckoutController < Spree::StoreController
class CheckoutController < ::BaseController
def edit
flash.keep
redirect_to main_app.checkout_path

View File

@@ -1,5 +1,5 @@
module Spree
class OrdersController < Spree::StoreController
class OrdersController < ::BaseController
include OrderCyclesHelper
include Rails.application.routes.url_helpers
@@ -85,7 +85,7 @@ module Spree
@order.next_transition.run_callbacks if @order.cart?
redirect_to checkout_state_path(@order.checkout_steps.first)
elsif @order.complete?
redirect_to spree.order_path(@order)
redirect_to order_path(@order)
else
redirect_to main_app.cart_path
end
@@ -132,27 +132,16 @@ module Spree
end
end
def clear
@order = current_order(true)
@order.empty!
@order.set_order_cycle! nil
redirect_to main_app.enterprise_path(@order.distributor.id)
end
def order_cycle_expired
@order_cycle = OrderCycle.find session[:expired_order_cycle_id]
end
def cancel
@order = Spree::Order.find_by!(number: params[:id])
authorize! :cancel, @order
if @order.cancel
if CustomerOrderCancellation.new(@order).call
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
else
flash[:error] = I18n.t(:orders_could_not_cancel)
end
redirect_to request.referer || spree.order_path(@order)
redirect_to request.referer || order_path(@order)
end
private
@@ -216,7 +205,7 @@ module Spree
if items.empty?
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
redirect_to spree.order_path(order_to_update)
redirect_to order_path(order_to_update)
end
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Spree
class PaypalController < StoreController
class PaypalController < ::BaseController
ssl_allowed
include OrderStockCheck
@@ -234,7 +234,7 @@ module Spree
end
def completion_route(order)
spree.order_path(order, token: order.token)
main_app.order_path(order, token: order.token)
end
def address_required?

View File

@@ -1,14 +0,0 @@
# frozen_string_literal: true
require 'spree/core/controller_helpers/order'
module Spree
class StoreController < Spree::BaseController
layout 'darkswarm'
include Spree::Core::ControllerHelpers::Order
include I18nHelper
before_action :set_locale
end
end

View File

@@ -1,5 +1,5 @@
module Spree
class UsersController < Spree::StoreController
class UsersController < ::BaseController
layout 'darkswarm'
ssl_required
skip_before_action :set_current_order, only: :show
@@ -12,9 +12,13 @@ module Spree
before_action :set_locale
before_action :enable_embedded_shopfront
# Ignores invoice orders, only order where state: 'complete'
def show
@orders = @user.orders.where(state: 'complete').order('completed_at desc')
@orders = orders_collection
customers = spree_current_user.customers
@shops = Enterprise
.where(id: @orders.pluck(:distributor_id).uniq | customers.pluck(:enterprise_id))
@unconfirmed_email = spree_current_user.unconfirmed_email
end
@@ -54,6 +58,14 @@ module Spree
private
def orders_collection
if OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, spree_current_user)
CompleteOrdersWithBalance.new(@user).query
else
@user.orders.where(state: 'complete').order('completed_at desc')
end
end
def load_object
@user ||= spree_current_user
if @user

View File

@@ -19,7 +19,7 @@ module CheckoutHelper
adjustments.reject! { |a| a.originator_type == 'EnterpriseFee' && a.source_type != 'Spree::LineItem' }
unless exclude.include? :admin_and_handling
adjustments << Spree::Adjustment.new(
label: I18n.t(:orders_form_admin), amount: enterprise_fee_adjustments.map(&:amount).sum
label: I18n.t(:orders_form_admin), amount: enterprise_fee_adjustments.sum(&:amount)
)
end
@@ -28,7 +28,7 @@ module CheckoutHelper
def display_checkout_admin_and_handling_adjustments_total_for(order)
adjustments = order.adjustments.eligible.where('originator_type = ? AND source_type != ? ', 'EnterpriseFee', 'Spree::LineItem')
Spree::Money.new adjustments.map(&:amount).sum, currency: order.currency
Spree::Money.new adjustments.sum(:amount), currency: order.currency
end
def checkout_line_item_adjustments(order)
@@ -36,7 +36,7 @@ module CheckoutHelper
end
def checkout_subtotal(order)
order.item_total + checkout_line_item_adjustments(order).map(&:amount).sum
order.item_total + checkout_line_item_adjustments(order).sum(:amount)
end
def display_checkout_subtotal(order)

View File

@@ -1,10 +1,10 @@
module EnterpriseFeesHelper
def angular_name(method)
"enterprise_fee_set[collection_attributes][{{ $index }}][#{method}]"
"sets_enterprise_fee_set[collection_attributes][{{ $index }}][#{method}]"
end
def angular_id(method)
"enterprise_fee_set_collection_attributes_{{ $index }}_#{method}"
"sets_enterprise_fee_set_collection_attributes_{{ $index }}_#{method}"
end
def enterprise_fee_type_options

View File

@@ -28,7 +28,7 @@ module Spree
end
def changeable_orders_link_path
changeable_orders.one? ? spree.order_path(changeable_orders.first) : spree.account_path
changeable_orders.one? ? order_path(changeable_orders.first) : spree.account_path
end
def shop_changeable_orders_alert_html

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
class PaymentMailer < Spree::BaseMailer
include I18nHelper
def authorize_payment(payment)
@payment = payment
subject = I18n.t('spree.payment_mailer.authorize_payment.subject',
distributor: @payment.order.distributor.name)
mail(to: payment.order.user.email,
from: from_address,
subject: subject)
end
end

View File

@@ -17,6 +17,16 @@ module Spree
end
end
def cancel_email_for_shop(order)
@order = order
I18n.with_locale valid_locale(@order.distributor.owner) do
subject = I18n.t('spree.order_mailer.cancel_email_for_shop.subject')
mail(to: @order.distributor.contact.email,
from: from_address,
subject: subject)
end
end
def confirm_email_for_customer(order_or_order_id, resend = false)
@order = find_order(order_or_order_id)
I18n.with_locale valid_locale(@order.user) do

View File

@@ -1,5 +0,0 @@
class ColumnPreferenceSet < ModelSet
def initialize(collection, attributes = {})
super(ColumnPreference, collection, attributes, nil, nil )
end
end

View File

@@ -1,6 +1,8 @@
class EnterpriseFee < ActiveRecord::Base
include Spree::Core::CalculatedAdjustments
acts_as_paranoid
belongs_to :enterprise
belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id'

View File

@@ -1,7 +0,0 @@
class EnterpriseFeeSet < ModelSet
def initialize(attributes = {})
super(EnterpriseFee, EnterpriseFee.all,
attributes,
proc { |attrs| attrs[:name].blank? })
end
end

View File

@@ -1,5 +0,0 @@
class EnterpriseSet < ModelSet
def initialize(collection, attributes = {})
super(Enterprise, collection, attributes)
end
end

View File

@@ -1,65 +0,0 @@
# Tableless model to handle updating multiple models at once from a single form
class ModelSet
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :collection
def initialize(klass, collection, attributes = {}, reject_if = nil, delete_if = nil)
@klass, @collection, @reject_if, @delete_if = klass, collection, reject_if, delete_if
# Set here first, to ensure that we apply collection_attributes to the right collection
@collection = attributes[:collection] if attributes[:collection]
attributes.each do |name, value|
public_send("#{name}=", value)
end
end
def collection_attributes=(collection_attributes)
collection_attributes.each do |_k, attributes|
# attributes == {:id => 123, :next_collection_at => '...'}
found_element = @collection.detect do |element|
element.id.to_s == attributes[:id].to_s && !element.id.nil?
end
if found_element.nil?
@collection << @klass.new(attributes) unless @reject_if.andand.call(attributes)
else
found_element.assign_attributes(attributes.except(:id))
end
end
end
def errors
errors = ActiveModel::Errors.new self
full_messages = @collection
.map { |model| model.errors.full_messages }
.flatten
full_messages.each { |message| errors.add(:base, message) }
errors
end
def save
collection_to_delete.each(&:destroy)
collection_to_keep.all?(&:save)
end
def collection_to_delete
# Remove all elements to be deleted from collection and return them
# Allows us to render @model_set.collection without deleted elements
deleted = []
@collection = collection.to_a
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes) }
end
def persisted?
false
end
end

View File

@@ -1,5 +0,0 @@
class OrderCycleSet < ModelSet
def initialize(collection, attributes = {})
super(OrderCycle, collection, attributes)
end
end

View File

@@ -39,6 +39,8 @@ module Spree
belongs_to :adjustable, polymorphic: true
belongs_to :source, polymorphic: true
belongs_to :originator, polymorphic: true
belongs_to :order, class_name: "Spree::Order"
belongs_to :tax_rate, -> { where spree_adjustments: { originator_type: 'Spree::TaxRate' } },
foreign_key: 'originator_id'
@@ -160,6 +162,13 @@ module Spree
result
end
# Allow accessing soft-deleted originator objects
def originator
return if originator_type.blank?
originator_type.constantize.unscoped { super }
end
private
def update_adjustable

View File

@@ -92,13 +92,11 @@ module Spree
preference :s3_host_alias, :string
# Default mail headers settings
preference :enable_mail_delivery, :boolean, default: false
preference :mails_from, :string, default: 'ofn@example.com'
preference :mail_bcc, :string, default: 'ofn@example.com'
preference :intercept_email, :string, default: nil
# Default smtp settings
preference :override_actionmailer_config, :boolean, default: true
preference :mail_host, :string, default: 'localhost'
preference :mail_domain, :string, default: 'localhost'
preference :mail_port, :integer, default: 25

View File

@@ -79,11 +79,17 @@ module Spree
end
def actions
%w{capture void credit}
%w{capture void credit resend_authorization_email}
end
def can_resend_authorization_email?(payment)
payment.pending? && payment.cvv_response_message.present?
end
# Indicates whether its possible to capture the payment
def can_capture?(payment)
return false if payment.cvv_response_message.present?
payment.pending? || payment.checkout?
end

View File

@@ -45,6 +45,11 @@ module Spree
failed_activemerchant_billing_response(e.message)
end
def capture(money, payment_intent_id, gateway_options)
options = basic_options(gateway_options)
provider.capture(money, payment_intent_id, options)
end
# NOTE: the name of this method is determined by Spree::Payment::Processing
def charge_offline(money, creditcard, gateway_options)
customer, payment_method =
@@ -110,7 +115,7 @@ module Spree
def options_for_authorize(money, creditcard, gateway_options)
options = basic_options(gateway_options)
options[:return_url] = full_checkout_path
options[:return_url] = gateway_options[:return_url] || full_checkout_path
customer_id, payment_method_id =
Stripe::CreditCardCloner.new(creditcard, stripe_account_id).find_or_clone

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Spree
class OptionValuesLineItem < ActiveRecord::Base
belongs_to :line_item, class_name: 'Spree::LineItem'
belongs_to :option_value, class_name: 'Spree::OptionValue'
end
end

View File

@@ -130,6 +130,11 @@ module Spree
where("state != ?", state)
}
# All the states an order can be in after completing the checkout
FINALIZED_STATES = %w(complete canceled resumed awaiting_return returned).freeze
scope :finalized, -> { where(state: FINALIZED_STATES) }
def self.by_number(number)
where(number: number)
end
@@ -387,11 +392,11 @@ module Spree
end
def ship_total
adjustments.shipping.map(&:amount).sum
adjustments.shipping.sum(:amount)
end
def tax_total
adjustments.tax.map(&:amount).sum
adjustments.tax.sum(:amount)
end
# Creates new tax charges if there are any applicable rates. If prices already
@@ -723,15 +728,7 @@ module Spree
end
def total_tax
(adjustments.to_a + price_adjustments.to_a).sum(&:included_tax)
end
def price_adjustments
adjustments = []
line_items.each { |line_item| adjustments.concat line_item.adjustments }
adjustments
(adjustments.to_a + line_item_adjustments.to_a).sum(&:included_tax)
end
def price_adjustment_totals

View File

@@ -121,6 +121,12 @@ module Spree
actions
end
def resend_authorization_email!
return unless cvv_response_message.present?
PaymentMailer.authorize_payment(self).deliver_later
end
def payment_source
res = source.is_a?(Payment) ? source.source : source
res || payment_method

View File

@@ -23,9 +23,9 @@ module Spree
end
end
def authorize!
def authorize!(return_url = nil)
started_processing!
gateway_action(source, :authorize, :pend)
gateway_action(source, :authorize, :pend, return_url: return_url)
end
def purchase!
@@ -44,19 +44,7 @@ module Spree
started_processing!
protect_from_connection_error do
check_environment
response = if payment_method.payment_profiles_supported?
# Gateways supporting payment profiles will need access to credit
# card object because this stores the payment profile information
# so supply the authorization itself as well as the credit card,
# rather than just the authorization code
payment_method.capture(self, source, gateway_options)
else
# Standard ActiveMerchant capture usage
payment_method.capture(money.money.cents,
response_code,
gateway_options)
end
response = payment_method.capture(money.money.cents, response_code, gateway_options)
handle_response(response, :complete, :failure)
end
@@ -222,7 +210,7 @@ module Spree
refund_amount.to_f
end
def gateway_action(source, action, success_state)
def gateway_action(source, action, success_state, options = {})
protect_from_connection_error do
check_environment
@@ -230,7 +218,7 @@ module Spree
action,
(amount * 100).round,
source,
gateway_options
gateway_options.merge(options)
)
handle_response(response, success_state, :failure)
end

View File

@@ -1,138 +0,0 @@
class Spree::ProductSet < ModelSet
def initialize(attributes = {})
super(Spree::Product, [], attributes)
end
def save
@collection_hash.each_value.all? do |product_attributes|
update_product_attributes(product_attributes)
end
end
def collection_attributes=(attributes)
@collection = Spree::Product
.where(id: attributes.each_value.map { |product| product[:id] })
@collection_hash = attributes
end
private
# A separate method of updating products was required due to an issue with
# the way Rails' assign_attributes and update behave when
# delegated attributes of a nested object are updated via the parent object
# (ie. price of variants). Updating such attributes by themselves did not
# work using:
#
# product.update(variants_attributes: [{ id: y, price: xx.x }])
#
# and so an explicit call to update on each individual variant was
# required. ie:
#
# variant.update( { price: xx.x } )
#
def update_product_attributes(attributes)
split_taxon_ids!(attributes)
product = find_model(@collection, attributes[:id])
return if product.nil?
update_product(product, attributes)
end
def split_taxon_ids!(attributes)
attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present?
end
def update_product(product, attributes)
original_supplier = product.supplier_id
return false unless update_product_only_attributes(product, attributes)
ExchangeVariantDeleter.new.delete(product) if original_supplier != product.supplier_id
update_product_variants(product, attributes) &&
update_product_master(product, attributes)
end
def update_product_only_attributes(product, attributes)
variant_related_attrs = [:id, :variants_attributes, :master_attributes]
product_related_attrs = attributes.except(*variant_related_attrs)
return true if product_related_attrs.blank?
product.assign_attributes(product_related_attrs)
validate_presence_of_unit_value_in_product(product)
product.errors.empty? && product.save
end
def validate_presence_of_unit_value_in_product(product)
product.variants.each do |variant|
validate_presence_of_unit_value_in_variant(product, variant)
end
end
def validate_presence_of_unit_value_in_variant(product, variant)
return unless %w(weight volume).include?(product.variant_unit)
return if variant.unit_value.present?
product.errors.add(:unit_value, "can't be blank")
end
def update_product_variants(product, attributes)
return true unless attributes[:variants_attributes]
update_variants_attributes(product, attributes[:variants_attributes])
end
def update_product_master(product, attributes)
return true unless attributes[:master_attributes]
create_or_update_variant(product, attributes[:master_attributes])
end
def update_variants_attributes(product, variants_attributes)
variants_attributes.each do |attributes|
create_or_update_variant(product, attributes)
end
end
def create_or_update_variant(product, variant_attributes)
variant = find_model(product.variants_including_master, variant_attributes[:id])
if variant.present?
variant.update(variant_attributes.except(:id))
else
create_variant(product, variant_attributes)
end
end
def create_variant(product, variant_attributes)
on_hand = variant_attributes.delete(:on_hand)
on_demand = variant_attributes.delete(:on_demand)
variant = product.variants.create(variant_attributes)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant, variant_attributes)
raise e
end
end
def notify_bugsnag(error, product, variant, variant_attributes)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant_attributes, variant_attributes)
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def find_model(collection, model_id)
collection.find do |model|
model.id.to_s == model_id.to_s && model.persisted?
end
end
end

View File

@@ -249,8 +249,11 @@ module Spree
def to_package
package = OrderManagement::Stock::Package.new(stock_location, order)
inventory_units.includes(:variant).each do |inventory_unit|
package.add inventory_unit.variant, 1, inventory_unit.state_name
grouped_inventory_units = inventory_units.includes(:variant).group_by do |iu|
[iu.variant, iu.state_name]
end
grouped_inventory_units.each do |(variant, state_name), inventory_units|
package.add variant, inventory_units.count, state_name
end
package
end

View File

@@ -71,6 +71,7 @@ module Spree
amount: amount,
source: order,
originator: self,
order: order,
state: "closed",
label: label
)
@@ -82,8 +83,8 @@ module Spree
order.adjustments(:reload)
order.line_items(:reload)
# TaxRate adjustments (order.adjustments.tax)
# and price adjustments (tax included on line items) consist of 100% tax
(order.adjustments.tax + order.price_adjustments).each do |adjustment|
# and line item adjustments (tax included on line items) consist of 100% tax
(order.adjustments.tax + order.line_item_adjustments.reload).each do |adjustment|
adjustment.set_absolute_included_tax! adjustment.amount
end
end

View File

@@ -1,30 +0,0 @@
class VariantOverrideSet < ModelSet
def initialize(collection, attributes = {})
super(VariantOverride, collection, attributes, nil, proc { |attrs, tag_list| deletable?(attrs, tag_list) } )
end
private
def deletable?(attrs, tag_list)
attrs['price'].blank? &&
attrs['count_on_hand'].blank? &&
attrs['default_stock'].blank? &&
attrs['resettable'].blank? &&
attrs['sku'].nil? &&
attrs['on_demand'].nil? &&
tag_list.empty?
end
# Override of ModelSet method to allow us to check presence of a tag_list (which is not an attribute)
# This method will delete VariantOverrides that have no values (see deletable? above)
# If the user sets all values to nil in the UI the VO will be deleted from the DB
def collection_to_delete
deleted = []
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes, e.tag_list) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes, e.tag_list) }
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
# Fetches complete orders of the specified user including their balance as a computed column
class CompleteOrdersWithBalance
def initialize(user)
@user = user
end
def query
OutstandingBalance.new(sorted_finalized_orders).query
end
private
def sorted_finalized_orders
@user.orders
.finalized
.select('spree_orders.*')
.order(completed_at: :desc)
end
end

View File

@@ -0,0 +1,39 @@
# frozen_string_literal: true
# Fetches the customers of the specified enterprise including the aggregated balance across the
# customer's orders. That is, we get the total balance for each customer with this enterprise.
class CustomersWithBalance
def initialize(enterprise)
@enterprise = enterprise
end
def query
Customer.of(enterprise).
joins(left_join_complete_orders).
group("customers.id").
select("customers.*").
select(outstanding_balance_sum)
end
private
attr_reader :enterprise
def outstanding_balance_sum
"SUM(#{OutstandingBalance.new.statement}) AS balance_value"
end
# The resulting orders are in states that belong after the checkout. Only these can be considered
# for a customer's balance.
def left_join_complete_orders
<<-SQL.strip_heredoc
LEFT JOIN spree_orders ON spree_orders.customer_id = customers.id
AND #{finalized_states.to_sql}
SQL
end
def finalized_states
states = Spree::Order::FINALIZED_STATES.map { |state| Arel::Nodes.build_quoted(state) }
Arel::Nodes::In.new(Spree::Order.arel_table[:state], states)
end
end

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
# Encapsulates the SQL statement that computes the balance of an order as a new column in the result
# set. This can then be reused chaining it with the ActiveRecord::Relation objects you pass in the
# constructor.
#
# Alternatively, you can get the SQL by calling #statement, which is suitable for more complex
# cases.
#
# See CompleteOrdersWithBalance or CustomersWithBalance as examples.
class OutstandingBalance
# All the states of a finished order but that shouldn't count towards the balance (the customer
# didn't get the order for whatever reason). Note it does not include complete
FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze
# The relation must be an ActiveRecord::Relation object with `spree_orders` in the SQL statement
# FROM for #statement to work.
def initialize(relation = nil)
@relation = relation
end
def query
relation.select("#{statement} AS balance_value")
end
# Arel doesn't support CASE statements until v7.1.0 so we'll have to wait with SQL literals
# a little longer. See https://github.com/rails/arel/pull/400 for details.
def statement
<<-SQL.strip_heredoc
CASE WHEN state IN #{non_fulfilled_states_group.to_sql} THEN payment_total
WHEN state IS NOT NULL THEN payment_total - total
ELSE 0 END
SQL
end
private
attr_reader :relation
def non_fulfilled_states_group
states = FINALIZED_NON_SUCCESSFUL_STATES.map { |state| Arel::Nodes.build_quoted(state) }
Arel::Nodes::Grouping.new(states)
end
end

View File

@@ -7,6 +7,14 @@ module Api
has_many :payments, serializer: Api::PaymentSerializer
def outstanding_balance
if OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, object.user)
-object.balance_value
else
object.outstanding_balance
end
end
def payments
object.payments.joins(:payment_method).completed
end
@@ -42,13 +50,13 @@ module Api
end
def path
Spree::Core::Engine.routes.url_helpers.order_path(object)
order_path(object)
end
def cancel_path
return nil unless object.changes_allowed?
Spree::Core::Engine.routes.url_helpers.cancel_order_path(object)
cancel_order_path(object)
end
def changes_allowed

View File

@@ -1,5 +1,7 @@
require 'open_food_network/scope_variant_to_hub'
# Previously Spree::OrderPopulator. Modified to work with max_quantity and variant overrides.
class CartService
attr_accessor :order, :currency
attr_reader :variants_h

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
class CustomerOrderCancellation
def initialize(order)
@order = order
end
def call
return unless order.cancel
Spree::OrderMailer.cancel_email_for_shop(order).deliver_later
end
private
attr_reader :order
end

View File

@@ -1,61 +0,0 @@
# frozen_string_literal: true
class CustomersWithBalance
def initialize(enterprise)
@enterprise = enterprise
end
def query
Customer.of(enterprise).
joins(left_join_complete_orders).
group("customers.id").
select("customers.*").
select(outstanding_balance)
end
private
attr_reader :enterprise
# Arel doesn't support CASE statements until v7.1.0 so we'll have to wait with SQL literals
# a little longer. See https://github.com/rails/arel/pull/400 for details.
def outstanding_balance
<<-SQL.strip_heredoc
SUM(
CASE WHEN state IN #{non_fulfilled_states_group.to_sql} THEN payment_total
WHEN state IS NOT NULL THEN payment_total - total
ELSE 0 END
) AS balance_value
SQL
end
# The resulting orders are in states that belong after the checkout. Only these can be considered
# for a customer's balance.
def left_join_complete_orders
<<-SQL.strip_heredoc
LEFT JOIN spree_orders ON spree_orders.customer_id = customers.id
AND #{complete_orders.to_sql}
SQL
end
def complete_orders
states_group = prior_to_completion_states.map { |state| Arel::Nodes.build_quoted(state) }
Arel::Nodes::NotIn.new(Spree::Order.arel_table[:state], states_group)
end
def non_fulfilled_states_group
states_group = non_fulfilled_states.map { |state| Arel::Nodes.build_quoted(state) }
Arel::Nodes::Grouping.new(states_group)
end
# All the states an order can be in before completing the checkout
def prior_to_completion_states
%w(cart address delivery payment)
end
# All the states of a complete order but that shouldn't count towards the balance. Those that the
# customer won't enjoy.
def non_fulfilled_states
%w(canceled returned)
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class ColumnPreferenceSet < ModelSet
def initialize(collection, attributes = {})
super(ColumnPreference, collection, attributes, nil, nil )
end
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
module Sets
class EnterpriseFeeSet < ModelSet
def initialize(attributes = {})
super(EnterpriseFee, EnterpriseFee.all,
attributes,
proc { |attrs| attrs[:name].blank? })
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class EnterpriseSet < ModelSet
def initialize(collection, attributes = {})
super(Enterprise, collection, attributes)
end
end
end

View File

@@ -0,0 +1,69 @@
# frozen_string_literal: true
# Tableless model to handle updating multiple models at once from a single form
module Sets
class ModelSet
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :collection
def initialize(klass, collection, attributes = {}, reject_if = nil, delete_if = nil)
@klass, @collection, @reject_if, @delete_if = klass, collection, reject_if, delete_if
# Set here first, to ensure that we apply collection_attributes to the right collection
@collection = attributes[:collection] if attributes[:collection]
attributes.each do |name, value|
public_send("#{name}=", value)
end
end
def collection_attributes=(collection_attributes)
collection_attributes.each do |_k, attributes|
# attributes == {:id => 123, :next_collection_at => '...'}
found_element = @collection.detect do |element|
element.id.to_s == attributes[:id].to_s && !element.id.nil?
end
if found_element.nil?
@collection << @klass.new(attributes) unless @reject_if.andand.call(attributes)
else
found_element.assign_attributes(attributes.except(:id))
end
end
end
def errors
errors = ActiveModel::Errors.new self
full_messages = @collection
.map { |model| model.errors.full_messages }
.flatten
full_messages.each { |message| errors.add(:base, message) }
errors
end
def save
collection_to_delete.each(&:destroy)
collection_to_keep.all?(&:save)
end
def collection_to_delete
# Remove all elements to be deleted from collection and return them
# Allows us to render @model_set.collection without deleted elements
deleted = []
@collection = collection.to_a
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes) }
end
def persisted?
false
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class OrderCycleSet < ModelSet
def initialize(collection, attributes = {})
super(OrderCycle, collection, attributes)
end
end
end

View File

@@ -0,0 +1,142 @@
# frozen_string_literal: true
module Sets
class ProductSet < ModelSet
def initialize(attributes = {})
super(Spree::Product, [], attributes)
end
def save
@collection_hash.each_value.all? do |product_attributes|
update_product_attributes(product_attributes)
end
end
def collection_attributes=(attributes)
@collection = Spree::Product
.where(id: attributes.each_value.map { |product| product[:id] })
@collection_hash = attributes
end
private
# A separate method of updating products was required due to an issue with
# the way Rails' assign_attributes and update behave when
# delegated attributes of a nested object are updated via the parent object
# (ie. price of variants). Updating such attributes by themselves did not
# work using:
#
# product.update(variants_attributes: [{ id: y, price: xx.x }])
#
# and so an explicit call to update on each individual variant was
# required. ie:
#
# variant.update( { price: xx.x } )
#
def update_product_attributes(attributes)
split_taxon_ids!(attributes)
product = find_model(@collection, attributes[:id])
return if product.nil?
update_product(product, attributes)
end
def split_taxon_ids!(attributes)
attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present?
end
def update_product(product, attributes)
original_supplier = product.supplier_id
return false unless update_product_only_attributes(product, attributes)
ExchangeVariantDeleter.new.delete(product) if original_supplier != product.supplier_id
update_product_variants(product, attributes) &&
update_product_master(product, attributes)
end
def update_product_only_attributes(product, attributes)
variant_related_attrs = [:id, :variants_attributes, :master_attributes]
product_related_attrs = attributes.except(*variant_related_attrs)
return true if product_related_attrs.blank?
product.assign_attributes(product_related_attrs)
validate_presence_of_unit_value_in_product(product)
product.errors.empty? && product.save
end
def validate_presence_of_unit_value_in_product(product)
product.variants.each do |variant|
validate_presence_of_unit_value_in_variant(product, variant)
end
end
def validate_presence_of_unit_value_in_variant(product, variant)
return unless %w(weight volume).include?(product.variant_unit)
return if variant.unit_value.present?
product.errors.add(:unit_value, "can't be blank")
end
def update_product_variants(product, attributes)
return true unless attributes[:variants_attributes]
update_variants_attributes(product, attributes[:variants_attributes])
end
def update_product_master(product, attributes)
return true unless attributes[:master_attributes]
create_or_update_variant(product, attributes[:master_attributes])
end
def update_variants_attributes(product, variants_attributes)
variants_attributes.each do |attributes|
create_or_update_variant(product, attributes)
end
end
def create_or_update_variant(product, variant_attributes)
variant = find_model(product.variants_including_master, variant_attributes[:id])
if variant.present?
variant.update(variant_attributes.except(:id))
else
create_variant(product, variant_attributes)
end
end
def create_variant(product, variant_attributes)
on_hand = variant_attributes.delete(:on_hand)
on_demand = variant_attributes.delete(:on_demand)
variant = product.variants.create(variant_attributes)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant, variant_attributes)
raise e
end
end
def notify_bugsnag(error, product, variant, variant_attributes)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant_attributes, variant_attributes)
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def find_model(collection, model_id)
collection.find do |model|
model.id.to_s == model_id.to_s && model.persisted?
end
end
end
end

View File

@@ -0,0 +1,38 @@
# frozen_string_literal: true
module Sets
class VariantOverrideSet < ModelSet
def initialize(collection, attributes = {})
super(VariantOverride,
collection,
attributes,
nil,
proc { |attrs, tag_list| deletable?(attrs, tag_list) } )
end
private
def deletable?(attrs, tag_list)
attrs['price'].blank? &&
attrs['count_on_hand'].blank? &&
attrs['default_stock'].blank? &&
attrs['resettable'].blank? &&
attrs['sku'].nil? &&
attrs['on_demand'].nil? &&
tag_list.empty?
end
# Overrides ModelSet method to check presence of a tag_list (which is not an attribute)
# This method will delete VariantOverrides that have no values (see deletable? above)
# If the user sets all values to nil in the UI the VO will be deleted from the DB
def collection_to_delete
deleted = []
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes, e.tag_list) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes, e.tag_list) }
end
end
end

View File

@@ -35,7 +35,7 @@
.row{ 'ng-if' => 'shop_id && RequestMonitor.loading' }
.sixteen.columns.alpha#loading
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1
=t :loading_customers

View File

@@ -1,16 +1,16 @@
-# Render only the calculator settings and not the surrounding form
- enterprise_fee_set = ModelSet.new(EnterpriseFee, EnterpriseFee.where(:id => enterprise_fee.id))
- enterprise_fee_set = Sets::ModelSet.new(EnterpriseFee, EnterpriseFee.where(:id => enterprise_fee.id))
- calculator_form_string = nil
- form_for enterprise_fee_set, :as => :enterprise_fee_set, :url => '' do |form|
- form_for enterprise_fee_set, :as => :sets_enterprise_fee_set, :url => '' do |form|
- form.fields_for :collection do |f|
- calculator_form_string = capture do
- if !enterprise_fee.new_record?
.calculator-settings
= f.fields_for :calculator do |calculator_form|
- preference_fields(enterprise_fee.calculator, calculator_form).each do |field|
.field.alpha.one.columns
%span.field
= field[:label]
.field.omega.two.columns
%p.field
= field[:field]
= calculator_form_string

View File

@@ -6,7 +6,7 @@
.four.columns.alpha
= render 'admin/shared/side_menu'
.one.column &nbsp;
.eleven.columns.omega.fullwidth_inputs
.eight.columns.omega.fullwidth_inputs
= render 'form_primary_details', f: f
= render 'form_users', f: f
= render 'form_about', f: f

View File

@@ -6,9 +6,9 @@
%input{ hidden: "true", name: "sells", ng: { required: true, pattern: "/^(none|own|any)$/", model: 'sells', value: "sells"} }
.row
.options.sixteen.columns.alpha
.options.container
- if @enterprise.is_primary_producer
.basic_producer.option.one-third.column.alpha
.basic_producer.option.six.columns.alpha
%a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } }
.top
%h3= t('.producer_profile')
@@ -17,7 +17,7 @@
%p.description
= t('.producer_description_text')
.producer_shop.option.one-third.column
.producer_shop.option.six.columns
%a.full-width.button.selector{ ng: { click: "sells='own'", class: "{selected: sells=='own'}" } }
.top
%h3= t('.producer_shop')
@@ -28,7 +28,7 @@
%br
= t('.producer_shop_description_text2')
.full_hub.option.one-third.column.omega
.full_hub.option.six.columns.omega
%a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } }
.top
%h3= t('.producer_hub')

View File

@@ -9,7 +9,7 @@
%columns-dropdown{ action: "#{controller_name}_#{action_name}" }
.row{ 'ng-if' => '!loaded' }
.sixteen.columns.alpha#loading
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1= t('.loading_enterprises')
.row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredEnterprises.length == 0'}
%h1#no_results= t('.no_enterprises_found')

View File

@@ -31,8 +31,7 @@
\/
= af.label :country_id, t(:country)
%span.required *
%div{ "ng-controller" => "countryCtrl" }
.four.columns
%input.ofn-select2.fullwidth#enterprise_address_attributes_state_id{ name: 'enterprise[address_attributes][state_id]', type: 'number', data: 'countriesById[Enterprise.address.country_id].states', placeholder: t('admin.choose'), ng: { model: 'Enterprise.address.state_id' } }
.four.columns.omega
%input.ofn-select2.fullwidth#enterprise_address_attributes_country_id{ name: 'enterprise[address_attributes][country_id]', type: 'number', data: 'countries', placeholder: t('admin.choose'), ng: { model: 'Enterprise.address.country_id' } }
.four.columns{ "ng-controller" => "countryCtrl" }
%input.ofn-select2.fullwidth#enterprise_address_attributes_state_id{ name: 'enterprise[address_attributes][state_id]', type: 'number', data: 'countriesById[Enterprise.address.country_id].states', placeholder: t('admin.choose'), ng: { model: 'Enterprise.address.state_id' } }
.four.columns.omega{ "ng-controller" => "countryCtrl" }
%input.ofn-select2.fullwidth#enterprise_address_attributes_country_id{ name: 'enterprise[address_attributes][country_id]', type: 'number', data: 'countries', placeholder: t('admin.choose'), ng: { model: 'Enterprise.address.country_id' } }

View File

@@ -16,11 +16,11 @@
%td= link_to t(:edit), edit_admin_payment_method_path(payment_method)
%br
.row
.six.columns.alpha
.eight.columns
%a.button{ href: "#{admin_payment_methods_path}"}
= t('.manage')
%i.icon-arrow-right
.five.columns.omega.text-right
.eight.columns.text-right
%a.button{ href: "#{new_admin_payment_method_path}"}
= t('.create_button')
%i.icon-plus

View File

@@ -1,19 +1,17 @@
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label :name, t('.name')
%span.required *
.eight.columns.omega
= f.text_field :name, { placeholder: t('.name_placeholder') }
.three.columns.alpha
= f.label :name, t('.name')
%span.required *
.eight.columns.omega
= f.text_field :name, { placeholder: t('.name_placeholder') }
- if @groups.present?
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label :group_ids, t('.groups')
%div{'ofn-with-tip' => t('.groups_tip')}
%a= t('admin.whats_this')
.eight.columns.omega
= f.collection_select :group_ids, @groups, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: t('.groups_placeholder')
.three.columns.alpha
= f.label :group_ids, t('.groups')
%div{'ofn-with-tip' => t('.groups_tip')}
%a= t('admin.whats_this')
.eight.columns.omega
= f.collection_select :group_ids, @groups, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: t('.groups_placeholder')
.row
.three.columns.alpha
@@ -25,20 +23,19 @@
= f.label :is_primary_producer, t('.producer')
- if spree_current_user.admin?
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label :sells, t('.sells')
%div{'ofn-with-tip' => t('.sells_tip')}
%a= t('admin.whats_this')
.two.columns
= f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.none'), value: "none"
.two.columns
= f.radio_button :sells, "own", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.own'), value: "own"
.four.columns.omega
= f.radio_button :sells, "any", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.any'), value: "any"
.three.columns.alpha
= f.label :sells, t('.sells')
%div{'ofn-with-tip' => t('.sells_tip')}
%a= t('admin.whats_this')
.two.columns
= f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.none'), value: "none"
.two.columns
= f.radio_button :sells, "own", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.own'), value: "own"
.four.columns.omega
= f.radio_button :sells, "any", 'ng-model' => 'Enterprise.sells'
= f.label :sells, t('.any'), value: "any"
.row
.three.columns.alpha
%label= t('.visible_in_search')
@@ -56,10 +53,11 @@
= f.label :permalink, t('.permalink')
%div{'ofn-with-tip' => t('.permalink_tip', link: main_app.root_url)}
%a= t('admin.whats_this')
.six.columns
.eight.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} }" }
.two.columns.omega
%img.spinner{ src: image_path("spinning-circles.svg"), width: "30px", ng: { show: "checking" } }
%div{ng: {show: "checking", cloak: true}, style: "width: 30px; height: 30px;"}
= render partial: "components/spinner"
%span{ ng: { class: 'availability.toLowerCase()', hide: "checking" } }
{{ availability }}
%i{ ng: { class: "{'icon-ok-sign': availability == 'Available', 'icon-remove-sign': availability == 'Unavailable'}" } }

View File

@@ -15,11 +15,11 @@
%td= link_to t(:edit), edit_admin_shipping_method_path(shipping_method)
%br
.row
.six.columns.alpha
.eight.columns
%a.button{ href: "#{admin_shipping_methods_path}"}
= t('.manage')
%i.icon-arrow-right
.five.columns.omega.text-right
.eight.columns.text-right
%a.button{ href: "#{new_admin_shipping_method_path}"}
= t('.create_button')
%i.icon-plus

View File

@@ -1,104 +1,95 @@
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_message", t('.shopfront_message')
.eight.columns.omega
%text-angular{'ng-model' => 'Enterprise.preferred_shopfront_message', 'id' => 'enterprise_preferred_shopfront_message', 'name' => 'enterprise[preferred_shopfront_message]', 'class' => 'text-angular textangular-strip', 'ta-paste' => "stripFormatting($html)", "textangular-links-target-blank" => true,
'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]",
'placeholder' => t('.shopfront_message_placeholder')}
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_message", t('.shopfront_message')
.eight.columns.omega
%text-angular{'ng-model' => 'Enterprise.preferred_shopfront_message', 'id' => 'enterprise_preferred_shopfront_message', 'name' => 'enterprise[preferred_shopfront_message]', 'class' => 'text-angular textangular-strip', 'ta-paste' => "stripFormatting($html)", "textangular-links-target-blank" => true,
'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]",
'placeholder' => t('.shopfront_message_placeholder')}
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_closed_message", t('.shopfront_closed_message')
.eight.columns.omega
%text-angular{'ng-model' => 'Enterprise.preferred_shopfront_closed_message', 'id' => 'enterprise_preferred_shopfront_closed_message', 'name' => 'enterprise[preferred_shopfront_closed_message]', 'class' => 'text-angular textangular-strip', 'ta-paste' => "stripFormatting($html)", "textangular-links-target-blank" => true,
'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]",
'placeholder' => t('.shopfront_closed_message_placeholder')}
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_closed_message", t('.shopfront_closed_message')
.eight.columns.omega
%text-angular{'ng-model' => 'Enterprise.preferred_shopfront_closed_message', 'id' => 'enterprise_preferred_shopfront_closed_message', 'name' => 'enterprise[preferred_shopfront_closed_message]', 'class' => 'text-angular textangular-strip', 'ta-paste' => "stripFormatting($html)", "textangular-links-target-blank" => true,
'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]",
'placeholder' => t('.shopfront_closed_message_placeholder')}
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_taxon_order", t('.shopfront_category_ordering')
%br
= t('.shopfront_category_ordering_note')
.eight.columns.omega
%textarea.fullwidth{ id: 'enterprise_preferred_shopfront_taxon_order', name: 'enterprise[preferred_shopfront_taxon_order]', rows: 6, 'ng-model' => 'Enterprise.preferred_shopfront_taxon_order', 'ofn-taxon-autocomplete' => '', 'multiple-selection' => 'true', placeholder: 'Category' }
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_taxon_order", t('.shopfront_category_ordering')
%br
= t('.shopfront_category_ordering_note')
.eight.columns.omega
%textarea.fullwidth{ id: 'enterprise_preferred_shopfront_taxon_order', name: 'enterprise[preferred_shopfront_taxon_order]', rows: 6, 'ng-model' => 'Enterprise.preferred_shopfront_taxon_order', 'ofn-taxon-autocomplete' => '', 'multiple-selection' => 'true', placeholder: 'Category' }
.row
.alpha.eleven.columns
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_order_cycle_order", t(:sort_order_cycles_on_shopfront_by)
.three.columns
= radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_open_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' }
= label :enterprise, :preferred_shopfront_order_cycle_order_orders_open_at, t('.open_date')
.five.columns.omega
= radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_close_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' }
= label :enterprise, :preferred_shopfront_order_cycle_order_orders_close_at, t('.close_date')
.three.columns.alpha
= f.label "enterprise_preferred_shopfront_order_cycle_order", t(:sort_order_cycles_on_shopfront_by)
.three.columns
= radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_open_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' }
= label :enterprise, :preferred_shopfront_order_cycle_order_orders_open_at, t('.open_date')
.five.columns.omega
= radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_close_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' }
= label :enterprise, :preferred_shopfront_order_cycle_order_orders_close_at, t('.close_date')
.row
.alpha.eleven.columns
.three.columns.alpha
%label= t '.shopfront_requires_login'
%div{'ofn-with-tip' => t('.shopfront_requires_login_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :require_login, false, "ng-model" => "Enterprise.require_login", "ng-value" => "false"
= f.label :require_login, t('.shopfront_requires_login_false'), value: :false
.five.columns.omega
= f.radio_button :require_login, true, "ng-model" => "Enterprise.require_login", "ng-value" => "true"
= f.label :require_login, t('.shopfront_requires_login_true'), value: :true
.three.columns.alpha
%label= t '.shopfront_requires_login'
%div{'ofn-with-tip' => t('.shopfront_requires_login_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :require_login, false, "ng-model" => "Enterprise.require_login", "ng-value" => "false"
= f.label :require_login, t('.shopfront_requires_login_false'), value: :false
.five.columns.omega
= f.radio_button :require_login, true, "ng-model" => "Enterprise.require_login", "ng-value" => "true"
= f.label :require_login, t('.shopfront_requires_login_true'), value: :true
.row{ng: {if: "!Enterprise.require_login"}}
.alpha.eleven.columns
.three.columns.alpha
%label= t '.allow_guest_orders'
%div{'ofn-with-tip' => t('.allow_guest_orders_tip')}
%a= t 'admin.whats_this'
.eight.columns.omega
.row
.three.columns.alpha
= f.radio_button :allow_guest_orders, true, "ng-model" => "Enterprise.allow_guest_orders", "ng-value" => "true"
= f.label :allow_guest_orders, t('.allow_guest_orders_true'), value: :true
.five.columns.omega
= f.radio_button :allow_guest_orders, false, "ng-model" => "Enterprise.allow_guest_orders", "ng-value" => "false"
= f.label :allow_guest_orders, t('.allow_guest_orders_false'), value: :false
.row.warning{ng: {show: 'Enterprise.allow_guest_orders && Enterprise.allow_order_changes'}}
.eight.columns.alpha.omega
%i.icon-warning-sign
= t '.recommend_require_login'
.three.columns.alpha
%label= t '.allow_guest_orders'
%div{'ofn-with-tip' => t('.allow_guest_orders_tip')}
%a= t 'admin.whats_this'
.eight.columns.omega
.row
.three.columns.alpha
= f.radio_button :allow_guest_orders, true, "ng-model" => "Enterprise.allow_guest_orders", "ng-value" => "true"
= f.label :allow_guest_orders, t('.allow_guest_orders_true'), value: :true
.five.columns.omega
= f.radio_button :allow_guest_orders, false, "ng-model" => "Enterprise.allow_guest_orders", "ng-value" => "false"
= f.label :allow_guest_orders, t('.allow_guest_orders_false'), value: :false
.row.warning{ng: {show: 'Enterprise.allow_guest_orders && Enterprise.allow_order_changes'}}
.eight.columns.alpha.omega
%i.icon-warning-sign
= t '.recommend_require_login'
.row
.alpha.eleven.columns
.three.columns.alpha
%label= t '.allow_order_changes'
%div{'ofn-with-tip' => t('.allow_order_changes_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :allow_order_changes, false, "ng-model" => "Enterprise.allow_order_changes", "ng-value" => "false"
= f.label :allow_order_changes, t('.allow_order_changes_false'), value: :false
.five.columns.omega
= f.radio_button :allow_order_changes, true, "ng-model" => "Enterprise.allow_order_changes", "ng-value" => "true"
= f.label :allow_order_changes, t('.allow_order_changes_true'), value: :true
.three.columns.alpha
%label= t '.allow_order_changes'
%div{'ofn-with-tip' => t('.allow_order_changes_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :allow_order_changes, false, "ng-model" => "Enterprise.allow_order_changes", "ng-value" => "false"
= f.label :allow_order_changes, t('.allow_order_changes_false'), value: :false
.five.columns.omega
= f.radio_button :allow_order_changes, true, "ng-model" => "Enterprise.allow_order_changes", "ng-value" => "true"
= f.label :allow_order_changes, t('.allow_order_changes_true'), value: :true
.row
.alpha.eleven.columns
.three.columns.alpha
%label= t '.enable_subscriptions'
%div{'ofn-with-tip' => t('.enable_subscriptions_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :enable_subscriptions, true
= f.label :enable_subscriptions, t('.enable_subscriptions_true'), value: :true
.five.columns.omega
= f.radio_button :enable_subscriptions, false
= f.label :enable_subscriptions, t('.enable_subscriptions_false'), value: :false
.three.columns.alpha
%label= t '.enable_subscriptions'
%div{'ofn-with-tip' => t('.enable_subscriptions_tip')}
%a= t 'admin.whats_this'
.three.columns
= f.radio_button :enable_subscriptions, true
= f.label :enable_subscriptions, t('.enable_subscriptions_true'), value: :true
.five.columns.omega
= f.radio_button :enable_subscriptions, false
= f.label :enable_subscriptions, t('.enable_subscriptions_false'), value: :false
.row
.alpha.eleven.columns
.three.columns.alpha
%label= t '.customer_names_in_reports'
%div{'ofn-with-tip' => t('.customer_names_tip')}
%a= t 'admin.whats_this'
.three.columns
= radio_button :enterprise, :preferred_show_customer_names_to_suppliers, true
= label :enterprise_preferred_show_customer_names_to_suppliers, t('.customer_names_true'), value: :true
.five.columns.omega
= radio_button :enterprise, :preferred_show_customer_names_to_suppliers, false
= label :enterprise_preferred_show_customer_names_to_suppliers, t('.customer_names_false'), value: :false
.three.columns.alpha
%label= t '.customer_names_in_reports'
%div{'ofn-with-tip' => t('.customer_names_tip')}
%a= t 'admin.whats_this'
.three.columns
= radio_button :enterprise, :preferred_show_customer_names_to_suppliers, true
= label :enterprise_preferred_show_customer_names_to_suppliers, t('.customer_names_true'), value: :true
.five.columns.omega
= radio_button :enterprise, :preferred_show_customer_names_to_suppliers, false
= label :enterprise_preferred_show_customer_names_to_suppliers, t('.customer_names_false'), value: :false

View File

@@ -5,7 +5,8 @@
= "admin.enterprises"
- content_for :page_actions do
= render 'admin/shared/user_guide_link'
%li#user_guide_link
= render 'admin/shared/user_guide_link'
%li#new_product_link
- button_href = spree_current_user.can_own_more_enterprises? ? main_app.new_admin_enterprise_path : '#'

View File

@@ -1,5 +1,5 @@
%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'RequestMonitor.loading' } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1{ ng: { hide: 'orderCycles.length > 0' } }
=t('.loading_order_cycles')
%h1{ ng: { show: 'orderCycles.length > 0' } }

View File

@@ -2,14 +2,14 @@
.alpha.two.columns
= f.label :name, t('.name')
%span.required *
.six.columns.omega
.six.columns.omega.fullwidth_inputs
- if viewing_as_coordinator_of?(@order_cycle)
= f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true, 'ng-disabled' => '!loaded()'
- else
{{ order_cycle.name }}
.two.columns
= f.label :orders_open_at, t('.orders_open')
.omega.six.columns
.omega.six.columns.fullwidth_inputs
- if viewing_as_coordinator_of?(@order_cycle)
= f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at', 'ng-disabled' => '!loaded()', 'change-warning' => 'order_cycle'
- else
@@ -18,11 +18,11 @@
.row
.alpha.two.columns
= f.label :coordinator, t('.coordinator')
.six.columns.omega
.six.columns.omega.fullwidth_inputs
= @order_cycle.coordinator.name
.two.columns
= f.label :orders_close, t('.orders_close')
.six.columns.omega
.six.columns.omega.fullwidth_inputs
- if viewing_as_coordinator_of?(@order_cycle)
= f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at', 'ng-disabled' => '!loaded()', 'change-warning' => 'order_cycle'
- else
@@ -33,7 +33,7 @@
.row
.two.columns.alpha
= f.label :schedule_ids, t('admin.order_cycles.index.schedules')
.twelve.columns
.six.columns
- if viewing_as_coordinator_of?(@order_cycle)
%input.fullwidth.ofn-select2#schedule_ids{ name: 'order_cycle[schedule_ids]',
data: 'schedules',
@@ -43,5 +43,3 @@
ng: { model: 'order_cycle.schedule_ids' } }
- else
%schedule-list{ 'order-cycle' => 'order_cycle' }
.two.columns.omega
&nbsp;

View File

@@ -4,14 +4,14 @@
.alpha.two.columns
= label_tag t('.ready_for')
%span.required *
.six.columns
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_time', 'required' => 'required', 'placeholder' => t('.ready_for_placeholder'), 'ng-model' => 'outgoing_exchange.pickup_time', 'size' => 30, 'maxlength' => 35
%span.icon-question-sign{'ofn-with-tip' => t('admin.order_cycles.exchange_form.pickup_time_tip')}
.six.columns.fullwidth_inputs
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_time', 'required' => 'required', 'placeholder' => t('.ready_for_placeholder'), 'ng-model' => 'outgoing_exchange.pickup_time', 'maxlength' => 35
.two.columns
= label_tag t('.customer_instructions')
.six.columns.omega
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_instructions', 'placeholder' => t('.customer_instructions_placeholder'), 'ng-model' => 'outgoing_exchange.pickup_instructions', 'size' => 30
%span.icon-question-sign{'ofn-with-tip' => t('admin.order_cycles.exchange_form.pickup_instructions_tip')}
.six.columns.omega.fullwidth_inputs
= text_field_tag 'order_cycle_outgoing_exchange_0_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_0_pickup_instructions', 'placeholder' => t('.customer_instructions_placeholder'), 'ng-model' => 'outgoing_exchange.pickup_instructions', 'size' => 30
= label_tag t('.products')
%table.exchanges

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