mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-17 19:26:48 +00:00
Compare commits
276 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ccc8dfaf6 | ||
|
|
55b32c828c | ||
|
|
25dfd8ad40 | ||
|
|
2a5311493f | ||
|
|
57ac28cfbd | ||
|
|
fb02043e6e | ||
|
|
30d7cc89fa | ||
|
|
d14b5eb46b | ||
|
|
933b5f1606 | ||
|
|
4c7b8209b9 | ||
|
|
802ac647e3 | ||
|
|
c83bded763 | ||
|
|
03246d425d | ||
|
|
7585e3d1d6 | ||
|
|
50cdda7c63 | ||
|
|
d0af046e59 | ||
|
|
5811f3ead1 | ||
|
|
d7d3c9ea53 | ||
|
|
a02cc1de34 | ||
|
|
2184c7c06b | ||
|
|
928bf0d9c7 | ||
|
|
0d02b2afcf | ||
|
|
1e76f3f744 | ||
|
|
7c3a0a292f | ||
|
|
c5ca0976a5 | ||
|
|
be7b3d5a12 | ||
|
|
6304a085c0 | ||
|
|
7c708de937 | ||
|
|
5d51e5d393 | ||
|
|
464717dec5 | ||
|
|
7a06018c3e | ||
|
|
b8c76ff633 | ||
|
|
1ecc0bfe07 | ||
|
|
600d2d23c8 | ||
|
|
bf3211fd01 | ||
|
|
5fd0d9406d | ||
|
|
c78a6bea91 | ||
|
|
be9f33312b | ||
|
|
f23575302b | ||
|
|
0042ab2f28 | ||
|
|
53a63775fe | ||
|
|
461b1b26f3 | ||
|
|
f13d7d6845 | ||
|
|
e4d09b5404 | ||
|
|
415415273c | ||
|
|
d969190ca5 | ||
|
|
a180576c0a | ||
|
|
7fdaa0f0c7 | ||
|
|
ba750547a2 | ||
|
|
af8369ae1b | ||
|
|
829a73c58d | ||
|
|
a2691df64e | ||
|
|
6c8b175344 | ||
|
|
ad10053271 | ||
|
|
a508c55700 | ||
|
|
2712be3fa4 | ||
|
|
484326561f | ||
|
|
7ea96f88e8 | ||
|
|
de752b05a7 | ||
|
|
0b18344572 | ||
|
|
690474c01a | ||
|
|
e4c5893c1e | ||
|
|
67aeae4a6d | ||
|
|
c37218fdc5 | ||
|
|
dad21a52b2 | ||
|
|
97a65d760f | ||
|
|
63e92197f5 | ||
|
|
fd534bf629 | ||
|
|
64d83bfc4d | ||
|
|
83065a798e | ||
|
|
b661b3ee40 | ||
|
|
b528903aa8 | ||
|
|
38215c2a88 | ||
|
|
d280bf0d4d | ||
|
|
4c3916a93d | ||
|
|
677f31ffa8 | ||
|
|
5848a46149 | ||
|
|
03c91dfac1 | ||
|
|
60e12063cd | ||
|
|
e5b57af315 | ||
|
|
a396a7f0af | ||
|
|
d084789c56 | ||
|
|
dafac32e70 | ||
|
|
6ca39f3aa5 | ||
|
|
234a9ef1b4 | ||
|
|
24a1327805 | ||
|
|
33d77d57f4 | ||
|
|
0571b657aa | ||
|
|
e4958baddc | ||
|
|
c985747297 | ||
|
|
03fac6f285 | ||
|
|
f5ffdfc258 | ||
|
|
2108a282c8 | ||
|
|
a3a61967a8 | ||
|
|
404e7c1f37 | ||
|
|
1bdeda4a21 | ||
|
|
47916f823f | ||
|
|
ab4add1954 | ||
|
|
10fff31dca | ||
|
|
7fb85092ce | ||
|
|
7584e96759 | ||
|
|
f8ab64d71e | ||
|
|
ccb4c77d1f | ||
|
|
5ef1510fc7 | ||
|
|
1afd712ff4 | ||
|
|
699110258b | ||
|
|
3fb1df9bb3 | ||
|
|
14c03ead31 | ||
|
|
4480c2f0f0 | ||
|
|
b3ac5d8f41 | ||
|
|
6fb74c88cd | ||
|
|
38fd028a9f | ||
|
|
4e84310d63 | ||
|
|
66440f9e4c | ||
|
|
b8457ebece | ||
|
|
668fd1c7c0 | ||
|
|
aff934c814 | ||
|
|
6bb04f6cc6 | ||
|
|
0e815439b3 | ||
|
|
c7b01c37af | ||
|
|
ac8f3c811f | ||
|
|
1b820ea85c | ||
|
|
ec7b91bb68 | ||
|
|
c773cde191 | ||
|
|
db1065a69e | ||
|
|
9fa4bad0b4 | ||
|
|
a52c4b542c | ||
|
|
283abf9a88 | ||
|
|
f691d1aafd | ||
|
|
5724c3bb0a | ||
|
|
49ba83da6d | ||
|
|
19d1497c4b | ||
|
|
06200c9d3c | ||
|
|
2412658e51 | ||
|
|
0c4f22f847 | ||
|
|
1803ea3c38 | ||
|
|
93fda02e43 | ||
|
|
77958f9afe | ||
|
|
82e402f31a | ||
|
|
03fa3e2269 | ||
|
|
11fbe7d5c9 | ||
|
|
799c1f08de | ||
|
|
6b66458bfd | ||
|
|
3e0a5bac6a | ||
|
|
2c2023df03 | ||
|
|
7306d379a5 | ||
|
|
e15c61d862 | ||
|
|
35aeb98d45 | ||
|
|
d99cba3b6e | ||
|
|
4c6fd96bcc | ||
|
|
1eba17f048 | ||
|
|
ff088c6203 | ||
|
|
a1cb6928db | ||
|
|
40a5b60dcb | ||
|
|
2711736004 | ||
|
|
18c165e893 | ||
|
|
0aaa04295b | ||
|
|
7639e9a38d | ||
|
|
d783bd771f | ||
|
|
9dd9d14107 | ||
|
|
e942266dd7 | ||
|
|
c8f78904d6 | ||
|
|
a982fd1e2b | ||
|
|
7e8b2f6be5 | ||
|
|
921c7bbc3a | ||
|
|
eaff6b0c68 | ||
|
|
e1ab424481 | ||
|
|
e59c9720fc | ||
|
|
b25f0007f0 | ||
|
|
65c5cdd52f | ||
|
|
a2873ea553 | ||
|
|
3a593ff255 | ||
|
|
92e1193ffb | ||
|
|
016968dcb9 | ||
|
|
9d8608f210 | ||
|
|
323ca906bc | ||
|
|
c43b34e0fa | ||
|
|
bc7f0e0962 | ||
|
|
cf4f7c562a | ||
|
|
4c7bd4d6a8 | ||
|
|
523b266308 | ||
|
|
212413c8b3 | ||
|
|
b248dc598e | ||
|
|
e7b74b99ba | ||
|
|
89d2750fc4 | ||
|
|
7100111f93 | ||
|
|
3dcb66014e | ||
|
|
06971b7198 | ||
|
|
56f9adc5b7 | ||
|
|
38374a9835 | ||
|
|
8d6a8ee214 | ||
|
|
fec653186a | ||
|
|
ebe7456b66 | ||
|
|
8187669a25 | ||
|
|
a6aa0df53b | ||
|
|
116695b1d9 | ||
|
|
387ac40dc9 | ||
|
|
66320b5055 | ||
|
|
858d2cc6c2 | ||
|
|
43280da187 | ||
|
|
3b399b899c | ||
|
|
01d69c89aa | ||
|
|
a2801e40a2 | ||
|
|
abd4f0b923 | ||
|
|
e1eface5f8 | ||
|
|
5cd14253d0 | ||
|
|
be691df7ac | ||
|
|
7783b28ca2 | ||
|
|
6d51856821 | ||
|
|
890704b75c | ||
|
|
922484b2e7 | ||
|
|
3e7288648b | ||
|
|
f0f537ff8f | ||
|
|
b7f920c4b6 | ||
|
|
e5c9468d09 | ||
|
|
8aed173127 | ||
|
|
61ecca7257 | ||
|
|
7eba657b2f | ||
|
|
784de340d1 | ||
|
|
9191628f29 | ||
|
|
88410b1efd | ||
|
|
afea032361 | ||
|
|
3decb4056c | ||
|
|
8e9b08feca | ||
|
|
694995ea5d | ||
|
|
22de7252d0 | ||
|
|
2acf8e5125 | ||
|
|
e1f61e645d | ||
|
|
687d827ceb | ||
|
|
2885e38113 | ||
|
|
9f3ca58b55 | ||
|
|
ffe3228848 | ||
|
|
834231eb8f | ||
|
|
68f5aabd3f | ||
|
|
7e7ea92833 | ||
|
|
ebf22ceb19 | ||
|
|
c38c7c35bc | ||
|
|
2663f74767 | ||
|
|
b41de52012 | ||
|
|
214eb43122 | ||
|
|
01fc4e0513 | ||
|
|
6ce50a5fa5 | ||
|
|
4fbd2cfa52 | ||
|
|
383b28e170 | ||
|
|
bf55a15f81 | ||
|
|
eb7e6dc5b8 | ||
|
|
139ecfe604 | ||
|
|
43a6798db2 | ||
|
|
06d6579486 | ||
|
|
76df526002 | ||
|
|
06569ea24c | ||
|
|
25431f851b | ||
|
|
bab2420bb3 | ||
|
|
0b2acb3a76 | ||
|
|
27db9e604f | ||
|
|
c4e58ebb9e | ||
|
|
9a0ee254af | ||
|
|
5ce3e1e0d2 | ||
|
|
4b345d928c | ||
|
|
52b1e6c71a | ||
|
|
140e0b9cb1 | ||
|
|
b3f05d1a98 | ||
|
|
9644b145cc | ||
|
|
6f644936b0 | ||
|
|
b86759d7a7 | ||
|
|
9d1e3f0318 | ||
|
|
67adf3c801 | ||
|
|
6e78ae762a | ||
|
|
9535c5647f | ||
|
|
6f8bb793e1 | ||
|
|
2476050f29 | ||
|
|
1cce106977 | ||
|
|
98b55287f1 | ||
|
|
48a75c956f | ||
|
|
bec73adc89 | ||
|
|
23ec66e338 |
@@ -39,7 +39,6 @@ Layout/LineLength:
|
||||
- app/controllers/admin/variant_overrides_controller.rb
|
||||
- app/controllers/api/enterprise_attachment_controller.rb
|
||||
- app/controllers/api/product_images_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/adjustments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders_controller_decorator.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
@@ -354,7 +353,6 @@ Metrics/AbcSize:
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/base_controller.rb
|
||||
- app/controllers/cart_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/discourse_sso_controller.rb
|
||||
- app/controllers/enterprises_controller.rb
|
||||
- app/controllers/spree/admin/adjustments_controller_decorator.rb
|
||||
@@ -371,7 +369,6 @@ Metrics/AbcSize:
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
- app/controllers/spree/admin/variants_controller.rb
|
||||
- app/controllers/spree/checkout_controller.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/controllers/spree/user_passwords_controller.rb
|
||||
@@ -389,7 +386,6 @@ Metrics/AbcSize:
|
||||
- app/helpers/spree/admin/base_helper.rb
|
||||
- app/helpers/spree/admin/zones_helper.rb
|
||||
- app/helpers/spree/orders_helper.rb
|
||||
- app/jobs/subscription_placement_job.rb
|
||||
- app/mailers/producer_mailer.rb
|
||||
- app/models/calculator/flat_percent_per_item.rb
|
||||
- app/models/column_preference.rb
|
||||
@@ -416,6 +412,8 @@ Metrics/AbcSize:
|
||||
- app/services/create_order_cycle.rb
|
||||
- app/services/order_syncer.rb
|
||||
- app/services/subscription_validator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_decorator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/customers_report.rb
|
||||
@@ -493,7 +491,6 @@ Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- app/controllers/admin/enterprise_fees_controller.rb
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -510,6 +507,7 @@ Metrics/CyclomaticComplexity:
|
||||
- app/models/spree/product_decorator.rb
|
||||
- app/models/variant_override_set.rb
|
||||
- app/services/cart_service.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
@@ -523,7 +521,6 @@ Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -536,6 +533,7 @@ Metrics/PerceivedComplexity:
|
||||
- app/models/spree/ability_decorator.rb
|
||||
- app/models/spree/order_decorator.rb
|
||||
- app/models/spree/product_decorator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
@@ -560,7 +558,6 @@ Metrics/MethodLength:
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/base_controller.rb
|
||||
- app/controllers/cart_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/shop_controller.rb
|
||||
- app/controllers/spree/admin/image_settings_controller.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller.rb
|
||||
@@ -606,6 +603,7 @@ Metrics/MethodLength:
|
||||
- app/serializers/api/cached_enterprise_serializer.rb
|
||||
- app/services/order_cycle_form.rb
|
||||
- engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/column_preference_defaults.rb
|
||||
@@ -669,6 +667,7 @@ Metrics/ClassLength:
|
||||
- app/serializers/api/enterprise_shopfront_serializer.rb
|
||||
- app/services/cart_service.rb
|
||||
- engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_fee_calculator.rb
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
|
||||
@@ -117,6 +117,15 @@ Style/FormatString:
|
||||
Enabled: false
|
||||
StyleGuide: http://relaxed.ruby.style/#styleformatstring
|
||||
|
||||
Style/HashEachMethods:
|
||||
Enabled: false
|
||||
|
||||
Style/HashTransformKeys:
|
||||
Enabled: false
|
||||
|
||||
Style/HashTransformValues:
|
||||
Enabled: false
|
||||
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
StyleGuide: http://relaxed.ruby.style/#styleifunlessmodifier
|
||||
|
||||
33
Gemfile
33
Gemfile
@@ -3,7 +3,7 @@ ruby "2.3.7"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'i18n', '~> 0.6.11'
|
||||
gem 'i18n-js', '~> 3.5.1'
|
||||
gem 'i18n-js', '~> 3.6.0'
|
||||
gem 'rails', '~> 3.2.22'
|
||||
gem 'rails-i18n', '~> 3.0.0'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
@@ -19,9 +19,8 @@ gem 'activerecord-postgresql-adapter'
|
||||
gem 'pg', '~> 0.21.0'
|
||||
|
||||
# OFN-maintained and patched version of Spree v2.0.4. See
|
||||
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-2.0-upgrade
|
||||
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Tech-Doc:-OFN's-Spree-fork%F0%9F%8D%B4
|
||||
# for details.
|
||||
gem 'spree_backend', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
|
||||
gem 'spree_core', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
|
||||
|
||||
gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable'
|
||||
@@ -39,7 +38,7 @@ gem 'activemerchant', '~> 1.78'
|
||||
gem 'devise', '~> 2.2.5'
|
||||
gem 'devise-encryptable', '0.2.0'
|
||||
gem 'jwt', '~> 2.2'
|
||||
gem 'oauth2', '~> 1.4.2' # Used for Stripe Connect
|
||||
gem 'oauth2', '~> 1.4.4' # Used for Stripe Connect
|
||||
|
||||
gem 'daemons'
|
||||
gem 'delayed_job_active_record'
|
||||
@@ -62,7 +61,7 @@ gem 'haml'
|
||||
gem 'rabl'
|
||||
gem 'redcarpet'
|
||||
gem 'sass', "~> 3.3"
|
||||
gem 'sass-rails', '~> 3.2.3', groups: [:default, :assets]
|
||||
gem 'sass-rails', '~> 3.2.3'
|
||||
gem 'truncate_html'
|
||||
gem 'unicorn'
|
||||
|
||||
@@ -93,33 +92,31 @@ gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'foreigner'
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.8.2'
|
||||
gem 'roo', '~> 2.8.3'
|
||||
|
||||
gem 'whenever', require: false
|
||||
|
||||
gem 'test-unit', '~> 3.3'
|
||||
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'mini_racer', '0.2.9'
|
||||
gem 'mini_racer', '0.2.9'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
gem 'angular-rails-templates', '~> 0.3.0'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
gem 'momentjs-rails'
|
||||
gem 'turbo-sprockets-rails3'
|
||||
end
|
||||
gem 'angular-rails-templates', '~> 0.3.0'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
gem 'momentjs-rails'
|
||||
gem 'turbo-sprockets-rails3'
|
||||
|
||||
gem "foundation-rails"
|
||||
gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3"
|
||||
|
||||
gem 'jquery-migrate-rails'
|
||||
gem 'jquery-rails', '3.0.4'
|
||||
gem 'jquery-ui-rails', '~> 4.0.0'
|
||||
gem 'select2-rails', '~> 3.4.7'
|
||||
|
||||
gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', ref: '60da2ae4c44cbb4c8d602f59fb5fff8d0f21db3c'
|
||||
|
||||
|
||||
57
Gemfile.lock
57
Gemfile.lock
@@ -34,18 +34,6 @@ GIT
|
||||
revision: 8a8585a43cd04d1a50dc65227f337a91b18d66d5
|
||||
branch: 2-0-4-stable
|
||||
specs:
|
||||
spree_api (2.0.4)
|
||||
rabl (= 0.8.4)
|
||||
spree_core (= 2.0.4)
|
||||
versioncake (= 1.0.0)
|
||||
spree_backend (2.0.4)
|
||||
deface (>= 0.9.0)
|
||||
jquery-rails (~> 3.0.0)
|
||||
jquery-ui-rails (~> 4.0.0)
|
||||
rails (~> 3.2.8)
|
||||
select2-rails (~> 3.4.7)
|
||||
spree_api (= 2.0.4)
|
||||
spree_core (= 2.0.4)
|
||||
spree_core (2.0.4)
|
||||
activemerchant (~> 1.34)
|
||||
acts_as_list (= 0.2.0)
|
||||
@@ -223,7 +211,7 @@ GEM
|
||||
activerecord (>= 3.2.0, < 5.0)
|
||||
fog (~> 1.0)
|
||||
rails (>= 3.2.0, < 5.0)
|
||||
ddtrace (0.32.0)
|
||||
ddtrace (0.33.1)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
deface (1.0.2)
|
||||
@@ -261,7 +249,7 @@ GEM
|
||||
factory_bot_rails (4.10.0)
|
||||
factory_bot (~> 4.10.0)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.17.1)
|
||||
faraday (1.0.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffaker (1.22.1)
|
||||
ffi (1.11.3)
|
||||
@@ -437,7 +425,7 @@ GEM
|
||||
httparty (0.16.2)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.11)
|
||||
i18n-js (3.5.1)
|
||||
i18n-js (3.6.0)
|
||||
i18n (>= 0.6.6)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
@@ -458,7 +446,7 @@ GEM
|
||||
kaminari (0.14.1)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.11.2)
|
||||
kgio (2.11.3)
|
||||
knapsack (1.18.0)
|
||||
rake
|
||||
launchy (2.4.3)
|
||||
@@ -479,20 +467,20 @@ GEM
|
||||
railties (>= 3.1)
|
||||
money (5.1.1)
|
||||
i18n (~> 0.6.0)
|
||||
msgpack (1.3.1)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.1.1)
|
||||
newrelic_rpm (3.18.1.330)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
oauth2 (1.4.2)
|
||||
oauth2 (1.4.4)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.10.2)
|
||||
oj (3.10.5)
|
||||
orm_adapter (0.5.0)
|
||||
paper_trail (5.2.3)
|
||||
activerecord (>= 3.0, < 6.0)
|
||||
@@ -506,7 +494,7 @@ GEM
|
||||
parallel (1.19.1)
|
||||
paranoia (1.3.4)
|
||||
activerecord (~> 3.1)
|
||||
parser (2.7.0.2)
|
||||
parser (2.7.0.4)
|
||||
ast (~> 2.4.0)
|
||||
paypal-sdk-core (0.2.10)
|
||||
multi_json (~> 1.0)
|
||||
@@ -559,7 +547,7 @@ GEM
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
raindrops (0.19.1)
|
||||
rake (13.0.0)
|
||||
ransack (0.7.2)
|
||||
actionpack (~> 3.0)
|
||||
@@ -578,15 +566,16 @@ GEM
|
||||
redcarpet (3.5.0)
|
||||
request_store (1.4.1)
|
||||
rack (>= 1.4)
|
||||
rexml (3.2.4)
|
||||
roadie (3.4.0)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.5)
|
||||
roadie-rails (1.3.0)
|
||||
railties (>= 3.0, < 5.3)
|
||||
roadie (~> 3.1)
|
||||
roo (2.8.2)
|
||||
roo (2.8.3)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (>= 1.2.1, < 2.0.0)
|
||||
rubyzip (>= 1.3.0, < 3.0.0)
|
||||
rspec (3.9.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
@@ -610,11 +599,12 @@ GEM
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.9.0)
|
||||
rubocop (0.79.0)
|
||||
rubocop (0.80.1)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
rexml
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
rubocop-rails (2.4.2)
|
||||
@@ -658,7 +648,7 @@ GEM
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
state_machine (1.2.0)
|
||||
stringex (1.5.1)
|
||||
stripe (5.11.0)
|
||||
stripe (5.15.0)
|
||||
test-unit (3.3.5)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
@@ -676,24 +666,20 @@ GEM
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.6.1)
|
||||
unicorn (5.5.2)
|
||||
unicorn (5.5.3)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
rack
|
||||
unicorn
|
||||
uuidtools (2.1.5)
|
||||
versioncake (1.0.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webdrivers (4.2.0)
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (>= 3.0, < 4.0)
|
||||
webmock (3.8.0)
|
||||
webmock (3.8.2)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -753,10 +739,11 @@ DEPENDENCIES
|
||||
gmaps4rails
|
||||
haml
|
||||
i18n (~> 0.6.11)
|
||||
i18n-js (~> 3.5.1)
|
||||
i18n-js (~> 3.6.0)
|
||||
immigrant
|
||||
jquery-migrate-rails
|
||||
jquery-rails (= 3.0.4)
|
||||
jquery-ui-rails (~> 4.0.0)
|
||||
json_spec (~> 1.1.4)
|
||||
jwt (~> 2.2)
|
||||
kaminari (~> 0.14.1)
|
||||
@@ -766,7 +753,7 @@ DEPENDENCIES
|
||||
momentjs-rails
|
||||
newrelic_rpm (~> 3.0)
|
||||
nokogiri (>= 1.6.7.1)
|
||||
oauth2 (~> 1.4.2)
|
||||
oauth2 (~> 1.4.4)
|
||||
ofn-qz!
|
||||
oj
|
||||
order_management!
|
||||
@@ -783,19 +770,19 @@ DEPENDENCIES
|
||||
rails_safe_tasks (~> 1.0)
|
||||
redcarpet
|
||||
roadie-rails (~> 1.3.0)
|
||||
roo (~> 2.8.2)
|
||||
roo (~> 2.8.3)
|
||||
rspec-rails (>= 3.5.2)
|
||||
rspec-retry
|
||||
rubocop
|
||||
rubocop-rails
|
||||
sass (~> 3.3)
|
||||
sass-rails (~> 3.2.3)
|
||||
select2-rails (~> 3.4.7)
|
||||
selenium-webdriver
|
||||
shoulda-matchers
|
||||
simple_form!
|
||||
simplecov
|
||||
spinjs-rails
|
||||
spree_backend!
|
||||
spree_core!
|
||||
spree_i18n!
|
||||
spree_paypal_express!
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
//= require jquery.jstree/jquery.jstree
|
||||
//= require jquery.vAlign
|
||||
//= require jquery.horizontalNav
|
||||
//= require jquery.adaptivemenu
|
||||
//= require angular
|
||||
//= require angular-resource
|
||||
//= require angular-animate
|
||||
@@ -28,11 +29,9 @@
|
||||
|
||||
// spree
|
||||
//= require spree
|
||||
//= require admin/spree-select2
|
||||
//= require admin/spree_backend
|
||||
//= require admin/spree/spree-select2
|
||||
//= require modernizr
|
||||
//= require spin
|
||||
//= require jquery.adaptivemenu
|
||||
//= require equalize
|
||||
//= require css_browser_selector_dev
|
||||
//= require responsive-tables
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor) ->
|
||||
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser) ->
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.columns = Columns.columns
|
||||
@@ -38,6 +38,8 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.query = ""
|
||||
$scope.DisplayProperties = DisplayProperties
|
||||
|
||||
$scope.sortOptions = SortOptions
|
||||
|
||||
$scope.initialise = ->
|
||||
$scope.fetchProducts()
|
||||
|
||||
@@ -54,6 +56,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
'q[name_cont]': $scope.query,
|
||||
'q[supplier_id_eq]': $scope.producerFilter,
|
||||
'q[primary_taxon_id_eq]': $scope.categoryFilter,
|
||||
'q[s]': $scope.sorting,
|
||||
import_date: $scope.importDateFilter,
|
||||
page: $scope.page,
|
||||
per_page: $scope.per_page
|
||||
@@ -103,9 +106,16 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.categoryFilter = "0"
|
||||
$scope.importDateFilter = "0"
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchProducts()
|
||||
, true
|
||||
|
||||
confirm_unsaved_changes = () ->
|
||||
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
|
||||
|
||||
|
||||
editProductUrl = (product, variant) ->
|
||||
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
|
||||
@@ -220,10 +230,9 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
BulkProducts.updateVariantLists(data.products || [])
|
||||
$timeout -> $scope.displaySuccess()
|
||||
).error (data, status) ->
|
||||
if status == 400 && data.errors? && data.errors.length > 0
|
||||
errors = error + "\n" for error in data.errors
|
||||
alert t("products_update_error") + "\n" + errors
|
||||
$scope.displayFailure t("products_update_error")
|
||||
if status == 400 && data.errors?
|
||||
errorsString = ErrorsParser.toString(data.errors, status)
|
||||
$scope.displayFailure t("products_update_error") + "\n" + errorsString
|
||||
else
|
||||
$scope.displayFailure t("products_update_error_data") + status
|
||||
|
||||
@@ -274,7 +283,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
|
||||
|
||||
$scope.displayFailure = (failMessage) ->
|
||||
StatusMessage.display 'failure', t("products_update_error_msg") + "#{failMessage}"
|
||||
StatusMessage.display 'failure', t("products_update_error_msg") + " #{failMessage}"
|
||||
|
||||
|
||||
$scope.displayDirtyProducts = ->
|
||||
|
||||
@@ -3,6 +3,11 @@ angular.module("admin.indexUtils").factory 'SortOptions', ->
|
||||
predicate: ""
|
||||
reverse: true
|
||||
|
||||
getSortingExpr: () ->
|
||||
sortingExpr = this.predicate + ' desc' if this.reverse
|
||||
sortingExpr = this.predicate + ' asc' if !this.reverse
|
||||
sortingExpr
|
||||
|
||||
toggle: (predicate) ->
|
||||
@reverse = (@predicate == predicate) && !@reverse
|
||||
@predicate = predicate
|
||||
|
||||
@@ -48,13 +48,15 @@ angular.module('admin.orderCycles')
|
||||
|
||||
return if enterprise.last_page_loaded? && enterprise.last_page_loaded >= page
|
||||
enterprise.last_page_loaded = page
|
||||
enterprise.loaded_variants ?= 0
|
||||
|
||||
incoming = true if $scope.view == 'incoming'
|
||||
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: incoming, page: page}
|
||||
ExchangeProduct.index params, (products, num_of_pages, num_of_products) ->
|
||||
ExchangeProduct.index params, (products, num_of_pages) ->
|
||||
enterprise.num_of_pages = num_of_pages
|
||||
enterprise.num_of_products = num_of_products
|
||||
enterprise.supplied_products.push products...
|
||||
angular.forEach products, (product) ->
|
||||
enterprise.loaded_variants += product.variants.length
|
||||
|
||||
$scope.loadMoreExchangeProducts = (exchange) ->
|
||||
$scope.loadExchangeProducts(exchange, $scope.enterprises[exchange.enterprise_id].last_page_loaded + 1)
|
||||
|
||||
@@ -64,10 +64,10 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, Reque
|
||||
$scope.selected_orders.push order.id if $scope.select_all
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
if sort && sort.predicate != ""
|
||||
$scope.sorting = sort.predicate + ' desc' if sort.reverse
|
||||
$scope.sorting = sort.predicate + ' asc' if !sort.reverse
|
||||
$scope.fetchResults()
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchProducts()
|
||||
, true
|
||||
|
||||
$scope.capturePayment = (order) ->
|
||||
|
||||
@@ -3,6 +3,9 @@ angular.module("admin.orders").directive 'customerSearchOverride', ->
|
||||
scope:
|
||||
distributorId: '@'
|
||||
link: (scope, element, attr) ->
|
||||
if $('#customer_autocomplete_template').length > 0
|
||||
customerTemplate = Handlebars.compile($('#customer_autocomplete_template').text())
|
||||
|
||||
formatCustomerResult = (customer) ->
|
||||
customerTemplate
|
||||
customer: customer
|
||||
|
||||
@@ -21,7 +21,7 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
|
||||
year: @form_data.card_year
|
||||
verification_value: @form_data.card_verification_value
|
||||
}
|
||||
when 'stripe'
|
||||
when 'stripe', 'stripe_sca'
|
||||
angular.extend munged_payment.payment, {
|
||||
source_attributes:
|
||||
gateway_payment_profile_id: @form_data.token
|
||||
@@ -35,6 +35,8 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
|
||||
purchase: ->
|
||||
if @paymentMethodType() == 'stripe'
|
||||
AdminStripeElements.requestToken(@form_data, @submit)
|
||||
else if @paymentMethodType() == 'stripe_sca'
|
||||
AdminStripeElements.createPaymentMethod(@form_data, @submit)
|
||||
else
|
||||
@submit()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
stripe: null
|
||||
card: null
|
||||
|
||||
# New Stripe Elements method
|
||||
# Create Token to be used with the Stripe Charges API
|
||||
requestToken: (secrets, submit) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
@@ -20,6 +20,21 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# Create Payment Method to be used with the Stripe Payment Intents API
|
||||
createPaymentMethod: (secrets, submit) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
cardData = @makeCardData(secrets)
|
||||
|
||||
@stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
|
||||
if(response.error)
|
||||
StatusMessage.display 'error', response.error.message
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = response.paymentMethod.card.brand
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
|
||||
@@ -21,9 +21,7 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
|
||||
|
||||
else
|
||||
value = @variant.unit_value
|
||||
unit_name = @variant.product.variant_unit_name
|
||||
# TODO needs to add pluralize to line below
|
||||
# unit_name = unit_name if value > 1
|
||||
unit_name = @pluralize(@variant.product.variant_unit_name, value)
|
||||
|
||||
value = parseInt(value, 10) if value == parseInt(value, 10)
|
||||
|
||||
@@ -32,6 +30,21 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
|
||||
|
||||
[value, unit_name]
|
||||
|
||||
pluralize: (unit_name, count) ->
|
||||
return unit_name if count == undefined
|
||||
unit_key = @unit_key(unit_name)
|
||||
return unit_name unless unit_key
|
||||
I18n.t(["inflections", unit_key], {count: count, defaultValue: unit_name})
|
||||
|
||||
unit_key: (unit_name) ->
|
||||
unless I18n.unit_keys
|
||||
I18n.unit_keys = {}
|
||||
for key, translations of I18n.t("inflections")
|
||||
for quantifier, translation of translations
|
||||
I18n.unit_keys[translation.toLowerCase()] = key
|
||||
|
||||
I18n.unit_keys[unit_name.toLowerCase()]
|
||||
|
||||
option_value_value_unit_scaled: ->
|
||||
[unit_scale, unit_name] = @scale_for_unit_value()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//= require_self
|
||||
//= require admin/handlebar_extensions
|
||||
//= require admin/variant_autocomplete
|
||||
//= require admin/spree/orders/variant_autocomplete
|
||||
|
||||
/**
|
||||
This is a collection of javascript functions and whatnot
|
||||
@@ -21,12 +21,14 @@ jQuery(function($) {
|
||||
if (typeof $('.field.checkbox label').vAlign === 'function' )
|
||||
$('.field.checkbox label').vAlign()
|
||||
|
||||
// if (typeof Spree !== 'undefined') {
|
||||
// $('.main-menu-wrapper ul').AdaptiveMenu({
|
||||
// text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
|
||||
// klass: "dropdown"
|
||||
// });
|
||||
// }
|
||||
// We activate AdaptiveMenu only if not on webdriver
|
||||
// Re-adjusting the admin menu during tests causes tests to fail.
|
||||
if (!navigator.webdriver && typeof Spree !== 'undefined') {
|
||||
$('.main-menu-wrapper ul').AdaptiveMenu({
|
||||
text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
|
||||
klass: "dropdown"
|
||||
});
|
||||
}
|
||||
|
||||
// Add some tips
|
||||
if (typeof $('.with-tip').powerTip === 'function' ) {
|
||||
@@ -174,6 +176,28 @@ $(document).ready(function() {
|
||||
$(target).prepend(new_table_row);
|
||||
})
|
||||
|
||||
$('body').on('click', '.delete-resource', function() {
|
||||
var el = $(this);
|
||||
if (confirm(el.data("confirm"))) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $(this).attr("href"),
|
||||
data: {
|
||||
_method: 'delete',
|
||||
authenticity_token: AUTH_TOKEN
|
||||
},
|
||||
dataType: 'html',
|
||||
success: function(response) {
|
||||
el.parents("tr").fadeOut('hide');
|
||||
},
|
||||
error: function(response, textStatus, errorThrown) {
|
||||
show_flash_error(response.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Fix sortable helper
|
||||
var fixHelper = function(e, ui) {
|
||||
ui.children().each(function() {
|
||||
|
||||
16
app/assets/javascripts/admin/spree/calculator.js
Normal file
16
app/assets/javascripts/admin/spree/calculator.js
Normal file
@@ -0,0 +1,16 @@
|
||||
$(function() {
|
||||
var calculator_select = $('select#calc_type')
|
||||
var original_calc_type = calculator_select.attr('value');
|
||||
$('.calculator-settings-warning').hide();
|
||||
calculator_select.change(function() {
|
||||
if (calculator_select.attr('value') == original_calc_type) {
|
||||
$('div.calculator-settings').show();
|
||||
$('.calculator-settings-warning').hide();
|
||||
$('.calculator-settings').find('input,textarea').prop("disabled", false);
|
||||
} else {
|
||||
$('div.calculator-settings').hide();
|
||||
$('.calculator-settings-warning').show();
|
||||
$('.calculator-settings').find('input,texttarea').prop("disabled", true);
|
||||
}
|
||||
});
|
||||
})
|
||||
59
app/assets/javascripts/admin/spree/image_settings.js.erb
Normal file
59
app/assets/javascripts/admin/spree/image_settings.js.erb
Normal file
@@ -0,0 +1,59 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
if ($('input#preferences_use_s3[type="checkbox"]:checked').length > 0) {
|
||||
$('#s3_settings, #s3_headers').show();
|
||||
}
|
||||
|
||||
// Toggle display of S3 settings based on value of use_s3 checkbox
|
||||
$('input#preferences_use_s3[type="checkbox"]').click(function() {
|
||||
$('#s3_settings, #s3_headers').toggle();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_style', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parent().remove();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_new_attachment_styles', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.new_attachment_styles').remove();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_new_s3_headers', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.new_s3_headers').remove();
|
||||
});
|
||||
|
||||
// Handle adding new styles
|
||||
var styles_hash_index = 1;
|
||||
$(document).on('click', '.add_new_style', function(e) {
|
||||
e.preventDefault();
|
||||
$('#new-styles').append(generate_html_for_hash("new_attachment_styles", styles_hash_index));
|
||||
});
|
||||
|
||||
// Handle adding new headers
|
||||
var headers_hash_index = 1;
|
||||
$(document).on('click', '.add_header', function(e) {
|
||||
e.preventDefault();
|
||||
$('#headers_list').append(generate_html_for_hash("new_s3_headers", headers_hash_index));
|
||||
});
|
||||
|
||||
// Generates html for new paperclip styles form fields
|
||||
generate_html_for_hash = function(hash_name, index) {
|
||||
var html = '<div class="' + hash_name + ' row"><div class="field">';
|
||||
html += '<div class="five columns">';
|
||||
html += '<label for="' + hash_name + '_' + index + '_name">';
|
||||
html += Spree.translations.name + '</label>';
|
||||
html += '<input id="' + hash_name + '_' + index + '_name" name="' + hash_name + '[' + index + '][name]" type="text" class="fullwidth"><br>';
|
||||
html += '</div><div class="five columns">'
|
||||
html += '<label for="' + hash_name + '_' + index + '_value">';
|
||||
html += Spree.translations.value + '</label>';
|
||||
html += '<input id="' + hash_name + '_' + index + '_value" name="' + hash_name + '[' + index + '][value]" type="text" class="fullwidth">';
|
||||
html += '</div><div class="two columns">'
|
||||
html += '<a href="#" title="' + Spree.translations.destroy + '" class="destroy_' + hash_name + ' with-tip button" style="margin-top: 19px;"><i class="icon-trash"></i> ' + Spree.translations.destroy + '</a>';
|
||||
html += '</div></div></div>';
|
||||
|
||||
index += 1;
|
||||
return html;
|
||||
};
|
||||
});
|
||||
23
app/assets/javascripts/admin/spree/nested-attribute.js
Normal file
23
app/assets/javascripts/admin/spree/nested-attribute.js
Normal file
@@ -0,0 +1,23 @@
|
||||
//On page load
|
||||
replace_ids = function(s){
|
||||
var new_id = new Date().getTime();
|
||||
return s.replace(/NEW_RECORD/g, new_id);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$('a[id*=nested]').click(function() {
|
||||
var template = $(this).attr('href').replace(/.*#/, '');
|
||||
html = replace_ids(eval(template));
|
||||
$('#ul-' + $(this).attr('id')).append(html);
|
||||
update_remove_links();
|
||||
});
|
||||
update_remove_links();
|
||||
})
|
||||
|
||||
var update_remove_links = function() {
|
||||
$('.remove').click(function() {
|
||||
$(this).prevAll(':first').val(1);
|
||||
$(this).parent().hide();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
@@ -15,11 +15,11 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
// $('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
|
||||
$('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
|
||||
|
||||
//handle shipping method edit click
|
||||
// $('a.edit-method').click(toggleMethodEdit);
|
||||
// $('a.cancel-method').click(toggleMethodEdit);
|
||||
$('a.edit-method').click(toggleMethodEdit);
|
||||
$('a.cancel-method').click(toggleMethodEdit);
|
||||
|
||||
handle_shipping_method_save = function(){
|
||||
var link = $(this);
|
||||
@@ -38,11 +38,11 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
// $('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
|
||||
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
|
||||
|
||||
//handle tracking edit click
|
||||
// $('a.edit-tracking').click(toggleTrackingEdit);
|
||||
// $('a.cancel-tracking').click(toggleTrackingEdit);
|
||||
$('a.edit-tracking').click(toggleTrackingEdit);
|
||||
$('a.cancel-tracking').click(toggleTrackingEdit);
|
||||
|
||||
handle_tracking_save = function(){
|
||||
var link = $(this);
|
||||
|
||||
@@ -29,10 +29,10 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
//handle edit click
|
||||
// $('a.edit-item').click(toggleItemEdit);
|
||||
$('a.edit-item').click(toggleItemEdit);
|
||||
|
||||
//handle cancel click
|
||||
// $('a.cancel-item').click(toggleItemEdit);
|
||||
$('a.cancel-item').click(toggleItemEdit);
|
||||
|
||||
handle_save_click = function(){
|
||||
var save = $(this);
|
||||
@@ -46,7 +46,7 @@ $(document).ready(function() {
|
||||
adjustItems(shipment_number, variant_id, quantity);
|
||||
return false;
|
||||
}
|
||||
// $('a.save-item').click(handle_save_click);
|
||||
$('a.save-item').click(handle_save_click);
|
||||
|
||||
handle_delete_click = function(){
|
||||
var del = $(this);
|
||||
@@ -57,7 +57,7 @@ $(document).ready(function() {
|
||||
|
||||
adjustItems(shipment_number, variant_id, 0);
|
||||
}
|
||||
// $('a.delete-item').click(handle_delete_click);
|
||||
$('a.delete-item').click(handle_delete_click);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -139,39 +139,3 @@ addVariantFromStockLocation = function() {
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
formatVariantResult = function(variant) {
|
||||
if (variant["images"][0] != undefined && variant["images"][0].urls != undefined) {
|
||||
variant.image = variant.images[0].urls.mini
|
||||
}
|
||||
return variantTemplate({ variant: variant })
|
||||
}
|
||||
|
||||
$.fn.variantAutocomplete = function() {
|
||||
this.parent().children(".options_placeholder").attr('id', this.parent().data('index'))
|
||||
this.select2({
|
||||
placeholder: Spree.translations.variant_placeholder,
|
||||
minimumInputLength: 3,
|
||||
ajax: {
|
||||
url: Spree.url(Spree.routes.variants_search),
|
||||
datatype: 'json',
|
||||
data: function(term, page) {
|
||||
return {
|
||||
q: {
|
||||
"product_name_or_sku_cont": term
|
||||
}
|
||||
}
|
||||
},
|
||||
results: function (data, page) {
|
||||
window.variants = data['variants'];
|
||||
|
||||
return { results: data['variants'] }
|
||||
}
|
||||
},
|
||||
formatResult: formatVariantResult,
|
||||
formatSelection: function (variant) {
|
||||
$(this.element).parent().children('.options_placeholder').html(variant.options_text)
|
||||
return variant.name;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
8
app/assets/javascripts/admin/spree/states.js
Executable file
8
app/assets/javascripts/admin/spree/states.js
Executable file
@@ -0,0 +1,8 @@
|
||||
$(document).ready(function() {
|
||||
$("#country").change(function() {
|
||||
var new_state_link_href = $('#new_state_link a').attr('href');
|
||||
var selected_country_id = $('#country option:selected').attr('value');
|
||||
var new_link = new_state_link_href.replace(/countries\/(\d+)/, 'countries/'+selected_country_id);
|
||||
$('#new_state_link a').attr('href', new_link);
|
||||
});
|
||||
});
|
||||
43
app/assets/javascripts/admin/spree/zone.js.coffee
Normal file
43
app/assets/javascripts/admin/spree/zone.js.coffee
Normal file
@@ -0,0 +1,43 @@
|
||||
$ ->
|
||||
($ '#country_based').click ->
|
||||
show_country()
|
||||
|
||||
($ '#state_based').click ->
|
||||
show_state()
|
||||
|
||||
if ($ '#country_based').is(':checked')
|
||||
show_country()
|
||||
else if ($ '#state_based').is(':checked')
|
||||
show_state()
|
||||
else
|
||||
show_state()
|
||||
($ '#state_based').click()
|
||||
|
||||
|
||||
show_country = ->
|
||||
($ '#state_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#state_members').hide()
|
||||
($ '#zone_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#zone_members').hide()
|
||||
($ '#country_members :input').each ->
|
||||
($ this).prop 'disabled', false
|
||||
|
||||
($ '#country_members').show()
|
||||
|
||||
show_state = ->
|
||||
($ '#country_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#country_members').hide()
|
||||
($ '#zone_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#zone_members').hide()
|
||||
($ '#state_members :input').each ->
|
||||
($ this).prop 'disabled', false
|
||||
|
||||
($ '#state_members').show()
|
||||
0
app/assets/javascripts/admin/spree_backend.js
Normal file
0
app/assets/javascripts/admin/spree_backend.js
Normal file
@@ -16,7 +16,7 @@ angular.module("admin.subscriptions").controller "DetailsController", ($scope, $
|
||||
return if !newValue?
|
||||
paymentMethod = ($scope.paymentMethods.filter (pm) -> pm.id == newValue)[0]
|
||||
return unless paymentMethod?
|
||||
$scope.cardRequired = (paymentMethod.type == "Spree::Gateway::StripeConnect")
|
||||
$scope.cardRequired = (paymentMethod.type == "Spree::Gateway::StripeConnect" || paymentMethod.type == "Spree::Gateway::StripeSCA")
|
||||
$scope.loadCustomer() if $scope.cardRequired && !$scope.customer
|
||||
|
||||
$scope.loadCustomer = ->
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
angular.module("admin.utils").directive "variantAutocomplete", ($timeout) ->
|
||||
restrict: 'C'
|
||||
link: (scope, element, attrs) ->
|
||||
# Make variantAutocomplete do nothing because it is called
|
||||
# from core/app/assets/javascripts/admin/orders/edit.js
|
||||
$.fn.variantAutocomplete = angular.noop
|
||||
|
||||
$timeout ->
|
||||
if $("#variant_autocomplete_template").length > 0
|
||||
variantTemplate = Handlebars.compile($("#variant_autocomplete_template").text())
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Parses a structure of errors that came from the server
|
||||
angular.module("admin.utils").factory "ErrorsParser", ->
|
||||
new class ErrorsParser
|
||||
toString: (errors, defaultContent = "") =>
|
||||
return defaultContent unless errors?
|
||||
|
||||
errorsString = ""
|
||||
if errors.length > 0
|
||||
# it is an array of errors
|
||||
errorsString = errors.join("\n") + "\n"
|
||||
else
|
||||
# it is a hash of errors
|
||||
keys = Object.keys(errors)
|
||||
for key in keys
|
||||
errorsString += errors[key].join("\n") + "\n"
|
||||
|
||||
this.defaultIfEmpty(errorsString, defaultContent)
|
||||
|
||||
defaultIfEmpty: (content, defaultContent) =>
|
||||
return defaultContent if content == ""
|
||||
content
|
||||
@@ -7,6 +7,8 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE
|
||||
purchase: ->
|
||||
if @paymentMethod()?.method_type == 'stripe' && !@secrets.selected_card
|
||||
StripeElements.requestToken(@secrets, @submit)
|
||||
else if @paymentMethod()?.method_type == 'stripe_sca' && !@secrets.selected_card
|
||||
StripeElements.createPaymentMethod(@secrets, @submit)
|
||||
else
|
||||
@submit()
|
||||
|
||||
@@ -59,7 +61,7 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE
|
||||
last_name: @order.bill_address.lastname
|
||||
}
|
||||
|
||||
if @paymentMethod()?.method_type == 'stripe'
|
||||
if @paymentMethod()?.method_type == 'stripe' || @paymentMethod()?.method_type == 'stripe_sca'
|
||||
if @secrets.selected_card
|
||||
angular.extend munged_order, {
|
||||
existing_card_id: @secrets.selected_card
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
new class StripeElements
|
||||
# TODO: add locale here for translations of error messages etc. from Stripe
|
||||
|
||||
# These are both set from the StripeElements directive
|
||||
stripe: null
|
||||
card: null
|
||||
|
||||
# New Stripe Elements method
|
||||
# Create Token to be used with the Stripe Charges API
|
||||
requestToken: (secrets, submit, loading_message = t("processing_payment")) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
@@ -23,6 +21,23 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# Create Payment Method to be used with the Stripe Payment Intents API
|
||||
createPaymentMethod: (secrets, submit, loading_message = t("processing_payment")) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
Loading.message = loading_message
|
||||
cardData = @makeCardData(secrets)
|
||||
|
||||
@stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
|
||||
if(response.error)
|
||||
Loading.clear()
|
||||
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = response.paymentMethod.card.brand
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' }
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.select_all_variants' | t:{ total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
|
||||
.exchange-products{ 'ng-hide' => 'productsLoading()' }
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products | filter:visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.pagination{ 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages && !productsLoading()'}
|
||||
.button{ 'ng-click' => 'loadMoreExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_more_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_more_variants' | t }}
|
||||
.button{ 'ng-click' => 'loadAllExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
.sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' }
|
||||
%br
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
%h1
|
||||
{{ 'js.admin.panels.exchange_products.loading_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.loading_variants' | t }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.exchange-load-all-variants
|
||||
%div
|
||||
{{ 'js.admin.panels.exchange_products.products_loaded' | t:{ num_of_products_loaded: enterprises[exchange.enterprise_id].supplied_products.length, total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.variants_loaded' | t:{ num_of_variants_loaded: enterprises[exchange.enterprise_id].loaded_variants, total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'selectAllVariants(exchange, exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.select_all_variants' | t:{ total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*
|
||||
|
||||
*= require admin/spree_backend
|
||||
*= require jquery.powertip
|
||||
*= require responsive-tables
|
||||
*= require normalize
|
||||
*= require skeleton
|
||||
*= require responsive-tables
|
||||
*= require jquery.powertip
|
||||
*= require jquery.ui.datepicker
|
||||
*= require jquery-ui-timepicker-addon
|
||||
*= require shared/textAngular
|
||||
*= require shared/ng-tags-input.min
|
||||
*= require select2
|
||||
|
||||
*= require_self
|
||||
*/
|
||||
@@ -33,6 +33,11 @@
|
||||
@import 'plugins/powertip';
|
||||
@import 'plugins/jstree';
|
||||
@import 'plugins/font-awesome';
|
||||
@import 'plugins/select2';
|
||||
|
||||
@import 'sections/image_settings';
|
||||
@import 'sections/orders';
|
||||
@import 'sections/products';
|
||||
|
||||
@import 'hacks/mozilla';
|
||||
@import 'hacks/opera';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.destroy_style, .destroy_header {
|
||||
float: right;
|
||||
}
|
||||
@@ -23,7 +23,6 @@ module Admin
|
||||
before_filter :setup_property, only: [:edit]
|
||||
|
||||
helper 'spree/products'
|
||||
include ActionView::Helpers::TextHelper
|
||||
include OrderCyclesHelper
|
||||
|
||||
def index
|
||||
@@ -77,19 +76,12 @@ module Admin
|
||||
|
||||
def bulk_update
|
||||
@enterprise_set = EnterpriseSet.new(collection, params[:enterprise_set])
|
||||
touched_enterprises = @enterprise_set.collection.select(&:changed?)
|
||||
if @enterprise_set.save
|
||||
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
|
||||
|
||||
# 18-3-2015: It seems that the form for this action sometimes loads bogus values for
|
||||
# the 'sells' field, and submitting that form results in a bunch of enterprises with
|
||||
# values that have mysteriously changed. This statement is here to help debug that
|
||||
# issue, and should be removed (along with its display in index.html.haml) when the
|
||||
# issue has been resolved.
|
||||
flash[:action] = "#{I18n.t(:updated)} #{pluralize(touched_enterprises.count, 'enterprise')}: #{touched_enterprises.map(&:name).join(', ')}"
|
||||
|
||||
redirect_to main_app.admin_enterprises_path
|
||||
else
|
||||
touched_enterprises = @enterprise_set.collection.select(&:changed?)
|
||||
@enterprise_set.collection.select! { |e| touched_enterprises.include? e }
|
||||
flash[:error] = I18n.t(:enterprise_bulk_update_error)
|
||||
render :index
|
||||
|
||||
@@ -48,16 +48,22 @@ module Api
|
||||
end
|
||||
|
||||
def bulk_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(current_api_user).
|
||||
editable_products.merge(product_scope)
|
||||
product_query = OpenFoodNetwork::Permissions.
|
||||
new(current_api_user).
|
||||
editable_products.
|
||||
merge(product_scope)
|
||||
|
||||
if params[:import_date].present?
|
||||
product_query = product_query.imported_on(params[:import_date]).group_by_products_id
|
||||
product_query = product_query.
|
||||
imported_on(params[:import_date]).
|
||||
group_by_products_id
|
||||
end
|
||||
|
||||
@products = product_query.order('created_at DESC').
|
||||
ransack(params[:q]).result.
|
||||
page(params[:page] || DEFAULT_PAGE).per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
@products = product_query.
|
||||
ransack(query_params_with_defaults).
|
||||
result.
|
||||
page(params[:page] || DEFAULT_PAGE).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
|
||||
render_paged_products @products
|
||||
end
|
||||
@@ -136,6 +142,10 @@ module Api
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def query_params_with_defaults
|
||||
params[:q].to_h.reverse_merge(s: 'created_at desc')
|
||||
end
|
||||
|
||||
def pagination_data(results)
|
||||
{
|
||||
results: results.total_count,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require 'open_food_network/referer_parser'
|
||||
require 'spree/authentication_helpers'
|
||||
require_dependency 'spree/authentication_helpers'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/address_finder'
|
||||
|
||||
class CheckoutController < Spree::CheckoutController
|
||||
class CheckoutController < Spree::StoreController
|
||||
layout 'darkswarm'
|
||||
|
||||
include CheckoutHelper
|
||||
include OrderCyclesHelper
|
||||
include EnterprisesHelper
|
||||
|
||||
ssl_required
|
||||
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
# Otherwise we fail on duplicate indexes or end up with negative stock.
|
||||
prepend_around_filter CurrentOrderLocker, only: :update
|
||||
@@ -12,10 +20,19 @@ class CheckoutController < Spree::CheckoutController
|
||||
prepend_before_filter :require_order_cycle
|
||||
prepend_before_filter :require_distributor_chosen
|
||||
|
||||
before_filter :load_order
|
||||
|
||||
before_filter :ensure_order_not_completed
|
||||
before_filter :ensure_checkout_allowed
|
||||
before_filter :ensure_sufficient_stock_lines
|
||||
|
||||
before_filter :associate_user
|
||||
before_filter :check_authorization
|
||||
before_filter :enable_embedded_shopfront
|
||||
|
||||
include OrderCyclesHelper
|
||||
include EnterprisesHelper
|
||||
helper 'spree/orders'
|
||||
|
||||
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
||||
|
||||
def edit
|
||||
# This is only required because of spree_paypal_express. If we implement
|
||||
@@ -25,54 +42,16 @@ class CheckoutController < Spree::CheckoutController
|
||||
end
|
||||
|
||||
def update
|
||||
shipping_method_id = object_params.delete(:shipping_method_id)
|
||||
params_adapter = Checkout::FormDataAdapter.new(params, @order, spree_current_user)
|
||||
return update_failed unless @order.update_attributes(params_adapter.order_params)
|
||||
|
||||
return update_failed unless @order.update_attributes(object_params)
|
||||
|
||||
check_order_for_phantom_fees
|
||||
fire_event('spree.checkout.update')
|
||||
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
return if redirect_to_paypal_express_form_if_needed
|
||||
end
|
||||
|
||||
if @order.state == "delivery"
|
||||
@order.select_shipping_method(shipping_method_id)
|
||||
end
|
||||
|
||||
next if advance_order_state(@order)
|
||||
|
||||
flash[:error] = if @order.errors.present?
|
||||
@order.errors.full_messages.to_sentence
|
||||
else
|
||||
t(:payment_processing_failed)
|
||||
end
|
||||
update_failed
|
||||
return
|
||||
end
|
||||
return update_failed unless @order.state == "complete" || @order.completed?
|
||||
|
||||
set_default_bill_address
|
||||
set_default_ship_address
|
||||
|
||||
ResetOrderService.new(self, current_order).call
|
||||
session[:access_token] = current_order.token
|
||||
|
||||
flash[:notice] = t(:order_processed_successfully)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_path(@order))
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_path(@order) }, status: :ok
|
||||
end
|
||||
end
|
||||
rescue Spree::Core::GatewayError => error
|
||||
# This is done for all actions in the Spree::CheckoutController.
|
||||
rescue_from_spree_gateway_error(error)
|
||||
rescue StandardError => error
|
||||
Bugsnag.notify(error)
|
||||
checkout_workflow(params_adapter.shipping_method_id)
|
||||
rescue Spree::Core::GatewayError => e
|
||||
rescue_from_spree_gateway_error(e)
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify(e)
|
||||
flash[:error] = I18n.t("checkout.failed")
|
||||
update_failed
|
||||
end
|
||||
@@ -87,111 +66,38 @@ class CheckoutController < Spree::CheckoutController
|
||||
|
||||
private
|
||||
|
||||
def set_default_bill_address
|
||||
if params[:order][:default_bill_address]
|
||||
new_bill_address = @order.bill_address.clone.attributes
|
||||
|
||||
user_bill_address_id = spree_current_user.bill_address.andand.id
|
||||
spree_current_user.update_attributes(
|
||||
bill_address_attributes: new_bill_address.merge('id' => user_bill_address_id)
|
||||
)
|
||||
|
||||
customer_bill_address_id = @order.customer.bill_address.andand.id
|
||||
@order.customer.update_attributes(
|
||||
bill_address_attributes: new_bill_address.merge('id' => customer_bill_address_id)
|
||||
)
|
||||
end
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
|
||||
def set_default_ship_address
|
||||
if params[:order][:default_ship_address]
|
||||
new_ship_address = @order.ship_address.clone.attributes
|
||||
|
||||
user_ship_address_id = spree_current_user.ship_address.andand.id
|
||||
spree_current_user.update_attributes(
|
||||
ship_address_attributes: new_ship_address.merge('id' => user_ship_address_id)
|
||||
)
|
||||
|
||||
customer_ship_address_id = @order.customer.ship_address.andand.id
|
||||
@order.customer.update_attributes(
|
||||
ship_address_attributes: new_ship_address.merge('id' => customer_ship_address_id)
|
||||
)
|
||||
end
|
||||
def ensure_checkout_allowed
|
||||
redirect_to main_app.cart_path unless @order.checkout_allowed?
|
||||
end
|
||||
|
||||
def check_order_for_phantom_fees
|
||||
phantom_fees = @order.adjustments.
|
||||
joins("LEFT OUTER JOIN spree_line_items"\
|
||||
" ON spree_line_items.id = spree_adjustments.source_id").
|
||||
where("originator_type = 'EnterpriseFee'"\
|
||||
" AND source_type = 'Spree::LineItem' AND spree_line_items.id IS NULL")
|
||||
|
||||
if phantom_fees.any?
|
||||
Bugsnag.notify(RuntimeError.new("Phantom Fees"),
|
||||
phantom_fees: {
|
||||
phantom_total: phantom_fees.sum(&:amount).to_s,
|
||||
phantom_fees: phantom_fees.as_json
|
||||
})
|
||||
end
|
||||
def ensure_order_not_completed
|
||||
redirect_to main_app.cart_path if @order.completed?
|
||||
end
|
||||
|
||||
# Copied and modified from spree. Remove check for order state, since the state machine is
|
||||
# progressed all the way in one go with the one page checkout.
|
||||
def object_params
|
||||
# For payment step, filter order parameters to produce the expected
|
||||
# nested attributes for a single payment and its source,
|
||||
# discarding attributes for payment methods other than the one selected
|
||||
if params[:payment_source].present? && source_params = params.delete(:payment_source)[params[:order][:payments_attributes].first[:payment_method_id].underscore]
|
||||
params[:order][:payments_attributes].first[:source_attributes] = source_params
|
||||
end
|
||||
if params[:order][:payments_attributes]
|
||||
params[:order][:payments_attributes].first[:amount] = @order.total
|
||||
end
|
||||
if params[:order][:existing_card_id]
|
||||
construct_saved_card_attributes
|
||||
end
|
||||
params[:order]
|
||||
end
|
||||
|
||||
# Perform order.next, guarding against StaleObjectErrors
|
||||
def advance_order_state(order)
|
||||
tries ||= 3
|
||||
order.next
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
retry unless (tries -= 1).zero?
|
||||
false
|
||||
end
|
||||
|
||||
def update_failed
|
||||
current_order.updater.shipping_address_from_distributor
|
||||
RestartCheckout.new(@order).call
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
end
|
||||
format.json do
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
def ensure_sufficient_stock_lines
|
||||
if @order.insufficient_stock_lines.present?
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
end
|
||||
|
||||
def load_order
|
||||
@order = current_order
|
||||
redirect_to(main_app.shop_path) && return unless @order && @order.checkout_allowed?
|
||||
|
||||
redirect_to(main_app.shop_path) && return if redirect_to_shop?
|
||||
redirect_to_cart_path && return unless valid_order_line_items?
|
||||
redirect_to(main_app.shop_path) && return if @order.completed?
|
||||
before_address
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
def redirect_to_shop?
|
||||
!@order ||
|
||||
!@order.checkout_allowed? ||
|
||||
@order.completed?
|
||||
end
|
||||
|
||||
def valid_order_line_items?
|
||||
@@ -212,32 +118,29 @@ class CheckoutController < Spree::CheckoutController
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_paypal_express_form_if_needed
|
||||
return unless params[:order][:payments_attributes]
|
||||
|
||||
payment_method_id = params[:order][:payments_attributes].first[:payment_method_id]
|
||||
payment_method = Spree::PaymentMethod.find(payment_method_id)
|
||||
return unless payment_method.is_a?(Spree::Gateway::PayPalExpress)
|
||||
|
||||
render json: { path: spree.paypal_express_path(payment_method_id: payment_method.id) },
|
||||
status: :ok
|
||||
true
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
__send__(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
def construct_saved_card_attributes
|
||||
existing_card_id = params[:order].delete(:existing_card_id)
|
||||
return if existing_card_id.blank?
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
credit_card = Spree::CreditCard.find(existing_card_id)
|
||||
if credit_card.try(:user_id).blank? || credit_card.user_id != spree_current_user.try(:id)
|
||||
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
|
||||
end
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
# Not currently supported but maybe we should add it...?
|
||||
credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
params[:order][:payments_attributes].first[:source] = credit_card
|
||||
params[:order][:payments_attributes].first.delete :source_attributes
|
||||
def before_delivery
|
||||
return if params[:order].present?
|
||||
|
||||
packages = @order.shipments.map(&:to_package)
|
||||
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def rescue_from_spree_gateway_error(error)
|
||||
@@ -247,4 +150,91 @@ class CheckoutController < Spree::CheckoutController
|
||||
format.json { render json: { flash: flash.to_hash }, status: :bad_request }
|
||||
end
|
||||
end
|
||||
|
||||
def checkout_workflow(shipping_method_id)
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
return if redirect_to_payment_gateway
|
||||
end
|
||||
|
||||
@order.select_shipping_method(shipping_method_id) if @order.state == "delivery"
|
||||
|
||||
next if advance_order_state(@order)
|
||||
|
||||
flash[:error] = order_workflow_error
|
||||
return update_failed
|
||||
end
|
||||
|
||||
update_result
|
||||
end
|
||||
|
||||
def redirect_to_payment_gateway
|
||||
redirect_path = Checkout::PaymentRedirect.new(params).path
|
||||
return if redirect_path.blank?
|
||||
|
||||
render json: { path: redirect_path }, status: :ok
|
||||
true
|
||||
end
|
||||
|
||||
# Perform order.next, guarding against StaleObjectErrors
|
||||
def advance_order_state(order)
|
||||
tries ||= 3
|
||||
order.next
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
retry unless (tries -= 1).zero?
|
||||
false
|
||||
end
|
||||
|
||||
def order_workflow_error
|
||||
if @order.errors.present?
|
||||
@order.errors.full_messages.to_sentence
|
||||
else
|
||||
t(:payment_processing_failed)
|
||||
end
|
||||
end
|
||||
|
||||
def update_result
|
||||
if @order.state == "complete" || @order.completed?
|
||||
save_order_addresses_as_user_default
|
||||
ResetOrderService.new(self, current_order).call
|
||||
|
||||
update_succeeded
|
||||
else
|
||||
update_failed
|
||||
end
|
||||
end
|
||||
|
||||
def save_order_addresses_as_user_default
|
||||
user_default_address_setter = UserDefaultAddressSetter.new(@order, spree_current_user)
|
||||
user_default_address_setter.set_default_bill_address if params[:order][:default_bill_address]
|
||||
user_default_address_setter.set_default_ship_address if params[:order][:default_ship_address]
|
||||
end
|
||||
|
||||
def update_succeeded
|
||||
session[:access_token] = current_order.token
|
||||
flash[:notice] = t(:order_processed_successfully)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_path(@order))
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_path(@order) }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_failed
|
||||
current_order.updater.shipping_address_from_distributor
|
||||
RestartCheckout.new(@order).call
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
end
|
||||
format.json do
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,8 +63,6 @@ class EnterprisesController < BaseController
|
||||
end
|
||||
|
||||
def reset_order
|
||||
distributor = Enterprise.is_distributor.find_by_permalink(params[:id]) ||
|
||||
Enterprise.is_distributor.find(params[:id])
|
||||
order = current_order(true)
|
||||
|
||||
reset_distributor(order, distributor)
|
||||
@@ -74,6 +72,14 @@ class EnterprisesController < BaseController
|
||||
reset_order_cycle(order, distributor)
|
||||
|
||||
order.save!
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
flash[:error] = I18n.t(:enterprise_shop_show_error)
|
||||
redirect_to shops_path
|
||||
end
|
||||
|
||||
def distributor
|
||||
@distributor ||= Enterprise.is_distributor.find_by_permalink(params[:id]) ||
|
||||
Enterprise.is_distributor.find(params[:id])
|
||||
end
|
||||
|
||||
def reset_distributor(order, distributor)
|
||||
|
||||
@@ -70,10 +70,6 @@ module Spree
|
||||
Spree.t(event_sym, resource: resource_desc)
|
||||
end
|
||||
|
||||
def render_js_for_destroy
|
||||
render partial: '/spree/admin/shared/destroy'
|
||||
end
|
||||
|
||||
# Index request for JSON needs to pass a CSRF token in order to prevent JSON Hijacking
|
||||
def check_json_authenticity
|
||||
return unless request.format.js? || request.format.json?
|
||||
@@ -86,10 +82,6 @@ module Spree
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
|
||||
def config_locale
|
||||
Spree::Backend::Config[:locale]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def html_request?
|
||||
|
||||
@@ -110,7 +110,7 @@ module Spree
|
||||
else
|
||||
Gateway.providers.reject{ |p| p.name.include? "Bogus" }.sort_by(&:name)
|
||||
end
|
||||
@providers.reject!{ |p| p.name.ends_with? "StripeConnect" } unless show_stripe?
|
||||
@providers.reject!{ |provider| stripe_provider?(provider) } unless show_stripe?
|
||||
@calculators = PaymentMethod.calculators.sort_by(&:name)
|
||||
end
|
||||
|
||||
@@ -134,12 +134,12 @@ module Spree
|
||||
# current payment_method is already a Stripe method
|
||||
def show_stripe?
|
||||
Spree::Config.stripe_connect_enabled ||
|
||||
@payment_method.try(:type) == "Spree::Gateway::StripeConnect"
|
||||
stripe_payment_method?
|
||||
end
|
||||
|
||||
def restrict_stripe_account_change
|
||||
return unless @payment_method
|
||||
return unless @payment_method.type == "Spree::Gateway::StripeConnect"
|
||||
return unless stripe_payment_method?
|
||||
return unless @payment_method.preferred_enterprise_id.andand > 0
|
||||
|
||||
@stripe_account_holder = Enterprise.find(@payment_method.preferred_enterprise_id)
|
||||
@@ -147,6 +147,15 @@ module Spree
|
||||
|
||||
params[:payment_method][:preferred_enterprise_id] = @stripe_account_holder.id
|
||||
end
|
||||
|
||||
def stripe_payment_method?
|
||||
["Spree::Gateway::StripeConnect",
|
||||
"Spree::Gateway::StripeSCA"].include? @payment_method.try(:type)
|
||||
end
|
||||
|
||||
def stripe_provider?(provider)
|
||||
provider.name.ends_with?("StripeConnect", "StripeSCA")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -217,7 +217,11 @@ module Spree
|
||||
end
|
||||
|
||||
def render_report(header, table, create_csv, csv_file_name)
|
||||
send_data csv_report(header, table), filename: csv_file_name if create_csv
|
||||
if create_csv
|
||||
@csv_report = csv_report(header, table)
|
||||
send_data @csv_report, filename: csv_file_name
|
||||
end
|
||||
|
||||
@header = header
|
||||
@table = table
|
||||
# Rendering HTML is the default.
|
||||
|
||||
@@ -1,93 +1,16 @@
|
||||
require 'open_food_network/address_finder'
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This controller (and respective route in the Spree engine)
|
||||
# is only needed for the spree_paypal_express gem that redirects here explicitly.
|
||||
#
|
||||
# According to the rails docs it would be possible to redirect
|
||||
# to CheckoutController directly in the routes
|
||||
# with a slash like "to: '/checkout#edit'", but it does not work in this case.
|
||||
module Spree
|
||||
class CheckoutController < Spree::StoreController
|
||||
include CheckoutHelper
|
||||
|
||||
ssl_required
|
||||
|
||||
before_filter :load_order
|
||||
|
||||
before_filter :ensure_order_not_completed
|
||||
before_filter :ensure_checkout_allowed
|
||||
before_filter :ensure_sufficient_stock_lines
|
||||
|
||||
before_filter :associate_user
|
||||
before_filter :check_authorization
|
||||
before_filter :enable_embedded_shopfront
|
||||
|
||||
helper 'spree/orders'
|
||||
|
||||
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
||||
|
||||
def edit
|
||||
flash.keep
|
||||
redirect_to main_app.checkout_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_order
|
||||
@order = current_order
|
||||
redirect_to(main_app.cart_path) && return unless @order
|
||||
|
||||
if params[:state]
|
||||
redirect_to checkout_state_path(@order.state) if @order.can_go_to_state?(params[:state])
|
||||
@order.state = params[:state]
|
||||
end
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def ensure_checkout_allowed
|
||||
redirect_to main_app.cart_path unless @order.checkout_allowed?
|
||||
end
|
||||
|
||||
def ensure_order_not_completed
|
||||
redirect_to main_app.cart_path if @order.completed?
|
||||
end
|
||||
|
||||
def ensure_sufficient_stock_lines
|
||||
if @order.insufficient_stock_lines.present?
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
end
|
||||
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
send(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
# Adapted from spree_last_address gem: https://github.com/TylerRick/spree_last_address
|
||||
# Originally, we used a forked version of this gem, but encountered strange errors where
|
||||
# it worked in dev but only intermittently in staging/prod.
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
def before_delivery
|
||||
return if params[:order].present?
|
||||
|
||||
packages = @order.shipments.map(&:to_package)
|
||||
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def rescue_from_spree_gateway_error
|
||||
flash[:error] = Spree.t(:spree_gateway_error_flash_for_checkout)
|
||||
render :edit
|
||||
end
|
||||
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,10 +10,14 @@ module Spree
|
||||
render json: @credit_card, serializer: ::Api::CreditCardSerializer, status: :ok
|
||||
else
|
||||
message = t(:card_could_not_be_saved)
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout, error: message) } }, status: :bad_request
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout,
|
||||
error: message) } },
|
||||
status: :bad_request
|
||||
end
|
||||
rescue Stripe::CardError => e
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout, error: e.message) } }, status: :bad_request
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout,
|
||||
error: e.message) } },
|
||||
status: :bad_request
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -52,12 +56,19 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
# Currently can only destroy the whole customer object
|
||||
# It destroys the whole customer object
|
||||
def destroy_at_stripe
|
||||
stripe_customer = Stripe::Customer.retrieve(@credit_card.gateway_customer_profile_id)
|
||||
stripe_customer = Stripe::Customer.retrieve(@credit_card.gateway_customer_profile_id, {})
|
||||
stripe_customer.delete if stripe_customer
|
||||
end
|
||||
|
||||
def stripe_account_id
|
||||
StripeAccount.
|
||||
find_by_enterprise_id(@credit_card.payment_method.preferred_enterprise_id).
|
||||
andand.
|
||||
stripe_user_id
|
||||
end
|
||||
|
||||
def create_customer(token)
|
||||
Stripe::Customer.create(email: spree_current_user.email, source: token)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
module NavigationHelper
|
||||
@@ -34,7 +36,9 @@ module Spree
|
||||
end
|
||||
|
||||
selected = if options[:match_path]
|
||||
request.fullpath.starts_with?("#{spree.root_path}admin#{options[:match_path]}")
|
||||
request.
|
||||
fullpath.
|
||||
starts_with?("#{main_app.root_path}admin#{options[:match_path]}")
|
||||
else
|
||||
args.include?(controller.controller_name.to_sym)
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@ class SubscriptionConfirmJob
|
||||
ids = proxy_orders.pluck(:id)
|
||||
proxy_orders.update_all(confirmed_at: Time.zone.now)
|
||||
ProxyOrder.where(id: ids).each do |proxy_order|
|
||||
Rails.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
|
||||
@order = proxy_order.order
|
||||
process!
|
||||
end
|
||||
|
||||
@@ -5,8 +5,7 @@ class SubscriptionPlacementJob
|
||||
ids = proxy_orders.pluck(:id)
|
||||
proxy_orders.update_all(placed_at: Time.zone.now)
|
||||
ProxyOrder.where(id: ids).each do |proxy_order|
|
||||
proxy_order.initialise_order!
|
||||
process(proxy_order.order)
|
||||
place_order_for(proxy_order)
|
||||
end
|
||||
|
||||
send_placement_summary_emails
|
||||
@@ -28,16 +27,18 @@ class SubscriptionPlacementJob
|
||||
.joins(:subscription).merge(Subscription.not_canceled.not_paused)
|
||||
end
|
||||
|
||||
def process(order)
|
||||
def place_order_for(proxy_order)
|
||||
Rails.logger.info "Placing Order for Proxy Order #{proxy_order.id}"
|
||||
proxy_order.initialise_order!
|
||||
place_order(proxy_order.order)
|
||||
end
|
||||
|
||||
def place_order(order)
|
||||
record_order(order)
|
||||
return record_issue(:complete, order) if order.completed?
|
||||
|
||||
changes = cap_quantity_and_store_changes(order)
|
||||
if order.line_items.where('quantity > 0').empty?
|
||||
order.reload.adjustments.destroy_all
|
||||
order.update!
|
||||
return send_empty_email(order, changes)
|
||||
end
|
||||
return handle_empty_order(order, changes) if order.line_items.where('quantity > 0').empty?
|
||||
|
||||
move_to_completion(order)
|
||||
send_placement_email(order, changes)
|
||||
@@ -58,12 +59,18 @@ class SubscriptionPlacementJob
|
||||
changes
|
||||
end
|
||||
|
||||
def handle_empty_order(order, changes)
|
||||
order.reload.adjustments.destroy_all
|
||||
order.update!
|
||||
send_empty_email(order, changes)
|
||||
end
|
||||
|
||||
def move_to_completion(order)
|
||||
AdvanceOrderService.new(order).call!
|
||||
end
|
||||
|
||||
def unavailable_stock_lines_for(order)
|
||||
order.line_items.where('variant_id NOT IN (?)', available_variants_for(order))
|
||||
order.line_items.where('variant_id NOT IN (?)', available_variants_for(order).select(&:id))
|
||||
end
|
||||
|
||||
def available_variants_for(order)
|
||||
|
||||
@@ -321,7 +321,7 @@ class Enterprise < ActiveRecord::Base
|
||||
def distributed_taxons
|
||||
Spree::Taxon.
|
||||
joins(:products).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_distributor(self)).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_distributor(self).select(&:id)).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
end
|
||||
|
||||
@@ -337,7 +337,7 @@ class Enterprise < ActiveRecord::Base
|
||||
def supplied_taxons
|
||||
Spree::Taxon.
|
||||
joins(:products).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_supplier(self)).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_supplier(self).select(&:id)).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
end
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
where('enterprise_id IN (?)', user.enterprises)
|
||||
where('enterprise_id IN (?)', user.enterprises.select(&:id))
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class EnterpriseRelationship < ActiveRecord::Base
|
||||
}
|
||||
|
||||
scope :involving_enterprises, ->(enterprises) {
|
||||
where('parent_id IN (?) OR child_id IN (?)', enterprises, enterprises)
|
||||
where('parent_id IN (?) OR child_id IN (?)', enterprises.select(&:id), enterprises.select(&:id))
|
||||
}
|
||||
|
||||
scope :permitting, ->(enterprise_ids) { where('child_id IN (?)', enterprise_ids) }
|
||||
|
||||
@@ -49,7 +49,7 @@ class Exchange < ActiveRecord::Base
|
||||
}
|
||||
scope :with_product, lambda { |product|
|
||||
joins(:exchange_variants).
|
||||
where('exchange_variants.variant_id IN (?)', product.variants_including_master)
|
||||
where('exchange_variants.variant_id IN (?)', product.variants_including_master.select(&:id))
|
||||
}
|
||||
scope :by_enterprise_name, -> {
|
||||
joins('INNER JOIN enterprises AS sender ON (sender.id = exchanges.sender_id)').
|
||||
|
||||
@@ -17,6 +17,7 @@ class OrderCycle < ActiveRecord::Base
|
||||
has_many :distributors, source: :receiver, through: :cached_outgoing_exchanges, uniq: true
|
||||
|
||||
has_and_belongs_to_many :schedules, join_table: 'order_cycle_schedules'
|
||||
has_paper_trail meta: { custom_data: :schedule_ids }
|
||||
|
||||
attr_accessor :incoming_exchanges, :outgoing_exchanges
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ module ProductImport
|
||||
|
||||
def save_new_inventory_item(entry)
|
||||
new_item = entry.product_object
|
||||
assign_defaults(new_item, entry)
|
||||
new_item.import_date = @import_time
|
||||
|
||||
if new_item.valid? && new_item.save
|
||||
@@ -136,7 +135,6 @@ module ProductImport
|
||||
|
||||
def save_existing_inventory_item(entry)
|
||||
existing_item = entry.product_object
|
||||
assign_defaults(existing_item, entry)
|
||||
existing_item.import_date = @import_time
|
||||
|
||||
if existing_item.valid? && existing_item.save
|
||||
@@ -164,7 +162,6 @@ module ProductImport
|
||||
product = Spree::Product.new
|
||||
product.assign_attributes(entry.attributes.except('id', 'on_hand', 'on_demand'))
|
||||
product.supplier_id = entry.producer_id
|
||||
assign_defaults(product, entry)
|
||||
|
||||
if product.save
|
||||
ensure_variant_updated(product, entry)
|
||||
@@ -179,7 +176,6 @@ module ProductImport
|
||||
|
||||
def save_variant(entry)
|
||||
variant = entry.product_object
|
||||
assign_defaults(variant, entry)
|
||||
variant.import_date = @import_time
|
||||
|
||||
if variant.valid? && variant.save
|
||||
@@ -199,37 +195,6 @@ module ProductImport
|
||||
)
|
||||
end
|
||||
|
||||
def assign_defaults(object, entry)
|
||||
# Assigns a default value for a specified field e.g. category='Vegetables', setting this value
|
||||
# either for all entries (overwrite_all), or only for those entries where the field was blank
|
||||
# in the spreadsheet (overwrite_empty), depending on selected import settings
|
||||
return unless settings.defaults(entry)
|
||||
|
||||
settings.defaults(entry).each do |attribute, setting|
|
||||
next unless setting['active']
|
||||
|
||||
case setting['mode']
|
||||
when 'overwrite_all'
|
||||
object.assign_attributes(attribute => setting['value'])
|
||||
# In case of new products, some attributes are saved on the variant.
|
||||
# We write them to the entry here to be copied to the variant later.
|
||||
if entry.respond_to? "#{attribute}="
|
||||
entry.public_send("#{attribute}=", setting['value'])
|
||||
end
|
||||
when 'overwrite_empty'
|
||||
if object.public_send(attribute).blank? ||
|
||||
((attribute == 'on_hand') &&
|
||||
entry.on_hand_nil)
|
||||
|
||||
object.assign_attributes(attribute => setting['value'])
|
||||
if entry.respond_to? "#{attribute}="
|
||||
entry.public_send("#{attribute}=", setting['value'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def display_in_inventory(variant_override, is_new = false)
|
||||
unless is_new
|
||||
existing_item = InventoryItem.where(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class Schedule < ActiveRecord::Base
|
||||
has_and_belongs_to_many :order_cycles, join_table: 'order_cycle_schedules'
|
||||
has_paper_trail meta: { custom_data: :order_cycle_ids }
|
||||
|
||||
has_many :coordinators, uniq: true, through: :order_cycles
|
||||
|
||||
attr_accessible :name, :order_cycle_ids
|
||||
|
||||
@@ -9,12 +9,6 @@ module Spree
|
||||
|
||||
attr_accessible :preferred_enterprise_id
|
||||
|
||||
CARD_TYPE_MAPPING = {
|
||||
'American Express' => 'american_express',
|
||||
'Diners Club' => 'diners_club',
|
||||
'Visa' => 'visa'
|
||||
}.freeze
|
||||
|
||||
def method_type
|
||||
'stripe'
|
||||
end
|
||||
@@ -77,11 +71,6 @@ module Spree
|
||||
[money, creditcard, options]
|
||||
end
|
||||
|
||||
def update_source!(source)
|
||||
source.cc_type = CARD_TYPE_MAPPING[source.cc_type] if CARD_TYPE_MAPPING.include?(source.cc_type)
|
||||
source
|
||||
end
|
||||
|
||||
def token_from_card_profile_ids(creditcard)
|
||||
token_or_card_id = creditcard.gateway_payment_profile_id
|
||||
customer = creditcard.gateway_customer_profile_id
|
||||
|
||||
91
app/models/spree/gateway/stripe_sca.rb
Normal file
91
app/models/spree/gateway/stripe_sca.rb
Normal file
@@ -0,0 +1,91 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'stripe/profile_storer'
|
||||
require 'stripe/credit_card_cloner'
|
||||
require 'active_merchant/billing/gateways/stripe_payment_intents'
|
||||
require 'active_merchant/billing/gateways/stripe_decorator'
|
||||
|
||||
module Spree
|
||||
class Gateway
|
||||
class StripeSCA < Gateway
|
||||
preference :enterprise_id, :integer
|
||||
|
||||
validate :ensure_enterprise_selected
|
||||
|
||||
attr_accessible :preferred_enterprise_id
|
||||
|
||||
def method_type
|
||||
'stripe_sca'
|
||||
end
|
||||
|
||||
def provider_class
|
||||
ActiveMerchant::Billing::StripePaymentIntentsGateway
|
||||
end
|
||||
|
||||
def payment_profiles_supported?
|
||||
true
|
||||
end
|
||||
|
||||
def stripe_account_id
|
||||
StripeAccount.find_by_enterprise_id(preferred_enterprise_id).andand.stripe_user_id
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def purchase(money, creditcard, gateway_options)
|
||||
provider.purchase(*options_for_purchase_or_auth(money, creditcard, gateway_options))
|
||||
rescue Stripe::StripeError => e
|
||||
# This will be an error caused by generating a stripe token
|
||||
failed_activemerchant_billing_response(e.message)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def void(response_code, _creditcard, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.void(response_code, gateway_options)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def credit(money, _creditcard, response_code, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.refund(money, response_code, gateway_options)
|
||||
end
|
||||
|
||||
def create_profile(payment)
|
||||
return unless payment.source.gateway_customer_profile_id.nil?
|
||||
|
||||
profile_storer = Stripe::ProfileStorer.new(payment, provider)
|
||||
profile_storer.create_customer_from_token
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# In this gateway, what we call 'secret_key' is the 'login'
|
||||
def options
|
||||
options = super
|
||||
options.merge(login: Stripe.api_key)
|
||||
end
|
||||
|
||||
def options_for_purchase_or_auth(money, creditcard, gateway_options)
|
||||
options = {}
|
||||
options[:description] = "Spree Order ID: #{gateway_options[:order_id]}"
|
||||
options[:currency] = gateway_options[:currency]
|
||||
options[:stripe_account] = stripe_account_id
|
||||
|
||||
customer_id, payment_method_id = Stripe::CreditCardCloner.new.clone(creditcard,
|
||||
stripe_account_id)
|
||||
options[:customer] = customer_id
|
||||
[money, payment_method_id, options]
|
||||
end
|
||||
|
||||
def failed_activemerchant_billing_response(error_message)
|
||||
ActiveMerchant::Billing::Response.new(false, error_message)
|
||||
end
|
||||
|
||||
def ensure_enterprise_selected
|
||||
return if preferred_enterprise_id.andand.positive?
|
||||
|
||||
errors.add(:stripe_account_owner, I18n.t(:error_required))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -56,7 +56,9 @@ Spree::Order.class_eval do
|
||||
# Find orders that are distributed by the user or have products supplied by the user
|
||||
# WARNING: This only filters orders, you'll need to filter line items separately using LineItem.managed_by
|
||||
with_line_items_variants_and_products_outer.
|
||||
where('spree_orders.distributor_id IN (?) OR spree_products.supplier_id IN (?)', user.enterprises, user.enterprises).
|
||||
where('spree_orders.distributor_id IN (?) OR spree_products.supplier_id IN (?)',
|
||||
user.enterprises.select(&:id),
|
||||
user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_orders.*')
|
||||
end
|
||||
}
|
||||
@@ -65,7 +67,7 @@ Spree::Order.class_eval do
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
where('spree_orders.distributor_id IN (?)', user.enterprises)
|
||||
where('spree_orders.distributor_id IN (?)', user.enterprises.select(&:id))
|
||||
end
|
||||
}
|
||||
|
||||
@@ -75,6 +77,10 @@ Spree::Order.class_eval do
|
||||
joins('LEFT OUTER JOIN spree_products ON (spree_products.id = spree_variants.product_id)')
|
||||
}
|
||||
|
||||
scope :with_line_items_variants_and_products, lambda {
|
||||
joins(line_items: { variant: :product })
|
||||
}
|
||||
|
||||
scope :not_state, lambda { |state|
|
||||
where("state != ?", state)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Spree::PaymentMethod.class_eval do
|
||||
scoped
|
||||
else
|
||||
joins(:distributors).
|
||||
where('distributors_payment_methods.distributor_id IN (?)', user.enterprises).
|
||||
where('distributors_payment_methods.distributor_id IN (?)', user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_payment_methods.*')
|
||||
end
|
||||
}
|
||||
@@ -68,6 +68,8 @@ Spree::PaymentMethod.class_eval do
|
||||
"Pin Payments"
|
||||
when "Spree::Gateway::StripeConnect"
|
||||
"Stripe"
|
||||
when "Spree::Gateway::StripeSCA"
|
||||
"Stripe SCA"
|
||||
when "Spree::Gateway::PayPalExpress"
|
||||
"PayPal Express"
|
||||
else
|
||||
|
||||
@@ -15,7 +15,7 @@ Spree::ShippingMethod.class_eval do
|
||||
scoped
|
||||
else
|
||||
joins(:distributors).
|
||||
where('distributors_shipping_methods.distributor_id IN (?)', user.enterprises).
|
||||
where('distributors_shipping_methods.distributor_id IN (?)', user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_shipping_methods.*')
|
||||
end
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
module Spree
|
||||
TaxRate.class_eval do
|
||||
class << self
|
||||
def match_with_sales_tax_registration(order)
|
||||
def match(order)
|
||||
return [] if order.distributor && !order.distributor.charges_sales_tax
|
||||
return [] unless order.tax_zone
|
||||
|
||||
match_without_sales_tax_registration(order)
|
||||
all.select do |rate|
|
||||
rate.zone == order.tax_zone || rate.zone.contains?(order.tax_zone) || rate.zone.default_tax
|
||||
end
|
||||
end
|
||||
alias_method_chain :match, :sales_tax_registration
|
||||
end
|
||||
|
||||
def adjust_with_included_tax(order)
|
||||
|
||||
@@ -49,7 +49,7 @@ Spree::Variant.class_eval do
|
||||
}
|
||||
|
||||
scope :for_distribution, lambda { |order_cycle, distributor|
|
||||
where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor))
|
||||
where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor).select(&:id))
|
||||
}
|
||||
|
||||
scope :visible_for, lambda { |enterprise|
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class Subscription < ActiveRecord::Base
|
||||
ALLOWED_PAYMENT_METHOD_TYPES = ["Spree::PaymentMethod::Check", "Spree::Gateway::StripeConnect"].freeze
|
||||
ALLOWED_PAYMENT_METHOD_TYPES = ["Spree::PaymentMethod::Check",
|
||||
"Spree::Gateway::StripeConnect",
|
||||
"Spree::Gateway::StripeSCA"].freeze
|
||||
|
||||
belongs_to :shop, class_name: 'Enterprise'
|
||||
belongs_to :customer
|
||||
|
||||
@@ -4,7 +4,8 @@ module Api
|
||||
delegate :serializable_hash, to: :method_serializer
|
||||
|
||||
def method_serializer
|
||||
if object.type == 'Spree::Gateway::StripeConnect'
|
||||
if object.type == 'Spree::Gateway::StripeConnect' ||
|
||||
object.type == 'Spree::Gateway::StripeSCA'
|
||||
Api::Admin::PaymentMethod::StripeSerializer.new(object)
|
||||
else
|
||||
Api::Admin::PaymentMethod::BaseSerializer.new(object)
|
||||
|
||||
68
app/services/checkout/form_data_adapter.rb
Normal file
68
app/services/checkout/form_data_adapter.rb
Normal file
@@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Adapts checkout form data (params) so that the order can be directly saved to the database
|
||||
module Checkout
|
||||
class FormDataAdapter
|
||||
attr_reader :shipping_method_id
|
||||
|
||||
def initialize(params, order, current_user)
|
||||
@params = params.dup
|
||||
@order = order
|
||||
@current_user = current_user
|
||||
|
||||
move_payment_source_to_payment_attributes!
|
||||
|
||||
set_amount_in_payments_attributes
|
||||
|
||||
construct_saved_card_attributes if @params[:order][:existing_card_id]
|
||||
|
||||
@shipping_method_id = @params[:order].delete(:shipping_method_id)
|
||||
end
|
||||
|
||||
def order_params
|
||||
@params[:order]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# For payment step, filter order parameters to produce the expected
|
||||
# nested attributes for a single payment and its source,
|
||||
# discarding attributes for payment methods other than the one selected
|
||||
def move_payment_source_to_payment_attributes!
|
||||
return unless @params[:payment_source].present? &&
|
||||
payment_source_params = delete_payment_source_params!
|
||||
|
||||
@params[:order][:payments_attributes].first[:source_attributes] = payment_source_params
|
||||
end
|
||||
|
||||
def delete_payment_source_params!
|
||||
@params.delete(:payment_source)[
|
||||
@params[:order][:payments_attributes].first[:payment_method_id].underscore
|
||||
]
|
||||
end
|
||||
|
||||
def set_amount_in_payments_attributes
|
||||
return unless @params[:order][:payments_attributes]
|
||||
|
||||
@params[:order][:payments_attributes].first[:amount] = @order.total
|
||||
end
|
||||
|
||||
def construct_saved_card_attributes
|
||||
existing_card_id = @params[:order].delete(:existing_card_id)
|
||||
return if existing_card_id.blank?
|
||||
|
||||
add_to_payment_attributes(existing_card_id)
|
||||
|
||||
@params[:order][:payments_attributes].first.delete :source_attributes
|
||||
end
|
||||
|
||||
def add_to_payment_attributes(existing_card_id)
|
||||
credit_card = Spree::CreditCard.find(existing_card_id)
|
||||
if credit_card.try(:user_id).blank? || credit_card.user_id != @current_user.try(:id)
|
||||
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
|
||||
end
|
||||
|
||||
@params[:order][:payments_attributes].first[:source] = credit_card
|
||||
end
|
||||
end
|
||||
end
|
||||
27
app/services/checkout/payment_redirect.rb
Normal file
27
app/services/checkout/payment_redirect.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Provides the redirect path if a redirect to the payment gateway is needed
|
||||
module Checkout
|
||||
class PaymentRedirect
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
# Returns the path to the Paypal Express form if a redirect is needed
|
||||
def path
|
||||
return unless @params[:order][:payments_attributes]
|
||||
|
||||
payment_method_id = @params[:order][:payments_attributes].first[:payment_method_id]
|
||||
payment_method = Spree::PaymentMethod.find(payment_method_id)
|
||||
return unless payment_method.is_a?(Spree::Gateway::PayPalExpress)
|
||||
|
||||
spree_routes_helper.paypal_express_path(payment_method_id: payment_method.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def spree_routes_helper
|
||||
Spree::Core::Engine.routes_url_helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
14
app/services/order_payment_finder.rb
Normal file
14
app/services/order_payment_finder.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderPaymentFinder
|
||||
def self.last_payment_method(order)
|
||||
# `max_by` avoids additional database queries when payments are loaded
|
||||
# already. There is usually only one payment and this shouldn't cause
|
||||
# any overhead compared to `order(:created_at).last`. Using `last`
|
||||
# without order is not deterministic.
|
||||
#
|
||||
# We are not using `updated_at` because all payments are touched when the
|
||||
# order is updated and then all payments have the same `updated_at` value.
|
||||
order.payments.max_by(&:created_at)&.payment_method
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'open_food_network/permissions'
|
||||
|
||||
module Permissions
|
||||
class Order
|
||||
def initialize(user)
|
||||
@@ -8,7 +10,7 @@ module Permissions
|
||||
# Find orders that the user can see
|
||||
def visible_orders
|
||||
Spree::Order.
|
||||
with_line_items_variants_and_products_outer.
|
||||
with_line_items_variants_and_products.
|
||||
where(visible_orders_where_values)
|
||||
end
|
||||
|
||||
|
||||
@@ -82,13 +82,18 @@ class SubscriptionValidator
|
||||
|
||||
def credit_card_ok?
|
||||
return unless customer && payment_method
|
||||
return unless payment_method.type == "Spree::Gateway::StripeConnect"
|
||||
return unless stripe_payment_method?(payment_method)
|
||||
return errors.add(:payment_method, :charges_not_allowed) unless customer.allow_charges
|
||||
return if customer.user.andand.default_card.present?
|
||||
|
||||
errors.add(:payment_method, :no_default_card)
|
||||
end
|
||||
|
||||
def stripe_payment_method?(payment_method)
|
||||
payment_method.type == "Spree::Gateway::StripeConnect" ||
|
||||
payment_method.type == "Spree::Gateway::StripeSCA"
|
||||
end
|
||||
|
||||
def subscription_line_items_present?
|
||||
return if subscription_line_items.reject(&:marked_for_destruction?).any?
|
||||
|
||||
|
||||
39
app/services/user_default_address_setter.rb
Normal file
39
app/services/user_default_address_setter.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Sets the order addresses as the user default addresses
|
||||
class UserDefaultAddressSetter
|
||||
def initialize(order, current_user)
|
||||
@order = order
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
# Sets the order bill address as the user default bill address
|
||||
def set_default_bill_address
|
||||
new_bill_address = @order.bill_address.clone.attributes
|
||||
|
||||
set_bill_address_attributes(@current_user, new_bill_address)
|
||||
set_bill_address_attributes(@order.customer, new_bill_address)
|
||||
end
|
||||
|
||||
# Sets the order ship address as the user default ship address
|
||||
def set_default_ship_address
|
||||
new_ship_address = @order.ship_address.clone.attributes
|
||||
|
||||
set_ship_address_attributes(@current_user, new_ship_address)
|
||||
set_ship_address_attributes(@order.customer, new_ship_address)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_bill_address_attributes(object, new_address)
|
||||
object.update_attributes(
|
||||
bill_address_attributes: new_address.merge('id' => object.bill_address.andand.id)
|
||||
)
|
||||
end
|
||||
|
||||
def set_ship_address_attributes(object, new_address)
|
||||
object.update_attributes(
|
||||
ship_address_attributes: new_address.merge('id' => object.ship_address.andand.id)
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,3 @@
|
||||
-# For purposes of debugging bulk_update. See Admin/Enterprises#bulk_update.
|
||||
- if flash[:action]
|
||||
%p= flash[:action]
|
||||
|
||||
= form_for @enterprise_set, url: main_app.bulk_update_admin_enterprises_path do |f|
|
||||
%table#listing_enterprises.index
|
||||
%colgroup
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
- if type == 'supplier'
|
||||
%tr.panel-row{ object: "exchange",
|
||||
panels: "{products: 'exchange_products_supplied'}",
|
||||
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,selectAllVariants,suppliedVariants,removeDistributionOfVariant,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
locals: "$index,exchangeTotalVariants,order_cycle,exchange,enterprises,setExchangeVariants,selectAllVariants,suppliedVariants,removeDistributionOfVariant,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
colspan: 4 }
|
||||
- if type == 'distributor'
|
||||
%tr.panel-row{ object: "exchange",
|
||||
panels: "{products: 'exchange_products_distributed', tags: 'exchange_tags'}",
|
||||
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
locals: "$index,exchangeTotalVariants,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
colspan: 5 }
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{{ orderCycle.producers.length }}
|
||||
= t('.suppliers')
|
||||
%span{ ng: { hide: 'orderCycle.producers.length > 3', bind: 'orderCycle.producerNames' } }
|
||||
%td.coordinator{ ng: { show: 'columns.coordinator.visible' } }
|
||||
{{ orderCycle.coordinator.name }}
|
||||
%td.coordinator{ ng: { show: 'columns.coordinator.visible', bind: { html: 'orderCycle.coordinator.name'} } }
|
||||
%td.shops{ ng: { show: 'columns.shops.visible' } }
|
||||
%span{'ofn-with-tip' => '{{ orderCycle.shopNames }}', ng: { show: 'orderCycle.shops.length > 3' } }
|
||||
{{ orderCycle.shops.length }}
|
||||
@@ -33,8 +32,8 @@
|
||||
= t('.variants')
|
||||
|
||||
%td.actions
|
||||
%a.edit-order-cycle.icon-edit.no-text{ ng: { href: '{{orderCycle.edit_path}}'} }
|
||||
%a.edit-order-cycle.icon-edit.no-text{ ng: { href: '{{orderCycle.edit_path}}'}, 'ofn-with-tip' => t(:edit) }
|
||||
%td.actions{ ng: { if: 'orderCycle.viewing_as_coordinator' } }
|
||||
%a.clone-order-cycle.icon-copy.no-text{ ng: { href: '{{orderCycle.clone_path}}'} }
|
||||
%td.actions{ ng: { if: 'orderCycle.deletable && orderCycle.viewing_as_coordinator' } }
|
||||
%a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure) } }
|
||||
%a.clone-order-cycle.icon-copy.no-text{ ng: { href: '{{orderCycle.clone_path}}'}, 'ofn-with-tip' => t(:clone) }
|
||||
%td.actions{ ng: { if: 'orderCycle.deletable && orderCycle.viewing_as_coordinator' }}
|
||||
%a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure) }, 'ofn-with-tip' => t(:remove) }
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
%th.hide=t('admin.variant_overrides.index.hide')
|
||||
%tbody{ ng: { repeat: 'product in filteredProducts | limitTo:productLimit' } }
|
||||
%tr{ id: "v_{{variant.id}}", ng: { repeat: 'variant in product.variants | inventoryVariants:hub_id:views' } }
|
||||
%td.producer{ ng: { bind: '::producersByID[product.producer_id].name'} }
|
||||
%td.producer{ ng: { bind: { html: '::producersByID[product.producer_id].name'} } }
|
||||
%td.product{ ng: { bind: '::product.name'} }
|
||||
%td.variant
|
||||
%span{ ng: { bind: '::variant.display_name || ""'} }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%tr.product.even
|
||||
%td.producer{ ng: { show: 'columns.producer.visible', bind: '::producersByID[product.producer_id].name'} }
|
||||
%td.producer{ ng: { show: 'columns.producer.visible', bind: { html: '::producersByID[product.producer_id].name'} } }
|
||||
%td.product{ ng: { show: 'columns.product.visible', bind: '::product.name'} }
|
||||
%td.sku{ ng: { show: 'columns.sku.visible' } }
|
||||
%td.price{ ng: { show: 'columns.price.visible' } }
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
- content_for :injection_data do
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
%fieldset#payment
|
||||
%ng-form{"ng-controller" => "PaymentCtrl", name: "payment"}
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
%span.or= t('spree.or')
|
||||
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product), id: 'cancel_link', class: 'button'
|
||||
|
||||
= javascript_include_tag 'admin/images/new.js'
|
||||
= javascript_include_tag 'admin/spree/images/new.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%div{"data-hook" => "admin_orders_index_search"}
|
||||
= form_tag false, {name: "orders_form", "ng-submit" => "fetchResults()"} do
|
||||
= form_tag nil, {name: "orders_form", "ng-submit" => "fetchResults()"} do
|
||||
.field-block.alpha.four.columns
|
||||
.date-range-filter.field
|
||||
= label_tag nil, t(:date_range)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<script type='text/template' id='customer_autocomplete_template'>
|
||||
<div class='customer-autocomplete-item'>
|
||||
<div class='customer-details'>
|
||||
<h5>{{customer.email}}</h5>
|
||||
{{#if bill_address.firstname }}
|
||||
<strong>{{t 'bill_address' }}</strong>
|
||||
{{bill_address.firstname}} {{bill_address.lastname}}<br>
|
||||
{{bill_address.address1}}, {{bill_address.address2}}<br>
|
||||
{{bill_address.city}}<br>
|
||||
{{#if bill_address.state_id }}
|
||||
{{bill_address.state.name}}
|
||||
{{else}}
|
||||
{{bill_address.state_name}}
|
||||
{{/if}}
|
||||
{{bill_address.country.name}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
- ['number', 'state', 'payment_state', 'shipment_state', 'email', 'total'].each do |column_name|
|
||||
%th
|
||||
= render partial: 'sortable_header', locals: {column_name: column_name}
|
||||
= render partial: 'spree/admin/shared/sortable_header', locals: {column_name: column_name}
|
||||
|
||||
%th.actions
|
||||
%tbody
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
= @payment_method
|
||||
- case @payment_method
|
||||
- when Spree::Gateway::StripeConnect
|
||||
= render 'stripe_connect'
|
||||
- when Spree::Gateway::StripeSCA
|
||||
= render 'stripe_connect'
|
||||
- else
|
||||
- if @payment_method.preferences.present?
|
||||
%fieldset.alpha.eleven.columns.no-border-bottom#gateway_fields
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.stripe
|
||||
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('admin.payments').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
.row
|
||||
.three.columns
|
||||
= label_tag :cardholder_name, t(:cardholder_name)
|
||||
.six.columns
|
||||
= text_field_tag :cardholder_name, nil, {size: 40, "ng-model" => 'form_data.name'}
|
||||
.row
|
||||
.three.columns
|
||||
= label_tag :card_details, t(:card_details)
|
||||
.six.columns
|
||||
%stripe-elements
|
||||
@@ -6,28 +6,28 @@
|
||||
%dt
|
||||
= Spree.t(:card_number)
|
||||
\:
|
||||
%dd= payment.source.display_number
|
||||
%dd= payment.source&.display_number
|
||||
%dt
|
||||
= Spree.t(:expiration)
|
||||
\:
|
||||
%dd
|
||||
= payment.source.month
|
||||
= payment.source&.month
|
||||
\/
|
||||
= payment.source.year
|
||||
= payment.source&.year
|
||||
%dt
|
||||
= Spree.t(:card_code)
|
||||
\:
|
||||
%dd= payment.source.verification_value
|
||||
%dd= payment.source&.verification_value
|
||||
.omega.six.columns
|
||||
%dl
|
||||
%dt
|
||||
= t(:maestro_or_solo_cards)
|
||||
\:
|
||||
%dd= payment.source.issue_number
|
||||
%dd= payment.source&.issue_number
|
||||
%dt
|
||||
= Spree.t(:start_date)
|
||||
\:
|
||||
%dd
|
||||
= payment.source.start_month
|
||||
= payment.source&.start_month
|
||||
\/
|
||||
= payment.source.start_year
|
||||
= payment.source&.start_year
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
= render "spree/admin/payments/source_views/gateway", payment: payment
|
||||
@@ -26,7 +26,8 @@
|
||||
%th.image{ 'ng-show' => 'columns.image.visible' }
|
||||
%th.producer{ 'ng-show' => 'columns.producer.visible' }=t('admin.producer')
|
||||
%th.sku{ 'ng-show' => 'columns.sku.visible' }=t('admin.sku')
|
||||
%th.name{ 'ng-show' => 'columns.name.visible' }=t('.name')
|
||||
%th.name{ 'ng-show' => 'columns.name.visible' }
|
||||
= render partial: 'spree/admin/shared/sortable_header', locals: {column_name: 'name'}
|
||||
%th.unit{ 'ng-show' => 'columns.unit.visible' }=t('.unit')
|
||||
%th.display_as{ 'ng-show' => 'columns.unit.visible' }=t('.display_as')
|
||||
%th.price{ 'ng-show' => 'columns.price.visible' }=t('admin.price')
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
= render "spree/admin/shared/routes"
|
||||
|
||||
%script
|
||||
= "jQuery.alerts.dialogClass = 'spree';"
|
||||
= raw "var AUTH_TOKEN = \"#{form_authenticity_token}\";"
|
||||
|
||||
= render "layouts/bugherd_script"
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
- content_for :injection_data do
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
.row{ "ng-show" => "savedCreditCards.length > 0" }
|
||||
.small-12.columns
|
||||
%h6= t('.used_saved_card')
|
||||
|
||||
22
app/views/spree/checkout/payment/_stripe_sca.html.haml
Normal file
22
app/views/spree/checkout/payment/_stripe_sca.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
- content_for :injection_data do
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
.row{ "ng-show" => "savedCreditCards.length > 0" }
|
||||
.small-12.columns
|
||||
%h6= t('.used_saved_card')
|
||||
%select{ name: "selected_card", required: false, ng: { model: "secrets.selected_card", options: "card.id as card.formatted for card in savedCreditCards" } }
|
||||
%option{ value: "" }= "{{ secrets.selected_card ? '#{t('.enter_new_card')}' : '#{t('.choose_one')}' }}"
|
||||
|
||||
%h6{ ng: { if: '!secrets.selected_card' } }
|
||||
= t('.or_enter_new_card')
|
||||
|
||||
%div{ ng: { if: '!secrets.selected_card' } }
|
||||
%stripe-elements
|
||||
|
||||
- if spree_current_user
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
= check_box_tag 'secrets.save_requested_by_customer', '1', false, 'ng-model' => 'secrets.save_requested_by_customer'
|
||||
= label_tag 'secrets.save_requested_by_customer', t('.remember_this_card')
|
||||
@@ -8,7 +8,7 @@
|
||||
= t :email_payment_summary
|
||||
%h4
|
||||
= t :email_payment_method
|
||||
%strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe
|
||||
%strong= OrderPaymentFinder.last_payment_method(@order)&.name
|
||||
%p
|
||||
%em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe
|
||||
%em= OrderPaymentFinder.last_payment_method(@order)&.description
|
||||
%p
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
.pad
|
||||
.text-big
|
||||
= t :order_payment
|
||||
%strong= order.payments.first.andand.payment_method.andand.name.andand.html_safe
|
||||
%strong= OrderPaymentFinder.last_payment_method(order)&.name
|
||||
%p.text-small.text-skinny.pre-line
|
||||
%em= order.payments.first.andand.payment_method.andand.description.andand.html_safe
|
||||
%em= OrderPaymentFinder.last_payment_method(order)&.description
|
||||
|
||||
.order-summary.text-small
|
||||
%strong
|
||||
|
||||
@@ -92,6 +92,7 @@ module Openfoodnetwork
|
||||
app.config.spree.payment_methods << Spree::Gateway::Migs
|
||||
app.config.spree.payment_methods << Spree::Gateway::Pin
|
||||
app.config.spree.payment_methods << Spree::Gateway::StripeConnect
|
||||
app.config.spree.payment_methods << Spree::Gateway::StripeSCA
|
||||
end
|
||||
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
|
||||
@@ -34,10 +34,14 @@ Openfoodnetwork::Application.configure do
|
||||
config.action_mailer.default_url_options = { protocol: 'https' }
|
||||
|
||||
# See everything in the log (default is :info)
|
||||
config.log_level = :info
|
||||
# config.log_level = :debug
|
||||
|
||||
# Use a different logger for distributed setups
|
||||
# config.logger = SyslogLogger.new
|
||||
# Configure logging for Rails 3.2:
|
||||
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(Rails.root.join("log", "#{Rails.env}.log")))
|
||||
config.logger.formatter = Logger::Formatter.new
|
||||
config.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
||||
# Once we get to Rails 4.0, we can replace the above with:
|
||||
#config.log_formatter = Logger::Formatter.new.tap { |f| f.datetime_format = "%Y-%m-%d %H:%M:%S" }
|
||||
|
||||
# Use a different cache store in production
|
||||
memcached_value_max_megabytes = ENV.fetch("MEMCACHED_VALUE_MAX_MEGABYTES", 1).to_i
|
||||
|
||||
@@ -36,8 +36,12 @@ Openfoodnetwork::Application.configure do
|
||||
# See everything in the log (default is :info)
|
||||
# config.log_level = :debug
|
||||
|
||||
# Use a different logger for distributed setups
|
||||
# config.logger = SyslogLogger.new
|
||||
# Configure logging for Rails 3.2:
|
||||
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(Rails.root.join("log", "#{Rails.env}.log")))
|
||||
config.logger.formatter = Logger::Formatter.new
|
||||
config.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
||||
# Once we get to Rails 4.0, we can replace the above with:
|
||||
#config.log_formatter = Logger::Formatter.new.tap { |f| f.datetime_format = "%Y-%m-%d %H:%M:%S" }
|
||||
|
||||
# Use a different cache store in production
|
||||
memcached_value_max_megabytes = ENV.fetch("MEMCACHED_VALUE_MAX_MEGABYTES", 1).to_i
|
||||
|
||||
15
config/initializers/form_builder.rb
Normal file
15
config/initializers/form_builder.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Allow some application_helper methods to be used in the scoped form_for manner
|
||||
#
|
||||
class ActionView::Helpers::FormBuilder
|
||||
def field_container(method, options = {}, &block)
|
||||
@template.field_container(@object_name,method,options,&block)
|
||||
end
|
||||
|
||||
def error_message_on(method, options = {})
|
||||
@template.error_message_on(@object_name, method, objectify_options(options))
|
||||
end
|
||||
end
|
||||
|
||||
ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "<span class=\"field_with_errors\">#{html_tag}</span>".html_safe }
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
PaperTrail.config.track_associations = false
|
||||
|
||||
module PaperTrail
|
||||
class Version < ActiveRecord::Base
|
||||
attr_accessible :custom_data
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1281,6 +1281,7 @@ ar:
|
||||
saving_credit_card: جارٍ حفظ بطاقة الائتمان ...
|
||||
card_has_been_removed: "تمت إزالة بطاقتك (الرقم: %{number})"
|
||||
card_could_not_be_removed: عذرًا ، تعذرت إزالة البطاقة
|
||||
invalid_credit_card: "بطاقة الائتمان غير صالحة"
|
||||
ie_warning_headline: "متصفحك غير محدث :-("
|
||||
ie_warning_text: "للحصول على أفضل تجربة لشبكة الغذاء المفتوح ، نوصي بشدة بترقية متصفحك:"
|
||||
ie_warning_chrome: تحميل متصفح كروم
|
||||
@@ -2425,11 +2426,11 @@ ar:
|
||||
description: وصف
|
||||
resolve: حل
|
||||
exchange_products:
|
||||
load_more_products: "تحميل المزيد من المنتجات"
|
||||
load_all_products: "تحميل جميع المنتجات"
|
||||
select_all_products: "حدد جميع المنتجات %{total_number_of_products}"
|
||||
products_loaded: "%{num_of_products_loaded} من %{total_number_of_products} المنتجات المحملة"
|
||||
loading_products: "تحميل المنتجات"
|
||||
load_more_variants: "تحميل المزيد من المتغيرات"
|
||||
load_all_variants: "تحميل جميع المتغيرات"
|
||||
select_all_variants: "حدد كل %{total_number_of_variants} المتغيرات"
|
||||
variants_loaded: "%{num_of_variants_loaded} من %{total_number_of_variants} تم تحميل المتغيرات"
|
||||
loading_variants: "تحميل المتغيرات"
|
||||
tag_rules:
|
||||
shipping_method_tagged_top: "طرق الشحن الموسومة"
|
||||
shipping_method_tagged_bottom: "هي:"
|
||||
@@ -2588,6 +2589,21 @@ ar:
|
||||
signup_or_login: "البدء بالتسجيل (أو تسجيل الدخول)"
|
||||
have_an_account: "هل لديك حساب؟"
|
||||
action_login: "تسجيل الدخول الآن."
|
||||
inflections:
|
||||
each:
|
||||
zero: "كل"
|
||||
one: "كل"
|
||||
two: "كل"
|
||||
few: "كل"
|
||||
many: "كل"
|
||||
other: "كل"
|
||||
pack:
|
||||
zero: "حزم"
|
||||
one: "رزمة"
|
||||
two: "حزم"
|
||||
few: "حزم"
|
||||
many: "حزم"
|
||||
other: "حزم"
|
||||
producers:
|
||||
signup:
|
||||
start_free_profile: "ابدأ بملف تعريف مجاني ، وتوسع عندما تكون جاهزًا!"
|
||||
@@ -2830,6 +2846,8 @@ ar:
|
||||
zipcode: الرمز البريدي
|
||||
weight: الوزن (لكل كجم)
|
||||
error_user_destroy_with_orders: "لا يمكن حذف المستخدمين الذين لديهم طلبات مكتملة"
|
||||
cannot_create_payment_without_payment_methods: "لا يمكنك إنشاء دفعة لطلب بدون تحديد طرقة الدفع."
|
||||
please_define_payment_methods: "يرجى تحديد بعض طرق الدفع أولاً."
|
||||
options: "خيارات"
|
||||
actions:
|
||||
update: "تحديث"
|
||||
@@ -3137,6 +3155,12 @@ ar:
|
||||
used_saved_card: "استخدم بطاقة المحفوظة:"
|
||||
or_enter_new_card: "أو أدخل تفاصيل البطاقة الجديدة:"
|
||||
remember_this_card: تذكر هذه البطاقة؟
|
||||
stripe_sca:
|
||||
choose_one: اختيار واحد
|
||||
enter_new_card: أدخل تفاصيل البطاقة الجديدة
|
||||
used_saved_card: "استخدم بطاقة المحفوظة:"
|
||||
or_enter_new_card: "أو أدخل تفاصيل البطاقة الجديدة:"
|
||||
remember_this_card: تذكر هذه البطاقة؟
|
||||
date_picker:
|
||||
format: '٪ س-٪ م-%d'
|
||||
js_format: 'يوم-شهر-سنة'
|
||||
|
||||
@@ -1285,6 +1285,7 @@ ca:
|
||||
saving_credit_card: Desant la targeta de crèdit...
|
||||
card_has_been_removed: "S'ha eliminat la teva targeta (número: %{number})"
|
||||
card_could_not_be_removed: Ho sentim, no s'ha pogut eliminar la targeta
|
||||
invalid_credit_card: "Targeta de crèdit no vàlida"
|
||||
ie_warning_headline: "El vostre navegador no està actualitzat :-("
|
||||
ie_warning_text: "Per obtenir la millor experiència a Open Food Network et recomanem que actualitzis el teu navegador:"
|
||||
ie_warning_chrome: Descarrega Chrome
|
||||
@@ -2276,6 +2277,7 @@ ca:
|
||||
enterprise_register_success_notice: "Enhorabona! El registre de %{enterprise} s'ha completat!"
|
||||
enterprise_bulk_update_success_notice: "Les organitzacions s'han actualitzat correctament"
|
||||
enterprise_bulk_update_error: 'No s''ha pogut actualitzar'
|
||||
enterprise_shop_show_error: "La botiga que busqueu no existeix o està inactiva a OFN. Consulteu altres botigues."
|
||||
order_cycles_create_notice: 'S''ha creat el cicle de comanda.'
|
||||
order_cycles_update_notice: 'S''ha actualitzat el cicle de comanda.'
|
||||
order_cycles_bulk_update_notice: 'S''han actualitzat els cicles de comanda.'
|
||||
@@ -2431,11 +2433,11 @@ ca:
|
||||
description: Descripció
|
||||
resolve: Resoldre
|
||||
exchange_products:
|
||||
load_more_products: "Carrega més productes"
|
||||
load_all_products: "Carrega tots els productes"
|
||||
select_all_products: "Seleccioneu tots els productes %{total_number_of_products}"
|
||||
products_loaded: "%{num_of_products_loaded} de %{total_number_of_products} productes carregats"
|
||||
loading_products: "Carregant productes"
|
||||
load_more_variants: "Carregueu més variants"
|
||||
load_all_variants: "Carregueu totes les variants"
|
||||
select_all_variants: "Seleccioneu totes les %{total_number_of_variants} variants"
|
||||
variants_loaded: "%{num_of_variants_loaded} de %{total_number_of_variants} variants carregades"
|
||||
loading_variants: "Carregant variants"
|
||||
tag_rules:
|
||||
shipping_method_tagged_top: "Els mètodes d'enviament etiquetats"
|
||||
shipping_method_tagged_bottom: "son:"
|
||||
@@ -2594,6 +2596,73 @@ ca:
|
||||
signup_or_login: "Comenceu registrant-vos (o iniciant sessió)"
|
||||
have_an_account: "Ja tens un compte?"
|
||||
action_login: "Inicia la sessió ara."
|
||||
inflections:
|
||||
each:
|
||||
one: "cadascun"
|
||||
other: "cadascun"
|
||||
bunch:
|
||||
one: "munt"
|
||||
other: "grapats"
|
||||
pack:
|
||||
one: "paquet"
|
||||
other: "paquets"
|
||||
box:
|
||||
one: "Caixa"
|
||||
other: "caixes"
|
||||
bottle:
|
||||
one: "ampolla"
|
||||
other: "ampolles"
|
||||
jar:
|
||||
one: "gerro"
|
||||
other: "pots"
|
||||
head:
|
||||
one: "cap"
|
||||
other: "caps"
|
||||
bag:
|
||||
one: "bossa"
|
||||
other: "bosses"
|
||||
loaf:
|
||||
one: "pa"
|
||||
other: "barres"
|
||||
single:
|
||||
one: "solter"
|
||||
other: "únics"
|
||||
tub:
|
||||
one: "tina"
|
||||
other: "cubells"
|
||||
item:
|
||||
one: "article"
|
||||
other: "articles"
|
||||
dozen:
|
||||
one: "dotzena"
|
||||
other: "dotzenes"
|
||||
unit:
|
||||
one: "unitat"
|
||||
other: "unitats"
|
||||
serve:
|
||||
one: "servir"
|
||||
other: "porcions"
|
||||
tray:
|
||||
one: "safata"
|
||||
other: "safates"
|
||||
piece:
|
||||
one: "peça"
|
||||
other: "peces"
|
||||
pot:
|
||||
one: "pot"
|
||||
other: "pots"
|
||||
bundle:
|
||||
one: "paquet"
|
||||
other: "paquets"
|
||||
flask:
|
||||
one: "matràs"
|
||||
other: "ampolleta"
|
||||
basket:
|
||||
one: "cistella"
|
||||
other: "cistelles"
|
||||
sack:
|
||||
one: "sac"
|
||||
other: "sacs"
|
||||
producers:
|
||||
signup:
|
||||
start_free_profile: "Comença amb un perfil gratuït i amplia'l quan estiguis preparada."
|
||||
@@ -3145,6 +3214,12 @@ ca:
|
||||
used_saved_card: "Utilitza una targeta desada:"
|
||||
or_enter_new_card: "O bé introdueix els detalls d'una nova targeta:"
|
||||
remember_this_card: Recordar aquesta targeta?
|
||||
stripe_sca:
|
||||
choose_one: Escull-ne un
|
||||
enter_new_card: Introdueix els detalls d'una targeta nova
|
||||
used_saved_card: "Utilitza una targeta desada:"
|
||||
or_enter_new_card: "O bé introdueix els detalls d'una nova targeta:"
|
||||
remember_this_card: Recordar aquesta targeta?
|
||||
date_picker:
|
||||
format: '%d-% m-% Y'
|
||||
js_format: 'dd-mm-yy'
|
||||
|
||||
@@ -2575,6 +2575,10 @@ de_DE:
|
||||
signup_or_login: "Beginnen Sie mit der Anmeldung (oder melden Sie sich an)"
|
||||
have_an_account: "Hast du schon ein Konto?"
|
||||
action_login: "Jetzt einloggen."
|
||||
inflections:
|
||||
bottle:
|
||||
one: "Flasche"
|
||||
other: "Flaschen"
|
||||
producers:
|
||||
signup:
|
||||
start_free_profile: "Beginnen Sie mit einem kostenlosen Profil und erweitern Sie es, wenn Sie fertig sind!"
|
||||
|
||||
@@ -1364,6 +1364,7 @@ en:
|
||||
saving_credit_card: Saving credit card...
|
||||
card_has_been_removed: "Your card has been removed (number: %{number})"
|
||||
card_could_not_be_removed: Sorry, the card could not be removed
|
||||
invalid_credit_card: "Invalid credit card"
|
||||
|
||||
ie_warning_headline: "Your browser is out of date :-("
|
||||
ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:"
|
||||
@@ -2411,6 +2412,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
enterprise_register_success_notice: "Congratulations! Registration for %{enterprise} is complete!"
|
||||
enterprise_bulk_update_success_notice: "Enterprises updated successfully"
|
||||
enterprise_bulk_update_error: 'Update failed'
|
||||
enterprise_shop_show_error: "The shop you are looking for doesn't exist or is inactive on OFN. Please check other shops."
|
||||
order_cycles_create_notice: 'Your order cycle has been created.'
|
||||
order_cycles_update_notice: 'Your order cycle has been updated.'
|
||||
order_cycles_bulk_update_notice: 'Order cycles have been updated.'
|
||||
@@ -2557,11 +2559,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
description: Description
|
||||
resolve: Resolve
|
||||
exchange_products:
|
||||
load_more_products: "Load More Products"
|
||||
load_all_products: "Load All Products"
|
||||
select_all_products: "Select All %{total_number_of_products} Products"
|
||||
products_loaded: "%{num_of_products_loaded} of %{total_number_of_products} Products Loaded"
|
||||
loading_products: "Loading Products"
|
||||
load_more_variants: "Load More Variants"
|
||||
load_all_variants: "Load All Variants"
|
||||
select_all_variants: "Select All %{total_number_of_variants} Variants"
|
||||
variants_loaded: "%{num_of_variants_loaded} of %{total_number_of_variants} Variants Loaded"
|
||||
loading_variants: "Loading Variants"
|
||||
tag_rules:
|
||||
shipping_method_tagged_top: "Shipping methods tagged"
|
||||
shipping_method_tagged_bottom: "are:"
|
||||
@@ -2721,6 +2723,90 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
have_an_account: "Already have an account?"
|
||||
action_login: "Log in now."
|
||||
|
||||
# Singular and plural forms of commonly used words.
|
||||
# We use these entries to pluralize unit names in every language.
|
||||
#
|
||||
# Extracted with the following query:
|
||||
# Spree::Product.group(:variant_unit_name).order("count_all DESC").count.each { |name, count|
|
||||
# puts " # Used #{count} times."
|
||||
# puts " #{name&.parameterize('_')}:"
|
||||
# puts " one: \"#{name}\""
|
||||
# puts " other: \"#{name}s\"";
|
||||
# }
|
||||
inflections:
|
||||
each:
|
||||
one: "each"
|
||||
other: "each"
|
||||
bunch:
|
||||
one: "bunch"
|
||||
other: "bunches"
|
||||
pack:
|
||||
one: "pack"
|
||||
other: "packs"
|
||||
box:
|
||||
one: "box"
|
||||
other: "boxes"
|
||||
bottle:
|
||||
one: "bottle"
|
||||
other: "bottles"
|
||||
jar:
|
||||
one: "jar"
|
||||
other: "jars"
|
||||
head:
|
||||
one: "head"
|
||||
other: "heads"
|
||||
bag:
|
||||
one: "bag"
|
||||
other: "bags"
|
||||
loaf:
|
||||
one: "loaf"
|
||||
other: "loaves"
|
||||
single:
|
||||
one: "single"
|
||||
other: "singles"
|
||||
tub:
|
||||
one: "tub"
|
||||
other: "tubs"
|
||||
punnet:
|
||||
one: "punnet"
|
||||
other: "punnets"
|
||||
packet:
|
||||
one: "packet"
|
||||
other: "packets"
|
||||
item:
|
||||
one: "item"
|
||||
other: "items"
|
||||
dozen:
|
||||
one: "dozen"
|
||||
other: "dozens"
|
||||
unit:
|
||||
one: "unit"
|
||||
other: "units"
|
||||
serve:
|
||||
one: "serve"
|
||||
other: "serves"
|
||||
tray:
|
||||
one: "tray"
|
||||
other: "trays"
|
||||
piece:
|
||||
one: "piece"
|
||||
other: "pieces"
|
||||
pot:
|
||||
one: "pot"
|
||||
other: "pots"
|
||||
bundle:
|
||||
one: "bundle"
|
||||
other: "bundles"
|
||||
flask:
|
||||
one: "flask"
|
||||
other: "flasks"
|
||||
basket:
|
||||
one: "basket"
|
||||
other: "baskets"
|
||||
sack:
|
||||
one: "sack"
|
||||
other: "sacks"
|
||||
|
||||
producers:
|
||||
signup:
|
||||
start_free_profile: "Start with a free profile, and expand when you're ready!"
|
||||
@@ -3298,6 +3384,12 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
stripe_sca:
|
||||
choose_one: Choose one
|
||||
enter_new_card: Enter details for a new card
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
date_picker:
|
||||
format: ! '%Y-%m-%d'
|
||||
js_format: 'yy-mm-dd'
|
||||
|
||||
@@ -119,7 +119,7 @@ en_AU:
|
||||
userguide: "Open Food Network User Guide"
|
||||
email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration."
|
||||
admin_panel: "Admin Panel"
|
||||
email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. %{link}"
|
||||
email_community_html: "Open Food Network is community supported software. If you have less than $500 per month turnover on OFN it's free to use. Read more about our membership options and make your selection: https://about.openfoodnetwork.org.au/software-pricing/. If you haven't yet chosen your membership option, you will be assigned to our basic community membership level, which is 1% of turnover through the platform. We'll invoice you monthly once you reach that level. "
|
||||
join_community: "Join the community"
|
||||
invite_manager:
|
||||
subject: "%{enterprise} has invited you to be a manager"
|
||||
@@ -782,7 +782,14 @@ en_AU:
|
||||
producer_shop_description_text2: A Producer Shop is for your produce only, if you want to sell produce grown/produced off site, select 'Producer Hub'.
|
||||
producer_hub: Producer Hub
|
||||
producer_hub_text: Sell produce from self and others
|
||||
producer_hub_description_text: Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network.
|
||||
producer_hub_description_text: 'You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network.
|
||||
|
||||
|
||||
Free to use for shopfronts with turnover of less than $500 a month.
|
||||
|
||||
1% of turnover for shopfronts with turnover of more than $500 a month.
|
||||
|
||||
2-3% of turnover for solidarity partners who choose to collaborate on growing a new food system.'
|
||||
profile: Profile Only
|
||||
get_listing: Get a listing
|
||||
profile_description_text: People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings.
|
||||
@@ -1617,7 +1624,7 @@ en_AU:
|
||||
sell_groups_detail: "Set up a tailored directory of enterprises (producers and other food enterprises) for your region or for your organisation."
|
||||
sell_user_guide: "Find out more in our user guide."
|
||||
sell_listing_price: "Listing on the OFN is free. Opening and running a shop on OFN is free up to $500 of monthly sales. If you sell more you can choose your community contribution between 1% and 3% of sales. For more detail on pricing visit the Software Platform section via the About link in the top menu."
|
||||
sell_embed: "We can also embed an OFN shop in your own customised website or build a customised local food network website for your region."
|
||||
sell_embed: "In addition to the Open Food Network platform, our social enterprise consultancy offers a range of services that help power a better, fairer food system. We can help with customised business and online solutions for your farm or food enterprise, lean enterprise support, regional food system development as well as food systems research and consulting."
|
||||
sell_ask_services: "Ask us about OFN services."
|
||||
shops_title: Shops
|
||||
shops_headline: Shopping, transformed.
|
||||
@@ -1730,46 +1737,46 @@ en_AU:
|
||||
registration:
|
||||
steps:
|
||||
introduction:
|
||||
registration_greeting: "Hi there!"
|
||||
registration_intro: "You can now create a profile for your Producer or Hub"
|
||||
registration_checklist: "What do I need?"
|
||||
registration_time: "5-10 minutes"
|
||||
registration_enterprise_address: "Enterprise address"
|
||||
registration_contact_details: "Primary contact details"
|
||||
registration_logo: "Your logo image"
|
||||
registration_promo_image: "Landscape image for your profile"
|
||||
registration_about_us: "'About Us' text"
|
||||
registration_outcome_headline: "What do I get?"
|
||||
registration_outcome1_html: "Your profile helps people <strong>find</strong> and <strong>contact</strong> you on the Open Food Network."
|
||||
registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence."
|
||||
registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store."
|
||||
registration_greeting: "Let’s get you set up"
|
||||
registration_intro: "Share your story to connect with the community (and eaters!)"
|
||||
registration_checklist: "Set up your profile"
|
||||
registration_time: "Tell us about yourself "
|
||||
registration_enterprise_address: "And all about your enterprise"
|
||||
registration_contact_details: "Then share a bit about what you do..."
|
||||
registration_logo: "...or even what you sell"
|
||||
registration_promo_image: "Add pictures, and stories, and ways to get in touch"
|
||||
registration_about_us: "Or manage your profile whenever you need"
|
||||
registration_outcome_headline: "Connect with the community and eaters"
|
||||
registration_outcome1_html: "Your profile helps people find and connect with you directly on the Open Food Network."
|
||||
registration_outcome2: "Use this space to tell your story, and to help drive connections with your enterprise. "
|
||||
registration_outcome3: "It’s also the first step to opening your own store on the Open Food Network. \nIt’s totally free to get set up. If your shop starts to earn more than $500 a month, a 1% membership fee will apply."
|
||||
registration_action: "Let's get started!"
|
||||
details:
|
||||
title: "Details"
|
||||
headline: "Let's Get Started"
|
||||
enterprise: "Woot! First need to know a little bit about your enterprise:"
|
||||
producer: "Woot! First we need to know a little bit about your farm:"
|
||||
enterprise_name_field: "Enterprise Name:"
|
||||
producer_name_field: "Farm Name:"
|
||||
headline: "Tell us about your enterprise"
|
||||
enterprise: "This will put you on our map, and be shared on your profile."
|
||||
producer: "This will put you on our map, and be shared on your profile."
|
||||
enterprise_name_field: "Enterprise Name"
|
||||
producer_name_field: "Farm Name"
|
||||
producer_name_field_placeholder: "e.g. Charlie's Awesome Farm"
|
||||
producer_name_field_error: "Please choose a unique name for your enterprise"
|
||||
address1_field: "Address line 1:"
|
||||
address1_field: "Address line 1"
|
||||
address1_field_placeholder: "e.g. 123 Cranberry Drive"
|
||||
address1_field_error: "Please enter an address"
|
||||
address2_field: "Address line 2:"
|
||||
suburb_field: "Suburb:"
|
||||
address2_field: "Address line 2"
|
||||
suburb_field: "Suburb"
|
||||
suburb_field_placeholder: "e.g. Northcote"
|
||||
suburb_field_error: "Please enter a suburb"
|
||||
postcode_field: "Postcode:"
|
||||
postcode_field: "Postcode"
|
||||
postcode_field_placeholder: "e.g. 3070"
|
||||
postcode_field_error: "Postcode required"
|
||||
state_field: "State:"
|
||||
state_field: "State"
|
||||
state_field_error: "State required"
|
||||
country_field: "Country:"
|
||||
country_field: "Country"
|
||||
country_field_error: "Please select a country"
|
||||
contact:
|
||||
title: "Contact"
|
||||
who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?"
|
||||
who_is_managing_enterprise: "Who is the main contact for %{enterprise}? We’ll add these details to your profile so people can get in touch."
|
||||
contact_field: "Primary Contact"
|
||||
contact_field_placeholder: "Contact Name"
|
||||
contact_field_required: "You need to enter a primary contact."
|
||||
@@ -1777,24 +1784,24 @@ en_AU:
|
||||
phone_field_placeholder: "eg. (03) 1234 5678"
|
||||
type:
|
||||
title: "Type"
|
||||
headline: "Last step to add %{enterprise}!"
|
||||
question: "Are you a producer?"
|
||||
yes_producer: "Yes, I'm a producer"
|
||||
no_producer: "No, I'm not a producer"
|
||||
headline: "How will you use the Open Food Network?"
|
||||
question: "This helps us work out what sort of package would best suit you."
|
||||
yes_producer: "I’ll share my own produce"
|
||||
no_producer: "I’ll share other people’s produce (and mine!)"
|
||||
producer_field_error: "Please choose one. Are you are producer?"
|
||||
yes_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it."
|
||||
no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other."
|
||||
yes_producer_help: "What sort of produce? All the yummy things you can eat and drink. If you grow it, raise it, brew it, bake it, ferment it, milk it or mould it – you’re a producer. "
|
||||
no_producer_help: "Maybe you’re someone who sells or distributes food that you or others have produced. You might be a hub, co-op, buying group, retailer, wholesaler, or someone else."
|
||||
create_profile: "Create Profile"
|
||||
about:
|
||||
title: "About"
|
||||
headline: "Nice one!"
|
||||
message: "Now let's flesh out the details about"
|
||||
success: "Success! %{enterprise} added to the Open Food Network"
|
||||
registration_exit_message: "If you exit this wizard at any stage, you can continue to create your profile by going to the admin interface."
|
||||
enterprise_description: "Short Description"
|
||||
headline: "Add to your profile"
|
||||
message: "Your profile is set up! You can complete the details later, or start adding details for"
|
||||
success: "Welcome to the Open Food Network, %{enterprise}!"
|
||||
registration_exit_message: "If you need a bit more time, you can upload an image and add to your story in the Admin section of your profile another time."
|
||||
enterprise_description: "What do you do?"
|
||||
enterprise_description_placeholder: "A short sentence describing your enterprise"
|
||||
enterprise_long_desc: "Long Description"
|
||||
enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words."
|
||||
enterprise_long_desc: "Tell us more"
|
||||
enterprise_long_desc_placeholder: "This is your opportunity to tell your story – what makes your enterprise different and wonderful? Try to keep it around 150 words so it doesn’t get cut off."
|
||||
enterprise_long_desc_length: "%{num} characters / up to 600 recommended"
|
||||
enterprise_abn: "ABN"
|
||||
enterprise_abn_placeholder: "eg. 99 123 456 789"
|
||||
@@ -1802,31 +1809,31 @@ en_AU:
|
||||
enterprise_acn_placeholder: "eg. 123 456 789"
|
||||
enterprise_tax_required: "You need to make a selection."
|
||||
images:
|
||||
title: "Images"
|
||||
headline: "Thanks!"
|
||||
description: "Let's upload some pretty pictures so your profile looks great! :)"
|
||||
title: "Logo"
|
||||
headline: "Upload your logo"
|
||||
description: "Help people recognise you (and make your profile stand out!)"
|
||||
uploading: "Uploading..."
|
||||
continue: "Continue"
|
||||
back: "Back"
|
||||
logo:
|
||||
select_logo: "Step 1. Select Logo Image"
|
||||
logo_tip: "Tip: Square images will work best, preferably at least 300×300px"
|
||||
logo_label: "Choose a logo image"
|
||||
logo_drag: "Drag and drop your logo here"
|
||||
review_logo: "Step 2. Review Your Logo"
|
||||
review_logo_tip: "Tip: for best results, your logo should fill the available space"
|
||||
logo_placeholder: "Your logo will appear here for review once uploaded"
|
||||
select_logo: "Select file to upload"
|
||||
logo_tip: "Try using a square image, around 300x300px."
|
||||
logo_label: "Select a file"
|
||||
logo_drag: "Drag and drop here"
|
||||
review_logo: "How does it look?"
|
||||
review_logo_tip: "For best results, the image should fill the available space"
|
||||
logo_placeholder: "Your logo will appear here once it's uploaded"
|
||||
promo:
|
||||
select_promo_image: "Step 3. Select Promo Image"
|
||||
promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px"
|
||||
promo_image_label: "Choose a promo image"
|
||||
promo_image_drag: "Drag and drop your promo here"
|
||||
review_promo_image: "Step 4. Review Your Promo Banner"
|
||||
review_promo_image_tip: "Tip: for best results, your promo image should fill the available space"
|
||||
promo_image_placeholder: "Your logo will appear here for review once uploaded"
|
||||
select_promo_image: "Choose a shop header image"
|
||||
promo_image_tip: "This displays as a banner. For best results try an image that's around 1200×260px"
|
||||
promo_image_label: "Choose a header image"
|
||||
promo_image_drag: "Drag and drop your header image here"
|
||||
review_promo_image: "How does it look?"
|
||||
review_promo_image_tip: "For best results, your banner image should fill the available space"
|
||||
promo_image_placeholder: "Your logo will appear here once it's uploaded"
|
||||
social:
|
||||
title: "Social"
|
||||
enterprise_final_step: "Final step!"
|
||||
enterprise_final_step: "One last thing..."
|
||||
enterprise_social_text: "How can people find %{enterprise} online?"
|
||||
website: "Website"
|
||||
website_placeholder: "eg. openfoodnetwork.org.au"
|
||||
@@ -1844,9 +1851,9 @@ en_AU:
|
||||
text: "You have reached the limit for the number of enterprises you are allowed to own on the"
|
||||
action: "Return to the homepage"
|
||||
finished:
|
||||
headline: "Finished!"
|
||||
headline: "You’re all set up! "
|
||||
thanks: "Thanks for filling out the details for %{enterprise}."
|
||||
login: "To manage your new Enterprise, go to openfoodnetwork.org.au/admin"
|
||||
login: "To manage your new Enterprise, go to openfoodnetwork.org.au/admin\n\nYou can also get to your Admin page in the top righthand corner of the Open Food Network homepage, just to the left of the shopping cart symbol."
|
||||
action: "Open Food Network home"
|
||||
back: "Back"
|
||||
continue: "Continue"
|
||||
@@ -3119,6 +3126,12 @@ en_AU:
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
stripe_sca:
|
||||
choose_one: Choose one
|
||||
enter_new_card: Enter details for a new card
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
date_picker:
|
||||
format: '%Y-%m-%d'
|
||||
js_format: 'yy-mm-dd'
|
||||
|
||||
@@ -3044,6 +3044,12 @@ en_BE:
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
stripe_sca:
|
||||
choose_one: Choose one
|
||||
enter_new_card: Enter details for a new card
|
||||
used_saved_card: "Use a saved card:"
|
||||
or_enter_new_card: "Or, enter details for a new card:"
|
||||
remember_this_card: Remember this card?
|
||||
date_picker:
|
||||
format: '%Y-%m-%d'
|
||||
js_format: 'yy-mm-dd'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user