Compare commits

...

160 Commits

Author SHA1 Message Date
Luis Ramos
eafffa2c23 Update all locales with the latest Transifex translations 2020-04-10 13:08:11 +01:00
Luis Ramos
82a4753eec Merge pull request #5191 from openfoodfoundation/transifex
Transifex
2020-04-10 13:05:30 +01:00
Matt-Yorkley
b92e858448 Merge pull request #5197 from Matt-Yorkley/cart-populate-performance
Cart populate performance
2020-04-10 11:41:56 +02:00
Luis Ramos
b5ba2acb21 Merge pull request #5169 from jeduardo824/enhancement/5102-make-shop-names-links
make shop name a link on /account
2020-04-09 22:01:34 +01:00
Transifex-Openfoodnetwork
e97a16cb40 Updating translations for config/locales/ca.yml 2020-04-10 04:14:21 +10:00
Matt-Yorkley
57d87a8042 Eager-load variant stock items
Avoids another N+1
2020-04-09 19:55:08 +02:00
Matt-Yorkley
0ca87580e8 Load variants in cart in one query
Avoids an N+1
2020-04-09 19:55:06 +02:00
Luis Ramos
59e0f3d9f4 Merge pull request #5175 from Matt-Yorkley/memoize-scoper
Memoize ScopeProductToHub in product list
2020-04-09 14:45:57 +01:00
Luis Ramos
fc5aff8c79 Merge pull request #5145 from luisramos0/inv_perf
Performance improvements to Inventory page
2020-04-09 13:56:33 +01:00
Luis Ramos
dd717fe8ac Merge pull request #5184 from Matt-Yorkley/inventory-loading
Inventory loading
2020-04-09 13:26:12 +01:00
Luis Ramos
6341c5fd80 Merge pull request #4964 from luisramos0/po_fix
Fix proxy orders controller in rails 4 by removing the use of responders
2020-04-09 10:12:33 +01:00
Matt-Yorkley
47ac6c1491 Remove unused methods from ProductSimpleSerializer 2020-04-09 09:51:32 +02:00
Matt-Yorkley
6afda141a1 Remove track_inventory_levels conditional
This value is always true in OFN
2020-04-09 09:19:37 +02:00
Matt-Yorkley
5bb2614f9d Refactor PagedFetcher so it makes one request at a time 2020-04-09 09:19:37 +02:00
Matt-Yorkley
b3c968856b Fix some rubocop issues 2020-04-09 09:19:37 +02:00
Matt-Yorkley
b0a7497f2a Remove another N+1 in product serialization 2020-04-09 09:19:37 +02:00
Matt-Yorkley
f959e632ea Modify Spree::Stock::Quantifier to not re-fetch stock items if they are already eager-loaded
This helps to remove a big N+1 here, and will also be very helpful elsewhere in the app
2020-04-09 09:19:37 +02:00
Matt-Yorkley
f9cf826f1c Bring Spree::Stock::Quantifier in to OFN
This is the original unmodified Class from Spree. Modifications added in subsequent commits.
2020-04-09 09:19:36 +02:00
Matt-Yorkley
ececbce596 Only select id in producers query 2020-04-09 09:16:44 +02:00
Matt-Yorkley
1b7ac1a252 Don't re-use fat serializers when thin ones are needed.
This cuts the pageload and query count in half, again.
2020-04-09 09:10:41 +02:00
Transifex-Openfoodnetwork
d31b24786a Updating translations for config/locales/en_NZ.yml 2020-04-09 16:16:24 +10:00
Matt-Yorkley
374bf04118 Merge pull request #5142 from Matt-Yorkley/shops-firefighting
Don't load distributed properties on inactive distributors
2020-04-08 20:45:01 +02:00
Matt-Yorkley
3aff7f62e3 Don't query distributed properties on enterprises that aren't active distributors
Cuts page load on /shops by ~75% (with production data) and removes ~300 expensive and superfluous queries.
2020-04-08 20:08:12 +02:00
Matt-Yorkley
fc5e346a06 Merge pull request #5156 from Matt-Yorkley/closed-shops
Load closed shops in a separate request on /shops page
2020-04-08 20:05:26 +02:00
Luis Ramos
29bbf2fa74 Merge pull request #5088 from coopdevs/do-not-recreate-when-booting-docker
Do not reset the dev env when booting docker
2020-04-07 21:56:09 +01:00
Matt-Yorkley
64c66ddedc Eager-load variant data for overridable products
Cuts query count and page load time in half for this endpoint.
2020-04-07 15:16:32 +02:00
Luis Ramos
e0e2c32d9f Merge pull request #5177 from openfoodfoundation/dependabot/bundler/oj-3.10.6
Bump oj from 3.10.5 to 3.10.6
2020-04-07 11:44:09 +01:00
Matt-Yorkley
003341ef7a Add loading indicator when showing closed shops 2020-04-07 10:40:49 +02:00
Matt-Yorkley
94f8ea2f93 Fix flicker effect showing 3 buttons when clicking "Show Closed Shops" button 2020-04-07 10:31:56 +02:00
Maikel
eb64112b22 Merge pull request #5144 from openfoodfoundation/transifex
Transifex
2020-04-07 16:23:35 +10:00
Maikel
3e14b62b46 Merge pull request #5136 from openfoodfoundation/dependabot/bundler/ddtrace-0.34.1
Bump ddtrace from 0.34.0 to 0.34.1
2020-04-07 16:05:53 +10:00
Maikel
3244650932 Merge pull request #5135 from openfoodfoundation/dependabot/bundler/rubocop-rails-2.5.1
Bump rubocop-rails from 2.5.0 to 2.5.1
2020-04-07 16:05:18 +10:00
Transifex-Openfoodnetwork
b6753a2593 Updating translations for config/locales/fil_PH.yml 2020-04-07 16:03:57 +10:00
Transifex-Openfoodnetwork
1b119805b4 Updating translations for config/locales/fil_PH.yml 2020-04-07 15:15:12 +10:00
Transifex-Openfoodnetwork
edde7689a9 Updating translations for config/locales/de_DE.yml 2020-04-07 13:57:54 +10:00
Transifex-Openfoodnetwork
8060977786 Updating translations for config/locales/en_GB.yml 2020-04-07 13:54:53 +10:00
Transifex-Openfoodnetwork
837a345958 Updating translations for config/locales/en_FR.yml 2020-04-07 13:54:46 +10:00
dependabot-preview[bot]
7c5e511fde Bump oj from 3.10.5 to 3.10.6
Bumps [oj](https://github.com/ohler55/oj) from 3.10.5 to 3.10.6.
- [Release notes](https://github.com/ohler55/oj/releases)
- [Changelog](https://github.com/ohler55/oj/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/ohler55/oj/compare/v3.10.5...v3.10.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-06 19:24:48 +00:00
Luis Ramos
d18a06a0f7 Merge pull request #4783 from luisramos0/stripe_sca_extra_subs
Move all subscriptions services to the OrderManagement engine
2020-04-06 20:20:02 +01:00
Luis Ramos
d23b4fd307 Merge pull request #5174 from coopdevs/change-pool-size
Allow changing the connection pool size
2020-04-06 19:29:09 +01:00
Matt-Yorkley
2cb4c6bec2 Memoize OpenFoodNetwork::ScopeProductToHub
This means we avoid fetching all of the hub's variants every time we scope a product. Applies to every product loaded when displaying a shops's product list.
2020-04-06 19:41:05 +02:00
Pau Pérez Fabregat
924e816a5b Merge pull request #5151 from luisramos0/fix_order_perms
Fix Permissions::Order spec in rails 4
2020-04-06 18:07:39 +02:00
Pau Pérez Fabregat
109da43905 Merge pull request #2 from luisramos0/do-not-recreate-when-booting-docker
Re-add setup instructions removed from docker-compose into Dockerfile…
2020-04-06 16:55:59 +02:00
Pau Perez
33ca6a2096 Allow changing the connection pool size
This allows us to tune for UK. The hypothesis from @kristinalim is:

> From what I understand, it can result to Rails processes waiting for
each other to complete, while the DB server can take more simultaneous
connections.
2020-04-06 16:03:06 +02:00
Eduardo
e7b780f963 make shop name a link on /account 2020-04-06 08:34:24 -03:00
dependabot-preview[bot]
13cba3d244 Bump ddtrace from 0.34.0 to 0.34.1
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.34.0 to 0.34.1.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.34.0...v0.34.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-06 11:27:10 +00:00
Matt-Yorkley
ce45e7cf71 Merge pull request #5162 from Matt-Yorkley/unicorn-worker-killer
Add optional unicorn-worker-killer configs
2020-04-06 13:25:22 +02:00
Matt-Yorkley
ba5a56db14 Make upper and lower bounds configurable 2020-04-06 10:39:51 +02:00
Matt-Yorkley
276dcf4a3b Add optional unicorn-worker-killer configs 2020-04-06 10:39:50 +02:00
Transifex-Openfoodnetwork
dcfb1aec6d Updating translations for config/locales/en_PH.yml 2020-04-06 18:11:07 +10:00
Pau Pérez Fabregat
fde2aac366 Merge pull request #5163 from coopdevs/collect-Ruby-GC
Enable Ruby Runtime Metrics in Datadog
2020-04-05 14:32:54 +02:00
Luis Ramos
63138aef30 Re-add setup instructions removed from docker-compose into Dockerfile and Docker.md 2020-04-05 00:06:31 +01:00
Pau Perez
7612415991 Enable Ruby Runtime Metrics in Datadog
This automatically collects a bunch of Ruby's GC-related metrics that
will come in handy while we tune the Unicorn workers. Some of theres
are:

* runtime.ruby.class_count
* runtime.ruby.gc.malloc_increase_bytes
* runtime.ruby.gc.total_allocated_objects
* runtime.ruby.gc.total_freed_objects
* runtime.ruby.gc.heap_marked_slots
* runtime.ruby.gc.heap_available_slots
* runtime.ruby.gc.heap_free_slots
* runtime.ruby.thread_count

Check https://docs.datadoghq.com/tracing/runtime_metrics/ruby/#data-collected for the complete list.

The cool thing is that

> Runtime metrics can be viewed in correlation with your Ruby services
2020-04-05 00:16:10 +02:00
Luis Ramos
53d901b41b Merge pull request #5064 from luisramos0/fix_sample_data
Fix sample data and custom paper_trail config on order_cycles and schedules on rails 4
2020-04-04 18:29:26 +01:00
Matt-Yorkley
bc859cf9f7 Add api/shops_controller and refactor 2020-04-04 17:02:27 +02:00
Matt-Yorkley
af48cac140 Load closed shops in a separate request on /shops page 2020-04-04 14:06:10 +02:00
Luis Ramos
59bb956677 Merge pull request #5010 from Matt-Yorkley/remove_simple_form
Remove simple_form
2020-04-04 11:53:06 +01:00
Luis Ramos
55b3f4d54f Move search params test case to a different context so that we dont have to set the producer of the products in the order
This is working in master by chance of the factories but breaks in rails 4 because the orders in this test dont have products supplied by the producer which is a necessary condition in the context where it was
2020-04-03 19:47:33 +01:00
Luis Ramos
452ab3a842 Add comment to better explain variant_override_set.collection_to_delete 2020-04-03 15:36:19 +01:00
Luis Ramos
a049e7a433 Add product to includes to avoid N+1 queries to fetch products when VO authorization is done right after this 2020-04-03 14:48:06 +01:00
Transifex-Openfoodnetwork
97063bf47e Updating translations for config/locales/en_GB.yml 2020-04-03 23:24:25 +11:00
Luis Ramos
e64d573337 Revert accidental push of a byebug statement with the direct translations push to master 🙈 2020-04-03 12:06:39 +01:00
Luis Ramos
7858a26e5e Update all locales with the latest Transifex translations 2020-04-03 12:03:40 +01:00
Luis Ramos
4922c05bcf Merge pull request #5139 from openfoodfoundation/transifex
Transifex
2020-04-03 11:28:11 +01:00
Transifex-Openfoodnetwork
d06b7b8606 Updating translations for config/locales/de_DE.yml 2020-04-03 19:26:04 +11:00
Transifex-Openfoodnetwork
72b47fbceb Updating translations for config/locales/de_DE.yml 2020-04-03 19:22:55 +11:00
Luis Ramos
da7b0966be Merge pull request #5133 from Matt-Yorkley/cache-in-hand
Cache counts used in homepage for 24 hours
2020-04-03 08:34:58 +01:00
Luis Ramos
445eb9f287 Merge pull request #5121 from Matt-Yorkley/flaky-maps-js
Update vendor/assets/angular-google-maps.min.js
2020-04-02 22:13:51 +01:00
dependabot-preview[bot]
5b4dd57380 Bump rubocop-rails from 2.5.0 to 2.5.1
Bumps [rubocop-rails](https://github.com/rubocop-hq/rubocop-rails) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/rubocop-hq/rubocop-rails/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop-rails/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop-rails/compare/v2.5.0...v2.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-02 19:15:11 +00:00
Matt-Yorkley
adb61e48c5 Cache counts used in homepage for 24 hours 2020-04-02 19:09:03 +02:00
Matt-Yorkley
838ffdbf00 Add code comment for dependency 2020-04-02 17:33:10 +02:00
Pau Pérez Fabregat
fe8beb5448 Merge pull request #5099 from Matt-Yorkley/ticking-time-bom
Improve BOM
2020-04-02 16:01:48 +02:00
Matt-Yorkley
18ee5254f9 Make Geo service calls more resilient in /shops page
The Geo service is used heavily in the /shops page and especially in the search function. If the google maps js library has failed to load it was throwing a lot of fatal errors, so this change ensures the /shops page can at least: a) load, b) show some shops, and c) search for shops by name (but not location)
2020-04-02 13:56:25 +02:00
Matt-Yorkley
40f8cf660c Update vendor/assets/angular-google-maps.min.js
Fixes an issue where if the js library from maps.googleapis.com failed to load in the <head>, all of our subsequent Angular would completely break.
See: https://github.com/angular-ui/angular-google-maps
Note: `bluebird.js` is a new dependency of `angular-google-maps.js`.
2020-04-02 13:32:55 +02:00
Matt-Yorkley
2c70db7952 Merge pull request #5070 from luisramos0/master_card
Translate credit card brand so that active merchand code handles the payment correctly
2020-04-02 12:18:00 +02:00
Pau Pérez Fabregat
1a38a4e1a7 Merge pull request #5091 from openfoodfoundation/transifex
Transifex
2020-04-02 11:56:29 +02:00
Pau Pérez Fabregat
15d106bb0a Merge pull request #5116 from openfoodfoundation/dependabot/bundler/ddtrace-0.34.0
Bump ddtrace from 0.33.1 to 0.34.0
2020-04-02 11:55:53 +02:00
Pau Pérez Fabregat
c8be2fb89a Merge pull request #5125 from openfoodfoundation/dependabot/bundler/rubocop-0.81.0
Bump rubocop from 0.80.1 to 0.81.0
2020-04-02 11:54:00 +02:00
Transifex-Openfoodnetwork
b1f8f91011 Updating translations for config/locales/fr.yml 2020-04-02 18:03:11 +11:00
Transifex-Openfoodnetwork
e08606a310 Updating translations for config/locales/en_FR.yml 2020-04-02 18:02:57 +11:00
Transifex-Openfoodnetwork
2230d83729 Updating translations for config/locales/pt_BR.yml 2020-04-02 08:41:25 +11:00
dependabot-preview[bot]
d38da02113 Bump rubocop from 0.80.1 to 0.81.0
Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 0.80.1 to 0.81.0.
- [Release notes](https://github.com/rubocop-hq/rubocop/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.80.1...v0.81.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-01 19:20:42 +00:00
Luis Ramos
0e268a171f Switch to console.error so we get a bugsnag alert everytime a user has a problem with their card
Add paymentMethodsAPI specific mapping function, we had some errors in production with mastercards probably caused by ActiveMerchant not handling the card type correctly
2020-04-01 18:58:43 +01:00
Luis Ramos
73c4eedd06 Translate credit card brand so that active merchant code handles the payment correctly
Adds a simple console.log statement in case there is an error adding the
card
2020-04-01 18:58:10 +01:00
Luis Ramos
8a220742f5 Merge pull request #5103 from jeduardo824/bug/5092-remove-blue-setence-in-payment-method-setting
remove blue sentence in payment method setting
2020-04-01 18:56:09 +01:00
Luis Ramos
9fa604db0d Merge pull request #5076 from luisramos0/rails_f_helper
Remove Rails Foundation Helper and improve error display and logging in checkout controller
2020-04-01 18:54:45 +01:00
Transifex-Openfoodnetwork
6083d91d3e Updating translations for config/locales/ca.yml 2020-04-02 01:57:50 +11:00
Transifex-Openfoodnetwork
9bdb396b86 Updating translations for config/locales/ca.yml 2020-04-02 01:54:42 +11:00
Matt-Yorkley
ad42b1b485 Remove pagination limits in BOM
We can re-assess this later, but for now it looks like some of the BOM functionality won't work if results are returned across multiple pages.
2020-04-01 13:43:21 +02:00
Luis Ramos
78170bc709 Merge pull request #4807 from openfoodfoundation/dependabot/bundler/compass-rails-4.0.0
Bump compass-rails from 3.1.0 to 4.0.0
2020-04-01 12:42:42 +01:00
Luis Ramos
957b398a54 Add call to $evalAsync() after Loading and FlashLoader are updated so
that a angular digest is triggered

This is required so that Loading.clear triggers a refresh and makes its placeholder to be cleared
2020-04-01 12:08:55 +01:00
Luis Ramos
c3d25bf163 Make checkout controller send bugsnag alerts on every checkout problem
There are two new situations here: we will see order.errors after update_attributes fails but before order restart; and we will see how often the order is not complete when the workflow finishes (maybe none)
2020-04-01 12:03:39 +01:00
Luis Ramos
ce2a164c66 Stop using f_form_for
Add labels for some fields, this was done automatically by rails foundation helper
2020-04-01 12:03:39 +01:00
Luis Ramos
b898ce1ae1 Make checkout controller add flash error if order contains any type of error
Here we add translations for a particular case where the credit card expiry date is in the past
2020-04-01 12:03:39 +01:00
Luis Ramos
409681b7af Merge pull request #5089 from Matt-Yorkley/checkout-scoping
Cart variant scoping
2020-04-01 11:49:23 +01:00
Luis Ramos
9317549103 Merge pull request #4813 from luisramos0/oc_selector
If there's only one OC available to the customer because there are tag rules hidding other OCs, select that OC by default
2020-04-01 10:44:58 +01:00
dependabot-preview[bot]
b22ad244f9 Bump ddtrace from 0.33.1 to 0.34.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.33.1 to 0.34.0.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.33.1...v0.34.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-31 19:40:01 +00:00
Transifex-Openfoodnetwork
9c17a91215 Updating translations for config/locales/en_GB.yml 2020-04-01 05:07:08 +11:00
Luis Ramos
6a1c541479 Remove specific error color on checkout page so that the error message takes the default foundation error color which is white 2020-03-31 16:32:33 +01:00
Luis Ramos
6c64261868 Merge pull request #5113 from mkllnk/5110-bugsnag-js-logging
Add Bugsnag JS logging
2020-03-31 15:51:12 +01:00
Matt-Yorkley
f437d0f8a0 Report environment correctly in Bugsnag JS 2020-03-31 15:59:17 +02:00
Transifex-Openfoodnetwork
79d6d7cc9e Updating translations for config/locales/de_DE.yml 2020-03-31 22:09:16 +11:00
Matt-Yorkley
e200ece280 Rename partial to bugsnag_js for clarity 2020-03-31 12:57:12 +02:00
Matt-Yorkley
30bf9257ab Move conditional inside partial and use default key as fallback 2020-03-31 12:55:28 +02:00
Matt-Yorkley
03e229da08 Add bugsnag js script to admin layout above all.js 2020-03-31 12:25:42 +02:00
Maikel Linke
28473c9087 Add Bugsnag JS logging 2020-03-31 15:51:04 +11:00
Matt-Yorkley
09c8819e5a Remove unnecessary Bugsnag calls
The Bugsnag notification was just here to see if this was dead code. It's not.
2020-03-30 20:36:45 +02:00
Matt-Yorkley
f7c047b798 Memoize ScopeVariantToHub to avoid fetching the hub's overrides each time 2020-03-30 20:33:27 +02:00
Eduardo
b6da0e2092 remove @payment_method explict variable from the view 2020-03-29 20:32:55 -03:00
Matt-Yorkley
95963c5732 Refactor Angular line_items_controller 2020-03-29 19:54:49 +02:00
Matt-Yorkley
aba1b5b67a Add pagination specs and refactor 2020-03-29 19:54:49 +02:00
Matt-Yorkley
fe58121c7f Fix clear filters button 2020-03-29 19:54:49 +02:00
Matt-Yorkley
5c4a2c2790 Update javascript specs 2020-03-29 19:54:49 +02:00
Matt-Yorkley
a07281910b Fix dropdown placeholders 2020-03-29 19:54:49 +02:00
Matt-Yorkley
72f9da3ac4 Fix regex validation and HTML "type" settings not allowing decimals for final_weight_volume
`final_weight_volume` can be a decimal, and often *is* in production data. Not regarding them as valid was breaking the form submission in various cases
2020-03-29 19:54:49 +02:00
Matt-Yorkley
375b4648dc Add pagination to Bulk Order Management 2020-03-29 19:54:47 +02:00
Transifex-Openfoodnetwork
9af0a39305 Updating translations for config/locales/en_FR.yml 2020-03-29 06:43:01 +11:00
Transifex-Openfoodnetwork
6817231f29 Updating translations for config/locales/fr.yml 2020-03-29 06:42:36 +11:00
Matt-Yorkley
7c7f9551d6 Fix bug in order cycle select dropdown
The date filters were being filled with the string: "Invalid date" because momentjs was not able to parse the input
2020-03-28 15:17:31 +01:00
Matt-Yorkley
d568b45d4a Limit spamming the server with lots of requests
This uses "debounce" to add a little waiting time whilst the user is part-way through selecting dates (or manually typing in the date filter) so we don't make too many unnecessary requests.
2020-03-28 11:20:23 +01:00
Matt-Yorkley
b76a6d15a3 Improve date field query triggers
Don't submit the request if the user is part-way through typing something in the date field and the date is (currently) invalid; this results in the date ranges being broken and triggering a query for *all* results (with no date range).
2020-03-28 11:16:25 +01:00
Luis Ramos
940423acfc Merge pull request #5067 from gao329700254/fix/fix_fr_translation_in_order_cycles_outgoing(4937)
Fix translation missing in order cycles outgoing page (closed #4937)
2020-03-27 22:00:19 +00:00
Transifex-Openfoodnetwork
6a57aa3b29 Updating translations for config/locales/en_FR.yml 2020-03-28 04:08:31 +11:00
Transifex-Openfoodnetwork
ca78e9d0e2 Updating translations for config/locales/fr.yml 2020-03-28 04:08:17 +11:00
Luis Ramos
e705e88007 Merge pull request #5087 from luisramos0/custom_data
Fix bug in subscriptions logging by making versions.custom_data a longer field
2020-03-27 16:04:31 +00:00
Pau Pérez Fabregat
1fbb9fa3df Merge pull request #5083 from openfoodfoundation/transifex
Transifex
2020-03-27 16:51:30 +01:00
Matt-Yorkley
71f00f9283 Remove comment warning about this issue 2020-03-27 14:23:48 +01:00
Matt-Yorkley
7d33a237d0 Add scoping to VariantsStockLevels when variant is not in the order 2020-03-27 14:15:15 +01:00
Matt-Yorkley
857cacb74b Add test for additional case where variant is not in the order 2020-03-27 14:03:17 +01:00
Matt-Yorkley
fbfe663ebc Add variant scoping to VariantStockLevels 2020-03-27 13:38:57 +01:00
Matt-Yorkley
83b90f3167 Add spec variant override test to VariantsStockLevels 2020-03-27 13:31:39 +01:00
Luis Ramos
14fd9a121e Make versions.custom_data text so it can take longer lists 2020-03-27 11:34:01 +00:00
Pau Perez
904e89e325 Do not reset the dev env when booting docker
The current web container's command destroys anything you might have in
your local DB from a previous session, assuming you always want start
from a clean environment. This is hardly the case and makes
`docker-compose up` take quite long. What if you just stopped containers
temporally while developing?

This changes the approach to not assume anything. If you need to install
a new gem or reset your DB just run the commands you would without
docker. You can run anything you want with `docker-compose run web bundle exec
<rails/rake command>` anyway.

For someone setting things for the first time, the `Dockerfile` process
still installs all dependencies.
2020-03-27 12:17:09 +01:00
Transifex-Openfoodnetwork
2e98b0b5c1 Updating translations for config/locales/en_GB.yml 2020-03-27 01:40:20 +11:00
Luis Ramos
523faa670d Remove FoundationRailsHelper, this is dead code now 2020-03-25 17:31:56 +00:00
Luis Ramos
e5e9325499 Fix paper_trail custom_data for order_cycle, custom data must be a string, cant be an array 2020-03-25 15:35:51 +00:00
Luis Ramos
2e4f8003b6 Fix group factory in rails 4
params[:address] was breaking the creation of the EnterpriseGroup
2020-03-25 15:32:16 +00:00
Yuhao Gao
60d9edb185 fix translation missing in order cycles outgoing page (closed #4937) 2020-03-25 21:51:13 +09:00
Luis Ramos
0e62dc04bd Improve comments to explain glitch on set_order_cycle and OrderCyclesList 2020-03-20 09:47:59 +00:00
Luis Ramos
e2940eb9ff Add namespace to OrderCyclesList
Shop will probably be the name of the future engine/domain for the shopfront pages
2020-03-20 09:47:59 +00:00
Luis Ramos
1c1f066884 Extract Shop::OrderCyclesList from base_controller 2020-03-20 09:47:59 +00:00
Luis Ramos
d5cf355a11 Call fetch_order_cycles from reset_order_cycles so we dont repeat the calculation
BaseController#set_order_cycle cant be used in reset_order_cycle because it will empty the order if the OC is not defined previously
2020-03-20 09:47:59 +00:00
Luis Ramos
d2eee1dafd Extract and fix reset_order_cycle logic from set_order_cycles: ActiveRecord.count will reload the relation and ignore the changes done by the TagRuleApplicator 2020-03-20 09:46:05 +00:00
Luis Ramos
540b26105f Delete dead code in oc helper 2020-03-20 09:46:05 +00:00
Matt-Yorkley
45b5e838b7 Remove simple_form
It looks like we don't use this at all. Discovered during the Spree 2.1 upgrade.
2020-03-17 17:03:48 +01:00
dependabot-preview[bot]
05ccd1ecbf Bump compass-rails from 3.1.0 to 4.0.0
Bumps [compass-rails](https://github.com/Compass/compass-rails) from 3.1.0 to 4.0.0.
- [Release notes](https://github.com/Compass/compass-rails/releases)
- [Changelog](https://github.com/Compass/compass-rails/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Compass/compass-rails/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-16 18:49:51 +00:00
Luis Ramos
f8a4f00d52 Fix rubocop issues in subs specs 2020-03-16 17:20:01 +00:00
Luis Ramos
29377bbff9 Move 5 subscriptions services from app/services to the engines/order_management/app/services 2020-03-16 17:20:01 +00:00
Luis Ramos
f68d0c2a0f Remove Subscription from the name of the subscription summarizer and summary because it is already in the namespace 2020-03-16 17:20:01 +00:00
Luis Ramos
3901c49af9 Fix rubocop issues 2020-03-16 17:20:01 +00:00
Luis Ramos
ae0ceb61a1 Move ProxyOrderSyncer to OrderManagement engine 2020-03-16 17:20:01 +00:00
Luis Ramos
fb1c825fbc Move both subscription summarizer and subscription summary to order management engine 2020-03-16 17:20:01 +00:00
Luis Ramos
e36b0249b9 Use nested module names to fix rubocpo issue 2020-03-16 17:20:01 +00:00
Luis Ramos
34fa2d7ad6 Move Subscriptions::PaymentSetup to OrderManagement engine where all subscription code will be at some point in the future 2020-03-16 17:19:04 +00:00
Luis Ramos
3aefea9f04 Prepare SubsConfirmJob to receive a bit more payment logic 2020-03-16 17:19:04 +00:00
Luis Ramos
15231a9128 Make SubsConfirmJob more readable 2020-03-16 17:19:04 +00:00
Luis Ramos
25e3f72934 Fix rubocop issues in subs payment_setup 2020-03-16 17:19:04 +00:00
Luis Ramos
523d819575 Move and rename SubscriptionPaymentUpdater to Subscriptios::PaymentSetup to move to services/subscriptions and call it Setup instead to make explicit this is executed before the payment is processed 2020-03-16 17:16:31 +00:00
Luis Ramos
bc0a1d9bae Remove one more responder and fix rubocop issues 2020-03-10 15:56:08 +00:00
Luis Ramos
a53dc3a8c1 Remove usage of the responder as this is a json only controller 2020-03-10 14:46:16 +00:00
170 changed files with 16096 additions and 3072 deletions

View File

@@ -33,7 +33,6 @@ Layout/LineLength:
- app/controllers/admin/inventory_items_controller.rb
- app/controllers/admin/manager_invitations_controller.rb
- app/controllers/admin/product_import_controller.rb
- app/controllers/admin/proxy_orders_controller.rb
- app/controllers/admin/schedules_controller.rb
- app/controllers/admin/subscriptions_controller.rb
- app/controllers/admin/variant_overrides_controller.rb
@@ -98,7 +97,6 @@ Layout/LineLength:
- app/services/embedded_page_service.rb
- app/services/order_cycle_form.rb
- app/services/order_factory.rb
- app/services/subscriptions_count.rb
- app/services/variants_stock_levels.rb
- engines/web/app/helpers/web/cookies_policy_helper.rb
- lib/discourse/single_sign_on.rb
@@ -239,10 +237,7 @@ Layout/LineLength:
- spec/lib/open_food_network/packing_report_spec.rb
- spec/lib/open_food_network/permissions_spec.rb
- spec/lib/open_food_network/products_and_inventory_report_spec.rb
- spec/lib/open_food_network/proxy_order_syncer_spec.rb
- spec/lib/open_food_network/scope_variant_to_hub_spec.rb
- spec/lib/open_food_network/subscription_payment_updater_spec.rb
- spec/lib/open_food_network/subscription_summarizer_spec.rb
- spec/lib/open_food_network/tag_rule_applicator_spec.rb
- spec/lib/open_food_network/users_and_enterprises_report_spec.rb
- spec/lib/open_food_network/xero_invoices_report_spec.rb
@@ -317,10 +312,6 @@ Layout/LineLength:
- spec/services/permissions/order_spec.rb
- spec/services/product_tag_rules_filterer_spec.rb
- spec/services/products_renderer_spec.rb
- spec/services/subscription_estimator_spec.rb
- spec/services/subscription_form_spec.rb
- spec/services/subscription_validator_spec.rb
- spec/services/subscription_variants_service_spec.rb
- spec/spec_helper.rb
- spec/support/cancan_helper.rb
- spec/support/delayed_job_helper.rb
@@ -411,7 +402,7 @@ Metrics/AbcSize:
- app/services/cart_service.rb
- app/services/create_order_cycle.rb
- app/services/order_syncer.rb
- app/services/subscription_validator.rb
- engines/order_management/app/services/order_management/subscriptions/validator.rb
- lib/active_merchant/billing/gateways/stripe_decorator.rb
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
- lib/discourse/single_sign_on.rb
@@ -686,6 +677,13 @@ Metrics/ModuleLength:
- app/helpers/injection_helper.rb
- app/helpers/spree/admin/navigation_helper.rb
- app/helpers/spree/admin/base_helper.rb
- engines/order_management/spec/services/order_management/subscriptions/estimator_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/form_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb
- engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb
- lib/open_food_network/column_preference_defaults.rb
- spec/controllers/admin/enterprises_controller_spec.rb
- spec/controllers/admin/order_cycles_controller_spec.rb
@@ -701,9 +699,7 @@ Metrics/ModuleLength:
- spec/lib/open_food_network/order_grouper_spec.rb
- spec/lib/open_food_network/permissions_spec.rb
- spec/lib/open_food_network/products_and_inventory_report_spec.rb
- spec/lib/open_food_network/proxy_order_syncer_spec.rb
- spec/lib/open_food_network/scope_variant_to_hub_spec.rb
- spec/lib/open_food_network/subscription_payment_updater_spec.rb
- spec/lib/open_food_network/tag_rule_applicator_spec.rb
- spec/lib/open_food_network/users_and_enterprises_report_spec.rb
- spec/models/spree/ability_spec.rb

View File

@@ -71,7 +71,6 @@ Lint/DuplicateHashKey:
Lint/DuplicateMethods:
Exclude:
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/subscription_summary.rb'
# Offense count: 10
Lint/IneffectiveAccessModifier:
@@ -159,7 +158,7 @@ Naming/MethodParameterName:
Exclude:
- 'app/helpers/spree/admin/base_helper_decorator.rb'
- 'app/helpers/spree/base_helper_decorator.rb'
- 'app/services/subscription_validator.rb'
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'lib/open_food_network/reports/bulk_coop_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'spec/lib/open_food_network/reports/report_spec.rb'
@@ -849,11 +848,6 @@ Style/FrozenStringLiteralComment:
- 'app/services/reset_order_service.rb'
- 'app/services/restart_checkout.rb'
- 'app/services/search_orders.rb'
- 'app/services/subscription_estimator.rb'
- 'app/services/subscription_form.rb'
- 'app/services/subscription_validator.rb'
- 'app/services/subscription_variants_service.rb'
- 'app/services/subscriptions_count.rb'
- 'app/services/tax_rate_finder.rb'
- 'app/services/upload_sanitizer.rb'
- 'app/services/variant_deleter.rb'
@@ -892,6 +886,7 @@ Style/FrozenStringLiteralComment:
- 'engines/order_management/lib/order_management/engine.rb'
- 'engines/order_management/lib/order_management/version.rb'
- 'engines/order_management/order_management.gemspec'
- 'engines/order_management/spec/performance/order_management/subscriptions/proxy_order_syncer_spec.rb'
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/authorizer_spec.rb'
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/parameters_spec.rb'
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/permissions_spec.rb'
@@ -949,7 +944,6 @@ Style/FrozenStringLiteralComment:
- 'lib/open_food_network/products_and_inventory_report.rb'
- 'lib/open_food_network/products_and_inventory_report_base.rb'
- 'lib/open_food_network/property_merge.rb'
- 'lib/open_food_network/proxy_order_syncer.rb'
- 'lib/open_food_network/rack_request_blocker.rb'
- 'lib/open_food_network/referer_parser.rb'
- 'lib/open_food_network/reports/bulk_coop_allocation_report.rb'
@@ -966,8 +960,6 @@ Style/FrozenStringLiteralComment:
- 'lib/open_food_network/scope_variants_for_search.rb'
- 'lib/open_food_network/spree_api_key_loader.rb'
- 'lib/open_food_network/subscription_payment_updater.rb'
- 'lib/open_food_network/subscription_summarizer.rb'
- 'lib/open_food_network/subscription_summary.rb'
- 'lib/open_food_network/tag_rule_applicator.rb'
- 'lib/open_food_network/user_balance_calculator.rb'
- 'lib/open_food_network/users_and_enterprises_report.rb'
@@ -1209,7 +1201,6 @@ Style/FrozenStringLiteralComment:
- 'spec/lib/open_food_network/permissions_spec.rb'
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
- 'spec/lib/open_food_network/property_merge_spec.rb'
- 'spec/lib/open_food_network/proxy_order_syncer_spec.rb'
- 'spec/lib/open_food_network/referer_parser_spec.rb'
- 'spec/lib/open_food_network/reports/report_spec.rb'
- 'spec/lib/open_food_network/reports/row_spec.rb'
@@ -1218,8 +1209,6 @@ Style/FrozenStringLiteralComment:
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
- 'spec/lib/open_food_network/scope_variants_to_search_spec.rb'
- 'spec/lib/open_food_network/subscription_payment_updater_spec.rb'
- 'spec/lib/open_food_network/subscription_summarizer_spec.rb'
- 'spec/lib/open_food_network/subscription_summary_spec.rb'
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/open_food_network/user_balance_calculator_spec.rb'
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
@@ -1304,7 +1293,6 @@ Style/FrozenStringLiteralComment:
- 'spec/models/variant_override_spec.rb'
- 'spec/performance/injection_helper_spec.rb'
- 'spec/performance/orders_controller_spec.rb'
- 'spec/performance/proxy_order_syncer_spec.rb'
- 'spec/performance/shop_controller_spec.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
- 'spec/requests/checkout/paypal_spec.rb'
@@ -1355,11 +1343,6 @@ Style/FrozenStringLiteralComment:
- 'spec/services/reset_order_service_spec.rb'
- 'spec/services/restart_checkout_spec.rb'
- 'spec/services/search_orders_spec.rb'
- 'spec/services/subscription_estimator_spec.rb'
- 'spec/services/subscription_form_spec.rb'
- 'spec/services/subscription_validator_spec.rb'
- 'spec/services/subscription_variants_service_spec.rb'
- 'spec/services/subscriptions_count_spec.rb'
- 'spec/services/tax_rate_finder_spec.rb'
- 'spec/services/upload_sanitizer_spec.rb'
- 'spec/services/variants_stock_levels_spec.rb'
@@ -1557,7 +1540,6 @@ Style/Send:
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
- 'spec/lib/open_food_network/sales_tax_report_spec.rb'
- 'spec/lib/open_food_network/subscription_payment_updater_spec.rb'
- 'spec/lib/open_food_network/subscription_summarizer_spec.rb'
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/open_food_network/xero_invoices_report_spec.rb'
- 'spec/lib/stripe/webhook_handler_spec.rb'

View File

@@ -35,13 +35,20 @@ Download the Docker images and build the containers:
$ docker-compose build
```
Run the app with all the required containers:
Setup the database and seed it with sample data:
```sh
$ docker-compose run web bundle exec rake db:reset
$ docker-compose run web bundle exec rake db:test:prepare
$ docker-compose run web bundle exec rake ofn:sample_data
```
Finally, 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.
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.

View File

@@ -12,7 +12,7 @@ ENV BUNDLE_PATH /bundles
WORKDIR /usr/src/app
COPY .ruby-version .
# Rbenv & Ruby part
# Install Rbenv & Ruby
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 && \
@@ -21,7 +21,7 @@ RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
rbenv global $(cat .ruby-version) && \
gem install bundler --version=1.17.2
# Postgres
# Install 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 && \
@@ -38,4 +38,6 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
unzip chromedriver_linux64.zip -d /usr/bin && \
chmod u+x /usr/bin/chromedriver
# Copy code and install app dependencies
COPY . /usr/src/app/
RUN bundle install

View File

@@ -45,10 +45,6 @@ gem 'daemons'
gem 'delayed_job_active_record'
gem 'delayed_job_web'
# Fix bug in simple_form preventing collection_check_boxes usage within form_for block
# When merged, revert to upstream gem
gem 'simple_form', github: 'RohanM/simple_form'
# Spree's default pagination gem (locked to the current version used by Spree)
# We use it's methods in OFN code as well, so this is a direct dependency
gem 'kaminari', '~> 0.14.1'
@@ -112,7 +108,6 @@ gem 'momentjs-rails'
gem 'turbo-sprockets-rails3'
gem "foundation-rails"
gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3"
gem 'jquery-migrate-rails'
gem 'jquery-rails', '3.1.5'
@@ -123,6 +118,7 @@ gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', ref: '60da2ae4c44cbb4c8d602f5
group :production, :staging do
gem 'ddtrace'
gem 'unicorn-worker-killer'
end
group :test, :development do

View File

@@ -1,11 +1,3 @@
GIT
remote: https://github.com/RohanM/simple_form.git
revision: 45f08a213b40f3d4bda5f5398db841137587160a
specs:
simple_form (2.0.2)
actionpack (~> 3.0)
activemodel (~> 3.0)
GIT
remote: https://github.com/jeremydurham/custom-err-msg.git
revision: 3a8ec9dddc7a5b0aab7c69a6060596de300c68f4
@@ -65,16 +57,6 @@ GIT
rails-i18n
spree_core (>= 1.1)
GIT
remote: https://github.com/willrjmarshall/foundation_rails_helper.git
revision: 4d5d53fdc4b1fb71e66524d298c5c635de82cfbb
branch: rails3
specs:
foundation_rails_helper (0.4)
actionpack (>= 3.0)
activemodel (>= 3.0)
railties (>= 3.0)
PATH
remote: engines/catalog
specs:
@@ -173,7 +155,7 @@ GEM
xpath (>= 2.0, < 4.0)
childprocess (3.0.0)
chronic (0.10.2)
chunky_png (1.3.10)
chunky_png (1.3.11)
climate_control (0.2.0)
cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0)
@@ -200,7 +182,7 @@ GEM
sass (>= 3.3.0, < 3.5)
compass-import-once (1.0.5)
sass (>= 3.2, < 3.5)
compass-rails (3.1.0)
compass-rails (4.0.0)
compass (~> 1.0.0)
sass-rails (< 5.1)
sprockets (< 4.0)
@@ -216,7 +198,7 @@ GEM
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.33.1)
ddtrace (0.34.1)
msgpack
debugger-linecache (1.2.0)
deface (1.0.2)
@@ -257,7 +239,7 @@ GEM
faraday (1.0.0)
multipart-post (>= 1.2, < 3)
ffaker (1.22.1)
ffi (1.11.3)
ffi (1.12.2)
figaro (1.1.1)
thor (~> 0.14)
fission (0.5.0)
@@ -421,6 +403,8 @@ GEM
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.1.8)
get_process_mem (0.2.5)
ffi (~> 1.0)
gmaps4rails (1.5.6)
haml (4.0.7)
tilt
@@ -485,7 +469,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.10.5)
oj (3.10.6)
orm_adapter (0.5.0)
paper_trail (5.2.3)
activerecord (>= 3.0, < 6.0)
@@ -559,8 +543,8 @@ GEM
activerecord (~> 3.0)
polyamorous (~> 0.5.0)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rbvmomi (1.13.0)
builder (~> 3.0)
json (>= 1.8)
@@ -604,15 +588,15 @@ GEM
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.9.2)
rubocop (0.80.1)
rubocop (0.81.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
rexml
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.5.0)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-rails (2.5.1)
activesupport
rack (>= 1.1)
rubocop (>= 0.72.0)
@@ -671,13 +655,16 @@ GEM
tzinfo (0.3.56)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.6.1)
unicode-display_width (1.7.0)
unicorn (5.5.4)
kgio (~> 2.6)
raindrops (~> 0.7)
unicorn-rails (2.2.1)
rack
unicorn
unicorn-worker-killer (0.4.4)
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uuidtools (2.1.5)
warden (1.2.7)
rack (>= 1.0)
@@ -740,7 +727,6 @@ DEPENDENCIES
foreigner
foundation-icons-sass-rails
foundation-rails
foundation_rails_helper!
fuubar (~> 2.5.0)
geocoder
gmaps4rails
@@ -787,7 +773,6 @@ DEPENDENCIES
select2-rails (~> 3.4.7)
selenium-webdriver
shoulda-matchers
simple_form!
simplecov
spinjs-rails
spree_core!
@@ -803,6 +788,7 @@ DEPENDENCIES
uglifier (>= 1.0.3)
unicorn
unicorn-rails
unicorn-worker-killer
web!
webdrivers
webmock

View File

@@ -2,19 +2,24 @@ angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) ->
new class PagedFetcher
# Given a URL like http://example.com/foo?page=::page::&per_page=20
# And the response includes an attribute pages with the number of pages to fetch
# Fetch each page async, and call the processData callback with the resulting data
fetch: (url, processData, onLastPageComplete) ->
dataFetcher(@urlForPage(url, 1)).then (data) =>
processData data
# Fetch each page async, and call the pageCallback callback with the resulting data
# Developer note: this class should not be re-used!
page: 1
last_page: 1
if data.pages > 1
for page in [2..data.pages]
lastPromise = dataFetcher(@urlForPage(url, page)).then (data) ->
processData data
onLastPageComplete && lastPromise.then onLastPageComplete
return
else
onLastPageComplete && onLastPageComplete()
fetch: (url, pageCallback) ->
@fetchPages(url, @page, pageCallback)
urlForPage: (url, page) ->
url.replace("::page::", page)
fetchPages: (url, page, pageCallback) ->
dataFetcher(@urlForPage(url, page)).then (data) =>
@page++
@last_page = data.pages
pageCallback(data) if pageCallback
if @page <= @last_page
@fetchPages(url, @page, pageCallback)

View File

@@ -1,7 +1,7 @@
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, SortOptions, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) ->
$scope.initialized = false
$scope.RequestMonitor = RequestMonitor
$scope.filteredLineItems = []
$scope.line_items = LineItems.all
$scope.confirmDelete = true
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
@@ -15,50 +15,77 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.confirmRefresh = ->
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
$scope.resetFilters = ->
$scope.distributorFilter = ''
$scope.supplierFilter = ''
$scope.orderCycleFilter = ''
$scope.quickSearch = ''
$scope.resetSelectFilters = ->
$scope.distributorFilter = 0
$scope.supplierFilter = 0
$scope.orderCycleFilter = 0
$scope.quickSearch = ""
$scope.resetFilters()
$scope.refreshData()
$scope.refreshData = ->
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == 0
$scope.startDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_open_at).format('YYYY-MM-DD')
$scope.endDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_close_at).startOf('day').format('YYYY-MM-DD')
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == ''
$scope.setOrderCycleDateRange()
formatted_start_date = moment($scope.startDate).format()
formatted_end_date = moment($scope.endDate).add(1,'day').format()
$scope.formattedStartDate = moment($scope.startDate).format()
$scope.formattedEndDate = moment($scope.endDate).add(1,'day').format()
return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid()
$scope.loadOrders()
$scope.loadLineItems()
unless $scope.initialized
$scope.loadAssociatedData()
$scope.dereferenceLoadedData()
$scope.setOrderCycleDateRange = ->
start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at
end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at
format = "YYYY-MM-DD HH:mm:ss Z"
$scope.startDate = moment(start_date, format).format('YYYY-MM-DD')
$scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD')
$scope.loadOrders = ->
RequestMonitor.load $scope.orders = Orders.index(
"q[state_not_eq]": "canceled",
"q[completed_at_not_null]": "true",
"q[completed_at_gteq]": formatted_start_date,
"q[completed_at_lt]": formatted_end_date
"q[distributor_id_eq]": $scope.distributorFilter,
"q[order_cycle_id_eq]": $scope.orderCycleFilter,
"q[completed_at_gteq]": $scope.formattedStartDate,
"q[completed_at_lt]": $scope.formattedEndDate
)
RequestMonitor.load $scope.lineItems = LineItems.index(
"q[order][state_not_eq]": "canceled",
"q[order][completed_at_not_null]": "true",
"q[order][completed_at_gteq]": formatted_start_date,
"q[order][completed_at_lt]": formatted_end_date
$scope.loadLineItems = ->
RequestMonitor.load LineItems.index(
"q[order_state_not_eq]": "canceled",
"q[order_completed_at_not_null]": "true",
"q[order_distributor_id_eq]": $scope.distributorFilter,
"q[variant_product_supplier_id_eq]": $scope.supplierFilter,
"q[order_order_cycle_id_eq]": $scope.orderCycleFilter,
"q[order_completed_at_gteq]": $scope.formattedStartDate,
"q[order_completed_at_lt]": $scope.formattedEndDate
)
unless $scope.initialized
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
$scope.loadAssociatedData = ->
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then ->
$scope.dereferenceLoadedData = ->
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.byID
Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.byID
Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
$scope.bulk_order_form.$setPristine()
StatusMessage.clear()
unless $scope.initialized
$scope.initialized = true
$timeout ->
$scope.resetSelectFilters()
$scope.$watch 'bulk_order_form.$dirty', (newVal, oldVal) ->
if newVal == true
@@ -77,13 +104,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.deleteLineItem = (lineItem) ->
if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
LineItems.delete lineItem, =>
$scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1
LineItems.delete lineItem
$scope.deleteLineItems = (lineItems) ->
$scope.deleteLineItems = (lineItemsToDelete) ->
existingState = $scope.confirmDelete
$scope.confirmDelete = false
$scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked
$scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked
$scope.confirmDelete = existingState
$scope.allBoxesChecked = ->
@@ -154,4 +180,5 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity
$scope.weightAdjustedPrice(lineItem)
$scope.resetFilters()
$scope.refreshData()

View File

@@ -14,9 +14,10 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
@stripe.createToken(@card, cardData).then (response) =>
if(response.error)
StatusMessage.display 'error', response.error.message
console.error(JSON.stringify(response.error))
else
secrets.token = response.token.id
secrets.cc_type = @mapCC(response.token.card.brand)
secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand)
secrets.card = response.token.card
submit()
@@ -29,15 +30,16 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
@stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
if(response.error)
StatusMessage.display 'error', response.error.message
console.error(JSON.stringify(response.error))
else
secrets.token = response.paymentMethod.id
secrets.cc_type = response.paymentMethod.card.brand
secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand)
secrets.card = response.paymentMethod.card
submit()
# Maps the brand returned by Stripe to that required by activemerchant
mapCC: (ccType) ->
switch ccType
# Maps the brand returned by Stripe's tokenAPI to that required by activemerchant
mapTokenApiCardBrand: (cardBrand) ->
switch cardBrand
when 'MasterCard' then return 'master'
when 'Visa' then return 'visa'
when 'American Express' then return 'american_express'
@@ -45,6 +47,14 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
when 'JCB' then return 'jcb'
when 'Diners Club' then return 'diners_club'
# Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant
mapPaymentMethodsApiCardBrand: (cardBrand) ->
switch cardBrand
when 'mastercard' then return 'master'
when 'amex' then return 'american_express'
when 'diners' then return 'diners_club'
else return cardBrand # a few brands are equal, for example, visa
# It doesn't matter if any of these are nil, all are optional.
makeCardData: (secrets) ->
{'name': secrets.name,

View File

@@ -2,7 +2,6 @@ angular.module("admin.resources").factory 'LineItemResource', ($resource) ->
$resource('/admin/bulk_line_items/:id.json', {}, {
'index':
method: 'GET'
isArray: true
'update':
method: 'PUT'
transformRequest: (data, headersGetter) =>

View File

@@ -1,20 +1,27 @@
angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
new class LineItems
all: []
byID: {}
pristineByID: {}
pagination: {}
index: (params={}, callback=null) ->
LineItemResource.index params, (data) =>
request = LineItemResource.index params, (data) =>
@load(data)
(callback || angular.noop)(data)
@all.$promise = request.$promise
@all
resetData: ->
@all.length = 0
@byID = {}
@pristineByID = {}
load: (lineItems) ->
load: (data) ->
angular.extend(@pagination, data.pagination)
@resetData()
for lineItem in lineItems
for lineItem in data.line_items
@all.push lineItem
@byID[lineItem.id] = lineItem
@pristineByID[lineItem.id] = angular.copy(lineItem)
@@ -25,8 +32,9 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
save: (lineItem) ->
deferred = $q.defer()
lineItemResource = new LineItemResource(lineItem)
lineItem.errors = {}
lineItem.$update({id: lineItem.id})
lineItemResource.$update({id: lineItem.id})
.then( (data) =>
@pristineByID[lineItem.id] = angular.copy(lineItem)
deferred.resolve(data)
@@ -54,8 +62,10 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
delete: (lineItem, callback=null) ->
deferred = $q.defer()
lineItem.$delete({id: lineItem.id})
lineItemResource = new LineItemResource(lineItem)
lineItemResource.$delete({id: lineItem.id})
.then( (data) =>
@all.splice(@all.indexOf(lineItem),1)
delete @byID[lineItem.id]
delete @pristineByID[lineItem.id]
(callback || angular.noop)(data)

View File

@@ -43,12 +43,11 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
$scope.fetchProducts = ->
url = "/api/products/overridable?page=::page::;per_page=100"
PagedFetcher.fetch url, (data) => $scope.addProducts data.products
PagedFetcher.fetch url, $scope.addProducts
$scope.addProducts = (products) ->
$scope.products = $scope.products.concat products
VariantOverrides.ensureDataFor hubs, products
$scope.addProducts = (data) ->
$scope.products = $scope.products.concat data.products
VariantOverrides.ensureDataFor hubs, data.products
$scope.displayDirty = ->
if DirtyVariantOverrides.count() > 0

View File

@@ -9,6 +9,8 @@
#= require angular-animate
#= require angular-resource
#= require lodash.underscore.js
# bluebird.js is a dependency of angular-google-maps.js 2.0.0
#= require bluebird.js
#= require angular-scroll.min.js
#= require angular-google-maps.min.js
#= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js

View File

@@ -9,8 +9,13 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
$scope.show_closed = false
$scope.filtersActive = false
$scope.distanceMatchesShown = false
$scope.closed_shops_loading = false
$scope.closed_shops_loaded = false
$scope.$watch "query", (query)->
$scope.resetSearch(query)
$scope.resetSearch = (query) ->
Enterprises.flagMatching query
Search.search query
$rootScope.$broadcast 'enterprisesChanged'
@@ -19,6 +24,7 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
$timeout ->
Enterprises.calculateDistance query, $scope.firstNameMatch()
$rootScope.$broadcast 'enterprisesChanged'
$scope.closed_shops_loading = false
$timeout ->
if $location.search()['show_closed']?
@@ -73,6 +79,12 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
undefined
$scope.showClosedShops = ->
unless $scope.closed_shops_loaded
$scope.closed_shops_loading = true
$scope.closed_shops_loaded = true
Enterprises.loadClosedEnterprises().then ->
$scope.resetSearch($scope.query)
$scope.show_closed = true
$location.search('show_closed', '1')

View File

@@ -24,7 +24,7 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http,
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/enterprises/" + $scope.hub.id + "/shopfront")
$http.get("/api/shops/" + $scope.hub.id)
.success (data) ->
$scope.shopfront_loading = false
$scope.hub = data

View File

@@ -24,7 +24,7 @@ Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll,
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/enterprises/" + $scope.producer.id + "/shopfront")
$http.get("/api/shops/" + $scope.producer.id)
.success (data) ->
$scope.shopfront_loading = false
$scope.producer = data

View File

@@ -7,7 +7,7 @@ window.Darkswarm = angular.module("Darkswarm", [
'templates',
'ngSanitize',
'ngAnimate',
'google-maps',
'uiGmapgoogle-maps',
'duScroll',
'angularFileUpload',
'angularSlideables'

View File

@@ -1,6 +1,6 @@
Darkswarm.directive 'mapOsmTiles', ($timeout) ->
restrict: 'E'
require: '^googleMap'
require: '^uiGmapGoogleMap'
scope: {}
link: (scope, elem, attrs, ctrl) ->
$timeout =>

View File

@@ -1,7 +1,7 @@
Darkswarm.directive 'mapSearch', ($timeout, Search) ->
# Install a basic search field in a map
restrict: 'E'
require: ['^googleMap', 'ngModel']
require: ['^uiGmapGoogleMap', 'ngModel']
replace: true
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '"></input>'
scope: {}

View File

@@ -5,7 +5,7 @@ Darkswarm.factory "EnterpriseModal", ($modal, $rootScope, $http)->
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
$http.get("/api/enterprises/" + enterprise.id + "/shopfront").success (data) ->
$http.get("/api/shops/" + enterprise.id).success (data) ->
scope.enterprise = data
$modal.open(templateUrl: "enterprise_modal.html", scope: scope)
.error (data) ->

View File

@@ -1,27 +1,30 @@
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, Matcher, Geo, $rootScope) ->
Darkswarm.factory 'Enterprises', (enterprises, ShopsResource, CurrentHub, Taxons, Dereferencer, Matcher, Geo, $rootScope) ->
new class Enterprises
enterprises: []
enterprises_by_id: {}
constructor: ->
# Populate Enterprises.enterprises from json in page.
@enterprises = enterprises
@initEnterprises(enterprises)
initEnterprises: (enterprises) ->
# Map enterprises to id/object pairs for lookup.
for enterprise in enterprises
@enterprises.push enterprise
@enterprises_by_id[enterprise.id] = enterprise
# Replace enterprise and taxons ids with actual objects.
@dereferenceEnterprises()
@dereferenceEnterprises(enterprises)
@producers = @enterprises.filter (enterprise)->
enterprise.category in ["producer_hub", "producer_shop", "producer"]
@hubs = @enterprises.filter (enterprise)->
enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"]
dereferenceEnterprises: ->
dereferenceEnterprises: (enteprises) ->
if CurrentHub.hub?.id
CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id]
for enterprise in @enterprises
for enterprise in enterprises
@dereferenceEnterprise enterprise
dereferenceEnterprise: (enterprise) ->
@@ -42,6 +45,12 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
for enterprise in new_enterprises
@enterprises_by_id[enterprise.id] = enterprise
loadClosedEnterprises: ->
request = ShopsResource.closed_shops {}, (data) =>
@initEnterprises(data)
request.$promise
flagMatching: (query) ->
for enterprise in @enterprises
enterprise.matches_name_query = if query? && query.length > 0
@@ -50,7 +59,7 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
false
calculateDistance: (query, firstMatching) ->
if query?.length > 0
if query?.length > 0 and Geo.OK
if firstMatching?
@setDistanceFrom firstMatching
else

View File

@@ -1,6 +1,6 @@
Darkswarm.service "Geo", ->
new class Geo
OK: google.maps.GeocoderStatus.OK
OK: google?.maps?.GeocoderStatus?.OK
# Usage:
# Geo.geocode address, (results, status) ->

View File

@@ -0,0 +1,7 @@
Darkswarm.factory 'ShopsResource', ($resource) ->
$resource('/api/shops/:id.json', {}, {
'closed_shops':
method: 'GET'
isArray: true
url: '/api/shops/closed_shops.json'
})

View File

@@ -15,9 +15,11 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
if(response.error)
Loading.clear()
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
@triggerAngularDigest()
console.error(JSON.stringify(response.error))
else
secrets.token = response.token.id
secrets.cc_type = @mapCC(response.token.card.brand)
secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand)
secrets.card = response.token.card
submit()
@@ -32,15 +34,21 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
if(response.error)
Loading.clear()
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
@triggerAngularDigest()
console.error(JSON.stringify(response.error))
else
secrets.token = response.paymentMethod.id
secrets.cc_type = response.paymentMethod.card.brand
secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand)
secrets.card = response.paymentMethod.card
submit()
# Maps the brand returned by Stripe to that required by activemerchant
mapCC: (ccType) ->
switch ccType
triggerAngularDigest: ->
# $evalAsync is improved way of triggering a digest without calling $apply
$rootScope.$evalAsync()
# Maps the brand returned by Stripe's tokenAPI to that required by activemerchant
mapTokenApiCardBrand: (cardBrand) ->
switch cardBrand
when 'MasterCard' then return 'master'
when 'Visa' then return 'visa'
when 'American Express' then return 'american_express'
@@ -48,6 +56,14 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
when 'JCB' then return 'jcb'
when 'Diners Club' then return 'diners_club'
# Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant
mapPaymentMethodsApiCardBrand: (cardBrand) ->
switch cardBrand
when 'mastercard' then return 'master'
when 'amex' then return 'american_express'
when 'diners' then return 'diners_club'
else return cardBrand # a few brands are equal, for example, visa
# It doesn't matter if any of these are nil, all are optional.
makeCardData: (secrets) ->
{'name': secrets.name,

View File

@@ -109,9 +109,4 @@ checkout {
}
}
}
.error {
color: #c82020;
}
}

View File

@@ -14,5 +14,10 @@
.more-controls {
text-align: center;
.spinner {
height: 2.25em;
margin-right: 0.5em;
}
}
}

View File

@@ -4,18 +4,17 @@ module Admin
#
def index
order_params = params[:q].andand.delete :order
orders = order_permissions.editable_orders.ransack(order_params).result
order_permissions = ::Permissions::Order.new(spree_current_user)
orders = order_permissions.
editable_orders.ransack(order_params).result
line_items = order_permissions.
@line_items = order_permissions.
editable_line_items.where(order_id: orders).
includes(variant: { option_values: :option_type }).
ransack(params[:q]).result.
reorder('spree_line_items.order_id ASC, spree_line_items.id ASC')
render_as_json line_items
@line_items = @line_items.page(page).per(params[:per_page]) if using_pagination?
render json: { line_items: serialized_line_items, pagination: pagination_data }
end
# PUT /admin/bulk_line_items/:id.json
@@ -65,6 +64,12 @@ module Admin
Api::Admin::LineItemSerializer
end
def serialized_line_items
ActiveModel::ArraySerializer.new(
@line_items, each_serializer: serializer(nil)
)
end
def authorize_update!
authorize! :update, order
authorize! :read, order
@@ -73,5 +78,28 @@ module Admin
def order
@line_item.order
end
def order_permissions
::Permissions::Order.new(spree_current_user)
end
def using_pagination?
params[:per_page]
end
def pagination_data
return unless using_pagination?
{
results: @line_items.total_count,
pages: @line_items.num_pages,
page: page.to_i,
per_page: params[:per_page].to_i
}
end
def page
params[:page] || 1
end
end
end

View File

@@ -16,7 +16,7 @@ module Admin
render_as_json @collection,
ams_prefix: params[:ams_prefix],
current_user: spree_current_user,
subscriptions_count: SubscriptionsCount.new(@collection)
subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection)
end
end
end
@@ -74,7 +74,7 @@ module Admin
render_as_json @order_cycles,
ams_prefix: 'index',
current_user: spree_current_user,
subscriptions_count: SubscriptionsCount.new(@collection)
subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection)
else
order_cycle = order_cycle_set.collection.find{ |oc| oc.errors.present? }
render json: { errors: order_cycle.errors.full_messages }, status: :unprocessable_entity

View File

@@ -9,25 +9,19 @@ module Admin
def cancel
if @proxy_order.cancel
respond_with(@proxy_order) do |format|
format.json { render_as_json @proxy_order }
end
render_as_json @proxy_order
else
respond_with(@proxy_order) do |format|
format.json { render json: { errors: [t('admin.proxy_orders.cancel.could_not_cancel_the_order')] }, status: :unprocessable_entity }
end
render json: { errors: [t('admin.proxy_orders.cancel.could_not_cancel_the_order')] },
status: :unprocessable_entity
end
end
def resume
if @proxy_order.resume
respond_with(@proxy_order) do |format|
format.json { render_as_json @proxy_order }
end
render_as_json @proxy_order
else
respond_with(@proxy_order) do |format|
format.json { render json: { errors: [t('admin.proxy_orders.resume.could_not_resume_the_order')] }, status: :unprocessable_entity }
end
render json: { errors: [t('admin.proxy_orders.resume.could_not_resume_the_order')] },
status: :unprocessable_entity
end
end
end

View File

@@ -1,5 +1,5 @@
require 'open_food_network/permissions'
require 'open_food_network/proxy_order_syncer'
require 'order_management/subscriptions/proxy_order_syncer'
module Admin
class SchedulesController < ResourceController
@@ -81,7 +81,7 @@ module Admin
return unless removed_ids.any? || new_ids.any?
subscriptions = Subscription.where(schedule_id: @schedule)
syncer = OpenFoodNetwork::ProxyOrderSyncer.new(subscriptions)
syncer = OrderManagement::Subscriptions::ProxyOrderSyncer.new(subscriptions)
syncer.sync!
end
end

View File

@@ -56,7 +56,7 @@ module Admin
end
def variant_if_eligible(variant_id)
SubscriptionVariantsService.eligible_variants(@shop).find_by_id(variant_id)
OrderManagement::Subscriptions::VariantsList.eligible_variants(@shop).find_by_id(variant_id)
end
end
end

View File

@@ -1,5 +1,4 @@
require 'open_food_network/permissions'
require 'open_food_network/proxy_order_syncer'
module Admin
class SubscriptionsController < ResourceController
@@ -65,7 +64,7 @@ module Admin
private
def save_form_and_render(render_issues = true)
form = SubscriptionForm.new(@subscription, params[:subscription])
form = OrderManagement::Subscriptions::Form.new(@subscription, params[:subscription])
unless form.save
render json: { errors: form.json_errors }, status: :unprocessable_entity
return

View File

@@ -73,8 +73,10 @@ module Admin
end
def collection
@variant_overrides = VariantOverride.includes(:variant).for_hubs(params[:hub_id] || @hubs)
@variant_overrides.select { |vo| vo.variant.present? }
@variant_overrides = VariantOverride.
includes(variant: :product).
for_hubs(params[:hub_id] || @hubs).
select { |vo| vo.variant.present? }
end
def collection_actions

View File

@@ -5,7 +5,6 @@ module Api
before_filter :override_sells, only: [:create, :update]
before_filter :override_visible, only: [:create, :update]
respond_to :json
skip_authorization_check only: [:shopfront]
def create
authorize! :create, Enterprise
@@ -42,12 +41,6 @@ module Api
end
end
def shopfront
enterprise = Enterprise.find_by_id(params[:id])
render text: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
end
private
def override_owner

View File

@@ -69,12 +69,12 @@ module Api
end
def overridable
producers = OpenFoodNetwork::Permissions.new(current_api_user).
variant_override_producers.by_name
producer_ids = OpenFoodNetwork::Permissions.new(current_api_user).
variant_override_producers.by_name.select('enterprises.id')
@products = paged_products_for_producers producers
@products = paged_products_for_producers producer_ids
render_paged_products @products
render_paged_products @products, ::Api::Admin::ProductSimpleSerializer
end
# POST /api/products/:product_id/clone
@@ -118,19 +118,20 @@ module Api
]
end
def paged_products_for_producers(producers)
def paged_products_for_producers(producer_ids)
Spree::Product.scoped.
merge(product_scope).
where(supplier_id: producers).
includes(variants: [:product, :default_price, :stock_items]).
where(supplier_id: producer_ids).
by_producer.by_name.
ransack(params[:q]).result.
page(params[:page]).per(params[:per_page])
end
def render_paged_products(products)
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
serializer = ActiveModel::ArraySerializer.new(
products,
each_serializer: ::Api::Admin::ProductSerializer
each_serializer: product_serializer
)
render text: {

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
module Api
class ShopsController < BaseController
respond_to :json
skip_authorization_check only: [:show, :closed_shops]
def show
enterprise = Enterprise.find_by_id(params[:id])
render text: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
end
def closed_shops
@active_distributor_ids = []
@earliest_closing_times = []
serialized_closed_shops = ActiveModel::ArraySerializer.new(
ShopsListService.new.closed_shops,
each_serializer: Api::EnterpriseSerializer,
data: OpenFoodNetwork::EnterpriseInjectionData.new
)
render json: serialized_closed_shops
end
end
end

View File

@@ -29,17 +29,19 @@ class BaseController < ApplicationController
return
end
@order_cycles = OrderCycle.with_distributor(@distributor).active
.order(@distributor.preferred_shopfront_order_cycle_order)
@order_cycles = Shop::OrderCyclesList.new(@distributor, current_customer).call
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor,
"FilterOrderCycles",
current_customer.andand.tag_list)
applicator.filter!(@order_cycles)
set_order_cycle
end
# And default to the only order cycle if there's only the one
if @order_cycles.count == 1
current_order(true).set_order_cycle! @order_cycles.first
end
# Default to the only order cycle if there's only one
#
# Here we need to use @order_cycles.size not @order_cycles.count
# because OrderCyclesList returns a modified ActiveRecord::Relation
# and these modifications are not seen if it is reloaded with count
def set_order_cycle
return if @order_cycles.size != 1
current_order(true).set_order_cycle! @order_cycles.first
end
end

View File

@@ -53,9 +53,8 @@ class CheckoutController < Spree::StoreController
rescue Spree::Core::GatewayError => e
rescue_from_spree_gateway_error(e)
rescue StandardError => e
Bugsnag.notify(e)
flash[:error] = I18n.t("checkout.failed")
update_failed
update_failed(e)
end
# Clears the cached order. Required for #current_order to return a new order
@@ -165,7 +164,7 @@ class CheckoutController < Spree::StoreController
checkout_succeeded
redirect_to(order_path(@order)) && return
else
flash[:error] = order_workflow_error
flash[:error] = order_error
checkout_failed
end
end
@@ -180,7 +179,6 @@ class CheckoutController < Spree::StoreController
next if advance_order_state(@order)
flash[:error] = order_workflow_error
return update_failed
end
@@ -205,7 +203,7 @@ class CheckoutController < Spree::StoreController
false
end
def order_workflow_error
def order_error
if @order.errors.present?
@order.errors.full_messages.to_sentence
else
@@ -218,7 +216,7 @@ class CheckoutController < Spree::StoreController
checkout_succeeded
update_succeeded_response
else
update_failed
update_failed(RuntimeError.new("Order not complete after the checkout workflow"))
end
end
@@ -244,7 +242,10 @@ class CheckoutController < Spree::StoreController
end
end
def update_failed
def update_failed(error = RuntimeError.new(order_error))
Bugsnag.notify(error)
flash[:error] = order_error if flash.empty?
checkout_failed
update_failed_response
end

View File

@@ -96,8 +96,8 @@ class EnterprisesController < BaseController
end
def reset_order_cycle(order, distributor)
order_cycle_options = OrderCycle.active.with_distributor(distributor)
order.order_cycle = order_cycle_options.first if order_cycle_options.count == 1
order_cycles = Shop::OrderCyclesList.new(distributor, current_customer).call
order.order_cycle = order_cycles.first if order_cycles.size == 1
end
def shop_order_cycles

View File

@@ -5,12 +5,23 @@ class HomeController < BaseController
def index
if ContentConfig.home_show_stats
@num_distributors = Enterprise.is_distributor.activated.visible.count
@num_producers = Enterprise.is_primary_producer.activated.visible.count
@num_users = Spree::Order.complete.count('DISTINCT user_id')
@num_orders = Spree::Order.complete.count
@num_distributors = cached_count('distributors', Enterprise.is_distributor.activated.visible)
@num_producers = cached_count('producers', Enterprise.is_primary_producer.activated.visible)
@num_orders = cached_count('orders', Spree::Order.complete)
@num_users = cached_count(
'users', Spree::Order.complete.select('DISTINCT spree_orders.user_id')
)
end
end
def sell; end
private
# Cache the value of the query count for 24 hours
def cached_count(key, query)
Rails.cache.fetch("home_stats_count_#{key}", expires_in: 1.day, race_condition_ttl: 10) do
query.count
end
end
end

View File

@@ -4,13 +4,6 @@ class ShopsController < BaseController
before_filter :enable_embedded_shopfront
def index
@enterprises = Enterprise
.activated
.visible
.is_distributor
.includes(address: [:state, :country])
.includes(:properties)
.includes(supplied_products: :properties)
.all
@enterprises = ShopsListService.new.open_shops
end
end

View File

@@ -1,6 +1,4 @@
module ApplicationHelper
include FoundationRailsHelper::FlashHelper
def feature?(feature)
OpenFoodNetwork::FeatureToggle.enabled? feature
end

View File

@@ -47,17 +47,6 @@ module OrderCyclesHelper
end
end
def order_cycle_options
@order_cycles.
with_distributor(current_distributor).
map { |oc| [order_cycle_close_to_s(oc.orders_close_at), oc.id] }
end
def order_cycle_close_to_s(orders_close_at)
"%s (%s)" % [orders_close_at.strftime("#{orders_close_at.day.ordinalize} %b"),
distance_of_time_in_words_to_now(orders_close_at)]
end
def active_order_cycle_for_distributor?(_distributor)
OrderCycle.active.with_distributor(@distributor).present?
end

View File

@@ -1,17 +1,9 @@
require 'open_food_network/subscription_payment_updater'
require 'open_food_network/subscription_summarizer'
require 'order_management/subscriptions/summarizer'
# Confirms orders of unconfirmed proxy orders in recently closed Order Cycles
class SubscriptionConfirmJob
def perform
ids = proxy_orders.pluck(:id)
proxy_orders.update_all(confirmed_at: Time.zone.now)
ProxyOrder.where(id: ids).each do |proxy_order|
Rails.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
@order = proxy_order.order
process!
end
send_confirmation_summary_emails
confirm_proxy_orders!
end
private
@@ -20,10 +12,26 @@ class SubscriptionConfirmJob
delegate :record_and_log_error, :send_confirmation_summary_emails, to: :summarizer
def summarizer
@summarizer ||= OpenFoodNetwork::SubscriptionSummarizer.new
@summarizer ||= OrderManagement::Subscriptions::Summarizer.new
end
def proxy_orders
def confirm_proxy_orders!
# Fetch all unconfirmed proxy orders
unconfirmed_proxy_orders_ids = unconfirmed_proxy_orders.pluck(:id)
# Mark these proxy orders as confirmed
unconfirmed_proxy_orders.update_all(confirmed_at: Time.zone.now)
# Confirm these proxy orders
ProxyOrder.where(id: unconfirmed_proxy_orders_ids).each do |proxy_order|
Rails.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
confirm_order!(proxy_order.order)
end
send_confirmation_summary_emails
end
def unconfirmed_proxy_orders
ProxyOrder.not_canceled.where('confirmed_at IS NULL AND placed_at IS NOT NULL')
.joins(:order_cycle).merge(recently_closed_order_cycles)
.joins(:order).merge(Spree::Order.complete.not_state('canceled'))
@@ -33,30 +41,43 @@ class SubscriptionConfirmJob
OrderCycle.closed.where('order_cycles.orders_close_at BETWEEN (?) AND (?) OR order_cycles.updated_at BETWEEN (?) AND (?)', 1.hour.ago, Time.zone.now, 1.hour.ago, Time.zone.now)
end
def process!
record_order(@order)
update_payment! if @order.payment_required?
return send_failed_payment_email if @order.errors.present?
# It sets up payments, processes payments and sends confirmation emails
def confirm_order!(order)
record_order(order)
@order.process_payments! if @order.payment_required?
return send_failed_payment_email if @order.errors.present?
send_confirm_email
if process_payment!(order)
send_confirmation_email(order)
else
send_failed_payment_email(order)
end
end
def update_payment!
OpenFoodNetwork::SubscriptionPaymentUpdater.new(@order).update!
def process_payment!(order)
return false if order.errors.present?
return true unless order.payment_required?
setup_payment!(order)
return false if order.errors.present?
order.process_payments!
return false if order.errors.present?
true
end
def send_confirm_email
@order.update!
record_success(@order)
SubscriptionMailer.confirmation_email(@order).deliver
def setup_payment!(order)
OrderManagement::Subscriptions::PaymentSetup.new(order).call!
end
def send_failed_payment_email
@order.update!
record_and_log_error(:failed_payment, @order)
SubscriptionMailer.failed_payment_email(@order).deliver
def send_confirmation_email(order)
order.update!
record_success(order)
SubscriptionMailer.confirmation_email(order).deliver
end
def send_failed_payment_email(order)
order.update!
record_and_log_error(:failed_payment, order)
SubscriptionMailer.failed_payment_email(order).deliver
end
end

View File

@@ -1,4 +1,4 @@
require 'open_food_network/subscription_summarizer'
require 'order_management/subscriptions/summarizer'
class SubscriptionPlacementJob
def perform
@@ -17,7 +17,7 @@ class SubscriptionPlacementJob
delegate :record_and_log_error, :send_placement_summary_emails, to: :summarizer
def summarizer
@summarizer ||= OpenFoodNetwork::SubscriptionSummarizer.new
@summarizer ||= OrderManagement::Subscriptions::Summarizer.new
end
def proxy_orders

View File

@@ -17,7 +17,7 @@ class OrderCycle < ActiveRecord::Base
has_many :distributors, source: :receiver, through: :cached_outgoing_exchanges, uniq: true
has_and_belongs_to_many :schedules, join_table: 'order_cycle_schedules'
has_paper_trail meta: { custom_data: :schedule_ids }
has_paper_trail meta: { custom_data: proc { |order_cycle| order_cycle.schedule_ids.to_s } }
attr_accessor :incoming_exchanges, :outgoing_exchanges

View File

@@ -1,6 +1,6 @@
class Schedule < ActiveRecord::Base
has_and_belongs_to_many :order_cycles, join_table: 'order_cycle_schedules'
has_paper_trail meta: { custom_data: :order_cycle_ids }
has_paper_trail meta: { custom_data: proc { |schedule| schedule.order_cycle_ids.to_s } }
has_many :coordinators, uniq: true, through: :order_cycles

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
module Spree
module Stock
class Quantifier
attr_reader :stock_items
def initialize(variant)
@variant = variant
@stock_items = fetch_stock_items
end
def total_on_hand
stock_items.sum(&:count_on_hand)
end
def backorderable?
stock_items.any?(&:backorderable)
end
def can_supply?(required)
total_on_hand >= required || backorderable?
end
private
def fetch_stock_items
# Don't re-fetch associated stock items from the DB if we've already eager-loaded them
return @variant.stock_items.to_a if @variant.stock_items.loaded?
Spree::StockItem.joins(:stock_location).
where(:variant_id => @variant, Spree::StockLocation.table_name => { active: true })
end
end
end
end

View File

@@ -15,8 +15,10 @@ class VariantOverrideSet < ModelSet
tag_list.empty?
end
# Override of ModelSet method to allow us to check presence of a tag_list (which is not an attribute)
# This method will delete VariantOverrides that have no values (see deletable? above)
# If the user sets all values to nil in the UI the VO will be deleted from the DB
def collection_to_delete
# Override of ModelSet method to allow us to check presence of a tag_list (which is not an attribute)
deleted = []
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes, e.tag_list) }
deleted

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
module Api
module Admin
class ProductSimpleSerializer < ActiveModel::Serializer
attributes :id, :name, :producer_id
has_many :variants, key: :variants, serializer: Api::Admin::VariantSimpleSerializer
def producer_id
object.supplier_id
end
end
end
end

View File

@@ -13,9 +13,9 @@ module Api
end
def in_open_and_upcoming_order_cycles
SubscriptionVariantsService.in_open_and_upcoming_order_cycles?(option_or_assigned_shop,
option_or_assigned_schedule,
object.variant)
OrderManagement::Subscriptions::VariantsList.in_open_and_upcoming_order_cycles?(option_or_assigned_shop,
option_or_assigned_schedule,
object.variant)
end
private

View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true
module Api
module Admin
class VariantSimpleSerializer < ActiveModel::Serializer
attributes :id, :name, :import_date,
:options_text, :unit_value, :unit_description, :unit_to_display,
:display_as, :display_name, :name_to_display,
:price, :on_demand, :on_hand
has_many :variant_overrides
def name
if object.full_name.present?
"#{object.name} - #{object.full_name}"
else
object.name
end
end
def on_hand
return 0 if object.on_hand.nil?
object.on_hand
end
def price
object.price.nil? ? 0.to_f : object.price
end
end
end
end

View File

@@ -73,12 +73,16 @@ module Api
# This results in 3 queries per enterprise
def distributed_properties
return [] unless active
(distributed_product_properties + distributed_producer_properties).uniq do |property_object|
property_object.property.presentation
end
end
def distributed_product_properties
return [] unless active
properties = Spree::Property
.joins(products: { variants: { exchanges: :order_cycle } })
.merge(Exchange.outgoing)
@@ -91,6 +95,8 @@ module Api
end
def distributed_producer_properties
return [] unless active
properties = Spree::Property
.joins(
producer_properties: {

View File

@@ -1,6 +1,6 @@
require 'open_food_network/permissions'
require 'open_food_network/proxy_order_syncer'
require 'open_food_network/order_cycle_form_applicator'
require 'order_management/subscriptions/proxy_order_syncer'
class OrderCycleForm
def initialize(order_cycle, params, user)
@@ -58,7 +58,7 @@ class OrderCycleForm
return unless schedule_ids?
return unless schedule_sync_required?
OpenFoodNetwork::ProxyOrderSyncer.new(subscriptions_to_sync).sync!
OrderManagement::Subscriptions::ProxyOrderSyncer.new(subscriptions_to_sync).sync!
end
def schedule_sync_required?

View File

@@ -40,7 +40,7 @@ class ProductsRenderer
end
def product_scoper
OpenFoodNetwork::ScopeProductToHub.new(distributor)
@product_scoper ||= OpenFoodNetwork::ScopeProductToHub.new(distributor)
end
def enterprise_fee_calculator

View File

@@ -0,0 +1,31 @@
# frozen_string_literal: true
# Lists available order cycles for a given customer in a given distributor
module Shop
class OrderCyclesList
def initialize(distributor, customer)
@distributor = distributor
@customer = customer
end
def call
order_cycles = OrderCycle.with_distributor(@distributor).active
.order(@distributor.preferred_shopfront_order_cycle_order)
apply_tag_rules!(order_cycles)
end
private
# order_cycles is a ActiveRecord::Relation that is modified with reject in the TagRuleApplicator
# If this relation is reloaded (for example by calling count on it), the modifications are lost
def apply_tag_rules!(order_cycles)
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor,
"FilterOrderCycles",
@customer.andand.tag_list)
applicator.filter!(order_cycles)
order_cycles
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
class ShopsListService
def open_shops
shops_list.ready_for_checkout.all
end
def closed_shops
shops_list.not_ready_for_checkout.all
end
private
def shops_list
Enterprise
.activated
.visible
.is_distributor
.includes(address: [:state, :country])
.includes(:properties)
.includes(supplied_products: :properties)
end
end

View File

@@ -1,63 +0,0 @@
require 'open_food_network/scope_variant_to_hub'
# Responsible for estimating prices and fees for subscriptions
# Used by SubscriptionForm as part of the create/update process
# The values calculated here are intended to be persisted in the db
class SubscriptionEstimator
def initialize(subscription)
@subscription = subscription
end
def estimate!
assign_price_estimates
assign_fee_estimates
end
private
attr_accessor :subscription
delegate :subscription_line_items, :shipping_method, :payment_method, :shop, to: :subscription
def assign_price_estimates
subscription_line_items.each do |item|
item.price_estimate =
price_estimate_for(item.variant, item.price_estimate_was)
end
end
def price_estimate_for(variant, fallback)
return fallback unless fee_calculator && variant
scoper.scope(variant)
fees = fee_calculator.indexed_fees_for(variant)
(variant.price + fees).to_d
end
def fee_calculator
return @fee_calculator unless @fee_calculator.nil?
next_oc = subscription.schedule.andand.current_or_next_order_cycle
return nil unless shop && next_oc
@fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, next_oc)
end
def scoper
OpenFoodNetwork::ScopeVariantToHub.new(shop)
end
def assign_fee_estimates
subscription.shipping_fee_estimate = shipping_fee_estimate
subscription.payment_fee_estimate = payment_fee_estimate
end
def shipping_fee_estimate
shipping_method.calculator.compute(subscription)
end
def payment_fee_estimate
payment_method.calculator.compute(subscription)
end
end

View File

@@ -1,34 +0,0 @@
require 'open_food_network/proxy_order_syncer'
class SubscriptionForm
attr_accessor :subscription, :params, :order_update_issues, :validator, :order_syncer, :estimator
delegate :json_errors, :valid?, to: :validator
delegate :order_update_issues, to: :order_syncer
def initialize(subscription, params = {})
@subscription = subscription
@params = params
@estimator = SubscriptionEstimator.new(subscription)
@validator = SubscriptionValidator.new(subscription)
@order_syncer = OrderSyncer.new(subscription)
end
def save
subscription.assign_attributes(params)
return false unless valid?
subscription.transaction do
estimator.estimate!
proxy_order_syncer.sync!
order_syncer.sync!
subscription.save!
end
end
private
def proxy_order_syncer
OpenFoodNetwork::ProxyOrderSyncer.new(subscription)
end
end

View File

@@ -1,127 +0,0 @@
# Encapsulation of all of the validation logic required for subscriptions
# Public interface consists of #valid? method provided by ActiveModel::Validations
# and #json_errors which compiles a serializable hash of errors
class SubscriptionValidator
include ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_reader :subscription
validates :shop, :customer, :schedule, :shipping_method, :payment_method, presence: true
validates :bill_address, :ship_address, :begins_at, presence: true
validate :shipping_method_allowed?
validate :payment_method_allowed?
validate :payment_method_type_allowed?
validate :ends_at_after_begins_at?
validate :customer_allowed?
validate :schedule_allowed?
validate :credit_card_ok?
validate :subscription_line_items_present?
validate :requested_variants_available?
delegate :shop, :customer, :schedule, :shipping_method, :payment_method, to: :subscription
delegate :bill_address, :ship_address, :begins_at, :ends_at, to: :subscription
delegate :subscription_line_items, to: :subscription
def initialize(subscription)
@subscription = subscription
end
def json_errors
errors.messages.each_with_object({}) do |(k, v), errors|
errors[k] = v.map { |msg| build_msg_from(k, msg) }
end
end
private
def shipping_method_allowed?
return unless shipping_method
return if shipping_method.distributors.include?(shop)
errors.add(:shipping_method, :not_available_to_shop, shop: shop.name)
end
def payment_method_allowed?
return unless payment_method
return if payment_method.distributors.include?(shop)
errors.add(:payment_method, :not_available_to_shop, shop: shop.name)
end
def payment_method_type_allowed?
return unless payment_method
return if Subscription::ALLOWED_PAYMENT_METHOD_TYPES.include? payment_method.type
errors.add(:payment_method, :invalid_type)
end
def ends_at_after_begins_at?
# Only validates ends_at if it is present
return if begins_at.blank? || ends_at.blank?
return if ends_at > begins_at
errors.add(:ends_at, :after_begins_at)
end
def customer_allowed?
return unless customer
return if customer.enterprise == shop
errors.add(:customer, :does_not_belong_to_shop, shop: shop.name)
end
def schedule_allowed?
return unless schedule
return if schedule.coordinators.include?(shop)
errors.add(:schedule, :not_coordinated_by_shop, shop: shop.name)
end
def credit_card_ok?
return unless customer && payment_method
return unless stripe_payment_method?(payment_method)
return errors.add(:payment_method, :charges_not_allowed) unless customer.allow_charges
return if customer.user.andand.default_card.present?
errors.add(:payment_method, :no_default_card)
end
def stripe_payment_method?(payment_method)
payment_method.type == "Spree::Gateway::StripeConnect" ||
payment_method.type == "Spree::Gateway::StripeSCA"
end
def subscription_line_items_present?
return if subscription_line_items.reject(&:marked_for_destruction?).any?
errors.add(:subscription_line_items, :at_least_one_product)
end
def requested_variants_available?
subscription_line_items.each { |sli| verify_availability_of(sli.variant) }
end
def verify_availability_of(variant)
return if available_variant_ids.include? variant.id
name = "#{variant.product.name} - #{variant.full_name}"
errors.add(:subscription_line_items, :not_available, name: name)
end
def available_variant_ids
return @available_variant_ids if @available_variant_ids.present?
subscription_variant_ids = subscription_line_items.map(&:variant_id)
@available_variant_ids = SubscriptionVariantsService.eligible_variants(shop)
.where(id: subscription_variant_ids).pluck(:id)
end
def build_msg_from(k, msg)
return msg[1..-1] if msg.starts_with?("^")
errors.full_message(k, msg)
end
end

View File

@@ -1,39 +0,0 @@
class SubscriptionVariantsService
# Includes the following variants:
# - Variants of permitted producers
# - Variants of hub
# - Variants that are in outgoing exchanges where the hub is receiver
def self.eligible_variants(distributor)
variant_conditions = ["spree_products.supplier_id IN (?)", permitted_producer_ids(distributor)]
exchange_variant_ids = outgoing_exchange_variant_ids(distributor)
if exchange_variant_ids.present?
variant_conditions[0] << " OR spree_variants.id IN (?)"
variant_conditions << exchange_variant_ids
end
Spree::Variant.joins(:product).where(is_master: false).where(*variant_conditions)
end
def self.in_open_and_upcoming_order_cycles?(distributor, schedule, variant)
scope = ExchangeVariant.joins(exchange: { order_cycle: :schedules })
.where(variant_id: variant, exchanges: { incoming: false, receiver_id: distributor })
.merge(OrderCycle.not_closed)
scope = scope.where(schedules: { id: schedule })
scope.any?
end
def self.permitted_producer_ids(distributor)
other_permitted_producer_ids = EnterpriseRelationship.joins(:parent)
.permitting(distributor.id).with_permission(:add_to_order_cycle)
.merge(Enterprise.is_primary_producer)
.pluck(:parent_id)
other_permitted_producer_ids | [distributor.id]
end
def self.outgoing_exchange_variant_ids(distributor)
ExchangeVariant.select("DISTINCT exchange_variants.variant_id").joins(:exchange)
.where(exchanges: { incoming: false, receiver_id: distributor.id })
.pluck(:variant_id)
end
end

View File

@@ -1,20 +0,0 @@
class SubscriptionsCount
def initialize(order_cycles)
@order_cycles = order_cycles
end
def for(order_cycle_id)
active[order_cycle_id] || 0
end
private
attr_accessor :order_cycles
def active
return @active unless @active.nil?
return @active = [] if order_cycles.blank?
@active ||= ProxyOrder.not_canceled.group(:order_cycle_id).where(order_cycle_id: order_cycles).count
end
end

View File

@@ -1,45 +1,49 @@
# Report the stock levels of:
# - all variants in the order
# - all requested variant ids
require 'open_food_network/scope_variant_to_hub'
class VariantsStockLevels
def call(order, requested_variant_ids)
variant_stock_levels = variant_stock_levels(order.line_items)
# Variants are not scoped here and so the stock levels reported are incorrect
# See cart_controller_spec for more details and #3222
order_variant_ids = variant_stock_levels.keys
missing_variant_ids = requested_variant_ids - order_variant_ids
missing_variant_ids.each do |variant_id|
variant = Spree::Variant.find(variant_id)
variant_stock_levels[variant_id] = { quantity: 0, max_quantity: 0, on_hand: variant.on_hand, on_demand: variant.on_demand }
end
missing_variants = Spree::Variant.includes(:stock_items).
where(id: (requested_variant_ids - order_variant_ids))
# The code above is most probably dead code, this bugsnag notification will confirm it
notify_bugsnag(order, requested_variant_ids, order_variant_ids) if missing_variant_ids.present?
missing_variants.each do |missing_variant|
variant = scoped_variant(order.distributor, missing_variant)
variant_stock_levels[variant.id] =
{ quantity: 0, max_quantity: 0, on_hand: variant.on_hand, on_demand: variant.on_demand }
end
variant_stock_levels
end
private
def notify_bugsnag(order, requested_variant_ids, order_variant_ids)
error_msg = "VariantsStockLevels.call with variants in the request that are not in the order"
Bugsnag.notify(RuntimeError.new(error_msg),
requested_variant_ids: requested_variant_ids.as_json,
order_variant_ids: order_variant_ids.as_json,
order: order.as_json,
line_items: order.line_items.as_json)
end
def variant_stock_levels(line_items)
Hash[
line_items.map do |line_item|
[line_item.variant.id,
variant = scoped_variant(line_item.order.distributor, line_item.variant)
[variant.id,
{ quantity: line_item.quantity,
max_quantity: line_item.max_quantity,
on_hand: line_item.variant.on_hand,
on_demand: line_item.variant.on_demand }]
on_hand: variant.on_hand,
on_demand: variant.on_demand }]
end
]
end
def scoped_variant(distributor, variant)
return variant if distributor.blank?
scoper(distributor).scope(variant)
variant
end
def scoper(distributor)
@scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(distributor)
end
end

View File

@@ -3,7 +3,7 @@
= inject_available_payment_methods
= inject_saved_credit_cards
= f_form_for current_order,
= form_for current_order,
html: {name: "checkout",
id: "checkout_form",
novalidate: true,

View File

@@ -52,7 +52,8 @@
.row
.small-12.columns
= f.text_area :special_instructions, label: t(:checkout_instructions), size: "60x4", "ng-model" => "order.special_instructions"
%label{ for: 'order_special_instructions'}= t(:checkout_instructions)
= f.text_area :special_instructions, size: "60x4", "ng-model" => "order.special_instructions"
.row
.small-12.columns.text-right

View File

@@ -34,10 +34,10 @@
select: "select(\'map\')"}
.map-container
%map{"ng-if" => "(isActive(\'/map\') && (mapShowed = true)) || mapShowed"}
%google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
%ui-gmap-google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
%map-osm-tiles
%map-search
%markers{models: "mapMarkers", fit: "true",
%ui-gmap-markers{models: "mapMarkers", fit: "true",
coords: "'self'", icon: "'icon'", click: "'reveal'"}
%tab{heading: t(:groups_about),

View File

@@ -0,0 +1,10 @@
- bugsnag_js_key = ENV['BUGSNAG_JS_KEY'] || ENV['BUGSNAG_API_KEY']
- if bugsnag_js_key.present?
%script{src: "//d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js"}
:javascript
window.bugsnagClient = bugsnag({
apiKey: "#{bugsnag_js_key}",
beforeSend: function (report) {
report.app.releaseStage = "#{Rails.env}"
}
});

View File

@@ -37,6 +37,7 @@
#footer
%loading
= render "layouts/bugsnag_js"
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry#{ ENV['GOOGLE_MAPS_API_KEY'] ? '&key=' + ENV['GOOGLE_MAPS_API_KEY'] : ''} "}
= javascript_include_tag "darkswarm/all"

View File

@@ -6,10 +6,10 @@
.map-container{"fill-vertical" => true}
%map{"ng-controller" => "MapCtrl"}
%google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
%ui-gmap-google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
%map-osm-tiles
%map-search
%markers{models: "OfnMap.enterprises", fit: "true",
%ui-gmap-markers{models: "OfnMap.enterprises", fit: "true",
coords: "'self'", icon: "'icon'", click: "'reveal'"}
.map-footer

View File

@@ -26,8 +26,11 @@
%a{href: "", "ng-click" => "showDistanceMatches()"}
= t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}"
.more-controls
%a.button{href: "", ng: {click: "showClosedShops()", show: "!show_closed"}}
= t '.show_closed_shops'
%a.button{href: "", ng: {click: "hideClosedShops()", show: "show_closed"}}
= t '.hide_closed_shops'
%img.spinner.text-center{ng: {show: "closed_shops_loading"}, src: "/assets/spinning-circles.svg" }
%span{ng: {if: "!show_closed", cloak: true}}
%a.button{href: "", ng: {click: "showClosedShops()"}}
= t '.show_closed_shops'
%span{ng: {if: "show_closed", cloak: true}}
%a.button{href: "", ng: {click: "hideClosedShops()"}}
= t '.hide_closed_shops'
%a.button{href: main_app.map_path}= t '.show_on_map'

View File

@@ -20,28 +20,28 @@
%label{ :for => 'start_date_filter' }
= t("admin.start_date")
%br
%input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' }
%input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()', 'ng-model-options' => '{ debounce: 1000 }' }
.date_filter{ :class => "two columns" }
%label{ :for => 'end_date_filter' }
= t("admin.end_date")
%br
%input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' }
%input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()', 'ng-model-options' => '{ debounce: 1000 }' }
.one.column &nbsp;
.filter_select{ :class => "three columns" }
%label{ :for => 'supplier_filter' }
= t("admin.producer")
%br
%input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', blank: "{ id: 0, name: '#{t(:all)}' }", ng: { model: 'supplierFilter' } }
%input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter', change: 'refreshData()' } }
.filter_select{ :class => "three columns" }
%label{ :for => 'distributor_filter' }
= t("admin.shop")
%br
%input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', blank: "{ id: 0, name: '#{t(:all)}' }", ng: { model: 'distributorFilter' } }
%input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter', change: 'refreshData()' } }
.filter_select{ :class => "three columns" }
%label{ :for => 'order_cycle_filter' }
= t("admin.order_cycle")
%br
%input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', blank: "{ id: 0, name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter', change: 'refreshData()' } }
%input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter', change: 'refreshData()' } }
.filter_clear{ :class => "two columns omega" }
%label{ :for => 'clear_all_filters' }
%br
@@ -94,7 +94,7 @@
%hr.divider.sixteen.columns.alpha.omega
.controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || lineItems.length == 0' } }
.controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || line_items.length == 0' } }
%div.three.columns.alpha
%input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' }
= render 'admin/shared/bulk_actions_dropdown'
@@ -157,7 +157,7 @@
= t("admin.orders.bulk_management.ask")
%input{ :type => 'checkbox', 'ng-model' => "confirmDelete" }
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
%tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | filter:quickSearch | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } }
%td.bulk
%input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true }
%td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }}
@@ -175,7 +175,7 @@
%span.error{ ng: { bind: 'line_item.errors.quantity' } }
%td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }}
%td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' }
%input.show-dirty{ :type => 'number', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[1-9]+/' }
%input.show-dirty{ type: 'number', step: 'any', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[0-9]*[.]?[0-9]+/' }
%span.error{ ng: { bind: 'line_item.errors.final_weight_volume' } }
%td.price{ 'ng-show' => 'columns.price.visible' }
%input.show-dirty{ :type => 'text', :name => 'price', :id => 'price', :ng => { value: 'line_item.price * line_item.quantity | currency:""', readonly: "true", class: '{"update-error": line_item.errors.price}' } }

View File

@@ -1,4 +1,3 @@
= @payment_method
- case @payment_method
- when Spree::Gateway::StripeConnect
= render 'stripe_connect'

View File

@@ -12,6 +12,7 @@
%link{:href => "//fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"}
= stylesheet_link_tag 'admin/all'
= render "layouts/bugsnag_js"
= javascript_include_tag 'admin/all'
= render "spree/admin/shared/translations"

View File

@@ -13,7 +13,8 @@
%tr.order-row
%td.order1
%a{"ng-href" => "{{::order.path}}", "ng-bind" => "::order.number"}
%td.order2{"ng-bind" => "::Orders.shopsByID[order.shop_id].name"}
%td.order2
%a{"ng-href" => "{{::Orders.shopsByID[order.shop_id].hash}}#{main_app.shop_path}", "ng-bind" => "::Orders.shopsByID[order.shop_id].name"}
%td.order3.show-for-large-up{"ng-bind" => "::order.changes_allowed_until"}
%td.order4.show-for-large-up{"ng-bind" => "::order.item_count"}
%td.order5.text-right{"ng-class" => "{'credit' : order.total < 0, 'debit' : order.total > 0, 'paid' : order.total == 0}","ng-bind" => "::order.total | localizeCurrency"}

View File

@@ -13,7 +13,8 @@
%tr.order-row
%td.order1
%a{"ng-href" => "{{::order.path}}", "ng-bind" => "::order.number"}
%td.order2{"ng-bind" => "::Orders.shopsByID[order.shop_id].name"}
%td.order2
%a{"ng-href" => "{{::Orders.shopsByID[order.shop_id].hash}}#{main_app.shop_path}", "ng-bind" => "::Orders.shopsByID[order.shop_id].name"}
%td.order3.show-for-large-up{"ng-bind" => "::order.completed_at"}
%td.order4.show-for-large-up{"ng-bind" => "::order.item_count"}
%td.order5.text-right{"ng-class" => "{'debit': order.payment_state != 'paid', 'credit': order.payment_state == 'paid'}","ng-bind" => "::order.total | localizeCurrency"}

View File

@@ -1,4 +1,4 @@
= f_form_for @spree_user, :as => :spree_user, :url => spree.spree_user_password_path, :method => :put do |f|
= form_for @spree_user, :as => :spree_user, :url => spree.spree_user_password_path, :method => :put do |f|
= render :partial => 'spree/shared/error_messages', :locals => { :target => @spree_user }
%fieldset
.row
@@ -6,9 +6,11 @@
%legend= t(:change_my_password)
.row
.small-12.medium-6.large-4.columns.medium-centered.large-centered
%label{ for: 'spree_user_password'}= t(:password)
= f.password_field :password
.row
.small-12.medium-6.large-4.columns.medium-centered.large-centered
%label{ for: 'spree_user_password_confirmation'}= t(:password_confirmation)
= f.password_field :password_confirmation
= f.hidden_field :reset_password_token
.row

View File

@@ -1,4 +1,17 @@
# This file is used by Rack-based servers to start the application.
if ENV.fetch('KILL_UNICORNS', false) && ['production', 'staging'].include?(ENV['RAILS_ENV'])
# Gracefully restart individual unicorn workers if they have:
# - performed between 25000 and 30000 requests
# - grown in memory usage to between 700 and 850 MB
require 'unicorn/worker_killer'
use Unicorn::WorkerKiller::MaxRequests,
ENV.fetch('UWK_REQS_MIN', 25_000).to_i,
ENV.fetch('UWK_REQS_MAX', 30_000).to_i
use Unicorn::WorkerKiller::Oom,
( ENV.fetch('UWK_MEM_MIN', 700).to_i * (1024**2) ),
( ENV.fetch('UWK_MEM_MAX', 850).to_i * (1024**2) )
end
require ::File.expand_path('../config/environment', __FILE__)
run Openfoodnetwork::Application

View File

@@ -37,6 +37,9 @@ SMTP_PASSWORD: 'f00d'
# MAILS_FROM: hello@example.com
# MAIL_BCC: manager@example.com
# Javascript error reporting via Bugsnag.
#BUGSNAG_JS_KEY: ""
# SingleSignOn login for Discourse
#
# DISCOURSE_SSO_SECRET should be a random string. It must be the same as provided to your Discourse instance.

View File

@@ -1,7 +1,7 @@
defaults: &defaults
adapter: postgresql
encoding: unicode
pool: 5
pool: <%= ENV.fetch('OFN_DB_POOL', 5) %>
host: <%= ENV.fetch('OFN_DB_HOST', 'localhost') %>
username: <%= ENV.fetch('OFN_DB_USERNAME', 'ofn') %>
password: <%= ENV.fetch('OFN_DB_PASSWORD', 'f00d') %>

View File

@@ -4,5 +4,6 @@ if ENV['DATADOG_RAILS_APM']
c.use :delayed_job, service_name: 'delayed_job'
c.use :dalli, service_name: 'memcached'
c.analytics_enabled = true
c.runtime_metrics_enabled = true
end
end

View File

@@ -1,138 +0,0 @@
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Wrappers are used by the form builder to generate a
# complete input. You can remove any component from the
# wrapper, change the order or even add your own to the
# stack. The options given below are used to wrap the
# whole input.
config.wrappers :default, :class => :input,
:hint_class => :field_with_hint, :error_class => :field_with_errors do |b|
## Extensions enabled by default
# Any of these extensions can be disabled for a
# given input by passing: `f.input EXTENSION_NAME => false`.
# You can make any of these extensions optional by
# renaming `b.use` to `b.optional`.
# Determines whether to use HTML5 (:email, :url, ...)
# and required attributes
b.use :html5
# Calculates placeholders automatically from I18n
# You can also pass a string as f.input :placeholder => "Placeholder"
b.use :placeholder
## Optional extensions
# They are disabled unless you pass `f.input EXTENSION_NAME => :lookup`
# to the input. If so, they will retrieve the values from the model
# if any exists. If you want to enable the lookup for any of those
# extensions by default, you can change `b.optional` to `b.use`.
# Calculates maxlength from length validations for string inputs
b.optional :maxlength
# Calculates pattern from format validations for string inputs
b.optional :pattern
# Calculates min and max from length validations for numeric inputs
b.optional :min_max
# Calculates readonly automatically from readonly attributes
b.optional :readonly
## Inputs
b.use :label_input
b.use :hint, :wrap_with => { :tag => :span, :class => :hint }
b.use :error, :wrap_with => { :tag => :span, :class => :error }
end
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :default
# Define the way to render check boxes / radio buttons with labels.
# Defaults to :nested for bootstrap config.
# :inline => input + label
# :nested => label > input
config.boolean_style = :nested
# Default class for buttons
config.button_class = 'btn'
# Method used to tidy up errors. Specify any Rails Array method.
# :first lists the first message for each field.
# Use :to_sentence to list all errors for each field.
# config.error_method = :first
# Default tag used for error notification helper.
config.error_notification_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'alert alert-error'
# ID to add for error notification helper.
# config.error_notification_id = nil
# Series of attempts to detect a default label method for collection.
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
# Series of attempts to detect a default value method for collection.
# config.collection_value_methods = [ :id, :to_s ]
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
# config.collection_wrapper_tag = nil
# You can define the class to use on all collection wrappers. Defaulting to none.
# config.collection_wrapper_class = nil
# You can wrap each item in a collection of radio/check boxes with a tag,
# defaulting to :span. Please note that when using :boolean_style = :nested,
# SimpleForm will force this option to be a label.
# config.item_wrapper_tag = :span
# You can define a class to use in all item wrappers. Defaulting to none.
# config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
# config.label_text = lambda { |label, required| "#{required} #{label}" }
# You can define the class to use on all labels. Default is nil.
config.label_class = 'control-label'
# You can define the class to use on all forms. Default is simple_form.
# config.form_class = :simple_form
# You can define which elements should obtain additional classes
# config.generate_additional_classes_for = [:wrapper, :label, :input]
# Whether attributes are required by default (or not). Default is true.
# config.required_by_default = true
# Tell browsers whether to use default HTML5 validations (novalidate option).
# Default is enabled.
config.browser_validations = false
# Collection of methods to detect if a file type was given.
# config.file_methods = [ :mounted_as, :file?, :public_filename ]
# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value.
# config.input_mappings = { /count/ => :integer }
# Default priority for time_zone inputs.
# config.time_zone_priority = nil
# Default priority for country inputs.
# config.country_priority = nil
# Default size for text inputs.
# config.default_input_size = 50
# When false, do not use translations for labels.
# config.translate_labels = true
# Automatically discover new inputs in Rails' autoload path.
# config.inputs_discovery = true
# Cache SimpleForm inputs discovery
# config.cache_discovery = !Rails.env.development?
end

View File

@@ -19,6 +19,8 @@ ar:
shipping_category_id: "نوع الشحن"
variant_unit: "وحدة النوع"
variant_unit_name: "اسم وحدة النوع"
spree/credit_card:
base: "بطاقة ائتمان"
order_cycle:
orders_close_at: تاريخ الاغلاق
errors:
@@ -53,7 +55,7 @@ ar:
messages:
inclusion: "غير مدرجة في القائمة"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^ الرجاء إضافة منتج واحد على الأقل"
@@ -860,6 +862,11 @@ ar:
cancel: "إلغاء"
back_to_list: "العودة للقائمة"
outgoing:
outgoing: "الصادر"
distributor: "الموزع"
products: "منتجات"
tags: "الاوسمة"
fees: "رسوم"
previous: "السابق"
save: "حفظ"
save_and_back_to_list: "حفظ والعودة إلى القائمة"

View File

@@ -19,6 +19,8 @@ ca:
shipping_category_id: "Categoria d'enviament"
variant_unit: "Unitat de la variant"
variant_unit_name: "Nom de la unitat de la variant"
spree/credit_card:
base: "Targeta de crèdit"
order_cycle:
orders_close_at: Data de tancament
errors:
@@ -29,6 +31,10 @@ ca:
taken: "Ja hi ha un compte per a aquest correu electrònic. Si us plau, inicia sessió o restableix la contrasenya."
spree/order:
no_card: No hi ha targetes de crèdit autoritzades disponibles per carregar
spree/credit_card:
attributes:
base:
card_expired: "Ha expirat"
order_cycle:
attributes:
orders_close_at:
@@ -53,7 +59,7 @@ ca:
messages:
inclusion: "no està inclòs a la llista"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Afegiu com a mínim un producte"
@@ -246,6 +252,8 @@ ca:
notes: Notes
error: Error
processing_payment: "S'està processant el pagament..."
no_pending_payments: "No hi ha pagaments pendents"
invalid_payment_state: "Estat de pagament no vàlid"
filter_results: Aplicar filtre
quantity: Quantitat
pick_up: Recollida
@@ -862,6 +870,12 @@ ca:
cancel: "Cancel·lar"
back_to_list: "Tornar a la llista"
outgoing:
outgoing: "Sortint"
distributor: "Distribuïdora"
products: "Productes "
tags: "Etiquetes"
delivery_details: "Detalls d'enviament"
fees: "Comissions"
previous: "Anterior"
save: "Desa"
save_and_back_to_list: "Desa i torna a la llista"
@@ -1487,6 +1501,7 @@ ca:
shopping_oc_closed_description: "Si us plau espera fins que s'obri el pròxim cicle (o posa't en contacte amb nosaltres directament per veure si podem acceptar alguna comanda fora de temps)"
shopping_oc_last_closed: "L'últim cicle va tancar fa %{distance_of_time} "
shopping_oc_next_open: "El següent cicle s'obre en %{distance_of_time}"
shopping_oc_select: "Selecciona ..."
shopping_tabs_home: "Inici"
shopping_tabs_shop: "Botiga"
shopping_tabs_about: "Sobre"
@@ -1860,6 +1875,7 @@ ca:
headline: "Acabat!"
thanks: "Gràcies per omplir els detalls de%{enterprise}."
login: "Pots canviar o actualitzar la teva organització en qualsevol moment accedint a Katuma i anant a Admin."
action: "Vés al panell de control de l'organització"
back: "Enrere"
continue: "Continua"
action_or: "O"
@@ -2629,6 +2645,12 @@ ca:
tub:
one: "tina"
other: "cubells"
punnet:
one: "puntet"
other: "capses"
packet:
one: "paquet"
other: "paquets"
item:
one: "article"
other: "articles"
@@ -3015,6 +3037,8 @@ ca:
tax_invoice: "FACTURA D'IMPOSTOS"
code: "Codi"
from: "De"
to: "Facturar a"
shipping: "Enviament"
form:
distribution_fields:
title: "Distribució"

View File

@@ -19,6 +19,8 @@ de_DE:
shipping_category_id: "Versandkategorie"
variant_unit: "Varianteneinheit"
variant_unit_name: "Name der Varianteneinheit"
spree/credit_card:
base: "Kreditkarte"
order_cycle:
orders_close_at: Schlussdatum
errors:
@@ -29,6 +31,10 @@ de_DE:
taken: "Es gibt bereits ein Konto für diese E-Mail-Adresse. Bitte versuchen Sie sich einzuloggen oder setzen Sie Ihr Passwort zurück."
spree/order:
no_card: Es sind keine belastbaren Karten verfügbar.
spree/credit_card:
attributes:
base:
card_expired: "abgelaufen"
order_cycle:
attributes:
orders_close_at:
@@ -36,7 +42,7 @@ de_DE:
variant_override:
count_on_hand:
using_producer_stock_settings_but_count_on_hand_set: "muss leer sein, da die Einstellungen des Produzentenbestands verwendet werden"
on_demand_but_count_on_hand_set: "muss bei Bedarf leer sein"
on_demand_but_count_on_hand_set: "muss leer sein falls Produktion auf Nachfrage"
limited_stock_but_no_count_on_hand: "muss angegeben werden, da nur begrenzte Lagerbestände erforderlich sind"
activemodel:
attributes:
@@ -46,12 +52,14 @@ de_DE:
distributor_ids: "Hubs"
producer_ids: "Erzeuger"
order_cycle_ids: "Bestellrunden"
enterprise_fee_ids: "Gebühren Namen"
enterprise_fee_ids: "Gebührennamen"
shipping_method_ids: "Lieferart"
payment_method_ids: "Zahlungsarten"
errors:
messages:
inclusion: "ist in der Liste nicht enthalten"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^ Bitte fügen Sie mindestens ein Produkt hinzu"
@@ -244,6 +252,8 @@ de_DE:
notes: Anmerkungen
error: Fehler
processing_payment: "Bezahlung wird verarbeitet..."
no_pending_payments: "Keine ausstehenden Zahlungen"
invalid_payment_state: "Ungültiger Zahlungsstatus"
filter_results: Ergebnisse filtern
quantity: Menge
pick_up: Abholen
@@ -700,6 +710,11 @@ de_DE:
enable_subscriptions_false: "deaktiviert"
enable_subscriptions_true: "aktiviert"
shopfront_message: "Laden-Nachricht"
shopfront_message_placeholder: >
Eine optionale Nachricht, um Kunden willkommen zu heißen und zu erklären,
wie Sie bei Ihnen einkaufen können. Wenn hier Text eingegeben wird,
wird dieser in einem Home-Tab in Ihrem Shop angezeigt, wenn Kunden zum
ersten Mal ihren Shop besuchen.
shopfront_message_link_tooltip: "Link einfügen / bearbeiten"
shopfront_message_link_prompt: "Bitte geben Sie eine einzufügende URL ein"
shopfront_closed_message: "Laden Geschlossen Nachricht"
@@ -854,6 +869,12 @@ de_DE:
cancel: "Abbrechen"
back_to_list: "Zurück zur Liste"
outgoing:
outgoing: "Ausgehend"
distributor: "Verteiler"
products: "Produkte"
tags: "Stichwörter"
delivery_details: "Lieferdetails"
fees: "Gebühren"
previous: "Bisherige"
save: "Speichern"
save_and_back_to_list: "Speichern und zurück zur Liste"
@@ -1100,10 +1121,13 @@ de_DE:
destroy_attachment_does_not_exist: "Logo existiert nicht"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Webebild existiert nicht"
orders:
failed_to_update: "Bestellung konnte nicht aktualisiert werden"
checkout:
already_ordered:
cart: "Warenkorb"
message_html: "Sie haben bereits eine Bestellung für diesen Bestellzyklus. Überprüfen Sie den %{cart}, um die Artikel zu sehen, die Sie zuvor bestellt haben. Sie können Artikel auch stornieren, solange der Bestellzyklus geöffnet ist."
failed: "Die Bestellung ist fehlgeschlagen. Bitte geben Sie uns Bescheid, damit wir Ihre Bestellung trotzdem bearbeiten können."
shops:
hubs:
show_closed_shops: "Geschlossene Läden anzeigen"
@@ -1121,7 +1145,7 @@ de_DE:
checkout: "Zur Kasse"
already_ordered_products: "Bereits in diesem Bestellzyklus bestellt"
register_call:
selling_on_ofn: "Interesse am Open Food Network?"
selling_on_ofn: "Sie möchten selbst im Open Food Network verkaufen?"
register: "Hier anmelden"
footer:
footer_secure: "Sicher und vertrauenswürdig."
@@ -1274,6 +1298,7 @@ de_DE:
saving_credit_card: Kreditkarte speichern ...
card_has_been_removed: "Ihre Karte wurde entfernt (Nummer: %{number})"
card_could_not_be_removed: Die Karte konnte nicht entfernt werden
invalid_credit_card: "Ungültige Kreditkarte"
ie_warning_headline: "Ihr Browser ist veraltet :-("
ie_warning_text: "Für das beste Open-Food-Network-Erlebnis empfehlen wir dringend, Ihren Browser zu aktualisieren:"
ie_warning_chrome: Chrome herunterladen
@@ -1361,7 +1386,7 @@ de_DE:
checkout_default_bill_address: "Als Standard-Rechnungsadresse speichern"
checkout_shipping: Versandinformation
checkout_default_ship_address: "Als Standardversandadresse speichern"
checkout_method_free: ??
checkout_method_free: kostenlos
checkout_address_same: Lieferadresse wie Rechnungsadresse?
checkout_ready_for: "Bereit am:"
checkout_instructions: "Kommentare oder spezielle Anweisungen?"
@@ -1475,6 +1500,7 @@ de_DE:
shopping_oc_closed_description: "Bitte warten Sie, bis der nächste Zyklus beginnt (oder kontaktieren Sie uns direkt, um zu sehen, ob wir verspätete Bestellungen annehmen können)"
shopping_oc_last_closed: "Der letzte Zyklus wurde vor %{distance_of_time} geschlossen"
shopping_oc_next_open: "Der nächste Zyklus wird in %{distance_of_time} geöffnet"
shopping_oc_select: "Wählen..."
shopping_tabs_home: "Startseite"
shopping_tabs_shop: "Laden"
shopping_tabs_about: "Über Uns"
@@ -1758,13 +1784,13 @@ de_DE:
address1_field_placeholder: "z.B. 123 Cranberry-Laufwerk"
address1_field_error: "Bitte geben Sie eine Adresse an"
address2_field: "Anschrift Zeile 2:"
suburb_field: "Vorort:"
suburb_field: "Ort:"
suburb_field_placeholder: "z.B. Northcote"
suburb_field_error: "Bitte geben Sie einen Vorort ein"
postcode_field: "Postleitzahl:"
postcode_field_placeholder: "z.B. 3070"
postcode_field_error: "Postleitzahl erforderlich"
state_field: "Zustand:"
state_field: "Bundesland:"
state_field_error: "Staat erforderlich"
country_field: "Land:"
country_field_error: "Bitte wähle ein Land"
@@ -1783,8 +1809,8 @@ de_DE:
yes_producer: "Ja, ich bin ein Produzent."
no_producer: "Nein, ich bin kein Produzent"
producer_field_error: "Bitte wählen Sie: Sind Sie ein Produzent?"
yes_producer_help: "Hersteller machen leckere Dinge zu essen und / oder zu trinken. Du bist ein Produzent, wenn du ihn anbaust, erziehst ihn, braue ihn, backe ihn, gähre ihn, melke ihn oder forme ihn."
no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Hub, Coop, Einkaufsgruppe, Einzelhändler, Großhändler oder andere sein."
yes_producer_help: "Die Produzenten machen leckere Sachen zum Essen und / oder Trinken. Sie sind ein Produzent, wenn Sie anbauen, brauen, backen, fermentieren, melken oder sonst wie Lebenmittel produzieren."
no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Foodhub, eine Coop, eine Einkaufsgruppe, Einzelhändler, ein Hofladen, Großhändler oder vergleichbares sein."
create_profile: "Profil erstellen"
about:
title: "Über Uns"
@@ -1848,6 +1874,7 @@ de_DE:
headline: "Fertig!"
thanks: "Vielen Dank, dass Sie die Details für %{enterprise} ausgefüllt haben."
login: "Sie können Ihr Unternehmen jederzeit ändern oder aktualisieren, indem Sie sich bei Open Food Network anmelden und zum Administrator wechseln."
action: "Gehen Sie zum Enterprise Dashboard"
back: "Zurück"
continue: "Fortsetzen"
action_or: "ODER"
@@ -1933,6 +1960,7 @@ de_DE:
tax_category: "Steuerkategorie"
calculator: "Rechner"
calculator_values: "Rechnerwerte"
calculator_settings_warning: "Wenn Sie den Gebühren-Typ ändern, müssen Sie zuerst speichern, bevor Sie die Gebühren-Einstellungen bearbeiten können"
flat_percent_per_item: "Flache Prozent (pro Artikel)"
flat_rate_per_item: "Pauschale (pro Stück)"
flat_rate_per_order: "Pauschalpreis pro Bestellung)"
@@ -2263,6 +2291,7 @@ de_DE:
enterprise_register_success_notice: "Herzliche Glückwünsche! Registrierung für %{enterprise} ist abgeschlossen!"
enterprise_bulk_update_success_notice: "Unternehmen wurden erfolgreich aktualisiert"
enterprise_bulk_update_error: 'Update fehlgeschlagen'
enterprise_shop_show_error: "Der gesuchte Shop existiert nicht oder ist auf OFN inaktiv. Bitte schauen Sie nach anderen Shops!"
order_cycles_create_notice: 'Ihr Bestellzyklus wurde erstellt.'
order_cycles_update_notice: 'Ihr Bestellzyklus wurde aktualisiert.'
order_cycles_bulk_update_notice: 'Bestellzyklen wurden aktualisiert.'
@@ -2417,6 +2446,12 @@ de_DE:
severity: Schwere
description: Beschreibung
resolve: Entschlossenheit
exchange_products:
load_more_variants: "Weitere Varianten laden"
load_all_variants: "Alle Varianten laden"
select_all_variants: "Wählen Sie alle %{total_number_of_variants}-Varianten aus"
variants_loaded: "%{num_of_variants_loaded} von %{total_number_of_variants} Varianten geladen"
loading_variants: "lade Varianten"
tag_rules:
shipping_method_tagged_top: "Versandarten markiert"
shipping_method_tagged_bottom: "sind:"
@@ -2499,6 +2534,7 @@ de_DE:
customer_placeholder: "Kunde@beispiel.org"
valid_email_error: "Bitte geben Sie eine gültige E-Mail-Adresse ein"
subscriptions:
error_saving: "Fehler beim Speichern des Abonnements"
new:
please_select_a_shop: "Bitte wählen Sie einen Laden"
insufficient_stock: "Nicht genügend Lagerbestand verfügbar, nur noch %{on_hand} verfügbar"
@@ -2575,9 +2611,78 @@ de_DE:
have_an_account: "Hast du schon ein Konto?"
action_login: "Jetzt einloggen."
inflections:
each:
one: "jeder"
other: "pro Stück"
bunch:
one: "Bündel"
other: "Bündel"
pack:
one: "Pack"
other: "Packungen"
box:
one: "Box"
other: "Kisten"
bottle:
one: "Flasche"
other: "Flaschen"
jar:
one: "Krug"
other: "Gläser"
head:
one: "Kopf"
other: "Köpfe"
bag:
one: "Tasche"
other: "Beutel"
loaf:
one: "Laib"
other: "Laibe"
single:
one: "Einzeln"
other: "Einzel"
tub:
one: "Wanne"
other: "Wannen"
punnet:
one: "Körbchen"
other: "Körbchen"
packet:
one: "Paket"
other: "Pakete"
item:
one: "Artikel"
other: "Artikel"
dozen:
one: "Dutzend"
other: "Dutzende"
unit:
one: "Einheit"
other: "Einheiten"
serve:
one: "Portion"
other: "Portionen"
tray:
one: "Schale"
other: "Schalen"
piece:
one: "Stück"
other: "Stücke"
pot:
one: "Topf"
other: "Töpfe"
bundle:
one: "bündeln"
other: "Bündel"
flask:
one: "Flasche"
other: "Flaschen"
basket:
one: "Korb"
other: "Körbe"
sack:
one: "Sack"
other: "Säcke"
producers:
signup:
start_free_profile: "Beginnen Sie mit einem kostenlosen Profil und erweitern Sie es, wenn Sie fertig sind!"
@@ -2643,6 +2748,7 @@ de_DE:
status: "Status"
new: "Neu"
start: "Start"
end: "Ende"
stop: "Halt"
first: "Zuerst"
previous: "Bisherige"
@@ -2819,6 +2925,8 @@ de_DE:
zipcode: Postleitzahl
weight: Gewicht (pro kg)
error_user_destroy_with_orders: "Benutzer mit abgeschlossenen Bestellungen dürfen nicht gelöscht werden"
cannot_create_payment_without_payment_methods: "Sie können keine Zahlung für eine Bestellung erstellen, ohne dass Zahlungsmethoden definiert sind."
please_define_payment_methods: "Bitte definieren Sie zunächst die Zahlungsmethoden."
options: "Optionen"
actions:
update: "Aktualisieren"
@@ -2905,6 +3013,7 @@ de_DE:
capture: "Erfassung"
ship: "Liefern"
edit: "Bearbeiten"
order_not_updated: "Die Bestellung konnte nicht aktualisiert werden"
note: "Hinweis"
first: "Zuerst"
last: "Letzte"
@@ -2927,6 +3036,8 @@ de_DE:
tax_invoice: "Steuerrechnung"
code: "Code"
from: "Von"
to: "Rechnungsempfänger"
shipping: "Versand"
form:
distribution_fields:
title: "Verteilung"
@@ -3124,6 +3235,12 @@ de_DE:
used_saved_card: "Verwende eine gespeicherte Karte:"
or_enter_new_card: "Oder geben Sie Details für eine neue Karte ein:"
remember_this_card: Erinnerst du dich an diese Karte?
stripe_sca:
choose_one: Wähle Sie eine Option
enter_new_card: Geben Sie Details für eine neue Karte ein
used_saved_card: "Verwenden Sie eine gespeicherte Karte:"
or_enter_new_card: "Oder geben Sie Details für eine neue Karte ein:"
remember_this_card: Diese Karte speichern?
date_picker:
format: '% Y-% m-%d'
js_format: 'JJ-MM-TT'

View File

@@ -40,6 +40,8 @@ en:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -50,6 +52,10 @@ en:
taken: "There's already an account for this email. Please login or reset your password."
spree/order:
no_card: There are no authorised credit cards available to charge
spree/credit_card:
attributes:
base:
card_expired: "has expired"
order_cycle:
attributes:
orders_close_at:
@@ -74,7 +80,7 @@ en:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -914,6 +920,12 @@ en:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
delivery_details: "Delivery Details"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"

View File

@@ -19,6 +19,8 @@ en_AU:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -51,7 +53,7 @@ en_AU:
payment_method_ids: "Payment Methods"
errors:
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -699,6 +701,16 @@ en_AU:
enable_subscriptions_false: "Disabled"
enable_subscriptions_true: "Enabled"
shopfront_message: "\"Home\" message"
shopfront_message_placeholder: >
Create your home page content to welcome customers and explain how people
can shop with you.
Include details about your delivery and pick up options, how often you
open the shop for orders, and all the details your customers will need
to understand the process of buying from you.
You can also include links to your newsletter sign up, so that people
can connect with you to hear when your next order cycle opens.
shopfront_message_link_tooltip: "Insert / edit link"
shopfront_message_link_prompt: "Please enter a URL to insert"
shopfront_closed_message: "Shopfront Closed Message"
@@ -860,6 +872,11 @@ en_AU:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"

View File

@@ -19,6 +19,8 @@ en_BE:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -51,7 +53,7 @@ en_BE:
payment_method_ids: "Payment Methods"
errors:
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -836,6 +838,11 @@ en_BE:
next: "Next"
cancel: "Cancel"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
fees: "Fees"
previous: "Previous"
save: "Save"
cancel: "Cancel"

View File

@@ -19,6 +19,8 @@ en_CA:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -53,7 +55,7 @@ en_CA:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -861,6 +863,11 @@ en_CA:
cancel: "Cancel"
back_to_list: "Back to List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"

View File

@@ -19,6 +19,8 @@ en_DE:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -51,7 +53,7 @@ en_DE:
payment_method_ids: "Payment Methods"
errors:
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -844,6 +846,11 @@ en_DE:
next: "Next"
cancel: "Cancel"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
fees: "Fees"
previous: "Previous"
save: "Save"
cancel: "Cancel"

View File

@@ -19,6 +19,8 @@ en_FR:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -29,6 +31,10 @@ en_FR:
taken: "There's already an account for this email. Please login or reset your password."
spree/order:
no_card: There are no authorised credit cards available to charge
spree/credit_card:
attributes:
base:
card_expired: "has expired"
order_cycle:
attributes:
orders_close_at:
@@ -53,7 +59,7 @@ en_FR:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -861,6 +867,12 @@ en_FR:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
delivery_details: "Delivery Details"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
@@ -3019,6 +3031,8 @@ en_FR:
tax_invoice: "TAX INVOICE"
code: "Code"
from: "From"
to: "Bill to"
shipping: "Shipping"
form:
distribution_fields:
title: "Distribution"

View File

@@ -19,6 +19,8 @@ en_GB:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -29,6 +31,10 @@ en_GB:
taken: "There's already an account for this email. Please login or reset your password."
spree/order:
no_card: There are no authorised credit cards available to charge
spree/credit_card:
attributes:
base:
card_expired: "has expired"
order_cycle:
attributes:
orders_close_at:
@@ -53,7 +59,7 @@ en_GB:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -861,6 +867,12 @@ en_GB:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
delivery_details: "Delivery Details"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
@@ -3025,6 +3037,8 @@ en_GB:
tax_invoice: "TAX INVOICE"
code: "Code"
from: "From"
to: "Bill to"
shipping: "Shipping"
form:
distribution_fields:
title: "Distribution"

View File

@@ -19,6 +19,8 @@ en_NZ:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -29,6 +31,10 @@ en_NZ:
taken: "There's already an account for this email. Please login or reset your password."
spree/order:
no_card: There are no authorised credit cards available to charge
spree/credit_card:
attributes:
base:
card_expired: "has expired"
order_cycle:
attributes:
orders_close_at:
@@ -53,7 +59,7 @@ en_NZ:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -861,6 +867,12 @@ en_NZ:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
delivery_details: "Delivery Details"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"

3373
config/locales/en_PH.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@ en_US:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close Date
errors:
@@ -53,7 +55,7 @@ en_US:
messages:
inclusion: "is not included in the list"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -608,7 +610,7 @@ en_US:
desc_long: About Us
desc_long_placeholder: Tell customers about yourself. This information appears on your public profile.
business_details:
abn: Tax ID Number or EIN (optional)
abn: Tax ID, DUNS Number, or other business ID
abn_placeholder: eg. 123456789
acn: Legal Business Name
acn_placeholder: eg. Martin's Produce LLC
@@ -861,6 +863,12 @@ en_US:
cancel: "Cancel"
back_to_list: "Back To List"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
delivery_details: "Delivery Details"
fees: "Fees"
previous: "Previous"
save: "Save"
save_and_back_to_list: "Save and Back to List"
@@ -1172,11 +1180,11 @@ en_US:
invoice_column_price_without_taxes: "Total price (Excl. tax)"
invoice_column_tax_rate: "Tax rate"
invoice_tax_total: "Tax total:"
tax_invoice: "TAX INVOICE"
tax_invoice: "INVOICE"
tax_total: "Total tax (%{rate}):"
total_excl_tax: "Total (Excl. tax):"
total_incl_tax: "Total (Incl. tax):"
abn: "Tax ID Number or EIN (optional)"
abn: "Tax ID, DUNS Number, or other business ID"
acn: "Legal Business Name"
invoice_issued_on: "Invoice issued on:"
order_number: "Invoice number:"
@@ -1809,7 +1817,7 @@ en_US:
enterprise_long_desc: "Long Description"
enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words."
enterprise_long_desc_length: "%{num} characters / up to 600 recommended"
enterprise_abn: "Tax ID Number or EIN (optional)"
enterprise_abn: "Tax ID, DUNS Number, or other business ID"
enterprise_abn_placeholder: "eg. 123456789"
enterprise_acn: "Legal Business Name"
enterprise_acn_placeholder: "eg. Justins Produce LLC"
@@ -2888,6 +2896,12 @@ en_US:
minimal_amount: "Minimal Amount"
normal_amount: "Normal Amount"
discount_amount: "Discount Amount"
no_images_found: "No Images Found"
new_image: "New Image"
filename: "Filename"
alt_text: "Alternative Text"
thumbnail: "Thumbnail"
back_to_images_list: "Back To Images List"
email: Email
account_updated: "Account updated!"
email_updated: "The account will be updated once the new email is confirmed."
@@ -2899,6 +2913,9 @@ en_US:
zipcode: Zipcode
weight: Weight (per lb)
error_user_destroy_with_orders: "Users with completed orders may not be deleted"
cannot_create_payment_without_payment_methods: "You cannot create a payment for an order without any payment methods defined."
please_define_payment_methods: "Please define some payment methods first."
options: "Options"
actions:
update: "Update"
errors:
@@ -2930,27 +2947,53 @@ en_US:
product_properties:
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
add_product_properties: "Add Product Properties"
select_from_prototype: "Select From Prototype"
properties:
index:
properties: "Properties"
new_property: "New Property"
name: "Name"
presentation: "Presentation"
new:
new_property: "New Property"
edit:
editing_property: "Editing Property"
back_to_properties_list: "Back To Properties List"
form:
name: "Name"
presentation: "Presentation"
return_authorizations:
index:
new_return_authorization: "New Return Authorization"
return_authorizations: "Return Authorizations"
back_to_orders_list: "Back To Orders List"
rma_number: "RMA Number"
status: "Status"
amount: "Amount"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
continue: "Continue"
new:
new_return_authorization: "New Return Authorization"
back_to_return_authorizations_list: "Back To Return Authorization List"
continue: "Continue"
edit:
receive: "receive"
are_you_sure: "Are you sure?"
return_authorization: "Return Authorization"
form:
product: "Product"
quantity_shipped: "Quantity Shipped"
quantity_returned: "Quantity Returned"
return_quantity: "Return Quantity"
amount: "Amount"
rma_value: "RMA Value"
reason: "Reason"
stock_location: "Stock Location"
states:
authorized: "Authorized"
received: "Received"
canceled: "Canceled"
orders:
index:
listing_orders: "Listing Orders"
@@ -2958,6 +3001,7 @@ en_US:
capture: "Capture"
ship: "Ship"
edit: "Edit"
order_not_updated: "The order could not be updated"
note: "Note"
first: "First"
last: "Last"
@@ -2977,9 +3021,11 @@ en_US:
email: "Customer E-mail"
invoice:
issued_on: "Issued on"
tax_invoice: "TAX INVOICE"
tax_invoice: "INVOICE"
code: "Code"
from: "From"
to: "Bill to"
shipping: "Shipping"
form:
distribution_fields:
title: "Distribution"
@@ -3140,12 +3186,22 @@ en_US:
index:
sku: "SKU"
price: "Price"
options: "Options"
no_results: "No results"
to_add_variants_you_must_first_define: "To add variants, you must first define"
option_types: "Option Types"
option_values: "Option Values"
and: "and"
new_variant: "New Variant"
show_active: "Show Active"
show_deleted: "Show Deleted"
new:
new_variant: "New Variant"
form:
sku: "SKU"
price: "Price"
display_as: "Display As"
display_name: "Display Name"
autocomplete:
producer_name: "Producer"
unit: "Unit"
@@ -3291,3 +3347,19 @@ en_US:
allow_charges?: "Allow Charges?"
localized_number:
invalid_format: has an invalid format. Please enter a number.
api:
invalid_api_key: "Invalid API key (%{key}) specified."
unauthorized: "You are not authorized to perform that action."
invalid_resource: "Invalid resource. Please fix errors and try again."
resource_not_found: "The resource you were looking for could not be found."
access: "API Access"
key: "Key"
clear_key: "Clear key"
regenerate_key: "Regenerate Key"
no_key: "No key"
generate_key: "Generate API key"
key_generated: "Key generated"
key_cleared: "Key cleared"
shipment:
cannot_ready: "Cannot ready shipment."
invalid_taxonomy_id: "Invalid taxonomy id."

View File

@@ -19,6 +19,8 @@ en_ZA:
shipping_category_id: "Shipping Category"
variant_unit: "Variant Unit"
variant_unit_name: "Variant Unit Name"
spree/credit_card:
base: "Credit Card"
order_cycle:
orders_close_at: Close date
errors:
@@ -51,7 +53,7 @@ en_ZA:
payment_method_ids: "Payment Methods"
errors:
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Please add at least one product"
@@ -840,6 +842,11 @@ en_ZA:
next: "Next"
cancel: "Cancel"
outgoing:
outgoing: "Outgoing"
distributor: "Distributor"
products: "Products"
tags: "Tags"
fees: "Fees"
previous: "Previous"
save: "Save"
cancel: "Cancel"

View File

@@ -19,6 +19,8 @@ es:
shipping_category_id: "Categoría de envío"
variant_unit: "Unidad Variante"
variant_unit_name: "Nombre de la unidad de la variante"
spree/credit_card:
base: "Tarjeta de crédito"
order_cycle:
orders_close_at: Fecha de cierre
errors:
@@ -51,7 +53,7 @@ es:
payment_method_ids: "Métodos de Pago"
errors:
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Por favor agrega al menos un producto"
@@ -845,6 +847,11 @@ es:
next: "Siguiente"
cancel: "Cancelar"
outgoing:
outgoing: "Saliente"
distributor: "Distribuidora"
products: "Productos"
tags: "Tags"
fees: "Comisiones"
previous: "Anterior"
save: "Guardar"
cancel: "Cancelar"

3385
config/locales/fil_PH.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@ fr:
shipping_category_id: "Condition de transport"
variant_unit: "Unité"
variant_unit_name: "Unité de la variante"
spree/credit_card:
base: "Carte de crédit"
order_cycle:
orders_close_at: Date de fermeture
errors:
@@ -29,6 +31,10 @@ fr:
taken: "Un compte existe déjà pour cet e-mail. Connectez-vous ou demandez un nouveau mot de passe."
spree/order:
no_card: Aucune carte de paiement autorisée disponible
spree/credit_card:
attributes:
base:
card_expired: "a expiré"
order_cycle:
attributes:
orders_close_at:
@@ -53,7 +59,7 @@ fr:
messages:
inclusion: "n'est pas inclus dans la liste"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Veuillez ajouter au moins un produit"
@@ -863,6 +869,12 @@ fr:
cancel: "Annuler"
back_to_list: "Retour à la liste"
outgoing:
outgoing: "Produits sortants (mis en vente par/via un ou plusieurs hubs)"
distributor: "Distributeur"
products: "Produits"
tags: "Tags"
delivery_details: "Détails de livraison"
fees: "Commissions"
previous: "Précédent"
save: "Sauvegarder"
save_and_back_to_list: "Sauvegarder et revenir à la liste"
@@ -1658,7 +1670,7 @@ fr:
orders_form_admin: Admin & traitements
orders_form_total: Total
orders_oc_expired_headline: Les commandes ne sont plus possibles pour ce cycle de vente.
orders_oc_expired_text: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives."
orders_oc_expired_text: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}!"
orders_oc_expired_text_others_html: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives <strong>%{link}</strong>."
orders_oc_expired_text_link: "ou voir si d'autres cycles de vente sont ouverts pour ce hub"
orders_oc_expired_email: "Email:"
@@ -3049,6 +3061,8 @@ fr:
tax_invoice: "FACTURE"
code: "Code"
from: "De"
to: "Facturer à"
shipping: "Envoi"
form:
distribution_fields:
title: "Distribution"

View File

@@ -19,6 +19,8 @@ fr_BE:
shipping_category_id: "Catégorie de livraison"
variant_unit: "Unité de variante"
variant_unit_name: "Nom de la variante"
spree/credit_card:
base: "Carte de crédit"
order_cycle:
orders_close_at: Date de fermeture
errors:
@@ -53,7 +55,7 @@ fr_BE:
messages:
inclusion: "N'est pas inclus dans la liste"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "^Veuillez ajouter au moins un produit"
@@ -861,6 +863,11 @@ fr_BE:
cancel: "Annuler"
back_to_list: "Retour à la liste."
outgoing:
outgoing: "Produits sortants (mis en vente par/via un ou plusieurs comptoir·s)"
distributor: "Distributeur·trice"
products: "Produits"
tags: "Tags"
fees: "Commission"
previous: "Précédent"
save: "Sauvergarder"
save_and_back_to_list: "Sauver et retour à la liste"
@@ -1131,7 +1138,7 @@ fr_BE:
checkout: "Passer la commande"
already_ordered_products: "Déjà commandé dans ce cycle de vente"
register_call:
selling_on_ofn: "Intéressé à participer à Open Food Network?"
selling_on_ofn: "Intéressé·e à vendre sur Open Food Network?"
register: "Démarrez ici"
footer:
footer_secure: "Fiable et sécurisé."

View File

@@ -19,6 +19,8 @@ fr_CA:
shipping_category_id: "Condition de transport"
variant_unit: "Unité"
variant_unit_name: "Unité de la variante"
spree/credit_card:
base: "Carte de crédit"
order_cycle:
orders_close_at: Date de fermeture
errors:
@@ -53,7 +55,7 @@ fr_CA:
messages:
inclusion: "n'est pas inclus dans la liste"
models:
subscription_validator:
order_management/subscriptions/validator:
attributes:
subscription_line_items:
at_least_one_product: "Veuillez ajouter au moins un produit"
@@ -863,6 +865,11 @@ fr_CA:
cancel: "Annuler"
back_to_list: "Retour à la liste"
outgoing:
outgoing: "Produits sortants (mis en vente par/via un ou plusieurs hubs)"
distributor: "Hub-distributeur"
products: "Produits"
tags: "Tags"
fees: "Commissions"
previous: "Précédent"
save: "Enregistrer"
save_and_back_to_list: "Sauvegarder et suivant"

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