Compare commits

..

180 Commits

Author SHA1 Message Date
Pau Pérez Fabregat
54a51a4995 Merge pull request #5988 from openfoodfoundation/dependabot/bundler/stripe-5.25.0
Bump stripe from 5.22.0 to 5.25.0
2020-09-23 21:14:32 +02:00
Pau Perez
37d113ed78 Update all locales with the latest Transifex translations 2020-09-23 21:09:49 +02:00
Pau Pérez Fabregat
8f6e527f8a Merge pull request #6044 from openfoodfoundation/transifex
Transifex
2020-09-23 21:06:44 +02:00
Pau Pérez Fabregat
0d259b217e Merge pull request #6017 from Matt-Yorkley/tidyup
Remove old code
2020-09-23 20:47:33 +02:00
Pau Pérez Fabregat
2e5b5eaac5 Merge pull request #6043 from andrewpbrett/imperial-units-product-import
Add oz and lb as allowed units in product import
2020-09-23 20:46:54 +02:00
Pau Pérez Fabregat
faaf391892 Merge pull request #6051 from luisramos0/russian
Adding the russian language to github
2020-09-23 20:43:22 +02:00
Transifex-Openfoodnetwork
feed9376dd Updating translations for config/locales/es.yml 2020-09-24 04:36:06 +10:00
Transifex-Openfoodnetwork
b829fea0c2 Updating translations for config/locales/ca.yml 2020-09-24 04:36:01 +10:00
Luis Ramos
a2610279d9 Merge pull request #5976 from luisramos0/address_form
Fix address state selector and "use billing address" checkbox in order customer details page
2020-09-23 12:42:37 +01:00
Matt-Yorkley
25a8c44c22 Merge pull request #6018 from Matt-Yorkley/tax-display
Tax display
2020-09-22 17:53:04 +02:00
Luis Ramos
7ebdc1d0da Merge pull request #6059 from andresgutgon/fix/sticky-search-bar-disapears-when-cart-is-open
Fix sticky search bar disappearing when cart is open.
2020-09-22 10:52:33 +01:00
Luis Ramos
5c25d92354 Merge pull request #6069 from arku/perf/adjustment-metadata-specs
Replace `create` with `build` in adjustment metadata model specs
2020-09-21 12:22:24 +01:00
Arun Kumar Mohan
0aa5ee081e Replace create with build in adjustment metadata model specs 2020-09-21 01:54:46 -05:00
Transifex-Openfoodnetwork
82cf2afec9 Updating translations for config/locales/en_CA.yml 2020-09-20 23:27:52 +10:00
Transifex-Openfoodnetwork
8ea7614353 Updating translations for config/locales/fr_CA.yml 2020-09-20 23:25:11 +10:00
Luis Ramos
714bfafede Merge pull request #6037 from arku/fix/hash-diff-deprecation
Remove Hash#diff from cancan helper
2020-09-19 20:21:24 +01:00
Luis Ramos
8f98fb830a Merge pull request #6053 from jhsu802701/bundler_1_17_3
Upgraded the bundler version in the Dockerfile
2020-09-19 19:33:48 +01:00
andresgutgon
636da229ad Fix sticky search bar disappearing when cart is open.
So I think the issue is that all the HTML is wrapped on an
`off-canvas-wrap` class that is used for doing the sidebar car open over
main content. The problem is that when this car sidebar is open body of
HTML overflow is changed to `overflow: hidden` and search bar use CSS
`position: sticky;` which doesn't work  when its parent has overflow
hidden. The issue was that `off-canvas-wrap` had an `overflow: inherit`
which means when body is set to overflow hidden this div inherits it and
break search bar position sticky when cart sidebar is opened. The
solution is to use `position: initial` which means use what a div has as
default value for `overflow` which I think it's `visible`. This class is
overriding the same class that comes from Foundation Framework that set
this div to be `overflow: hidden`. The override was added when [we added
search sticky](ff69389bb0)

