mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-13 18:46:49 +00:00
Compare commits
407 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 | ||
|
|
be19d50639 | ||
|
|
0ceb8ab6c4 | ||
|
|
e387c7db83 | ||
|
|
d5df48f3c0 | ||
|
|
c9abdac2e0 | ||
|
|
ff08d9f210 | ||
|
|
e5c9468d09 | ||
|
|
8aed173127 | ||
|
|
61ecca7257 | ||
|
|
7eba657b2f | ||
|
|
784de340d1 | ||
|
|
9191628f29 | ||
|
|
88410b1efd | ||
|
|
afea032361 | ||
|
|
3decb4056c | ||
|
|
8e9b08feca | ||
|
|
694995ea5d | ||
|
|
22de7252d0 | ||
|
|
2acf8e5125 | ||
|
|
e1f61e645d | ||
|
|
687d827ceb | ||
|
|
2885e38113 | ||
|
|
9f3ca58b55 | ||
|
|
ffe3228848 | ||
|
|
834231eb8f | ||
|
|
68f5aabd3f | ||
|
|
7e7ea92833 | ||
|
|
ebf22ceb19 | ||
|
|
9313a57d19 | ||
|
|
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 | ||
|
|
b0f2e01c70 | ||
|
|
747be81aec | ||
|
|
fd124daf50 | ||
|
|
ef33d27e6c | ||
|
|
3a01e00d7b | ||
|
|
81103f3f71 | ||
|
|
9d1e3f0318 | ||
|
|
67adf3c801 | ||
|
|
a8b48a561f | ||
|
|
c7038f6ac9 | ||
|
|
ea75714561 | ||
|
|
b7d19dd34c | ||
|
|
44d1b3f00c | ||
|
|
f8376c0aeb | ||
|
|
88464d58c2 | ||
|
|
962779bba1 | ||
|
|
1ae73dd6ae | ||
|
|
f48ab57782 | ||
|
|
8b6ebfb351 | ||
|
|
9c0788c3e0 | ||
|
|
f61258d0b4 | ||
|
|
289f62f115 | ||
|
|
c3a98d5a4f | ||
|
|
d8374e9caa | ||
|
|
0648f23e94 | ||
|
|
e37cb2d63f | ||
|
|
ee64238bdd | ||
|
|
d3130e111f | ||
|
|
93f4eee887 | ||
|
|
408ada9097 | ||
|
|
e14ebd9503 | ||
|
|
289b99c30e | ||
|
|
f85c36a17e | ||
|
|
4715df3258 | ||
|
|
b20be101cc | ||
|
|
d424987587 | ||
|
|
a0575430dd | ||
|
|
c1e6344b18 | ||
|
|
ed0198382f | ||
|
|
577fb88843 | ||
|
|
2b879221b2 | ||
|
|
22db2e99f0 | ||
|
|
ef4aa488c3 | ||
|
|
57775f49bc | ||
|
|
e8a12d9897 | ||
|
|
f3b8d5b868 | ||
|
|
246235b921 | ||
|
|
0ded41afee | ||
|
|
d32e106bf0 | ||
|
|
bbb3748d3c | ||
|
|
08dd992344 | ||
|
|
914244a1ee | ||
|
|
e5f089610c | ||
|
|
6e26841817 | ||
|
|
ab60c4a9dd | ||
|
|
7009cd89e0 | ||
|
|
b93af37ea9 | ||
|
|
76b6a85509 | ||
|
|
a1d4b4ee98 | ||
|
|
51bca7ce2f | ||
|
|
70147f908a | ||
|
|
c4bf4f001f | ||
|
|
7d71f21753 | ||
|
|
285c78a5e4 | ||
|
|
6e78ae762a | ||
|
|
9a2ad16926 | ||
|
|
afec21eb3d | ||
|
|
651ee720c6 | ||
|
|
2e6c5e1fad | ||
|
|
87366ae7fc | ||
|
|
27aea0b277 | ||
|
|
d4edc9f20d | ||
|
|
6c7991be75 | ||
|
|
3f81352df5 | ||
|
|
9d0e26ae28 | ||
|
|
d80554a14a | ||
|
|
a5fe5fb448 | ||
|
|
4c51d60bfd | ||
|
|
9218008530 | ||
|
|
f36c5b8938 | ||
|
|
67199fd2d6 | ||
|
|
18d17ec674 | ||
|
|
3981ee7ec1 | ||
|
|
a0475ee8a4 | ||
|
|
4cdc604f45 | ||
|
|
dbf44c41b2 | ||
|
|
9535c5647f | ||
|
|
6f8bb793e1 | ||
|
|
2476050f29 | ||
|
|
1cce106977 | ||
|
|
98b55287f1 | ||
|
|
25c4aed368 | ||
|
|
c5a6ef673c | ||
|
|
79ba15fe9a | ||
|
|
e192207f4e | ||
|
|
48a75c956f | ||
|
|
baf1ecb436 | ||
|
|
947914724a | ||
|
|
b5004f1cbf | ||
|
|
4596399bc2 | ||
|
|
685abccb61 | ||
|
|
9254928656 | ||
|
|
228997c35b | ||
|
|
667f44336d | ||
|
|
0a136ff2fb | ||
|
|
3f3577e73c | ||
|
|
cf1664bed3 | ||
|
|
b8aee4e857 | ||
|
|
cfe3435851 | ||
|
|
53e342ba1a | ||
|
|
6bdb14248c | ||
|
|
8e27291b15 | ||
|
|
ec67736dff | ||
|
|
766303b332 | ||
|
|
74226fbdf8 | ||
|
|
dc5374e284 | ||
|
|
f6ecf57737 | ||
|
|
940953b043 | ||
|
|
fbc5887fa6 | ||
|
|
61ce849546 | ||
|
|
afddaed9fc | ||
|
|
12158d73fa | ||
|
|
5f3abbf00e | ||
|
|
a02c58e231 | ||
|
|
59ebfb9bd4 | ||
|
|
57ca1d54bb | ||
|
|
138248e1c9 | ||
|
|
5fccd5fe58 | ||
|
|
bec73adc89 | ||
|
|
23ec66e338 | ||
|
|
4658b7a533 | ||
|
|
d0f33e7c8a | ||
|
|
55bb328d48 | ||
|
|
a8a6fce385 | ||
|
|
60677a2414 |
@@ -2,7 +2,7 @@ version: "2"
|
||||
plugins:
|
||||
rubocop:
|
||||
enabled: true
|
||||
channel: "rubocop-0-57"
|
||||
channel: "rubocop-0-76"
|
||||
config:
|
||||
file: ".rubocop_styleguide.yml"
|
||||
scss-lint:
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/release.md
vendored
7
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Release task
|
||||
about: Track the process of a new release
|
||||
title: ''
|
||||
title: 'Release v'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
@@ -10,10 +10,13 @@ assignees: ''
|
||||
Steps:
|
||||
|
||||
- [ ] Include translations
|
||||
- [ ] Draft: https://github.com/openfoodfoundation/openfoodnetwork/releases/new <!-- replace the URL -->
|
||||
- [ ] [Draft new release]
|
||||
- [ ] Notify #instance-managers of user-facing changes.
|
||||
- [ ] Test: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master <!-- replace the URL -->
|
||||
- [ ] Publish and notify #global-community
|
||||
- [ ] Deploy and notify #instance-managers
|
||||
- [ ] Nudge next release manager
|
||||
|
||||
The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing.
|
||||
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
|
||||
|
||||
@@ -39,11 +39,8 @@ 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/application_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/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
- app/controllers/spree/paypal_controller_decorator.rb
|
||||
- app/controllers/stripe/callbacks_controller.rb
|
||||
@@ -356,24 +353,22 @@ 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
|
||||
- app/controllers/spree/admin/image_settings_controller.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller.rb
|
||||
- app/controllers/spree/admin/orders_controller.rb
|
||||
- app/controllers/spree/admin/overview_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/payments_controller.rb
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/search_controller_decorator.rb
|
||||
- app/controllers/spree/admin/search_controller.rb
|
||||
- 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
|
||||
@@ -391,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
|
||||
@@ -418,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
|
||||
@@ -495,8 +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/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -513,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
|
||||
@@ -526,8 +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/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -540,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
|
||||
@@ -564,16 +558,15 @@ 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_decorator.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller.rb
|
||||
- app/controllers/spree/admin/orders_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/payments_controller.rb
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/products_controller.rb
|
||||
- app/controllers/spree/admin/search_controller_decorator.rb
|
||||
- app/controllers/spree/admin/tax_categories_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
@@ -610,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
|
||||
@@ -652,8 +646,10 @@ Metrics/ClassLength:
|
||||
- app/controllers/admin/order_cycles_controller.rb
|
||||
- app/controllers/admin/subscriptions_controller.rb
|
||||
- app/controllers/api/products_controller.rb
|
||||
- app/controllers/application_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/base_controller.rb
|
||||
- app/controllers/spree/admin/orders_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
@@ -671,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
|
||||
|
||||
@@ -335,6 +335,7 @@ Rails/OutputSafety:
|
||||
- 'app/helpers/spree/admin/zones_helper.rb'
|
||||
- 'app/helpers/spree/reports_helper.rb'
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
- 'app/helpers/spree/admin/orders_helper.rb'
|
||||
- 'app/serializers/api/product_serializer.rb'
|
||||
- 'lib/spree/money_decorator.rb'
|
||||
- 'spec/features/admin/orders_spec.rb'
|
||||
|
||||
34
Gemfile
34
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,34 +92,31 @@ gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'foreigner'
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.8.2'
|
||||
gem 'roo-xls', '~> 1.1.0'
|
||||
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'
|
||||
|
||||
|
||||
69
Gemfile.lock
69
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)
|
||||
@@ -166,7 +154,7 @@ GEM
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
blockenspiel (0.5.0)
|
||||
bugsnag (6.12.2)
|
||||
bugsnag (6.13.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.0.4)
|
||||
byebug (9.0.6)
|
||||
@@ -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.1)
|
||||
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)
|
||||
@@ -530,7 +518,7 @@ GEM
|
||||
rack (1.4.7)
|
||||
rack-cache (1.9.0)
|
||||
rack (>= 0.4)
|
||||
rack-mini-profiler (1.1.4)
|
||||
rack-mini-profiler (1.1.6)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (1.5.5)
|
||||
rack
|
||||
@@ -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,19 +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)
|
||||
roo-xls (1.1.0)
|
||||
nokogiri
|
||||
roo (>= 2.0.0beta1, < 3)
|
||||
spreadsheet (> 0.9.0)
|
||||
rubyzip (>= 1.3.0, < 3.0.0)
|
||||
rspec (3.9.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
@@ -614,17 +599,17 @@ 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)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
ruby-ole (1.2.12.1)
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-rc4 (0.1.5)
|
||||
rubyzip (1.3.0)
|
||||
@@ -653,8 +638,6 @@ GEM
|
||||
tilt (>= 1.3, < 3)
|
||||
spinjs-rails (1.4)
|
||||
rails (>= 3.1)
|
||||
spreadsheet (1.1.7)
|
||||
ruby-ole (>= 1.0)
|
||||
spring (1.7.2)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
@@ -665,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)
|
||||
@@ -683,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)
|
||||
@@ -760,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)
|
||||
@@ -773,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!
|
||||
@@ -790,20 +770,19 @@ DEPENDENCIES
|
||||
rails_safe_tasks (~> 1.0)
|
||||
redcarpet
|
||||
roadie-rails (~> 1.3.0)
|
||||
roo (~> 2.8.2)
|
||||
roo-xls (~> 1.1.0)
|
||||
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!
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
//= require jquery.ui.all
|
||||
//= require jquery-ui-timepicker-addon
|
||||
//= require jquery.powertip
|
||||
//= require jquery.cookie
|
||||
//= require jquery.jstree/jquery.jstree
|
||||
//= require jquery.vAlign
|
||||
//= require jquery.horizontalNav
|
||||
//= require jquery.adaptivemenu
|
||||
//= require angular
|
||||
//= require angular-resource
|
||||
//= require angular-animate
|
||||
@@ -20,13 +25,18 @@
|
||||
//= require ../shared/ng-infinite-scroll.min.js
|
||||
//= require ../shared/ng-tags-input.min.js
|
||||
//= require angular-rails-templates
|
||||
//= require lodash.underscore.js
|
||||
|
||||
// spree
|
||||
//= require admin/spree_backend
|
||||
//= require spree
|
||||
//= require admin/spree/spree-select2
|
||||
//= require modernizr
|
||||
//= require spin
|
||||
//= require equalize
|
||||
//= require css_browser_selector_dev
|
||||
//= require responsive-tables
|
||||
//= require admin/spree_paypal_express
|
||||
//= require admin/handlebar_extensions
|
||||
|
||||
// OFN specific
|
||||
//= require_tree ../templates/admin
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,7 +8,7 @@ angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
|
||||
|
||||
index: (params={}, callback=null) ->
|
||||
ExchangeProductResource.index params, (data) =>
|
||||
(callback || angular.noop)(data.products, data.pagination.pages, data.pagination.results)
|
||||
(callback || angular.noop)(data.products, data.pagination?.pages, data.pagination?.results)
|
||||
|
||||
countVariants: (params={}, callback=null) ->
|
||||
ExchangeProductResource.variant_count params, (data) =>
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
252
app/assets/javascripts/admin/spree/base.js.erb
Normal file
252
app/assets/javascripts/admin/spree/base.js.erb
Normal file
@@ -0,0 +1,252 @@
|
||||
//= require_self
|
||||
//= require admin/handlebar_extensions
|
||||
//= require admin/spree/orders/variant_autocomplete
|
||||
|
||||
/**
|
||||
This is a collection of javascript functions and whatnot
|
||||
under the spree namespace that do stuff we find helpful.
|
||||
Hopefully, this will evolve into a propper class.
|
||||
**/
|
||||
|
||||
jQuery(function($) {
|
||||
// Make main menu use full width
|
||||
mainMenu = $('.fullwidth-menu')
|
||||
if (typeof mainMenu.horizontalNav === 'function' )
|
||||
mainMenu.horizontalNav({
|
||||
tableDisplay: false,
|
||||
responsiveDelay: 0
|
||||
});
|
||||
|
||||
// Vertical align of checkbox fields
|
||||
if (typeof $('.field.checkbox label').vAlign === 'function' )
|
||||
$('.field.checkbox label').vAlign()
|
||||
|
||||
// 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' ) {
|
||||
$('.with-tip').powerTip({
|
||||
smartPlacement: true,
|
||||
fadeInTime: 50,
|
||||
fadeOutTime: 50,
|
||||
intentPollInterval: 300
|
||||
});
|
||||
|
||||
$('.with-tip').on({
|
||||
powerTipPreRender: function(){
|
||||
$('#powerTip').addClass($(this).attr("data-action"));
|
||||
$('#powerTip').addClass($(this).attr("data-tip-color"));
|
||||
},
|
||||
powerTipClose: function(){
|
||||
$('#powerTip').removeClass($(this).attr("data-action"))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Make flash messages dissapear
|
||||
setTimeout('$(".flash").fadeOut()', 5000);
|
||||
|
||||
// Highlight hovered table column
|
||||
$('table tbody tr td.actions a').hover(function(){
|
||||
var tr = $(this).closest('tr');
|
||||
var klass = 'highlight action-' + $(this).attr('data-action')
|
||||
tr.addClass(klass)
|
||||
tr.prev().addClass('before-' + klass);
|
||||
}, function(){
|
||||
var tr = $(this).closest('tr');
|
||||
var klass = 'highlight action-' + $(this).attr('data-action')
|
||||
tr.removeClass(klass)
|
||||
tr.prev().removeClass('before-' + klass);
|
||||
});
|
||||
|
||||
// Trunkate text in page_title that didn't fit
|
||||
var truncate_elements = $('.truncate');
|
||||
|
||||
truncate_elements.each(function(){
|
||||
$(this).trunk8();
|
||||
});
|
||||
$(window).resize(function (event) {
|
||||
truncate_elements.each(function(){
|
||||
$(this).trunk8();
|
||||
})
|
||||
});
|
||||
|
||||
// Make height of dt/dd elements the same
|
||||
if (typeof $("dl").equalize === 'function' )
|
||||
$("dl").equalize('outerHeight');
|
||||
});
|
||||
|
||||
|
||||
$.fn.visible = function(cond) { this[cond ? 'show' : 'hide' ]() };
|
||||
|
||||
// Overriding a broken function in Spree. Bug report at
|
||||
// https://github.com/spree/spree/issues/4032
|
||||
show_flash_error = function(message) {
|
||||
error_div = $('.flash.error');
|
||||
if (error_div.length > 0) {
|
||||
error_div.html(message);
|
||||
error_div.show();
|
||||
} else {
|
||||
if ($("#content .toolbar").length > 0) {
|
||||
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
|
||||
} else {
|
||||
$("#progress").before('<div class="flash error">' + message + '</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply to individual radio button that makes another element visible when checked
|
||||
$.fn.radioControlsVisibilityOfElement = function(dependentElementSelector){
|
||||
if(!this.get(0)){ return }
|
||||
showValue = this.get(0).value;
|
||||
radioGroup = $("input[name='" + this.get(0).name + "']");
|
||||
radioGroup.each(function(){
|
||||
$(this).click(function(){
|
||||
$(dependentElementSelector).visible(this.checked && this.value == showValue)
|
||||
});
|
||||
if(this.checked){ this.click() }
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
if (typeof Spree !== 'undefined') {
|
||||
handle_date_picker_fields = function(){
|
||||
$('.datepicker').datepicker({
|
||||
dateFormat: Spree.translations.date_picker,
|
||||
dayNames: Spree.translations.abbr_day_names,
|
||||
dayNamesMin: Spree.translations.abbr_day_names,
|
||||
monthNames: Spree.translations.month_names,
|
||||
prevText: Spree.translations.previous,
|
||||
nextText: Spree.translations.next,
|
||||
showOn: "focus"
|
||||
});
|
||||
|
||||
// Correctly display range dates
|
||||
$('.date-range-filter .datepicker-from').datepicker('option', 'onSelect', function(selectedDate) {
|
||||
$(".date-range-filter .datepicker-to" ).datepicker( "option", "minDate", selectedDate );
|
||||
});
|
||||
$('.date-range-filter .datepicker-to').datepicker('option', 'onSelect', function(selectedDate) {
|
||||
$(".date-range-filter .datepicker-from" ).datepicker( "option", "maxDate", selectedDate );
|
||||
});
|
||||
}
|
||||
|
||||
handle_date_picker_fields();
|
||||
}
|
||||
|
||||
$(".observe_field").on('change', function() {
|
||||
target = $(this).attr("data-update");
|
||||
ajax_indicator = $(this).attr("data-ajax-indicator") || '#busy_indicator';
|
||||
$(target).hide();
|
||||
$(ajax_indicator).show();
|
||||
$.ajax({ dataType: 'html',
|
||||
url: $(this).attr("data-base-url")+encodeURIComponent($(this).val()),
|
||||
type: 'get',
|
||||
success: function(data){
|
||||
$(target).html(data);
|
||||
$(ajax_indicator).hide();
|
||||
$(target).show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.spree_add_fields').click(function() {
|
||||
var target = $(this).data("target");
|
||||
var new_table_row = $(target + ' tr:visible:last').clone();
|
||||
var new_id = new Date().getTime();
|
||||
new_table_row.find("input, select").each(function () {
|
||||
var el = $(this);
|
||||
el.val("");
|
||||
if (typeof el.attr("id") !== 'undefined') el.attr("id", el.attr("id").replace(/\d+/, new_id))
|
||||
if (typeof el.attr("name") !== 'undefined') el.attr("name", el.attr("name").replace(/\d+/, new_id))
|
||||
})
|
||||
// When cloning a new row, set the href of all icons to be an empty "#"
|
||||
// This is so that clicking on them does not perform the actions for the
|
||||
// duplicated row
|
||||
new_table_row.find("a").each(function () {
|
||||
var el = $(this);
|
||||
el.attr('href', '#');
|
||||
})
|
||||
$(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() {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
return ui;
|
||||
};
|
||||
|
||||
$('table.sortable').ready(function(){
|
||||
var td_count = $(this).find('tbody tr:first-child td').length
|
||||
|
||||
if (typeof $('table.sortable tbody').sortable !== 'function' )
|
||||
return
|
||||
|
||||
$('table.sortable tbody').sortable(
|
||||
{
|
||||
handle: '.handle',
|
||||
helper: fixHelper,
|
||||
placeholder: 'ui-sortable-placeholder',
|
||||
update: function(event, ui) {
|
||||
$("#progress").show();
|
||||
positions = {};
|
||||
$.each($('table.sortable tbody tr'), function(position, obj){
|
||||
reg = /spree_(\w+_?)+_(\d+)/;
|
||||
parts = reg.exec($(obj).attr('id'));
|
||||
if (parts) {
|
||||
positions['positions['+parts[2]+']'] = position;
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'script',
|
||||
url: $(ui.item).closest("table.sortable").data("sortable-link"),
|
||||
data: positions,
|
||||
success: function(data){ $("#progress").hide(); }
|
||||
});
|
||||
},
|
||||
start: function (event, ui) {
|
||||
// Set correct height for placehoder (from dragged tr)
|
||||
ui.placeholder.height(ui.item.height())
|
||||
// Fix placeholder content to make it correct width
|
||||
ui.placeholder.html("<td colspan='"+(td_count-1)+"'></td><td class='actions'></td>")
|
||||
},
|
||||
stop: function (event, ui) {
|
||||
// Fix odd/even classes after reorder
|
||||
$("table.sortable tr:even").removeClass("odd even").addClass("even");
|
||||
$("table.sortable tr:odd").removeClass("odd even").addClass("odd");
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
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;
|
||||
});
|
||||
};
|
||||
27
app/assets/javascripts/admin/spree/orders/address_states.js
Normal file
27
app/assets/javascripts/admin/spree/orders/address_states.js
Normal file
@@ -0,0 +1,27 @@
|
||||
var update_state = function(region) {
|
||||
var country = $('span#' + region + 'country .select2').select2('val');
|
||||
|
||||
var state_select = $('span#' + region + 'state select.select2');
|
||||
var state_input = $('span#' + region + 'state input.state_name');
|
||||
|
||||
$.get(Spree.routes.states_search + "?country_id=" + country, function(data) {
|
||||
var states = data["states"]
|
||||
if (states.length > 0) {
|
||||
state_select.html('');
|
||||
var states_with_blank = [{name: '', id: ''}].concat(states);
|
||||
$.each(states_with_blank, function(pos,state) {
|
||||
var opt = $(document.createElement('option'))
|
||||
.attr('value', state.id)
|
||||
.html(state.name);
|
||||
state_select.append(opt);
|
||||
});
|
||||
state_select.prop("disabled", false).show();
|
||||
state_select.select2();
|
||||
state_input.hide().prop("disabled", true);
|
||||
|
||||
} else {
|
||||
state_input.prop("disabled", false).show();
|
||||
state_select.select2('destroy').hide();
|
||||
}
|
||||
})
|
||||
};
|
||||
64
app/assets/javascripts/admin/spree/orders/shipments.js.erb
Normal file
64
app/assets/javascripts/admin/spree/orders/shipments.js.erb
Normal file
@@ -0,0 +1,64 @@
|
||||
// Shipments AJAX API
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
handle_ship_click = function(){
|
||||
var link = $(this);
|
||||
var shipment_number = link.data('shipment-number');
|
||||
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + "/ship.json");
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: url
|
||||
}).done(function( msg ) {
|
||||
window.location.reload();
|
||||
}).error(function( msg ) {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
$('[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);
|
||||
|
||||
handle_shipping_method_save = function(){
|
||||
var link = $(this);
|
||||
var shipment_number = link.data('shipment-number');
|
||||
var selected_shipping_rate_id = link.parents('tbody').find("select#selected_shipping_rate_id[data-shipment-number='" + shipment_number + "']").val();
|
||||
var unlock = link.parents('tbody').find("input[name='open_adjustment'][data-shipment-number='" + shipment_number + "']:checked").val();
|
||||
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: url,
|
||||
data: { shipment: { selected_shipping_rate_id: selected_shipping_rate_id, unlock: unlock } }
|
||||
}).done(function( msg ) {
|
||||
window.location.reload();
|
||||
}).error(function( msg ) {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
$('[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);
|
||||
|
||||
handle_tracking_save = function(){
|
||||
var link = $(this);
|
||||
var shipment_number = link.data('shipment-number');
|
||||
var tracking = link.parents('tbody').find('input#tracking').val();
|
||||
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: url,
|
||||
data: { shipment: { tracking: tracking } }
|
||||
}).done(function( msg ) {
|
||||
window.location.reload();
|
||||
}).error(function( msg ) {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
$('[data-hook=admin_order_edit_form] a.save-tracking').click(handle_tracking_save);
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
// variant autocompletion
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if ($('#variant_autocomplete_template').length > 0) {
|
||||
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
|
||||
window.variantStockTemplate = Handlebars.compile($('#variant_autocomplete_stock_template').text());
|
||||
|
||||
// handle variant selection, show stock level.
|
||||
$('#add_variant_id').change(function(){
|
||||
var variant_id = $(this).val();
|
||||
|
||||
var variant = _.find(window.variants, function(variant){
|
||||
return variant.id == variant_id
|
||||
})
|
||||
$('#stock_details').html(variantStockTemplate({variant: variant}));
|
||||
$('#stock_details').show();
|
||||
|
||||
$('button.add_variant').click(addVariantFromStockLocation);
|
||||
|
||||
// Add some tips
|
||||
$('.with-tip').powerTip({
|
||||
smartPlacement: true,
|
||||
fadeInTime: 50,
|
||||
fadeOutTime: 50,
|
||||
intentPollInterval: 300
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
//handle edit click
|
||||
$('a.edit-item').click(toggleItemEdit);
|
||||
|
||||
//handle cancel click
|
||||
$('a.cancel-item').click(toggleItemEdit);
|
||||
|
||||
handle_save_click = function(){
|
||||
var save = $(this);
|
||||
var shipment_number = save.data('shipment-number');
|
||||
var variant_id = save.data('variant-id');
|
||||
|
||||
var quantity = parseInt(save.parents('tr').find('input.line_item_quantity').val());
|
||||
|
||||
toggleItemEdit();
|
||||
|
||||
adjustItems(shipment_number, variant_id, quantity);
|
||||
return false;
|
||||
}
|
||||
$('a.save-item').click(handle_save_click);
|
||||
|
||||
handle_delete_click = function(){
|
||||
var del = $(this);
|
||||
var shipment_number = del.data('shipment-number');
|
||||
var variant_id = del.data('variant-id');
|
||||
|
||||
toggleItemEdit();
|
||||
|
||||
adjustItems(shipment_number, variant_id, 0);
|
||||
}
|
||||
$('a.delete-item').click(handle_delete_click);
|
||||
}
|
||||
});
|
||||
|
||||
adjustItems = function(shipment_number, variant_id, quantity){
|
||||
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
|
||||
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
|
||||
|
||||
var url = Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number;
|
||||
|
||||
var new_quantity = 0;
|
||||
if(inventory_units.length<quantity){
|
||||
url += "/add"
|
||||
new_quantity = (quantity - inventory_units.length);
|
||||
}else if(inventory_units.length>quantity){
|
||||
url += "/remove"
|
||||
new_quantity = (inventory_units.length - quantity);
|
||||
}
|
||||
url += '.json';
|
||||
|
||||
if(new_quantity!=0){
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: Spree.url(url),
|
||||
data: { variant_id: variant_id, quantity: new_quantity }
|
||||
}).done(function( msg ) {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
toggleTrackingEdit = function(){
|
||||
var link = $(this);
|
||||
link.parents('tbody').find('tr.edit-tracking').toggle();
|
||||
link.parents('tbody').find('tr.show-tracking').toggle();
|
||||
}
|
||||
|
||||
toggleMethodEdit = function(){
|
||||
var link = $(this);
|
||||
link.parents('tbody').find('tr.edit-method').toggle();
|
||||
link.parents('tbody').find('tr.show-method').toggle();
|
||||
}
|
||||
|
||||
toggleItemEdit = function(){
|
||||
var link = $(this);
|
||||
link.parent().find('a.edit-item').toggle();
|
||||
link.parent().find('a.cancel-item').toggle();
|
||||
link.parent().find('a.save-item').toggle();
|
||||
link.parent().find('a.delete-item').toggle();
|
||||
link.parents('tr').find('td.item-qty-show').toggle();
|
||||
link.parents('tr').find('td.item-qty-edit').toggle();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
addVariantFromStockLocation = function() {
|
||||
$('#stock_details').hide();
|
||||
|
||||
var variant_id = $('input.variant_autocomplete').val();
|
||||
var stock_location_id = $(this).data('stock-location-id');
|
||||
var quantity = $("input.quantity[data-stock-location-id='" + stock_location_id + "']").val();
|
||||
|
||||
var shipment = _.find(shipments, function(shipment){
|
||||
return shipment.stock_location_id == stock_location_id && (shipment.state == 'ready' || shipment.state == 'pending');
|
||||
});
|
||||
|
||||
if(shipment==undefined){
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: Spree.url(Spree.routes.orders_api + "/" + order_number + "/shipments.json"),
|
||||
data: { variant_id: variant_id, quantity: quantity, stock_location_id: stock_location_id }
|
||||
}).done(function( msg ) {
|
||||
window.location.reload();
|
||||
}).error(function( msg ) {
|
||||
console.log(msg);
|
||||
});
|
||||
}else{
|
||||
//add to existing shipment
|
||||
adjustItems(shipment.number, variant_id, quantity);
|
||||
}
|
||||
return 1
|
||||
}
|
||||
27
app/assets/javascripts/admin/spree/progress.coffee
Normal file
27
app/assets/javascripts/admin/spree/progress.coffee
Normal file
@@ -0,0 +1,27 @@
|
||||
$(document).ready ->
|
||||
opts =
|
||||
lines: 11
|
||||
length: 2
|
||||
width: 3
|
||||
radius: 9
|
||||
corners: 1
|
||||
rotate: 0
|
||||
color: '#fff'
|
||||
speed: 0.8
|
||||
trail: 48
|
||||
shadow: false
|
||||
hwaccel: true
|
||||
className: 'spinner'
|
||||
zIndex: 2e9
|
||||
top: 'auto'
|
||||
left: 'auto'
|
||||
|
||||
target = document.getElementById("spinner")
|
||||
|
||||
$(document).ajaxStart ->
|
||||
$("#progress").fadeIn()
|
||||
spinner = new Spinner(opts).spin(target)
|
||||
|
||||
$(document).ajaxStop ->
|
||||
$("#progress").fadeOut()
|
||||
|
||||
8
app/assets/javascripts/admin/spree/spree-select2.js.erb
Normal file
8
app/assets/javascripts/admin/spree/spree-select2.js.erb
Normal file
@@ -0,0 +1,8 @@
|
||||
//= require select2
|
||||
jQuery(function($) {
|
||||
// Make select beautiful
|
||||
if (typeof $('select.select2').select2 === 'function' )
|
||||
$('select.select2').select2({
|
||||
allowClear: true
|
||||
});
|
||||
})
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
root = exports ? this
|
||||
|
||||
root.taxon_tree_menu = (obj, context) ->
|
||||
|
||||
base_url = Spree.url(Spree.routes.taxonomy_taxons_path)
|
||||
admin_base_url = Spree.url(Spree.routes.admin_taxonomy_taxons_path)
|
||||
edit_url = admin_base_url.clone()
|
||||
edit_url.setPath(edit_url.path() + '/' + obj.attr("id") + "/edit");
|
||||
|
||||
create:
|
||||
label: "<i class='icon-plus'></i> " + Spree.translations.add,
|
||||
action: (obj) -> context.create(obj)
|
||||
rename:
|
||||
label: "<i class='icon-pencil'></i> " + Spree.translations.rename,
|
||||
action: (obj) -> context.rename(obj)
|
||||
remove:
|
||||
label: "<i class='icon-trash'></i> " + Spree.translations.remove,
|
||||
action: (obj) -> context.remove(obj)
|
||||
edit:
|
||||
separator_before: true,
|
||||
label: "<i class='icon-edit'></i> " + Spree.translations.edit,
|
||||
action: (obj) -> window.location = edit_url.toString()
|
||||
127
app/assets/javascripts/admin/spree/taxons/taxonomy.js.coffee
Normal file
127
app/assets/javascripts/admin/spree/taxons/taxonomy.js.coffee
Normal file
@@ -0,0 +1,127 @@
|
||||
handle_ajax_error = (XMLHttpRequest, textStatus, errorThrown) ->
|
||||
$.jstree.rollback(last_rollback)
|
||||
$("#ajax_error").show().html("<strong>" + server_error + "</strong><br />" + taxonomy_tree_error)
|
||||
|
||||
handle_move = (e, data) ->
|
||||
last_rollback = data.rlbk
|
||||
position = data.rslt.cp
|
||||
node = data.rslt.o
|
||||
new_parent = data.rslt.np
|
||||
|
||||
url = Spree.url(base_url).clone()
|
||||
url.setPath url.path() + '/' + node.attr("id")
|
||||
$.ajax
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: url.toString(),
|
||||
data: ({_method: "put", "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
|
||||
error: handle_ajax_error
|
||||
|
||||
true
|
||||
|
||||
handle_create = (e, data) ->
|
||||
last_rollback = data.rlbk
|
||||
node = data.rslt.obj
|
||||
name = data.rslt.name
|
||||
position = data.rslt.position
|
||||
new_parent = data.rslt.parent
|
||||
|
||||
$.ajax
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: base_url.toString(),
|
||||
data: ({"taxon[name]": name, "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
|
||||
error: handle_ajax_error,
|
||||
success: (data,result) ->
|
||||
node.attr('id', data.id)
|
||||
|
||||
handle_rename = (e, data) ->
|
||||
last_rollback = data.rlbk
|
||||
node = data.rslt.obj
|
||||
name = data.rslt.new_name
|
||||
|
||||
url = Spree.url(base_url).clone()
|
||||
url.setPath(url.path() + '/' + node.attr("id"))
|
||||
|
||||
$.ajax
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: url.toString(),
|
||||
data: {_method: "put", "taxon[name]": name },
|
||||
error: handle_ajax_error
|
||||
|
||||
handle_delete = (e, data) ->
|
||||
last_rollback = data.rlbk
|
||||
node = data.rslt.obj
|
||||
delete_url = base_url.clone()
|
||||
delete_url.setPath delete_url.path() + '/' + node.attr("id")
|
||||
if confirm(Spree.translations.are_you_sure_delete)
|
||||
$.ajax
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: delete_url.toString(),
|
||||
data: {_method: "delete"},
|
||||
error: handle_ajax_error
|
||||
else
|
||||
$.jstree.rollback(last_rollback)
|
||||
last_rollback = null
|
||||
|
||||
root = exports ? this
|
||||
root.setup_taxonomy_tree = (taxonomy_id) ->
|
||||
if taxonomy_id != undefined
|
||||
# this is defined within admin/taxonomies/edit
|
||||
root.base_url = Spree.url(Spree.routes.taxonomy_taxons_path)
|
||||
|
||||
$.ajax
|
||||
url: base_url.path().replace("/taxons", "/jstree"),
|
||||
success: (taxonomy) ->
|
||||
last_rollback = null
|
||||
|
||||
conf =
|
||||
json_data:
|
||||
data: taxonomy,
|
||||
ajax:
|
||||
url: (e) ->
|
||||
base_url.path() + '/' + e.attr('id') + '/jstree'
|
||||
themes:
|
||||
theme: "apple",
|
||||
url: "/assets/jquery.jstree/themes/apple/style.css"
|
||||
strings:
|
||||
new_node: new_taxon,
|
||||
loading: Spree.translations.loading + "..."
|
||||
crrm:
|
||||
move:
|
||||
check_move: (m) ->
|
||||
position = m.cp
|
||||
node = m.o
|
||||
new_parent = m.np
|
||||
|
||||
# no parent or cant drag and drop
|
||||
if !new_parent || node.attr("rel") == "root"
|
||||
return false
|
||||
|
||||
# can't drop before root
|
||||
if new_parent.attr("id") == "taxonomy_tree" && position == 0
|
||||
return false
|
||||
|
||||
true
|
||||
contextmenu:
|
||||
items: (obj) ->
|
||||
taxon_tree_menu(obj, this)
|
||||
plugins: ["themes", "json_data", "dnd", "crrm", "contextmenu"]
|
||||
|
||||
$("#taxonomy_tree").jstree(conf)
|
||||
.bind("move_node.jstree", handle_move)
|
||||
.bind("remove.jstree", handle_delete)
|
||||
.bind("create.jstree", handle_create)
|
||||
.bind("rename.jstree", handle_rename)
|
||||
.bind "loaded.jstree", ->
|
||||
$(this).jstree("core").toggle_node($('.jstree-icon').first())
|
||||
|
||||
$("#taxonomy_tree a").on "dblclick", (e) ->
|
||||
$("#taxonomy_tree").jstree("rename", this)
|
||||
|
||||
# surpress form submit on enter/return
|
||||
$(document).keypress (e) ->
|
||||
if e.keyCode == 13
|
||||
e.preventDefault()
|
||||
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 = ->
|
||||
|
||||
@@ -16,22 +16,6 @@ $(document).ready(function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Overriding a broken function in Spree. Bug report at
|
||||
// https://github.com/spree/spree/issues/4032
|
||||
show_flash_error = function(message) {
|
||||
error_div = $('.flash.error');
|
||||
if (error_div.length > 0) {
|
||||
error_div.html(message);
|
||||
error_div.show();
|
||||
} else {
|
||||
if ($("#content .toolbar").length > 0) {
|
||||
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
|
||||
} else {
|
||||
$("#progress").before('<div class="flash error">' + message + '</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$('a.close').click(function(event){
|
||||
event.preventDefault();
|
||||
|
||||
@@ -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,25 +4,45 @@
|
||||
* 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 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
|
||||
*/
|
||||
|
||||
//************************************************************************//
|
||||
//************************************************************************//
|
||||
@import 'globals/functions';
|
||||
@import 'globals/variables';
|
||||
@import 'variables';
|
||||
@import 'globals/mixins';
|
||||
|
||||
@import 'shared/typography';
|
||||
@import 'shared/tables';
|
||||
@import 'shared/icons';
|
||||
@import 'shared/forms';
|
||||
@import 'shared/layout';
|
||||
|
||||
@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';
|
||||
@import 'hacks/ie';
|
||||
|
||||
@import 'variables';
|
||||
@import 'components/*';
|
||||
@import 'plugins/font-awesome';
|
||||
@import 'pages/*';
|
||||
|
||||
@import '*';
|
||||
|
||||
33
app/assets/stylesheets/admin/components/actions.scss
Normal file
33
app/assets/stylesheets/admin/components/actions.scss
Normal file
@@ -0,0 +1,33 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
table tbody tr {
|
||||
&.highlight {
|
||||
|
||||
@each $action in $actions {
|
||||
&.action-#{$action} td {
|
||||
background-color: get-value($actions, $actions-bg-colors, $action);
|
||||
border-color: get-value($actions, $actions-brd-colors, $action);
|
||||
}
|
||||
}
|
||||
|
||||
&.action-remove td, &.action-void td {
|
||||
text-decoration: line-through;
|
||||
|
||||
&.actions {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.before-highlight {
|
||||
@each $action in $actions {
|
||||
&.action-#{$action} td {
|
||||
border-bottom-color: get-value($actions, $actions-brd-colors, $action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td.actions {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#admin-menu {
|
||||
li {
|
||||
a {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a::before {
|
||||
font-weight: normal;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
app/assets/stylesheets/admin/components/date-picker.scss
Normal file
158
app/assets/stylesheets/admin/components/date-picker.scss
Normal file
@@ -0,0 +1,158 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/globals/mixins';
|
||||
@import 'admin/plugins/font-awesome';
|
||||
|
||||
.date-range-filter {
|
||||
.range-divider {
|
||||
padding: 0;
|
||||
}
|
||||
input.datepicker {
|
||||
width: 96px !important;
|
||||
}
|
||||
}
|
||||
|
||||
#ui-datepicker-div {
|
||||
@include border-radius($border-radius);
|
||||
border-color: $color-3;
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid $color-3;
|
||||
top: 0px;
|
||||
margin-top: -10px;
|
||||
left: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ui-datepicker-header {
|
||||
padding: 0;
|
||||
background-image: none;
|
||||
background-color: $color-3;
|
||||
border: none;
|
||||
border-bottom: none;
|
||||
border-radius: 0;
|
||||
height: 32px;
|
||||
|
||||
.ui-datepicker-prev, .ui-datepicker-next {
|
||||
border-radius: 0;
|
||||
top: 0;
|
||||
height: 32px;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
background-image: none;
|
||||
background-color: $color-3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
background-image: none;
|
||||
text-indent: 0;
|
||||
color: $color-1;
|
||||
width: 10px;
|
||||
margin-left: -5px;
|
||||
@extend [class^="icon-"]:before;
|
||||
|
||||
&:hover {
|
||||
color: very-light($color-2, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
.ui-datepicker-prev {
|
||||
left: 0;
|
||||
|
||||
.ui-icon {
|
||||
@extend .icon-arrow-left;
|
||||
}
|
||||
}
|
||||
.ui-datepicker-next {
|
||||
right: 0;
|
||||
|
||||
.ui-icon {
|
||||
@extend .icon-arrow-right;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.ui-icon {
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-title {
|
||||
color: $color-1;
|
||||
text-transform: uppercase;
|
||||
font-size: 85% !important;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
table.ui-datepicker-calendar {
|
||||
border: none;
|
||||
|
||||
thead {
|
||||
th {
|
||||
border-bottom: 1px solid $color-border;
|
||||
border-right: 1px solid $color-border;
|
||||
color: $color-body-text;
|
||||
width: 33px;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
tr:hover {
|
||||
td {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
&:last-child tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
td {
|
||||
a {
|
||||
border: 1px solid transparent;
|
||||
background-color: $color-1;
|
||||
background-image: none;
|
||||
font-size: 85%;
|
||||
color: $color-body-text;
|
||||
|
||||
&.ui-state-active {
|
||||
background-color: $color-2;
|
||||
color: $color-1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $color-2;
|
||||
color: $color-1;
|
||||
border-color: darken($color-2, 5);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
.ui-state-default {
|
||||
border: none;
|
||||
background-image: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-datepicker-today {
|
||||
a {
|
||||
background-color: $color-6;
|
||||
color: $color-1;
|
||||
border: 1px solid darken($color-6, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
app/assets/stylesheets/admin/components/messages.scss
Normal file
45
app/assets/stylesheets/admin/components/messages.scss
Normal file
@@ -0,0 +1,45 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
.errorExplanation {
|
||||
padding: 5px;
|
||||
border: 1px solid very-light($color-error, 12);
|
||||
background-color: very-light($color-error, 6);
|
||||
border-radius: 3px;
|
||||
color: very-light($color-error, 30);
|
||||
margin-bottom: 15px;
|
||||
|
||||
h2 {
|
||||
font-size: 140%;
|
||||
color: very-light($color-error, 30);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
|
||||
li {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flash {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
z-index: 1000;
|
||||
font-size: 120%;
|
||||
color: $color-1;
|
||||
font-weight: 600;
|
||||
|
||||
&.notice { background-color: rgba($color-notice, 0.8) }
|
||||
&.success { background-color: rgba($color-success, 0.8) }
|
||||
&.error { background-color: rgba($color-error, 0.8) }
|
||||
}
|
||||
162
app/assets/stylesheets/admin/components/navigation.scss
Normal file
162
app/assets/stylesheets/admin/components/navigation.scss
Normal file
@@ -0,0 +1,162 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
// Navigation
|
||||
//---------------------------------------------------
|
||||
.inline-menu {
|
||||
margin: 0;
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-padding-start: 0;
|
||||
}
|
||||
|
||||
nav.menu {
|
||||
ul {
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
a {
|
||||
padding: 10px 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
border: 1px solid transparent;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
&.active a {
|
||||
color: $color-2;
|
||||
border-left-width: 0;
|
||||
border-bottom-color: $color-2;
|
||||
}
|
||||
|
||||
&:hover a {
|
||||
color: $color-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-hook="admin_login_navigation_bar"] {
|
||||
ul {
|
||||
text-align: right;
|
||||
|
||||
li {
|
||||
padding: 5px 0 5px 10px;
|
||||
text-align: right;
|
||||
font-size: 90%;
|
||||
color: $color-link;
|
||||
margin-top: 8px;
|
||||
|
||||
&[data-hook="user-logged-in-as"] {
|
||||
width: 50%;
|
||||
color: $color-body-text;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
i {
|
||||
color: $color-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#admin-menu {
|
||||
background-color: $color-3;
|
||||
|
||||
li {
|
||||
min-width: 90px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 25px 20px;
|
||||
color: $color-1 !important;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $color-2;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 5px solid $color-2;
|
||||
bottom: 0px;
|
||||
margin-bottom: -5px;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
span.text {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
a::before {
|
||||
font-weight: normal;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
width: 300px;
|
||||
background-color: $color-3;
|
||||
width: 200px;
|
||||
z-index: 100000;
|
||||
|
||||
> li {
|
||||
width: 200px !important;
|
||||
|
||||
a:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.selected a {
|
||||
@extend a:hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#sub-menu {
|
||||
background-color: $color-2;
|
||||
padding-bottom: 0;
|
||||
|
||||
li {
|
||||
a {
|
||||
display: block;
|
||||
padding: 12px 20px;
|
||||
color: $color-1;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
&.selected a, a:hover {
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 5px solid $color-2;
|
||||
bottom: 0px;
|
||||
margin-bottom: -5px;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,23 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import "admin/variables";
|
||||
|
||||
.pagination {
|
||||
text-align: center;
|
||||
margin: 2em 0 1em;
|
||||
padding: 10px 0;
|
||||
|
||||
.page {
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
|
||||
&.current {
|
||||
background-color: $color-2;
|
||||
border-radius: 3px;
|
||||
color: $color-1;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0 0.35em;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
.select2-result-label {
|
||||
.variant-autocomplete-item {
|
||||
.variant-details {
|
||||
padding: 0 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.variant-image {
|
||||
margin-top: 5px;
|
||||
background-color: white;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
ul.variant-details {
|
||||
li {
|
||||
display: inline-block;
|
||||
|
||||
&:after {
|
||||
content: ' / ';
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/assets/stylesheets/admin/components/progress.scss
Normal file
38
app/assets/stylesheets/admin/components/progress.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/globals/mixins';
|
||||
|
||||
#progress {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
opacity: 0.8;
|
||||
width: 100%;
|
||||
|
||||
.wrapper {
|
||||
@include border-radius(10px);
|
||||
top: -10px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
padding: 11px 0;
|
||||
background-color: $color-3;
|
||||
color: $color-1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#spinner {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.progress-message {
|
||||
font-size: 120%;
|
||||
font-weight: $font-weight-bold;
|
||||
margin-top: 20px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
28
app/assets/stylesheets/admin/components/sidebar.scss
Normal file
28
app/assets/stylesheets/admin/components/sidebar.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
// Sidebar
|
||||
//---------------------------------------------------
|
||||
#sidebar {
|
||||
overflow: visible;
|
||||
border-top: 1px solid $color-border;
|
||||
margin-top: 17px;
|
||||
|
||||
.sidebar-title {
|
||||
color: $color-2;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
> span {
|
||||
display: inline;
|
||||
background: #fff;
|
||||
padding: 5px 10px;
|
||||
position: relative;
|
||||
top: -14px;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
app/assets/stylesheets/admin/components/states.scss
Normal file
36
app/assets/stylesheets/admin/components/states.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
.state {
|
||||
text-transform: uppercase;
|
||||
font-size: 80%;
|
||||
font-weight: 600;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
border-radius: $body-font-size/2;
|
||||
width: $body-font-size - 4px;
|
||||
height: $body-font-size - 4px;
|
||||
}
|
||||
|
||||
@each $state in $states {
|
||||
&.#{$state}:before {
|
||||
background-color: get-value($states, $states-bg-colors, $state);
|
||||
|
||||
// &, a {
|
||||
// color: get-value($states, $states-text-colors, $state);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
&[class*="state"] td:first-child {
|
||||
border-left-width: 3px;
|
||||
}
|
||||
&.state-complete td:first-child { border-left-color: $color-success }
|
||||
&.state-cart td:first-child { border-left-color: very-light($color-notice, 6) }
|
||||
&.state-canceled td:first-child { border-left-color: $color-error }
|
||||
}
|
||||
14
app/assets/stylesheets/admin/components/table-filter.scss
Normal file
14
app/assets/stylesheets/admin/components/table-filter.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
#table-filter {
|
||||
|
||||
.field {
|
||||
input[type="text"], input[type="phone"],
|
||||
input[type="email"], input[type="number"],
|
||||
input[type="url"] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
25
app/assets/stylesheets/admin/globals/functions.scss
Normal file
25
app/assets/stylesheets/admin/globals/functions.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
// Make color very close to white
|
||||
@function very-light($color, $adjust: 3){
|
||||
@if type-of($adjust) == 'number' and $adjust > 0 {
|
||||
@for $i from 0 through 100 {
|
||||
@if lighten($color, $i) == white and ($i - $adjust) > $adjust {
|
||||
@return lighten($color, $i - $adjust);
|
||||
}
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@debug "Please correct $adjust value. It should be number and larger then 0. Currently it is '#{type-of($adjust)}' with value '#{$adjust}'"
|
||||
}
|
||||
};
|
||||
|
||||
// Quick fix for dynamic variables missing in SASS
|
||||
@function get-value($prop, $val, $search) {
|
||||
$n1: index($prop, $search);
|
||||
$n2: index($val, $search);
|
||||
|
||||
@if($n1) {
|
||||
@return nth($val, $n1);
|
||||
} @else {
|
||||
@return nth($prop, $n2);
|
||||
}
|
||||
}
|
||||
135
app/assets/stylesheets/admin/plugins/jstree.scss
Normal file
135
app/assets/stylesheets/admin/plugins/jstree.scss
Normal file
@@ -0,0 +1,135 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/globals/mixins';
|
||||
@import 'admin/plugins/font-awesome';
|
||||
|
||||
#taxonomy_tree {
|
||||
> ul, .jstree-icon {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.jstree-icon {
|
||||
@extend [class^="icon-"]:before;
|
||||
}
|
||||
|
||||
.jstree-open > .jstree-icon {
|
||||
@extend .icon-caret-down;
|
||||
}
|
||||
.jstree-closed > .jstree-icon {
|
||||
@extend .icon-caret-right;
|
||||
}
|
||||
|
||||
li {
|
||||
background-image: none;
|
||||
|
||||
a {
|
||||
background-color: very-light($color-3);
|
||||
border: 1px solid $color-border;
|
||||
color: $color-body-text;
|
||||
font-weight: $font-weight-bold;
|
||||
text-shadow: none;
|
||||
width: 90%;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
padding: 5px 0 5px 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.jstree-icon {
|
||||
padding-left: 0px;
|
||||
@extend .icon-move;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#vakata-dragged.jstree-apple .jstree-invalid,
|
||||
#vakata-dragged.jstree-apple .jstree-ok,
|
||||
#jstree-marker {
|
||||
background-image: none !important;
|
||||
background-color: transparent !important;
|
||||
@extend [class^="icon-"]:before;
|
||||
}
|
||||
#vakata-dragged.jstree-apple .jstree-invalid {
|
||||
@extend .icon-remove;
|
||||
color: $color-5;
|
||||
}
|
||||
#vakata-dragged.jstree-apple .jstree-ok {
|
||||
@extend .icon-ok;
|
||||
color: $color-2;
|
||||
}
|
||||
|
||||
#jstree-marker {
|
||||
@extend .icon-caret-right;
|
||||
color: $color-body-text !important;
|
||||
width: 4px !important;
|
||||
}
|
||||
|
||||
#jstree-marker-line {
|
||||
@include border-radius($border-radius !important);
|
||||
height: 0px !important;
|
||||
margin-left: 5px !important;
|
||||
margin-top: -2px !important;
|
||||
border: none !important;
|
||||
border-bottom: 1px solid $color-body-text !important;
|
||||
background-color: very-light($color-3) !important;
|
||||
|
||||
-webkit-box-shadow: none !important;
|
||||
-moz-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
}
|
||||
|
||||
#vakata-contextmenu {
|
||||
background-color: $color-3 !important;
|
||||
-moz-box-shadow: none !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
@include border-radius($border-radius !important);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid $color-3;
|
||||
top: 0px;
|
||||
margin-top: -10px;
|
||||
left: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $color-1 !important;
|
||||
line-height: inherit !important;
|
||||
padding: 5px 10px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 90% !important;
|
||||
|
||||
&:hover {
|
||||
@include border-radius($border-radius !important);
|
||||
background-color: $color-2 !important;
|
||||
border: none !important;
|
||||
-moz-box-shadow: none !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
line-height: inherit !important;
|
||||
padding: 5px 10px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
li:first-child a:hover:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid $color-2;
|
||||
top: 0px;
|
||||
margin-top: -10px;
|
||||
left: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
li.vakata-separator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
193
app/assets/stylesheets/admin/plugins/select2.scss
Normal file
193
app/assets/stylesheets/admin/plugins/select2.scss
Normal file
@@ -0,0 +1,193 @@
|
||||
@import 'admin/globals/functions';
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/globals/mixins';
|
||||
|
||||
@import 'admin/shared/forms';
|
||||
|
||||
@import 'admin/plugins/font-awesome';
|
||||
|
||||
.select2-container {
|
||||
&:hover .select2-choice, &.select2-container-active .select2-choice {
|
||||
background-color: $color-sel-hover-bg !important;
|
||||
border-color: $color-sel-hover-bg !important;
|
||||
}
|
||||
.select2-choice {
|
||||
background-image: none !important;
|
||||
background-color: $color-sel-bg;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
@include border-radius($border-radius);
|
||||
color: $color-1 !important;
|
||||
font-size: 90%;
|
||||
height: 31px;
|
||||
line-height: inherit !important;
|
||||
padding: 5px 15px 7px;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.select2-search-choice-close {
|
||||
background-image: none !important;
|
||||
font-size: 100% !important;
|
||||
@extend .icon-remove;
|
||||
@extend [class^="icon-"]:before;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.select2-container-active {
|
||||
.select2-choice {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&.select2-dropdown-open .select2-choice div b {
|
||||
@extend .icon-caret-up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-drop {
|
||||
border-color: $color-sel-hover-bg;
|
||||
box-shadow: none !important;
|
||||
z-index: 1000000;
|
||||
|
||||
&.select2-drop-above {
|
||||
border-color: $color-sel-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-search {
|
||||
@extend .icon-search;
|
||||
|
||||
font-size: 100%;
|
||||
color: darken($color-border, 15);
|
||||
padding: 0 9px 0 0;
|
||||
|
||||
&:before {
|
||||
@extend [class^="icon-"]:before;
|
||||
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 13px;
|
||||
}
|
||||
|
||||
input {
|
||||
@extend input[type="text"];
|
||||
|
||||
padding: 6px 0 6px 25px;
|
||||
margin: 5px 0 0 5px;
|
||||
font-family: $base-font-family;
|
||||
font-size: 90%;
|
||||
box-shadow: none;
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container .select2-choice .select2-arrow {
|
||||
background-image: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
||||
b {
|
||||
padding-top: 7px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none;
|
||||
font-family: FontAwesome;
|
||||
font-weight: 200 !important;
|
||||
|
||||
&:before {
|
||||
content: "\f0d7";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-results {
|
||||
padding-left: 0 !important;
|
||||
|
||||
li {
|
||||
font-size: 85% !important;
|
||||
|
||||
&.select2-highlighted {
|
||||
.select2-result-label {
|
||||
&, h6 {
|
||||
color: $color-1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-result-label {
|
||||
color: $color-body-text;
|
||||
min-height: 22px;
|
||||
clear: both;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&.select2-no-results, &.select2-searching {
|
||||
padding: 5px;
|
||||
background-color: transparent;
|
||||
color: $color-body-text;
|
||||
text-align: center;
|
||||
font-weight: $font-weight-bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-highlighted {
|
||||
background-color: $color-sel-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container-multi {
|
||||
&.select2-container-active, &.select2-dropdown-open {
|
||||
.select2-choices {
|
||||
border-color: $color-sel-hover-bg !important;
|
||||
box-shadow: none;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
.select2-choices {
|
||||
@extend input[type="text"];
|
||||
padding: 6px 3px 3px 3px;
|
||||
box-shadow: none;
|
||||
background-image: none !important;
|
||||
|
||||
.select2-search-choice {
|
||||
@include border-radius($border-radius);
|
||||
margin: 0 0 3px 3px;
|
||||
background-image: none;
|
||||
background-color: $color-sel-bg;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
color: $color-1 !important;
|
||||
font-size: 85%;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-sel-hover-bg;
|
||||
}
|
||||
|
||||
.select2-search-choice-close {
|
||||
background-image: none !important;
|
||||
font-size: 85% !important;
|
||||
@extend .icon-remove;
|
||||
@extend [class^="icon-"]:before;
|
||||
margin-left: 2px;
|
||||
color: $color-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label .select2-container {
|
||||
margin-top: -6px;
|
||||
.select2-choice {
|
||||
span {
|
||||
text-transform: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.destroy_style, .destroy_header {
|
||||
float: right;
|
||||
}
|
||||
270
app/assets/stylesheets/admin/shared/forms.scss
Normal file
270
app/assets/stylesheets/admin/shared/forms.scss
Normal file
@@ -0,0 +1,270 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/globals/mixins';
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="date"],
|
||||
input[type="datetime"],
|
||||
input[type="time"],
|
||||
input[type="url"],
|
||||
input[type="number"],
|
||||
input[type="tel"],
|
||||
textarea, fieldset {
|
||||
@include border-radius($border-radius);
|
||||
padding: 7px 10px;
|
||||
border: 1px solid $color-txt-brd;
|
||||
color: $color-txt-text;
|
||||
font-size: 90%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $color-txt-hover-brd;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.fullwidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
display: inline;
|
||||
margin-bottom: 5px;
|
||||
color: $color-4;
|
||||
|
||||
&.inline {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
&.block {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.label-block label { display: block }
|
||||
|
||||
input[type="submit"],
|
||||
input[type="button"],
|
||||
button, .button {
|
||||
@include border-radius($border-radius);
|
||||
display: inline-block;
|
||||
padding: 8px 15px;
|
||||
border: none;
|
||||
background-color: $color-btn-bg;
|
||||
color: $color-btn-text;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600 !important;
|
||||
|
||||
&:before {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
&:visited, &:active, &:focus { color: $color-btn-text }
|
||||
|
||||
&:hover {
|
||||
background-color: $color-btn-hover-bg;
|
||||
color: $color-btn-hover-text;
|
||||
}
|
||||
|
||||
&:active:focus {
|
||||
box-shadow: 0 0 8px 0 darken($color-btn-hover-bg, 5) inset;
|
||||
}
|
||||
|
||||
&.fullwidth {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
span.info {
|
||||
font-style: italic;
|
||||
font-size: 85%;
|
||||
color: lighten($color-body-text, 15);
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
padding: 10px 0;
|
||||
|
||||
&.checkbox {
|
||||
min-height: 73px;
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
border-top: 1px solid $color-border;
|
||||
list-style: none;
|
||||
padding-top: 5px;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
&.white-space-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.withError {
|
||||
.field_with_errors {
|
||||
label {
|
||||
color: very-light($color-error, 30);
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: very-light($color-error, 15);
|
||||
}
|
||||
}
|
||||
.formError {
|
||||
color: very-light($color-error, 30);
|
||||
font-style: italic;
|
||||
font-size: 85%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
border-color: $color-border;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
position: relative;
|
||||
margin-bottom: 35px;
|
||||
padding: 10px 0 15px 0;
|
||||
background-color: transparent;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-radius: 0;
|
||||
|
||||
&.no-border-bottom {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.no-border-top {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
background-color: $color-1;
|
||||
color: $color-2;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
padding: 8px 15px;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
i {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
color: lighten($color-body-text, 8);
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-bottom: -32px;
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
|
||||
form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button, .button, input[type="submit"], input[type="button"], span.or {
|
||||
@include border-radius($border-radius);
|
||||
|
||||
-webkit-box-shadow: 0 0 0 15px $color-1;
|
||||
-moz-box-shadow: 0 0 0 15px $color-1;
|
||||
-ms-box-shadow: 0 0 0 15px $color-1;
|
||||
-o-box-shadow: 0 0 0 15px $color-1;
|
||||
box-shadow: 0 0 0 15px $color-1;
|
||||
|
||||
&:hover {
|
||||
border-color: $color-1;
|
||||
}
|
||||
}
|
||||
|
||||
span.or {
|
||||
background-color: $color-1;
|
||||
border-width: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
|
||||
-webkit-box-shadow: 0 0 0 5px $color-1;
|
||||
-moz-box-shadow: 0 0 0 5px $color-1;
|
||||
-ms-box-shadow: 0 0 0 5px $color-1;
|
||||
-o-box-shadow: 0 0 0 5px $color-1;
|
||||
box-shadow: 0 0 0 5px $color-1;
|
||||
}
|
||||
}
|
||||
|
||||
&.labels-inline {
|
||||
.field {
|
||||
margin-bottom: 0;
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
||||
label, input {
|
||||
display: table-cell !important;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.checkbox {
|
||||
input {
|
||||
width: auto !important
|
||||
}
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.form-buttons {
|
||||
text-align: center;
|
||||
}
|
||||
25
app/assets/stylesheets/admin/shared/icons.scss
Normal file
25
app/assets/stylesheets/admin/shared/icons.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
@import 'admin/plugins/font-awesome';
|
||||
|
||||
// Some fixes for fontwesome stylesheets
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
&:before {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
&.button, &.icon_link {
|
||||
width: auto;
|
||||
|
||||
&:before {
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-email:before { @extend .icon-envelope:before }
|
||||
.icon-resume:before { @extend .icon-refresh:before }
|
||||
|
||||
.icon-cancel:before,
|
||||
.icon-void:before { @extend .icon-remove:before }
|
||||
|
||||
.icon-capture:before { @extend .icon-ok:before }
|
||||
.icon-credit:before { @extend .icon-ok:before }
|
||||
92
app/assets/stylesheets/admin/shared/layout.scss
Normal file
92
app/assets/stylesheets/admin/shared/layout.scss
Normal file
@@ -0,0 +1,92 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
// Basics
|
||||
//---------------------------------------------------
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
.block-table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
||||
.table-cell {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding: 0 10px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// For block grids
|
||||
.frameless {
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
// Header
|
||||
//---------------------------------------------------
|
||||
#header {
|
||||
background-color: $color-1;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
#logo { height: 40px }
|
||||
|
||||
[data-hook="admin-title"] { font-size: 14px }
|
||||
|
||||
.page-title {
|
||||
i {
|
||||
color: $color-2;
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
//---------------------------------------------------
|
||||
#content {
|
||||
background-color: $color-1;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#content-header {
|
||||
padding: 15px 0;
|
||||
background-color: very-light($color-3, 4);
|
||||
border-bottom: 1px solid $color-border;
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.page-actions {
|
||||
text-align: right;
|
||||
form {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
//---------------------------------------------------
|
||||
#footer {
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid $color-border;
|
||||
padding: 10px 0;
|
||||
}
|
||||
208
app/assets/stylesheets/admin/shared/tables.scss
Normal file
208
app/assets/stylesheets/admin/shared/tables.scss
Normal file
@@ -0,0 +1,208 @@
|
||||
@import 'admin/globals/variables';
|
||||
@import 'admin/plugins/font-awesome';
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
border-collapse: separate;
|
||||
|
||||
th, td {
|
||||
padding: 7px 5px;
|
||||
border-right: 1px solid $color-border;
|
||||
border-bottom: 1px solid $color-border;
|
||||
vertical-align: middle;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
img {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-left: 1px solid $color-border;
|
||||
}
|
||||
|
||||
a {
|
||||
border-bottom: 1px dotted lighten($color-link, 10);
|
||||
|
||||
&:hover {
|
||||
border-color: lighten($color-link-hover, 10);
|
||||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
display: block !important;
|
||||
text-align: center;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.actions {
|
||||
background-color: transparent;
|
||||
border: none !important;
|
||||
text-align: center;
|
||||
|
||||
span.text {
|
||||
font-size: $body-font-size;
|
||||
}
|
||||
|
||||
[class*='icon-'].no-text {
|
||||
font-size: 120%;
|
||||
background-color: very-light($color-3);
|
||||
border: 1px solid $color-border;
|
||||
border-radius: 15px;
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
display: inline-block;
|
||||
padding-top: 2px;
|
||||
|
||||
&:before {
|
||||
text-align: center !important;
|
||||
width: 27px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
button[class*='icon-'] {
|
||||
color: $color-link;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.icon-envelope-alt, .icon-eye-open {
|
||||
color: $color-link;
|
||||
padding-left: 0px;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-3;
|
||||
color: $color-1;
|
||||
}
|
||||
}
|
||||
.icon-trash:hover, .icon-void:hover {
|
||||
background-color: $color-error;
|
||||
color: $color-1;
|
||||
}
|
||||
.icon-cancel:hover {
|
||||
background-color: $color-notice;
|
||||
color: $color-1;
|
||||
}
|
||||
.icon-edit:hover, .icon-capture:hover, .icon-ok:hover, .icon-plus:hover {
|
||||
background-color: $color-success;
|
||||
color: $color-1;
|
||||
}
|
||||
.icon-copy:hover {
|
||||
background-color: $color-notice;
|
||||
color: $color-1;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="number"],
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.no-border {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.handle {
|
||||
@extend [class^="icon-"]:before;
|
||||
@extend .icon-reorder;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.no-borders {
|
||||
td, th {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
thead {
|
||||
th {
|
||||
padding: 10px;
|
||||
border-top: 1px solid $color-border;
|
||||
border-bottom: none;
|
||||
background-color: $color-tbl-thead;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
&:first-child th,
|
||||
&:first-child td {
|
||||
border-top: 1px solid $color-border;
|
||||
}
|
||||
&.even td {
|
||||
background-color: $color-tbl-even;
|
||||
|
||||
img {
|
||||
border: 1px solid very-light($color-3, 6);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover td {
|
||||
background-color: very-light($color-3, 5);
|
||||
|
||||
img {
|
||||
border: 1px solid $color-border;
|
||||
}
|
||||
}
|
||||
|
||||
&.deleted td {
|
||||
background-color: very-light($color-error, 6);
|
||||
border-color: very-light($color-error, 15);
|
||||
}
|
||||
|
||||
&.ui-sortable-placeholder td {
|
||||
border: 1px solid $color-2 !important;
|
||||
visibility: visible !important;
|
||||
|
||||
&.actions {
|
||||
background-color: transparent;
|
||||
border-right: none !important;
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
border-left: 1px solid $color-2 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-sortable-helper {
|
||||
width: 100%;
|
||||
|
||||
td {
|
||||
background-color: lighten($color-3, 33);
|
||||
border-bottom: 1px solid $color-border;
|
||||
|
||||
&.actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.no-border-top tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&.grand-total {
|
||||
td {
|
||||
border-color: $color-2 !important;
|
||||
text-transform: uppercase;
|
||||
font-size: 110%;
|
||||
font-weight: 600;
|
||||
background-color: lighten($color-2, 50);
|
||||
}
|
||||
.total {
|
||||
background-color: $color-2;
|
||||
color: $color-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
app/assets/stylesheets/admin/shared/typography.scss
Normal file
134
app/assets/stylesheets/admin/shared/typography.scss
Normal file
@@ -0,0 +1,134 @@
|
||||
@import 'admin/globals/variables';
|
||||
|
||||
// Base
|
||||
//--------------------------------------------------------------
|
||||
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; font-size: 13px; }
|
||||
|
||||
body {
|
||||
font-family: $base-font-family;
|
||||
font-size: $body-font-size;
|
||||
font-weight: 400;
|
||||
color: $color-body-text;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top: 1px solid $color-border;
|
||||
border-bottom: 1px solid white;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
strong, b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// links
|
||||
//--------------------------------------------------------------
|
||||
a {
|
||||
color: $color-link;
|
||||
text-decoration: none;
|
||||
line-height: inherit;
|
||||
|
||||
&, &:hover, &:active, &:visited, &:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: $color-link-visited;
|
||||
}
|
||||
&:focus {
|
||||
color: $color-link-focus;
|
||||
}
|
||||
&:active {
|
||||
color: $color-link-active;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-link-hover;
|
||||
}
|
||||
}
|
||||
|
||||
// Headings
|
||||
//--------------------------------------------------------------
|
||||
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
font-weight: 600;
|
||||
color: $color-headers;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
h1 { font-size: $h1-size; line-height: $h1-size + 6 }
|
||||
h2 { font-size: $h2-size; line-height: $h1-size + 4 }
|
||||
h3 { font-size: $h3-size; line-height: $h1-size + 2 }
|
||||
h4 { font-size: $h4-size; line-height: $h1-size }
|
||||
h5 { font-size: $h5-size; line-height: $h1-size }
|
||||
h6 { font-size: $h6-size; line-height: $h1-size }
|
||||
|
||||
|
||||
// Lists
|
||||
//--------------------------------------------------------------
|
||||
ul {
|
||||
&.inline-menu {
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
&.fields {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
dl {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 5px 0;
|
||||
color: lighten($color-body-text, 15);
|
||||
|
||||
dt, dd {
|
||||
float: left;
|
||||
line-height: 16px;
|
||||
padding: 5px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
dt {
|
||||
width: 40%;
|
||||
font-weight: 600;
|
||||
padding-left: 0;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
dd {
|
||||
width: 60%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
dd:after {
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Helpers
|
||||
.align-center { text-align: center }
|
||||
.align-right { text-align: right }
|
||||
.align-left { text-align: left }
|
||||
.align-justify { text-align: justify }
|
||||
|
||||
.uppercase { text-transform: uppercase }
|
||||
|
||||
.green { color: $color-2 }
|
||||
.blue { color: $color-3 }
|
||||
.red { color: $color-5 }
|
||||
.yellow { color: $color-6 }
|
||||
|
||||
.no-objects-found {
|
||||
text-align: center;
|
||||
font-size: 120%;
|
||||
text-transform: uppercase;
|
||||
padding: 40px 0px;
|
||||
color: lighten($color-body-text, 15);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'open_food_network/referer_parser'
|
||||
require 'open_food_network/permissions'
|
||||
require 'open_food_network/order_cycle_permissions'
|
||||
|
||||
module Admin
|
||||
class EnterprisesController < ResourceController
|
||||
@@ -22,7 +23,6 @@ module Admin
|
||||
before_filter :setup_property, only: [:edit]
|
||||
|
||||
helper 'spree/products'
|
||||
include ActionView::Helpers::TextHelper
|
||||
include OrderCyclesHelper
|
||||
|
||||
def index
|
||||
@@ -76,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
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This controller lists products that can be added to an exchange
|
||||
#
|
||||
# Pagination is optional and can be required by using param[:page]
|
||||
module Api
|
||||
class ExchangeProductsController < Api::BaseController
|
||||
DEFAULT_PAGE = 1
|
||||
DEFAULT_PER_PAGE = 100
|
||||
|
||||
skip_authorization_check only: [:index]
|
||||
@@ -29,22 +32,28 @@ module Api
|
||||
|
||||
def render_variant_count
|
||||
render text: {
|
||||
count: Spree::Variant.
|
||||
not_master.
|
||||
where(product_id: products).
|
||||
count
|
||||
count: variants.count
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def variants
|
||||
renderer.exchange_variants(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def products
|
||||
ExchangeProductsRenderer.
|
||||
new(@order_cycle, spree_current_user).
|
||||
exchange_products(@incoming, @enterprise)
|
||||
renderer.exchange_products(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def renderer
|
||||
@renderer ||= ExchangeProductsRenderer.
|
||||
new(@order_cycle, spree_current_user)
|
||||
end
|
||||
|
||||
def paginated_products
|
||||
return products unless pagination_required?
|
||||
|
||||
products.
|
||||
page(params[:page] || DEFAULT_PAGE).
|
||||
page(params[:page]).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
end
|
||||
|
||||
@@ -74,19 +83,23 @@ module Api
|
||||
order_cycle: @order_cycle
|
||||
)
|
||||
|
||||
render text: {
|
||||
products: serializer,
|
||||
pagination: pagination_data(paginated_products)
|
||||
}.to_json
|
||||
result = { products: serializer }
|
||||
result = result.merge(pagination: pagination_data(paginated_products)) if pagination_required?
|
||||
|
||||
render text: result.to_json
|
||||
end
|
||||
|
||||
def pagination_data(paginated_products)
|
||||
{
|
||||
results: paginated_products.total_count,
|
||||
pages: paginated_products.num_pages,
|
||||
page: (params[:page] || DEFAULT_PAGE).to_i,
|
||||
page: params[:page].to_i,
|
||||
per_page: (params[:per_page] || DEFAULT_PER_PAGE).to_i
|
||||
}
|
||||
end
|
||||
|
||||
def pagination_required?
|
||||
params[:page].present?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -23,7 +23,11 @@ class ApplicationController < ActionController::Base
|
||||
referer_path = OpenFoodNetwork::RefererParser.path(request.referer)
|
||||
if referer_path
|
||||
is_checkout_path_the_referer = [main_app.checkout_path].include?(referer_path)
|
||||
session["spree_user_return_to"] = is_checkout_path_the_referer ? referer_path : root_path
|
||||
session["spree_user_return_to"] = if is_checkout_path_the_referer
|
||||
referer_path
|
||||
else
|
||||
main_app.root_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,11 +52,11 @@ class ApplicationController < ActionController::Base
|
||||
def after_sign_in_path_for(resource_or_scope)
|
||||
return session[:shopfront_redirect] if session[:shopfront_redirect]
|
||||
|
||||
stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
|
||||
stored_location_for(resource_or_scope) || main_app.root_path
|
||||
end
|
||||
|
||||
def after_sign_out_path_for(_resource_or_scope)
|
||||
session[:shopfront_redirect] || root_path
|
||||
session[:shopfront_redirect] || main_app.root_path
|
||||
end
|
||||
|
||||
private
|
||||
@@ -74,7 +78,7 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
def require_distributor_chosen
|
||||
unless @distributor = current_distributor
|
||||
redirect_to spree.root_path
|
||||
redirect_to main_app.root_path
|
||||
false
|
||||
end
|
||||
end
|
||||
@@ -86,26 +90,29 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def check_hub_ready_for_checkout
|
||||
# This condition is more rigourous than required by development to avoid coupling this
|
||||
# condition to every controller spec
|
||||
if current_distributor && current_order &&
|
||||
current_distributor.respond_to?(:ready_for_checkout?) &&
|
||||
!current_distributor.ready_for_checkout?
|
||||
|
||||
if current_distributor_closed?
|
||||
current_order.empty!
|
||||
current_order.set_distribution! nil, nil
|
||||
flash[:info] = "The hub you have selected is temporarily closed for orders. Please try again later."
|
||||
redirect_to root_url
|
||||
flash[:info] = "The hub you have selected is temporarily closed for orders. "\
|
||||
"Please try again later."
|
||||
redirect_to main_app.root_url
|
||||
end
|
||||
end
|
||||
|
||||
def current_distributor_closed?
|
||||
current_distributor &&
|
||||
current_order &&
|
||||
current_distributor.respond_to?(:ready_for_checkout?) &&
|
||||
!current_distributor.ready_for_checkout?
|
||||
end
|
||||
|
||||
def check_order_cycle_expiry
|
||||
if current_order_cycle.andand.closed?
|
||||
session[:expired_order_cycle_id] = current_order_cycle.id
|
||||
current_order.empty!
|
||||
current_order.set_order_cycle! nil
|
||||
flash[:info] = "The order cycle you've selected has just closed. Please try again!"
|
||||
redirect_to root_url
|
||||
redirect_to main_app.root_url
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,11 +15,10 @@ module Spree
|
||||
# Warn the user when they have an active order cycle with hubs that are not ready
|
||||
# for checkout (ie. does not have valid shipping and payment methods).
|
||||
def warn_invalid_order_cycles
|
||||
distributors = active_distributors_not_ready_for_checkout
|
||||
return if flash[:notice].present?
|
||||
|
||||
return if distributors.empty? || flash[:notice].present?
|
||||
|
||||
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
|
||||
warning = OrderCycleWarning.new(spree_current_user).call
|
||||
flash[:notice] = warning if warning.present?
|
||||
end
|
||||
|
||||
# This is in Spree::Core::ControllerHelpers::Auth
|
||||
@@ -30,7 +29,7 @@ module Spree
|
||||
redirect_to '/unauthorized'
|
||||
else
|
||||
store_location
|
||||
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
|
||||
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,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?
|
||||
@@ -87,30 +82,8 @@ module Spree
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
|
||||
def config_locale
|
||||
Spree::Backend::Config[:locale]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active_distributors_not_ready_for_checkout
|
||||
ocs = OrderCycle.managed_by(spree_current_user).active
|
||||
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
|
||||
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
|
||||
end
|
||||
|
||||
def active_distributors_not_ready_for_checkout_message(distributors)
|
||||
distributor_names = distributors.map(&:name).join ', '
|
||||
|
||||
if distributors.count > 1
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_plural,
|
||||
distributor_names: distributor_names)
|
||||
else
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_singular,
|
||||
distributor_names: distributor_names)
|
||||
end
|
||||
end
|
||||
|
||||
def html_request?
|
||||
request.format.html?
|
||||
end
|
||||
@@ -121,13 +94,17 @@ module Spree
|
||||
|
||||
def render_as_json(data, options = {})
|
||||
ams_prefix = options.delete :ams_prefix
|
||||
if [Array, ActiveRecord::Relation].include? data.class
|
||||
if each_serializer_required?(data)
|
||||
render options.merge(json: data, each_serializer: serializer(ams_prefix))
|
||||
else
|
||||
render options.merge(json: data, serializer: serializer(ams_prefix))
|
||||
end
|
||||
end
|
||||
|
||||
def each_serializer_required?(data)
|
||||
['Array', 'ActiveRecord::Relation'].include?(data.class.name)
|
||||
end
|
||||
|
||||
def serializer(ams_prefix)
|
||||
unless ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym)
|
||||
raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}."
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
module Spree
|
||||
module Admin
|
||||
module Orders
|
||||
class CustomerDetailsController < Spree::Admin::BaseController
|
||||
before_filter :load_order
|
||||
before_filter :check_authorization
|
||||
before_filter :set_guest_checkout_status, only: :update
|
||||
|
||||
def show
|
||||
edit
|
||||
render action: :edit
|
||||
end
|
||||
|
||||
def edit
|
||||
country_id = Address.default.country.id
|
||||
@order.build_bill_address(country_id: country_id) if @order.bill_address.nil?
|
||||
@order.build_ship_address(country_id: country_id) if @order.ship_address.nil?
|
||||
end
|
||||
|
||||
def update
|
||||
if @order.update_attributes(params[:order])
|
||||
if params[:guest_checkout] == "false"
|
||||
@order.associate_user!(Spree.user_class.find_by_email(@order.email))
|
||||
end
|
||||
|
||||
AdvanceOrderService.new(@order).call
|
||||
|
||||
@order.shipments.map(&:refresh_rates)
|
||||
flash[:success] = Spree.t('customer_details_updated')
|
||||
redirect_to admin_order_customer_path(@order)
|
||||
else
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
||||
# Inherit CanCan permissions for the current order
|
||||
def model_class
|
||||
load_order unless @order
|
||||
@order
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_order
|
||||
@order = Order.find_by_number!(params[:order_id], include: :adjustments)
|
||||
end
|
||||
|
||||
def check_authorization
|
||||
load_order
|
||||
session[:access_token] ||= params[:token]
|
||||
|
||||
resource = @order
|
||||
action = params[:action].to_sym
|
||||
action = :edit if action == :show # show route renders :edit for this controller
|
||||
|
||||
authorize! action, resource, session[:access_token]
|
||||
end
|
||||
|
||||
def set_guest_checkout_status
|
||||
registered_user = Spree::User.find_by_email(params[:order][:email])
|
||||
|
||||
params[:order][:guest_checkout] = registered_user.nil?
|
||||
|
||||
return unless registered_user
|
||||
|
||||
@order.user_id = registered_user.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,49 +0,0 @@
|
||||
Spree::Admin::Orders::CustomerDetailsController.class_eval do
|
||||
before_filter :check_authorization
|
||||
before_filter :set_guest_checkout_status, only: :update
|
||||
|
||||
def update
|
||||
if @order.update_attributes(params[:order])
|
||||
if params[:guest_checkout] == "false"
|
||||
@order.associate_user!(Spree.user_class.find_by_email(@order.email))
|
||||
end
|
||||
|
||||
AdvanceOrderService.new(@order).call
|
||||
|
||||
@order.shipments.map &:refresh_rates
|
||||
flash[:success] = Spree.t('customer_details_updated')
|
||||
redirect_to admin_order_customer_path(@order)
|
||||
else
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
||||
# Inherit CanCan permissions for the current order
|
||||
def model_class
|
||||
load_order unless @order
|
||||
@order
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_authorization
|
||||
load_order
|
||||
session[:access_token] ||= params[:token]
|
||||
|
||||
resource = @order
|
||||
action = params[:action].to_sym
|
||||
action = :edit if action == :show # show route renders :edit for this controller
|
||||
|
||||
authorize! action, resource, session[:access_token]
|
||||
end
|
||||
|
||||
def set_guest_checkout_status
|
||||
registered_user = Spree::User.find_by_email(params[:order][:email])
|
||||
|
||||
params[:order][:guest_checkout] = registered_user.nil?
|
||||
|
||||
return unless registered_user
|
||||
|
||||
@order.user_id = registered_user.id
|
||||
end
|
||||
end
|
||||
150
app/controllers/spree/admin/orders_controller.rb
Normal file
150
app/controllers/spree/admin/orders_controller.rb
Normal file
@@ -0,0 +1,150 @@
|
||||
require 'open_food_network/spree_api_key_loader'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
class OrdersController < Spree::Admin::BaseController
|
||||
require 'spree/core/gateway_error'
|
||||
include OpenFoodNetwork::SpreeApiKeyLoader
|
||||
helper CheckoutHelper
|
||||
|
||||
before_filter :load_order, only: [:edit, :update, :fire, :resend,
|
||||
:invoice, :print, :print_ticket]
|
||||
before_filter :load_distribution_choices, only: [:new, :edit, :update]
|
||||
|
||||
# Ensure that the distributor is set for an order when
|
||||
before_filter :ensure_distribution, only: :new
|
||||
|
||||
# After updating an order, the fees should be updated as well
|
||||
# Currently, adding or deleting line items does not trigger updating the
|
||||
# fees! This is a quick fix for that.
|
||||
# TODO: update fees when adding/removing line items
|
||||
# instead of the update_distribution_charge method.
|
||||
after_filter :update_distribution_charge, only: :update
|
||||
|
||||
before_filter :require_distributor_abn, only: :invoice
|
||||
|
||||
respond_to :html, :json
|
||||
|
||||
def new
|
||||
@order = Order.create
|
||||
@order.created_by = try_spree_current_user
|
||||
@order.save
|
||||
redirect_to edit_admin_order_url(@order)
|
||||
end
|
||||
|
||||
def edit
|
||||
@order.shipments.map(&:refresh_rates)
|
||||
|
||||
AdvanceOrderService.new(@order).call
|
||||
|
||||
# The payment step shows an error of 'No pending payments'
|
||||
# Clearing the errors from the order object will stop this error
|
||||
# appearing on the edit page where we don't want it to.
|
||||
@order.errors.clear
|
||||
end
|
||||
|
||||
def update
|
||||
unless @order.update_attributes(params[:order]) && @order.line_items.present?
|
||||
if @order.line_items.empty?
|
||||
@order.errors.add(:line_items, Spree.t('errors.messages.blank'))
|
||||
end
|
||||
return redirect_to(edit_admin_order_path(@order),
|
||||
flash: { error: @order.errors.full_messages.join(', ') })
|
||||
end
|
||||
|
||||
@order.update!
|
||||
if @order.complete?
|
||||
redirect_to edit_admin_order_path(@order)
|
||||
else
|
||||
# Jump to next step if order is not complete
|
||||
redirect_to admin_order_customer_path(@order)
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_management
|
||||
load_spree_api_key
|
||||
end
|
||||
|
||||
def fire
|
||||
event = params[:e]
|
||||
if @order.public_send(event.to_s)
|
||||
flash[:success] = Spree.t(:order_updated)
|
||||
else
|
||||
flash[:error] = Spree.t(:cannot_perform_operation)
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = e.message.to_s
|
||||
ensure
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
def resend
|
||||
Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver
|
||||
flash[:success] = t(:order_email_resent)
|
||||
|
||||
respond_with(@order) { |format| format.html { redirect_to :back } }
|
||||
end
|
||||
|
||||
def invoice
|
||||
pdf = InvoiceRenderer.new.render_to_string(@order)
|
||||
|
||||
Spree::OrderMailer.invoice_email(@order.id, pdf).deliver
|
||||
flash[:success] = t('admin.orders.invoice_email_sent')
|
||||
|
||||
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
|
||||
end
|
||||
|
||||
def print
|
||||
render InvoiceRenderer.new.args(@order)
|
||||
end
|
||||
|
||||
def print_ticket
|
||||
render template: "spree/admin/orders/ticket", layout: false
|
||||
end
|
||||
|
||||
def update_distribution_charge
|
||||
@order.update_distribution_charge!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_order
|
||||
@order = Order.find_by_number!(params[:id], include: :adjustments) if params[:id]
|
||||
authorize! action, @order
|
||||
end
|
||||
|
||||
def model_class
|
||||
Spree::Order
|
||||
end
|
||||
|
||||
def require_distributor_abn
|
||||
return if @order.distributor.abn.present?
|
||||
|
||||
flash[:error] = t(:must_have_valid_business_number,
|
||||
enterprise_name: @order.distributor.name)
|
||||
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
|
||||
end
|
||||
|
||||
def load_distribution_choices
|
||||
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
|
||||
|
||||
ocs = OrderCycle.managed_by(spree_current_user)
|
||||
@order_cycles = ocs.soonest_closing +
|
||||
ocs.soonest_opening +
|
||||
ocs.closed +
|
||||
ocs.undated
|
||||
end
|
||||
|
||||
def ensure_distribution
|
||||
unless @order
|
||||
@order = Spree::Order.new
|
||||
@order.generate_order_number
|
||||
@order.save!
|
||||
end
|
||||
return if @order.distribution_set?
|
||||
|
||||
render 'set_distribution', locals: { order: @order }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,120 +0,0 @@
|
||||
require 'open_food_network/spree_api_key_loader'
|
||||
|
||||
Spree::Admin::OrdersController.class_eval do
|
||||
include OpenFoodNetwork::SpreeApiKeyLoader
|
||||
helper CheckoutHelper
|
||||
before_filter :load_order, only: %i[show edit update fire resend invoice print print_ticket]
|
||||
|
||||
before_filter :load_distribution_choices, only: [:new, :edit, :update]
|
||||
|
||||
# Ensure that the distributor is set for an order when
|
||||
before_filter :ensure_distribution, only: :new
|
||||
|
||||
# After updating an order, the fees should be updated as well
|
||||
# Currently, adding or deleting line items does not trigger updating the
|
||||
# fees! This is a quick fix for that.
|
||||
# TODO: update fees when adding/removing line items
|
||||
# instead of the update_distribution_charge method.
|
||||
after_filter :update_distribution_charge, only: :update
|
||||
|
||||
before_filter :require_distributor_abn, only: :invoice
|
||||
|
||||
respond_to :html, :json
|
||||
|
||||
def index
|
||||
# Overriding the action so we only render the page template. An angular request
|
||||
# within the page then fetches the data it needs from Api::OrdersController
|
||||
end
|
||||
|
||||
def bulk_management
|
||||
load_spree_api_key
|
||||
end
|
||||
|
||||
def edit
|
||||
@order.shipments.map &:refresh_rates
|
||||
|
||||
AdvanceOrderService.new(@order).call
|
||||
|
||||
# The payment step shows an error of 'No pending payments'
|
||||
# Clearing the errors from the order object will stop this error
|
||||
# appearing on the edit page where we don't want it to.
|
||||
@order.errors.clear
|
||||
end
|
||||
|
||||
# Re-implement spree method so that it redirects to edit instead of rendering edit
|
||||
# This allows page reloads while adding variants to the order (/edit), without being redirected to customer details page (/update)
|
||||
def update
|
||||
unless @order.update_attributes(params[:order]) && @order.line_items.present?
|
||||
@order.errors.add(:line_items, Spree.t('errors.messages.blank')) if @order.line_items.empty?
|
||||
return redirect_to edit_admin_order_path(@order), flash: { error: @order.errors.full_messages.join(', ') }
|
||||
end
|
||||
|
||||
@order.update!
|
||||
if @order.complete?
|
||||
redirect_to edit_admin_order_path(@order)
|
||||
else
|
||||
# Jump to next step if order is not complete
|
||||
redirect_to admin_order_customer_path(@order)
|
||||
end
|
||||
end
|
||||
|
||||
# Overwrite to use confirm_email_for_customer instead of confirm_email.
|
||||
# This uses a new template. See mailers/spree/order_mailer_decorator.rb.
|
||||
def resend
|
||||
Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver
|
||||
flash[:success] = t(:order_email_resent)
|
||||
|
||||
respond_with(@order) { |format| format.html { redirect_to :back } }
|
||||
end
|
||||
|
||||
def invoice
|
||||
pdf = InvoiceRenderer.new.render_to_string(@order)
|
||||
|
||||
Spree::OrderMailer.invoice_email(@order.id, pdf).deliver
|
||||
flash[:success] = t('admin.orders.invoice_email_sent')
|
||||
|
||||
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
|
||||
end
|
||||
|
||||
def print
|
||||
render InvoiceRenderer.new.args(@order)
|
||||
end
|
||||
|
||||
def print_ticket
|
||||
render template: "spree/admin/orders/ticket", layout: false
|
||||
end
|
||||
|
||||
def update_distribution_charge
|
||||
@order.update_distribution_charge!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_distributor_abn
|
||||
if @order.distributor.abn.blank?
|
||||
flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name)
|
||||
respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } }
|
||||
end
|
||||
end
|
||||
|
||||
def load_distribution_choices
|
||||
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
|
||||
|
||||
ocs = OrderCycle.managed_by(spree_current_user)
|
||||
@order_cycles = ocs.soonest_closing +
|
||||
ocs.soonest_opening +
|
||||
ocs.closed +
|
||||
ocs.undated
|
||||
end
|
||||
|
||||
def ensure_distribution
|
||||
unless @order
|
||||
@order = Spree::Order.new
|
||||
@order.generate_order_number
|
||||
@order.save!
|
||||
end
|
||||
unless @order.distribution_set?
|
||||
render 'set_distribution', locals: { order: @order }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
129
app/controllers/spree/admin/payments_controller.rb
Normal file
129
app/controllers/spree/admin/payments_controller.rb
Normal file
@@ -0,0 +1,129 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
class PaymentsController < Spree::Admin::BaseController
|
||||
before_filter :load_order, except: [:show]
|
||||
before_filter :load_payment, only: [:fire, :show]
|
||||
before_filter :load_data
|
||||
before_filter :can_transition_to_payment
|
||||
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
@payments = @order.payments
|
||||
redirect_to new_admin_order_payment_url(@order) if @payments.empty?
|
||||
end
|
||||
|
||||
def new
|
||||
@payment = @order.payments.build
|
||||
end
|
||||
|
||||
def create
|
||||
@payment = @order.payments.build(object_params)
|
||||
load_payment_source
|
||||
|
||||
begin
|
||||
unless @payment.save
|
||||
redirect_to admin_order_payments_path(@order)
|
||||
return
|
||||
end
|
||||
|
||||
if @order.completed?
|
||||
@payment.process!
|
||||
flash[:success] = flash_message_for(@payment, :successfully_created)
|
||||
|
||||
redirect_to admin_order_payments_path(@order)
|
||||
else
|
||||
AdvanceOrderService.new(@order).call!
|
||||
|
||||
flash[:success] = Spree.t(:new_order_completed)
|
||||
redirect_to edit_admin_order_url(@order)
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = e.message.to_s
|
||||
redirect_to new_admin_order_payment_path(@order)
|
||||
end
|
||||
end
|
||||
|
||||
# When a user fires an event, take them back to where they came from
|
||||
# (we can't use respond_override because Spree no longer uses respond_with)
|
||||
def fire
|
||||
event = params[:e]
|
||||
return unless event && @payment.payment_source
|
||||
|
||||
# Because we have a transition method also called void, we do this to avoid conflicts.
|
||||
event = "void_transaction" if event == "void"
|
||||
if @payment.public_send("#{event}!")
|
||||
flash[:success] = t(:payment_updated)
|
||||
else
|
||||
flash[:error] = t(:cannot_perform_operation)
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = e.message
|
||||
ensure
|
||||
redirect_to request.referer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_payment_source
|
||||
if @payment.payment_method.is_a?(Spree::Gateway) &&
|
||||
@payment.payment_method.payment_profiles_supported? &&
|
||||
params[:card].present? &&
|
||||
(params[:card] != 'new')
|
||||
@payment.source = CreditCard.find_by_id(params[:card])
|
||||
end
|
||||
end
|
||||
|
||||
def object_params
|
||||
if params[:payment] &&
|
||||
params[:payment_source] &&
|
||||
source_params = params.delete(:payment_source)[params[:payment][:payment_method_id]]
|
||||
params[:payment][:source_attributes] = source_params
|
||||
end
|
||||
params[:payment]
|
||||
end
|
||||
|
||||
def load_data
|
||||
@amount = params[:amount] || load_order.total
|
||||
|
||||
# Only show payments for the order's distributor
|
||||
@payment_methods = PaymentMethod.
|
||||
available(:back_end).
|
||||
select{ |pm| pm.has_distributor? @order.distributor }
|
||||
|
||||
@payment_method = if @payment && @payment.payment_method
|
||||
@payment.payment_method
|
||||
else
|
||||
@payment_methods.first
|
||||
end
|
||||
|
||||
@previous_cards = @order.credit_cards.with_payment_profile
|
||||
end
|
||||
|
||||
# At this point admin should have passed through Customer Details step
|
||||
# where order.next is called which leaves the order in payment step
|
||||
#
|
||||
# Orders in complete step also allows to access this controller
|
||||
#
|
||||
# Otherwise redirect user to that step
|
||||
def can_transition_to_payment
|
||||
return if @order.payment? || @order.complete?
|
||||
|
||||
flash[:notice] = Spree.t(:fill_in_customer_info)
|
||||
redirect_to edit_admin_order_customer_url(@order)
|
||||
end
|
||||
|
||||
def load_order
|
||||
@order = Order.find_by_number!(params[:order_id])
|
||||
authorize! action, @order
|
||||
@order
|
||||
end
|
||||
|
||||
def load_payment
|
||||
@payment = Payment.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,59 +0,0 @@
|
||||
Spree::Admin::PaymentsController.class_eval do
|
||||
append_before_filter :filter_payment_methods
|
||||
|
||||
def create
|
||||
@payment = @order.payments.build(object_params)
|
||||
if @payment.payment_method.is_a?(Spree::Gateway) && @payment.payment_method.payment_profiles_supported? && params[:card].present? && (params[:card] != 'new')
|
||||
@payment.source = CreditCard.find_by_id(params[:card])
|
||||
end
|
||||
|
||||
begin
|
||||
unless @payment.save
|
||||
redirect_to admin_order_payments_path(@order)
|
||||
return
|
||||
end
|
||||
|
||||
if @order.completed?
|
||||
@payment.process!
|
||||
flash[:success] = flash_message_for(@payment, :successfully_created)
|
||||
|
||||
redirect_to admin_order_payments_path(@order)
|
||||
else
|
||||
AdvanceOrderService.new(@order).call!
|
||||
|
||||
flash[:success] = Spree.t(:new_order_completed)
|
||||
redirect_to edit_admin_order_url(@order)
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = e.message.to_s
|
||||
redirect_to new_admin_order_payment_path(@order)
|
||||
end
|
||||
end
|
||||
|
||||
# When a user fires an event, take them back to where they came from
|
||||
# (we can't use respond_override because Spree no longer uses respond_with)
|
||||
def fire
|
||||
event = params[:e]
|
||||
return unless event && @payment.payment_source
|
||||
|
||||
# Because we have a transition method also called void, we do this to avoid conflicts.
|
||||
event = "void_transaction" if event == "void"
|
||||
if @payment.public_send("#{event}!")
|
||||
flash[:success] = t(:payment_updated)
|
||||
else
|
||||
flash[:error] = t(:cannot_perform_operation)
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
flash[:error] = e.message
|
||||
ensure
|
||||
redirect_to request.referer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Only show payments for the order's distributor
|
||||
def filter_payment_methods
|
||||
@payment_methods = @payment_methods.select{ |pm| pm.has_distributor? @order.distributor }
|
||||
@payment_method ||= @payment_methods.first
|
||||
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.
|
||||
|
||||
43
app/controllers/spree/admin/search_controller.rb
Normal file
43
app/controllers/spree/admin/search_controller.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class SearchController < Spree::Admin::BaseController
|
||||
# http://spreecommerce.com/blog/2010/11/02/json-hijacking-vulnerability/
|
||||
before_filter :check_json_authenticity, only: :index
|
||||
respond_to :json
|
||||
|
||||
def known_users
|
||||
@users = if exact_match = Spree.user_class.find_by_email(params[:q])
|
||||
[exact_match]
|
||||
else
|
||||
spree_current_user.known_users.ransack(ransack_hash).result.limit(10)
|
||||
end
|
||||
|
||||
render json: @users, each_serializer: ::Api::Admin::UserSerializer
|
||||
end
|
||||
|
||||
def customers
|
||||
@customers = []
|
||||
if spree_current_user.enterprises.pluck(:id).include? params[:distributor_id].to_i
|
||||
@customers = Customer.
|
||||
ransack(m: 'or', email_start: params[:q], name_start: params[:q]).
|
||||
result.
|
||||
where(enterprise_id: params[:distributor_id])
|
||||
end
|
||||
render json: @customers, each_serializer: ::Api::Admin::CustomerSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ransack_hash
|
||||
{
|
||||
m: 'or',
|
||||
email_start: params[:q],
|
||||
ship_address_firstname_start: params[:q],
|
||||
ship_address_lastname_start: params[:q],
|
||||
bill_address_firstname_start: params[:q],
|
||||
bill_address_lastname_start: params[:q]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,36 +0,0 @@
|
||||
Spree::Admin::SearchController.class_eval do
|
||||
def known_users
|
||||
@users = if exact_match = Spree.user_class.find_by_email(params[:q])
|
||||
[exact_match]
|
||||
else
|
||||
spree_current_user.known_users.ransack(
|
||||
m: 'or',
|
||||
email_start: params[:q],
|
||||
ship_address_firstname_start: params[:q],
|
||||
ship_address_lastname_start: params[:q],
|
||||
bill_address_firstname_start: params[:q],
|
||||
bill_address_lastname_start: params[:q]
|
||||
).result.limit(10)
|
||||
end
|
||||
|
||||
render json: @users, each_serializer: Api::Admin::UserSerializer
|
||||
end
|
||||
|
||||
def customers
|
||||
@customers = if spree_current_user.enterprises.pluck(:id).include? params[:distributor_id].to_i
|
||||
Customer.ransack(m: 'or', email_start: params[:q], name_start: params[:q])
|
||||
.result.where(enterprise_id: params[:distributor_id])
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
render json: @customers, each_serializer: Api::Admin::CustomerSerializer
|
||||
end
|
||||
|
||||
def users_with_ams
|
||||
users_without_ams
|
||||
render json: @users, each_serializer: Api::Admin::UserSerializer
|
||||
end
|
||||
|
||||
alias_method_chain :users, :ams
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -71,7 +71,7 @@ module Spree
|
||||
@order = order_to_update
|
||||
unless @order
|
||||
flash[:error] = t(:order_not_found)
|
||||
redirect_to(root_path) && return
|
||||
redirect_to(main_app.root_path) && return
|
||||
end
|
||||
|
||||
if @order.update_attributes(params[:order])
|
||||
|
||||
@@ -32,7 +32,7 @@ module Spree
|
||||
session[:guest_token] = nil
|
||||
end
|
||||
|
||||
redirect_back_or_default(root_url)
|
||||
redirect_back_or_default(main_app.root_url)
|
||||
else
|
||||
render :new
|
||||
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
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
module Spree
|
||||
module Admin
|
||||
module OrdersHelper
|
||||
def event_links
|
||||
links = []
|
||||
links << event_link("cancel") if @order.can_cancel?
|
||||
links << event_link("resume") if @order.can_resume?
|
||||
links.join(' ').html_safe
|
||||
end
|
||||
|
||||
def line_item_shipment_price(line_item, quantity)
|
||||
Spree::Money.new(line_item.price * quantity, currency: line_item.currency)
|
||||
end
|
||||
|
||||
def order_links(order)
|
||||
@order ||= order
|
||||
links = []
|
||||
@@ -100,6 +111,14 @@ module Spree
|
||||
icon: 'icon-trash',
|
||||
confirm: t(:are_you_sure) }
|
||||
end
|
||||
|
||||
def event_link(event)
|
||||
button_link_to(Spree.t(event),
|
||||
fire_admin_order_url(@order, e: event),
|
||||
method: :put,
|
||||
icon: "icon-#{event}",
|
||||
data: { confirm: Spree.t(:order_sure_want_to, event: Spree.t(event)) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
11
app/helpers/spree/admin/payments_helper.rb
Normal file
11
app/helpers/spree/admin/payments_helper.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
module Spree
|
||||
module Admin
|
||||
module PaymentsHelper
|
||||
def payment_method_name(payment)
|
||||
# hack to allow us to retrieve the name of a "deleted" payment method
|
||||
id = payment.payment_method_id
|
||||
Spree::PaymentMethod.find_with_destroyed(id).name
|
||||
end
|
||||
end
|
||||
end
|
||||
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)
|
||||
|
||||
@@ -9,6 +9,6 @@ Spree::BaseMailer.class_eval do
|
||||
|
||||
def roadie_options
|
||||
# This lets us specify assets using relative paths in email templates
|
||||
super.merge(url_options: { host: URI(spree.root_url).host })
|
||||
super.merge(url_options: { host: URI(main_app.root_url).host })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,8 @@ class Enterprise < ActiveRecord::Base
|
||||
has_many :relationships_as_child, class_name: 'EnterpriseRelationship',
|
||||
foreign_key: 'child_id',
|
||||
dependent: :destroy
|
||||
has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup'
|
||||
has_and_belongs_to_many :groups, join_table: 'enterprise_groups_enterprises',
|
||||
class_name: 'EnterpriseGroup'
|
||||
has_many :producer_properties, foreign_key: 'producer_id'
|
||||
has_many :properties, through: :producer_properties
|
||||
has_many :supplied_products, class_name: 'Spree::Product',
|
||||
@@ -115,7 +116,10 @@ class Enterprise < ActiveRecord::Base
|
||||
scope :not_ready_for_checkout, lambda {
|
||||
# When ready_for_checkout is empty, return all rows when there are no enterprises ready for
|
||||
# checkout.
|
||||
ready_enterprises = Enterprise.ready_for_checkout.select('enterprises.id')
|
||||
ready_enterprises = Enterprise.ready_for_checkout.
|
||||
except(:select).
|
||||
select('DISTINCT enterprises.id')
|
||||
|
||||
if ready_enterprises.present?
|
||||
where("enterprises.id NOT IN (?)", ready_enterprises)
|
||||
else
|
||||
@@ -317,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
|
||||
|
||||
@@ -333,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
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class EnterpriseGroup < ActiveRecord::Base
|
||||
include PermalinkGenerator
|
||||
acts_as_list
|
||||
|
||||
has_and_belongs_to_many :enterprises
|
||||
has_and_belongs_to_many :enterprises, join_table: 'enterprise_groups_enterprises'
|
||||
belongs_to :owner, class_name: 'Spree::User', foreign_key: :owner_id, inverse_of: :owned_groups
|
||||
belongs_to :address, class_name: 'Spree::Address'
|
||||
accepts_nested_attributes_for :address
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -63,7 +64,7 @@ class OrderCycle < ActiveRecord::Base
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
where('coordinator_id IN (?)', user.enterprises.map(&:id))
|
||||
where(coordinator_id: user.enterprises)
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user