mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-15 19:06:50 +00:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6806035a45 | ||
|
|
b1dbf4fe0e | ||
|
|
0829eaf0fb | ||
|
|
da2e3c7cd1 | ||
|
|
d5f793f451 | ||
|
|
8269665a7c | ||
|
|
1e7a3d7f46 | ||
|
|
6c95444339 | ||
|
|
48ea804615 | ||
|
|
8626e7b433 | ||
|
|
88b17372af | ||
|
|
961e559a31 | ||
|
|
85cee9e8cb | ||
|
|
1e6fa1666c | ||
|
|
ce856750aa | ||
|
|
07e625ec32 | ||
|
|
a20762a1f2 | ||
|
|
0edd341d46 | ||
|
|
c31380cd1d | ||
|
|
0c109d6384 | ||
|
|
427d1aaeff | ||
|
|
3b130327a5 | ||
|
|
ced5329835 | ||
|
|
ee6f0f7951 | ||
|
|
6149a79038 | ||
|
|
7321e4ef89 | ||
|
|
76def3a74f | ||
|
|
a5309627b7 | ||
|
|
80ea80f26e | ||
|
|
14f5ecfe0b | ||
|
|
df4ec67974 | ||
|
|
4b1e1afa69 | ||
|
|
ef70c1fc5c | ||
|
|
83456f94e3 | ||
|
|
ff9f374950 | ||
|
|
6c22ee43a7 | ||
|
|
6a2e49b9e7 | ||
|
|
ab0ecfb203 | ||
|
|
db2e760c4c | ||
|
|
da82df39c2 | ||
|
|
98fb60a5e0 | ||
|
|
854916907e | ||
|
|
1cc950a4d9 | ||
|
|
4858f5fb80 | ||
|
|
350a3c0e1e | ||
|
|
b850f10d2e | ||
|
|
362ab8a605 | ||
|
|
f5985de802 | ||
|
|
6c27ac5f99 | ||
|
|
bb38523767 | ||
|
|
e109ed6be7 | ||
|
|
780f31f98e | ||
|
|
eda904337b | ||
|
|
a85e500bb1 | ||
|
|
8480e16cac | ||
|
|
2eeba0483c | ||
|
|
e2ae566e88 | ||
|
|
01c6d5bc9c | ||
|
|
856ad24c6f | ||
|
|
678e7acde0 | ||
|
|
4914a0d3a8 | ||
|
|
e79187c1df | ||
|
|
c518e7e0dd | ||
|
|
2babfa9a7b | ||
|
|
c7d6b2782c | ||
|
|
3d71c2c0b8 | ||
|
|
807f91b0b8 | ||
|
|
6a722f2bbd | ||
|
|
d5ab3b47eb | ||
|
|
0100963a39 | ||
|
|
06a3e55348 | ||
|
|
bbef0bac5f | ||
|
|
dbe6c316f8 | ||
|
|
21eef7187b | ||
|
|
4f942db0ba | ||
|
|
9b5bd7bb1c | ||
|
|
c377e316d5 | ||
|
|
aa74b58810 | ||
|
|
8919adb6bf | ||
|
|
e04b85e900 | ||
|
|
b8fae8e3a3 | ||
|
|
01fdee9dba | ||
|
|
dfd54c1cbc | ||
|
|
2234ea6f5a | ||
|
|
bc77b8bcb2 | ||
|
|
38ab95a9a4 | ||
|
|
698d8b35fa | ||
|
|
a257a9e9d2 | ||
|
|
231fbcd11a | ||
|
|
c098ba0ce3 | ||
|
|
f7a9cc63a7 | ||
|
|
317618595c | ||
|
|
289fd09e20 | ||
|
|
239b6e7577 | ||
|
|
5bca561c4a | ||
|
|
6737270ab7 | ||
|
|
70bd2161ba | ||
|
|
1d01fc955d | ||
|
|
4080e7daa4 | ||
|
|
cca8f9faf6 | ||
|
|
ef85ac3e27 | ||
|
|
368772dad2 | ||
|
|
857f4e3a37 | ||
|
|
67d136548f | ||
|
|
4c77c41533 | ||
|
|
49bd9bd778 | ||
|
|
0ba3977422 | ||
|
|
4ed34bb942 | ||
|
|
52d82d0a96 | ||
|
|
d11d67561f | ||
|
|
d2c147109d | ||
|
|
b60c1c9003 | ||
|
|
59fb2abc5d | ||
|
|
6508897e3d | ||
|
|
1baba5b61c | ||
|
|
4f2b7094d0 | ||
|
|
d5800642e7 | ||
|
|
c3cf08156d | ||
|
|
8d4587506b | ||
|
|
99e905c768 | ||
|
|
46d38930d9 | ||
|
|
a4e8982351 | ||
|
|
13e15f823e | ||
|
|
508ecd6bf7 | ||
|
|
d10fda6227 | ||
|
|
2a8268ca73 | ||
|
|
8687e0199d | ||
|
|
2549d454ab | ||
|
|
645b4a9505 | ||
|
|
9f02ee3874 | ||
|
|
21fc14a9fe |
@@ -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
3
CODE_OF_CONDUCT.md
Normal 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).
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
1
Gemfile
1
Gemfile
@@ -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'
|
||||
|
||||
11
Gemfile.lock
11
Gemfile.lock
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import 'shared/variables/layout';
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Variables used in all other files
|
||||
//--------------------------------------------------------------
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
app/assets/stylesheets/shared/variables/_layout.scss
Normal file
6
app/assets/stylesheets/shared/variables/_layout.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
// Breakpoints
|
||||
$desktop_breakpoint: 1024px;
|
||||
$tablet_breakpoint: 768px;
|
||||
$phablet_breakpoint: 640px;
|
||||
$mobile_breakpoint: 480px;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'] : ''} "}
|
||||
|
||||
|
||||
5
app/views/shared/_stripe_js.html.haml
Normal file
5
app/views/shared/_stripe_js.html.haml
Normal 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"}
|
||||
@@ -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}"))
|
||||
|
||||
@@ -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}"))
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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') %>');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: متجر المنتج مخصص لمنتجاتك فقط ، إذا كنت ترغب في بيع المنتجات المنتجة خارج الموقع ، فحدد "متجر المنتج".
|
||||
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 يومًا. إذا كنت تريد حذف ملف تعريف الارتباط هذا كمستخدم ، فأنت تحتاج فقط إلى تسجيل الخروج. إذا كنت لا ترغب في تثبيت ملف تعريف الارتباط هذا على جهاز الكمبيوتر الخاص بك ، فلا يجب عليك تحديد مربع الاختيار "تذكرني" عند تسجيل الدخول."
|
||||
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 من خلال الرابط "حول" في القائمة العلوية."
|
||||
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: >
|
||||
متجر المنتج مخصص لمنتجاتك فقط ، إذا كنت ترغب في بيع المنتجات التي يتم
|
||||
إنتاجها / إنتاجها خارج الموقع ، فيرجى تحديد "مركز المنتج".
|
||||
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: "السبب"
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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: Nå
|
||||
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
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
@@ -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 '
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]) }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!).
|
||||
|
||||
@@ -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 } }
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
54
spec/features/admin/configuration/content_spec.rb
Normal file
54
spec/features/admin/configuration/content_spec.rb
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
116
spec/models/product_import/entry_validator_spec.rb
Normal file
116
spec/models/product_import/entry_validator_spec.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) { "" } }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
59
spec/support/fixtures/stripejs-mock.js
Normal file
59
spec/support/fixtures/stripejs-mock.js
Normal 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() } } });
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
95
spec/support/request/stripe_helper.rb
Normal file
95
spec/support/request/stripe_helper.rb
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user