Compare commits

...

313 Commits

Author SHA1 Message Date
Maikel
1a61357be8 Merge pull request #4024 from openfoodfoundation/transifex
Transifex
2019-07-10 11:17:12 +10:00
Maikel
b2eeb5d687 Merge pull request #4025 from openfoodfoundation/revert-3988-js_render_blocking
Revert "Fix JS render-blocking in Darkswarm"
2019-07-10 11:00:17 +10:00
Maikel
ffeca41ef4 Revert "Fix JS render-blocking in Darkswarm" 2019-07-10 10:24:25 +10:00
Transifex-Openfoodnetwork
68c0936766 Updating translations for config/locales/en_GB.yml 2019-07-10 04:39:47 +10:00
Luis Ramos
23b6267e30 Merge pull request #3916 from luisramos0/schedules_of_coordinator
OC schedules are now filtered to schedules of OCs with same coordinator
2019-07-09 14:21:34 +01:00
Luis Ramos
6872ccfb72 Merge pull request #3963 from luisramos0/hide_invoices
Hide invoices feature
2019-07-09 14:20:50 +01:00
luisramos0
b1b791e4ee Update all locales with the latest Transifex translations 2019-07-09 14:19:35 +01:00
Luis Ramos
1779b759f7 Merge pull request #4017 from openfoodfoundation/transifex
Transifex
2019-07-09 14:15:38 +01:00
Transifex-Openfoodnetwork
3ebbf5a55d Updating translations for config/locales/fr.yml 2019-07-09 22:42:05 +10:00
Luis Ramos
83ce2ac3dc Merge pull request #3988 from Matt-Yorkley/js_render_blocking
Fix JS render-blocking in Darkswarm
2019-07-09 12:30:30 +01:00
Luis Ramos
aaf850a095 Merge pull request #4010 from luisramos0/fuubar_upgrade
Apply 2.1.1 hotfix to master
2019-07-09 12:24:54 +01:00
Luis Ramos
3e9bf8aa1f Merge pull request #3964 from openfoodfoundation/hand-soft-deleted-products-in-orders-and-fulfillment-report
Handle soft deleted products in orders and fulfillment report
2019-07-09 12:19:28 +01:00
luisramos0
95832c96ac Delete redundant spec, this case is now covered in a view spec 2019-07-09 09:15:38 +01:00
Luis Ramos
8a700307d2 Merge pull request #3992 from Matt-Yorkley/cart_button_bug
Fix display bug in cart button on mobile menu
2019-07-08 18:33:36 +01:00
Luis Ramos
1756ddd0e9 Merge pull request #3989 from Matt-Yorkley/preload_primary_font
Preload primary font to speed up page rendering
2019-07-08 17:43:15 +01:00
Luis Ramos
fe71781d70 Merge pull request #3791 from jazzdragon/1766-registration-scroll
1766 registration scroll
2019-07-08 15:10:35 +01:00
Luis Ramos
ec65951ef3 Merge pull request #3959 from jonleighton/issue-1848
Edit tooltip text to make it readable
2019-07-08 15:09:11 +01:00
Transifex-Openfoodnetwork
633c7737e4 Updating translations for config/locales/en_ZA.yml 2019-07-08 04:04:48 +10:00
Pau Perez
f42bf72b32 Fix rubocop violations
It's not affordable to fix the failed Metrics cops in the scope of this
bug fix, but now, with a separate class, it'll be easier.
2019-07-05 18:19:58 +02:00
Pau Perez
8ab77b077b Fix spec
This needs to be revisited. I'm not sure it's the most appropriate spec
as it seems to be testing just part of story.
2019-07-05 18:19:58 +02:00
Pau Perez
9b03833df0 Extract orders & fulfillments default report clasts 2019-07-05 18:19:58 +02:00
Pau Perez
fada30435f DRY some procs from report 2019-07-05 18:19:58 +02:00
Luis Ramos
397729ed3d Merge pull request #4001 from pacodelaluna/3990-display-enterprise-id-in-profile
Display enterprise ID in profile
2019-07-05 11:14:43 +01:00
luisramos0
88312b8e4a Make OrdersHelper.invoice_links simpler 2019-07-05 11:08:51 +01:00
luisramos0
9be3ff90f7 Make OrdersHelper.complete_order_links simpler 2019-07-05 11:06:49 +01:00
luisramos0
e66dea7e03 Improve query in filter_schedules_by_enterprise_id. Makes join to OCs explicity. 2019-07-05 10:37:27 +01:00
Maikel
4c5ed2fa45 Merge pull request #3999 from openfoodfoundation/transifex
Transifex
2019-07-05 11:59:40 +10:00
Maikel
40c0e69f11 Merge pull request #4000 from Matt-Yorkley/v2_migrations
Fix failing migration by clearing schema cache
2019-07-05 10:56:26 +10:00
Transifex-Openfoodnetwork
f3837fb6af Updating translations for config/locales/en_GB.yml 2019-07-05 05:08:46 +10:00
Pau Perez
db7b7bbde2 Fix finding supplier for a deleted product 2019-07-04 18:52:56 +02:00
luisramos0
91da0114e3 Upgrade fuubar to 241 to skip yanked 240 2019-07-04 16:09:36 +01:00
Pau Perez
ee301c5e2f Address Code Climate (rubocop) issues 2019-07-04 17:01:06 +02:00
Pau Perez
4a67ffab25 Add a test case for reports with deleted products 2019-07-04 17:01:06 +02:00
Pau Perez
4d060815d0 Extract unscoping logic into method
DRYies out the code a bit.
2019-07-04 17:01:06 +02:00
Pau Perez
f91bd03c25 Add clarifying comment to report 2019-07-04 17:01:06 +02:00
Pau Perez
7de4ec2a90 Fix orders and fulfillments report
It bypasses the default_scope to retrieve deleted variants in the orders
and fulfillment report.
2019-07-04 17:01:06 +02:00
Pau Perez
812d8cac4a Make complex method a bit more readable 2019-07-04 17:01:06 +02:00
Transifex-Openfoodnetwork
d1faed282f Updating translations for config/locales/es.yml 2019-07-05 00:22:01 +10:00
Transifex-Openfoodnetwork
26195314eb Updating translations for config/locales/ca.yml 2019-07-05 00:09:05 +10:00
François Turbelin
912483660f Put the uid under the permalink 2019-07-04 09:51:32 +02:00
François Turbelin
d12a7c2c76 Apply last suggestions 2019-07-04 09:43:35 +02:00
François Turbelin
101cff02c6 Add locale keys 2019-07-03 23:06:50 +02:00
François Turbelin
5ce2af454e Clean id field 2019-07-03 23:00:36 +02:00
Luis Ramos
951787d456 Merge pull request #3987 from mkllnk/3978-optimise-products-cache
3978 Optimise products cache
2019-07-03 19:59:07 +01:00
Luis Ramos
b35743b6e4 Merge pull request #3968 from mkllnk/3924-rescue-failing-shipment
Rescue checkout on shipping id conflict
2019-07-03 19:56:24 +01:00
Luis Ramos
4fc30ba50e Merge pull request #3994 from openfoodfoundation/dependabot/bundler/knapsack-1.17.2
Bump knapsack from 1.17.1 to 1.17.2
2019-07-03 19:55:20 +01:00
François Turbelin
97a401a307 Display enterprise ID in profile 2019-07-03 18:10:59 +02:00
Pau Pérez Fabregat
232e3b8262 Merge pull request #3993 from openfoodfoundation/dependabot/bundler/simplecov-0.17.0
Bump simplecov from 0.16.1 to 0.17.0
2019-07-03 16:28:56 +02:00
Pau Pérez Fabregat
b3b66d5825 Merge pull request #3970 from mkllnk/444-feature-toggle-use-env
Toggle features via env instead of initializer
2019-07-03 16:27:01 +02:00
Pau Pérez Fabregat
1b532f995c Merge pull request #3919 from mkllnk/db-mirror-script
Db mirror script
2019-07-03 16:24:01 +02:00
Pau Pérez Fabregat
f5544494f0 Merge pull request #3997 from mkllnk/remove-ng-test-script
Remove obsolete Javascript test script
2019-07-03 16:23:41 +02:00
Pau Pérez Fabregat
2de32b54ba Merge pull request #3995 from mkllnk/how-to-not-archive-branches
Remove obsolete script to archive branches
2019-07-03 16:23:15 +02:00
Pau Pérez Fabregat
b6e2dadbb3 Merge pull request #3996 from mkllnk/remove-backup-script
Remove obsolete backup script
2019-07-03 16:22:36 +02:00
Matt-Yorkley
473e635d54 Merge pull request #3982 from luisramos0/mobile_bug
Fixes menu problem with long shop names
2019-07-03 13:30:41 +01:00
Transifex-Openfoodnetwork
3e11cdb5c1 Updating translations for config/locales/fr.yml 2019-07-03 22:14:43 +10:00
Transifex-Openfoodnetwork
842e8da2c3 Updating translations for config/locales/fr.yml 2019-07-03 22:11:35 +10:00
Matt-Yorkley
24f8e6d2ec Fix failing migration by clearing schema cache 2019-07-03 12:59:10 +01:00
Maikel
568d58405d Merge pull request #3986 from openfoodfoundation/transifex
Transifex
2019-07-03 18:46:14 +10:00
Maikel Linke
b23489fa40 Remove obsolete Javascript test script
This script has been replaced by a rake task a long time ago:

  bundle exec rake karma:run   # to run the specs once
  bundle exec rake karma:start # to run the spec after each change

We don't need it any more and it doesn't work on my machine.
2019-07-03 14:50:33 +10:00
Maikel Linke
503658f930 Remove obsolete backup script
This script doesn't work anymore. It was written for old an Australian
production server. We have automatic backups now. And if we wanted to
take a backup manually, we should probably give it a meaningful name,
not using a script.
2019-07-03 14:46:56 +10:00
Maikel Linke
24bf5f0fea Remove obsolete script to archive branches
Apparently, there was a practice to archive branches by tagging them
"archive/branch-name" and then deleting them. We don't practice that
anymore and I would suggest to not start doing it again. Our setup is a
bit different now.

