Compare commits

...

131 Commits

Author SHA1 Message Date
Luis Ramos
6806035a45 Update all locales with the latest Transifex translations 2020-10-08 10:15:26 +01:00
Luis Ramos
b1dbf4fe0e Merge pull request #6147 from openfoodfoundation/transifex
Transifex
2020-10-08 10:13:00 +01:00
Luis Ramos
0829eaf0fb Merge pull request #6148 from openfoodfoundation/luisramos0-patch-1
Add break line troubleshooting to docker guide
2020-10-08 09:24:27 +01:00
Luis Ramos
da2e3c7cd1 Merge pull request #6144 from arku/fix/adjustment-metadata-spec
Fix adjustment metadata spec
2020-10-08 08:59:52 +01:00
Luis Ramos
d5f793f451 Add break line troubleshooting to docker guide 2020-10-08 08:40:14 +01:00
Transifex-Openfoodnetwork
8269665a7c Updating translations for config/locales/en_NZ.yml 2020-10-08 16:53:14 +11:00
Luis Ramos
1e7a3d7f46 Merge pull request #6123 from openfoodfoundation/mkllnk-patch-1
Create an initial Code of Conduct placeholder file
2020-10-07 23:36:59 +01:00
Luis Ramos
6c95444339 Merge pull request #6020 from andresgutgon/fix/admin-menu-in-multiple-lines
Fixed admin header buttons dropping multiple lines.
2020-10-07 21:24:48 +01:00
Luis Ramos
48ea804615 Merge pull request #6121 from cillian/fix-shop-variant-search
When searching for shop products check the variant :display_name and :display_as fields too
2020-10-07 21:22:46 +01:00
Luis Ramos
8626e7b433 Merge pull request #6094 from andrewpbrett/fix-inventory-import
Fix #6090 (Inventory import fails for products with units other than "g")
2020-10-07 21:21:40 +01:00
Luis Ramos
88b17372af Merge pull request #6131 from Snick555/issue-6118
Show product description in creation form after errors appeared
2020-10-07 21:21:13 +01:00
Luis Ramos
961e559a31 Merge pull request #6083 from luisramos0/shop_tabs_style
Make shop tabs black and capitalized in smaller screens
2020-10-07 14:39:23 +01:00
Pau Pérez Fabregat
85cee9e8cb Merge pull request #6091 from luisramos0/vat_rates_cache
Delete dead code brought from spree
2020-10-07 11:58:26 +02:00
Pau Pérez Fabregat
1e6fa1666c Merge pull request #6113 from openfoodfoundation/dependabot/bundler/knapsack-1.19.0
Bump knapsack from 1.18.0 to 1.19.0
2020-10-07 11:57:12 +02:00
Pau Pérez Fabregat
ce856750aa Merge pull request #6116 from openfoodfoundation/dependabot/bundler/ddtrace-0.41.0
Bump ddtrace from 0.40.0 to 0.41.0
2020-10-07 11:56:45 +02:00
Pau Pérez Fabregat
07e625ec32 Merge pull request #6104 from openfoodfoundation/dependabot/bundler/haml-5.2.0
Bump haml from 5.1.2 to 5.2.0
2020-10-07 11:56:12 +02:00
Arun Kumar Mohan
a20762a1f2 Fix adjustment metadata spec 2020-10-06 19:02:59 -05:00
Andy Brett
0edd341d46 add unit test for inventory import entry_validation 2020-10-06 10:17:30 -07:00
Luis Ramos
c31380cd1d Merge pull request #6132 from Matt-Yorkley/stripe-multiple-payments-spec
Stripe Tests: multiple attempted payments
2020-10-06 17:17:25 +01:00
Matt-Yorkley
0c109d6384 Merge pull request #6103 from openfoodfoundation/dependabot/bundler/dalli-2.7.11
Bump dalli from 2.7.10 to 2.7.11
2020-10-06 11:15:37 +02:00
Pau Pérez Fabregat
427d1aaeff Merge pull request #6079 from arku/perf/test-prof-setup
Set up test_prof gem
2020-10-06 10:30:43 +02:00
Matt-Yorkley
3b130327a5 DRY error_message variable used multiple times 2020-10-05 00:51:01 +01:00
Matt-Yorkley
ced5329835 Add Stripe spec for multiple attempted payments at checkout 2020-10-05 00:50:59 +01:00
dependabot-preview[bot]
ee6f0f7951 Bump ddtrace from 0.40.0 to 0.41.0
Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.40.0 to 0.41.0.
- [Release notes](https://github.com/DataDog/dd-trace-rb/releases)
- [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.40.0...v0.41.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-04 19:18:47 +00:00
dependabot-preview[bot]
6149a79038 Bump knapsack from 1.18.0 to 1.19.0
Bumps [knapsack](https://github.com/ArturT/knapsack) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/ArturT/knapsack/releases)
- [Changelog](https://github.com/ArturT/knapsack/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ArturT/knapsack/compare/v1.18.0...v1.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-04 19:15:57 +00:00
ivoloshy
7321e4ef89 show product description in creation form after errors appeared 2020-10-04 20:44:57 +03:00
Matt-Yorkley
76def3a74f Merge pull request #6124 from luisramos0/stripe_specs
Add more checkout feature specs covering stripe SCA cases
2020-10-03 18:24:06 +02:00
Luis Ramos
a5309627b7 Fix a typo 2020-10-03 15:46:29 +01:00
Luis Ramos
80ea80f26e DRY stripe spec 2020-10-03 11:50:04 +01:00
Luis Ramos
14f5ecfe0b Add spec to cover StripeSCA extra auth with redirect and failed auth 2020-10-03 11:39:56 +01:00
Luis Ramos
df4ec67974 Add feature spec that covers stripe_sca redirect case 2020-10-03 11:09:24 +01:00
Maikel
4b1e1afa69 Create an initial Code of Conduct placeholder file 2020-10-03 08:48:15 +10:00
Luis Ramos
ef70c1fc5c Make helper more flexible and use it in a spec 2020-10-02 19:20:32 +01:00
Luis Ramos
83456f94e3 Simplify test by re-using helper stripe mock 2020-10-02 19:12:56 +01:00
Luis Ramos
ff9f374950 Merge pull request #6050 from Matt-Yorkley/stripe-checkout-spec
Stripe checkout spec
2020-10-02 18:57:22 +01:00
Cillian O'Ruanaidh
6c22ee43a7 When searching for shop products check the variant :display_name and :display_as fields too.
Fixes #5757
2020-10-02 16:05:48 +01:00
Luis Ramos
6a2e49b9e7 Update all locales with the latest Transifex translations 2020-10-02 12:54:45 +01:00
Luis Ramos
ab0ecfb203 Merge pull request #6106 from openfoodfoundation/transifex
Transifex
2020-10-02 12:46:56 +01:00
Luis Ramos
db2e760c4c Merge pull request #6120 from luisramos0/restart_checkout
Comment 2 broken specs until we manage to fix them
2020-10-02 12:39:04 +01:00
Luis Ramos
da82df39c2 Comment 2 broken specs until we manage to fix them 2020-10-02 11:59:38 +01:00
Luis Ramos
98fb60a5e0 Merge pull request #6110 from filipefurtad0/content_spec.rb
content_spec.rb improvement
2020-10-01 23:22:04 +01:00
Andy Brett
854916907e add feature spec for inventory import in kg 2020-10-01 13:27:57 -07:00
Matt-Yorkley
1cc950a4d9 DRY StripeHelper methods 2020-10-01 15:54:16 +01:00
Arun Kumar Mohan
4858f5fb80 Improve order specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
350a3c0e1e Improve order checkout specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
b850f10d2e Improve classification specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
362ab8a605 Improve variant override specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
f5985de802 Improve order cycle specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
6c27ac5f99 Improve filter shipping methods tag rule specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
bb38523767 Improve filter products tag rule specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
e109ed6be7 Improve filter payment methods tag rule specs' performance 2020-10-01 01:43:08 -05:00
Arun Kumar Mohan
780f31f98e Improve filter order cycles tag rule specs' performance 2020-10-01 01:43:05 -05:00
François Turbelin
eda904337b Merge pull request #5810 from pacodelaluna/improve-dfc-standard-integration
Improve DFC Provider engine to support version 1.3
2020-09-30 11:13:03 +02:00
filipefurtad0
a85e500bb1 moves content_spec.rb into /configuration and adds an href string, incresing Redcarpet test coverage 2020-09-29 22:39:05 +01:00
Transifex-Openfoodnetwork
8480e16cac Updating translations for config/locales/tr.yml 2020-09-29 22:33:21 +10:00
Transifex-Openfoodnetwork
2eeba0483c Updating translations for config/locales/tr.yml 2020-09-29 22:30:18 +10:00
Transifex-Openfoodnetwork
e2ae566e88 Updating translations for config/locales/pl.yml 2020-09-29 22:08:01 +10:00
Transifex-Openfoodnetwork
01c6d5bc9c Updating translations for config/locales/ru.yml 2020-09-29 20:47:26 +10:00
Transifex-Openfoodnetwork
856ad24c6f Updating translations for config/locales/ru.yml 2020-09-29 20:44:21 +10:00
dependabot-preview[bot]
678e7acde0 Bump haml from 5.1.2 to 5.2.0
Bumps [haml](https://github.com/haml/haml) from 5.1.2 to 5.2.0.
- [Release notes](https://github.com/haml/haml/releases)
- [Changelog](https://github.com/haml/haml/blob/main/CHANGELOG.md)
- [Commits](https://github.com/haml/haml/compare/v5.1.2...v5.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 09:32:15 +00:00
dependabot-preview[bot]
4914a0d3a8 Bump dalli from 2.7.10 to 2.7.11
Bumps [dalli](https://github.com/petergoldstein/dalli) from 2.7.10 to 2.7.11.
- [Release notes](https://github.com/petergoldstein/dalli/releases)
- [Changelog](https://github.com/petergoldstein/dalli/blob/master/History.md)
- [Commits](https://github.com/petergoldstein/dalli/compare/v2.7.10...v2.7.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 09:29:14 +00:00
Pau Pérez Fabregat
e79187c1df Merge pull request #6097 from openfoodfoundation/transifex
Transifex
2020-09-29 09:22:59 +02:00
Matt-Yorkley
c518e7e0dd Extract Stripe credit card form filling 2020-09-28 19:59:26 +01:00
Matt-Yorkley
2babfa9a7b Add rejected card test 2020-09-28 19:55:52 +01:00
Transifex-Openfoodnetwork
c7d6b2782c Updating translations for config/locales/nb.yml 2020-09-28 22:29:05 +10:00
Transifex-Openfoodnetwork
3d71c2c0b8 Updating translations for config/locales/en_GB.yml 2020-09-28 21:31:02 +10:00
Pau Pérez Fabregat
807f91b0b8 Merge pull request #6093 from openfoodfoundation/RachL-patch-1
Hacktoberfest
2020-09-28 12:04:55 +02:00
Arun Kumar Mohan
6a722f2bbd Set up test-prof gem 2020-09-26 15:22:43 -05:00
Luis Ramos
d5ab3b47eb Merge pull request #6065 from arku/perf/address-specs
Replace `create` with `build_stubbed` in address model specs
2020-09-26 21:19:28 +01:00
Luis Ramos
0100963a39 Delete dead code brought from spree 2020-09-26 21:16:27 +01:00
Arun Kumar Mohan
06a3e55348 Replace create with build_stubbed in address model specs 2020-09-25 22:54:58 -05:00
Luis Ramos
bbef0bac5f Make font size 1em for larger screens 2020-09-25 21:28:22 +01:00
Rachel Arnould
dbe6c316f8 Change typo
"participating in" instead of "participating to"
2020-09-25 19:07:33 +02:00
Rachel Arnould
21eef7187b Hacktoberfest
A proposal for the banner on Hacktoberfest.
2020-09-25 18:13:00 +02:00
Andy Brett
4f942db0ba convert entry's unscaled_units to a float
This was coming in as a string, e.g. "1", so when we did "1" * 1000 it returned "11111111111111....." :)
2020-09-25 09:12:19 -07:00
Luis Ramos
9b5bd7bb1c Merge pull request #6066 from arku/perf/variant-override-specs
Replace `create` with `build_stubbed` in variant override model specs
2020-09-24 18:47:08 +01:00
Luis Ramos
c377e316d5 Merge pull request #6068 from arku/perf/coordinator-specs
Replace `create` with `build_stubbed` in coordinator model specs
2020-09-24 18:46:45 +01:00
Luis Ramos
aa74b58810 Merge pull request #6067 from arku/perf/tag-rule-specs
Remove unnecessary create call in tag rule model specs
2020-09-24 18:45:18 +01:00
Luis Ramos
8919adb6bf Merge pull request #6074 from arku/perf/shipping-rate-specs
Replace `create` with `build_stubbed` in shipping rate model specs
2020-09-24 18:42:30 +01:00
Luis Ramos
e04b85e900 Merge pull request #6078 from arku/perf/per-item-flexi-rate-specs
Replace `build` with `build_stubbed` in flexi rate and per item model specs
2020-09-24 18:41:29 +01:00
Luis Ramos
b8fae8e3a3 Merge pull request #6077 from arku/perf/calculator-price-sack-specs
Replace `build` with `build_stubbed` in price sack model specs
2020-09-24 18:41:19 +01:00
Luis Ramos
01fdee9dba Merge pull request #6076 from arku/perf/calculator-flat-percent-item-total-specs
Replace `build` with `build_stubbed` in flat percent item total specs
2020-09-24 18:40:55 +01:00
Luis Ramos
dfd54c1cbc Merge pull request #6075 from arku/perf/calculator-weight-specs
Replace `create` with `build_stubbed` in calculator weight model specs
2020-09-24 18:40:31 +01:00
Luis Ramos
2234ea6f5a Make shop tabs black and capitalized 2020-09-22 16:59:42 +01:00
Arun Kumar Mohan
bc77b8bcb2 Replace build with build_stubbed in per item model specs 2020-09-21 21:24:38 -05:00
Arun Kumar Mohan
38ab95a9a4 Replace build with build_stubbed in flexi rate model specs 2020-09-21 21:22:59 -05:00
Arun Kumar Mohan
698d8b35fa Replace build with build_stubbed in price sack model specs 2020-09-21 21:18:36 -05:00
Arun Kumar Mohan
a257a9e9d2 Replace build with build_stubbed in calculator weight model specs 2020-09-21 21:12:30 -05:00
Arun Kumar Mohan
231fbcd11a Replace build with build_stubbed in flat percent item total specs 2020-09-21 21:04:16 -05:00
Arun Kumar Mohan
c098ba0ce3 Replace create with build_stubbed in calculator weight model specs 2020-09-21 20:46:50 -05:00
Arun Kumar Mohan
f7a9cc63a7 Replace create with build_stubbed in shipping rate model specs 2020-09-21 20:37:35 -05:00
Arun Kumar Mohan
317618595c Replace create with build_stubbed in coordinator model specs 2020-09-21 01:47:19 -05:00
Arun Kumar Mohan
289fd09e20 Remove unnecessary create call in tag rule model specs 2020-09-21 01:09:14 -05:00
Arun Kumar Mohan
239b6e7577 Replace create with build_stubbed in variant override model specs 2020-09-21 00:45:35 -05:00
Matt-Yorkley
5bca561c4a Refactor Stripe request stubbing to helper 2020-09-20 17:06:03 +01:00
Matt-Yorkley
6737270ab7 Rename partial 2020-09-19 15:49:14 +01:00
Matt-Yorkley
70bd2161ba Rename Stripe Connect factory for clarity 2020-09-19 12:55:31 +01:00
Matt-Yorkley
1d01fc955d Include StripeJS fixture and refactor to partial 2020-09-19 12:20:55 +01:00
Matt-Yorkley
4080e7daa4 Add comments in StripeJS mock 2020-09-19 12:20:55 +01:00
Matt-Yorkley
cca8f9faf6 Add missing behaviour in StripeJS mock 2020-09-19 12:20:55 +01:00
andresgutgon
ef85ac3e27 Fixed admin header buttons dropping multiple lines. 2020-09-19 11:37:09 +02:00
Matt-Yorkley
368772dad2 Add another missing method to StripeJS mock
Our Angular code calls this method in some tests and expects a Promise object with a hash describing a payment method.
2020-09-19 00:39:39 +01:00
Matt-Yorkley
857f4e3a37 Add missing method to StripeJS mock
Our Angular code calls this method for interacting with live form validations and messages. We don't really need to use in tests, it just needs to exist otherwise the specs fail.
2020-09-19 00:39:39 +01:00
Matt-Yorkley
67d136548f Update form elements to more accurately reflect actual Stripe form
These attributes more closely reflect the actual form injected by Stripe. The additional attributes are also useful for selecting elements in tests
2020-09-19 00:39:39 +01:00
Matt-Yorkley
4c77c41533 Remove unused postal field from form 2020-09-19 00:39:39 +01:00
Matt-Yorkley
49bd9bd778 Import StripeJS mock from fake_stripe gem
This is the original from https://github.com/thoughtbot/fake_stripe/blob/v0.3.0/lib/fake_stripe/assets/v3.js
2020-09-19 00:39:39 +01:00
Matt-Yorkley
0ba3977422 Add new Stripe checkout spec 2020-09-19 00:39:32 +01:00
Matt-Yorkley
4ed34bb942 Add javascript debug option to test suite 2020-09-18 21:20:47 +01:00
François Turbelin
52d82d0a96 Use not found instead of 404 for spec descriptions 2020-09-03 00:59:21 +02:00
François Turbelin
d11d67561f Remove extra delegation in serializers 2020-09-03 00:58:54 +02:00
François Turbelin
d2c147109d Use Persons instead of People 2020-09-03 00:56:42 +02:00
François Turbelin
b60c1c9003 Dry current_enterprise emtod with enterprise_id_param_name 2020-09-03 00:50:23 +02:00
François Turbelin
59fb2abc5d Fix inheritance issue with specs on Semaphore 2020-09-02 23:08:47 +02:00
François Turbelin
6508897e3d Introduce current_enterprise with memoization 2020-09-02 22:28:12 +02:00
François Turbelin
1baba5b61c Introduce current_user with memoization 2020-09-02 22:18:56 +02:00
François Turbelin
4f2b7094d0 Use before_action instead of before_filter 2020-09-02 21:48:13 +02:00
François Turbelin
d5800642e7 Map DFC SuppliedProduct with OFN Variant 2020-08-30 23:11:59 +02:00
François Turbelin
c3cf08156d Add People controller 2020-08-13 11:49:45 +02:00
François Turbelin
8d4587506b Add SuppliedProducts controller 2020-08-13 11:46:48 +02:00
François Turbelin
99e905c768 Simplify enterprise check logic 2020-08-13 11:02:31 +02:00
François Turbelin
46d38930d9 Add some ajustements 2020-08-12 20:53:02 +02:00
François Turbelin
a4e8982351 Rename ProductsController into CatalogItemsController 2020-08-12 19:51:12 +02:00
François Turbelin
13e15f823e Add Read action for Enterprise and CatalogItem 2020-08-12 19:43:20 +02:00
François Turbelin
508ecd6bf7 Adjust with Rubocop suggestions 2020-08-12 13:46:23 +02:00
François Turbelin
d10fda6227 Put context inside serializer 2020-08-12 13:37:48 +02:00
François Turbelin
2a8268ca73 Use ActiveModelSerializer for DFC serialization 2020-08-12 13:37:01 +02:00
François Turbelin
8687e0199d Apply cosmetics 2020-08-05 08:20:14 +02:00
François Turbelin
2549d454ab Maintain specs 2020-07-23 09:28:32 +02:00
François Turbelin
645b4a9505 Use multiple serializers 2020-07-22 22:45:50 +02:00
François Turbelin
9f02ee3874 Use new serialization 2020-07-22 21:08:25 +02:00
François Turbelin
21fc14a9fe Use a better products list for catalog 2020-07-22 21:08:25 +02:00
97 changed files with 8470 additions and 479 deletions

View File

@@ -20,6 +20,9 @@ plugins:
enabled: false
DeclarationOrder:
enabled: false
NestingDepth:
enabled: false
duplication:
enabled: true
exclude_patterns:

3
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,3 @@
# Code of Conduct of the Open Food Network
**Welcome!** We are currently working on a Code of Conduct. You are welcome to contribute. We started this work at the Global Gathering 2020 and you find [notes of the initial session](https://community.openfoodnetwork.org/t/global-gathering-2020-day-5-a-code-of-conduct-for-ofn/2071/1) in the community forum. You can edit this file on Github to suggest new content. Alternatively, you can post in our [community forum](https://community.openfoodnetwork.org) or reach us on [Slack](https://openfoodnetwork.org/slack-invite).

View File

@@ -40,7 +40,7 @@ Push your changes to a branch on your fork:
## Submitting a Pull Request
Use the GitHub UI to submit a [new pull request][pr] against upstream/master. To increase the chances that your pull request is swiftly accepted please have a look at our guide to [making a great pull request][great-pr].
Use the GitHub UI to submit a [new pull request][pr] against upstream/master. To increase the chances that your pull request is swiftly accepted please have a look at our guide to [making a great pull request][great-pr].
TL;DR:
* Write tests

View File

@@ -60,3 +60,6 @@ Check the app in the browser at `http://localhost:3000`.
You will then get the trace of the containers in the terminal. You can stop the containers using Ctrl-C in the terminal.
You can find some useful tips and commands [here](https://github.com/openfoodfoundation/openfoodnetwork/wiki/Docker:-useful-tips-and-commands).
### Troubleshooting
If you are using Windows and having issues related to the ruby-build not finding a definition for the ruby version, you may need to follow these commands [here](https://stackoverflow.com/questions/2517190/how-do-i-force-git-to-use-lf-instead-of-crlf-under-windows/33424884#33424884) to fix your local git config related to line breaks.

View File

@@ -156,6 +156,7 @@ end
group :test do
gem 'simplecov', require: false
gem 'test-prof'
gem 'webmock'
# See spec/spec_helper.rb for instructions
# gem 'perftools.rb'

View File

@@ -64,6 +64,7 @@ PATH
remote: engines/dfc_provider
specs:
dfc_provider (0.0.1)
active_model_serializers (~> 0.8.4)
jwt (~> 2.2)
rspec (~> 3.9)
@@ -198,13 +199,13 @@ GEM
css_parser (1.7.1)
addressable
daemons (1.3.1)
dalli (2.7.10)
dalli (2.7.11)
database_cleaner (1.8.5)
db2fog (0.9.0)
activerecord (>= 3.2.0, < 5.0)
fog (~> 1.0)
rails (>= 3.2.0, < 5.0)
ddtrace (0.40.0)
ddtrace (0.41.0)
msgpack
debugger-linecache (1.2.0)
delayed_job (4.1.8)
@@ -411,7 +412,7 @@ GEM
get_process_mem (0.2.5)
ffi (~> 1.0)
gmaps4rails (2.1.2)
haml (5.1.2)
haml (5.2.0)
temple (>= 0.8.0)
tilt
hashdiff (1.0.1)
@@ -444,7 +445,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.11.3)
knapsack (1.18.0)
knapsack (1.19.0)
rake
launchy (2.4.3)
addressable (~> 2.3)
@@ -660,6 +661,7 @@ GEM
stringex (1.5.1)
stripe (5.25.0)
temple (0.8.2)
test-prof (0.7.5)
test-unit (3.3.6)
power_assert
thor (0.20.3)
@@ -810,6 +812,7 @@ DEPENDENCIES
state_machine (= 1.2.0)
stringex (~> 1.5.1)
stripe
test-prof
test-unit (~> 3.3)
timecop
truncate_html (= 0.9.2)

View File

@@ -9,7 +9,7 @@ Supported by the Open Food Foundation and a network of global affiliates, we are
We're part of global movement - get involved!
* Join the conversation [on Slack][slack-invite]. Make sure you introduce yourself in the #general channel.
* Join the conversation [on Slack][slack-invite]. Make sure you introduce yourself in the #general channel and join #dev for all tech-related topics.
* Head to [https://openfoodnetwork.org](https://openfoodnetwork.org) for more information about the global OFN project.
* Check out the [User Guide](https://guide.openfoodnetwork.org/) for a list of features and tutorials.
* Join our [discussion forum](https://community.openfoodnetwork.org).
@@ -20,6 +20,11 @@ If you are interested in contributing to the OFN in any capacity, please introdu
Our [GETTING_STARTED](GETTING_STARTED.md) and [CONTRIBUTING](CONTRIBUTING.md) guides are the best place to start for developers looking to set up a development environment and make contributions to the codebase.
### Hacktoberfest :tada:
Are you participating in [Hacktoberfest](https://hacktoberfest.digitalocean.com/)? Go check out our [Welcome New Developers project board][welcome-dev]! We have curated all issues we consider to be a good starting point for new members of the community and categorized them by skills and level of complexity.
Have a look and pick the one you would prefer working on!
## Provisioning
If you're interested in provisioning a server, see [ofn-install][ofn-install] for the project's Ansible playbooks.
@@ -39,3 +44,4 @@ Copyright (c) 2012 - 2020 Open Food Foundation, released under the AGPL licence.
[contributor-guide]: https://ofn-user-guide.gitbook.io/ofn-contributor-guide/who-are-we
[ofn-install]: https://github.com/openfoodfoundation/ofn-install
[super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide
[welcome-dev]: https://github.com/openfoodfoundation/openfoodnetwork/projects/27

View File

@@ -68,7 +68,7 @@ Darkswarm.controller "ProductsCtrl", ($scope, $sce, $filter, $rootScope, Product
id: $scope.order_cycle.order_cycle_id,
page: page || $scope.page,
per_page: $scope.per_page,
'q[name_or_meta_keywords_or_supplier_name_cont]': $scope.query,
'q[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont]': $scope.query,
'q[properties_id_or_supplier_properties_id_in_any][]': $scope.activeProperties,
'q[primary_taxon_id_in_any][]': $scope.activeTaxons
}

View File

@@ -1,3 +1,5 @@
@import 'shared/variables/layout';
// -------------------------------------------------------------
// Variables used in all other files
//--------------------------------------------------------------

View File

@@ -8,23 +8,61 @@
box-sizing: border-box;
}
// Helpers
.block-table {
display: table;
width: 100%;
.admin {
&__section-header {
padding: 15px 0;
background-color: very-light($color-3, 4);
border-bottom: 1px solid $color-border;
.table-cell {
display: table-cell;
vertical-align: middle;
padding: 0 10px;
&:first-child {
padding-left: 0;
width: 70%;
.ofn-drop-down {
border: 0;
background-color: $spree-blue;
color: $color-1;
float: none;
margin-left: 3px;
&:hover,
&.expanded {
border: 0;
color: $color-1;
}
}
&:last-child {
padding-right: 0;
width: 30%;
&__content {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
@media all and (min-width: $tablet_breakpoint) {
flex-wrap: nowrap;
}
}
&__title {
width: 100%;
margin-bottom: 10px;
@media all and (min-width: $tablet_breakpoint) {
margin-bottom: 0;
}
}
&__actions {
display: flex;
flex: 1 0 auto;
align-items: center;
list-style: none;
@media all and (min-width: $tablet_breakpoint) {
justify-content: flex-end;
}
> li {
display: flex;
margin-right: 10px;
&:empty {
display: none;
}
&:last-child {
margin-right: 0;
}
}
}
}
}
@@ -66,25 +104,6 @@
margin-top: 15px;
}
#content-header {
padding: 15px 0;
background-color: very-light($color-3, 4);
border-bottom: 1px solid $color-border;
.page-title {
font-size: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page-actions {
text-align: right;
form {
display: inline-block;
}
}
}
// Footer
//---------------------------------------------------
#footer {

View File

@@ -1,4 +1,5 @@
// Note this mixin file is used in ADMIN and FRONTEND
@import 'shared/variables/layout';
@import "branding";
@@ -258,15 +259,15 @@
@mixin breakpoint($point) {
@if $point == desktop {
@media all and (max-width: 1024px) { @content; }
@media all and (max-width: $desktop_breakpoint) { @content; }
}
@else if $point == tablet {
@media all and (max-width: 768px) { @content; }
@media all and (max-width: $tablet_breakpoint) { @content; }
}
@else if $point == phablet {
@media all and (max-width: 640px) { @content; }
@media all and (max-width: $phablet_breakpoint) { @content; }
}
@else if $point == mobile {
@media all and (max-width: 480px) { @content; }
@media all and (max-width: $mobile_breakpoint) { @content; }
}
}

View File

@@ -34,7 +34,7 @@
>a {
outline: none;
display: block;
color: $grey-500;
color: $black;
font-family: "Oswald", sans-serif;
}
@@ -42,9 +42,9 @@
@include headingFont;
background: transparent;
text-transform: uppercase;
text-transform: capitalize;
line-height: 1;
font-size: 0.875em;
font-size: 1em;
padding: 1em 2em;
border: none;
@@ -58,6 +58,7 @@
@include breakpoint(phablet) {
padding: 0.35em 0 0.65em 0;
font-size: 0.875em;
}
}

View File

@@ -0,0 +1,6 @@
// Breakpoints
$desktop_breakpoint: 1024px;
$tablet_breakpoint: 768px;
$phablet_breakpoint: 640px;
$mobile_breakpoint: 480px;

View File

@@ -75,7 +75,7 @@ module Api
end
def permitted_ransack_params
[:name_or_meta_keywords_or_supplier_name_cont,
[:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont,
:properties_id_or_supplier_properties_id_in_any,
:primary_taxon_id_in_any]
end

View File

@@ -3,9 +3,6 @@ module Spree
class TaxRatesController < ResourceController
before_action :load_data
update.after :update_after
create.after :create_after
private
def load_data
@@ -14,14 +11,6 @@ module Spree
@calculators = TaxRate.calculators.sort_by(&:name)
end
def update_after
Rails.cache.delete('vat_rates')
end
def create_after
Rails.cache.delete('vat_rates')
end
def permitted_resource_params
params.require(:tax_rate).permit(
:name, :amount, :included_in_price, :zone_id,

View File

@@ -253,7 +253,7 @@ module ProductImport
products.flat_map(&:variants).each do |existing_variant|
unit_scale = existing_variant.product.variant_unit_scale
unscaled_units = entry.unscaled_units || 0
unscaled_units = entry.unscaled_units.to_f || 0
entry.unit_value = unscaled_units * unit_scale unless unit_scale.nil?
if entry_matches_existing_variant?(entry, existing_variant)

View File

@@ -1,5 +1,5 @@
%h1
=t'new_order_cycle'
- content_for :page_title do
=t('new_order_cycle')
- ng_controller = order_cycles_simple_form ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl'
= admin_inject_order_cycle_instance

View File

@@ -40,7 +40,8 @@
= render "layouts/bugsnag_js"
- if Spree::Config.stripe_connect_enabled
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
= render "shared/stripe_js"
- if !ContentConfig.open_street_map_enabled
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry#{ ENV['GOOGLE_MAPS_API_KEY'] ? '&key=' + ENV['GOOGLE_MAPS_API_KEY'] : ''} "}

View File

@@ -0,0 +1,5 @@
- if Rails.env.test?
%script{type: "text/javascript"}
= render file: "spec/support/fixtures/stripejs-mock.js"
- else
%script{src: "https://js.stripe.com/v3/", type: "text/javascript"}

View File

@@ -1,6 +1,7 @@
-# = render "spree/admin/payments/source_forms/gateway", payment_method: payment_method
.stripe
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
= render "shared/stripe_js"
- if Stripe.publishable_key
:javascript
angular.module('admin.payments').value("stripeObject", Stripe("#{Stripe.publishable_key}"))

View File

@@ -1,5 +1,6 @@
.stripe
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
= render "shared/stripe_js"
- if Stripe.publishable_key
:javascript
angular.module('admin.payments').value("stripeObject", Stripe("#{Stripe.publishable_key}"))

View File

@@ -78,6 +78,7 @@
= f.label :product_description, t(".product_description")
%br/
%text-angular{'id' => 'product_description', 'name' => 'product[description]', 'class' => 'text-angular', "textangular-links-target-blank" => true, 'ta-toolbar' => "[['bold','italics','underline','clear'],['insertLink']]"}
= sanitize(@product.description)
= f.error_message_on :description
.four.columns.omega{ style: "text-align: center" }
%fieldset.no-border-bottom{ id: "image" }

View File

@@ -9,4 +9,4 @@ $("#admin_new_product").parent().hide();
<%# We need to replace the page's title as well. We're navigating to a new page
although through ajax %>
$('#content-header .page-title').html('<%= t('.title') %>');
$('.js-admin-page-title').html('<%= t('.title') %>');

View File

@@ -1,7 +1,7 @@
= admin_inject_currency_config
= render "layouts/i18n_script"
#wrapper{"data-hook" => ""}
#wrapper{ data: { hook: '' } }
- if flash[:error]
.flash.error= flash[:error]
- if notice
@@ -18,30 +18,30 @@
%nav.columns.eleven{"data-hook" => "admin_login_navigation_bar"}
= render :partial => 'spree/layouts/admin/login_nav'
%nav#admin-menu{"data-hook" => ""}
%nav#admin-menu{ data: { hook: '' }}
.container
.sixteen.columns.main-menu-wrapper
%ul.inline-menu.fullwidth-menu{"data-hook" => "admin_tabs"}
= render :partial => 'spree/admin/shared/tabs'
- if content_for?(:sub_menu)
%nav#sub-menu{"data-hook" => ""}
%nav#sub-menu{ data: { hook: ''} }
.container
.sixteen.columns
= yield :sub_menu
- if content_for?(:page_title) || content_for?(:page_actions)
#content-header{"data-hook" => ""}
.js-admin-section-header.admin__section-header{ data: { hook: '' } }
.container
.sixteen.columns
.block-table
.admin__section-header__content
- if content_for?(:page_title)
.table-cell
%h1{:class => "page-title"}= yield :page_title
.admin__section-header__title
%h1.js-admin-page-title= yield :page_title
- if content_for?(:page_actions)
.page-actions.table-cell.toolbar{"data-hook" => "toolbar"}
%ul.inline-menu
= yield :page_actions
%ul.admin__section-header__actions{ data: { hook: 'toolbar' } }
= yield :page_actions
.container
.row

View File

@@ -1,5 +1,5 @@
ar:
language_name: "العربي"
language_name: "عربي"
activerecord:
attributes:
enterprise_fee:
@@ -53,7 +53,7 @@ ar:
order_management/reports/enterprise_fee_summary/parameters:
start_at: "البداية "
end_at: "النهاية"
distributor_ids: "مراكز "
distributor_ids: "مراكز بيع"
producer_ids: "المنتجين"
order_cycle_ids: "دورات الطلب"
enterprise_fee_ids: "أسماء الرسوم"
@@ -131,8 +131,8 @@ ar:
subject: "%{enterprise} الآن على %{sitename}"
email_welcome: "أهلا بك"
email_registered: "الآن هو جزء من"
email_userguide_html: "دليل المستخدم مع دعم مفصل لإعداد المنتج أو المحور الخاص بك هنا: %{link}"
userguide: "افتح دليل مستخدم شبكة الغذاء"
email_userguide_html: "دليل المستخدم مع دعم مفصل لإعداد واجهة المنتج أو مركز البيع الخاص بك هنا: %{link}"
userguide: "افتح دليل مستخدم شبكة الغذاء المفتوح الاردن - فلاحة جو"
email_admin_html: "يمكنك إدارة حسابك عن طريق تسجيل الدخول إلى %{link} أو عن طريق النقر على cog في أعلى الجانب الأيمن من الصفحة الرئيسية ، واختيار الإدارة."
admin_panel: "لوحة الادارة"
email_community_html: "لدينا أيضًا منتدى عبر الإنترنت لمناقشات المجتمع المتعلقة ببرامج بشبكة الغذاء المفتوح والتحديات الفريدة لإدارة مؤسسة للأغذية. نحن نشجعك على الانضمام. نحن نتطور باستمرار وسوف تشكل مدخلاتك في هذا المنتدى ما سيحدث بعد ذلك. %{link}"
@@ -412,7 +412,7 @@ ar:
header: الترويس
home_page: الصفحة الرئيسية
producer_signup_page: صفحة تسجيل المنتج
hub_signup_page: 'صفحة تسجيل المتجر '
hub_signup_page: 'صفحة تسجيل مركز البيع '
group_signup_page: صفحة تسجيل المجموعة
main_links: روابط القائمة الرئيسية
footer_and_external_links: تذييل والروابط الخارجية
@@ -819,13 +819,13 @@ ar:
sell_your_produce: بيع المنتجات الخاصة بك
producer_shop_description_text: بيع منتجاتك مباشرة للعملاء من خلال واجهة متجر شبكة الغذاء المفتوح الخاصة بك.
producer_shop_description_text2: متجر المنتج مخصص لمنتجاتك فقط ، إذا كنت ترغب في بيع المنتجات المنتجة خارج الموقع ، فحدد &quot;متجر المنتج&quot;.
producer_hub: نقطة بيع منتج
producer_hub: مركز بيع المنتج
producer_hub_text: بيع المنتجات من انتاجك و انتاج الآخرين
producer_hub_description_text: مؤسستك هي العمود الفقري لنظام الغذاء المحلي. يمكنك بيع المنتجات الخاصة بك وكذلك المنتجات المجمعة من المؤسسات الأخرى من خلال واجهة المحل الخاصة بك على شبكة الغذاء المفتوح.
profile: الملف الشخصي فقط
get_listing: الحصول على قائمة
profile_description_text: يمكن للأشخاص العثور على شبكة الغذاء المفتوح والاتصال بك. ستكون مؤسستك مرئية على الخريطة ، وستكون قابلة للبحث في القوائم.
hub_shop: نقطة بيع متجر
hub_shop: 'متجر لمركز بيع '
hub_shop_text: بيع المنتجات من الآخرين
hub_shop_description_text: مؤسستك هي العمود الفقري لنظام الغذاء المحلي. يمكنك تجميع المنتجات من المؤسسات الأخرى ويمكنك بيعها من خلال متجرك على شبكة الغذاء المفتوح.
choose_option: يرجى اختيار واحد من الخيارات المذكورة أعلاه.
@@ -851,7 +851,7 @@ ar:
title: شركة جديدة
back_link: العودة إلى قائمة المؤسسات
welcome:
welcome_title: مرحبا بكم في شبكة الغذاء المفتوح!
welcome_title: مرحبا بكم في شبكة الغذاء المفتوح الاردن - فلاحة جو !
welcome_text: لقد نجحت في إنشاء
next_step: الخطوة التالية
choose_starting_point: 'اختر الحزمة الخاصة بك:'
@@ -1181,7 +1181,7 @@ ar:
mobile_menu:
cart: "سلة"
register_call:
selling_on_ofn: "هل أنت مهتم بالحصول على شبكة الغذاء المفتوح؟"
selling_on_ofn: "هل أنت مهتم بالبيع من خلال منصة فلاحة جو؟ "
register: "سجل هنا"
footer:
footer_secure: "آمن وموثوق به."
@@ -1239,7 +1239,7 @@ ar:
ticket_column_item: "بند"
ticket_column_unit_price: "سعر الوحدة"
ticket_column_total_price: "السعر الكلي"
menu_1_title: حلات"
menu_1_title: "متاجر"
menu_1_url: "/shops"
menu_2_title: "خريطة"
menu_2_url: "/map"
@@ -1297,7 +1297,7 @@ ar:
days: أيام
authorization_failure: "فشل التفويض"
label_shop: "متجر"
label_shops: حلات"
label_shops: "متاجر"
label_map: "خريطة"
label_producer: "المنتج"
label_producers: "المنتجين"
@@ -1359,7 +1359,7 @@ ar:
cookie_domain: "تم ظبطها من قبل:"
cookie_session_desc: "يستخدم للسماح لموقع الويب بتذكر المستخدمين بين زيارات الصفحات ، على سبيل المثال ، تذكر العناصر الموجودة في سلة التسوق الخاصة بك."
cookie_consent_desc: "يستخدم للحفاظ على حالة موافقة المستخدم لتخزين ملفات تعريف الارتباط"
cookie_remember_me_desc: "يستخدم إذا طلب المستخدم من الموقع أن يتذكره. يتم حذف ملف تعريف الارتباط هذا تلقائيًا بعد 12 يومًا. إذا كنت تريد حذف ملف تعريف الارتباط هذا كمستخدم ، فأنت تحتاج فقط إلى تسجيل الخروج. إذا كنت لا ترغب في تثبيت ملف تعريف الارتباط هذا على جهاز الكمبيوتر الخاص بك ، فلا يجب عليك تحديد مربع الاختيار &quot;تذكرني&quot; عند تسجيل الدخول."
cookie_remember_me_desc: "يستخدم إذا طلب المستخدم من الموقع أن يتذكره. يتم حذف ملف تعريف الارتباط هذا تلقائيًا بعد 12 يومًا. إذا كنت تريد حذف ملف تعريف الارتباط هذا كمستخدم ، فأنت تحتاج فقط إلى تسجيل الخروج. إذا كنت لا ترغب في تثبيت ملف تعريف الارتباط هذا على جهاز الكمبيوتر الخاص بك ، فلا يجب عليك تحديد مربع الاختيار \"تذكرني\" عند تسجيل الدخول."
cookie_openstreemap_desc: "تستخدم من قبل موفر خرائط مفتوح المصدر (OpenStreetMap) للتأكد من أنه لا يتلقى الكثير من الطلبات خلال فترة زمنية معينة ، لمنع إساءة استخدام خدماتهم."
cookie_stripe_desc: "البيانات التي يتم جمعها بواسطة معالج الدفع لدينا Stripe للكشف عن الاحتيال https://stripe.com/cookies-policy/legal. لا تستخدم جميع المتاجر Stripe كوسيلة للدفع ، ولكن من الممارسات الجيدة منع الاحتيال لتطبيقه على جميع الصفحات. من المحتمل أن يقوم Stripe بإنشاء صورة لصفحاتنا التي تتفاعل عادةً مع واجهة برمجة التطبيقات الخاصة بهم ومن ثم وضع علامة على أي شيء غير عادي. لذلك فإن إعداد ملف تعريف الارتباط للشريط له وظيفة أوسع من مجرد توفير طريقة الدفع للمستخدم. يمكن أن تؤثر إزالتها على أمان الخدمة نفسها. يمكنك معرفة المزيد حول الشريط وقراءة سياسة الخصوصية الخاصة به على https://stripe.com/privacy."
statistics_cookies: "إحصائيات ملفات تعريف الارتباط"
@@ -1407,7 +1407,7 @@ ar:
cta_label: "أنا مستعد"
stats_headline: "نحن بصدد إنشاء نظام غذائي جديد."
stats_producers: "منتجي المواد الغذائية"
stats_shops: حلات المواد الغذائية"
stats_shops: "متاجر المواد الغذائية"
stats_shoppers: "المتسوقين الغذاء"
stats_orders: "طلبات الغذاء"
checkout_title: تابع للخروج
@@ -1449,7 +1449,7 @@ ar:
order_total_price: مجموع
order_includes_tax: (يشمل الضريبة)
order_payment_paypal_successful: تمت معالجة دفعتك عبر PayPal بنجاح.
order_hub_info: معلومات نقطة البيع
order_hub_info: معلومات مركز البيع
order_back_to_store: عودة للمخزن
order_back_to_cart: العودة إلى السلة
bom_tip: "استخدم هذه الصفحة لتغيير كميات المنتج عبر طلبات متعددة. يمكن أيضًا إزالة المنتجات من الطلبات تمامًا ، إذا لزم الأمر."
@@ -1669,22 +1669,22 @@ ar:
register_title: تسجيل
sell_title: "تسجيل "
sell_headline: "الحصول على شبكة الغذاء المفتوح!"
sell_motivation: "اعرض طعامك الجميل."
sell_motivation: "اعرض منتجاتك الرائعة."
sell_producers: "المنتجين"
sell_hubs: نافذ"
sell_hubs: راكز البيع"
sell_groups: "مجموعات"
sell_producers_detail: "قم بإعداد ملف تعريف لعملك على شكبة الغذاء المفتوح في دقائق معدودة. في أي وقت يمكنك ترقية ملف التعريف الخاص بك إلى متجر على الإنترنت وبيع منتجاتك مباشرة للعملاء."
sell_hubs_detail: "قم بإنشاء ملف تعريف للشركة طعام أو المؤسسة الخاصة بك على شبكة الغذاء المفتوح. في أي وقت يمكنك ترقية ملف التعريف الخاص بك إلى متجر متعدد المنتجين."
sell_groups_detail: "قم بإعداد دليل مخصص للمؤسسات (المنتجين والمؤسسات الغذائية الأخرى) لمنطقتك أو لمؤسستك."
sell_user_guide: "اكتشف المزيد في دليل المستخدم الخاص بنا."
sell_listing_price: "الإدراج في شبكة الغذاء المفتوح مجاني. فتح وتشغيل متجر على شبكة الغذاء المفتوح مجاني يصل إلى 500 دولار من المبيعات الشهرية. إذا كنت تبيع أكثر ، يمكنك اختيار مساهمة مجتمعك بين 1 ٪ و 3 ٪ من المبيعات. لمزيد من التفاصيل حول الأسعار ، تفضل بزيارة قسم Software Platform من خلال الرابط &quot;حول&quot; في القائمة العلوية."
sell_embed: "يمكننا أيضًا تضمين متجر شبكة الغذاء المفتوح في موقع الويب المخصص الخاص بك أو إنشاء موقع ويب مخصص لشبكة الأغذية المحلية لمنطقتك."
sell_listing_price: "ادراج منتجاتك في منصة فلاحة - شبكة الغذاء المفتوح مجاني. قم بفتح وتشغيل متجرك على منصة فلاحة - شبكة الغذاء المفتوح بشكل مجاني إلى ان تصل مبيعاتك الشهرية الى 500 دينار . إذا كنت تبيع أكثر ، فيمكنك اختيار مساهمة مجتمعك بين 1 ٪ و 3 ٪ من المبيعات. لمزيد من التفاصيل حول الأسعار ، تفضل بزيارة قسم Software Platform من خلال الرابط \"حول\" في القائمة العلوية."
sell_embed: "يمكننا أيضًا تضمين متجرك على منصة فلاحة - شبكة الغذاء المفتوح في موقع الويب الخاص بك أو إنشاء موقع ويب مخصص لشبكة الأغذية المحلية لمنطقتك."
sell_ask_services: "اسألنا عن خدمات شبكة الغذاء المفتوح."
shops_title: محلات
shops_headline: التسوق ، التحول.
shops_text: ينمو الغذاء في دورات ، ويحصد المزارعون في دورات ، ونحن نطلب الطعام في دورات. إذا وجدت دورة طلب مغلقة ، فراجعها قريبًا.
shops_signup_title: الاشتراك كنقطة بيع
shops_signup_headline: مراكز غذاء ، غير محدود.
shops_signup_title: الاشتراك كمركز بيع
shops_signup_headline: مراكز بيع الاغذية ، غير محدود.
shops_signup_motivation: مهما كان النموذج الخاص بك ، نحن ندعمك. ومع ذلك قمت بتغيير ، نحن معك. نحن غير ربحيين ومستقلين ومنفتحين. نحن شركاء البرنامج الذين حلمت بهم.
shops_signup_action: انظم الان
shops_signup_pricing: حسابات الشركات
@@ -2025,7 +2025,7 @@ ar:
loading_customers: "تحميل العملاء"
no_customers_found: "لم يتم العثور على العملاء"
go: "اذهب"
hub: "نقطة بيع"
hub: "مركز بيع"
producer: "المنتج"
product: "المنتج"
price: "السعر"
@@ -2042,7 +2042,7 @@ ar:
spree_admin_enterprises_fees: "رسوم الشركة"
spree_admin_enterprises_none_create_a_new_enterprise: "إنشاء مؤسسة جديدة"
spree_admin_enterprises_none_text: "ليس لديك أي مؤسسات حتى الآن"
spree_admin_enterprises_tabs_hubs: "نقاط بيع"
spree_admin_enterprises_tabs_hubs: "مراكز بيع"
spree_admin_enterprises_producers_manage_products: "إدارة المنتجات"
spree_admin_enterprises_create_new_product: "إنشاء منتج جديد"
spree_admin_single_enterprise_alert_mail_confirmation: "يرجى تأكيد عنوان البريد الإلكتروني ل"
@@ -2099,7 +2099,7 @@ ar:
admin_share_zipcode: "الرمز البريدي"
admin_share_country: "بلد"
admin_share_state: "ولاية"
hub_sidebar_hubs: نافذ"
hub_sidebar_hubs: راكز بيع"
hub_sidebar_none_available: "لا شيء متاح"
hub_sidebar_manage: "يدير"
hub_sidebar_at_least: "يجب تحديد مركز واحد على الأقل"
@@ -2113,7 +2113,7 @@ ar:
report_customers_csv: "تنزيل بتنسيق CSV"
report_producers: "المنتجين:"
report_type: "نوع التقرير:"
report_hubs: "المراكز:"
report_hubs: "مراكز البيع :"
report_payment: "طرق الدفع:"
report_distributor: "موزع:"
report_payment_by: 'المدفوعات حسب النوع'
@@ -2137,10 +2137,10 @@ ar:
report_header_address: عنوان
report_header_billing_address: عنوان وصول الفواتير
report_header_relationship: العلاقة
report_header_hub: نقطة بيع
report_header_hub_address: عنوان المركز
report_header_to_hub: إلى المركز
report_header_hub_code: رمز المركز
report_header_hub: مركز بيع
report_header_hub_address: عنوان مركز البيع
report_header_to_hub: 'إلى مركز البيع '
report_header_hub_code: رمز مركز البيع
report_header_code: الشفرة
report_header_paid: دفع؟
report_header_delivery: توصيل؟
@@ -2363,7 +2363,7 @@ ar:
error: خطأ
unavailable: غير متوفره
profile: الملف الشخصي
hub: نقطة بيع
hub: مركز بيع
shop: متجر
choose: أختر
resolve_errors: يرجى حل الأخطاء التالية
@@ -2400,7 +2400,7 @@ ar:
saved: تم الحفظ
saving: حفظ
enterprise_package:
hub_profile: الملف الشخصي للمركز
hub_profile: الملف الشخصي لمركز البيع
hub_profile_cost: "التكلفة: دائما مجانا"
hub_profile_text1: >
يمكن للأشخاص العثور على شبكة الغذاء المفتوح والاتصال بك. ستكون مؤسستك
@@ -2408,7 +2408,7 @@ ar:
hub_profile_text2: >
سيكون لديك دائمًا ملف تعريف ، وإجراء اتصالات داخل نظام الأغذية المحلي
الخاص بك من خلال شبكة الغذاء المفتوح.
hub_shop: نقطة بيع متجر
hub_shop: متجر مركز بيع
hub_shop_text1: >
مؤسستك هي العمود الفقري لنظام الغذاء المحلي. يمكنك تجميع المنتجات من
المؤسسات الأخرى ويمكنك بيعها من خلال متجرك على شبكة الغذاء المفتوح.
@@ -2442,8 +2442,8 @@ ar:
بك.
producer_shop_text2: >
متجر المنتج مخصص لمنتجاتك فقط ، إذا كنت ترغب في بيع المنتجات التي يتم
إنتاجها / إنتاجها خارج الموقع ، فيرجى تحديد &quot;مركز المنتج&quot;.
producer_hub: نقطة بيع منتج
إنتاجها / إنتاجها خارج الموقع ، فيرجى تحديد "مركز المنتج".
producer_hub: مركز بيع منتج
producer_hub_text1: >
مؤسستك هي العمود الفقري لنظام الغذاء المحلي. يمكنك بيع المنتجات الخاصة
بك وكذلك المنتجات المجمعة من المؤسسات الأخرى من خلال واجهة المحل الخاصة
@@ -2654,7 +2654,7 @@ ar:
users:
order: "طلب"
registration:
welcome_to_ofn: "مرحبا بكم في شبكة الغذاء المفتوح!"
welcome_to_ofn: "مرحبا بكم في شبكة الغذاء المفتوح الاردن - فلاحة جو!"
signup_or_login: "البدء بالتسجيل (أو تسجيل الدخول)"
have_an_account: "هل لديك حساب؟"
action_login: "تسجيل الدخول الآن."
@@ -3168,7 +3168,7 @@ ar:
product: "المنتج"
quantity_shipped: "الكمية التي تم شحنها"
quantity_returned: "الكمية المرتجعة"
return_quantity: ودة الكمية"
return_quantity: "ارجاع الكمية"
amount: "القيمة"
rma_value: "قيمة RMA"
reason: "السبب"

View File

@@ -1214,8 +1214,8 @@ en_AU:
menu_4_url: "/groups"
menu_5_title: "About"
menu_5_url: "https://about.openfoodnetwork.org.au/"
menu_6_title: "Connect"
menu_6_url: "https://openfoodnetwork.org/au/connect/"
menu_6_title: "Shop in Vic"
menu_6_url: "https://about.openfoodnetwork.org.au/buy-food-online-from-local-farmers-in-victoria/"
menu_7_title: "Learn"
menu_7_url: "https://openfoodnetwork.org/au/learn/"
logo: "Logo (640x130)"

View File

@@ -121,6 +121,7 @@ en_GB:
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:
@@ -600,6 +601,7 @@ 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"
@@ -636,6 +638,8 @@ en_GB:
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. Amanda Plum
@@ -1144,12 +1148,17 @@ en_GB:
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:
@@ -2295,6 +2304,7 @@ en_GB:
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"
@@ -2579,6 +2589,8 @@ en_GB:
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 has been removed successfully"
insufficient_stock: "Insufficient stock available, only %{on_hand} remaining"
out_of_stock:
reduced_stock_available: Reduced stock available

View File

@@ -121,6 +121,7 @@ en_NZ:
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:
@@ -600,6 +601,7 @@ en_NZ:
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume (g)"
ask: "Ask?"
page_title: "Bulk Order Management"
actions_delete: "Delete Selected"
@@ -636,6 +638,8 @@ en_NZ:
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
@@ -1144,12 +1148,17 @@ en_NZ:
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:
@@ -2295,6 +2304,7 @@ en_NZ:
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"
@@ -2573,6 +2583,8 @@ en_NZ:
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
@@ -2646,6 +2658,11 @@ en_NZ:
signup_or_login: "Start By Signing Up (or logging in)"
have_an_account: "Already have an account?"
action_login: "Log in now."
stripe_elements:
unknown_error_from_stripe: |
There was a problem setting up your card in our payments gateway.
Please refresh the page and try again, if it fails a second time,
please contact us for support.
inflections:
each:
one: "each"

View File

@@ -121,6 +121,7 @@ en_US:
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:
@@ -1240,7 +1241,7 @@ en_US:
menu_4_title: "Groups"
menu_4_url: "/groups"
menu_5_title: "About"
menu_5_url: "https://www.openfoodnetwork.org/about-us/"
menu_5_url: "https://about.openfoodnetwork.net"
menu_6_title: "Connect"
menu_6_url: "https://openfoodnetwork.org/au/connect/"
menu_7_title: "Learn"

View File

@@ -1251,7 +1251,7 @@ fr:
menu_4_title: "Groupes"
menu_4_url: "/groups"
menu_5_title: "Créer ma boutique"
menu_5_url: "https://openfoodfrance.org/sell"
menu_5_url: "/sell"
menu_6_title: "A propos"
menu_6_url: "https://apropos.coopcircuits.fr/"
menu_7_title: "Support"

View File

@@ -121,6 +121,7 @@ nb:
not_array_error: "må være en tabell"
invalid_element_error: "må kun inneholde gyldige heltall"
datetime_picker_ui:
current_text:
close_text: Ferdig
time_text: Tid
enterprise_mailer:
@@ -600,6 +601,7 @@ nb:
order_date: "Fullført på"
max: "Max"
product_unit: "Produkt: Enhet"
weight_volume: "Vekt/Volum (g)"
ask: "Spør?"
page_title: "Bulk ordrehåndtering"
actions_delete: "Slett Valgte"
@@ -636,6 +638,8 @@ nb:
acn_placeholder: f.eks. 999 000 123
display_invoice_logo: Vis logo i fakturaer
invoice_text: Legg til tilpasset tekst på slutten av fakturaer
terms_and_conditions: "Vilkår og Betingelser"
remove_terms_and_conditions: "Fjern Fil"
contact:
name: Navn
name_placeholder: f.eks. Gustav Plum
@@ -1144,12 +1148,17 @@ nb:
destroy_attachment_does_not_exist: "Logo eksisterer ikke"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Promo-bilde eksisterer ikke"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "Vilkår og Betingelser-filen eksisterer ikke"
orders:
failed_to_update: "Kunne ikke oppdatere bestillingen"
checkout:
already_ordered:
cart: "handlekurv"
message_html: "Du har allerede en bestilling for denne bestillingsrunden. Sjekk %{cart}en for å se varene du bestilte før. Du kan også avbryte varer så lenge bestillingsrunden er åpen."
terms_and_conditions:
message_html: "Ved å gjøre denne bestillingen godtar du %{terms_and_conditions_link}."
link_text: "Vilkår for bruk"
failed: "Utsjekk fra kassen mislyktes. Gi oss beskjed slik at vi kan behandle bestillingen din."
shops:
hubs:
@@ -2295,6 +2304,7 @@ nb:
enterprise_name_error: "har allerede blitt tatt. Hvis dette er din bedrift og du ønsker å kreve eierskap, eller hvis du ønsker å handle med denne bedriften, vennligst kontakt gjeldende administrator av denne profilen på %{email}."
enterprise_owner_error: "^ %{email} kan ikke eie flere bedrifter (grense er %{enterprise_limit})."
enterprise_role_uniqueness_error: "^Den rollen finnes allerede."
enterprise_terms_and_conditions_type_error: "Kun PDF-filer er tillatt"
inventory_item_visibility_error: må være true eller false
product_importer_file_error: "feil: ingen fil lastet opp"
product_importer_spreadsheet_error: "kunne ikke behandle filen: ugyldig filtype"
@@ -2573,6 +2583,8 @@ nb:
immediate_logo_removal_warning: "Logoen vil bli fjernet umiddelbart etter at du har bekreftet."
removed_promo_image_successfully: "Promobilde fjernet vellykket"
immediate_promo_image_removal_warning: "Promo-bildet vil bli fjernet umiddelbart etter at du har bekreftet."
immediate_terms_and_conditions_removal_warning: "Vilkår og Betingelser-filen vil bli fjernet umiddelbart etter at du har bekreftet."
removed_terms_and_conditions_successfully: "Vilkår og Betingleser-filen ble fjernet"
insufficient_stock: "Utilstrekkelig lager tilgjengelig, kun %{on_hand} gjenværende"
out_of_stock:
reduced_stock_available: Redusert lager tilgjengelig

3563
config/locales/pl.yml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -121,8 +121,9 @@ tr:
not_array_error: "Sıralı olmalı"
invalid_element_error: "Yalnızca geçerli tam sayılar içermelidir"
datetime_picker_ui:
current_text: Şimdi
close_text: Tamam
time_text: zaman
time_text: Zaman
enterprise_mailer:
confirmation_instructions:
subject: "Lütfen %{enterprise} için e-posta adresini doğrulayın"
@@ -600,6 +601,7 @@ tr:
order_date: "Tamamlanma Tarihi:"
max: "En Fazla"
product_unit: "Ürün: Birim"
weight_volume: "Ağırlık / Hacim "
ask: "Sor?"
page_title: "TOPLU SİPARİŞ YÖNETİMİ"
actions_delete: "Seçilenleri Sil"
@@ -636,6 +638,8 @@ tr:
acn_placeholder: Örn. 0530 123 45 67
display_invoice_logo: Faturalarda logoyu göster
invoice_text: Faturaların sonuna özelleştirilmiş metin ekle
terms_and_conditions: "Şartlar ve Koşullar"
remove_terms_and_conditions: "Dosyayı Kaldır"
contact:
name: Ad
name_placeholder: Örn. Toprak Güneş
@@ -726,6 +730,8 @@ tr:
enable_subscriptions_tip: "Üyelik işlevselliği etkinleştirilsin mi?"
enable_subscriptions_false: "Kapalı"
enable_subscriptions_true: "Etkin"
customer_names_in_reports: "Raporlardaki Müşteri İsimleri"
customer_names_tip: "Tedarikçilerinizin raporlardaki müşteri isimlerini görmesine izin verin"
customer_names_false: "Kapalı"
customer_names_true: "Etkin"
shopfront_message: "DÜKKAN MESAJINIZ"
@@ -1142,12 +1148,17 @@ tr:
destroy_attachment_does_not_exist: "Logo mevcut değil"
enterprise_promo_image:
destroy_attachment_does_not_exist: "Kapak resmi mevcut değil"
enterprise_terms_and_conditions:
destroy_attachment_does_not_exist: "Şartlar ve Koşullar dosyası mevcut değil"
orders:
failed_to_update: "Sipariş güncellenemedi"
checkout:
already_ordered:
cart: "Sepet"
message_html: "Bu sipariş dönemi için zaten bir siparişiniz var. Daha önce sipariş ettiğiniz ürünleri görmek için %{cart} adresini kontrol edin. Sipariş dönemi açık olduğu sürece siparişlerinizde değişiklik yapabilirsiniz."
terms_and_conditions:
message_html: "Bu siparişi vererek %{terms_and_conditions_link} 'ı onaylamış oluyorsunuz."
link_text: "Üyelik Sözleşmesi"
failed: "Ödeme başarısız oldu. Siparişinizi işleme koyabilmemiz için lütfen bizimle iletişime geçin."
shops:
hubs:
@@ -2293,6 +2304,7 @@ tr:
enterprise_name_error: "çoktan alındı. İşletme size aitse ve sahiplik talebinde bulunmak istiyorsanız veya bu işletmeyle ticaret yapmak istiyorsanız lütfen %{email} üzerinden bu profilin şu anki yöneticisiyle iletişime geçin."
enterprise_owner_error: "^ %{email}ın daha fazla işletmeye sahip olmasına izin verilmiyor (limit %{enterprise_limit})."
enterprise_role_uniqueness_error: "^ Bu rol zaten var."
enterprise_terms_and_conditions_type_error: "Sadece PDF dosyalarına izin verilir"
inventory_item_visibility_error: doğru veya yanlış olmalı
product_importer_file_error: "hata: hiçbir dosya yüklenmedi"
product_importer_spreadsheet_error: "dosya işlenemedi: geçersiz dosya tipi"
@@ -2573,6 +2585,8 @@ tr:
immediate_logo_removal_warning: "Onayladıktan hemen sonra logo kaldırılacaktır."
removed_promo_image_successfully: "Kapak resmi başarıyla kaldırıldı"
immediate_promo_image_removal_warning: "Kapak resmi, onayladıktan hemen sonra kaldırılacaktır."
immediate_terms_and_conditions_removal_warning: "Şartlar ve Koşullar dosyası, onayladıktan hemen sonra kaldırılacaktır."
removed_terms_and_conditions_successfully: "Şartlar ve Koşullar dosyası başarıyla kaldırıldı. "
insufficient_stock: "Yetersiz stok, sadece %{on_hand} kaldı"
out_of_stock:
reduced_stock_available: Azaltılmış stok mevcut
@@ -2646,6 +2660,10 @@ tr:
signup_or_login: "Kaydolarak Başlayın (veya giriş yapın)"
have_an_account: "Zaten hesabınız var mı?"
action_login: "Şimdi giriş yapın."
stripe_elements:
unknown_error_from_stripe: |
Ödeme sağlayıcımızda kart ayarlarınız ile ilgili bir sorun oluştu.
Lütfen sayfayı yenileyip tekrar deneyin, eğer sorun devam ediyorsa lütfen destek için bize ulaşın.
inflections:
each:
one: "her biri"
@@ -2776,6 +2794,7 @@ tr:
adjustments: "Düzeltmeler"
payments: "Ödemeler"
return_authorizations: "İade Yetkileri"
credit_owed: "Artı Bakiye"
new_adjustment: "Yeni Düzenleme"
payment: "Ödeme"
payment_method: "ÖDEME YÖNTEMİ"
@@ -3251,6 +3270,8 @@ tr:
bulk_coop_allocation: 'Müşteri Bazında Toplu Alım Rakamları'
bulk_coop_packing_sheets: 'Toplu Alım - Paketleme Sayfaları'
bulk_coop_customer_payments: 'Toplu Alım - Müşteri Ödemeleri'
customer_names_message:
customer_names_tip: "Tedarik ettiğiniz siparişlerde müşteri isimleri gizli ise dağıtımcıya ulaşıp alışveriş tercihlerini tedarikçilerinin müşteri isimlerini görebilmelerini sağlayacak şekilde güncellemelerini isteyebilirsiniz. "
users:
index:
listing_users: "Kullanıcılar Listeleniyor"
@@ -3483,3 +3504,8 @@ tr:
shipment:
cannot_ready: "Gönderim hazırlanamıyor."
invalid_taxonomy_id: "Geçersiz kategori kimliği."
activerecord:
models:
spree/payment:
one: Ödemeler
other: 'Ödemeler '

View File

@@ -0,0 +1,63 @@
# frozen_string_literal: true
# Controller used to provide the API products for the DFC application
module DfcProvider
module Api
class BaseController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :not_found
before_action :check_authorization,
:check_user,
:check_enterprise
respond_to :json
private
def check_authorization
return if access_token.present?
head :unprocessable_entity
end
def check_user
return if current_user.present?
head :unauthorized
end
def check_enterprise
current_enterprise
end
def current_enterprise
@current_enterprise ||=
if params[enterprise_id_param_name] == 'default'
current_user.enterprises.first!
else
current_user.enterprises.find(params[enterprise_id_param_name])
end
end
def enterprise_id_param_name
:enterprise_id
end
def current_user
@current_user ||= authorization_control.process
end
def access_token
request.headers['Authorization'].to_s.split(' ').last
end
def authorization_control
DfcProvider::AuthorizationControl.new(access_token)
end
def not_found
head :not_found
end
end
end
end

View File

@@ -0,0 +1,26 @@
# frozen_string_literal: true
# Controller used to provide the API products for the DFC application
module DfcProvider
module Api
class CatalogItemsController < DfcProvider::Api::BaseController
def index
render json: current_user, serializer: DfcProvider::PersonSerializer
end
def show
render json: variant, serializer: DfcProvider::CatalogItemSerializer
end
private
def variant
@variant ||=
Spree::Variant.
joins(product: :supplier).
where('enterprises.id' => current_enterprise.id).
find(params[:id])
end
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
# Controller used to provide the CatalogItem API for the DFC application
module DfcProvider
module Api
class EnterprisesController < DfcProvider::Api::BaseController
def show
render json: current_enterprise, serializer: DfcProvider::EnterpriseSerializer
end
private
def enterprise_id_param_name
:id
end
end
end
end

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: true
# Controller used to provide the Persons API for the DFC application
module DfcProvider
module Api
class PersonsController < DfcProvider::Api::BaseController
skip_before_action :check_enterprise
before_action :check_user_accessibility
def show
render json: user, serializer: DfcProvider::PersonSerializer
end
private
def user
@user ||= Spree::User.find(params[:id])
end
def check_user_accessibility
return if current_user == user
not_found
end
end
end
end

View File

@@ -1,70 +0,0 @@
# frozen_string_literal: true
# Controller used to provide the API products for the DFC application
module DfcProvider
module Api
class ProductsController < ::ActionController::Base
# To access 'base_url' helper
include Rails.application.routes.url_helpers
before_filter :check_authorization,
:check_user,
:check_enterprise
respond_to :json
def index
products = @enterprise.
inventory_variants.
includes(:product, :inventory_items)
serialized_data = ::DfcProvider::ProductSerializer.
new(products, base_url).
serialized_data
render json: serialized_data
end
private
def check_enterprise
@enterprise =
if params[:enterprise_id] == 'default'
@user.enterprises.first
else
@user.enterprises.where(id: params[:enterprise_id]).first
end
return if @enterprise.present?
head :not_found
end
def check_authorization
return if access_token.present?
head :unprocessable_entity
end
def check_user
@user = authorization_control.process
return if @user.present?
head :unauthorized
end
def base_url
"#{root_url}api/dfc_provider"
end
def access_token
request.headers['Authorization'].to_s.split(' ').last
end
def authorization_control
DfcProvider::AuthorizationControl.new(access_token)
end
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
# Controller used to provide the SuppliedProducts API for the DFC application
module DfcProvider
module Api
class SuppliedProductsController < DfcProvider::Api::BaseController
def show
render json: variant, serializer: DfcProvider::SuppliedProductSerializer
end
private
def variant
@variant ||=
Spree::Variant.
joins(product: :supplier).
where('enterprises.id' => current_enterprise.id).
find(params[:id])
end
end
end
end

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: true
# Serializer used to render the DFC Address from an OFN User
# into JSON-LD format based on DFC ontology
module DfcProvider
class AddressSerializer < ActiveModel::Serializer
attribute :type, key: '@type'
attribute :city, key: 'dfc:city'
attribute :country, key: 'dfc:country'
attribute :postcode, key: 'dfc:postcode'
attribute :street, key: 'dfc:street'
def type
'dfc:Address'
end
def city; end
def country; end
def postcode; end
def street; end
end
end

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
# Serializer used to render a DFC CatalogItem from an OFN Product
# into JSON-LD format based on DFC ontology
module DfcProvider
class CatalogItemSerializer < ActiveModel::Serializer
attribute :id, key: '@id'
attribute :type, key: '@type'
attribute :references, key: 'dfc:references'
attribute :sku, key: 'dfc:sku'
attribute :stock_limitation, key: 'dfc:stockLimitation'
has_many :offered_through,
serializer: DfcProvider::OfferSerializer,
key: 'dfc:offeredThrough'
def id
dfc_provider_routes.api_dfc_provider_enterprise_catalog_item_url(
enterprise_id: object.product.supplier_id,
id: object.id,
host: root_url
)
end
def type
'dfc:CatalogItem'
end
def references
{
'@type' => '@id',
'@id' => "/supplied_products/#{object.product_id}"
}
end
def stock_limitation; end
def offered_through
[object]
end
private
def reference_id
dfc_provider_routes.api_dfc_provider_enterprise_supplied_product_url(
enterprise_id: object.product.supplier_id,
id: object.product_id,
host: root_url
)
end
def dfc_provider_routes
DfcProvider::Engine.routes.url_helpers
end
end
end

View File

@@ -0,0 +1,53 @@
# frozen_string_literal: true
# Serializer used to render a DFC Enterprise from an OFN Enterprise
# into JSON-LD format based on DFC ontology
module DfcProvider
class EnterpriseSerializer < ActiveModel::Serializer
attribute :id, key: '@id'
attribute :type, key: '@type'
attribute :vat_number, key: 'dfc:VATnumber'
has_many :defines, key: 'dfc:defines'
has_many :supplies,
key: 'dfc:supplies',
serializer: DfcProvider::SuppliedProductSerializer
has_many :manages,
key: 'dfc:manages',
serializer: DfcProvider::CatalogItemSerializer
def id
dfc_provider_routes.api_dfc_provider_enterprise_url(
id: object.id,
host: root_url
)
end
def type
'dfc:Entreprise'
end
def vat_number; end
def defines
[]
end
def supplies
Spree::Variant.
joins(product: :supplier).
where('enterprises.id' => object.id)
end
def manages
Spree::Variant.
joins(product: :supplier).
where('enterprises.id' => object.id)
end
private
def dfc_provider_routes
DfcProvider::Engine.routes.url_helpers
end
end
end

View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true
# Serializer used to render the DFC Offer from an OFN Product
# into JSON-LD format based on DFC ontology
module DfcProvider
class OfferSerializer < ActiveModel::Serializer
attribute :id, key: '@id'
attribute :type, key: '@type'
attribute :offeres_to, key: 'dfc:offeres_to'
attribute :price, key: 'dfc:price'
attribute :stock_limitation, key: 'dfc:stockLimitation'
def id
"/offers/#{object.id}"
end
def type
'dfc:Offer'
end
def offeres_to
{
'@type' => '@id',
'@id' => nil
}
end
def stock_limitation
object.on_hand
end
end
end

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
# Serializer used to render the DFC Person from an OFN User
# into JSON-LD format based on DFC ontology
module DfcProvider
class PersonSerializer < ActiveModel::Serializer
attribute :context, key: '@context'
attribute :id, key: '@id'
attribute :type, key: '@type'
attribute :family_name, key: 'dfc:familyName'
attribute :first_name, key: 'dfc:firstName'
has_one :address,
key: 'dfc:hasAddress',
serializer: DfcProvider::AddressSerializer
has_many :affiliates,
key: 'dfc:affiliates',
serializer: DfcProvider::EnterpriseSerializer
# Context should be provided inside the controller,
# but AMS doesn't not supported `meta` and `meta_key` with `root` to nil...
def context
{
'dfc' => 'http://datafoodconsortium.org/ontologies/DFC_FullModel.owl#',
'@base' => "#{root_url}api/dfc_provider"
}
end
def id
dfc_provider_routes.api_dfc_provider_person_url(
id: object.id,
host: root_url
)
end
def type
'dfc:Person'
end
def family_name; end
def first_name; end
def address; end
def affiliates
object.enterprises
end
private
def dfc_provider_routes
DfcProvider::Engine.routes.url_helpers
end
end
end

View File

@@ -1,37 +0,0 @@
# frozen_string_literal: true
# Serializer used to render the products passed
# into JSON-LD format based on DFC ontology
module DfcProvider
class ProductSerializer
def initialize(products, base_url)
@products = products
@base_url = base_url
end
def serialized_data
{
"@context" =>
{
"DFC" => "http://datafoodconsortium.org/ontologies/DFC_FullModel.owl#",
"@base" => @base_url
},
"@id" => "/enterprise/products",
"DFC:supplies" => serialized_products
}
end
private
def serialized_products
@products.map do |variant|
{
"DFC:description" => variant.name,
"DFC:quantity" => variant.total_on_hand,
"@id" => variant.id,
"DFC:hasUnit" => { "@id" => "/unit/#{variant.unit_description.presence || 'piece'}" }
}
end
end
end
end

View File

@@ -0,0 +1,72 @@
# frozen_string_literal: true
# Serializer used to render a DFC SuppliedProduct from an OFN Variant
# into JSON-LD format based on DFC ontology
module DfcProvider
class SuppliedProductSerializer < ActiveModel::Serializer
attribute :id, key: '@id'
attribute :type, key: '@type'
attribute :unit, key: 'dfc:hasUnit'
attribute :quantity, key: 'dfc:quantity'
attribute :description, key: 'dfc:description'
attribute :total_theoritical_stock, key: 'dfc:totalTheoriticalStock'
attribute :brand, key: 'dfc:brand'
attribute :claim, key: 'dfc:claim'
attribute :image, key: 'dfc:image'
attribute :life_time, key: 'lifeTime'
has_many :physical_characteristics, key: 'dfc:physicalCharacterisctics'
def id
dfc_provider_routes.api_dfc_provider_enterprise_supplied_product_url(
enterprise_id: object.product.supplier_id,
id: object.id,
host: root_url
)
end
def type
'dfc:SuppliedProduct'
end
def unit
{
'@id' => "/unit/#{unit_name}",
'rdfs:label' => unit_name
}
end
def quantity
object.on_hand
end
def description
object.name
end
def total_theoritical_stock; end
def brand; end
def claim; end
def image
object.images.first.try(:attachment, :url)
end
def life_time; end
def physical_characteristics
[]
end
private
def unit_name
object.unit_description.presence || 'piece'
end
def dfc_provider_routes
DfcProvider::Engine.routes.url_helpers
end
end
end

View File

@@ -3,9 +3,11 @@
DfcProvider::Engine.routes.draw do
namespace :api do
scope :dfc_provider, as: :dfc_provider, path: '/dfc_provider' do
resources :enterprises, only: :none do
resources :products, only: [:index]
resources :enterprises, only: [:show] do
resources :catalog_items, only: [:index, :show]
resources :supplied_products, only: [:show]
end
resources :persons, only: [:show]
end
end
end

View File

@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
spec.files = Dir["{app,config,lib}/**/*"] + ['README.md']
spec.test_files = Dir['spec/**/*']
spec.add_dependency 'active_model_serializers', '~> 0.8.4'
spec.add_dependency 'jwt', '~> 2.2'
spec.add_dependency 'rspec', '~> 3.9'
end

View File

@@ -2,18 +2,13 @@
require 'spec_helper'
describe DfcProvider::Api::ProductsController, type: :controller do
describe DfcProvider::Api::CatalogItemsController, type: :controller do
render_views
let(:user) { create(:user) }
let(:enterprise) { create(:distributor_enterprise, owner: user) }
let(:product) { create(:simple_product, supplier: enterprise ) }
let!(:visible_inventory_item) do
create(:inventory_item,
enterprise: enterprise,
variant: product.variants.first,
visible: true)
end
let!(:user) { create(:user) }
let!(:enterprise) { create(:distributor_enterprise, owner: user) }
let!(:product) { create(:simple_product, supplier: enterprise ) }
let!(:variant) { product.variants.first }
describe('.index') do
context 'with authorization token' do
@@ -37,9 +32,13 @@ describe DfcProvider::Api::ProductsController, type: :controller do
expect(response.status).to eq 200
end
it 'renders the related product' do
it 'renders the required content' do
expect(response.body)
.to include(product.variants.first.name)
.to include(variant.name)
expect(response.body)
.to include(variant.sku)
expect(response.body)
.to include("offers/#{variant.id}")
end
end
@@ -60,9 +59,13 @@ describe DfcProvider::Api::ProductsController, type: :controller do
expect(response.status).to eq 200
end
it 'renders the related product' do
it 'renders the required content' do
expect(response.body)
.to include(product.variants.first.name)
.to include(variant.name)
expect(response.body)
.to include(variant.sku)
expect(response.body)
.to include("offers/#{variant.id}")
end
end
end
@@ -70,7 +73,7 @@ describe DfcProvider::Api::ProductsController, type: :controller do
context 'without a recorded enterprise' do
let(:enterprise) { create(:enterprise) }
it 'returns not_found head' do
it 'is not found' do
api_get :index, enterprise_id: 'default'
expect(response.status).to eq 404
end
@@ -96,4 +99,53 @@ describe DfcProvider::Api::ProductsController, type: :controller do
end
end
end
describe('.show') do
context 'with authorization token' do
before do
request.headers['Authorization'] = 'Bearer 123456.abcdef.123456'
end
context 'with an authenticated user' do
before do
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:process)
.and_return(user)
end
context 'with an enterprise' do
context 'given with an id' do
before do
api_get :show,
enterprise_id: enterprise.id,
id: variant.id
end
it 'is successful' do
expect(response.status).to eq 200
end
it 'renders the required content' do
expect(response.body)
.to include('dfc:CatalogItem')
expect(response.body)
.to include("offers/#{variant.id}")
end
end
context 'with a variant not linked to the enterprise' do
before do
api_get :show,
enterprise_id: enterprise.id,
id: create(:simple_product).variants.first.id
end
it 'is not found' do
expect(response.status).to eq 404
end
end
end
end
end
end
end

View File

@@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'spec_helper'
describe DfcProvider::Api::EnterprisesController, type: :controller do
render_views
let!(:user) { create(:user) }
let!(:enterprise) { create(:distributor_enterprise, owner: user) }
let!(:product) { create(:simple_product, supplier: enterprise ) }
describe('.show') do
context 'with authorization token' do
before do
request.headers['Authorization'] = 'Bearer 123456.abcdef.123456'
end
context 'with an authenticated user' do
before do
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:process)
.and_return(user)
end
context 'with an enterprise' do
context 'given with an id' do
before { api_get :show, id: 'default' }
it 'is successful' do
expect(response.status).to eq 200
end
it 'renders the required content' do
expect(response.body)
.to include(product.name)
expect(response.body)
.to include(product.sku)
expect(response.body)
.to include("offers/#{product.variants.first.id}")
end
end
context 'given with a wrong id' do
before { api_get :show, id: 999 }
it 'is not found' do
expect(response.status).to eq 404
end
end
end
end
end
end
end

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
describe DfcProvider::Api::PersonsController, type: :controller do
render_views
let!(:user) { create(:user) }
describe('.show') do
context 'with authorization token' do
before do
request.headers['Authorization'] = 'Bearer 123456.abcdef.123456'
end
context 'with an authenticated user' do
before do
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:process)
.and_return(user)
end
context 'given with an accessible id' do
before do
api_get :show,
id: user.id
end
it 'is successful' do
expect(response.status).to eq 200
end
it 'renders the required content' do
expect(response.body).to include('dfc:Person')
end
end
context 'with an other user id' do
before { api_get :show, id: create(:user).id }
it 'is not found' do
expect(response.status).to eq 404
end
end
end
end
end
end

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'spec_helper'
describe DfcProvider::Api::SuppliedProductsController, type: :controller do
render_views
let!(:user) { create(:user) }
let!(:enterprise) { create(:distributor_enterprise, owner: user) }
let!(:product) { create(:simple_product, supplier: enterprise ) }
let!(:variant) { product.variants.first }
describe('.show') do
context 'with authorization token' do
before do
request.headers['Authorization'] = 'Bearer 123456.abcdef.123456'
end
context 'with an authenticated user' do
before do
allow_any_instance_of(DfcProvider::AuthorizationControl)
.to receive(:process)
.and_return(user)
end
context 'with an enterprise' do
context 'given with an id' do
before do
api_get :show,
enterprise_id: 'default',
id: variant.id
end
it 'is successful' do
expect(response.status).to eq 200
end
it 'renders the required content' do
expect(response.body)
.to include(variant.name)
end
end
context 'given with a wrong id' do
before { api_get :show, id: 999 }
it 'is not found' do
expect(response.status).to eq 404
end
end
end
end
end
end
end

View File

@@ -5,7 +5,12 @@ require 'spec_helper'
module OrderManagement
module Stock
describe Coordinator do
let!(:order) { create(:order_with_line_items, distributor: create(:distributor_enterprise)) }
let!(:order) do
build_stubbed(
:order_with_line_items,
distributor: build_stubbed(:distributor_enterprise)
)
end
subject { Coordinator.new(order) }

View File

@@ -34,7 +34,7 @@ module OrderManagement
end
context "when the payment method is a stripe payment method" do
let(:payment_method) { create(:stripe_payment_method) }
let(:payment_method) { create(:stripe_connect_payment_method) }
context "and the card is already set (the payment source is a credit card)" do
it "returns the pending payment with no change" do

View File

@@ -704,7 +704,7 @@ describe Admin::SubscriptionsController, type: :controller do
end
context "when other payment methods exist" do
let!(:stripe) { create(:stripe_payment_method, distributors: [shop]) }
let!(:stripe) { create(:stripe_connect_payment_method, distributors: [shop]) }
let!(:paypal) { Spree::Gateway::PayPalExpress.create!(name: "PayPalExpress", distributor_ids: [shop.id]) }
let!(:bogus) { create(:bogus_payment_method, distributors: [shop]) }

View File

@@ -3,6 +3,8 @@
require 'spec_helper'
describe Spree::Admin::PaymentsController, type: :controller do
include StripeHelper
let!(:shop) { create(:enterprise) }
let!(:user) { shop.owner }
let!(:order) { create(:order, distributor: shop, state: 'complete') }
@@ -21,7 +23,7 @@ describe Spree::Admin::PaymentsController, type: :controller do
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
context "that was processed by stripe" do
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributors: [shop]) }
let!(:payment) do
create(:payment, order: order, state: 'completed', payment_method: payment_method,
response_code: 'ch_1a2b3c', amount: order.total)
@@ -80,7 +82,7 @@ describe Spree::Admin::PaymentsController, type: :controller do
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
context "that was processed by stripe" do
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributors: [shop]) }
let!(:payment) do
create(:payment, order: order, state: 'completed', payment_method: payment_method,
response_code: 'ch_1a2b3c', amount: order.total + 5)
@@ -152,19 +154,13 @@ describe Spree::Admin::PaymentsController, type: :controller do
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
allow(StripeAccount).to receive(:find_by) { stripe_account }
# Retrieves payment intent info
stub_request(:get, "https://api.stripe.com/v1/payment_intents/pi_123")
.with(headers: { 'Stripe-Account' => 'abc123' })
.to_return({ status: 200, body: JSON.generate(
amount_received: 2000,
charges: { data: [{ id: "ch_1a2b3c" }] }
) })
stub_payment_intent_get_request
end
context "where the request succeeds" do
before do
# Issues the refund
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1234/refunds").
with(basic_auth: ["sk_test_12345", ""]).
to_return(status: 200,
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
@@ -184,7 +180,7 @@ describe Spree::Admin::PaymentsController, type: :controller do
context "where the request fails" do
before do
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1234/refunds").
with(basic_auth: ["sk_test_12345", ""]).
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
end
@@ -220,17 +216,12 @@ describe Spree::Admin::PaymentsController, type: :controller do
before do
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
# Retrieves payment intent info
stub_request(:get, "https://api.stripe.com/v1/payment_intents/pi_123")
.to_return({ status: 200, body: JSON.generate(
amount_received: 2000,
charges: { data: [{ id: "ch_1a2b3c" }] }
) })
stub_payment_intent_get_request stripe_account_header: false
end
context "where the request succeeds" do
before do
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1234/refunds").
with(basic_auth: ["sk_test_12345", ""]).
to_return(status: 200,
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
@@ -250,7 +241,7 @@ describe Spree::Admin::PaymentsController, type: :controller do
context "where the request fails" do
before do
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1234/refunds").
with(basic_auth: ["sk_test_12345", ""]).
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
end

View File

@@ -45,7 +45,7 @@ describe Spree::Admin::PaymentsController, type: :controller do
end
context "with Stripe payment where payment.process! errors out" do
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributors: [shop]) }
before do
allow_any_instance_of(Spree::Payment).
to receive(:process!).

View File

@@ -68,7 +68,7 @@ module Spree
let!(:user) { create(:user, enterprise_limit: 2) }
let!(:enterprise1) { create(:distributor_enterprise, owner: user) }
let!(:enterprise2) { create(:distributor_enterprise, owner: create(:user)) }
let!(:payment_method) { create(:stripe_payment_method, distributor_ids: [enterprise1.id, enterprise2.id], preferred_enterprise_id: enterprise2.id) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributor_ids: [enterprise1.id, enterprise2.id], preferred_enterprise_id: enterprise2.id) }
before { allow(controller).to receive(:spree_current_user) { user } }

View File

@@ -6,12 +6,12 @@ FactoryBot.define do
promo_image {}
end
owner { FactoryBot.create :user }
owner factory: :user
sequence(:name) { |n| "Enterprise #{n}" }
sells 'any'
description 'enterprise'
long_description '<p>Hello, world!</p><p>This is a paragraph.</p>'
address { FactoryBot.create(:address) }
address
after(:create) do |enterprise, proxy|
proxy.users.each do |user|

View File

@@ -23,8 +23,8 @@ FactoryBot.define do
environment 'test'
end
factory :stripe_payment_method, class: Spree::Gateway::StripeConnect do
name 'Stripe'
factory :stripe_connect_payment_method, class: Spree::Gateway::StripeConnect do
name 'StripeConnect'
environment 'test'
distributors { [FactoryBot.create(:enterprise)] }
preferred_enterprise_id { distributors.first.id }

View File

@@ -1,18 +1,18 @@
FactoryBot.define do
factory :filter_order_cycles_tag_rule, class: TagRule::FilterOrderCycles do
enterprise { FactoryBot.create :distributor_enterprise }
enterprise factory: :distributor_enterprise
end
factory :filter_shipping_methods_tag_rule, class: TagRule::FilterShippingMethods do
enterprise { FactoryBot.create :distributor_enterprise }
enterprise factory: :distributor_enterprise
end
factory :filter_products_tag_rule, class: TagRule::FilterProducts do
enterprise { FactoryBot.create :distributor_enterprise }
enterprise factory: :distributor_enterprise
end
factory :filter_payment_methods_tag_rule, class: TagRule::FilterPaymentMethods do
enterprise { FactoryBot.create :distributor_enterprise }
enterprise factory: :distributor_enterprise
end
factory :tag_rule, class: TagRule::DiscountOrder do

View File

@@ -0,0 +1,54 @@
require "spec_helper"
describe "
As a site administrator
I want to configure the site content
" do
include AuthenticationHelper
include WebHelper
before do
login_as_admin_and_visit spree.edit_admin_general_settings_path
click_link "Content"
end
it "fills in a setting shows the result on the home page" do
fill_in "footer_facebook_url", with: ""
fill_in "footer_twitter_url", with: "http://twitter.com/me"
fill_in "footer_links_md", with: \
"[markdown link](/:/?#@!$&'()*+,;=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"
click_button "Update"
expect(page).to have_content "Your content has been successfully updated!"
visit root_path
# Then social media icons are only shown if they have a value
expect(page).not_to have_selector "i.ofn-i_044-facebook"
expect(page).to have_selector "i.ofn-i_041-twitter"
# And markdown is rendered
# expect(page).to have_link "markdown link" and the correct href
expect(page).to have_selector :link, "markdown link", href: \
"/:/?#@!$&'()*+,;=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
end
it "uploads logos" do
attach_file "logo", Rails.root.join('app/assets/images/logo-white.png')
click_button "Update"
expect(page).to have_content "Your content has been successfully updated!"
expect(ContentConfig.logo.to_s).to include "logo-white"
end
it "sets the user guide link" do
fill_in "user_guide_link", with: "http://www.openfoodnetwork.org/platform/user-guide/"
click_button "Update"
expect(page).to have_content "Your content has been successfully updated!"
visit spree.admin_dashboard_path
expect(page).to have_link("User Guide", href: "http://www.openfoodnetwork.org/platform/user-guide/")
expect(find_link("User Guide")[:target]).to eq("_blank")
end
end

View File

@@ -1,51 +0,0 @@
require 'spec_helper'
feature '
As a site administrator
I want to configure the site content
' do
include AuthenticationHelper
include WebHelper
before do
login_as_admin_and_visit spree.edit_admin_general_settings_path
click_link 'Content'
end
scenario "filling in a setting shows the result on the home page" do
fill_in 'footer_facebook_url', with: ''
fill_in 'footer_twitter_url', with: 'http://twitter.com/me'
fill_in 'footer_links_md', with: '[markdown link](/)'
click_button 'Update'
expect(page).to have_content 'Your content has been successfully updated!'
visit root_path
# Then social media icons are only shown if they have a value
expect(page).not_to have_selector 'i.ofn-i_044-facebook'
expect(page).to have_selector 'i.ofn-i_041-twitter'
# And markdown is rendered
expect(page).to have_link 'markdown link'
end
scenario "uploading logos" do
attach_file 'logo', "#{Rails.root}/app/assets/images/logo-white.png"
click_button 'Update'
expect(page).to have_content 'Your content has been successfully updated!'
expect(ContentConfig.logo.to_s).to include "logo-white"
end
scenario "setting user guide link" do
fill_in 'user_guide_link', with: 'http://www.openfoodnetwork.org/platform/user-guide/'
click_button 'Update'
expect(page).to have_content 'Your content has been successfully updated!'
visit spree.admin_dashboard_path
expect(page).to have_link('User Guide', href: 'http://www.openfoodnetwork.org/platform/user-guide/')
expect(find_link('User Guide')[:target]).to eq('_blank')
end
end

View File

@@ -139,7 +139,7 @@ feature 'Enterprises Index' do
expect(page).to have_no_content "supplier2.name"
expect(page).to have_no_content "distributor2.name"
expect(find("#content-header")).to have_link "New Enterprise"
expect(find('.js-admin-section-header')).to have_link "New Enterprise"
end
it "does not give me an option to change or update the package and producer properties of enterprises I manage" do

View File

@@ -151,7 +151,7 @@ feature '
page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS
click_button 'Update'
expect(page).to have_selector 'h1.page-title', text: "Customer Details"
expect(page).to have_selector 'h1.js-admin-page-title', text: "Customer Details"
# The customer selection partial should be visible
expect(page).to have_selector '#select-customer'
@@ -160,7 +160,7 @@ feature '
targetted_select2_search customer.email, from: '#customer_search_override',
dropdown_css: '.select2-drop'
click_button 'Update'
expect(page).to have_selector "h1.page-title", text: "Customer Details"
expect(page).to have_selector "h1.js-admin-page-title", text: "Customer Details"
# Then their addresses should be associated with the order
order = Spree::Order.last

View File

@@ -297,6 +297,31 @@ feature "Product Import", js: true do
end
end
it "handles a unit of kg for inventory import" do
product = create(:simple_product, supplier: enterprise, on_hand: 100, name: 'Beets', unit_value: '1000', variant_unit_scale: 1000)
csv_data = CSV.generate do |csv|
csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", "units", "on_demand"]
csv << ["Beets", "Another Enterprise", "User Enterprise", "Vegetables", nil, "3.20", "kg", "1", "true"]
end
File.write('/tmp/test.csv', csv_data)
visit main_app.admin_product_import_path
select2_select I18n.t('admin.product_import.index.inventories'), from: "settings_import_into"
attach_file 'file', '/tmp/test.csv'
click_button 'Upload'
proceed_to_validation
expect(page).to have_selector '.item-count', text: "1"
expect(page).to have_no_selector '.invalid-count'
expect(page).to have_selector '.inv-create-count', text: '1'
save_data
expect(page).to have_selector '.inv-created-count', text: '1'
end
it "handles on_demand and on_hand validations with inventory" do
csv_data = CSV.generate do |csv|
csv << ["name", "distributor", "producer", "category", "on_hand", "price", "units", "on_demand"]

View File

@@ -172,7 +172,7 @@ feature 'Subscriptions' do
let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) }
let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [test_variant, shop_variant], enterprise_fees: [enterprise_fee]) }
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop]) }
let!(:payment_method) { create(:stripe_connect_payment_method, name: 'Credit Card', distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
before do
@@ -319,7 +319,7 @@ feature 'Subscriptions' do
let!(:variant3_oc) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) }
let!(:variant3_ex) { variant3_oc.exchanges.create(sender: shop, receiver: shop, variants: [variant3]) }
let!(:payment_method) { create(:payment_method, distributors: [shop]) }
let!(:stripe_payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop]) }
let!(:stripe_payment_method) { create(:stripe_connect_payment_method, name: 'Credit Card', distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
let!(:subscription) {
create(:subscription,
@@ -457,7 +457,7 @@ feature 'Subscriptions' do
let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) }
let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) }
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributors: [shop]) }
let!(:shipping_method) { create(:shipping_method, distributors: [shop]) }
before do

View File

@@ -1,9 +1,12 @@
# frozen_string_literal: true
require 'spec_helper'
feature "Check out with Stripe", js: true do
include AuthenticationHelper
include ShopWorkflow
include CheckoutHelper
include StripeHelper
let(:distributor) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [variant]) }
@@ -12,12 +15,14 @@ feature "Check out with Stripe", js: true do
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor, bill_address_id: nil, ship_address_id: nil) }
let(:shipping_with_fee) { create(:shipping_method, require_ship_address: false, name: "Donkeys", calculator: Calculator::FlatRate.new(preferred_amount: 4.56)) }
let(:free_shipping) { create(:shipping_method) }
let!(:check_with_fee) { create(:payment_method, distributors: [distributor], calculator: Calculator::FlatRate.new(preferred_amount: 5.67)) }
before do
setup_stripe
set_order order
add_product_to_cart order, product
distributor.shipping_methods << shipping_with_fee
distributor.shipping_methods << [shipping_with_fee, free_shipping]
end
context 'login in as user' do
@@ -27,9 +32,9 @@ feature "Check out with Stripe", js: true do
login_as(user)
end
context "with Stripe" do
context "with Stripe Connect" do
let!(:stripe_pm) do
create(:stripe_payment_method, distributors: [distributor])
create(:stripe_connect_payment_method, distributors: [distributor])
end
let!(:saved_card) do
@@ -54,9 +59,6 @@ feature "Check out with Stripe", js: true do
end
before do
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
allow(Stripe).to receive(:publishable_key) { "some_key" }
Spree::Config.set(stripe_connect_enabled: true)
stub_request(:post, "https://api.stripe.com/v1/charges")
.with(basic_auth: ["sk_test_12345", ""])
.to_return(status: 200, body: JSON.generate(response_mock))
@@ -79,4 +81,129 @@ feature "Check out with Stripe", js: true do
end
end
end
describe "using Stripe SCA" do
let!(:stripe_account) { create(:stripe_account, enterprise: distributor) }
let!(:stripe_sca_payment_method) {
create(:stripe_sca_payment_method, distributors: [distributor])
}
let!(:shipping_method) { create(:shipping_method) }
let(:error_message) { "Card was declined: insufficient funds." }
context "with guest checkout" do
before do
stub_payment_intent_get_request
stub_hub_payment_methods_request
end
context "when the card is accepted" do
before do
stub_payment_intents_post_request order: order
stub_successful_capture_request order: order
end
it "completes checkout successfully" do
checkout_with_stripe
expect(page).to have_content "Confirmed"
expect(order.reload.completed?).to eq true
expect(order.payments.first.state).to eq "completed"
end
end
context "when the card is rejected" do
before do
stub_payment_intents_post_request order: order
stub_failed_capture_request order: order, response: { message: error_message }
end
it "shows an error message from the Stripe response" do
checkout_with_stripe
expect(page).to have_content error_message
expect(order.reload.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
end
end
context "when the card needs extra SCA authorization", js: true do
let(:stripe_redirect_url) { checkout_path(payment_intent: "pi_123") }
let(:payment_intent_authorize_response) do
{ status: 200, body: JSON.generate(id: "pi_123",
object: "payment_intent",
next_source_action: {
type: "authorize_with_url",
authorize_with_url: { url: stripe_redirect_url }
},
status: "requires_source_action") }
end
before do
stub_request(:post, "https://api.stripe.com/v1/payment_intents")
.with(basic_auth: ["sk_test_12345", ""], body: /.*#{order.number}/)
.to_return(payment_intent_authorize_response)
end
describe "and the authorization succeeds" do
before do
stub_successful_capture_request order: order
end
it "completes checkout successfully" do
checkout_with_stripe
# We make stripe return stripe_redirect_url (which is already sending the user back to the checkout) as if the authorization was done
# We can then control the actual authorization or failure of the payment through the mock stub_successful_capture_request
expect(page).to have_content "Confirmed"
expect(order.reload.completed?).to eq true
expect(order.payments.first.state).to eq "completed"
end
end
describe "and the authorization fails" do
before do
stub_failed_capture_request order: order, response: { message: error_message }
end
it "shows an error message from the Stripe response" do
checkout_with_stripe
# We make stripe return stripe_redirect_url (which is already sending the user back to the checkout) as if the authorization was done
# We can then control the actual authorization or failure of the payment through the mock stub_failed_capture_request
expect(page).to have_content error_message
expect(order.reload.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
end
end
end
context "with multiple payment attempts; one failed and one succeeded" do
before do
stub_payment_intents_post_request order: order
end
it "records failed payment attempt and allows order completion" do
# First payment attempt is rejected
stub_failed_capture_request(order: order, response: { message: error_message })
checkout_with_stripe
expect(page).to have_content error_message
expect(order.reload.payments.count).to eq 1
expect(order.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
# Second payment attempt is accepted
stub_successful_capture_request order: order
place_order
expect(page).to have_content "Confirmed"
expect(order.reload.payments.count).to eq 2
expect(order.state).to eq "complete"
expect(order.payments.last.state).to eq "completed"
end
end
end
end
end

View File

@@ -194,8 +194,6 @@ feature "As a consumer I want to shop with a distributor", js: true do
end
it "returns search results for products where the search term matches one of the product's variant names" do
pending "has been broken for a while"
visit shop_path
fill_in "search", with: "Badg" # For variant with display_name "Badgers"

View File

@@ -237,8 +237,8 @@ describe EnterprisesHelper, type: :helper do
end
context "when StripeConnect payment methods are present" do
let!(:pm3) { create(:stripe_payment_method, distributors: [distributor], preferred_enterprise_id: distributor.id) }
let!(:pm4) { create(:stripe_payment_method, distributors: [distributor], preferred_enterprise_id: some_other_distributor.id) }
let!(:pm3) { create(:stripe_connect_payment_method, distributors: [distributor], preferred_enterprise_id: distributor.id) }
let!(:pm4) { create(:stripe_connect_payment_method, distributors: [distributor], preferred_enterprise_id: some_other_distributor.id) }
let(:available_payment_methods) { helper.available_payment_methods }
around do |example|

View File

@@ -6,7 +6,7 @@ module Stripe
describe ProfileStorer do
describe "create_customer_from_token" do
let(:payment) { create(:payment) }
let(:stripe_payment_method) { create(:stripe_payment_method) }
let(:stripe_payment_method) { create(:stripe_connect_payment_method) }
let(:profile_storer) { Stripe::ProfileStorer.new(payment, stripe_payment_method.provider) }
let(:customer_id) { "cus_A123" }

View File

@@ -2,7 +2,7 @@ require 'spec_helper'
describe AdjustmentMetadata do
it "is valid when built from factory" do
adjustment = build(:adjustment)
expect(adjustment).to be_valid
adjustment_metadata = build(:adjustment_metadata)
expect(adjustment_metadata).to be_valid
end
end

View File

@@ -2,7 +2,7 @@ require 'spec_helper'
describe Calculator::FlatPercentItemTotal do
let(:calculator) { Calculator::FlatPercentItemTotal.new }
let(:line_item) { build(:line_item, price: 10, quantity: 1) }
let(:line_item) { build_stubbed(:line_item, price: 10, quantity: 1) }
before { allow(calculator).to receive_messages preferred_flat_percent: 10 }

View File

@@ -1,7 +1,7 @@
require 'spec_helper'
describe Calculator::FlexiRate do
let(:line_item) { build(:line_item, quantity: quantity) }
let(:line_item) { build_stubbed(:line_item, quantity: quantity) }
let(:calculator) do
Calculator::FlexiRate.new(
preferred_first_item: 2,

View File

@@ -3,7 +3,7 @@ require 'spec_helper'
describe Calculator::PerItem do
let(:calculator) { Calculator::PerItem.new(preferred_amount: 10) }
let(:shipping_calculable) { double(:calculable) }
let(:line_item) { build(:line_item, quantity: 5) }
let(:line_item) { build_stubbed(:line_item, quantity: 5) }
it "correctly calculates on a single line item object" do
allow(calculator).to receive_messages(calculable: shipping_calculable)

View File

@@ -8,7 +8,7 @@ describe Calculator::PriceSack do
calculator.preferred_discount_amount = 1
calculator
end
let(:line_item) { build(:line_item, price: price, quantity: 2) }
let(:line_item) { build_stubbed(:line_item, price: price, quantity: 2) }
context 'when the order amount is below preferred minimal' do
let(:price) { 2 }

View File

@@ -4,13 +4,13 @@ describe Calculator::Weight do
it_behaves_like "a model using the LocalizedNumber module", [:preferred_per_kg]
it "computes shipping cost for an order by total weight" do
variant1 = build(:variant, unit_value: 10_000)
variant2 = build(:variant, unit_value: 20_000)
variant3 = build(:variant, unit_value: nil)
variant1 = build_stubbed(:variant, unit_value: 10_000)
variant2 = build_stubbed(:variant, unit_value: 20_000)
variant3 = build_stubbed(:variant, unit_value: nil)
line_item1 = build(:line_item, variant: variant1, quantity: 1)
line_item2 = build(:line_item, variant: variant2, quantity: 3)
line_item3 = build(:line_item, variant: variant3, quantity: 5)
line_item1 = build_stubbed(:line_item, variant: variant1, quantity: 1)
line_item2 = build_stubbed(:line_item, variant: variant2, quantity: 3)
line_item3 = build_stubbed(:line_item, variant: variant3, quantity: 5)
order = double(:order, line_items: [line_item1, line_item2, line_item3])
@@ -19,8 +19,8 @@ describe Calculator::Weight do
end
describe "line item with variant_unit weight and variant unit_value" do
let(:variant) { create(:variant, unit_value: 10_000) }
let(:line_item) { create(:line_item, variant: variant, quantity: 2) }
let(:variant) { build_stubbed(:variant, unit_value: 10_000) }
let(:line_item) { build_stubbed(:line_item, variant: variant, quantity: 2) }
before { subject.set_preference(:per_kg, 5) }
@@ -29,7 +29,9 @@ describe Calculator::Weight do
end
describe "and with final_weight_volume defined" do
before { line_item.update_attribute :final_weight_volume, '18000' }
before do
line_item.final_weight_volume = '18000'
end
it "computes fee using final_weight_volume, not the variant weight" do
expect(subject.compute(line_item)).to eq(90) # 18 * 5
@@ -37,8 +39,8 @@ describe Calculator::Weight do
context "where variant unit is not weight" do
it "uses both final_weight_volume and weight to calculate fee" do
line_item.variant.update_attribute :weight, 7
line_item.variant.product.update_attribute :variant_unit, 'items'
line_item.variant.weight = 7
line_item.variant.product.variant_unit = 'items'
expect(subject.compute(line_item)).to eq(63) # 7 * (18000/10000) * 5
end
end
@@ -46,11 +48,11 @@ describe Calculator::Weight do
end
it "computes shipping cost for an object with an order" do
variant1 = create(:variant, unit_value: 10_000)
variant2 = create(:variant, unit_value: 20_000)
variant1 = build_stubbed(:variant, unit_value: 10_000)
variant2 = build_stubbed(:variant, unit_value: 20_000)
line_item1 = create(:line_item, variant: variant1, quantity: 1)
line_item2 = create(:line_item, variant: variant2, quantity: 2)
line_item1 = build_stubbed(:line_item, variant: variant1, quantity: 1)
line_item2 = build_stubbed(:line_item, variant: variant2, quantity: 2)
order = double(:order, line_items: [line_item1, line_item2])
object_with_order = double(:object_with_order, order: order)
@@ -60,12 +62,12 @@ describe Calculator::Weight do
end
context "when line item final_weight_volume is set" do
let!(:product) { create(:product, product_attributes) }
let!(:variant) { create(:variant, variant_attributes.merge(product: product)) }
let!(:product) { build_stubbed(:product, product_attributes) }
let!(:variant) { build_stubbed(:variant, variant_attributes.merge(product: product)) }
let(:calculator) { described_class.new(preferred_per_kg: 6) }
let(:line_item) do
create(:line_item, variant: variant, quantity: 2).tap do |object|
build_stubbed(:line_item, variant: variant, quantity: 2).tap do |object|
object.send(:calculate_final_weight_volume)
end
end
@@ -176,14 +178,14 @@ describe Calculator::Weight do
context "when variant_unit is 'items'" do
let(:product) {
create(:product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: "bunch")
build_stubbed(:product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: "bunch")
}
let(:line_item) { create(:line_item, variant: variant, quantity: 1) }
let(:line_item) { build_stubbed(:line_item, variant: variant, quantity: 1) }
before { subject.set_preference(:per_kg, 5) }
context "when unit_value is zero variant.weight is present" do
let(:variant) { create(:variant, product: product, unit_value: 0, weight: 10.0) }
let(:variant) { build_stubbed(:variant, product: product, unit_value: 0, weight: 10.0) }
it "uses the variant weight" do
expect(subject.compute(line_item)).to eq 50.0
@@ -191,7 +193,7 @@ describe Calculator::Weight do
end
context "when unit_value is zero variant.weight is nil" do
let(:variant) { create(:variant, product: product, unit_value: 0, weight: nil) }
let(:variant) { build_stubbed(:variant, product: product, unit_value: 0, weight: nil) }
it "uses zero weight" do
expect(subject.compute(line_item)).to eq 0
@@ -200,7 +202,7 @@ describe Calculator::Weight do
context "when unit_value is nil and variant.weight is present" do
let(:variant) {
create(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: 10.0)
build_stubbed(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: 10.0)
}
it "uses the variant weight" do
@@ -212,7 +214,7 @@ describe Calculator::Weight do
context "when unit_value is nil and variant.weight is nil" do
let(:variant) {
create(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: nil)
build_stubbed(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: nil)
}
it "uses zero weight" do

View File

@@ -288,7 +288,7 @@ describe OrderCycle do
end
describe "checking status" do
let(:oc) { create(:simple_order_cycle) }
let(:oc) { build_stubbed(:simple_order_cycle) }
it "reports status when an order cycle is upcoming" do
Timecop.freeze(oc.orders_open_at - 1.second) do
@@ -319,7 +319,8 @@ describe OrderCycle do
end
it "reports status when an order cycle is undated" do
oc.update!(orders_open_at: nil, orders_close_at: nil)
oc.orders_open_at = nil
oc.orders_close_at = nil
expect(oc).to be_undated
expect(oc).not_to be_dated
@@ -329,7 +330,7 @@ describe OrderCycle do
end
it "reports status when an order cycle is partially dated - opening time only" do
oc.update!(orders_close_at: nil)
oc.orders_close_at = nil
expect(oc).to be_undated
expect(oc).not_to be_dated
@@ -339,7 +340,7 @@ describe OrderCycle do
end
it "reports status when an order cycle is partially dated - closing time only" do
oc.update!(orders_open_at: nil)
oc.orders_open_at = nil
expect(oc).to be_undated
expect(oc).not_to be_dated

View File

@@ -0,0 +1,116 @@
# frozen_string_literal: true
require 'spec_helper'
describe ProductImport::EntryValidator do
let(:current_user) { double(:current_user) }
let(:import_time) { double(:import_time) }
let(:spreadsheet_data) { double(:spreadsheet_data) }
let(:editable_enterprises) { double(:editable_enterprises) }
let(:inventory_permissions) { double(:inventory_permissions) }
let(:reset_counts) { double(:reset_counts) }
let(:import_settings) { double(:import_settings) }
let(:all_entries) { double(:all_entries) }
let(:entry_validator) do
described_class.new(
current_user,
import_time,
spreadsheet_data,
editable_enterprises,
inventory_permissions,
reset_counts,
import_settings,
all_entries
)
end
let(:enterprise) { create(:enterprise, name: "User Enterprise") }
let(:entry_g) do
ProductImport::SpreadsheetEntry.new(
unscaled_units: "500",
units: "500",
unit_type: "g",
name: 'Tomato',
enterprise: enterprise,
enterprise_id: enterprise.id,
producer: enterprise,
producer_id: enterprise.id,
distributor: enterprise
)
end
let(:entry_kg) do
ProductImport::SpreadsheetEntry.new(
unscaled_units: "1",
units: "1",
unit_type: "kg",
name: 'Potatoes',
enterprise: enterprise,
enterprise_id: enterprise.id,
producer: enterprise,
producer_id: enterprise.id,
distributor: enterprise
)
end
describe "inventory validation" do
before do
allow(entry_validator).to receive(:import_into_inventory?) { true }
allow(entry_validator).to receive(:enterprise_validation) {}
allow(entry_validator).to receive(:producer_validation) {}
allow(entry_validator).to receive(:variant_of_product_validation) {}
end
context "products exist" do
let!(:product_g) {
create(
:simple_product,
supplier: enterprise,
on_hand: '100',
name: 'Tomato',
unit_value: 500,
variant_unit_scale: 1,
variant_unit: 'weight'
)
}
let!(:product_kg) {
create(
:simple_product,
supplier: enterprise,
on_hand: '100',
name: 'Potatoes',
unit_value: 1000,
variant_unit_scale: 1000,
variant_unit: 'weight'
)
}
it "validates a spreadsheet entry in g" do
entries = [entry_g]
entry_validator.validate_all(entries)
expect(entries.first.errors.count).to eq(0)
end
it "validates a spreadsheet entry in kg" do
entries = [entry_kg]
entry_validator.validate_all(entries)
expect(entries.first.errors.count).to eq(0)
end
end
context "products do not exist" do
# stub
end
end
describe "enterprise validation" do
# stub
end
describe "producer_validation" do
# stub
end
end

View File

@@ -5,8 +5,8 @@ require 'spec_helper'
describe Spree::Address do
describe "clone" do
it "creates a copy of the address with the exception of the id, updated_at and created_at attributes" do
state = create(:state)
original = create(:address,
state = build_stubbed(:state)
original = build_stubbed(:address,
address1: 'address1',
address2: 'address2',
alternative_phone: 'alternative_phone',
@@ -62,9 +62,9 @@ describe Spree::Address do
end
end
let(:state) { create(:state, name: 'maryland', abbr: 'md') }
let(:state) { build_stubbed(:state, name: 'maryland', abbr: 'md') }
let(:country) { state.country }
let(:address) { create(:address, country: country, state: state) }
let(:address) { build_stubbed(:address, country: country, state: state) }
before do
country.states_required = true
@@ -77,15 +77,18 @@ describe Spree::Address do
end
it "full state name is in state_name and country does contain that state" do
allow(country.states).to receive_messages find_all_by_name_or_abbr: [create(:state, name: 'alabama', abbr: 'al')]
address.update state_name: 'alabama'
allow(country).to receive_message_chain(:states, :find_all_by_name_or_abbr) do
[build_stubbed(:state, name: 'alabama', abbr: 'al')]
end
address.state_name = 'alabama'
expect(address).to be_valid
expect(address.state.name).to eq 'alabama'
expect(address.state_name).to eq 'alabama'
end
it "state abbr is in state_name and country does contain that state" do
allow(country.states).to receive_messages find_all_by_name_or_abbr: [state]
allow(country).to receive_message_chain(:states, :find_all_by_name_or_abbr) { [state] }
address.state_name = state.abbr
expect(address).to be_valid
expect(address.state.abbr).to eq state.abbr
@@ -93,7 +96,7 @@ describe Spree::Address do
end
it "both state and state_name are entered and country does contain the state" do
allow(country.states).to receive_messages find_all_by_name_or_abbr: [state]
allow(country).to receive_message_chain(:states, :find_all_by_name_or_abbr) { [state] }
address.state = state
address.state_name = 'maryland'
expect(address).to be_valid

View File

@@ -2,11 +2,11 @@ require 'spec_helper'
module Spree
describe Classification do
let!(:product) { create(:simple_product) }
let!(:taxon) { create(:taxon) }
let(:classification) { create(:classification, taxon: taxon, product: product) }
let(:product) { build_stubbed(:simple_product) }
let(:taxon) { build_stubbed(:taxon) }
it "won't destroy if classification is the primary taxon" do
classification = Classification.new(taxon: taxon, product: product)
product.primary_taxon = taxon
expect(classification.destroy).to be false
expect(classification.errors.messages[:base]).to eq(["Taxon #{taxon.name} is the primary taxon of #{product.name} and cannot be deleted"])

View File

@@ -1,6 +1,6 @@
require 'spec_helper'
describe Spree::Order do
describe Spree::Order::Checkout do
let(:order) { Spree::Order.new }
context "with default state machine" do
@@ -15,8 +15,6 @@ describe Spree::Order do
it "has the following transitions" do
transitions.each do |transition|
puts transition.keys.first
puts transition.values.first
transition = Spree::Order.find_transition(from: transition.keys.first,
to: transition.values.first)
expect(transition).to_not be_nil
@@ -302,12 +300,12 @@ describe Spree::Order do
end
describe 'event :restart_checkout' do
let(:order) { create(:order) }
let(:order) { build_stubbed(:order) }
context 'when the order is not complete' do
before { allow(order).to receive(:completed?) { false } }
it 'does transition to cart state' do
it 'transitions to cart state' do
expect(order.state).to eq('cart')
end
end

View File

@@ -54,7 +54,7 @@ describe Spree::Order do
end
it "does nothing when the line item is not found" do
p = create(:simple_product)
p = build_stubbed(:simple_product)
subject.set_variant_attributes(p.master, { 'max_quantity' => '3' }.with_indifferent_access)
end
end
@@ -730,7 +730,7 @@ describe Spree::Order do
describe "determining checkout steps for an order" do
let!(:enterprise) { create(:enterprise) }
let!(:order) { create(:order, distributor: enterprise) }
let!(:payment_method) { create(:stripe_payment_method, distributor_ids: [enterprise.id]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributor_ids: [enterprise.id]) }
let!(:payment) { create(:payment, order: order, payment_method: payment_method) }
it "does not include the :confirm step" do
@@ -823,10 +823,14 @@ describe Spree::Order do
end
describe '#restart_checkout!' do
let(:order) { build(:order, line_items: [build(:line_item)]) }
context 'when the order is complete' do
before { order.completed_at = Time.zone.now }
let(:order) do
build_stubbed(
:order,
completed_at: Time.zone.now,
line_items: [build_stubbed(:line_item)]
)
end
it 'raises' do
expect { order.restart_checkout! }
@@ -835,7 +839,13 @@ describe Spree::Order do
end
context 'when the is not complete' do
before { order.completed_at = nil }
let(:order) do
build(
:order,
completed_at: nil,
line_items: [build(:line_item)]
)
end
it 'transitions to :cart state' do
order.restart_checkout!

View File

@@ -847,7 +847,7 @@ describe Spree::Payment do
context "to Stripe payments" do
let(:shop) { create(:enterprise) }
let(:payment_method) { create(:stripe_payment_method, distributor_ids: [create(:distributor_enterprise).id], preferred_enterprise_id: shop.id) }
let(:payment_method) { create(:stripe_connect_payment_method, distributor_ids: [create(:distributor_enterprise).id], preferred_enterprise_id: shop.id) }
let(:payment) { create(:payment, order: order, payment_method: payment_method, amount: order.total) }
let(:calculator) { ::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }

View File

@@ -4,7 +4,7 @@ require 'spec_helper'
describe Spree::ShippingRate do
let(:shipment) { create(:shipment) }
let(:shipping_method) { create(:shipping_method) }
let(:shipping_method) { build_stubbed(:shipping_method) }
let(:shipping_rate) {
Spree::ShippingRate.new(shipment: shipment,
shipping_method: shipping_method,

View File

@@ -1,7 +1,7 @@
require 'spec_helper'
describe TagRule::FilterOrderCycles, type: :model do
let!(:tag_rule) { create(:filter_order_cycles_tag_rule) }
let!(:tag_rule) { build_stubbed(:filter_order_cycles_tag_rule) }
describe "determining whether tags match for a given exchange" do
context "when the exchange is nil" do

View File

@@ -1,7 +1,7 @@
require 'spec_helper'
describe TagRule::FilterPaymentMethods, type: :model do
let!(:tag_rule) { create(:filter_payment_methods_tag_rule) }
let!(:tag_rule) { build_stubbed(:filter_payment_methods_tag_rule) }
describe "determining whether tags match for a given payment method" do
context "when the payment method is nil" do

View File

@@ -1,7 +1,7 @@
require 'spec_helper'
describe TagRule::FilterProducts, type: :model do
let!(:tag_rule) { create(:filter_products_tag_rule) }
let!(:tag_rule) { build_stubbed(:filter_products_tag_rule) }
describe "determining whether tags match for a given variant" do
context "when the variant is nil" do

View File

@@ -1,7 +1,7 @@
require 'spec_helper'
describe TagRule::FilterShippingMethods, type: :model do
let!(:tag_rule) { create(:filter_shipping_methods_tag_rule) }
let!(:tag_rule) { build_stubbed(:filter_shipping_methods_tag_rule) }
describe "determining whether tags match for a given shipping method" do
context "when the shipping method is nil" do
@@ -11,7 +11,7 @@ describe TagRule::FilterShippingMethods, type: :model do
end
context "when the shipping method is not nil" do
let(:shipping_method) { create(:shipping_method, tag_list: ["member", "local", "volunteer"]) }
let(:shipping_method) { build_stubbed(:shipping_method, tag_list: ["member", "local", "volunteer"]) }
context "when the rule has no preferred shipping method tags specified" do
before { allow(tag_rule).to receive(:preferred_shipping_method_tags) { "" } }

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe TagRule, type: :model do
let!(:tag_rule) { create(:tag_rule) }
describe "validations" do
it "requires a enterprise" do
expect(tag_rule).to validate_presence_of :enterprise
expect(subject).to validate_presence_of(:enterprise)
end
end
end

View File

@@ -42,10 +42,15 @@ describe VariantOverride do
describe "validation" do
describe "ensuring that on_demand and count_on_hand are compatible" do
let(:variant_override) {
build(:variant_override, hub: hub, variant: variant,
on_demand: on_demand, count_on_hand: count_on_hand)
}
let(:variant_override) do
build_stubbed(
:variant_override,
hub: build_stubbed(:distributor_enterprise),
variant: build_stubbed(:variant),
on_demand: on_demand,
count_on_hand: count_on_hand
)
end
context "when using producer stock settings" do
let(:on_demand) { nil }
@@ -54,7 +59,7 @@ describe VariantOverride do
let(:count_on_hand) { nil }
it "is valid" do
expect(variant_override.save).to be_truthy
expect(variant_override).to be_valid
end
end
@@ -62,7 +67,7 @@ describe VariantOverride do
let(:count_on_hand) { 1 }
it "is invalid" do
expect(variant_override.save).to be_falsey
expect(variant_override).not_to be_valid
error_message = I18n.t("using_producer_stock_settings_but_count_on_hand_set",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
@@ -77,7 +82,7 @@ describe VariantOverride do
let(:count_on_hand) { nil }
it "is valid" do
expect(variant_override.save).to be_truthy
expect(variant_override).to be_valid
end
end
@@ -85,7 +90,7 @@ describe VariantOverride do
let(:count_on_hand) { 1 }
it "is invalid" do
expect(variant_override.save).to be_falsey
expect(variant_override).not_to be_valid
error_message = I18n.t("on_demand_but_count_on_hand_set",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
@@ -100,7 +105,7 @@ describe VariantOverride do
let(:count_on_hand) { nil }
it "is invalid" do
expect(variant_override.save).to be_falsey
expect(variant_override).not_to be_valid
error_message = I18n.t("limited_stock_but_no_count_on_hand",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
@@ -111,7 +116,7 @@ describe VariantOverride do
let(:count_on_hand) { 1 }
it "is valid" do
expect(variant_override.save).to be_truthy
expect(variant_override).to be_valid
end
end
end
@@ -139,7 +144,14 @@ describe VariantOverride do
end
describe "with price" do
let(:variant_override) { create(:variant_override, variant: variant, hub: hub, price: 12.34) }
let(:variant_override) do
build_stubbed(
:variant_override,
variant: build_stubbed(:variant),
hub: build_stubbed(:distributor_enterprise),
price: 12.34
)
end
it "returns the numeric price" do
expect(variant_override.price).to eq(12.34)
@@ -147,7 +159,15 @@ describe VariantOverride do
end
describe "with nil count on hand" do
let(:variant_override) { create(:variant_override, variant: variant, hub: hub, count_on_hand: nil, on_demand: true) }
let(:variant_override) do
build_stubbed(
:variant_override,
variant: build_stubbed(:variant),
hub: build_stubbed(:distributor_enterprise),
count_on_hand: nil,
on_demand: true
)
end
describe "stock_overridden?" do
it "returns false" do
@@ -164,7 +184,14 @@ describe VariantOverride do
end
describe "with count on hand" do
let(:variant_override) { create(:variant_override, variant: variant, hub: hub, count_on_hand: 12) }
let(:variant_override) do
build_stubbed(
:variant_override,
variant: build_stubbed(:variant),
hub: build_stubbed(:distributor_enterprise),
count_on_hand: 12
)
end
it "returns the numeric count on hand" do
expect(variant_override.count_on_hand).to eq(12)
@@ -177,6 +204,15 @@ describe VariantOverride do
end
describe "move_stock!" do
let(:variant_override) do
create(
:variant_override,
variant: variant,
hub: hub,
count_on_hand: 12
)
end
it "does nothing for quantity zero" do
variant_override.move_stock!(0)
expect(variant_override.reload.count_on_hand).to eq(12)
@@ -196,12 +232,24 @@ describe VariantOverride do
describe "checking default stock value is present" do
it "returns true when a default stock level has been set" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20)
vo = build_stubbed(
:variant_override,
variant: build_stubbed(:variant),
hub: build_stubbed(:distributor_enterprise),
count_on_hand: 12,
default_stock: 20
)
expect(vo.default_stock?).to be true
end
it "returns false when the override has no default stock level" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: nil)
vo = build_stubbed(
:variant_override,
variant: build_stubbed(:variant),
hub: build_stubbed(:distributor_enterprise),
count_on_hand: 12,
default_stock: nil
)
expect(vo.default_stock?).to be false
end
end

View File

@@ -14,7 +14,7 @@ describe "checking out an order with a Stripe Connect payment method", type: :re
distributors: [enterprise]
)
end
let!(:payment_method) { create(:stripe_payment_method, distributors: [enterprise]) }
let!(:payment_method) { create(:stripe_connect_payment_method, distributors: [enterprise]) }
let!(:stripe_account) { create(:stripe_account, enterprise: enterprise) }
let!(:line_item) { create(:line_item, price: 12.34) }
let!(:order) { line_item.order }

View File

@@ -23,7 +23,7 @@ describe OrderCheckoutRestart do
order.update_attribute(:state, "payment")
end
context "when order ship address is nil" do
xcontext "when order ship address is nil" do
before { order.ship_address = nil }
it "resets the order state, and clears incomplete shipments and payments" do
@@ -33,7 +33,7 @@ describe OrderCheckoutRestart do
end
end
context "when order ship address is not empty" do
xcontext "when order ship address is not empty" do
before { order.ship_address = order.address_from_distributor }
it "resets the order state, and clears incomplete shipments and payments" do

View File

@@ -130,6 +130,15 @@ RSpec.configure do |config|
ActionController::Base.perform_caching = caching
end
# Show javascript errors in test output with `js_debug: true`
config.after(:each, :js_debug) do
errors = page.driver.browser.manage.logs.get(:browser)
if errors.present?
message = errors.map(&:message).join("\n")
puts message
end
end
config.before(:all) { restart_driver }
# Geocoding

View File

@@ -0,0 +1,59 @@
// StripeJS fixture for using Stripe in feature specs. Mimics credit card form and Element objects.
// Based on: https://github.com/thoughtbot/fake_stripe/blob/v0.3.0/lib/fake_stripe/assets/v3.js
// The original has been adapted to work with OFN (see commit history for details).
class Element {
mount(el) {
if (typeof el === "string") {
el = document.querySelector(el);
}
el.classList.add('StripeElement');
el.innerHTML = `
<input id="stripe-cardnumber" name="cardnumber" placeholder="Card number" size="16" type="text">
<input name="exp-date" placeholder="MM / YY" size="6" type="text">
<input name="cvc" placeholder="CVC" size="3" type="text">
`;
}
addEventListener(event) {
return true;
}
}
window.Stripe = () => {
const fetchLastFour = () => {
return document.getElementById("stripe-cardnumber").value.substr(-4, 4);
};
return {
createPaymentMethod: () => {
return new Promise(resolve => {
resolve({
paymentMethod: {
id: "pm_123",
card: {
brand: 'visa',
last4: fetchLastFour(),
exp_month: "10",
exp_year: "2050"
}
}
});
});
},
elements: () => {
return {
create: (type, options) => new Element()
};
},
createToken: card => {
return new Promise(resolve => {
resolve({ token: { id: "tok_123", card: { last4: fetchLastFour() } } });
});
}
};
};

View File

@@ -0,0 +1,95 @@
# frozen_string_literal: true
module StripeHelper
def checkout_with_stripe
visit checkout_path
checkout_as_guest
fill_out_form(
free_shipping.name,
stripe_sca_payment_method.name,
save_default_addresses: false
)
fill_out_card_details
place_order
end
def fill_out_card_details
expect(page).to have_css("input[name='cardnumber']")
fill_in 'Card number', with: '4242424242424242'
fill_in 'MM / YY', with: "01/#{DateTime.now.year + 1}"
fill_in 'CVC', with: '123'
end
def setup_stripe
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
allow(Stripe).to receive(:publishable_key) { "pk_test_12345" }
Spree::Config.set(stripe_connect_enabled: true)
end
def stub_payment_intents_post_request(order:, response: {})
stub_request(:post, "https://api.stripe.com/v1/payment_intents")
.with(basic_auth: ["sk_test_12345", ""], body: /.*#{order.number}/)
.to_return(payment_intent_authorize_response_mock(response))
end
def stub_payment_intent_get_request(response: {}, stripe_account_header: true)
stub = stub_request(:get, "https://api.stripe.com/v1/payment_intents/pi_123")
stub = stub.with(headers: { 'Stripe-Account' => 'abc123' }) if stripe_account_header
stub.to_return(payment_intent_authorize_response_mock(response))
end
def stub_hub_payment_methods_request(response: {})
stub_request(:post, "https://api.stripe.com/v1/payment_methods")
.with(body: { payment_method: "pm_123" },
headers: { 'Stripe-Account' => 'abc123' })
.to_return(hub_payment_method_response_mock(response))
end
def stub_successful_capture_request(order:, response: {})
stub_capture_request(order, payment_successful_capture_mock(response))
end
def stub_failed_capture_request(order:, response: {})
stub_capture_request(order, payment_failed_capture_mock(response))
end
def stub_capture_request(order, response_mock)
stub_request(:post, "https://api.stripe.com/v1/payment_intents/pi_123/capture")
.with(body: { amount_to_capture: Spree::Money.new(order.total).cents },
headers: { 'Stripe-Account' => 'abc123' })
.to_return(response_mock)
end
private
def payment_intent_authorize_response_mock(options)
{ status: options[:code] || 200,
body: JSON.generate(id: "pi_123",
object: "payment_intent",
amount: 2000,
amount_received: 2000,
status: options[:intent_status] || "requires_capture",
last_payment_error: nil,
charges: { data: [{ id: "ch_1234", amount: 2000 }] }) }
end
def payment_successful_capture_mock(options)
{ status: options[:code] || 200,
body: JSON.generate(object: "payment_intent",
amount: 2000,
charges: { data: [{ id: "ch_1234", amount: 2000 }] }) }
end
def payment_failed_capture_mock(options)
{ status: options[:code] || 402,
body: JSON.generate(error: { message:
options[:message] || "payment-method-failure" }) }
end
def hub_payment_method_response_mock(options)
{ status: options[:code] || 200,
body: JSON.generate(id: "pm_456", customer: "cus_A123") }
end
end