More info about the problem with [position:sticky and its parent having
overflow hidden](https://css-tricks.com/dealing-with-overflow-and-position-sticky/) also info about [position initial vs inherit](https://stackoverflow.com/a/29661356)
2020-09-19 12:21:24 +02:00
Jason Hsu
560577827f Upgraded the bundler version in the Dockerfile to be consistent with that specified in Gemfile.lock 2020-09-18 15:07:44 -05:00
Luis Ramos
9c1a7c13dc Adding the russian language 2020-09-18 16:43:57 +01:00
Transifex-Openfoodnetwork
9cb7275250 Updating translations for config/locales/ar.yml 2020-09-18 19:49:55 +10:00
Transifex-Openfoodnetwork
3bf98e295d Updating translations for config/locales/en_FR.yml 2020-09-18 17:36:38 +10:00
Transifex-Openfoodnetwork
678e0be0a5 Updating translations for config/locales/fr.yml 2020-09-18 17:36:29 +10:00
Andy Brett
0ef4dec15e specs for product import using pounds and ounces 2020-09-17 09:34:22 -07:00
Transifex-Openfoodnetwork
880ef9cf4f Updating translations for config/locales/es.yml 2020-09-18 01:01:34 +10:00
Transifex-Openfoodnetwork
095633f21e Updating translations for config/locales/ca.yml 2020-09-18 00:59:40 +10:00
Pau Pérez Fabregat
3a894a1cdb Merge pull request #6002 from openfoodfoundation/dependabot/bundler/unicorn-5.7.0
Bump unicorn from 5.6.0 to 5.7.0
2020-09-17 16:57:16 +02:00
Transifex-Openfoodnetwork
ee862891c1 Updating translations for config/locales/en_FR.yml 2020-09-18 00:51:30 +10:00
Transifex-Openfoodnetwork
9dccb641c7 Updating translations for config/locales/fr.yml 2020-09-18 00:50:02 +10:00
Andy Brett
d1a87f7ba0 add oz and lb as allowed units in product import 2020-09-17 06:34:07 -07:00
Matt-Yorkley
77eaebc2a7 Merge pull request #5888 from andrewpbrett/imperial-auto-units
Allow US units on products/variants
2020-09-17 15:04:15 +02:00
Matt-Yorkley
3ececb04c5 Update all locales with the latest Transifex translations 2020-09-17 13:33:53 +01:00
Matt-Yorkley
0aa515101b Merge pull request #6015 from openfoodfoundation/transifex
Transifex
2020-09-17 14:31:41 +02:00
Luis Ramos
c6139a975a Merge pull request #6028 from openfoodfoundation/dependabot/bundler/webmock-3.9.1
Bump webmock from 3.8.3 to 3.9.1
2020-09-17 12:19:08 +01:00
Luis Ramos
212820b3da Merge pull request #5912 from luisramos0/tcs
Terms and Conditions - upload PDF in the Enterprise Business Details and read them on Checkout
2020-09-17 12:01:45 +01:00
Luis Ramos
f1358dfa9a Merge pull request #5722 from rioug/4206-back-from-Edit-Product-removed-filters-products-page
4206 back from edit product removed filters products page
2020-09-17 11:51:58 +01:00
Luis Ramos
f061545a92 Merge pull request #5995 from andresgutgon/fix/datetime-picker-ui-pick-the-right-translations-on-order-cycles
Fix date time picker translations on Order cycles screen
2020-09-17 11:50:37 +01:00
Luis Ramos
e99fdeb972 Merge pull request #5996 from andresgutgon/fix/iframe-shop-failing-because-jquery-is-not-loaded
Fix embedded shops failing javascript because jQuery is try to be accessed before being loaded
2020-09-17 11:32:08 +01:00
Transifex-Openfoodnetwork
875eb292be Updating translations for config/locales/ar.yml 2020-09-17 08:42:12 +10:00
Arun Kumar Mohan
08604ae8fd Remove Hash#diff usage from cancan helper 2020-09-16 12:14:37 -05:00
Luis Ramos
87d6a73e54 Merge pull request #5878 from luisramos0/shipping
[Bye bye Spree] Bring models shipping_method, shipping_rates, address and shipping_category from spree_core
2020-09-16 14:58:39 +01:00
Luis Ramos
f566c2127d Merge pull request #5924 from luisramos0/basic_spree_core
[Bye bye spree] Bring base_helper and log_entry from spree core
2020-09-16 14:57:41 +01:00
Transifex-Openfoodnetwork
2573e3b7c5 Updating translations for config/locales/es_CO.yml 2020-09-16 23:24:39 +10:00
Transifex-Openfoodnetwork
7fe876266e Updating translations for config/locales/es_CO.yml 2020-09-16 23:21:34 +10:00
Luis Ramos
8d5ed630d6 Merge pull request #6035 from arku/fix/adjustment-metadata-spec
Fix uninitialized constant error when running model specs
2020-09-16 10:15:46 +01:00
Luis Ramos
66d206ecb3 Merge pull request #6036 from arku/fix/pr-template
Fix typo in the PR template
2020-09-16 10:15:00 +01:00
Arun Kumar Mohan
fd0a7971e9 Fix typo in the PR template 2020-09-15 20:30:23 -05:00
Arun Kumar Mohan
ed3cb56c11 Fix uninitialized constant error when running model specs 2020-09-15 20:23:05 -05:00
Transifex-Openfoodnetwork
a367c3720c Updating translations for config/locales/en_US.yml 2020-09-16 10:21:50 +10:00
Luis Ramos
6eb43053e9 Merge pull request #5925 from luisramos0/spree_core_user
[Bye bye spree] Bring classes related to users to OFN
2020-09-15 17:13:27 +01:00
Pau Pérez Fabregat
7006b0af4c Merge pull request #6021 from Matt-Yorkley/concerns
Move concern to concerns directory
2020-09-15 17:45:24 +02:00
Transifex-Openfoodnetwork
d86b879972 Updating translations for config/locales/nb.yml 2020-09-15 20:46:33 +10:00
Transifex-Openfoodnetwork
fd339488e6 Updating translations for config/locales/es.yml 2020-09-15 20:38:08 +10:00
Transifex-Openfoodnetwork
b6f5eab6e1 Updating translations for config/locales/ca.yml 2020-09-15 20:37:51 +10:00
dependabot-preview[bot]
ced29c1f3d Bump webmock from 3.8.3 to 3.9.1
Bumps [webmock](https://github.com/bblimke/webmock) from 3.8.3 to 3.9.1.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.8.3...v3.9.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-15 09:49:14 +00:00
Pau Pérez Fabregat
27ffe9edb2 Merge pull request #5971 from luisramos0/testing
Remove some old spree test helpers that are not needed
2020-09-15 09:13:20 +02:00
Luis Ramos
0d7b5cd32c Merge pull request #5868 from luisramos0/taxonomies
[Bye bye Spree] Bring models taxon and taxonomy from spree_core
2020-09-14 20:45:04 +01:00
Matt-Yorkley
276fea6942 Fix rubocop offenses 2020-09-14 15:14:16 +01:00
Matt-Yorkley
c45dcad975 Move concern to concerns directory 2020-09-14 13:23:21 +01:00
Transifex-Openfoodnetwork
f32d8e2678 Updating translations for config/locales/en_GB.yml 2020-09-13 04:29:16 +10:00
Transifex-Openfoodnetwork
a9a05debba Updating translations for config/locales/en_CA.yml 2020-09-13 02:30:11 +10:00
Matt-Yorkley
9481876595 Fix line_item tax sum in OrderTaxAdjustmentsFetcher 2020-09-12 15:41:29 +01:00
Matt-Yorkley
cdf4e88e21 Add failing spec for OrderTaxAdjustmentsFetcher with multiple line items 2020-09-12 15:40:06 +01:00
Matt-Yorkley
7ddc53bb5b Remove temporary debugging helpers added during the Rails 4 upgrade. 2020-09-12 12:25:43 +01:00
Transifex-Openfoodnetwork
b9f6dc6de5 Updating translations for config/locales/en_FR.yml 2020-09-12 00:25:49 +10:00
Transifex-Openfoodnetwork
811985cf0f Updating translations for config/locales/fr.yml 2020-09-12 00:25:36 +10:00
Luis Ramos
175b78b51f Merge pull request #5827 from jeduardo824/bug/remove-environment-column-when-user-is-not-super-admin
Bug/4592 - Hide Environment column on Payment Methods page when user is not admin
2020-09-11 10:47:10 +01:00
Luis Ramos
6f59158153 Remove useless TODOs 2020-09-10 17:36:25 +01:00
dependabot-preview[bot]
5eb64f431a Bump unicorn from 5.6.0 to 5.7.0
Bumps [unicorn](https://yhbt.net/unicorn/) from 5.6.0 to 5.7.0.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 09:20:12 +00:00
andresgutgon
2297e20c78 Fix embedded shops failing javascript because jQuery is try to be
accessed before being loaded
Show we check for $.ready when jQuery is not downloaded yet in the
browser. The solution is to check if document is ready with plain DOM
javascript event `DOMContentLoaded`
2020-09-06 15:24:22 +02:00
andresgutgon
d1331ac78a Fix datetime picker translations on Order cycles screen
So the thing is we initialize jQuery plugin on `admin/util.js.erb` but
then we override those defaults on order_cycles.js.erb.coffe. Now both
plugin initializations use the same defaults. Also added 3 missing
translations for `Done`, `Now` and `Time` copies on that timepicker popover
2020-09-06 13:43:35 +02:00
Luis Ramos
5521b4bb04 Remove unused product filters 2020-09-05 16:43:27 +01:00
Luis Ramos
4183dda27e Fix long line 2020-09-05 16:43:27 +01:00
Luis Ramos
fcbb883244 Transpec taxon_spec 2020-09-05 16:43:27 +01:00
Luis Ramos
c35b330d25 Fix taxon_spec 2020-09-05 16:43:27 +01:00
Luis Ramos
57e74a4980 Fix rubocop issues 2020-09-05 16:43:27 +01:00
Luis Ramos
a1b64fe27b Rubocop auto-correct 2020-09-05 16:43:27 +01:00
Luis Ramos
49060892e8 Merge decorators into original files from spree_core 2020-09-05 16:43:27 +01:00
Luis Ramos
9175504bc1 Bring taxon, taxonomy and classification from spree_core 2020-09-05 16:43:27 +01:00
Eduardo
71876ca23a change spec to use new authentication helper and user factory 2020-09-04 08:48:16 -03:00
Eduardo
07e5f8ed8d fix typo on title of specs 2020-09-04 08:45:58 -03:00
Eduardo
11684dae65 hide environment on payment methods when user is not admin 2020-09-04 08:45:57 -03:00
Luis Ramos
e44efd3db2 Change test of attachment from present? to file? 2020-09-04 10:06:41 +01:00
dependabot-preview[bot]
cb61c83688 Bump stripe from 5.22.0 to 5.25.0
Bumps [stripe](https://github.com/stripe/stripe-ruby) from 5.22.0 to 5.25.0.
- [Release notes](https://github.com/stripe/stripe-ruby/releases)
- [Changelog](https://github.com/stripe/stripe-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-ruby/compare/v5.22.0...v5.25.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-03 17:49:28 +00:00
Luis Ramos
e413920335 Move both OptionValueNamer and VariantAndLineItemNaming to app/services/variant_units 2020-09-03 09:11:00 -07:00
Luis Ramos
83ae13d7c7 Extract method to make code easier to read 2020-09-03 09:11:00 -07:00
Luis Ramos
9204687e4d Fix rubocop issues 2020-09-03 09:11:00 -07:00
Luis Ramos
c6741dda36 Fix rubocop complexity issues by making code easier to read 2020-09-03 09:11:00 -07:00
Andy Brett
55f9fef2c3 denote that weight is in grams on the bulk order management page 2020-09-03 09:11:00 -07:00
Andy Brett
4bc3101f4d Add specs for shipping weight calculations using lbs and oz 2020-09-03 09:11:00 -07:00
Andy Brett
404d7bbc43 add test for options_text using g/lb 2020-09-03 09:11:00 -07:00
Andy Brett
55e448897f use fewer sigfigs for ounces; add spec to option_value_namer 2020-09-03 09:11:00 -07:00
Andy Brett
2fe9f4abc8 style updates for rubocop 2020-09-03 09:11:00 -07:00
Andy Brett
d7a8873ee9 return empty string for unitName if no scale matches 2020-09-03 09:11:00 -07:00
Andy Brett
a2993652c1 get only compatible scales in option_value_namer.js.coffee 2020-09-03 09:11:00 -07:00
Andy Brett
08e6e5a459 add compatibleUnitScales function and spec 2020-09-03 09:11:00 -07:00
Andy Brett
0018ef6eb4 refactor variant_unit_manager.coffee and add systems to scales 2020-09-03 09:11:00 -07:00
Andy Brett
9b9b6ded09 add each scales system in option_value_namer.rb 2020-09-03 09:11:00 -07:00
Andy Brett
e5e7e12a32 green tests for sorted scales 2020-09-03 09:11:00 -07:00
Andy Brett
2f216039ac update variantunitmanager test expectation 2020-09-03 09:11:00 -07:00
Andy Brett
e99799bca2 add ounces and round up for lbs 2020-09-03 09:11:00 -07:00
Zee Spencer
f5e300a5de Presenting a line item or variants options_text uses overriden values
This changes how we display the description of weight, but it doesn't
change the `Spree::OptionValue`s that are being created when someone
adds a product to their cart.

This takes us closer by making the UI look more correct; but it feels
odd compared to settiong the `Spree::OptionValue` to the correct
unit on creation.

But on the other hand, that could possibly make things worse for the
shipping calculation bits.
2020-09-03 09:10:59 -07:00
Zee Spencer
ae0b76e610 Support imperial units when scaling the unit value
We are pretty sure this is not the correct final implementation, but
we wanted to get some tests failing so we can start to fix them.
2020-09-03 09:10:59 -07:00
Zee Spencer
cb09c935dc WIP: Products may be created with pounds for their weight unit_converter
See: https://community.openfoodnetwork.org/t/hubs-managers-can-choose-the-adapted-weight-and-measure-units-for-their-shops-given-their-own-local-situation/1289/11

We're not entirely sure what needs to be changed in order for this to
accurately work with shipping and other parts of the eCommerce platform.

We are assuming that so long as we canonically store the weight scale
in grams, that the shipping calculation will be able to do what it needs
to. So if we put in values for "oz" as grams, we may not need to do
much else in order to let product(s) be sold by the pound (or ounce).

Next steps appear to be:

- [ ] When looking at an order as a customer, do we want to show pounds
      instead of grams? (See: http://localhost:3000/orders/R125684626)
- [ ] Compile a list of tests that are worth writing (because we have
      no confidence that we know what we are supposed to be doing in
      order for this feature to be "ready" to be used by people.)
- [ ] Write a test that demonstrates when we create a product with a
      variant in pound that the product's shipping weight is correctly
      calculated?
- [ ] Do we want to think about i18n?
2020-09-03 09:10:59 -07:00
Luis Ramos
f28241cc5e Merge duplicate Spree::User#superadmin? into existing Spree::admin? 2020-09-02 22:41:42 +01:00
Luis Ramos
ebf4175662 MErge user class extensions into the User class 2020-09-02 22:41:39 +01:00
Luis Ramos
737fc699ed Fix rubocop issues 2020-09-02 22:40:46 +01:00
Luis Ramos
caf61e3a7e Run rubocop -a 2020-09-02 22:40:28 +01:00
Luis Ramos
dfa00a770a Bring user and ability related files from spree_core 2020-09-02 22:37:27 +01:00
Luis Ramos
ffac38d934 Remove option to enter state_name for countries without states
Thisis not supported in OFN
2020-09-01 16:06:49 +01:00
Luis Ramos
5716ea8611 Fix rubocop issue 2020-09-01 15:55:46 +01:00
Luis Ramos
88d90a37e8 Run transpec 2020-09-01 15:53:18 +01:00
Luis Ramos
ed114f4c4c Fix rubocop issues 2020-09-01 15:52:36 +01:00
Luis Ramos
ffa0d202be Add states api endpoint to support existing code on the customer details page when user changes country 2020-09-01 15:46:22 +01:00
Luis Ramos
41d797489d Make onchange code simpler 2020-09-01 14:52:18 +01:00
Luis Ramos
46623242f7 Make use_billing checkbox work again, missign js code from spree 2020-09-01 14:28:45 +01:00
Luis Ramos
1a39a55009 Fix problem of converting from erb to haml 2020-09-01 14:09:06 +01:00
Luis Ramos
56fb09c006 Remove unused class 2020-09-01 14:03:20 +01:00
Luis Ramos
e4dee8a2fb Replace hash rockets 2020-09-01 13:57:57 +01:00
Luis Ramos
04f9c5ec5e Convert address form from erb to haml 2020-09-01 13:53:19 +01:00
Luis Ramos
8c322c1a0f Move shared address_form partial to the only place where it is used, order customer_details 2020-09-01 13:49:17 +01:00
Luis Ramos
865a4b3063 Fix base helper spec 2020-09-01 09:46:41 +01:00
Luis Ramos
b413f856a5 Clarify that it's a Spree issue number 2020-09-01 09:46:41 +01:00
Luis Ramos
2f93a06dd5 Run rubocop -a on files brought from spree_core 2020-09-01 09:46:41 +01:00
Luis Ramos
c151195e3a Merge decorator with original class and delete dead code 2020-09-01 09:46:41 +01:00
Luis Ramos
7ec00cf40a Bring base files from spree_core 2020-09-01 09:45:11 +01:00
Luis Ramos
208be3ede6 Fix rubocop issues 2020-08-31 20:00:08 +01:00
Luis Ramos
de061b4c54 Make it a keyword argument so it's easier to read 2020-08-31 18:47:24 +01:00
Luis Ramos
c7a5dd65cf Ensure all specs that change stripe_connect_enable set the value back to what it was before, which should be the default value false
This will speed up specs as it ensures the stripe is always disabled and its JS script is not loaded
2020-08-31 18:38:50 +01:00
Luis Ramos
d9a228e5ec Replace before and after hook with an around hook 2020-08-31 17:47:41 +01:00
Luis Ramos
8a75fe777c Refactor enterprises controller to reduce code duplication 2020-08-31 17:40:34 +01:00
Luis Ramos
66587ccc00 Allow user to remove terms and conditions file 2020-08-31 17:40:34 +01:00
Luis Ramos
24cdd0c467 Refactor enterprise controller to reduce code duplication 2020-08-31 17:40:34 +01:00
Luis Ramos
a3e9226878 Add option to remove existing terms and conditions file 2020-08-31 17:40:34 +01:00
Luis Ramos
0974c4b2ac Move enterprise images translations to the correct place using lazylookup on the server and to main js: namespace for js translations 2020-08-31 17:40:34 +01:00
Luis Ramos
5a10a2861e Reduce the size of the terms and conditions message on the checkout page 2020-08-31 17:40:34 +01:00
Luis Ramos
aedc12e0e3 Add top padding to terms file upload input in enterprises form 2020-08-31 17:40:34 +01:00
Luis Ramos
fc4cc65e07 Merge typography files in css admin 2020-08-31 17:40:34 +01:00
Luis Ramos
07cee32f04 Move enterprisse_console to pages/enterprise_form 2020-08-31 17:40:34 +01:00
Luis Ramos
42d5344179 Fix checkout spec by fixing wrong default value 2020-08-31 17:40:34 +01:00
Luis Ramos
12d18b2825 Add specs to checkout_spec to validate terms and conditions link 2020-08-31 17:40:34 +01:00
Luis Ramos
ad111e837e Add spec to test terms and conditions link on checkout page 2020-08-31 17:40:34 +01:00
Luis Ramos
746533d3f6 Improve spec titles 2020-08-31 17:40:34 +01:00
Luis Ramos
4ef4a58532 Merge two describe sections with same before method and call it what it is: guest checkout 2020-08-31 17:40:34 +01:00
Luis Ramos
685a5465f1 Simplify checkout stripe spec 2020-08-31 17:40:34 +01:00
Luis Ramos
70e9ef93bb Extract stripe spec from checkout spec so we can expand stripe tests in checkout 2020-08-31 17:40:34 +01:00
Luis Ramos
86ad31eb5c Reuse checkout form filling code from CheckoutHelper in checkout paypal spec 2020-08-31 17:40:34 +01:00
Luis Ramos
edfd0fd95c Move checkout helpers to checkout_helper 2020-08-31 17:40:34 +01:00
Luis Ramos
d1f5828d13 Rename checkout_workflow to checkout_helper 2020-08-31 17:40:34 +01:00
Luis Ramos
785f8ada4d Refactor checkout_spec by removing unnecessary initial describe section 2020-08-31 17:40:34 +01:00
Luis Ramos
b9511d4f07 Show terms and conditions on checkout if enterprise has an associated PDF file 2020-08-31 17:40:34 +01:00
Luis Ramos
16a475d8af Fix some rubocop issues 2020-08-31 17:40:34 +01:00
Luis Ramos
1a734aacf8 Allow user to upload terms and conditions PDF file to an enterprise 2020-08-31 17:40:34 +01:00
Luis Ramos
cfdfd82d9a Replace api controller test helpers with easier existing alternatives 2020-08-31 17:05:51 +01:00
Luis Ramos
b872bf49c5 Replace usage of helper method with a more simple approach 2020-08-31 16:56:48 +01:00
Gaetan Riou
6564ea7b00 rename ProductFiltersService to ProductFiltersUrl 2020-08-21 12:13:16 +10:00
Gaetan Riou
f71013c514 rename query filter where it was missed, fix bulk_update_product spec 2020-08-21 11:46:36 +10:00
Gaetan Riou
44487af2c8 remove dead filter code 2020-08-21 11:40:48 +10:00
Gaetan Riou
c3279941f5 Remove product filter helper spec as it's not needed anymore 2020-08-21 10:42:02 +10:00
Gaetan Riou
7356d0fe77 move url filter functionality to service ProductFiltersService 2020-08-21 10:42:02 +10:00
Gaetan Riou
bba683469b add product filter parameters on the group buy options and search pages, so that the bulk import product page filters can be preserved 2020-08-21 10:39:43 +10:00
Gaetan Riou
c6e1f458cc add product filter parameters on the various product properties pages, so that the bulk import product page filters can be preserved 2020-08-21 10:39:43 +10:00
Gaetan Riou
9bc928fd48 update product variants feature test to use ulr helpers 2020-08-21 10:39:43 +10:00
Gaetan Riou
a6444e76a5 add product filter parameters on the various product image pages, so that the bulk import product page filters can be preserved 2020-08-21 10:29:07 +10:00
Gaetan Riou
9b26ff2fa4 move product filter helper to a service 2020-08-21 10:29:07 +10:00
Gaetan Riou
684ae2ca22 update product feature test to use ulr helpers 2020-08-21 10:29:07 +10:00
Gaetan Riou
459708dbc8 add product filter parameters on the various product variants pages, so that the bulk import product page filters can be preserved 2020-08-21 10:29:07 +10:00
Gaetan Riou
f75aaf0b45 extract product filter functionality to a helper 2020-08-21 10:19:53 +10:00
Gaetan Riou
1a186affcf refactor create and update to get rid of respond_override 2020-08-21 10:19:53 +10:00
Gaetan Riou
6e5c168d3b add filter parameters to link leading back to bulk import product page and preserve filter parameters when updating product 2020-08-21 10:19:53 +10:00
Gaetan Riou
39564e612f on admin product page, add selected filter to url and apply filter from url on page load 2020-08-21 10:19:53 +10:00
Luis Ramos
2d3578bb30 Fix address_spec 2020-08-07 14:05:36 +01:00
Luis Ramos
02f50774bb Run transpec 2020-08-07 12:47:44 +01:00
Luis Ramos
ff8735d7a4 Make new specs pass 2020-08-07 12:45:06 +01:00
Luis Ramos
51a499d5c9 Revert rubocop autocorrect so that spec keeps working 2020-08-07 10:45:15 +01:00
Luis Ramos
086c521a27 Fix easy rubocop issues 2020-08-07 10:28:41 +01:00
Luis Ramos
7cefdda579 Run rubocop autocorrect 2020-08-07 10:21:09 +01:00
Luis Ramos
ba859111de Merge decorators with original files brought from spree 2020-08-07 10:17:30 +01:00
Luis Ramos
fd9479f720 Bring modesl from spree_core 2020-08-07 10:06:08 +01:00
Luis Ramos
0c7a0e3e96 Bring modesl from spree_core 2020-08-07 10:06:04 +01:00
172 changed files with 7564 additions and 1671 deletions

View File

@@ -38,6 +38,5 @@ Add the link or remove this section. -->
#### Documentation updates
<!-- Are their any wiki pages that need updating after merging this PR?
<!-- Are there any wiki pages that need updating after merging this PR?
List them here or remove this section. -->

View File

@@ -19,7 +19,7 @@ RUN git clone --depth 1 --branch v1.1.2 https://github.com/rbenv/rbenv.git ${RBE
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
rbenv install $(cat .ruby-version) && \
rbenv global $(cat .ruby-version) && \
gem install bundler --version=1.17.2
gem install bundler --version=1.17.3
# Install Postgres
RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main' > /etc/apt/sources.list.d/pgdg.list" && \

View File

@@ -509,7 +509,7 @@ GEM
pry-byebug (3.7.0)
byebug (~> 11.0)
pry (~> 0.10)
public_suffix (4.0.5)
public_suffix (4.0.6)
rack (1.5.5)
rack-mini-profiler (2.0.2)
rack (>= 1.2.0)
@@ -658,7 +658,7 @@ GEM
sprockets (>= 2.8, < 4.0)
state_machine (1.2.0)
stringex (1.5.1)
stripe (5.22.0)
stripe (5.25.0)
temple (0.8.2)
test-unit (3.3.6)
power_assert
@@ -671,7 +671,7 @@ GEM
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.7.0)
unicorn (5.6.0)
unicorn (5.7.0)
kgio (~> 2.6)
raindrops (~> 0.7)
unicorn-rails (2.2.1)
@@ -687,7 +687,7 @@ GEM
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (>= 3.0, < 4.0)
webmock (3.8.3)
webmock (3.9.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)

View File

@@ -1,4 +1,4 @@
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser) ->
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, $location, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser, ProductFiltersUrl) ->
$scope.StatusMessage = StatusMessage
$scope.columns = Columns.columns
@@ -13,37 +13,29 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.filterableColumns = [
{ name: t("label_producers"), db_column: "producer_name" },
{ name: t("name"), db_column: "name" }
]
$scope.filterTypes = [
{ name: t("equals"), predicate: "eq" },
{ name: t("contains"), predicate: "cont" }
]
$scope.optionTabs =
filters: { title: t("filter_products"), visible: false }
$scope.q = {
producerFilter: ""
categoryFilter: ""
importDateFilter: ""
query: ""
sorting: ""
}
$scope.producers = producers
$scope.taxons = Taxons.all
$scope.tax_categories = tax_categories
$scope.producerFilter = ""
$scope.categoryFilter = ""
$scope.importDateFilter = ""
$scope.page = 1
$scope.per_page = 15
$scope.products = BulkProducts.products
$scope.query = ""
$scope.DisplayProperties = DisplayProperties
$scope.sortOptions = SortOptions
$scope.initialise = ->
$scope.q = ProductFiltersUrl.loadFromUrl($location.search())
$scope.fetchProducts()
$scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter, per_page]', ->
$scope.$watchCollection '[q.query, q.producerFilter, q.categoryFilter, q.importDateFilter, per_page]', ->
$scope.page = 1 # Reset page when changing filters for new search
$scope.changePage = (newPage) ->
@@ -53,25 +45,27 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.fetchProducts = ->
removeClearedValues()
params = {
'q[name_cont]': $scope.query,
'q[supplier_id_eq]': $scope.producerFilter,
'q[primary_taxon_id_eq]': $scope.categoryFilter,
'q[s]': $scope.sorting,
import_date: $scope.importDateFilter,
'q[name_cont]': $scope.q.query,
'q[supplier_id_eq]': $scope.q.producerFilter,
'q[primary_taxon_id_eq]': $scope.q.categoryFilter,
'q[s]': $scope.q.sorting,
import_date: $scope.q.importDateFilter,
page: $scope.page,
per_page: $scope.per_page
}
RequestMonitor.load(BulkProducts.fetch(params).$promise).then ->
# update url with the filters used
$location.search(ProductFiltersUrl.generate($scope.q))
$scope.resetProducts()
removeClearedValues = ->
delete $scope.producerFilter if $scope.producerFilter == "0"
delete $scope.categoryFilter if $scope.categoryFilter == "0"
delete $scope.importDateFilter if $scope.importDateFilter == "0"
delete $scope.q.producerFilter if $scope.q.producerFilter == "0"
delete $scope.q.categoryFilter if $scope.q.categoryFilter == "0"
delete $scope.q.importDateFilter if $scope.q.importDateFilter == "0"
$timeout ->
if $scope.showLatestImport
$scope.importDateFilter = $scope.importDates[1].id
$scope.q.importDateFilter = $scope.importDates[1].id
$scope.resetProducts = ->
DirtyProducts.clear()
@@ -101,10 +95,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.visibleTab = tab
$scope.resetSelectFilters = ->
$scope.query = ""
$scope.producerFilter = "0"
$scope.categoryFilter = "0"
$scope.importDateFilter = "0"
$scope.q.query = ""
$scope.q.producerFilter = "0"
$scope.q.categoryFilter = "0"
$scope.q.importDateFilter = "0"
$scope.fetchProducts()
$scope.$watch 'sortOptions', (sort) ->
@@ -122,8 +116,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.editWarn = (product, variant) ->
if confirm_unsaved_changes()
window.open(editProductUrl(product, variant), "_blank")
$window.location.href = ProductFiltersUrl.buildUrl(editProductUrl(product, variant), $scope.q)
$scope.toggleShowAllVariants = ->
showVariants = !DisplayProperties.showVariants 0
@@ -220,10 +213,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
data:
products: productsToSubmit
filters:
'q[name_cont]': $scope.query
'q[supplier_id_eq]': $scope.producerFilter
'q[primary_taxon_id_eq]': $scope.categoryFilter
import_date: $scope.importDateFilter
'q[name_cont]': $scope.q.query
'q[supplier_id_eq]': $scope.q.producerFilter
'q[primary_taxon_id_eq]': $scope.q.categoryFilter
import_date: $scope.q.importDateFilter
page: $scope.page
per_page: $scope.per_page
).success((data) ->

View File

@@ -5,5 +5,10 @@ angular.module("ofn.admin").directive "ofnSelect2MinSearch", ->
minimumResultsForSearch: attrs.ofnSelect2MinSearch
ngModel.$formatters.push (value) ->
if (value)
element.select2('val', value);
# select2 populates options with a value like "number:3" or "string:category" but
# select2('val', value) doesn't do the type conversion for us as one would expect
if isNaN(value)
element.select2('val', "string:#{value}")
else
element.select2('val', "number:#{value}")

View File

@@ -69,25 +69,24 @@ angular.module("admin.enterprises")
$scope.newUser = $scope.invite_errors = $scope.invite_success = null
$scope.removeLogo = ->
return unless confirm(t("admin.enterprises.remove_logo.immediate_removal_warning"))
Enterprises.removeLogo($scope.Enterprise).then (data) ->
$scope.Enterprise = angular.copy(data)
$scope.$emit("enterprise:updated", $scope.Enterprise)
StatusMessage.display("success", t("admin.enterprises.remove_logo.removed_successfully"))
, (response) ->
if response.data.error?
StatusMessage.display("failure", response.data.error)
$scope.performEnterpriseAction("removeLogo", "immediate_logo_removal_warning", "removed_logo_successfully")
$scope.removePromoImage = ->
return unless confirm(t("admin.enterprises.remove_promo_image.immediate_removal_warning"))
$scope.performEnterpriseAction("removePromoImage", "immediate_promo_image_removal_warning", "removed_promo_image_successfully")
Enterprises.removePromoImage($scope.Enterprise).then (data) ->
$scope.removeTermsAndConditions = ->
$scope.performEnterpriseAction("removeTermsAndConditions", "immediate_terms_and_conditions_removal_warning", "removed_terms_and_conditions_successfully")
$scope.performEnterpriseAction = (enterpriseActionName, warning_message_key, success_message_key) ->
return unless confirm($scope.translation(warning_message_key))
Enterprises[enterpriseActionName]($scope.Enterprise).then (data) ->
$scope.Enterprise = angular.copy(data)
$scope.$emit("enterprise:updated", $scope.Enterprise)
StatusMessage.display("success", t("admin.enterprises.remove_promo_image.removed_successfully"))
StatusMessage.display("success", $scope.translation(success_message_key))
, (response) ->
if response.data.error?
StatusMessage.display("failure", response.data.error)
$scope.translation = (key) ->
t('js.admin.enterprises.form.images.' + key)

View File

@@ -9,22 +9,19 @@ angular.module('admin.orderCycles', ['ngTagsInput', 'admin.indexUtils', 'admin.e
$timeout ->
# using $parse instead of scope[attrs.datetimepicker] for cases
# where attrs.datetimepicker is 'foo.bar.lol'
$(element).datetimepicker
dateFormat: 'yy-mm-dd'
timeFormat: 'HH:mm'
showOn: 'button'
controlType: 'select'
oneLine: true
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
buttonImageOnly: true
stepMinute: 15
onSelect: (dateText, inst) ->
scope.$apply(->
element.val(dateText)
parsed = $parse(attrs.datetimepicker)
parsed.assign(scope, dateText)
)
$(element).datetimepicker(
Object.assign(
window.JQUERY_UI_DATETIME_PICKER_DEFAULTS,
{
onSelect: (dateText, inst) ->
scope.$apply(->
element.val(dateText)
parsed = $parse(attrs.datetimepicker)
parsed.assign(scope, dateText)
)
}
)
)
.directive 'ofnOnChange', ->
(scope, element, attrs) ->

View File

@@ -53,17 +53,18 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
[value, unit_name]
scale_for_unit_value: ->
# Find the largest available unit where unit_value comes to >= 1 when expressed in it.
# If there is none available where this is true, use the smallest available unit.
unit = ([scale, unit_name] for scale, unit_name of VariantUnitManager.unitNames[@variant.product.variant_unit] when @variant.unit_value / scale >= 1).reduce (unit, [scale, unit_name]) ->
if (unit && scale > unit[0]) || !unit?
[scale, unit_name]
else
unit
, null
if !unit?
unit = ([scale, unit_name] for scale, unit_name of VariantUnitManager.unitNames[@variant.product.variant_unit]).reduce (unit, [scale, unit_name]) ->
if scale < unit[0] then [scale, unit_name] else unit
, [Infinity,""]
# Find the largest available and compatible unit where unit_value comes
# to >= 1 when expressed in it.
# If there is none available where this is true, use the smallest
# available unit.
product = @variant.product
scales = VariantUnitManager.compatibleUnitScales(product.variant_unit_scale, product.variant_unit)
variantUnitValue = @variant.unit_value
unit
# sets largestScale = last element in filtered scales array
[_, ..., largestScale] = (scales.filter (s) -> variantUnitValue / s >= 1)
if (largestScale)
[largestScale, VariantUnitManager.getUnitName(largestScale, product.variant_unit)]
else
[scales[0], VariantUnitManager.getUnitName(scales[0], product.variant_unit)]

View File

@@ -1,17 +1,35 @@
angular.module("admin.products").factory "VariantUnitManager", ->
class VariantUnitManager
@unitNames:
@units:
'weight':
1.0: 'g'
1000.0: 'kg'
1000000.0: 'T'
1.0:
name: 'g'
system: 'metric'
1000.0:
name: 'kg'
system: 'metric'
1000000.0:
name: 'T'
system: 'metric'
453.6:
name: 'lb'
system: 'imperial'
28.35:
name: 'oz'
system: 'imperial'
'volume':
0.001: 'mL'
1.0: 'L'
1000.0: 'kL'
0.001:
name: 'mL'
system: 'metric'
1.0:
name: 'L'
system: 'metric'
1000.0:
name: 'kL'
system: 'metric'
@variantUnitOptions: ->
options = for unit_type, scale_with_name of @unitNames
options = for unit_type, _ of @units
for scale in @unitScales(unit_type)
name = @getUnitName(scale, unit_type)
["#{I18n.t(unit_type)} (#{name})", "#{unit_type}_#{scale}"]
@@ -30,7 +48,16 @@ angular.module("admin.products").factory "VariantUnitManager", ->
unitScales[0]
@getUnitName: (scale, unitType) ->
@unitNames[unitType][scale]
if @units[unitType][scale]
@units[unitType][scale]['name']
else
''
@unitScales: (unitType) ->
(parseFloat(scale) for scale in Object.keys(@unitNames[unitType])).sort()
(parseFloat(scale) for scale in Object.keys(@units[unitType])).sort (a, b) ->
a - b
@compatibleUnitScales: (scale, unitType) ->
scaleSystem = @units[unitType][scale]['system']
(parseFloat(scale) for scale, scaleInfo of @units[unitType] when scaleInfo['system'] == scaleSystem).sort (a, b) ->
a - b

View File

@@ -14,4 +14,7 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
'removePromoImage':
url: '/api/enterprises/:id/promo_image.json'
method: 'DELETE'
'removeTermsAndConditions':
url: '/api/enterprises/:id/terms_and_conditions.json'
method: 'DELETE'
})

View File

@@ -52,3 +52,4 @@ angular.module("admin.resources").factory 'Enterprises', ($q, EnterpriseResource
removeLogo: performActionOnEnterpriseResource(EnterpriseResource.removeLogo)
removePromoImage: performActionOnEnterpriseResource(EnterpriseResource.removePromoImage)
removeTermsAndConditions: performActionOnEnterpriseResource(EnterpriseResource.removeTermsAndConditions)

View File

@@ -0,0 +1,23 @@
angular.module("ofn.admin").factory "ProductFiltersUrl", ($httpParamSerializer) ->
new class ProductFiltersUrl
productFilters: ['producerFilter', 'categoryFilter', 'query', 'sorting', 'importDateFilter']
loadFromUrl: (filters) ->
loadedFilters = {}
for filter in @productFilters
loadedFilters[filter] = if filters[filter] then filters[filter] else ""
loadedFilters
generate: (ctrlFilters) ->
filters = {}
for filter in @productFilters
filters[filter] = ctrlFilters[filter] if ctrlFilters[filter]
filters
buildUrl: (baseUrl, ctrlFilters) ->
filterUrl = $httpParamSerializer(@generate(ctrlFilters))
filterUrl = "?#{filterUrl}" if filterUrl isnt ""
"#{baseUrl}#{filterUrl}"

View File

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

View File

@@ -0,0 +1,17 @@
$(document).ready(function() {
var order_use_billing_input = $('input#order_use_billing');
var order_use_billing = function () {
if (!order_use_billing_input.is(':checked')) {
$('#shipping').show();
} else {
$('#shipping').hide();
}
};
order_use_billing_input.click(function() {
order_use_billing();
});
order_use_billing();
});

View File

@@ -1,22 +1,30 @@
$(document).ready(function() {
$('.datetimepicker').datetimepicker({
dateFormat: 'yy-mm-dd',
timeFormat: 'HH:mm',
$(document).ready(function(){
window.JQUERY_UI_DATE_PICKER_DEFAULTS = {
dateFormat: Spree.translations.date_picker,
dayNames: Spree.translations.abbr_day_names,
dayNamesMin: Spree.translations.abbr_day_names,
monthNames: Spree.translations.month_names,
prevText: Spree.translations.previous,
nextText: Spree.translations.next,
showOn: 'button',
controlType: 'select',
oneLine: true,
showOn: 'button',
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>",
buttonImageOnly: true,
stepMinute: 15
});
});
buttonImageOnly: true
}
$(document).ready(function(){
window.JQUERY_UI_DATETIME_PICKER_DEFAULTS = Object.assign(
{},
window.JQUERY_UI_DATE_PICKER_DEFAULTS,
{
currentText: Spree.translations.datetime_ui_current_text,
closeText: Spree.translations.datetime_ui_close_text,
timeText: Spree.translations.datetime_ui_time_text,
timeFormat: 'HH:mm',
controlType: 'select',
stepMinute: 15
}
);
$('.datetimepicker').datetimepicker(window.JQUERY_UI_DATETIME_PICKER_DEFAULTS);
$('a.close').click(function(event){
event.preventDefault();
$(this).parent().slideUp(250);

View File

@@ -1,7 +1,9 @@
@import "variables";
@import "../variables";
span.unavailable, span.available {
span.unavailable,
span.available {
font-weight: bold;
i {
font-size: 150%;
}
@@ -13,4 +15,4 @@ span.available {
span.unavailable {
color: $warning-red;
}
}

View File

@@ -132,3 +132,25 @@ dl {
padding: 40px 0px;
color: lighten($color-body-text, 15);
}
.text-normal {
font-size: 1rem;
font-weight: 300;
}
.text-big {
font-size: 1.2rem;
font-weight: 300;
}
.text-red {
color: $warning-red;
}
input.text-big {
font-size: 1.1rem;
}
.pad-top {
padding-top: 1em;
}

View File

@@ -1,20 +0,0 @@
@import "variables";
.text-normal {
font-size: 1.0rem;
font-weight: 300;
}
.text-big {
font-size: 1.2rem;
font-weight: 300;
}
.text-red {
color: $warning-red;
}
input.text-big {
font-size: 1.1rem;
}

View File

@@ -212,7 +212,7 @@ nav.top-bar {
}
.off-canvas-wrap {
overflow: inherit;
overflow: initial;
}
.off-canvas-list li.language-switcher ul li {

View File

@@ -33,12 +33,6 @@ module Api
use_renderers :json
check_authorization
# Temporary measure to help debugging strong_parameters
rescue_from ActiveModel::ForbiddenAttributesError, with: :print_params
def print_params
raise ActiveModel::ForbiddenAttributesError, params.to_s
end
def set_jsonp_format
return unless params[:callback] && request.get?

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
module Api
class StatesController < Api::BaseController
respond_to :json
skip_authorization_check
def index
render json: states, each_serializer: Api::StateSerializer, status: :ok
end
def show
@state = scope.find(params[:id])
render json: @state, serializer: Api::StateSerializer, status: :ok
end
private
def scope
if params[:country_id]
@country = Spree::Country.find(params[:country_id])
@country.states
else
Spree::State.all
end
end
def states
states = scope.ransack(params[:q]).result.
includes(:country).order('name ASC')
if pagination?
states = states.page(params[:page]).per(params[:per_page])
end
states
end
def pagination?
params[:page] || params[:per_page]
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Api
class TermsAndConditionsController < Api::EnterpriseAttachmentController
private
def attachment_name
:terms_and_conditions
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_terms_and_conditions
end
end
end
end

View File

@@ -10,12 +10,6 @@ class ApplicationController < ActionController::Base
include EnterprisesHelper
include Spree::AuthenticationHelpers
# Temporary measure to help debugging strong_parameters
rescue_from ActiveModel::ForbiddenAttributesError, with: :print_params
def print_params
raise ActiveModel::ForbiddenAttributesError, params.to_s
end
def redirect_to(options = {}, response_status = {})
::Rails.logger.error("Redirected by #{begin
caller(1).first

View File

@@ -8,9 +8,55 @@ module Spree
before_action :load_data
create.before :set_viewable
update.before :set_viewable
destroy.before :destroy_before
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def new
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
render layout: !request.xhr?
end
def create
@url_filters = ::ProductFilters.new.extract(params)
set_viewable
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
redirect_to admin_product_images_url(params[:product_id], @url_filters)
else
respond_with(@object)
end
end
def edit
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def update
@url_filters = ::ProductFilters.new.extract(params)
set_viewable
if @object.update(permitted_resource_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
redirect_to admin_product_images_url(params[:product_id], @url_filters)
else
respond_with(@object)
end
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
destroy_before
if @object.destroy
flash[:success] = flash_message_for(@object, :successfully_removed)
end
redirect_to admin_product_images_url(params[:product_id], @url_filters)
end
private

View File

@@ -5,6 +5,20 @@ module Spree
before_action :find_properties
before_action :setup_property, only: [:index]
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
if @object.destroy
flash[:success] = flash_message_for(@object, :successfully_removed)
end
# if destroy fails it won't show any errors to the user
redirect_to admin_product_product_properties_url(params[:product_id], @url_filters)
end
private
def find_properties

View File

@@ -10,35 +10,32 @@ module Spree
include OrderCyclesHelper
include EnterprisesHelper
create.before :create_before
update.before :update_before
before_action :load_data
before_action :load_form_data, only: [:index, :new, :create, :edit, :update]
before_action :load_spree_api_key, only: [:index, :variant_overrides]
before_action :strip_new_properties, only: [:create, :update]
respond_override create: { html: {
success: lambda {
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
},
failure: lambda {
render :new
}
} }
def new
@object.shipping_category = DefaultShippingCategory.find_or_create
super
end
def create
delete_stock_params_and_set_after do
super
if params[:product][:prototype_id].present?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
else
render :new
end
end
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
invoke_callbacks(:create, :fails)
@@ -56,14 +53,24 @@ module Spree
@show_latest_import = params[:latest_import] || false
end
def update
original_supplier_id = @product.supplier_id
def edit
@url_filters = ::ProductFilters.new.extract(params)
end
def update
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
original_supplier_id = @product.supplier_id
delete_stock_params_and_set_after do
super
if original_supplier_id != @product.supplier_id
ExchangeVariantDeleter.new.delete(@product)
params[:product] ||= {} if params[:clear_product_properties]
if @object.update(permitted_resource_params)
if original_supplier_id != @product.supplier_id
ExchangeVariantDeleter.new.delete(@product)
end
flash[:success] = flash_message_for(@object, :successfully_updated)
end
redirect_to edit_admin_product_url(@object, @url_filters)
end
end
@@ -93,6 +100,14 @@ module Spree
redirect_to edit_admin_product_url(@new)
end
def group_buy_options
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def seo
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
protected
def find_resource
@@ -135,19 +150,6 @@ module Spree
@collection
end
def create_before
return if params[:product][:prototype_id].blank?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
def update_before
# We only reset the product properties if we're receiving a post from the form on that tab
return unless params[:clear_product_properties]
params[:product] ||= {}
end
def product_includes
[{ variants: [:images, { option_values: :option_type }] },
{ master: [:images, :default_price] }]

View File

@@ -4,14 +4,46 @@ module Spree
module Admin
class VariantsController < ResourceController
helper 'spree/products'
belongs_to 'spree/product', find_by: :permalink
new_action.before :new_before
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def edit
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def update
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
if @object.update(permitted_resource_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
else
redirect_to edit_admin_product_variant_url(params[:product_id], @object, @url_filters)
end
end
def new
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def create
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
on_demand = params[:variant].delete(:on_demand)
on_hand = params[:variant].delete(:on_hand)
super
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
else
redirect_to new_admin_product_variant_url(params[:product_id], @url_filters)
end
return unless @object.present? && @object.valid?
@@ -26,6 +58,8 @@ module Spree
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
@variant = Spree::Variant.find(params[:id])
flash[:success] = if VariantDeleter.new.delete(@variant)
Spree.t('notice_messages.variant_deleted')
@@ -33,9 +67,7 @@ module Spree
Spree.t('notice_messages.variant_not_deleted')
end
respond_with(@variant) do |format|
format.html { redirect_to admin_product_variants_url(params[:product_id]) }
end
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
end
protected

View File

@@ -1,12 +1,7 @@
# frozen_string_literal: true
module Spree
module BaseHelper
# human readable list of variant options
# Override: Do not show out of stock text
def variant_options(variant, _options = {})
variant.options_text
end
# Overriden to eager-load :states
def available_countries
checkout_zone = Zone.find_by(name: Spree::Config[:checkout_zone])
@@ -21,5 +16,10 @@ module Spree
country
end.sort { |a, b| a.name <=> b.name }
end
def pretty_time(time)
[I18n.l(time.to_date, format: :long),
time.strftime("%l:%M %p")].join(" ")
end
end
end

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'active_support/concern'
# This concern is used to duplicate the associations distributors and distributor_ids
# across payment method and gateway
# this fixes the inheritance problem https://github.com/openfoodfoundation/openfoodnetwork/issues/2781
module PaymentMethodDistributors
extend ActiveSupport::Concern
def self.included(base)
base.class_eval do
has_and_belongs_to_many :distributors, join_table: 'distributors_payment_methods',
class_name: 'Enterprise',
foreign_key: 'payment_method_id',
association_foreign_key: 'distributor_id'
end
end
end

View File

@@ -12,10 +12,7 @@ class Enterprise < ActiveRecord::Base
preference :shopfront_order_cycle_order, :string, default: "orders_close_at"
preference :show_customer_names_to_suppliers, :boolean, default: false
# This is hopefully a temporary measure, pending the arrival of multiple named inventories
# for shops. We need this here to allow hubs to restrict visible variants to only those in
# their inventory if they so choose
# TODO: delegate this to a separate model instead of abusing Preferences.
# Allow hubs to restrict visible variants to only those in their inventory
preference :product_selection_from_inventory_only, :boolean, default: false
has_paper_trail only: [:owner_id, :sells], on: [:update]
@@ -78,10 +75,16 @@ class Enterprise < ActiveRecord::Base
},
url: '/images/enterprises/promo_images/:id/:style/:basename.:extension',
path: 'public/images/enterprises/promo_images/:id/:style/:basename.:extension'
validates_attachment_content_type :logo, content_type: %r{\Aimage/.*\Z}
validates_attachment_content_type :promo_image, content_type: %r{\Aimage/.*\Z}
has_attached_file :terms_and_conditions,
url: '/files/enterprises/terms_and_conditions/:id/:basename.:extension',
path: 'public/files/enterprises/terms_and_conditions/:id/:basename.:extension'
validates_attachment_content_type :terms_and_conditions,
content_type: "application/pdf",
message: I18n.t(:enterprise_terms_and_conditions_type_error)
include Spree::Core::S3Support
supports_s3 :logo
supports_s3 :promo_image

View File

@@ -159,7 +159,7 @@ module ProductImport
end
def unit_fields_validation(entry)
unit_types = ['g', 'kg', 't', 'ml', 'l', 'kl', '']
unit_types = ['g', 'oz', 'lb', 'kg', 't', 'ml', 'l', 'kl', '']
unless entry.units&.present?
mark_as_invalid(entry, attribute: 'units',

View File

@@ -32,6 +32,8 @@ module ProductImport
{
'g' => { scale: 1, unit: 'weight' },
'kg' => { scale: 1000, unit: 'weight' },
'oz' => { scale: 28.35, unit: 'weight' },
'lb' => { scale: 453.6, unit: 'weight' },
't' => { scale: 1_000_000, unit: 'weight' },
'ml' => { scale: 0.001, unit: 'volume' },
'l' => { scale: 1, unit: 'volume' },

View File

@@ -97,7 +97,9 @@ class AbilityDecorator
end
can [:admin, :index, :create], Enterprise
can [:read, :edit, :update, :remove_logo, :remove_promo_image, :bulk_update, :resend_confirmation], Enterprise do |enterprise|
can [:read, :edit, :update,
:remove_logo, :remove_promo_image, :remove_terms_and_conditions,
:bulk_update, :resend_confirmation], Enterprise do |enterprise|
OpenFoodNetwork::Permissions.new(user).editable_enterprises.include? enterprise
end
can [:welcome, :register], Enterprise do |enterprise|

162
app/models/spree/address.rb Normal file
View File

@@ -0,0 +1,162 @@
# frozen_string_literal: true
module Spree
class Address < ActiveRecord::Base
include AddressDisplay
belongs_to :country, class_name: "Spree::Country"
belongs_to :state, class_name: "Spree::State"
has_one :enterprise, dependent: :restrict_with_exception
has_many :shipments
validates :firstname, :lastname, :address1, :city, :country, presence: true
validates :zipcode, presence: true, if: :require_zipcode?
validates :phone, presence: true, if: :require_phone?
validate :state_validate
after_save :touch_enterprise
alias_attribute :first_name, :firstname
alias_attribute :last_name, :lastname
delegate :name, to: :state, prefix: true, allow_nil: true
geocoded_by :geocode_address
def self.default
country = begin
Spree::Country.find(Spree::Config[:default_country_id])
rescue StandardError
Spree::Country.first
end
new(country: country)
end
def full_name
"#{firstname} #{lastname}".strip
end
def state_text
state.try(:abbr) || state.try(:name) || state_name
end
def same_as?(other)
return false if other.nil?
attributes.except('id', 'updated_at', 'created_at') ==
other.attributes.except('id', 'updated_at', 'created_at')
end
alias same_as same_as?
def to_s
"#{full_name}: #{address1}"
end
def clone
self.class.new(attributes.except('id', 'updated_at', 'created_at'))
end
def ==(other)
self_attrs = attributes
other_attrs = other.respond_to?(:attributes) ? other.attributes : {}
[self_attrs, other_attrs].each { |attrs|
attrs.except!('id', 'created_at', 'updated_at', 'order_id')
}
self_attrs == other_attrs
end
def empty?
attributes.except('id', 'created_at', 'updated_at', 'order_id', 'country_id').all? { |_, v|
v.nil?
}
end
# Generates an ActiveMerchant compatible address hash
def active_merchant_hash
{
name: full_name,
address1: address1,
address2: address2,
city: city,
state: state_text,
zip: zipcode,
country: country.try(:iso),
phone: phone
}
end
def geocode_address
render_address([address1, address2, zipcode, city, country.andand.name, state.andand.name])
end
def full_address
render_address([address1, address2, city, zipcode, state.andand.name])
end
def address_part1
render_address([address1, address2])
end
def address_part2
render_address([city, zipcode, state.andand.name])
end
private
def require_phone?
true
end
def require_zipcode?
true
end
def state_validate
# Skip state validation without country (also required)
# or when disabled by preference
return if country.blank? || !Spree::Config[:address_requires_state]
return unless country.states_required
# Ensure associated state belongs to country
if state.present?
if state.country == country
self.state_name = nil # not required as we have a valid state and country combo
elsif state_name.present?
self.state = nil
else
errors.add(:state, :invalid)
end
end
# Ensure state_name belongs to country without states,
# or that it matches a predefined state name/abbr
if state_name.present?
if country.states.present?
states = country.states.find_all_by_name_or_abbr(state_name)
if states.size == 1
self.state = states.first
self.state_name = nil
else
errors.add(:state, :invalid)
end
end
end
# ensure at least one state field is populated
errors.add :state, :blank if state.blank? && state_name.blank?
end
def touch_enterprise
enterprise.andand.touch
end
def render_address(parts)
parts.select(&:present?).join(', ')
end
end
end

View File

@@ -1,38 +0,0 @@
Spree::Address.class_eval do
include AddressDisplay
has_one :enterprise, dependent: :restrict_with_exception
belongs_to :country, class_name: "Spree::Country"
after_save :touch_enterprise
geocoded_by :geocode_address
delegate :name, to: :state, prefix: true, allow_nil: true
def geocode_address
render_address([address1, address2, zipcode, city, country.andand.name, state.andand.name])
end
def full_address
render_address([address1, address2, city, zipcode, state.andand.name])
end
def address_part1
render_address([address1, address2])
end
def address_part2
render_address([city, zipcode, state.andand.name])
end
private
def touch_enterprise
enterprise.andand.touch
end
def render_address(parts)
parts.select(&:present?).join(', ')
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
module Spree
class Classification < ActiveRecord::Base
self.table_name = 'spree_products_taxons'
belongs_to :product, class_name: "Spree::Product", touch: true
belongs_to :taxon, class_name: "Spree::Taxon", touch: true
before_destroy :dont_destroy_if_primary_taxon
private
def dont_destroy_if_primary_taxon
return unless product.primary_taxon == taxon
errors.add :base, I18n.t(:spree_classification_primary_taxon_error, taxon: taxon.name,
product: product.name)
false
end
end
end

View File

@@ -1,15 +0,0 @@
Spree::Classification.class_eval do
belongs_to :product, class_name: "Spree::Product", touch: true
belongs_to :taxon, class_name: "Spree::Taxon", touch: true
before_destroy :dont_destroy_if_primary_taxon
private
def dont_destroy_if_primary_taxon
if product.primary_taxon == taxon
errors.add :base, I18n.t(:spree_classification_primary_taxon_error, taxon: taxon.name, product: product.name)
false
end
end
end

View File

@@ -1,16 +0,0 @@
require 'active_support/concern'
# This concern is used to duplicate the associations distributors and distributor_ids
# across payment method and gateway
# this fixes the inheritance problem https://github.com/openfoodfoundation/openfoodnetwork/issues/2781
module Spree
module PaymentMethodDistributors
extend ActiveSupport::Concern
def self.included(base)
base.class_eval do
has_and_belongs_to_many :distributors, join_table: 'distributors_payment_methods', class_name: 'Enterprise', foreign_key: 'payment_method_id', association_foreign_key: 'distributor_id'
end
end
end
end

View File

@@ -1,10 +1,8 @@
# frozen_string_literal: true
require 'spree/concerns/payment_method_distributors'
module Spree
class Gateway < PaymentMethod
include Spree::PaymentMethodDistributors
include PaymentMethodDistributors
delegate_belongs_to :provider, :authorize, :purchase, :capture, :void, :credit

View File

@@ -1,8 +1,8 @@
require 'open_food_network/scope_variant_to_hub'
require 'open_food_network/variant_and_line_item_naming'
require 'variant_units/variant_and_line_item_naming'
Spree::LineItem.class_eval do
include OpenFoodNetwork::VariantAndLineItemNaming
include VariantUnits::VariantAndLineItemNaming
include LineItemBasedAdjustmentHandling
has_and_belongs_to_many :option_values, join_table: 'spree_option_values_line_items', class_name: 'Spree::OptionValue'

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Spree
class LogEntry < ActiveRecord::Base
belongs_to :source, polymorphic: true
# Fix for Spree #1767
# If a payment fails, we want to make sure we keep the record of it failing
after_rollback :save_anyway
def save_anyway
log = Spree::LogEntry.new
log.source = source
log.details = details
log.save!
end
end
end

View File

@@ -194,8 +194,6 @@ Spree::Order.class_eval do
end
# After changing line items of a completed order
# TODO: perhaps this should be triggered from a controller
# rather than an after_save callback?
def update_shipping_fees!
shipments.each do |shipment|
next if shipment.shipped?
@@ -222,8 +220,6 @@ Spree::Order.class_eval do
end
# After changing line items of a completed order
# TODO: perhaps this should be triggered from a controller
# rather than an after_save callback?
def update_payment_fees!
payments.each do |payment|
next if payment.completed?

View File

@@ -1,11 +1,9 @@
# frozen_string_literal: true
require 'spree/concerns/payment_method_distributors'
module Spree
class PaymentMethod < ActiveRecord::Base
include Spree::Core::CalculatedAdjustments
include Spree::PaymentMethodDistributors
include PaymentMethodDistributors
acts_as_taggable
acts_as_paranoid

8
app/models/spree/role.rb Normal file
View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Spree
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, join_table: 'spree_roles_users',
class_name: Spree.user_class.to_s
end
end

View File

@@ -90,7 +90,7 @@ module Spree
end
def selected_shipping_rate
shipping_rates.where(selected: true).first
shipping_rates.find_by(selected: true)
end
def selected_shipping_rate_id
@@ -294,7 +294,7 @@ module Spree
record = true
while record
random = "H#{Array.new(11) { rand(9) }.join}"
record = self.class.where(number: random).first
record = self.class.find_by(number: random)
end
self.number = random
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
module Spree
class ShippingCategory < ActiveRecord::Base
validates :name, presence: true
has_many :products
has_many :shipping_method_categories
has_many :shipping_methods, through: :shipping_method_categories
end
end

View File

@@ -0,0 +1,132 @@
# frozen_string_literal: true
module Spree
class ShippingMethod < ActiveRecord::Base
include Spree::Core::CalculatedAdjustments
DISPLAY = [:both, :front_end, :back_end].freeze
acts_as_taggable
default_scope -> { where(deleted_at: nil) }
has_many :shipments
has_many :shipping_method_categories
has_many :shipping_categories, through: :shipping_method_categories
has_many :shipping_rates
has_many :distributor_shipping_methods
has_many :distributors, through: :distributor_shipping_methods,
class_name: 'Enterprise',
foreign_key: 'distributor_id'
has_and_belongs_to_many :zones, join_table: 'spree_shipping_methods_zones',
class_name: 'Spree::Zone',
foreign_key: 'shipping_method_id'
validates :name, presence: true
validate :distributor_validation
validate :at_least_one_shipping_category
after_save :touch_distributors
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
where(nil)
else
joins(:distributors).
where('distributors_shipping_methods.distributor_id IN (?)',
user.enterprises.select(&:id)).
select('DISTINCT spree_shipping_methods.*')
end
}
scope :for_distributors, ->(distributors) {
non_unique_matches = unscoped.joins(:distributors).where(enterprises: { id: distributors })
where(id: non_unique_matches.map(&:id))
}
scope :for_distributor, lambda { |distributor|
joins(:distributors).
where('enterprises.id = ?', distributor)
}
scope :by_name, -> { order('spree_shipping_methods.name ASC') }
scope :display_on_checkout, -> {
where("spree_shipping_methods.display_on is null OR spree_shipping_methods.display_on = ''")
}
def adjustment_label
I18n.t('shipping')
end
# Here we allow checkout with shipping methods without zones (see issue #3928 for details)
# and also checkout with addresses outside of the zones of the selected shipping method
# This method could be used, like in Spree, to validate shipping method zones on checkout.
def include?(address)
address.present?
end
def build_tracking_url(tracking)
tracking_url.gsub(/:tracking/, tracking) unless tracking.blank? || tracking_url.blank?
end
def self.calculators
spree_calculators.__send__ model_name_without_spree_namespace
end
# Some shipping methods are only meant to be set via backend
def frontend?
display_on != "back_end"
end
def has_distributor?(distributor)
distributors.include?(distributor)
end
# Checks whether the shipping method is of delivery type, meaning that it
# requires the user to specify a ship address at checkout.
#
# @return [Boolean]
def delivery?
require_ship_address
end
# Return the services (pickup, delivery) that different distributors provide, in the format:
# {distributor_id => {pickup: true, delivery: false}, ...}
def self.services
Hash[
Spree::ShippingMethod.
joins(:distributor_shipping_methods).
group('distributor_id').
select("distributor_id").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery").
map { |sm| [sm.distributor_id.to_i, { pickup: sm.pickup, delivery: sm.delivery }] }
]
end
def self.on_backend_query
"#{table_name}.display_on != 'front_end' OR #{table_name}.display_on IS NULL"
end
def self.on_frontend_query
"#{table_name}.display_on != 'back_end' OR #{table_name}.display_on IS NULL"
end
private
def at_least_one_shipping_category
return unless shipping_categories.empty?
errors[:base] << "You need to select at least one shipping category"
end
def touch_distributors
distributors.each do |distributor|
distributor.touch if distributor.persisted?
end
end
def distributor_validation
validates_with DistributorsValidator
end
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Spree
class ShippingMethodCategory < ActiveRecord::Base
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
end
end

View File

@@ -1,90 +0,0 @@
Spree::ShippingMethod.class_eval do
acts_as_taggable
has_many :distributor_shipping_methods
has_many :distributors, through: :distributor_shipping_methods, class_name: 'Enterprise', foreign_key: 'distributor_id'
after_save :touch_distributors
validate :distributor_validation
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
where(nil)
else
joins(:distributors).
where('distributors_shipping_methods.distributor_id IN (?)', user.enterprises.select(&:id)).
select('DISTINCT spree_shipping_methods.*')
end
}
scope :for_distributors, ->(distributors) {
non_unique_matches = unscoped.joins(:distributors).where(enterprises: { id: distributors })
where(id: non_unique_matches.map(&:id))
}
scope :for_distributor, lambda { |distributor|
joins(:distributors).
where('enterprises.id = ?', distributor)
}
scope :by_name, -> { order('spree_shipping_methods.name ASC') }
scope :display_on_checkout, -> { where("spree_shipping_methods.display_on is null OR spree_shipping_methods.display_on = ''") }
# Return the services (pickup, delivery) that different distributors provide, in the format:
# {distributor_id => {pickup: true, delivery: false}, ...}
def self.services
Hash[
Spree::ShippingMethod.
joins(:distributor_shipping_methods).
group('distributor_id').
select("distributor_id").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery").
map { |sm| [sm.distributor_id.to_i, { pickup: sm.pickup, delivery: sm.delivery }] }
]
end
# This method is overriden so that we can remove the restriction added in Spree
# Spree restricts shipping method calculators to the ones that inherit from Spree::Shipping::ShippingCalculator
# Spree::Shipping::ShippingCalculator makes sure that calculators are able to handle packages and not orders as input
# This is not necessary in OFN because calculators in OFN are already customized to work with different types of input
def self.calculators
spree_calculators.send model_name_without_spree_namespace
end
# This is bypassing the validation of shipping method zones on checkout
# It allows checkout using shipping methods without zones (see issue #3928 for details)
# and it allows checkout with addresses outside of the zones of the selected shipping method
def include?(address)
address.present?
end
def has_distributor?(distributor)
distributors.include?(distributor)
end
def adjustment_label
I18n.t('shipping')
end
# Checks whether the shipping method is of delivery type, meaning that it
# requires the user to specify a ship address at checkout. Note this is
# a setting we added onto the +spree_shipping_methods+ table.
#
# @return [Boolean]
def delivery?
require_ship_address
end
private
def touch_distributors
distributors.each do |distributor|
distributor.touch if distributor.persisted?
end
end
def distributor_validation
validates_with DistributorsValidator
end
end

View File

@@ -0,0 +1,38 @@
# frozen_string_literal: true
module Spree
class ShippingRate < ActiveRecord::Base
belongs_to :shipment, class_name: 'Spree::Shipment'
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
scope :frontend,
-> {
includes(:shipping_method).
where(ShippingMethod.on_frontend_query).
references(:shipping_method).
order("cost ASC")
}
scope :backend,
-> {
includes(:shipping_method).
where(ShippingMethod.on_backend_query).
references(:shipping_method).
order("cost ASC")
}
delegate :order, :currency, to: :shipment
delegate :name, to: :shipping_method
def display_price
price = if Spree::Config[:shipment_inc_vat]
(1 + Spree::TaxRate.default) * cost
else
cost
end
Spree::Money.new(price, { currency: currency })
end
alias_method :display_cost, :display_price
end
end

113
app/models/spree/taxon.rb Normal file
View File

@@ -0,0 +1,113 @@
# frozen_string_literal: true
module Spree
class Taxon < ActiveRecord::Base
acts_as_nested_set dependent: :destroy
belongs_to :taxonomy, class_name: 'Spree::Taxonomy', touch: true
has_many :classifications, dependent: :destroy
has_many :products, through: :classifications
before_create :set_permalink
validates :name, presence: true
has_attached_file :icon,
styles: { mini: '32x32>', normal: '128x128>' },
default_style: :mini,
url: '/spree/taxons/:id/:style/:basename.:extension',
path: ':rails_root/public/spree/taxons/:id/:style/:basename.:extension',
default_url: '/assets/default_taxon.png'
include Spree::Core::S3Support
supports_s3 :icon
# Indicate which filters should be used for this taxon
def applicable_filters
[]
end
# Return meta_title if set otherwise generates from root name and/or taxon name
def seo_title
if meta_title
meta_title
else
root? ? name : "#{root.name} - #{name}"
end
end
# Creates permalink based on Stringex's .to_url method
def set_permalink
if parent.present?
self.permalink = [parent.permalink, permalink_end].join('/')
elsif permalink.blank?
self.permalink = name.to_url
end
end
# For #2759
def to_param
permalink
end
def active_products
scope = products.active
scope
end
def pretty_name
ancestor_chain = ancestors.inject("") do |name, ancestor|
name += "#{ancestor.name} -> "
end
ancestor_chain + name.to_s
end
# Find all the taxons of supplied products for each enterprise, indexed by enterprise.
# Format: {enterprise_id => [taxon_id, ...]}
def self.supplied_taxons
taxons = {}
Spree::Taxon.
joins(products: :supplier).
select('spree_taxons.*, enterprises.id AS enterprise_id').
each do |t|
taxons[t.enterprise_id.to_i] ||= Set.new
taxons[t.enterprise_id.to_i] << t.id
end
taxons
end
# Find all the taxons of distributed products for each enterprise, indexed by enterprise.
# May return :all taxons (distributed in open and closed order cycles),
# or :current taxons (distributed in an open order cycle).
#
# Format: {enterprise_id => [taxon_id, ...]}
def self.distributed_taxons(which_taxons = :all)
ents_and_vars = ExchangeVariant.joins(exchange: :order_cycle).merge(Exchange.outgoing)
.select("DISTINCT variant_id, receiver_id AS enterprise_id")
ents_and_vars = ents_and_vars.merge(OrderCycle.active) if which_taxons == :current
taxons = Spree::Taxon
.select("DISTINCT spree_taxons.id, ents_and_vars.enterprise_id")
.joins(products: :variants_including_master)
.joins("
INNER JOIN (#{ents_and_vars.to_sql}) AS ents_and_vars
ON spree_variants.id = ents_and_vars.variant_id")
taxons.each_with_object({}) do |t, ts|
ts[t.enterprise_id.to_i] ||= Set.new
ts[t.enterprise_id.to_i] << t.id
end
end
private
def permalink_end
return name.to_url if permalink.blank?
permalink.split('/').last
end
end
end

View File

@@ -1,47 +0,0 @@
Spree::Taxon.class_eval do
has_many :classifications, dependent: :destroy
# Indicate which filters should be used for this taxon
def applicable_filters
fs = []
# fs << Spree::ProductFilters.distributor_filter if Spree::ProductFilters.respond_to? :distributor_filter
fs
end
# Find all the taxons of supplied products for each enterprise, indexed by enterprise.
# Format: {enterprise_id => [taxon_id, ...]}
def self.supplied_taxons
taxons = {}
Spree::Taxon.
joins(products: :supplier).
select('spree_taxons.*, enterprises.id AS enterprise_id').
each do |t|
taxons[t.enterprise_id.to_i] ||= Set.new
taxons[t.enterprise_id.to_i] << t.id
end
taxons
end
# Find all the taxons of distributed products for each enterprise, indexed by enterprise.
# May return :all taxons (distributed in open and closed order cycles),
# or :current taxons (distributed in an open order cycle).
#
# Format: {enterprise_id => [taxon_id, ...]}
def self.distributed_taxons(which_taxons = :all)
ents_and_vars = ExchangeVariant.joins(exchange: :order_cycle).merge(Exchange.outgoing)
.select("DISTINCT variant_id, receiver_id AS enterprise_id")
ents_and_vars = ents_and_vars.merge(OrderCycle.active) if which_taxons == :current
taxons = Spree::Taxon
.select("DISTINCT spree_taxons.id, ents_and_vars.enterprise_id").joins(products: :variants_including_master)
.joins("INNER JOIN (#{ents_and_vars.to_sql}) AS ents_and_vars ON spree_variants.id = ents_and_vars.variant_id")
taxons.each_with_object({}) do |t, ts|
ts[t.enterprise_id.to_i] ||= Set.new
ts[t.enterprise_id.to_i] << t.id
end
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
module Spree
class Taxonomy < ActiveRecord::Base
validates :name, presence: true
has_many :taxons
has_one :root, -> { where parent_id: nil }, class_name: "Spree::Taxon", dependent: :destroy
after_save :set_name
default_scope -> { order("#{table_name}.position") }
private
def set_name
if root
root.update_column(:name, name)
else
self.root = Taxon.create!(taxonomy_id: id, name: name)
end
end
end
end

View File

@@ -7,6 +7,13 @@ module Spree
belongs_to :ship_address, foreign_key: 'ship_address_id', class_name: 'Spree::Address'
belongs_to :bill_address, foreign_key: 'bill_address_id', class_name: 'Spree::Address'
has_and_belongs_to_many :spree_roles,
join_table: 'spree_roles_users',
foreign_key: "user_id",
class_name: "Spree::Role"
has_many :spree_orders, foreign_key: "user_id", class_name: "Spree::Order"
before_validation :set_login
before_destroy :check_completed_orders
@@ -41,6 +48,12 @@ module Spree
User.admin.count > 0
end
# Whether a user has a role or not.
def has_spree_role?(role_in_question)
spree_roles.where(name: role_in_question.to_s).any?
end
# Checks whether the specified user is a superadmin, with full control of the instance
def admin?
has_spree_role?('admin')
end
@@ -107,14 +120,6 @@ module Spree
end
end
# Checks whether the specified user is a superadmin, with full control of the
# instance
#
# @return [Boolean]
def superadmin?
has_spree_role?('admin')
end
def generate_spree_api_key!
self.spree_api_key = SecureRandom.hex(24)
save!
@@ -125,6 +130,10 @@ module Spree
save!
end
def last_incomplete_spree_order
spree_orders.incomplete.where(created_by_id: id).order('created_at DESC').first
end
protected
def password_required?

View File

@@ -1,5 +1,5 @@
require 'open_food_network/enterprise_fee_calculator'
require 'open_food_network/variant_and_line_item_naming'
require 'variant_units/variant_and_line_item_naming'
require 'concerns/variant_stock'
Spree::Variant.class_eval do
@@ -8,7 +8,7 @@ Spree::Variant.class_eval do
# This file may be double-loaded in delayed job environment, so we check before
# removing the Spree method to prevent error.
remove_method :options_text if instance_methods(false).include? :options_text
include OpenFoodNetwork::VariantAndLineItemNaming
include VariantUnits::VariantAndLineItemNaming
include VariantStock
has_many :exchange_variants

View File

@@ -6,7 +6,7 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
:preferred_product_selection_from_inventory_only,
:preferred_show_customer_names_to_suppliers, :owner, :contact, :users, :tag_groups,
:default_tag_group, :require_login, :allow_guest_orders, :allow_order_changes,
:logo, :promo_image
:logo, :promo_image, :terms_and_conditions, :terms_and_conditions_file_name
has_one :owner, serializer: Api::Admin::UserSerializer
has_many :users, serializer: Api::Admin::UserSerializer
@@ -20,6 +20,12 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
attachment_urls(object.promo_image, [:thumb, :medium, :large])
end
def terms_and_conditions
return unless @object.terms_and_conditions.file?
@object.terms_and_conditions.url
end
def tag_groups
object.tag_rules.prioritised.reject(&:is_default).each_with_object([]) do |tag_rule, tag_groups|
tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.

View File

@@ -1,3 +1,3 @@
class Api::StateSerializer < ActiveModel::Serializer
attributes :id, :name, :abbr
attributes :id, :name, :abbr, :country_id
end

View File

@@ -32,7 +32,7 @@ class OrderTaxAdjustmentsFetcher
end
def line_item_adjustments
table[:adjustable_id].eq(order.line_item_ids.join(','))
table[:adjustable_id].eq_any(order.line_item_ids)
.and(table[:adjustable_type].eq('Spree::LineItem'))
end

View File

@@ -25,7 +25,7 @@ module PermittedAttributes
[
:id, :name, :visible, :permalink, :owner_id, :contact_name, :email_address, :phone,
:is_primary_producer, :sells, :website, :facebook, :instagram, :linkedin, :twitter,
:description, :long_description, :logo, :promo_image,
:description, :long_description, :logo, :promo_image, :terms_and_conditions,
:allow_guest_orders, :allow_order_changes, :require_login, :enable_subscriptions,
:abn, :acn, :charges_sales_tax, :display_invoice_logo, :invoice_text,
:preferred_product_selection_from_inventory_only, :preferred_shopfront_message,

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class ProductFilters
PRODUCT_FILTERS = [
'query', 'producerFilter', 'categoryFilter', 'sorting', 'importDateFilter'
].freeze
def extract(params)
params.select { |key, _value| PRODUCT_FILTERS.include?(key) }
end
end

View File

@@ -2,7 +2,7 @@
require "open_food_network/i18n_inflections"
module OpenFoodNetwork
module VariantUnits
class OptionValueNamer
def initialize(variant = nil)
@variant = variant
@@ -39,7 +39,6 @@ module OpenFoodNetwork
if @variant.unit_value.present?
if %w(weight volume).include? @variant.product.variant_unit
value, unit_name = option_value_value_unit_scaled
else
value = @variant.unit_value
unit_name = pluralize(@variant.product.variant_unit_name, value)
@@ -63,21 +62,44 @@ module OpenFoodNetwork
end
def scale_for_unit_value
units = { 'weight' => { 1.0 => 'g', 1000.0 => 'kg', 1_000_000.0 => 'T' },
'volume' => { 0.001 => 'mL', 1.0 => 'L', 1000.0 => 'kL' } }
units = {
'weight' => {
1.0 => { 'name' => 'g', 'system' => 'metric' },
28.35 => { 'name' => 'oz', 'system' => 'imperial' },
453.6 => { 'name' => 'lb', 'system' => 'imperial' },
1000.0 => { 'name' => 'kg', 'system' => 'metric' },
1_000_000.0 => { 'name' => 'T', 'system' => 'metric' }
},
'volume' => {
0.001 => { 'name' => 'mL', 'system' => 'metric' },
1.0 => { 'name' => 'L', 'system' => 'metric' },
1000.0 => { 'name' => 'kL', 'system' => 'metric' }
}
}
# Find the largest available unit where unit_value comes to >= 1 when expressed in it.
# If there is none available where this is true, use the smallest available unit.
unit = units[@variant.product.variant_unit].select { |scale, _unit_name|
@variant.unit_value / scale >= 1
}.to_a.last
unit = units[@variant.product.variant_unit].first if unit.nil?
scales = units[@variant.product.variant_unit]
product_scale = @variant.product.variant_unit_scale
product_scale_system = scales[product_scale.to_f]['system']
unit
largest_unit = find_largest_unit(scales, product_scale_system)
[largest_unit[0], largest_unit[1]["name"]]
end
# Find the largest available and compatible unit where unit_value comes
# to >= 1 when expressed in it.
# If there is none available where this is true, use the smallest available unit.
def find_largest_unit(scales, product_scale_system)
largest_unit = scales.select { |scale, unit_info|
unit_info['system'] == product_scale_system &&
@variant.unit_value / scale >= 1
}.max
return scales.first if largest_unit.nil?
largest_unit
end
def pluralize(unit_name, count)
I18nInflections.pluralize(unit_name, count)
OpenFoodNetwork::I18nInflections.pluralize(unit_name, count)
end
end
end

View File

@@ -2,9 +2,9 @@
# It contains all of our logic for creating and naming option values (which are associated
# with both models) and methods for printing human readable "names" for instances of these models.
require 'open_food_network/option_value_namer'
require 'variant_units/option_value_namer'
module OpenFoodNetwork
module VariantUnits
module VariantAndLineItemNaming
# Copied and modified from Spree::Variant
def options_text
@@ -18,7 +18,24 @@ module OpenFoodNetwork
order("#{Spree::OptionType.table_name}.position asc")
end
values.map(&:presentation).to_sentence(words_connector: ", ", two_words_connector: ", ")
values.map { |option_value|
presentation(option_value)
}.to_sentence(words_connector: ", ", two_words_connector: ", ")
end
def presentation(option_value)
return option_value.presentation unless option_value.option_type.name == "unit_weight"
return display_as if has_attribute?(:display_as) && display_as.present?
return variant.display_as if variant_display_as?
option_value.presentation
end
def variant_display_as?
respond_to?(:variant) && variant.present? &&
variant.respond_to?(:display_as) && variant.display_as.present?
end
def product_and_full_name
@@ -48,9 +65,11 @@ module OpenFoodNetwork
end
def unit_to_display
return options_text if !has_attribute?(:display_as) || display_as.blank?
return display_as if has_attribute?(:display_as) && display_as.present?
display_as
return variant.display_as if variant_display_as?
options_text
end
def update_units
@@ -84,7 +103,7 @@ module OpenFoodNetwork
if has_attribute?(:display_as) && display_as.present?
display_as
else
option_value_namer = OpenFoodNetwork::OptionValueNamer.new self
option_value_namer = VariantUnits::OptionValueNamer.new self
option_value_namer.name
end
end

View File

@@ -31,3 +31,16 @@
= f.label :invoice_text, t('.invoice_text')
.omega.eight.columns
= f.text_area :invoice_text, style: "width: 100%; height: 100px;"
.row
.alpha.three.columns
= f.label :terms_and_conditions, t('.terms_and_conditions')
.omega.eight.columns
%a{ href: '{{ Enterprise.terms_and_conditions }}', ng: { if: 'Enterprise.terms_and_conditions' } }
= '{{ Enterprise.terms_and_conditions_file_name }}'
.pad-top
= f.file_field :terms_and_conditions
.pad-top
%a.button.red{ href: '', ng: {click: 'removeTermsAndConditions()', if: 'Enterprise.terms_and_conditions'} }
= t('.remove_terms_and_conditions')

View File

@@ -7,7 +7,7 @@
%img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.logo.medium }}', if: 'Enterprise.logo' } }
= f.file_field :logo
%a.button.red{ href: '', ng: {click: 'removeLogo()', if: 'Enterprise.logo'} }
= t('admin.enterprises.remove_logo.remove')
= t('.remove_logo')
.row.page-admin-enterprises-form__promo-image-field-group.image-field-group
.alpha.three.columns
@@ -23,4 +23,4 @@
%img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.promo_image.large }}', if: 'Enterprise.promo_image' } }
= f.file_field :promo_image
%a.button.red{ href: '', ng: {click: 'removePromoImage()', if: 'Enterprise.promo_image'} }
= t('admin.enterprises.remove_promo_image.remove')
= t('.remove_promo_image')

View File

@@ -14,6 +14,7 @@
= render "checkout/shipping", f: f
= render "checkout/payment", f: f
= render "checkout/already_ordered", f: f if show_bought_items?
= render "checkout/terms_and_conditions", f: f
%p
%button.button.primary{type: :submit}
= t :checkout_send

View File

@@ -13,8 +13,6 @@
"ng-class" => "{valid: payment.$valid, open: accordion.payment}"}
= render 'checkout/accordion_heading'
-# TODO render this in Angular instead of server-side
-# The problem being how to render the partials
.row
.small-12.medium-12.large-6.columns
- available_payment_methods.each do |method|

View File

@@ -0,0 +1,2 @@
%p.small
= t('.message_html', terms_and_conditions_link: link_to( t( '.link_text' ), current_order.distributor.terms_and_conditions.url, target: '_blank')) if current_order.distributor.terms_and_conditions.file?

View File

@@ -2,7 +2,6 @@
.row
.small-12.text-center.columns
%h1
/ TODO: Rohan - logo asset & width is content manageable:
%img{src: image_path("logo-white-notext.png"), title: Spree::Config.site_name}
%br/
%a.button.transparent{href: "/shops"}

View File

@@ -1,29 +1,34 @@
:javascript
$(document).ready(function() {
var in_iframe = function(){
try {
return window.self !== window.top;
} catch (e) {
return true;
var isInIframe = function(){
try {
return window.self !== window.top;
} catch (e) {
return true;
}
};
document.addEventListener(
'DOMContentLoaded',
function() {
var inIframe = isInIframe();
var embedded_styles_active = $('body.off-canvas').hasClass('embedded');
var set_shopfront_styles = function (state) {
$.ajax({
url: '/embedded_shopfront/' + state,
type: 'POST'
});
};
if (inIframe && !embedded_styles_active){
$('body.off-canvas').addClass('embedded');
set_shopfront_styles('enable');
}
};
var embedded_styles_active = $('body.off-canvas').hasClass('embedded');
var set_shopfront_styles = function(state) {
$.ajax({
url: '/embedded_shopfront/'+state,
type: 'POST'
});
};
if (in_iframe() && !embedded_styles_active){
$('body.off-canvas').addClass('embedded');
set_shopfront_styles('enable');
}
if (!in_iframe() && embedded_styles_active) {
$('body.off-canvas').removeClass('embedded');
set_shopfront_styles('disable');
}
});
if (!inIframe && embedded_styles_active) {
$('body.off-canvas').removeClass('embedded');
set_shopfront_styles('disable');
}
},
false
);

View File

@@ -18,14 +18,14 @@
= stylesheet_link_tag "darkswarm/all"
= csrf_meta_tags
%body{class: body_classes, "body-scroll" => true , ng: {app: "Darkswarm"}}
%body{ class: body_classes, "body-scroll" => true , ng: { app: 'Darkswarm' }}
/ [if lte IE 8]
= render partial: "shared/ie_warning"
= javascript_include_tag "iehack"
= render "layouts/shopfront_script" if @shopfront_layout
.off-canvas-wrap{offcanvas: true}
.off-canvas-wrap{ offcanvas: true }
.fixed.off-canvas-fixed
= render "shared/menu/menu" unless @hide_menu
= yield :page_alert

View File

@@ -3,9 +3,9 @@
= render partial: 'spree/shared/error_messages', locals: { target: @image }
- content_for :page_actions do
%li= button_link_to t('spree.back_to_images_list'), admin_product_images_url(@product), icon: 'icon-arrow-left'
%li= button_link_to t('spree.back_to_images_list'), admin_product_images_url(@product, @url_filters), icon: 'icon-arrow-left'
= form_for [:admin, @product, @image], html: { multipart: true } do |f|
= form_for [:admin, @product, @image], url: admin_product_image_path(@product, @image, @url_filters), html: { multipart: true } do |f|
%fieldset
%legend{align: "center"}= @image.attachment_file_name
.field.alpha.three.columns.align-center
@@ -18,4 +18,4 @@
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), admin_product_images_url(@product), id: 'cancel_link', class: 'button icon-remove'
= link_to t('spree.actions.cancel'), admin_product_images_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

@@ -2,7 +2,7 @@
= render partial: 'spree/admin/shared/product_tabs', locals: { current: 'Images'}
- content_for :page_actions do
%li= link_to_with_icon('icon-plus', t('spree.new_image'), new_admin_product_image_url(@product), id: 'new_image_link', class: 'button')
%li= link_to_with_icon('icon-plus', t('spree.new_image'), new_admin_product_image_url(@product, @url_filters), id: 'new_image_link', class: 'button')
#images
@@ -11,7 +11,7 @@
= t('spree.no_images_found')
\.
- else
%table.index.sortable{ "data-sortable-link" => "#{update_positions_admin_product_images_url(@product)}" }
%table.index.sortable{ "data-sortable-link" => "#{update_positions_admin_product_images_url(@product, @url_filters)}" }
%colgroup
%col{ style: "width: 5%" }/
%col{ style: "width: 10%" }/
@@ -36,5 +36,5 @@
%td= options_text_for(image)
%td= image.alt
%td.actions
= link_to_with_icon 'icon-edit', t('spree.edit'), edit_admin_product_image_url(@product, image), no_text: true, data: { action: 'edit'}
= link_to_delete image, { url: admin_product_image_url(@product, image), no_text: true }
= link_to_with_icon 'icon-edit', t('spree.edit'), edit_admin_product_image_url(@product, image, @url_filters), no_text: true, data: { action: 'edit'}
= link_to_delete image, { url: admin_product_image_url(@product, image, @url_filters), no_text: true }

View File

@@ -1,10 +1,10 @@
= form_for [:admin, @product, @image], html: { multipart: true } do |f|
= form_for [:admin, @product, @image], url: admin_product_images_path(@product, @image, @url_filters), html: { multipart: true } do |f|
%fieldset
%legend{ align: "center" }= t('spree.new_image')
= render partial: 'form', locals: { f: f }
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product), id: 'cancel_link', class: 'button'
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product, @url_filters), id: 'cancel_link', class: 'button'
= javascript_include_tag 'admin/spree/images/new.js'

View File

@@ -0,0 +1,45 @@
- if use_billing
.field{style: "position: absolute;margin-top: -15px;right: 0;"}
%span
= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address))
= label_tag 'order[use_billing]', Spree.t(:use_billing_address)
- is_shipping_address = name == Spree.t(:shipping_address)
- s_or_b = is_shipping_address ? 's' : 'b'
- display_style = (use_billing && (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address == @order.ship_address)) ? 'none' : 'block'
%div{id: "#{is_shipping_address ? 'shipping' : 'billing'}", style: "display: #{display_style}"}
%div{class: "field"}
= f.label :firstname, Spree.t(:first_name) + ':'
= f.text_field :firstname, class: 'fullwidth'
%div{class: "field"}
= f.label :lastname, Spree.t(:last_name) + ':'
= f.text_field :lastname, class: 'fullwidth'
- if Spree::Config[:company]
%div{class: "field"}
= f.label :company, Spree.t(:company) + ':'
= f.text_field :company, class: 'fullwidth'
%div{class: "field"}
= f.label :address1, Spree.t(:street_address) + ':'
= f.text_field :address1, class: 'fullwidth'
%div{class: "field"}
= f.label :address2, Spree.t(:street_address_2) + ':'
= f.text_field :address2, class: 'fullwidth'
%div{class: "field"}
= f.label :city, Spree.t(:city) + ':'
= f.text_field :city, class: 'fullwidth'
%div{class: "field"}
= f.label :zipcode, Spree.t(:zip) + ':'
= f.text_field :zipcode, class: 'fullwidth'
%div{class: "field"}
= f.label :country_id, Spree.t(:country) + ':'
%span{id: "#{s_or_b}country"}
= f.collection_select :country_id, available_countries, :id, :name, {}, {class: 'select2 fullwidth', onchange: "update_state('#{s_or_b}')"}
%div{class: "field"}
= f.label :state_id, Spree.t(:state) + ':'
%span{id: "#{s_or_b}state"}
= f.collection_select :state_id, f.object.country.states.sort, :id, :name, {include_blank: true}, {class: 'select2 fullwidth', disabled: f.object.country.states.empty?}
%div{class: "field"}
= f.label :phone, Spree.t(:phone) + ':'
= f.phone_field :phone, class: 'fullwidth'

View File

@@ -11,13 +11,13 @@
%fieldset.no-border-bottom
%legend{:align => "center"}= Spree.t(:billing_address)
= f.fields_for :bill_address do |ba_form|
= render :partial => 'spree/admin/shared/address_form', :locals => { :f => ba_form, :name => Spree.t(:billing_address), :use_billing => false }
= render :partial => 'address_form', :locals => { :f => ba_form, :name => Spree.t(:billing_address), :use_billing => false }
.omega.six.columns{"data-hook" => "ship_address_wrapper"}
%fieldset.no-border-bottom
%legend{:align => "center"}= Spree.t(:shipping_address)
= f.fields_for :ship_address do |sa_form|
= render :partial => 'spree/admin/shared/address_form', :locals => { :f => sa_form, :name => Spree.t(:shipping_address), :use_billing => true }
= render :partial => 'address_form', :locals => { :f => sa_form, :name => Spree.t(:shipping_address), :use_billing => true }
.clear
@@ -26,3 +26,4 @@
- content_for :head do
= javascript_include_tag 'admin/spree/orders/address_states.js'
= javascript_include_tag 'admin/spree/orders/use_billing.js'

View File

@@ -11,7 +11,8 @@
%col{style: "width: 13%"}
%col{style: "width: 14%"}
%col{style: "width: 32%"}
%col{style: "width: 14%"}
- if spree_current_user.admin?
%col{style: "width: 14%"}
%col{style: "width: 8%"}
%col{style: "width: 8%"}
%col{style: "width: 11%"}
@@ -20,7 +21,8 @@
%th= t('.name')
%th= t('.products_distributor')
%th= t('.provider')
%th= t('.environment')
- if spree_current_user.admin?
%th= t('.environment')
%th= t('.display')
%th= t('.active')
%th.actions
@@ -33,7 +35,8 @@
= distributor.name
%br/
%td= method.class.clean_name
%td.align-center= method.environment.to_s.titleize
- if spree_current_user.admin?
%td.align-center= method.environment.to_s.titleize
%td.align-center= method.display_on.blank? ? t('.both') : t('.' + method.display_on.to_s)
%td.align-center= method.active ? t('.active_yes') : t('.active_no')
%td.actions

View File

@@ -11,4 +11,4 @@
= f.text_field :value, class: 'autocomplete'
%td.actions
- if f.object.persisted?
= link_to_delete f.object, no_text: true
= link_to_delete f.object, { url: admin_product_product_property_url(@product, f.object, @url_filters), no_text: true }

View File

@@ -9,7 +9,7 @@
%li
= link_to_add_fields t('.add_product_properties'), 'tbody#product_properties', class: 'icon-plus button'
= form_for @product, url: admin_product_url(@product), method: :put do |f|
= form_for @product, url: admin_product_url(@product, @url_filters), method: :put do |f|
%fieldset.no-border-top
.add_product_properties
= image_tag 'select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
@@ -46,7 +46,10 @@
%td.actions
= render partial: 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), admin_product_product_properties_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'
= hidden_field_tag 'clear_product_properties', 'true'

View File

@@ -1,5 +1,5 @@
- content_for :page_actions do
%li= button_link_to t('admin.products.back_to_products_list'), admin_products_path, :icon => 'icon-arrow-left'
%li= button_link_to t('admin.products.back_to_products_list'), "#{admin_products_path}#{(@url_filters.empty? ? "" : "#?#{@url_filters.to_query}")}", :icon => 'icon-arrow-left'
%li#new_product_link
= button_link_to t(:new_product), new_object_url, { :icon => 'icon-plus', :id => 'admin_new_product' }
@@ -8,7 +8,10 @@
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => 'Product Details' }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], :url => admin_product_path(@product, @url_filters), :method => :put, :html => { :multipart => true } do |f|
%fieldset.no-border-top{'ng-app' => 'admin.products'}
= render :partial => 'form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t(:update), 'icon-refresh'
%span.or= t(:or)
= button_link_to t(:cancel), "#{collection_url}#{(@url_filters.empty? ? "" : "#?#{@url_filters.to_query}")}", icon: 'icon-remove'

View File

@@ -1,8 +1,11 @@
= render partial: 'spree/admin/shared/product_sub_menu'
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => 'Group Buy Options' }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= render partial: 'spree/admin/shared/product_tabs', locals: { :current => 'Group Buy Options' }
= render partial: 'spree/shared/error_messages', locals: { :target => @product }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], url: admin_product_url(@product, @url_filters), method: :put, html: { :multipart => true } do |f|
%fieldset.no-border-top
= render :partial => 'group_buy_form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
= render partial: 'group_buy_form', locals: { f: f }
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), edit_admin_product_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

@@ -5,22 +5,20 @@
.quick_search.three.columns.alpha
%label{ for: 'quick_filter' }
%br
%input.quick-search.fullwidth{ ng: {model: 'query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchProducts()" }
%input.quick-search.fullwidth{ ng: {model: 'q.query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchProducts()" }
.one.columns &nbsp;
.filter_select.three.columns
%label{ for: 'producer_filter' }= t 'producer'
%br
%select.fullwidth{ id: 'producer_filter', 'ofn-select2-min-search' => 5, ng: {model: 'producerFilter', options: 'producer.id as producer.name for producer in producers'} }
%select.fullwidth{ id: 'producer_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.producerFilter', options: 'producer.id as producer.name for producer in producers'} }
.filter_select.three.columns
%label{ for: 'category_filter' }= t 'category'
%br
%select.fullwidth{ id: 'category_filter', 'ofn-select2-min-search' => 5, ng: {model: 'categoryFilter', options: 'taxon.id as taxon.name for taxon in taxons'} }
%select.fullwidth{ id: 'category_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.categoryFilter', options: 'taxon.id as taxon.name for taxon in taxons'} }
.filter_select.three.columns
%label{ for: 'import_filter' } Import Date
%br
%select.fullwidth{ id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'importDateFilter', init: "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}"}}
%option{value: "{{date.id}}", ng: {repeat: "date in importDates" }}
{{date.name}}
%select.fullwidth{ id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.importDateFilter', init: "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}", options: 'date.id as date.name for date in importDates'} }
.filter_clear.three.columns.omega
%label{ for: 'clear_all_filters' }

View File

@@ -3,12 +3,12 @@
%img.spinner{ src: image_path("spinning-circles.svg") }
%h1= t('.title')
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && query.length==0' }
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' }
%h1#no_results= t('.no_products')
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && query.length!=0' }
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length != 0' }
%h1#no_results
= t('.no_results')
'
{{query}}
'
{{q.query}}
'

View File

@@ -1,9 +1,12 @@
= render partial: 'spree/admin/shared/product_sub_menu'
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => t(:search) }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= render partial: 'spree/admin/shared/product_tabs', locals: { current: t(:search) }
= render partial: 'spree/shared/error_messages', locals: { target: @product }
%div{ 'ng-app' => 'ofn.admin' }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], url: admin_product_url(@product, @url_filters), method: :put, html: { :multipart => true } do |f|
%fieldset.no-border-top
= render :partial => 'seo_form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), edit_admin_product_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

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

View File

@@ -12,22 +12,22 @@
- if can?(:admin, Spree::Product)
- klass = current == 'Product Details' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-edit', t('admin.products.tabs.product_details'), edit_admin_product_url(@product)
= link_to_with_icon 'icon-edit', t('admin.products.tabs.product_details'), edit_admin_product_url(@product, @url_filters)
- if can?(:admin, Spree::Image)
- klass = current == 'Images' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-picture', t('admin.products.tabs.images'), admin_product_images_url(@product)
= link_to_with_icon 'icon-picture', t('admin.products.tabs.images'), admin_product_images_url(@product, @url_filters)
- if can?(:admin, Spree::Variant)
- klass = current == 'Variants' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-th-large', t('admin.products.tabs.variants'), admin_product_variants_url(@product)
= link_to_with_icon 'icon-th-large', t('admin.products.tabs.variants'), admin_product_variants_url(@product, @url_filters)
- if can?(:admin, Spree::ProductProperty)
- klass = current == 'Product Properties' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.product_properties'), admin_product_product_properties_url(@product)
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.product_properties'), admin_product_product_properties_url(@product, @url_filters)
- klass = current == 'Group Buy Options' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.group_buy_options'), group_buy_options_admin_product_url(@product)
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.group_buy_options'), group_buy_options_admin_product_url(@product, @url_filters)
- klass = current == t(:search) ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t(:search), seo_admin_product_url(@product)
= link_to_with_icon 'icon-tasks', t(:search), seo_admin_product_url(@product, @url_filters)

View File

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

View File

@@ -15,6 +15,9 @@
:loading => Spree.t(:loading),
:month_names => I18n.t(:month_names, :scope => :date).reject(&:blank?),
:more => Spree.t(:more),
:datetime_ui_current_text => I18n.t('datetime_picker_ui.current_text'),
:datetime_ui_close_text => I18n.t('datetime_picker_ui.close_text'),
:datetime_ui_time_text => I18n.t('datetime_picker_ui.time_text'),
:name => Spree.t(:name),
:next => Spree.t(:next),
:no_results => Spree.t(:no_results),

View File

@@ -4,8 +4,11 @@
= render partial: 'spree/shared/error_messages', locals: { target: @variant }
= form_for [:admin, @product, @variant] do |f|
= form_for [:admin, @product, @variant], :url => admin_product_variant_path(@product, @variant, @url_filters) do |f|
%fieldset.no-border-top
%div
= render partial: 'form', locals: { f: f }
= render partial: 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t(:update), 'icon-refresh'
%span.or= t(:or)
= button_link_to t(:cancel), collection_url(@url_filters), icon: 'icon-remove'

View File

@@ -27,8 +27,8 @@
%td.align-center= variant.display_price.to_html
%td.align-center= variant.sku
%td.actions
= link_to_edit(variant, no_text: true) unless variant.deleted?
= link_to_delete(variant, no_text: true) unless variant.deleted?
= link_to_with_icon('icon-edit', Spree.t(:edit), edit_object_url(variant, @url_filters), no_text: true) unless variant.deleted?
= link_to_delete(variant, { url: object_url(variant, @url_filters), no_text: true }) unless variant.deleted?
- if @product.empty_option_values?
%p.first_add_option_types.no-objects-found
@@ -41,6 +41,6 @@
- content_for :page_actions do
%ul.inline-menu
%li#new_var_link
= link_to_with_icon('icon-plus', t('.new_variant'), new_admin_product_variant_url(@product), class: 'button')
= link_to_with_icon('icon-plus', t('.new_variant'), new_admin_product_variant_url(@product, @url_filters), class: 'button')
%li= link_to_with_icon('icon-filter', @deleted.blank? ? t('.show_deleted') : t('.show_active'), admin_product_variants_url(@product, deleted: @deleted.blank? ? "on" : "off"), class: 'button')
%li= link_to_with_icon('icon-filter', @deleted.blank? ? t('.show_deleted') : t('.show_active'), admin_product_variants_url(@product, @url_filters.merge(deleted: @deleted.blank? ? "on" : "off")), class: 'button')

View File

@@ -1,7 +1,11 @@
= render partial: 'spree/shared/error_messages', locals: { target: @variant }
= form_for [:admin, @product, @variant] do |f|
= form_for [:admin, @product, @variant], :url => admin_product_variants_path(@product, @url_filters) do |f|
%fieldset{'data-hook' => "admin_variant_new_form"}
%legend{align: "center"}= t('.new_variant')
= render partial: 'form', locals: { f: f }
= render partial: 'spree/admin/shared/new_resource_links'
.form-buttons.filter-actions.actions
= button t('actions.create'), 'icon-ok'
%span.or= t(:or)
= button_link_to t('actions.cancel'), collection_url(@url_filters), icon: 'icon-remove'

View File

@@ -9,11 +9,11 @@ ar:
shipment_state: حالة الشحن
completed_at: 'تم الانتهاء عند '
number: 'رقم '
state: ولاية
state: محافظة
email: الايميل الالكتروني للعميل
spree/payment:
amount: القيمة
state: ولاية
state: محافظة
source: المصدر
spree/product:
primary_taxon: "نوع المنتج "
@@ -34,7 +34,7 @@ ar:
email:
taken: "يوجد حساب متصل بهذا الايميل.\nالرجاء الدخول او اعادة تعيين كلمة السر."
spree/order:
no_card: لا يوجد بطاقة ائتمان معتمدة متاحة لاتمام عملية الدفع
no_card: لا توجد بطاقات ائتمان معتمدة لشحنها
spree/credit_card:
attributes:
base:
@@ -53,12 +53,12 @@ ar:
order_management/reports/enterprise_fee_summary/parameters:
start_at: "البداية "
end_at: "النهاية"
distributor_ids: نافذ"
distributor_ids: راكز "
producer_ids: "المنتجين"
order_cycle_ids: "دورات الطلب"
enterprise_fee_ids: "أسماء الرسوم"
shipping_method_ids: "طرق الشحن"
payment_method_ids: "طريقة الدفع"
payment_method_ids: "طرق الدفع"
errors:
messages:
inclusion: "غير مدرجة في القائمة"
@@ -120,6 +120,10 @@ ar:
integer_array_validator:
not_array_error: "يجب أن يكون مصفوفة"
invalid_element_error: "يجب أن يحتوي فقط على أعداد صحيحة فاعلة"
datetime_picker_ui:
current_text: الآن
close_text: تم
time_text: الوقت
enterprise_mailer:
confirmation_instructions:
subject: "يرجى تأكيد عنوان البريد الإلكتروني ل %{enterprise}"
@@ -144,7 +148,7 @@ ar:
instructions: "تم شحن طلبك"
shipment_summary: "ملخص الشحن"
subject: "إشعار الشحن"
thanks: "شكرا لك على اعمالك."
thanks: "شكرا لك على تعاملاتك."
track_information: "معلومات التتبع : %{tracking}"
track_link: "رابط التتبع: %{url}"
subscription_mailer:
@@ -597,7 +601,7 @@ ar:
order_date: "اكتمل في"
max: "اعلى"
product_unit: "المنتج: وحدة"
weight_volume: "الوزن / الحجم"
weight_volume: "الوزن / الحجم (جم)"
ask: "يطلب؟"
page_title: "ادارة طلب الجملة "
actions_delete: "حذف المواد المختارة"
@@ -634,6 +638,8 @@ ar:
acn_placeholder: على سبيل المثال. 123 456 789
display_invoice_logo: عرض الشعار في الفواتير
invoice_text: أضف نصًا مخصصًا في نهاية الفواتير
terms_and_conditions: "الأحكام والشروط"
remove_terms_and_conditions: "إزالة الملف"
contact:
name: اسم
name_placeholder: على سبيل المثال. برقوق غوستاف
@@ -656,6 +662,8 @@ ar:
promo_image_note1: 'يرجى الملاحظة:'
promo_image_note2: سيتم اقتصاص أي صورة ترويجية تم تحميلها هنا إلى 1200 × 260.
promo_image_note3: يتم عرض صورة الترويجي في الجزء العلوي من صفحة الملف الشخصي للشركات والنوافذ المنبثقة.
remove_logo: "إزالة الصورة"
remove_promo_image: "إزالة الصورة"
inventory_settings:
text1: يمكنك اختيار إدارة مستويات المخزون والأسعار عبر
inventory: المخزون
@@ -754,7 +762,7 @@ ar:
confirm_modal:
title: تواصل مع Stripe
part1: Stripe هي خدمة معالجة دفع تسمح للمحلات على شبكة الغذاء المفتوح بقبول مدفوعات بطاقات الائتمان من العملاء.
part2: لاستخدام هذه الميزة ، يجب عليك توصيل حساب Stripe الخاص بك بـ شبكة الغذاء المفتوح. النقر فوق &quot;أوافق&quot; أدناه إلى إعادة توجيهك إلى موقع Stripe على الويب حيث يمكنك توصيل حساب Stripe الموجود ، أو إنشاء حساب جديد إذا لم يكن لديك حساب بالفعل.
part2: لاستخدام هذه الميزة ، يجب عليك توصيل حساب Stripe الخاص بك بـ شبكة الغذاء المفتوح. النقر فوق 'أوافق' أدناه إلى إعادة توجيهك إلى موقع Stripe على الويب حيث يمكنك توصيل حساب Stripe الموجود ، أو إنشاء حساب جديد إذا لم يكن لديك حساب بالفعل.
part3: سيتيح هذا لشبكة الغذاء المفتوح قبول مدفوعات بطاقات الائتمان من العملاء نيابة عنك. يرجى ملاحظة أنك سوف تحتاج إلى الحفاظ على حساب Stripe الخاص بك ، ودفع رسوم Stripe والتعامل مع أي عمليات رد المبالغ المدفوعة وخدمة العملاء بنفسك.
i_agree: أنا موافق
cancel: إلغاء
@@ -842,14 +850,6 @@ ar:
new:
title: شركة جديدة
back_link: العودة إلى قائمة المؤسسات
remove_logo:
remove: "إزالة الصورة"
removed_successfully: "تمت إزالة الشعار بنجاح"
immediate_removal_warning: "سيتم إزالة الشعار مباشرة بعد التأكيد."
remove_promo_image:
remove: "إزالة الصورة"
removed_successfully: "تمت إزالة الصورة الترويجية بنجاح"
immediate_removal_warning: "ستتم إزالة صورة الترويجي فور التأكيد."
welcome:
welcome_title: مرحبا بكم في شبكة الغذاء المفتوح!
welcome_text: لقد نجحت في إنشاء
@@ -1147,12 +1147,17 @@ ar:
destroy_attachment_does_not_exist: "الشعار غير موجود"
enterprise_promo_image:
destroy_attachment_does_not_exist: "الصورة الترويجية غير موجودة"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "ملف الشروط والأحكام غير موجود"
orders:
failed_to_update: "فشل في تحديث الطلب"
checkout:
already_ordered:
cart: "سلة التسوق"
message_html: "لديك طلب لدورة الطلب هذه بالفعل. تحقق من %{cart} لرؤية العناصر التي طلبتها من قبل. يمكنك أيضًا إلغاء العناصر طالما كانت دورة الطلب مفتوحة."
terms_and_conditions:
message_html: "من خلال تقديم هذا الطلب ، فإنك توافق على %{terms_and_conditions_link}."
link_text: "شروط الخدمة"
failed: "فشل الخروج. يرجى إعلامنا حتى نتمكن من معالجة طلبك."
shops:
hubs:
@@ -1180,17 +1185,17 @@ ar:
register: "سجل هنا"
footer:
footer_secure: "آمن وموثوق به."
footer_secure_text: "تستخدم شبكة الغذاء المفتوح تشفير( SSL 2048 بت RSA) في كل مكان للحفاظ على خصوصية معلومات التسوق والدفع. لا تقوم خوادمنا بتخزين تفاصيل بطاقة الائتمان الخاصة بك ، وتتم معالجة المدفوعات بواسطة خدمات متوافقة مع PCI."
footer_secure_text: "تستخدم منصة فلاحة - شبكة الغذاء المفتوح تشفير ( 2048 بت RSA) في كل مكان للحفاظ على خصوصية معلومات التسوق والدفع. لا تقوم خوادمنا بتخزين تفاصيل بطاقة الائتمان الخاصة بك ، وتتم معالجة المدفوعات بواسطة خدمات متوافقة مع PCI."
footer_contact_headline: "أبق على اتصال"
footer_contact_email: "راسلنا عبر البريد الإلكتروني"
footer_contact_email: "راسلنا "
footer_nav_headline: "التنقل"
footer_join_headline: "انضم إلينا"
footer_join_body: "إنشاء قائمة أو متجر أو دليل المجموعة على شبكة الغذاء المفتوح."
footer_join_body: "إنشاء مركز تجاري أو متجر أو مجموعة على منصة فلاحة - شبكة الغذاء المفتوح."
footer_join_cta: "اخبرني المزيد!"
footer_legal_call: "اقراء"
footer_legal_tos: "الأحكام والشروط"
footer_legal_visit: "تجدنا على"
footer_legal_text_html: "شبكة الغذاء المفتوح عبترة عن منصة حرة ومفتوحة برمجيات المصدر. تم ترخيص المحتوى الخاص بنا باستخدام %{content_license} ورمزنا مع %{code_license}."
footer_legal_text_html: "فلاحة شبكة الغذاء المفتوح عبارة عن منصة حرة ومفتوحة البرمجيات. تم ترخيص المحتوى الخاص بنا باستخدام %{content_license} ورمزنا مع %{code_license}."
footer_data_text_with_privacy_policy_html: "نحن نحرص على البيانات الخاصة بك. انظر %{privacy_policy} و %{cookies_policy}"
footer_data_text_without_privacy_policy_html: "نحن نحرص على البيانات الخاصة بك. انظر %{cookies_policy} لدينا"
footer_data_privacy_policy: "سياسة خاصة"
@@ -1379,26 +1384,26 @@ ar:
cookies_policy_link: "اتفاقية ملفات تعريف الارتباط"
cookies_accept_button: "قبول ملفات تعريف الارتباط"
home_shop: تسوق الآن
brandstory_headline: "الغذاء ، غير مدمج."
brandstory_intro: "في بعض الأحيان تكون أفضل طريقة لإصلاح النظام هي بدء نظام جديد ..."
brandstory_part1: "نبدأ من الألف إلى الياء. مع المزارعين على استعداد لرواية قصصهم بفخر وحقا. مع الموزعين على استعداد لتوصيل الأشخاص بالمنتجات بطريقة عادلة وبصدق. مع المشترين الذين يعتقدون أن أفضل قرارات التسوق الأسبوعية يمكن أن تغير العالم بشكل خطير."
brandstory_part2: "ثم نحن بحاجة إلى وسيلة لجعلها حقيقية. طريقة لتمكين كل من يزرع, يبيع ويشتري الغذاء. طريقة لرواية جميع القصص ، للتعامل مع جميع الخدمات اللوجستية. طريقة لتحويل المعاملة إلى تطور كل يوم."
brandstory_part3: "لذلك نقوم ببناء سوق على الإنترنت يعمل على تسوية الملعب. بشكل شفاف ، وذلك لخلق علاقات حقيقية. إنه مفتوح المصدر ، لذلك يمتلكه الجميع. يتم توسيعه حسب المناطق والأمم ، لذا يبدأ الناس بالإصدارات في جميع أنحاء العالم."
brandstory_part4: "إنه يعمل في كل مكان. إنه يغير كل شيء."
brandstory_part5_strong: "نحن نسميها شبكة الغذاء المفتوح."
brandstory_part6: "كلنا نحب الطعام. الآن يمكننا أن نحب نظامنا الغذائي أيضا."
brandstory_headline: "قم بدعم الانتاج المحلي"
brandstory_intro: "سيساعدك المجتمع عبر الإنترنت في بناء مشروعك الغذائي بنجاح ..."
brandstory_part1: "تتيح منصة فلاحة - شبكة الغذاء المفتوح للمزارعين ببيع منتجاتهم عبر الإنترنت بسعر يناسبهم. لقد تم تصميمه خصيصًا لبيع الغذاء حتى يتمكن من التعامل مع الإجراءات الصعبة أو و بمعايير مناسبة للافراد و العائلات كعدد من حبات البندورة الى الكيلو انتهاءا بالطن, دزينة من البيض ، وضمةمن البقدونس ، ودجاجة كاملة بمختلف الاوزان ..."
brandstory_part2: "يمكن لمنتجي الغذاء إنشاء متجر الكتروني ، وتحصيل المدفوعات ، والبيع من خلال متاجر أخرى على المنصة والوصول إلى خدمات التوصيل بأسعار مخفضة. "
brandstory_part3: "يمكن لتجار الجملة دمج منصة فلاحة مع أنظمتهم الحالية وإدارة مجموعات الشراء لتزويد العملاء بمنتجاتهم من خلال شبكتنا الوطنية و مراكز البيع ومتاجر الأغذية."
brandstory_part4: "يمكن للمجتمعات أن تجمع المنتجين في منطقتهم المحلية لإنشاء أسواق افتراضية للمزارعين ، وبناء اقتصاد غذائي محلي مرن."
brandstory_part5_strong: "وما لا يقل أهمية عن المنصة نفسها هو القيم التي تدعمها."
brandstory_part6: "إذا كنت تبيع طعامًا جيدًا - كمزارع ، سوق زراعي، جمعية تعاونية منتجة، مركز طعام - فاختر برنامجًا يتوافق مع قيمك لبناء أنظمة غذائية للناس و الوطن. من خلال العمل الجماعي بدلاً من التنافس ، نشارك تكاليف تطوير برامج جديدة ، ونضمن أن مشروعنا مرن!"
learn_body: "استكشف النماذج والقصص والموارد لدعمك لتطوير أعمالك التجارية أو مؤسستك الخاصة بالطعام الجيد. البحث عن التدريب والأحداث والفرص الأخرى للتعلم من أقرانهم."
learn_cta: "اشعل حماسك"
connect_body: "ابحث في الدلائل الكاملة للمنتجين والمراكز والمجموعات للعثور على تجار المواد الغذائية العادلة بالقرب منك. أدرج عملك أو مؤسستك على شبكة الغذاء المفتوحة حتى يتمكن المشترون من العثور عليك. انضم إلى المجتمع للحصول على المشورة وحل المشكلات معًا."
connect_cta: "الذهاب لاستكشاف"
system_headline: "التسوق - إليك كيف تعمل."
system_step1: "1. البحث"
system_step1_text: "ابحث في متاجرنا المتنوعة والمستقلة للطعام المحلي الموسمي. البحث عن طريق الحي وفئة الطعام ، أو ما إذا كنت تفضل التسليم أو الاستلام."
system_step2: "2. متجر"
system_step2_text: "حول معاملاتك الغذائية المحلية بأسعار معقولة من مختلف المنتجين والمراكز. تعرف على القصص وراء طعامك والأشخاص الذين يصنعونه!"
system_step3: "3. الاستلام / التسليم"
system_step3_text: "تشبث بالتسليم ، أو تفضل بزيارة المنتج أو المحور للحصول على اتصال شخصي أكثر مع طعامك. التسوق الطعام متنوعة مثل الطبيعة المقصود منه."
cta_headline: "التسوق الذي يجعل العالم مكانًا أفضل."
system_headline: "البيع على منصة فلاحة 3 خطوات سهلة"
system_step1: "1. أنشئ مؤسستك"
system_step1_text: "قم بإعداد مؤسستك بالاسم والوصف والصور وتفاصيل الاتصال وروابط الوسائط الاجتماعية."
system_step2: "2. أضف منتجاتك"
system_step2_text: "أضف منتجات إلى متجرك - خاصة بك و / أو من منتجين آخرين من حولك. تعيين الصور والأوصاف والأسعار المخزون والأوزان والمقاييس المرنة!"
system_step3: "3. عمليات التسليم الخاصة بك"
system_step3_text: "قم بإعداد طرق الدفع. إنشاء نقاط استلام متعددة وتفاصيل التسليم. إنشاء أوامر متكررة وتوزيعات منتظمة."
cta_headline: "شبكة الغذاء المستدامة في المستقبل."
cta_label: "أنا مستعد"
stats_headline: "نحن بصدد إنشاء نظام غذائي جديد."
stats_producers: "منتجي المواد الغذائية"
@@ -2306,6 +2311,7 @@ ar:
enterprise_name_error: "لقد اتخذت بالفعل. إذا كانت هذه هي مؤسستك وترغب في المطالبة بالملكية ، أو إذا كنت ترغب في التداول مع هذا المشروع ، فيرجى الاتصال بالمدير الحالي لملف التعريف هذا على %{email}."
enterprise_owner_error: "^ %{email} غير مسموح به لامتلاك أي مؤسسات أخرى (الحد الأقصى هو %{enterprise_limit})."
enterprise_role_uniqueness_error: "^ هذا الدور موجود بالفعل."
enterprise_terms_and_conditions_type_error: "يسمح فقط بملفات PDF"
inventory_item_visibility_error: يجب أن تكون صحيحة أو خاطئة
product_importer_file_error: "خطأ: لم يتم تحميل الملف"
product_importer_spreadsheet_error: "لا يمكن معالجة الملف: ملف غير صالح"
@@ -2570,6 +2576,15 @@ ar:
error_saving: "خطأ في حفظ الاشتراك"
new:
please_select_a_shop: "يرجى اختيار متجر"
enterprises:
form:
images:
removed_logo_successfully: "تمت إزالة الشعار بنجاح"
immediate_logo_removal_warning: "سيتم إزالة الشعار مباشرة بعد التأكيد."
removed_promo_image_successfully: "تمت إزالة الصورة الترويجية بنجاح"
immediate_promo_image_removal_warning: "ستتم إزالة صورة الترويجي فور التأكيد."
immediate_terms_and_conditions_removal_warning: "ستتم إزالة ملف الشروط والأحكام فورًا بعد التأكيد."
removed_terms_and_conditions_successfully: "تمت إزالة ملف الشروط والأحكام بنجاح"
insufficient_stock: "مخزون غير متوفر ، تبقى %{on_hand}"
out_of_stock:
reduced_stock_available: انخفاض المخزون المتاح
@@ -2643,6 +2658,11 @@ ar:
signup_or_login: "البدء بالتسجيل (أو تسجيل الدخول)"
have_an_account: "هل لديك حساب؟"
action_login: "تسجيل الدخول الآن."
stripe_elements:
unknown_error_from_stripe: |
كانت هناك مشكلة في إعداد بطاقتك في بوابة المدفوعات الخاصة بنا.
يرجى تحديث الصفحة والمحاولة مرة أخرى ، إذا فشلت مرة أخرى ،
يرجى الاتصال بنا للحصول على الدعم.
inflections:
each:
zero: "كل"

View File

@@ -120,6 +120,10 @@ ca:
integer_array_validator:
not_array_error: "ha de ser una matriu"
invalid_element_error: "ha de contenir només nombres enters vàlids"
datetime_picker_ui:
current_text: Ara
close_text: Fet
time_text: Hora
enterprise_mailer:
confirmation_instructions:
subject: "Sisplau, confirma l'adreça electrònica d'%{enterprise}"
@@ -597,7 +601,7 @@ ca:
order_date: "Completada a"
max: "Màx"
product_unit: "Producte: Unitat"
weight_volume: "Pes/Volum"
weight_volume: "Pes / Volum (g)"
ask: "Preguntar?"
page_title: "Gestió de les comandes en bloc"
actions_delete: "Suprimeix seleccionats"
@@ -634,6 +638,8 @@ ca:
acn_placeholder: p. ex. 123 456 789
display_invoice_logo: Mostreu el logotip a les factures
invoice_text: Afegeix text personalitzat al final de les factures
terms_and_conditions: "Termes i condicions"
remove_terms_and_conditions: "Elimina el fitxer"
contact:
name: Nom
name_placeholder: 'p. ex: Josep Ribes'
@@ -656,6 +662,8 @@ ca:
promo_image_note1: 'ATENCIÓ:'
promo_image_note2: Qualsevol imatge promocional que es carregui aquí es tallarà a 1200 x 260.
promo_image_note3: 'La imatge promocional es mostra a la part superior de la pàgina de perfil i finestres emergents d''una organització '
remove_logo: "Elimina la imatge "
remove_promo_image: "Elimina la imatge "
inventory_settings:
text1: 'Pots optar per gestionar els nivells d''existències i els preus a través del teu '
inventory: inventari
@@ -846,14 +854,6 @@ ca:
new:
title: Nova organització
back_link: 'Tornar a la llista d''organitzacions '
remove_logo:
remove: "Elimina la imatge"
removed_successfully: "Logotip eliminat correctament"
immediate_removal_warning: "El logotip s'eliminarà immediatament després de confirmar."
remove_promo_image:
remove: "Elimina la imatge "
removed_successfully: "Imatge promocional eliminada correctament"
immediate_removal_warning: "La imatge promocional s'eliminarà immediatament després de confirmar."
welcome:
welcome_title: Benvingut a Katuma - Open Food Network!
welcome_text: Heu creat correctament un
@@ -1151,12 +1151,17 @@ ca:
destroy_attachment_does_not_exist: "El logotip no existeix"
enterprise_promo_image:
destroy_attachment_does_not_exist: "La imatge promocional no existeix"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "El fitxer de termes i condicions no existeix"
orders:
failed_to_update: "No s'ha pogut actualitzar la comanda"
checkout:
already_ordered:
cart: "cistella"
message_html: "Ja teniu una comanda per a aquest cicle de comanda. Consulteu %{cart} per veure els articles que heu demanat anteriorment. També podeu cancel·lar articles sempre que el cicle de comanda estigui obert."
terms_and_conditions:
message_html: "En fer aquesta comanda accepteu %{terms_and_conditions_link}."
link_text: "Termes del servei"
failed: "La comanda ha fallat. Informeu-nos perquè puguem processar la vostra comanda."
shops:
hubs:
@@ -2302,6 +2307,7 @@ ca:
enterprise_name_error: "ja ha estat agafat. Si aquesta és la vostra organització i voleu reclamar-ne la propietat o si voleu comerciar amb aquesta organització, poseu-vos en contacte amb l'administradora actual d'aquest perfil %{email}."
enterprise_owner_error: "^ %{email} no està autoritzat a tenir més organitzacions (el límit és %{enterprise_limit})."
enterprise_role_uniqueness_error: "^ Aquest rol ja està present."
enterprise_terms_and_conditions_type_error: "Només sadmeten fitxers PDF"
inventory_item_visibility_error: ha de ser veritable o falsa
product_importer_file_error: "error: no s'ha carregat cap fitxer"
product_importer_spreadsheet_error: "no s'ha pogut processar el fitxer: tipus de fitxer no vàlid"
@@ -2576,6 +2582,15 @@ ca:
error_saving: "S'ha produït un error en desar la subscripció"
new:
please_select_a_shop: "Si us plau, seleccioneu una botiga"
enterprises:
form:
images:
removed_logo_successfully: "Logotip eliminat correctament"
immediate_logo_removal_warning: "El logotip s'eliminarà immediatament després de confirmar."
removed_promo_image_successfully: "Imatge promocional eliminada correctament"
immediate_promo_image_removal_warning: "La imatge promocional s'eliminarà immediatament després de confirmar."
immediate_terms_and_conditions_removal_warning: "El fitxer de termes i condicions seliminarà immediatament després de confirmar-ho."
removed_terms_and_conditions_successfully: "El fitxer de termes i condicions sha eliminat correctament"
insufficient_stock: "No hi ha prou estoc disponible, només queda %{on_hand}"
out_of_stock:
reduced_stock_available: Estoc reduït disponible
@@ -2649,6 +2664,9 @@ ca:
signup_or_login: "Comenceu registrant-vos (o iniciant sessió)"
have_an_account: "Ja tens un compte?"
action_login: "Inicia la sessió ara."
stripe_elements:
unknown_error_from_stripe: |
Hi ha hagut un problema al configurar la vostra targeta a la nostra passarel·la de pagaments. Actualitzeu la pàgina i torneu-ho a provar; si falla una segona vegada, poseu-vos en contacte amb nosaltres per obtenir assistència.
inflections:
each:
one: "cadascun"

View File

@@ -586,7 +586,6 @@ de_DE:
order_date: "Abgeschlossen am"
max: "Max"
product_unit: "Produkt: Einheit"
weight_volume: "Gewicht / Volumen"
ask: "Fragen?"
page_title: "Massenbearbeitung von Bestellungen"
actions_delete: "Ausgewählte löschen"
@@ -645,6 +644,8 @@ de_DE:
promo_image_note1: 'BITTE BEACHTEN SIE:'
promo_image_note2: Jedes hier hochgeladene Werbebild wird auf 1200 x 260 beschnitten.
promo_image_note3: Das Werbebild wird oben auf der Profilseite eines Unternehmens und in Pop-ups angezeigt.
remove_logo: "Bild entfernen"
remove_promo_image: "Bild entfernen"
inventory_settings:
text1: Sie verwalten optional Ihre Lagerbestände und Preise auch in Ihrem
inventory: Katalog
@@ -831,14 +832,6 @@ de_DE:
new:
title: Neues Unternehmen
back_link: Zurück zur Unternehmensliste
remove_logo:
remove: "Bild entfernen"
removed_successfully: "Das Logo wurde erfolgreich entfernt"
immediate_removal_warning: "Das Logo wird sofort nach der Bestätigung entfernt."
remove_promo_image:
remove: "Bild entfernen"
removed_successfully: "Werbebild wurde erfolgreich entfernt"
immediate_removal_warning: "Das Werbebild wird sofort nach der Bestätigung entfernt."
welcome:
welcome_title: Willkommen im Open Food Network!
welcome_text: 'Erfolgreich erstellt:'
@@ -2529,6 +2522,13 @@ de_DE:
error_saving: "Fehler beim Speichern des Abonnements"
new:
please_select_a_shop: "Bitte wählen Sie einen Laden"
enterprises:
form:
images:
removed_logo_successfully: "Das Logo wurde erfolgreich entfernt"
immediate_logo_removal_warning: "Das Logo wird sofort nach der Bestätigung entfernt."
removed_promo_image_successfully: "Werbebild wurde erfolgreich entfernt"
immediate_promo_image_removal_warning: "Das Werbebild wird sofort nach der Bestätigung entfernt."
insufficient_stock: "Nicht genügend Lagerbestand verfügbar, nur noch %{on_hand} verfügbar"
out_of_stock:
reduced_stock_available: Reduzierter Bestand verfügbar

View File

@@ -145,6 +145,10 @@ en:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
current_text: Now
close_text: Done
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -652,7 +656,7 @@ en:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
weight_volume: "Weight/Volume (g)"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -690,6 +694,8 @@ en:
acn_placeholder: eg. 123 456 789
display_invoice_logo: Display logo in invoices
invoice_text: Add customized text at the end of invoices
terms_and_conditions: "Terms and Conditions"
remove_terms_and_conditions: "Remove File"
contact:
name: Name
name_placeholder: eg. Gustav Plum
@@ -712,6 +718,8 @@ en:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -896,14 +904,6 @@ en:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -1211,6 +1211,8 @@ en:
destroy_attachment_does_not_exist: "Logo does not exist"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Promo image does not exist"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "Terms and Conditions file does not exist"
orders:
failed_to_update: "Failed to update order"
@@ -1223,6 +1225,9 @@ en:
already_ordered:
cart: "cart"
message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open."
terms_and_conditions:
message_html: "By placing this order you agree to the %{terms_and_conditions_link}."
link_text: "Terms of Service"
failed: "The checkout failed. Please let us know so that we can process your order."
shops:
hubs:
@@ -2433,6 +2438,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
enterprise_name_error: "has already been taken. If this is your enterprise and you would like to claim ownership, or if you would like to trade with this enterprise please contact the current manager of this profile at %{email}."
enterprise_owner_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})."
enterprise_role_uniqueness_error: "^That role is already present."
enterprise_terms_and_conditions_type_error: "Only PDFs are allowed"
inventory_item_visibility_error: must be true or false
product_importer_file_error: "error: no file uploaded"
product_importer_spreadsheet_error: "could not process file: invalid filetype"
@@ -2698,6 +2704,15 @@ See the %{link} to find out more about %{sitename}'s features and to start using
error_saving: "Error saving subscription"
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
immediate_terms_and_conditions_removal_warning: "The Terms and Conditions file will be removed immediately after you confirm."
removed_terms_and_conditions_successfully: "Terms and Conditions file removed successfully"
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -113,6 +113,8 @@ en_AU:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -578,7 +580,6 @@ en_AU:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -637,6 +638,8 @@ en_AU:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -831,14 +834,6 @@ en_AU:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -2515,6 +2510,13 @@ en_AU:
subscriptions:
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -112,6 +112,8 @@ en_BE:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -567,7 +569,6 @@ en_BE:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -626,6 +627,8 @@ en_BE:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -806,14 +809,6 @@ en_BE:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -2475,6 +2470,13 @@ en_BE:
subscriptions:
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -120,6 +120,10 @@ en_CA:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
current_text: Now
close_text: Done
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -597,7 +601,7 @@ en_CA:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
weight_volume: "Weight/Volume (g)"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -634,6 +638,8 @@ en_CA:
acn_placeholder: eg.80781 2466
display_invoice_logo: Display logo in invoices
invoice_text: Add customized text at the end of invoices
terms_and_conditions: "Terms and Conditions"
remove_terms_and_conditions: "Remove File"
contact:
name: Name
name_placeholder: eg. Gustav Plum
@@ -656,6 +662,8 @@ en_CA:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -843,14 +851,6 @@ en_CA:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -1148,12 +1148,17 @@ en_CA:
destroy_attachment_does_not_exist: "Logo does not exist"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Promo image does not exist"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "Terms and Conditions file does not exist"
orders:
failed_to_update: "Failed to update order"
checkout:
already_ordered:
cart: "cart"
message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open."
terms_and_conditions:
message_html: "By placing this order you agree to the %{terms_and_conditions_link}."
link_text: "Terms of Service"
failed: "The checkout failed. Please let us know so that we can process your order."
shops:
hubs:
@@ -2299,6 +2304,7 @@ en_CA:
enterprise_name_error: "has already been taken. If this is your enterprise and you would like to claim ownership, or if you would like to trade with this enterprise please contact the current manager of this profile at %{email}."
enterprise_owner_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})."
enterprise_role_uniqueness_error: "^That role is already present."
enterprise_terms_and_conditions_type_error: "Only PDFs are allowed"
inventory_item_visibility_error: must be true or false
product_importer_file_error: "error: no file uploaded"
product_importer_spreadsheet_error: "could not process file: invalid filetype"
@@ -2569,6 +2575,15 @@ en_CA:
error_saving: "Error saving subscription"
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
immediate_terms_and_conditions_removal_warning: "The Terms and Conditions file will be removed immediately after you confirm."
removed_terms_and_conditions_successfully: "Terms and Conditions file removed successfully"
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -113,6 +113,8 @@ en_DE:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -575,7 +577,6 @@ en_DE:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -634,6 +635,8 @@ en_DE:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -814,14 +817,6 @@ en_DE:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -2485,6 +2480,13 @@ en_DE:
subscriptions:
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -120,6 +120,10 @@ en_FR:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
current_text: Now
close_text: Done
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -597,7 +601,7 @@ en_FR:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
weight_volume: "Weight/Volume (g)"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -634,6 +638,8 @@ en_FR:
acn_placeholder: eg. 123 456 789
display_invoice_logo: Display logo in invoices
invoice_text: Add customized text at the end of invoices
terms_and_conditions: "Terms and Conditions"
remove_terms_and_conditions: "Remove File"
contact:
name: Name
name_placeholder: eg. Gustav Plum
@@ -656,6 +662,8 @@ en_FR:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -843,14 +851,6 @@ en_FR:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -1148,12 +1148,17 @@ en_FR:
destroy_attachment_does_not_exist: "Logo does not exist"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Promo image does not exist"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "Terms and Conditions file does not exist"
orders:
failed_to_update: "Failed to update order"
checkout:
already_ordered:
cart: "cart"
message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open."
terms_and_conditions:
message_html: "By placing this order you agree to the%{terms_and_conditions_link}."
link_text: "Terms of Service"
failed: "The checkout failed. Please let us know so that we can process your order."
shops:
hubs:
@@ -1243,9 +1248,9 @@ en_FR:
menu_3_url: "/producers"
menu_4_title: "Groups"
menu_4_url: "/groups"
menu_5_title: "About"
menu_5_title: "Open my shop"
menu_5_url: "https://apropos.coopcircuits.fr/"
menu_6_title: "Connect"
menu_6_title: "About"
menu_6_url: "https://apropos.coopcircuits.fr/blog/"
menu_7_title: "Learn"
menu_7_url: "https://apropos.coopcircuits.fr/ressources/"
@@ -2299,6 +2304,7 @@ en_FR:
enterprise_name_error: "has already been taken. If this is your enterprise and you would like to claim ownership, or if you would like to trade with this enterprise please contact the current manager of this profile at %{email}."
enterprise_owner_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})."
enterprise_role_uniqueness_error: "^That role is already present."
enterprise_terms_and_conditions_type_error: "Only PDFs are allowed"
inventory_item_visibility_error: must be true or false
product_importer_file_error: "error: no file uploaded"
product_importer_spreadsheet_error: "could not process file: invalid filetype"
@@ -2570,6 +2576,15 @@ en_FR:
error_saving: "Error saving subscription"
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
immediate_terms_and_conditions_removal_warning: "The Terms and Conditions file will be removed immediately after you confirm."
removed_terms_and_conditions_successfully: "Terms and Conditions file removed successfully"
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -120,6 +120,9 @@ en_GB:
integer_array_validator:
not_array_error: "must be an array"
invalid_element_error: "must contain only valid integers"
datetime_picker_ui:
close_text: Done
time_text: Time
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -597,7 +600,6 @@ en_GB:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -656,6 +658,8 @@ en_GB:
promo_image_note1: 'PLEASE NOTE:'
promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260.
promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups.
remove_logo: "Remove Image"
remove_promo_image: "Remove Image"
inventory_settings:
text1: You may opt to manage stock levels and prices in via your
inventory: inventory
@@ -843,14 +847,6 @@ en_GB:
new:
title: New Enterprise
back_link: Back to enterprises list
remove_logo:
remove: "Remove Image"
removed_successfully: "Logo removed successfully"
immediate_removal_warning: "The logo will be removed immediately after you confirm."
remove_promo_image:
remove: "Remove Image"
removed_successfully: "Promo image removed successfully"
immediate_removal_warning: "The promo image will be removed immediately after you confirm."
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -2576,6 +2572,13 @@ en_GB:
error_saving: "Error saving subscription"
new:
please_select_a_shop: "Please select a shop"
enterprises:
form:
images:
removed_logo_successfully: "Logo removed successfully"
immediate_logo_removal_warning: "The logo will be removed immediately after you confirm."
removed_promo_image_successfully: "Promo image removed successfully"
immediate_promo_image_removal_warning: "The promo image will be removed immediately after you confirm."
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

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