We now use our own forks for feature branches and can have our own,
individual archiving practices in our forks. There is no need to have a
central graveyard of people's "work after progress".

The old feature branches we used to have in the central repository got
archived in another fork:
https://github.com/openfoodfoundation/openfoodnetwork-archive/

Branches associated to pull requests should be deleted after the pull
request has been closed. Github keeps a reference to those branches in
the pull request which is like an archive.

Special branches we still have and delete from time to time:

- transifex: Created for new translations, deleted afterwards.
- dependabot/*: Dependabot always creates pull requests. See above.
- 1-31-1-stable etc: They only live as long as they are supported.

I would also like to delete the old `archive/*` tags. They are in the
openfoodnetwork-archive repository and could confuse developers in the
main repository. Let's keep it clean.
2019-07-03 14:36:16 +10:00
Matt-Yorkley
9d86249bcb Merge pull request #3954 from openfoodfoundation/improve-database-yml
DRY and make database.yml more flexible
2019-07-02 21:43:49 +01:00
Kristina Lim
3e37c8a3f1 Change underscores in CSS classes to hyphens
These were triggering a warning from scss-lint.
2019-07-03 04:43:35 +08:00
Kristina Lim
691d7d735b Reduce padding between top bar menu items 2019-07-03 04:35:44 +08:00
Kristina Lim
6d1dd76590 Cap width of hub name in top bar and wrap 2019-07-03 04:30:06 +08:00
Matt-Yorkley
2eb8121644 Update Docker config 2019-07-02 20:36:21 +01:00
Matt-Yorkley
b685e0d3a7 Add ENV var for db host 2019-07-02 20:34:16 +01:00
Matt-Yorkley
1dd3d86a2b Add prefixes to ENV vars 2019-07-02 20:34:16 +01:00
Pau Perez
7ee41902f2 DRY and make database.yml more flexible
This enables us to fix
https://github.com/openfoodfoundation/ofn-install/issues/387 and brings the
needed flexibility so things like
https://github.com/openfoodfoundation/openfoodnetwork/pull/3887 or any CI don't
require a custom version. That's what ENV vars are for!

For instance, I no longer need to mess with my LXC setup to connect to the DB.
I just need to have 2 env vars.
2019-07-02 20:34:16 +01:00
dependabot-preview[bot]
490ced4a92 Bump knapsack from 1.17.1 to 1.17.2
Bumps [knapsack](https://github.com/ArturT/knapsack) from 1.17.1 to 1.17.2.
- [Release notes](https://github.com/ArturT/knapsack/releases)
- [Changelog](https://github.com/ArturT/knapsack/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ArturT/knapsack/compare/v1.17.1...v1.17.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-07-02 19:21:44 +00:00
dependabot-preview[bot]
a682115fb1 Bump simplecov from 0.16.1 to 0.17.0
Bumps [simplecov](https://github.com/colszowka/simplecov) from 0.16.1 to 0.17.0.
- [Release notes](https://github.com/colszowka/simplecov/releases)
- [Changelog](https://github.com/colszowka/simplecov/blob/master/CHANGELOG.md)
- [Commits](https://github.com/colszowka/simplecov/compare/v0.16.1...v0.17.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-07-02 19:19:31 +00:00
Kristina Lim
653067f58c Omit text for top bar items with icon in large
This affects only top bar menu items for:

* Language
* Profile

This does not update the "Log in" menu item, because the currently
selected icon might not be self explanatory.
2019-07-03 01:49:36 +08:00
Kristina Lim
06ed9c838e Remove extra left padding from top bar menu items 2019-07-03 01:49:35 +08:00
Matt-Yorkley
726d87de6b Fix display bug in cart button on mobile menu 2019-07-02 16:47:22 +01:00
Matt-Yorkley
fbb97c3db8 Fix JS render-blocking in Darkswarm 2019-07-02 13:43:20 +01:00
Matt-Yorkley
cea8cbd924 Preload primary font to speed up page rendering 2019-07-02 13:23:03 +01:00
Maikel Linke
73372a58ef Remove unused and dangerous Exchange#eql? override
The `eql?` override has been added in very early commits but was
actually not used except in a test. It also caused performance problems
since each call to `eql?` would issue two database queries. A developer
would unknowingly trigger these when using `exchanges.uniq`. A mistake
that could have happened again in the future.

I moved the implementation to the test that was actually using it and
made a second test a bit more explicit.
2019-07-02 18:42:02 +10:00
Maikel Linke
287d6a926a Document Exchange 2019-07-02 18:42:02 +10:00
Maikel Linke
04b07a1ff5 Further optimise querying affected exchanges
The implementation queried the database for each incoming variant that
was changed. This rewrite combines ActiveRecord relations so that it
creates only one query. This saves another 5-10% of execution time when
updating enterprise fees on production instances.
2019-07-02 18:42:01 +10:00
Maikel Linke
0da7c93bc6 Collect affected exchanges more efficiently
When an incoming exchange of an order cycle changes, the ProductsCache
queries all affected outgoing exchanges to update them. It was creating
a big collection of exchanges with duplicates and then calling `uniq`.
That call was hitting a custom implementation of `eql?` which is very
inefficient. And since `Exchange.eql?` is ignoring the order cycle id,
it was probably filtering too many exchanges from the collection.

Fixed bug: If two order cycles sell exactly the same variants to the
same shop, the two outgoing exchanges are seen as equal. When the
variants change, ProductsCache would only update one of those two
exchanges, leaving one order cycle out of sync. This case is very rare.
It only happens if there is a shop with two active order cycles selling
exactly the same.

The new uniqueness test looks only at the attributes that are later used
to refresh the cache. I measured a page speed improvement from 90
seconds to 3 seconds (30 times faster).
2019-07-02 18:29:50 +10:00
Maikel Linke
bfe41d4cbc Extract method to simplify 2019-07-02 18:29:50 +10:00
Maikel Linke
3e6e0b73ee Style products cache
The `private` modifier doesn't affect class methods. Class methods have
to be declared as private separately. Also using more guard clauses,
brackets and linebreaks.
2019-07-02 18:29:49 +10:00
Maikel
592a468448 Merge pull request #3960 from mkllnk/test-spec-stability
Remove retries from previously flaky specs
2019-07-02 13:50:16 +10:00
Transifex-Openfoodnetwork
f09b1fc1a8 Updating translations for config/locales/en_ZA.yml 2019-07-02 01:13:09 +10:00
Ecksi
bdbd869b06 Modify English tool tip text in yml file
Modify GB English tool tip text in yml file

Modify US English tool tip text in yml file
2019-06-28 10:59:50 +10:00
Maikel
8b42bd205a Merge pull request #3977 from openfoodfoundation/transifex
Transifex
2019-06-27 09:29:09 +10:00
Transifex-Openfoodnetwork
6108a74fec Updating translations for config/locales/fr.yml 2019-06-27 00:47:45 +10:00
Luis Ramos
5ecab7a0c2 Merge pull request #3971 from Matt-Yorkley/basket_errors
Avoid `undefined method 'order'` errors when removing lineitems from basket
2019-06-26 15:33:26 +01:00
Luis Ramos
d8785bdaba Merge pull request #3932 from luisramos0/shpfront_message
Improve message in private shopfront when user is not logged in or not a customer
2019-06-26 15:31:06 +01:00
Luis Ramos
00841cb537 Merge pull request #3923 from luisramos0/prod_image_error
User friendly error when creating a product with an image in an unsupported format
2019-06-26 15:30:18 +01:00
luisramos0
61c8859da8 Fix problem in array concatenation in orders helper and cover it with specs 2019-06-26 14:17:41 +01:00
luisramos0
e33ce235c4 Hide invoice links in order edit page if invoices are disabled 2019-06-26 14:17:41 +01:00
luisramos0
ec2f99a467 Refactor OrderHelper.order_links: extract some logic from order_links to make it more simple and pass rubocop tests 2019-06-26 14:17:41 +01:00
luisramos0
67b5f08b07 Refactor OrderHelper.order_links: extract links object to separate methods 2019-06-26 14:17:41 +01:00
luisramos0
95170bacd5 Show print invoices button in orders list page only if invoices are enabled in the backoffice 2019-06-26 14:17:25 +01:00
Maikel
eebc49f27c Merge pull request #3887 from pacodelaluna/add-docker-configuration-for-dev-setup
Add docker configuration for quick local setup
2019-06-26 21:23:34 +10:00
luisramos0
8b0296eae6 Use a single query to get filter_schedules_by_enterprise_id 2019-06-26 11:38:03 +01:00
François Turbelin
b6120a9105 Fix docker-compose application yml file 2019-06-26 11:03:20 +02:00
Maikel Linke
5d3dbca9c3 Toggle features via env instead of initializer
A FeatureToggle could be switched via a class_eval in an initializer.
The initializer was installed via ofn-install. We want to get rid of
custom, untracked initializers. Here I'm changing the FeatureToggle
class to use environment variables instead.

This change needs to be followed up with a change in ofn-install to use
the new environment variable. It affects only Australian production.
2019-06-26 11:43:22 +10:00
Maikel Linke
5bbd63bcd8 Add spec for rescuing order saving shipment 2019-06-26 11:32:01 +10:00
Maikel Linke
980b4a86ab Move shipment rescue to its own method 2019-06-26 10:48:51 +10:00
Luis Ramos
118ed79070 Merge pull request #3904 from luisramos0/subs_stock
Fix ofn-on-hand directive and fix usage of variant.on_hand in subscriptions order factory
2019-06-25 20:25:06 +01:00
François Turbelin
623d1e0285 Add missing file 2019-06-25 20:05:55 +02:00
François Turbelin
2dcced8810 Put back application example file as it was 2019-06-25 17:59:37 +02:00
Matt-Yorkley
5100ad6b51 Improve #line_items_for logic and add more tests 2019-06-25 15:33:42 +01:00
luisramos0
7e2bead54d Make finalizedquantity optional in the ofn-on-hand directive and extract avaiable quantity to a separate method for clarity 2019-06-25 14:50:03 +01:00
luisramos0
2b6e6c62dd Simplify order_factory_spec by extracting copy pasted code to method 2019-06-25 14:50:03 +01:00
luisramos0
05a15d9441 Extract method to remove some copy pasted code in order_factory_spec 2019-06-25 14:50:03 +01:00
luisramos0
567196fe0e Fix line item verification of stock on the browser side by adding logic to handle completed orders with some reserved stock
The shopping/orders_spec is now validating this edge case by using all stock available in one of the line items
2019-06-25 14:50:03 +01:00
luisramos0
7bd32d4967 Fix usage of variant.on_hand in subscriptions order factory, we now take on_demand into account
This fixes a problem introduced in 12eab1bfa9 (diff-c3c4192f302cc77e9a8547012fe86ddb), since then variant.on_hand does not return infinity if variant is on_demand
2019-06-25 14:47:47 +01:00
Luis Ramos
9b6f1a5e11 Merge pull request #3958 from Matt-Yorkley/pi_encoding
Improve handling of invalid characters in uploaded CSV files
2019-06-25 14:44:32 +01:00
François Turbelin
84389d1392 Fix setup script with new application YAML file 2019-06-25 14:38:38 +02:00
François Turbelin
da3b467d47 Use https to fetch Postgres repo 2019-06-25 14:09:59 +02:00
François Turbelin
ceffae2efc Delete extra example file 2019-06-25 14:08:18 +02:00
Matt-Yorkley
f19a6f0dec Add tests for class_eval on Spree::Calculator private method #line_items_for 2019-06-25 12:19:16 +01:00
Matt-Yorkley
0a155da273 Avoid undefined method 'order' errors when removing lineitems from basket 2019-06-25 11:13:56 +01:00
François Turbelin
c0d02c8d42 Fix errors while relauching container 2019-06-25 12:05:26 +02:00
François Turbelin
e68d72c0dd Update DOCKER.md 2019-06-25 11:47:54 +02:00
François Turbelin
5dcf456c90 Remove old Rake task for sample data 2019-06-25 11:18:48 +02:00
François Turbelin
79ec3d5a3f Remove extra copy on Docker file 2019-06-25 11:18:48 +02:00
François Turbelin
e893197c49 Use example files instead of extra Docker ones 2019-06-25 11:18:48 +02:00
Matt-Yorkley
60e67ae1a4 Run all setup steps on startup
It's a bit dirty, but it works :)
2019-06-25 11:18:48 +02:00
Matt-Yorkley
53a0f70aa0 Add default login details via ENV vars 2019-06-25 11:18:48 +02:00
Matt-Yorkley
64d6ae445b Change postgres repo from precise to bionic 2019-06-25 11:18:48 +02:00
Matt-Yorkley
5712f67673 Remove phantomjs from dependencies 2019-06-25 11:18:48 +02:00
François Turbelin
9c45444ea6 Clean database.docker.yml file 2019-06-25 11:18:48 +02:00
François Turbelin
4e41a73c4f Enhance Dockerfile 2019-06-25 11:18:48 +02:00
François Turbelin
75267268d7 Update Postgres to 9.5 2019-06-25 11:18:48 +02:00
François Turbelin
7455053879 Update DOCKER.md file 2019-06-25 11:18:48 +02:00
François Turbelin
c5addb8c1d Fix comment spacings, gloupsss 2019-06-25 11:18:48 +02:00
François Turbelin
e5d4b216ba Add DOCKER.md file as documentation 2019-06-25 11:18:48 +02:00
François Turbelin
b4fe44510b Tune up the Rake task 2019-06-25 11:18:48 +02:00
François Turbelin
9df5d78f27 Add a Rake task in order to setup dev env 2019-06-25 11:18:48 +02:00
François Turbelin
f9d72e10d5 Add Docker configuration 2019-06-25 11:18:48 +02:00
Maikel
723499332a Merge pull request #3006 from mkllnk/i18n-error-detection
Raise error on missing translation keys
2019-06-25 16:13:15 +10:00
Maikel Linke
6a06e0ac3b Raise error on missing translation keys 2019-06-25 15:06:56 +10:00
Luis Ramos
5600102729 Merge pull request #3876 from luisramos0/3804_insuf_stock
3804 Fix editing cart when variant on_demand but its on_hand value is zero or negative
2019-06-24 22:24:15 +01:00
luisramos0
a6ea975848 Fix editing cart when variant on_demand but on_hand is zero or negative 2019-06-24 22:07:46 +01:00
Luis Ramos
57917a498b Merge pull request #3946 from luisramos0/missing_translatiojns
Fix last missing translations covered by specs
2019-06-24 20:52:59 +01:00
Luis Ramos
b30da27e92 Merge pull request #3950 from openfoodfoundation/dependabot/bundler/stripe-4.19.0
Bump stripe from 4.18.1 to 4.19.0
2019-06-24 20:44:57 +01:00
Maikel Linke
1e1e88fe51 Rescue checkout on shipping id conflict
Helps to investigate https://github.com/openfoodfoundation/openfoodnetwork/issues/3924

A better solution would be to move the update of shipments out of the
after_save callback and deal with it in controllers. Unfortunately,
that's a big and tricky task.

Since this exception is causing a lot of pain for some Australian
farmers, I introduced more logging here to understand the problem
better. The issue was observed in OFN v1 and may disappear with v2. But
we don't know that and should monitor it.
2019-06-21 16:05:54 +10:00
Maikel
e4c3c1664a Merge pull request #3943 from jonleighton/issue-2112
Bring back ‘Shop for X products at:’ title
2019-06-21 15:06:21 +10:00
Maikel
5b0c8bbaef Merge pull request #3836 from Matt-Yorkley/fast_and_furious2
Improve /shops, /producers, and /groups performance
2019-06-21 15:02:19 +10:00
Maikel
71f396a44f Merge pull request #3896 from luisramos0/clear_unit_desc
Clear product.variants.unit_description if product.variant_unit is items
2019-06-21 15:00:06 +10:00
Maikel
eb162f6b29 Merge pull request #3934 from openfoodfoundation/dependabot/bundler/activerecord-import-1.0.2
Bump activerecord-import from 1.0.1 to 1.0.2
2019-06-21 14:59:03 +10:00
Maikel
5aea361d87 Merge pull request #3709 from luisramos0/2-0-prepare-4-rails-4
Start upgrade to Rails 4
2019-06-21 14:58:24 +10:00
Maikel
3721c017fa Merge pull request #3670 from luisramos0/2-0-without-spree-fe-assets
[Spree Upgrade] Remove dependency to spree frontend - routes and controllers
2019-06-21 14:57:03 +10:00
Maikel
12f0ed9955 Merge pull request #3940 from luisramos0/zones
Remove matching of shipping method zone to user address on checkout
2019-06-21 14:43:40 +10:00
Maikel
4c4881430c Merge pull request #3908 from kristinalim/fix/3843-unit_type_in_product_import
3877,3843 Improve handling of units and unit type in product import
2019-06-21 14:43:01 +10:00
luisramos0
bcc02f35e7 Add new preference to invoice settings: enable invoices 2019-06-19 13:29:30 +01:00
Matt-Yorkley
08b2b19d5e Improve handling of invalid characters in uploaded CSV files 2019-06-19 09:29:59 +01:00
Maikel Linke
cccd35fdf3 Remove retries from previously flaky specs
It's not acceptable to have flaky specs that only pass once in three
tries. Our specs might be more stable now that we use Chrome as test
browser. Otherwise we have to find out why these specs are not stable.
It might be an important bug that happens only sometimes.
2019-06-19 16:48:04 +10:00
Matt-Yorkley
ab1ed53507 Merge pull request #3955 from openfoodfoundation/remove-travis-leftover
No need to keep old and used Travis config file
2019-06-18 16:48:13 +01:00
luisramos0
2b3865855d Simplify shipping_method.include? method 2019-06-18 11:42:34 +01:00
luisramos0
1da18d3386 Fix enterprise model scopes by making ready_for_checkout return enterprises and not ids again and by making not_ready_for_checkout select the id field from the ready_for_checkout scope 2019-06-18 10:52:56 +01:00
luisramos0
b025df1798 Fix a few more rubocop issues 2019-06-18 10:52:56 +01:00
luisramos0
178924af5d Fix long lines in order_cycle model 2019-06-18 10:52:45 +01:00
luisramos0
bb1f7fde2f Remove unused routes related to taxons
t/products was already an old route 4 years ago and t/*id is not currently working in master
2019-06-18 10:49:20 +01:00
luisramos0
c61e7a1672 Remove unused routes
This fix is in spree 2.1 here: ba420fe70e
2019-06-18 10:49:20 +01:00
luisramos0
3c964933b1 Remove stub of a deleted method #deadcode 2019-06-18 10:49:20 +01:00
luisramos0
e7b625edcf Remove spree locale route
This route is not used, ofn code uses query param locale that is picked up in base controller set_locale
2019-06-18 10:49:20 +01:00
luisramos0
eab6cc563b Fix rubocop issues 2019-06-18 10:49:20 +01:00
luisramos0
8daf9b9247 Revert fix in previous commit (orders/populate route used in product import view) and adding a simple fix by setting the product import route to use in the form 2019-06-18 10:49:20 +01:00
luisramos0
5ba0f906a1 For some reason we need the orders populate route to make product import routes work 2019-06-18 10:49:20 +01:00
luisramos0
35c8236c21 Add missing datepicker/cal.gif to ofn assets 2019-06-18 10:49:20 +01:00
luisramos0
b397a8e661 Remove spree_frontend and spree_sample from ofn's gemfile
spree_frontend will only be removed from gemfile.lock when spree_auth_devise is removed from OFN
2019-06-18 10:49:20 +01:00
luisramos0
61def8f2e7 Convert match routes to get routes to be compatible with rails 4 2019-06-18 10:49:20 +01:00
luisramos0
4ff0bf8162 Change gemfile to depend on the different spree gems, not spree as a whole 2019-06-18 10:49:20 +01:00
luisramos0
02b9fca620 Remove unused spree checkout and spree cart routes 2019-06-18 10:49:19 +01:00
luisramos0
40f7d07e27 Remove unnecessary method from spree storecontroller and spree orderscontroller 2019-06-18 10:47:34 +01:00
luisramos0
c56486d7ae Remove unnecessary methods from spree checkoutcontroller 2019-06-18 10:47:34 +01:00
luisramos0
56db90f49e Remove unused checkout controller update action and order controller populate action
Additionally, remove routes of these actions as well as routes already defined in routes.rb (checkout and cart)
2019-06-18 10:47:34 +01:00
luisramos0
a8b4037885 Remove dependency to coupon code logic in checkoutcontroller, ofn doesnt have that feature 2019-06-18 10:47:34 +01:00
luisramos0
fc145d472d Add home controller to ofn codebase from spree frontend 2019-06-18 10:47:34 +01:00
luisramos0
5037cce6f5 Add all the code missing from checkout, order and store controllers to ofn controllers 2019-06-18 10:47:34 +01:00
luisramos0
c399491314 Move spree frontend routes to ofn and transform decorators with class_eval to controller classes 2019-06-18 10:47:34 +01:00
luisramos0
c15c5435ff Fix long lines in exchange model 2019-06-18 10:45:01 +01:00
luisramos0
0cdb49818d Remove distinct from enterprise.ready_for_checkout scope
Adapt use of enterprise scope not_ready_for_checkout to rails 4 by adding enterprises table alias to selected field id
2019-06-18 10:40:49 +01:00
luisramos0
9548f5c5f7 Adapt order_cycle scopes to rail 4 2019-06-18 10:40:49 +01:00
luisramos0
ba91abd20e Adapt exchanges.with_any_variant scope to rails 4 2019-06-18 10:40:49 +01:00
luisramos0
0d07bf2a3b Adapt enterprise.distributing_products scope to rails 4 2019-06-18 10:40:49 +01:00
luisramos0
07a6e62d09 Adapt query in product destroy process to rails 4 2019-06-18 10:40:49 +01:00
luisramos0
e9667ab289 Adapt product.managed_by scope to rails 4 2019-06-18 10:40:49 +01:00
Pau Perez
d003ba81e3 No need to keep old and used Travis config file
If we ever want to come back to Travis we just need to do a git revert.
Meanwhile, no need to care about it.
2019-06-18 11:12:14 +02:00
Luis Ramos
a783f4609a Merge pull request #3942 from jonleighton/exit-if-no-nodenv
Exit script/setup if node is not found
2019-06-17 22:10:44 +01:00
dependabot-preview[bot]
756b3ec73b Bump stripe from 4.18.1 to 4.19.0
Bumps [stripe](https://github.com/stripe/stripe-ruby) from 4.18.1 to 4.19.0.
- [Release notes](https://github.com/stripe/stripe-ruby/releases)
- [Changelog](https://github.com/stripe/stripe-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-ruby/compare/v4.18.1...v4.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 19:19:17 +00:00
Luis Ramos
d9b47ad1b3 Merge pull request #3944 from openfoodfoundation/transifex
Transifex
2019-06-17 16:39:25 +01:00
luisramos0
0235a04530 Add missing translation key in user sessins controller 2019-06-17 15:48:52 +01:00
luisramos0
043abdef39 Add missing translations in the account confirmation process 2019-06-17 15:48:52 +01:00
luisramos0
b372e131a1 Add missing translation key in Spree.
This was fixed in spree 2.1, 7de7fcd502
2019-06-17 15:48:52 +01:00
luisramos0
642d3cbfbd Fix translation key namespace in subs order confirm email subject 2019-06-17 15:48:52 +01:00
Transifex-Openfoodnetwork
930de1b6a2 Updating translations for config/locales/en_ZA.yml 2019-06-17 20:33:27 +10:00
Pau Pérez Fabregat
e3db218e7c Merge pull request #3941 from luisramos0/enza
Add en_ZA.yml copied from en_GB.yml as requested by instance manager
2019-06-17 11:54:09 +02:00
Jon Leighton
fcff302d55 Bring back ‘Shop for X products at:’ title
Fixes #2112

It looks like this bug was accidentally introduced during a refactor in
e5ca494db8. The `bo-html` attribute was
replaced with `ng-html`, but it appears that it should actually have
used `ng-bind-html`
(https://docs.angularjs.org/api/ng/directive/ngBindHtml) because the
former is not a valid AngularJS directive.

It was also necessary to bubble up the `.cta-container` class in order to
get the appropriate styling on the title.
2019-06-17 15:43:15 +10:00
Jon Leighton
150310bf67 Exit script/setup if node is not found
This mirrors what the script already does if ruby is not found
2019-06-17 11:26:54 +10:00
luisramos0
926fc5ee43 Add en_ZA.yml copied from en_GB.yml as requested by instance manager 2019-06-16 20:43:20 +01:00
luisramos0
ccc7a43a06 Bypass validation of shipping methods zone on checkout 2019-06-15 21:11:51 +01:00
luisramos0
e4fcaa5992 Fix typo in package.rb 2019-06-15 20:45:57 +01:00
Luis Ramos
b1d4461c77 Merge pull request #3936 from luisramos0/factories_refactor
Extract factories to separate files and fix rubocop issues
2019-06-14 22:28:55 +01:00
Pau Pérez Fabregat
45c77196b3 Merge pull request #3935 from mkllnk/update-rubocop-style
Update rubocop style
2019-06-14 19:52:22 +02:00
luisramos0
13d41dc1aa Remove duplicate modify entry in factories 2019-06-14 13:59:24 +01:00
luisramos0
5ca382be42 Remove spec/factories.rb from rubocop exceptions, it doesnt look like a problem any longer 2019-06-14 13:55:47 +01:00
luisramos0
3259db69f0 Remove unnecessary code, this code as been accepted in spree code base and included in spree v1.2 2019-06-14 13:43:30 +01:00
luisramos0
6efb71d903 Fix more rubocop issues in spec/factories 2019-06-14 13:42:27 +01:00
Luis Ramos
91401446a5 Merge pull request #3915 from mkllnk/3844-permalink-conflicts
Consider deleted products when creating permalinks
2019-06-14 13:20:35 +01:00
luisramos0
59593c824a Fix rubocop issues in different factories 2019-06-14 13:10:53 +01:00
luisramos0
79cf03b124 Fix rubocop issues in spec/factories 2019-06-14 13:01:08 +01:00
luisramos0
a6b3c26bbe Extract users factories to separate file 2019-06-14 12:50:26 +01:00
luisramos0
8345765ada Extract tag rules factories to separate file 2019-06-14 12:49:17 +01:00
luisramos0
03fb33ba86 Extract variant factory modify to separate file 2019-06-14 12:48:04 +01:00
luisramos0
2c8ce6e4e5 Extract products factories to separate file 2019-06-14 12:45:55 +01:00
luisramos0
3e10c703bf Extract shipments factories to separate file 2019-06-14 12:43:07 +01:00
luisramos0
42f8b2efed Fix static file path in order_cycle_factory 2019-06-14 12:30:36 +01:00
luisramos0
bd493c392a Extract shipping_methods factories to separate file 2019-06-14 12:25:10 +01:00
luisramos0
dc701f55b3 Extract calculators factories to separate file 2019-06-14 12:23:32 +01:00
luisramos0
06e3328ce2 Extract orders factories to separate file 2019-06-14 12:17:24 +01:00
luisramos0
91fcb7c7c4 Extract enterprises factories to separate file 2019-06-14 12:12:45 +01:00
luisramos0
42f2e78b10 Extract subscriptions factories to separate file 2019-06-14 12:10:54 +01:00
luisramos0
0501859c23 Extract order_cycle factories to separate file 2019-06-14 12:10:27 +01:00
luisramos0
a7c970054c Add rescue in product controller create action to avoid server error and send appropriate error message to client when product image upload fails 2019-06-14 10:20:28 +01:00
luisramos0
c97925aa4a Improve message in private shopfront when closed or when user is not logged in 2019-06-14 10:13:33 +01:00
Maikel
5e89c54144 Merge pull request #3931 from openfoodfoundation/RachL-patch-1-3
Update README.md
2019-06-14 14:20:22 +10:00
Maikel
2307df980c Merge pull request #3930 from openfoodfoundation/transifex
Transifex
2019-06-14 14:19:13 +10:00
Maikel Linke
b5f3f98f87 Make BlockLength style apply to all files except todo list 2019-06-14 11:49:27 +10:00
Maikel Linke
ac79e44d9a Fix current rubocop violations 2019-06-14 11:40:09 +10:00
Transifex-Openfoodnetwork
f0977f3c39 Updating translations for config/locales/nb.yml 2019-06-14 08:08:37 +10:00
dependabot-preview[bot]
16ad94f3a8 Bump activerecord-import from 1.0.1 to 1.0.2
Bumps [activerecord-import](https://github.com/zdennis/activerecord-import) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/zdennis/activerecord-import/releases)
- [Changelog](https://github.com/zdennis/activerecord-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zdennis/activerecord-import/compare/v1.0.1...v1.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-13 19:23:40 +00:00
luisramos0
6223a738a7 Fix rubocop issues in collapsible.css 2019-06-13 19:32:33 +01:00
luisramos0
a370216f54 Change default translation in the registration introduction so that it looks better when collapsed 2019-06-13 19:28:31 +01:00
luisramos0
473fb45132 Remove an extra break line to remove html space and make sure alignment is correct 2019-06-13 19:28:10 +01:00
luisramos0
ae0563a58f Merge branch 'master' into 1766-registration-scroll 2019-06-13 19:10:05 +01:00
Luis Ramos
2b593a59f5 Merge pull request #3900 from luisramos0/subs_trans
Missing translations in Subscriptions
2019-06-13 18:51:20 +01:00
Rachel Arnould
d3cb106ae4 Update README.md
In order to benefit from a free unlimited account at Browser stack, they asked us to 
*Give us a shoutout on your GitHub project page. Post an hyperlink and a line about how you use BrowserStack.*
2019-06-13 18:58:58 +02:00
Luis Ramos
ddfd14ebb3 Merge pull request #3905 from openfoodfoundation/dependabot/bundler/i18n-js-3.3.0
Bump i18n-js from 3.2.2 to 3.3.0
2019-06-13 17:33:50 +01:00
luisramos0
1db220e1e0 Remove unnecessary referer setting from products controller spec 2019-06-13 17:22:09 +01:00
Luis Ramos
29bc961e8a Merge pull request #3890 from luisramos0/transl_prod_page
Add missing translation keys to the create product page
2019-06-13 17:13:40 +01:00
Transifex-Openfoodnetwork
216e181c6e Updating translations for config/locales/en_GB.yml 2019-06-14 01:53:18 +10:00
Transifex-Openfoodnetwork
b65b94b6f9 Updating translations for config/locales/en_GB.yml 2019-06-14 01:50:11 +10:00
Luis Ramos
85ee1e7806 Merge pull request #3889 from luisramos0/tag_rules_transl
Fix missing translations in tag rules management page
2019-06-13 16:15:49 +01:00
Luis Ramos
7d24bcf868 Merge pull request #3798 from m-budryte/order_cycles_add_fee_remove_link
the translations for Remove in the exchange forms added
2019-06-13 16:15:28 +01:00
Luis Ramos
1df1ba9403 Merge pull request #3888 from luisramos0/translations
Add translation keys to actions in the order payments list page
2019-06-13 15:36:43 +01:00
Luis Ramos
e15ccdbd74 Merge pull request #3840 from mkllnk/3727-default-credit-cards
Make only reusable credit cards the default card
2019-06-13 14:13:32 +01:00
Luis Ramos
1a2703f630 Merge pull request #3886 from luisramos0/fees_translations
Make a few more things translatable in the Enterprise Fees management page
2019-06-13 14:12:16 +01:00
Luis Ramos
09a5426095 Merge pull request #3880 from kristinalim/fix/3792-optimize_order_cycle_variant_updates
3792 Optimize saving Order Cycle changes
2019-06-13 12:33:22 +01:00
Luis Ramos
bb7a2d7a5e Merge pull request #3252 from mllocs/fix/navbar-right-items-styles
Top navbar redesign and cleanup
2019-06-13 12:04:20 +01:00
Pau Pérez Fabregat
080689ccfa Merge pull request #3917 from luisramos0/subs_visible
Make subs radio button visible to all managers
2019-06-12 16:42:31 +02:00
Matt-Yorkley
785595a951 Reduce initial queries on shop page by ~95% 2019-06-12 09:47:01 +01:00
Matt-Yorkley
0aa8b1a30e Make shop hidden ams injection consistent with other uses 2019-06-12 09:47:01 +01:00
Matt-Yorkley
874fb884b8 Refactor Angular controllers further 2019-06-12 09:47:01 +01:00
Matt-Yorkley
a91ae8947b Only query visible enterprises 2019-06-12 09:47:01 +01:00
Matt-Yorkley
11e83af0b6 Remove duplication of enterprise angular controllers
We can re-use the enterprise and producers controllers here in their respective groups tabs, no need for a third controller
2019-06-12 09:47:01 +01:00
Matt-Yorkley
9703d848ef Improve syntax in injection_helper 2019-06-12 09:47:01 +01:00
Matt-Yorkley
4155b17633 Remove tests that reference removed code 2019-06-12 09:47:01 +01:00
Matt-Yorkley
d5bd058754 Fix groups show page
It can definitely be cleaned up more in the future, but this should get us around a 90% reduction in page load times.
2019-06-12 09:47:01 +01:00
Matt-Yorkley
4ec46c2db6 Fix groups index page 2019-06-12 09:47:01 +01:00
Matt-Yorkley
aca1f92060 Adapt specs to new loading methods 2019-06-12 09:47:01 +01:00
Matt-Yorkley
1b8dfaf049 Fix HashNavigation reload bug 2019-06-12 09:47:01 +01:00
Matt-Yorkley
ae8f1a92e8 Remove "profiles" from /shops 2019-06-12 09:47:01 +01:00
Matt-Yorkley
698d3672a6 List only current taxons for active enterprises 2019-06-12 09:47:01 +01:00
Matt-Yorkley
ed97400a23 Improve producers performance 2019-06-12 09:47:01 +01:00
Matt-Yorkley
45d65baf8e Improve shops performance 2019-06-12 09:47:01 +01:00
Matt-Yorkley
e329b4bfb0 Improve queries for serializers 2019-06-12 09:47:01 +01:00
Maikel Linke
76d77ceb51 Update and generalise database mirror script
The old script was specfic to the use in Australia and out of date. The
new version isn't perfect but works well for me.
2019-06-12 10:19:56 +10:00
luisramos0
f23094df09 Fix file name of js unit test files in admin/order_cycles/controllers 2019-06-11 21:28:55 +01:00
luisramos0
9c539da811 Fix rubocop issue in schedules_controller.collection by extracting method 2019-06-11 21:28:55 +01:00
luisramos0
6cf705ea55 In the order cycle pages (both create and edit, both simple and normal), add OC coordinator to schedules request to the server and make the controller filter schedules of order cycles coordinated by that enterprise id. 2019-06-11 21:28:52 +01:00
Maikel Linke
d1901cbea4 Allow class_eval to have more than 25 lines 2019-06-11 18:01:41 +01:00
Maikel Linke
5c72c35060 Use better credit card factory for subs specs
The specs for subscriptions were creating simple one-time-use credit
cards. They should not be used for subscriptions. After this was fixed
in previous commits, these integration tests failed. Now we use a new
factory that mimics stored credit cards that can be used for
subscriptions.
2019-06-11 18:01:41 +01:00
Maikel Linke
b3b8cb778f Make only reusable credit cards the default card
https://github.com/openfoodfoundation/openfoodnetwork/issues/3727

When a user didn't have a default credit card and then checked out with
a credit card it became the default even when the user didn't intend to
store it. That lead to subscriptions trying to charge a one-time card
which fails.
2019-06-11 18:01:41 +01:00
luisramos0
d8a7f60f40 Make subs radio button visible to all managers 2019-06-11 17:21:13 +01:00
Maikel Linke
a10bb5acbd Consider deleted products when creating permalinks
https://github.com/openfoodfoundation/openfoodnetwork/issues/3844

Spree's implementation and our implementation to create a unique
permalink failed to notice conflicts with soft-deleted products. This
patch looks at deleted products as well.
2019-06-11 18:20:41 +10:00
Kristina Lim
cf11ef8ba2 Ignore parameter list length for ProductImport::EntryValidator
This can be done in a separate PR. More tests might be needed when
extracting to a class.
2019-06-10 04:20:04 +08:00
Kristina Lim
8c69ee67b8 Refactor code for validating variant in reference to existing product 2019-06-10 04:20:04 +08:00
Kristina Lim
70614de955 Require product import rows to match unit of preceding rows 2019-06-10 04:20:03 +08:00
Kristina Lim
5d282f7e9f Update code layout in ProductImporter method 2019-06-10 04:20:03 +08:00
Kristina Lim
73b388da87 Simplify product import all_entries method 2019-06-10 04:20:03 +08:00
Kristina Lim
38519b2bae Simplify building of product import entries for range 2019-06-10 04:19:50 +08:00
Kristina Lim
37101a6b64 Extract build_entries_from_rows for product import 2019-06-09 23:38:36 +08:00
dependabot-preview[bot]
93811a6d8f Bump i18n-js from 3.2.2 to 3.3.0
Bumps [i18n-js](https://github.com/fnando/i18n-js) from 3.2.2 to 3.3.0.
- [Release notes](https://github.com/fnando/i18n-js/releases)
- [Changelog](https://github.com/fnando/i18n-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fnando/i18n-js/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-06 19:19:50 +00:00
luisramos0
ff2901a9d1 Add address suburb translation key to fix edit subscription address page
Keeping these in the root of the translations file so that they can be used in other places later
2019-06-04 20:59:45 +01:00
luisramos0
9944f3e1e6 Make translations of tooltips in subscriptions list page work again 2019-06-04 20:59:45 +01:00
luisramos0
cfcf73b6a1 Make save and saving messages translatable in subscriptions items panel 2019-06-04 20:59:45 +01:00
luisramos0
eb42e81afc Make some text entries translatable in create subscription pages: title in step 1 details and 4 review and date placeholders in step 1 details 2019-06-04 20:59:45 +01:00
luisramos0
64af81a4a2 Make search_by_email text translatable in subscriptions list page 2019-06-04 20:59:42 +01:00
luisramos0
1a4e83d633 Change products controller to clear variants unit description if variant_unit is items 2019-06-01 16:56:17 +01:00
luisramos0
9078a1edaa Add missing translation keys to the create product page: fields names displayed in error messages 2019-05-30 23:50:51 +01:00
luisramos0
32c107ab5a move translation to use relative path so that the new translation key is used 2019-05-30 23:26:23 +01:00
Masha
fbf52dd1f9 removed translations that are not required, left exchange_form.remove key but reference a global remove in the table 2019-05-30 23:08:05 +01:00
Masha
436a2ba0a2 went much shorted way 2019-05-30 23:08:05 +01:00
Masha
412fffba1d the translations for Remove in the exchange forms added 2019-05-30 23:08:05 +01:00
luisramos0
5024da4123 Make tag rules type descriptions in the management page translatable 2019-05-30 20:39:58 +01:00
luisramos0
6620243603 Make the submit button on the add tag rule dialog translatable 2019-05-30 20:22:02 +01:00
luisramos0
48361b146c Add translation keys to actions in the order payments list page 2019-05-30 20:13:17 +01:00
luisramos0
1d4529092f Make fee type options in enterprise fees page translatable 2019-05-30 00:46:20 +01:00
luisramos0
6e08310744 Make the fields in the column calculator values in the enterprise fees page translatable for all fields of all current calculators 2019-05-29 21:57:03 +01:00
luisramos0
5719455731 Make enterprise fees name placeholder translatable 2019-05-29 21:33:30 +01:00
Kristina Lim
99f163d294 Make code style changes for spec file 2019-05-28 21:32:25 +08:00
Kristina Lim
75fdfb3c6a Remove some usage of guard clauses for OC update 2019-05-28 21:32:25 +08:00
Kristina Lim
56a2347ea0 Extract removal of unauthorized OC exchange attributes to method 2019-05-28 21:29:20 +08:00
Kristina Lim
ea8d189d6c Bulk update OC variant changes in OC form 2019-05-28 21:27:43 +08:00
Kristina Lim
bc6f14105e Require spec_helper.rb in spec file for OC form applicator 2019-05-28 21:26:33 +08:00
Kristina Lim
9def777d02 Install activerecord-import gem 2019-05-28 21:26:32 +08:00
luisramos0
4143c50b1f Move joyride translations to the correct path 2019-05-24 23:54:00 +01:00
luisramos0
dc32d282a6 Fix some rubocop issues in menu css file 2019-05-24 23:54:00 +01:00
luisramos0
2654db7c82 Add new menu style as per design 2019-05-24 23:53:56 +01:00
luisramos0
0dc0776705 Separate mobile menu cart code from large menu cart code 2019-05-24 15:17:41 +01:00
luisramos0
ec9d50c824 Add new cart icon and extract cart joyride from cart haml file 2019-05-24 15:16:36 +01:00
luisramos0
60c08347a5 Extract language selector from large menu haml file, add language name and clean up the code that generates the shopping@ label 2019-05-24 15:12:31 +01:00
luisramos0
15360971b0 Move signed_* files to views/shared/menu and add new logged in and profile icons 2019-05-23 15:58:02 +01:00
jazzdragon
37d15a1be3 Add classes to indicate collapsible is for medium screens or smaller 2019-05-04 20:12:12 -07:00
jazzdragon
623471290e Make sure styling doesn't get messed up if resizing down and back up 2019-05-04 19:52:08 -07:00
jazzdragon
a876f81f0a Make collapsible always visible on large screen 2019-05-04 19:48:30 -07:00
jazzdragon
271330d2fc Add collapsible elements and styles 2019-05-04 19:43:46 -07:00
231 changed files with 7153 additions and 2566 deletions

4
.dockerignore Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

47
DOCKER.md Normal file
View File

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

31
Dockerfile Normal file
View File

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

View File

@@ -3,11 +3,12 @@ ruby "2.1.5"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
gem 'i18n', '~> 0.6.11'
gem 'i18n-js', '~> 3.2.2'
gem 'i18n-js', '~> 3.3.0'
gem 'rails', '~> 3.2.22'
gem 'rails-i18n', '~> 3.0.0'
gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
# Patched version. See http://rubysec.com/advisories/CVE-2015-5312/.
gem 'nokogiri', '>= 1.6.7.1'
@@ -19,7 +20,9 @@ gem 'pg'
# OFN-maintained and patched version of Spree v2.0.4. See
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-2.0-upgrade
# for details.
gem 'spree', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_api', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_backend', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_core', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '2-0-stable'
gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable'
@@ -122,7 +125,7 @@ group :test, :development do
gem 'capybara', '>= 2.15.4'
gem 'database_cleaner', '0.7.1', require: false
gem "factory_bot_rails", require: false
gem 'fuubar', '~> 2.4.0'
gem 'fuubar', '~> 2.4.1'
gem 'json_spec', '~> 1.1.4'
gem 'knapsack'
gem 'letter_opener', '>= 1.4.1'

View File

@@ -34,13 +34,6 @@ GIT
revision: 46d6f8f5fd434105b0c69958276d1727a3066a97
branch: 2-0-4-stable
specs:
spree (2.0.4)
spree_api (= 2.0.4)
spree_backend (= 2.0.4)
spree_cmd (= 2.0.4)
spree_core (= 2.0.4)
spree_frontend (= 2.0.4)
spree_sample (= 2.0.4)
spree_api (2.0.4)
rabl (= 0.8.4)
spree_core (= 2.0.4)
@@ -53,8 +46,6 @@ GIT
select2-rails (~> 3.4.7)
spree_api (= 2.0.4)
spree_core (= 2.0.4)
spree_cmd (2.0.4)
thor (>= 0.14.6)
spree_core (2.0.4)
activemerchant (~> 1.34)
acts_as_list (= 0.2.0)
@@ -83,8 +74,6 @@ GIT
spree_api (= 2.0.4)
spree_core (= 2.0.4)
stringex (~> 1.5.1)
spree_sample (2.0.4)
spree_core (= 2.0.4)
GIT
remote: https://github.com/spree/spree_auth_devise.git
@@ -160,6 +149,8 @@ GEM
activesupport (= 3.2.22.5)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activerecord-import (1.0.2)
activerecord (>= 3.2)
activeresource (3.2.22.5)
activemodel (= 3.2.22.5)
activesupport (= 3.2.22.5)
@@ -279,7 +270,7 @@ GEM
devise (>= 2.1.0)
diff-lcs (1.3)
diffy (3.3.0)
docile (1.3.1)
docile (1.3.2)
dry-inflector (0.1.2)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
@@ -456,7 +447,7 @@ GEM
foundation-rails (5.5.2.1)
railties (>= 3.1.0)
sass (>= 3.3.0, < 3.5)
fuubar (2.4.0)
fuubar (2.4.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.1.8)
@@ -492,7 +483,7 @@ GEM
httparty (0.16.2)
multi_xml (>= 0.5.2)
i18n (0.6.11)
i18n-js (3.2.2)
i18n-js (3.3.0)
i18n (>= 0.6.6)
immigrant (0.3.6)
activerecord (>= 3.0)
@@ -514,7 +505,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.11.2)
knapsack (1.17.1)
knapsack (1.17.2)
rake
launchy (2.4.3)
addressable (~> 2.3)
@@ -704,7 +695,7 @@ GEM
shellany (0.0.1)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
simplecov (0.16.1)
simplecov (0.17.0)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
@@ -729,7 +720,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
state_machine (1.2.0)
stringex (1.5.1)
stripe (4.18.1)
stripe (4.19.0)
faraday (~> 0.13)
net-http-persistent (~> 3.0)
therubyracer (0.12.0)
@@ -785,6 +776,7 @@ PLATFORMS
DEPENDENCIES
active_model_serializers (= 0.8.4)
activemerchant (~> 1.78)
activerecord-import
acts-as-taggable-on (~> 3.4)
andand
angular-rails-templates (~> 0.3.0)
@@ -817,7 +809,7 @@ DEPENDENCIES
foundation-icons-sass-rails
foundation-rails
foundation_rails_helper!
fuubar (~> 2.4.0)
fuubar (~> 2.4.1)
geocoder
gmaps4rails
guard
@@ -826,7 +818,7 @@ DEPENDENCIES
guard-rspec (~> 4.7.3)
haml
i18n (~> 0.6.11)
i18n-js (~> 3.2.2)
i18n-js (~> 3.3.0)
immigrant
jquery-migrate-rails
jquery-rails (= 3.0.4)
@@ -868,8 +860,10 @@ DEPENDENCIES
simplecov
skylight (< 2.0)
spinjs-rails
spree!
spree_api!
spree_auth_devise!
spree_backend!
spree_core!
spree_i18n!
spree_paypal_express!
spring (= 1.7.2)

View File

@@ -27,10 +27,13 @@ If you're interested in provisioning a server, see [ofn-install][ofn-install] fo
We also have a [Super Admin Guide][super-admin-guide] to help with configuration of new servers.
## Testing
We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them!
## Licence
Copyright (c) 2012 - 2018 Open Food Foundation, released under the AGPL licence.
Copyright (c) 2012 - 2019 Open Food Foundation, released under the AGPL licence.
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
[slack-invite]: https://openfoodnetwork.org/slack-invite

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

View File

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

After

Width:  |  Height:  |  Size: 648 B

View File

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

After

Width:  |  Height:  |  Size: 812 B

View File

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

After

Width:  |  Height:  |  Size: 451 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -175,10 +175,6 @@ class CheckoutController < Spree::CheckoutController
end
end
def skip_state_validation?
true
end
def load_order
@order = current_order
redirect_to(main_app.shop_path) && return unless @order && @order.checkout_allowed?
@@ -206,11 +202,11 @@ class CheckoutController < Spree::CheckoutController
def redirect_to_cart_path
respond_to do |format|
format.html do
redirect_to cart_path
redirect_to main_app.cart_path
end
format.json do
render json: { path: cart_path }, status: :bad_request
render json: { path: main_app.cart_path }, status: :bad_request
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

@@ -52,7 +52,7 @@ Spree::Admin::BaseController.class_eval do
def active_distributors_not_ready_for_checkout
ocs = OrderCycle.managed_by(spree_current_user).active
distributors = ocs.map(&:distributors).flatten.uniq
Enterprise.where('id IN (?)', distributors).not_ready_for_checkout
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
end
def active_distributors_not_ready_for_checkout_message(distributors)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ Spree::UserSessionsController.class_eval do
if spree_user_signed_in?
respond_to do |format|
format.html {
flash[:success] = t(:logged_in_succesfully)
flash[:success] = t('devise.success.logged_in_succesfully')
redirect_back_or_default(after_sign_in_path_for(spree_current_user))
}
format.js {

View File

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

View File

@@ -1,19 +1,41 @@
require 'open_food_network/enterprise_injection_data'
module InjectionHelper
include SerializerHelper
def inject_enterprises(enterprises = Enterprise.activated.includes(address: :state).all)
inject_json_ams(
'enterprises',
"enterprises",
enterprises,
Api::EnterpriseSerializer,
enterprise_injection_data
)
end
def inject_enterprise_shopfront_list
def inject_groups
select_only = required_attributes EnterpriseGroup, Api::GroupListSerializer
inject_json_ams(
'enterprises',
Enterprise.activated.includes(address: :state).all,
"groups",
EnterpriseGroup.on_front_page.by_position.select(select_only).includes(address: :state).all,
Api::GroupListSerializer
)
end
def inject_enterprise_shopfront(enterprise)
inject_json_ams(
"shopfront",
enterprise,
Api::EnterpriseShopfrontSerializer
)
end
def inject_enterprise_shopfront_list
select_only = required_attributes Enterprise, Api::EnterpriseShopfrontListSerializer
inject_json_ams(
"enterprises",
Enterprise.activated.visible.select(select_only).includes(address: :state).all,
Api::EnterpriseShopfrontListSerializer
)
end
@@ -23,7 +45,12 @@ module InjectionHelper
end
def inject_group_enterprises
inject_json_ams "group_enterprises", @group.enterprises.activated.all, Api::EnterpriseSerializer, enterprise_injection_data
inject_json_ams(
"enterprises",
@group.enterprises.activated.all,
Api::EnterpriseSerializer,
enterprise_injection_data
)
end
def inject_current_hub
@@ -79,11 +106,7 @@ module InjectionHelper
end
def inject_saved_credit_cards
data = if spree_current_user
spree_current_user.credit_cards.with_payment_profile.all
else
[]
end
data = spree_current_user ? spree_current_user.credit_cards.with_payment_profile.all : []
inject_json_ams "savedCreditCards", data, Api::CreditCardSerializer
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,9 @@
module ProductImport
class EntryValidator
def initialize(current_user, import_time, spreadsheet_data, editable_enterprises, inventory_permissions, reset_counts, import_settings)
# rubocop:disable Metrics/ParameterLists
def initialize(current_user, import_time, spreadsheet_data, editable_enterprises,
inventory_permissions, reset_counts, import_settings, all_entries)
@current_user = current_user
@import_time = import_time
@spreadsheet_data = spreadsheet_data
@@ -12,7 +14,9 @@ module ProductImport
@inventory_permissions = inventory_permissions
@reset_counts = reset_counts
@import_settings = import_settings
@all_entries = all_entries
end
# rubocop:enable Metrics/ParameterLists
def self.non_updatable_fields
{
@@ -30,6 +34,7 @@ module ProductImport
assign_enterprise_field(entry)
enterprise_validation(entry)
unit_fields_validation(entry)
variant_of_product_validation(entry)
next if entry.enterprise_id.blank?
@@ -158,6 +163,27 @@ module ProductImport
mark_as_invalid(entry, attribute: 'variant_unit_name', error: I18n.t('admin.product_import.model.conditional_blank')) unless entry.variant_unit_name && entry.variant_unit_name.present?
end
def variant_of_product_validation(entry)
return if entry.producer.blank? || entry.name.blank?
validate_unit_type_unchanged(entry)
validate_variant_unit_name_unchanged(entry)
end
def validate_unit_type_unchanged(entry)
return if entry.unit_type.blank?
reference_entry = all_entries_for_product(entry).first
return if entry.unit_type.to_s == reference_entry.unit_type.to_s
mark_as_not_updatable(entry, "unit_type")
end
def validate_variant_unit_name_unchanged(entry)
return if entry.variant_unit_name.blank?
reference_entry = all_entries_for_product(entry).first
return if entry.variant_unit_name.to_s == reference_entry.variant_unit_name.to_s
mark_as_not_updatable(entry, "variant_unit_name")
end
def producer_validation(entry)
producer_name = entry.producer
@@ -332,6 +358,11 @@ module ProductImport
entry.product_validations = options[:product_validations] if options[:product_validations]
end
def mark_as_not_updatable(entry, attribute)
mark_as_invalid(entry, attribute: attribute,
error: I18n.t("admin.product_import.model.not_updatable"))
end
def import_into_inventory?
@import_settings[:settings].andand['import_into'] == 'inventories'
end
@@ -386,5 +417,19 @@ module ProductImport
object.on_demand = object.count_on_hand.blank? if entry.on_demand.blank?
entry.on_hand_nil = object.count_on_hand.blank?
end
def all_entries_for_product(entry)
all_entries_by_product[entries_by_product_key(entry)]
end
def all_entries_by_product
@all_entries_by_product ||= @all_entries.group_by do |entry|
entries_by_product_key(entry)
end
end
def entries_by_product_key(entry)
[entry.producer.to_s, entry.name.to_s]
end
end
end

View File

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

View File

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

View File

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

View File

@@ -31,13 +31,17 @@ Spree::CreditCard.class_eval do
private
def reusable?
gateway_customer_profile_id.present?
end
def default_missing?
!user.credit_cards.exists?(is_default: true)
end
def ensure_single_default_card
return unless user
return unless is_default? || default_missing?
return unless is_default? || (reusable? && default_missing?)
user.credit_cards.update_all(['is_default=(id=?)', id])
self.is_default = true
end

View File

@@ -166,7 +166,23 @@ Spree::Order.class_eval do
shipments.each do |shipment|
next if shipment.shipped?
update_adjustment! shipment.adjustment if shipment.adjustment
shipment.save # updates included tax
save_or_rescue_shipment(shipment)
end
end
def save_or_rescue_shipment(shipment)
shipment.save # updates included tax
rescue ActiveRecord::RecordNotUnique => error
# This error was seen in production on `shipment.save` above.
# It caused lost payments and duplicate payments due to database rollbacks.
# While we don't understand the cause of this error yet, we rescue here
# because an outdated shipping fee is not as bad as a lost payment.
# And the shipping fee is already up-to-date when this error occurs.
# https://github.com/openfoodfoundation/openfoodnetwork/issues/3924
Bugsnag.notify(error) do |report|
report.add_tab(:order, attributes)
report.add_tab(:shipment, shipment.attributes)
report.add_tab(:shipment_in_db, Spree::Shipment.find_by_id(shipment.id).attributes)
end
end

View File

@@ -112,7 +112,7 @@ Spree::Product.class_eval do
if user.has_spree_role?('admin')
scoped
else
where('supplier_id IN (?)', user.enterprises)
where('supplier_id IN (?)', user.enterprises.select("enterprises.id"))
end
}
@@ -189,7 +189,9 @@ Spree::Product.class_eval do
OpenFoodNetwork::ProductsCache.product_deleted(self) do
touch_distributors
ExchangeVariant.where('exchange_variants.variant_id IN (?)', variants_including_master.with_deleted).destroy_all
ExchangeVariant.
where('exchange_variants.variant_id IN (?)', variants_including_master.with_deleted.
select(:id)).destroy_all
destroy_without_delete_from_order_cycles
end
@@ -216,7 +218,7 @@ Spree::Product.class_eval do
end
def touch_distributors
Enterprise.distributing_products(self).each(&:touch)
Enterprise.distributing_products(id).each(&:touch)
end
def add_primary_taxon_to_taxons
@@ -264,6 +266,7 @@ Spree::Product.class_eval do
raise
end
# Spree creates a permalink already but our implementation fixes an edge case.
def sanitize_permalink
if permalink.blank? || permalink_changed?
requested = permalink.presence || permalink_was.presence || name.presence || 'product'

View File

@@ -53,6 +53,13 @@ Spree::ShippingMethod.class_eval do
spree_calculators.send model_name_without_spree_namespace
end
# This is bypassing the validation of shipping method zones on checkout
# It allows checkout using shipping methods without zones (see issue #3928 for details)
# and it allows checkout with addresses outside of the zones of the selected shipping method
def include?(address)
address.present?
end
def has_distributor?(distributor)
distributors.include?(distributor)
end

View File

@@ -10,7 +10,7 @@
#
module Stock
class Package < Spree::Stock::Package
# Returns all exsiting shipping categories.
# Returns all existing shipping categories.
# It does not filter by the shipping categories of the products in the order.
# It allows checkout of products with categories that are not the shipping methods categories
# It disables the matching of product shipping category with shipping method's category

View File

@@ -14,7 +14,7 @@ module Api
:long_description, :website, :instagram, :linkedin, :twitter,
:facebook, :is_primary_producer, :is_distributor, :phone, :visible,
:email_address, :hash, :logo, :promo_image, :path, :pickup, :delivery,
:icon, :icon_font, :producer_icon_font, :category, :producers, :hubs
:icon, :icon_font, :producer_icon_font, :category
attributes :taxons, :supplied_taxons
@@ -53,16 +53,6 @@ module Api
enterprise_shop_path(enterprise)
end
def producers
relatives = data.relatives[enterprise.id]
ids_to_objs(relatives.andand[:producers])
end
def hubs
relatives = data.relatives[enterprise.id]
ids_to_objs(relatives.andand[:distributors])
end
def taxons
if active
ids_to_objs data.current_distributed_taxons[enterprise.id]

View File

@@ -2,7 +2,8 @@
module Api
class EnterpriseShopfrontListSerializer < ActiveModel::Serializer
attributes :name, :id, :latitude, :longitude, :is_primary_producer, :is_distributor,
:visible, :path, :icon, :icon_font, :producer_icon_font
:path, :icon, :icon_font, :producer_icon_font, :address_id, :sells,
:permalink
has_one :address, serializer: Api::AddressSerializer

View File

@@ -51,7 +51,10 @@ module Api
def producers
ActiveModel::ArraySerializer.new(
enterprise.suppliers, each_serializer: Api::EnterpriseThinSerializer
enterprise.plus_relatives_and_oc_producers(
OrderCycle.not_closed.with_distributor(enterprise)
),
each_serializer: Api::EnterpriseThinSerializer
)
end
@@ -62,8 +65,10 @@ module Api
end
def taxons
taxons = active ? enterprise.current_distributed_taxons : enterprise.distributed_taxons
ActiveModel::ArraySerializer.new(
enterprise.distributed_taxons, each_serializer: Api::TaxonSerializer
taxons, each_serializer: Api::TaxonSerializer
)
end

View File

@@ -0,0 +1,16 @@
module Api
class GroupListSerializer < ActiveModel::Serializer
attributes :id, :name, :permalink, :email, :website, :facebook, :instagram,
:linkedin, :twitter, :enterprises, :state, :address_id
def state
object.address.state.abbr
end
def enterprises
ActiveModel::ArraySerializer.new(
object.enterprises, each_serializer: Api::EnterpriseThinSerializer
)
end
end
end

View File

@@ -0,0 +1,36 @@
class ExchangeVariantBulkUpdater
def initialize(exchange)
@exchange = exchange
end
def update!(variant_ids)
sanitized_variant_ids = variant_ids.map(&:to_i).uniq
existing_variant_ids = @exchange.variant_ids
disassociate_variants!(existing_variant_ids - sanitized_variant_ids)
associate_variants!(sanitized_variant_ids - existing_variant_ids)
uncache_variant_associations
end
private
def disassociate_variants!(variant_ids)
return if variant_ids.blank?
@exchange.exchange_variants.where(variant_id: variant_ids).delete_all
end
def associate_variants!(variant_ids)
return if variant_ids.blank?
new_exchange_variants = variant_ids.map do |variant_id|
ExchangeVariant.new(exchange_id: @exchange.id, variant_id: variant_id)
end
ExchangeVariant.import!(new_exchange_variants)
end
def uncache_variant_associations
@exchange.exchange_variants.reset
@exchange.variants.proxy_association.reset
end
end

View File

@@ -48,7 +48,7 @@ class OrderFactory
attrs[:line_items].each do |li|
next unless variant = Spree::Variant.find_by_id(li[:variant_id])
scoper.scope(variant)
li[:quantity] = stock_limited_quantity(variant.on_hand, li[:quantity])
li[:quantity] = stock_limited_quantity(variant.on_demand, variant.on_hand, li[:quantity])
li[:price] = variant.price
build_item_from(li)
end
@@ -81,9 +81,9 @@ class OrderFactory
@order.payments.create(payment_method_id: attrs[:payment_method_id], amount: @order.reload.total)
end
def stock_limited_quantity(stock, requested)
return requested if opts[:skip_stock_check]
[stock, requested].min
def stock_limited_quantity(variant_on_demand, variant_on_hand, requested)
return requested if opts[:skip_stock_check] || variant_on_demand
[variant_on_hand, requested].min
end
def scoper

View File

@@ -13,6 +13,6 @@ class UploadSanitizer
private
def strip_bom_character
@data.gsub("\xEF\xBB\xBF".force_encoding("UTF-8"), '')
@data.scrub.gsub("\xEF\xBB\xBF".force_encoding("UTF-8"), '')
end
end

View File

@@ -32,7 +32,7 @@
%ofn-select{ :id => angular_id(:enterprise_id), data: 'enterprises', include_blank: true, ng: { model: 'enterprise_fee.enterprise_id' } }
%input{ type: "hidden", name: angular_name(:enterprise_id), ng: { value: "enterprise_fee.enterprise_id" } }
%td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type'
%td= f.ng_text_field :name, { placeholder: 'e.g. packing fee' }
%td= f.ng_text_field :name, { placeholder: t('.name_placeholder') }
%td
%ofn-select{ :id => angular_id(:tax_category_id), data: 'tax_categories', include_blank: true, ng: { model: 'enterprise_fee.tax_category_id' } }
%input{ type: "hidden", name: angular_name(:tax_category_id), 'watch-tax-category' => true }

View File

@@ -71,3 +71,10 @@
.eight.columns.omega
= surround spree.root_url, "/shop" do
{{Enterprise.permalink}}
.row
.three.columns.alpha
= f.label :id, t('.ofn_uid')
%div{'ofn-with-tip' => t('.ofn_uid_tip')}
%a= t('admin.whats_this')
.six.columns
{{Enterprise.id}}

View File

@@ -77,16 +77,15 @@
= 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
- if spree_current_user.admin?
.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
.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

View File

@@ -5,6 +5,11 @@
= form_tag main_app.admin_invoice_settings_path, :method => :put do
.field.align-center
= hidden_field_tag 'preferences[enable_invoices?]', '0'
= check_box_tag 'preferences[enable_invoices?]', '1', Spree::Config[:enable_invoices?]
= label_tag nil, t('.enable_invoices?')
.field.align-center
= hidden_field_tag 'preferences[invoice_style2?]', '0'
= check_box_tag 'preferences[invoice_style2?]', '1', Spree::Config[:invoice_style2?]

View File

@@ -26,7 +26,7 @@
= select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', 'ng-model' => 'enterprise_fee.id', 'ng-options' => 'enterprise_fee.id as enterprise_fee.name for enterprise_fee in enterpriseFeesForEnterprise(enterprise_fee.enterprise_id)'}
= link_to 'Remove', '#', {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_remove', 'ng-click' => 'removeExchangeFee($event, exchange, $index)'}
= link_to t('.remove'), '#', {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_remove', 'ng-click' => 'removeExchangeFee($event, exchange, $index)'}
= f.submit t('.add_fee'), 'ng-click' => 'addExchangeFee($event, exchange)', 'ng-hide' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator'
%td.actions

View File

@@ -25,6 +25,7 @@
= t :edit_order_cycle
- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
= admin_inject_order_cycle_instance
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f|
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }

View File

@@ -30,7 +30,7 @@
%p.red
{{ exception }}
= form_tag false, {class: 'product-import', name: 'importForm', 'ng-show' => 'step == "results"'} do
= form_tag main_app.admin_product_import_path, {class: 'product-import', name: 'importForm', 'ng-show' => 'step == "results"'} do
= render 'import_review' if @importer.table_headings

View File

@@ -1,5 +1,5 @@
%fieldset.no-border-bottom
%legend{ align: 'center'}= t("admin.subscriptions.form.details")
%legend{ align: 'center'}= t(".details")
.row
.seven.columns.alpha.field
%label{ for: 'customer_id'}= t('admin.customer')
@@ -34,11 +34,11 @@
.row
.seven.columns.alpha.field
%label{ for: 'begins_at'}= t('admin.begins_at')
%input.fullwidth#begins_at{ name: 'begins_at', type: 'text', placeholder: 'Select A Date', datepicker: 'subscription.begins_at', required: true, ng: { model: 'subscription.begins_at' } }
%input.fullwidth#begins_at{ name: 'begins_at', type: 'text', placeholder: "#{t('.begins_at_placeholder')}", datepicker: 'subscription.begins_at', required: true, ng: { model: 'subscription.begins_at' } }
.error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.begins_at.$error.required' } }= t(:error_required)
.error{ ng: { repeat: 'error in errors.begins_at', show: 'subscription_details_form.begins_at.$pristine' } } {{ error }}
.two.columns &nbsp;
.seven.columns.omega.field
%label{ for: 'ends_at'}= t('admin.ends_at')
%input.fullwidth#ends_at{ name: 'ends_at', type: 'text', placeholder: 'Optional', datepicker: 'subscription.begins_at', ng: { model: 'subscription.ends_at' } }
%input.fullwidth#ends_at{ name: 'ends_at', type: 'text', placeholder: "#{t('.ends_at_placeholder')}", datepicker: 'subscription.begins_at', ng: { model: 'subscription.ends_at' } }
.error{ ng: { repeat: 'error in errors.ends_at', show: 'subscription_details_form.ends_at.$pristine' } } {{ error }}

View File

@@ -3,7 +3,7 @@
.filter_select.five.columns.alpha
%label{ :for => 'query', ng: {class: '{disabled: !shop_id}'} }=t('admin.quick_search')
%br
%input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!shop_id'}, :placeholder => "Search by email..." }
%input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!shop_id'}, :placeholder => "#{t('.query_placeholder')}" }
.filter_select.four.columns
%label{ :for => 'shop_id', ng: { bind: "shop_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } }
%br

View File

@@ -5,11 +5,11 @@
= render 'products'
%a.button.update.fullwidth{ ng: { class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } }
%span{ ng: {hide: "saved() || saving" } }
SAVE
= t('.save')
%i.icon-save
%span{ ng: {show: "saved() && !saving" } }
SAVED
= t('.saved')
%i.icon-ok-sign
%span{ ng: {show: "saving" } }
SAVING
= t('.saving')
%i.icon-refresh

View File

@@ -1,5 +1,5 @@
%fieldset.no-border-bottom
%legend{ align: 'center'}= t("admin.subscriptions.form.review")
%legend{ align: 'center'}= t(".details")
.row
.eight.columns.alpha
.row

View File

@@ -1,2 +1,2 @@
%p.alert-box.info
= t '.message_html', cart: link_to(t('.cart'), "#{cart_path}#bought-products")
= t '.message_html', cart: link_to(t('.cart'), "#{main_app.cart_path}#bought-products")

View File

@@ -30,6 +30,6 @@
%td.text-right {{ Checkout.cartTotal() | localizeCurrency }}
//= f.submit "Purchase", class: "button", "ofn-focus" => "accordion['payment']"
%a.button.secondary{href: cart_url}
%a.button.secondary{href: main_app.cart_url}
%i.ofn-i_008-caret-left
= t :checkout_back_to_cart

View File

@@ -5,7 +5,7 @@
- content_for(:image) do
= current_distributor.logo.url
= render partial: 'json/injection_ams', locals: enterprises
= inject_enterprise_shopfront(@enterprise)
%shop.darkswarm
- if @shopfront_layout == 'embedded'

View File

@@ -1,10 +1,7 @@
- content_for(:title) do
= t :groups_title
= inject_enterprises
:javascript
angular.module('Darkswarm').value('groups', #{render partial: "json/groups", object: @groups})
= inject_groups
#groups.pad-top.footer-pad{"ng-controller" => "GroupsCtrl"}
.row

View File

@@ -5,12 +5,6 @@
- content_for(:image) do
= @group.logo.url
-# inject all enterprises as "enterprises"
-# it could be more efficient to inject only the enterprises that are related to the group
= inject_enterprises
-# inject enterprises in this group
-# further hubs and producers of these enterprises can't be resolved within this small subset
= inject_group_enterprises
#group-page.row.pad-top.footer-pad{"ng-controller" => "GroupPageCtrl"}
@@ -68,8 +62,8 @@
.small-12.columns
.active_table
%producer.active_table_node.row.animate-repeat{id: "{{producer.path}}",
"ng-repeat" => "producer in filteredEnterprises = (group_producers | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties:'supplied_properties')",
"ng-controller" => "GroupEnterpriseNodeCtrl",
"ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties:'supplied_properties')",
"ng-controller" => "ProducerNodeCtrl",
"ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}",
id: "{{producer.hash}}"}
@@ -89,15 +83,15 @@
= t :groups_hubs
= render "shared/components/enterprise_search"
= render "shops/filters", resource: "group_hubs", property_filters: "| searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles"
= render "shops/filters", resource: "Enterprises.hubs", property_filters: "| searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes"
.row
.small-12.columns
.active_table
%hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}",
"ng-repeat" => "hub in filteredEnterprises = (group_hubs | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | properties:activeProperties:'distributed_properties' | orderBy:['-active', '+orders_close_at'])",
"ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | properties:activeProperties:'distributed_properties' | orderBy:['-active', '+orders_close_at'])",
"ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
"ng-controller" => "GroupEnterpriseNodeCtrl"}
"ng-controller" => "HubNodeCtrl"}
.small-12.columns
= render 'shops/skinny'
= render 'shops/fat'

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